├── .editorconfig ├── .eslintrc.cjs ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── documentation.md │ └── feature.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── csharp.yml │ ├── go.yml │ ├── java.yml │ ├── node.yml │ ├── php.yml │ └── python.yml ├── .gitignore ├── .pylintrc ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── RESOURCES.md ├── commonjs ├── README.md ├── code-root.cjs ├── code-root.cjs.d.ts ├── code-root.spec.ts ├── generated │ ├── google.cjs │ ├── google.cjs.d.ts │ ├── google.spec.ts │ ├── puppet.cjs │ ├── puppet.cjs.d.ts │ └── puppet.spec.ts └── package.json ├── docs ├── Makefile ├── README.md └── images │ └── wechaty-grpc-logo.svg ├── examples ├── auth │ ├── .gitignore │ ├── README.md │ ├── client.ts │ ├── generate.sh │ ├── raw-https │ │ ├── README.md │ │ ├── client.ts │ │ ├── generate.sh │ │ └── server.ts │ └── server.ts ├── client.py ├── client.ts ├── promisify.ts └── server.ts ├── openapi ├── .gitignore ├── Makefile ├── README.md ├── generate.sh ├── go.mod └── install.sh ├── package.json ├── polyglot ├── csharp │ └── Wechaty.Grpc │ │ ├── .gitignore │ │ ├── README.md │ │ ├── Wechaty.Grpc.csproj │ │ ├── Wechaty.Grpc.sln │ │ ├── buildTool │ │ ├── common.ps1 │ │ └── copy.ps1 │ │ ├── common.props │ │ ├── proto │ │ └── google │ │ │ └── api │ │ │ ├── annotations.proto │ │ │ └── openapiv2.proto │ │ └── wechaty.png ├── go │ ├── .gitignore │ ├── Makefile │ ├── build_test.sh │ ├── generate.sh │ ├── grpc_test.go │ └── publish.sh ├── java │ ├── generate.sh │ └── pom.xml ├── php │ ├── .gitignore │ ├── Makefile │ ├── generate.sh │ ├── grpc_test.php │ └── publish.sh └── python │ ├── .gitignore │ ├── Makefile │ ├── generate.sh │ ├── requirements-dev.txt │ ├── requirements.txt │ ├── setup.py │ └── tests │ └── integration_test.py ├── proto └── wechaty │ ├── deprecated │ └── file-box.proto │ ├── puppet.proto │ └── puppet │ ├── base.proto │ ├── contact.proto │ ├── conversation.proto │ ├── download-upload.proto │ ├── event.proto │ ├── friendship.proto │ ├── image.proto │ ├── location.proto │ ├── message.proto │ ├── mini-program.proto │ ├── referrer.proto │ ├── room-invitation.proto │ ├── room-member.proto │ ├── room.proto │ ├── tag.proto │ └── url-link.proto ├── scripts ├── generate-package-json.sh ├── generate-stub.sh ├── install-protoc.sh ├── npm-pack-testing.sh └── package-publish-config-tag.sh ├── src ├── chunk-transformer.spec.ts ├── chunk-transformer.ts ├── cjs.spec.ts ├── cjs.ts ├── config.ts ├── google.spec.ts ├── google.ts ├── mod.spec.ts ├── mod.ts ├── openapi.spec.ts ├── openapi.ts ├── package-json.spec.ts ├── package-json.ts ├── proto.spec.ts └── proto.ts ├── tests ├── fixtures │ └── smoke-testing.ts ├── health-check.spec.ts ├── integration.spec.ts ├── nullable.spec.ts ├── puppet-server-impl.ts └── streaming.spec.ts ├── tsconfig.cjs.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | indent_style = space 15 | trim_trailing_whitespace = false 16 | 17 | # 4 tab indentation 18 | [Makefile] 19 | indent_style = tab 20 | indent_size = 4 21 | 22 | [*.py] 23 | indent_size = 4 24 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | 2 | const rules = { 3 | } 4 | 5 | module.exports = { 6 | extends: '@chatie', 7 | rules, 8 | } 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/about-codeowners/ 3 | # 4 | /openapi/ @huan 5 | /polyglot/csharp/ @Jesn 6 | /polyglot/go/ @dingdayu @dchaofei 7 | /polyglot/java/ @diaozxin007 8 | /polyglot/php/ @zhangchunsheng 9 | /polyglot/python/ @wj-Mcat 10 | /proto/ @wechaty/grpc 11 | /src/ @huan 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Submit a bug report to help us improve 4 | --- 5 | 6 | ## 🐛 Bug Report 7 | 8 | (A clear and concise description of what the bug is.) 9 | 10 | ## To Reproduce 11 | 12 | (Write your steps here:) 13 | 14 | 1. Step 1... 15 | 1. Step 2... 16 | 1. Step 3... 17 | 18 | ## Expected behavior 19 | 20 | (Write what you thought would happen.) 21 | 22 | ## Actual Behavior 23 | 24 | (Write what happened. Add screenshots, if applicable.) 25 | 26 | ## Your Environment 27 | 28 | (Environment name, version and operating system.) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 📚 Documentation 3 | about: Report an issue related to documentation 4 | --- 5 | 6 | ## 📚 Documentation 7 | 8 | (A clear and concise description of what the issue is.) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature 3 | about: Submit a proposal/request for a new feature 4 | --- 5 | 6 | ## 🚀 Feature 7 | 8 | (A clear and concise description of what the feature is.) -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | #### Checklist 13 | 14 | - [ ] Commit Messages follow the [Conventional Commits](https://conventionalcommits.org/) pattern 15 | - A feature commit message is prefixed "feat:" 16 | - A bugfix commit message is prefixed "fix:" 17 | - [ ] Tests for the changes have been added 18 | - [ ] CI has been passed. (GitHub actions all turns green) 19 | - [ ] CLA has been signed 20 | 21 | #### References to other Issues or PRs 22 | 23 | 28 | 29 | #### Have you read the [Contributing Guidelines](https://wechaty.js.org/docs/contributing/)? 30 | 31 | #### Brief description of what is fixed or changed 32 | 33 | #### Other comments 34 | 35 | 45 | -------------------------------------------------------------------------------- /.github/workflows/csharp.yml: -------------------------------------------------------------------------------- 1 | name: C# Nuget 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | paths: 9 | - 'polyglot/csharp/**' 10 | - 'proto/**' 11 | 12 | jobs: 13 | build-push: 14 | runs-on: windows-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions/setup-dotnet@master 18 | with: 19 | dotnet-version: 3.1.100 20 | 21 | - name: copy wechaty proto 22 | run: .\copy.ps1 23 | working-directory: .\polyglot\csharp\Wechaty.Grpc\buildTool 24 | shell: powershell 25 | 26 | - name: publish on version change 27 | uses: rohith/publish-nuget@v2 28 | with: 29 | PROJECT_FILE_PATH: .\polyglot\csharp\Wechaty.Grpc\Wechaty.Grpc.csproj 30 | VERSION_FILE_PATH: .\polyglot\csharp\Wechaty.Grpc\common.props 31 | NUGET_KEY: ${{ secrets.NUGET_APIKEY }} 32 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | paths: 9 | - 'polyglot/go/**' 10 | - 'proto/**' 11 | 12 | jobs: 13 | 14 | build: 15 | name: Build 16 | runs-on: ubuntu-latest 17 | steps: 18 | 19 | - name: Set up Go 1.17 20 | uses: actions/setup-go@v2 21 | with: 22 | go-version: 1.18 23 | id: go 24 | - name: Install Protoc 25 | uses: arduino/setup-protoc@v1 26 | 27 | - name: Check out code into the Go module directory 28 | uses: actions/checkout@v2 29 | 30 | - name: Get dependencies 31 | run: | 32 | cd polyglot/go 33 | make install 34 | 35 | - name: Build 36 | run: | 37 | cd polyglot/go 38 | make generate 39 | WECHATY_GRPC="$(go env GOPATH)/src/github.com/wechaty/go-grpc" 40 | rm -rf "$WECHATY_GRPC/wechaty" 41 | [ -e "$WECHATY_GRPC" ] || mkdir -p "$WECHATY_GRPC" 42 | ln -s "$(pwd)/out/wechaty/" "$WECHATY_GRPC/wechaty" 43 | 44 | make build_test 45 | 46 | publish: 47 | if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/v')) 48 | name: Publish 49 | needs: [build] 50 | runs-on: ubuntu-latest 51 | steps: 52 | - name: Set up Go 1.17 53 | uses: actions/setup-go@v2 54 | with: 55 | go-version: 1.18 56 | - name: Install Protoc 57 | uses: arduino/setup-protoc@v1 58 | - name: Check out code into the Go module directory 59 | uses: actions/checkout@v2 60 | - name: Deploy 61 | run: | 62 | cd polyglot/go 63 | make install 64 | # https://stackoverflow.com/a/4565746/1123955 65 | # https://stackoverflow.com/a/46253163/1123955 66 | ssh-agent bash -c "\ 67 | echo '${SSH_PRIVATE_KEY}' | ssh-add - ;\ 68 | make publish ;\ 69 | " 70 | env: 71 | SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} 72 | -------------------------------------------------------------------------------- /.github/workflows/java.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java Maven 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | paths: 12 | - 'polyglot/java/**' 13 | - 'proto/**' 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up JDK 1.8 23 | uses: actions/setup-java@v1 24 | with: 25 | java-version: 1.8 26 | - name: Build with Maven 27 | run: mvn -B package --file polyglot/java/pom.xml 28 | 29 | publish: 30 | if: github.event_name == 'release' && github.event.action == 'released' 31 | name: Publish 32 | needs: 33 | - build 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v2 37 | - name: Set up JDK 1.8 38 | uses: actions/setup-java@v1 39 | with: 40 | java-version: 1.8 41 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 42 | settings-path: ${{ github.workspace }} # location for the settings.xml file 43 | 44 | - name: Build with Maven 45 | run: mvn -B package --file polyglot/java/pom.xml 46 | 47 | - name: Publish to GitHub Packages Apache Maven 48 | run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml 49 | env: 50 | GITHUB_TOKEN: ${{ github.token }} 51 | -------------------------------------------------------------------------------- /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: Node.js NPM 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | paths: 9 | - 'openapi/**' 10 | - 'proto/**' 11 | - 'src/**' 12 | 13 | jobs: 14 | build: 15 | name: Build 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-node@v2 20 | with: 21 | node-version: 16 22 | cache: npm 23 | cache-dependency-path: package.json 24 | 25 | - name: Set up Go 26 | uses: actions/setup-go@v2 27 | with: 28 | go-version: 1.18 29 | - name: Install Protoc 30 | uses: arduino/setup-protoc@v1 31 | 32 | - name: Install Dependencies 33 | run: | 34 | npm install 35 | npm run install:protoc 36 | 37 | - name: Generate gRPC Stubs 38 | run: npm run generate 39 | 40 | - name: Test 41 | run: npm test 42 | 43 | pack: 44 | name: Pack 45 | needs: build 46 | runs-on: ubuntu-latest 47 | steps: 48 | - uses: actions/checkout@v2 49 | - uses: actions/setup-node@v2 50 | with: 51 | node-version: 16 52 | cache: npm 53 | cache-dependency-path: package.json 54 | - name: Set up Go 55 | uses: actions/setup-go@v2 56 | with: 57 | go-version: 1.18 58 | 59 | - name: Install Protoc 60 | uses: arduino/setup-protoc@v1 61 | - name: Install Protoc 3rd party protos 62 | run: npm run install:protoc 63 | 64 | - name: Install Dependencies 65 | run: npm install 66 | 67 | - name: Generate Package JSON 68 | run: ./scripts/generate-package-json.sh 69 | 70 | - name: Pack Testing 71 | run: ./scripts/npm-pack-testing.sh 72 | 73 | publish: 74 | if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/v')) 75 | name: Publish 76 | needs: [build, pack] 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v2 80 | - uses: actions/setup-node@v2 81 | with: 82 | node-version: 16 83 | registry-url: https://registry.npmjs.org/ 84 | cache: npm 85 | cache-dependency-path: package.json 86 | - name: Set up Go 87 | uses: actions/setup-go@v2 88 | with: 89 | go-version: 1.18 90 | - name: Install Protoc 91 | uses: arduino/setup-protoc@v1 92 | - name: Install Protoc 3rd party protos 93 | run: npm run install:protoc 94 | 95 | - name: Install Dependencies 96 | run: npm install 97 | - name: Generate Package JSON 98 | run: ./scripts/generate-package-json.sh 99 | - name: Set Publish Config 100 | run: ./scripts/package-publish-config-tag.sh 101 | - run: npm run dist 102 | 103 | - name: Check Branch 104 | id: check-branch 105 | run: | 106 | if [[ ${{ github.ref }} =~ ^refs/heads/(main|v[0-9]+\.[0-9]+.*)$ ]]; then 107 | echo ::set-output name=match::true 108 | fi # See: https://stackoverflow.com/a/58869470/1123955 109 | - name: Is A Publish Branch 110 | if: steps.check-branch.outputs.match == 'true' 111 | run: | 112 | NAME=$(npx pkg-jq -r .name) 113 | VERSION=$(npx pkg-jq -r .version) 114 | if npx version-exists "$NAME" "$VERSION" 115 | then echo "$NAME@$VERSION exists on NPM, skipped." 116 | else npm publish 117 | fi 118 | env: 119 | NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} 120 | - name: Is Not A Publish Branch 121 | if: steps.check-branch.outputs.match != 'true' 122 | run: echo 'Not A Publish Branch' 123 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | # For more information see: https://docs.github.com/en/actions/getting-started-with-github-actions 2 | # For more setup-php see: https://github.com/shivammathur/setup-php 3 | # For more setup-php protoc see: https://github.com/shivammathur/setup-php/issues/262 4 | 5 | name: PHP 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | pull_request: 12 | paths: 13 | - 'polyglot/php/**' 14 | - 'proto/**' 15 | 16 | jobs: 17 | run: 18 | name: Run 19 | runs-on: ${{ matrix.operating-system }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | operating-system: 24 | - ubuntu-latest 25 | php-version: 26 | - 7.4 27 | node-version: 28 | - 16 29 | env: 30 | extensions: xml, opcache, xdebug, pcov, grpc-1.30.0, protobuf-3.12.2 31 | key: cache-v3 32 | 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v2 36 | - name: Setup PHP with extensions and custom config 37 | uses: shivammathur/setup-php@v2 38 | with: 39 | php-version: ${{ matrix.php-version }} 40 | extensions: ${{ env.extensions }} 41 | ini-values: post_max_size=256M, short_open_tag=On, date.timezone=Asia/Shanghai 42 | tools: protoc, grpc_php_plugin 43 | - name: Testing PHP version 44 | run: | 45 | php -v 46 | php -r "if(strpos(phpversion(), '${{ matrix.php-version }}') === false) {throw new Exception('Wrong PHP version Installed');}" 47 | - name: Use Node.js ${{ matrix.node-version }} 48 | uses: actions/setup-node@v2 49 | with: 50 | node-version: ${{ matrix.node-version }} 51 | cache: npm 52 | cache-dependency-path: package.json 53 | - name: Set up Go 54 | uses: actions/setup-go@v2 55 | with: 56 | go-version: 1.18 57 | id: go 58 | - name: Install Dependencies 59 | run: | 60 | npm install 61 | npm run install:protoc 62 | - name: Test grpc_php_plugin and protoc 63 | run: | 64 | protoc --version 65 | cd polyglot/php 66 | cp /home/linuxbrew/.linuxbrew/bin/grpc_php_plugin /usr/local/bin/grpc_php_plugin 67 | make generate 68 | -------------------------------------------------------------------------------- /.github/workflows/python.yml: -------------------------------------------------------------------------------- 1 | name: Python PyPI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | paths: 9 | - 'polyglot/python/**' 10 | - 'proto/**' 11 | 12 | jobs: 13 | build: 14 | name: Build 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest] 18 | 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v1 24 | with: 25 | python-version: 3.9 26 | - name: Set up Go 27 | uses: actions/setup-go@v2 28 | with: 29 | go-version: 1.18 30 | - uses: actions/cache@master 31 | with: 32 | path: ~/.cache/pip 33 | key: ${{ runner.os }}-pip-${{ hashFiles('python/requirements.txt') }} 34 | restore-keys: | 35 | ${{ runner.os }}-pip- 36 | 37 | - name: Install Protoc 38 | uses: arduino/setup-protoc@v1 39 | - name: Install Protoc 3rd party protos 40 | run: scripts/install-protoc.sh 41 | 42 | - name: Install dependencies 43 | run: | 44 | cd polyglot/python 45 | python -m pip install --upgrade pip 46 | make install 47 | - name: Test 48 | run: | 49 | cd polyglot/python 50 | make generate 51 | make test 52 | 53 | pack: 54 | name: Pack 55 | needs: build 56 | runs-on: ubuntu-latest 57 | steps: 58 | - uses: actions/checkout@v2 59 | - uses: actions/setup-python@v1 60 | with: 61 | python-version: 3.9 62 | - name: Set up Go 63 | uses: actions/setup-go@v2 64 | with: 65 | go-version: 1.18 66 | 67 | - uses: actions/cache@master 68 | with: 69 | path: ~/.cache/pip 70 | key: ${{ runner.os }}-pip-${{ hashFiles('python/requirements.txt') }} 71 | restore-keys: | 72 | ${{ runner.os }}-pip- 73 | 74 | - name: Install Protoc 75 | uses: arduino/setup-protoc@v1 76 | - name: Install Protoc 3rd party protos 77 | run: scripts/install-protoc.sh 78 | 79 | - name: Install dependencies 80 | run: | 81 | cd polyglot/python 82 | python -m pip install --upgrade pip 83 | pip install setuptools wheel twine 84 | make install 85 | - name: Pack Testing 86 | run: | 87 | cd polyglot/python 88 | make generate 89 | make dist 90 | echo "To be add: pack testing" 91 | 92 | deploy: 93 | if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/v')) 94 | name: Deploy 95 | needs: [build, pack] 96 | runs-on: ubuntu-latest 97 | steps: 98 | - uses: actions/checkout@v2 99 | - name: Set up Python 100 | uses: actions/setup-python@v1 101 | with: 102 | python-version: '3.9' 103 | - name: Set up Go 104 | uses: actions/setup-go@v2 105 | with: 106 | go-version: 1.18 107 | 108 | - uses: actions/cache@master 109 | with: 110 | path: ~/.cache/pip 111 | key: ${{ runner.os }}-pip-${{ hashFiles('python/requirements.txt') }} 112 | restore-keys: | 113 | ${{ runner.os }}-pip- 114 | 115 | - name: Install Protoc 116 | uses: arduino/setup-protoc@v1 117 | - name: Install Protoc 3rd party protos 118 | run: scripts/install-protoc.sh 119 | 120 | - name: Install dependencies 121 | run: | 122 | cd polyglot/python 123 | python -m pip install --upgrade pip 124 | pip3 install setuptools wheel twine 125 | 126 | - name: Check Branch 127 | id: check-branch 128 | run: | 129 | if [[ ${{ github.ref }} =~ ^refs/heads/(main|v[0-9]+\.[0-9]+.*)$ ]]; then 130 | echo ::set-output name=match::true 131 | fi # See: https://stackoverflow.com/a/58869470/1123955 132 | 133 | - name: Is A Publish Branch 134 | if: steps.check-branch.outputs.match == 'true' 135 | env: 136 | TWINE_USERNAME: __token__ 137 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 138 | run: | 139 | cd polyglot/python 140 | make install 141 | make generate 142 | make dist 143 | python3 setup.py sdist bdist_wheel 144 | twine upload --skip-existing dist/* 145 | 146 | - name: Is Not A Publish Branch 147 | if: steps.check-branch.outputs.match != 'true' 148 | run: echo 'Not A Publish Branch' 149 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | package-lock.json 61 | t/ 62 | t.* 63 | dist/ 64 | .ropeproject/ 65 | build/ 66 | __pycache__/ 67 | .idea 68 | 69 | # Generated temp dirs 70 | out/ 71 | 72 | id_rsa* 73 | t/ 74 | t.* 75 | 76 | .idea 77 | target/ 78 | 79 | # csharp 80 | *.nupkg 81 | .vs/ 82 | *.suo 83 | *.user 84 | *.userosscache 85 | *.sln.docstates 86 | 87 | csharp/Wechaty.Grpc/.vs/ 88 | csharp/Wechaty.Grpc/proto 89 | csharp/Wechaty.Grpc/obj/ 90 | csharp/Wechaty.Grpc/bin/ 91 | csharp/Wechaty.Grpc/nupkg/package/ 92 | csharp/Wechaty.Grpc/Properties 93 | 94 | third-party/ 95 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib", 3 | 4 | "editor.fontFamily": "Consolas, 'Courier New', monospace", 5 | "editor.fontLigatures": true, 6 | 7 | "editor.tokenColorCustomizations": { 8 | "textMateRules": [ 9 | { 10 | "scope": [ 11 | //following will be in italics (=Pacifico) 12 | "comment", 13 | // "entity.name.type.class", //class names 14 | "keyword", //import, export, return… 15 | "support.class.builtin.js", //String, Number, Boolean…, this, super 16 | "storage.modifier", //static keyword 17 | "storage.type.class.js", //class keyword 18 | "storage.type.function.js", // function keyword 19 | "storage.type.js", // Variable declarations 20 | "keyword.control.import.js", // Imports 21 | "keyword.control.from.js", // From-Keyword 22 | "entity.name.type.js", // new … Expression 23 | "keyword.control.flow.js", // await 24 | "keyword.control.conditional.js", // if 25 | "keyword.control.loop.js", // for 26 | "keyword.operator.new.js", // new 27 | ], 28 | "settings": { 29 | "fontStyle": "italic", 30 | }, 31 | }, 32 | { 33 | "scope": [ 34 | //following will be excluded from italics (My theme (Monokai dark) has some defaults I don't want to be in italics) 35 | "invalid", 36 | "keyword.operator", 37 | "constant.numeric.css", 38 | "keyword.other.unit.px.css", 39 | "constant.numeric.decimal.js", 40 | "constant.numeric.json", 41 | "entity.name.type.class.js" 42 | ], 43 | "settings": { 44 | "fontStyle": "", 45 | }, 46 | } 47 | ] 48 | }, 49 | 50 | "files.exclude": { 51 | "dist/": true, 52 | "doc/": true, 53 | "node_modules/": true, 54 | "package/": true, 55 | }, 56 | "protoc": { 57 | "options": [ 58 | "--proto_path=source_relative" // https://github.com/zxh0/vscode-proto3/issues/31#issuecomment-628162495 59 | ], 60 | }, 61 | "alignment": { 62 | "operatorPadding": "right", 63 | "indentBase": "firstline", 64 | "surroundSpace": { 65 | "colon": [1, 1], // The first number specify how much space to add to the left, can be negative. The second number is how much space to the right, can be negative. 66 | "assignment": [1, 1], // The same as above. 67 | "arrow": [1, 1], // The same as above. 68 | "comment": 2, // Special how much space to add between the trailing comment and the code. 69 | // If this value is negative, it means don't align the trailing comment. 70 | } 71 | }, 72 | "editor.formatOnSave": false, 73 | "python.pythonPath": "python3", 74 | "eslint.validate": [ 75 | "javascript", 76 | "typescript", 77 | ], 78 | "cSpell.words": [ 79 | "PYPI", 80 | "bdist", 81 | "proto", 82 | "sdist", 83 | "setuptools" 84 | ], 85 | "python.linting.pylintEnabled": true, 86 | 87 | } 88 | 89 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guidelines for Contributing 2 | 3 | Thank you for your time on Wechaty gRPC. Wechaty gRPC is gRPC Service & Protocol Buffers for Wechaty Puppet. 4 | 5 | Check Wechaty contributing guidelines at [https://wechaty.js.org/docs/contributing/](https://wechaty.js.org/docs/contributing/). 6 | 7 | As an open-source product, Wechaty thrives on the contributions of community members. Whatever your skill set is, there is a lot you can do to help us make Wechaty better! 8 | So start forking! 9 | 10 | We built wechaty with pleasure because it can help others. Help from you for wechaty will be very much appreciated by the community. 11 | 12 | Cheers! 13 | 14 | Huan 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018-now Huan LI (李卓桓) 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Chatie GRPC Service 2 | Copyright 2016 - now, Huan LI (李卓桓) 3 | 4 | This product includes software developed at 5 | The Wechaty Organization (https://github.com/wechaty). 6 | 7 | This software contains code derived from the Stackoverflow, 8 | including various modifications by GitHub. 9 | -------------------------------------------------------------------------------- /RESOURCES.md: -------------------------------------------------------------------------------- 1 |
2 |

