├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ ├── auto-invite-comment.yml │ ├── cla-assistant.yml │ ├── comment-check.yml │ ├── help-comment-issue.yml │ ├── issue-translator.yml │ └── release.yml ├── .gitignore ├── .husky ├── .gitignore ├── pre-commit └── prepare-commit-msg ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── assets ├── openIM.wasm ├── sql-wasm.wasm └── wasm_exec.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── api │ ├── database │ │ ├── alter.ts │ │ ├── black.ts │ │ ├── conversation.ts │ │ ├── friend.ts │ │ ├── friendRequest.ts │ │ ├── groupMember.ts │ │ ├── groupRequest.ts │ │ ├── groups.ts │ │ ├── index.ts │ │ ├── init.ts │ │ ├── instance.ts │ │ ├── localAppSdkVersion.ts │ │ ├── message.ts │ │ ├── notification.ts │ │ ├── sendingMessages.ts │ │ ├── stranger.ts │ │ ├── superGroup.ts │ │ ├── tableMaster.ts │ │ ├── tempCacheChatLogs.ts │ │ ├── unreadMessage.ts │ │ ├── upload.ts │ │ ├── users.ts │ │ └── versionSync.ts │ ├── index.ts │ ├── upload.ts │ └── worker.ts ├── constant │ └── index.ts ├── index.ts ├── sdk │ ├── index.ts │ └── initialize.ts ├── sqls │ ├── index.ts │ ├── localAdminGroupRequests.ts │ ├── localAppSdkVersion.ts │ ├── localBlack.ts │ ├── localChatLogsConversationID.ts │ ├── localConversationUnreadMessages.ts │ ├── localConversations.ts │ ├── localFriend.ts │ ├── localFriendRequest.ts │ ├── localGroupMembers.ts │ ├── localGroupRequests.ts │ ├── localGroups.ts │ ├── localNotification.ts │ ├── localSendingMessages.ts │ ├── localStranger.ts │ ├── localSuperGroups.ts │ ├── localTableMaster.ts │ ├── localUpload.ts │ ├── localUsers.ts │ ├── localVersionSync.ts │ └── tempCacheLocalChatLogs.ts ├── types │ ├── @jlongster │ │ └── sql.js │ │ │ └── index.d.ts │ ├── absurd-sql-optimized │ │ └── index.d.ts │ ├── entity.ts │ ├── enum.ts │ ├── eventData.ts │ ├── index.d.ts │ └── params.ts └── utils │ ├── emitter.ts │ ├── escape.ts │ ├── index.ts │ ├── is.ts │ ├── key.ts │ ├── logFormat.ts │ ├── response.ts │ ├── timer.ts │ └── value.ts ├── tsconfig.build.json └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | src/types/global.d.ts 2 | assets/ 3 | lib/ 4 | wasm_exec.js -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | plugins: ['@typescript-eslint', 'node', 'prettier'], 5 | parserOptions: { 6 | tsconfigRootDir: __dirname, 7 | project: ['./tsconfig.json'], 8 | }, 9 | extends: [ 10 | 'eslint:recommended', 11 | 'plugin:node/recommended', 12 | 'plugin:@typescript-eslint/eslint-recommended', 13 | 'plugin:@typescript-eslint/recommended', 14 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', 15 | 'plugin:prettier/recommended', 16 | ], 17 | rules: { 18 | 'prettier/prettier': 'warn', 19 | 'node/no-missing-import': 'off', 20 | 'node/no-empty-function': 'off', 21 | 'node/no-unsupported-features/es-syntax': 'off', 22 | 'node/no-missing-require': 'off', 23 | 'node/shebang': 'off', 24 | '@typescript-eslint/no-use-before-define': 'off', 25 | quotes: ['warn', 'single', { avoidEscape: true }], 26 | 'node/no-unpublished-import': 'off', 27 | '@typescript-eslint/no-unsafe-assignment': 'off', 28 | '@typescript-eslint/no-var-requires': 'off', 29 | '@typescript-eslint/ban-ts-comment': 'off', 30 | '@typescript-eslint/no-explicit-any': 'off', 31 | '@typescript-eslint/explicit-module-boundary-types': 'off', 32 | '@typescript-eslint/restrict-template-expressions': 'off', 33 | '@typescript-eslint/no-unsafe-return': 'off', 34 | '@typescript-eslint/no-floating-promises': 'off', 35 | '@typescript-eslint/no-empty-function': 'off', 36 | '@typescript-eslint/no-misused-promises': 'off', 37 | 'no-async-promise-executor': 'off', 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the repository to show as TypeScript rather than JS in GitHub 2 | *.js linguist-detectable=false -------------------------------------------------------------------------------- /.github/workflows/auto-invite-comment.yml: -------------------------------------------------------------------------------- 1 | name: Invite users to join OpenIM Community. 2 | on: 3 | issue_comment: 4 | types: 5 | - created 6 | jobs: 7 | issue_comment: 8 | name: Invite users to join OpenIM Community 9 | if: ${{ github.event.comment.body == '/invite' || github.event.comment.body == '/close' || github.event.comment.body == '/comment' }} 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | steps: 14 | 15 | - name: Invite user to join OpenIM Community 16 | uses: peter-evans/create-or-update-comment@v4 17 | with: 18 | token: ${{ secrets.BOT_GITHUB_TOKEN }} 19 | issue-number: ${{ github.event.issue.number }} 20 | body: | 21 | 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. 22 | 23 | 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. 24 | 25 | In addition to Slack, we also offer the following ways to get in touch: 26 | 27 | + 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. 28 | + 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. 29 | + 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. 30 | + 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. 31 | 32 | # - name: Close Issue 33 | # uses: peter-evans/close-issue@v3 34 | # with: 35 | # token: ${{ secrets.BOT_GITHUB_TOKEN }} 36 | # issue-number: ${{ github.event.issue.number }} 37 | # comment: 🤖 Auto-closing issue, if you still need help please reopen the issue or ask for help in the community above 38 | # labels: | 39 | # accepted -------------------------------------------------------------------------------- /.github/workflows/cla-assistant.yml: -------------------------------------------------------------------------------- 1 | name: CLA Assistant 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened,closed,synchronize] 7 | 8 | # explicitly configure permissions, in case your GITHUB_TOKEN workflow permissions are set to read-only in repository settings 9 | permissions: 10 | actions: write 11 | contents: write # this can be 'read' if the signatures are in remote repository 12 | pull-requests: write 13 | statuses: write 14 | 15 | jobs: 16 | CLA-Assistant: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: "CLA Assistant" 20 | 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' 21 | uses: contributor-assistant/github-action@v2.4.0 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | PERSONAL_ACCESS_TOKEN: ${{ secrets.BOT_TOKEN }} 25 | with: 26 | path-to-signatures: 'signatures/cla.json' 27 | path-to-document: 'https://github.com/OpenIM-Robot/cla/blob/main/README.md' # e.g. a CLA or a DCO document 28 | branch: 'main' 29 | allowlist: 'bot*,*bot,OpenIM-Robot' 30 | 31 | # the followings are the optional inputs - If the optional inputs are not given, then default values will be taken 32 | remote-organization-name: OpenIM-Robot 33 | remote-repository-name: cla 34 | create-file-commit-message: 'Creating file for storing CLA Signatures' 35 | # signed-commit-message: '$contributorName has signed the CLA in $owner/$repo#$pullRequestNo' 36 | custom-notsigned-prcomment: '💕 Thank you for your contribution and please kindly read and sign our CLA. [CLA Docs](https://github.com/OpenIM-Robot/cla/blob/main/README.md)' 37 | custom-pr-sign-comment: 'I have read the CLA Document and I hereby sign the CLA' 38 | custom-allsigned-prcomment: '🤖 All Contributors have signed the [CLA](https://github.com/OpenIM-Robot/cla/blob/main/README.md).
The signed information is recorded [**here**](https://github.com/OpenIM-Robot/cla/blob/main/signatures/cla.json)' 39 | #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) 40 | #use-dco-flag: true - If you are using DCO instead of CLA -------------------------------------------------------------------------------- /.github/workflows/comment-check.yml: -------------------------------------------------------------------------------- 1 | name: Non-English Comments Check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | non-english-comments-check: 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | # need ignore Dirs 15 | EXCLUDE_DIRS: ".git docs tests scripts assets node_modules build" 16 | # need ignore Files 17 | EXCLUDE_FILES: "*.md *.txt *.html *.css *.min.js *.mdx" 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Search for Non-English comments 23 | run: | 24 | set -e 25 | # Define the regex pattern to match Chinese characters 26 | pattern='[\p{Han}]' 27 | 28 | # Process the directories to be excluded 29 | exclude_dirs="" 30 | for dir in $EXCLUDE_DIRS; do 31 | exclude_dirs="$exclude_dirs --exclude-dir=$dir" 32 | done 33 | 34 | # Process the file types to be excluded 35 | exclude_files="" 36 | for file in $EXCLUDE_FILES; do 37 | exclude_files="$exclude_files --exclude=$file" 38 | done 39 | 40 | # Use grep to find all comments containing Non-English characters and save to file 41 | grep -Pnr "$pattern" . $exclude_dirs $exclude_files > non_english_comments.txt || true 42 | 43 | - name: Output non-English comments are found 44 | run: | 45 | if [ -s non_english_comments.txt ]; then 46 | echo "Non-English comments found in the following locations:" 47 | cat non_english_comments.txt 48 | exit 1 # terminate the workflow 49 | else 50 | echo "No Non_English comments found." 51 | fi -------------------------------------------------------------------------------- /.github/workflows/help-comment-issue.yml: -------------------------------------------------------------------------------- 1 | name: Good frist issue add comment 2 | on: 3 | issues: 4 | types: 5 | - labeled 6 | 7 | jobs: 8 | add-comment: 9 | if: github.event.label.name == 'help wanted' || github.event.label.name == 'good first issue' 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | steps: 14 | - name: Add comment 15 | uses: peter-evans/create-or-update-comment@v4 16 | with: 17 | issue-number: ${{ github.event.issue.number }} 18 | token: ${{ secrets.BOT_TOKEN }} 19 | body: | 20 | 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: 21 | [Join slack 🤖](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) to connect and communicate with our developers. 22 | If you wish to accept this assignment, please leave a comment in the comments section: `/accept`.🎯 -------------------------------------------------------------------------------- /.github/workflows/issue-translator.yml: -------------------------------------------------------------------------------- 1 | name: 'issue-translator' 2 | on: 3 | issue_comment: 4 | types: [created] 5 | issues: 6 | types: [opened] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: usthe/issues-translate-action@v2.7 13 | with: 14 | BOT_GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} 15 | IS_MODIFY_TITLE: true 16 | # not require, default false, . Decide whether to modify the issue title 17 | # if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot. 18 | CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿 19 | # not require. Customize the translation robot prefix message. -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | description: 'Version to release' 11 | required: true 12 | 13 | jobs: 14 | release: 15 | name: Release 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v2 21 | 22 | - name: Set up Node.js 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: '20' 26 | 27 | - name: Install dependencies 28 | run: npm install 29 | 30 | - name: Extract version from tag or dispatch 31 | id: get_version 32 | run: | 33 | if [ "${{ github.event_name }}" == "push" ]; then 34 | echo "Version from tag ${GITHUB_REF##*/}" 35 | echo "::set-output name=version::${GITHUB_REF##*/v}" 36 | elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then 37 | echo "Version from dispatch input ${{ github.event.inputs.version }}" 38 | echo "::set-output name=version::${{ github.event.inputs.version }}" 39 | fi 40 | 41 | - name: Update version in package.json 42 | run: npm version ${{ steps.get_version.outputs.version }} --no-git-tag-version 43 | 44 | - name: Build 45 | run: npm run build 46 | 47 | - name: Authenticate with npm registry 48 | run: npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }} 49 | 50 | - name: Config npm 51 | run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc 52 | env: 53 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 54 | 55 | - name: Publish 56 | env: 57 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 58 | run: npm publish --access public --no-git-checks 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env.test 73 | .env.local 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | 118 | # Compiled code 119 | lib/ 120 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | exec ` tag. 34 | 35 | ### Possible Issues ❗ 36 | 37 | > if you are using webpack4, you may flow this issue [How to import @openim/wasm-client-sdk in webpack4.x](https://github.com/openimsdk/open-im-sdk-web-wasm/issues/73). 38 | 39 | ## Usage 🚀 40 | 41 | The following examples demonstrate how to use the SDK. TypeScript is used, providing complete type hints. 42 | 43 | ### Importing the SDK 44 | 45 | ```typescript 46 | import { getSDK } from '@openim/wasm-client-sdk'; 47 | 48 | const OpenIM = getSDK(); 49 | ``` 50 | 51 | ### Logging In and Listening for Connection Status 52 | 53 | > Note: You need to [deploy](https://github.com/openimsdk/open-im-server#rocket-quick-start) OpenIM Server first, the default port of OpenIM Server is 10001, 10002. 54 | 55 | ```typescript 56 | import { CbEvents } from '@openim/wasm-client-sdk'; 57 | import type { WSEvent } from '@openim/wasm-client-sdk/lib/types/entity'; 58 | 59 | OpenIM.on(CbEvents.OnConnecting, handleConnecting); 60 | OpenIM.on(CbEvents.OnConnectFailed, handleConnectFailed); 61 | OpenIM.on(CbEvents.OnConnectSuccess, handleConnectSuccess); 62 | 63 | OpenIM.login({ 64 | userID: 'IM user ID', 65 | token: 'IM user token', 66 | platformID: 5, 67 | apiAddr: 'http://your-server-ip:10002', 68 | wsAddr: 'ws://your-server-ip:10001', 69 | }); 70 | 71 | function handleConnecting() { 72 | // Connecting... 73 | } 74 | 75 | function handleConnectFailed({ errCode, errMsg }: WSEvent) { 76 | // Connection failed ❌ 77 | console.log(errCode, errMsg); 78 | } 79 | 80 | function handleConnectSuccess() { 81 | // Connection successful ✅ 82 | } 83 | ``` 84 | 85 | To log into the IM server, you need to create an account and obtain a user ID and token. Refer to the [access token documentation](https://docs.openim.io/restapi/userManagement/userRegister) for details. 86 | 87 | ### Receiving and Sending Messages 💬 88 | 89 | OpenIM makes it easy to send and receive messages. By default, there is no restriction on having a friend relationship to send messages (although you can configure other policies on the server). If you know the user ID of the recipient, you can conveniently send a message to them. 90 | 91 | ```typescript 92 | import { CbEvents } from '@openim/wasm-client-sdk'; 93 | import type { 94 | WSEvent, 95 | MessageItem, 96 | } from '@openim/wasm-client-sdk/lib/types/entity'; 97 | 98 | // Listenfor new messages 📩 99 | OpenIM.on(CbEvents.OnRecvNewMessages, handleNewMessages); 100 | 101 | const message = (await OpenIM.createTextMessage('hello openim')).data; 102 | 103 | OpenIM.sendMessage({ 104 | recvID: 'recipient user ID', 105 | groupID: '', 106 | message, 107 | }) 108 | .then(() => { 109 | // Message sent successfully ✉️ 110 | }) 111 | .catch(err => { 112 | // Failed to send message ❌ 113 | console.log(err); 114 | }); 115 | 116 | function handleNewMessages({ data }: WSEvent) { 117 | // New message list 📨 118 | console.log(data); 119 | } 120 | ``` 121 | 122 | ## Examples 🌟 123 | 124 | You can find a demo web app that uses the SDK in the [openim-pc-web-demo](https://github.com/openimsdk/open-im-pc-web-demo) repository. 125 | 126 | ## Browser Support 🌐 127 | 128 | | Browser | Desktop OS | Mobile OS | 129 | | ------------------- | --------------------- | --------- | 130 | | Chrome (61+) | Windows, macOS, Linux | Android | 131 | | Firefox (58+) | Windows, macOS, Linux | Android | 132 | | Safari (15+) | macOS | iOS | 133 | | Edge (Chromium 16+) | Windows, macOS | | 134 | 135 | ## Community :busts_in_silhouette: 136 | 137 | - 📚 [OpenIM Community](https://github.com/OpenIMSDK/community) 138 | - 💕 [OpenIM Interest Group](https://github.com/Openim-sigs) 139 | - 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) 140 | - :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) 141 | 142 | ## Community Meetings :calendar: 143 | 144 | We want anyone to get involved in our community and contributing code, we offer gifts and rewards, and we welcome you to join us every Thursday night. 145 | 146 | Our conference is in the [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2ijy1ys1f-O0aEDCr7ExRZ7mwsHAVg9A) 🎯, then you can search the Open-IM-Server pipeline to join 147 | 148 | We take notes of each [biweekly meeting](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) in [GitHub discussions](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Our historical meeting notes, as well as replays of the meetings are available at [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing). 149 | 150 | ## Who are using OpenIM :eyes: 151 | 152 | Check out our [user case studies](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) page for a list of the project users. Don't hesitate to leave a [📝comment](https://github.com/openimsdk/open-im-server/issues/379) and share your use case. 153 | 154 | ## License :page_facing_up: 155 | 156 | This software is licensed under a dual-license model: 157 | 158 | - The GNU Affero General Public License (AGPL), Version 3 or later; **OR** 159 | - Commercial license terms from OpenIMSDK. 160 | 161 | If you wish to use this software under commercial terms, please contact us at: contact@openim.io 162 | 163 | For more information, see: https://www.openim.io/en/licensing 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /assets/openIM.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openimsdk/openim-sdk-js-wasm/82bdcf63418c113c71f58bc7a99f3ddd2e47e565/assets/openIM.wasm -------------------------------------------------------------------------------- /assets/sql-wasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openimsdk/openim-sdk-js-wasm/82bdcf63418c113c71f58bc7a99f3ddd2e47e565/assets/sql-wasm.wasm -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openim/wasm-client-sdk", 3 | "version": "3.8.2-1", 4 | "description": "open im sdk for web", 5 | "source": "src/index.ts", 6 | "main": "lib/index.js", 7 | "unpkg": "lib/index.umd.js", 8 | "module": "lib/index.es.js", 9 | "jsdelivr": "lib/index.umd.js", 10 | "types": "lib/index.d.ts", 11 | "exports": { 12 | ".": { 13 | "import": "./lib/index.es.js", 14 | "require": "./lib/index.js", 15 | "types": "./lib/index.d.ts" 16 | } 17 | }, 18 | "files": [ 19 | "lib/**/*", 20 | "assets/**/*" 21 | ], 22 | "scripts": { 23 | "build": "rimraf lib && rollup -c && tsc-alias", 24 | "cm": "cz", 25 | "lint": "eslint ./src/ --fix", 26 | "prepare": "husky install", 27 | "semantic-release": "semantic-release", 28 | "test:watch": "jest --watch", 29 | "test": "jest --coverage", 30 | "typecheck": "tsc --noEmit" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/OpenIMSDK/Open-IM-SDK-Web-Wasm.git" 35 | }, 36 | "license": "MIT", 37 | "author": { 38 | "name": "blooming", 39 | "email": "blooming2477@gmail.com", 40 | "url": "https://github.com/Bloomingg" 41 | }, 42 | "engines": { 43 | "node": ">=12.0" 44 | }, 45 | "keywords": [ 46 | "openim" 47 | ], 48 | "bugs": { 49 | "url": "https://github.com/OpenIMSDK/Open-IM-SDK-Web-Wasm/issues" 50 | }, 51 | "homepage": "https://github.com/OpenIMSDK/Open-IM-SDK-Web-Wasm#readme", 52 | "devDependencies": { 53 | "@commitlint/cli": "^16.2.4", 54 | "@commitlint/config-conventional": "^16.2.4", 55 | "@jlongster/sql.js": "^1.6.7", 56 | "@rollup/plugin-alias": "^5.1.0", 57 | "@rollup/plugin-commonjs": "^25.0.7", 58 | "@rollup/plugin-node-resolve": "^15.2.3", 59 | "@rollup/plugin-terser": "^0.4.4", 60 | "@types/jest": "^27.5.2", 61 | "@types/node": "^12.20.11", 62 | "@types/uuid": "^8.3.4", 63 | "@typescript-eslint/eslint-plugin": "^4.22.0", 64 | "@typescript-eslint/parser": "^4.22.0", 65 | "absurd-sql": "^0.0.53", 66 | "absurd-sql-optimized": "^0.0.1", 67 | "conventional-changelog-conventionalcommits": "^5.0.0", 68 | "eslint": "^7.25.0", 69 | "eslint-config-prettier": "^8.3.0", 70 | "eslint-plugin-node": "^11.1.0", 71 | "eslint-plugin-prettier": "^3.4.0", 72 | "husky": "^6.0.0", 73 | "jest": "^27.2.0", 74 | "lint-staged": "^10.5.4", 75 | "prettier": "^2.2.1", 76 | "rollup": "^2.79.1", 77 | "rollup-plugin-polyfill-node": "^0.13.0", 78 | "rollup-plugin-typescript2": "^0.36.0", 79 | "rpc-shooter": "^0.0.14", 80 | "semantic-release": "^19.0.2", 81 | "squel": "^5.13.0", 82 | "tsc-alias": "^1.7.0", 83 | "typescript": "^4.2.4", 84 | "uuid": "^9.0.0" 85 | }, 86 | "config": { 87 | "commitizen": { 88 | "path": "./node_modules/@commitlint/cz-conventional-changelog" 89 | } 90 | }, 91 | "lint-staged": { 92 | "*.ts": "eslint --cache --cache-location .eslintcache --fix" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import alias from '@rollup/plugin-alias'; 5 | import polyfillNode from 'rollup-plugin-polyfill-node'; 6 | import terser from '@rollup/plugin-terser'; 7 | 8 | export default [ 9 | { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: 'lib/index.js', 14 | format: 'cjs', 15 | inlineDynamicImports: true, 16 | sourcemap: false, 17 | }, 18 | { 19 | file: 'lib/index.es.js', 20 | format: 'esm', 21 | inlineDynamicImports: true, 22 | sourcemap: false, 23 | }, 24 | { 25 | file: 'lib/index.umd.js', 26 | format: 'umd', 27 | name: 'openImSdkWasm', 28 | inlineDynamicImports: true, 29 | sourcemap: false, 30 | }, 31 | ], 32 | plugins: [alias(), typescript(), resolve(), commonjs()], 33 | }, 34 | { 35 | input: 'src/api/worker.ts', 36 | output: [ 37 | { 38 | file: 'lib/worker.js', 39 | format: 'esm', 40 | sourcemap: false, 41 | }, 42 | { 43 | file: 'lib/worker-legacy.js', 44 | format: 'iife', 45 | sourcemap: false, 46 | }, 47 | ], 48 | plugins: [ 49 | alias(), 50 | typescript({ 51 | tsconfig: './tsconfig.build.json', 52 | }), 53 | resolve(), 54 | commonjs(), 55 | polyfillNode(), 56 | terser(), 57 | ], 58 | }, 59 | ]; 60 | -------------------------------------------------------------------------------- /src/api/database/alter.ts: -------------------------------------------------------------------------------- 1 | import { Database } from '@jlongster/sql.js'; 2 | 3 | export function alterTable(db: Database) { 4 | alter351(db); 5 | alter380(db); 6 | } 7 | 8 | function alter351(db: Database) { 9 | try { 10 | db.exec( 11 | ` 12 | ALTER TABLE local_friends ADD COLUMN is_pinned numeric; 13 | ` 14 | ); 15 | } catch (error) { 16 | // alter table error 17 | } 18 | } 19 | 20 | function alter380(db: Database) { 21 | try { 22 | db.exec( 23 | ` 24 | ALTER TABLE local_groups ADD COLUMN display_is_read numeric; 25 | ` 26 | ); 27 | } catch (error) { 28 | // alter table error 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/database/black.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | getBlackList as databaseGetBlackList, 4 | getBlackListUserID as databaseGetBlackListUserID, 5 | getBlackInfoByBlockUserID as databaseGetBlackInfoByBlockUserID, 6 | getBlackInfoList as databaseGetBlackInfoList, 7 | insertBlack as databaseInsertBlack, 8 | deleteBlack as databasedeleteBlack, 9 | updateBlack as databaseupdateBlack, 10 | LocalBlack, 11 | } from '@/sqls'; 12 | import { 13 | converSqlExecResult, 14 | convertObjectField, 15 | convertToSnakeCaseObject, 16 | formatResponse, 17 | } from '@/utils'; 18 | import { getInstance } from './instance'; 19 | 20 | export async function getBlackList(): Promise { 21 | try { 22 | const db = await getInstance(); 23 | 24 | const execResult = databaseGetBlackList(db); 25 | 26 | return formatResponse( 27 | converSqlExecResult(execResult[0], 'CamelCase', [], { 28 | block_user_id: 'userID', 29 | }) 30 | ); 31 | } catch (e) { 32 | console.error(e); 33 | 34 | return formatResponse( 35 | undefined, 36 | DatabaseErrorCode.ErrorInit, 37 | JSON.stringify(e) 38 | ); 39 | } 40 | } 41 | 42 | export async function getBlackListUserID(): Promise { 43 | try { 44 | const db = await getInstance(); 45 | 46 | const execResult = databaseGetBlackListUserID(db); 47 | 48 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 49 | } catch (e) { 50 | console.error(e); 51 | 52 | return formatResponse( 53 | undefined, 54 | DatabaseErrorCode.ErrorInit, 55 | JSON.stringify(e) 56 | ); 57 | } 58 | } 59 | 60 | export async function getBlackInfoByBlockUserID( 61 | blockUserID: string, 62 | loginUserID: string 63 | ): Promise { 64 | try { 65 | const db = await getInstance(); 66 | 67 | const execResult = databaseGetBlackInfoByBlockUserID( 68 | db, 69 | blockUserID, 70 | loginUserID 71 | ); 72 | 73 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 74 | } catch (e) { 75 | console.error(e); 76 | 77 | return formatResponse( 78 | undefined, 79 | DatabaseErrorCode.ErrorInit, 80 | JSON.stringify(e) 81 | ); 82 | } 83 | } 84 | 85 | export async function getBlackInfoList( 86 | blockUserIDListStr: string 87 | ): Promise { 88 | try { 89 | const db = await getInstance(); 90 | 91 | const execResult = databaseGetBlackInfoList( 92 | db, 93 | JSON.parse(blockUserIDListStr) 94 | ); 95 | 96 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 97 | } catch (e) { 98 | console.error(e); 99 | 100 | return formatResponse( 101 | undefined, 102 | DatabaseErrorCode.ErrorInit, 103 | JSON.stringify(e) 104 | ); 105 | } 106 | } 107 | 108 | export async function insertBlack(localBlackStr: string): Promise { 109 | try { 110 | const db = await getInstance(); 111 | 112 | const localBlack = convertToSnakeCaseObject( 113 | convertObjectField(JSON.parse(localBlackStr), { 114 | userID: 'block_user_id', 115 | name: 'nickname', 116 | }) 117 | ) as LocalBlack; 118 | 119 | databaseInsertBlack(db, localBlack); 120 | 121 | return formatResponse(''); 122 | } catch (e) { 123 | console.error(e); 124 | 125 | return formatResponse( 126 | undefined, 127 | DatabaseErrorCode.ErrorInit, 128 | JSON.stringify(e) 129 | ); 130 | } 131 | } 132 | 133 | export async function deleteBlack( 134 | blockUserID: string, 135 | loginUserID: string 136 | ): Promise { 137 | try { 138 | const db = await getInstance(); 139 | 140 | databasedeleteBlack(db, blockUserID, loginUserID); 141 | 142 | return formatResponse(''); 143 | } catch (e) { 144 | console.error(e); 145 | 146 | return formatResponse( 147 | undefined, 148 | DatabaseErrorCode.ErrorInit, 149 | JSON.stringify(e) 150 | ); 151 | } 152 | } 153 | 154 | export async function updateBlack(localBlackStr: string): Promise { 155 | try { 156 | const db = await getInstance(); 157 | 158 | const localBlack = convertToSnakeCaseObject( 159 | convertObjectField(JSON.parse(localBlackStr)) 160 | ) as LocalBlack; 161 | 162 | databaseupdateBlack(db, localBlack); 163 | 164 | return formatResponse(''); 165 | } catch (e) { 166 | console.error(e); 167 | 168 | return formatResponse( 169 | undefined, 170 | DatabaseErrorCode.ErrorInit, 171 | JSON.stringify(e) 172 | ); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/api/database/friend.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | insertFriend as databaseInsertFriend, 4 | deleteFriend as databasedeleteFriend, 5 | updateFriend as databaseupdateFriend, 6 | updateColumnsFriend as databaseupdateColumnsFriend, 7 | getAllFriendList as databaseGetAllFriendList, 8 | getPageFriendList as databaseGetPageFriendList, 9 | searchFriendList as databasesearchFriendList, 10 | getFriendInfoByFriendUserID as databaseGetFriendInfoByFriendUserID, 11 | getFriendInfoList as databaseGetFriendInfoList, 12 | getFriendListCount as databaseGetFriendListCount, 13 | deleteAllFriend as databaseDeleteAllFriend, 14 | LocalFriend, 15 | } from '@/sqls'; 16 | import { 17 | converSqlExecResult, 18 | convertObjectField, 19 | convertToSnakeCaseObject, 20 | formatResponse, 21 | } from '@/utils'; 22 | import { getInstance } from './instance'; 23 | 24 | export async function insertFriend(localFriendStr: string): Promise { 25 | try { 26 | const db = await getInstance(); 27 | const localFriend = convertToSnakeCaseObject( 28 | convertObjectField(JSON.parse(localFriendStr), { 29 | userID: 'friend_user_id', 30 | nickname: 'name', 31 | }) 32 | ) as LocalFriend; 33 | 34 | databaseInsertFriend(db, localFriend); 35 | 36 | return formatResponse(''); 37 | } catch (e) { 38 | console.error(e); 39 | 40 | return formatResponse( 41 | undefined, 42 | DatabaseErrorCode.ErrorInit, 43 | JSON.stringify(e) 44 | ); 45 | } 46 | } 47 | 48 | export async function deleteFriend( 49 | friendUserID: string, 50 | loginUserID: string 51 | ): Promise { 52 | try { 53 | const db = await getInstance(); 54 | 55 | databasedeleteFriend(db, friendUserID, loginUserID); 56 | 57 | return formatResponse(''); 58 | } catch (e) { 59 | console.error(e); 60 | 61 | return formatResponse( 62 | undefined, 63 | DatabaseErrorCode.ErrorInit, 64 | JSON.stringify(e) 65 | ); 66 | } 67 | } 68 | 69 | export async function updateFriend(localFriendStr: string): Promise { 70 | try { 71 | const db = await getInstance(); 72 | const localFriend = convertToSnakeCaseObject( 73 | convertObjectField(JSON.parse(localFriendStr), { 74 | userID: 'friend_user_id', 75 | nickname: 'name', 76 | }) 77 | ) as LocalFriend; 78 | 79 | databaseupdateFriend(db, localFriend); 80 | 81 | return formatResponse(''); 82 | } catch (e) { 83 | console.error(e); 84 | 85 | return formatResponse( 86 | undefined, 87 | DatabaseErrorCode.ErrorInit, 88 | JSON.stringify(e) 89 | ); 90 | } 91 | } 92 | 93 | export async function getAllFriendList(loginUserID: string): Promise { 94 | try { 95 | const db = await getInstance(); 96 | 97 | const execResult = databaseGetAllFriendList(db, loginUserID); 98 | 99 | return formatResponse( 100 | converSqlExecResult(execResult[0], 'CamelCase', ['isPinned'], { 101 | name: 'nickname', 102 | friend_user_id: 'userID', 103 | }) 104 | ); 105 | } catch (e) { 106 | console.error(e); 107 | 108 | return formatResponse( 109 | undefined, 110 | DatabaseErrorCode.ErrorInit, 111 | JSON.stringify(e) 112 | ); 113 | } 114 | } 115 | 116 | export async function getPageFriendList( 117 | offset: number, 118 | count: number, 119 | loginUserID: string 120 | ): Promise { 121 | try { 122 | const db = await getInstance(); 123 | 124 | const execResult = databaseGetPageFriendList( 125 | db, 126 | offset, 127 | count, 128 | loginUserID 129 | ); 130 | 131 | return formatResponse( 132 | converSqlExecResult(execResult[0], 'CamelCase', ['isPinned'], { 133 | name: 'nickname', 134 | friend_user_id: 'userID', 135 | }) 136 | ); 137 | } catch (e) { 138 | console.error(e); 139 | 140 | return formatResponse( 141 | undefined, 142 | DatabaseErrorCode.ErrorInit, 143 | JSON.stringify(e) 144 | ); 145 | } 146 | } 147 | 148 | export async function searchFriendList( 149 | key: string, 150 | isSearchUserID: boolean, 151 | isSearchNickname: boolean, 152 | isSearchRemark: boolean 153 | ): Promise { 154 | try { 155 | const db = await getInstance(); 156 | 157 | const execResult = databasesearchFriendList( 158 | db, 159 | key, 160 | isSearchUserID, 161 | isSearchNickname, 162 | isSearchRemark 163 | ); 164 | 165 | return formatResponse( 166 | converSqlExecResult(execResult[0], 'CamelCase', ['isPinned'], { 167 | name: 'nickname', 168 | friend_user_id: 'userID', 169 | }) 170 | ); 171 | } catch (e) { 172 | console.error(e); 173 | 174 | return formatResponse( 175 | undefined, 176 | DatabaseErrorCode.ErrorInit, 177 | JSON.stringify(e) 178 | ); 179 | } 180 | } 181 | 182 | export async function getFriendInfoByFriendUserID( 183 | friendUserID: string, 184 | loginUserID: string 185 | ): Promise { 186 | try { 187 | const db = await getInstance(); 188 | 189 | const execResult = databaseGetFriendInfoByFriendUserID( 190 | db, 191 | friendUserID, 192 | loginUserID 193 | ); 194 | 195 | if (execResult.length === 0) { 196 | return formatResponse( 197 | '', 198 | DatabaseErrorCode.ErrorNoRecord, 199 | `no friend with id ${friendUserID}` 200 | ); 201 | } 202 | 203 | return formatResponse( 204 | converSqlExecResult(execResult[0], 'CamelCase', ['isPinned'], { 205 | name: 'nickname', 206 | friend_user_id: 'userID', 207 | })[0] 208 | ); 209 | } catch (e) { 210 | console.error(e); 211 | 212 | return formatResponse( 213 | undefined, 214 | DatabaseErrorCode.ErrorInit, 215 | JSON.stringify(e) 216 | ); 217 | } 218 | } 219 | 220 | export async function getFriendInfoList( 221 | friendUserIDListStr: string 222 | ): Promise { 223 | try { 224 | const db = await getInstance(); 225 | 226 | const execResult = databaseGetFriendInfoList( 227 | db, 228 | JSON.parse(friendUserIDListStr) 229 | ); 230 | 231 | return formatResponse( 232 | converSqlExecResult(execResult[0], 'CamelCase', ['isPinned'], { 233 | name: 'nickname', 234 | friend_user_id: 'userID', 235 | }) 236 | ); 237 | } catch (e) { 238 | console.error(e); 239 | 240 | return formatResponse( 241 | undefined, 242 | DatabaseErrorCode.ErrorInit, 243 | JSON.stringify(e) 244 | ); 245 | } 246 | } 247 | 248 | export async function updateColumnsFriend( 249 | friendUserIDListStr: string, 250 | localFriendStr: string 251 | ): Promise { 252 | try { 253 | const db = await getInstance(); 254 | const localFriend = convertToSnakeCaseObject( 255 | convertObjectField(JSON.parse(localFriendStr), { 256 | userID: 'friend_user_id', 257 | nickname: 'name', 258 | }) 259 | ) as LocalFriend; 260 | 261 | databaseupdateColumnsFriend( 262 | db, 263 | JSON.parse(friendUserIDListStr), 264 | localFriend 265 | ); 266 | 267 | return formatResponse(''); 268 | } catch (e) { 269 | console.error(e); 270 | 271 | return formatResponse( 272 | undefined, 273 | DatabaseErrorCode.ErrorInit, 274 | JSON.stringify(e) 275 | ); 276 | } 277 | } 278 | 279 | export async function getFriendListCount(): Promise { 280 | try { 281 | const db = await getInstance(); 282 | 283 | const execResult = databaseGetFriendListCount(db); 284 | 285 | return formatResponse(execResult[0]?.values[0]?.[0] ?? 0); 286 | } catch (e) { 287 | console.error(e); 288 | 289 | return formatResponse( 290 | undefined, 291 | DatabaseErrorCode.ErrorInit, 292 | JSON.stringify(e) 293 | ); 294 | } 295 | } 296 | 297 | export async function batchInsertFriend( 298 | localFriendListStr: string 299 | ): Promise { 300 | try { 301 | const db = await getInstance(); 302 | 303 | const list = JSON.parse(localFriendListStr) as LocalFriend[]; 304 | 305 | list.map(item => { 306 | const localFriend = convertToSnakeCaseObject( 307 | convertObjectField(item, { 308 | userID: 'friend_user_id', 309 | nickname: 'name', 310 | }) 311 | ) as LocalFriend; 312 | databaseInsertFriend(db, localFriend); 313 | 314 | return null; 315 | }); 316 | 317 | return formatResponse(''); 318 | } catch (e) { 319 | console.error(e); 320 | 321 | return formatResponse( 322 | undefined, 323 | DatabaseErrorCode.ErrorInit, 324 | JSON.stringify(e) 325 | ); 326 | } 327 | } 328 | 329 | export async function deleteAllFriend(): Promise { 330 | try { 331 | const db = await getInstance(); 332 | 333 | databaseDeleteAllFriend(db); 334 | 335 | return formatResponse(''); 336 | } catch (e) { 337 | console.error(e); 338 | 339 | return formatResponse( 340 | undefined, 341 | DatabaseErrorCode.ErrorInit, 342 | JSON.stringify(e) 343 | ); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /src/api/database/friendRequest.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | insertFriendRequest as databaseInsertFriendRequest, 4 | deleteFriendRequestBothUserID as databasedeleteFriendRequestBothUserID, 5 | updateFriendRequest as databaseupdateFriendRequest, 6 | getRecvFriendApplication as databaseGetRecvFriendApplication, 7 | getSendFriendApplication as databaseGetSendFriendApplication, 8 | getFriendApplicationByBothID as databaseGetFriendApplicationByBothID, 9 | getBothFriendReq as databaseGetBothFriendReq, 10 | LocalFriendRequest, 11 | } from '@/sqls'; 12 | import { 13 | converSqlExecResult, 14 | convertObjectField, 15 | convertToSnakeCaseObject, 16 | formatResponse, 17 | } from '@/utils'; 18 | import { getInstance } from './instance'; 19 | 20 | export async function insertFriendRequest( 21 | localFriendRequestStr: string 22 | ): Promise { 23 | try { 24 | const db = await getInstance(); 25 | const localFriendRequest = convertToSnakeCaseObject( 26 | convertObjectField(JSON.parse(localFriendRequestStr)) 27 | ) as LocalFriendRequest; 28 | 29 | databaseInsertFriendRequest(db, localFriendRequest); 30 | 31 | return formatResponse(''); 32 | } catch (e) { 33 | console.error(e); 34 | 35 | return formatResponse( 36 | undefined, 37 | DatabaseErrorCode.ErrorInit, 38 | JSON.stringify(e) 39 | ); 40 | } 41 | } 42 | 43 | export async function deleteFriendRequestBothUserID( 44 | fromUserID: string, 45 | toUserID: string 46 | ): Promise { 47 | try { 48 | const db = await getInstance(); 49 | 50 | databasedeleteFriendRequestBothUserID(db, fromUserID, toUserID); 51 | 52 | return formatResponse(''); 53 | } catch (e) { 54 | console.error(e); 55 | 56 | return formatResponse( 57 | undefined, 58 | DatabaseErrorCode.ErrorInit, 59 | JSON.stringify(e) 60 | ); 61 | } 62 | } 63 | 64 | export async function updateFriendRequest( 65 | localFriendRequestStr: string 66 | ): Promise { 67 | try { 68 | const db = await getInstance(); 69 | const localFriendRequest = convertToSnakeCaseObject( 70 | convertObjectField(JSON.parse(localFriendRequestStr)) 71 | ) as LocalFriendRequest; 72 | databaseupdateFriendRequest(db, localFriendRequest); 73 | 74 | return formatResponse(''); 75 | } catch (e) { 76 | console.error(e); 77 | 78 | return formatResponse( 79 | undefined, 80 | DatabaseErrorCode.ErrorInit, 81 | JSON.stringify(e) 82 | ); 83 | } 84 | } 85 | 86 | export async function getRecvFriendApplication( 87 | loginUserID: string 88 | ): Promise { 89 | try { 90 | const db = await getInstance(); 91 | 92 | const execResult = databaseGetRecvFriendApplication(db, loginUserID); 93 | 94 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 95 | } catch (e) { 96 | console.error(e); 97 | 98 | return formatResponse( 99 | undefined, 100 | DatabaseErrorCode.ErrorInit, 101 | JSON.stringify(e) 102 | ); 103 | } 104 | } 105 | 106 | export async function getSendFriendApplication( 107 | fromUserId: string 108 | ): Promise { 109 | try { 110 | const db = await getInstance(); 111 | 112 | const execResult = databaseGetSendFriendApplication(db, fromUserId); 113 | 114 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 115 | } catch (e) { 116 | console.error(e); 117 | 118 | return formatResponse( 119 | undefined, 120 | DatabaseErrorCode.ErrorInit, 121 | JSON.stringify(e) 122 | ); 123 | } 124 | } 125 | 126 | export async function getFriendApplicationByBothID( 127 | fromUserID: string, 128 | toUserID: boolean 129 | ): Promise { 130 | try { 131 | const db = await getInstance(); 132 | 133 | const execResult = databaseGetFriendApplicationByBothID( 134 | db, 135 | fromUserID, 136 | toUserID 137 | ); 138 | 139 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 140 | } catch (e) { 141 | console.error(e); 142 | 143 | return formatResponse( 144 | undefined, 145 | DatabaseErrorCode.ErrorInit, 146 | JSON.stringify(e) 147 | ); 148 | } 149 | } 150 | 151 | export async function getBothFriendReq( 152 | fromUserID: string, 153 | toUserID: boolean 154 | ): Promise { 155 | try { 156 | const db = await getInstance(); 157 | 158 | const execResult = databaseGetBothFriendReq(db, fromUserID, toUserID); 159 | 160 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 161 | } catch (e) { 162 | console.error(e); 163 | 164 | return formatResponse( 165 | undefined, 166 | DatabaseErrorCode.ErrorInit, 167 | JSON.stringify(e) 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/api/database/groupRequest.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | insertGroupRequest as databaseInsertGroupRequest, 4 | deleteGroupRequest as databaseDeleteGroupRequest, 5 | updateGroupRequest as databaseUpdateGroupRequest, 6 | getSendGroupApplication as databaseGetSendGroupApplication, 7 | insertAdminGroupRequest as databaseInsertAdminGroupRequest, 8 | deleteAdminGroupRequest as databaseDeleteAdminGroupRequest, 9 | updateAdminGroupRequest as databaseUpdateAdminGroupRequest, 10 | getAdminGroupApplication as databaseGetAdminGroupApplication, 11 | LocalGroupRequest, 12 | } from '@/sqls'; 13 | import { 14 | convertToSnakeCaseObject, 15 | convertObjectField, 16 | formatResponse, 17 | converSqlExecResult, 18 | } from '@/utils'; 19 | import { getInstance } from './instance'; 20 | 21 | export async function insertGroupRequest( 22 | localGroupRequestStr: string 23 | ): Promise { 24 | try { 25 | const db = await getInstance(); 26 | 27 | const localGroupRequest = convertToSnakeCaseObject( 28 | convertObjectField(JSON.parse(localGroupRequestStr), { 29 | groupFaceURL: 'face_url', 30 | userFaceURL: 'user_face_url', 31 | handledMsg: 'handle_msg', 32 | handledTime: 'handle_time', 33 | }) 34 | ) as LocalGroupRequest; 35 | 36 | databaseInsertGroupRequest(db, localGroupRequest); 37 | 38 | return formatResponse(''); 39 | } catch (e) { 40 | console.error(e); 41 | 42 | return formatResponse( 43 | undefined, 44 | DatabaseErrorCode.ErrorInit, 45 | JSON.stringify(e) 46 | ); 47 | } 48 | } 49 | 50 | export async function deleteGroupRequest( 51 | groupID: string, 52 | userID: string 53 | ): Promise { 54 | try { 55 | const db = await getInstance(); 56 | 57 | databaseDeleteGroupRequest(db, groupID, userID); 58 | 59 | return formatResponse(''); 60 | } catch (e) { 61 | console.error(e); 62 | 63 | return formatResponse( 64 | undefined, 65 | DatabaseErrorCode.ErrorInit, 66 | JSON.stringify(e) 67 | ); 68 | } 69 | } 70 | 71 | export async function updateGroupRequest( 72 | localGroupRequestStr: string 73 | ): Promise { 74 | try { 75 | const db = await getInstance(); 76 | const localGroupRequest = convertToSnakeCaseObject( 77 | convertObjectField(JSON.parse(localGroupRequestStr), { 78 | groupFaceURL: 'face_url', 79 | userFaceURL: 'user_face_url', 80 | handledMsg: 'handle_msg', 81 | handledTime: 'handle_time', 82 | }) 83 | ) as LocalGroupRequest; 84 | databaseUpdateGroupRequest(db, localGroupRequest); 85 | 86 | return formatResponse(''); 87 | } catch (e) { 88 | console.error(e); 89 | 90 | return formatResponse( 91 | undefined, 92 | DatabaseErrorCode.ErrorInit, 93 | JSON.stringify(e) 94 | ); 95 | } 96 | } 97 | 98 | export async function getSendGroupApplication(): Promise { 99 | try { 100 | const db = await getInstance(); 101 | 102 | const execResult = databaseGetSendGroupApplication(db); 103 | 104 | return formatResponse( 105 | converSqlExecResult(execResult[0], 'CamelCase', [], { 106 | face_url: 'groupFaceURL', 107 | user_face_url: 'userFaceURL', 108 | handle_msg: 'handledMsg', 109 | handle_time: 'handledTime', 110 | }) 111 | ); 112 | } catch (e) { 113 | console.error(e); 114 | 115 | return formatResponse( 116 | undefined, 117 | DatabaseErrorCode.ErrorInit, 118 | JSON.stringify(e) 119 | ); 120 | } 121 | } 122 | 123 | export async function insertAdminGroupRequest( 124 | localAdminGroupRequestStr: string 125 | ): Promise { 126 | try { 127 | const db = await getInstance(); 128 | 129 | const localAminGroupRequest = convertToSnakeCaseObject( 130 | convertObjectField(JSON.parse(localAdminGroupRequestStr), { 131 | groupFaceURL: 'face_url', 132 | userFaceURL: 'user_face_url', 133 | handledMsg: 'handle_msg', 134 | handledTime: 'handle_time', 135 | }) 136 | ) as LocalGroupRequest; 137 | 138 | databaseInsertAdminGroupRequest(db, localAminGroupRequest); 139 | 140 | return formatResponse(''); 141 | } catch (e) { 142 | console.error(e); 143 | 144 | return formatResponse( 145 | undefined, 146 | DatabaseErrorCode.ErrorInit, 147 | JSON.stringify(e) 148 | ); 149 | } 150 | } 151 | 152 | export async function deleteAdminGroupRequest( 153 | groupID: string, 154 | userID: string 155 | ): Promise { 156 | try { 157 | const db = await getInstance(); 158 | 159 | databaseDeleteAdminGroupRequest(db, groupID, userID); 160 | 161 | return formatResponse(''); 162 | } catch (e) { 163 | console.error(e); 164 | 165 | return formatResponse( 166 | undefined, 167 | DatabaseErrorCode.ErrorInit, 168 | JSON.stringify(e) 169 | ); 170 | } 171 | } 172 | 173 | export async function updateAdminGroupRequest( 174 | localGroupRequestStr: string 175 | ): Promise { 176 | try { 177 | const db = await getInstance(); 178 | const localGroupRequest = convertToSnakeCaseObject( 179 | convertObjectField(JSON.parse(localGroupRequestStr), { 180 | groupFaceURL: 'face_url', 181 | userFaceURL: 'user_face_url', 182 | handledMsg: 'handle_msg', 183 | handledTime: 'handle_time', 184 | }) 185 | ) as LocalGroupRequest; 186 | databaseUpdateAdminGroupRequest(db, localGroupRequest); 187 | 188 | return formatResponse(''); 189 | } catch (e) { 190 | console.error(e); 191 | 192 | return formatResponse( 193 | undefined, 194 | DatabaseErrorCode.ErrorInit, 195 | JSON.stringify(e) 196 | ); 197 | } 198 | } 199 | 200 | export async function getAdminGroupApplication(): Promise { 201 | try { 202 | const db = await getInstance(); 203 | 204 | const execResult = databaseGetAdminGroupApplication(db); 205 | 206 | return formatResponse( 207 | converSqlExecResult(execResult[0], 'CamelCase', [], { 208 | face_url: 'groupFaceURL', 209 | user_face_url: 'userFaceURL', 210 | handle_msg: 'handledMsg', 211 | handle_time: 'handledTime', 212 | }) 213 | ); 214 | } catch (e) { 215 | console.error(e); 216 | 217 | return formatResponse( 218 | undefined, 219 | DatabaseErrorCode.ErrorInit, 220 | JSON.stringify(e) 221 | ); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/api/database/groups.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | insertGroup as databaseInsertGroup, 4 | deleteGroup as databasedeleteGroup, 5 | updateGroup as databaseupdateGroup, 6 | getJoinedGroupList as databaseGetJoinedGroupList, 7 | getGroupInfoByGroupID as databaseGetGroupInfoByGroupID, 8 | getGroupMemberAllGroupIDs as databaseGetGroupMemberAllGroupIDs, 9 | getAllGroupInfoByGroupIDOrGroupName as databaseGetAllGroupInfoByGroupIDOrGroupName, 10 | subtractMemberCount as databaseSubtractMemberCount, 11 | addMemberCount as databaseAddMemberCount, 12 | getGroups as databaseGetGroups, 13 | deleteAllGroup as databaseDeleteAllGroup, 14 | LocalGroup, 15 | } from '@/sqls'; 16 | import { 17 | converSqlExecResult, 18 | convertObjectField, 19 | convertToSnakeCaseObject, 20 | formatResponse, 21 | } from '@/utils'; 22 | import { getInstance } from './instance'; 23 | 24 | export async function insertGroup(localGroupStr: string): Promise { 25 | try { 26 | const db = await getInstance(); 27 | 28 | const localGroup = convertToSnakeCaseObject( 29 | convertObjectField(JSON.parse(localGroupStr), { groupName: 'name' }) 30 | ) as LocalGroup; 31 | 32 | databaseInsertGroup(db, localGroup); 33 | 34 | return formatResponse(''); 35 | } catch (e) { 36 | console.error(e); 37 | 38 | return formatResponse( 39 | undefined, 40 | DatabaseErrorCode.ErrorInit, 41 | JSON.stringify(e) 42 | ); 43 | } 44 | } 45 | 46 | export async function deleteGroup(groupID: string): Promise { 47 | try { 48 | const db = await getInstance(); 49 | 50 | databasedeleteGroup(db, groupID); 51 | 52 | return formatResponse(''); 53 | } catch (e) { 54 | console.error(e); 55 | 56 | return formatResponse( 57 | undefined, 58 | DatabaseErrorCode.ErrorInit, 59 | JSON.stringify(e) 60 | ); 61 | } 62 | } 63 | 64 | export async function updateGroup( 65 | groupID: string, 66 | localGroupStr: string 67 | ): Promise { 68 | try { 69 | const db = await getInstance(); 70 | 71 | const localGroup = convertToSnakeCaseObject( 72 | convertObjectField(JSON.parse(localGroupStr), { groupName: 'name' }) 73 | ) as LocalGroup; 74 | 75 | databaseupdateGroup(db, groupID, localGroup); 76 | 77 | return formatResponse(''); 78 | } catch (e) { 79 | console.error(e); 80 | 81 | return formatResponse( 82 | undefined, 83 | DatabaseErrorCode.ErrorInit, 84 | JSON.stringify(e) 85 | ); 86 | } 87 | } 88 | 89 | export async function getJoinedGroupList(): Promise { 90 | try { 91 | const db = await getInstance(); 92 | 93 | const execResult = databaseGetJoinedGroupList(db); 94 | 95 | return formatResponse( 96 | converSqlExecResult(execResult[0], 'CamelCase', ['displayIsRead'], { 97 | name: 'groupName', 98 | }) 99 | ); 100 | } catch (e) { 101 | console.error(e); 102 | 103 | return formatResponse( 104 | undefined, 105 | DatabaseErrorCode.ErrorInit, 106 | JSON.stringify(e) 107 | ); 108 | } 109 | } 110 | 111 | export async function getGroupInfoByGroupID(groupID: string): Promise { 112 | try { 113 | const db = await getInstance(); 114 | 115 | const execResult = databaseGetGroupInfoByGroupID(db, groupID); 116 | 117 | if (execResult.length === 0) { 118 | return formatResponse( 119 | '', 120 | DatabaseErrorCode.ErrorNoRecord, 121 | `no group with id ${groupID}` 122 | ); 123 | } 124 | 125 | return formatResponse( 126 | converSqlExecResult(execResult[0], 'CamelCase', ['displayIsRead'], { 127 | name: 'groupName', 128 | })[0] 129 | ); 130 | } catch (e) { 131 | console.error(e); 132 | 133 | return formatResponse( 134 | undefined, 135 | DatabaseErrorCode.ErrorInit, 136 | JSON.stringify(e) 137 | ); 138 | } 139 | } 140 | 141 | export async function getAllGroupInfoByGroupIDOrGroupName( 142 | keyword: string, 143 | isSearchGroupID: boolean, 144 | isSearchGroupName: boolean 145 | ): Promise { 146 | try { 147 | const db = await getInstance(); 148 | 149 | const execResult = databaseGetAllGroupInfoByGroupIDOrGroupName( 150 | db, 151 | keyword, 152 | isSearchGroupID, 153 | isSearchGroupName 154 | ); 155 | 156 | return formatResponse( 157 | converSqlExecResult(execResult[0], 'CamelCase', ['displayIsRead'], { 158 | name: 'groupName', 159 | }) 160 | ); 161 | } catch (e) { 162 | console.error(e); 163 | 164 | return formatResponse( 165 | undefined, 166 | DatabaseErrorCode.ErrorInit, 167 | JSON.stringify(e) 168 | ); 169 | } 170 | } 171 | 172 | export async function subtractMemberCount(groupID: string): Promise { 173 | try { 174 | const db = await getInstance(); 175 | 176 | databaseSubtractMemberCount(db, groupID); 177 | 178 | return formatResponse(''); 179 | } catch (e) { 180 | console.error(e); 181 | 182 | return formatResponse( 183 | undefined, 184 | DatabaseErrorCode.ErrorInit, 185 | JSON.stringify(e) 186 | ); 187 | } 188 | } 189 | 190 | export async function addMemberCount(groupID: string): Promise { 191 | try { 192 | const db = await getInstance(); 193 | 194 | databaseAddMemberCount(db, groupID); 195 | 196 | return formatResponse(''); 197 | } catch (e) { 198 | console.error(e); 199 | 200 | return formatResponse( 201 | undefined, 202 | DatabaseErrorCode.ErrorInit, 203 | JSON.stringify(e) 204 | ); 205 | } 206 | } 207 | 208 | export async function getJoinedWorkingGroupIDList(): Promise { 209 | try { 210 | const db = await getInstance(); 211 | 212 | const execResult = databaseGetJoinedGroupList(db); 213 | const allJoinedGroupList = converSqlExecResult(execResult[0], 'CamelCase', [ 214 | 'displayIsRead', 215 | ]); 216 | const filterIDList = [] as string[]; 217 | allJoinedGroupList.forEach(group => { 218 | if (group.groupType === 2) { 219 | filterIDList.push(group.groupID as string); 220 | } 221 | }); 222 | return formatResponse(JSON.stringify(filterIDList)); 223 | } catch (e) { 224 | console.error(e); 225 | 226 | return formatResponse( 227 | undefined, 228 | DatabaseErrorCode.ErrorInit, 229 | JSON.stringify(e) 230 | ); 231 | } 232 | } 233 | 234 | export async function getJoinedWorkingGroupList(): Promise { 235 | try { 236 | const db = await getInstance(); 237 | 238 | const execResult = databaseGetJoinedGroupList(db); 239 | const allJoinedGroupList = converSqlExecResult( 240 | execResult[0], 241 | 'CamelCase', 242 | ['displayIsRead'], 243 | { name: 'groupName' } 244 | ); 245 | const filterList = allJoinedGroupList.filter( 246 | group => group.groupType === 2 247 | ); 248 | 249 | return formatResponse(JSON.stringify(filterList)); 250 | } catch (e) { 251 | console.error(e); 252 | 253 | return formatResponse( 254 | undefined, 255 | DatabaseErrorCode.ErrorInit, 256 | JSON.stringify(e) 257 | ); 258 | } 259 | } 260 | 261 | export async function getGroupMemberAllGroupIDs(): Promise { 262 | try { 263 | const db = await getInstance(); 264 | 265 | const execResult = databaseGetGroupMemberAllGroupIDs(db); 266 | return formatResponse( 267 | converSqlExecResult(execResult[0], 'CamelCase', ['displayIsRead']).map( 268 | item => item.groupID 269 | ) 270 | ); 271 | } catch (e) { 272 | console.error(e); 273 | 274 | return formatResponse( 275 | undefined, 276 | DatabaseErrorCode.ErrorInit, 277 | JSON.stringify(e) 278 | ); 279 | } 280 | } 281 | 282 | export async function getGroups(groupIDListStr: string): Promise { 283 | try { 284 | const db = await getInstance(); 285 | 286 | const execResult = databaseGetGroups(db, JSON.parse(groupIDListStr)); 287 | const allJoinedGroupList = converSqlExecResult( 288 | execResult[0], 289 | 'CamelCase', 290 | ['displayIsRead'], 291 | { name: 'groupName' } 292 | ); 293 | return formatResponse(JSON.stringify(allJoinedGroupList)); 294 | } catch (e) { 295 | console.error(e); 296 | 297 | return formatResponse( 298 | undefined, 299 | DatabaseErrorCode.ErrorInit, 300 | JSON.stringify(e) 301 | ); 302 | } 303 | } 304 | 305 | export async function batchInsertGroup( 306 | localGroupListStr: string 307 | ): Promise { 308 | try { 309 | const db = await getInstance(); 310 | 311 | const list = JSON.parse(localGroupListStr) as LocalGroup[]; 312 | 313 | list.map(item => { 314 | const localGroup = convertToSnakeCaseObject( 315 | convertObjectField(item, { groupName: 'name' }) 316 | ) as LocalGroup; 317 | databaseInsertGroup(db, localGroup); 318 | 319 | return null; 320 | }); 321 | 322 | return formatResponse(''); 323 | } catch (e) { 324 | console.error(e); 325 | 326 | return formatResponse( 327 | undefined, 328 | DatabaseErrorCode.ErrorInit, 329 | JSON.stringify(e) 330 | ); 331 | } 332 | } 333 | 334 | export async function deleteAllGroup(): Promise { 335 | try { 336 | const db = await getInstance(); 337 | 338 | databaseDeleteAllGroup(db); 339 | 340 | return formatResponse(''); 341 | } catch (e) { 342 | console.error(e); 343 | 344 | return formatResponse( 345 | undefined, 346 | DatabaseErrorCode.ErrorInit, 347 | JSON.stringify(e) 348 | ); 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/api/database/index.ts: -------------------------------------------------------------------------------- 1 | export * from './init'; 2 | export * from './message'; 3 | export * from './conversation'; 4 | export * from './users'; 5 | export * from './superGroup'; 6 | export * from './unreadMessage'; 7 | export * from './black'; 8 | export * from './friend'; 9 | export * from './friendRequest'; 10 | export * from './groups'; 11 | export * from './groupRequest'; 12 | export * from './groupMember'; 13 | export * from './tempCacheChatLogs'; 14 | export * from './upload'; 15 | export * from './stranger'; 16 | export * from './sendingMessages'; 17 | export * from './localAppSdkVersion'; 18 | export * from './versionSync'; 19 | export * from './notification'; 20 | export * from './tableMaster'; 21 | -------------------------------------------------------------------------------- /src/api/database/init.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | locaBlacks, 4 | localFriends, 5 | localGroups, 6 | localFriendRequests, 7 | localGroupRequests, 8 | localAdminGroupRequests, 9 | localConversations, 10 | localUsers, 11 | localSuperGroups, 12 | localConversationUnreadMessages, 13 | localGroupMembers, 14 | tempCacheLocalChatLogs, 15 | localNotification, 16 | localUploads, 17 | localStranger, 18 | localSendingMessages, 19 | localAppSDKVersions, 20 | localVersionSyncs, 21 | } from '@/sqls'; 22 | import { formatResponse } from '@/utils'; 23 | import { QueryExecResult } from '@jlongster/sql.js'; 24 | import { getInstance, resetInstance } from './instance'; 25 | import { alterTable } from './alter'; 26 | 27 | let sqlWasmPath: string; 28 | 29 | export function setSqlWasmPath(wasmPath: string) { 30 | sqlWasmPath = wasmPath; 31 | } 32 | 33 | export async function init(userId: string, dir: string): Promise { 34 | // console.info( 35 | // `=> (database api) invoke init with args ${JSON.stringify({ 36 | // userId, 37 | // dir, 38 | // })}` 39 | // ); 40 | 41 | try { 42 | // console.time('SDK => (performance measure) init database used '); 43 | 44 | const db = await getInstance(`${dir}${userId}.sqlite`, sqlWasmPath); 45 | const results: QueryExecResult[][] = []; 46 | const execResultLocalUploads = localUploads(db); 47 | const execResultLocalStrangers = localStranger(db); 48 | const execResultLocalConversations = localConversations(db); 49 | const execResultLocalUsers = localUsers(db); 50 | const execResultLocalBlack = locaBlacks(db); 51 | const execResultLocalFriend = localFriends(db); 52 | const execResuLocalGroup = localGroups(db); 53 | const execResuLocalGroupRequest = localGroupRequests(db); 54 | const execResuLocalGroupMembers = localGroupMembers(db); 55 | const execResuLocalAdminGroupRequest = localAdminGroupRequests(db); 56 | const execResultlocaFendRequest = localFriendRequests(db); 57 | const execResultLocalSuperGroups = localSuperGroups(db); 58 | const execResultTempCacheLocalChatLogs = tempCacheLocalChatLogs(db); 59 | const execResultLocalNotification = localNotification(db); 60 | const execResultLocalSendMessages = localSendingMessages(db); 61 | const execResultLocalConversationUnreadMessages = 62 | localConversationUnreadMessages(db); 63 | const execResultLocalAppSDKVersions = localAppSDKVersions(db); 64 | const execResultLocalVersionSync = localVersionSyncs(db); 65 | alterTable(db); 66 | results.push( 67 | ...[ 68 | execResultLocalUploads, 69 | execResultLocalStrangers, 70 | execResultLocalConversations, 71 | execResultLocalUsers, 72 | execResultLocalSuperGroups, 73 | execResultLocalConversationUnreadMessages, 74 | execResultLocalBlack, 75 | execResultLocalFriend, 76 | execResuLocalGroup, 77 | execResuLocalGroupMembers, 78 | execResultlocaFendRequest, 79 | execResuLocalGroupRequest, 80 | execResuLocalAdminGroupRequest, 81 | execResultTempCacheLocalChatLogs, 82 | execResultLocalNotification, 83 | execResultLocalSendMessages, 84 | execResultLocalAppSDKVersions, 85 | execResultLocalVersionSync, 86 | ] 87 | ); 88 | 89 | return formatResponse(results); 90 | } catch (e) { 91 | console.error(e); 92 | 93 | return formatResponse( 94 | undefined, 95 | DatabaseErrorCode.ErrorInit, 96 | JSON.stringify(e) 97 | ); 98 | } finally { 99 | // console.timeEnd('SDK => (performance measure) init database used '); 100 | } 101 | } 102 | 103 | export async function close() { 104 | // console.info('=> (database api) invoke close'); 105 | 106 | try { 107 | await resetInstance(); 108 | 109 | return formatResponse(''); 110 | } catch (e) { 111 | console.error(e); 112 | 113 | return formatResponse( 114 | undefined, 115 | DatabaseErrorCode.ErrorInit, 116 | JSON.stringify(e) 117 | ); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/api/database/instance.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 3 | import initSqlJs, { Database, SqlJsStatic } from '@jlongster/sql.js'; 4 | import { SQLiteFS } from 'absurd-sql-optimized'; 5 | import IndexedDBBackend from 'absurd-sql-optimized/dist/indexeddb-backend'; 6 | (self as any).$RefreshReg$ = () => {}; 7 | (self as any).$RefreshSig$ = () => () => {}; 8 | 9 | let instance: Promise | undefined; 10 | let SQL: SqlJsStatic | undefined; 11 | 12 | async function InitializeDB(filePath: string, sqlWasmPath = '/sql-wasm.wasm') { 13 | if (!SQL) { 14 | SQL = await initSqlJs({ locateFile: () => sqlWasmPath }); 15 | const sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend()); 16 | 17 | SQL.register_for_idb(sqlFS); 18 | SQL.FS.mkdir('/sql'); 19 | SQL.FS.mount(sqlFS, {}, '/sql'); 20 | } 21 | 22 | const path = `/sql/${filePath}`; 23 | 24 | const db = new SQL.Database(path, { filename: true }); 25 | 26 | if (typeof SharedArrayBuffer === 'undefined') { 27 | // @ts-ignore 28 | const stream = SQL.FS.open(path, 'a+'); 29 | await stream.node.contents.readIfFallback(); 30 | // @ts-ignore 31 | SQL.FS.close(stream); 32 | } 33 | 34 | db.exec(` 35 | PRAGMA page_size=8192; 36 | PRAGMA journal_mode=MEMORY; 37 | `); 38 | 39 | return db; 40 | } 41 | 42 | export function getInstance( 43 | filePath?: string, 44 | sqlWasmPath?: string 45 | ): Promise { 46 | if (instance) { 47 | return instance; 48 | } 49 | 50 | if (!filePath) { 51 | throw new Error('must speciefic database file'); 52 | } 53 | 54 | instance = new Promise((resolve, reject) => { 55 | const db = InitializeDB(filePath, sqlWasmPath); 56 | db.then(res => resolve(res)).catch(err => reject(err)); 57 | }); 58 | 59 | return instance; 60 | } 61 | 62 | export async function resetInstance() { 63 | if (!instance) { 64 | return; 65 | } 66 | 67 | const db = await instance; 68 | 69 | db.close(); 70 | instance = undefined; 71 | } 72 | -------------------------------------------------------------------------------- /src/api/database/localAppSdkVersion.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | LocalAppSDKVersion, 4 | getAppSDKVersion as databaseGetAppSDKVersion, 5 | insertAppSDKVersion as databaseInsertAppSDKVersion, 6 | updateAppSDKVersion as databaseUpdateAppSDKVersion, 7 | } from '@/sqls'; 8 | import { 9 | converSqlExecResult, 10 | convertObjectField, 11 | convertToSnakeCaseObject, 12 | formatResponse, 13 | } from '@/utils'; 14 | import { getInstance } from './instance'; 15 | 16 | export async function getAppSDKVersion(): Promise { 17 | try { 18 | const db = await getInstance(); 19 | 20 | const execResult = databaseGetAppSDKVersion(db); 21 | 22 | if (execResult.length === 0) { 23 | return formatResponse( 24 | '', 25 | DatabaseErrorCode.ErrorNoRecord, 26 | 'no app version with database' 27 | ); 28 | } 29 | 30 | return formatResponse( 31 | converSqlExecResult(execResult[0], 'CamelCase', [])[0] 32 | ); 33 | } catch (e) { 34 | console.error(e); 35 | 36 | return formatResponse( 37 | undefined, 38 | DatabaseErrorCode.ErrorInit, 39 | JSON.stringify(e) 40 | ); 41 | } 42 | } 43 | 44 | export async function setAppSDKVersion( 45 | appSdkVersionStr: string 46 | ): Promise { 47 | try { 48 | const db = await getInstance(); 49 | 50 | const localAppSDKVersion = convertToSnakeCaseObject( 51 | convertObjectField(JSON.parse(appSdkVersionStr)) 52 | ) as LocalAppSDKVersion; 53 | 54 | const execResult = databaseGetAppSDKVersion(db); 55 | 56 | const result = converSqlExecResult(execResult[0], 'CamelCase', []); 57 | if (result[0] && result[0].version) { 58 | databaseUpdateAppSDKVersion( 59 | db, 60 | result[0].version as string, 61 | localAppSDKVersion 62 | ); 63 | return formatResponse(''); 64 | } else { 65 | databaseInsertAppSDKVersion(db, localAppSDKVersion); 66 | return formatResponse(''); 67 | } 68 | } catch (e) { 69 | console.error(e); 70 | 71 | return formatResponse( 72 | undefined, 73 | DatabaseErrorCode.ErrorInit, 74 | JSON.stringify(e) 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/api/database/notification.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | insertNotificationSeq as databaseInsertNotificationSeq, 4 | setNotificationSeq as databaseSetNotificationSeq, 5 | getNotificationAllSeqs as databaseGetNotificationAllSeqs, 6 | LocalNotification, 7 | } from '@/sqls'; 8 | import { converSqlExecResult, formatResponse } from '@/utils'; 9 | import { getInstance } from './instance'; 10 | 11 | export async function setNotificationSeq( 12 | conversationID: string, 13 | seq: number 14 | ): Promise { 15 | try { 16 | const db = await getInstance(); 17 | let execResult = databaseSetNotificationSeq(db, conversationID, seq); 18 | 19 | const modified = db.getRowsModified(); 20 | if (modified === 0) { 21 | execResult = databaseInsertNotificationSeq(db, conversationID, seq); 22 | } 23 | 24 | return formatResponse(execResult[0]); 25 | } catch (e) { 26 | console.error(e); 27 | 28 | return formatResponse( 29 | undefined, 30 | DatabaseErrorCode.ErrorInit, 31 | JSON.stringify(e) 32 | ); 33 | } 34 | } 35 | 36 | export async function getNotificationAllSeqs(): Promise { 37 | try { 38 | const db = await getInstance(); 39 | const execResult = databaseGetNotificationAllSeqs(db); 40 | 41 | return formatResponse(converSqlExecResult(execResult[0], 'CamelCase')); 42 | } catch (e) { 43 | console.error(e); 44 | 45 | return formatResponse( 46 | undefined, 47 | DatabaseErrorCode.ErrorInit, 48 | JSON.stringify(e) 49 | ); 50 | } 51 | } 52 | 53 | export async function batchInsertNotificationSeq( 54 | local_notification_seqs: string 55 | ): Promise { 56 | try { 57 | const list = JSON.parse(local_notification_seqs) as LocalNotification[]; 58 | const db = await getInstance(); 59 | 60 | list.map(item => { 61 | const { conversationID, seq } = item; 62 | databaseInsertNotificationSeq(db, conversationID, seq); 63 | }); 64 | 65 | return formatResponse(''); 66 | } catch (e) { 67 | console.error(e); 68 | 69 | return formatResponse( 70 | undefined, 71 | DatabaseErrorCode.ErrorInit, 72 | JSON.stringify(e) 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/api/database/sendingMessages.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | LocalSendingMessage, 4 | insertSendingMessage as databaseInsertSendingMessage, 5 | deleteSendingMessage as databaseDeleteSendingMessage, 6 | getAllSendingMessages as datbaseGetAllSendingMessages, 7 | } from '@/sqls'; 8 | import { 9 | converSqlExecResult, 10 | convertToSnakeCaseObject, 11 | formatResponse, 12 | } from '@/utils'; 13 | import { getInstance } from './instance'; 14 | 15 | export async function insertSendingMessage( 16 | sendMessageStr: string 17 | ): Promise { 18 | try { 19 | const db = await getInstance(); 20 | const message = convertToSnakeCaseObject( 21 | JSON.parse(sendMessageStr) 22 | ) as LocalSendingMessage; 23 | 24 | const execResult = databaseInsertSendingMessage(db, message); 25 | 26 | return formatResponse(execResult); 27 | } catch (e) { 28 | console.error(e); 29 | 30 | return formatResponse( 31 | undefined, 32 | DatabaseErrorCode.ErrorInit, 33 | JSON.stringify(e) 34 | ); 35 | } 36 | } 37 | 38 | export async function deleteSendingMessage( 39 | conversationID: string, 40 | clientMsgID: string 41 | ): Promise { 42 | try { 43 | const db = await getInstance(); 44 | 45 | const execResult = databaseDeleteSendingMessage( 46 | db, 47 | conversationID, 48 | clientMsgID 49 | ); 50 | 51 | return formatResponse(execResult); 52 | } catch (e) { 53 | console.error(e); 54 | 55 | return formatResponse( 56 | undefined, 57 | DatabaseErrorCode.ErrorInit, 58 | JSON.stringify(e) 59 | ); 60 | } 61 | } 62 | 63 | export async function getAllSendingMessages(): Promise { 64 | try { 65 | const db = await getInstance(); 66 | 67 | const execResult = datbaseGetAllSendingMessages(db); 68 | 69 | return formatResponse( 70 | converSqlExecResult(execResult[0], 'CamelCase', [ 71 | 'isRead', 72 | 'isReact', 73 | 'isExternalExtensions', 74 | ]) 75 | ); 76 | } catch (e) { 77 | console.error(e); 78 | 79 | return formatResponse( 80 | undefined, 81 | DatabaseErrorCode.ErrorInit, 82 | JSON.stringify(e) 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/api/database/stranger.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LocalStranger, 3 | getStrangerInfo as databaseGetStrangerInfo, 4 | insertStrangerInfo as databseInsertStrangerInfo, 5 | updateStrangerInfo as databaseUpdateStrangerInfo, 6 | } from '@/sqls'; 7 | import { getInstance } from './instance'; 8 | import { 9 | converSqlExecResult, 10 | convertObjectField, 11 | convertToSnakeCaseObject, 12 | formatResponse, 13 | } from '@/utils'; 14 | import { DatabaseErrorCode } from '@/constant'; 15 | 16 | export async function getStrangerInfo(userIDListStr: string): Promise { 17 | try { 18 | const db = await getInstance(); 19 | const execResult = databaseGetStrangerInfo(db, JSON.parse(userIDListStr)); 20 | 21 | return formatResponse( 22 | converSqlExecResult(execResult[0], 'CamelCase', [], { 23 | name: 'nickname', 24 | }) 25 | ); 26 | } catch (e) { 27 | console.error(e); 28 | 29 | return formatResponse( 30 | undefined, 31 | DatabaseErrorCode.ErrorInit, 32 | JSON.stringify(e) 33 | ); 34 | } 35 | } 36 | 37 | export function setStrangerInfo( 38 | localStrangerInfoListStr: string 39 | ): Promise { 40 | try { 41 | const localStrangerInfoList = ( 42 | JSON.parse(localStrangerInfoListStr) as LocalStranger[] 43 | ).map( 44 | item => 45 | convertToSnakeCaseObject( 46 | convertObjectField(item, { 47 | nickname: 'name', 48 | }) 49 | ) as LocalStranger 50 | ); 51 | localStrangerInfoList.map((localStrangerInfo: LocalStranger) => 52 | setSingleStrangerInfo(localStrangerInfo) 53 | ); 54 | 55 | return Promise.resolve(formatResponse('')); 56 | } catch (e) { 57 | console.error(e); 58 | 59 | return Promise.resolve( 60 | formatResponse(undefined, DatabaseErrorCode.ErrorInit, JSON.stringify(e)) 61 | ); 62 | } 63 | } 64 | 65 | export async function setSingleStrangerInfo( 66 | localStrangerInfo: LocalStranger 67 | ): Promise { 68 | try { 69 | const db = await getInstance(); 70 | 71 | const execResult = databaseGetStrangerInfo(db, [localStrangerInfo.user_id]); 72 | const result = converSqlExecResult(execResult[0]); 73 | if (result.length) { 74 | databaseUpdateStrangerInfo(db, localStrangerInfo); 75 | } else { 76 | databseInsertStrangerInfo(db, localStrangerInfo); 77 | } 78 | return formatResponse(''); 79 | } catch (e) { 80 | console.error(e); 81 | 82 | return formatResponse( 83 | undefined, 84 | DatabaseErrorCode.ErrorInit, 85 | JSON.stringify(e) 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/api/database/superGroup.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | getJoinedSuperGroupList as databaseGetJoinedSuperGroupList, 4 | insertSuperGroup as databaseInsertSuperGroup, 5 | updateSuperGroup as databaseUpdateSuperGroup, 6 | deleteSuperGroup as databaseDeleteSuperGroup, 7 | getSuperGroupInfoByGroupID as databaseGetSuperGroupInfoByGroupID, 8 | ClientGroup, 9 | } from '@/sqls'; 10 | import { 11 | formatResponse, 12 | converSqlExecResult, 13 | convertToSnakeCaseObject, 14 | convertObjectField, 15 | } from '@/utils'; 16 | import { getInstance } from './instance'; 17 | 18 | export async function getJoinedSuperGroupList(): Promise { 19 | try { 20 | const db = await getInstance(); 21 | 22 | const execResult = databaseGetJoinedSuperGroupList(db); 23 | 24 | return formatResponse(converSqlExecResult(execResult[0])); 25 | } catch (e) { 26 | console.error(e); 27 | 28 | return formatResponse( 29 | undefined, 30 | DatabaseErrorCode.ErrorInit, 31 | JSON.stringify(e) 32 | ); 33 | } 34 | } 35 | 36 | export async function getJoinedSuperGroupIDList(): Promise { 37 | try { 38 | const db = await getInstance(); 39 | 40 | const execResult = databaseGetJoinedSuperGroupList(db); 41 | const records = converSqlExecResult(execResult[0]); 42 | const groupIds = records.map(r => r.groupID); 43 | 44 | return formatResponse(groupIds); 45 | } catch (e) { 46 | console.error(e); 47 | 48 | return formatResponse( 49 | undefined, 50 | DatabaseErrorCode.ErrorInit, 51 | JSON.stringify(e) 52 | ); 53 | } 54 | } 55 | 56 | export async function getSuperGroupInfoByGroupID( 57 | groupID: string 58 | ): Promise { 59 | try { 60 | const db = await getInstance(); 61 | 62 | const execResult = databaseGetSuperGroupInfoByGroupID(db, groupID); 63 | 64 | if (execResult.length === 0) { 65 | return formatResponse( 66 | '', 67 | DatabaseErrorCode.ErrorNoRecord, 68 | `no super group with id ${groupID}` 69 | ); 70 | } 71 | 72 | return formatResponse(converSqlExecResult(execResult[0])[0]); 73 | } catch (e) { 74 | console.error(e); 75 | 76 | return formatResponse( 77 | undefined, 78 | DatabaseErrorCode.ErrorInit, 79 | JSON.stringify(e) 80 | ); 81 | } 82 | } 83 | 84 | export async function deleteSuperGroup(groupID: string): Promise { 85 | try { 86 | const db = await getInstance(); 87 | 88 | const execResult = databaseDeleteSuperGroup(db, groupID); 89 | 90 | return formatResponse(execResult); 91 | } catch (e) { 92 | console.error(e); 93 | 94 | return formatResponse( 95 | undefined, 96 | DatabaseErrorCode.ErrorInit, 97 | JSON.stringify(e) 98 | ); 99 | } 100 | } 101 | 102 | export async function insertSuperGroup(groupStr: string): Promise { 103 | try { 104 | const db = await getInstance(); 105 | const group = convertToSnakeCaseObject( 106 | convertObjectField(JSON.parse(groupStr), { groupName: 'name' }) 107 | ) as ClientGroup; 108 | 109 | const execResult = databaseInsertSuperGroup(db, group); 110 | 111 | return formatResponse(execResult); 112 | } catch (e) { 113 | console.error(e); 114 | 115 | return formatResponse( 116 | undefined, 117 | DatabaseErrorCode.ErrorInit, 118 | JSON.stringify(e) 119 | ); 120 | } 121 | } 122 | 123 | export async function updateSuperGroup( 124 | groupID: string, 125 | groupStr: string 126 | ): Promise { 127 | try { 128 | const db = await getInstance(); 129 | const group = convertToSnakeCaseObject( 130 | convertObjectField(JSON.parse(groupStr), { groupName: 'name' }) 131 | ) as ClientGroup; 132 | 133 | const execResult = databaseUpdateSuperGroup(db, groupID, group); 134 | const modifed = db.getRowsModified(); 135 | if (modifed === 0) { 136 | throw 'updateSuperGroup no record updated'; 137 | } 138 | 139 | return formatResponse(execResult); 140 | } catch (e) { 141 | console.error(e); 142 | 143 | return formatResponse( 144 | undefined, 145 | DatabaseErrorCode.ErrorInit, 146 | JSON.stringify(e) 147 | ); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/api/database/tableMaster.ts: -------------------------------------------------------------------------------- 1 | import { converSqlExecResult, formatResponse } from '@/utils'; 2 | import { getExistedTables as databaseGetExistedTables } from '@/sqls'; 3 | import { DatabaseErrorCode } from '@/constant'; 4 | import { getInstance } from './instance'; 5 | 6 | export async function getExistedTables(): Promise { 7 | try { 8 | const db = await getInstance(); 9 | 10 | const execResult = databaseGetExistedTables(db); 11 | 12 | return formatResponse( 13 | converSqlExecResult(execResult[0], 'CamelCase', [], { 14 | tbl_name: 'tblName', 15 | }) 16 | ); 17 | } catch (e) { 18 | console.error(e); 19 | 20 | return formatResponse( 21 | undefined, 22 | DatabaseErrorCode.ErrorInit, 23 | JSON.stringify(e) 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/api/database/tempCacheChatLogs.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | TempCacheClientMessage, 4 | batchInsertTempCacheMessageList as databseBatchInsertTempCacheMessageList, 5 | } from '@/sqls'; 6 | import { convertToSnakeCaseObject, formatResponse } from '@/utils'; 7 | import { getInstance } from './instance'; 8 | 9 | export async function batchInsertTempCacheMessageList( 10 | messageListStr: string 11 | ): Promise { 12 | try { 13 | const db = await getInstance(); 14 | const messageList = ( 15 | JSON.parse(messageListStr) as TempCacheClientMessage[] 16 | ).map((v: Record) => convertToSnakeCaseObject(v)); 17 | 18 | const execResult = databseBatchInsertTempCacheMessageList(db, messageList); 19 | 20 | return formatResponse(execResult[0]); 21 | } catch (e) { 22 | console.error(e); 23 | 24 | return formatResponse( 25 | undefined, 26 | DatabaseErrorCode.ErrorInit, 27 | JSON.stringify(e) 28 | ); 29 | } 30 | } 31 | 32 | export async function InsertTempCacheMessage( 33 | messageStr: string 34 | ): Promise { 35 | return batchInsertTempCacheMessageList(`[${messageStr}]`); 36 | } 37 | -------------------------------------------------------------------------------- /src/api/database/unreadMessage.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | ClientLocalConversationUnreadMessage, 4 | batchInsertConversationUnreadMessageList as databaseBatchInsertConversationUnreadMessageList, 5 | deleteConversationUnreadMessageList as databaseDeleteConversationUnreadMessageList, 6 | } from '@/sqls'; 7 | import { convertToSnakeCaseObject, formatResponse } from '@/utils'; 8 | import { getInstance } from './instance'; 9 | 10 | export async function deleteConversationUnreadMessageList( 11 | conversationID: string, 12 | sendTime: number 13 | ): Promise { 14 | try { 15 | const db = await getInstance(); 16 | 17 | databaseDeleteConversationUnreadMessageList(db, conversationID, sendTime); 18 | const modifed = db.getRowsModified(); 19 | 20 | return formatResponse(modifed); 21 | } catch (e) { 22 | console.error(e); 23 | 24 | return formatResponse( 25 | undefined, 26 | DatabaseErrorCode.ErrorInit, 27 | JSON.stringify(e) 28 | ); 29 | } 30 | } 31 | 32 | export async function batchInsertConversationUnreadMessageList( 33 | messageListStr: string 34 | ): Promise { 35 | try { 36 | const db = await getInstance(); 37 | const messageList = ( 38 | JSON.parse(messageListStr) as ClientLocalConversationUnreadMessage[] 39 | ).map((v: Record) => convertToSnakeCaseObject(v)); 40 | 41 | const execResult = databaseBatchInsertConversationUnreadMessageList( 42 | db, 43 | messageList 44 | ); 45 | 46 | return formatResponse(execResult[0]); 47 | } catch (e) { 48 | console.error(e); 49 | 50 | return formatResponse( 51 | undefined, 52 | DatabaseErrorCode.ErrorInit, 53 | JSON.stringify(e) 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/api/database/upload.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | ClientUpload, 4 | getUpload as databaseGetUpload, 5 | insertUpload as databaseInsertUpload, 6 | updateUpload as databaseUpdateUpload, 7 | deleteUpload as databaseDeleteUpload, 8 | } from '@/sqls'; 9 | import { 10 | converSqlExecResult, 11 | convertObjectField, 12 | convertToSnakeCaseObject, 13 | formatResponse, 14 | } from '@/utils'; 15 | import { getInstance } from './instance'; 16 | 17 | export async function getUpload(partHash: string): Promise { 18 | try { 19 | const db = await getInstance(); 20 | 21 | const execResult = databaseGetUpload(db, partHash); 22 | 23 | const upload = converSqlExecResult(execResult[0], 'CamelCase'); 24 | if (upload.length === 0) { 25 | throw `no upload with partHash = ${partHash}`; 26 | } 27 | 28 | return formatResponse(upload[0]); 29 | } catch (e) { 30 | console.error(e); 31 | 32 | return formatResponse( 33 | undefined, 34 | DatabaseErrorCode.ErrorInit, 35 | JSON.stringify(e) 36 | ); 37 | } 38 | } 39 | 40 | export async function insertUpload(uploadStr: string): Promise { 41 | try { 42 | const db = await getInstance(); 43 | const upload = convertToSnakeCaseObject( 44 | convertObjectField(JSON.parse(uploadStr)) 45 | ) as ClientUpload; 46 | 47 | const execResult = databaseInsertUpload(db, upload); 48 | 49 | return formatResponse(execResult); 50 | } catch (e) { 51 | console.error(e); 52 | 53 | return formatResponse( 54 | undefined, 55 | DatabaseErrorCode.ErrorInit, 56 | JSON.stringify(e) 57 | ); 58 | } 59 | } 60 | 61 | export async function updateUpload(uploadStr: string): Promise { 62 | try { 63 | const db = await getInstance(); 64 | const upload = convertToSnakeCaseObject( 65 | convertObjectField(JSON.parse(uploadStr)) 66 | ) as ClientUpload; 67 | 68 | const execResult = databaseUpdateUpload(db, upload); 69 | 70 | return formatResponse(execResult); 71 | } catch (e) { 72 | console.error(e); 73 | 74 | return formatResponse( 75 | undefined, 76 | DatabaseErrorCode.ErrorInit, 77 | JSON.stringify(e) 78 | ); 79 | } 80 | } 81 | 82 | export async function deleteUpload(partHash: string): Promise { 83 | try { 84 | const db = await getInstance(); 85 | 86 | const execResult = databaseDeleteUpload(db, partHash); 87 | 88 | return formatResponse(execResult); 89 | } catch (e) { 90 | console.error(e); 91 | 92 | return formatResponse( 93 | undefined, 94 | DatabaseErrorCode.ErrorInit, 95 | JSON.stringify(e) 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/api/database/users.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | ClientUser, 4 | getLoginUser as databaseGetLoginUser, 5 | insertLoginUser as databaseInsertLoginUser, 6 | updateLoginUser as databaseUpdateLoginUser, 7 | } from '@/sqls'; 8 | import { 9 | formatResponse, 10 | converSqlExecResult, 11 | convertToSnakeCaseObject, 12 | convertObjectField, 13 | } from '@/utils'; 14 | import { getInstance } from './instance'; 15 | 16 | export async function getLoginUser(userID: string): Promise { 17 | try { 18 | const db = await getInstance(); 19 | 20 | const execResult = databaseGetLoginUser(db, userID); 21 | 22 | if (execResult.length === 0) { 23 | return formatResponse( 24 | '', 25 | DatabaseErrorCode.ErrorNoRecord, 26 | `no login user with id ${userID}` 27 | ); 28 | } 29 | 30 | return formatResponse( 31 | converSqlExecResult(execResult[0], 'CamelCase', [], { 32 | name: 'nickname', 33 | })[0] 34 | ); 35 | } catch (e) { 36 | console.error(e); 37 | 38 | return formatResponse( 39 | undefined, 40 | DatabaseErrorCode.ErrorInit, 41 | JSON.stringify(e) 42 | ); 43 | } 44 | } 45 | 46 | export async function insertLoginUser(userStr: string): Promise { 47 | try { 48 | const db = await getInstance(); 49 | const user = convertToSnakeCaseObject( 50 | convertObjectField(JSON.parse(userStr), { nickname: 'name' }) 51 | ) as ClientUser; 52 | 53 | const execResult = databaseInsertLoginUser(db, user); 54 | 55 | return formatResponse(execResult); 56 | } catch (e) { 57 | console.error(e); 58 | 59 | return formatResponse( 60 | undefined, 61 | DatabaseErrorCode.ErrorInit, 62 | JSON.stringify(e) 63 | ); 64 | } 65 | } 66 | 67 | export async function updateLoginUser(userStr: string): Promise { 68 | try { 69 | const db = await getInstance(); 70 | const user = convertToSnakeCaseObject( 71 | convertObjectField(JSON.parse(userStr), { nickname: 'name' }) 72 | ) as ClientUser; 73 | 74 | const execResult = databaseUpdateLoginUser(db, user); 75 | const modifed = db.getRowsModified(); 76 | if (modifed === 0) { 77 | throw 'updateLoginUser no record updated'; 78 | } 79 | return formatResponse(execResult); 80 | } catch (e) { 81 | console.error(e); 82 | 83 | return formatResponse( 84 | undefined, 85 | DatabaseErrorCode.ErrorInit, 86 | JSON.stringify(e) 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/api/database/versionSync.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseErrorCode } from '@/constant'; 2 | import { 3 | LocalVersionSync, 4 | getVersionSync as databaseGetVersionSync, 5 | insertVersionSync as databaseInsertVersionSync, 6 | updateVersionSync as databaseUpdateVersionSync, 7 | deleteVersionSync as databaseDeleteVersionSync, 8 | } from '@/sqls'; 9 | import { 10 | converSqlExecResult, 11 | convertObjectField, 12 | convertToSnakeCaseObject, 13 | formatResponse, 14 | } from '@/utils'; 15 | import { getInstance } from './instance'; 16 | 17 | export async function getVersionSync( 18 | tableName: string, 19 | entityID: string 20 | ): Promise { 21 | try { 22 | const db = await getInstance(); 23 | 24 | const execResult = databaseGetVersionSync(db, tableName, entityID); 25 | 26 | if (execResult.length === 0) { 27 | return formatResponse( 28 | '', 29 | DatabaseErrorCode.ErrorNoRecord, 30 | `no sync version with tableName ${tableName}, entityID ${entityID}` 31 | ); 32 | } 33 | 34 | const result = converSqlExecResult(execResult[0], 'CamelCase', [], { 35 | id_list: 'uidList', 36 | })[0]; 37 | result.uidList = JSON.parse(result.uidList as string); 38 | 39 | return formatResponse(result); 40 | } catch (e) { 41 | console.error(e); 42 | 43 | return formatResponse( 44 | undefined, 45 | DatabaseErrorCode.ErrorInit, 46 | JSON.stringify(e) 47 | ); 48 | } 49 | } 50 | 51 | export async function setVersionSync(versionSyncStr: string): Promise { 52 | try { 53 | const db = await getInstance(); 54 | 55 | const localVersionSync = convertToSnakeCaseObject( 56 | convertObjectField(JSON.parse(versionSyncStr)) 57 | ) as LocalVersionSync; 58 | 59 | localVersionSync.id_list = JSON.stringify(localVersionSync.uid_list); 60 | delete localVersionSync.uid_list; 61 | 62 | const execResult = databaseGetVersionSync( 63 | db, 64 | localVersionSync.table_name, 65 | localVersionSync.entity_id 66 | ); 67 | const result = converSqlExecResult(execResult[0], 'CamelCase', []); 68 | 69 | if (result[0] && result[0].tableName) { 70 | databaseUpdateVersionSync( 71 | db, 72 | result[0].tableName as string, 73 | result[0].entityID as string, 74 | localVersionSync 75 | ); 76 | return formatResponse(''); 77 | } else { 78 | databaseInsertVersionSync(db, localVersionSync); 79 | return formatResponse(''); 80 | } 81 | } catch (e) { 82 | console.error(e); 83 | 84 | return formatResponse( 85 | undefined, 86 | DatabaseErrorCode.ErrorInit, 87 | JSON.stringify(e) 88 | ); 89 | } 90 | } 91 | 92 | export async function deleteVersionSync( 93 | tablename: string, 94 | entityID: string 95 | ): Promise { 96 | try { 97 | const db = await getInstance(); 98 | 99 | databaseDeleteVersionSync(db, tablename, entityID); 100 | 101 | return formatResponse(''); 102 | } catch (e) { 103 | console.error(e); 104 | 105 | return formatResponse( 106 | undefined, 107 | DatabaseErrorCode.ErrorInit, 108 | JSON.stringify(e) 109 | ); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/api/upload.ts: -------------------------------------------------------------------------------- 1 | import { formatResponse } from '@/utils'; 2 | 3 | const fileMap = new Map(); 4 | 5 | export const fileMapSet = (uuid: string, file: File) => { 6 | fileMap.set(uuid, file); 7 | return formatResponse(uuid); 8 | }; 9 | 10 | export const fileMapClear = () => { 11 | fileMap.clear(); 12 | return formatResponse(''); 13 | }; 14 | 15 | export const wasmOpen = (uuid: string) => { 16 | return new Promise((resolve, reject) => { 17 | const file = fileMap.get(uuid); 18 | if (!file) { 19 | reject('file not found'); 20 | } else { 21 | resolve(formatResponse(file.size)); 22 | } 23 | }); 24 | }; 25 | 26 | export const wasmClose = async (uuid: string) => { 27 | return new Promise(resolve => { 28 | fileMap.delete(uuid); 29 | resolve(formatResponse(uuid)); 30 | }); 31 | }; 32 | 33 | export const wasmRead = (uuid: string, offset: number, length: number) => { 34 | const file = fileMap.get(uuid); 35 | if (!file) { 36 | throw 'file not found'; 37 | } 38 | const blob = file.slice(offset, offset + length); 39 | return new Promise((resolve, reject) => { 40 | const reader = new FileReader(); 41 | reader.onload = () => { 42 | resolve(reader.result); 43 | }; 44 | reader.onerror = () => { 45 | reject(reader.error); 46 | }; 47 | reader.readAsArrayBuffer(blob); 48 | }); 49 | }; 50 | -------------------------------------------------------------------------------- /src/constant/index.ts: -------------------------------------------------------------------------------- 1 | export const DatabaseErrorCode = { 2 | ErrorInit: 10001, 3 | ErrorNoRecord: 10002, 4 | ErrorDBTimeout: 10003, 5 | }; 6 | 7 | export enum CbEvents { 8 | Login = 'Login', 9 | OnConnectFailed = 'OnConnectFailed', 10 | OnConnectSuccess = 'OnConnectSuccess', 11 | OnConnecting = 'OnConnecting', 12 | OnKickedOffline = 'OnKickedOffline', 13 | OnSelfInfoUpdated = 'OnSelfInfoUpdated', 14 | OnUserTokenExpired = 'OnUserTokenExpired', 15 | OnUserTokenInvalid = 'OnUserTokenInvalid', 16 | OnProgress = 'OnProgress', 17 | OnRecvNewMessage = 'OnRecvNewMessage', 18 | OnRecvNewMessages = 'OnRecvNewMessages', 19 | OnRecvOnlineOnlyMessage = 'OnRecvOnlineOnlyMessage', 20 | OnRecvOfflineNewMessage = 'onRecvOfflineNewMessage', 21 | OnRecvOnlineOnlyMessages = 'OnRecvOnlineOnlyMessages', 22 | OnRecvOfflineNewMessages = 'onRecvOfflineNewMessages', 23 | OnRecvMessageRevoked = 'OnRecvMessageRevoked', 24 | OnNewRecvMessageRevoked = 'OnNewRecvMessageRevoked', 25 | OnRecvC2CReadReceipt = 'OnRecvC2CReadReceipt', 26 | OnRecvGroupReadReceipt = 'OnRecvGroupReadReceipt', 27 | OnConversationChanged = 'OnConversationChanged', 28 | OnNewConversation = 'OnNewConversation', 29 | OnConversationUserInputStatusChanged = 'OnConversationUserInputStatusChanged', 30 | OnSyncServerFailed = 'OnSyncServerFailed', 31 | OnSyncServerFinish = 'OnSyncServerFinish', 32 | OnSyncServerProgress = 'OnSyncServerProgress', 33 | OnSyncServerStart = 'OnSyncServerStart', 34 | OnTotalUnreadMessageCountChanged = 'OnTotalUnreadMessageCountChanged', 35 | OnBlackAdded = 'OnBlackAdded', 36 | OnBlackDeleted = 'OnBlackDeleted', 37 | OnFriendApplicationAccepted = 'OnFriendApplicationAccepted', 38 | OnFriendApplicationAdded = 'OnFriendApplicationAdded', 39 | OnFriendApplicationDeleted = 'OnFriendApplicationDeleted', 40 | OnFriendApplicationRejected = 'OnFriendApplicationRejected', 41 | OnFriendInfoChanged = 'OnFriendInfoChanged', 42 | OnFriendAdded = 'OnFriendAdded', 43 | OnFriendDeleted = 'OnFriendDeleted', 44 | OnJoinedGroupAdded = 'OnJoinedGroupAdded', 45 | OnJoinedGroupDeleted = 'OnJoinedGroupDeleted', 46 | OnGroupDismissed = 'OnGroupDismissed', 47 | OnGroupMemberAdded = 'OnGroupMemberAdded', 48 | OnGroupMemberDeleted = 'OnGroupMemberDeleted', 49 | OnGroupApplicationAdded = 'OnGroupApplicationAdded', 50 | OnGroupApplicationDeleted = 'OnGroupApplicationDeleted', 51 | OnGroupInfoChanged = 'OnGroupInfoChanged', 52 | OnGroupMemberInfoChanged = 'OnGroupMemberInfoChanged', 53 | OnGroupApplicationAccepted = 'OnGroupApplicationAccepted', 54 | OnGroupApplicationRejected = 'OnGroupApplicationRejected', 55 | 56 | UploadComplete = 'UploadComplete', 57 | OnRecvCustomBusinessMessage = 'OnRecvCustomBusinessMessage', 58 | OnUserStatusChanged = 'OnUserStatusChanged', 59 | OnUploadLogsProgress = 'OnUploadLogsProgress', 60 | 61 | // rtc 62 | OnReceiveNewInvitation = 'OnReceiveNewInvitation', 63 | OnInviteeAccepted = 'OnInviteeAccepted', 64 | OnInviteeRejected = 'OnInviteeRejected', 65 | OnInvitationCancelled = 'OnInvitationCancelled', 66 | OnHangUp = 'OnHangUp', 67 | OnInvitationTimeout = 'OnInvitationTimeout', 68 | OnInviteeAcceptedByOtherDevice = 'OnInviteeAcceptedByOtherDevice', 69 | OnInviteeRejectedByOtherDevice = 'OnInviteeRejectedByOtherDevice', 70 | 71 | // meeting 72 | OnStreamChange = 'OnStreamChange', 73 | OnRoomParticipantConnected = 'OnRoomParticipantConnected', 74 | OnRoomParticipantDisconnected = 'OnRoomParticipantDisconnected', 75 | OnReceiveCustomSignal = 'OnReceiveCustomSignal', 76 | 77 | // unuse 78 | UnUsedEvent = 'UnUsedEvent', 79 | } 80 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { getSDK } from './sdk'; 2 | 3 | export { CbEvents } from './constant'; 4 | export * from './types/enum'; 5 | export * from './types/entity'; 6 | export * from './types/eventData'; 7 | export * from './types/params'; 8 | -------------------------------------------------------------------------------- /src/sdk/initialize.ts: -------------------------------------------------------------------------------- 1 | import { wait } from '@/utils'; 2 | 3 | let initialized = false; 4 | let go: Go; 5 | let goExitPromise: Promise | undefined; 6 | 7 | const CACHE_KEY = 'openim-wasm-cache'; 8 | 9 | export async function initializeWasm(url: string): Promise { 10 | if (initialized) { 11 | return null; 12 | } 13 | 14 | if (typeof window === 'undefined') { 15 | return Promise.resolve(null); 16 | } 17 | 18 | go = new Go(); 19 | let wasm; 20 | try { 21 | if ('instantiateStreaming' in WebAssembly) { 22 | wasm = await WebAssembly.instantiateStreaming( 23 | fetchWithCache(url), 24 | go.importObject 25 | ); 26 | } else { 27 | const bytes = await fetchWithCache(url).then(resp => resp.arrayBuffer()); 28 | wasm = await WebAssembly.instantiate(bytes, go.importObject); 29 | } 30 | go.run(wasm.instance); 31 | } catch (error) { 32 | console.error('Failed to initialize WASM:', error); 33 | return null; 34 | } 35 | 36 | await wait(100); 37 | initialized = true; 38 | return go; 39 | } 40 | 41 | export function reset() { 42 | initialized = false; 43 | } 44 | 45 | export function getGO() { 46 | return go; 47 | } 48 | 49 | export function getGoExitPromise() { 50 | return goExitPromise; 51 | } 52 | 53 | async function fetchWithCache(url: string): Promise { 54 | if (!('caches' in window)) { 55 | return fetch(url); 56 | } 57 | 58 | const isResourceUpdated = async () => { 59 | const serverResponse = await fetch(url, { method: 'HEAD' }); 60 | const etag = serverResponse.headers.get('ETag'); 61 | const lastModified = serverResponse.headers.get('Last-Modified'); 62 | return ( 63 | serverResponse.ok && 64 | (etag !== cachedResponse?.headers.get('ETag') || 65 | lastModified !== cachedResponse?.headers.get('Last-Modified')) 66 | ); 67 | }; 68 | 69 | const cache = await caches.open(CACHE_KEY); 70 | const cachedResponse = await cache.match(url); 71 | if (cachedResponse && !(await isResourceUpdated())) { 72 | return cachedResponse; 73 | } 74 | 75 | return fetchAndUpdateCache(url, cache); 76 | } 77 | 78 | async function fetchAndUpdateCache( 79 | url: string, 80 | cache: Cache 81 | ): Promise { 82 | const response = await fetch(url, { cache: 'no-cache' }); 83 | try { 84 | await cache.put(url, response.clone()); 85 | } catch (error) { 86 | console.warn('Failed to put cache'); 87 | } 88 | return response; 89 | } 90 | -------------------------------------------------------------------------------- /src/sqls/index.ts: -------------------------------------------------------------------------------- 1 | export * from './localChatLogsConversationID'; 2 | export * from './localConversations'; 3 | export * from './localUsers'; 4 | export * from './localSuperGroups'; 5 | export * from './localConversationUnreadMessages'; 6 | export * from './localBlack'; 7 | export * from './localFriend'; 8 | export * from './localGroups'; 9 | export * from './localGroupRequests'; 10 | export * from './localAdminGroupRequests'; 11 | export * from './localFriendRequest'; 12 | export * from './localGroupMembers'; 13 | export * from './tempCacheLocalChatLogs'; 14 | export * from './localNotification'; 15 | export * from './localUpload'; 16 | export * from './localStranger'; 17 | export * from './localSendingMessages'; 18 | export * from './localAppSdkVersion'; 19 | export * from './localVersionSync'; 20 | export * from './localTableMaster'; 21 | -------------------------------------------------------------------------------- /src/sqls/localAdminGroupRequests.ts: -------------------------------------------------------------------------------- 1 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 2 | import squel from 'squel'; 3 | 4 | export type LocalAdminGroupRequest = { [key: string]: any }; 5 | 6 | export function localAdminGroupRequests(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists "local_admin_group_requests" ( 10 | "group_id" varchar(64), 11 | "group_name" text, 12 | "notification" varchar(255), 13 | "introduction" varchar(255), 14 | "face_url" varchar(255), 15 | "create_time" integer, 16 | "status" integer, 17 | "creator_user_id" varchar(64), 18 | "group_type" integer, 19 | "owner_user_id" varchar(64), 20 | "member_count" integer, 21 | "user_id" varchar(64), 22 | "nickname" varchar(255), 23 | "user_face_url" varchar(255), 24 | "gender" integer, 25 | "handle_result" integer, 26 | "req_msg" varchar(255), 27 | "handle_msg" varchar(255), 28 | "req_time" integer, 29 | "handle_user_id" varchar(64), 30 | "handle_time" integer, 31 | "ex" varchar(1024), 32 | "attached_info" varchar(1024), 33 | "join_source" integer, 34 | "inviter_user_id" text, 35 | PRIMARY KEY ("group_id", "user_id") 36 | ); 37 | ` 38 | ); 39 | } 40 | 41 | export function insertAdminGroupRequest( 42 | db: Database, 43 | localGroupRequest: LocalAdminGroupRequest 44 | ): QueryExecResult[] { 45 | const sql = squel 46 | .insert() 47 | .into('local_admin_group_requests') 48 | .setFields(localGroupRequest) 49 | .toString(); 50 | 51 | return db.exec(sql); 52 | } 53 | 54 | export function deleteAdminGroupRequest( 55 | db: Database, 56 | groupID: string, 57 | userID: string 58 | ): QueryExecResult[] { 59 | return db.exec( 60 | ` 61 | delete 62 | from local_admin_group_requests 63 | where group_id = "${groupID}" 64 | and user_id = "${userID}" 65 | ` 66 | ); 67 | } 68 | 69 | export function updateAdminGroupRequest( 70 | db: Database, 71 | localGroupRequest: LocalAdminGroupRequest 72 | ): QueryExecResult[] { 73 | const sql = squel 74 | .update() 75 | .table('local_admin_group_requests') 76 | .setFields(localGroupRequest) 77 | .where( 78 | `group_id = '${localGroupRequest.group_id}' and user_id = '${localGroupRequest.user_id}'` 79 | ) 80 | .toString(); 81 | 82 | return db.exec(sql); 83 | } 84 | 85 | export function getAdminGroupApplication(db: Database): QueryExecResult[] { 86 | return db.exec( 87 | ` 88 | select * 89 | from local_admin_group_requests 90 | order by create_time desc 91 | ` 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /src/sqls/localAppSdkVersion.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type LocalAppSDKVersion = { [key: string]: any }; 5 | 6 | export function localAppSDKVersions(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_app_sdk_version' ( 10 | 'version' varchar(255), 11 | primary key ('version') 12 | ) 13 | ` 14 | ); 15 | } 16 | 17 | export function getAppSDKVersion(db: Database): QueryExecResult[] { 18 | return db.exec( 19 | ` 20 | SELECT * FROM local_app_sdk_version LIMIT 1 21 | ` 22 | ); 23 | } 24 | 25 | export function insertAppSDKVersion( 26 | db: Database, 27 | localAppSDKVersion: LocalAppSDKVersion 28 | ): QueryExecResult[] { 29 | const sql = squel 30 | .insert() 31 | .into('local_app_sdk_version') 32 | .setFields(localAppSDKVersion) 33 | .toString(); 34 | 35 | return db.exec(sql); 36 | } 37 | 38 | export function updateAppSDKVersion( 39 | db: Database, 40 | oldVersion: string, 41 | localAppSDKVersion: LocalAppSDKVersion 42 | ): QueryExecResult[] { 43 | const sql = squel 44 | .update() 45 | .table('local_app_sdk_version') 46 | .setFields(localAppSDKVersion) 47 | .where(`version = '${oldVersion}'`) 48 | .toString(); 49 | 50 | return db.exec(sql); 51 | } 52 | -------------------------------------------------------------------------------- /src/sqls/localBlack.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type LocalBlack = { [key: string]: any }; 5 | 6 | export function locaBlacks(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_blacks' ( 10 | 'owner_user_id' varchar(64), 11 | 'block_user_id' varchar(64), 12 | 'nickname' varchar(255), 13 | 'face_url' varchar(255), 14 | 'gender' INTEGER, 15 | 'create_time' INTEGER, 16 | 'add_source' INTEGER, 17 | 'operator_user_id' varchar(64), 18 | 'ex' varchar(1024), 19 | 'attached_info' varchar(1024), 20 | primary key ('owner_user_id', 'block_user_id') 21 | ) 22 | ` 23 | ); 24 | } 25 | 26 | export function getBlackList(db: Database): QueryExecResult[] { 27 | return db.exec( 28 | ` 29 | select * 30 | from local_blacks 31 | ` 32 | ); 33 | } 34 | 35 | export function getBlackListUserID(db: Database): QueryExecResult[] { 36 | return db.exec( 37 | ` 38 | SELECT block_user_id 39 | FROM local_blacks 40 | ` 41 | ); 42 | } 43 | 44 | export function getBlackInfoByBlockUserID( 45 | db: Database, 46 | blockUserID: string, 47 | loginUserID: string 48 | ): QueryExecResult[] { 49 | return db.exec( 50 | ` 51 | SELECT * 52 | FROM local_blacks 53 | WHERE owner_user_id = "${loginUserID}" 54 | AND block_user_id = "${blockUserID}" 55 | LIMIT 1 56 | ` 57 | ); 58 | } 59 | 60 | export function getBlackInfoList( 61 | db: Database, 62 | blockUserIDList: string[] 63 | ): QueryExecResult[] { 64 | const ids = blockUserIDList.map(v => `'${v}'`); 65 | return db.exec( 66 | ` 67 | select * 68 | from local_blacks 69 | where block_user_id in (${ids.join(',')}) 70 | ` 71 | ); 72 | } 73 | 74 | export function insertBlack( 75 | db: Database, 76 | localBlack: LocalBlack 77 | ): QueryExecResult[] { 78 | const sql = squel 79 | .insert() 80 | .into('local_blacks') 81 | .setFields(localBlack) 82 | .toString(); 83 | 84 | return db.exec(sql); 85 | } 86 | 87 | export function updateBlack( 88 | db: Database, 89 | localBlack: LocalBlack 90 | ): QueryExecResult[] { 91 | const sql = squel 92 | .update() 93 | .table('local_blacks') 94 | .setFields(localBlack) 95 | .where( 96 | `owner_user_id = '${localBlack.owner_user_id}' and block_user_id = '${localBlack.block_user_id}'` 97 | ) 98 | .toString(); 99 | 100 | return db.exec(sql); 101 | } 102 | 103 | export function deleteBlack( 104 | db: Database, 105 | blockUserID: string, 106 | loginUserID: string 107 | ): QueryExecResult[] { 108 | return db.exec( 109 | ` 110 | delete 111 | from local_blacks 112 | where owner_user_id = "${loginUserID}" 113 | and block_user_id = "${blockUserID}" 114 | ` 115 | ); 116 | } 117 | -------------------------------------------------------------------------------- /src/sqls/localConversationUnreadMessages.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type ClientLocalConversationUnreadMessage = { [key: string]: any }; 5 | 6 | export function localConversationUnreadMessages( 7 | db: Database 8 | ): QueryExecResult[] { 9 | return db.exec( 10 | ` 11 | create table if not exists 'local_conversation_unread_messages' ( 12 | 'conversation_id' char(128), 13 | 'client_msg_id' char(64), 14 | 'send_time' integer, 15 | 'ex' varchar(1024), 16 | primary key ( 17 | 'conversation_id', 18 | 'client_msg_id' 19 | ) 20 | ); 21 | ` 22 | ); 23 | } 24 | 25 | export function deleteConversationUnreadMessageList( 26 | db: Database, 27 | conversationID: string, 28 | sendTime: number 29 | ): QueryExecResult[] { 30 | return db.exec( 31 | ` 32 | delete from local_conversation_unread_messages where conversation_id = '${conversationID}' and send_time <= ${sendTime}; 33 | ` 34 | ); 35 | } 36 | 37 | export function batchInsertConversationUnreadMessageList( 38 | db: Database, 39 | messageList: ClientLocalConversationUnreadMessage[] 40 | ): QueryExecResult[] { 41 | const sql = squel 42 | .insert() 43 | .into('local_conversation_unread_messages') 44 | .setFieldsRows(messageList) 45 | .toString(); 46 | 47 | return db.exec(sql); 48 | } 49 | -------------------------------------------------------------------------------- /src/sqls/localConversations.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type ClientConversation = { [key: string]: any }; 5 | 6 | export function localConversations(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_conversations' ( 10 | 'conversation_id' char(128), 11 | 'conversation_type' integer, 12 | 'user_id' char(64), 13 | 'group_id' char(128), 14 | 'show_name' varchar(255), 15 | 'face_url' varchar(255), 16 | 'recv_msg_opt' integer, 17 | 'unread_count' integer, 18 | 'group_at_type' integer, 19 | 'latest_msg' varchar(1000), 20 | 'latest_msg_send_time' integer, 21 | 'draft_text' text, 22 | 'draft_text_time' integer, 23 | 'is_pinned' numeric, 24 | 'burn_duration' integer, 25 | 'is_private_chat' numeric, 26 | 'is_not_in_group' numeric, 27 | 'update_unread_count_time' integer, 28 | 'attached_info' varchar(1024), 29 | 'ex' varchar(1024), 30 | 'max_seq' integer, 31 | 'min_seq' integer, 32 | 'has_read_seq' integer, 33 | 'msg_destruct_time' integer default 604800, 34 | 'is_msg_destruct' numeric default false, 35 | primary key ('conversation_id') 36 | ) 37 | ` 38 | ); 39 | } 40 | 41 | export function getConversationByUserID( 42 | db: Database, 43 | userID: string 44 | ): QueryExecResult[] { 45 | return db.exec( 46 | ` 47 | select * from local_conversations where user_id = "${userID}" limit 1; 48 | ` 49 | ); 50 | } 51 | 52 | export function getAllConversationList(db: Database): QueryExecResult[] { 53 | return db.exec( 54 | ` 55 | select * from local_conversations where latest_msg_send_time > 0 order by case when is_pinned=1 then 0 else 1 end,max(latest_msg_send_time,draft_text_time) desc; 56 | ` 57 | ); 58 | } 59 | 60 | export function getAllConversationListToSync(db: Database): QueryExecResult[] { 61 | return db.exec( 62 | ` 63 | select * from local_conversations; 64 | ` 65 | ); 66 | } 67 | 68 | export function getAllSingleConversationIDList( 69 | db: Database 70 | ): QueryExecResult[] { 71 | return db.exec( 72 | ` 73 | select conversation_id from local_conversations where conversation_type = 1; 74 | ` 75 | ); 76 | } 77 | 78 | export function getAllConversationIDList(db: Database): QueryExecResult[] { 79 | return db.exec( 80 | ` 81 | select conversation_id from local_conversations; 82 | ` 83 | ); 84 | } 85 | 86 | export function getHiddenConversationList(db: Database): QueryExecResult[] { 87 | return db.exec( 88 | ` 89 | select * from local_conversations where latest_msg_send_time = 0; 90 | ` 91 | ); 92 | } 93 | 94 | export function getConversationListSplit( 95 | db: Database, 96 | offset: number, 97 | count: number 98 | ): QueryExecResult[] { 99 | return db.exec( 100 | ` 101 | SELECT * 102 | FROM local_conversations 103 | WHERE latest_msg_send_time > 0 104 | ORDER BY case 105 | when is_pinned = 1 then 0 106 | else 1 end, max(latest_msg_send_time, draft_text_time) DESC 107 | LIMIT ${count} OFFSET ${offset} 108 | ` 109 | ); 110 | } 111 | 112 | export function getConversation( 113 | db: Database, 114 | conversationID: string 115 | ): QueryExecResult[] { 116 | return db.exec( 117 | ` 118 | select * from local_conversations where conversation_id = '${conversationID}' limit 1; 119 | ` 120 | ); 121 | } 122 | 123 | export function getMultipleConversation( 124 | db: Database, 125 | conversationIDList: string[] 126 | ): QueryExecResult[] { 127 | const ids = conversationIDList.map(v => `'${v}'`); 128 | 129 | return db.exec( 130 | ` 131 | select * from local_conversations where conversation_id in (${ids.join( 132 | ',' 133 | )}); 134 | ` 135 | ); 136 | } 137 | 138 | export function updateColumnsConversation( 139 | db: Database, 140 | conversationID: string, 141 | conversation: ClientConversation 142 | ): QueryExecResult[] { 143 | const sql = squel 144 | .update() 145 | .table('local_conversations') 146 | .setFields(conversation) 147 | .where(`conversation_id = '${conversationID}'`) 148 | .toString(); 149 | 150 | return db.exec(sql); 151 | } 152 | 153 | export function incrConversationUnreadCount( 154 | db: Database, 155 | conversationID: string 156 | ): QueryExecResult[] { 157 | return db.exec( 158 | ` 159 | update local_conversations set 160 | unread_count=unread_count+1 161 | where conversation_id = '${conversationID}'; 162 | ` 163 | ); 164 | } 165 | 166 | export function decrConversationUnreadCount( 167 | db: Database, 168 | conversationID: string, 169 | count: number 170 | ): QueryExecResult[] { 171 | db.exec('begin'); 172 | db.exec( 173 | ` 174 | update local_conversations set 175 | unread_count=unread_count-${count} 176 | where conversation_id = '${conversationID}'; 177 | ` 178 | ); 179 | const current = db.exec( 180 | `select unread_count from local_conversations where conversation_id = '${conversationID}'` 181 | ); 182 | 183 | if (Number(current[0].values[0]) < 0) { 184 | db.exec( 185 | ` 186 | update local_conversations set 187 | unread_count=${0} 188 | where conversation_id = '${conversationID}'; 189 | ` 190 | ); 191 | } 192 | return db.exec('commit'); 193 | } 194 | 195 | export function batchInsertConversationList( 196 | db: Database, 197 | conversationList: ClientConversation[] 198 | ): QueryExecResult[] { 199 | const sql = squel 200 | .insert() 201 | .into('local_conversations') 202 | .setFieldsRows(conversationList) 203 | .toString(); 204 | 205 | return db.exec(sql); 206 | } 207 | 208 | export function insertConversation( 209 | db: Database, 210 | localConversation: ClientConversation 211 | ): QueryExecResult[] { 212 | const sql = squel 213 | .insert() 214 | .into('local_conversations') 215 | .setFields(localConversation) 216 | .toString(); 217 | 218 | return db.exec(sql); 219 | } 220 | 221 | export function updateConversation( 222 | db: Database, 223 | localConversation: ClientConversation 224 | ): QueryExecResult[] { 225 | const sql = squel 226 | .update() 227 | .table('local_conversations') 228 | .setFields(localConversation) 229 | .where(`conversation_id = '${localConversation.conversation_id}'`) 230 | .toString(); 231 | 232 | return db.exec(sql); 233 | } 234 | 235 | export function deleteConversation( 236 | db: Database, 237 | conversationID: string 238 | ): QueryExecResult[] { 239 | return db.exec(` 240 | DELETE 241 | FROM local_conversations 242 | WHERE conversation_id = "${conversationID}" 243 | `); 244 | } 245 | 246 | export function deleteAllConversation(db: Database): QueryExecResult[] { 247 | return db.exec(` 248 | DELETE FROM local_conversations; 249 | `); 250 | } 251 | 252 | export function conversationIfExists( 253 | db: Database, 254 | conversationID: string 255 | ): QueryExecResult[] { 256 | return db.exec(` 257 | SELECT count(*) 258 | FROM local_conversations 259 | WHERE conversation_id = "${conversationID}" 260 | `); 261 | } 262 | 263 | export function resetConversation( 264 | db: Database, 265 | conversationID: string 266 | ): QueryExecResult[] { 267 | return db.exec(` 268 | UPDATE local_conversations 269 | SET unread_count=0, 270 | latest_msg="", 271 | latest_msg_send_time=0, 272 | draft_text="", 273 | draft_text_time=0 274 | WHERE conversation_id = "${conversationID}" 275 | `); 276 | } 277 | 278 | export function resetAllConversation(db: Database): QueryExecResult[] { 279 | return db.exec(` 280 | UPDATE local_conversations 281 | SET unread_count=0, 282 | latest_msg="", 283 | latest_msg_send_time=0, 284 | draft_text="", 285 | draft_text_time=0 286 | `); 287 | } 288 | 289 | export function clearConversation( 290 | db: Database, 291 | conversationID: string 292 | ): QueryExecResult[] { 293 | return db.exec(` 294 | UPDATE local_conversations 295 | SET unread_count=0, 296 | latest_msg="", 297 | draft_text="", 298 | draft_text_time=0 299 | WHERE conversation_id = "${conversationID}" 300 | `); 301 | } 302 | 303 | export function clearAllConversation(db: Database): QueryExecResult[] { 304 | return db.exec(` 305 | UPDATE local_conversations 306 | SET unread_count=0, 307 | latest_msg="", 308 | draft_text="", 309 | draft_text_time=0 310 | `); 311 | } 312 | 313 | export function setConversationDraft( 314 | db: Database, 315 | conversationID: string, 316 | draftText: string 317 | ): QueryExecResult[] { 318 | const nowDate = new Date().getTime(); 319 | return db.exec(` 320 | update local_conversations 321 | set draft_text='${draftText}', 322 | draft_text_time=${nowDate}, 323 | latest_msg_send_time=case when latest_msg_send_time = 0 then ${nowDate} else latest_msg_send_time end 324 | where conversation_id = "${conversationID}" 325 | `); 326 | } 327 | 328 | export function removeConversationDraft( 329 | db: Database, 330 | conversationID: string, 331 | draftText: string 332 | ): QueryExecResult[] { 333 | return db.exec(` 334 | update local_conversations 335 | set draft_text="${draftText}", 336 | draft_text_time=0 337 | where conversation_id = "${conversationID}" 338 | `); 339 | } 340 | 341 | export function unPinConversation( 342 | db: Database, 343 | conversationID: string, 344 | isPinned: number 345 | ): QueryExecResult[] { 346 | return db.exec(` 347 | update local_conversations 348 | set is_pinned=${isPinned}, 349 | draft_text_time=case when draft_text = "" then 0 else draft_text_time end 350 | where conversation_id = "${conversationID}" 351 | `); 352 | } 353 | 354 | export function getTotalUnreadMsgCount(db: Database): QueryExecResult[] { 355 | return db.exec( 356 | ` 357 | select sum(unread_count) from local_conversations where recv_msg_opt < 2 and latest_msg_send_time > 0; 358 | ` 359 | ); 360 | } 361 | 362 | export function setMultipleConversationRecvMsgOpt( 363 | db: Database, 364 | conversationIDList: string[], 365 | opt: number 366 | ): QueryExecResult[] { 367 | const values = conversationIDList.map(v => `'${v}'`).join(','); 368 | return db.exec( 369 | ` 370 | UPDATE local_conversations 371 | SET recv_msg_opt=${opt} 372 | WHERE conversation_id IN (${values}) 373 | ` 374 | ); 375 | } 376 | 377 | export function getAllConversations(db: Database): QueryExecResult[] { 378 | return db.exec( 379 | ` 380 | SELECT * FROM local_conversations 381 | ` 382 | ); 383 | } 384 | 385 | export function searchConversations( 386 | db: Database, 387 | keyword: string 388 | ): QueryExecResult[] { 389 | return db.exec( 390 | ` 391 | SELECT * FROM local_conversations 392 | WHERE show_name LIKE '%${keyword}%' 393 | ORDER BY latest_msg_send_time DESC 394 | ` 395 | ); 396 | } 397 | -------------------------------------------------------------------------------- /src/sqls/localFriend.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type LocalFriend = { [key: string]: any }; 5 | 6 | export function localFriends(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_friends' 10 | ( 11 | 'owner_user_id' varchar(64), 12 | 'friend_user_id' varchar(64), 13 | 'remark' varchar(255), 14 | 'create_time' INTEGER, 15 | 'add_source' INTEGER, 16 | 'operator_user_id' varchar(64), 17 | 'name' varchar(255), 18 | 'face_url' varchar(255), 19 | 'ex' varchar(1024), 20 | 'attached_info' varchar(1024), 21 | 'is_pinned' numeric, 22 | primary key ('owner_user_id', 'friend_user_id') 23 | ) 24 | ` 25 | ); 26 | } 27 | 28 | export function insertFriend( 29 | db: Database, 30 | localFriend: LocalFriend 31 | ): QueryExecResult[] { 32 | const sql = squel 33 | .insert() 34 | .into('local_friends') 35 | .setFields(localFriend) 36 | .toString(); 37 | 38 | return db.exec(sql); 39 | } 40 | 41 | export function deleteFriend( 42 | db: Database, 43 | friendUserID: string, 44 | loginUserID: string 45 | ): QueryExecResult[] { 46 | return db.exec( 47 | ` 48 | DELETE FROM local_friends 49 | WHERE owner_user_id="${loginUserID}" 50 | and friend_user_id="${friendUserID}" 51 | ` 52 | ); 53 | } 54 | export function updateFriend( 55 | db: Database, 56 | localFriend: LocalFriend 57 | ): QueryExecResult[] { 58 | const sql = squel 59 | .update() 60 | .table('local_friends') 61 | .setFields(localFriend) 62 | .where( 63 | `owner_user_id = '${localFriend.owner_user_id}' and friend_user_id = '${localFriend.friend_user_id}'` 64 | ) 65 | .toString(); 66 | 67 | return db.exec(sql); 68 | } 69 | 70 | export function getAllFriendList( 71 | db: Database, 72 | loginUser: string 73 | ): QueryExecResult[] { 74 | return db.exec( 75 | ` 76 | select * 77 | from local_friends 78 | where owner_user_id = "${loginUser}" 79 | ` 80 | ); 81 | } 82 | 83 | export function getPageFriendList( 84 | db: Database, 85 | offset: number, 86 | count: number, 87 | loginUser: string 88 | ): QueryExecResult[] { 89 | return db.exec( 90 | ` 91 | select * 92 | from local_friends 93 | where owner_user_id = "${loginUser}" 94 | order by name 95 | limit ${count} offset ${offset} 96 | ` 97 | ); 98 | } 99 | 100 | export function searchFriendList( 101 | db: Database, 102 | keyword: string, 103 | isSearchUserID: boolean, 104 | isSearchNickname: boolean, 105 | isSearchRemark: boolean 106 | ): QueryExecResult[] { 107 | let totalConditionStr = ''; 108 | const userIDCondition = `friend_user_id like "%${keyword}%"`; 109 | const nicknameCondition = `name like "%${keyword}%"`; 110 | const remarkCondition = `remark like "%${keyword}%"`; 111 | if (isSearchUserID) { 112 | totalConditionStr = userIDCondition; 113 | } 114 | if (isSearchNickname) { 115 | totalConditionStr = totalConditionStr 116 | ? totalConditionStr + ' or ' + nicknameCondition 117 | : nicknameCondition; 118 | } 119 | if (isSearchRemark) { 120 | totalConditionStr = totalConditionStr 121 | ? totalConditionStr + ' or ' + remarkCondition 122 | : remarkCondition; 123 | } 124 | return db.exec( 125 | ` 126 | select * 127 | from local_friends 128 | where ${totalConditionStr} 129 | order by create_time desc 130 | ` 131 | ); 132 | } 133 | 134 | export function getFriendInfoByFriendUserID( 135 | db: Database, 136 | friendUserID: string, 137 | loginUser: string 138 | ): QueryExecResult[] { 139 | return db.exec( 140 | ` 141 | select * 142 | from local_friends 143 | where owner_user_id = "${loginUser}" 144 | and friend_user_id = "${friendUserID}" 145 | limit 1 146 | ` 147 | ); 148 | } 149 | 150 | export function getFriendInfoList( 151 | db: Database, 152 | friendUserIDList: string[] 153 | ): QueryExecResult[] { 154 | const values = friendUserIDList.map(v => `'${v}'`).join(','); 155 | return db.exec( 156 | ` 157 | select * 158 | from local_friends 159 | where friend_user_id in (${values}) 160 | ` 161 | ); 162 | } 163 | 164 | export function updateColumnsFriend( 165 | db: Database, 166 | friendUserIDs: string[], 167 | localFriend: LocalFriend 168 | ): QueryExecResult[] { 169 | const values = friendUserIDs.map(v => `'${v}'`).join(','); 170 | const sql = squel 171 | .update() 172 | .table('local_friends') 173 | .setFields(localFriend) 174 | .where(`friend_user_id IN (${values})`) 175 | .toString(); 176 | 177 | return db.exec(sql); 178 | } 179 | 180 | export function getFriendListCount(db: Database): QueryExecResult[] { 181 | return db.exec( 182 | ` 183 | SELECT COUNT(*) FROM local_friends; 184 | ` 185 | ); 186 | } 187 | 188 | export function deleteAllFriend(db: Database): QueryExecResult[] { 189 | return db.exec( 190 | ` 191 | DELETE FROM local_friends; 192 | ` 193 | ); 194 | } 195 | -------------------------------------------------------------------------------- /src/sqls/localFriendRequest.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type LocalFriendRequest = { [key: string]: any }; 5 | 6 | export function localFriendRequests(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_friend_requests' 10 | ( 11 | 'from_user_id' varchar(64), 12 | 'from_nickname' varchar(255), 13 | 'from_face_url' varchar(255), 14 | 'to_user_id' varchar(64), 15 | 'to_nickname' varchar(255), 16 | 'to_face_url' varchar(255), 17 | 'handle_result' INTEGER, 18 | 'req_msg' varchar(255), 19 | 'create_time' INTEGER, 20 | 'handler_user_id' varchar(64), 21 | 'handle_msg' varchar(255), 22 | 'handle_time' INTEGER, 23 | 'ex' varchar(1024), 24 | 'attached_info' varchar(1024), 25 | primary key ('from_user_id', 'to_user_id') 26 | ); 27 | ` 28 | ); 29 | } 30 | 31 | export function insertFriendRequest( 32 | db: Database, 33 | localFriendRequest: LocalFriendRequest 34 | ): QueryExecResult[] { 35 | const sql = squel 36 | .insert() 37 | .into('local_friend_requests') 38 | .setFields(localFriendRequest) 39 | .toString(); 40 | 41 | return db.exec(sql); 42 | } 43 | 44 | export function deleteFriendRequestBothUserID( 45 | db: Database, 46 | fromUserID: string, 47 | toUserID: string 48 | ): QueryExecResult[] { 49 | return db.exec( 50 | ` 51 | delete 52 | from local_friend_requests 53 | where from_user_id = "${fromUserID}" 54 | and to_user_id = "${toUserID}" 55 | ` 56 | ); 57 | } 58 | 59 | export function updateFriendRequest( 60 | db: Database, 61 | localFriendRequest: LocalFriendRequest 62 | ): QueryExecResult[] { 63 | const sql = squel 64 | .update() 65 | .table('local_friend_requests') 66 | .setFields(localFriendRequest) 67 | .where( 68 | `from_user_id = '${localFriendRequest.from_user_id}' and to_user_id = '${localFriendRequest.to_user_id}'` 69 | ) 70 | .toString(); 71 | 72 | return db.exec(sql); 73 | } 74 | 75 | export function getRecvFriendApplication( 76 | db: Database, 77 | loginUserID: string 78 | ): QueryExecResult[] { 79 | return db.exec( 80 | ` 81 | select * 82 | from local_friend_requests 83 | where to_user_id = "${loginUserID}" 84 | order by create_time desc 85 | ` 86 | ); 87 | } 88 | 89 | export function getSendFriendApplication( 90 | db: Database, 91 | loginUserID: string 92 | ): QueryExecResult[] { 93 | return db.exec( 94 | ` 95 | select * from local_friend_requests 96 | where from_user_id = "${loginUserID}" 97 | order by create_time desc 98 | ` 99 | ); 100 | } 101 | 102 | export function getFriendApplicationByBothID( 103 | db: Database, 104 | fromUserID: string, 105 | toUserID: boolean 106 | ): QueryExecResult[] { 107 | return db.exec( 108 | ` 109 | select * 110 | from local_friend_requests 111 | where from_user_id = "${fromUserID}" 112 | and to_user_id = "${toUserID}" 113 | limit 1 114 | ` 115 | ); 116 | } 117 | 118 | export function getBothFriendReq( 119 | db: Database, 120 | fromUserID: string, 121 | toUserID: boolean 122 | ): QueryExecResult[] { 123 | return db.exec( 124 | ` 125 | select * 126 | from local_friend_requests 127 | where (from_user_id = "${fromUserID}" 128 | and to_user_id = "${toUserID}") 129 | or (from_user_id = "${toUserID}" 130 | and to_user_id = "${fromUserID}") 131 | ` 132 | ); 133 | } 134 | -------------------------------------------------------------------------------- /src/sqls/localGroupRequests.ts: -------------------------------------------------------------------------------- 1 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 2 | import squel from 'squel'; 3 | 4 | export type LocalGroupRequest = { [key: string]: any }; 5 | 6 | export function localGroupRequests(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists "local_group_requests" ( 10 | "group_id" varchar(64), 11 | "group_name" text, 12 | "notification" varchar(255), 13 | "introduction" varchar(255), 14 | "face_url" varchar(255), 15 | "create_time" integer, 16 | "status" integer, 17 | "creator_user_id" varchar(64), 18 | "group_type" integer, 19 | "owner_user_id" varchar(64), 20 | "member_count" integer, 21 | "user_id" varchar(64), 22 | "nickname" varchar(255), 23 | "user_face_url" varchar(255), 24 | "gender" integer, 25 | "handle_result" integer, 26 | "req_msg" varchar(255), 27 | "handle_msg" varchar(255), 28 | "req_time" integer, 29 | "handle_user_id" varchar(64), 30 | "handle_time" integer, 31 | "ex" varchar(1024), 32 | "attached_info" varchar(1024), 33 | "join_source" integer, 34 | "inviter_user_id" text, 35 | PRIMARY KEY ("group_id", "user_id") 36 | ); 37 | ` 38 | ); 39 | } 40 | 41 | export function insertGroupRequest( 42 | db: Database, 43 | localGroupRequest: LocalGroupRequest 44 | ): QueryExecResult[] { 45 | const sql = squel 46 | .insert() 47 | .into('local_group_requests') 48 | .setFields(localGroupRequest) 49 | .toString(); 50 | 51 | return db.exec(sql); 52 | } 53 | 54 | export function deleteGroupRequest( 55 | db: Database, 56 | groupID: string, 57 | userID: string 58 | ): QueryExecResult[] { 59 | return db.exec( 60 | ` 61 | delete 62 | from local_group_requests 63 | where group_id = "${groupID}" 64 | and user_id = "${userID}" 65 | ` 66 | ); 67 | } 68 | 69 | export function updateGroupRequest( 70 | db: Database, 71 | localGroupRequest: LocalGroupRequest 72 | ): QueryExecResult[] { 73 | const sql = squel 74 | .update() 75 | .table('local_group_requests') 76 | .setFields(localGroupRequest) 77 | .where( 78 | `group_id = '${localGroupRequest.group_id}' and user_id = '${localGroupRequest.user_id}'` 79 | ) 80 | .toString(); 81 | 82 | return db.exec(sql); 83 | } 84 | 85 | export function getSendGroupApplication(db: Database): QueryExecResult[] { 86 | return db.exec( 87 | ` 88 | select * 89 | from local_group_requests 90 | order by create_time desc 91 | ` 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /src/sqls/localGroups.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type LocalGroup = { [key: string]: any }; 5 | 6 | export function localGroups(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_groups' 10 | ( 11 | 'group_id' varchar(64) PRIMARY KEY, 12 | 'name' TEXT, 13 | 'notification' varchar(255), 14 | 'introduction' varchar(255), 15 | 'face_url' varchar(255), 16 | 'create_time' INTEGER, 17 | 'status' INTEGER, 18 | 'creator_user_id' varchar(64), 19 | 'group_type' INTEGER, 20 | 'owner_user_id' varchar(64), 21 | 'member_count' INTEGER, 22 | 'ex' varchar(1024), 23 | 'attached_info' varchar(1024), 24 | 'need_verification' INTEGER, 25 | 'look_member_info' INTEGER, 26 | 'apply_member_friend' INTEGER, 27 | 'notification_update_time' INTEGER, 28 | 'notification_user_id' TEXT, 29 | 'display_is_read' numeric 30 | ) 31 | ` 32 | ); 33 | } 34 | 35 | export function insertGroup( 36 | db: Database, 37 | localGroup: LocalGroup 38 | ): QueryExecResult[] { 39 | const sql = squel 40 | .insert() 41 | .into('local_groups') 42 | .setFields(localGroup) 43 | .toString(); 44 | 45 | return db.exec(sql); 46 | } 47 | 48 | export function deleteGroup(db: Database, groupID: string): QueryExecResult[] { 49 | return db.exec( 50 | ` 51 | DELETE FROM local_groups 52 | WHERE group_id="${groupID}" 53 | ` 54 | ); 55 | } 56 | 57 | export function updateGroup( 58 | db: Database, 59 | groupID: string, 60 | localGroup: LocalGroup 61 | ): QueryExecResult[] { 62 | const sql = squel 63 | .update() 64 | .table('local_groups') 65 | .setFields(localGroup) 66 | .where(`group_id = '${groupID}'`) 67 | .toString(); 68 | 69 | return db.exec(sql); 70 | } 71 | 72 | export function getJoinedGroupList(db: Database): QueryExecResult[] { 73 | return db.exec( 74 | ` 75 | SELECT * FROM local_groups 76 | ` 77 | ); 78 | } 79 | 80 | export function getGroupInfoByGroupID( 81 | db: Database, 82 | groupID: string 83 | ): QueryExecResult[] { 84 | return db.exec( 85 | ` 86 | SELECT * 87 | FROM local_groups 88 | WHERE group_id = "${groupID}" 89 | ` 90 | ); 91 | } 92 | 93 | export function getAllGroupInfoByGroupIDOrGroupName( 94 | db: Database, 95 | keyword: string, 96 | isSearchGroupID: boolean, 97 | isSearchGroupName: boolean 98 | ): QueryExecResult[] { 99 | let totalConditionStr = ''; 100 | const groupIDCondition = `group_id like "%${keyword}%"`; 101 | const groupNameCondition = `name like "%${keyword}%"`; 102 | if (isSearchGroupID) { 103 | totalConditionStr = groupIDCondition; 104 | } 105 | if (isSearchGroupName) { 106 | totalConditionStr = groupNameCondition; 107 | } 108 | if (isSearchGroupName && isSearchGroupID) { 109 | totalConditionStr = groupIDCondition + ' or ' + groupNameCondition; 110 | } 111 | return db.exec( 112 | ` 113 | select * 114 | from local_groups 115 | where ${totalConditionStr} 116 | order by create_time desc 117 | ` 118 | ); 119 | } 120 | 121 | export function subtractMemberCount( 122 | db: Database, 123 | groupID: string 124 | ): QueryExecResult[] { 125 | return db.exec( 126 | ` 127 | update local_groups set member_count = member_count-1 where group_id = '${groupID}' 128 | ` 129 | ); 130 | } 131 | 132 | export function addMemberCount( 133 | db: Database, 134 | groupID: string 135 | ): QueryExecResult[] { 136 | return db.exec( 137 | ` 138 | update local_groups set member_count = member_count+1 where group_id = '${groupID}' 139 | ` 140 | ); 141 | } 142 | 143 | export function getGroupMemberAllGroupIDs(db: Database): QueryExecResult[] { 144 | return db.exec( 145 | ` 146 | select distinct group_id from local_group_members 147 | ` 148 | ); 149 | } 150 | 151 | export function getGroups(db: Database, groupIDs: string[]): QueryExecResult[] { 152 | const values = groupIDs.map(v => `'${v}'`).join(','); 153 | return db.exec( 154 | ` 155 | select * from local_groups where group_id in (${values}); 156 | ` 157 | ); 158 | } 159 | 160 | export function deleteAllGroup(db: Database): QueryExecResult[] { 161 | return db.exec( 162 | ` 163 | DELETE FROM local_groups; 164 | ` 165 | ); 166 | } 167 | -------------------------------------------------------------------------------- /src/sqls/localNotification.ts: -------------------------------------------------------------------------------- 1 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 2 | 3 | export type LocalNotification = { [key: string]: any }; 4 | 5 | export function localNotification(db: Database): QueryExecResult[] { 6 | return db.exec( 7 | ` 8 | create table if not exists 'local_notification_seqs' 9 | ( 10 | 'conversation_id' char(128), 11 | 'seq' integer, 12 | PRIMARY KEY ('conversation_id') 13 | ) 14 | ` 15 | ); 16 | } 17 | 18 | export function insertNotificationSeq( 19 | db: Database, 20 | conversationID: string, 21 | seq: number 22 | ): QueryExecResult[] { 23 | return db.exec( 24 | `INSERT INTO local_notification_seqs (conversation_id, seq) VALUES ("${conversationID}", ${seq});` 25 | ); 26 | } 27 | 28 | export function setNotificationSeq( 29 | db: Database, 30 | conversationID: string, 31 | seq: number 32 | ): QueryExecResult[] { 33 | return db.exec( 34 | `UPDATE local_notification_seqs set seq = ${seq} where conversation_id = "${conversationID}"` 35 | ); 36 | } 37 | 38 | export function getNotificationAllSeqs(db: Database): QueryExecResult[] { 39 | return db.exec('SELECT * from local_notification_seqs where 1 = 1;'); 40 | } 41 | -------------------------------------------------------------------------------- /src/sqls/localSendingMessages.ts: -------------------------------------------------------------------------------- 1 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 2 | import squel from 'squel'; 3 | 4 | export type LocalSendingMessage = { [key: string]: any }; 5 | 6 | export function localSendingMessages(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_sending_messages' 10 | ( 11 | conversation_id varchar(128), 12 | client_msg_id varchar(64), 13 | ex varchar(1024), 14 | PRIMARY KEY ('conversation_id', 'client_msg_id') 15 | ); 16 | ` 17 | ); 18 | } 19 | 20 | export function insertSendingMessage( 21 | db: Database, 22 | localSendingMessage: LocalSendingMessage 23 | ): QueryExecResult[] { 24 | const sql = squel 25 | .insert() 26 | .into('local_sending_messages') 27 | .setFields(localSendingMessage) 28 | .toString(); 29 | 30 | return db.exec(sql); 31 | } 32 | 33 | export function deleteSendingMessage( 34 | db: Database, 35 | conversationID: string, 36 | clientMsgID: string 37 | ): QueryExecResult[] { 38 | const sql = squel 39 | .delete() 40 | .from('local_sending_messages') 41 | .where(`conversation_id = '${conversationID}'`) 42 | .where(`client_msg_id = '${clientMsgID}'`) 43 | .toString(); 44 | 45 | return db.exec(sql); 46 | } 47 | 48 | export function getAllSendingMessages(db: Database): QueryExecResult[] { 49 | const sql = squel.select().from('local_sending_messages').toString(); 50 | 51 | return db.exec(sql); 52 | } 53 | -------------------------------------------------------------------------------- /src/sqls/localStranger.ts: -------------------------------------------------------------------------------- 1 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 2 | import squel from 'squel'; 3 | 4 | export type LocalStranger = { [key: string]: any }; 5 | 6 | export function localStranger(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_stranger' 10 | ( 11 | 'user_id' varchar(64), 12 | 'name' varchar(255), 13 | 'face_url' varchar(255), 14 | 'create_time' integer, 15 | 'app_manger_level' integer, 16 | 'ex' varchar(1024), 17 | 'attached_info' varchar(1024), 18 | 'global_recv_msg_opt' integer, 19 | PRIMARY KEY ('user_id') 20 | ) 21 | ` 22 | ); 23 | } 24 | 25 | export function getStrangerInfo( 26 | db: Database, 27 | userIDList: string[] 28 | ): QueryExecResult[] { 29 | const ids = userIDList.map(v => `'${v}'`); 30 | return db.exec( 31 | ` 32 | select * 33 | from local_stranger 34 | WHERE user_id = (${ids.join(',')}) 35 | ` 36 | ); 37 | } 38 | 39 | export function insertStrangerInfo( 40 | db: Database, 41 | localStranger: LocalStranger 42 | ): QueryExecResult[] { 43 | const sql = squel 44 | .insert() 45 | .into('local_stranger') 46 | .setFields(localStranger) 47 | .toString(); 48 | 49 | return db.exec(sql); 50 | } 51 | 52 | export function updateStrangerInfo( 53 | db: Database, 54 | localStranger: LocalStranger 55 | ): QueryExecResult[] { 56 | const sql = squel 57 | .update() 58 | .table('local_stranger') 59 | .setFields(localStranger) 60 | .where(`user_id = '${localStranger.user_id}'`) 61 | .toString(); 62 | 63 | return db.exec(sql); 64 | } 65 | -------------------------------------------------------------------------------- /src/sqls/localSuperGroups.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type ClientGroup = { [key: string]: unknown }; 5 | 6 | export function localSuperGroups(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_super_groups' ( 10 | 'group_id' varchar(64), 11 | 'name' text, 12 | 'notification' varchar(255), 13 | 'introduction' varchar(255), 14 | 'face_url' varchar(255), 15 | 'create_time' integer, 16 | 'status' integer, 17 | 'creator_user_id' varchar(64), 18 | 'group_type' integer, 19 | 'owner_user_id' varchar(64), 20 | 'member_count' integer, 21 | 'ex' varchar(1024), 22 | 'attached_info' varchar(1024), 23 | 'need_verification' integer, 24 | 'look_member_info' integer, 25 | 'apply_member_friend' integer, 26 | 'notification_update_time' integer, 27 | 'notification_user_id' text, 28 | primary key ('group_id') 29 | ) 30 | ` 31 | ); 32 | } 33 | 34 | export function getJoinedSuperGroupList(db: Database): QueryExecResult[] { 35 | return db.exec( 36 | ` 37 | select * from local_super_groups; 38 | ` 39 | ); 40 | } 41 | 42 | export function insertSuperGroup( 43 | db: Database, 44 | group: ClientGroup 45 | ): QueryExecResult[] { 46 | const sql = squel 47 | .insert() 48 | .into('local_super_groups') 49 | .setFields(group) 50 | .toString(); 51 | 52 | return db.exec(sql); 53 | } 54 | 55 | export function updateSuperGroup( 56 | db: Database, 57 | groupID: string, 58 | group: ClientGroup 59 | ): QueryExecResult[] { 60 | const sql = squel 61 | .update() 62 | .table('local_super_groups') 63 | .setFields(group) 64 | .where(`group_id = '${groupID}'`) 65 | .toString(); 66 | 67 | return db.exec(sql); 68 | } 69 | 70 | export function deleteSuperGroup( 71 | db: Database, 72 | groupID: string 73 | ): QueryExecResult[] { 74 | return db.exec( 75 | ` 76 | delete from local_super_groups where group_id = '${groupID}'; 77 | ` 78 | ); 79 | } 80 | 81 | export function getSuperGroupInfoByGroupID( 82 | db: Database, 83 | groupID: string 84 | ): QueryExecResult[] { 85 | return db.exec( 86 | ` 87 | select * from local_super_groups where group_id = '${groupID}' LIMIT 1; 88 | ` 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /src/sqls/localTableMaster.ts: -------------------------------------------------------------------------------- 1 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 2 | 3 | export type LocalTableMaster = { [key: string]: any }; 4 | 5 | // Init Table Tips: 6 | // This table is automatically generated by sqlite and does not need to be manually created! 7 | export function getExistedTables(db: Database): QueryExecResult[] { 8 | return db.exec( 9 | ` 10 | SELECT name FROM sqlite_master WHERE type="table"; 11 | ` 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/sqls/localUpload.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type ClientUpload = { [key: string]: unknown }; 5 | 6 | export function localUploads(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_uploads' ( 10 | 'part_hash' text, 11 | 'upload_id' varchar(1000), 12 | 'upload_info' varchar(2000), 13 | 'expire_time' integer, 14 | 'create_time' integer, 15 | PRIMARY KEY ('part_hash') 16 | ) 17 | ` 18 | ); 19 | } 20 | 21 | export function getUpload(db: Database, partHash: string): QueryExecResult[] { 22 | return db.exec( 23 | ` 24 | select * from local_uploads where part_hash = '${partHash}' limit 1; 25 | ` 26 | ); 27 | } 28 | 29 | export function insertUpload( 30 | db: Database, 31 | upload: ClientUpload 32 | ): QueryExecResult[] { 33 | const sql = squel.insert().into('local_uploads').setFields(upload).toString(); 34 | 35 | return db.exec(sql); 36 | } 37 | 38 | export function updateUpload( 39 | db: Database, 40 | upload: ClientUpload 41 | ): QueryExecResult[] { 42 | const sql = squel 43 | .update() 44 | .table('local_uploads') 45 | .setFields(upload) 46 | .where(`part_hash = '${upload.part_hash}'`) 47 | .toString(); 48 | 49 | return db.exec(sql); 50 | } 51 | 52 | export function deleteUpload( 53 | db: Database, 54 | partHash: string 55 | ): QueryExecResult[] { 56 | const sql = squel 57 | .delete() 58 | .from('local_uploads') 59 | .where(`part_hash = '${partHash}'`) 60 | .toString(); 61 | 62 | return db.exec(sql); 63 | } 64 | 65 | export function deleteExpireUpload(db: Database): QueryExecResult[] { 66 | const sql = squel 67 | .delete() 68 | .from('local_uploads') 69 | .where(`expire_time <= ${Date.now()}`) 70 | .toString(); 71 | 72 | return db.exec(sql); 73 | } 74 | -------------------------------------------------------------------------------- /src/sqls/localUsers.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type ClientUser = { [key: string]: unknown }; 5 | 6 | export function localUsers(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_users' ( 10 | 'user_id' varchar(64), 11 | 'name' varchar(255), 12 | 'face_url' varchar(255), 13 | 'create_time' integer, 14 | 'app_manger_level' integer, 15 | 'ex' varchar(1024), 16 | 'attached_info' varchar(1024), 17 | 'global_recv_msg_opt' integer, 18 | primary key ('user_id') 19 | ) 20 | ` 21 | ); 22 | } 23 | 24 | export function getLoginUser(db: Database, userID: string): QueryExecResult[] { 25 | return db.exec( 26 | ` 27 | select *, name as nickname from local_users where user_id = '${userID}' limit 1; 28 | ` 29 | ); 30 | } 31 | 32 | export function insertLoginUser( 33 | db: Database, 34 | user: ClientUser 35 | ): QueryExecResult[] { 36 | const sql = squel.insert().into('local_users').setFields(user).toString(); 37 | 38 | return db.exec(sql); 39 | } 40 | 41 | export function updateLoginUser( 42 | db: Database, 43 | user: ClientUser 44 | ): QueryExecResult[] { 45 | const sql = squel 46 | .update() 47 | .table('local_users') 48 | .setFields(user) 49 | .where(`user_id = '${user.user_id}'`) 50 | .toString(); 51 | 52 | return db.exec(sql); 53 | } 54 | -------------------------------------------------------------------------------- /src/sqls/localVersionSync.ts: -------------------------------------------------------------------------------- 1 | import squel from 'squel'; 2 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 3 | 4 | export type LocalVersionSync = { [key: string]: any }; 5 | 6 | export function localVersionSyncs(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'local_sync_version' ( 10 | 'table_name' varchar(255), 11 | 'entity_id' varchar(255), 12 | 'version_id' text, 13 | 'version' integer, 14 | 'create_time' integer, 15 | 'id_list' text, 16 | primary key ('table_name','entity_id') 17 | ) 18 | ` 19 | ); 20 | } 21 | 22 | export function getVersionSync( 23 | db: Database, 24 | tableName: string, 25 | entityID: string 26 | ): QueryExecResult[] { 27 | return db.exec( 28 | ` 29 | SELECT * FROM local_sync_version WHERE table_name = "${tableName}" AND entity_id = "${entityID}" 30 | ` 31 | ); 32 | } 33 | 34 | export function insertVersionSync( 35 | db: Database, 36 | localVersionSync: LocalVersionSync 37 | ): QueryExecResult[] { 38 | delete localVersionSync.table; 39 | const sql = squel 40 | .insert() 41 | .into('local_sync_version') 42 | .setFields(localVersionSync) 43 | .toString(); 44 | 45 | return db.exec(sql); 46 | } 47 | 48 | export function updateVersionSync( 49 | db: Database, 50 | oldTable: string, 51 | oldEntityID: string, 52 | localVersionSync: LocalVersionSync 53 | ): QueryExecResult[] { 54 | delete localVersionSync.table; 55 | const sql = squel 56 | .update() 57 | .table('local_sync_version') 58 | .setFields(localVersionSync) 59 | .where(`table_name = '${oldTable}' AND entity_id = '${oldEntityID}'`) 60 | .toString(); 61 | 62 | return db.exec(sql); 63 | } 64 | 65 | export function deleteVersionSync( 66 | db: Database, 67 | tableName: string, 68 | entityID: string 69 | ): QueryExecResult[] { 70 | return db.exec( 71 | ` 72 | DELETE FROM local_sync_version WHERE table_name = "${tableName}" AND entity_id = "${entityID}"; 73 | ` 74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /src/sqls/tempCacheLocalChatLogs.ts: -------------------------------------------------------------------------------- 1 | import { Database, QueryExecResult } from '@jlongster/sql.js'; 2 | import squel from 'squel'; 3 | 4 | export type TempCacheClientMessage = { [key: string]: any }; 5 | 6 | export function tempCacheLocalChatLogs(db: Database): QueryExecResult[] { 7 | return db.exec( 8 | ` 9 | create table if not exists 'temp_cache_local_chat_logs' ( 10 | 'client_msg_id' char(64), 11 | 'server_msg_id' char(64), 12 | 'send_id' char(64), 13 | 'recv_id' char(64), 14 | 'sender_platform_id' integer, 15 | 'sender_nick_name' varchar(255), 16 | 'sender_face_url' varchar(255), 17 | 'session_type' integer, 18 | 'msg_from' integer, 19 | 'content_type' integer, 20 | 'content' varchar(1000), 21 | 'is_read' numeric, 22 | 'status' integer, 23 | 'seq' integer DEFAULT 0, 24 | 'send_time' integer, 25 | 'create_time' integer, 26 | 'attached_info' varchar(1024), 27 | 'ex' varchar(1024), 28 | PRIMARY KEY ('client_msg_id') 29 | ); 30 | ` 31 | ); 32 | } 33 | 34 | export function batchInsertTempCacheMessageList( 35 | db: Database, 36 | messageList: TempCacheClientMessage[] 37 | ): QueryExecResult[] { 38 | const sql = squel 39 | .insert() 40 | .into('temp_cache_local_chat_logs') 41 | .setFieldsRows(messageList) 42 | .toString(); 43 | 44 | return db.exec(sql); 45 | } 46 | -------------------------------------------------------------------------------- /src/types/@jlongster/sql.js/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | declare module '@jlongster/sql.js' { 3 | /// 4 | /// 5 | 6 | export type SqlValue = number | string | Uint8Array | null; 7 | export type ParamsObject = Record; 8 | export type ParamsCallback = (obj: ParamsObject) => void; 9 | export type SqlJsConfig = { 10 | locateFile: (str: string) => string; 11 | }; 12 | export type BindParams = SqlValue[] | ParamsObject | null; 13 | 14 | export interface QueryExecResult { 15 | columns: string[]; 16 | values: SqlValue[][]; 17 | } 18 | 19 | export interface StatementIteratorResult { 20 | /** `true` if there are no more available statements */ 21 | done: boolean; 22 | /** the next available Statement (as returned by `Database.prepare`) */ 23 | value: Statement; 24 | } 25 | 26 | export interface SqlJsStatic { 27 | Database: typeof Database; 28 | Statement: typeof Statement; 29 | 30 | register_for_idb: (v: any) => any; 31 | FS: { 32 | mkdir: (path: string) => void; 33 | mount: (...args: any[]) => void; 34 | }; 35 | } 36 | 37 | export interface InitSqlJsStatic extends Function { 38 | (config?: SqlJsConfig): Promise; 39 | readonly default: this; 40 | } 41 | 42 | export declare class Database { 43 | /** 44 | * Represents an SQLite database 45 | * @see [https://sql.js.org/documentation/Database.html#Database](https://sql.js.org/documentation/Database.html#Database) 46 | * 47 | * @param data An array of bytes representing an SQLite database file 48 | */ 49 | constructor(data?: ArrayLike | Buffer | null | string, opts?: any); 50 | 51 | /** 52 | * Close the database, and all associated prepared statements. The 53 | * memory associated to the database and all associated statements will 54 | * be freed. 55 | * 56 | * **Warning**: A statement belonging to a database that has been closed 57 | * cannot be used anymore. 58 | * 59 | * Databases must be closed when you're finished with them, or the 60 | * memory consumption will grow forever 61 | * @see [https://sql.js.org/documentation/Database.html#["close"]](https://sql.js.org/documentation/Database.html#%5B%22close%22%5D) 62 | */ 63 | close(): void; 64 | 65 | /** 66 | * Register a custom function with SQLite 67 | * @see [https://sql.js.org/documentation/Database.html#["create_function"]](https://sql.js.org/documentation/Database.html#%5B%22create_function%22%5D) 68 | * 69 | * @param name the name of the function as referenced in SQL statements. 70 | * @param func the actual function to be executed. 71 | */ 72 | create_function(name: string, func: (...args: any[]) => any): Database; 73 | 74 | /** 75 | * Execute an sql statement, and call a callback for each row of result. 76 | * 77 | * Currently this method is synchronous, it will not return until the 78 | * callback has been called on every row of the result. But this might 79 | * change. 80 | * @see [https://sql.js.org/documentation/Database.html#["each"]](https://sql.js.org/documentation/Database.html#%5B%22each%22%5D) 81 | * 82 | * @param sql A string of SQL text. Can contain placeholders that will 83 | * be bound to the parameters given as the second argument 84 | * @param params Parameters to bind to the query 85 | * @param callback Function to call on each row of result 86 | * @param done A function that will be called when all rows have been 87 | * retrieved 88 | */ 89 | each( 90 | sql: string, 91 | params: BindParams, 92 | callback: ParamsCallback, 93 | done: () => void 94 | ): Database; 95 | each(sql: string, callback: ParamsCallback, done: () => void): Database; 96 | 97 | /** 98 | * Execute an SQL query, and returns the result. 99 | * 100 | * This is a wrapper against `Database.prepare`, `Statement.bind`, `Statement.step`, `Statement.get`, and `Statement.free`. 101 | * 102 | * The result is an array of result elements. There are as many result elements as the number of statements in your sql string (statements are separated by a semicolon) 103 | * @see [https://sql.js.org/documentation/Database.html#["exec"]](https://sql.js.org/documentation/Database.html#%5B%22exec%22%5D) 104 | * 105 | * @param sql a string containing some SQL text to execute 106 | * @param params When the SQL statement contains placeholders, you can 107 | * pass them in here. They will be bound to the statement before it is 108 | * executed. If you use the params argument as an array, you **cannot** 109 | * provide an sql string that contains several statements (separated by 110 | * `;`). This limitation does not apply to params as an object. 111 | */ 112 | exec(sql: string, params?: BindParams): QueryExecResult[]; 113 | 114 | /** 115 | * Exports the contents of the database to a binary array 116 | * @see [https://sql.js.org/documentation/Database.html#["export"]](https://sql.js.org/documentation/Database.html#%5B%22export%22%5D) 117 | */ 118 | export(): Uint8Array; 119 | 120 | /** 121 | * Returns the number of changed rows (modified, inserted or deleted) by 122 | * the latest completed `INSERT`, `UPDATE` or `DELETE` statement on the 123 | * database. Executing any other type of SQL statement does not modify 124 | * the value returned by this function. 125 | * @see [https://sql.js.org/documentation/Database.html#["getRowsModified"]](https://sql.js.org/documentation/Database.html#%5B%22getRowsModified%22%5D) 126 | */ 127 | getRowsModified(): number; 128 | 129 | /** 130 | * Analyze a result code, return null if no error occured, and throw an 131 | * error with a descriptive message otherwise 132 | * @see [https://sql.js.org/documentation/Database.html#["handleError"]](https://sql.js.org/documentation/Database.html#%5B%22handleError%22%5D) 133 | */ 134 | handleError(): null | never; 135 | 136 | /** 137 | * Iterate over multiple SQL statements in a SQL string. This function 138 | * returns an iterator over Statement objects. You can use a `for..of` 139 | * loop to execute the returned statements one by one. 140 | * @see [https://sql.js.org/documentation/Database.html#["iterateStatements"]](https://sql.js.org/documentation/Database.html#%5B%22iterateStatements%22%5D) 141 | * 142 | * @param sql a string of SQL that can contain multiple statements 143 | */ 144 | iterateStatements(sql: string): StatementIterator; 145 | 146 | /** 147 | * Prepare an SQL statement 148 | * @see [https://sql.js.org/documentation/Database.html#["prepare"]](https://sql.js.org/documentation/Database.html#%5B%22prepare%22%5D) 149 | * 150 | * @param sql a string of SQL, that can contain placeholders (`?`, `:VVV`, `:AAA`, `@AAA`) 151 | * @param params values to bind to placeholders 152 | */ 153 | prepare(sql: string, params?: BindParams): Statement; 154 | 155 | /** 156 | * Execute an SQL query, ignoring the rows it returns. 157 | * @see [https://sql.js.org/documentation/Database.html#["run"]](https://sql.js.org/documentation/Database.html#%5B%22run%22%5D) 158 | * 159 | * @param sql a string containing some SQL text to execute 160 | * @param params When the SQL statement contains placeholders, you can 161 | * pass them in here. They will be bound to the statement before it is 162 | * executed. If you use the params argument as an array, you **cannot** 163 | * provide an sql string that contains several statements (separated by 164 | * `;`). This limitation does not apply to params as an object. 165 | */ 166 | run(sql: string, params?: BindParams): Database; 167 | } 168 | 169 | export declare class Statement { 170 | /** 171 | * Bind values to the parameters, after having reseted the statement. If 172 | * values is null, do nothing and return true. 173 | * 174 | * SQL statements can have parameters, named '?', '?NNN', ':VVV', 175 | * '@VVV', '$VVV', where NNN is a number and VVV a string. This function 176 | * binds these parameters to the given values. 177 | * 178 | * Warning: ':', '@', and '$' are included in the parameters names 179 | * 180 | * ### Value types 181 | * 182 | * |Javascript type|SQLite type| 183 | * |-|-| 184 | * |number|REAL, INTEGER| 185 | * |boolean|INTEGER| 186 | * |string|TEXT| 187 | * |Array, Uint8Array|BLOB| 188 | * |null|NULL| 189 | * @see [https://sql.js.org/documentation/Statement.html#["bind"]](https://sql.js.org/documentation/Statement.html#%5B%22bind%22%5D) 190 | * 191 | * @param values The values to bind 192 | */ 193 | bind(values?: BindParams): boolean; 194 | 195 | /** 196 | * Free the memory used by the statement 197 | * @see [https://sql.js.org/documentation/Statement.html#["free"]](https://sql.js.org/documentation/Statement.html#%5B%22free%22%5D) 198 | */ 199 | free(): boolean; 200 | 201 | /** 202 | * Free the memory allocated during parameter binding 203 | * @see [https://sql.js.org/documentation/Statement.html#["freemem"]](https://sql.js.org/documentation/Statement.html#%5B%22freemem%22%5D) 204 | */ 205 | freemem(): void; 206 | 207 | /** 208 | * Get one row of results of a statement. If the first parameter is not 209 | * provided, step must have been called before. 210 | * @see [https://sql.js.org/documentation/Statement.html#["get"]](https://sql.js.org/documentation/Statement.html#%5B%22get%22%5D) 211 | * 212 | * @param params If set, the values will be bound to the statement 213 | * before it is executed 214 | */ 215 | get(params?: BindParams): SqlValue[]; 216 | 217 | /** 218 | * Get one row of result as a javascript object, associating column 219 | * names with their value in the current row 220 | * @see [https://sql.js.org/documentation/Statement.html#["getAsObject"]](https://sql.js.org/documentation/Statement.html#%5B%22getAsObject%22%5D) 221 | * 222 | * @param params If set, the values will be bound to the statement, and 223 | * it will be executed 224 | */ 225 | getAsObject(params?: BindParams): ParamsObject; 226 | 227 | /** 228 | * Get the list of column names of a row of result of a statement. 229 | * @see [https://sql.js.org/documentation/Statement.html#["getColumnNames"]](https://sql.js.org/documentation/Statement.html#%5B%22getColumnNames%22%5D) 230 | */ 231 | getColumnNames(): string[]; 232 | 233 | /** 234 | * Get the SQLite's normalized version of the SQL string used in 235 | * preparing this statement. The meaning of "normalized" is not 236 | * well-defined: see 237 | * [the SQLite documentation](https://sqlite.org/c3ref/expanded_sql.html). 238 | * @see [https://sql.js.org/documentation/Statement.html#["getNormalizedSQL"]](https://sql.js.org/documentation/Statement.html#%5B%22getNormalizedSQL%22%5D) 239 | */ 240 | getNormalizedSQL(): string; 241 | 242 | /** 243 | * Get the SQL string used in preparing this statement. 244 | * @see [https://sql.js.org/documentation/Statement.html#["getSQL"]](https://sql.js.org/documentation/Statement.html#%5B%22getSQL%22%5D) 245 | */ 246 | getSQL(): string; 247 | 248 | /** 249 | * Reset a statement, so that it's parameters can be bound to new 250 | * values. It also clears all previous bindings, freeing the memory used 251 | * by bound parameters. 252 | * @see [https://sql.js.org/documentation/Statement.html#["reset"]](https://sql.js.org/documentation/Statement.html#%5B%22reset%22%5D) 253 | */ 254 | reset(): void; 255 | 256 | /** 257 | * Shorthand for bind + step + reset Bind the values, execute the 258 | * statement, ignoring the rows it returns, and resets it 259 | * @param values Value to bind to the statement 260 | */ 261 | run(values?: BindParams): void; 262 | 263 | /** 264 | * Execute the statement, fetching the the next line of result, that can 265 | * be retrieved with `Statement.get`. 266 | * @see [https://sql.js.org/documentation/Statement.html#["step"]](https://sql.js.org/documentation/Statement.html#%5B%22step%22%5D) 267 | */ 268 | step(): boolean; 269 | } 270 | 271 | /** 272 | * An iterator over multiple SQL statements in a string, preparing and 273 | * returning a Statement object for the next SQL statement on each 274 | * iteration. 275 | * 276 | * You can't instantiate this class directly, you have to use a Database 277 | * object in order to create a statement iterator 278 | * @see [https://sql.js.org/documentation/StatementIterator.html#StatementIterator](https://sql.js.org/documentation/StatementIterator.html#StatementIterator) 279 | */ 280 | export declare class StatementIterator 281 | implements Iterator, Iterable 282 | { 283 | [Symbol.iterator](): Iterator; 284 | /** 285 | * Get any un-executed portions remaining of the original SQL string 286 | * @see [https://sql.js.org/documentation/StatementIterator.html#["getRemainingSQL"]](https://sql.js.org/documentation/StatementIterator.html#%5B%22getRemainingSQL%22%5D) 287 | */ 288 | getRemainingSql(): string; 289 | 290 | /** 291 | * Prepare the next available SQL statement 292 | * @see [https://sql.js.org/documentation/StatementIterator.html#["next"]](https://sql.js.org/documentation/StatementIterator.html#%5B%22next%22%5D) 293 | */ 294 | next(): StatementIteratorResult; 295 | } 296 | 297 | declare global { 298 | let SqlJs: InitSqlJsStatic; 299 | } 300 | 301 | export default SqlJs; 302 | } 303 | -------------------------------------------------------------------------------- /src/types/absurd-sql-optimized/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | declare module 'absurd-sql-optimized' { 3 | export class SQLiteFS { 4 | constructor(fs: any, backend: any); 5 | } 6 | } 7 | declare module 'absurd-sql-optimized/dist/memory-backend' { 8 | export default class MemoryBBackend {} 9 | } 10 | 11 | declare module 'absurd-sql-optimized/dist/indexeddb-backend' { 12 | export default class IndexedDBBackend {} 13 | } 14 | 15 | declare module 'absurd-sql-optimized/dist/indexeddb-main-thread' { 16 | export function initBackend(worker: Worker); 17 | } 18 | -------------------------------------------------------------------------------- /src/types/entity.ts: -------------------------------------------------------------------------------- 1 | import { CbEvents } from '../constant'; 2 | import { 3 | GroupType, 4 | SessionType, 5 | MessageType, 6 | Platform, 7 | MessageStatus, 8 | GroupStatus, 9 | GroupVerificationType, 10 | AllowType, 11 | GroupJoinSource, 12 | GroupMemberRole, 13 | MessageReceiveOptType, 14 | GroupAtType, 15 | LogLevel, 16 | ApplicationHandleResult, 17 | Relationship, 18 | OnlineState, 19 | } from './enum'; 20 | export type WSEvent = { 21 | event: CbEvents; 22 | data: T; 23 | errCode: number; 24 | errMsg: string; 25 | operationID: string; 26 | }; 27 | export type WsResponse = { 28 | event: string; 29 | errCode: number; 30 | errMsg: string; 31 | data: T; 32 | operationID: string; 33 | }; 34 | export type IMConfig = { 35 | platformID: Platform; 36 | apiAddr: string; 37 | wsAddr: string; 38 | dataDir: string; 39 | logLevel: LogLevel; 40 | isLogStandardOutput: boolean; 41 | logFilePath: string; 42 | isExternalExtensions: boolean; 43 | }; 44 | export type MessageEntity = { 45 | type: string; 46 | offset: number; 47 | length: number; 48 | url?: string; 49 | info?: string; 50 | }; 51 | export type PicBaseInfo = { 52 | uuid: string; 53 | type: string; 54 | size: number; 55 | width: number; 56 | height: number; 57 | url: string; 58 | }; 59 | export type AtUsersInfoItem = { 60 | atUserID: string; 61 | groupNickname: string; 62 | }; 63 | export type GroupApplicationItem = { 64 | createTime: number; 65 | creatorUserID: string; 66 | ex: string; 67 | groupFaceURL: string; 68 | groupID: string; 69 | groupName: string; 70 | groupType: GroupType; 71 | handleResult: ApplicationHandleResult; 72 | handleUserID: string; 73 | handledMsg: string; 74 | handledTime: number; 75 | introduction: string; 76 | memberCount: number; 77 | nickname: string; 78 | notification: string; 79 | ownerUserID: string; 80 | reqMsg: string; 81 | reqTime: number; 82 | joinSource: GroupJoinSource; 83 | status: GroupStatus; 84 | userFaceURL: string; 85 | userID: string; 86 | }; 87 | export type FriendApplicationItem = { 88 | createTime: number; 89 | ex: string; 90 | fromFaceURL: string; 91 | fromNickname: string; 92 | fromUserID: string; 93 | handleMsg: string; 94 | handleResult: ApplicationHandleResult; 95 | handleTime: number; 96 | handlerUserID: string; 97 | reqMsg: string; 98 | toFaceURL: string; 99 | toNickname: string; 100 | toUserID: string; 101 | }; 102 | export type PublicUserItem = { 103 | nickname: string; 104 | userID: string; 105 | faceURL: string; 106 | ex: string; 107 | }; 108 | export type SelfUserInfo = { 109 | createTime: number; 110 | ex: string; 111 | faceURL: string; 112 | nickname: string; 113 | userID: string; 114 | globalRecvMsgOpt: MessageReceiveOptType; 115 | }; 116 | export type PartialUserInfo = { 117 | userID: string; 118 | } & Partial>; 119 | export type FriendUserItem = { 120 | addSource: number; 121 | createTime: number; 122 | ex: string; 123 | faceURL: string; 124 | userID: string; 125 | nickname: string; 126 | operatorUserID: string; 127 | ownerUserID: string; 128 | remark: string; 129 | isPinned: boolean; 130 | attachedInfo: string; 131 | }; 132 | export type SearchedFriendsInfo = FriendUserItem & { 133 | relationship: Relationship; 134 | }; 135 | export type FriendshipInfo = { 136 | result: number; 137 | userID: string; 138 | }; 139 | export type BlackUserItem = { 140 | addSource: number; 141 | userID: string; 142 | createTime: number; 143 | ex: string; 144 | faceURL: string; 145 | nickname: string; 146 | operatorUserID: string; 147 | ownerUserID: string; 148 | }; 149 | export type GroupItem = { 150 | groupID: string; 151 | groupName: string; 152 | notification: string; 153 | notificationUserID: string; 154 | notificationUpdateTime: number; 155 | introduction: string; 156 | faceURL: string; 157 | ownerUserID: string; 158 | createTime: number; 159 | memberCount: number; 160 | status: GroupStatus; 161 | creatorUserID: string; 162 | groupType: GroupType; 163 | needVerification: GroupVerificationType; 164 | ex: string; 165 | applyMemberFriend: AllowType; 166 | lookMemberInfo: AllowType; 167 | displayIsRead: boolean; 168 | }; 169 | export type GroupMemberItem = { 170 | groupID: string; 171 | userID: string; 172 | nickname: string; 173 | faceURL: string; 174 | roleLevel: GroupMemberRole; 175 | muteEndTime: number; 176 | joinTime: number; 177 | joinSource: GroupJoinSource; 178 | inviterUserID: string; 179 | operatorUserID: string; 180 | ex: string; 181 | }; 182 | export type ConversationItem = { 183 | conversationID: string; 184 | conversationType: SessionType; 185 | userID: string; 186 | groupID: string; 187 | showName: string; 188 | faceURL: string; 189 | recvMsgOpt: MessageReceiveOptType; 190 | unreadCount: number; 191 | groupAtType: GroupAtType; 192 | latestMsg: string; 193 | latestMsgSendTime: number; 194 | draftText: string; 195 | draftTextTime: number; 196 | burnDuration: number; 197 | msgDestructTime: number; 198 | isPinned: boolean; 199 | isNotInGroup: boolean; 200 | isPrivateChat: boolean; 201 | isMsgDestruct: boolean; 202 | attachedInfo: string; 203 | ex?: string; 204 | }; 205 | export type MessageItem = { 206 | clientMsgID: string; 207 | serverMsgID: string; 208 | createTime: number; 209 | sendTime: number; 210 | sessionType: SessionType; 211 | sendID: string; 212 | recvID: string; 213 | msgFrom: number; 214 | contentType: MessageType; 215 | senderPlatformID: Platform; 216 | senderNickname: string; 217 | senderFaceUrl: string; 218 | groupID: string; 219 | content: string; 220 | seq: number; 221 | isRead: boolean; 222 | status: MessageStatus; 223 | isReact?: boolean; 224 | isExternalExtensions?: boolean; 225 | offlinePush?: OfflinePush; 226 | ex?: string; 227 | localEx?: string; 228 | textElem?: TextElem; 229 | cardElem?: CardElem; 230 | pictureElem?: PictureElem; 231 | soundElem?: SoundElem; 232 | videoElem?: VideoElem; 233 | fileElem?: FileElem; 234 | mergeElem?: MergeElem; 235 | atTextElem?: AtTextElem; 236 | faceElem?: FaceElem; 237 | locationElem?: LocationElem; 238 | customElem?: CustomElem; 239 | quoteElem?: QuoteElem; 240 | notificationElem?: NotificationElem; 241 | advancedTextElem?: AdvancedTextElem; 242 | typingElem?: TypingElem; 243 | attachedInfoElem: AttachedInfoElem; 244 | }; 245 | export type TextElem = { 246 | content: string; 247 | }; 248 | export type CardElem = { 249 | userID: string; 250 | nickname: string; 251 | faceURL: string; 252 | ex: string; 253 | }; 254 | export type AtTextElem = { 255 | text: string; 256 | atUserList: string[]; 257 | atUsersInfo?: AtUsersInfoItem[]; 258 | quoteMessage?: MessageItem; 259 | isAtSelf?: boolean; 260 | }; 261 | export type NotificationElem = { 262 | detail: string; 263 | }; 264 | export type AdvancedTextElem = { 265 | text: string; 266 | messageEntityList: MessageEntity[]; 267 | }; 268 | export type TypingElem = { 269 | msgTips: string; 270 | }; 271 | export type CustomElem = { 272 | data: string; 273 | description: string; 274 | extension: string; 275 | }; 276 | export type FileElem = { 277 | filePath: string; 278 | uuid: string; 279 | sourceUrl: string; 280 | fileName: string; 281 | fileSize: number; 282 | }; 283 | export type FaceElem = { 284 | index: number; 285 | data: string; 286 | }; 287 | export type LocationElem = { 288 | description: string; 289 | longitude: number; 290 | latitude: number; 291 | }; 292 | export type MergeElem = { 293 | title: string; 294 | abstractList: string[]; 295 | multiMessage: MessageItem[]; 296 | messageEntityList: MessageEntity[]; 297 | }; 298 | export type OfflinePush = { 299 | title: string; 300 | desc: string; 301 | ex: string; 302 | iOSPushSound: string; 303 | iOSBadgeCount: boolean; 304 | }; 305 | export type PictureElem = { 306 | sourcePath: string; 307 | sourcePicture: Picture; 308 | bigPicture: Picture; 309 | snapshotPicture: Picture; 310 | }; 311 | export type AttachedInfoElem = { 312 | groupHasReadInfo: GroupHasReadInfo; 313 | isPrivateChat: boolean; 314 | isEncryption: boolean; 315 | inEncryptStatus: boolean; 316 | burnDuration: number; 317 | hasReadTime: number; 318 | messageEntityList?: MessageEntity[]; 319 | uploadProgress?: UploadProgress; 320 | }; 321 | export type UploadProgress = { 322 | total: number; 323 | save: number; 324 | current: number; 325 | }; 326 | export type GroupHasReadInfo = { 327 | hasReadCount: number; 328 | unreadCount: number; 329 | hasReadUserIDList: string[]; 330 | groupMemberCount: number; 331 | }; 332 | export type Picture = { 333 | uuid: string; 334 | type: string; 335 | size: number; 336 | width: number; 337 | height: number; 338 | url: string; 339 | }; 340 | export type QuoteElem = { 341 | text: string; 342 | quoteMessage: MessageItem; 343 | }; 344 | export type SoundElem = { 345 | uuid: string; 346 | soundPath: string; 347 | sourceUrl: string; 348 | dataSize: number; 349 | duration: number; 350 | }; 351 | export type VideoElem = { 352 | videoPath: string; 353 | videoUUID: string; 354 | videoUrl: string; 355 | videoType: string; 356 | videoSize: number; 357 | duration: number; 358 | snapshotPath: string; 359 | snapshotUUID: string; 360 | snapshotSize: number; 361 | snapshotUrl: string; 362 | snapshotWidth: number; 363 | snapshotHeight: number; 364 | }; 365 | export type AdvancedRevokeContent = { 366 | clientMsgID: string; 367 | revokeTime: number; 368 | revokerID: string; 369 | revokerNickname: string; 370 | revokerRole: number; 371 | seq: number; 372 | sessionType: SessionType; 373 | sourceMessageSendID: string; 374 | sourceMessageSendTime: number; 375 | sourceMessageSenderNickname: string; 376 | }; 377 | 378 | export type RevokedInfo = { 379 | revokerID: string; 380 | revokerRole: number; 381 | clientMsgID: string; 382 | revokerNickname: string; 383 | revokeTime: number; 384 | sourceMessageSendTime: number; 385 | sourceMessageSendID: string; 386 | sourceMessageSenderNickname: string; 387 | sessionType: number; 388 | seq: number; 389 | ex: string; 390 | }; 391 | 392 | export type ReceiptInfo = { 393 | userID: string; 394 | groupID: string; 395 | msgIDList: string[]; 396 | readTime: number; 397 | msgFrom: number; 398 | contentType: MessageType; 399 | sessionType: SessionType; 400 | }; 401 | 402 | export type SearchMessageResult = { 403 | totalCount: number; 404 | searchResultItems?: SearchMessageResultItem[]; 405 | findResultItems?: SearchMessageResultItem[]; 406 | }; 407 | 408 | export type SearchMessageResultItem = { 409 | conversationID: string; 410 | messageCount: number; 411 | conversationType: SessionType; 412 | showName: string; 413 | faceURL: string; 414 | messageList: MessageItem[]; 415 | }; 416 | 417 | export type AdvancedGetMessageResult = { 418 | isEnd: boolean; 419 | lastMinSeq: number; 420 | errCode: number; 421 | errMsg: string; 422 | messageList: MessageItem[]; 423 | }; 424 | 425 | export type RtcInvite = { 426 | inviterUserID: string; 427 | inviteeUserIDList: string[]; 428 | customData?: string; 429 | groupID: string; 430 | roomID: string; 431 | timeout: number; 432 | mediaType: string; 433 | sessionType: number; 434 | platformID: number; 435 | initiateTime?: number; 436 | busyLineUserIDList?: string[]; 437 | }; 438 | 439 | export type UserOnlineState = { 440 | platformIDs?: Platform[]; 441 | status: OnlineState; 442 | userID: string; 443 | }; 444 | 445 | export type ConversationInputStatus = { 446 | conversationID: string; 447 | userID: string; 448 | platformIDs: Platform[]; 449 | }; 450 | 451 | export type GroupMessageReceiptInfo = { 452 | conversationID: string; 453 | groupMessageReadInfo: GroupMessageReadInfo[]; 454 | }; 455 | export type GroupMessageReadInfo = { 456 | clientMsgID: string; 457 | hasReadCount: number; 458 | unreadCount: number; 459 | readMembers: GroupMemberItem[]; 460 | }; 461 | 462 | export type RtcInviteResults = { 463 | liveURL: string; 464 | roomID: string; 465 | token: string; 466 | busyLineUserIDList?: string[]; 467 | }; 468 | 469 | export type ParticipantInfo = { 470 | userInfo: PublicUserItem; 471 | groupMemberInfo?: GroupMemberItem; 472 | groupInfo?: GroupItem; 473 | }; 474 | 475 | export type CallingRoomData = { 476 | participant?: ParticipantInfo[]; 477 | invitation?: RtcInvite; 478 | roomID: string; 479 | }; 480 | -------------------------------------------------------------------------------- /src/types/enum.ts: -------------------------------------------------------------------------------- 1 | export enum MessageReceiveOptType { 2 | Normal = 0, 3 | NotReceive = 1, 4 | NotNotify = 2, 5 | } 6 | export enum AllowType { 7 | Allowed = 0, 8 | NotAllowed = 1, 9 | } 10 | export enum GroupType { 11 | Group = 2, 12 | WorkingGroup = 2, 13 | } 14 | export enum GroupJoinSource { 15 | Invitation = 2, 16 | Search = 3, 17 | QrCode = 4, 18 | } 19 | export enum GroupMemberRole { 20 | Normal = 20, 21 | Admin = 60, 22 | Owner = 100, 23 | } 24 | export enum GroupVerificationType { 25 | ApplyNeedInviteNot = 0, 26 | AllNeed = 1, 27 | AllNot = 2, 28 | } 29 | export enum MessageStatus { 30 | Sending = 1, 31 | Succeed = 2, 32 | Failed = 3, 33 | } 34 | export enum Platform { 35 | iOS = 1, 36 | Android = 2, 37 | Windows = 3, 38 | MacOSX = 4, 39 | Web = 5, 40 | Linux = 7, 41 | AndroidPad = 8, 42 | iPad = 9, 43 | } 44 | export enum LogLevel { 45 | Verbose = 6, 46 | Debug = 5, 47 | Info = 4, 48 | Warn = 3, 49 | Error = 2, 50 | Fatal = 1, 51 | Panic = 0, 52 | } 53 | export enum ApplicationHandleResult { 54 | Unprocessed = 0, 55 | Agree = 1, 56 | Reject = -1, 57 | } 58 | export enum MessageType { 59 | TextMessage = 101, 60 | PictureMessage = 102, 61 | VoiceMessage = 103, 62 | VideoMessage = 104, 63 | FileMessage = 105, 64 | AtTextMessage = 106, 65 | MergeMessage = 107, 66 | CardMessage = 108, 67 | LocationMessage = 109, 68 | CustomMessage = 110, 69 | TypingMessage = 113, 70 | QuoteMessage = 114, 71 | FaceMessage = 115, 72 | FriendAdded = 1201, 73 | OANotification = 1400, 74 | GroupCreated = 1501, 75 | GroupInfoUpdated = 1502, 76 | MemberQuit = 1504, 77 | GroupOwnerTransferred = 1507, 78 | MemberKicked = 1508, 79 | MemberInvited = 1509, 80 | MemberEnter = 1510, 81 | GroupDismissed = 1511, 82 | GroupMemberMuted = 1512, 83 | GroupMemberCancelMuted = 1513, 84 | GroupMuted = 1514, 85 | GroupCancelMuted = 1515, 86 | GroupAnnouncementUpdated = 1519, 87 | GroupNameUpdated = 1520, 88 | BurnMessageChange = 1701, 89 | RevokeMessage = 2101, 90 | } 91 | export enum SessionType { 92 | Single = 1, 93 | Group = 3, 94 | WorkingGroup = 3, 95 | Notification = 4, 96 | } 97 | export enum GroupStatus { 98 | Normal = 0, 99 | Banned = 1, 100 | Dismissed = 2, 101 | Muted = 3, 102 | } 103 | export enum GroupAtType { 104 | AtNormal = 0, 105 | AtMe = 1, 106 | AtAll = 2, 107 | AtAllAtMe = 3, 108 | AtGroupNotice = 4, 109 | } 110 | export enum GroupMemberFilter { 111 | All = 0, 112 | Owner = 1, 113 | Admin = 2, 114 | Normal = 3, 115 | AdminAndNormal = 4, 116 | AdminAndOwner = 5, 117 | } 118 | export enum Relationship { 119 | isBlack = 0, 120 | isFriend = 1, 121 | } 122 | export enum LoginStatus { 123 | Logout = 1, 124 | Logging = 2, 125 | Logged = 3, 126 | } 127 | export enum OnlineState { 128 | Online = 1, 129 | Offline = 0, 130 | } 131 | export enum GroupMessageReaderFilter { 132 | Read = 0, 133 | UnRead = 1, 134 | } 135 | -------------------------------------------------------------------------------- /src/types/eventData.ts: -------------------------------------------------------------------------------- 1 | import { CbEvents } from '..'; 2 | import { 3 | BlackUserItem, 4 | ConversationInputStatus, 5 | ConversationItem, 6 | FriendApplicationItem, 7 | FriendUserItem, 8 | GroupApplicationItem, 9 | GroupItem, 10 | GroupMemberItem, 11 | GroupMessageReceiptInfo, 12 | MessageItem, 13 | ReceiptInfo, 14 | RevokedInfo, 15 | SelfUserInfo, 16 | UserOnlineState, 17 | } from './entity'; 18 | 19 | export type EventDataMap = { 20 | [CbEvents.OnProgress]: { progress: number; clientMsgID: string }; 21 | [CbEvents.OnBlackAdded]: BlackUserItem; 22 | [CbEvents.OnBlackDeleted]: BlackUserItem; 23 | [CbEvents.OnConversationChanged]: ConversationItem[]; 24 | [CbEvents.OnFriendAdded]: FriendUserItem; 25 | [CbEvents.OnFriendApplicationAdded]: FriendApplicationItem; 26 | [CbEvents.OnFriendApplicationDeleted]: FriendApplicationItem; 27 | [CbEvents.OnFriendApplicationRejected]: FriendApplicationItem; 28 | [CbEvents.OnFriendDeleted]: FriendUserItem; 29 | [CbEvents.OnFriendInfoChanged]: FriendUserItem; 30 | [CbEvents.OnGroupApplicationAdded]: GroupApplicationItem; 31 | [CbEvents.OnGroupApplicationDeleted]: GroupApplicationItem; 32 | [CbEvents.OnGroupApplicationRejected]: GroupApplicationItem; 33 | [CbEvents.OnGroupApplicationAccepted]: GroupApplicationItem; 34 | [CbEvents.OnGroupDismissed]: GroupItem; 35 | [CbEvents.OnGroupMemberDeleted]: GroupMemberItem; 36 | [CbEvents.OnGroupMemberInfoChanged]: GroupMemberItem; 37 | [CbEvents.OnJoinedGroupAdded]: GroupItem; 38 | [CbEvents.OnJoinedGroupDeleted]: GroupItem; 39 | [CbEvents.OnNewConversation]: ConversationItem[]; 40 | [CbEvents.OnConversationUserInputStatusChanged]: ConversationInputStatus; 41 | [CbEvents.OnNewRecvMessageRevoked]: RevokedInfo; 42 | [CbEvents.OnRecvC2CReadReceipt]: ReceiptInfo[]; 43 | [CbEvents.OnRecvGroupReadReceipt]: GroupMessageReceiptInfo; 44 | [CbEvents.OnRecvNewMessage]: MessageItem; 45 | [CbEvents.OnRecvNewMessages]: MessageItem[]; 46 | [CbEvents.OnRecvOfflineNewMessage]: MessageItem; 47 | [CbEvents.OnRecvOnlineOnlyMessage]: MessageItem; 48 | [CbEvents.OnRecvOfflineNewMessages]: MessageItem[]; 49 | [CbEvents.OnRecvOnlineOnlyMessages]: MessageItem[]; 50 | [CbEvents.OnSelfInfoUpdated]: SelfUserInfo; 51 | [CbEvents.OnSyncServerFailed]: void; 52 | [CbEvents.OnSyncServerStart]: boolean; 53 | [CbEvents.OnSyncServerProgress]: number; 54 | [CbEvents.OnSyncServerFinish]: void; 55 | [CbEvents.OnTotalUnreadMessageCountChanged]: number; 56 | [CbEvents.OnUserStatusChanged]: UserOnlineState; 57 | [CbEvents.OnConnectFailed]: void; 58 | [CbEvents.OnConnectSuccess]: void; 59 | [CbEvents.OnConnecting]: void; 60 | [CbEvents.OnKickedOffline]: void; 61 | [CbEvents.OnUserTokenExpired]: void; 62 | [CbEvents.OnUserTokenInvalid]: void; 63 | }; 64 | 65 | export type DataOfEvent = E extends keyof EventDataMap 66 | ? EventDataMap[E] 67 | : never; 68 | -------------------------------------------------------------------------------- /src/types/params.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MessageEntity, 3 | OfflinePush, 4 | PicBaseInfo, 5 | AtUsersInfoItem, 6 | MessageItem, 7 | SelfUserInfo, 8 | RtcInvite, 9 | GroupItem, 10 | } from './entity'; 11 | import { 12 | AllowType, 13 | GroupJoinSource, 14 | GroupVerificationType, 15 | MessageType, 16 | MessageReceiveOptType, 17 | GroupMemberRole, 18 | GroupMemberFilter, 19 | LogLevel, 20 | GroupMessageReaderFilter, 21 | GroupAtType, 22 | } from './enum'; 23 | 24 | export type WasmPathConfig = { 25 | coreWasmPath?: string; 26 | sqlWasmPath?: string; 27 | debug?: boolean; 28 | }; 29 | 30 | export type InitAndLoginConfig = { 31 | userID: string; 32 | token: string; 33 | platformID: number; 34 | apiAddr: string; 35 | wsAddr: string; 36 | logLevel?: LogLevel; 37 | isLogStandardOutput?: boolean; 38 | isExternalExtensions?: boolean; 39 | tryParse?: boolean; 40 | }; 41 | 42 | export type GetOneConversationParams = { 43 | sourceID: string; 44 | sessionType: number; 45 | }; 46 | export type GetAdvancedHistoryMsgParams = { 47 | userID?: string; 48 | groupID?: string; 49 | lastMinSeq: number; 50 | count: number; 51 | startClientMsgID: string; 52 | conversationID: string; 53 | }; 54 | export type GetHistoryMsgParams = { 55 | userID: string; 56 | groupID: string; 57 | count: number; 58 | startClientMsgID: string; 59 | conversationID?: string; 60 | }; 61 | export type SendGroupReadReceiptParams = { 62 | conversationID: string; 63 | clientMsgIDList: string[]; 64 | }; 65 | export type GetGroupMessageReaderParams = { 66 | conversationID: string; 67 | clientMsgID: string; 68 | filter: GroupMessageReaderFilter; 69 | offset: number; 70 | count: number; 71 | }; 72 | export type GetGroupMemberParams = { 73 | groupID: string; 74 | filter: GroupMemberFilter; 75 | offset: number; 76 | count: number; 77 | }; 78 | export type SendMsgParams = { 79 | recvID: string; 80 | groupID: string; 81 | offlinePushInfo?: OfflinePush; 82 | message: MessageItem; 83 | isOnlineOnly?: boolean; 84 | }; 85 | export type SetMessageLocalExParams = { 86 | conversationID: string; 87 | clientMsgID: string; 88 | localEx: string; 89 | }; 90 | export type ImageMsgParamsByURL = { 91 | sourcePicture: PicBaseInfo; 92 | bigPicture: PicBaseInfo; 93 | snapshotPicture: PicBaseInfo; 94 | sourcePath: string; 95 | }; 96 | export type VideoMsgParamsByURL = { 97 | videoPath: string; 98 | duration: number; 99 | videoType: string; 100 | snapshotPath: string; 101 | videoUUID: string; 102 | videoUrl: string; 103 | videoSize: number; 104 | snapshotUUID: string; 105 | snapshotSize: number; 106 | snapshotUrl: string; 107 | snapshotWidth: number; 108 | snapshotHeight: number; 109 | snapShotType?: string; 110 | }; 111 | export type VideoMsgParamsByFullPath = { 112 | videoFullPath: string; 113 | videoType: string; 114 | duration: number; 115 | snapshotFullPath: string; 116 | }; 117 | export type CustomMsgParams = { 118 | data: string; 119 | extension: string; 120 | description: string; 121 | }; 122 | export type QuoteMsgParams = { 123 | text: string; 124 | message: string; 125 | }; 126 | export type AdvancedQuoteMsgParams = { 127 | text: string; 128 | message: MessageItem; 129 | messageEntityList?: MessageEntity[]; 130 | }; 131 | export type AdvancedMsgParams = { 132 | text: string; 133 | messageEntityList?: MessageEntity[]; 134 | }; 135 | export type SetConversationParams = { 136 | conversationID: string; 137 | recvMsgOpt?: MessageReceiveOptType; 138 | groupAtType?: GroupAtType; 139 | burnDuration?: number; 140 | msgDestructTime?: number; 141 | isPinned?: boolean; 142 | isPrivateChat?: boolean; 143 | isMsgDestruct?: boolean; 144 | ex?: string; 145 | }; 146 | export type SetConversationPrivateStateParams = { 147 | conversationID: string; 148 | isPrivate: boolean; 149 | }; 150 | export type SplitConversationParams = { 151 | offset: number; 152 | count: number; 153 | }; 154 | export type SetConversationDraftParams = { 155 | conversationID: string; 156 | draftText: string; 157 | }; 158 | export type SetConversationPinParams = { 159 | conversationID: string; 160 | isPinned: boolean; 161 | }; 162 | export type JoinGroupParams = { 163 | groupID: string; 164 | reqMsg: string; 165 | joinSource: GroupJoinSource; 166 | ex?: string; 167 | }; 168 | export type SearchGroupParams = { 169 | keywordList: string[]; 170 | isSearchGroupID: boolean; 171 | isSearchGroupName: boolean; 172 | }; 173 | export type ChangeGroupMuteParams = { 174 | groupID: string; 175 | isMute: boolean; 176 | }; 177 | export type ChangeGroupMemberMuteParams = { 178 | groupID: string; 179 | userID: string; 180 | mutedSeconds: number; 181 | }; 182 | export type TransferGroupParams = { 183 | groupID: string; 184 | newOwnerUserID: string; 185 | }; 186 | export type AccessGroupApplicationParams = { 187 | groupID: string; 188 | fromUserID: string; 189 | handleMsg: string; 190 | }; 191 | export type SetGroupRoleParams = { 192 | groupID: string; 193 | userID: string; 194 | roleLevel: GroupMemberRole; 195 | }; 196 | export type SetGroupVerificationParams = { 197 | verification: GroupVerificationType; 198 | groupID: string; 199 | }; 200 | export type SetBurnDurationParams = { 201 | conversationID: string; 202 | burnDuration: number; 203 | }; 204 | export type AtMsgParams = { 205 | text: string; 206 | atUserIDList: string[]; 207 | atUsersInfo?: AtUsersInfoItem[]; 208 | message?: MessageItem; 209 | }; 210 | export type SoundMsgParamsByURL = { 211 | uuid: string; 212 | soundPath: string; 213 | sourceUrl: string; 214 | dataSize: number; 215 | duration: number; 216 | soundType?: string; 217 | }; 218 | export type FileMsgParamsByURL = { 219 | filePath: string; 220 | fileName: string; 221 | uuid: string; 222 | sourceUrl: string; 223 | fileSize: number; 224 | fileType?: string; 225 | }; 226 | export type FileMsgParamsByFullPath = { 227 | fileFullPath: string; 228 | fileName: string; 229 | }; 230 | export type SoundMsgParamsByFullPath = { 231 | soundPath: string; 232 | duration: number; 233 | }; 234 | export type MergerMsgParams = { 235 | messageList: MessageItem[]; 236 | title: string; 237 | summaryList: string[]; 238 | }; 239 | export type FaceMessageParams = { 240 | index: number; 241 | data: string; 242 | }; 243 | export type LocationMsgParams = { 244 | description: string; 245 | longitude: number; 246 | latitude: number; 247 | }; 248 | export type InsertSingleMsgParams = { 249 | message: MessageItem; 250 | recvID: string; 251 | sendID: string; 252 | }; 253 | export type InsertGroupMsgParams = { 254 | message: MessageItem; 255 | groupID: string; 256 | sendID: string; 257 | }; 258 | export type AccessMessageParams = { 259 | conversationID: string; 260 | clientMsgID: string; 261 | }; 262 | export type TypingUpdateParams = { 263 | recvID: string; 264 | msgTip: string; 265 | }; 266 | export type ChangeInputStatesParams = { 267 | conversationID: string; 268 | focus: boolean; 269 | }; 270 | export type GetInputstatesParams = { 271 | conversationID: string; 272 | userID: string; 273 | }; 274 | export type SetConversationExParams = { 275 | conversationID: string; 276 | ex: string; 277 | }; 278 | export type SetConversationRecvOptParams = { 279 | conversationID: string; 280 | opt: MessageReceiveOptType; 281 | }; 282 | export type SearchLocalParams = { 283 | conversationID: string; 284 | keywordList: string[]; 285 | keywordListMatchType?: number; 286 | senderUserIDList?: string[]; 287 | messageTypeList?: MessageType[]; 288 | searchTimePosition?: number; 289 | searchTimePeriod?: number; 290 | pageIndex?: number; 291 | count?: number; 292 | }; 293 | export type AddFriendParams = { 294 | toUserID: string; 295 | reqMsg: string; 296 | }; 297 | export type SearchFriendParams = { 298 | keywordList: string[]; 299 | isSearchUserID: boolean; 300 | isSearchNickname: boolean; 301 | isSearchRemark: boolean; 302 | }; 303 | export type GetSpecifiedFriendsParams = { 304 | friendUserIDList: string[]; 305 | filterBlack?: boolean; 306 | }; 307 | export type UpdateFriendsParams = { 308 | friendUserIDs: string[]; 309 | isPinned?: boolean; 310 | remark?: boolean; 311 | ex?: boolean; 312 | }; 313 | export type RemarkFriendParams = { 314 | toUserID: string; 315 | remark: string; 316 | }; 317 | export type PinFriendParams = { 318 | toUserIDs: string[]; 319 | isPinned: boolean; 320 | }; 321 | export type SetFriendExParams = { 322 | toUserIDs: string[]; 323 | ex: string; 324 | }; 325 | export type AccessFriendApplicationParams = { 326 | toUserID: string; 327 | handleMsg: string; 328 | }; 329 | export type AddBlackParams = { 330 | toUserID: string; 331 | ex?: string; 332 | }; 333 | export type AccessToGroupParams = { 334 | groupID: string; 335 | reason: string; 336 | userIDList: string[]; 337 | }; 338 | export type GetGroupMemberByTimeParams = { 339 | groupID: string; 340 | filterUserIDList: string[]; 341 | offset: number; 342 | count: number; 343 | joinTimeBegin: number; 344 | joinTimeEnd: number; 345 | }; 346 | export type SearchGroupMemberParams = { 347 | groupID: string; 348 | keywordList: string[]; 349 | isSearchUserID: boolean; 350 | isSearchMemberNickname: boolean; 351 | offset: number; 352 | count: number; 353 | }; 354 | export type SetMemberPermissionParams = { 355 | rule: AllowType; 356 | groupID: string; 357 | }; 358 | export type OffsetParams = { 359 | offset: number; 360 | count: number; 361 | }; 362 | export type CreateGroupParams = { 363 | memberUserIDs: string[]; 364 | groupInfo: Partial; 365 | adminUserIDs?: string[]; 366 | ownerUserID?: string; 367 | }; 368 | export type SetGroupMemberNickParams = { 369 | groupID: string; 370 | userID: string; 371 | groupMemberNickname: string; 372 | }; 373 | export type UpdateMemberInfoParams = { 374 | groupID: string; 375 | userID: string; 376 | nickname?: string; 377 | faceURL?: string; 378 | roleLevel?: GroupMemberRole; 379 | ex?: string; 380 | }; 381 | export type FindMessageParams = { 382 | conversationID: string; 383 | clientMsgIDList: string[]; 384 | }; 385 | export type UploadFileParams = { 386 | name: string; 387 | contentType: string; 388 | uuid: string; 389 | file?: File; 390 | filepath?: string; 391 | cause?: string; 392 | }; 393 | export type PartialUserItem = Partial; 394 | 395 | export type SignalingInviteParams = { 396 | invitation: RtcInvite; 397 | offlinePushInfo?: OfflinePush; 398 | }; 399 | export type RtcActionParams = { 400 | opUserID: string; 401 | invitation: RtcInvite; 402 | }; 403 | export type CustomSignalParams = { 404 | roomID: string; 405 | customInfo: string; 406 | }; 407 | 408 | export type SetConversationMsgDestructParams = { 409 | conversationID: string; 410 | isMsgDestruct: boolean; 411 | }; 412 | 413 | export type SetConversationMsgDestructTimeParams = { 414 | conversationID: string; 415 | msgDestructTime: number; 416 | }; 417 | -------------------------------------------------------------------------------- /src/utils/emitter.ts: -------------------------------------------------------------------------------- 1 | import { WSEvent } from '@/types/entity'; 2 | import { CbEvents } from '../constant'; 3 | import { DataOfEvent } from '../types/eventData'; 4 | 5 | interface Events { 6 | [key: string]: Cbfn[]; 7 | } 8 | 9 | type Cbfn = (data: WSEvent>) => void; 10 | 11 | class Emitter { 12 | private events: Events; 13 | 14 | constructor() { 15 | this.events = {}; 16 | } 17 | 18 | emit(event: E, data: WSEvent>) { 19 | if (this.events[event]) { 20 | this.events[event].forEach(fn => { 21 | return fn(data); 22 | }); 23 | } 24 | 25 | return this; 26 | } 27 | 28 | on(event: E, fn: Cbfn) { 29 | if (this.events[event]) { 30 | this.events[event].push(fn); 31 | } else { 32 | this.events[event] = [fn]; 33 | } 34 | 35 | return this; 36 | } 37 | 38 | off(event: E, fn: Cbfn) { 39 | if (event && typeof fn === 'function' && this.events[event]) { 40 | const listeners = this.events[event]; 41 | if (!listeners || listeners.length === 0) { 42 | return; 43 | } 44 | const index = listeners.findIndex(_fn => { 45 | return _fn === fn; 46 | }); 47 | if (index !== -1) { 48 | listeners.splice(index, 1); 49 | } 50 | } 51 | 52 | return this; 53 | } 54 | } 55 | 56 | export default Emitter; 57 | -------------------------------------------------------------------------------- /src/utils/escape.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-control-regex 2 | const CHARS_GLOBAL_BACKSLASH_SUPPORTED_RX = /[\0\b\t\n\r\x1a"'\\]/g; 3 | const CHARS_ESCAPE_BACKSLASH_SUPPORTED_MAP: Record = { 4 | '\0': '\\0', 5 | '\b': '\\b', 6 | '\t': '\\t', 7 | '\n': '\\n', 8 | '\r': '\\r', 9 | '\x1a': '\\Z', 10 | '"': '\\"', 11 | "'": "\\'", 12 | '\\': '\\\\', 13 | }; 14 | 15 | /** 16 | * Escapes the given string to protect against SQL injection attacks. 17 | * 18 | * By default it assumes that backslashes are not supported as they are not part of the standard SQL spec. 19 | * Quoting from the [SQLlite web site](https://sqlite.org/lang_expr.html): 20 | * 21 | * > C-style escapes using the backslash character are not supported because they are not standard SQL. 22 | * 23 | * This means three things: 24 | * 25 | * - backslashes and double quotes `"` are not escaped by default 26 | * - single quotes are escaped via `''` instead of `\'` 27 | * - your sql engine should throw an error when encountering a backslash escape 28 | * as part of a string, unless it is a literal backslash, i.e. `'backslash: \\'`. 29 | * 30 | * It is recommended to set the `backslashSupported` option `true` if your SQL 31 | * engine supports it. In that case backslash sequences are escaped and single 32 | * and double quotes are escaped via a backslash, i.e. `'\''`. 33 | * 34 | */ 35 | export function escapeString( 36 | val: string, 37 | opts: { backslashSupported: boolean } = { backslashSupported: false } 38 | ) { 39 | if (val == null) { 40 | throw new Error('Need to pass a valid string'); 41 | } 42 | opts = opts || {}; 43 | const backslashSupported = !!opts.backslashSupported; 44 | 45 | if (!backslashSupported) return "'" + val.replace(/'/g, "''") + "'"; 46 | 47 | const charsRx = CHARS_GLOBAL_BACKSLASH_SUPPORTED_RX; 48 | const charsEscapeMap = CHARS_ESCAPE_BACKSLASH_SUPPORTED_MAP; 49 | let chunkIndex = (charsRx.lastIndex = 0); 50 | let escapedVal = ''; 51 | let match; 52 | 53 | while ((match = charsRx.exec(val))) { 54 | escapedVal += val.slice(chunkIndex, match.index) + charsEscapeMap[match[0]]; 55 | chunkIndex = charsRx.lastIndex; 56 | } 57 | 58 | // Nothing was escaped 59 | if (chunkIndex === 0) return "'" + val + "'"; 60 | 61 | if (chunkIndex < val.length) 62 | return "'" + escapedVal + val.slice(chunkIndex) + "'"; 63 | return "'" + escapedVal + "'"; 64 | } 65 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './response'; 2 | export * from './timer'; 3 | export * from './key'; 4 | export * from './value'; 5 | export * from './is'; 6 | export * from './escape'; 7 | export * from './logFormat'; 8 | -------------------------------------------------------------------------------- /src/utils/is.ts: -------------------------------------------------------------------------------- 1 | export function isString(value: unknown) { 2 | return typeof value === 'string'; 3 | } 4 | 5 | export function isNumber(value: unknown) { 6 | return typeof value === 'number'; 7 | } 8 | 9 | export function isBoolean(value: unknown) { 10 | return typeof value === 'boolean'; 11 | } 12 | 13 | export function isUndefined(value: unknown) { 14 | return typeof value === 'undefined'; 15 | } 16 | 17 | export function isObject(value: unknown) { 18 | return typeof value === 'object' && value !== null; 19 | } 20 | 21 | export function isFunction(value: unknown) { 22 | return typeof value === 'function'; 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/key.ts: -------------------------------------------------------------------------------- 1 | export type KeyType = 'CamelCase' | 'SnakeCase'; 2 | 3 | const InternalConstraint = [ 4 | ['user_id', 'userID'], 5 | ['group_id', 'groupID'], 6 | ['client_msg_id', 'clientMsgID'], 7 | ['server_msg_id', 'serverMsgID'], 8 | ['send_id', 'sendID'], 9 | ['recv_id', 'recvID'], 10 | ['sender_platform_id', 'senderPlatformID'], 11 | ['sender_nick_name', 'senderNickname'], 12 | ['sender_face_url', 'senderFaceURL'], 13 | ['session_type', 'sessionType'], 14 | ['msg_from', 'msgFrom'], 15 | ['content_type', 'contentType'], 16 | ['content', 'content'], 17 | ['is_read', 'isRead'], 18 | ['is_react', 'isReact'], 19 | ['is_external_extensions', 'isExternalExtensions'], 20 | ['msg_first_modify_time', 'msgFirstModifyTime'], 21 | ['status', 'status'], 22 | ['seq', 'seq'], 23 | ['send_time', 'sendTime'], 24 | ['create_time', 'createTime'], 25 | ['attached_info', 'attachedInfo'], 26 | ['ex', 'ex'], 27 | ['face_url', 'faceURL'], 28 | ['creator_user_id', 'creatorUserID'], 29 | ['conversation_id', 'conversationID'], 30 | ['owner_user_id', 'ownerUserID'], 31 | ['notification_user_id', 'notificationUserID'], 32 | ['operator_user_id', 'operatorUserID'], 33 | ['from_face_url', 'fromFaceURL'], 34 | ['from_user_id', 'fromUserID'], 35 | ['from_gender', 'fromGender'], 36 | ['from_nickname', 'fromNickname'], 37 | ['to_user_id', 'toUserID'], 38 | ['to_nickname', 'toNickname'], 39 | ['to_face_url', 'toFaceURL'], 40 | ['to_gender', 'toGender'], 41 | ['req_msg', 'reqMsg'], 42 | ['handle_msg', 'handleMsg'], 43 | ['handle_time', 'handleTime'], 44 | ['handle_result', 'handleResult'], 45 | ['handler_user_id', 'handlerUserID'], 46 | ['handle_user_id', 'handleUserID'], 47 | ['inviter_user_id', 'inviterUserID'], 48 | ['mute_end_time', 'muteEndTime'], 49 | ['role_level', 'roleLevel'], 50 | ['join_time', 'joinTime'], 51 | ['join_source', 'joinSource'], 52 | ['friend_user_id', 'friendUserID'], 53 | ['recv_msg_opt', 'recvMsgOpt'], 54 | ['group_at_type', 'groupAtType'], 55 | ['latest_msg_send_time', 'latestMsgSendTime'], 56 | ['draft_text_time', 'draftTextTime'], 57 | ['is_private_chat', 'isPrivateChat'], 58 | ['is_not_in_group', 'isNotInGroup'], 59 | ['update_unread_count_time', 'updateUnreadCountTime'], 60 | ['is_msg_destruct', 'isMsgDestruct'], 61 | ['msg_destruct_time', 'msgDestructTime'], 62 | ['part_hash', 'partHash'], 63 | ['upload_id', 'uploadID'], 64 | ['upload_info', 'uploadInfo'], 65 | ['expire_time', 'expireTime'], 66 | ['entity_id', 'entityID'], 67 | ['version_id', 'versionID'], 68 | ['display_is_read', 'displayIsRead'], 69 | ]; 70 | 71 | function _getInternalCamelCaseBySnakeCase(key: string) { 72 | const pair = InternalConstraint.find(p => { 73 | return p[0] === key; 74 | }); 75 | 76 | if (pair) { 77 | return pair[1]; 78 | } 79 | } 80 | 81 | function _getInternalSnakeCaseByCamelCase(key: string) { 82 | const pair = InternalConstraint.find(p => { 83 | return p[1] === key; 84 | }); 85 | 86 | if (pair) { 87 | return pair[0]; 88 | } 89 | } 90 | 91 | export function convertSnakeCaseToCamelCase(key: string) { 92 | const internalKey = _getInternalCamelCaseBySnakeCase(key); 93 | if (internalKey) { 94 | return internalKey; 95 | } 96 | 97 | const cArr = []; 98 | let lastSign = -2; 99 | for (let i = 0; i < key.length; i++) { 100 | const c = key[i]; 101 | 102 | if (c === '_' && i < key.length - 1) { 103 | lastSign = i; 104 | continue; 105 | } 106 | 107 | if (i - 1 === lastSign) { 108 | cArr.push(c.toUpperCase()); 109 | } else { 110 | cArr.push(c); 111 | } 112 | } 113 | 114 | return cArr.join(''); 115 | } 116 | 117 | export function convertCamelCaseToSnakeCase(key: string) { 118 | const internalKey = _getInternalSnakeCaseByCamelCase(key); 119 | if (internalKey) { 120 | return internalKey; 121 | } 122 | 123 | const cArr = []; 124 | for (let i = 0; i < key.length; i++) { 125 | const c = key[i]; 126 | 127 | if (c.toLowerCase() !== c) { 128 | cArr.push('_'); 129 | } 130 | 131 | cArr.push(c.toLowerCase()); 132 | } 133 | 134 | return cArr.join(''); 135 | } 136 | -------------------------------------------------------------------------------- /src/utils/logFormat.ts: -------------------------------------------------------------------------------- 1 | export function logBoxStyleValue(backgroundColor?: string, color?: string) { 2 | return `font-size:14px; background:${backgroundColor ?? '#ffffff'}; color:${ 3 | color ?? '#000000' 4 | }; border-radius:4px; padding-inline:4px;`; 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/response.ts: -------------------------------------------------------------------------------- 1 | export function formatResponse( 2 | data: unknown, 3 | errCode?: number, 4 | errMsg?: string 5 | ): any { 6 | let serializedData = data; 7 | if (typeof data === 'object') { 8 | serializedData = JSON.stringify(data); 9 | } 10 | 11 | return { 12 | data: data !== undefined ? serializedData : '{}', 13 | errCode: errCode || 0, 14 | errMsg: errMsg || '', 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/timer.ts: -------------------------------------------------------------------------------- 1 | export async function wait(duration: number) { 2 | return new Promise(resolve => { 3 | const timer = setTimeout(() => { 4 | clearTimeout(timer); 5 | resolve(null); 6 | }, duration); 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/value.ts: -------------------------------------------------------------------------------- 1 | import { QueryExecResult } from '@jlongster/sql.js'; 2 | import { escapeString } from './escape'; 3 | import { isString } from './is'; 4 | import { 5 | KeyType, 6 | convertSnakeCaseToCamelCase, 7 | convertCamelCaseToSnakeCase, 8 | } from './key'; 9 | 10 | export function converSqlExecResult( 11 | record: QueryExecResult, 12 | keyType: KeyType = 'CamelCase', 13 | booleanKeys: string[] = [], 14 | convertMap: Record = {} 15 | ) { 16 | const { columns = [], values = [] } = record || {}; 17 | const result: Record[] = []; 18 | 19 | values.forEach(v => { 20 | const converted: Record = {}; 21 | columns.forEach((k, i) => { 22 | let ck = k; 23 | let cv: unknown = v[i]; 24 | 25 | if (keyType === 'CamelCase') { 26 | ck = convertSnakeCaseToCamelCase(k); 27 | } 28 | if (keyType === 'SnakeCase') { 29 | ck = convertCamelCaseToSnakeCase(k); 30 | } 31 | if (booleanKeys.find(bk => bk === ck)) { 32 | cv = !!cv; 33 | } 34 | 35 | ck = convertMap[k] || ck; 36 | 37 | converted[ck] = cv; 38 | }); 39 | result.push(converted); 40 | }); 41 | 42 | return result; 43 | } 44 | 45 | export function convertToCamelCaseObject(obj: Record) { 46 | const retObj: Record = {}; 47 | 48 | Object.keys(obj).forEach(k => { 49 | retObj[convertSnakeCaseToCamelCase(k)] = obj[k]; 50 | }); 51 | 52 | return retObj; 53 | } 54 | 55 | export function convertToSnakeCaseObject( 56 | obj: Record, 57 | escape = true 58 | ) { 59 | const retObj: Record = {}; 60 | 61 | Object.keys(obj).forEach(k => { 62 | let value = obj[k]; 63 | if (escape && isString(value)) { 64 | value = escapeString(value as string).slice(1, -1); 65 | } 66 | retObj[convertCamelCaseToSnakeCase(k)] = value; 67 | }); 68 | 69 | return retObj; 70 | } 71 | 72 | export function convertObjectField( 73 | obj: Record, 74 | convertMap: Record = {} 75 | ) { 76 | const ret: Record = {}; 77 | 78 | Object.keys(obj).forEach(k => { 79 | const nk = convertMap[k] || k; 80 | 81 | ret[nk] = obj[k]; 82 | }); 83 | 84 | return ret; 85 | } 86 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": false 5 | }, 6 | "exclude": ["test/**/*.spec.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "esnext", 5 | "declaration": true, 6 | "outDir": "./lib/", 7 | "strict": true, 8 | "moduleResolution": "node", 9 | "types": ["./src/types"], 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "baseUrl": "./", 14 | "paths": { 15 | "@/*": ["src/*"] 16 | } 17 | }, 18 | "include": ["src/**/*.ts"] 19 | } 20 | --------------------------------------------------------------------------------