├── .github ├── dependabot.yml ├── labels.yml ├── release-drafter.yml ├── sync-release.yml ├── sync.yml ├── weekly-digest.yml └── workflows │ ├── auto-assign-issue.yml │ ├── auto-gh-pr.yml │ ├── auto-invite.yml │ ├── auto-tag.yml │ ├── bot-auto-cherry-pick.yml │ ├── bot-cherry-pick.yml │ ├── build-test.yml │ ├── cla.yml │ ├── codeql-analysis.yml │ ├── depsreview.yaml │ ├── golangci-lint.yml │ ├── gosec.yml │ ├── greetings.yml │ ├── help-comment-issue.yml │ ├── issue-robot.yml │ ├── link-pr.yml │ ├── milestone.yml │ ├── opencommit.yml │ ├── project-progress.yml │ ├── release-drafter.yml │ └── stale.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── LICENSE ├── Makefile ├── README.md ├── bootstrap.bat ├── bootstrap.sh ├── cmd └── openim-sdk │ └── main.go ├── config ├── config.yml └── log.yml ├── go.mod ├── go.sum ├── internal └── sdk │ ├── sdk.go │ └── start.go ├── magefile.go ├── magefile_unix.go ├── magefile_windows.go ├── pkg ├── common │ ├── cmd │ │ ├── constant.go │ │ ├── root.go │ │ └── sdk.go │ ├── config │ │ ├── config.go │ │ ├── constant.go │ │ ├── doc.go │ │ ├── load_config.go │ │ ├── version │ │ └── version.go │ ├── struct.go │ └── utils.go ├── core_func │ ├── responder.go │ ├── ws_conversation_msg.go │ ├── ws_conversation_msg_test.go │ ├── ws_file.go │ ├── ws_friend.go │ ├── ws_group.go │ ├── ws_handler.go │ ├── ws_init_login.go │ ├── ws_init_login_test.go │ ├── ws_listener.go │ ├── ws_third.go │ └── ws_user.go ├── gate │ ├── agent.go │ └── gate.go ├── module │ ├── MActor.go │ ├── jsCore.go │ ├── netmodule.go │ ├── statusActor.go │ ├── statusNetmodule.go │ └── struct.go └── network │ ├── agent.go │ ├── conn.go │ ├── defines.go │ ├── processor.go │ ├── tjson │ └── tjson.go │ ├── ws_client.go │ ├── ws_conn.go │ └── ws_server.go └── start-config.yml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # To get started with Dependabot version updates, you'll need to specify which 16 | # package ecosystems to update and where the package manifests are located. 17 | # Please see the documentation for all configuration options: 18 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 19 | 20 | version: 2 21 | updates: 22 | - package-ecosystem: "gomod" 23 | directory: "/" 24 | schedule: 25 | interval: "daily" 26 | time: "08:00" 27 | labels: 28 | - "dependencies" 29 | commit-message: 30 | prefix: "feat" 31 | include: "scope" 32 | groups: 33 | gomod-deps: 34 | patterns: 35 | - "*" 36 | - package-ecosystem: "github-actions" 37 | directory: "/" 38 | schedule: 39 | interval: "daily" 40 | time: "08:00" 41 | labels: 42 | - "dependencies" 43 | commit-message: 44 | prefix: "chore" 45 | include: "scope" 46 | groups: 47 | github-actions: 48 | patterns: 49 | - "*" 50 | - package-ecosystem: "docker" 51 | directory: "/" 52 | schedule: 53 | interval: "daily" 54 | time: "08:00" 55 | labels: 56 | - "dependencies" 57 | commit-message: 58 | prefix: "feat" 59 | include: "scope" -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Refer to Kubernetes for size/* Settings 16 | # https://github.com/Kubernetes/Kubernetes 17 | XS: 18 | name: size/XS 19 | lines: 0 20 | color: 3CBF00 21 | S: 22 | name: size/S 23 | lines: 10 24 | color: 5D9801 25 | M: 26 | name: size/M 27 | lines: 30 28 | color: 7F7203 29 | L: 30 | name: size/L 31 | lines: 100 32 | color: A14C05 33 | XL: 34 | name: size/XL 35 | lines: 500 36 | color: C32607 37 | XXL: 38 | name: size/XXL 39 | lines: 1000 40 | color: E50009 41 | comment: | 42 | # Whoa! Easy there, Partner! 43 | This PR is too big. Please break it up into smaller PRs. -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name-template: 'v$RESOLVED_VERSION 🌈' 16 | tag-template: 'v$RESOLVED_VERSION' 17 | categories: 18 | - title: '🚀 Features' 19 | labels: 20 | - 'feature' 21 | - 'enhancement' 22 | - title: '🐛 Bug Fixes' 23 | labels: 24 | - 'kind/fix' 25 | - 'kind/feature' 26 | - 'enhancement' 27 | - 'kind/documentation' 28 | - 'good first issue' 29 | - title: '🧰 Maintenance' 30 | label: 'chore' 31 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 32 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 33 | version-resolver: 34 | major: 35 | labels: 36 | - 'major' 37 | minor: 38 | labels: 39 | - 'minor' 40 | patch: 41 | labels: 42 | - 'patch' 43 | default: patch 44 | template: | 45 | ## Changes $PREVIOUS_TAG 46 | 47 | $CHANGES 48 | 49 | ## Contributors to this $REPOSITORY release 50 | 51 | $CONTRIBUTORS -------------------------------------------------------------------------------- /.github/sync-release.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | OpenIMSDK/openim-docker: 16 | - source: ./config 17 | dest: ./openim-server/release/config 18 | replace: true 19 | - source: ./docs 20 | dest: ./openim-server/release/docs 21 | replace: true 22 | - source: ./scripts 23 | dest: ./openim-server/release/scripts 24 | replace: true 25 | - source: ./scripts 26 | dest: ./scripts 27 | replace: false 28 | - source: ./Makefile 29 | dest: ./Makefile 30 | replace: false 31 | -------------------------------------------------------------------------------- /.github/sync.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # https://github.com/BetaHuhn/repo-file-sync-action 16 | # Synchronization for the.github repository 17 | OpenIMSDK/.github: 18 | - source: LICENSE 19 | dest: LICENSE 20 | - source: scripts/LICENSE/ 21 | dest: scripts/LICENSE/ 22 | replace: false 23 | 24 | OpenIMSDK/community: 25 | - source: LICENSE 26 | dest: LICENSE 27 | - source: scripts/LICENSE/ 28 | dest: scripts/LICENSE/ 29 | replace: false 30 | - source: .github/workflows/ 31 | dest: .github/workflows/ 32 | 33 | OpenIMSDK/openim-sdk-core: 34 | - source: LICENSE 35 | dest: LICENSE 36 | - source: scripts/LICENSE/ 37 | dest: scripts/LICENSE/ 38 | replace: false 39 | - source: .github/workflows/issue-robot.yml 40 | dest: .github/workflows/issue-robot.yml 41 | replace: false 42 | - source: .github/workflows/stale.yml 43 | dest: .github/workflows/stale.yml 44 | replace: false 45 | - source: .github/.codecov.yml 46 | dest: .github/.codecov.yml 47 | replace: false 48 | 49 | OpenIMSDK/OpenIM-Docs: 50 | - source: .github/workflows/ 51 | dest: .github/workflows/ 52 | exclude: | 53 | e2e-test.yml 54 | sync.yml 55 | - source: scripts/githooks/ 56 | dest: scripts/githooks/ 57 | replace: true 58 | - source: .github/.codecov.yml 59 | dest: .github/.codecov.yml 60 | replace: false 61 | 62 | OpenIMSDK/OpenKF: 63 | - source: LICENSE 64 | dest: LICENSE 65 | - source: scripts/LICENSE/ 66 | dest: scripts/LICENSE/ 67 | replace: false 68 | - source: .github/workflows/issue-robot.yml 69 | dest: .github/workflows/issue-robot.yml 70 | replace: false 71 | - source: .github/workflows/stale.yml 72 | dest: .github/workflows/stale.yml 73 | replace: false 74 | - source: .github/.codecov.yml 75 | dest: .github/.codecov.yml 76 | replace: false 77 | 78 | OpenIMSDK/openim-docker: 79 | - source: ./config 80 | dest: ./openim-server/main/config 81 | replace: true 82 | - source: ./docs 83 | dest: ./openim-server/main/docs 84 | replace: true 85 | - source: ./scripts 86 | dest: ./openim-server/main/scripts 87 | replace: true 88 | - source: ./scripts 89 | dest: ./scripts 90 | replace: true 91 | - source: ./Makefile 92 | dest: ./Makefile 93 | replace: true 94 | 95 | group: 96 | # first group:common to all warehouses 97 | # TODO: add the required warehouse here 98 | - repos: | 99 | OpenIMSDK/OpenKF@main 100 | OpenIMSDK/openim-miniprogram-demo@main 101 | OpenIMSDK/docs 102 | OpenIMSDK/chat 103 | OpenIMSDK/community 104 | OpenIMSDK/openim-charts 105 | OpenIMSDK/openim-sdk-cpp@main 106 | files: 107 | - source: LICENSE 108 | dest: LICENSE 109 | replace: false 110 | - source: .github/workflows/issue-robot.yml 111 | dest: .github/workflows/issue-robot.yml 112 | replace: false 113 | - source: .github/workflows/stale.yml 114 | dest: .github/workflows/stale.yml 115 | replace: false 116 | - source: .github/workflows/project-progress.yml 117 | dest: .github/workflows/project-progress.yml 118 | replace: false 119 | - source: .github/workflows/help-comment-issue.yml 120 | dest: .github/workflows/help-comment-issue.yml 121 | replace: false 122 | - source: .github/.codecov.yml 123 | dest: .github/.codecov.yml 124 | replace: false 125 | - source: .github/workflows/cla.yml 126 | dest: .github/workflows/cla.yml 127 | replace: false 128 | - source: .github/workflows/auto-assign-issue.yml 129 | dest: .github/workflows/auto-assign-issue.yml 130 | replace: false 131 | - source: .github/workflows/release.yml 132 | dest: .github/workflows/release.yml 133 | replace: false 134 | - source: ./scripts/githooks/ 135 | dest: ./scripts/githooks/ 136 | replace: true 137 | -------------------------------------------------------------------------------- /.github/weekly-digest.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # https://github.com/apps/weekly-digest/installations/new 16 | publishDay: sun 17 | canPublishIssues: true 18 | canPublishPullRequests: true 19 | canPublishContributors: true 20 | canPublishStargazers: true 21 | canPublishCommits: true -------------------------------------------------------------------------------- /.github/workflows/auto-assign-issue.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Assign issue to comment author 16 | on: 17 | issue_comment: 18 | types: [created] 19 | jobs: 20 | assign-issue: 21 | if: | 22 | contains(github.event.comment.body, '/assign') || contains(github.event.comment.body, '/accept') && 23 | !contains(github.event.comment.user.login, 'openimbot') && 24 | !contains(github.event.comment.user.login, 'kubbot') 25 | runs-on: ubuntu-latest 26 | permissions: 27 | issues: write 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@v4 31 | 32 | - name: Assign the issue 33 | run: | 34 | export LETASE_MILESTONES=$(curl 'https://api.github.com/repos/$OWNER/$PEPO/milestones' | jq -r 'last(.[]).title') 35 | gh issue edit ${{ github.event.issue.number }} --add-assignee "${{ github.event.comment.user.login }}" 36 | gh issue edit ${{ github.event.issue.number }} --add-label "triage/accepted" 37 | gh issue edit ${{ github.event.issue.number }} --milestone "$LETASE_MILESTONES" 38 | gh issue comment $ISSUE --body "@${{ github.event.comment.user.login }} Glad to see you accepted this issue🤲, this issue has been assigned to you. I set the milestones for this issue to [$LETASE_MILESTONES](https://github.com/$OWNER/$PEPO/milestones), We are looking forward to your PR!" 39 | env: 40 | GH_TOKEN: ${{ secrets.REDBOT_GITHUB_TOKEN }} 41 | ISSUE: ${{ github.event.issue.html_url }} 42 | OWNER: ${{ github.repository_owner }} 43 | REPO: ${{ github.event.repository.name }} -------------------------------------------------------------------------------- /.github/workflows/auto-gh-pr.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Auto PR to release 16 | 17 | on: 18 | pull_request: 19 | # types: 20 | # - closed 21 | issue_comment: 22 | types: [created] 23 | pull_request_review_comment: 24 | types: [created] 25 | 26 | jobs: 27 | sync-issue-to-pr: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v4 32 | 33 | - name: Sync Issue to PR 34 | if: github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main' 35 | run: | 36 | PR_BODY="${{ github.event.pull_request.body }}" 37 | 38 | ISSUE_NUMBER=$(echo "$PR_BODY" | grep -oP 'Fixes #\K\d+') 39 | if [[ -z "$ISSUE_NUMBER" ]]; then 40 | echo "No Issue number found." 41 | exit 1 42 | fi 43 | 44 | echo "Issue number found: $ISSUE_NUMBER" 45 | 46 | # Using GitHub CLI to get issue details 47 | gh issue view "$ISSUE_NUMBER" --repo "${{ github.repository }}" --json labels,assignees,milestone,title > issue_data.json 48 | 49 | # Check if jq is installed 50 | if ! command -v jq &> /dev/null; then 51 | echo "Installing jq..." 52 | sudo apt-get install -y jq 53 | fi 54 | 55 | # Parse data with jq 56 | LABELS=$(jq -r '.labels | map(.name) | join(",")' issue_data.json) 57 | ASSIGNEES=$(jq -r '.assignees | map(.login) | join(",")' issue_data.json) 58 | MILESTONE=$(jq -r '.milestone.title' issue_data.json) 59 | 60 | # Check if any of the fields are empty and set them to None 61 | LABELS=${LABELS:-None} 62 | ASSIGNEES=${ASSIGNEES:-None} 63 | MILESTONE=${MILESTONE:-None} 64 | 65 | # Edit the PR with issue details, handling empty fields 66 | gh pr edit "${{ github.event.pull_request.number }}" --repo "${{ github.repository }}" \ 67 | ${LABELS:+--add-label "$LABELS"} \ 68 | ${ASSIGNEES:+--add-assignee "$ASSIGNEES"} \ 69 | ${MILESTONE:+--milestone "$MILESTONE"} 70 | continue-on-error: true 71 | env: 72 | GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 73 | -------------------------------------------------------------------------------- /.github/workflows/auto-invite.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Invite users to join our group 16 | on: 17 | issue_comment: 18 | types: 19 | - created 20 | jobs: 21 | issue_comment: 22 | name: Invite users to join our group 23 | if: ${{ github.event.comment.body == '/invite' || github.event.comment.body == '/close' || github.event.comment.body == '/comment' }} 24 | runs-on: ubuntu-latest 25 | permissions: 26 | issues: write 27 | steps: 28 | 29 | - name: Invite user to join our group 30 | uses: peter-evans/create-or-update-comment@v4 31 | with: 32 | token: ${{ secrets.BOT_GITHUB_TOKEN }} 33 | issue-number: ${{ github.event.issue.number }} 34 | body: | 35 | We value close connections with our users, developers, and contributors here at Open-IM-Server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us. 36 | 37 | Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of Open-IM-Server. You can ask technical questions, seek help, or share your experiences with other users of Open-IM-Server. 38 | 39 | In addition to Slack, we also offer the following ways to get in touch: 40 | 41 | + We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 Open-IM-Server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) team channel. 42 | + Get in touch with us on [Gmail](https://mail.google.com/mail/u/0/?fs=1&tf=cm&to=winxu81@gmail.com). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email. 43 | + Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with Open-IM-Server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information. 44 | + Add [Wechat](https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of Open-IM-Server. We will process your request as soon as possible. 45 | 46 | - name: Close Issue 47 | uses: peter-evans/close-issue@v3 48 | with: 49 | token: ${{ secrets.BOT_GITHUB_TOKEN }} 50 | issue-number: ${{ github.event.issue.number }} 51 | comment: 🤖 Auto-closing issue, if you still need help please reopen the issue or ask for help in the community above 52 | labels: | 53 | triage/accepted -------------------------------------------------------------------------------- /.github/workflows/auto-tag.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: OpenIM Create Tag 16 | 17 | on: 18 | issue_comment: 19 | types: [created] 20 | pull_request_review_comment: 21 | types: [created] 22 | 23 | jobs: 24 | create_tag: 25 | runs-on: ubuntu-latest 26 | if: startsWith(github.event.comment.body, '/create tag') 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v4 30 | 31 | - name: Validate version number and get comment 32 | id: validate 33 | run: | 34 | COMMENT="${{ github.event.comment.body }}" 35 | VERSION=$(echo $COMMENT | cut -d ' ' -f 3) 36 | TAG_COMMENT=$(echo $COMMENT | cut -d '"' -f 2) 37 | if [[ $VERSION =~ ^v([0-9]+\.){2}[0-9]+$ ]]; then 38 | echo "version=$VERSION" >> $GITHUB_STATE 39 | echo "tag_comment=$TAG_COMMENT" >> $GITHUB_STATE 40 | else 41 | echo "Invalid version number." 42 | exit 1 43 | fi 44 | 45 | - name: Create a new tag 46 | env: 47 | GH_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 48 | run: | 49 | source $GITHUB_STATE 50 | git tag -a $VERSION -m "$tag_comment" 51 | git push origin $VERSION 52 | echo "tag_created=$VERSION" >> $GITHUB_OUTPUT 53 | -------------------------------------------------------------------------------- /.github/workflows/bot-auto-cherry-pick.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Github Rebot for Cherry Pick when PR is merged 16 | on: 17 | pull_request_target: 18 | types: 19 | - closed 20 | 21 | jobs: 22 | comment: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Comment cherry-pick command 26 | uses: actions/github-script@v7 27 | with: 28 | github-token: ${{ secrets.BOT_GITHUB_TOKEN }} 29 | script: | 30 | const pr = context.payload.pull_request; 31 | if (!pr.merged) { 32 | console.log("PR is not merged. Skipping..."); 33 | return; 34 | } 35 | if (!pr.milestone || !pr.milestone.title) { 36 | console.log("Milestone is not set. Skipping..."); 37 | return; 38 | } 39 | const milestone = pr.milestone.title; 40 | const ref = `heads/release-${milestone}`; 41 | let branchExists; 42 | try { 43 | await github.rest.git.getRef({ 44 | owner: context.repo.owner, 45 | repo: context.repo.repo, 46 | ref: ref 47 | }); 48 | branchExists = true; 49 | } catch (error) { 50 | if (error.status === 404) { 51 | console.log(`Branch ${ref} does not exist. Skipping...`); 52 | branchExists = false; 53 | } else { 54 | throw error; // Rethrow if it's another error 55 | } 56 | } 57 | if (!branchExists) { 58 | return; 59 | } 60 | const cherryPickCmd = `/cherry-pick release-${milestone}`; 61 | console.log(`Adding comment: ${cherryPickCmd}`); 62 | await github.rest.issues.createComment({ 63 | owner: context.repo.owner, 64 | repo: context.repo.repo, 65 | issue_number: pr.number, 66 | body: cherryPickCmd 67 | }); -------------------------------------------------------------------------------- /.github/workflows/bot-cherry-pick.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Github Robot for Cherry Pick On Comment 16 | 17 | on: 18 | issue_comment: 19 | types: [created] 20 | 21 | jobs: 22 | cherry-pick: 23 | name: Cherry Pick 24 | if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/cherry-pick') 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - name: Checkout the latest code 29 | uses: actions/checkout@v4 30 | with: 31 | token: ${{ secrets.BOT_GITHUB_TOKEN }} 32 | fetch-depth: 0 # To ensure all history is available for cherry-picking 33 | 34 | - name: Automatic Cherry Pick 35 | uses: vendoo/gha-cherry-pick@v1 36 | with: 37 | # Assuming the cherry-pick commit SHA is passed in the comment like '/cherry-pick sha' 38 | commit-sha: ${{ github.event.comment.body }} 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 41 | 42 | - name: Create a new branch for PR 43 | run: | 44 | PR_BRANCH="cherry-pick-${GITHUB_SHA}-to-${{ github.base_ref }}" 45 | git checkout -b $PR_BRANCH 46 | git push origin $PR_BRANCH 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 49 | 50 | - name: Create Pull Request 51 | uses: actions/github-script@v5 52 | with: 53 | script: | 54 | const prTitle = "Cherry-pick to ${{ github.base_ref }}" 55 | const prBody = "Automated cherry-pick of ${{ github.event.comment.body }}\n\n/cc @kubbot" 56 | const base = "${{ github.base_ref }}" 57 | const head = "cherry-pick-${{ github.sha }}-to-${{ github.base_ref }}" 58 | const createPr = await github.rest.pulls.create({ 59 | owner: context.repo.owner, 60 | repo: context.repo.repo, 61 | title: prTitle, 62 | body: prBody, 63 | head: head, 64 | base: base, 65 | maintainer_can_modify: true, // Allows maintainers to edit the PR 66 | }) 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 69 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: Mage Build Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - "README.md" 9 | - "README_zh-CN.md" 10 | - "**.md" 11 | - "CONTRIBUTING.md" 12 | - ".github/**" 13 | pull_request: 14 | branches: 15 | - main 16 | paths-ignore: 17 | - "README.md" 18 | - "README_zh-CN.md" 19 | - "**.md" 20 | - "CONTRIBUTING.md" 21 | - ".github/**" 22 | workflow_dispatch: 23 | 24 | jobs: 25 | build: 26 | name: Execute Mage On Linux 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Set up Go 30 | uses: actions/setup-go@v4 31 | with: 32 | go-version: "1.20" 33 | 34 | - name: Check out code into the Go module directory 35 | uses: actions/checkout@v4 36 | 37 | - name: init 38 | run: sudo bash bootstrap.sh 39 | timeout-minutes: 20 40 | 41 | - name: Build, Start, Check Services and Print Logs for Linux 42 | run: | 43 | sudo mage 44 | sudo mage start 45 | sudo mage check 46 | 47 | - name: Restart Services and Print Logs 48 | run: | 49 | sudo mage stop 50 | sudo mage start 51 | sudo mage check 52 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: OpenIM CLA Assistant 16 | on: 17 | issue_comment: 18 | types: [created] 19 | pull_request_target: 20 | types: [opened,closed,synchronize] 21 | 22 | # explicitly configure permissions, in case your GITHUB_TOKEN workflow permissions are set to read-only in repository settings 23 | permissions: 24 | actions: write 25 | contents: write 26 | pull-requests: write 27 | statuses: write 28 | 29 | env: 30 | # Define Open-IM-Server variables here 31 | OPEN_IM_SERVER_REMOTE_ORGANIZATION: openim-sigs 32 | REMOTE_REPOSITORY: cla 33 | OPEN_IM_SERVER_CLA_DOCUMENT: https://github.com/openim-sigs/cla/blob/main/README.md 34 | OPEN_IM_SERVER_SIGNATURES_PATH: signatures/${{ github.event.repository.name }}/cla.json 35 | 36 | OPEN_IM_SERVER_ALLOWLIST: kubbot,bot*,bot-*,bot/*,bot-/*,bot,*[bot] 37 | 38 | jobs: 39 | CLAAssistant: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - name: "CLA Assistant" 43 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 44 | uses: contributor-assistant/github-action@v2.3.1 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 47 | PERSONAL_ACCESS_TOKEN: ${{ secrets.REDBOT_GITHUB_TOKEN }} 48 | with: 49 | path-to-signatures: ${{ env.OPEN_IM_SERVER_SIGNATURES_PATH }} 50 | path-to-document: ${{ env.OPEN_IM_SERVER_CLA_DOCUMENT }} 51 | branch: 'main' 52 | allowlist: ${{ env.OPEN_IM_SERVER_ALLOWLIST }} 53 | 54 | remote-organization-name: ${{ env.OPEN_IM_SERVER_REMOTE_ORGANIZATION }} 55 | remote-repository-name: ${{ env.REMOTE_REPOSITORY }} 56 | 57 | create-file-commit-message: '📚 Docs: Creating file for storing ${{ github.event.repository.name }} CLA Signatures' 58 | custom-notsigned-prcomment: '💕 Thank you for your contribution and please kindly read and sign our [🎯https://github.com/openim-sigs/cla/blob/main/README.md](https://github.com/openim-sigs/cla/blob/main/README.md).
If you wish to sign the CRA, **Please copy and comment on the following sentence:**' 59 | custom-pr-sign-comment: 'I have read the CLA Document and I hereby sign the CLA' 60 | custom-allsigned-prcomment: '🤖 All Contributors have signed the [${{ github.event.repository.name }} CLA](https://github.com/openim-sigs/cla/blob/main/README.md).
The signed information is recorded [🤖here](https://github.com/openim-sigs/cla/tree/main/signatures/${{ github.event.repository.name }}/cla.json)' 61 | # lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) 62 | # use-dco-flag: true - If you are using DCO instead of CLA 63 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | name: "OpenIM Code Scanning - Action" 17 | 18 | on: 19 | push: 20 | branches: [main] 21 | pull_request: 22 | branches: [main] 23 | schedule: 24 | # ┌───────────── minute (0 - 59) 25 | # │ ┌───────────── hour (0 - 23) 26 | # │ │ ┌───────────── day of the month (1 - 31) 27 | # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) 28 | # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) 29 | # │ │ │ │ │ 30 | # │ │ │ │ │ 31 | # │ │ │ │ │ 32 | # * * * * * 33 | - cron: '30 1 * * 0' 34 | 35 | jobs: 36 | CodeQL-Build: 37 | # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest 38 | runs-on: ubuntu-latest 39 | 40 | permissions: 41 | # required for all workflows 42 | security-events: write 43 | 44 | # only required for workflows in private repositories 45 | actions: write 46 | contents: write 47 | 48 | steps: 49 | - name: Checkout repository 50 | uses: actions/checkout@v4 51 | 52 | # Initializes the CodeQL tools for scanning. 53 | - name: Initialize CodeQL 54 | uses: github/codeql-action/init@v3 55 | # Override language selection by uncommenting this and choosing your languages 56 | with: 57 | languages: go 58 | 59 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 60 | # If this step fails, then you should remove it and run the build manually (see below). 61 | - name: Autobuild 62 | uses: github/codeql-action/autobuild@v3 63 | 64 | # ℹ️ Command-line programs to run using the OS shell. 65 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 66 | 67 | # ✏️ If the Autobuild fails above, remove it and uncomment the following 68 | # three lines and modify them (or add more) to build your code if your 69 | # project uses a compiled language 70 | 71 | # - run: | 72 | # make bootstrap 73 | # make release 74 | 75 | - name: Perform CodeQL Analysis 76 | uses: github/codeql-action/analyze@v3 -------------------------------------------------------------------------------- /.github/workflows/depsreview.yaml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 KubeCub open source community. All rights reserved. 2 | # Licensed under the MIT License (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | 5 | name: OpenIM Dependency Review 6 | on: [pull_request] 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | dependency-review: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: 'Checkout Repository' 16 | uses: actions/checkout@v4 17 | - name: 'Dependency Review' 18 | uses: actions/dependency-review-action@v3 -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | name: OpenIM golangci-lint 17 | on: 18 | push: 19 | branches: [main] 20 | pull_request: 21 | jobs: 22 | golangci: 23 | name: lint 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: actions/setup-go@v4 28 | with: 29 | go-version: '1.21' 30 | cache: false 31 | - name: golangci-lint 32 | uses: golangci/golangci-lint-action@v3.7.0 33 | with: 34 | # Require: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version 35 | version: v1.54 36 | 37 | # Optional: working directory, useful for monorepos 38 | # working-directory: server 39 | 40 | # Optional: golangci-lint command line arguments. 41 | # 42 | # Note: by default the `.golangci.yml` file should be at the root of the repository. 43 | # The location of the configuration file can be changed by using `--config=` 44 | # args: --timeout=30m --config=/scripts/.golangci.yml --issues-exit-code=0 45 | 46 | # Optional: show only new issues if it's a pull request. The default value is `false`. 47 | only-new-issues: true 48 | 49 | # Optional:The mode to install golangci-lint. It can be 'binary' or 'goinstall'. 50 | # install-mode: "goinstall" -------------------------------------------------------------------------------- /.github/workflows/gosec.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: OpenIM Run Gosec 16 | 17 | # gosec is a source code security audit tool for the Go language. It performs a static 18 | # analysis of the Go code, looking for potential security problems. The main functions of gosec are: 19 | # 1. Find common security vulnerabilities, such as SQL injection, command injection, and cross-site scripting (XSS). 20 | # 2. Audit codes according to common security standards and find non-standard codes. 21 | # 3. Assist the Go language engineer to write safe and reliable code. 22 | # https://github.com/securego/gosec/ 23 | on: 24 | push: 25 | branches: "*" 26 | pull_request: 27 | branches: "*" 28 | paths-ignore: 29 | - '*.md' 30 | - '*.yml' 31 | - '.github' 32 | 33 | jobs: 34 | golang-security-action: 35 | runs-on: ubuntu-latest 36 | env: 37 | GO111MODULE: on 38 | steps: 39 | - name: Check out code 40 | uses: actions/checkout@v4 41 | - name: Run Gosec Security Scanner 42 | uses: securego/gosec@master 43 | with: 44 | args: ./... 45 | continue-on-error: true -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: OpenIM First Interaction 16 | 17 | on: 18 | issues: 19 | types: [opened] 20 | pull_request: 21 | branches: [main] 22 | types: [opened] 23 | 24 | jobs: 25 | check_for_first_interaction: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: actions/first-interaction@v1.3.0 30 | with: 31 | repo-token: ${{ secrets.BOT_GITHUB_TOKEN }} 32 | pr-message: | 33 | Hello! Thank you for your contribution. 34 | 35 | If you are fixing a bug, please reference the issue number in the description. 36 | 37 | If you are implementing a feature request, please check with the maintainers that the feature will be accepted first. 38 | 39 | [Join slack 🤖](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) to connect and communicate with our developers. 40 | 41 | Please leave your information in the [✨ discussions](https://github.com/orgs/OpenIMSDK/discussions/426), we expect anyone to join OpenIM developer community. 42 | 43 | issue-message: | 44 | Hello! Thank you for filing an issue. 45 | 46 | If this is a bug report, please include relevant logs to help us debug the problem. 47 | 48 | [Join slack 🤖](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) to connect and communicate with our developers. 49 | continue-on-error: true -------------------------------------------------------------------------------- /.github/workflows/help-comment-issue.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Good frist issue add comment 16 | on: 17 | issues: 18 | types: 19 | - labeled 20 | 21 | jobs: 22 | add-comment: 23 | if: github.event.label.name == 'help wanted' || github.event.label.name == 'good first issue' 24 | runs-on: ubuntu-latest 25 | permissions: 26 | issues: write 27 | steps: 28 | - name: Add comment 29 | uses: peter-evans/create-or-update-comment@v4 30 | with: 31 | issue-number: ${{ github.event.issue.number }} 32 | token: ${{ secrets.BOT_GITHUB_TOKEN }} 33 | body: | 34 | This issue is available for anyone to work on. **Make sure to reference this issue in your pull request.** :sparkles: Thank you for your contribution! :sparkles: 35 | [Join slack 🤖](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) to connect and communicate with our developers. 36 | If you wish to accept this assignment, please leave a comment in the comments section: `/accept`.🎯 37 | -------------------------------------------------------------------------------- /.github/workflows/issue-robot.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: OpenIM Issue Aotu Translator 16 | on: 17 | issue_comment: 18 | types: [created] 19 | issues: 20 | types: [opened] 21 | 22 | jobs: 23 | build: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: usthe/issues-translate-action@v2.7 27 | with: 28 | # it is not necessary to decide whether you need to modify the issue header content 29 | IS_MODIFY_TITLE: true 30 | BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 31 | # Required, input your bot github token -------------------------------------------------------------------------------- /.github/workflows/link-pr.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Github Rebot for Link check error 16 | 17 | # Every Monday at 12:30 p.m 18 | on: 19 | schedule: 20 | - cron: '30 12 * * 1' 21 | 22 | jobs: 23 | linkChecker: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - name: Link Checker 29 | id: lychee 30 | uses: lycheeverse/lychee-action@v1.9.3 31 | with: 32 | # For parameter description, see https://github.com/lycheeverse/lychee#commandline-parameters 33 | # Actions Link address -> https://github.com/lycheeverse/lychee-action 34 | # -E, --exclude-all-private Exclude all private IPs from checking. 35 | # -i, --insecure Proceed for server connections considered insecure (invalid TLS) 36 | # -n, --no-progress Do not show progress bar. 37 | # -t, --timeout Website timeout in seconds from connect to response finished [default:20] 38 | # --max-concurrency Maximum number of concurrent network requests [default: 128] 39 | # -a --accept Comma-separated list of accepted status codes for valid links 40 | # docs/.vitepress/dist the site directory to check 41 | # ./*.md all markdown files in the root directory 42 | args: --verbose -E -i --no-progress --exclude-path './CHANGELOG' './**/*.md' 43 | env: 44 | GITHUB_TOKEN: ${{secrets.BOT_GITHUB_TOKEN}} 45 | 46 | - name: Create Issue From File 47 | if: env.lychee_exit_code != 0 48 | uses: peter-evans/create-issue-from-file@v5 49 | with: 50 | title: Bug reports for links in OpenIM docs 51 | content-filepath: ./lychee/out.md 52 | labels: kind/documentation, triage/unresolved, report 53 | token: ${{ secrets.BOT_GITHUB_TOKEN }} 54 | -------------------------------------------------------------------------------- /.github/workflows/milestone.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # shamelessly copied from https://github.com/sigstore/cosign/blob/main/.github/workflows/milestone.yaml 16 | 17 | name: milestone 18 | 19 | on: 20 | pull_request_target: 21 | types: [closed] 22 | branches: 23 | - main 24 | 25 | jobs: 26 | milestone: 27 | runs-on: ubuntu-latest 28 | 29 | permissions: 30 | actions: none 31 | checks: none 32 | contents: read 33 | deployments: none 34 | issues: write 35 | packages: none 36 | pull-requests: write 37 | repository-projects: none 38 | security-events: none 39 | statuses: none 40 | 41 | steps: 42 | - uses: actions/github-script@v7 # v6 43 | with: 44 | github-token: ${{ secrets.BOT_GITHUB_TOKEN }} 45 | script: | 46 | if (!context.payload.pull_request.merged) { 47 | console.log('PR was not merged, skipping.'); 48 | return; 49 | } 50 | 51 | if (!!context.payload.pull_request.milestone) { 52 | console.log('PR has existing milestone, skipping.'); 53 | return; 54 | } 55 | 56 | milestones = await github.rest.issues.listMilestones({ 57 | owner: context.repo.owner, 58 | repo: context.repo.repo, 59 | state: 'open', 60 | sort: 'title', 61 | direction: 'desc' 62 | }) 63 | 64 | if (milestones.data.length === 0) { 65 | console.log('There are no milestones, skipping.'); 66 | return; 67 | } 68 | 69 | await github.rest.issues.update({ 70 | owner: context.repo.owner, 71 | repo: context.repo.repo, 72 | issue_number: context.payload.pull_request.number, 73 | milestone: milestones.data[0].number 74 | }); 75 | -------------------------------------------------------------------------------- /.github/workflows/opencommit.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: OpenIM OpenCommit Action 16 | 17 | on: 18 | push: 19 | # this list of branches is often enough, 20 | # but you may still ignore other public branches 21 | branches-ignore: [main master dev development release] 22 | 23 | jobs: 24 | opencommit: 25 | timeout-minutes: 10 26 | name: OpenCommit 27 | runs-on: ubuntu-latest 28 | permissions: write-all 29 | steps: 30 | - name: Setup Node.js Environment 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: '16' 34 | - uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | - uses: di-sukharev/opencommit@github-action-v1.0.4 38 | with: 39 | GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} 40 | 41 | env: 42 | # set openAI api key in repo actions secrets, 43 | # for openAI keys go to: https://platform.openai.com/account/api-keys 44 | # for repo secret go to: /settings/secrets/actions 45 | OCO_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 46 | 47 | # customization 48 | OCO_OPENAI_MAX_TOKENS: 500 49 | OCO_OPENAI_BASE_PATH: '' 50 | OCO_DESCRIPTION: false 51 | OCO_EMOJI: false 52 | OCO_MODEL: gpt-3.5-turbo-16k 53 | OCO_LANGUAGE: en 54 | OCO_PROMPT_MODULE: conventional-commit 55 | continue-on-error: true -------------------------------------------------------------------------------- /.github/workflows/project-progress.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # GitHub recommends pinning actions to a commit SHA. 16 | # To get a newer version, you will need to update the SHA. 17 | # You can also reference a tag or branch, but the action may change without warning. 18 | 19 | name: Move assigned card 20 | on: 21 | issues: 22 | types: 23 | - assigned 24 | pull_request: 25 | types: 26 | - assigned 27 | branches-ignore: 28 | - 'asf-auto-updates' 29 | - 'ignore' 30 | 31 | jobs: 32 | move-assigned-card: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: alex-page/github-project-automation-plus@v0.9.0 36 | with: 37 | project: openim-powerful 38 | column: In Progress 39 | repo-token: ${{ secrets.BOT_GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Release Drafter 16 | 17 | on: 18 | push: 19 | # branches to consider in the event; optional, defaults to all 20 | branches: 21 | - main 22 | # pull_request event is required only for autolabeler 23 | pull_request: 24 | # Only following types are handled by the action, but one can default to all as well 25 | # types: [opened, reopened, synchronize] 26 | # pull_request_target event is required for autolabeler to support PRs from forks 27 | # pull_request_target: 28 | # types: [opened, reopened, synchronize] 29 | 30 | permissions: 31 | contents: read 32 | 33 | jobs: 34 | update_release_draft: 35 | permissions: 36 | # write permission is required to create a github release 37 | contents: write 38 | # write permission is required for autolabeler 39 | # otherwise, read permission is required at least 40 | pull-requests: write 41 | runs-on: ubuntu-latest 42 | steps: 43 | # (Optional) GitHub Enterprise requires GHE_HOST variable set 44 | #- name: Set GHE_HOST 45 | # run: | 46 | # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV 47 | 48 | # Drafts your next Release notes as Pull Requests are merged into "master" 49 | - uses: release-drafter/release-drafter@v5 50 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 51 | # with: 52 | # config-name: my-config.yml 53 | # disable-autolabeler: true 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.REDBOT_GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 16 | # 17 | # You can adjust the behavior by modifying this file. 18 | # For more information, see: 19 | # https://github.com/actions/stale 20 | name: Mark stale issues and pull requests 21 | 22 | on: 23 | schedule: 24 | - cron: '0 8 * * 1' 25 | 26 | jobs: 27 | stale: 28 | 29 | runs-on: ubuntu-latest 30 | permissions: 31 | issues: write 32 | pull-requests: write 33 | 34 | steps: 35 | - uses: actions/stale@v9 36 | with: 37 | repo-token: ${{ secrets.BOT_GITHUB_TOKEN }} 38 | days-before-stale: 60 39 | days-before-close: 305 40 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.' 41 | stale-pr-message: 'This issue is stale because it has been open 60 days with no activity.' 42 | close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' 43 | close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity. You can reopen it if you want.' 44 | stale-pr-label: lifecycle/stale 45 | stale-issue-label: lifecycle/stale 46 | exempt-issue-labels: 'openim' 47 | exempt-pr-labels: 'openim' 48 | exempt-draft-pr: true 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIMSDK. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # ============================================================================== 16 | # For the entire design of.gitignore, ignore git commits and ignore files 17 | #=============================================================================== 18 | # 19 | 20 | ### OpenIM developer supplement ### 21 | logs 22 | ./db 23 | .devcontainer 24 | components 25 | out-test 26 | Dockerfile.cross 27 | ### Makefile ### 28 | tmp/ 29 | bin/ 30 | output/ 31 | _output/ 32 | deployments/charts/generated-configs/ 33 | 34 | ### OpenIM Config ### 35 | config/config.yaml 36 | ./config/config.yaml 37 | .env 38 | ./.env 39 | 40 | ### OpenIM deploy ### 41 | deployments/openim-server/charts 42 | 43 | # files used by the developer 44 | .idea.md 45 | .todo.md 46 | .note.md 47 | 48 | # ============================================================================== 49 | # Created by https://www.toptal.com/developers/gitignore/api/go,git,vim,tags,test,emacs,backup,jetbrains 50 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,git,vim,tags,test,emacs,backup,jetbrains 51 | 52 | ### Backup ### 53 | *.bak 54 | *.gho 55 | *.ori 56 | *.orig 57 | *.tmp 58 | 59 | ### Emacs ### 60 | # -*- mode: gitignore; -*- 61 | *~ 62 | \#*\# 63 | /.emacs.desktop 64 | /.emacs.desktop.lock 65 | *.elc 66 | auto-save-list 67 | tramp 68 | .\#* 69 | 70 | # Org-mode 71 | .org-id-locations 72 | *_archive 73 | 74 | # flymake-mode 75 | *_flymake.* 76 | 77 | # eshell files 78 | /eshell/history 79 | /eshell/lastdir 80 | 81 | # elpa packages 82 | /elpa/ 83 | 84 | # reftex files 85 | *.rel 86 | 87 | # AUCTeX auto folder 88 | /auto/ 89 | 90 | # cask packages 91 | .cask/ 92 | dist/ 93 | 94 | # Flycheck 95 | flycheck_*.el 96 | 97 | # server auth directory 98 | /server/ 99 | 100 | # projectiles files 101 | .projectile 102 | 103 | # directory configuration 104 | .dir-locals.el 105 | 106 | # network security 107 | /network-security.data 108 | 109 | ### vscode ### 110 | .vscode 111 | .vscode/* 112 | !.vscode/settings.json 113 | !.vscode/tasks.json 114 | !.vscode/launch.json 115 | !.vscode/extensions.json 116 | *.code-workspace 117 | 118 | # End of https://www.toptal.com/developers/gitignore/api/vim,jetbrains,vscode,git,go,tags,backup,test 119 | 120 | ### Git ### 121 | # Created by git for backups. To disable backups in Git: 122 | # $ git config --global mergetool.keepBackup false 123 | 124 | # Created by git when using merge tools for conflicts 125 | *.BACKUP.* 126 | *.BASE.* 127 | *.LOCAL.* 128 | *.REMOTE.* 129 | *_BACKUP_*.txt 130 | *_BASE_*.txt 131 | *_LOCAL_*.txt 132 | *_REMOTE_*.txt 133 | 134 | ### Go ### 135 | # If you prefer the allow list template instead of the deny list, see community template: 136 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 137 | # 138 | # Binaries for programs and plugins 139 | *.exe 140 | *.exe~ 141 | *.dll 142 | *.so 143 | *.dylib 144 | 145 | # Test binary, built with `go test -c` 146 | *.test 147 | 148 | # Output of the go coverage tool, specifically when used with LiteIDE 149 | *.out 150 | 151 | # Dependency directories (remove the comment below to include it) 152 | vendor/ 153 | 154 | # Go workspace file 155 | # go.work 156 | go.work.sum 157 | 158 | ### JetBrains ### 159 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 160 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 161 | 162 | # User-specific stuff 163 | .idea/ 164 | .idea/**/workspace.xml 165 | .idea/**/tasks.xml 166 | .idea/**/usage.statistics.xml 167 | .idea/**/dictionaries 168 | .idea/**/shelf 169 | 170 | # AWS User-specific 171 | .idea/**/aws.xml 172 | 173 | # Generated files 174 | .idea/**/contentModel.xml 175 | 176 | # Sensitive or high-churn files 177 | .idea/**/dataSources/ 178 | .idea/**/dataSources.ids 179 | .idea/**/dataSources.local.xml 180 | .idea/**/sqlDataSources.xml 181 | .idea/**/dynamic.xml 182 | .idea/**/uiDesigner.xml 183 | .idea/**/dbnavigator.xml 184 | 185 | # Gradle 186 | .idea/**/gradle.xml 187 | .idea/**/libraries 188 | 189 | # Gradle and Maven with auto-import 190 | # When using Gradle or Maven with auto-import, you should exclude module files, 191 | # since they will be recreated, and may cause churn. Uncomment if using 192 | # auto-import. 193 | # .idea/artifacts 194 | # .idea/compiler.xml 195 | # .idea/jarRepositories.xml 196 | # .idea/modules.xml 197 | # .idea/*.iml 198 | # .idea/modules 199 | # *.iml 200 | # *.ipr 201 | 202 | # CMake 203 | cmake-build-*/ 204 | 205 | # Mongo Explorer plugin 206 | .idea/**/mongoSettings.xml 207 | 208 | # File-based project format 209 | *.iws 210 | 211 | # IntelliJ 212 | out/ 213 | 214 | # mpeltonen/sbt-idea plugin 215 | .idea_modules/ 216 | 217 | # JIRA plugin 218 | atlassian-ide-plugin.xml 219 | 220 | # Cursive Clojure plugin 221 | .idea/replstate.xml 222 | 223 | # SonarLint plugin 224 | .idea/sonarlint/ 225 | 226 | # Crashlytics plugin (for Android Studio and IntelliJ) 227 | com_crashlytics_export_strings.xml 228 | crashlytics.properties 229 | crashlytics-build.properties 230 | fabric.properties 231 | 232 | # Editor-based Rest Client 233 | .idea/httpRequests 234 | 235 | # Android studio 3.1+ serialized cache file 236 | .idea/caches/build_file_checksums.ser 237 | 238 | ### JetBrains Patch ### 239 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 240 | 241 | # *.iml 242 | # modules.xml 243 | # .idea/misc.xml 244 | # *.ipr 245 | 246 | # Sonarlint plugin 247 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 248 | .idea/**/sonarlint/ 249 | 250 | # SonarQube Plugin 251 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 252 | .idea/**/sonarIssues.xml 253 | 254 | # Markdown Navigator plugin 255 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 256 | .idea/**/markdown-navigator.xml 257 | .idea/**/markdown-navigator-enh.xml 258 | .idea/**/markdown-navigator/ 259 | 260 | # Cache file creation bug 261 | # See https://youtrack.jetbrains.com/issue/JBR-2257 262 | .idea/$CACHE_FILE$ 263 | 264 | # CodeStream plugin 265 | # https://plugins.jetbrains.com/plugin/12206-codestream 266 | .idea/codestream.xml 267 | 268 | # Azure Toolkit for IntelliJ plugin 269 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 270 | .idea/**/azureSettings.xml 271 | 272 | ### Tags ### 273 | # Ignore tags created by etags, ctags, gtags (GNU global) and cscope 274 | TAGS 275 | .TAGS 276 | !TAGS/ 277 | tags 278 | .tags 279 | !tags/ 280 | gtags.files 281 | GTAGS 282 | GRTAGS 283 | GPATH 284 | GSYMS 285 | cscope.files 286 | cscope.out 287 | cscope.in.out 288 | cscope.po.out 289 | 290 | 291 | ### Test ### 292 | ### Ignore all files that could be used to test your code and 293 | ### you wouldn't want to push 294 | 295 | # Reference https://en.wikipedia.org/wiki/Metasyntactic_variable 296 | 297 | # Most common 298 | *foo 299 | *bar 300 | *fubar 301 | *foobar 302 | *baz 303 | 304 | # Less common 305 | *qux 306 | *quux 307 | *bongo 308 | *bazola 309 | *ztesch 310 | 311 | # UK, Australia 312 | *wibble 313 | *wobble 314 | *wubble 315 | *flob 316 | *blep 317 | *blah 318 | *boop 319 | *beep 320 | 321 | # Japanese 322 | *hoge 323 | *piyo 324 | *fuga 325 | *hogera 326 | *hogehoge 327 | 328 | # Portugal, Spain 329 | *fulano 330 | *sicrano 331 | *beltrano 332 | *mengano 333 | *perengano 334 | *zutano 335 | 336 | # France, Italy, the Netherlands 337 | *toto 338 | *titi 339 | *tata 340 | *tutu 341 | *pipppo 342 | *pluto 343 | *paperino 344 | *aap 345 | *noot 346 | *mies 347 | 348 | # Other names that would make sense 349 | *tests 350 | *testsdir 351 | *testsfile 352 | *testsfiles 353 | *testdir 354 | *testfile 355 | *testfiles 356 | *testing 357 | *testingdir 358 | *testingfile 359 | *testingfiles 360 | *temp 361 | *tempdir 362 | *tempfile 363 | *tempfiles 364 | *tmp 365 | *tmpdir 366 | *tmpfile 367 | *tmpfiles 368 | *lol 369 | 370 | ### Vim ### 371 | # Swap 372 | [._]*.s[a-v][a-z] 373 | !*.svg # comment out if you don't need vector files 374 | [._]*.sw[a-p] 375 | [._]s[a-rt-v][a-z] 376 | [._]ss[a-gi-z] 377 | [._]sw[a-p] 378 | 379 | # Session 380 | Session.vim 381 | Sessionx.vim 382 | 383 | # Temporary 384 | .netrwhist 385 | # Auto-generated tag files 386 | # Persistent undo 387 | [._]*.un~ 388 | 389 | # End of https://www.toptal.com/developers/gitignore/api/go,git,vim,tags,test,emacs,backup,jetbrains 390 | .idea 391 | dist/ 392 | .env 393 | config/config.yaml 394 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 OpenIM open source community. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This is an example .goreleaser.yml file with some sensible defaults. 16 | # Make sure to check the documentation at https://goreleaser.com 17 | 18 | before: 19 | hooks: 20 | # You may remove this if you don't use go modules. 21 | - go mod tidy 22 | # you may remove this if you don't need go generate 23 | - go generate ./... 24 | 25 | report_sizes: true 26 | 27 | builds: 28 | - binary: oimws 29 | id: oimws 30 | main: ./example/main.go 31 | goos: 32 | - linux 33 | goarch: 34 | - amd64 35 | - arm64 36 | goarm: 37 | - "6" 38 | - "7" 39 | 40 | archives: 41 | - format: tar.gz 42 | # this name template makes the OS and Arch compatible with the results of uname. 43 | name_template: >- 44 | {{ .ProjectName }}_ 45 | {{- title .Os }}_ 46 | {{- if eq .Arch "amd64" }}x86_64 47 | {{- else if eq .Arch "386" }}i386 48 | {{- else }}{{ .Arch }}{{ end }} 49 | {{- if .Arm }}v{{ .Arm }}{{ end }} 50 | # use zip for windows archives 51 | files: 52 | - LICENSE 53 | - README.md 54 | - docs/* 55 | # a more complete example, check the globbing deep dive below 56 | - src: "*.md" 57 | dst: docs 58 | 59 | # Strip parent folders when adding files to the archive. 60 | strip_parent: true 61 | 62 | # File info. 63 | # Not all fields are supported by all formats available formats. 64 | # 65 | # Default: copied from the source file 66 | info: 67 | # Templates: allowed (since v1.14) 68 | owner: root 69 | 70 | # Templates: allowed (since v1.14) 71 | group: root 72 | 73 | # Must be in time.RFC3339Nano format. 74 | # 75 | # Templates: allowed (since v1.14) 76 | mtime: "{{ .CommitDate }}" 77 | 78 | # File mode. 79 | mode: 0644 80 | 81 | format_overrides: 82 | - goos: windows 83 | format: zip 84 | 85 | changelog: 86 | sort: asc 87 | use: github 88 | filters: 89 | exclude: 90 | - "^test:" 91 | - "^chore" 92 | - "merge conflict" 93 | - Merge pull request 94 | - Merge remote-tracking branch 95 | - Merge branch 96 | - go mod tidy 97 | groups: 98 | - title: Dependency updates 99 | regexp: '^.*?(feat|fix)\(deps\)!?:.+$' 100 | order: 300 101 | - title: "New Features" 102 | regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' 103 | order: 100 104 | - title: "Security updates" 105 | regexp: '^.*?sec(\([[:word:]]+\))??!?:.+$' 106 | order: 150 107 | - title: "Bug fixes" 108 | regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' 109 | order: 200 110 | - title: "Documentation updates" 111 | regexp: ^.*?doc(\([[:word:]]+\))??!?:.+$ 112 | order: 400 113 | - title: "Build process updates" 114 | regexp: ^.*?build(\([[:word:]]+\))??!?:.+$ 115 | order: 400 116 | - title: Other work 117 | order: 9999 118 | 119 | nfpms: 120 | - id: packages 121 | builds: 122 | - admin-api 123 | - chat-api 124 | - chat-rpc 125 | - admin-rpc 126 | # Your app's vendor. 127 | vendor: openim 128 | homepage: https://github.com/openim-sigs/oimws 129 | maintainer: kubbot 130 | description: |- 131 | Auto sync github labels 132 | kubbot && openimbot 133 | license: MIT 134 | formats: 135 | - apk 136 | - deb 137 | - rpm 138 | - termux.deb # Since: v1.11 139 | - archlinux # Since: v1.13 140 | dependencies: 141 | - git 142 | recommends: 143 | - golang 144 | 145 | 146 | # The lines beneath this are called `modelines`. See `:help modeline` 147 | # Feel free to remove those if you don't want/use them. 148 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json 149 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj 150 | 151 | # Default: './dist' 152 | dist: ./_output/dist 153 | 154 | publishers: 155 | - name: "fury.io" 156 | ids: 157 | - packages 158 | dir: "{{ dir .ArtifactPath }}" 159 | cmd: | 160 | bash -c ' 161 | if [[ "{{ .Tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 162 | curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/openim/ 163 | else 164 | echo "Skipping deployment: Non-production release detected" 165 | fi' 166 | 167 | # snapcrafts: 168 | # - name_template: "{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 169 | # summary: openim is open source instant messaging 170 | # description: | 171 | # OpenIM yyds 172 | # grade: stable 173 | # confinement: classic 174 | # publish: true 175 | 176 | checksum: 177 | name_template: "{{ .ProjectName }}_checksums.txt" 178 | algorithm: sha256 179 | 180 | snapshot: 181 | name_template: "{{ .Tag }}-next" 182 | 183 | release: 184 | prerelease: auto 185 | footer: | 186 | 187 | ## Welcome to the {{ .Tag }} release of [chat](https://github.com/openim-sigs/oimws)!🎉🎉! 188 | 189 | **Full Changelog**: https://github.com/openim-sigs/oimws/compare/{{ .PreviousTag }}...{{ .Tag }} 190 | 191 | ## Helping out 192 | 193 | + We release logs are recorded on [✨ CHANGELOG](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/CHANGELOG/CHANGELOG.md) 194 | 195 | + For information on versions of OpenIM and how to maintain branches, read [📚this article](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/version.md) 196 | 197 | + If you wish to use mirroring, read OpenIM's [image management policy](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/images.md) 198 | 199 | **Want to be one of them 😘?** 200 | 201 |

202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 |

212 | 213 | > **Note** 214 | > @openimbot and @kubbot have made great contributions to the community as community 🤖robots(@openimsdk/bot), respectively. 215 | > Thanks to the @openimsdk/openim team for all their hard work on this release. 216 | > Thank you to all the [💕developers and contributors](https://github.com/openim-sigs/oimws/graphs/contributors), people from all over the world, OpenIM brings us together 217 | > Contributions to this project are welcome! Please see [CONTRIBUTING.md](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/CONTRIBUTING.md) for details. 218 | 219 | 220 | ## Get Involved with OpenIM! 221 | 222 | **Here are some ways to get involved with the OpenIM community:** 223 | 224 | 📢 **Slack Channel**: Join our Slack channels for discussions, communication, and support. Click [here](https://openimsdk.slack.com) to join the chat Slack team channel. 225 | 226 | 📧 **Gmail Contact**: If you have any questions, suggestions, or feedback for our open-source projects, please feel free to [contact us via email](https://mail.google.com/mail/?view=cm&fs=1&tf=1&to=info@openim.io). 227 | 228 | 📖 **Blog**: Stay up-to-date with OpenIM-Server projects and trends by reading our [blog](https://openim.io/). We share the latest developments, tech trends, and other interesting information related to OpenIM. 229 | 230 | 📱 **WeChat**: Add us on WeChat (QR Code) and indicate that you are a user or developer of chat. We'll process your request as soon as possible. 231 | 232 | Remember, your contributions play a vital role in making OpenIM successful, and we look forward to your active participation in our community! 🙌 233 | 234 | # webhook 235 | # announce: 236 | # slack: 237 | # enabled: false 238 | # message_template: "slack {{ .Tag }} is out! Check it out: https://github.com/OpenIMSDK/Open-IM-Server/releases/tag/{{ .Tag }}" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 openim open source community(public welfare community) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 OpenIM. All rights reserved. 2 | # Use of this source code is governed by a MIT style 3 | # license that can be found in the LICENSE file. 4 | 5 | ###################################=> common commands <=############################################# 6 | # ========================== Capture Environment =============================== 7 | # get the repo root and output path 8 | ROOT_PACKAGE=github.com/openim-sigs/oimws 9 | OUT_DIR=$(REPO_ROOT)/_output 10 | # ============================================================================== 11 | 12 | # define the default goal 13 | # 14 | 15 | SHELL := /bin/bash 16 | DIRS=$(shell ls) 17 | GO=go 18 | 19 | .DEFAULT_GOAL := help 20 | 21 | # include the common makefile 22 | COMMON_SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 23 | # ROOT_DIR: root directory of the code base 24 | ifeq ($(origin ROOT_DIR),undefined) 25 | ROOT_DIR := $(abspath $(shell cd $(COMMON_SELF_DIR)/. && pwd -P)) 26 | endif 27 | # OUTPUT_DIR: The directory where the build output is stored. 28 | ifeq ($(origin OUTPUT_DIR),undefined) 29 | OUTPUT_DIR := $(ROOT_DIR)/_output 30 | $(shell mkdir -p $(OUTPUT_DIR)) 31 | endif 32 | 33 | # BIN_DIR: The directory where the build output is stored. 34 | ifeq ($(origin BIN_DIR),undefined) 35 | BIN_DIR := $(OUTPUT_DIR)/bin 36 | $(shell mkdir -p $(BIN_DIR)) 37 | endif 38 | 39 | ifeq ($(origin TOOLS_DIR),undefined) 40 | TOOLS_DIR := $(OUTPUT_DIR)/tools 41 | $(shell mkdir -p $(TOOLS_DIR)) 42 | endif 43 | 44 | ifeq ($(origin TMP_DIR),undefined) 45 | TMP_DIR := $(OUTPUT_DIR)/tmp 46 | $(shell mkdir -p $(TMP_DIR)) 47 | endif 48 | 49 | ifeq ($(origin VERSION), undefined) 50 | VERSION := $(shell git describe --tags --always --match="v*" --dirty | sed 's/-/./g') #v2.3.3.631.g00abdc9b.dirty 51 | endif 52 | 53 | # Check if the tree is dirty. default to dirty(maybe u should commit?) 54 | GIT_TREE_STATE:="dirty" 55 | ifeq (, $(shell git status --porcelain 2>/dev/null)) 56 | GIT_TREE_STATE="clean" 57 | endif 58 | GIT_COMMIT:=$(shell git rev-parse HEAD) 59 | 60 | IMG ?= openim_chat:latest 61 | 62 | BUILDFILE = "./cmd/main.go" 63 | BUILDAPP = "$(OUTPUT_DIR)/" 64 | 65 | # Define the directory you want to copyright 66 | CODE_DIRS := $(ROOT_DIR)/ #$(ROOT_DIR)/pkg $(ROOT_DIR)/core $(ROOT_DIR)/integrationtest $(ROOT_DIR)/lib $(ROOT_DIR)/mock $(ROOT_DIR)/db $(ROOT_DIR)/openapi 67 | FINDS := find $(CODE_DIRS) 68 | 69 | ifndef V 70 | MAKEFLAGS += --no-print-directory 71 | endif 72 | 73 | # The OS must be linux when building docker images 74 | # !WARNING: linux_mips64 linux_mips64le 75 | PLATFORMS ?= linux_s390x darwin_amd64 windows_amd64 linux_amd64 linux_arm64 linux_ppc64le 76 | 77 | # Set a specific PLATFORM 78 | ifeq ($(origin PLATFORM), undefined) 79 | ifeq ($(origin GOOS), undefined) 80 | GOOS := $(shell go env GOOS) 81 | endif 82 | ifeq ($(origin GOARCH), undefined) 83 | GOARCH := $(shell go env GOARCH) 84 | endif 85 | PLATFORM := $(GOOS)_$(GOARCH) 86 | # Use linux as the default OS when building images 87 | IMAGE_PLAT := linux_$(GOARCH) 88 | else 89 | GOOS := $(word 1, $(subst _, ,$(PLATFORM))) 90 | GOARCH := $(word 2, $(subst _, ,$(PLATFORM))) 91 | IMAGE_PLAT := $(PLATFORM) 92 | endif 93 | 94 | # Linux command settings 95 | FIND := find . ! -path './image/*' ! -path './vendor/*' ! -path './bin/*' 96 | XARGS := xargs -r 97 | 98 | # ============================================================================== 99 | # COMMA: Concatenate multiple strings to form a list of strings 100 | COMMA := , 101 | # SPACE: Used to separate strings 102 | SPACE := 103 | # SPACE: Replace multiple consecutive Spaces with a single space 104 | SPACE += 105 | 106 | PID = $(shell lsof -ti:10003) 107 | PORT = 10003 108 | 109 | # ============================================================================== 110 | # Build definition 111 | 112 | GO_SUPPORTED_VERSIONS ?= 1.19|1.20|1.21|1.22 113 | GO_LDFLAGS += -X $(VERSION_PACKAGE).GitVersion=$(VERSION) \ 114 | -X $(VERSION_PACKAGE).GitCommit=$(GIT_COMMIT) \ 115 | -X $(VERSION_PACKAGE).GitTreeState=$(GIT_TREE_STATE) \ 116 | -X $(VERSION_PACKAGE).BuildDate=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') 117 | ifneq ($(DLV),) 118 | GO_BUILD_FLAGS += -gcflags "all=-N -l" 119 | LDFLAGS = "" 120 | endif 121 | GO_BUILD_FLAGS += -ldflags "$(GO_LDFLAGS)" 122 | 123 | ifeq ($(GOOS),windows) 124 | GO_OUT_EXT := .exe 125 | endif 126 | 127 | ifeq ($(ROOT_PACKAGE),) 128 | $(error the variable ROOT_PACKAGE must be set prior to including golang.mk) 129 | endif 130 | 131 | GOPATH := $(shell go env GOPATH) 132 | ifeq ($(origin GOBIN), undefined) 133 | GOBIN := $(GOPATH)/bin 134 | endif 135 | 136 | EXCLUDE_TESTS=github.com/openim-sigs/oimws/test 137 | 138 | # ============================================================================== 139 | # Build 140 | 141 | ## all: Build all the necessary targets. 142 | .PHONY: all 143 | all: tidy style test build 144 | 145 | ## build: Build binaries by default. 146 | .PHONY: build 147 | build: 148 | @$(GO) build $(GO_BUILD_FLAGS) -o $(BIN_DIR)/oimws $(BUILDFILE) 149 | 150 | ## start: Start the service. 151 | .PHONY: start 152 | start: 153 | @mkdir -p db 154 | @mkdir -p logs 155 | @nohup $(BIN_DIR)/oimws >> logs/oimws.log 2>&1 & 156 | 157 | ## check: Check binaries is 158 | .PHONY: check 159 | check: 160 | @echo "Checking port $(PORT)..." 161 | @if [ -z "$(PID)" ]; then \ 162 | echo "Port $(PORT) is not in use."; \ 163 | else \ 164 | echo "Port $(PORT) is in use by PID $(PID)."; \ 165 | ps -p $(PID) -o start,etime,cmd; \ 166 | fi 167 | 168 | ## stop: Stop the service. 169 | .PHONY: stop 170 | stop: 171 | @echo "Stopping service with PID $(PID)..." 172 | @if [ -n "$(PID)" ]; then \ 173 | kill $(PID); \ 174 | else \ 175 | echo "No service to stop on port $(PORT)."; \ 176 | fi 177 | # ============================================================================== 178 | ## tidy: tidy go.mod 179 | .PHONY: tidy 180 | tidy: 181 | @$(GO) mod tidy 182 | 183 | ## style: Code style -> fmt,vet,lint 184 | .PHONY: style 185 | style: fmt vet lint 186 | 187 | ## fmt: Run go fmt against code. 188 | .PHONY: fmt 189 | fmt: 190 | @$(GO) fmt ./... 191 | 192 | ## vet: Run go vet against code. 193 | .PHONY: vet 194 | vet: 195 | @$(GO) vet ./... 196 | 197 | ## generate: Run go generate against code. 198 | .PHONY: generate 199 | generate: 200 | @$(GO) generate ./... 201 | 202 | ## lint: Run go lint against code. 203 | .PHONY: lint 204 | lint: tools.verify.golangci-lint 205 | @echo "===========> Run golangci to lint source codes" 206 | @$(TOOLS_DIR)/golangci-lint run -c $(ROOT_DIR)/.golangci.yml $(ROOT_DIR)/... 207 | 208 | ## test: Run unit test 209 | .PHONY: test 210 | test: 211 | @$(GO) test ./... 212 | 213 | ## cover: Run unit test with coverage. 214 | .PHONY: cover 215 | cover: test 216 | @$(GO) test -cover 217 | 218 | ## clean: Clean all builds. 219 | .PHONY: clean 220 | clean: 221 | @echo "===========> Cleaning all builds TMP_DIR($(TMP_DIR)) AND BIN_DIR($(BIN_DIR))" 222 | @-rm -vrf $(TMP_DIR) $(BIN_DIR) 223 | @echo "===========> End clean..." 224 | 225 | ## help: Show this help info. 226 | .PHONY: help 227 | help: Makefile 228 | @printf "\n\033[1mUsage: make ...\033[0m\n\n\\033[1mTargets:\\033[0m\n\n" 229 | @sed -n 's/^##//p' $< | awk -F':' '{printf "\033[36m%-28s\033[0m %s\n", $$1, $$2}' | sed -e 's/^/ /' 230 | 231 | ######################################=> common tools<= ############################################ 232 | # tools 233 | 234 | BUILD_TOOLS ?= go-gitlint golangci-lint goimports addlicense deepcopy-gen conversion-gen ginkgo go-junit-report 235 | 236 | ## tools.verify.%: Check if a tool is installed and install it 237 | .PHONY: tools.verify.% 238 | tools.verify.%: 239 | @echo "===========> Verifying $* is installed" 240 | @if [ ! -f $(TOOLS_DIR)/$* ]; then GOBIN=$(TOOLS_DIR) $(MAKE) tools.install.$*; fi 241 | @echo "===========> $* is install in $(TOOLS_DIR)/$*" 242 | 243 | # tools: Install a must tools 244 | .PHONY: tools 245 | tools: $(addprefix tools.verify., $(BUILD_TOOLS)) 246 | 247 | # tools.install.%: Install a single tool in $GOBIN/ 248 | .PHONY: tools.install.% 249 | tools.install.%: 250 | @echo "===========> Installing $,The default installation path is $(GOBIN)/$*" 251 | @$(MAKE) install.$* 252 | 253 | .PHONY: install.golangci-lint 254 | install.golangci-lint: 255 | @$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest 256 | 257 | .PHONY: install.goimports 258 | install.goimports: 259 | @$(GO) install golang.org/x/tools/cmd/goimports@latest 260 | 261 | .PHONY: install.addlicense 262 | install.addlicense: 263 | @$(GO) install github.com/google/addlicense@latest 264 | 265 | .PHONY: install.deepcopy-gen 266 | install.deepcopy-gen: 267 | @$(GO) install k8s.io/code-generator/cmd/deepcopy-gen@latest 268 | 269 | .PHONY: install.conversion-gen 270 | install.conversion-gen: 271 | @$(GO) install k8s.io/code-generator/cmd/conversion-gen@latest 272 | 273 | .PHONY: install.ginkgo 274 | install.ginkgo: 275 | @$(GO) install github.com/onsi/ginkgo/ginkgo@v1.16.2 276 | 277 | .PHONY: install.go-gitlint 278 | # wget -P _output/tools/ https://openim-1306374445.cos.ap-guangzhou.myqcloud.com/openim/tools/go-gitlint 279 | # go install github.com/antham/go-gitlint/cmd/gitlint@latest 280 | install.go-gitlint: 281 | @wget -q https://openim-1306374445.cos.ap-guangzhou.myqcloud.com/openim/tools/go-gitlint -O ${TOOLS_DIR}/go-gitlint 282 | @chmod +x ${TOOLS_DIR}/go-gitlint 283 | 284 | .PHONY: install.go-junit-report 285 | install.go-junit-report: 286 | @$(GO) install github.com/jstemmer/go-junit-report@latest 287 | 288 | # ============================================================================== 289 | # Tools that might be used include go gvm, cos 290 | # 291 | 292 | ## install.kube-score: Install kube-score, used to check kubernetes yaml files 293 | .PHONY: install.kube-score 294 | install.kube-score: 295 | @$(GO) install github.com/zegl/kube-score/cmd/kube-score@latest 296 | 297 | ## install.kubeconform: Install kubeconform, used to check kubernetes yaml files 298 | .PHONY: install.kubeconform 299 | install.kubeconform: 300 | @$(GO) install github.com/yannh/kubeconform/cmd/kubeconform@latest 301 | 302 | ## install.gsemver: Install gsemver, used to generate semver 303 | .PHONY: install.gsemver 304 | install.gsemver: 305 | @$(GO) install github.com/arnaud-deprez/gsemver@latest 306 | 307 | ## install.git-chglog: Install git-chglog, used to generate changelog 308 | .PHONY: install.git-chglog 309 | install.git-chglog: 310 | @$(GO) install github.com/git-chglog/git-chglog/cmd/git-chglog@latest 311 | 312 | ## install.github-release: Install github-release, used to create github release 313 | .PHONY: install.github-release 314 | install.github-release: 315 | @$(GO) install github.com/github-release/github-release@latest 316 | 317 | ## install.coscli: Install coscli, used to upload files to cos 318 | # example: ./coscli cp/sync -r /root/workspaces/kubecub/chat/ cos://kubecub-1306374445/code/ -e cos.ap-hongkong.myqcloud.com 319 | # https://cloud.tencent.com/document/product/436/71763 320 | # kubecub/* 321 | # - code/ 322 | # - docs/ 323 | # - images/ 324 | # - scripts/ 325 | .PHONY: install.coscli 326 | install.coscli: 327 | @wget -q https://github.com/tencentyun/coscli/releases/download/v0.13.0-beta/coscli-linux -O ${TOOLS_DIR}/coscli 328 | @chmod +x ${TOOLS_DIR}/coscli 329 | 330 | ## install.coscmd: Install coscmd, used to upload files to cos 331 | .PHONY: install.coscmd 332 | install.coscmd: 333 | @if which pip &>/dev/null; then pip install coscmd; else pip3 install coscmd; fi 334 | 335 | ## install.delve: Install delve, used to debug go program 336 | .PHONY: install.delve 337 | install.delve: 338 | @$(GO) install github.com/go-delve/delve/cmd/dlv@latest 339 | 340 | ## install.air: Install air, used to hot reload go program 341 | .PHONY: install.air 342 | install.air: 343 | @$(GO) install github.com/cosmtrek/air@latest 344 | 345 | ## install.gvm: Install gvm, gvm is a Go version manager, built on top of the official go tool. 346 | .PHONY: install.gvm 347 | install.gvm: 348 | @echo "===========> Installing gvm,The default installation path is ~/.gvm/scripts/gvm" 349 | @bash < <(curl -s -S -L https://raw.gitee.com/moovweb/gvm/master/binscripts/gvm-installer) 350 | @$(shell source /root/.gvm/scripts/gvm) 351 | 352 | ## install.golines: Install golines, used to format long lines 353 | .PHONY: install.golines 354 | install.golines: 355 | @$(GO) install github.com/segmentio/golines@latest 356 | 357 | ## install.go-mod-outdated: Install go-mod-outdated, used to check outdated dependencies 358 | .PHONY: install.go-mod-outdated 359 | install.go-mod-outdated: 360 | @$(GO) install github.com/psampaz/go-mod-outdated@latest 361 | 362 | ## install.mockgen: Install mockgen, used to generate mock functions 363 | .PHONY: install.mockgen 364 | install.mockgen: 365 | @$(GO) install github.com/golang/mock/mockgen@latest 366 | 367 | ## install.gotests: Install gotests, used to generate test functions 368 | .PHONY: install.gotests 369 | install.gotests: 370 | @$(GO) install github.com/cweill/gotests/gotests@latest 371 | 372 | ## install.protoc-gen-go: Install protoc-gen-go, used to generate go source files from protobuf files 373 | .PHONY: install.protoc-gen-go 374 | install.protoc-gen-go: 375 | @$(GO) install github.com/golang/protobuf/protoc-gen-go@latest 376 | 377 | ## install.cfssl: Install cfssl, used to generate certificates 378 | .PHONY: install.cfssl 379 | install.cfssl: 380 | @$(ROOT_DIR)/scripts/install/install.sh OpenIM::install::install_cfssl 381 | 382 | ## install.depth: Install depth, used to check dependency tree 383 | .PHONY: install.depth 384 | install.depth: 385 | @$(GO) install github.com/KyleBanks/depth/cmd/depth@latest 386 | 387 | ## install.go-callvis: Install go-callvis, used to visualize call graph 388 | .PHONY: install.go-callvis 389 | install.go-callvis: 390 | @$(GO) install github.com/ofabry/go-callvis@latest 391 | 392 | ## install.gothanks: Install gothanks, used to thank go dependencies 393 | .PHONY: install.gothanks 394 | install.gothanks: 395 | @$(GO) install github.com/psampaz/gothanks@latest 396 | 397 | ## install.richgo: Install richgo 398 | .PHONY: install.richgo 399 | install.richgo: 400 | @$(GO) install github.com/kyoh86/richgo@latest 401 | 402 | ## install.rts: Install rts 403 | .PHONY: install.rts 404 | install.rts: 405 | @$(GO) install github.com/galeone/rts/cmd/rts@latest 406 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ Project No Longer Maintained 2 | This project is no longer maintained and may contain unpatched vulnerabilities or compatibility issues with modern environments. Feel free to fork and modify the project if needed. 3 | If you need to use jssdk in the new version (v3.8.2+), you can introduce [@openim/client-sdk](https://www.npmjs.com/package/@openim/client-sdk) to directly communicate with IM Server without the need to deploy this project. 4 | 5 | # OIMWS - OpenIM WebSocket Service 😎 6 | 7 | ![Build Status](https://github.com/openim-sigs/oimws/actions/workflows/test.yml/badge.svg) 8 | [![Codecov](https://img.shields.io/codecov/c/github/openim-sigs/oimws)](https://app.codecov.io/github/openim-sigs/oimws) 9 | [![Go Report Card](https://goreportcard.com/badge/github.com/openim-sigs/oimws)](https://goreportcard.com/report/github.com/openim-sigs/oimws) 10 | [![GoDoc](https://godoc.org/github.com/openim-sigs/oimws?status.svg)](https://godoc.org/github.com/openim-sigs/oimws) 11 | 12 | > :notes: OIMWS (OpenIM WebSocket Service) is a high-performance, scalable WebSocket framework designed specifically for building instant messaging (IM) systems. Harnessing the concurrent capabilities of Go and the real-time communication provided by WebSocket protocol, OIMWS offers a robust backend solution to support modern instant communication needs, ranging from basic message transit to complex session management and network optimization. 13 | 14 | ## Features 🚀 15 | 16 | + **WebSocket Support**: Provides full WebSocket connection handling for high-concurrency client communication. 17 | + **Modular Architecture**: Features a set of decoupled modules for message processing, connection management, and user/group interactions, facilitating development and maintenance. 18 | + **Real-Time Message Processing**: Ensures swift response and distribution of real-time messages for timely and reliable communication. 19 | + **Network Layer Abstraction**: Includes low-level network abstractions allowing for customized protocol and data process flows. 20 | + **Configuration Management**: Simplified configuration management supports rapid deployment and environmental adaptation. 21 | + **Utility Toolkit**: Offers a wide array of utility functions for common operations such as date processing and text manipulation. 22 | + **Automated Testing**: Built-in unit tests ensure the stability of modules and features. 23 | + **Clear Build Scripts**: Makefile support simplifies the build process, enhancing developer productivity. 24 | + **Code Quality Assurance**: Integrates `golangci-lint` to ensure consistency in code quality and style. 25 | + **Sample Code**: Includes examples to demonstrate framework usage, use openim jssdk(10003) accelerating the learning curve for developers. 26 | 27 | ## Quick Start 🚗💨 28 | 29 | Clone the repository to your local machine: 30 | ```bash 31 | git clone https://github.com/openim-sigs/oimws.git 32 | cd oimws 33 | ``` 34 | 35 | **Build oimws** 36 | 37 | ```bash 38 | mage 39 | ``` 40 | 41 | **Start oimws** 42 | 43 | ```bash 44 | mage start 45 | ``` 46 | 47 | **Check oimws status** 48 | 49 | ```bash 50 | mage check 51 | ``` 52 | 53 | **Stop oimws Status:** 54 | 55 | ```bash 56 | mage stop 57 | ``` 58 | 59 | ### [main](https://github.com/openim-sigs/oimws/tree/main/cmd) 60 | 61 | An encapsulated framework within `jssdk` connecting to `openim-sdk-core`, providing streamlined management and integration of WebSocket, TCP, and HTTP protocols in the OpenIM ecosystem. 62 | 63 | 64 | ### code 65 | 66 | the folders of oimws: 67 | 68 | cmd-------------- the main.go folder 69 | 70 | 71 | common----------- common structures and functions, primarily used by the network frame 72 | 73 | 74 | core_func-------- Some functions encapsulate the interface for calling the JS SDK. 75 | 76 | 77 | gate------------- network frame,functions for websocket 78 | 79 | 80 | module----------- the module codes 81 | 82 | 83 | network---------- network frame 84 | 85 | 86 | 87 | ### Cluster solution(Consistent Hashing) 88 | 89 | Using consistent hashing in Nginx typically involves the hash directive within the upstream module. 90 | Starting from Nginx 1.7.2, it supports consistent hashing based on specified variables. 91 | You can use the request parameter sendId as the key for consistent hashing to distribute requests. 92 | 93 | Here's a configuration example that demonstrates how to use consistent hashing for the /ws endpoint to select backend servers: 94 | ``` 95 | http { 96 | upstream backend { 97 | # Use userId as the key for consistent hashing 98 | hash $arg_sendId consistent; 99 | 100 | # Define backend servers 101 | server backend1.example.com; 102 | server backend2.example.com; 103 | # ... More backend servers ... 104 | } 105 | 106 | server { 107 | listen 80; 108 | 109 | location /ws { 110 | proxy_pass http://backend; 111 | # ... Other possible proxy settings ... 112 | } 113 | 114 | # ... Other location definitions ... 115 | } 116 | } 117 | 118 | ``` 119 | ## Contribution Ⓜ️ 120 | 121 | Feel free to contribute to this project by opening issues or submitting pull requests. 122 | 123 | 124 | 125 | 126 | 127 | ## License 🤝 128 | 129 | This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details. 130 | -------------------------------------------------------------------------------- /bootstrap.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | 4 | mage -version >nul 2>&1 5 | IF %ERRORLEVEL% EQU 0 ( 6 | echo Mage is already installed. 7 | GOTO DOWNLOAD 8 | ) 9 | 10 | go version >nul 2>&1 11 | IF NOT %ERRORLEVEL% EQU 0 ( 12 | echo Go is not installed. Please install Go and try again. 13 | exit /b 1 14 | ) 15 | 16 | echo Installing Mage... 17 | go install github.com/magefile/mage@latest 18 | 19 | mage -version >nul 2>&1 20 | IF NOT %ERRORLEVEL% EQU 0 ( 21 | echo Mage installation failed. 22 | echo Please ensure that %GOPATH%/bin is in your PATH. 23 | exit /b 1 24 | ) 25 | 26 | echo Mage installed successfully. 27 | 28 | :DOWNLOAD 29 | go mod download 30 | 31 | ENDLOCAL 32 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ ":$PATH:" == *":$HOME/.local/bin:"* ]]; then 4 | TARGET_DIR="$HOME/.local/bin" 5 | else 6 | TARGET_DIR="/usr/local/bin" 7 | echo "Using /usr/local/bin as the installation directory. Might require sudo permissions." 8 | fi 9 | 10 | if ! command -v mage &> /dev/null; then 11 | echo "Installing Mage to $TARGET_DIR ..." 12 | GOBIN=$TARGET_DIR go install github.com/magefile/mage@latest 13 | fi 14 | 15 | if ! command -v mage &> /dev/null; then 16 | echo "Mage installation failed." 17 | echo "Please ensure that $TARGET_DIR is in your \$PATH." 18 | exit 1 19 | fi 20 | 21 | echo "Mage installed successfully." 22 | 23 | go mod download 24 | -------------------------------------------------------------------------------- /cmd/openim-sdk/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/openim-sigs/oimws/pkg/common/cmd" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | func main() { 11 | if err := cmd.NewSdkCmd().Exec(); err != nil { 12 | ExitWithError(err) 13 | } 14 | } 15 | 16 | // TODO 17 | func ExitWithError(err error) { 18 | progName := filepath.Base(os.Args[0]) 19 | fmt.Fprintf(os.Stderr, "%s exit -1: %+v\n", progName, err) 20 | os.Exit(-1) 21 | } 22 | -------------------------------------------------------------------------------- /config/config.yml: -------------------------------------------------------------------------------- 1 | openimApi: "http://127.0.0.1:10002" 2 | openimWs: "ws://127.0.0.1:10001" 3 | sdkWsPort: [ 10003 ] 4 | dbDir: "../../../../db/" 5 | -------------------------------------------------------------------------------- /config/log.yml: -------------------------------------------------------------------------------- 1 | 2 | # Log storage path, default is acceptable, change to a full path if modification is needed 3 | storageLocation: ../../../../logs/ 4 | # Log rotation period (in hours), default is acceptable 5 | rotationTime: 24 6 | # Number of log files to retain, default is acceptable 7 | remainRotationCount: 2 8 | # Log level settings: 3 for production environment; 6 for more verbose logging in debugging environments 9 | remainLogLevel: 6 10 | # Whether to output to standard output, default is acceptable 11 | isStdout: false 12 | # Whether to log in JSON format, default is acceptable 13 | isJson: false 14 | 15 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/openim-sigs/oimws 2 | 3 | go 1.21.2 4 | 5 | require ( 6 | github.com/OpenIMSDK/tools v0.0.24 7 | github.com/bwmarrin/snowflake v0.3.0 8 | github.com/gorilla/websocket v1.5.1 9 | github.com/mitchellh/mapstructure v1.5.0 10 | github.com/openimsdk/gomake v0.0.12 11 | github.com/openimsdk/openim-sdk-core/v3 v3.5.1 12 | github.com/spf13/cobra v1.8.0 13 | github.com/spf13/viper v1.18.2 14 | github.com/stretchr/testify v1.9.0 15 | github.com/xuexihuang/new_log15 v1.0.0 16 | ) 17 | 18 | require ( 19 | github.com/google/go-cmp v0.6.0 // indirect 20 | github.com/jinzhu/copier v0.4.0 // indirect 21 | github.com/mattn/go-sqlite3 v1.14.22 // indirect 22 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect 23 | golang.org/x/image v0.15.0 // indirect 24 | golang.org/x/net v0.22.0 // indirect 25 | golang.org/x/text v0.14.0 // indirect 26 | google.golang.org/grpc v1.62.1 // indirect 27 | gorm.io/driver/sqlite v1.5.5 // indirect 28 | nhooyr.io/websocket v1.8.10 // indirect 29 | ) 30 | 31 | require ( 32 | github.com/OpenIMSDK/protocol v0.0.40 // indirect 33 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 34 | github.com/fsnotify/fsnotify v1.7.0 // indirect 35 | github.com/go-ole/go-ole v1.2.6 // indirect 36 | github.com/golang/protobuf v1.5.3 // indirect 37 | github.com/hashicorp/hcl v1.0.0 // indirect 38 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 39 | github.com/jinzhu/inflection v1.0.0 // indirect 40 | github.com/jinzhu/now v1.1.5 // indirect 41 | github.com/json-iterator/go v1.1.12 // indirect 42 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect 43 | github.com/lestrrat-go/strftime v1.0.6 // indirect 44 | github.com/magefile/mage v1.15.0 // indirect 45 | github.com/magiconair/properties v1.8.7 // indirect 46 | github.com/mattn/go-colorable v0.1.13 // indirect 47 | github.com/mattn/go-isatty v0.0.19 // indirect 48 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 49 | github.com/modern-go/reflect2 v1.0.2 // indirect 50 | github.com/patrickmn/go-cache v2.1.0+incompatible // indirect 51 | github.com/pelletier/go-toml/v2 v2.1.0 // indirect 52 | github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect 53 | github.com/pkg/errors v0.9.1 // indirect 54 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 55 | github.com/sagikazarmark/locafero v0.4.0 // indirect 56 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 57 | github.com/shirou/gopsutil v3.21.11+incompatible // indirect 58 | github.com/sourcegraph/conc v0.3.0 // indirect 59 | github.com/spf13/afero v1.11.0 // indirect 60 | github.com/spf13/cast v1.6.0 // indirect 61 | github.com/spf13/pflag v1.0.5 // indirect 62 | github.com/subosito/gotenv v1.6.0 // indirect 63 | github.com/tklauser/go-sysconf v0.3.13 // indirect 64 | github.com/tklauser/numcpus v0.7.0 // indirect 65 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 66 | go.uber.org/atomic v1.9.0 // indirect 67 | go.uber.org/multierr v1.11.0 // indirect 68 | go.uber.org/zap v1.24.0 // indirect 69 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 70 | golang.org/x/sys v0.19.0 // indirect 71 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect 72 | google.golang.org/protobuf v1.33.0 // indirect 73 | gopkg.in/ini.v1 v1.67.0 // indirect 74 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect 75 | gopkg.in/yaml.v3 v3.0.1 // indirect 76 | gorm.io/gorm v1.25.10 // indirect 77 | ) 78 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/OpenIMSDK/protocol v0.0.40 h1:1/Oij6RSAaePCPrWGwp9Cyz976/8Uxr94hM5M5FXzlg= 2 | github.com/OpenIMSDK/protocol v0.0.40/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= 3 | github.com/OpenIMSDK/tools v0.0.24 h1:P8n7ZtsZEbm4W3dQAem29O+bigzy6YPXxFzd/D8Vh3U= 4 | github.com/OpenIMSDK/tools v0.0.24/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= 5 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 6 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 7 | github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= 8 | github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= 9 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 13 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 15 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 16 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 17 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 18 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 19 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 20 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 21 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 22 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 23 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 24 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 25 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 26 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 27 | github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= 28 | github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 29 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 30 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 31 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 32 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 33 | github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= 34 | github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= 35 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 36 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 37 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 38 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 39 | github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= 40 | github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= 41 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 42 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 43 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 44 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 45 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 46 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 47 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= 48 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= 49 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= 50 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= 51 | github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= 52 | github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= 53 | github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= 54 | github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= 55 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 56 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 57 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 58 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 59 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 60 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 61 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 62 | github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= 63 | github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 64 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 65 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 66 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 67 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 68 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 69 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 70 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 71 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= 72 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 73 | github.com/openimsdk/gomake v0.0.12 h1:OL0MzU+1Ks+qAvOuftXEOXz/ka5+4SpqDTgYhb4oNlc= 74 | github.com/openimsdk/gomake v0.0.12/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI= 75 | github.com/openimsdk/openim-sdk-core/v3 v3.5.1 h1:lvC8TlouT9zFxjoBeH/S4YCkwbQ5IgSnNRO9NJ9H5v8= 76 | github.com/openimsdk/openim-sdk-core/v3 v3.5.1/go.mod h1:d2aVHT6pSYthQQd+/4JaruHPTLsQV2D+eQisRQkoP3I= 77 | github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= 78 | github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= 79 | github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= 80 | github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= 81 | github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA= 82 | github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= 83 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 84 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 85 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 86 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 87 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 88 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 89 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 90 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 91 | github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= 92 | github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= 93 | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= 94 | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= 95 | github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= 96 | github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 97 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 98 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 99 | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= 100 | github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= 101 | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= 102 | github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 103 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= 104 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 105 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 106 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 107 | github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= 108 | github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= 109 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 110 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 111 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 112 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 113 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 114 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 115 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 116 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 117 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 118 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 119 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 120 | github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= 121 | github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= 122 | github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= 123 | github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= 124 | github.com/xuexihuang/new_log15 v1.0.0 h1:rIfjUeC4dgkjDYvbb6zv3q4Jk/aSvBo03UJ1mJCyJvg= 125 | github.com/xuexihuang/new_log15 v1.0.0/go.mod h1:psqerkM8mBbXXWN8DG3NUo33C3Hyr9RsaRjq8VfyA2c= 126 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 127 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 128 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= 129 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 130 | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= 131 | go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 132 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 133 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 134 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= 135 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 136 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= 137 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 138 | golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= 139 | golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= 140 | golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= 141 | golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 142 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 144 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 145 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= 146 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 147 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 148 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 149 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 150 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= 151 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= 152 | google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= 153 | google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= 154 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 155 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 156 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 157 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 158 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 159 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 160 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 161 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 162 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 163 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 164 | gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 165 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 166 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 167 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 168 | gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= 169 | gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= 170 | gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= 171 | gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 172 | nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= 173 | nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= 174 | -------------------------------------------------------------------------------- /internal/sdk/sdk.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "fmt" 5 | "github.com/openim-sigs/oimws/pkg/common/config" 6 | gate2 "github.com/openim-sigs/oimws/pkg/gate" 7 | "github.com/openim-sigs/oimws/pkg/network/tjson" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | const ( 13 | MaxMsgLen = 1024 * 1024 * 10 14 | MaxConnNum = 100 * 100 * 10 15 | HTTPTimeout = 10 * time.Second 16 | WriterChanLen = 1000 17 | ) 18 | 19 | type Config struct { 20 | SdkConfig config.Config 21 | } 22 | 23 | var Processor = tjson.NewProcessor() 24 | 25 | type GateNet struct { 26 | *gate2.Gate 27 | CloseSig chan bool 28 | Wg sync.WaitGroup 29 | } 30 | 31 | // Initsever initializes a new GateNet instance with the given WebSocket port and default configurations. 32 | func Initsever(wsPort int) *GateNet { 33 | gatenet := new(GateNet) 34 | gatenet.Gate = gate2.NewGate(MaxConnNum, MaxMsgLen, 35 | Processor, fmt.Sprintf(":%d", wsPort), HTTPTimeout, WriterChanLen) 36 | gatenet.CloseSig = make(chan bool, 1) 37 | return gatenet 38 | } 39 | 40 | // SetMsgFun sets the functions that will be called on new connection, disconnection, and data reception events. 41 | func (gt *GateNet) SetMsgFun(Fun1 func(gate2.Agent), Fun2 func(gate2.Agent), Fun3 func(interface{}, gate2.Agent)) { 42 | gt.Gate.SetFun(Fun1, Fun2, Fun3) 43 | } 44 | 45 | // Runloop starts the server loop in a new goroutine and ensures the WaitGroup is properly managed. 46 | func (gt *GateNet) Runloop() { 47 | gt.Wg.Add(1) 48 | gt.Run(gt.CloseSig) 49 | gt.Wg.Done() 50 | } 51 | 52 | // CloseGate sends a signal to close the server and waits for all goroutines to finish before calling OnDestroy. 53 | func (gt *GateNet) CloseGate() { 54 | gt.CloseSig <- true 55 | gt.Wg.Wait() 56 | gt.Gate.OnDestroy() 57 | } 58 | -------------------------------------------------------------------------------- /internal/sdk/start.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/openim-sigs/oimws/pkg/common/config" 7 | "github.com/openim-sigs/oimws/pkg/core_func" 8 | "github.com/openim-sigs/oimws/pkg/module" 9 | log "github.com/xuexihuang/new_log15" 10 | "os" 11 | "os/signal" 12 | "syscall" 13 | "time" 14 | ) 15 | 16 | func Start(ctx context.Context, index int, conf *Config, logConf *config.Log) error { 17 | listenPort, err := datautilGetElemByIndex(conf.SdkConfig.SdkWsPort, index) 18 | if err != nil { 19 | return err 20 | } 21 | if err := os.MkdirAll(conf.SdkConfig.DbDir, os.ModePerm); err != nil && !os.IsExist(err) { 22 | return err 23 | } 24 | core_func.Config.WsAddr = conf.SdkConfig.OpenimWs 25 | core_func.Config.ApiAddr = conf.SdkConfig.OpenimApi 26 | core_func.Config.DataDir = conf.SdkConfig.DbDir 27 | core_func.Config.LogLevel = uint32(logConf.RemainLogLevel) 28 | core_func.Config.IsLogStandardOutput = logConf.IsStdout 29 | core_func.Config.LogFilePath = logConf.StorageLocation 30 | core_func.Config.IsExternalExtensions = true 31 | log.SetOutLevel(log.LvlInfo) 32 | fmt.Println("Client starting....") 33 | log.Info("Client starting....") 34 | gatenet := Initsever(listenPort) 35 | gatenet.SetMsgFun(module.NewAgent, module.CloseAgent, module.DataRecv) 36 | go gatenet.Runloop() 37 | module.ProgressStartTime = time.Now().Unix() 38 | c := make(chan os.Signal, 1) 39 | signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGQUIT, syscall.SIGTERM) 40 | sig := <-c 41 | log.Info("wsconn server closing down ", "sig", sig) 42 | gatenet.CloseGate() 43 | return nil 44 | } 45 | 46 | // TODO: datautil.GetElemByIndex 47 | func datautilGetElemByIndex(array []int, index int) (int, error) { 48 | if index < 0 || index >= len(array) { 49 | return 0, fmt.Errorf("index out of range index %d array %+v", index, array) 50 | } 51 | return array[index], nil 52 | } 53 | -------------------------------------------------------------------------------- /magefile.go: -------------------------------------------------------------------------------- 1 | //go:build mage 2 | // +build mage 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/openimsdk/gomake/mageutil" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var Default = Build 13 | 14 | func Build() { 15 | platforms := os.Getenv("PLATFORMS") 16 | if platforms == "" { 17 | platforms = mageutil.DetectPlatform() 18 | } 19 | 20 | for _, platform := range strings.Split(platforms, " ") { 21 | mageutil.CompileForPlatform(platform) 22 | } 23 | 24 | mageutil.PrintGreen("All binaries under cmd and tools were successfully compiled.") 25 | } 26 | 27 | func Start() { 28 | mageutil.InitForSSC() 29 | err := setMaxOpenFiles() 30 | if err != nil { 31 | mageutil.PrintRed("setMaxOpenFiles failed " + err.Error()) 32 | os.Exit(1) 33 | } 34 | mageutil.StartToolsAndServices() 35 | } 36 | 37 | func Stop() { 38 | mageutil.StopAndCheckBinaries() 39 | } 40 | 41 | func Check() { 42 | mageutil.CheckAndReportBinariesStatus() 43 | } 44 | -------------------------------------------------------------------------------- /magefile_unix.go: -------------------------------------------------------------------------------- 1 | //go:build mage && !windows 2 | // +build mage,!windows 3 | 4 | package main 5 | 6 | import ( 7 | "github.com/openimsdk/gomake/mageutil" 8 | "syscall" 9 | ) 10 | 11 | func setMaxOpenFiles() error { 12 | var rLimit syscall.Rlimit 13 | err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) 14 | if err != nil { 15 | return err 16 | } 17 | rLimit.Max = uint64(mageutil.MaxFileDescriptors) 18 | rLimit.Cur = uint64(mageutil.MaxFileDescriptors) 19 | return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) 20 | } 21 | -------------------------------------------------------------------------------- /magefile_windows.go: -------------------------------------------------------------------------------- 1 | //go:build mage 2 | // +build mage 3 | 4 | package main 5 | 6 | func setMaxOpenFiles() error { 7 | return nil 8 | } 9 | -------------------------------------------------------------------------------- /pkg/common/cmd/constant.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | var ( 22 | FileName = "config.yml" 23 | LogConfigFileName = "log.yml" 24 | ) 25 | 26 | var ConfigEnvPrefixMap map[string]string 27 | 28 | func init() { 29 | ConfigEnvPrefixMap = make(map[string]string) 30 | fileNames := []string{ 31 | FileName, 32 | LogConfigFileName, 33 | } 34 | for _, fileName := range fileNames { 35 | envKey := strings.TrimSuffix(strings.TrimSuffix(fileName, ".yml"), ".yaml") 36 | envKey = "IMENV_" + envKey 37 | envKey = strings.ToUpper(strings.ReplaceAll(envKey, "-", "_")) 38 | ConfigEnvPrefixMap[fileName] = envKey 39 | } 40 | } 41 | 42 | const ( 43 | FlagConf = "config_folder_path" 44 | FlagTransferIndex = "index" 45 | ) 46 | -------------------------------------------------------------------------------- /pkg/common/cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "fmt" 19 | "github.com/openim-sigs/oimws/pkg/common/config" 20 | "path/filepath" 21 | 22 | "github.com/OpenIMSDK/tools/errs" 23 | "github.com/OpenIMSDK/tools/log" 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | type RootCmd struct { 28 | Command cobra.Command 29 | processName string 30 | port int 31 | prometheusPort int 32 | log config.Log 33 | index int 34 | } 35 | 36 | func (r *RootCmd) Index() int { 37 | return r.index 38 | } 39 | 40 | func (r *RootCmd) Port() int { 41 | return r.port 42 | } 43 | 44 | type CmdOpts struct { 45 | loggerPrefixName string 46 | configMap map[string]any 47 | } 48 | 49 | func WithCronTaskLogName() func(*CmdOpts) { 50 | return func(opts *CmdOpts) { 51 | opts.loggerPrefixName = "openim-crontask" 52 | } 53 | } 54 | 55 | func WithLogName(logName string) func(*CmdOpts) { 56 | return func(opts *CmdOpts) { 57 | opts.loggerPrefixName = logName 58 | } 59 | } 60 | func WithConfigMap(configMap map[string]any) func(*CmdOpts) { 61 | return func(opts *CmdOpts) { 62 | opts.configMap = configMap 63 | } 64 | } 65 | 66 | func NewRootCmd(processName string, opts ...func(*CmdOpts)) *RootCmd { 67 | rootCmd := &RootCmd{processName: processName} 68 | cmd := cobra.Command{ 69 | Use: "Start openIM application", 70 | Long: fmt.Sprintf(`Start %s `, processName), 71 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 72 | return rootCmd.persistentPreRun(cmd, opts...) 73 | }, 74 | SilenceUsage: true, 75 | SilenceErrors: false, 76 | } 77 | cmd.Flags().StringP(FlagConf, "c", "", "path of config directory") 78 | cmd.Flags().IntP(FlagTransferIndex, "i", 0, "process startup sequence number") 79 | 80 | rootCmd.Command = cmd 81 | return rootCmd 82 | } 83 | 84 | func (r *RootCmd) persistentPreRun(cmd *cobra.Command, opts ...func(*CmdOpts)) error { 85 | cmdOpts := r.applyOptions(opts...) 86 | if err := r.initializeConfiguration(cmd, cmdOpts); err != nil { 87 | return err 88 | } 89 | //TODO: ERRS 90 | if err := r.initializeLogger(cmdOpts); err != nil { 91 | return errs.Wrap(err, "failed to initialize logger") 92 | } 93 | 94 | return nil 95 | } 96 | 97 | func (r *RootCmd) initializeConfiguration(cmd *cobra.Command, opts *CmdOpts) error { 98 | configDirectory, _, err := r.getFlag(cmd) 99 | if err != nil { 100 | return err 101 | } 102 | // Load common configuration file 103 | //opts.configMap[ShareFileName] = StructEnvPrefix{EnvPrefix: shareEnvPrefix, ConfigStruct: &r.share} 104 | for configFileName, configStruct := range opts.configMap { 105 | err := config.LoadConfig(filepath.Join(configDirectory, configFileName), 106 | ConfigEnvPrefixMap[configFileName], configStruct) 107 | if err != nil { 108 | return err 109 | } 110 | } 111 | // Load common log configuration file 112 | return config.LoadConfig(filepath.Join(configDirectory, LogConfigFileName), 113 | ConfigEnvPrefixMap[LogConfigFileName], &r.log) 114 | } 115 | 116 | func (r *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts { 117 | cmdOpts := defaultCmdOpts() 118 | for _, opt := range opts { 119 | opt(cmdOpts) 120 | } 121 | 122 | return cmdOpts 123 | } 124 | 125 | func (r *RootCmd) initializeLogger(cmdOpts *CmdOpts) error { 126 | //TODO log 127 | return log.InitFromConfig( 128 | cmdOpts.loggerPrefixName, 129 | r.processName, 130 | r.log.RemainLogLevel, 131 | r.log.IsStdout, 132 | r.log.IsJson, 133 | r.log.StorageLocation, 134 | r.log.RemainRotationCount, 135 | r.log.RotationTime, 136 | //config.Version, 137 | ) 138 | } 139 | 140 | func defaultCmdOpts() *CmdOpts { 141 | return &CmdOpts{ 142 | loggerPrefixName: "openim-service-log", 143 | } 144 | } 145 | 146 | func (r *RootCmd) getFlag(cmd *cobra.Command) (string, int, error) { 147 | configDirectory, err := cmd.Flags().GetString(FlagConf) 148 | if err != nil { 149 | return "", 0, errs.Wrap(err) 150 | } 151 | index, err := cmd.Flags().GetInt(FlagTransferIndex) 152 | if err != nil { 153 | return "", 0, errs.Wrap(err) 154 | } 155 | r.index = index 156 | return configDirectory, index, nil 157 | } 158 | 159 | func (r *RootCmd) Execute() error { 160 | return r.Command.Execute() 161 | } 162 | -------------------------------------------------------------------------------- /pkg/common/cmd/sdk.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cmd 16 | 17 | import ( 18 | "context" 19 | "github.com/openim-sigs/oimws/internal/sdk" 20 | "github.com/openim-sigs/oimws/pkg/common/config" 21 | "github.com/spf13/cobra" 22 | "os" 23 | "strings" 24 | ) 25 | 26 | type SdkCmd struct { 27 | *RootCmd 28 | ctx context.Context 29 | configMap map[string]any 30 | sdkConfig sdk.Config 31 | } 32 | 33 | func NewSdkCmd() *SdkCmd { 34 | var ret SdkCmd 35 | ret.configMap = map[string]any{ 36 | FileName: &ret.sdkConfig.SdkConfig, 37 | } 38 | // TODO program.GetProcessName() 39 | var processName string 40 | if args := os.Args; len(args) > 0 { 41 | segments := strings.Split(args[0], "/") 42 | processName = segments[len(segments)-1] 43 | } 44 | //TODO 45 | ret.RootCmd = NewRootCmd(processName, WithConfigMap(ret.configMap)) 46 | //ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap)) 47 | ret.ctx = context.WithValue(context.Background(), "version", config.Version) 48 | ret.Command.RunE = func(cmd *cobra.Command, args []string) error { 49 | return ret.runE() 50 | } 51 | return &ret 52 | } 53 | 54 | func (a *SdkCmd) Exec() error { 55 | return a.Execute() 56 | } 57 | 58 | func (a *SdkCmd) runE() error { 59 | return sdk.Start(a.ctx, a.Index(), &a.sdkConfig, &a.RootCmd.log) 60 | } 61 | -------------------------------------------------------------------------------- /pkg/common/config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | type Log struct { 18 | StorageLocation string `mapstructure:"storageLocation"` 19 | RotationTime uint `mapstructure:"rotationTime"` 20 | RemainRotationCount uint `mapstructure:"remainRotationCount"` 21 | RemainLogLevel int `mapstructure:"remainLogLevel"` 22 | IsStdout bool `mapstructure:"isStdout"` 23 | IsJson bool `mapstructure:"isJson"` 24 | WithStack bool `mapstructure:"withStack"` 25 | } 26 | 27 | type Config struct { 28 | OpenimApi string `mapstructure:"openimApi"` 29 | OpenimWs string `mapstructure:"openimWs"` 30 | SdkWsPort []int `mapstructure:"sdkWsPort"` 31 | DbDir string `mapstructure:"dbDir"` 32 | } 33 | -------------------------------------------------------------------------------- /pkg/common/config/constant.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 OpenIM. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | const ConfKey = "conf" 18 | 19 | const ( 20 | // DefaultDirPerm is used for creating general directories, allowing the owner to read, write, and execute, 21 | // while the group and others can only read and execute. 22 | DefaultDirPerm = 0755 23 | 24 | // PrivateFilePerm is used for sensitive files, allowing only the owner to read and write. 25 | PrivateFilePerm = 0600 26 | 27 | // ExecFilePerm is used for executable files, allowing the owner to read, write, and execute, 28 | // while the group and others can only read. 29 | ExecFilePerm = 0754 30 | 31 | // SharedDirPerm is used for shared directories, allowing the owner and group to read, write, and execute, 32 | // with no permissions for others. 33 | SharedDirPerm = 0770 34 | 35 | // ReadOnlyDirPerm is used for read-only directories, allowing the owner, group, and others to only read. 36 | ReadOnlyDirPerm = 0555 37 | ) 38 | -------------------------------------------------------------------------------- /pkg/common/config/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 OpenIM. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config // import "github.com/openimsdk/open-im-server/v3/pkg/common/config" 16 | -------------------------------------------------------------------------------- /pkg/common/config/load_config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/OpenIMSDK/tools/errs" 5 | "github.com/mitchellh/mapstructure" 6 | "github.com/spf13/viper" 7 | "strings" 8 | ) 9 | 10 | func LoadConfig(path string, envPrefix string, config any) error { 11 | v := viper.New() 12 | v.SetConfigFile(path) 13 | v.SetEnvPrefix(envPrefix) 14 | v.AutomaticEnv() 15 | v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 16 | 17 | if err := v.ReadInConfig(); err != nil { 18 | return errs.Wrap(err, "failed to read config file", "path", path, "envPrefix", envPrefix) 19 | } 20 | 21 | if err := v.Unmarshal(config, func(config *mapstructure.DecoderConfig) { 22 | config.TagName = "mapstructure" 23 | }); err != nil { 24 | return errs.Wrap(err, "failed to unmarshal config", "path", path, "envPrefix", envPrefix) 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /pkg/common/config/version: -------------------------------------------------------------------------------- 1 | 3.7.0 -------------------------------------------------------------------------------- /pkg/common/config/version.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2023 OpenIM. All rights reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import _ "embed" 18 | 19 | //go:embed version 20 | var Version string 21 | -------------------------------------------------------------------------------- /pkg/common/struct.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // TAppParam defines the configuration parameters related to an application. 4 | type TAppParam struct { 5 | ModuleType string // The type of the module, used to identify different modules or features. 6 | PairSessionId string // The unique identifier for a pairing session. 7 | Robot3dId int // The unique identifier for a 3D robot. 8 | UE_Ip string // The IP address of the User Equipment (UE). 9 | UE_Port int // The port number of the User Equipment (UE). 10 | UE_h5_weith int // The width of the HTML5 element on the User Equipment (UE), likely a typo and should be 'UE_h5_width'. 11 | UE_h5_higth int // The height of the HTML5 element on the User Equipment (UE), likely a typo and should be 'UE_h5_height'. 12 | } 13 | 14 | // TAgentUserData contains user-specific data passed to an agent. 15 | type TAgentUserData struct { 16 | SessionID string // The session ID uniquely identifying the user session. 17 | CookieVal string // The value of the cookie stored for the user. 18 | AppString string // A string related to the application, possibly containing user-specific settings or states. 19 | ProxyBody interface{} // A generic interface to hold different types of data for proxy communication. 20 | UserId string 21 | } 22 | 23 | // TWSData defines the structure for WebSocket data transmission. 24 | type TWSData struct { 25 | MsgType int // The type of the message, used to handle different data or requests. 26 | Msg []byte // The actual message data in bytes. 27 | } 28 | 29 | const ( 30 | // MessageText is for UTF-8 encoded text messages like JSON. 31 | MessageText = iota + 1 32 | // MessageBinary is for binary messages like protobufs. 33 | MessageBinary 34 | // CloseMessage denotes a close control message. The optional message 35 | // payload contains a numeric code and text. Use the FormatCloseMessage 36 | // function to format a close message payload. 37 | CloseMessage = 8 38 | 39 | // PingMessage denotes a ping control message. The optional message payload 40 | // is UTF-8 encoded text. 41 | PingMessage = 9 42 | 43 | // PongMessage denotes a pong control message. The optional message payload 44 | // is UTF-8 encoded text. 45 | PongMessage = 10 46 | ) 47 | -------------------------------------------------------------------------------- /pkg/common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bwmarrin/snowflake" 6 | log "github.com/xuexihuang/new_log15" 7 | "runtime/debug" 8 | ) 9 | 10 | // Method used to capture panic and print stack information. 11 | func TryRecoverAndDebugPrint() { 12 | errs := recover() 13 | if errs == nil { 14 | return 15 | } 16 | fmt.Printf("panic: %+v\n%s", errs, debug.Stack()) 17 | log.Crit("[Panic]", "err", errs, "stackInfo", debug.Stack()) 18 | 19 | } 20 | 21 | var G_flakeNode snowflake.Node 22 | 23 | func GetRandomSessionId() string { 24 | 25 | return G_flakeNode.Generate().String() 26 | } 27 | -------------------------------------------------------------------------------- /pkg/core_func/responder.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | import ( 4 | "fmt" 5 | log "github.com/xuexihuang/new_log15" 6 | 7 | "github.com/OpenIMSDK/tools/errs" 8 | "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs" 9 | ) 10 | 11 | type RespMessage struct { 12 | respMessagesChan chan *EventData 13 | } 14 | 15 | // NewRespMessage creates a new instance of RespMessage with the given channel. 16 | func NewRespMessage(respMessagesChan chan *EventData) *RespMessage { 17 | return &RespMessage{respMessagesChan: respMessagesChan} 18 | } 19 | 20 | // sendOnSuccessResp sends a success response message for a given operation. 21 | func (r *RespMessage) sendOnSuccessResp(operationID, event string, data string) { 22 | r.respMessagesChan <- &EventData{ 23 | Event: event, 24 | OperationID: operationID, 25 | Data: data, 26 | } 27 | } 28 | 29 | // sendOnErrorResp sends an error response message for a given operation. 30 | func (r *RespMessage) sendOnErrorResp(operationID, event string, err error) { 31 | log.Error("sendOnErrorResp", "operationID", operationID, "event", event, "err", err) 32 | resp := &EventData{ 33 | Event: event, 34 | OperationID: operationID, 35 | } 36 | if code, ok := err.(errs.CodeError); ok { 37 | resp.ErrCode = int32(code.Code()) 38 | resp.ErrMsg = code.Error() 39 | } else { 40 | resp.ErrCode = sdkerrs.UnknownCode 41 | resp.ErrMsg = fmt.Sprintf("error %T not implement CodeError: %s", err, err) 42 | } 43 | r.respMessagesChan <- resp 44 | } 45 | 46 | // sendEventFailedRespNoErr sends a failed event response without error details. 47 | // event: Name of the event. 48 | func (r *RespMessage) sendEventFailedRespNoErr(event string) { 49 | r.respMessagesChan <- &EventData{ 50 | Event: event, 51 | } 52 | } 53 | 54 | // sendEventSuccessRespWithData sends a successful event response with associated data. 55 | func (r *RespMessage) sendEventSuccessRespWithData(event string, data string) { 56 | r.respMessagesChan <- &EventData{ 57 | Event: event, 58 | Data: data, 59 | } 60 | } 61 | 62 | // sendEventSuccessRespNoData sends a successful event response without any associated data. 63 | // This is included for completeness but not used in the above callback methods. 64 | func (r *RespMessage) sendEventSuccessRespNoData(event string) { 65 | r.respMessagesChan <- &EventData{ 66 | Event: event, 67 | } 68 | } 69 | 70 | // sendEventFailedRespNoData sends a failed event response with error code and message, without any associated data. 71 | // This function may be used if there are any future error handling requirements in the SignalingCallback. 72 | func (r *RespMessage) sendEventFailedRespNoData(event string, errCode int32, errMsg string) { 73 | r.respMessagesChan <- &EventData{ 74 | Event: event, 75 | ErrCode: errCode, 76 | ErrMsg: errMsg, 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pkg/core_func/ws_conversation_msg_test.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGetAllConversationList(T *testing.T) { 9 | 10 | ev := make(chan *EventData, 10) 11 | fu := NewFuncRouter(ev, "123456") 12 | fu.GetAllConversationList("11111") 13 | 14 | msg := <-fu.respMessage.respMessagesChan 15 | fmt.Println("msg:", msg) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/core_func/ws_file.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | // UploadFile handles the file upload process. 4 | func (f *FuncRouter) UploadFile(operationID string, args ...any) { 5 | f.call(operationID, f.userForSDK.File().UploadFile, args) 6 | } 7 | -------------------------------------------------------------------------------- /pkg/core_func/ws_friend.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | // CheckFriend checks the friend status between users. 4 | func (f *FuncRouter) CheckFriend(operationID string, args ...any) { 5 | f.call(operationID, f.userForSDK.Friend().CheckFriend, args...) 6 | } 7 | 8 | // GetSpecifiedFriendsInfo gets the information of specific friends. 9 | func (f *FuncRouter) GetSpecifiedFriendsInfo(operationID string, args ...any) { 10 | f.call(operationID, f.userForSDK.Friend().GetSpecifiedFriendsInfo, args...) 11 | } 12 | 13 | // GetFriendList retrieves the user's friend list. 14 | func (f *FuncRouter) GetFriendList(operationID string) { 15 | f.call(operationID, f.userForSDK.Friend().GetFriendList) 16 | } 17 | 18 | // GetFriendListPage retrieves a paginated friend list. 19 | func (f *FuncRouter) GetFriendListPage(operationID string, args ...any) { 20 | f.call(operationID, f.userForSDK.Friend().GetFriendListPage, args...) 21 | } 22 | 23 | // SearchFriends searches for friends based on given criteria. 24 | func (f *FuncRouter) SearchFriends(operationID string, args ...any) { 25 | f.call(operationID, f.userForSDK.Friend().SearchFriends, args...) 26 | } 27 | 28 | // AddFriend sends a friend request to another user. 29 | func (f *FuncRouter) AddFriend(operationID string, args ...any) { 30 | f.call(operationID, f.userForSDK.Friend().AddFriend, args...) 31 | } 32 | 33 | // SetFriendRemark sets a remark for a friend. 34 | func (f *FuncRouter) SetFriendRemark(operationID string, args ...any) { 35 | f.call(operationID, f.userForSDK.Friend().SetFriendRemark, args...) 36 | } 37 | 38 | // PinFriends pins friends to the top of the friend list. 39 | func (f *FuncRouter) PinFriends(operationID string, args ...any) { 40 | f.call(operationID, f.userForSDK.Friend().PinFriends, args...) 41 | } 42 | 43 | // DeleteFriend removes a user from the friend list. 44 | func (f *FuncRouter) DeleteFriend(operationID string, args ...any) { 45 | f.call(operationID, f.userForSDK.Friend().DeleteFriend, args...) 46 | } 47 | 48 | // GetFriendApplicationListAsRecipient retrieves friend requests received. 49 | func (f *FuncRouter) GetFriendApplicationListAsRecipient(operationID string) { 50 | f.call(operationID, f.userForSDK.Friend().GetFriendApplicationListAsRecipient) 51 | } 52 | 53 | // GetFriendApplicationListAsApplicant retrieves friend requests sent. 54 | func (f *FuncRouter) GetFriendApplicationListAsApplicant(operationID string) { 55 | f.call(operationID, f.userForSDK.Friend().GetFriendApplicationListAsApplicant) 56 | } 57 | 58 | // AcceptFriendApplication accepts a friend request. 59 | func (f *FuncRouter) AcceptFriendApplication(operationID string, args ...any) { 60 | f.call(operationID, f.userForSDK.Friend().AcceptFriendApplication, args...) 61 | } 62 | 63 | // RefuseFriendApplication declines a friend request. 64 | func (f *FuncRouter) RefuseFriendApplication(operationID string, args ...any) { 65 | f.call(operationID, f.userForSDK.Friend().RefuseFriendApplication, args...) 66 | } 67 | 68 | // AddBlack adds a user to the blacklist. 69 | func (f *FuncRouter) AddBlack(operationID string, args ...any) { 70 | f.call(operationID, f.userForSDK.Friend().AddBlack, args...) 71 | } 72 | 73 | // GetBlackList retrieves the blacklist. 74 | func (f *FuncRouter) GetBlackList(operationID string) { 75 | f.call(operationID, f.userForSDK.Friend().GetBlackList) 76 | } 77 | 78 | // RemoveBlack removes a user from the blacklist. 79 | func (f *FuncRouter) RemoveBlack(operationID string, args ...any) { 80 | f.call(operationID, f.userForSDK.Friend().RemoveBlack, args...) 81 | } 82 | 83 | // SetFriendsEx sets the friend ex info. 84 | func (f *FuncRouter) SetFriendsEx(operationID string, args ...any) { 85 | f.call(operationID, f.userForSDK.Friend().SetFriendsEx, args...) 86 | } 87 | -------------------------------------------------------------------------------- /pkg/core_func/ws_group.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | // CreateGroup creates a new group with specified settings. 4 | func (f *FuncRouter) CreateGroup(operationID string, args ...any) { 5 | f.call(operationID, f.userForSDK.Group().CreateGroup, args...) 6 | } 7 | 8 | // JoinGroup sends a request to join a group. 9 | func (f *FuncRouter) JoinGroup(operationID string, args ...any) { 10 | f.call(operationID, f.userForSDK.Group().JoinGroup, args...) 11 | } 12 | 13 | // QuitGroup leaves a group. 14 | func (f *FuncRouter) QuitGroup(operationID string, args ...any) { 15 | f.call(operationID, f.userForSDK.Group().QuitGroup, args...) 16 | } 17 | 18 | // DismissGroup disbands a group. 19 | func (f *FuncRouter) DismissGroup(operationID string, args ...any) { 20 | f.call(operationID, f.userForSDK.Group().DismissGroup, args...) 21 | } 22 | 23 | // ChangeGroupMute toggles the mute status of the group. 24 | func (f *FuncRouter) ChangeGroupMute(operationID string, args ...any) { 25 | f.call(operationID, f.userForSDK.Group().ChangeGroupMute, args...) 26 | } 27 | 28 | // ChangeGroupMemberMute toggles the mute status of a group member. 29 | func (f *FuncRouter) ChangeGroupMemberMute(operationID string, args ...any) { 30 | f.call(operationID, f.userForSDK.Group().ChangeGroupMemberMute, args...) 31 | } 32 | 33 | // SetGroupMemberRoleLevel changes the role or level of a group member. 34 | func (f *FuncRouter) SetGroupMemberRoleLevel(operationID string, args ...any) { 35 | f.call(operationID, f.userForSDK.Group().SetGroupMemberRoleLevel, args...) 36 | } 37 | 38 | // SetGroupMemberInfo updates information for a group member. 39 | func (f *FuncRouter) SetGroupMemberInfo(operationID string, args ...any) { 40 | f.call(operationID, f.userForSDK.Group().SetGroupMemberInfo, args...) 41 | } 42 | 43 | // GetJoinedGroupList retrieves the list of groups a user has joined. 44 | func (f *FuncRouter) GetJoinedGroupList(operationID string) { 45 | f.call(operationID, f.userForSDK.Group().GetJoinedGroupList) 46 | } 47 | 48 | // GetSpecifiedGroupsInfo gets information of specified groups. 49 | func (f *FuncRouter) GetSpecifiedGroupsInfo(operationID string, args ...any) { 50 | f.call(operationID, f.userForSDK.Group().GetSpecifiedGroupsInfo, args...) 51 | } 52 | 53 | // SearchGroups searches for groups based on criteria. 54 | func (f *FuncRouter) SearchGroups(operationID string, args ...any) { 55 | f.call(operationID, f.userForSDK.Group().SearchGroups, args...) 56 | } 57 | 58 | // SetGroupInfo updates group settings or information. 59 | func (f *FuncRouter) SetGroupInfo(operationID string, args ...any) { 60 | f.call(operationID, f.userForSDK.Group().SetGroupInfo, args...) 61 | } 62 | 63 | // SetGroupVerification sets the group's join request verification method. 64 | func (f *FuncRouter) SetGroupVerification(operationID string, args ...any) { 65 | f.call(operationID, f.userForSDK.Group().SetGroupVerification, args...) 66 | } 67 | 68 | // SetGroupLookMemberInfo toggles whether to allow group members to view each other's information. 69 | func (f *FuncRouter) SetGroupLookMemberInfo(operationID string, args ...any) { 70 | f.call(operationID, f.userForSDK.Group().SetGroupLookMemberInfo, args...) 71 | } 72 | 73 | // SetGroupApplyMemberFriend configures settings related to adding group members as friends. 74 | func (f *FuncRouter) SetGroupApplyMemberFriend(operationID string, args ...any) { 75 | f.call(operationID, f.userForSDK.Group().SetGroupApplyMemberFriend, args...) 76 | } 77 | 78 | // GetGroupMemberList retrieves the member list of a group. 79 | func (f *FuncRouter) GetGroupMemberList(operationID string, args ...any) { 80 | f.call(operationID, f.userForSDK.Group().GetGroupMemberList, args...) 81 | } 82 | 83 | // GetGroupMemberOwnerAndAdmin gets the owner and admin list of the group. 84 | func (f *FuncRouter) GetGroupMemberOwnerAndAdmin(operationID string, args ...any) { 85 | f.call(operationID, f.userForSDK.Group().GetGroupMemberOwnerAndAdmin, args...) 86 | } 87 | 88 | // GetGroupMemberListByJoinTimeFilter gets the group member list filtered by join time. 89 | func (f *FuncRouter) GetGroupMemberListByJoinTimeFilter(operationID string, args ...any) { 90 | f.call(operationID, f.userForSDK.Group().GetGroupMemberListByJoinTimeFilter, args...) 91 | } 92 | 93 | // GetSpecifiedGroupMembersInfo gets information for specified group members. 94 | func (f *FuncRouter) GetSpecifiedGroupMembersInfo(operationID string, args ...any) { 95 | f.call(operationID, f.userForSDK.Group().GetSpecifiedGroupMembersInfo, args...) 96 | } 97 | 98 | // KickGroupMember removes a specific member from the group. 99 | func (f *FuncRouter) KickGroupMember(operationID string, args ...any) { 100 | f.call(operationID, f.userForSDK.Group().KickGroupMember, args...) 101 | } 102 | 103 | // TransferGroupOwner changes the ownership of the group to another member. 104 | func (f *FuncRouter) TransferGroupOwner(operationID string, args ...any) { 105 | f.call(operationID, f.userForSDK.Group().TransferGroupOwner, args...) 106 | } 107 | 108 | // InviteUserToGroup sends an invitation to a user to join the group. 109 | func (f *FuncRouter) InviteUserToGroup(operationID string, args ...any) { 110 | f.call(operationID, f.userForSDK.Group().InviteUserToGroup, args...) 111 | } 112 | 113 | // GetGroupApplicationListAsRecipient retrieves the list of join requests received by the group. 114 | func (f *FuncRouter) GetGroupApplicationListAsRecipient(operationID string) { 115 | f.call(operationID, f.userForSDK.Group().GetGroupApplicationListAsRecipient) 116 | } 117 | 118 | // GetGroupApplicationListAsApplicant retrieves the list of join requests sent by the user. 119 | func (f *FuncRouter) GetGroupApplicationListAsApplicant(operationID string) { 120 | f.call(operationID, f.userForSDK.Group().GetGroupApplicationListAsApplicant) 121 | } 122 | 123 | // AcceptGroupApplication approves a join request to the group. 124 | func (f *FuncRouter) AcceptGroupApplication(operationID string, args ...any) { 125 | f.call(operationID, f.userForSDK.Group().AcceptGroupApplication, args...) 126 | } 127 | 128 | // RefuseGroupApplication denies a join request to the group. 129 | func (f *FuncRouter) RefuseGroupApplication(operationID string, args ...any) { 130 | f.call(operationID, f.userForSDK.Group().RefuseGroupApplication, args...) 131 | } 132 | 133 | // SetGroupMemberNickname sets or changes a group member's nickname. 134 | func (f *FuncRouter) SetGroupMemberNickname(operationID string, args ...any) { 135 | f.call(operationID, f.userForSDK.Group().SetGroupMemberNickname, args...) 136 | } 137 | 138 | // SearchGroupMembers looks for members in the group matching certain criteria. 139 | func (f *FuncRouter) SearchGroupMembers(operationID string, args ...any) { 140 | f.call(operationID, f.userForSDK.Group().SearchGroupMembers, args...) 141 | } 142 | 143 | // IsJoinGroup checks whether the user is a member of the specified group. 144 | func (f *FuncRouter) IsJoinGroup(operationID string, args ...any) { 145 | f.call(operationID, f.userForSDK.Group().IsJoinGroup, args...) 146 | } 147 | -------------------------------------------------------------------------------- /pkg/core_func/ws_handler.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk_callback" 8 | "reflect" 9 | "runtime" 10 | "runtime/debug" 11 | "strings" 12 | "time" 13 | 14 | "github.com/OpenIMSDK/tools/log" 15 | "github.com/openimsdk/openim-sdk-core/v3/open_im_sdk" 16 | "github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext" 17 | "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs" 18 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 19 | ) 20 | 21 | const ( 22 | Success = "OnSuccess" 23 | Failed = "OnError" 24 | ) 25 | 26 | type EventData struct { 27 | Event string `json:"event"` 28 | ErrCode int32 `json:"errCode"` 29 | ErrMsg string `json:"errMsg"` 30 | Data string `json:"data"` 31 | OperationID string `json:"operationID"` 32 | } 33 | 34 | type FuncRouter struct { 35 | userForSDK *open_im_sdk.LoginMgr 36 | respMessage *RespMessage 37 | sessionId string 38 | } 39 | 40 | // NewFuncRouter creates a new instance of FuncRouter with the provided session id and channel for event data. 41 | func NewFuncRouter(respMessagesChan chan *EventData, sessionId string) *FuncRouter { 42 | return &FuncRouter{respMessage: NewRespMessage(respMessagesChan), 43 | userForSDK: new(open_im_sdk.LoginMgr), sessionId: sessionId} 44 | } 45 | 46 | // call is an asynchronous wrapper that invokes SDK functions and handles their responses. 47 | func (f *FuncRouter) call(operationID string, fn any, args ...any) { 48 | go func() { 49 | funcPtr := reflect.ValueOf(fn).Pointer() 50 | funcName := runtime.FuncForPC(funcPtr).Name() 51 | parts := strings.Split(funcName, ".") 52 | var trimFuncName string 53 | if trimFuncNameList := strings.Split(parts[len(parts)-1], "-"); len(trimFuncNameList) == 0 { 54 | f.respMessage.sendOnErrorResp(operationID, "FuncError", errors.New("call function trimFuncNameList is empty")) 55 | return 56 | } else { 57 | trimFuncName = trimFuncNameList[0] 58 | } 59 | res, err := f.call_(operationID, fn, funcName, args...) 60 | if err != nil { 61 | f.respMessage.sendOnErrorResp(operationID, trimFuncName, err) 62 | return 63 | } 64 | data, err := json.Marshal(res) 65 | if err != nil { 66 | f.respMessage.sendOnErrorResp(operationID, trimFuncName, err) 67 | return 68 | } else { 69 | f.respMessage.sendOnSuccessResp(operationID, trimFuncName, string(data)) 70 | } 71 | }() 72 | } 73 | 74 | // CheckResourceLoad checks the SDK is resource load status. 75 | func CheckResourceLoad(uSDK *open_im_sdk.LoginMgr, funcName string) error { 76 | if uSDK == nil { 77 | return utils.Wrap(errors.New("CheckResourceLoad failed uSDK == nil "), "") 78 | } 79 | if funcName == "" { 80 | return nil 81 | } 82 | parts := strings.Split(funcName, ".") 83 | if parts[len(parts)-1] == "Login-fm" { 84 | return nil 85 | } 86 | if uSDK.Friend() == nil || uSDK.User() == nil || uSDK.Group() == nil || uSDK.Conversation() == nil || 87 | uSDK.Full() == nil { 88 | return utils.Wrap(errors.New("CheckResourceLoad failed, resource nil "), "") 89 | } 90 | return nil 91 | } 92 | 93 | // call_ is the internal function that actually invokes the SDK functions. 94 | func (f *FuncRouter) call_(operationID string, fn any, funcName string, args ...any) (res any, err error) { 95 | defer func() { 96 | if r := recover(); r != nil { 97 | fmt.Printf("panic: %+v\n%s", r, debug.Stack()) 98 | err = fmt.Errorf("call panic: %+v", r) 99 | } 100 | }() 101 | if operationID == "" { 102 | return nil, sdkerrs.ErrArgs.Wrap("call function operationID is empty") 103 | } 104 | if err := CheckResourceLoad(f.userForSDK, funcName); err != nil { 105 | return nil, sdkerrs.ErrResourceLoad.Wrap("not load resource") 106 | } 107 | 108 | ctx := ccontext.WithOperationID(f.userForSDK.BaseCtx(), operationID) 109 | 110 | fnv := reflect.ValueOf(fn) 111 | if fnv.Kind() != reflect.Func { 112 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("call function fn is not function, is %T", fn)) 113 | } 114 | fnt := fnv.Type() 115 | nin := fnt.NumIn() 116 | if len(args)+1 != nin { 117 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("go code error: fn in args num is not match")) 118 | } 119 | t := time.Now() 120 | log.ZInfo(ctx, "input req", "function name", funcName, "args", args) 121 | ins := make([]reflect.Value, 0, nin) 122 | ins = append(ins, reflect.ValueOf(ctx)) 123 | for i := 0; i < len(args); i++ { 124 | inFnField := fnt.In(i + 1) 125 | arg := reflect.TypeOf(args[i]) 126 | if arg.String() == inFnField.String() || inFnField.Kind() == reflect.Interface { 127 | ins = append(ins, reflect.ValueOf(args[i])) 128 | continue 129 | } 130 | //convert float64 to int when javascript call with number,because javascript only have double 131 | //precision floating-point format 132 | if arg.String() == "float64" && isInteger(inFnField) { 133 | ins = append(ins, reflect.ValueOf(convert(args[i].(float64), inFnField))) 134 | continue 135 | } 136 | if arg.Kind() == reflect.String { // json 137 | var ptr int 138 | for inFnField.Kind() == reflect.Ptr { 139 | inFnField = inFnField.Elem() 140 | ptr++ 141 | } 142 | switch inFnField.Kind() { 143 | case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: 144 | v := reflect.New(inFnField) 145 | if err := json.Unmarshal([]byte(args[i].(string)), v.Interface()); err != nil { 146 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("go call json.Unmarshal error: %s", 147 | err)) 148 | } 149 | if ptr == 0 { 150 | v = v.Elem() 151 | } else if ptr != 1 { 152 | for i := ptr - 1; i > 0; i-- { 153 | temp := reflect.New(v.Type()) 154 | temp.Elem().Set(v) 155 | v = temp 156 | } 157 | } 158 | ins = append(ins, v) 159 | continue 160 | } 161 | } 162 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("go code error: fn in args type is not match")) 163 | } 164 | outs := fnv.Call(ins) 165 | if len(outs) == 0 { 166 | return "", nil 167 | } 168 | if fnt.Out(len(outs) - 1).Implements(reflect.ValueOf(new(error)).Elem().Type()) { 169 | if errValueOf := outs[len(outs)-1]; !errValueOf.IsNil() { 170 | log.ZError(ctx, "fn call error", errValueOf.Interface().(error), "function name", 171 | funcName, "cost time", time.Since(t)) 172 | return nil, errValueOf.Interface().(error) 173 | } 174 | if len(outs) == 1 { 175 | return "", nil 176 | } 177 | outs = outs[:len(outs)-1] 178 | } 179 | for i := 0; i < len(outs); i++ { 180 | out := outs[i] 181 | switch out.Kind() { 182 | case reflect.Map: 183 | if out.IsNil() { 184 | outs[i] = reflect.MakeMap(out.Type()) 185 | } 186 | case reflect.Slice: 187 | if out.IsNil() { 188 | outs[i] = reflect.MakeSlice(out.Type(), 0, 0) 189 | } 190 | } 191 | } 192 | if len(outs) == 1 { 193 | log.ZInfo(ctx, "output resp", "function name", funcName, "resp", outs[0].Interface(), 194 | "cost time", time.Since(t)) 195 | return outs[0].Interface(), nil 196 | } 197 | val := make([]any, 0, len(outs)) 198 | for i := range outs { 199 | val = append(val, outs[i].Interface()) 200 | } 201 | log.ZInfo(ctx, "output resp", "function name", funcName, "resp", val, "cost time", time.Since(t)) 202 | return val, nil 203 | } 204 | func isInteger(arg reflect.Type) bool { 205 | switch arg.Kind() { 206 | case reflect.Int: 207 | fallthrough 208 | case reflect.Int8: 209 | fallthrough 210 | case reflect.Int16: 211 | fallthrough 212 | case reflect.Int32: 213 | fallthrough 214 | case reflect.Int64: 215 | return true 216 | default: 217 | return false 218 | 219 | } 220 | } 221 | func convert(arg float64, p reflect.Type) any { 222 | switch p.Kind() { 223 | case reflect.Int: 224 | return int(arg) 225 | case reflect.Int8: 226 | return int8(arg) 227 | case reflect.Int16: 228 | return int16(arg) 229 | case reflect.Int32: 230 | return int32(arg) 231 | case reflect.Int64: 232 | return int64(arg) 233 | default: 234 | return arg 235 | 236 | } 237 | } 238 | func (f *FuncRouter) messageCall(operationID string, fn any, args ...any) { 239 | go func() { 240 | funcPtr := reflect.ValueOf(fn).Pointer() 241 | funcName := runtime.FuncForPC(funcPtr).Name() 242 | parts := strings.Split(funcName, ".") 243 | var trimFuncName string 244 | if trimFuncNameList := strings.Split(parts[len(parts)-1], "-"); len(trimFuncNameList) == 0 { 245 | f.respMessage.sendOnErrorResp(operationID, "FuncError", 246 | errors.New("call function trimFuncNameList is empty")) 247 | return 248 | } else { 249 | trimFuncName = trimFuncNameList[0] 250 | } 251 | sendMessageCallback := NewSendMessageCallback(trimFuncName, f.respMessage) 252 | res, err := f.messageCall_(sendMessageCallback, operationID, fn, funcName, args...) 253 | if err != nil { 254 | f.respMessage.sendOnErrorResp(operationID, trimFuncName, err) 255 | return 256 | } 257 | data, err := json.Marshal(res) 258 | if err != nil { 259 | f.respMessage.sendOnErrorResp(operationID, trimFuncName, err) 260 | return 261 | } else { 262 | f.respMessage.sendOnSuccessResp(operationID, trimFuncName, string(data)) 263 | } 264 | }() 265 | } 266 | func (f *FuncRouter) messageCall_(callback open_im_sdk_callback.SendMsgCallBack, operationID string, 267 | fn any, funcName string, args ...any) (res any, err error) { 268 | 269 | defer func() { 270 | if r := recover(); r != nil { 271 | fmt.Printf("panic: %+v\n%s", r, debug.Stack()) 272 | err = fmt.Errorf("call panic: %+v", r) 273 | } 274 | }() 275 | if operationID == "" { 276 | return nil, sdkerrs.ErrArgs.Wrap("call function operationID is empty") 277 | } 278 | if err := CheckResourceLoad(f.userForSDK, ""); err != nil { 279 | return nil, sdkerrs.ErrResourceLoad.Wrap("not load resource") 280 | } 281 | 282 | ctx := ccontext.WithOperationID(f.userForSDK.BaseCtx(), operationID) 283 | ctx = ccontext.WithSendMessageCallback(ctx, callback) 284 | 285 | fnv := reflect.ValueOf(fn) 286 | if fnv.Kind() != reflect.Func { 287 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("call function fn is not function, is %T", fn)) 288 | } 289 | log.ZInfo(ctx, "input req", "function name", funcName, "args", args) 290 | fnt := fnv.Type() 291 | nin := fnt.NumIn() 292 | if len(args)+1 != nin { 293 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("go code error: fn in args num is not match")) 294 | } 295 | t := time.Now() 296 | ins := make([]reflect.Value, 0, nin) 297 | ins = append(ins, reflect.ValueOf(ctx)) 298 | for i := 0; i < len(args); i++ { 299 | inFnField := fnt.In(i + 1) 300 | arg := reflect.TypeOf(args[i]) 301 | if arg.String() == inFnField.String() || inFnField.Kind() == reflect.Interface { 302 | ins = append(ins, reflect.ValueOf(args[i])) 303 | continue 304 | } 305 | if arg.String() == "float64" && isInteger(inFnField) { 306 | ins = append(ins, reflect.ValueOf(convert(args[i].(float64), inFnField))) 307 | continue 308 | } 309 | if arg.Kind() == reflect.String { // json 310 | var ptr int 311 | for inFnField.Kind() == reflect.Ptr { 312 | inFnField = inFnField.Elem() 313 | ptr++ 314 | } 315 | switch inFnField.Kind() { 316 | case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: 317 | v := reflect.New(inFnField) 318 | if err := json.Unmarshal([]byte(args[i].(string)), v.Interface()); err != nil { 319 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("go call json.Unmarshal error: %s", 320 | err)) 321 | } 322 | if ptr == 0 { 323 | v = v.Elem() 324 | } else if ptr != 1 { 325 | for i := ptr - 1; i > 0; i-- { 326 | temp := reflect.New(v.Type()) 327 | temp.Elem().Set(v) 328 | v = temp 329 | } 330 | } 331 | ins = append(ins, v) 332 | continue 333 | } 334 | } 335 | return nil, sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("go code error: fn in args type is not match")) 336 | } 337 | outs := fnv.Call(ins) 338 | if len(outs) == 0 { 339 | return "", nil 340 | } 341 | if fnt.Out(len(outs) - 1).Implements(reflect.ValueOf(new(error)).Elem().Type()) { 342 | if errValueOf := outs[len(outs)-1]; !errValueOf.IsNil() { 343 | log.ZError(ctx, "fn call error", errValueOf.Interface().(error), "function name", 344 | funcName, "cost time", time.Since(t)) 345 | return nil, errValueOf.Interface().(error) 346 | } 347 | if len(outs) == 1 { 348 | return "", nil 349 | } 350 | outs = outs[:len(outs)-1] 351 | } 352 | for i := 0; i < len(outs); i++ { 353 | out := outs[i] 354 | switch out.Kind() { 355 | case reflect.Map: 356 | if out.IsNil() { 357 | outs[i] = reflect.MakeMap(out.Type()) 358 | } 359 | case reflect.Slice: 360 | if out.IsNil() { 361 | outs[i] = reflect.MakeSlice(out.Type(), 0, 0) 362 | } 363 | } 364 | } 365 | if len(outs) == 1 { 366 | log.ZInfo(ctx, "output resp", "function name", funcName, "resp", outs[0].Interface(), 367 | "cost time", time.Since(t)) 368 | return outs[0].Interface(), nil 369 | } 370 | val := make([]any, 0, len(outs)) 371 | for i := range outs { 372 | val = append(val, outs[i].Interface()) 373 | } 374 | log.ZInfo(ctx, "output resp", "function name", funcName, "resp", val, "cost time", time.Since(t)) 375 | return val, nil 376 | } 377 | -------------------------------------------------------------------------------- /pkg/core_func/ws_init_login.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/OpenIMSDK/tools/log" 8 | "github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs" 9 | "github.com/openimsdk/openim-sdk-core/v3/sdk_struct" 10 | ) 11 | 12 | var Config sdk_struct.IMConfig 13 | 14 | const ( 15 | rotateCount uint = 0 16 | rotationTime uint = 24 17 | ) 18 | 19 | // InitSDK initializes the SDK with the given operation ID and platform ID. 20 | func (f *FuncRouter) InitSDK(operationID, platformID string) { 21 | fmt.Println("InitSDK", "data=", platformID, operationID) 22 | callback := NewConnCallback(f.respMessage) 23 | j, err := strconv.ParseInt(platformID, 10, 64) 24 | if err != nil { 25 | f.respMessage.sendOnErrorResp(operationID, "InitSDK", err) 26 | return 27 | } 28 | config := sdk_struct.IMConfig{ 29 | PlatformID: int32(j), 30 | ApiAddr: Config.ApiAddr, 31 | WsAddr: Config.WsAddr, 32 | DataDir: Config.DataDir, 33 | LogLevel: Config.LogLevel, 34 | IsLogStandardOutput: Config.IsLogStandardOutput, 35 | LogFilePath: Config.LogFilePath, 36 | IsExternalExtensions: Config.IsExternalExtensions, 37 | } 38 | if err := log.InitFromConfig("open-im-sdk-core", "", 39 | int(config.LogLevel), config.IsLogStandardOutput, false, config.LogFilePath, 40 | rotateCount, rotationTime); err != nil { 41 | f.respMessage.sendOnErrorResp(operationID, "InitSDK", err) 42 | return 43 | } 44 | if f.userForSDK.InitSDK(config, callback) { 45 | f.respMessage.sendOnSuccessResp(operationID, "InitSDK", "") 46 | } else { 47 | f.respMessage.sendOnErrorResp(operationID, "InitSDK", sdkerrs.ErrArgs) 48 | } 49 | } 50 | 51 | // UnInitSDK uninitializes the SDK. 52 | func (f *FuncRouter) UnInitSDK(operationID string) { 53 | if f.userForSDK == nil { 54 | fmt.Println(operationID, "UserForSDK is nil,") 55 | return 56 | } 57 | f.userForSDK.UnInitSDK() 58 | f.userForSDK = nil 59 | 60 | } 61 | 62 | // Login logs in a user using the provided arguments. 63 | func (f *FuncRouter) Login(operationID string, args ...any) { 64 | f.setAllListener() 65 | f.call(operationID, f.userForSDK.Login, args...) 66 | } 67 | 68 | // Logout logs out the current user. 69 | func (f *FuncRouter) Logout(operationID string, args ...any) { 70 | f.call(operationID, f.userForSDK.Logout, args...) 71 | } 72 | 73 | // GetLoginUserID returns the logged-in user's ID. 74 | func (f *FuncRouter) GetLoginUserID() string { 75 | if f.userForSDK == nil { 76 | return "" 77 | } 78 | return f.userForSDK.GetLoginUserID() 79 | } 80 | 81 | // SetAppBackgroundStatus updates the app's background status. 82 | func (f *FuncRouter) SetAppBackgroundStatus(operationID string, args ...any) { 83 | f.call(operationID, f.userForSDK.SetAppBackgroundStatus, args...) 84 | } 85 | 86 | // NetworkStatusChanged handles the change in network status. 87 | func (f *FuncRouter) NetworkStatusChanged(operationID string, args ...any) { 88 | f.call(operationID, f.userForSDK.NetworkStatusChanged, args...) 89 | } 90 | 91 | // GetLoginStatus retrieves the current login status of the user. 92 | func (f *FuncRouter) GetLoginStatus(operationID string, args ...any) { 93 | f.call(operationID, f.userForSDK.GetLoginStatus, args...) 94 | } 95 | 96 | // setAllListener sets all listeners for the SDK to handle various events. 97 | func (f *FuncRouter) setAllListener() { 98 | f.userForSDK.SetConversationListener(NewConversationCallback(f.respMessage)) 99 | f.userForSDK.SetGroupListener(NewGroupCallback(f.respMessage)) 100 | f.userForSDK.SetUserListener(NewUserCallback(f.respMessage)) 101 | f.userForSDK.SetAdvancedMsgListener(NewAdvancedMsgCallback(f.respMessage)) 102 | f.userForSDK.SetFriendListener(NewFriendCallback(f.respMessage)) 103 | f.userForSDK.SetBatchMsgListener(NewBatchMessageCallback(f.respMessage)) 104 | 105 | } 106 | -------------------------------------------------------------------------------- /pkg/core_func/ws_init_login_test.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "testing/quick" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type TestServer struct { 12 | fu *FuncRouter 13 | operationID string 14 | sessionID string 15 | platformID string 16 | } 17 | 18 | const ( 19 | operationID string = "123456" 20 | sessionID string = "111" 21 | platformID string = "1" 22 | ) 23 | 24 | func NewTestServer() *TestServer { 25 | ev := make(chan *EventData, 10) 26 | return &TestServer{ 27 | operationID: operationID, 28 | sessionID: sessionID, 29 | platformID: platformID, 30 | fu: NewFuncRouter(ev, sessionID), 31 | } 32 | } 33 | 34 | func TestInitSDK(t *testing.T) { 35 | te := NewTestServer() 36 | fn := func() bool { 37 | te.fu.InitSDK(te.operationID, te.platformID) 38 | msg, err := <-te.fu.respMessage.respMessagesChan 39 | 40 | ret := &EventData{ 41 | OperationID: te.operationID, 42 | Event: "InitSDK", 43 | Data: "", 44 | } 45 | assert.Equal(t, true, err) 46 | assert.Equal(t, ret, msg) 47 | return true 48 | } 49 | err := quick.Check(fn, nil) 50 | assert.Nil(t, err) 51 | } 52 | 53 | func TestLogin(t *testing.T) { 54 | te := NewTestServer() 55 | fn := func() bool { 56 | te.fu.Login(te.sessionID, te.operationID) 57 | msg, err := <-te.fu.respMessage.respMessagesChan 58 | 59 | ret := &EventData{ 60 | OperationID: te.operationID, 61 | Event: "Login", 62 | Data: "", 63 | } 64 | fmt.Printf("ret,msg:%v", msg) 65 | assert.Equal(t, true, err) 66 | assert.Equal(t, ret, msg) 67 | return true 68 | } 69 | err := quick.Check(fn, nil) 70 | assert.Nil(t, err) 71 | } 72 | 73 | //func (f *FuncRouter) Login(operationID string, args ...any) { 74 | // f.setAllListener() 75 | // fmt.Println(operationID, "Login") 76 | // f.call(operationID, f.userForSDK.Login, args...) 77 | //} 78 | -------------------------------------------------------------------------------- /pkg/core_func/ws_third.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | // UpdateFcmToken updates the FCM (Firebase Cloud Messaging) token for the current device session. 4 | func (f *FuncRouter) UpdateFcmToken(operationID string, args ...any) { 5 | f.call(operationID, f.userForSDK.Third().UpdateFcmToken, args...) 6 | } 7 | 8 | // SetAppBadge sets the badge count for the application's icon, typically reflecting the number of unread notifications or messages. 9 | func (f *FuncRouter) SetAppBadge(operationID string, args ...any) { 10 | f.call(operationID, f.userForSDK.Third().SetAppBadge, args...) 11 | } 12 | 13 | // UploadLogs initiates the upload of application logs to a remote server for diagnostic purposes. 14 | func (f *FuncRouter) UploadLogs(operationID string, args ...any) { 15 | f.call(operationID, f.userForSDK.Third().UploadLogs, args...) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/core_func/ws_user.go: -------------------------------------------------------------------------------- 1 | package core_func 2 | 3 | // GetUsersInfo retrieves information for the specified users. 4 | func (f *FuncRouter) GetUsersInfo(operationID string, args ...any) { 5 | f.call(operationID, f.userForSDK.Full().GetUsersInfo, args...) 6 | } 7 | 8 | // GetUsersInfoWithCache retrieves information for the specified users and uses local cache if available. 9 | func (f *FuncRouter) GetUsersInfoWithCache(operationID string, args ...any) { 10 | f.call(operationID, f.userForSDK.Full().GetUsersInfoWithCache, args...) 11 | } 12 | 13 | // GetUsersInfoFromSrv retrieves user information directly from the server, bypassing any cache. 14 | func (f *FuncRouter) GetUsersInfoFromSrv(operationID string, args ...any) { 15 | f.call(operationID, f.userForSDK.User().GetUsersInfo, args...) 16 | } 17 | 18 | // SetSelfInfo updates the current user's information. 19 | func (f *FuncRouter) SetSelfInfo(operationID string, args ...any) { 20 | f.call(operationID, f.userForSDK.User().SetSelfInfo, args...) 21 | } 22 | 23 | // SetSelfInfoEx updates the current user's information. 24 | func (f *FuncRouter) SetSelfInfoEx(operationID string, args ...any) { 25 | f.call(operationID, f.userForSDK.User().SetSelfInfo, args...) 26 | } 27 | 28 | // SetGlobalRecvMessageOpt sets the global option for receiving messages for the user. 29 | func (f *FuncRouter) SetGlobalRecvMessageOpt(operationID string, args ...any) { 30 | f.call(operationID, f.userForSDK.User().SetGlobalRecvMessageOpt, args...) 31 | } 32 | 33 | // GetSelfUserInfo retrieves the current user's information. 34 | func (f *FuncRouter) GetSelfUserInfo(operationID string) { 35 | f.call(operationID, f.userForSDK.User().GetSelfUserInfo) 36 | } 37 | 38 | // UpdateMsgSenderInfo updates the information of the message sender. 39 | func (f *FuncRouter) UpdateMsgSenderInfo(operationID string, args ...any) { 40 | f.call(operationID, f.userForSDK.User().UpdateMsgSenderInfo, args...) 41 | } 42 | 43 | // SubscribeUsersStatus subscribes to the online status updates of the specified users. 44 | func (f *FuncRouter) SubscribeUsersStatus(operationID string, args ...any) { 45 | f.call(operationID, f.userForSDK.User().SubscribeUsersStatus, args...) 46 | } 47 | 48 | // UnsubscribeUsersStatus unsubscribes from the online status updates of the specified users. 49 | func (f *FuncRouter) UnsubscribeUsersStatus(operationID string, args ...any) { 50 | f.call(operationID, f.userForSDK.User().UnsubscribeUsersStatus, args...) 51 | } 52 | 53 | // GetSubscribeUsersStatus retrieves the subscription status of online status updates for the specified users. 54 | func (f *FuncRouter) GetSubscribeUsersStatus(operationID string) { 55 | f.call(operationID, f.userForSDK.User().GetSubscribeUsersStatus) 56 | } 57 | 58 | // GetUserStatus retrieves the current status of a user. 59 | func (f *FuncRouter) GetUserStatus(operationID string, args ...any) { 60 | f.call(operationID, f.userForSDK.User().GetUserStatus, args...) 61 | } 62 | -------------------------------------------------------------------------------- /pkg/gate/agent.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type Agent interface { 8 | WriteMsg(msg interface{}) 9 | LocalAddr() net.Addr 10 | RemoteAddr() net.Addr 11 | Close() 12 | Destroy() 13 | UserData() interface{} 14 | SetUserData(data interface{}) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/gate/gate.go: -------------------------------------------------------------------------------- 1 | package gate 2 | 3 | import ( 4 | common2 "github.com/openim-sigs/oimws/pkg/common" 5 | network2 "github.com/openim-sigs/oimws/pkg/network" 6 | "net" 7 | "reflect" 8 | "time" 9 | 10 | log "github.com/xuexihuang/new_log15" 11 | ) 12 | 13 | type Gate struct { 14 | MaxConnNum int 15 | PendingWriteNum int 16 | MaxMsgLen uint32 17 | Processor network2.Processor 18 | //AgentChanRPC *chanrpc.Server 19 | 20 | // websocket 21 | WSAddr string 22 | HTTPTimeout time.Duration 23 | CertFile string 24 | KeyFile string 25 | 26 | // tcp 27 | TCPAddr string 28 | LenMsgLen int 29 | 30 | //add by huanglin 31 | FunNewAgent func(Agent) 32 | FunCloseAgent func(Agent) 33 | FuncMsgRecv func(interface{}, Agent) 34 | } 35 | 36 | func NewGate(maxConnNum int, maxMsgLen uint32, processor network2.Processor, WSAddr string, 37 | HTTPTimeout time.Duration, writerChanLen int) *Gate { 38 | return &Gate{MaxConnNum: maxConnNum, MaxMsgLen: maxMsgLen, Processor: processor, WSAddr: WSAddr, 39 | HTTPTimeout: HTTPTimeout, PendingWriteNum: writerChanLen} 40 | } 41 | 42 | // SetFun sets the functions for handling new agents, closing agents, and receiving messages. 43 | func (gate *Gate) SetFun(Fun1 func(Agent), Fun2 func(Agent), Fun3 func(interface{}, Agent)) { 44 | gate.FunNewAgent = Fun1 45 | gate.FunCloseAgent = Fun2 46 | gate.FuncMsgRecv = Fun3 47 | } 48 | 49 | // Run starts the gate service and listens for incoming connections. 50 | func (gate *Gate) Run(closeSig chan bool) { 51 | var wsServer *network2.WSServer 52 | if gate.WSAddr != "" { 53 | wsServer = new(network2.WSServer) 54 | wsServer.Addr = gate.WSAddr 55 | wsServer.MaxConnNum = gate.MaxConnNum 56 | wsServer.PendingWriteNum = gate.PendingWriteNum 57 | wsServer.MaxMsgLen = gate.MaxMsgLen 58 | wsServer.HTTPTimeout = gate.HTTPTimeout 59 | wsServer.CertFile = gate.CertFile 60 | wsServer.KeyFile = gate.KeyFile 61 | wsServer.NewAgent = func(conn *network2.WSConn) network2.Agent { 62 | a := &agent{conn: conn, gate: gate} 63 | /*if gate.AgentChanRPC != nil { 64 | gate.AgentChanRPC.Go("NewAgent", a) 65 | }*/ 66 | ///////////////////////////////////////////////////// 67 | tagent := common2.TAgentUserData{SessionID: conn.SessionId, AppString: conn.AppURL, CookieVal: conn.CookieVal} 68 | a.SetUserData(&tagent) 69 | gate.FunNewAgent(a) 70 | return a 71 | } 72 | } 73 | /* 74 | var tcpServer *network.TCPServer 75 | if gate.TCPAddr != "" { 76 | tcpServer = new(network.TCPServer) 77 | tcpServer.Addr = gate.TCPAddr 78 | tcpServer.MaxConnNum = gate.MaxConnNum 79 | tcpServer.PendingWriteNum = gate.PendingWriteNum 80 | tcpServer.LenMsgLen = gate.LenMsgLen 81 | tcpServer.MaxMsgLen = gate.MaxMsgLen 82 | tcpServer.UsePacketMode = gate.Processor.UsePacketMode() 83 | tcpServer.NewAgent = func(conn *network.TCPConn) network.Agent { 84 | a := &agent{conn: conn, gate: gate} 85 | if gate.AgentChanRPC != nil { 86 | gate.AgentChanRPC.Go("NewAgent", a) 87 | } 88 | gate.FunNewAgent(a) 89 | return a 90 | } 91 | }*/ 92 | 93 | if wsServer != nil { 94 | wsServer.Start() 95 | } 96 | /*if tcpServer != nil { 97 | tcpServer.Start() 98 | }*/ 99 | <-closeSig 100 | if wsServer != nil { 101 | wsServer.Close() 102 | } 103 | /*if tcpServer != nil { 104 | tcpServer.Close() 105 | }*/ 106 | } 107 | 108 | func (gate *Gate) OnDestroy() {} 109 | 110 | type agent struct { 111 | conn network2.Conn 112 | gate *Gate 113 | userData interface{} 114 | } 115 | 116 | // Run processes incoming messages in a loop. 117 | func (a *agent) Run() { 118 | defer common2.TryRecoverAndDebugPrint() 119 | for { 120 | nType, data, err := a.conn.ReadMsg() 121 | if err != nil { 122 | //log.Debug("read message: %v", err) 123 | log.Info("read message error", "error", err) 124 | break 125 | } 126 | log.Debug("recve one ws msg ", "nType", nType) 127 | if a.gate.Processor != nil { 128 | msg, err := a.gate.Processor.UnmarshalMul(nType, data) 129 | if err != nil { 130 | //log.Debug("unmarshal message error: %v", err) 131 | log.Error("unmarshal message error", "err", err) 132 | break 133 | } 134 | a.gate.FuncMsgRecv(msg, a) 135 | /*err = a.gate.Processor.Route(msg, a) 136 | if err != nil { 137 | log.Debug("route message error: %v", err) 138 | break 139 | }*/ 140 | } 141 | } 142 | } 143 | 144 | // OnClose is called when the agent's connection is closed. 145 | func (a *agent) OnClose() { 146 | 147 | /*if a.gate.AgentChanRPC != nil { 148 | err := a.gate.AgentChanRPC.Call0("CloseAgent", a) 149 | if err != nil { 150 | log.Error("chanrpc error: %v", err) 151 | } 152 | }*/ 153 | a.gate.FunCloseAgent(a) 154 | } 155 | 156 | // WriteMsg sends a message to the client. 157 | func (a *agent) WriteMsg(msg interface{}) { 158 | if a.gate.Processor != nil { 159 | data, err := a.gate.Processor.Marshal(msg) 160 | if err != nil { 161 | //log.Error("marshal message %v error: %v", reflect.TypeOf(msg), err) 162 | log.Error("marshal message", "reflect.TypeOf(msg)", reflect.TypeOf(msg), "error", err) 163 | return 164 | } 165 | err = a.conn.WriteMsg(data) 166 | if err != nil { 167 | //log.Error("write message %v error: %v", reflect.TypeOf(msg), err) 168 | log.Error("write message error", "reflect.TypeOf(msg)", reflect.TypeOf(msg), "error", err) 169 | } 170 | } 171 | } 172 | 173 | func (a *agent) LocalAddr() net.Addr { 174 | return a.conn.LocalAddr() 175 | } 176 | 177 | func (a *agent) RemoteAddr() net.Addr { 178 | return a.conn.RemoteAddr() 179 | } 180 | 181 | func (a *agent) Close() { 182 | a.conn.Close() 183 | } 184 | 185 | func (a *agent) Destroy() { 186 | a.conn.Destroy() 187 | } 188 | 189 | func (a *agent) UserData() interface{} { 190 | return a.userData 191 | } 192 | 193 | func (a *agent) SetUserData(data interface{}) { 194 | a.userData = data 195 | } 196 | -------------------------------------------------------------------------------- /pkg/module/MActor.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | common2 "github.com/openim-sigs/oimws/pkg/common" 8 | "github.com/openim-sigs/oimws/pkg/core_func" 9 | "github.com/openim-sigs/oimws/pkg/gate" 10 | "net/url" 11 | "runtime" 12 | "sync" 13 | "sync/atomic" 14 | "time" 15 | 16 | log "github.com/xuexihuang/new_log15" 17 | ) 18 | 19 | const ( 20 | WsUserID = "sendID" 21 | OperationID = "operationID" 22 | PlatformID = "platformID" 23 | ) 24 | const ProtocolError = "Protocol Error" 25 | const DisconnectGCLimit = 100 26 | 27 | var disConnectNum atomic.Int64 28 | 29 | type ParamStru struct { 30 | UrlPath string 31 | Token string 32 | SessionId string 33 | UserId int64 34 | GroupId int64 35 | OrgId int64 36 | OrgName string 37 | } 38 | 39 | // GetUserID parses the URL to get the UserID parameter. 40 | func (p *ParamStru) GetUserID() string { 41 | u, err := url.Parse(p.UrlPath) 42 | if err != nil { 43 | return "" 44 | } 45 | return u.Query().Get(WsUserID) 46 | } 47 | 48 | // GetOperationID parses the URL to get the OperationID parameter. 49 | func (p *ParamStru) GetOperationID() string { 50 | u, err := url.Parse(p.UrlPath) 51 | if err != nil { 52 | return "" 53 | } 54 | return u.Query().Get(OperationID) 55 | } 56 | 57 | // GetPlatformID parses the URL to get the PlatformID parameter. 58 | func (p *ParamStru) GetPlatformID() string { 59 | u, err := url.Parse(p.UrlPath) 60 | if err != nil { 61 | return "" 62 | } 63 | return u.Query().Get(PlatformID) 64 | } 65 | 66 | type ResReleaseStru struct { 67 | BackSign chan bool 68 | } 69 | type MActorIm struct { 70 | //todo your module ojb values 71 | mJsCore *JsCore 72 | heartTickerSend *time.Ticker //用于心跳send 73 | param *ParamStru 74 | nChanLen int //接收数据网络缓存 75 | wg sync.WaitGroup 76 | a gate.Agent 77 | SessionId string 78 | closeChan chan bool //主动关闭协程的通道 79 | releaseResChan chan *ResReleaseStru 80 | ReceivMsgChan chan interface{} //接收网络层数据通道 81 | heartTicker *time.Ticker //用于心跳监测 82 | heartFlag bool //初始为false,收到心跳pack设置为true 83 | isclosing bool 84 | isReleasedJscore bool 85 | } 86 | 87 | // NewMActor creates a new actor instance. 88 | func NewMActor(a gate.Agent, sessionId string, appParam *ParamStru) (MActor, error) { 89 | ret := &MActorIm{param: appParam, a: a, SessionId: sessionId, releaseResChan: make(chan *ResReleaseStru, 1), closeChan: make(chan bool, 1), nChanLen: 10, ReceivMsgChan: make(chan interface{}, 10), isclosing: false, 90 | heartTicker: time.NewTicker(100 * time.Second), heartFlag: false, heartTickerSend: time.NewTicker(28 * time.Second), isReleasedJscore: false} 91 | /////////////////////////////////////// 92 | ret.mJsCore = NewJsCore(appParam, sessionId) //todo 93 | /////////////////////////////////////// 94 | go ret.run() 95 | return ret, nil 96 | } 97 | 98 | // run contains the main loop for the actor, handling various operations. 99 | func (actor *MActorIm) run() { 100 | actor.wg.Add(1) 101 | defer common2.TryRecoverAndDebugPrint() 102 | defer actor.wg.Done() 103 | for { 104 | select { 105 | case <-actor.heartTickerSend.C: //send the heart pack 106 | if actor.isclosing == true { 107 | continue 108 | } 109 | actor.sendHeart() 110 | case <-actor.closeChan: 111 | log.Info("收到退出信号", "sessionId", actor.SessionId) 112 | if !actor.isReleasedJscore { 113 | actor.mJsCore.Destroy() 114 | } 115 | if disConnectNum.Add(1) > DisconnectGCLimit { 116 | runtime.GC() 117 | disConnectNum.Store(0) 118 | } 119 | return 120 | case resChan := <-actor.releaseResChan: 121 | log.Info("收到释放资源通道消息") 122 | actor.mJsCore.Destroy() 123 | actor.a.Destroy() 124 | actor.isReleasedJscore = true 125 | resChan.BackSign <- true 126 | case recvData := <-actor.ReceivMsgChan: 127 | if actor.isclosing == true { 128 | continue 129 | } 130 | data := recvData.(*common2.TWSData) 131 | _ = actor.doRecvPro(data) 132 | case resp := <-actor.mJsCore.RecvMsg(): 133 | actor.sendEventResp(resp) 134 | if resp.Event == LogoutName { 135 | actor.isReleasedJscore = true 136 | actor.isclosing = true 137 | actor.sendClosingResp() 138 | } 139 | //case <-actor.heartTicker.C: 140 | // if actor.heartFlag == true { 141 | // actor.heartFlag = false 142 | // } else { 143 | // log.Error("心跳包超时错误", "sessionId", actor.SessionId) 144 | // actor.isclosing = true 145 | // actor.a.Destroy() 146 | // } 147 | } 148 | } 149 | } 150 | func (actor *MActorIm) ReleaseRes() { 151 | log.Info("get ReleaseRes sign") 152 | ind := &ResReleaseStru{BackSign: make(chan bool, 1)} 153 | actor.releaseResChan <- ind 154 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 155 | defer cancel() 156 | select { 157 | case <-ctx.Done(): 158 | log.Info("出现超时返回,actor可能已经被异步destroy") 159 | case <-ind.BackSign: 160 | log.Info("通过releaseRes接口回收资源") 161 | } 162 | 163 | } 164 | func (actor *MActorIm) Destroy() { 165 | actor.closeChan <- true 166 | actor.wg.Wait() 167 | actor.a = nil 168 | log.Info("退出MQPushActorIm", "sessionId", actor.SessionId) 169 | } 170 | 171 | // ProcessRecvMsg processes received messages and sends them to the ReceivMsgChan. 172 | func (actor *MActorIm) ProcessRecvMsg(msg interface{}) error { 173 | if len(actor.ReceivMsgChan) == actor.nChanLen { 174 | log.Error("send channel is full", "sessionId", actor.SessionId) 175 | return errors.New("send channel is full") 176 | } 177 | actor.ReceivMsgChan <- msg 178 | return nil 179 | } 180 | 181 | // doRecvPro processes the message received from the network layer. 182 | func (actor *MActorIm) doRecvPro(data *common2.TWSData) error { 183 | log.Info("message come here", "data", data) 184 | if data.MsgType == common2.MessageText { 185 | req := &Req{} 186 | err := json.Unmarshal(data.Msg, req) 187 | if err != nil { 188 | log.Error("parse protocol err", "err", err, "sessionId", actor.SessionId) 189 | actor.sendEventResp(&core_func.EventData{Event: ProtocolError, ErrCode: 20000, ErrMsg: err.Error(), 190 | OperationID: req.OperationID}) 191 | return err 192 | } 193 | log.Info("receive req", "req", req, "sessionId", actor.SessionId) 194 | err = actor.mJsCore.SendMsg(req) 195 | if err != nil { 196 | actor.sendEventResp(&core_func.EventData{Event: req.ReqFuncName, ErrCode: 20000, ErrMsg: err.Error(), 197 | OperationID: req.OperationID}) 198 | } 199 | } 200 | return nil 201 | } 202 | 203 | // sendResp sends a response message to the WebSocket client. 204 | func (actor *MActorIm) sendHeart() { 205 | //heart := []byte("ping") 206 | resSend := &common2.TWSData{MsgType: common2.PingMessage, Msg: nil} 207 | actor.a.WriteMsg(resSend) 208 | } 209 | 210 | // sendEventResp sends an event response to the WebSocket client. 211 | func (actor *MActorIm) sendEventResp(res *core_func.EventData) { 212 | resb, _ := json.Marshal(res) 213 | resSend := &common2.TWSData{MsgType: common2.MessageText, Msg: resb} 214 | actor.a.WriteMsg(resSend) 215 | } 216 | 217 | func (actor *MActorIm) sendClosingResp() { 218 | resSend := &common2.TWSData{MsgType: common2.CloseMessage, Msg: nil} 219 | actor.a.WriteMsg(resSend) 220 | } 221 | -------------------------------------------------------------------------------- /pkg/module/jsCore.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/openim-sigs/oimws/pkg/core_func" 7 | "reflect" 8 | 9 | "github.com/openimsdk/openim-sdk-core/v3/pkg/utils" 10 | ) 11 | 12 | const ( 13 | LogoutTips = "js sdk socket close" 14 | LogoutName = "Logout" 15 | ) 16 | 17 | type JsCore struct { 18 | RespMessagesChan chan *core_func.EventData 19 | funcRouter *core_func.FuncRouter 20 | } 21 | 22 | type Req struct { 23 | ReqFuncName string `json:"reqFuncName" ` 24 | OperationID string `json:"operationID"` 25 | Data string `json:"data"` 26 | UserID string `json:"userID"` 27 | Batch int `json:"batchMsg"` 28 | } 29 | type JsInterface interface { 30 | RecvMsg() chan interface{} //todo your sturct,error or response 31 | SendMsg(interface{}) error 32 | Destroy() 33 | } 34 | 35 | // NewJsCore creates a new JsCore instance. 36 | func NewJsCore(para *ParamStru, sessionId string) *JsCore { 37 | respChan := make(chan *core_func.EventData, 100) 38 | funcRouter := core_func.NewFuncRouter(respChan, sessionId) 39 | fmt.Println("NewJsCore", "data=", "sessionId", sessionId) 40 | funcRouter.InitSDK(para.GetOperationID(), para.GetPlatformID()) 41 | return &JsCore{RespMessagesChan: respChan, funcRouter: funcRouter} 42 | } 43 | 44 | // RecvMsg returns the channel to receive messages. 45 | func (core *JsCore) RecvMsg() chan *core_func.EventData { 46 | return core.RespMessagesChan 47 | } 48 | 49 | // SendMsg processes the incoming request and calls the corresponding method. 50 | func (core *JsCore) SendMsg(req *Req) error { 51 | fmt.Println("method is valid", "data=", req) 52 | methodValue := reflect.ValueOf(core.funcRouter).MethodByName(req.ReqFuncName) 53 | if !methodValue.IsValid() { 54 | //log.ZWarn(context.Background(), "method is valid", errors.New("method is valid"), "data", req) 55 | fmt.Println("method is valid", "data=", req) 56 | return utils.Wrap(fmt.Errorf("method is valid"), "method is valid") 57 | } 58 | var args []any 59 | if err := json.Unmarshal([]byte(req.Data), &args); err != nil { 60 | return utils.Wrap(err, "json.Unmarshal failed") 61 | } 62 | // Convert args to []reflect.Value 63 | args = append([]any{req.OperationID}, args...) 64 | argsValue := make([]reflect.Value, len(args)) 65 | for i, arg := range args { 66 | if arg == nil { 67 | return utils.Wrap(fmt.Errorf("args[%d] is not nil", i), "args has nil") 68 | } 69 | argsValue[i] = reflect.ValueOf(arg) 70 | } 71 | methodValue.Call(argsValue) 72 | return nil 73 | } 74 | 75 | // Destroy performs cleanup when the core is no longer needed. 76 | func (core *JsCore) Destroy() { 77 | core.funcRouter.Logout(LogoutTips) 78 | } 79 | -------------------------------------------------------------------------------- /pkg/module/netmodule.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "github.com/openim-sigs/oimws/pkg/common" 7 | "github.com/openim-sigs/oimws/pkg/gate" 8 | log "github.com/xuexihuang/new_log15" 9 | "net/url" 10 | "sync" 11 | ) 12 | 13 | type JsActorMap struct { 14 | sync.Mutex 15 | uActors map[string]MActor 16 | } 17 | 18 | var GJsActors *JsActorMap 19 | 20 | func init() { 21 | GJsActors = &JsActorMap{uActors: make(map[string]MActor)} 22 | } 23 | 24 | type MActor interface { 25 | ProcessRecvMsg(interface{}) error 26 | Destroy() 27 | // 28 | ReleaseRes() 29 | run() 30 | } 31 | 32 | // NewAgent is called when a new WebSocket connection is established. It initializes agent-related data and checks the token validity. 33 | func NewAgent(a gate.Agent) { 34 | aUerData := a.UserData().(*common.TAgentUserData) 35 | log.Info("one ws connect", "sessionId", aUerData.SessionID) 36 | param, err := checkToken(aUerData) 37 | if err != nil { 38 | log.Error("Token validation failed", "userData", aUerData, "sessionId", aUerData.SessionID) 39 | 40 | res := &ResponseSt{Type: RESP_OP_TYPE, Cmd: CONN_CMD, Success: false, ErrMsg: "check token error"} 41 | resb, _ := json.Marshal(res) 42 | resSend := &common.TWSData{MsgType: common.MessageText, Msg: resb} 43 | a.WriteMsg(resSend) 44 | a.Close() 45 | return 46 | } 47 | log.Info("checkToken info", "param", param, "err", err) 48 | actor, err := NewMActor(a, param.SessionId, param) 49 | if err != nil { 50 | log.Error("NewMQActor error", "err", err, "sessionId", aUerData.SessionID) 51 | res := &ResponseSt{Type: RESP_OP_TYPE, Cmd: CONN_CMD, Success: false, ErrMsg: "NewMQActor error"} 52 | resb, _ := json.Marshal(res) 53 | resSend := &common.TWSData{MsgType: common.MessageText, Msg: resb} 54 | a.WriteMsg(resSend) 55 | a.Close() 56 | return 57 | } 58 | GJsActors.Lock() 59 | v, ok := GJsActors.uActors[param.GetUserID()] 60 | if ok { 61 | v.ReleaseRes() 62 | } 63 | GJsActors.uActors[param.GetUserID()] = actor 64 | GJsActors.Unlock() 65 | aUerData.ProxyBody = actor 66 | aUerData.UserId = param.GetUserID() 67 | a.SetUserData(aUerData) 68 | log.Info("one linked", "param", param, "sessionId", aUerData.SessionID) 69 | } 70 | 71 | // CloseAgent is called when the WebSocket connection is closed. It performs cleanup actions for the agent. 72 | func CloseAgent(a gate.Agent) { 73 | aUerData := a.UserData().(*common.TAgentUserData) 74 | if aUerData.ProxyBody != nil { 75 | aUerData.ProxyBody.(MActor).Destroy() 76 | aUerData.ProxyBody = nil 77 | } 78 | GJsActors.Lock() 79 | _, ok := GJsActors.uActors[aUerData.UserId] 80 | if ok { 81 | delete(GJsActors.uActors, aUerData.UserId) 82 | } 83 | GJsActors.Unlock() 84 | log.Info("one dislinkder", "sessionId", a.UserData().(*common.TAgentUserData).SessionID) 85 | } 86 | 87 | // DataRecv is called when new data is received on the WebSocket connection. It processes the incoming data through the actor. 88 | func DataRecv(data interface{}, a gate.Agent) { 89 | aUerData := a.UserData().(*common.TAgentUserData) 90 | if aUerData.ProxyBody != nil { 91 | err := aUerData.ProxyBody.(MActor).ProcessRecvMsg(data) 92 | if err != nil { 93 | log.Error("Overflow error", "sessionId", aUerData.SessionID) 94 | a.Destroy() 95 | } 96 | } 97 | } 98 | 99 | // checkToken validates the session token contained in the user data. 100 | func checkToken(data *common.TAgentUserData) (*ParamStru, error) { 101 | ret := new(ParamStru) 102 | ret.SessionId = data.SessionID 103 | var token string 104 | if data.CookieVal != "" { 105 | token = data.CookieVal 106 | } else { 107 | ///////////////////// 108 | u, err := url.Parse(data.AppString) 109 | if err != nil { 110 | log.Error("ws url path not correct", "sessionId", data.SessionID) 111 | return nil, errors.New("ws url path not correct") 112 | } 113 | q := u.Query() 114 | token = q.Get("token") 115 | ////////////////////// 116 | } 117 | if token == "" { 118 | log.Error("Token retrieval is empty", "sessionId", data.SessionID) 119 | return nil, errors.New("Token retrieval is empty") 120 | } 121 | // TODO: Add your token validation logic here to verify the legitimacy of the token 122 | //ret.UserId="" 123 | ret.UrlPath = data.AppString 124 | ret.Token = token 125 | if ret.GetUserID() == "" { 126 | log.Error("userId is empty!") 127 | return nil, errors.New("userId is empty") 128 | } 129 | return ret, nil 130 | } 131 | -------------------------------------------------------------------------------- /pkg/module/statusActor.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "encoding/json" 5 | common2 "github.com/openim-sigs/oimws/pkg/common" 6 | "github.com/openim-sigs/oimws/pkg/gate" 7 | log "github.com/xuexihuang/new_log15" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type StatusActorIm struct { 13 | nChanLen int //接收数据网络缓存 14 | heartTickerSend *time.Ticker //用于心跳send 15 | wg sync.WaitGroup 16 | a gate.Agent 17 | SessionId string 18 | closeChan chan bool //主动关闭协程的通道 19 | ReceivMsgChan chan interface{} //接收网络层数据通道 20 | isclosing bool 21 | } 22 | 23 | func NewStatusActor(a gate.Agent, sessionId string, appParam *ParamStru) (MActor, error) { 24 | 25 | ret := &StatusActorIm{a: a, SessionId: sessionId, closeChan: make(chan bool, 1), nChanLen: 10, ReceivMsgChan: make(chan interface{}, 10), isclosing: false, 26 | heartTickerSend: time.NewTicker(100 * time.Second)} 27 | go ret.run() 28 | return ret, nil 29 | } 30 | func (actor *StatusActorIm) run() { 31 | actor.wg.Add(1) 32 | defer common2.TryRecoverAndDebugPrint() 33 | defer actor.wg.Done() 34 | for { 35 | select { 36 | case <-actor.heartTickerSend.C: //send the heart pack 37 | if actor.isclosing == true { 38 | continue 39 | } 40 | actor.sendHeart() 41 | case <-actor.closeChan: 42 | log.Info("收到退出信号", "sessionId", actor.SessionId) 43 | return 44 | case recvData := <-actor.ReceivMsgChan: 45 | if actor.isclosing == true { 46 | continue 47 | } 48 | data := recvData.(*common2.TWSData) 49 | _ = actor.doRecvPro(data) 50 | } 51 | } 52 | } 53 | func (actor *StatusActorIm) ProcessRecvMsg(interface{}) error { 54 | 55 | return nil 56 | } 57 | func (actor *StatusActorIm) Destroy() { 58 | actor.closeChan <- true 59 | actor.wg.Wait() 60 | actor.a = nil 61 | log.Info("退出MQPushActorIm", "sessionId", actor.SessionId) 62 | } 63 | func (actor *StatusActorIm) ReleaseRes() { 64 | 65 | } 66 | func (actor *StatusActorIm) sendHeart() { 67 | //heart := []byte("ping") 68 | resSend := &common2.TWSData{MsgType: common2.PingMessage, Msg: nil} 69 | actor.a.WriteMsg(resSend) 70 | } 71 | func (actor *StatusActorIm) doRecvPro(data *common2.TWSData) error { 72 | log.Info("message come here", "data.type", data.MsgType) 73 | if data.MsgType == common2.MessageText { 74 | req := &RequestSt{} 75 | err := json.Unmarshal(data.Msg, req) 76 | if err != nil { 77 | log.Error("解析前端协议出错", "err", err, "sessionId", actor.SessionId) 78 | return err 79 | } 80 | log.Info("收到命令", "req", req, "sessionId", actor.SessionId) 81 | //////////////////////////////////////////////// 82 | res := ResponseSt{Type: RESP_OP_TYPE, Success: true, UserId: genGroupUserIds(), Duration: time.Now().Unix() - ProgressStartTime} 83 | resb, _ := json.Marshal(res) 84 | resSend := &common2.TWSData{MsgType: common2.MessageText, Msg: resb} 85 | actor.a.WriteMsg(resSend) 86 | } 87 | return nil 88 | } 89 | func genGroupUserIds() []string { 90 | var ret []string 91 | GJsActors.Lock() 92 | defer GJsActors.Unlock() 93 | for k, _ := range GJsActors.uActors { 94 | ret = append(ret, k) 95 | } 96 | return ret 97 | } 98 | -------------------------------------------------------------------------------- /pkg/module/statusNetmodule.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/openim-sigs/oimws/pkg/common" 6 | "github.com/openim-sigs/oimws/pkg/gate" 7 | log "github.com/xuexihuang/new_log15" 8 | ) 9 | 10 | var ProgressStartTime int64 11 | 12 | func NewStatusAgent(a gate.Agent) { 13 | aUerData := a.UserData().(*common.TAgentUserData) 14 | log.Info("one status ws connect", "sessionId", aUerData.SessionID) 15 | actor, err := NewStatusActor(a, aUerData.SessionID, nil) 16 | if err != nil { 17 | log.Error("NewStatusActor error", "err", err, "sessionId", aUerData.SessionID) 18 | res := &ResponseSt{Type: RESP_OP_TYPE, Success: false, ErrMsg: "NewMQActor error"} 19 | resb, _ := json.Marshal(res) 20 | resSend := &common.TWSData{MsgType: common.MessageText, Msg: resb} 21 | a.WriteMsg(resSend) 22 | a.Close() 23 | return 24 | } 25 | aUerData.ProxyBody = actor 26 | a.SetUserData(aUerData) 27 | log.Info("one status linked", "sessionId", aUerData.SessionID) 28 | 29 | } 30 | func CloseStatusAgent(a gate.Agent) { 31 | aUerData := a.UserData().(*common.TAgentUserData) 32 | if aUerData.ProxyBody != nil { 33 | aUerData.ProxyBody.(MActor).Destroy() 34 | aUerData.ProxyBody = nil 35 | } 36 | log.Info("one status dislinkder", "sessionId", a.UserData().(*common.TAgentUserData).SessionID) 37 | } 38 | func DataRecvStatus(data interface{}, a gate.Agent) { 39 | aUerData := a.UserData().(*common.TAgentUserData) 40 | if aUerData.ProxyBody != nil { 41 | err := aUerData.ProxyBody.(MActor).ProcessRecvMsg(data) 42 | if err != nil { 43 | log.Error("溢出错误", "sessionId", aUerData.SessionID) 44 | a.Destroy() 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pkg/module/struct.go: -------------------------------------------------------------------------------- 1 | package module 2 | 3 | const ( 4 | RESP_OP_TYPE = "response" 5 | MQ_MSG_TYPE = "mqMessage" 6 | HEART_CONFIG_TYPE = "heartConfig" 7 | CONN_CMD = "connect" 8 | SUB_CMD = "subscribe" 9 | UNSUB_CMD = "unsubscribe" 10 | HEART_CMD = "heart" 11 | ) 12 | 13 | type ResponseSt struct { 14 | Type string `json:"type"` //"response" or "mqMessage" or "heartConfig" 15 | Cmd string `json:"cmd"` //"connect" "subscribe" "unsubscribe" 16 | Success bool `json:"success"` // 17 | ErrMsg string `json:"errMsg"` 18 | UserId []string `json:"userIds"` 19 | Duration int64 `json:"duration"` // progress run time ,seconds 20 | RequestId string `json:"requestId"` 21 | Topic string `json:"topic"` 22 | Extra string `json:"extra"` 23 | MsgTimeStamp int64 `json:"msgTimeStamp"` 24 | MsgSeqId int64 `json:"msgSeqId"` 25 | Data string `json:"data"` 26 | Rate int64 `json:"rate"` 27 | } 28 | 29 | type RequestSt struct { 30 | Cmd string `json:"cmd"` // "subscribe" "unsubscribe" or "heart" 31 | RequestId string `json:"requestId"` 32 | Topic string `json:"topic"` 33 | Extra string `json:"extra"` 34 | MsgTimeStamp int64 `json:"msgStartTime"` 35 | MsgSeqId int64 `json:"msgSeqId"` 36 | } 37 | -------------------------------------------------------------------------------- /pkg/network/agent.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | type Agent interface { 4 | Run() 5 | OnClose() 6 | } 7 | -------------------------------------------------------------------------------- /pkg/network/conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/openim-sigs/oimws/pkg/common" 5 | "net" 6 | ) 7 | 8 | type Conn interface { 9 | ReadMsg() (int, []byte, error) 10 | WriteMsg(args *common.TWSData) error 11 | LocalAddr() net.Addr 12 | RemoteAddr() net.Addr 13 | Close() 14 | Destroy() 15 | } 16 | -------------------------------------------------------------------------------- /pkg/network/defines.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | const ( 4 | LittleEndian = false 5 | ) 6 | -------------------------------------------------------------------------------- /pkg/network/processor.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "github.com/openim-sigs/oimws/pkg/common" 5 | ) 6 | 7 | type Processor interface { 8 | // must goroutine safe 9 | Route(msg interface{}, userData interface{}) error 10 | // must goroutine safe 11 | Unmarshal(data []byte) (interface{}, error) 12 | UnmarshalMul(nType int, data []byte) (interface{}, error) 13 | // must goroutine safe 14 | Marshal(msg interface{}) (*common.TWSData, error) 15 | // Whether to use packet mode for packing/unpacking 16 | UsePacketMode() bool 17 | } 18 | -------------------------------------------------------------------------------- /pkg/network/tjson/tjson.go: -------------------------------------------------------------------------------- 1 | package tjson 2 | 3 | import ( 4 | "github.com/openim-sigs/oimws/pkg/common" 5 | ) 6 | 7 | type Login struct { 8 | UserName string 9 | PassWord string 10 | } 11 | 12 | type Processor struct { 13 | } 14 | 15 | // NewProcessor is a constructor for Processor. 16 | func NewProcessor() *Processor { 17 | p := new(Processor) 18 | return p 19 | } 20 | 21 | // UsePacketMode returns false indicating that the processor is likely used in a stream mode and not packet mode. 22 | func (p *Processor) UsePacketMode() bool { 23 | return false 24 | } 25 | 26 | // Marshal takes a message interface and converts it into a WebSocket data structure. 27 | func (p *Processor) Marshal(msg interface{}) (*common.TWSData, error) { 28 | tsend := msg.(*common.TWSData) 29 | //if tsend.MsgType != common.MessageText && tsend.MsgType != common.MessageBinary { 30 | // return nil, errors.New("msg is not correct") 31 | //} 32 | return tsend, nil 33 | } 34 | 35 | // Unmarshal creates a new Login struct with preset credentials, not actually using the input data. 36 | func (p *Processor) Unmarshal(data []byte) (interface{}, error) { 37 | return &Login{UserName: "nihao", PassWord: "huanglin"}, nil 38 | } 39 | 40 | // Route currently does nothing and always returns nil, indicating no error. 41 | func (p *Processor) Route(msg interface{}, userData interface{}) error { 42 | return nil 43 | } 44 | 45 | // UnmarshalMul takes a message type and data, wraps it into a TWSData struct, and returns it. 46 | func (p *Processor) UnmarshalMul(nType int, data []byte) (interface{}, error) { 47 | ret := &common.TWSData{} 48 | if nType == common.MessageText { 49 | ret.MsgType = common.MessageText 50 | ret.Msg = data 51 | } else { 52 | ret.MsgType = common.MessageBinary 53 | ret.Msg = data 54 | } 55 | return ret, nil 56 | } 57 | -------------------------------------------------------------------------------- /pkg/network/ws_client.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | 7 | log "github.com/xuexihuang/new_log15" 8 | 9 | "github.com/gorilla/websocket" 10 | ) 11 | 12 | const ( 13 | MaxMsgLen = 1024 * 1024 * 10 14 | ) 15 | 16 | // WSClient just for client dial to websocket server 17 | type WSClient struct { 18 | sync.Mutex 19 | Addr string 20 | ConnNum int 21 | ConnectInterval time.Duration 22 | PendingWriteNum int 23 | MaxMsgLen uint32 24 | HandshakeTimeout time.Duration 25 | AutoReconnect bool 26 | NewAgent func(*WSConn) Agent 27 | dialer websocket.Dialer 28 | conns WebsocketConnSet 29 | wg sync.WaitGroup 30 | closeFlag bool 31 | } 32 | 33 | // Start initializes and starts the WebSocket client. 34 | func (client *WSClient) Start() { 35 | client.init() 36 | 37 | for i := 0; i < client.ConnNum; i++ { 38 | client.wg.Add(1) 39 | go client.connect() 40 | } 41 | } 42 | 43 | // init prepares the client by setting default values and validating settings. 44 | func (client *WSClient) init() { 45 | client.Lock() 46 | defer client.Unlock() 47 | 48 | if client.ConnNum <= 0 { 49 | client.ConnNum = 1 50 | //log.Release("invalid ConnNum, reset to %v", client.ConnNum) 51 | log.Info("invalid ConnNum, reset", "client.ConnNum", client.ConnNum) 52 | } 53 | if client.ConnectInterval <= 0 { 54 | client.ConnectInterval = 3 * time.Second 55 | //log.Release("invalid ConnectInterval, reset to %v", client.ConnectInterval) 56 | log.Info("invalid ConnectInterval, reset", "client.ConnectInterval", client.ConnectInterval) 57 | } 58 | if client.PendingWriteNum <= 0 { 59 | client.PendingWriteNum = 100 60 | //log.Release("invalid PendingWriteNum, reset to %v", client.PendingWriteNum) 61 | log.Info("invalid PendingWriteNum, reset", "client.PendingWriteNum", client.PendingWriteNum) 62 | } 63 | if client.MaxMsgLen <= 0 { 64 | client.MaxMsgLen = MaxMsgLen 65 | //log.Release("invalid MaxMsgLen, reset to %v", client.MaxMsgLen) 66 | log.Info("invalid MaxMsgLen, reset", "client.MaxMsgLen", client.MaxMsgLen) 67 | } 68 | if client.HandshakeTimeout <= 0 { 69 | client.HandshakeTimeout = 10 * time.Second 70 | //log.Release("invalid HandshakeTimeout, reset to %v", client.HandshakeTimeout) 71 | log.Info("invalid HandshakeTimeout, reset", "client.HandshakeTimeout", client.HandshakeTimeout) 72 | } 73 | if client.NewAgent == nil { 74 | //log.Fatal("NewAgent must not be nil") 75 | log.Crit("NewAgent must not be nil") 76 | } 77 | if client.conns != nil { 78 | //log.Fatal("client is running") 79 | log.Crit("client is running") 80 | } 81 | 82 | client.conns = make(WebsocketConnSet) 83 | client.closeFlag = false 84 | client.dialer = websocket.Dialer{ 85 | HandshakeTimeout: client.HandshakeTimeout, 86 | } 87 | } 88 | 89 | // dial creates a new WebSocket connection. 90 | func (client *WSClient) dial() *websocket.Conn { 91 | for { 92 | conn, _, err := client.dialer.Dial(client.Addr, nil) 93 | if err == nil || client.closeFlag { 94 | return conn 95 | } 96 | 97 | //log.Release("connect to %v error: %v", client.Addr, err) 98 | log.Info("connect error", "client.Addr", client.Addr, "err", err) 99 | time.Sleep(client.ConnectInterval) 100 | continue 101 | } 102 | } 103 | 104 | // connect handles the connection lifecycle. 105 | func (client *WSClient) connect() { 106 | defer client.wg.Done() 107 | 108 | reconnect: 109 | conn := client.dial() 110 | if conn == nil { 111 | return 112 | } 113 | conn.SetReadLimit(int64(client.MaxMsgLen)) 114 | 115 | client.Lock() 116 | if client.closeFlag { 117 | client.Unlock() 118 | conn.Close() 119 | return 120 | } 121 | client.conns[conn] = struct{}{} 122 | client.Unlock() 123 | 124 | wsConn := newWSConn(conn, client.PendingWriteNum, client.MaxMsgLen, "", "") 125 | agent := client.NewAgent(wsConn) 126 | agent.Run() 127 | 128 | // cleanup 129 | wsConn.Close() 130 | client.Lock() 131 | delete(client.conns, conn) 132 | client.Unlock() 133 | agent.OnClose() 134 | 135 | if client.AutoReconnect { 136 | time.Sleep(client.ConnectInterval) 137 | goto reconnect 138 | } 139 | } 140 | 141 | // Close initiates the shutdown process for the client. 142 | func (client *WSClient) Close() { 143 | client.Lock() 144 | client.closeFlag = true 145 | for conn := range client.conns { 146 | conn.Close() 147 | } 148 | client.conns = nil 149 | client.Unlock() 150 | 151 | client.wg.Wait() 152 | } 153 | -------------------------------------------------------------------------------- /pkg/network/ws_conn.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "errors" 5 | "github.com/openim-sigs/oimws/pkg/common" 6 | "net" 7 | "sync" 8 | 9 | "github.com/gorilla/websocket" 10 | log "github.com/xuexihuang/new_log15" 11 | ) 12 | 13 | type WebsocketConnSet map[*websocket.Conn]struct{} 14 | 15 | type WSConn struct { 16 | sync.Mutex 17 | conn *websocket.Conn 18 | writeChan chan *common.TWSData 19 | maxMsgLen uint32 20 | closeFlag bool 21 | //add by hl 22 | SessionId string 23 | AppParam common.TAppParam 24 | AppURL string 25 | CookieVal string 26 | } 27 | 28 | // newWSConn initializes a new WSConn object. 29 | func newWSConn(conn *websocket.Conn, pendingWriteNum int, maxMsgLen uint32, appurl string, cookieVal string) *WSConn { 30 | log.Error("test4.1", pendingWriteNum) 31 | wsConn := new(WSConn) 32 | wsConn.conn = conn 33 | wsConn.writeChan = make(chan *common.TWSData, pendingWriteNum) 34 | log.Error("test4.1.1", pendingWriteNum) 35 | wsConn.maxMsgLen = maxMsgLen 36 | /////////////////////////////生成唯一sessionid。 37 | var sessionID string 38 | log.Error("test4.1.2", pendingWriteNum) 39 | //sessionID = common.GetRandomSessionId() 40 | 41 | log.Error("test4.1.3", pendingWriteNum) 42 | wsConn.SessionId = sessionID 43 | wsConn.AppURL = appurl 44 | wsConn.CookieVal = cookieVal 45 | log.Error("test4.2") 46 | go func() { 47 | for b := range wsConn.writeChan { 48 | if b == nil { 49 | break 50 | } 51 | var err error 52 | if b.MsgType == common.MessageBinary { 53 | err = conn.WriteMessage(websocket.BinaryMessage, b.Msg) 54 | } else if b.MsgType == common.MessageText { 55 | err = conn.WriteMessage(websocket.TextMessage, b.Msg) 56 | } else if b.MsgType == common.PingMessage { 57 | log.Info("ping message", "b", b) 58 | err = conn.WriteMessage(websocket.PingMessage, b.Msg) 59 | } else if b.MsgType == common.CloseMessage { 60 | log.Info("close message", "b", b) 61 | err = conn.WriteMessage(websocket.CloseMessage, b.Msg) 62 | break 63 | } 64 | if err != nil { 65 | log.Error("send message err", "err", err.Error()) 66 | break 67 | } 68 | //fmt.Println("send msg is :", b) 69 | } 70 | 71 | conn.Close() 72 | wsConn.Lock() 73 | wsConn.closeFlag = true 74 | wsConn.Unlock() 75 | }() 76 | log.Error("test4.3") 77 | return wsConn 78 | } 79 | 80 | // doDestroy forcefully closes the connection without waiting for pending writes. 81 | func (wsConn *WSConn) doDestroy() { 82 | wsConn.conn.UnderlyingConn().(*net.TCPConn).SetLinger(0) 83 | wsConn.conn.Close() 84 | 85 | if !wsConn.closeFlag { 86 | close(wsConn.writeChan) 87 | wsConn.closeFlag = true 88 | } 89 | } 90 | 91 | // Destroy cleanly closes the connection. 92 | func (wsConn *WSConn) Destroy() { 93 | wsConn.Lock() 94 | defer wsConn.Unlock() 95 | 96 | wsConn.doDestroy() 97 | } 98 | 99 | // Close initiates a graceful shutdown of the connection. 100 | func (wsConn *WSConn) Close() { 101 | wsConn.Lock() 102 | defer wsConn.Unlock() 103 | if wsConn.closeFlag { 104 | return 105 | } 106 | 107 | wsConn.doWrite(nil) 108 | wsConn.closeFlag = true 109 | } 110 | 111 | // doWrite enqueues a message for writing to the websocket connection. 112 | func (wsConn *WSConn) doWrite(b *common.TWSData) { 113 | if len(wsConn.writeChan) == cap(wsConn.writeChan) { 114 | //log.Debug("close conn: channel full") 115 | log.Error("close conn: channel full") 116 | wsConn.doDestroy() 117 | return 118 | } 119 | 120 | wsConn.writeChan <- b 121 | } 122 | 123 | // LocalAddr returns the local network address. 124 | func (wsConn *WSConn) LocalAddr() net.Addr { 125 | return wsConn.conn.LocalAddr() 126 | } 127 | 128 | // RemoteAddr returns the remote network address. 129 | func (wsConn *WSConn) RemoteAddr() net.Addr { 130 | return wsConn.conn.RemoteAddr() 131 | } 132 | 133 | // ReadMsg reads a message from the websocket connection.(goroutine not safe) 134 | // goroutine not safe. 135 | func (wsConn *WSConn) ReadMsg() (int, []byte, error) { 136 | nTye, b, err := wsConn.conn.ReadMessage() 137 | return nTye, b, err 138 | } 139 | 140 | // WriteMsg writes a message to the websocket connection.(Args must not be modified by the others goroutines) 141 | // args must not be modified by the others goroutines. 142 | func (wsConn *WSConn) WriteMsg(args *common.TWSData) error { 143 | wsConn.Lock() 144 | defer wsConn.Unlock() 145 | if wsConn.closeFlag { 146 | return nil 147 | } 148 | 149 | // get len 150 | var msgLen uint32 151 | msgLen = uint32(len(args.Msg)) 152 | 153 | // check len 154 | if msgLen > wsConn.maxMsgLen { 155 | return errors.New("message too long") 156 | } 157 | 158 | wsConn.doWrite(args) 159 | return nil 160 | 161 | } 162 | -------------------------------------------------------------------------------- /pkg/network/ws_server.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "github.com/openim-sigs/oimws/pkg/common" 7 | "net" 8 | "net/http" 9 | "sync" 10 | "time" 11 | 12 | log "github.com/xuexihuang/new_log15" 13 | 14 | "github.com/gorilla/websocket" 15 | ) 16 | 17 | type WSServer struct { 18 | Addr string 19 | MaxConnNum int 20 | PendingWriteNum int 21 | MaxMsgLen uint32 22 | HTTPTimeout time.Duration 23 | CertFile string 24 | KeyFile string 25 | NewAgent func(*WSConn) Agent 26 | ln net.Listener 27 | handler *WSHandler 28 | } 29 | 30 | type WSHandler struct { 31 | maxConnNum int 32 | pendingWriteNum int 33 | maxMsgLen uint32 34 | newAgent func(*WSConn) Agent 35 | upgrader websocket.Upgrader 36 | conns WebsocketConnSet 37 | mutexConns sync.Mutex 38 | wg sync.WaitGroup 39 | } 40 | 41 | // ServeHTTP handles HTTP requests and upgrades them to WebSocket if the request is valid. 42 | func (handler *WSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 43 | defer common.TryRecoverAndDebugPrint() 44 | if r.Method != "GET" { 45 | http.Error(w, "Method not allowed", 405) 46 | return 47 | } 48 | var cookieVal string 49 | /* 50 | cookieToken, err := r.Cookie("token") 51 | if err != nil { 52 | log.Info("http的cookie中没有对应token", "err", err) 53 | } else { 54 | cookieVal = cookieToken.Value 55 | }*/ 56 | token := r.Header.Get("Authorization") 57 | if token != "" && len(token) > 7 { 58 | cookieVal = token[7:] 59 | } else { 60 | log.Info("http的headers Authorization中没有对应token") 61 | } 62 | log.Info("token info", "token", cookieVal) 63 | 64 | fmt.Println("url is:", r.URL.Path) 65 | conn, err := handler.upgrader.Upgrade(w, r, nil) 66 | if err != nil { 67 | log.Error("upgrade error", "err", err, "remoteIp", r.Host) 68 | return 69 | } 70 | conn.SetReadLimit(int64(handler.maxMsgLen)) 71 | _ = conn.SetReadDeadline(time.Now().Add(30 * time.Second)) 72 | conn.SetPongHandler(func(appData string) error { 73 | conn.SetReadDeadline(time.Now().Add(30 * time.Second)) 74 | log.Info("js replying with a pong packet.") 75 | return nil 76 | }) 77 | log.Error("test1") 78 | handler.wg.Add(1) 79 | defer handler.wg.Done() 80 | 81 | handler.mutexConns.Lock() 82 | if handler.conns == nil { 83 | handler.mutexConns.Unlock() 84 | conn.Close() 85 | return 86 | } 87 | log.Error("test2") 88 | if len(handler.conns) >= handler.maxConnNum { 89 | handler.mutexConns.Unlock() 90 | conn.Close() 91 | log.Error("too many connections") 92 | return 93 | } 94 | log.Error("test3") 95 | handler.conns[conn] = struct{}{} 96 | handler.mutexConns.Unlock() 97 | 98 | log.Error("test4") 99 | wsConn := newWSConn(conn, handler.pendingWriteNum, handler.maxMsgLen, r.URL.String(), cookieVal) 100 | log.Error("tes5") 101 | agent := handler.newAgent(wsConn) 102 | agent.Run() 103 | 104 | // cleanup 105 | wsConn.Close() 106 | handler.mutexConns.Lock() 107 | delete(handler.conns, conn) 108 | handler.mutexConns.Unlock() 109 | agent.OnClose() 110 | } 111 | 112 | // Start initializes and starts the WebSocket server. 113 | func (server *WSServer) Start() { 114 | ln, err := net.Listen("tcp", server.Addr) 115 | if err != nil { 116 | //log.Fatal("%v", err) 117 | log.Crit("net.listen err", "err", err) 118 | } 119 | 120 | if server.MaxConnNum <= 0 { 121 | server.MaxConnNum = 100 122 | //log.Release("invalid MaxConnNum, reset to %v", server.MaxConnNum) 123 | log.Info("invalid MaxConnNum,reset", "server.MaxConnNum", server.MaxConnNum) 124 | } 125 | if server.PendingWriteNum <= 0 { 126 | server.PendingWriteNum = 100 127 | //log.Release("invalid PendingWriteNum, reset to %v", server.PendingWriteNum) 128 | log.Info("invalid PendingWriteNum,reset", "server.PendingWriteNum", server.PendingWriteNum) 129 | } 130 | if server.MaxMsgLen <= 0 { 131 | server.MaxMsgLen = 4096 132 | //log.Release("invalid MaxMsgLen, reset to %v", server.MaxMsgLen) 133 | log.Info("invalid MaxMsgLen,reset", "server.MaxMsgLen", server.MaxMsgLen) 134 | } 135 | if server.HTTPTimeout <= 0 { 136 | server.HTTPTimeout = 10 * time.Second 137 | //log.Release("invalid HTTPTimeout, reset to %v", server.HTTPTimeout) 138 | log.Info("invalid HTTPTimeout,reset", "server.HTTPTimeout", server.HTTPTimeout) 139 | } 140 | if server.NewAgent == nil { 141 | //log.Fatal("NewAgent must not be nil") 142 | log.Crit("NewAgent must not be nil") 143 | } 144 | 145 | if server.CertFile != "" || server.KeyFile != "" { 146 | config := &tls.Config{} 147 | config.NextProtos = []string{"http/1.1"} 148 | 149 | var err error 150 | config.Certificates = make([]tls.Certificate, 1) 151 | config.Certificates[0], err = tls.LoadX509KeyPair(server.CertFile, server.KeyFile) 152 | if err != nil { 153 | //log.Fatal("%v", err) 154 | log.Crit("cerfiti file error", "err", err) 155 | } 156 | 157 | ln = tls.NewListener(ln, config) 158 | } 159 | 160 | server.ln = ln 161 | server.handler = &WSHandler{ 162 | maxConnNum: server.MaxConnNum, 163 | pendingWriteNum: server.PendingWriteNum, 164 | maxMsgLen: server.MaxMsgLen, 165 | newAgent: server.NewAgent, 166 | conns: make(WebsocketConnSet), 167 | upgrader: websocket.Upgrader{ 168 | HandshakeTimeout: server.HTTPTimeout, 169 | CheckOrigin: func(_ *http.Request) bool { return true }, 170 | }, 171 | } 172 | 173 | httpServer := &http.Server{ 174 | Addr: server.Addr, 175 | Handler: server.handler, 176 | ReadTimeout: server.HTTPTimeout, 177 | WriteTimeout: server.HTTPTimeout, 178 | MaxHeaderBytes: 1024, 179 | } 180 | 181 | go httpServer.Serve(ln) 182 | } 183 | 184 | // Close shuts down the WebSocket server and closes all active connections. 185 | func (server *WSServer) Close() { 186 | server.ln.Close() 187 | 188 | server.handler.mutexConns.Lock() 189 | for conn := range server.handler.conns { 190 | conn.Close() 191 | } 192 | server.handler.conns = nil 193 | server.handler.mutexConns.Unlock() 194 | 195 | server.handler.wg.Wait() 196 | } 197 | -------------------------------------------------------------------------------- /start-config.yml: -------------------------------------------------------------------------------- 1 | serviceBinaries: 2 | openim-sdk: 1 3 | toolBinaries: 4 | maxFileDescriptors: 10000 5 | --------------------------------------------------------------------------------