Wechaty gRPC

3 |

4 | gRPC Service & Protocol Buffers for Wechaty Puppet 5 |

6 | Learning Resources 7 |

8 |
9 | 10 | ## gRPC 11 | 12 | - [探讨 gRPC 的 Node 技术生态及实现工具](https://xenojoshua.com/2018/02/grpc-node-ecosystem/) 13 | - [gRPC Basics - Node.js](https://grpc.io/docs/tutorials/basic/node.html) 14 | - [Building a gRPC service with Node.js](https://codelabs.developers.google.com/codelabs/cloud-grpc/) 15 | - [gRPC in 3 minutes (Node.js)](https://github.com/grpc/grpc/tree/master/examples/node) 16 | - [Listen gRPC and HTTP requests on the same port](https://medium.com/@drgarcia1986/listen-grpc-and-http-requests-on-the-same-port-263c40cb45ff) 17 | - [gRPC to JSON proxy generator following the gRPC HTTP spec](https://github.com/grpc-ecosystem/grpc-gateway) 18 | - [如何在 Node.js 中更优雅地使用 gRPC:grpc-helper](https://github.com/xizhibei/blog/issues/86) 19 | 20 | #### Official Libraries and Tools 21 | 22 | - [gRPC Core](https://github.com/grpc/grpc) - C, C++, Ruby, Node.js, Python, PHP, C#, Objective-C 23 | - [gRPC Java](https://github.com/grpc/grpc-java) - The Java gRPC implementation. HTTP/2 based RPC 24 | - [gRPC Kotlin](https://github.com/grpc/grpc-kotlin) - The Kotlin gRPC implementation. Based on gRPC Java 25 | - [gRPC Node.js](https://github.com/grpc/grpc-node) - gRPC for Node.js 26 | - [gRPC Go](https://github.com/grpc/grpc-go) - The Go language implementation of gRPC. HTTP/2 based RPC 27 | - [gRPC Swift](https://github.com/grpc/grpc-swift) - The Swift language implementation of gRPC 28 | - [gRPC Dart](https://github.com/grpc/grpc-dart) - The Dart language implementation of gRPC 29 | - [gRPC C#](https://github.com/grpc/grpc-dotnet) - The C# language implementation of gRPC 30 | - [gRPC Web](https://github.com/grpc/grpc-web) - gRPC for Web Clients 31 | - [gRPC Ecosystem](https://github.com/grpc-ecosystem) - gRPC Ecosystem that complements gRPC 32 | - [gRPC contrib](https://github.com/grpc/grpc-contrib) - Known useful contributions around github 33 | - [Homebrew gRPC](https://github.com/grpc/homebrew-grpc) - gRPC formulae repo for Homebrew 34 | - [grpc_cli](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md) - gRPC CLI tool 35 | 36 | ## Protocol Buffers 37 | 38 | - [Protocol Buffers for JavaScript (& TypeScript)](https://github.com/dcodeIO/protobuf.js) 39 | - [Missing value/null support for scalar value types in proto 3](https://github.com/protocolbuffers/protobuf/issues/1606) 40 | - [How to Make a Nullable Field in Proto3 for a HTTP Response?](https://stackoverflow.com/questions/57908389/how-to-make-a-nullable-field-in-proto3-for-a-http-response) 41 | 42 | #### Documentation 43 | 44 | - [Website](https://developers.google.com/protocol-buffers/) - Official website and documentation 45 | - [Third-Party Add-ons for Protocol Buffers](https://github.com/protocolbuffers/protobuf/blob/master/docs/third_party.md) - List of add-ons for Protocol Buffers in the main Github repository 46 | 47 | #### Tools 48 | 49 | - [buf](https://buf.build) - Protobuf tool that includes linting and breaking change detection. 50 | Allows many types of input including directly checking remote repositories and tarballs, and has a built-in compiler as well. 51 | - [prototools](https://github.com/sourcegraph/prototools) - Documentation generator & other tools for protobuf/gRPC 52 | - [protoc-gen-doc](https://github.com/pseudomuto/protoc-gen-doc) - Documentation generator plugin for Google Protocol Buffers 53 | - [Protoxygen](https://github.com/lisroach/Protoxygen) - [Doxygen](http://doxygen.nl) plugin to generate documentation for protobuf/gRPC 54 | - [openapi2proto](https://github.com/NYTimes/openapi2proto) - A tool for generating Protobuf v3 schemas and gRPC service definitions from OpenAPI specifications 55 | - [Wireshark Protobuf Dissector](https://github.com/128technology/protobuf_dissector) - A Wireshark Lua plugin for decoding Google protobuf packets. [Relevant PR and discussion](https://github.com/google/protobuf/issues/3303). 56 | - [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint) - A plug-in for Google's Protocol Buffers (protobufs) compiler to lint .proto files for style violations 57 | - [prototool](https://github.com/uber/prototool) - Compile, lint, and format Protobuf files, and generate stubs for any lanuguage/plugin, along with Vim/IDE integration 58 | - [protoc-gen-validate](https://github.com/lyft/protoc-gen-validate) - Protoc plugin to generate polyglot message validators 59 | - [go-proto-validators](https://github.com/mwitkow/go-proto-validators) - Generate message validators from .proto annotations, used in `grpc_validator` Go gRPC middleware. 60 | - [protolock](https://github.com/nilslice/protolock) - Protocol Buffer companion tool to `protoc` and `git`. Track your .proto files and prevent changes to messages and services which impact API compatibilty. 61 | - [protoc-gen-map](https://github.com/jackskj/protoc-gen-map) - SQL data mapper framework for Protocol Buffers. 62 | - [api-linter](https://github.com/googleapis/api-linter) - A linter for APIs defined in protocol buffers. 63 | - [protoc-gen-struct-transformer](https://github.com/bold-commerce/protoc-gen-struct-transformer) - Transformation functions generator for Protocol Buffers. 64 | - [pbvm](https://github.com/ekalinin/pbvm) - Protocol Buffers Version Manager 65 | - [clang-format](https://clang.llvm.org/docs/ClangFormat.html) - Protocol Buffers formating tool 66 | Can be used to format on save in editor such as [Visual studio code](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) or [IntelliJ](https://plugins.jetbrains.com/plugin/14004-protocol-buffer-editor). 67 | - [intellij-protobuf-plugin](https://github.com/devkanro/intellij-protobuf-plugin) - IntelliJ-based IDEs Protobuf Language Plugin that provides Protobuf language support. 68 | 69 | ## gRPC Web 70 | 71 | - [gRPC-Web: Moving past REST+JSON towards type-safe Web APIs](https://improbable.io/blog/grpc-web-moving-past-restjson-towards-type-safe-web-apis) 72 | - [Library for making gRPC-Web requests intended for TypeScript from either a browser or Node.js.](https://github.com/improbable-eng/grpc-web/tree/master/ts) 73 | - [gRPC-Web: Moving past REST+JSON towards type-safe Web APIs](https://improbable.io/blog/grpc-web-moving-past-restjson-towards-type-safe-web-apis) 74 | - [Library for making gRPC-Web requests intended for TypeScript from either a browser or Node.js.](https://github.com/improbable-eng/grpc-web/tree/master/ts) 75 | 76 | ## TypeScript Generator 77 | 78 | - [A Typescript definition file generator for gRPC services](https://github.com/anfema/grpc-code-generator) 79 | - [gRPC Web TypeScript Code Generation](https://github.com/improbable-eng/grpc-web/blob/master/ts/docs/code-generation.md) 80 | - [Protocol Buffers Compiler (protoc) plugin for TypeScript and gRPC-Web.](https://github.com/improbable-eng/ts-protoc-gen) 81 | 82 | ## gRPC with XDS (Universal Data Plane API) and Service Mesh 83 | 84 | - [Traffic Director with proxyless gRPC services overview](https://cloud.google.com/traffic-director/docs/proxyless-overview) 85 | - [Build Your Own Envoy Control Plane - Steve Sloka, VMware](https://www.youtube.com/watch?v=qAuq4cKEG_E) 86 | - [Service mesh data plane vs. control plane](https://blog.envoyproxy.io/service-mesh-data-plane-vs-control-plane-2774e720f7fc) 87 | - [gRPC xDS Loadbalancing](https://salmaan-rashid.medium.com/grpc-xds-loadbalancing-a05f8bd754b8) 88 | - [Envoy Dynamic configuration (control plane)](https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/dynamic-configuration-control-plane) 89 | 90 | #### More TypeScript 91 | 92 | - [ts-protoc-gen](https://github.com/improbable-eng/ts-protoc-gen) - Protoc Plugin for TypeScript Declarations 93 | - [protoc-gen-tstypes](https://godoc.org/github.com/tmc/grpcutil/protoc-gen-tstypes) - Configurable Protoc Plugin to generate TypeScript types. 94 | - [sisyphus.js](https://github.com/ButterCam/sisyphus-js) - gRPC runtime and compiler for Web Clients by HTTP transcoding. Recommend using with [Sisyphus](https://github.com/ButterCam/sisyphus) back-end framework. 95 | - [protoc-gen-grpc-gateway-ts](https://github.com/grpc-ecosystem/protoc-gen-grpc-gateway-ts) - TypeScript client generator for the grpc-gateway project that generates idiomatic TypeScript clients that connect the web frontend and golang backend fronted by grpc-gateway. 96 | - [protobuf-ts](https://github.com/timostamm/protobuf-ts) - Protocol buffers and RPC for Node.js and the Web Browser. Pure TypeScript. 97 | - [ts-proto](https://github.com/stephenh/ts-proto) - Transforms your .proto files into strongly-typed, idiomatic TypeScript files! 98 | - [grpc-js-typescript](https://github.com/badsyntax/grpc-js-typescript) - Examples of how to use gRPC with TypeScript & Node.js. 99 | 100 | ## ZooKeeper 101 | 102 | - [ZooKeeper - The King of Coordination](https://www.elastic.co/blog/found-zookeeper-king-of-coordination) 103 | 104 | ## Session Traversal Utilities for NAT (STUN) 105 | 106 | - [How To TCP NAT Traversal using Node.js and a STUN Server](http://sogilis.com/blog/tcp-nat-traversal-nodejs-stun/) 107 | - [chownat, allows two peers behind two separate NATs to directly communicate with each other.](https://samy.pl/chownat/) 108 | - [What is STUN & TURN Server](https://stackoverflow.com/a/23307588/1123955) 109 | - [NPM Search for: NAT Traversal](https://www.npmjs.com/search?q=nat+traversal) 110 | - [NAT traversal by way of UPnP or NAT-PMP](https://github.com/tintfoundation/nat-traverse) 111 | - [How To TCP NAT Traversal using Node.js and a STUN Server](https://gist.github.com/mildred/b803e48801f9cdd8a4a8) 112 | - [STUN, TURN, and ICE - AnyConnect pioneered the STUN, TURN, and ICE NAT Traversal protocols](https://anyconnect.com/stun-turn-ice/) 113 | - [What are STUN, TURN, and ICE?](https://www.twilio.com/docs/stun-turn/faq#faq-what-is-nat) 114 | - [Set Phasers to STUN/TURN: Getting Started with WebRTC using Node.js, Socket.io, and Twilio’s NAT Traversal Service](https://www.twilio.com/blog/2014/12/set-phasers-to-stunturn-getting-started-with-webrtc-using-node-js-socket-io-and-twilios-nat-traversal-service.html) 115 | 116 | ## Reverse Engineering 117 | 118 | - [A toolset for reverse engineering and fuzzing Protobuf-based apps](https://github.com/marin-m/pbtk) 119 | 120 | ## Python Stub 121 | 122 | - [gRPC typing stubs for Python](https://github.com/shabbyrobe/grpc-stubs) 123 | 124 | ## CSharp gRPC 125 | 126 | - [An introduction to NuGet (microsoft)](https://docs.microsoft.com/en-us/nuget/what-is-nuget) 127 | - [Create a gRPC client and server in ASP.NET Core (microsoft)](https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.1&tabs=visual-studio) 128 | - [ASP.NET Core 3.0 使用 gRPC (晓晨 Master)](https://www.cnblogs.com/stulzq/p/11581967.html) 129 | 130 | ## Miscellaneous Documentation 131 | 132 | - [Protocol Buffers Language Guide (proto3)](https://developers.google.com/protocol-buffers/docs/proto3) 133 | - [Protocol Buffers for TypeScript with Decorators](https://github.com/protobufjs/protobuf.js#using-decorators) 134 | - [Troubleshooting gRPC](https://github.com/grpc/grpc/blob/master/TROUBLESHOOTING.md) 135 | - [gRPC environment variables](https://github.com/grpc/grpc/blob/master/doc/environment_variables.md) 136 | - [How to Interact With and Debug a gRPC Server](https://medium.com/@EdgePress/how-to-interact-with-and-debug-a-grpc-server-c4bc30ddeb0b) 137 | -------------------------------------------------------------------------------- /commonjs/README.md: -------------------------------------------------------------------------------- 1 | # CommonJS Wrappers 2 | 3 | Huan(202108): CommonJS wrappers for workaround 4 | 5 | ## package.json 6 | 7 | It must be exists because the ts-node will require this file for running under CommonJS mode. 8 | 9 | ## `.cjs` 10 | 11 | `.cjs` is a file that contains CommonJS wrapper code. 12 | 13 | ## `.cjs.d.ts` 14 | 15 | `.cjs.d.ts` is a file that contains the type definition of the CommonJS wrapper code. 16 | -------------------------------------------------------------------------------- /commonjs/code-root.cjs: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const codeRoot = path.join( 4 | __dirname, 5 | '..', 6 | ) 7 | 8 | module.exports = { 9 | codeRoot, 10 | } 11 | -------------------------------------------------------------------------------- /commonjs/code-root.cjs.d.ts: -------------------------------------------------------------------------------- 1 | export declare const codeRoot: string 2 | -------------------------------------------------------------------------------- /commonjs/code-root.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S ts-node --project tsconfig.cjs.json 2 | 3 | import { test } from 'tstest' 4 | 5 | import { codeRoot } from './code-root.cjs' 6 | 7 | test('CJS: codeRoot()', async t => { 8 | t.ok(codeRoot, 'should exist codeRoot') 9 | }) 10 | -------------------------------------------------------------------------------- /commonjs/generated/google.cjs: -------------------------------------------------------------------------------- 1 | const googleFileList = [ 2 | '../../out/google/api/health_check/v1/health_check_grpc_pb.js', 3 | '../../out/google/api/health_check/v1/health_check_pb.js', 4 | ] 5 | 6 | module.exports = googleFileList.reduce((acc, pkg) => ({ 7 | ...acc, 8 | ...require(pkg), 9 | }), {}) // Huan(202108): must provide a `{}` as the initial value, or it will be `[]` 10 | -------------------------------------------------------------------------------- /commonjs/generated/google.cjs.d.ts: -------------------------------------------------------------------------------- 1 | export * from '../../out/google/api/health_check/v1/health_check_grpc_pb.js' 2 | export * from '../../out/google/api/health_check/v1/health_check_pb.js' 3 | -------------------------------------------------------------------------------- /commonjs/generated/google.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S ts-node --project tsconfig.cjs.json 2 | 3 | import { test } from 'tstest' 4 | 5 | import google from './google.cjs' 6 | 7 | test('CJS: HealthCheckResponse', async t => { 8 | t.ok(google.HealthCheckResponse, 'should exists "HealthCheckResponse"') 9 | }) 10 | 11 | test('CJS: ServingStatus.SERVING', async t => { 12 | t.ok(google.HealthCheckResponse.ServingStatus.SERVING, 'should exists "ServingStatus.SERVING"') 13 | }) 14 | -------------------------------------------------------------------------------- /commonjs/generated/puppet.cjs: -------------------------------------------------------------------------------- 1 | const puppetFileList = [ 2 | '../../out/wechaty/deprecated/file-box_pb.js', 3 | 4 | '../../out/wechaty/puppet/base_pb.js', 5 | '../../out/wechaty/puppet/contact_pb.js', 6 | '../../out/wechaty/puppet/download-upload_pb.js', 7 | '../../out/wechaty/puppet/event_pb.js', 8 | '../../out/wechaty/puppet/friendship_pb.js', 9 | '../../out/wechaty/puppet/location_pb.js', 10 | '../../out/wechaty/puppet/message_pb.js', 11 | '../../out/wechaty/puppet/mini-program_pb.js', 12 | '../../out/wechaty/puppet/referrer_pb.js', 13 | '../../out/wechaty/puppet/room_pb.js', 14 | '../../out/wechaty/puppet/room-invitation_pb.js', 15 | '../../out/wechaty/puppet/room-member_pb.js', 16 | '../../out/wechaty/puppet/tag_pb.js', 17 | '../../out/wechaty/puppet/url-link_pb.js', 18 | 19 | '../../out/wechaty/puppet_grpc_pb.js', 20 | '../../out/wechaty/puppet_pb.js', 21 | ] 22 | 23 | /** 24 | * Huan(202108): 25 | * if there's a "package.json" file in the `out/` directory, 26 | * then all the files in the `out/` directory will be treated as one module, 27 | * which means tht `require` each file under that directory will add methods to the same module. 28 | */ 29 | // for (const pkg of pkgs) { 30 | // console.info('## pkg:', pkg) 31 | // const module = require(pkg) 32 | // console.info(Object.keys(module).length) 33 | // // OOPS! The output number above will be keep increasing 34 | // } 35 | 36 | module.exports = puppetFileList.reduce((acc, pkg) => ({ 37 | ...acc, 38 | ...require(pkg), 39 | }), {}) // Huan(202108): must provide a `{}` as the initial value, or it will be `[]` 40 | -------------------------------------------------------------------------------- /commonjs/generated/puppet.cjs.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Huan(202108) 3 | * 4 | * Re-exporting namespace declarations in ES6 ambient declaration #4336 5 | * https://github.com/microsoft/TypeScript/issues/4336 6 | */ 7 | 8 | /** 9 | * Huan(202108): I want to `declare namespace puppet {...}` 10 | * but it seemss the `export * from '../out/...js` is not working 11 | * 12 | * So I export them on the top level, 13 | * then import them in another `puppet.js` file 14 | * with the `puppet` namespace 15 | * 16 | * This is because the ESM module system need to do the following things 17 | * when import a CJS module: 18 | * 19 | * ```ts 20 | * import pkg from './cjs-pkg' 21 | * const puppet = pkg['puppet'] 22 | * ``` 23 | */ 24 | export * from '../../out/wechaty/deprecated/file-box_pb.js' 25 | 26 | export * from '../../out/wechaty/puppet/base_pb.js' 27 | export * from '../../out/wechaty/puppet/contact_pb.js' 28 | export * from '../../out/wechaty/puppet/download-upload_pb.js' 29 | export * from '../../out/wechaty/puppet/event_pb.js' 30 | export * from '../../out/wechaty/puppet/friendship_pb.js' 31 | export * from '../../out/wechaty/puppet/location_pb.js' 32 | export * from '../../out/wechaty/puppet/message_pb.js' 33 | export * from '../../out/wechaty/puppet/mini-program_pb.js' 34 | export * from '../../out/wechaty/puppet/referrer_pb.js' 35 | export * from '../../out/wechaty/puppet/room_pb.js' 36 | export * from '../../out/wechaty/puppet/room-invitation_pb.js' 37 | export * from '../../out/wechaty/puppet/room-member_pb.js' 38 | export * from '../../out/wechaty/puppet/tag_pb.js' 39 | export * from '../../out/wechaty/puppet/url-link_pb.js' 40 | 41 | export * from '../../out/wechaty/puppet_grpc_pb.js' 42 | export * from '../../out/wechaty/puppet_pb.js' 43 | -------------------------------------------------------------------------------- /commonjs/generated/puppet.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S ts-node --project tsconfig.cjs.json 2 | 3 | import { test } from 'tstest' 4 | 5 | import puppet from './puppet.cjs' 6 | 7 | test('CJS: EventRequest', async t => { 8 | t.ok(puppet.EventRequest, 'should export EventRequest') 9 | }) 10 | 11 | test('CJS: PuppetService', async t => { 12 | t.ok(puppet.PuppetService, 'should export PuppetSevice') 13 | }) 14 | 15 | test('CJS: EventTypeMap', async t => { 16 | const map: puppet.EventTypeMap = {} as any 17 | map.EVENT_TYPE_DIRTY = puppet.EventType.EVENT_TYPE_DIRTY 18 | t.equal(Object.keys(map).length, 1, 'should export type EventTypeMap') 19 | }) 20 | -------------------------------------------------------------------------------- /commonjs/package.json: -------------------------------------------------------------------------------- 1 | { "type": "commonjs" } 2 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all : docs 3 | 4 | .PHONY: docs 5 | docs: 6 | protoc \ 7 | -I ../third-party/ \ 8 | -I ../proto/ \ 9 | --doc_out=./ \ 10 | --doc_opt=markdown,README.md \ 11 | ../proto/wechaty/puppet.proto 12 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Protocol Documentation 2 | 3 | 4 | ## Table of Contents 5 | 6 | - [wechaty/puppet.proto](#wechaty/puppet.proto) 7 | - [Puppet](#wechaty.Puppet) 8 | 9 | - [Scalar Value Types](#scalar-value-types) 10 | 11 | 12 | 13 | 14 |

Top

15 | 16 | ## wechaty/puppet.proto 17 | Wechaty Puppet gRPC Protocol Buffers 18 | https://github.com/wechaty/grpc/ 19 | Huan LI <zixia@zixia.net> 20 | Apr 2018 21 | License: Apache-2.0 22 | 23 | Google Protocol Buffers 24 | Style Guide - https://developers.google.com/protocol-buffers/docs/style 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ### Puppet 36 | 37 | 38 | | Method Name | Request Type | Response Type | Description | 39 | | ----------- | ------------ | ------------- | ------------| 40 | | Start | [puppet.StartRequest](#wechaty.puppet.StartRequest) | [puppet.StartResponse](#wechaty.puppet.StartResponse) | Base | 41 | | Stop | [puppet.StopRequest](#wechaty.puppet.StopRequest) | [puppet.StopResponse](#wechaty.puppet.StopResponse) | | 42 | | Logout | [puppet.LogoutRequest](#wechaty.puppet.LogoutRequest) | [puppet.LogoutResponse](#wechaty.puppet.LogoutResponse) | | 43 | | Ding | [puppet.DingRequest](#wechaty.puppet.DingRequest) | [puppet.DingResponse](#wechaty.puppet.DingResponse) | | 44 | | Version | [puppet.VersionRequest](#wechaty.puppet.VersionRequest) | [puppet.VersionResponse](#wechaty.puppet.VersionResponse) | | 45 | | Event | [puppet.EventRequest](#wechaty.puppet.EventRequest) | [puppet.EventResponse](#wechaty.puppet.EventResponse) stream | Event - Server Stream | 46 | | DirtyPayload | [puppet.DirtyPayloadRequest](#wechaty.puppet.DirtyPayloadRequest) | [puppet.DirtyPayloadResponse](#wechaty.puppet.DirtyPayloadResponse) | | 47 | | ContactSelfQRCode | [puppet.ContactSelfQRCodeRequest](#wechaty.puppet.ContactSelfQRCodeRequest) | [puppet.ContactSelfQRCodeResponse](#wechaty.puppet.ContactSelfQRCodeResponse) | Contact Self | 48 | | ContactSelfName | [puppet.ContactSelfNameRequest](#wechaty.puppet.ContactSelfNameRequest) | [puppet.ContactSelfNameResponse](#wechaty.puppet.ContactSelfNameResponse) | | 49 | | ContactSelfSignature | [puppet.ContactSelfSignatureRequest](#wechaty.puppet.ContactSelfSignatureRequest) | [puppet.ContactSelfSignatureResponse](#wechaty.puppet.ContactSelfSignatureResponse) | | 50 | | ContactPayload | [puppet.ContactPayloadRequest](#wechaty.puppet.ContactPayloadRequest) | [puppet.ContactPayloadResponse](#wechaty.puppet.ContactPayloadResponse) | Contact | 51 | | ContactAlias | [puppet.ContactAliasRequest](#wechaty.puppet.ContactAliasRequest) | [puppet.ContactAliasResponse](#wechaty.puppet.ContactAliasResponse) | | 52 | | ContactAvatar | [puppet.ContactAvatarRequest](#wechaty.puppet.ContactAvatarRequest) | [puppet.ContactAvatarResponse](#wechaty.puppet.ContactAvatarResponse) | | 53 | | ContactPhone | [puppet.ContactPhoneRequest](#wechaty.puppet.ContactPhoneRequest) | [puppet.ContactPhoneResponse](#wechaty.puppet.ContactPhoneResponse) | | 54 | | ContactCorporationRemark | [puppet.ContactCorporationRemarkRequest](#wechaty.puppet.ContactCorporationRemarkRequest) | [puppet.ContactCorporationRemarkResponse](#wechaty.puppet.ContactCorporationRemarkResponse) | | 55 | | ContactDescription | [puppet.ContactDescriptionRequest](#wechaty.puppet.ContactDescriptionRequest) | [puppet.ContactDescriptionResponse](#wechaty.puppet.ContactDescriptionResponse) | | 56 | | ContactList | [puppet.ContactListRequest](#wechaty.puppet.ContactListRequest) | [puppet.ContactListResponse](#wechaty.puppet.ContactListResponse) | Huan(202002): consider changing response to a stream in the future for better performance | 57 | | FriendshipPayload | [puppet.FriendshipPayloadRequest](#wechaty.puppet.FriendshipPayloadRequest) | [puppet.FriendshipPayloadResponse](#wechaty.puppet.FriendshipPayloadResponse) | Friendship | 58 | | FriendshipSearchPhone | [puppet.FriendshipSearchPhoneRequest](#wechaty.puppet.FriendshipSearchPhoneRequest) | [puppet.FriendshipSearchPhoneResponse](#wechaty.puppet.FriendshipSearchPhoneResponse) | | 59 | | FriendshipSearchWeixin | [puppet.FriendshipSearchWeixinRequest](#wechaty.puppet.FriendshipSearchWeixinRequest) | [puppet.FriendshipSearchWeixinResponse](#wechaty.puppet.FriendshipSearchWeixinResponse) | | 60 | | FriendshipAdd | [puppet.FriendshipAddRequest](#wechaty.puppet.FriendshipAddRequest) | [puppet.FriendshipAddResponse](#wechaty.puppet.FriendshipAddResponse) | | 61 | | FriendshipAccept | [puppet.FriendshipAcceptRequest](#wechaty.puppet.FriendshipAcceptRequest) | [puppet.FriendshipAcceptResponse](#wechaty.puppet.FriendshipAcceptResponse) | | 62 | | MessageFile | [puppet.MessageFileRequest](#wechaty.puppet.MessageFileRequest) | [puppet.MessageFileResponse](#wechaty.puppet.MessageFileResponse) | @deprecated: using MessageFileStream to transfer files Huan(202010): will be removed (replaced by MessageFileStream) after Dec 31, 2021 | 63 | | MessageImage | [puppet.MessageImageRequest](#wechaty.puppet.MessageImageRequest) | [puppet.MessageImageResponse](#wechaty.puppet.MessageImageResponse) | @deprecated: using MessageImageStream to transfer images Huan(202010): will be removed (replaced by MessageImageStream) after Dec 31, 2021 | 64 | | MessageSendFile | [puppet.MessageSendFileRequest](#wechaty.puppet.MessageSendFileRequest) | [puppet.MessageSendFileResponse](#wechaty.puppet.MessageSendFileResponse) | @deprecated: using MesageSendFileStream to transfer file message to server Huan(202010): will be removed (replaced by MessageSendFileStream) after Dec 31, 2021 | 65 | | MessagePayload | [puppet.MessagePayloadRequest](#wechaty.puppet.MessagePayloadRequest) | [puppet.MessagePayloadResponse](#wechaty.puppet.MessagePayloadResponse) | | 66 | | MessageContact | [puppet.MessageContactRequest](#wechaty.puppet.MessageContactRequest) | [puppet.MessageContactResponse](#wechaty.puppet.MessageContactResponse) | | 67 | | MessageFileStream | [puppet.MessageFileStreamRequest](#wechaty.puppet.MessageFileStreamRequest) | [puppet.MessageFileStreamResponse](#wechaty.puppet.MessageFileStreamResponse) stream | | 68 | | MessageImageStream | [puppet.MessageImageStreamRequest](#wechaty.puppet.MessageImageStreamRequest) | [puppet.MessageImageStreamResponse](#wechaty.puppet.MessageImageStreamResponse) stream | | 69 | | MessageMiniProgram | [puppet.MessageMiniProgramRequest](#wechaty.puppet.MessageMiniProgramRequest) | [puppet.MessageMiniProgramResponse](#wechaty.puppet.MessageMiniProgramResponse) | | 70 | | MessageUrl | [puppet.MessageUrlRequest](#wechaty.puppet.MessageUrlRequest) | [puppet.MessageUrlResponse](#wechaty.puppet.MessageUrlResponse) | | 71 | | MessageRecall | [puppet.MessageRecallRequest](#wechaty.puppet.MessageRecallRequest) | [puppet.MessageRecallResponse](#wechaty.puppet.MessageRecallResponse) | | 72 | | MessageForward | [puppet.MessageForwardRequest](#wechaty.puppet.MessageForwardRequest) | [puppet.MessageForwardResponse](#wechaty.puppet.MessageForwardResponse) | | 73 | | MessageSendContact | [puppet.MessageSendContactRequest](#wechaty.puppet.MessageSendContactRequest) | [puppet.MessageSendContactResponse](#wechaty.puppet.MessageSendContactResponse) | | 74 | | MessageSendFileStream | [puppet.MessageSendFileStreamRequest](#wechaty.puppet.MessageSendFileStreamRequest) stream | [puppet.MessageSendFileStreamResponse](#wechaty.puppet.MessageSendFileStreamResponse) | | 75 | | MessageSendText | [puppet.MessageSendTextRequest](#wechaty.puppet.MessageSendTextRequest) | [puppet.MessageSendTextResponse](#wechaty.puppet.MessageSendTextResponse) | | 76 | | MessageSendMiniProgram | [puppet.MessageSendMiniProgramRequest](#wechaty.puppet.MessageSendMiniProgramRequest) | [puppet.MessageSendMiniProgramResponse](#wechaty.puppet.MessageSendMiniProgramResponse) | | 77 | | MessageSendUrl | [puppet.MessageSendUrlRequest](#wechaty.puppet.MessageSendUrlRequest) | [puppet.MessageSendUrlResponse](#wechaty.puppet.MessageSendUrlResponse) | | 78 | | RoomPayload | [puppet.RoomPayloadRequest](#wechaty.puppet.RoomPayloadRequest) | [puppet.RoomPayloadResponse](#wechaty.puppet.RoomPayloadResponse) | Room | 79 | | RoomList | [puppet.RoomListRequest](#wechaty.puppet.RoomListRequest) | [puppet.RoomListResponse](#wechaty.puppet.RoomListResponse) | | 80 | | RoomAdd | [puppet.RoomAddRequest](#wechaty.puppet.RoomAddRequest) | [puppet.RoomAddResponse](#wechaty.puppet.RoomAddResponse) | | 81 | | RoomAvatar | [puppet.RoomAvatarRequest](#wechaty.puppet.RoomAvatarRequest) | [puppet.RoomAvatarResponse](#wechaty.puppet.RoomAvatarResponse) | | 82 | | RoomCreate | [puppet.RoomCreateRequest](#wechaty.puppet.RoomCreateRequest) | [puppet.RoomCreateResponse](#wechaty.puppet.RoomCreateResponse) | | 83 | | RoomDel | [puppet.RoomDelRequest](#wechaty.puppet.RoomDelRequest) | [puppet.RoomDelResponse](#wechaty.puppet.RoomDelResponse) | | 84 | | RoomQuit | [puppet.RoomQuitRequest](#wechaty.puppet.RoomQuitRequest) | [puppet.RoomQuitResponse](#wechaty.puppet.RoomQuitResponse) | | 85 | | RoomTopic | [puppet.RoomTopicRequest](#wechaty.puppet.RoomTopicRequest) | [puppet.RoomTopicResponse](#wechaty.puppet.RoomTopicResponse) | | 86 | | RoomQRCode | [puppet.RoomQRCodeRequest](#wechaty.puppet.RoomQRCodeRequest) | [puppet.RoomQRCodeResponse](#wechaty.puppet.RoomQRCodeResponse) | | 87 | | RoomAnnounce | [puppet.RoomAnnounceRequest](#wechaty.puppet.RoomAnnounceRequest) | [puppet.RoomAnnounceResponse](#wechaty.puppet.RoomAnnounceResponse) | | 88 | | RoomMemberPayload | [puppet.RoomMemberPayloadRequest](#wechaty.puppet.RoomMemberPayloadRequest) | [puppet.RoomMemberPayloadResponse](#wechaty.puppet.RoomMemberPayloadResponse) | Room Member | 89 | | RoomMemberList | [puppet.RoomMemberListRequest](#wechaty.puppet.RoomMemberListRequest) | [puppet.RoomMemberListResponse](#wechaty.puppet.RoomMemberListResponse) | | 90 | | RoomInvitationPayload | [puppet.RoomInvitationPayloadRequest](#wechaty.puppet.RoomInvitationPayloadRequest) | [puppet.RoomInvitationPayloadResponse](#wechaty.puppet.RoomInvitationPayloadResponse) | Room Invitation | 91 | | RoomInvitationAccept | [puppet.RoomInvitationAcceptRequest](#wechaty.puppet.RoomInvitationAcceptRequest) | [puppet.RoomInvitationAcceptResponse](#wechaty.puppet.RoomInvitationAcceptResponse) | | 92 | | TagContactAdd | [puppet.TagContactAddRequest](#wechaty.puppet.TagContactAddRequest) | [puppet.TagContactAddResponse](#wechaty.puppet.TagContactAddResponse) | Tag | 93 | | TagContactRemove | [puppet.TagContactRemoveRequest](#wechaty.puppet.TagContactRemoveRequest) | [puppet.TagContactRemoveResponse](#wechaty.puppet.TagContactRemoveResponse) | | 94 | | TagContactDelete | [puppet.TagContactDeleteRequest](#wechaty.puppet.TagContactDeleteRequest) | [puppet.TagContactDeleteResponse](#wechaty.puppet.TagContactDeleteResponse) | Operate Sub-Collections https://cloud.google.com/apis/design/design_patterns#list_sub-collections | 95 | | TagContactList | [puppet.TagContactListRequest](#wechaty.puppet.TagContactListRequest) | [puppet.TagContactListResponse](#wechaty.puppet.TagContactListResponse) | | 96 | 97 | 98 | 99 | 100 | 101 | ## Scalar Value Types 102 | 103 | | .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | 104 | | ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | 105 | | double | | double | double | float | float64 | double | float | Float | 106 | | float | | float | float | float | float32 | float | float | Float | 107 | | int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | 108 | | int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | 109 | | uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | 110 | | uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | 111 | | sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | 112 | | sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | 113 | | fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | 114 | | fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | 115 | | sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | 116 | | sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | 117 | | bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | 118 | | string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | 119 | | bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | 120 | 121 | -------------------------------------------------------------------------------- /examples/auth/.gitignore: -------------------------------------------------------------------------------- 1 | # all generated openssl files 2 | *.crt 3 | *.key 4 | *.csr 5 | *.srl 6 | -------------------------------------------------------------------------------- /examples/auth/README.md: -------------------------------------------------------------------------------- 1 | # gRPC Authorization 2 | 3 | Use Wechaty TOKEN for authorization. 4 | 5 | ## DEVELOPMENT 6 | 7 | Enable debug trace messages: 8 | 9 | ```sh 10 | export GRPC_TRACE=all 11 | ``` 12 | 13 | ## GLOSSARY 14 | 15 | 1. Certificate Signing Requests (CSRs) 16 | 17 | ## RESOURCES 18 | 19 | - [TLS mutual authentication fails with Node.js grpc/grpc#6757](https://github.com/grpc/grpc/issues/6757#issuecomment-261703455) 20 | - [What is a Pem file and how does it differ from other OpenSSL Generated Key File Formats?](https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file) 21 | 22 | ## AUTHOR 23 | 24 | [Huan LI](https://github.com/huan) ([李卓桓](http://linkedin.com/in/zixia)), Google Developer Expert in Machine Learning (ML GDE), 25 | 26 | [![Profile of Huan LI (李卓桓) on StackOverflow](https://stackexchange.com/users/flair/265499.png)](https://stackexchange.com/users/265499) 27 | 28 | ## COPYRIGHT & LICENSES 29 | 30 | - Code & Docs © 2021-now Huan LI \ 31 | - Code released under the Apache-2.0 License 32 | - Docs released under Creative Commons 33 | -------------------------------------------------------------------------------- /examples/auth/client.ts: -------------------------------------------------------------------------------- 1 | import type { CallMetadataGenerator } from '@grpc/grpc-js/build/src/call-credentials' 2 | import fs from 'fs' 3 | 4 | import { 5 | grpc, 6 | puppet, 7 | } from '../../src/mod.js' 8 | 9 | import { promisify } from '../promisify.js' 10 | 11 | export async function testDing (client: puppet.PuppetClient) { 12 | const ding = promisify(client.ding.bind(client)) 13 | const dingRequest = new puppet.DingRequest() 14 | dingRequest.setData('dingdong') 15 | try { 16 | // const metadata = new Metadata() 17 | // metadata.set('grpc.default_authority', 'puppet_token') 18 | await ding(dingRequest/* metadata */) 19 | } catch (e) { 20 | console.error(e) 21 | } 22 | } 23 | 24 | async function main () { 25 | const TOKEN = '__token__' 26 | void TOKEN 27 | 28 | const rootCerts = fs.readFileSync('root-ca.crt') 29 | 30 | /** 31 | * With server authentication SSL/TLS and a custom header with token 32 | * https://grpc.io/docs/guides/auth/#with-server-authentication-ssltls-and-a-custom-header-with-token-1 33 | */ 34 | const metaCallback: CallMetadataGenerator = (_params, callback) => { 35 | const meta = new grpc.Metadata() 36 | // metadata.add('authorization', `Wechaty ${TOKEN}`) 37 | callback(null, meta) 38 | } 39 | 40 | const channelCred = grpc.credentials.createSsl(rootCerts) 41 | const callCred = grpc.credentials.createFromMetadataGenerator(metaCallback) 42 | const combCreds = grpc.credentials.combineChannelCredentials(channelCred, callCred) 43 | 44 | const client = new puppet.PuppetClient( 45 | 'localhost:8788', 46 | combCreds, 47 | { 48 | 'grpc.default_authority': '__token__', 49 | 'grpc.ssl_target_name_override': 'wechaty-puppet-service', 50 | }, 51 | ) 52 | 53 | await testDing(client) 54 | 55 | return 0 56 | } 57 | 58 | main() 59 | .catch(e => { 60 | console.error(e) 61 | process.exit(1) 62 | }) 63 | -------------------------------------------------------------------------------- /examples/auth/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Credit: https://github.com/grpc/grpc/issues/6757#issuecomment-261703455 4 | # 5 | set -e 6 | 7 | PASSPHRASE=wechaty 8 | DAYS=3650 9 | SNI=wechaty-puppet-service 10 | 11 | # CA 12 | openssl genrsa -passout pass:"$PASSPHRASE" -des3 -out root-ca.key 4096 13 | openssl req -passin pass:"$PASSPHRASE" -key root-ca.key -out root-ca.crt \ 14 | -x509 -new -days "$DAYS" \ 15 | -subj "/C=US/ST=San Francisco/L=Palo Alto/O=Wechaty/OU=CA/CN=wechaty-root-ca" 16 | 17 | # Server 18 | openssl genrsa -passout pass:"$PASSPHRASE" -des3 -out server.key 1024 19 | openssl req -passin pass:"$PASSPHRASE" -new -out server.csr -key server.key \ 20 | -subj "/C=US/ST=San Francisco/L=Palo Alto/O=Wechaty/OU=Puppet/CN=${SNI}" 21 | 22 | openssl x509 -req -passin pass:"$PASSPHRASE" -days "$DAYS" -set_serial 01 \ 23 | -CA root-ca.crt -CAkey root-ca.key \ 24 | -in server.csr -out server.crt 25 | openssl rsa -passin pass:"$PASSPHRASE" -in server.key -out server.key 26 | 27 | # Client 28 | # openssl genrsa -passout pass:"$PASSPHRASE" -des3 -out client.key 1024 29 | # openssl req -passin pass:"$PASSPHRASE" -new -key client.key -out client.csr \ 30 | # -subj "/C=US/ST=San Francisco/L=Palo Alto/O=Wechaty/OU=Client/CN=${SNI}" 31 | # openssl x509 -passin pass:"$PASSPHRASE" -req -days "$DAYS" -in client.csr -CA root-ca.crt -CAkey root-ca.key -set_serial 01 -out client.crt 32 | # openssl rsa -passin pass:"$PASSPHRASE" -in client.key -out client.key 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /examples/auth/raw-https/README.md: -------------------------------------------------------------------------------- 1 | # See 2 | 3 | - [Monkey patching tls in node.js to support self-signed certificates with custom root certificate authorities](https://medium.com/trabe/monkey-patching-tls-in-node-js-to-support-self-signed-certificates-with-custom-root-cas-25c7396dfd2a) 4 | -------------------------------------------------------------------------------- /examples/auth/raw-https/client.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import https from 'https' 3 | 4 | console.info('faint') 5 | 6 | https.request({ 7 | ca: [fs.readFileSync('./rootCA.crt')], 8 | hostname: '127.0.0.1', 9 | method: 'GET', 10 | path: '/', 11 | port: 6000, 12 | // rejectUnauthorized: false, 13 | }, res => { 14 | res.on('data', data => { 15 | process.stdout.write(data) 16 | }) 17 | }).end() 18 | -------------------------------------------------------------------------------- /examples/auth/raw-https/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Private key for the root cert 4 | openssl genrsa -des3 -out rootCA.key 4096 5 | 6 | # root certificate 7 | openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 365 -out rootCA.crt 8 | 9 | # Private key for the server cert 10 | openssl genrsa -out server.key 2048 11 | 12 | # Signing request for the server 13 | openssl req -new -key server.key -out server.csr 14 | 15 | # Server cert using the root certificate 16 | openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial \ 17 | -out server.crt -days 365 -sha256 18 | -------------------------------------------------------------------------------- /examples/auth/raw-https/server.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import https from 'https' 3 | 4 | const server = https.createServer({ 5 | cert: fs.readFileSync('./server.crt'), 6 | key: fs.readFileSync('./server.key'), 7 | // ca: fs.readFileSync('./rootCA.crt') 8 | }) 9 | 10 | server.on('request', (_req, res) => { 11 | res.writeHead(200) 12 | res.end('Alive!\n') 13 | }) 14 | 15 | server.listen(6000) 16 | -------------------------------------------------------------------------------- /examples/auth/server.ts: -------------------------------------------------------------------------------- 1 | import util from 'util' 2 | import fs from 'fs' 3 | 4 | import { Status as GrpcServerStatus } from '@grpc/grpc-js/build/src/constants' 5 | 6 | import { 7 | grpc, 8 | puppet, 9 | } from '../../src/mod' 10 | 11 | import { 12 | puppetServerImpl, 13 | } from '../../tests/puppet-server-impl.js' 14 | import { 15 | StatusBuilder, 16 | Metadata, 17 | UntypedHandleCall, 18 | } from '@grpc/grpc-js' 19 | // import { Http2SecureServer } from 'http2' 20 | import type { 21 | sendUnaryData, 22 | ServerUnaryCall, 23 | } from '@grpc/grpc-js/build/src/server-call' 24 | 25 | import type http2 from 'http2' 26 | 27 | function monkeyPatchMetadataFromHttp2Headers ( 28 | MetadataClass: typeof Metadata, 29 | ): void { 30 | const fromHttp2Headers = MetadataClass.fromHttp2Headers 31 | MetadataClass.fromHttp2Headers = function ( 32 | headers: http2.IncomingHttpHeaders 33 | ): Metadata { 34 | const metadata = fromHttp2Headers.call(MetadataClass, headers) 35 | 36 | if (metadata.get('authorization').length <= 0) { 37 | const authority = headers[':authority'] 38 | const authorization = `Wechaty ${authority}` 39 | metadata.set('authorization', authorization) 40 | } 41 | return metadata 42 | } 43 | } 44 | 45 | /** 46 | * The following handlers using `cb` for errors 47 | * handleUnaryCall 48 | * handleClientStreamingCall 49 | */ 50 | type ServiceHandlerCb = (call: ServerUnaryCall, cb: sendUnaryData) => void 51 | /** 52 | * The following handlers using `emit` for errors 53 | * handleServerStreamingCall 54 | * handleBidiStreamingCall 55 | */ 56 | type ServiceHandlerEmit = (call: ServerUnaryCall) => void 57 | type ServiceHandler = ServiceHandlerCb | ServiceHandlerEmit 58 | 59 | /** 60 | * Huan(202108): wrap the following handle calls with authorization: 61 | * - handleUnaryCall 62 | * - handleClientStreamingCall 63 | * - handleServerStreamingCall 64 | * - handleBidiStreamingCall 65 | * 66 | * See: 67 | * https://grpc.io/docs/guides/auth/#with-server-authentication-ssltls-and-a-custom-header-with-token 68 | */ 69 | function authHandler ( 70 | validToken : string, 71 | handler : UntypedHandleCall, 72 | ): ServiceHandler { 73 | console.info('wrapAuthHandler', handler.name) 74 | return function ( 75 | call: ServerUnaryCall, 76 | cb?: sendUnaryData, 77 | ) { 78 | // console.info('wrapAuthHandler internal') 79 | 80 | const authorization = call.metadata.get('authorization')[0] 81 | // console.info('authorization', authorization) 82 | 83 | let errMsg = '' 84 | if (typeof authorization === 'string') { 85 | if (authorization.startsWith('Wechaty ')) { 86 | const token = authorization.substring(8 /* 'Wechaty '.length */) 87 | if (token === validToken) { 88 | 89 | return handler( 90 | call as any, 91 | cb as any, 92 | ) 93 | 94 | } else { 95 | errMsg = `Invalid Wechaty TOKEN "${token}"` 96 | } 97 | } else { 98 | const type = authorization.split(/\s+/)[0] 99 | errMsg = `Invalid authorization type: "${type}"` 100 | } 101 | } else { 102 | errMsg = 'No Authorization found.' 103 | } 104 | 105 | /** 106 | * Not authorized 107 | */ 108 | const error = new StatusBuilder() 109 | .withCode(GrpcServerStatus.UNAUTHENTICATED) 110 | .withDetails(errMsg) 111 | .withMetadata(call.metadata) 112 | .build() 113 | 114 | if (cb) { 115 | cb(error) 116 | } else if ('emit' in call) { 117 | call.emit('error', error) 118 | } else { 119 | throw new Error('no callback and call is not emit-able') 120 | } 121 | } 122 | } 123 | 124 | const wechatyAuthToken = (validToken: string) => ( 125 | puppetServer: puppet.IPuppetServer, 126 | ) => { 127 | for (const [key, val] of Object.entries(puppetServer)) { 128 | puppetServer[key] = authHandler(validToken, val) 129 | } 130 | return puppetServer 131 | } 132 | 133 | monkeyPatchMetadataFromHttp2Headers(Metadata) 134 | 135 | const puppetServerExample: puppet.IPuppetServer = { 136 | ...puppetServerImpl, 137 | 138 | ding: (call, callback) => { 139 | const data = call.request.getData() 140 | console.info(`ding(${data})`) 141 | console.info('authorization:', call.metadata.getMap()['authorization']) 142 | callback(null, new puppet.DingResponse()) 143 | }, 144 | } 145 | 146 | async function main () { 147 | const puppetServerExampleWithAuth = wechatyAuthToken('__token__')(puppetServerExample) 148 | 149 | const server = new grpc.Server() 150 | server.addService( 151 | puppet.PuppetService, 152 | puppetServerExampleWithAuth, 153 | ) 154 | 155 | const serverBindPromise = util.promisify( 156 | server.bindAsync.bind(server) 157 | ) 158 | 159 | void fs 160 | 161 | const rootCerts: null | Buffer = fs.readFileSync('root-ca.crt') 162 | void rootCerts 163 | const keyCertPairs: grpc.KeyCertPair[] = [{ 164 | cert_chain : fs.readFileSync('server.crt'), 165 | private_key : fs.readFileSync('server.key'), 166 | }] 167 | // const checkClientCertificate = false 168 | 169 | const port = await serverBindPromise( 170 | '0.0.0.0:8788', 171 | // grpc.ServerCredentials.createInsecure(), 172 | grpc.ServerCredentials.createSsl(null, keyCertPairs) //, checkClientCertificate), 173 | ) 174 | console.info('Listen on port:', port) 175 | server.start() 176 | return 0 177 | } 178 | 179 | main() 180 | .catch(console.error) 181 | -------------------------------------------------------------------------------- /examples/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """doc""" 3 | import asyncio 4 | import time 5 | 6 | from grpclib.client import Channel 7 | from pyee import AsyncIOEventEmitter 8 | from wechaty_grpc.wechaty import ( 9 | PuppetStub, 10 | ) 11 | from wechaty_grpc.wechaty.puppet import ( 12 | EventType, 13 | ) 14 | 15 | 16 | async def init_event_stream ( 17 | event_stream : AsyncIOEventEmitter, 18 | puppet : PuppetStub, 19 | ) -> None: 20 | """doc""" 21 | async for response in puppet.event(): 22 | # print(response) 23 | if response is not None: 24 | event_type = EventType(response.type).name 25 | payload = response.payload 26 | # print(event_type, payload) 27 | event_stream.emit(event_type, payload) 28 | 29 | 30 | async def loop(callback): 31 | """doc""" 32 | while True: 33 | await callback() 34 | time.sleep(1) 35 | 36 | 37 | async def main(): 38 | """doc""" 39 | channel = Channel(host="127.0.0.1", port=8788) 40 | puppet = PuppetStub(channel) 41 | 42 | event_stream = AsyncIOEventEmitter() 43 | event_stream.on('EVENT_TYPE_DONG', \ 44 | lambda payload: print('on(dong) %s' % payload)) 45 | 46 | await asyncio.gather( 47 | loop(lambda: puppet.ding(data='haha')), 48 | init_event_stream(event_stream, puppet), 49 | ) 50 | 51 | channel.close() 52 | 53 | 54 | if __name__ == "__main__": 55 | asyncio.run(main()) 56 | -------------------------------------------------------------------------------- /examples/client.ts: -------------------------------------------------------------------------------- 1 | // import { Metadata } from '@grpc/grpc-js' 2 | // import { CallMetadataGenerator } from '@grpc/grpc-js/build/src/call-credentials' 3 | 4 | import { 5 | grpc, 6 | puppet, 7 | } from '../src/mod.js' 8 | 9 | import { promisify } from './promisify.js' 10 | 11 | export async function testAlias (client: puppet.PuppetClient) { 12 | const request = new puppet.ContactAliasRequest() 13 | 14 | const contactAlias = promisify(client.contactAlias.bind(client)) 15 | 16 | { 17 | const response = await contactAlias(request) 18 | const alias = response.getAlias() 19 | console.info('returned alias:', alias) 20 | } 21 | 22 | console.info('##############') 23 | 24 | { 25 | request.setAlias('test alias') 26 | const response = await contactAlias(request) 27 | 28 | const returnAliasWrapper = response.getAlias() 29 | if (returnAliasWrapper) { 30 | console.info('returned alias:', returnAliasWrapper) 31 | throw new Error('should not has alas return') 32 | } 33 | 34 | console.info('ok') 35 | } 36 | } 37 | 38 | export async function testDing (client: puppet.PuppetClient) { 39 | const ding = promisify(client.ding.bind(client)) 40 | const dingRequest = new puppet.DingRequest() 41 | dingRequest.setData('dingdong') 42 | try { 43 | // const metadata = new Metadata() 44 | // metadata.set('grpc.default_authority', 'puppet_token') 45 | await ding(dingRequest/* metadata */) 46 | } catch (e) { 47 | console.error(e) 48 | } 49 | } 50 | 51 | export function testStream (client: puppet.PuppetClient) { 52 | // event(request: wechaty_puppet_event_pb.EventRequest, options?: Partial): grpc.ClientReadableStream; 53 | const eventStream = client.event(new puppet.EventRequest()) 54 | eventStream 55 | .on('data', (chunk: puppet.EventResponse) => { 56 | // console.info('EventType:', EventType) 57 | // console.info('type:', chunk.getType(), EventType[chunk.getType()], EventType[23]) 58 | console.info('payload:', chunk.getPayload()) 59 | // console.info('eventStream.on(data):', chunk) 60 | }) 61 | .on('end', () => { 62 | console.info('eventStream.on(end)') 63 | }) 64 | } 65 | 66 | async function main () { 67 | // const metadata = new grpc.Metadata() 68 | // metadata.add('authorization', 'Bearer ' + 'access_token') 69 | // const generateMetadata: CallMetadataGenerator = (_params, callback) => { console.info('generateMetadata'); callback(null, metadata) } 70 | 71 | // const authCred = grpc.credentials.createFromMetadataGenerator(generateMetadata) 72 | // const sslCred = grpc.credentials.createSsl() 73 | 74 | // const creds = grpc.credentials.combineChannelCredentials( 75 | // sslCred, 76 | // authCred, 77 | // ) 78 | const creds = grpc.credentials.createInsecure() 79 | 80 | const client = new puppet.PuppetClient( 81 | 'localhost:8788', 82 | creds, 83 | { 84 | 'grpc.default_authority': 'puppet_token', 85 | }, 86 | ) 87 | 88 | testStream(client) 89 | setInterval(() => testDing(client), 1000) 90 | // await testAlias(client) 91 | 92 | return 0 93 | } 94 | 95 | main() 96 | // .then(process.exit) 97 | .catch(e => { 98 | console.error(e) 99 | process.exit(1) 100 | }) 101 | -------------------------------------------------------------------------------- /examples/promisify.ts: -------------------------------------------------------------------------------- 1 | import { promisify } from 'util' 2 | 3 | /** 4 | * Issue #7: https://github.com/wechaty/grpc/issues/7 5 | */ 6 | export type Callback = (err: E | null, reply: T) => void 7 | 8 | export type PromisifyOne = 9 | T extends [Callback?] ? () => Promise : 10 | T extends [infer T1, Callback?] ? (arg1: T1) => Promise

: 11 | T extends [infer T1, infer T2, Callback?] ? (arg1: T1, arg2: T2) => Promise : 12 | T extends [infer T1, infer T2, infer T3, Callback?]? (arg1: T1, arg2: T2, arg3: T3) => Promise : 13 | T extends [infer T1, infer T2, infer T3, infer T4, Callback?] ? (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise : 14 | never 15 | 16 | // prettier-ignore 17 | export type GetOverloadArgs = 18 | T extends { 19 | (...o: infer U) : void, 20 | (...o: infer U2) : void, 21 | (...o: infer U3) : void, 22 | (...o: infer U4) : void, 23 | (...o: infer U5) : void, 24 | (...o: infer U6) : void, 25 | (...o: infer U7) : void 26 | } ? U | U2 | U3 | U4 | U5 | U6 | U7: 27 | T extends { 28 | (...o: infer U) : void, 29 | (...o: infer U2) : void, 30 | (...o: infer U3) : void, 31 | (...o: infer U4) : void, 32 | (...o: infer U5) : void, 33 | (...o: infer U6) : void, 34 | } ? U | U2 | U3 | U4 | U5 | U6: 35 | T extends { 36 | (...o: infer U) : void, 37 | (...o: infer U2) : void, 38 | (...o: infer U3) : void, 39 | (...o: infer U4) : void, 40 | (...o: infer U5) : void, 41 | } ? U | U2 | U3 | U4 | U5: 42 | T extends { 43 | (...o: infer U) : void, 44 | (...o: infer U2) : void, 45 | (...o: infer U3) : void, 46 | (...o: infer U4) : void, 47 | } ? U | U2 | U3 | U4 : 48 | T extends { 49 | (...o: infer U) : void, 50 | (...o: infer U2) : void, 51 | (...o: infer U3) : void, 52 | } ? U | U2 | U3 : 53 | T extends { 54 | (...o: infer U) : void, 55 | (...o: infer U2) : void, 56 | } ? U | U2 : 57 | T extends { 58 | (...o: infer U) : void, 59 | } ? U : 60 | never 61 | 62 | export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never 63 | 64 | export type Promisify = UnionToIntersection< 65 | PromisifyOne> 66 | > 67 | 68 | declare module 'util' { 69 | function promisify (fn: T): Promisify 70 | } 71 | 72 | export { promisify } 73 | -------------------------------------------------------------------------------- /examples/server.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable sort-keys */ 2 | import util from 'util' 3 | 4 | import { 5 | grpc, 6 | puppet, 7 | } from '../src/mod.js' 8 | 9 | import { 10 | puppetServerImpl, 11 | } from '../tests/puppet-server-impl.js' 12 | 13 | let eventStream: undefined | grpc.ServerWritableStream 14 | 15 | /** 16 | * Huan(202003): gRPC Wait for Ready Semantics 17 | * https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md 18 | */ 19 | const dingQueue = [] as string[] 20 | 21 | /** 22 | * Implements the SayHello RPC method. 23 | */ 24 | const puppetServerExample: puppet.IPuppetServer = { 25 | ...puppetServerImpl, 26 | 27 | event: (streammingCall) => { 28 | console.info('event(streamingCall)') 29 | 30 | if (eventStream) { 31 | console.info('event() end old eventStream to accept the new one.') 32 | eventStream.end() 33 | eventStream = streammingCall 34 | } 35 | 36 | eventStream = streammingCall 37 | while (dingQueue.length > 0) { 38 | const data = dingQueue.shift() 39 | const eventResponse = new puppet.EventResponse() 40 | eventResponse.setType(puppet.EventType.EVENT_TYPE_DONG) 41 | eventResponse.setPayload(data!) 42 | eventStream.write(eventResponse) 43 | } 44 | /** 45 | * Detect if Inexor Core is gone (GRPC disconnects) 46 | * https://github.com/grpc/grpc/issues/8117#issuecomment-362198092 47 | */ 48 | eventStream.on('cancelled', () => { 49 | console.info('eventStream.on(calcelled)') 50 | eventStream?.end() 51 | eventStream = undefined 52 | }) 53 | }, 54 | 55 | ding: (call, callback) => { 56 | const data = call.request.getData() 57 | console.info(`ding(${data})`) 58 | console.info('metadata:', call.metadata.getMap()) 59 | console.info('getPeer:', call.getPeer()) 60 | console.info('getDeadLine:', call.getDeadline()) 61 | // console.info('getDeadLine:', call.) 62 | if (!eventStream) { 63 | dingQueue.push(data) 64 | } else { 65 | const eventResponse = new puppet.EventResponse() 66 | eventResponse.setType(puppet.EventType.EVENT_TYPE_DONG) 67 | eventResponse.setPayload(data) 68 | eventStream.write(eventResponse) 69 | } 70 | 71 | callback(null, new puppet.DingResponse()) 72 | }, 73 | } 74 | 75 | /** 76 | * Starts an RPC server that receives requests for the Greeter service at the 77 | * sample server port 78 | */ 79 | async function main () { 80 | const server = new grpc.Server() 81 | server.addService( 82 | puppet.PuppetService, 83 | puppetServerExample, 84 | ) 85 | const serverBindPromise = util.promisify( 86 | server.bindAsync.bind(server) 87 | ) 88 | 89 | const port = await serverBindPromise( 90 | '127.0.0.1:8788', 91 | grpc.ServerCredentials.createInsecure(), 92 | ) 93 | console.info('Listen on port:', port) 94 | server.start() 95 | return 0 96 | } 97 | 98 | main() 99 | .catch(e => { 100 | console.error(e) 101 | process.exit(1) 102 | }) 103 | -------------------------------------------------------------------------------- /openapi/.gitignore: -------------------------------------------------------------------------------- 1 | go.sum 2 | -------------------------------------------------------------------------------- /openapi/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: generate 3 | 4 | .PHONY: generate 5 | generate: 6 | ./generate.sh 7 | 8 | .PHONY: clean 9 | clean: 10 | echo clean 11 | 12 | .PHONY: install 13 | install: 14 | ./install.sh 15 | -------------------------------------------------------------------------------- /openapi/README.md: -------------------------------------------------------------------------------- 1 | # GO gRPC Gateway with OpenAPI Specification 2 | 3 | - gRPC Gateway: 4 | - Template: 5 | - Google APIs Proto Files: Node.js Client: 6 | -------------------------------------------------------------------------------- /openapi/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | # https://stackoverflow.com/a/4774063/1123955 7 | WORK_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 8 | REPO_DIR="$( cd "$WORK_DIR/../" >/dev/null 2>&1 ; pwd -P )" 9 | 10 | OUT_DIR="$REPO_DIR/out/" 11 | [ -d "$OUT_DIR" ] || mkdir -p $OUT_DIR 12 | 13 | PACKAGE_JSON_FILE="$REPO_DIR/package.json" 14 | SWAGGER_JSON_FILE="$OUT_DIR/wechaty/puppet.swagger.json" 15 | OPENAPI_YAML_FILE="${OUT_DIR}/wechaty/puppet.openapi.yaml" 16 | 17 | function check_package_json () { 18 | if [ ! -f "$PACKAGE_JSON_FILE" ]; then 19 | echo "$PACKAGE_JSON_FILE not found" 20 | exit 1 21 | fi 22 | } 23 | 24 | function generate_swagger () { 25 | PROTOC="protoc \ 26 | -I $REPO_DIR/proto/ \ 27 | -I $REPO_DIR/third-party/ \ 28 | $REPO_DIR/proto/wechaty/puppet.proto \ 29 | " 30 | 31 | ${PROTOC} \ 32 | --openapiv2_out ${OUT_DIR} \ 33 | --openapiv2_opt logtostderr=true \ 34 | --openapiv2_opt generate_unbound_methods=true 35 | } 36 | 37 | function update_version () { 38 | [ -f "$SWAGGER_JSON_FILE" ] || { 39 | echo "File not found: $SWAGGER_JSON_FILE" 40 | exit 1 41 | } 42 | 43 | grpcVersion=$(jq -r .version ${PACKAGE_JSON_FILE}) 44 | tmpFile="/tmp/openapi.$$" 45 | cat "$SWAGGER_JSON_FILE" \ 46 | | jq --arg version "$grpcVersion" \ 47 | '.info.version = $version' \ 48 | > "$tmpFile" 49 | mv "$tmpFile" "$SWAGGER_JSON_FILE" 50 | 51 | echo "Swagger version synced with gRPC version $grpcVersion for $SWAGGER_JSON_FILE" 52 | } 53 | 54 | function generate_yaml () { 55 | [ -e "$SWAGGER_JSON_FILE" ] || { 56 | echo "File not found: $SWAGGER_JSON_FILE" 57 | } 58 | 59 | npx js-yaml < "$SWAGGER_JSON_FILE" > "$OPENAPI_YAML_FILE" 60 | } 61 | 62 | function main () { 63 | check_package_json 64 | generate_swagger 65 | update_version 66 | generate_yaml 67 | } 68 | 69 | main 70 | -------------------------------------------------------------------------------- /openapi/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wechaty/grpc/openapi 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 // indirect 7 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 // indirect 8 | google.golang.org/protobuf v1.27.1 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /openapi/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # https://stackoverflow.com/a/4774063/1123955 6 | SCRIPTPATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 7 | THIRD_PARTY_DIR="${SCRIPTPATH}/../third-party/" 8 | 9 | function go_install () { 10 | go mod download github.com/grpc-ecosystem/grpc-gateway@latest 11 | # go mod download google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest 12 | 13 | go install \ 14 | google.golang.org/protobuf/cmd/protoc-gen-go@latest 15 | 16 | go install \ 17 | google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest 18 | 19 | # 20 | # Huan(202108): https://github.com/golang/go/issues/44129 21 | # workaround: `go get ...` first. 22 | # 23 | go install \ 24 | github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest 25 | 26 | go install \ 27 | github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest 28 | 29 | } 30 | 31 | function install_proto_protoc_gen_openapiv2 () { 32 | if [ -d ${THIRD_PARTY_DIR}/protoc-gen-openapiv2/options ]; then 33 | echo "install skipped: ${THIRD_PARTY_DIR}/protoc-gen-openapiv2/options exists" 34 | return 35 | fi 36 | 37 | mkdir -p ${THIRD_PARTY_DIR}/protoc-gen-openapiv2/options 38 | curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-openapiv2/options/annotations.proto > ${THIRD_PARTY_DIR}/protoc-gen-openapiv2/options/annotations.proto 39 | curl https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-openapiv2/options/openapiv2.proto > ${THIRD_PARTY_DIR}/protoc-gen-openapiv2/options/openapiv2.proto 40 | } 41 | 42 | main () { 43 | go_install 44 | install_proto_protoc_gen_openapiv2 45 | } 46 | 47 | main 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechaty-grpc", 3 | "version": "1.5.4", 4 | "description": "gRPC for Wechaty", 5 | "type": "module", 6 | "exports": { 7 | ".": { 8 | "import": "./dist/esm/src/mod.js", 9 | "require": "./dist/cjs/src/mod.js" 10 | } 11 | }, 12 | "typings": "./dist/esm/src/mod.d.ts", 13 | "engines": { 14 | "node": ">=16", 15 | "npm": ">=7" 16 | }, 17 | "scripts": { 18 | "clean": "shx rm -fr dist/* out/*", 19 | "dist": "npm-run-all clean generate build dist:copy dist:commonjs", 20 | "build": "tsc && tsc -p tsconfig.cjs.json", 21 | "dist:commonjs": "jq -n \"{ type: \\\"commonjs\\\" }\" > dist/cjs/package.json", 22 | "dist:copy": "npm-run-all copy:esm copy:cjs", 23 | "copy:esm": "shx cp -R out/ dist/esm && shx cp -R proto/ dist/esm && shx cp -R commonjs/ dist/esm/", 24 | "copy:cjs": "shx cp -R out/ dist/cjs && shx cp -R proto/ dist/cjs && shx cp -R commonjs/ dist/cjs/", 25 | "dist:py": "python3 setup.py sdist bdist_wheel", 26 | "publish:py": "twine upload dist/*", 27 | "example:server": "nodemon --exec ts-node examples/server.ts", 28 | "example:client": "ts-node examples/client.ts", 29 | "generate": "bash -x scripts/generate-stub.sh", 30 | "lint": "npm-run-all lint:es lint:ts lint:proto", 31 | "lint:es": "eslint --ignore-pattern fixtures/ \"src/**/*.ts\" \"tests/**/*.ts\" \"examples/**/*.ts\"", 32 | "lint:ts": "tsc --isolatedModules --noEmit", 33 | "lint:proto": "echo \"SKIP proto lint due to not support options\" || bash -c 'protoc -I third-party -I proto --lint_out=sort_imports:. $(find proto/ -type f -name *.proto)'", 34 | "install:protoc": "bash -x scripts/install-protoc.sh", 35 | "test": "npm-run-all lint test:unit test:commonjs", 36 | "test:pack": "bash -x scripts/npm-pack-testing.sh", 37 | "test:unit": "cross-env NODE_OPTIONS=\"--no-warnings --loader=ts-node/esm\" tap \"src/**/*.spec.ts\" \"tests/**/*.spec.ts\"", 38 | "test:commonjs": "cross-env TS_NODE_PROJECT=tsconfig.cjs.json NODE_OPTIONS=--require=ts-node/register tap \"commonjs/**/*.spec.ts\"" 39 | }, 40 | "repository": { 41 | "type": "git", 42 | "url": "git+https://github.com/wechaty/grpc.git" 43 | }, 44 | "keywords": [ 45 | "grpc", 46 | "chatie", 47 | "chatbot", 48 | "hostie", 49 | "puppet", 50 | "wechaty" 51 | ], 52 | "author": "Huan LI ", 53 | "license": "Apache-2.0", 54 | "bugs": { 55 | "url": "https://github.com/wechaty/grpc/issues" 56 | }, 57 | "homepage": "https://github.com/wechaty/grpc#readme", 58 | "dependencies": { 59 | "@grpc/grpc-js": "^1.3.7", 60 | "google-protobuf": "^3.18.0", 61 | "stronger-typed-streams": "^0.2.0" 62 | }, 63 | "devDependencies": { 64 | "@chatie/eslint-config": "^0.14.1", 65 | "@chatie/git-scripts": "^0.6.2", 66 | "@chatie/semver": "^0.4.7", 67 | "@chatie/tsconfig": "^0.20.2", 68 | "@types/google-protobuf": "^3.15.5", 69 | "@types/protobufjs": "^6.0.0", 70 | "cross-env": "^7.0.3", 71 | "grpc_tools_node_protoc_ts": "^5.3.2", 72 | "grpc-tools": "^1.11.2", 73 | "grpcc": "^1.1.3", 74 | "nodemon": "^2.0.12", 75 | "npm-run-all": "^4.1.5", 76 | "request": "^2.88.2", 77 | "shx": "^0.3.3", 78 | "ts-protoc-gen": "^0.15.0", 79 | "tstest": "^0.7.3" 80 | }, 81 | "files": [ 82 | "bin/", 83 | "dist/", 84 | "out/", 85 | "src/" 86 | ], 87 | "publishConfig": { 88 | "access": "public", 89 | "tag": "next" 90 | }, 91 | "tap": { 92 | "check-coverage": false 93 | }, 94 | "git": { 95 | "scripts": { 96 | "pre-push": "npx git-scripts-pre-push" 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb 341 | /proto/wechaty 342 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 生成步骤: 4 | 1、执行 buildTool目录下的`./copy.ps1` powshell 文件 5 | 2、dotnet build 生成Nuget包 6 | 7 | # 问题 8 | ## 【gRPC使用问题3】生成出来无法识别Google.Api.AnnotationsReflection.Descriptor 9 | 处理方案:Install the package "Google.Api.Gax.Grpc" 10 | 11 | ## 找不到 `protoc-gen-openapiv2/options/annotations.proto` proto文件 12 | 去对应的目录下拷贝对应的proto文件到项目中 https://github.com/grpc-ecosystem/grpc-gateway/tree/master/protoc-gen-openapiv2/options 13 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/Wechaty.Grpc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | True 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | 35 | proto\ 36 | 37 | 38 | proto\ 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/Wechaty.Grpc.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32319.34 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wechaty.Grpc", "Wechaty.Grpc.csproj", "{538E3020-E50F-4820-B6DF-086E5A78D0CF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {538E3020-E50F-4820-B6DF-086E5A78D0CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {538E3020-E50F-4820-B6DF-086E5A78D0CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {538E3020-E50F-4820-B6DF-086E5A78D0CF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {538E3020-E50F-4820-B6DF-086E5A78D0CF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B748CF40-C440-4F85-8EF8-8483A7244284} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/buildTool/common.ps1: -------------------------------------------------------------------------------- 1 | # Paths 2 | 3 | # csharp build Folder 4 | $buildFolder = (Get-Item -Path "./" -Verbose).FullName 5 | 6 | # wechaty root folder 7 | $rootFolder = Join-Path $buildFolder "../../../../" 8 | 9 | # wechaty proto folder 10 | $wechatyProtoFolder=Join-Path $rootFolder "proto/wechaty" 11 | 12 | # chsarp solution Folder 13 | $solutionFolder = Join-Path $rootFolder "polyglot/csharp/Wechaty.Grpc" 14 | 15 | # csharp wechaty proto folder 16 | $csharpProtoFolder=Join-Path $solutionFolder "proto" 17 | 18 | $csharpWechatyProtoFolder = Join-Path $rootFolder "polyglot/csharp/Wechaty.Grpc/proto/wechaty" 19 | 20 | # get package.json data 21 | $packageData=Get-Content (Join-Path $rootFolder "package.json") | ConvertFrom-Json 22 | 23 | # get package.json version 24 | $version=($packageData | Select-Object -Property version).version 25 | 26 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/buildTool/copy.ps1: -------------------------------------------------------------------------------- 1 | . ".\common.ps1" 2 | 3 | 4 | # Get-ChildItem $projectFolder | ?{$_.psiscontainer -eq $false} 5 | 6 | Set-Location $buildFolder 7 | 8 | # if csharp proto folder is exists, delete if exists 9 | 10 | if(Test-Path $csharpWechatyProtoFolder) 11 | { 12 | Remove-Item -Path $csharpWechatyProtoFolder -Recurse -Force 13 | } 14 | 15 | # copy wechaty proto folder to csharp proto folder 16 | Copy-Item $wechatyProtoFolder $csharpWechatyProtoFolder -Recurse 17 | 18 | $puppetFilePath = Join-Path $csharpProtoFolder "wechaty/puppet.proto" 19 | # get puppet.proto content 20 | $file = Get-Content $puppetFilePath 21 | 22 | # update import as csharp catalog 23 | $file.Replace("import public `"puppet/","import public `"proto/wechaty/puppet/") | set-Content $puppetFilePath 24 | 25 | $puppetChildPath = Join-Path $csharpProtoFolder "wechaty/puppet" 26 | Get-ChildItem $puppetChildPath| ForEach-Object -Process{ 27 | if($_ -is [System.IO.FileInfo]) 28 | { 29 | Write-Host($_.fullname); 30 | 31 | $childfile = Get-Content $_.fullname 32 | $childfile.Replace("import `"puppet/","import `"proto/wechaty/puppet/") | set-Content $_.fullname 33 | } 34 | } 35 | 36 | # update common.props version as package.json version 37 | $commonPropPath = Join-Path $solutionFolder "common.props" 38 | $commonPropsData = Get-Content $commonPropPath 39 | 40 | $versionNode="" + $version + "" 41 | echo $versionNode 42 | $commonPropsData -Replace "(.*)" , $versionNode | set-Content $commonPropPath 43 | 44 | Set-Location $buildFolder 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | latest 5 | 0.20.4 6 | Wechaty 7 | Wechaty 8 | .NET Wechaty Grpc 9 | $(NoWarn);CS1591 10 | wechaty.png 11 | https://github.com/wechaty/grpc 12 | Apache-2.0 13 | git 14 | https://github.com/wechaty/grpc 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/proto/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package grpc.gateway.protoc_gen_openapiv2.options; 4 | 5 | option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"; 6 | 7 | import "google/protobuf/descriptor.proto"; 8 | import "protoc-gen-openapiv2/options/openapiv2.proto"; 9 | 10 | extend google.protobuf.FileOptions { 11 | // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. 12 | // 13 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 14 | // different descriptor messages. 15 | Swagger openapiv2_swagger = 1042; 16 | } 17 | extend google.protobuf.MethodOptions { 18 | // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. 19 | // 20 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 21 | // different descriptor messages. 22 | Operation openapiv2_operation = 1042; 23 | } 24 | extend google.protobuf.MessageOptions { 25 | // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. 26 | // 27 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 28 | // different descriptor messages. 29 | Schema openapiv2_schema = 1042; 30 | } 31 | extend google.protobuf.ServiceOptions { 32 | // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. 33 | // 34 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 35 | // different descriptor messages. 36 | Tag openapiv2_tag = 1042; 37 | } 38 | extend google.protobuf.FieldOptions { 39 | // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. 40 | // 41 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 42 | // different descriptor messages. 43 | JSONSchema openapiv2_field = 1042; 44 | } 45 | -------------------------------------------------------------------------------- /polyglot/csharp/Wechaty.Grpc/wechaty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wechaty/grpc/f1ecd6c6327c82cd372fd867c2846835e9552fd1/polyglot/csharp/Wechaty.Grpc/wechaty.png -------------------------------------------------------------------------------- /polyglot/go/.gitignore: -------------------------------------------------------------------------------- 1 | wechaty-go-grpc.*/ 2 | -------------------------------------------------------------------------------- /polyglot/go/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Python Wechaty 2 | # 3 | # GitHb: https://github.com/wechaty/python-wechaty 4 | # Author: Huan LI git.io/zixia 5 | # 6 | 7 | .PHONY: all 8 | all : clean lint 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -fr out/* 13 | 14 | .PHONY: install 15 | install: 16 | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest 17 | bash -x ../../scripts/install-protoc.sh 18 | 19 | .PHONY: test 20 | test: 21 | go test ./... 22 | 23 | .PHONY: generate 24 | generate: 25 | bash -x ./generate.sh 26 | 27 | .PHONY: publish 28 | publish: clean generate 29 | ./publish.sh 30 | 31 | .PHONY: build_test 32 | build_test: 33 | ./build_test.sh 34 | -------------------------------------------------------------------------------- /polyglot/go/build_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | OUT_DIR="$(pwd)/out" 6 | 7 | TEST_DIR="wechaty-go-grpc-test.$$" 8 | 9 | mkdir "$TEST_DIR" 10 | pushd "$TEST_DIR" 11 | 12 | git clone https://github.com/wechaty/go-grpc.git 13 | cd go-grpc 14 | cp -Rav "$OUT_DIR"/wechaty . 15 | 16 | # build test 17 | go get ./... 18 | go build -v wechaty/puppet.pb.go 19 | -------------------------------------------------------------------------------- /polyglot/go/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # Huan(202110): enable `**/*.proto` to include all files 6 | # https://stackoverflow.com/a/28199633/1123955 7 | shopt -s globstar 8 | 9 | # https://stackoverflow.com/a/4774063/1123955 10 | WORK_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 11 | REPO_DIR="$( cd "$WORK_DIR/../../" >/dev/null 2>&1 ; pwd -P )" 12 | 13 | OUT_DIR="$WORK_DIR/out/" 14 | [ -d "$OUT_DIR" ] || mkdir -p $OUT_DIR 15 | 16 | protoc --version 17 | 18 | protoc \ 19 | -I "$REPO_DIR/proto" \ 20 | -I "$REPO_DIR/third-party" \ 21 | --go_out=$OUT_DIR \ 22 | --go_opt=paths=source_relative \ 23 | --go-grpc_out=$OUT_DIR \ 24 | --go-grpc_opt=paths=source_relative \ 25 | "$REPO_DIR/proto/wechaty"/**/*.proto \ 26 | "$REPO_DIR/third-party"/**/*.proto 27 | 28 | cp $OUT_DIR/wechaty/deprecated/* $OUT_DIR/wechaty/puppet/ 29 | rm -rf $OUT_DIR/wechaty/deprecated/* 30 | -------------------------------------------------------------------------------- /polyglot/go/grpc_test.go: -------------------------------------------------------------------------------- 1 | package _go 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/wechaty/go-grpc/wechaty" 7 | ) 8 | 9 | func TestProto(t *testing.T) { 10 | var _ wechaty.PuppetClient = wechaty.NewPuppetClient(nil) 11 | } 12 | -------------------------------------------------------------------------------- /polyglot/go/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | OUT_DIR="$(pwd)/out" 6 | 7 | VERSION=$(jq -r .version ../../package.json) 8 | DEPLOY_DIR="wechaty-go-grpc.$$" 9 | 10 | mkdir "$DEPLOY_DIR" 11 | pushd "$DEPLOY_DIR" 12 | # trap "rm -rfv $(pwd)/$DEPLOY_DIR" EXIT 13 | 14 | git clone git@github.com:wechaty/go-grpc.git 15 | cd go-grpc 16 | cp -Rav "$OUT_DIR"/wechaty . 17 | echo "$VERSION" > VERSION 18 | 19 | if [ -z "$(git status --porcelain)" ]; then 20 | echo 21 | echo "[Publish] There's no new generated code found" 22 | echo 23 | exit 0 24 | fi 25 | 26 | git add . 27 | 28 | git \ 29 | -c "user.name=Mike BO" \ 30 | -c "user.email=mike@zixia.net" \ 31 | \ 32 | commit \ 33 | -am "Deploy Go Grpc Module v${VERSION}" 34 | 35 | git push 36 | echo 37 | echo '[Publish] New code has been generated and pushed' 38 | echo 39 | 40 | git tag v"$VERSION" 41 | git push origin v"$VERSION" 42 | echo 43 | echo "[Publish] New version ${VERSION} has been tagged" 44 | echo 45 | 46 | git status 47 | popd 48 | -------------------------------------------------------------------------------- /polyglot/java/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # Huan(202110): enable `**/*.proto` to include all files 6 | # https://stackoverflow.com/a/28199633/1123955 7 | shopt -s globstar 8 | 9 | # https://stackoverflow.com/a/4774063/1123955 10 | WORK_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 11 | REPO_DIR="$( cd "$WORK_DIR/../../" >/dev/null 2>&1 ; pwd -P )" 12 | 13 | OUT_DIR="$WORK_DIR/src/main/java/" 14 | [ -d "$OUT_DIR" ] || mkdir -p $OUT_DIR 15 | 16 | 17 | protoc --version 18 | 19 | protoc \ 20 | -I $REPO_DIR/proto \ 21 | -I $REPO_DIR/third-party \ 22 | --plugin=protoc-gen-grpc-java=/usr/local/bin/protoc-gen-grpc-java-1.37.1-linux-x86_64.exe \ 23 | --java_out=$OUT_DIR \ 24 | --grpc-java_out=$OUT_DIR \ 25 | $REPO_DIR/proto/**/*.proto 26 | -------------------------------------------------------------------------------- /polyglot/java/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | io.github.wechaty 5 | 0.11.26 6 | 4.0.0 7 | grpc 8 | jar 9 | wechaty-grpc 10 | 11 | 12 | Wechaty is a Wechat Bot SDK for Personal Account that lets you create software to extend the functionality of the 13 | Wechat, 14 | writen in Node.js with TypeScript, Support all platforms including Linux, Windows, Darwin(OSX/Mac) and Docker. 15 | 16 | https://github.com/Chatie/grpc 17 | 18 | 19 | 20 | Zhengxin Diao 21 | diaozxin@gmail.com 22 | 23 | 24 | 25 | 26 | 27 | Apache License, Version 2.0 28 | http://www.apache.org/licenses/LICENSE-2.0 29 | 30 | 31 | 32 | 33 | 34 | Zhengxin Diao 35 | diaozxin@gmail.com 36 | 37 | 38 | 39 | 40 | 41 | 42 | sonatype 43 | https://oss.sonatype.org/content/repositories/snapshots 44 | 45 | 46 | sonatype 47 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 48 | 49 | 50 | 51 | 52 | https://github.com/Chatie/grpc 53 | scm:git:ssh://github.com/Chatie/grpc.git 54 | scm:git:ssh://git@github.com/Chatie/grpc.git 55 | HEAD 56 | 57 | 58 | 59 | 1.8 60 | 1.3.72 61 | 1.3.4 62 | 1.25.0 63 | 0.1.1 64 | 3.11.4 65 | UTF-8 66 | 67 | 68 | 69 | 70 | org.jetbrains.kotlin 71 | kotlin-stdlib 72 | ${kotlin.version} 73 | 74 | 75 | org.jetbrains.kotlinx 76 | kotlinx-coroutines-core 77 | ${kotlinx.coroutines.version} 78 | 79 | 80 | com.google.protobuf 81 | protobuf-java 82 | ${protobuf.version} 83 | 84 | 85 | com.google.protobuf 86 | protobuf-java-util 87 | ${protobuf.version} 88 | 89 | 90 | io.grpc 91 | grpc-netty-shaded 92 | ${grpc.version} 93 | 94 | 95 | io.grpc 96 | grpc-protobuf 97 | ${grpc.version} 98 | 99 | 100 | io.grpc 101 | grpc-stub 102 | ${grpc.version} 103 | 104 | 105 | javax.annotation 106 | javax.annotation-api 107 | 1.3.2 108 | 109 | 110 | io.grpc 111 | grpc-kotlin-stub 112 | ${grpc.kotlin.version} 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | kr.motd.maven 122 | os-maven-plugin 123 | 1.6.2 124 | 125 | 126 | 127 | 128 | 129 | org.xolstice.maven.plugins 130 | protobuf-maven-plugin 131 | 0.6.1 132 | 133 | com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} 134 | ${basedir}/proto/wechaty 135 | 136 | 137 | 138 | grpc-java 139 | 140 | compile 141 | compile-custom 142 | 143 | 144 | grpc-java 145 | io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} 146 | 147 | ${basedir}/proto/wechaty 148 | 149 | 150 | 151 | grpc-kotlin 152 | 153 | compile 154 | compile-custom 155 | 156 | 157 | grpc-kotlin 158 | io.grpc:protoc-gen-grpc-kotlin:${grpc.kotlin.version}:exe:${os.detected.classifier} 159 | 160 | ${basedir}/proto/wechaty 161 | 162 | 163 | 164 | 165 | 166 | 167 | org.jetbrains.kotlin 168 | kotlin-maven-plugin 169 | ${kotlin.version} 170 | 171 | 172 | compile 173 | compile 174 | 175 | compile 176 | 177 | 178 | 179 | ${project.build.directory}/generated-sources/protobuf/java/ 180 | ${project.build.directory}/generated-sources/protobuf/grpc-java/ 181 | ${project.build.directory}/generated-sources/protobuf/grpc-kotlin/ 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | org.apache.maven.plugins 190 | maven-compiler-plugin 191 | 3.8.1 192 | 193 | 194 | 195 | default-compile 196 | none 197 | 198 | 199 | 200 | default-testCompile 201 | none 202 | 203 | 204 | 205 | java-compile 206 | compile 207 | 208 | compile 209 | 210 | 211 | 212 | 213 | ${java.version} 214 | ${java.version} 215 | 216 | 217 | 218 | 219 | org.codehaus.mojo 220 | exec-maven-plugin 221 | 1.6.0 222 | 223 | 224 | 225 | java 226 | 227 | 228 | 229 | 230 | 231 | 232 | org.apache.maven.plugins 233 | maven-dependency-plugin 234 | 3.0.1 235 | 236 | 237 | copy-dependencies 238 | package 239 | 240 | copy-dependencies 241 | 242 | 243 | 244 | 245 | 246 | org.apache.maven.plugins 247 | maven-gpg-plugin 248 | 1.6 249 | 250 | 251 | sign-artifacts 252 | verify 253 | 254 | sign 255 | 256 | 257 | 258 | --pinentry-mode 259 | loopback 260 | 261 | 262 | 263 | 264 | 265 | 266 | org.apache.maven.plugins 267 | maven-source-plugin 268 | 3.2.1 269 | 270 | 271 | package 272 | 273 | jar-no-fork 274 | 275 | 276 | 277 | 278 | 279 | 280 | org.apache.maven.plugins 281 | maven-javadoc-plugin 282 | 3.2.0 283 | 284 | 285 | package 286 | 287 | jar 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | -------------------------------------------------------------------------------- /polyglot/php/.gitignore: -------------------------------------------------------------------------------- 1 | wechaty-php-grpc.*/ 2 | -------------------------------------------------------------------------------- /polyglot/php/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for PHP Wechaty 2 | # 3 | # GitHb: https://github.com/wechaty/php-wechaty 4 | # Author: Chunsheng Zhang https://git.io/JJmKd 5 | # 6 | 7 | .PHONY: all 8 | all : clean lint 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -fr out/* 13 | 14 | .PHONY: test 15 | test: 16 | bash -x ./generate.sh 17 | php grpc_test.php 18 | 19 | .PHONY: generate 20 | generate: 21 | bash -x ./generate.sh 22 | 23 | .PHONY: publish 24 | publish: clean generate 25 | ./publish.sh 26 | 27 | -------------------------------------------------------------------------------- /polyglot/php/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # Huan(202110): enable `**/*.proto` to include all files 6 | # https://stackoverflow.com/a/28199633/1123955 7 | shopt -s globstar 8 | 9 | # https://stackoverflow.com/a/4774063/1123955 10 | WORK_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 11 | REPO_DIR="$( cd "$WORK_DIR/../../" >/dev/null 2>&1 ; pwd -P )" 12 | 13 | OUT_DIR="$WORK_DIR/out/wechaty/" 14 | [ -d "$OUT_DIR" ] || mkdir -p $OUT_DIR 15 | 16 | protoc --version 17 | 18 | protoc \ 19 | -I $REPO_DIR/proto \ 20 | -I $REPO_DIR/third-party \ 21 | --php_out=$OUT_DIR \ 22 | --grpc_out=$OUT_DIR \ 23 | --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin \ 24 | $REPO_DIR/proto/**/*.proto 25 | -------------------------------------------------------------------------------- /polyglot/php/grpc_test.php: -------------------------------------------------------------------------------- 1 | Grpc\ChannelCredentials::createInsecure() 27 | ]); 28 | $request = new \Wechaty\Puppet\DingRequest(); 29 | list($response, $status) = $client->Ding($request)->wait(); 30 | echo sprintf("code: %s, msg: %s \n", $status->code, $status->details); 31 | -------------------------------------------------------------------------------- /polyglot/php/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | OUT_DIR="$(pwd)/out" 6 | 7 | VERSION=$(jq -r .version ../package.json) 8 | DEPLOY_DIR="wechaty-php-grpc.$$" 9 | 10 | mkdir "$DEPLOY_DIR" 11 | pushd "$DEPLOY_DIR" 12 | # trap "rm -rfv $(pwd)/$DEPLOY_DIR" EXIT 13 | 14 | git clone git@github.com:wechaty/php-grpc.git 15 | cd php-grpc 16 | cp -Rav "$OUT_DIR"/wechaty . 17 | echo "$VERSION" > VERSION 18 | 19 | if [ -z "$(git status --porcelain)" ]; then 20 | echo 21 | echo "[Publish] There's no new generated code found" 22 | echo 23 | exit 0 24 | fi 25 | 26 | git \ 27 | -c "user.name=zhangchunsheng" \ 28 | -c "user.email=zhangchunsheng423@gmail.com" \ 29 | \ 30 | commit \ 31 | -am "Deploy PHP Grpc Module v${VERSION}" 32 | 33 | git push 34 | echo 35 | echo '[Publish] New code has been generated and pushed' 36 | echo 37 | 38 | git tag v"$VERSION" 39 | git push origin v"$VERSION" 40 | echo 41 | echo "[Publish] New version ${VERSION} has been tagged" 42 | echo 43 | 44 | git status 45 | popd 46 | -------------------------------------------------------------------------------- /polyglot/python/.gitignore: -------------------------------------------------------------------------------- 1 | src/ 2 | -------------------------------------------------------------------------------- /polyglot/python/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Python Wechaty 2 | # 3 | # GitHb: https://github.com/wechaty/python-wechaty 4 | # Author: Huan LI https://github.com/huan 5 | # 6 | 7 | .PHONY: all 8 | all : generate 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -fr src/* 13 | 14 | .PHONY: install 15 | install: 16 | pip3 install -r requirements.txt 17 | pip3 install -r requirements-dev.txt 18 | 19 | # TODO: to be removed, refer to: https://github.com/danielgtaylor/python-betterproto/issues/373 20 | pip3 install --upgrade jinja2 21 | ../../scripts/install-protoc.sh 22 | 23 | .PHONY: test 24 | test: 25 | python3 -m pytest tests/ 26 | 27 | code: 28 | code . 29 | 30 | .PHONY: dist 31 | dist: 32 | python3 setup.py sdist bdist_wheel 33 | 34 | .PHONY: publish 35 | publish: 36 | PATH=~/.local/bin:${PATH} twine upload dist/* 37 | 38 | .PHONY: generate 39 | generate: 40 | ./generate.sh 41 | -------------------------------------------------------------------------------- /polyglot/python/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Huan(202110): enable `**/*.proto` to include all files 5 | # https://stackoverflow.com/a/28199633/1123955 6 | shopt -s globstar 7 | 8 | # 9 | # Python Import Class With Same Name as Directory 10 | # https://stackoverflow.com/q/16245106/1123955 11 | # 12 | 13 | # https://stackoverflow.com/a/4774063/1123955 14 | WORK_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 15 | REPO_DIR="$( cd "$WORK_DIR/../../" >/dev/null 2>&1 ; pwd -P )" 16 | 17 | OUT_DIR="$WORK_DIR/src/wechaty_grpc/" 18 | [ -d "$OUT_DIR" ] || mkdir -p $OUT_DIR 19 | 20 | function generate_proto_stub () { 21 | PROTOC_CMD="python3 \ 22 | -m grpc_tools.protoc \ 23 | --proto_path=$REPO_DIR/proto \ 24 | --proto_path=$REPO_DIR/third-party \ 25 | --proto_path=/usr/local/include/ \ 26 | wechaty/puppet.proto \ 27 | " 28 | $PROTOC_CMD \ 29 | --python_betterproto_out=${OUT_DIR} 30 | } 31 | 32 | # https://github.com/wechaty/grpc/issues/120 33 | function workaround_issue_120 () { 34 | sed -i \ 35 | 's/from typing import AsyncIterable, AsyncIterator, Iterable, Optional, Union/from typing import AsyncIterable, AsyncIterator, Iterable, Optional, Union, List/' \ 36 | ${OUT_DIR}/wechaty/__init__.py 37 | } 38 | 39 | main () { 40 | generate_proto_stub 41 | 42 | # Huan(202110): 2.0.0-beta.3 fixed this 43 | # workaround_issue_120 44 | } 45 | 46 | main 47 | -------------------------------------------------------------------------------- /polyglot/python/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | betterproto[compiler]>=2.0.0b4 2 | grpcio-tools 3 | pyee 4 | pylint_quotes 5 | pytest 6 | pytype 7 | semver 8 | black 9 | jinja2 -------------------------------------------------------------------------------- /polyglot/python/requirements.txt: -------------------------------------------------------------------------------- 1 | betterproto>=2.0.0b4 2 | -------------------------------------------------------------------------------- /polyglot/python/setup.py: -------------------------------------------------------------------------------- 1 | ''' setup ''' 2 | 3 | import json 4 | import semver 5 | import setuptools 6 | 7 | 8 | def versioning(version: str) -> str: 9 | """version to specification""" 10 | sem_ver = semver.parse(version) 11 | 12 | major = sem_ver['major'] 13 | minor = sem_ver['minor'] 14 | patch = str(sem_ver['patch']) 15 | 16 | if minor % 2: 17 | patch = 'dev' + patch 18 | 19 | fin_ver = '%d.%d.%s' % ( 20 | major, 21 | minor, 22 | patch, 23 | ) 24 | 25 | return fin_ver 26 | 27 | 28 | def setup() -> None: 29 | """setup""" 30 | with open('../../README.md', 'r') as fh: 31 | long_description = fh.read() 32 | 33 | with open('../../package.json') as f: 34 | pkg = json.load(f) 35 | version = versioning(pkg['version']) 36 | 37 | setuptools.setup( 38 | name='wechaty-grpc', 39 | version=version, 40 | author='Huan LI (李卓桓)', 41 | author_email='zixia@zixia.net', 42 | description='Wechaty Puppet Service gRPC API', 43 | long_description=long_description, 44 | long_description_content_type='text/markdown', 45 | license='Apache-2.0', 46 | url='https://github.com/wechaty/grpc', 47 | packages=setuptools.find_packages("src"), 48 | package_dir={'': 'src'}, 49 | classifiers=[ 50 | 'Programming Language :: Python :: 3.7', 51 | 'License :: OSI Approved :: Apache Software License', 52 | 'Operating System :: OS Independent', 53 | ], 54 | install_requires=[ 55 | 'betterproto', 56 | 'grpclib', 57 | ] 58 | ) 59 | 60 | 61 | setup() 62 | -------------------------------------------------------------------------------- /polyglot/python/tests/integration_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Unit Test 3 | """ 4 | # pylint: disable=W0621 5 | 6 | # from typing import ( 7 | # # Any, 8 | # Iterable, 9 | # ) 10 | 11 | import pytest # type: ignore 12 | 13 | # from agent import Agent 14 | 15 | 16 | def test_smoke_testing() -> None: 17 | """ wechaty """ 18 | assert pytest, 'To-be added' 19 | -------------------------------------------------------------------------------- /proto/wechaty/deprecated/file-box.proto: -------------------------------------------------------------------------------- 1 | /** 2 | * Huan(202110): Deprecated 3 | * - use Puppet.Download / Puppet.Upload 4 | * - use DownloadResponse & UploadRequest to streaming chunks 5 | * 6 | * This file will be removed after Dec 31, 2022 7 | */ 8 | syntax = "proto3"; 9 | package wechaty.puppet; 10 | 11 | option go_package = "github.com/wechaty/go-grpc/wechaty/puppet"; 12 | option csharp_namespace = "github.wechaty.grpc.puppet"; 13 | 14 | import "google/protobuf/wrappers.proto"; 15 | import "wechaty/puppet/image.proto"; 16 | 17 | message FileBoxChunk { 18 | /** 19 | * Huan(202110): `oneof xxx {}` seems equal with `optional`? 20 | * to be confirmed. 21 | */ 22 | // oneof payload { 23 | optional bytes data = 1; 24 | optional string name = 2; 25 | // } 26 | } 27 | 28 | message MessageImageStreamRequest { 29 | string id = 1; 30 | ImageType type = 2; 31 | } 32 | message MessageImageStreamResponse { 33 | FileBoxChunk file_box_chunk = 1; 34 | } 35 | 36 | message MessageFileStreamRequest { 37 | string id = 1; 38 | } 39 | message MessageFileStreamResponse { 40 | FileBoxChunk file_box_chunk = 1; 41 | } 42 | 43 | message MessageSendFileStreamRequest { 44 | /** 45 | * Huan(202110): `oneof xxx {}` seems equal with `optional`? 46 | * to be confirmed. 47 | */ 48 | // oneof payload { 49 | optional string conversation_id = 1; 50 | optional FileBoxChunk file_box_chunk = 2; 51 | // }; 52 | } 53 | message MessageSendFileStreamResponse { 54 | /** 55 | * @deprecated: 56 | * Huan(202109): Wrapper types must not be used going forward. 57 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 58 | */ 59 | google.protobuf.StringValue id_string_value_deprecated = 1 [deprecated = true]; // Deprecated after Sep 31, 2021, will be removed after Dec 31, 2022 60 | string id = 2; 61 | } 62 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/base.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package = "github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package = "io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | enum PayloadType { 9 | PAYLOAD_TYPE_UNSPECIFIED = 0; 10 | PAYLOAD_TYPE_MESSAGE = 1; 11 | PAYLOAD_TYPE_CONTACT = 2; 12 | PAYLOAD_TYPE_ROOM = 3; 13 | PAYLOAD_TYPE_ROOM_MEMBER = 4; 14 | PAYLOAD_TYPE_FRIENDSHIP = 5; 15 | } 16 | 17 | message StartRequest {} 18 | message StartResponse {} 19 | 20 | message StopRequest {} 21 | message StopResponse {} 22 | 23 | message VersionRequest {} 24 | message VersionResponse { 25 | string version = 1; 26 | } 27 | 28 | message LogoutRequest {} 29 | message LogoutResponse {} 30 | 31 | message DingRequest { 32 | string data = 1; 33 | } 34 | message DingResponse { 35 | /** 36 | * Ding has no return value 37 | * Puppet should emit a `ding` event when it receives a `ding()` call 38 | */ 39 | } 40 | 41 | message DirtyPayloadRequest { 42 | PayloadType type = 1; 43 | string id = 2; 44 | } 45 | 46 | message DirtyPayloadResponse {} 47 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/contact.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | import "google/protobuf/wrappers.proto"; 9 | 10 | enum ContactGender { 11 | CONTACT_GENDER_UNSPECIFIED = 0; 12 | CONTACT_GENDER_MALE = 1; 13 | CONTACT_GENDER_FEMALE = 2; 14 | } 15 | 16 | enum ContactType { 17 | CONTACT_TYPE_UNSPECIFIED = 0; 18 | CONTACT_TYPE_PERSONAL = 1; 19 | CONTACT_TYPE_OFFICIAL = 2; 20 | CONTACT_TYPE_CORPORATION = 3; 21 | } 22 | 23 | message ContactListRequest {} 24 | message ContactListResponse { 25 | repeated string ids = 1; 26 | } 27 | 28 | message ContactPayloadRequest { 29 | string id = 1; 30 | } 31 | message ContactPayloadResponse { 32 | string id = 1; 33 | ContactGender gender = 2; 34 | ContactType type = 3; 35 | string name = 4; 36 | string avatar = 5; 37 | string address = 6; 38 | string alias = 7; 39 | string city = 8; 40 | bool friend = 9; 41 | string province = 10; 42 | string signature = 11; 43 | bool star = 12; 44 | /** 45 | * Huan(202203): in gRPC, we can freely rename the field name as long as the number keep consistent. 46 | * so we can just rename `weixin` to `handle` here. 47 | * 48 | * (Huan(20220329): use `weixin` for now, until we upgrade to v2.0 49 | * the `wechaty-grpc@1.0.0` will use the latest v1.x automatically when running `npm install` 50 | * which will causing a breaking change. 51 | * @link https://github.com/wechaty/getting-started/issues/254 52 | * 53 | * TODO: rename `weixin` to `handle` in v2.0.0 54 | */ 55 | string weixin = 13; 56 | string corporation = 14; 57 | string title = 15; 58 | string description = 16; 59 | bool coworker = 17; 60 | repeated string phones = 18; 61 | } 62 | 63 | message ContactSelfQRCodeRequest {} 64 | message ContactSelfQRCodeResponse { 65 | string qrcode = 1; 66 | } 67 | 68 | message ContactSelfNameRequest { 69 | string name = 1; 70 | } 71 | message ContactSelfNameResponse {} 72 | 73 | message ContactSelfSignatureRequest { 74 | string signature = 1; 75 | } 76 | message ContactSelfSignatureResponse {} 77 | 78 | message ContactAliasRequest { 79 | string id = 1; 80 | // nullable 81 | google.protobuf.StringValue alias_string_value_deprecated = 2 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 82 | optional string alias = 3; 83 | } 84 | message ContactAliasResponse { 85 | google.protobuf.StringValue alias_string_value_deprecated = 1 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 86 | string alias = 2; 87 | } 88 | 89 | message ContactAvatarRequest { 90 | string id = 1; 91 | google.protobuf.StringValue filebox_string_value_deprecated = 2 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 92 | optional string file_box = 3; 93 | } 94 | message ContactAvatarResponse { 95 | google.protobuf.StringValue filebox_string_value_deprecated = 1 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 96 | string file_box = 2; 97 | } 98 | 99 | message ContactPhoneRequest { 100 | string contact_id = 1; 101 | repeated string phones = 2; 102 | } 103 | 104 | message ContactPhoneResponse { 105 | repeated string phones = 2; 106 | } 107 | 108 | message ContactCorporationRemarkRequest { 109 | string contact_id = 1; 110 | google.protobuf.StringValue corporation_remark_string_value_deprecated = 2 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 111 | string corporation_remark = 3; 112 | } 113 | 114 | message ContactCorporationRemarkResponse {} 115 | 116 | message ContactDescriptionRequest { 117 | string contact_id = 1; 118 | google.protobuf.StringValue description_string_value_deprecated = 2 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 119 | string description = 3; 120 | } 121 | 122 | message ContactDescriptionResponse {} 123 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/conversation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | message ConversationReadRequest { 9 | string conversation_id = 1; 10 | bool has_read = 2; 11 | } 12 | 13 | message ConversationReadResponse { 14 | bool has_read = 1; 15 | } 16 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/download-upload.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option csharp_namespace = "github.wechaty.grpc.puppet"; 6 | 7 | message UploadRequest { 8 | bytes chunk = 1; 9 | } 10 | message UploadResponse { 11 | string id = 1; 12 | } 13 | 14 | message DownloadRequest { 15 | string id = 1; 16 | } 17 | message DownloadResponse { 18 | bytes chunk = 1; 19 | } 20 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/event.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | enum EventType { 9 | EVENT_TYPE_UNSPECIFIED = 0; 10 | 11 | EVENT_TYPE_HEARTBEAT = 1; 12 | EVENT_TYPE_MESSAGE = 2; 13 | EVENT_TYPE_DONG = 3; 14 | EVENT_TYPE_POST = 4; 15 | 16 | reserved 5 to 15; 17 | 18 | EVENT_TYPE_ERROR = 16; 19 | EVENT_TYPE_FRIENDSHIP = 17; 20 | EVENT_TYPE_ROOM_INVITE = 18; 21 | EVENT_TYPE_ROOM_JOIN = 19; 22 | EVENT_TYPE_ROOM_LEAVE = 20; 23 | EVENT_TYPE_ROOM_TOPIC = 21; 24 | EVENT_TYPE_SCAN = 22; 25 | EVENT_TYPE_READY = 23; 26 | EVENT_TYPE_RESET = 24; 27 | EVENT_TYPE_LOGIN = 25; 28 | EVENT_TYPE_LOGOUT = 26; 29 | EVENT_TYPE_DIRTY = 27; 30 | 31 | EVENT_TYPE_ROOM_ANNOUNCE = 32; 32 | } 33 | 34 | message EventRequest {} 35 | 36 | message EventResponse { 37 | EventType type = 1; 38 | // TODO: Huan(202002) consider to use a PB Map? 39 | string payload = 2; // JSON.stringify({ ... }) 40 | } 41 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/friendship.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | import "google/protobuf/wrappers.proto"; 9 | import "wechaty/puppet/referrer.proto"; 10 | 11 | enum FriendshipType { 12 | FRIENDSHIP_TYPE_UNSPECIFIED = 0; 13 | FRIENDSHIP_TYPE_CONFIRM = 1; 14 | FRIENDSHIP_TYPE_RECEIVE = 2; 15 | FRIENDSHIP_TYPE_VERIFY = 3; 16 | } 17 | 18 | enum FriendshipSceneType { 19 | FRIENDSHIP_SCENE_TYPE_UNSPECIFIED = 0; 20 | 21 | FRIENDSHIP_SCENE_TYPE_QQ = 1; 22 | FRIENDSHIP_SCENE_TYPE_EMAIL = 2; 23 | FRIENDSHIP_SCENE_TYPE_WEIXIN = 3; 24 | FRIENDSHIP_SCENE_TYPE_QQTBD = 12; 25 | FRIENDSHIP_SCENE_TYPE_ROOM = 14; 26 | FRIENDSHIP_SCENE_TYPE_PHONE = 15; 27 | FRIENDSHIP_SCENE_TYPE_CARD = 17; 28 | FRIENDSHIP_SCENE_TYPE_LOCATION = 18; 29 | FRIENDSHIP_SCENE_TYPE_BOTTLE = 25; 30 | FRIENDSHIP_SCENE_TYPE_SHAKING = 29; 31 | FRIENDSHIP_SCENE_TYPE_QRCODE = 30; 32 | } 33 | 34 | message FriendshipPayloadRequest { 35 | string id = 1; 36 | /** 37 | * Huan(202003): 38 | * What's the reason we need belowing payload? 39 | * We should remove it if possible. 40 | */ 41 | google.protobuf.StringValue payload_string_value_deprecated = 2 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 42 | string payload = 3; 43 | } 44 | message FriendshipPayloadResponse { 45 | string id = 1; 46 | string contact_id = 2; 47 | string hello = 3; 48 | FriendshipType type = 4; 49 | string stranger = 5; 50 | string ticket = 6; 51 | FriendshipSceneType scene = 7; 52 | } 53 | 54 | message FriendshipSearchPhoneRequest { 55 | string phone = 1; 56 | } 57 | message FriendshipSearchPhoneResponse { 58 | // nullable 59 | google.protobuf.StringValue contact_id_string_value_deprecated = 1 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 60 | string contact_id = 2; 61 | } 62 | 63 | /** 64 | * Huan(202003): we just rename the payload message because gRPC only care about the message structure 65 | * (and as long as we keep consistent with the old structure). 66 | * 67 | * @link https://github.com/wechaty/grpc/pull/173 68 | * 69 | * Rename: 70 | * 1. `FriendshipSearchWeixinRequest` -> `FriendshipSearchHandleRequest` 71 | * 2. `FriendshipSearchWeixinReqsponse` -> `FriendshipSearchHandleResponse` 72 | */ 73 | message FriendshipSearchHandleRequest { 74 | /** 75 | * Huan(202203): in gRPC, we can freely rename the field name as long as the number keep consistent. 76 | * so we can just rename `weixin` to `handle` here. 77 | * 78 | * (Huan(20220329): use `weixin` for now, until we upgrade to v2.0 79 | * the `wechaty-grpc@1.0.0` will use the latest v1.x automatically when running `npm install` 80 | * which will causing a breaking change. 81 | * @link https://github.com/wechaty/getting-started/issues/254 82 | * 83 | * TODO: rename `weixin` to `handle` in v2.0.0 84 | */ 85 | string weixin = 1; 86 | } 87 | message FriendshipSearchHandleResponse { 88 | // nullable 89 | google.protobuf.StringValue contact_id_string_value_deprecated = 1 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 90 | string contact_id = 2; 91 | } 92 | 93 | message FriendshipAddRequest { 94 | string contact_id = 1; 95 | string hello = 2; 96 | google.protobuf.StringValue source_room_id_string_value_deprecated = 3 [deprecated = true]; // Deprecated: will be removed after Dec 31, 2022 97 | google.protobuf.StringValue source_contact_id_string_value_deprecated = 4 [deprecated = true]; // Deprecated: will be removed after Dec 31, 2022 98 | Referrer referrer = 5; 99 | } 100 | 101 | message FriendshipAddResponse {} 102 | 103 | message FriendshipAcceptRequest { 104 | string id = 1; 105 | } 106 | message FriendshipAcceptResponse {} 107 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/image.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | enum ImageType { 9 | IMAGE_TYPE_UNSPECIFIED = 0; 10 | IMAGE_TYPE_THUMBNAIL = 1; 11 | IMAGE_TYPE_HD = 2; 12 | IMAGE_TYPE_ARTWORK = 3; 13 | } 14 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/location.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package = "github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package = "io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | message LocationPayload { 9 | float accuracy = 1; 10 | string address = 2; 11 | double latitude = 3; 12 | double longitude = 4; 13 | string name = 5; 14 | } 15 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | import "google/protobuf/timestamp.proto"; 9 | 10 | /** 11 | * @deprecated 12 | * Huan(202109): will be removed after Dec 31, 2022 13 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 14 | */ 15 | import "google/protobuf/wrappers.proto"; 16 | 17 | import "wechaty/puppet/image.proto"; 18 | import "wechaty/puppet/location.proto"; 19 | import "wechaty/puppet/mini-program.proto"; 20 | import "wechaty/puppet/url-link.proto"; 21 | 22 | enum MessageType { 23 | MESSAGE_TYPE_UNSPECIFIED = 0; 24 | 25 | MESSAGE_TYPE_ATTACHMENT = 1; 26 | MESSAGE_TYPE_AUDIO = 2; 27 | MESSAGE_TYPE_CONTACT = 3; 28 | MESSAGE_TYPE_EMOTICON = 4; 29 | MESSAGE_TYPE_IMAGE = 5; 30 | MESSAGE_TYPE_TEXT = 6; 31 | MESSAGE_TYPE_VIDEO = 7; 32 | MESSAGE_TYPE_CHAT_HISTORY = 8; 33 | MESSAGE_TYPE_LOCATION = 9; 34 | MESSAGE_TYPE_MINI_PROGRAM = 10; 35 | MESSAGE_TYPE_TRANSFER = 11; 36 | MESSAGE_TYPE_RED_ENVELOPE = 12; 37 | MESSAGE_TYPE_RECALLED = 13; 38 | MESSAGE_TYPE_URL = 14; 39 | MESSAGE_TYPE_GROUP_NOTE = 15; 40 | MESSAGE_TYPE_POST = 16; 41 | 42 | MESSAGE_TYPE_SYSTEM = 18; 43 | } 44 | 45 | message MessagePayloadRequest { 46 | string id = 1; 47 | } 48 | message MessagePayloadResponse { 49 | string id = 1; 50 | string filename = 2; 51 | string text = 3; 52 | /** 53 | * @deprecated will be removed after Dec 31, 2022 54 | * Huan(202109): use receive_time(10) instead 55 | */ 56 | uint64 timestamp_deprecated = 4 [deprecated = true]; 57 | MessageType type = 5; 58 | string talker_id = 6; 59 | string room_id = 7; 60 | string listener_id = 8; 61 | repeated string mention_ids = 9; 62 | google.protobuf.Timestamp receive_time = 10; 63 | } 64 | 65 | message MessageImageRequest { 66 | string id = 1; 67 | ImageType type = 2; 68 | } 69 | message MessageImageResponse { 70 | string file_box = 1; 71 | } 72 | 73 | message MessageContactRequest { 74 | string id = 1; 75 | } 76 | message MessageContactResponse { 77 | string id = 1; 78 | } 79 | 80 | message MessageFileRequest { 81 | string id = 1; 82 | } 83 | message MessageFileResponse { 84 | string file_box = 1; 85 | } 86 | 87 | message MessageMiniProgramRequest { 88 | string id = 1; 89 | } 90 | message MessageMiniProgramResponse { 91 | /** 92 | * Huan(202110): We should use payload instead of JSON.stringify string 93 | * The compatible code will be removed after Dec 31, 2022 94 | */ 95 | string mini_program_deprecated = 1 [deprecated = true]; // deprecated after Sep 31, 2021, remove compatible code after Dec 31, 2022 96 | MiniProgramPayload mini_program = 2; 97 | } 98 | 99 | message MessageUrlRequest { 100 | string id = 1; 101 | } 102 | message MessageUrlResponse { 103 | /** 104 | * Huan(202110): We should use payload instead of JSON.stringify string 105 | * The compatible code will be removed after Dec 31, 2022 106 | */ 107 | string url_link_deprecated = 1 [deprecated = true]; // deprecated after Sep 31, 2021, remove compatible code after Dec 31, 2022 108 | UrlLinkPayload url_link = 2; 109 | } 110 | 111 | message MessageSendContactRequest { 112 | string conversation_id = 1; 113 | string contact_id = 2; 114 | } 115 | message MessageSendContactResponse { 116 | /** 117 | * @deprecated: 118 | * Huan(202109): Wrapper types must not be used going forward. 119 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 120 | */ 121 | google.protobuf.StringValue id_string_value_deprecated = 1 [deprecated = true]; // Deprecated after Sep 31, 2021, will be removed after Dec 31, 2022 122 | string id = 2; 123 | } 124 | 125 | message MessageSendFileRequest { 126 | string conversation_id = 1; 127 | string file_box = 2; 128 | } 129 | message MessageSendFileResponse { 130 | /** 131 | * @deprecated: 132 | * Huan(202109): Wrapper types must not be used going forward. 133 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 134 | */ 135 | google.protobuf.StringValue id_string_value_deprecated = 1 [deprecated = true]; // Deprecated after Sep 31, 2021, will be removed after Dec 31, 2022 136 | string id = 2; 137 | } 138 | 139 | message MessageSendTextRequest { 140 | string conversation_id = 1; 141 | string text = 2; 142 | 143 | // Huan(202011) FIXME: Issue #99 144 | // https://github.com/wechaty/grpc/issues/99 145 | repeated string mentional_ids = 3; 146 | } 147 | message MessageSendTextResponse { 148 | /** 149 | * @deprecated: 150 | * Huan(202109): Wrapper types must not be used going forward. 151 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 152 | */ 153 | google.protobuf.StringValue id_string_value_deprecated = 1 [deprecated = true]; // Deprecated after Sep 31, 2021, will be removed after Dec 31, 2022 154 | string id = 2; 155 | } 156 | 157 | message MessageSendMiniProgramRequest { 158 | string conversation_id = 1; 159 | /** 160 | * Huan(202110): We should use payload instead of JSON.stringify string 161 | * The compatible code will be removed after Dec 31, 2022 162 | */ 163 | string mini_program_deprecated = 2 [deprecated = true]; // deprecated after Sep 31, 2021, remove compatible code after Dec 31, 2022 164 | MiniProgramPayload mini_program = 3; 165 | } 166 | message MessageSendMiniProgramResponse { 167 | /** 168 | * @deprecated: 169 | * Huan(202109): Wrapper types must not be used going forward. 170 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 171 | */ 172 | google.protobuf.StringValue id_string_value_deprecated = 1 [deprecated = true]; // Deprecated after Sep 31, 2021, will be removed after Dec 31, 2022 173 | string id = 2; 174 | } 175 | 176 | message MessageSendUrlRequest { 177 | string conversation_id = 1; 178 | /** 179 | * Huan(202110): We should use payload instead of JSON.stringify string 180 | * The compatible code will be removed after Dec 31, 2022 181 | */ 182 | string url_link_deprecated = 2 [deprecated = true]; // deprecated after Sep 31, 2021, remove compatible code after Dec 31, 2022 183 | UrlLinkPayload url_link = 3; 184 | } 185 | message MessageSendUrlResponse { 186 | /** 187 | * @deprecated: 188 | * Huan(202109): Wrapper types must not be used going forward. 189 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 190 | */ 191 | google.protobuf.StringValue id_string_value_deprecated = 1 [deprecated = true]; // Deprecated after Sep 31, 2021, will be removed after Dec 31, 2022 192 | string id = 2; 193 | } 194 | 195 | message MessageRecallRequest { 196 | string id = 1; 197 | } 198 | message MessageRecallResponse { 199 | bool success = 1; 200 | } 201 | message MessageForwardRequest{ 202 | string message_id = 1; 203 | string conversation_id = 2; 204 | } 205 | message MessageForwardResponse { 206 | /** 207 | * @deprecated: use payload instead. 208 | * Huan(202109): Wrapper types must not be used going forward. 209 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 210 | */ 211 | google.protobuf.StringValue id_string_value_deprecated = 1 [deprecated = true]; // Deprecated after Sep 31, 2021, will be removed after Dec 31, 2022 212 | string id = 2; 213 | } 214 | 215 | message MessageLocationRequest{ 216 | string id = 1; 217 | } 218 | message MessageLocationResponse { 219 | LocationPayload location = 1; 220 | } 221 | 222 | message MessageSendLocationRequest { 223 | string conversation_id = 1; 224 | LocationPayload location = 2; 225 | } 226 | message MessageSendLocationResponse { 227 | /** 228 | * Huan(202110): we will not use wrappers any more by following the Google Style Guide: 229 | * If not using optional would add complexity or ambiguity, then use optional primitive fields, 230 | * Wrapper types must not be used going forward. 231 | * — Optional Primitive Fields (https://cloud.google.com/apis/design/design_patterns.md#optional-primitive-fields) 232 | */ 233 | string id = 1; 234 | } 235 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/mini-program.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package = "github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package = "io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | message MiniProgramPayload { 9 | string appid = 1; 10 | string description = 2; 11 | string page_path = 3; 12 | string icon_url = 4; 13 | string share_id = 5; 14 | string thumb_url = 6; 15 | string title = 7; 16 | string username = 8; 17 | string thumb_key = 9; 18 | } 19 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/referrer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option csharp_namespace = "github.wechaty.grpc.puppet"; 5 | option go_package = "github.com/wechaty/go-grpc/wechaty/puppet"; 6 | option java_package = "io.github.wechaty.grpc.puppet"; 7 | 8 | /** 9 | * "Referrer" and "Referral" refers to different things. "Referrer" is something or somebody who refers. "Referral" is the act of referring. 10 | * - https://english.stackexchange.com/questions/33135/referrer-versus-referral-versus-referer 11 | */ 12 | message Referrer { 13 | string contact_id = 1; 14 | string room_id = 2; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/room-invitation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | import "google/protobuf/timestamp.proto"; 9 | 10 | /** 11 | * @deprecated 12 | * Huan(202109): will be removed after Dec 31, 2022 13 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 14 | */ 15 | import "google/protobuf/wrappers.proto"; 16 | 17 | message RoomInvitationPayloadRequest { 18 | string id = 1; 19 | /** 20 | * Huan(202002): `payload` should be removed. 21 | * The puppet server should take the responsibilities 22 | * for storing the unaccepted friend-request payload. 23 | * 24 | * @deprecated: use payload instead. 25 | * Huan(202109): Wrapper types must not be used going forward. 26 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 27 | */ 28 | google.protobuf.StringValue payload_string_value_deprecated = 2 [deprecated = true]; // Deprecated: will be removed after Dec 31, 2022 29 | string payload = 3; 30 | } 31 | message RoomInvitationPayloadResponse { 32 | string id = 1; 33 | string inviter_id = 2; 34 | string topic = 3; 35 | int32 member_count = 4; 36 | repeated string member_ids = 5; 37 | /** 38 | * @deprecated will be removed after Dec 31, 2022 39 | * Huan(202109): use receive_time(10) instead 40 | */ 41 | uint64 timestamp_uint64_deprecated = 6 [deprecated = true]; // Deprecated: will be removed after Dec 31, 2022 42 | string avatar = 7; 43 | string invitation = 8; 44 | string receiver_id = 9; 45 | google.protobuf.Timestamp receive_time = 10; 46 | } 47 | 48 | message RoomInvitationAcceptRequest { 49 | string id = 1; 50 | } 51 | message RoomInvitationAcceptResponse {} 52 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/room-member.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | message RoomMemberPayloadRequest { 9 | string id = 1; 10 | string member_id = 2; 11 | } 12 | message RoomMemberPayloadResponse { 13 | string id = 1; 14 | string room_alias = 2; 15 | string inviter_id = 3; 16 | string avatar = 4; 17 | string name = 5; 18 | } 19 | 20 | message RoomMemberListRequest { 21 | string id = 1; 22 | } 23 | message RoomMemberListResponse { 24 | repeated string member_ids = 1; 25 | } 26 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/room.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | import "google/protobuf/wrappers.proto"; 9 | 10 | message RoomListRequest {} 11 | message RoomListResponse { 12 | repeated string ids = 1; 13 | } 14 | 15 | message RoomPayloadRequest { 16 | string id = 1; 17 | } 18 | message RoomPayloadResponse { 19 | string id = 1; 20 | string topic = 2; 21 | string avatar = 3; 22 | string owner_id = 4; 23 | repeated string admin_ids = 5; 24 | repeated string member_ids = 6; 25 | string handle = 7; 26 | } 27 | 28 | message RoomAddRequest { 29 | string id = 1; 30 | string contact_id = 2 [deprecated = true]; 31 | bool invite_only = 3; 32 | repeated string contact_ids = 4; 33 | } 34 | message RoomAddResponse {} 35 | 36 | message RoomAvatarRequest { 37 | string id = 1; 38 | } 39 | message RoomAvatarResponse { 40 | string file_box = 1; 41 | } 42 | 43 | message RoomCreateRequest { 44 | repeated string contact_ids = 1; 45 | string topic = 2; 46 | } 47 | message RoomCreateResponse { 48 | string id = 1; 49 | } 50 | 51 | message RoomDelRequest { 52 | string id = 1; 53 | string contact_id = 2 [deprecated = true]; 54 | repeated string contact_ids = 3; 55 | } 56 | message RoomDelResponse {} 57 | 58 | message RoomQuitRequest { 59 | string id = 1; 60 | } 61 | message RoomQuitResponse {} 62 | 63 | message RoomTopicRequest { 64 | string id = 1; 65 | google.protobuf.StringValue topic_string_value_deprecated = 2 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 66 | optional string topic = 3; 67 | } 68 | message RoomTopicResponse { 69 | google.protobuf.StringValue topic_string_value_deprecated = 1 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 70 | string topic = 2; 71 | } 72 | 73 | message RoomQRCodeRequest { 74 | string id = 1; 75 | } 76 | message RoomQRCodeResponse { 77 | string qrcode = 1; 78 | } 79 | 80 | message RoomAnnounceRequest { 81 | string id = 1; 82 | google.protobuf.StringValue text_string_value_deprecated = 2 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 83 | optional string text = 3; 84 | } 85 | message RoomAnnounceResponse { 86 | google.protobuf.StringValue text_string_value_deprecated = 1 [deprecated = true]; // DEPRECATED, will be removed after Dec 31, 2022 87 | string text = 2; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/tag.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package="github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package="io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | /** 9 | * @deprecated 10 | * Huan(202109): will be removed after Dec 31, 2022 11 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 12 | */ 13 | import "google/protobuf/wrappers.proto"; 14 | 15 | message TagContactAddRequest { 16 | string id = 1; 17 | string contact_id = 2; 18 | } 19 | message TagContactAddResponse {} 20 | 21 | message TagContactRemoveRequest { 22 | string id = 1; 23 | string contact_id = 2; 24 | } 25 | message TagContactRemoveResponse {} 26 | 27 | message TagContactDeleteRequest { 28 | string id = 1; 29 | } 30 | message TagContactDeleteResponse {} 31 | 32 | message TagContactListRequest { 33 | /* 34 | * @deprecated: 35 | * Huan(202109): Wrapper types must not be used going forward. 36 | * https://cloud.google.com/apis/design/design_patterns#optional_primitive_fields 37 | */ 38 | google.protobuf.StringValue contact_id_string_value_deprecated = 1 [deprecated = true]; // Deprecated: will be removed after Dec 31, 2022 39 | string contact_id = 2; 40 | } 41 | message TagContactListResponse { 42 | repeated string ids = 1; 43 | } 44 | -------------------------------------------------------------------------------- /proto/wechaty/puppet/url-link.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package wechaty.puppet; 3 | 4 | option go_package = "github.com/wechaty/go-grpc/wechaty/puppet"; 5 | option java_package = "io.github.wechaty.grpc.puppet"; 6 | option csharp_namespace = "github.wechaty.grpc.puppet"; 7 | 8 | message UrlLinkPayload { 9 | string description = 1; 10 | string thumbnail_url = 2; 11 | string title = 3; 12 | string url = 4; 13 | } 14 | -------------------------------------------------------------------------------- /scripts/generate-package-json.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | SRC_PACKAGE_JSON_TS_FILE='src/package-json.ts' 5 | 6 | [ -f ${SRC_PACKAGE_JSON_TS_FILE} ] || { 7 | echo ${SRC_PACKAGE_JSON_TS_FILE}" not found" 8 | exit 1 9 | } 10 | 11 | cat <<_SRC_ > ${SRC_PACKAGE_JSON_TS_FILE} 12 | /** 13 | * This file was auto generated from scripts/generate-version.sh 14 | */ 15 | import type { PackageJson } from 'type-fest' 16 | export const packageJson: PackageJson = $(cat package.json) as any 17 | _SRC_ 18 | -------------------------------------------------------------------------------- /scripts/generate-stub.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Huan(202110): enable `**/*.proto` to include all files 5 | # https://stackoverflow.com/a/28199633/1123955 6 | shopt -s globstar 7 | 8 | # https://stackoverflow.com/a/4774063/1123955 9 | WORK_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 10 | REPO_DIR="$( cd "$WORK_DIR/../" >/dev/null 2>&1 ; pwd -P )" 11 | 12 | # Directory to write generated code to (.js and .d.ts files) 13 | OUT_DIR="$REPO_DIR/out/" 14 | [ -d "$OUT_DIR" ] || mkdir -p $OUT_DIR 15 | 16 | PROTOC_CMD="protoc \ 17 | -I $REPO_DIR/third-party/ \ 18 | -I $REPO_DIR/proto/ \ 19 | $REPO_DIR/third-party/**/*.proto \ 20 | $REPO_DIR/proto/**/*.proto \ 21 | " 22 | 23 | function gen_js_pb () { 24 | # 25 | # 1. JS for Protocol Buffer 26 | # - https://github.com/google/protobuf/releases/latest 27 | # 28 | # Generate: wechaty-puppet_pb.js 29 | $PROTOC_CMD \ 30 | --js_out="import_style=commonjs,binary:${OUT_DIR}" 31 | } 32 | 33 | function gen_js_stub () { 34 | # 35 | # 2. JS for gRPC Stubs 36 | # - https://www.npmjs.com/package/grpc-tools 37 | # - https://github.com/grpc/grpc/tree/master/examples/node/static_codegen 38 | # 39 | # Generate: wechaty-puppet_grpc_pb.js 40 | $PROTOC_CMD \ 41 | --plugin="protoc-gen-grpc=node_modules/.bin/grpc_tools_node_protoc_plugin" \ 42 | --grpc_out="grpc_js:${OUT_DIR}" 43 | } 44 | 45 | function gen_ts_typing () { 46 | # 47 | # 3. TS for Protocol Buffer & gRPC Stubs 48 | # - https://github.com/agreatfool/grpc_tools_node_protoc_ts 49 | # 50 | # Generate: 51 | # wechaty-puppet_grpc_pb.d.ts 52 | # wechaty-puppet_pb.d.ts 53 | $PROTOC_CMD \ 54 | --plugin="protoc-gen-grpc=node_modules/grpc_tools_node_protoc_ts/bin/protoc-gen-ts" \ 55 | --grpc_out="grpc_js:${OUT_DIR}" 56 | } 57 | 58 | function gen_web_grpc () { 59 | # 60 | # 4. JS & TS for gRPC Web 61 | # - https://github.com/improbable-eng/ts-protoc-gen 62 | # 63 | # Generate: 64 | # wechaty-puppet_pb_service.d.ts 65 | # wechaty-puppet_pb_service.js 66 | $PROTOC_CMD \ 67 | --plugin="protoc-gen-ts=node_modules/ts-protoc-gen/bin/protoc-gen-ts" \ 68 | --ts_out="service=grpc-web:${OUT_DIR}" 69 | } 70 | function gen_openapi () { 71 | pushd openapi 72 | make generate 73 | popd 74 | } 75 | 76 | # 77 | # Huan(202108): make out/ a CJS module 78 | # 79 | function gen_cjs_package_json () { 80 | echo '{"type": "commonjs"}' > out/package.json 81 | } 82 | 83 | function main () { 84 | gen_js_pb 85 | gen_js_stub 86 | gen_ts_typing 87 | gen_cjs_package_json 88 | 89 | gen_web_grpc 90 | 91 | gen_openapi 92 | } 93 | 94 | main 95 | -------------------------------------------------------------------------------- /scripts/install-protoc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | set -o pipefail 4 | 5 | # https://stackoverflow.com/a/4774063/1123955 6 | WORK_DIR="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" 7 | REPO_DIR="$( cd "$WORK_DIR/../" >/dev/null 2>&1 ; pwd -P )" 8 | 9 | THIRD_PARTY_DIR="$REPO_DIR/third-party/" 10 | 11 | function install_protoc () { 12 | if command -v protoc > /dev/null; then 13 | echo "install skipped: protoc exists" 14 | return 15 | fi 16 | 17 | # https://grpc.io/docs/protoc-installation/ 18 | if [ $(uname) = 'Linux' ]; then 19 | sudo apt install -y protobuf-compiler 20 | elif [ $(uname) = 'Darwin' ]; then 21 | brew install protobuf 22 | else 23 | echo "UNKNOWN PLATFORM: $(uname)" 24 | exit 1 25 | fi 26 | 27 | } 28 | 29 | function check_protoc_version () { 30 | # libprotoc 3.11.3 31 | protocVersion=$(protoc --version | cut -d' ' -f 2) 32 | majorVer=$(echo $protocVersion | cut -d. -f 1) 33 | minorVer=$(echo $protocVersion | cut -d. -f 2) 34 | 35 | # https://github.com/wechaty/grpc/issues/109 36 | 37 | ((($majorVer == 3 && $minorVer >= 17) || $majorVer > 3)) || { 38 | echo "protoc version must >= 3.17 (the installed version is $protocVersion)" 39 | exit 1 40 | } 41 | 42 | echo "protoc version check: v${protocVersion} OK" 43 | } 44 | 45 | function install_protoc_gen_lint () { 46 | go install github.com/ckaznocha/protoc-gen-lint@latest 47 | } 48 | 49 | function install_proto_google_api () { 50 | if [ -d ${THIRD_PARTY_DIR}/google/api ]; then 51 | echo "install skipped: ${THIRD_PARTY_DIR}/google/api exists" 52 | return 53 | fi 54 | 55 | mkdir -p ${THIRD_PARTY_DIR}/google/api 56 | curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto > ${THIRD_PARTY_DIR}/google/api/annotations.proto 57 | curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto > ${THIRD_PARTY_DIR}/google/api/http.proto 58 | curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto > ${THIRD_PARTY_DIR}/google/api/http.proto 59 | } 60 | 61 | function install_proto_health_check () { 62 | if [ -d ${THIRD_PARTY_DIR}/google/api/health_check/v1/health_check.proto ]; then 63 | echo "install skipped: ${THIRD_PARTY_DIR}/google/api/health_check/v1/health_check.proto exists" 64 | return 65 | fi 66 | 67 | mkdir -p ${THIRD_PARTY_DIR}/google/api/health_check/v1/ 68 | curl https://raw.githubusercontent.com/grpc/grpc/master/src/proto/grpc/health/v1/health.proto > ${THIRD_PARTY_DIR}/google/api/health_check/v1/health_check.proto 69 | } 70 | 71 | function install_protoc_gen_openapiv2 () { 72 | pushd "$REPO_DIR/openapi" 73 | make install 74 | popd 75 | } 76 | 77 | # function install_google_protobuf_wrappers () { 78 | # if [ -d ${THIRD_PARTY_DIR}/google/protobuf ]; then 79 | # echo "install skipped: ${THIRD_PARTY_DIR}/google/protobuf exists" 80 | # return 81 | # fi 82 | 83 | # wget \ 84 | # --directory-prefix ${THIRD_PARTY_DIR}/google/protobuf \ 85 | # https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/wrappers.proto \ 86 | # https://raw.githubusercontent.com/protocolbuffers/protobuf/master/src/google/protobuf/descriptor.proto 87 | # } 88 | 89 | function install_protoc_gen_doc () { 90 | if command -v protoc-gen-doc; then 91 | echo "install skipped: $(command -v protoc-gen-doc) exists" 92 | return 0 93 | fi 94 | go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest 95 | } 96 | 97 | function main () { 98 | install_protoc 99 | check_protoc_version 100 | 101 | install_protoc_gen_lint 102 | install_protoc_gen_openapiv2 103 | install_protoc_gen_doc 104 | 105 | install_proto_google_api 106 | install_proto_health_check 107 | } 108 | 109 | main 110 | -------------------------------------------------------------------------------- /scripts/npm-pack-testing.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | npm run dist 5 | npm pack 6 | 7 | TMPDIR="/tmp/npm-pack-testing.$$" 8 | mkdir "$TMPDIR" 9 | mv ./*-*.*.*.tgz "$TMPDIR" 10 | cp tests/fixtures/smoke-testing.ts "$TMPDIR" 11 | 12 | cd $TMPDIR 13 | 14 | npm init -y 15 | npm install --production ./*-*.*.*.tgz \ 16 | @types/node \ 17 | pkg-jq \ 18 | typescript \ 19 | 20 | # 21 | # CommonJS 22 | # 23 | ./node_modules/.bin/tsc \ 24 | --target es6 \ 25 | --module CommonJS \ 26 | \ 27 | --moduleResolution node \ 28 | --esModuleInterop \ 29 | --lib esnext \ 30 | --noEmitOnError \ 31 | --noImplicitAny \ 32 | --skipLibCheck \ 33 | smoke-testing.ts 34 | 35 | echo 36 | echo "CommonJS: pack testing..." 37 | node smoke-testing.js 38 | 39 | # 40 | # ES Modules 41 | # 42 | 43 | # https://stackoverflow.com/a/59203952/1123955 44 | # echo "`jq '.type="module"' package.json`" > package.json 45 | npx pkg-jq -i '.type="module"' 46 | 47 | ./node_modules/.bin/tsc \ 48 | --target es2020 \ 49 | --module es2020 \ 50 | \ 51 | --moduleResolution node \ 52 | --esModuleInterop \ 53 | --lib esnext \ 54 | --noEmitOnError \ 55 | --noImplicitAny \ 56 | --skipLibCheck \ 57 | smoke-testing.ts 58 | 59 | echo 60 | echo "ES Module: pack testing..." 61 | node smoke-testing.js 62 | -------------------------------------------------------------------------------- /scripts/package-publish-config-tag.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION=$(npx pkg-jq -r .version) 5 | 6 | if npx --package @chatie/semver semver-is-prod $VERSION; then 7 | npx pkg-jq -i '.publishConfig.tag="latest"' 8 | echo "production release: publicConfig.tag set to latest." 9 | else 10 | npx pkg-jq -i '.publishConfig.tag="next"' 11 | echo 'development release: publicConfig.tag set to next.' 12 | fi 13 | 14 | -------------------------------------------------------------------------------- /src/chunk-transformer.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { 4 | test, 5 | AssertEqual, 6 | } from 'tstest' 7 | 8 | import type { 9 | Chunk, 10 | } from './chunk-transformer.js' 11 | 12 | test('Chunk type imported from generated code', async t => { 13 | const typeOk: AssertEqual< 14 | Chunk, 15 | string | Uint8Array 16 | > = true 17 | t.ok(typeOk, 'should be `string | Uint8Array`') 18 | }) 19 | -------------------------------------------------------------------------------- /src/chunk-transformer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A stream transformer that encodes chunks into protobufs (and vice versa) 3 | */ 4 | import { 5 | Transform, 6 | } from 'stronger-typed-streams' 7 | 8 | import type { 9 | UploadRequest, 10 | } from '../out/wechaty/puppet/download-upload_pb.js' 11 | 12 | // string | Uint8Array 13 | type Chunk = UploadRequest.AsObject['chunk'] 14 | 15 | /** 16 | * the interface for satisfyign UploadRequest & DownloadResponse 17 | */ 18 | interface ChunkPb { 19 | getChunk(): Chunk 20 | setChunk(value: Chunk): void 21 | } 22 | 23 | /** 24 | * Wrap Chunk 25 | */ 26 | const chunkEncoder = ( 27 | PbConstructor: { new(): T }, 28 | ) => new Transform({ 29 | objectMode: true, 30 | transform: (chunk: Chunk, _: any, callback: (error: Error | null, pb: T) => void) => { 31 | const pb = new PbConstructor() 32 | pb.setChunk(chunk) 33 | callback(null, pb) 34 | }, 35 | }) 36 | 37 | /** 38 | * Unwrap Chunk 39 | */ 40 | const chunkDecoder = () => new Transform({ 41 | objectMode: true, 42 | transform: (pb: T, _: any, callback: (error: Error | null, chunk?: Chunk) => void) => { 43 | const chunk = pb.getChunk() 44 | if (chunk) { 45 | callback(null, chunk) 46 | } else { 47 | callback(new Error('NOCHUNK')) 48 | } 49 | }, 50 | }) 51 | 52 | export type { 53 | Chunk, 54 | } 55 | export { 56 | chunkEncoder, 57 | chunkDecoder, 58 | } 59 | -------------------------------------------------------------------------------- /src/cjs.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | codeRoot, 7 | puppet, 8 | google, 9 | } from './cjs.js' 10 | 11 | test('ESM: codeRoot', async t => { 12 | t.ok(codeRoot, 'should exists "codeRoot"') 13 | }) 14 | 15 | test('ESM: puppet', async t => { 16 | t.ok(puppet, 'should exists "puppet"') 17 | t.ok(puppet.EventRequest, 'should exists "EventRequest"') 18 | }) 19 | 20 | test('ESM: puppet.EventTypeMap', async t => { 21 | const map: puppet.EventTypeMap = {} as any 22 | map.EVENT_TYPE_DIRTY = puppet.EventType.EVENT_TYPE_DIRTY 23 | t.equal(Object.keys(map).length, 1, 'should export type "EventTypeMap"') 24 | }) 25 | 26 | test('ESM: puppet.DingRequest', async t => { 27 | t.equal(typeof puppet.DingRequest, 'function', 'should exists "DingRequest"') 28 | }) 29 | 30 | test('ESM: google.HealthCheckResponse', async t => { 31 | t.ok(google.HealthCheckResponse, 'should exists "HealthCheckResponse"') 32 | }) 33 | 34 | test('ESM: google.ServingStatus.SERVING', async t => { 35 | t.ok(google.HealthCheckResponse.ServingStatus.SERVING, 'should exists "ServingStatus.SERVING"') 36 | }) 37 | -------------------------------------------------------------------------------- /src/cjs.ts: -------------------------------------------------------------------------------- 1 | import puppet from '../commonjs/generated/puppet.cjs' 2 | import google from '../commonjs/generated/google.cjs' 3 | 4 | import { codeRoot } from '../commonjs/code-root.cjs' 5 | 6 | export { 7 | codeRoot, 8 | google, 9 | puppet, 10 | } 11 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { packageJson } from './package-json.js' 2 | 3 | const VERSION = packageJson.version || '0.0.0' 4 | 5 | enum MajorVersionEnum { 6 | v0 = 'v0', 7 | // v1 = 'v1', 8 | } 9 | 10 | export type ApiStore = { 11 | [key in MajorVersionEnum]: { 12 | file: string, 13 | data: string, 14 | } 15 | } 16 | 17 | export { 18 | VERSION, 19 | } 20 | -------------------------------------------------------------------------------- /src/google.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | Timestamp, 7 | } from './google.js' 8 | 9 | test('Timestamp', async t => { 10 | const timestamp: Timestamp = new Timestamp() 11 | timestamp.fromDate(new Date()) 12 | t.ok(timestamp, 'should create a timestamp protocol buffer') 13 | }) 14 | -------------------------------------------------------------------------------- /src/google.ts: -------------------------------------------------------------------------------- 1 | import timestamp from 'google-protobuf/google/protobuf/timestamp_pb.js' 2 | import wrappers from 'google-protobuf/google/protobuf/wrappers_pb.js' 3 | 4 | class StringValue extends wrappers.StringValue {} 5 | class Timestamp extends timestamp.Timestamp {} 6 | 7 | export { 8 | StringValue, 9 | Timestamp, 10 | } 11 | -------------------------------------------------------------------------------- /src/mod.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | grpc, 7 | puppet, 8 | google, 9 | } from './mod.js' 10 | 11 | test('`grpc` is exported', async t => { 12 | t.ok(grpc, 'should imported grpc') 13 | }) 14 | 15 | test('`IPuppetServer` is exported', async t => { 16 | const i: puppet.IPuppetServer = {} as any 17 | t.ok(i, 'should has typing IPuppetServer') 18 | }) 19 | 20 | test('`PuppetService` is exported', async t => { 21 | t.ok(puppet.PuppetService, 'should export PuppetSevice') 22 | }) 23 | 24 | test('CJS: HealthCheckResponse', async t => { 25 | t.ok(google.HealthCheckResponse, 'should exists "HealthCheckResponse"') 26 | }) 27 | 28 | test('CJS: ServingStatus.SERVING', async t => { 29 | t.ok(google.HealthCheckResponse.ServingStatus.SERVING, 'should exists "ServingStatus.SERVING"') 30 | }) 31 | -------------------------------------------------------------------------------- /src/mod.ts: -------------------------------------------------------------------------------- 1 | import * as grpc from '@grpc/grpc-js' 2 | 3 | import * as proto from './proto.js' 4 | import * as openApi from './openapi.js' 5 | 6 | import { 7 | google, 8 | puppet, 9 | } from './cjs.js' 10 | import { VERSION } from './config.js' 11 | import { 12 | StringValue, 13 | Timestamp, 14 | } from './google.js' 15 | import { 16 | chunkEncoder, 17 | chunkDecoder, 18 | } from './chunk-transformer.js' 19 | 20 | export { 21 | chunkEncoder, 22 | chunkDecoder, 23 | google, 24 | grpc, 25 | openApi, 26 | proto, 27 | puppet, 28 | StringValue, 29 | Timestamp, 30 | VERSION, 31 | } 32 | -------------------------------------------------------------------------------- /src/openapi.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | puppet, 7 | } from './openapi.js' 8 | 9 | test('puppet.v0.file', async t => { 10 | const EXPECTED_PATH_REG = /puppet.swagger.json$/ 11 | t.ok(EXPECTED_PATH_REG.test(puppet.v0.file), 'puppet OpenAPI file should be puppet.swagger.json') 12 | }) 13 | 14 | test('puppet.v0.data', async t => { 15 | t.ok(puppet.v0.data.length > 0, 'should get JSON data for puppet.v0.data') 16 | try { 17 | const obj = JSON.parse(puppet.v0.data) 18 | t.ok(obj, 'should be able to parse JSON for puppet.v0.data') 19 | } catch (e) { 20 | t.fail('should be able to parse JSON data for puppet.v0.data') 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /src/openapi.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import fs from 'fs' 3 | 4 | import { codeRoot } from './cjs.js' 5 | 6 | import type { ApiStore } from './config.js' 7 | 8 | function getOpenApiPath (...paths: string[]): string { 9 | return path.join( 10 | codeRoot, 11 | 'out', 12 | ...paths, 13 | ) 14 | } 15 | 16 | const puppetSwaggerFile = getOpenApiPath( 17 | 'wechaty', 18 | 'puppet.swagger.json', 19 | ) 20 | 21 | const puppet: ApiStore = { 22 | v0: { 23 | data: fs.readFileSync(puppetSwaggerFile, 'utf-8'), 24 | file: puppetSwaggerFile, 25 | }, 26 | } 27 | 28 | export { 29 | puppet, 30 | } 31 | -------------------------------------------------------------------------------- /src/package-json.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import { packageJson } from './package-json.js' 6 | 7 | test('Make sure the packageJson is fresh in source code', async t => { 8 | const keyNum = Object.keys(packageJson).length 9 | t.equal(keyNum, 0, 'packageJson should be empty in source code, only updated before publish to NPM') 10 | }) 11 | -------------------------------------------------------------------------------- /src/package-json.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file will be overwrite when we publish NPM module 3 | * by scripts/generate_version.ts 4 | */ 5 | import type { PackageJson } from 'type-fest' 6 | 7 | /** 8 | * Huan(202108): 9 | * The below default values is only for unit testing 10 | */ 11 | export const packageJson: PackageJson = {} 12 | -------------------------------------------------------------------------------- /src/proto.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | puppet, 7 | } from './proto.js' 8 | 9 | test('puppet.v0.file', async t => { 10 | const EXPECTED_PATH_REG = /puppet\.proto$/ 11 | t.ok(EXPECTED_PATH_REG.test(puppet.v0.file), 'puppet proto file should be puppet.proto') 12 | }) 13 | 14 | test('puppet.v0.data', async t => { 15 | t.ok(puppet.v0.data.length > 0, 'should get proto data for puppet.v0.data') 16 | }) 17 | -------------------------------------------------------------------------------- /src/proto.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Huan(20200222): inspired from googleapis/nodejs-proto-files 3 | * https://github.com/googleapis/nodejs-proto-files/blob/920fe4e5f8dee9a187e1858903894810a9b5feca/src/index.ts#L18-L20 4 | */ 5 | import path from 'path' 6 | import fs from 'fs' 7 | 8 | import { codeRoot } from './cjs.js' 9 | 10 | import type { ApiStore } from './config.js' 11 | 12 | function getProtoPath (...paths: string[]): string { 13 | return path.join( 14 | codeRoot, 15 | 'proto', 16 | ...paths, 17 | ) 18 | } 19 | 20 | const puppetProtoFile = getProtoPath( 21 | 'wechaty', 22 | 'puppet.proto', 23 | ) 24 | 25 | const puppet: ApiStore = { 26 | v0: { 27 | data: fs.readFileSync(puppetProtoFile).toString(), 28 | file: puppetProtoFile, 29 | }, 30 | } 31 | 32 | export { 33 | puppet, 34 | } 35 | -------------------------------------------------------------------------------- /tests/fixtures/smoke-testing.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { 4 | puppet, 5 | VERSION, 6 | } from 'wechaty-grpc' 7 | 8 | async function main () { 9 | const messagePayloadResponse = new puppet.MessagePayloadResponse() 10 | messagePayloadResponse.setId('id') 11 | 12 | if (VERSION === '0.0.0') { 13 | throw new Error('version should be set before publishing') 14 | } 15 | 16 | console.info(`wechaty-grpc v${VERSION} smoking test passed.`) 17 | return 0 18 | } 19 | 20 | main() 21 | .then(process.exit) 22 | .catch(e => { 23 | console.error(e) 24 | process.exit(1) 25 | }) 26 | -------------------------------------------------------------------------------- /tests/health-check.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import { promisify } from 'util' 6 | 7 | import { 8 | grpc, 9 | google, 10 | } from '../src/mod.js' 11 | 12 | test('HealthCheck protocol buffer', async t => { 13 | const request = new google.HealthCheckRequest() 14 | const response = new google.HealthCheckResponse() 15 | response.setStatus(google.HealthCheckResponse.ServingStatus.SERVING) 16 | await t.ok(request && response, 'should export HealCheck protobuf') 17 | }) 18 | 19 | test('health check smoke testing', async t => { 20 | const ENDPOINT = 'localhost:18788' 21 | 22 | /** 23 | * Create Server 24 | */ 25 | const testServer = getTestServer() 26 | 27 | const server = new grpc.Server() 28 | server.addService( 29 | google.HealthService, 30 | testServer, 31 | ) 32 | await promisify( 33 | server.bindAsync 34 | .bind(server) 35 | )( 36 | ENDPOINT, 37 | grpc.ServerCredentials.createInsecure(), 38 | ) 39 | server.start() 40 | 41 | /** 42 | * Create Client 43 | */ 44 | const client = new google.HealthClient( 45 | ENDPOINT, 46 | grpc.credentials.createInsecure() 47 | ) 48 | 49 | const request = new google.HealthCheckRequest() 50 | const response = await promisify( 51 | client.check 52 | .bind(client) 53 | )(request) as any 54 | 55 | t.equal(response.getStatus(), google.HealthCheckResponse.ServingStatus.SERVING, 'should return SERVING_STATUS_SERVING for check method') 56 | 57 | /** 58 | * gRPC: Stream 59 | */ 60 | const eventStream = client.watch(request) 61 | 62 | let counter = 0 63 | const future = new Promise((resolve, reject) => { 64 | eventStream 65 | .on('data', (response: google.HealthCheckResponse) => { 66 | t.equal(response.getStatus(), google.HealthCheckResponse.ServingStatus.SERVING, `should return SERVING_STATUS_SERVING for watch method with counter #${counter}`) 67 | counter++ 68 | }) 69 | .on('close', resolve) 70 | .on('error', reject) 71 | }) 72 | 73 | await future 74 | t.equal(counter, 2, 'should return 2 responses') 75 | 76 | await new Promise(resolve => server.tryShutdown(resolve)) 77 | // server.forceShutdown() 78 | }) 79 | 80 | function getTestServer () { 81 | 82 | const puppetTestServer: google.IHealthServer = { 83 | check: (_call, callback) => { 84 | const response = new google.HealthCheckResponse() 85 | response.setStatus(google.HealthCheckResponse.ServingStatus.SERVING) 86 | callback(null, response) 87 | }, 88 | watch: (call) => { 89 | const response1 = new google.HealthCheckResponse() 90 | response1.setStatus(google.HealthCheckResponse.ServingStatus.SERVING) 91 | call.write(response1) 92 | 93 | const response2 = new google.HealthCheckResponse() 94 | response2.setStatus(google.HealthCheckResponse.ServingStatus.SERVING) 95 | call.write(response2) 96 | 97 | call.end() 98 | }, 99 | } 100 | 101 | return puppetTestServer 102 | } 103 | -------------------------------------------------------------------------------- /tests/integration.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import util from 'util' 6 | 7 | import { 8 | grpc, 9 | puppet, 10 | } from '../src/mod.js' 11 | 12 | import { 13 | puppetServerImpl, 14 | } from '../tests/puppet-server-impl.js' 15 | 16 | test('integration testing', async t => { 17 | const ENDPOINT = 'localhost:18788' 18 | 19 | const DING_DATA_LIST = ['data1', 'data2'] 20 | const EVENT_DATA_LIST = [] as string[] 21 | 22 | /** 23 | * Create Server 24 | */ 25 | const testServer = getTestServer() 26 | 27 | const server = new grpc.Server() 28 | server.addService( 29 | puppet.PuppetService, 30 | testServer, 31 | ) 32 | await util.promisify(server.bindAsync.bind(server))( 33 | ENDPOINT, 34 | grpc.ServerCredentials.createInsecure(), 35 | ) 36 | server.start() 37 | 38 | /** 39 | * Create Client 40 | */ 41 | const client = new puppet.PuppetClient( 42 | ENDPOINT, 43 | grpc.credentials.createInsecure() 44 | ) 45 | 46 | /** 47 | * gRPC: Stream 48 | */ 49 | const eventStream = client.event(new puppet.EventRequest()) 50 | 51 | const future = new Promise((resolve, reject) => { 52 | eventStream 53 | .on('data', (chunk: puppet.EventResponse) => { 54 | const payload = chunk.getPayload() 55 | EVENT_DATA_LIST.push(payload) 56 | 57 | console.info('on(data)', payload) 58 | 59 | if (EVENT_DATA_LIST.length === DING_DATA_LIST.length) { 60 | resolve() 61 | } 62 | }) 63 | .on('error', reject) 64 | }) 65 | 66 | /** 67 | * gRPC: Ding 68 | */ 69 | for (const data of DING_DATA_LIST) { 70 | const request = new puppet.DingRequest() 71 | request.setData(data) 72 | 73 | console.info('ding() for ', data) 74 | await util.promisify(client.ding.bind(client))(request) 75 | } 76 | 77 | /** 78 | * Check Result 79 | */ 80 | await future 81 | t.same(EVENT_DATA_LIST, DING_DATA_LIST, 'should get ding data back through event stream') 82 | 83 | /** 84 | * Close Client & Server 85 | */ 86 | 87 | /** 88 | * Issue #130: Assertion `(current_nghttp2_memory_) >= (previous_size)' failed. 89 | * https://github.com/wechaty/grpc/issues/130 90 | */ 91 | await new Promise(resolve => setImmediate(resolve)) 92 | eventStream.cancel() 93 | 94 | await new Promise(resolve => server.tryShutdown(resolve)) 95 | // server.forceShutdown() 96 | }) 97 | 98 | function getTestServer () { 99 | 100 | let eventStream: undefined | grpc.ServerWritableStream 101 | const dataQueue = [] as string[] 102 | 103 | /** 104 | * Implements the SayHello RPC method. 105 | */ 106 | const puppetTestServer: puppet.IPuppetServer = { 107 | ...puppetServerImpl, 108 | 109 | ding: (call, callback) => { 110 | const data = call.request.getData() 111 | 112 | if (!eventStream) { 113 | dataQueue.push(data) 114 | } else { 115 | const eventResponse = new puppet.EventResponse() 116 | eventResponse.setType(puppet.EventType.EVENT_TYPE_DONG) 117 | eventResponse.setPayload(data) 118 | eventStream.write(eventResponse) 119 | } 120 | 121 | callback(null, new puppet.DingResponse()) 122 | }, 123 | 124 | event: (streamingCall) => { 125 | if (eventStream) { 126 | throw new Error('eventStream can not be created again') 127 | } 128 | 129 | eventStream = streamingCall 130 | while (dataQueue.length > 0) { 131 | const data = dataQueue.shift() 132 | const eventResponse = new puppet.EventResponse() 133 | eventResponse.setType(puppet.EventType.EVENT_TYPE_DONG) 134 | eventResponse.setPayload(data!) 135 | eventStream.write(eventResponse) 136 | } 137 | /** 138 | * Detect if Inexor Core is gone (GRPC disconnects) 139 | * https://github.com/grpc/grpc/issues/8117#issuecomment-362198092 140 | */ 141 | eventStream.on('cancelled', () => console.info('eventStream.on(calcelled)')) 142 | }, 143 | 144 | } 145 | 146 | return puppetTestServer 147 | } 148 | -------------------------------------------------------------------------------- /tests/nullable.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | import util from 'util' 6 | 7 | import { 8 | grpc, 9 | puppet, 10 | } from '../src/mod.js' 11 | 12 | import { puppetServerImpl } from './puppet-server-impl.js' 13 | 14 | const SERVER_ENDPOINT = '127.0.0.1:8788' 15 | const ID = 'test-id' 16 | const ALIAS = 'test-alias' 17 | 18 | const contactAlias: grpc.handleUnaryCall< 19 | puppet.ContactAliasRequest, 20 | puppet.ContactAliasResponse 21 | > = (call, callback) => { 22 | const id = call.request.getId() 23 | 24 | if (call.request.hasAlias()) { 25 | /** 26 | * Set alias, return void 27 | */ 28 | const alias = call.request.getAlias() 29 | if (alias !== ALIAS) { 30 | throw new Error(`alias argument value error: ${alias} not equal to ${ALIAS}`) 31 | } 32 | callback(null, new puppet.ContactAliasResponse()) 33 | 34 | } else { 35 | /** 36 | * Get alias, return alias 37 | */ 38 | const response = new puppet.ContactAliasResponse() 39 | response.setAlias(id + ALIAS) 40 | callback(null, response) 41 | } 42 | } 43 | 44 | test('use StringValue to support nullable values', async t => { 45 | 46 | const puppetServerImplTest = { 47 | ...puppetServerImpl, 48 | contactAlias, 49 | } 50 | 51 | const server = new grpc.Server() 52 | server.addService( 53 | puppet.PuppetService, 54 | puppetServerImplTest, 55 | ) 56 | 57 | try { 58 | // FIXME: Huan(202002) if the port has been used by another grpc server, this will still bind with succeed! 59 | // The result will be one port binded by two grpc server, and they are all working well... 60 | const port = await util.promisify(server.bindAsync.bind(server))( 61 | SERVER_ENDPOINT, 62 | grpc.ServerCredentials.createInsecure(), 63 | ) 64 | // console.info('port:', port) 65 | if (port <= 0) { 66 | t.fail(`server bind to ${SERVER_ENDPOINT} failed, port get ${port}.`) 67 | return 68 | } 69 | } catch (e) { 70 | /** 71 | * Run gRPC server failed 72 | * https://medium.com/@yuanchaodu/run-grpc-server-failed-289172dbe6e 73 | * 74 | * No address added out of total 1 resolved 75 | * The above error message means the port is in use. 76 | */ 77 | t.fail('server bindAsync fail.') 78 | console.error(e) 79 | } 80 | 81 | server.start() 82 | 83 | const client = new puppet.PuppetClient( 84 | SERVER_ENDPOINT, 85 | grpc.credentials.createInsecure() 86 | ) 87 | 88 | const contactAliasPromise = util.promisify(client.contactAlias.bind(client)) 89 | 90 | /** 91 | * Get alias 92 | */ 93 | { 94 | const request = new puppet.ContactAliasRequest() 95 | request.setId(ID) 96 | 97 | const response = await contactAliasPromise(request) as puppet.ContactAliasResponse 98 | 99 | const alias = response.getAlias() 100 | t.equal(alias, ID + ALIAS, 'should get the right alias value') 101 | } 102 | 103 | /** 104 | * Set alias 105 | */ 106 | { 107 | const request = new puppet.ContactAliasRequest() 108 | request.setId(ID) 109 | request.setAlias(ALIAS) 110 | 111 | const response = await contactAliasPromise(request) as puppet.ContactAliasResponse 112 | 113 | const alias = response.getAlias() 114 | t.notOk(alias, 'should return empty for after set a value') 115 | } 116 | 117 | await new Promise(resolve => server.tryShutdown(resolve)) 118 | setImmediate(() => server.forceShutdown()) 119 | }) 120 | -------------------------------------------------------------------------------- /tests/puppet-server-impl.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable sort-keys */ 2 | import type { 3 | puppet, 4 | } from '../src/mod.js' 5 | 6 | type IPuppetServer = puppet.IPuppetServer 7 | 8 | /** 9 | * Implements the SayHello RPC method. 10 | */ 11 | export const puppetServerImpl: IPuppetServer = { 12 | 13 | contactAlias: (call, callback) => { 14 | void call 15 | void callback 16 | throw new Error('not implemented.') 17 | }, 18 | 19 | contactAvatar: (call, callback) => { 20 | void call 21 | void callback 22 | throw new Error('not implemented.') 23 | }, 24 | 25 | contactCorporationRemark: (call, callback) => { 26 | void call 27 | void callback 28 | throw new Error('not implemented') 29 | }, 30 | 31 | contactDescription: (call, callback) => { 32 | void call 33 | void callback 34 | throw new Error('not implemented') 35 | }, 36 | 37 | contactList: (call, callback) => { 38 | void call 39 | void callback 40 | throw new Error('not implemented.') 41 | }, 42 | 43 | contactPayload: (call, callback) => { 44 | void call 45 | void callback 46 | throw new Error('not implemented.') 47 | }, 48 | 49 | contactPhone: (call, callback) => { 50 | void call 51 | void callback 52 | throw new Error('not implemented.') 53 | }, 54 | 55 | contactSelfName: (call, callback) => { 56 | void call 57 | void callback 58 | throw new Error('not implemented.') 59 | }, 60 | 61 | contactSelfQRCode: (call, callback) => { 62 | void call 63 | void callback 64 | throw new Error('not implemented.') 65 | }, 66 | 67 | contactSelfSignature: (call, callback) => { 68 | void call 69 | void callback 70 | throw new Error('not implemented.') 71 | }, 72 | 73 | ding: (call, callback) => { 74 | void call 75 | void callback 76 | throw new Error('not implemented.') 77 | }, 78 | 79 | dirtyPayload: (call, callback) => { 80 | void call 81 | void callback 82 | throw new Error('not implemented.') 83 | }, 84 | 85 | event: (streamnigCall) => { 86 | void streamnigCall 87 | throw new Error('not implemented.') 88 | 89 | }, 90 | 91 | friendshipAccept: (call, callback) => { 92 | void call 93 | void callback 94 | throw new Error('not implemented.') 95 | }, 96 | 97 | friendshipAdd: (call, callback) => { 98 | void call 99 | void callback 100 | throw new Error('not implemented.') 101 | }, 102 | 103 | friendshipPayload: (call, callback) => { 104 | void call 105 | void callback 106 | throw new Error('not implemented.') 107 | }, 108 | 109 | friendshipSearchPhone: (call, callback) => { 110 | void call 111 | void callback 112 | throw new Error('not implemented.') 113 | }, 114 | 115 | friendshipSearchWeixin: (call, callback) => { 116 | void call 117 | void callback 118 | throw new Error('not implemented.') 119 | }, 120 | 121 | friendshipSearchHandle: (call, callback) => { 122 | void call 123 | void callback 124 | throw new Error('not implemented.') 125 | }, 126 | 127 | logout: (call, callback) => { 128 | void call 129 | void callback 130 | throw new Error('not implemented.') 131 | }, 132 | 133 | messageContact: (call, callback) => { 134 | void call 135 | void callback 136 | throw new Error('not implemented.') 137 | }, 138 | 139 | messageFile: (call, callback) => { 140 | void call 141 | void callback 142 | throw new Error('not implemented.') 143 | }, 144 | 145 | messageFileStream: (call) => { 146 | void call 147 | throw new Error('not implemented.') 148 | }, 149 | 150 | messageForward: (call, callback) => { 151 | void call 152 | void callback 153 | throw new Error('not implemented.') 154 | }, 155 | 156 | messageImage: (call, callback) => { 157 | void call 158 | void callback 159 | throw new Error('not implemented.') 160 | }, 161 | 162 | messageImageStream: (call) => { 163 | void call 164 | throw new Error('not implemented.') 165 | }, 166 | 167 | messageLocation: (call, callback) => { 168 | void call 169 | void callback 170 | throw new Error('not implemented.') 171 | }, 172 | 173 | messageMiniProgram: (call, callback) => { 174 | void call 175 | void callback 176 | throw new Error('not implemented.') 177 | }, 178 | 179 | messagePayload: (call, callback) => { 180 | void call 181 | void callback 182 | throw new Error('not implemented.') 183 | }, 184 | 185 | messageRecall: (call, callback) => { 186 | void call 187 | void callback 188 | throw new Error('not implemented.') 189 | }, 190 | 191 | messageSendContact: (call, callback) => { 192 | void call 193 | void callback 194 | throw new Error('not implemented.') 195 | }, 196 | 197 | messageSendFile: (call, callback) => { 198 | void call 199 | void callback 200 | throw new Error('not implemented.') 201 | }, 202 | 203 | messageSendFileStream: (call, callback) => { 204 | void call 205 | void callback 206 | throw new Error('not implemented.') 207 | }, 208 | 209 | messageSendLocation: (call, callback) => { 210 | void call 211 | void callback 212 | throw new Error('not implemented.') 213 | }, 214 | 215 | messageSendMiniProgram: (call, callback) => { 216 | void call 217 | void callback 218 | throw new Error('not implemented.') 219 | }, 220 | 221 | messageSendText: (call, callback) => { 222 | void call 223 | void callback 224 | throw new Error('not implemented.') 225 | }, 226 | 227 | messageSendUrl: (call, callback) => { 228 | void call 229 | void callback 230 | throw new Error('not implemented.') 231 | }, 232 | 233 | messageUrl: (call, callback) => { 234 | void call 235 | void callback 236 | throw new Error('not implemented.') 237 | }, 238 | 239 | roomAdd: (call, callback) => { 240 | void call 241 | void callback 242 | throw new Error('not implemented.') 243 | }, 244 | 245 | roomAnnounce: (call, callback) => { 246 | void call 247 | void callback 248 | throw new Error('not implemented.') 249 | }, 250 | 251 | roomAvatar: (call, callback) => { 252 | void call 253 | void callback 254 | throw new Error('not implemented.') 255 | }, 256 | 257 | roomCreate: (call, callback) => { 258 | void call 259 | void callback 260 | throw new Error('not implemented.') 261 | }, 262 | 263 | roomDel: (call, callback) => { 264 | void call 265 | void callback 266 | throw new Error('not implemented.') 267 | }, 268 | 269 | roomInvitationAccept: (call, callback) => { 270 | void call 271 | void callback 272 | throw new Error('not implemented.') 273 | }, 274 | 275 | roomInvitationPayload: (call, callback) => { 276 | void call 277 | void callback 278 | throw new Error('not implemented.') 279 | }, 280 | 281 | roomList: (call, callback) => { 282 | void call 283 | void callback 284 | throw new Error('not implemented.') 285 | }, 286 | 287 | roomMemberList: (call, callback) => { 288 | void call 289 | void callback 290 | throw new Error('not implemented.') 291 | }, 292 | 293 | roomMemberPayload: (call, callback) => { 294 | void call 295 | void callback 296 | throw new Error('not implemented.') 297 | }, 298 | 299 | roomPayload: (call, callback) => { 300 | void call 301 | void callback 302 | throw new Error('not implemented.') 303 | }, 304 | 305 | roomQRCode: (call, callback) => { 306 | void call 307 | void callback 308 | throw new Error('not implemented.') 309 | }, 310 | 311 | roomQuit: (call, callback) => { 312 | void call 313 | void callback 314 | throw new Error('not implemented.') 315 | }, 316 | 317 | roomTopic: (call, callback) => { 318 | void call 319 | void callback 320 | throw new Error('not implemented.') 321 | }, 322 | 323 | start: (call, callback) => { 324 | void call 325 | void callback 326 | throw new Error('not implemented.') 327 | }, 328 | 329 | stop: (call, callback) => { 330 | void call 331 | void callback 332 | throw new Error('not implemented.') 333 | }, 334 | 335 | tagContactAdd: (call, callback) => { 336 | void call 337 | void callback 338 | throw new Error('not implemented.') 339 | }, 340 | 341 | tagContactDelete: (call, callback) => { 342 | void call 343 | void callback 344 | throw new Error('not implemented.') 345 | }, 346 | 347 | tagContactList: (call, callback) => { 348 | void call 349 | void callback 350 | throw new Error('not implemented.') 351 | }, 352 | 353 | tagContactRemove: (call, callback) => { 354 | void call 355 | void callback 356 | throw new Error('not implemented.') 357 | }, 358 | 359 | version: (call, callback) => { 360 | void call 361 | void callback 362 | throw new Error('not implemented.') 363 | }, 364 | 365 | download: (call) => { 366 | void call 367 | throw new Error('not implemented.') 368 | }, 369 | 370 | upload: (call, callback) => { 371 | void call 372 | void callback 373 | throw new Error('not implemented.') 374 | }, 375 | 376 | conversationRead: (call, callback) => { 377 | void call 378 | void callback 379 | throw new Error('not implemented.') 380 | }, 381 | 382 | } 383 | -------------------------------------------------------------------------------- /tests/streaming.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | 3 | import { test } from 'tstest' 4 | 5 | test('stream testing', async t => { 6 | void t.skip('to be writen') 7 | }) 8 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist/cjs", 5 | "module": "CommonJS", 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@chatie/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist/esm", 5 | "verbatimModuleSyntax": false, 6 | // See: https://github.com/wechaty/wechaty/issues/2551 7 | "ignoreDeprecations": "5.0" 8 | }, 9 | "exclude": [ 10 | "node_modules/", 11 | "dist/", 12 | "tests/fixtures/", 13 | ], 14 | "include": [ 15 | "app/**/*.ts", 16 | "bin/*.ts", 17 | "bot/**/*.ts", 18 | "examples/**/*.ts", 19 | "scripts/**/*.ts", 20 | "src/**/*.ts", 21 | "tests/**/*.spec.ts", "commonjs/google.spec.ts", 22 | ], 23 | } 24 | --------------------------------------------------------------------------------