├── .dockerignore ├── .env.example ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── config.yml └── workflows │ └── publish-docker-hub.yml ├── .gitignore ├── Dockerfile ├── README.md ├── README_ZH.md ├── docker-compose.yml ├── docs └── images │ ├── railway-deployed.png │ ├── railway-deploying.png │ └── railway-succeed.png ├── package-lock.json ├── package.json ├── public └── .gitignore ├── src ├── bot.ts ├── config.ts ├── data.ts ├── interface.ts ├── main.ts ├── openai.ts └── utils.ts └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | .pnpm-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # Snowpack dependency directory (https://snowpack.dev/) 50 | web_modules/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional stylelint cache 62 | .stylelintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variable files 80 | .env 81 | .env.development.local 82 | .env.test.local 83 | .env.production.local 84 | .env.local 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | .parcel-cache 89 | 90 | # Next.js build output 91 | .next 92 | out 93 | 94 | # Nuxt.js build / generate output 95 | .nuxt 96 | dist 97 | 98 | # Gatsby files 99 | .cache/ 100 | # Comment in the public line in if your project uses Gatsby and not Next.js 101 | # https://nextjs.org/blog/next-9-1#public-directory-support 102 | # public 103 | 104 | # vuepress build output 105 | .vuepress/dist 106 | 107 | # vuepress v2.x temp and cache directory 108 | .temp 109 | 110 | # Docusaurus cache and generated files 111 | .docusaurus 112 | 113 | # Serverless directories 114 | .serverless/ 115 | 116 | # FuseBox cache 117 | .fusebox/ 118 | 119 | # DynamoDB Local files 120 | .dynamodb/ 121 | 122 | # TernJS port file 123 | .tern-port 124 | 125 | # Stores VSCode versions used for testing VSCode extensions 126 | .vscode-test 127 | 128 | # yarn v2 129 | .yarn/cache 130 | .yarn/unplugged 131 | .yarn/build-state.yml 132 | .yarn/install-state.gz 133 | .pnp.* 134 | 135 | ### Node Patch ### 136 | # Serverless Webpack directories 137 | .webpack/ 138 | 139 | # Optional stylelint cache 140 | 141 | # SvelteKit build / generate output 142 | .svelte-kit 143 | 144 | # End of https://www.toptal.com/developers/gitignore/api/node 145 | n 146 | *memory-card.json 147 | # Created by https://www.toptal.com/developers/gitignore/api/python 148 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 149 | 150 | ### Python ### 151 | # Byte-compiled / optimized / DLL files 152 | __pycache__/ 153 | *.py[cod] 154 | *$py.class 155 | 156 | # C extensions 157 | *.so 158 | 159 | # Distribution / packaging 160 | .Python 161 | build/ 162 | develop-eggs/ 163 | dist/ 164 | downloads/ 165 | eggs/ 166 | .eggs/ 167 | lib/ 168 | lib64/ 169 | parts/ 170 | sdist/ 171 | var/ 172 | wheels/ 173 | share/python-wheels/ 174 | *.egg-info/ 175 | .installed.cfg 176 | *.egg 177 | MANIFEST 178 | 179 | # PyInstaller 180 | # Usually these files are written by a python script from a template 181 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 182 | *.manifest 183 | *.spec 184 | 185 | # Installer logs 186 | pip-log.txt 187 | pip-delete-this-directory.txt 188 | 189 | # Unit test / coverage reports 190 | htmlcov/ 191 | .tox/ 192 | .nox/ 193 | .coverage 194 | .coverage.* 195 | .cache 196 | nosetests.xml 197 | coverage.xml 198 | *.cover 199 | *.py,cover 200 | .hypothesis/ 201 | .pytest_cache/ 202 | cover/ 203 | 204 | # Translations 205 | *.mo 206 | *.pot 207 | 208 | # Django stuff: 209 | *.log 210 | local_settings.py 211 | db.sqlite3 212 | db.sqlite3-journal 213 | 214 | # Flask stuff: 215 | instance/ 216 | .webassets-cache 217 | 218 | # Scrapy stuff: 219 | .scrapy 220 | 221 | # Sphinx documentation 222 | docs/_build/ 223 | 224 | # PyBuilder 225 | .pybuilder/ 226 | target/ 227 | 228 | # Jupyter Notebook 229 | .ipynb_checkpoints 230 | 231 | # IPython 232 | profile_default/ 233 | ipython_config.py 234 | 235 | # pyenv 236 | # For a library or package, you might want to ignore these files since the code is 237 | # intended to run in multiple environments; otherwise, check them in: 238 | # .python-version 239 | 240 | # pipenv 241 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 242 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 243 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 244 | # install all needed dependencies. 245 | #Pipfile.lock 246 | 247 | # poetry 248 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 249 | # This is especially recommended for binary packages to ensure reproducibility, and is more 250 | # commonly ignored for libraries. 251 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 252 | #poetry.lock 253 | 254 | # pdm 255 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 256 | #pdm.lock 257 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 258 | # in version control. 259 | # https://pdm.fming.dev/#use-with-ide 260 | .pdm.toml 261 | 262 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 263 | __pypackages__/ 264 | 265 | # Celery stuff 266 | celerybeat-schedule 267 | celerybeat.pid 268 | 269 | # SageMath parsed files 270 | *.sage.py 271 | 272 | # Environments 273 | .env 274 | .venv 275 | env/ 276 | venv/ 277 | ENV/ 278 | env.bak/ 279 | venv.bak/ 280 | 281 | # Spyder project settings 282 | .spyderproject 283 | .spyproject 284 | 285 | # Rope project settings 286 | .ropeproject 287 | 288 | # mkdocs documentation 289 | /site 290 | 291 | # mypy 292 | .mypy_cache/ 293 | .dmypy.json 294 | dmypy.json 295 | 296 | # Pyre type checker 297 | .pyre/ 298 | 299 | # pytype static type analyzer 300 | .pytype/ 301 | 302 | # Cython debug symbols 303 | cython_debug/ 304 | 305 | # PyCharm 306 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 307 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 308 | # and can be added to the global gitignore or merged into this file. For a more nuclear 309 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 310 | #.idea/ 311 | 312 | ### Python Patch ### 313 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 314 | poetry.toml 315 | 316 | docs/ 317 | 318 | 319 | # End of https://www.toptal.com/developers/gitignore/api/python 320 | n 321 | config.json 322 | cache.json 323 | config.yaml 324 | .vscode -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ENDPOINT="https://API.EXAMPLE.COM/v1" 2 | OPENAI_API_KEY="sk-XXXXXXXXXXXXXXXXX" 3 | MODEL="gpt-3.5-turbo" 4 | CHAT_PRIVATE_TRIGGER_KEYWORD= 5 | TEMPERATURE=0.6 6 | BLOCK_WORDS="VPN" 7 | CHATGPT_BLOCK_WORDS="VPN" 8 | WECHATY_PUPPET=wechaty-puppet-wechat -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | title: "[Bug]: " 3 | labels: ["bug"] 4 | description: "Create a report to help us improve." 5 | body: 6 | - type: checkboxes 7 | id: terms 8 | attributes: 9 | label: Welcome 10 | options: 11 | - label: Yes, I've searched similar issues on GitHub and didn't find any. 12 | required: true 13 | - label: Yes, I've included all information below (version, **FULL** config, **FULL** log, etc). 14 | required: true 15 | - type: checkboxes 16 | id: platform 17 | attributes: 18 | label: Deployment Platform 19 | options: 20 | - label: Railway 21 | - label: Fly.io 22 | - label: Docker & Docker Compose 23 | - label: Node.js 24 | - type: textarea 25 | id: problem 26 | attributes: 27 | label: Description of the problem 28 | placeholder: Your problem description 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: log 33 | attributes: 34 | label: wechat-chatgpt operation log 35 | value: |- 36 |
37 | ```console 38 | # Paste output here 39 | ``` 40 |
41 | validations: 42 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord Chat 4 | url: https://discord.gg/8xWdppS7bE 5 | about: Join our Discord server to chat with the community and get help with your issue. -------------------------------------------------------------------------------- /.github/workflows/publish-docker-hub.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docker image 2 | 3 | on: 4 | # Test pub with dev tag 5 | push: 6 | branches: 7 | - ci/fix-muti-platform 8 | release: 9 | types: [published] 10 | 11 | jobs: 12 | push_to_registry: 13 | name: Push Docker image to Docker Hub 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Check out the repo 17 | uses: actions/checkout@v3 18 | 19 | - name: Set up QEMU 20 | uses: docker/setup-qemu-action@v2 21 | 22 | - name: Set up Docker Buildx 23 | uses: docker/setup-buildx-action@v2 24 | 25 | - name: Log in to Docker Hub 26 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 27 | with: 28 | username: ${{ secrets.DOCKER_USERNAME }} 29 | password: ${{ secrets.DOCKER_PASSWORD }} 30 | 31 | - name: Extract metadata (tags, labels) for Docker 32 | id: meta 33 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 34 | with: 35 | images: holegots/wechat-chatgpt 36 | 37 | - name: Build and push Docker image 38 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc 39 | with: 40 | context: . 41 | push: true 42 | tags: ${{ steps.meta.outputs.tags }} 43 | # FIXME: Chrome doesn't seem to install on ARM, so skip it for now 44 | # platforms: linux/amd64,linux/arm64 45 | labels: ${{ steps.meta.outputs.labels }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | .pnpm-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # Snowpack dependency directory (https://snowpack.dev/) 50 | web_modules/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional stylelint cache 62 | .stylelintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variable files 80 | .env 81 | .env.development.local 82 | .env.test.local 83 | .env.production.local 84 | .env.local 85 | 86 | # parcel-bundler cache (https://parceljs.org/) 87 | .cache 88 | .parcel-cache 89 | 90 | # Next.js build output 91 | .next 92 | out 93 | 94 | # Nuxt.js build / generate output 95 | .nuxt 96 | dist 97 | 98 | # Gatsby files 99 | .cache/ 100 | # Comment in the public line in if your project uses Gatsby and not Next.js 101 | # https://nextjs.org/blog/next-9-1#public-directory-support 102 | # public 103 | 104 | # vuepress build output 105 | .vuepress/dist 106 | 107 | # vuepress v2.x temp and cache directory 108 | .temp 109 | 110 | # Docusaurus cache and generated files 111 | .docusaurus 112 | 113 | # Serverless directories 114 | .serverless/ 115 | 116 | # FuseBox cache 117 | .fusebox/ 118 | 119 | # DynamoDB Local files 120 | .dynamodb/ 121 | 122 | # TernJS port file 123 | .tern-port 124 | 125 | # Stores VSCode versions used for testing VSCode extensions 126 | .vscode-test 127 | 128 | # yarn v2 129 | .yarn/cache 130 | .yarn/unplugged 131 | .yarn/build-state.yml 132 | .yarn/install-state.gz 133 | .pnp.* 134 | 135 | ### Node Patch ### 136 | # Serverless Webpack directories 137 | .webpack/ 138 | 139 | # Optional stylelint cache 140 | 141 | # SvelteKit build / generate output 142 | .svelte-kit 143 | 144 | # End of https://www.toptal.com/developers/gitignore/api/node 145 | n 146 | *memory-card.json 147 | # Created by https://www.toptal.com/developers/gitignore/api/python 148 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 149 | 150 | ### Python ### 151 | # Byte-compiled / optimized / DLL files 152 | __pycache__/ 153 | *.py[cod] 154 | *$py.class 155 | 156 | # C extensions 157 | *.so 158 | 159 | # Distribution / packaging 160 | .Python 161 | build/ 162 | develop-eggs/ 163 | dist/ 164 | downloads/ 165 | eggs/ 166 | .eggs/ 167 | lib/ 168 | lib64/ 169 | parts/ 170 | sdist/ 171 | var/ 172 | wheels/ 173 | share/python-wheels/ 174 | *.egg-info/ 175 | .installed.cfg 176 | *.egg 177 | MANIFEST 178 | 179 | # PyInstaller 180 | # Usually these files are written by a python script from a template 181 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 182 | *.manifest 183 | *.spec 184 | 185 | # Installer logs 186 | pip-log.txt 187 | pip-delete-this-directory.txt 188 | 189 | # Unit test / coverage reports 190 | htmlcov/ 191 | .tox/ 192 | .nox/ 193 | .coverage 194 | .coverage.* 195 | .cache 196 | nosetests.xml 197 | coverage.xml 198 | *.cover 199 | *.py,cover 200 | .hypothesis/ 201 | .pytest_cache/ 202 | cover/ 203 | 204 | # Translations 205 | *.mo 206 | *.pot 207 | 208 | # Django stuff: 209 | *.log 210 | local_settings.py 211 | db.sqlite3 212 | db.sqlite3-journal 213 | 214 | # Flask stuff: 215 | instance/ 216 | .webassets-cache 217 | 218 | # Scrapy stuff: 219 | .scrapy 220 | 221 | # Sphinx documentation 222 | docs/_build/ 223 | 224 | # PyBuilder 225 | .pybuilder/ 226 | target/ 227 | 228 | # Jupyter Notebook 229 | .ipynb_checkpoints 230 | 231 | # IPython 232 | profile_default/ 233 | ipython_config.py 234 | 235 | # pyenv 236 | # For a library or package, you might want to ignore these files since the code is 237 | # intended to run in multiple environments; otherwise, check them in: 238 | # .python-version 239 | 240 | # pipenv 241 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 242 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 243 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 244 | # install all needed dependencies. 245 | #Pipfile.lock 246 | 247 | # poetry 248 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 249 | # This is especially recommended for binary packages to ensure reproducibility, and is more 250 | # commonly ignored for libraries. 251 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 252 | #poetry.lock 253 | 254 | # pdm 255 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 256 | #pdm.lock 257 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 258 | # in version control. 259 | # https://pdm.fming.dev/#use-with-ide 260 | .pdm.toml 261 | 262 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 263 | __pypackages__/ 264 | 265 | # Celery stuff 266 | celerybeat-schedule 267 | celerybeat.pid 268 | 269 | # SageMath parsed files 270 | *.sage.py 271 | 272 | # Environments 273 | .env 274 | .venv 275 | env/ 276 | venv/ 277 | ENV/ 278 | env.bak/ 279 | venv.bak/ 280 | 281 | # Spyder project settings 282 | .spyderproject 283 | .spyproject 284 | 285 | # Rope project settings 286 | .ropeproject 287 | 288 | # mkdocs documentation 289 | /site 290 | 291 | # mypy 292 | .mypy_cache/ 293 | .dmypy.json 294 | dmypy.json 295 | 296 | # Pyre type checker 297 | .pyre/ 298 | 299 | # pytype static type analyzer 300 | .pytype/ 301 | 302 | # Cython debug symbols 303 | cython_debug/ 304 | 305 | # PyCharm 306 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 307 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 308 | # and can be added to the global gitignore or merged into this file. For a more nuclear 309 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 310 | .idea/ 311 | 312 | ### Python Patch ### 313 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 314 | poetry.toml 315 | 316 | 317 | # End of https://www.toptal.com/developers/gitignore/api/python 318 | n 319 | config.json 320 | cache.json 321 | config.yaml 322 | .vscode 323 | 324 | data/ 325 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:19 AS app 2 | 3 | # We don't need the standalone Chromium 4 | RUN apt-get install -y wget \ 5 | && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ 6 | && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \ 7 | && apt-get update && apt-get -y install google-chrome-stable chromium xvfb\ 8 | && rm -rf /var/lib/apt/lists/* \ 9 | && echo "Chrome: " && google-chrome --version 10 | WORKDIR /app 11 | COPY package*.json ./ 12 | RUN npm install 13 | COPY . . 14 | CMD xvfb-run --server-args="-screen 0 1280x800x24 -ac -nolisten tcp -dpi 96 +extension RANDR" npm run dev -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

!!!! Project Archived 📦 !!!!

4 | 5 | > This project has been archived. Thank you to everyone who contributed! 🙌😔 6 |
7 | 8 | 9 |

Welcome to wechat-chatgpt 👋

10 |

11 | Version 12 | 13 | License: ISC 14 | 15 | 16 | Twitter: fuergaosi 17 | 18 | 19 | 20 | join discord community of github profile readme generator 21 | 22 |

23 | 24 | > Use ChatGPT On Wechat via wechaty 25 | > English | [中文文档](README_ZH.md) 26 | 27 | [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/dMLG70?referralCode=bIYugQ) 28 | 29 | ## 🌟 Features 30 | 31 | - Interact with WeChat and ChatGPT: 32 | - Use ChatGPT on WeChat with [wechaty](https://github.com/wechaty/wechaty) and [Official API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis) 33 | - Add conversation support 34 | - Support command setting 35 | 36 | - Deployment and configuration options: 37 | - Add Dockerfile, deployable with [docker](#use-with-docker) 38 | - Support deployment using [docker compose](#use-with-docker-compose) 39 | - Support [Railway](#use-with-railway) and [Fly.io](#use-with-flyio) deployment 40 | 41 | - Other features: 42 | - Support [Dall·E](https://labs.openai.com/) 43 | - Support [whisper](https://openai.com/blog/introducing-chatgpt-and-whisper-apis) 44 | - Support setting prompt 45 | - Support proxy (in development) 46 | 47 | ## 🚀 Usage 48 | - [Use with Railway](#use-with-railway)(PaaS, Free, Stable, ✅Recommended) 49 | - [Use with Fly.io](#use-with-flyio)(Paas, Free, ✅Recommended) 50 | - [Use with docker](#use-with-docker)(Self-hosted, Stable, ✅Recommended) 51 | - [Use with docker compose](#use-with-docker-compose)(Self-hosted, Stable, ✅Recommended) 52 | - [Use with nodejs](#use-with-nodejs)(Self-hosted) 53 | 54 | ## Use with Railway 55 | > Railway offers $5 or 500 hours of runtime per month 56 | 1. Click the [Railway](https://railway.app/template/dMLG70?referralCode=bIYugQ) button to go to the Railway deployment page 57 | 2. Click the `Deploy Now` button to enter the Railway deployment page 58 | 3. Fill in the repository name and `OPENAI_API_KEY` (need to link GitHub account) 59 | 4. Click the `Deploy` button 60 | 5. Click the `View Logs` button and wait for the deployment to complete 61 | 62 | ## Use with Fly.io 63 | > Please allocate 512MB memory for the application to meet the application requirements 64 | 65 | > fly.io offers free bills up to $5(Free Allowances 3 256MB are not included in the bill) 66 | 1. Install [flyctl](https://fly.io/docs/getting-started/installing-flyctl/) 67 | ```shell 68 | # macOS 69 | brew install flyctl 70 | # Windows 71 | scoop install flyctl 72 | # Linux 73 | curl https://fly.io/install.sh | sh 74 | ``` 75 | 2. Clone the project and enter the project directory 76 | ```shell 77 | git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt 78 | ``` 79 | 3. Create a new app 80 | ```shell 81 | ➜ flyctl launch 82 | ? Would you like to copy its configuration to the new app? No 83 | ? App Name (leave blank to use an auto-generated name): 84 | ? Select region: 85 | ? Would you like to setup a Postgresql database now? No 86 | ? Would you like to deploy now? No 87 | ``` 88 | 4. Configure the environment variables 89 | ```shell 90 | flyctl secrets set OPENAI_API_KEY="" MODEL="" 91 | ``` 92 | 5. Deploy the app 93 | ```shell 94 | flyctl deploy 95 | ``` 96 | 97 | ## Use with docker 98 | 99 | ```sh 100 | # pull image 101 | docker pull holegots/wechat-chatgpt 102 | # run container 103 | docker run -d --name wechat-chatgpt \ 104 | -e OPENAI_API_KEY= \ 105 | -e MODEL="gpt-3.5-turbo" \ 106 | -e CHAT_PRIVATE_TRIGGER_KEYWORD="" \ 107 | -v $(pwd)/data:/app/data/wechat-assistant.memory-card.json \ 108 | holegots/wechat-chatgpt:latest 109 | # View the QR code to log in to wechat 110 | docker logs -f wechat-chatgpt 111 | ``` 112 | > How to get OPENAI API KEY? [Click here](https://platform.openai.com/account/api-keys) 113 | 114 | ## Use with docker compose 115 | 116 | ```sh 117 | # Copy the configuration file according to the template 118 | cp .env.example .env 119 | # Edit the configuration file 120 | vim .env 121 | # Start the container 122 | docker-compose up -d 123 | # View the QR code to log in to wechat 124 | docker logs -f wechat-chatgpt 125 | ``` 126 | 127 | ## Use with nodejs 128 | 129 | > You need NodeJS 18.0.0 version and above 130 | 131 | ```sh 132 | # Clone the project 133 | git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt 134 | # Install dependencies 135 | npm install 136 | # Copy the configuration file according to the template 137 | cp .env.example .env 138 | # Edit the configuration file 139 | vim .env 140 | # Start project 141 | npm run dev 142 | ``` 143 | 144 | > Please make sure your WeChat account can log in [WeChat on web](https://wx.qq.com/) 145 | 146 | ## 📝 Environment Variables 147 | 148 | | name | description | 149 | |------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 150 | | API | API endpoint of ChatGPT | 151 | | OPENAI_API_KEY | [create new secret key](https://platform.openai.com/account/api-keys) | 152 | | MODEL | ID of the model to use. Currently, only gpt-3.5-turbo and gpt-3.5-turbo-0301 are supported. | 153 | | TEMPERATURE | What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. | 154 | | CHAT_TRIGGER_RULE | Private chat triggering rules. | 155 | | DISABLE_GROUP_MESSAGE | Prohibited to use ChatGPT in group chat. | 156 | | CHAT_PRIVATE_TRIGGER_KEYWORD | Keyword to trigger ChatGPT reply in WeChat private chat | 157 | | BLOCK_WORDS | Chat blocker words, (works for both private and group chats, Use, Split) | 158 | | CHATGPT_BLOCK_WORDS | The blocked words returned by ChatGPT(works for both private and group chats, Use, Split) | 159 | 160 | ## 📝 Using Custom ChatGPT API 161 | 162 | > https://github.com/fuergaosi233/openai-proxy 163 | 164 | ```shell 165 | # Clone the project 166 | git clone https://github.com/fuergaosi233/openai-proxy 167 | # Install dependencies 168 | npm install && npm install -g wrangler && npm run build 169 | # Deploy to CloudFlare Workers 170 | npm run deploy 171 | # Custom domain (optional) 172 | Add `Route` to `wrangler.toml` 173 | routes = [ 174 | { pattern = "Your Custom Domain", custom_domain = true }, 175 | ] 176 | ``` 177 | 178 | ## ⌨️ Commands 179 | > Enter in the WeChat chat box 180 | ```shell 181 | /cmd help # Show help 182 | /cmd prompt # Set prompt 183 | /cmd clear # Clear all sessions since last boot 184 | ``` 185 | 186 | ## ✨ Contributor 187 | 188 | 189 | 190 | 191 | 192 | ## 🤝 Contributing 193 | 194 | Contributions, issues and feature requests are welcome!
Feel free to 195 | check [issues page](https://github.com/fuergaosi233/wechat-chatgpt/issues). 196 | 197 | ## Show your support 198 | 199 | Give a ⭐️ if this project helped you! 200 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 |

欢迎使用 wechat-chatgpt 👋

2 |

3 | Version 4 | 5 | License: ISC 6 | 7 | 8 | Twitter: fuergaosi 9 | 10 | 11 | join discord community of github profile readme generator 12 | 13 |

14 | 15 | > 在微信上迅速接入 ChatGPT,让它成为你最好的助手! 16 | > [English](README.md) | 中文文档 17 | 18 | [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/dMLG70?referralCode=bIYugQ) 19 | 20 | ## 🌟 功能点 21 | 22 | - 使用 WeChat 和 ChatGPT 进行互动: 23 | - 基于 [wechaty](https://github.com/wechaty/wechaty) 和 [Official API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis) 在微信中使用 ChatGPT 24 | - 支持多轮对话 25 | - 支持[命令](#-命令)设置 26 | 27 | - 部署和配置选项: 28 | - 提供 Dockerfile,可以通过 [docker](#通过docker使用) 进行部署 29 | - 支持使用 [docker compose](#通过docker-compose使用) 进行部署 30 | - 支持在 [Railway](#使用railway进行部署) 和 [Fly.io](#通过flyio进行部署) 上部署 31 | 32 | - 其他功能: 33 | - 支持 [Dall·E](https://labs.openai.com/) 34 | - 支持 [whisper](https://openai.com/blog/introducing-chatgpt-and-whisper-apis) 35 | - 支持设置 prompt 36 | - 支持代理(开发中) 37 | 38 | ## 🚀 使用 39 | 40 | - [在 Railway 部署](#使用railway进行部署)(PaaS, 免费, 稳定, ✅推荐) 41 | - [在 Fly.io 部署](#通过flyio进行部署)(PaaS, 免费, ✅推荐) 42 | - [使用 Docker 部署](#通过docker使用)(自托管, 稳定, ✅推荐) 43 | - [使用 Docker Compose 部署](#通过docker-compose使用)(自托管, 稳定, ✅推荐) 44 | - [使用 NodeJS 部署](#使用nodejs运行) 45 | 46 | ## 使用Railway进行部署 47 | 48 | > Railway 是一个免费的 PaaS 平台,5刀以内的账单免费或者每个月500小时的运行时间 49 | 50 | 1. 点击 [Railway](https://railway.app/template/dMLG70?referralCode=bIYugQ) 按钮,进入 Railway 部署页面 51 | 2. 点击 `Deploy Now` 按钮,进入 Railway 部署页面 52 | 3. 填写 仓库名称和 `OPENAI_API_KEY`(需要连接 GitHub 账号) 53 | 4. 点击 `Deploy` 按钮 54 | 5. 点击 `View Logs` 按钮,等待部署完成 55 | 56 | ## 通过Fly.io进行部署 57 | 58 | > 请为应用程序分配 512 MB 内存,否则可能会出现内存溢出 59 | 60 | > Fly.io 5刀以内的账单免费(免费计划的3个256MB的应用不在账单内)也就是可以同时可以部署 `1*512MB + 3*256MB` 61 | 62 | 1. 安装 [flyctl](https://fly.io/docs/getting-started/installing-flyctl/) 63 | ```shell 64 | # macOS 65 | brew install flyctl 66 | # Windows 67 | scoop install flyctl 68 | # Linux 69 | curl https://fly.io/install.sh | sh 70 | ``` 71 | 2. 克隆项目并进入项目目录 72 | ```shell 73 | git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt 74 | ``` 75 | 3. 创建应用 76 | ```shell 77 | ➜ flyctl launch 78 | ? Would you like to copy its configuration to the new app? No 79 | ? App Name (leave blank to use an auto-generated name): 80 | ? Select region: 81 | ? Would you like to setup a Postgresql database now? No 82 | ? Would you like to deploy now? No 83 | ``` 84 | 4. 配置环境变量 85 | ```shell 86 | flyctl secrets set OPENAI_API_KEY="" MODEL="" 87 | ``` 88 | 5. 部署应用 89 | ```shell 90 | flyctl deploy 91 | ``` 92 | 93 | ## 通过Docker使用 94 | 95 | ```sh 96 | # 拉取镜像 97 | docker pull holegots/wechat-chatgpt:latest 98 | # 运行容器 99 | docker run -it --name wechat-chatgpt \ 100 | -e OPENAI_API_KEY= \ 101 | -e MODEL="gpt-3.5-turbo" \ 102 | -e CHAT_PRIVATE_TRIGGER_KEYWORD="" \ 103 | -v $(pwd)/data:/app/data/wechat-assistant.memory-card.json \ 104 | holegots/wechat-chatgpt:latest 105 | # 使用二维码登陆 106 | docker logs -f wechat-chatgpt 107 | ``` 108 | 109 | > 如何获取 OPENAI API KEY?请参考 [OpenAI API](https://platform.openai.com/account/api-keys)。 110 | 111 | ## 通过docker compose使用 112 | 113 | ```sh 114 | # 根据模板拷贝配置文件 115 | cp .env.example .env 116 | # 使用你喜欢的文本编辑器修改配置文件 117 | vim .env 118 | # 在Linux或WindowsPowerShell上运行如下命令 119 | docker compose up -d 120 | # 使用二维码登陆 121 | docker logs -f wechat-chatgpt 122 | ``` 123 | 124 | ## 使用NodeJS运行 125 | 126 | > 请确认安装的NodeJS版本为18.0.0以上 127 | 128 | ```sh 129 | # 克隆项目 130 | git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt 131 | # 安装依赖 132 | npm install 133 | # 编辑配置 134 | cp .env.example .env 135 | vim .env # 使用你喜欢的文本编辑器修改配置文件 136 | # 启动项目 137 | npm run dev 138 | # 如果您是初次登陆,那么需要扫描二维码 139 | ``` 140 | 141 | > 请确保您的账号可以登陆 [网页版微信](https://wx.qq.com/)。 142 | 143 | ## 📝 Environment Variables 144 | 145 | | name | default | example | description | 146 | |--------------------------|------------------------|------------------------------------------------|-------------------------------------------------------------| 147 | | API | https://api.openai.com | | 自定义ChatGPT API 地址 | 148 | | OPENAI_API_KEY | 123456789 | sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | [创建你的 API 密钥](https://platform.openai.com/account/api-keys) | 149 | | MODEL | gpt-3.5-turbo | | 要使用的模型ID, 目前仅支持`gpt-3.5-turbo` 和 `gpt-3.5-turbo-0301` | 150 | | TEMPERATURE | 0.6 | | 在0和2之间。较高的数值如0.8会使 ChatGPT 输出更加随机,而较低的数值如0.2会使其更加稳定。 | 151 | | CHAT_TRIGGER_RULE | | | 私聊触发规则 | 152 | | DISABLE_GROUP_MESSAGE | true | | 禁用在群聊里使用ChatGPT | 153 | | CHAT_PRIVATE_TRIGGER_KEYWORD | | | 在私聊中触发ChatGPT的关键词, 默认是无需关键词即可触发 | 154 | | BLOCK_WORDS | "VPN" | "WORD1,WORD2,WORD3" | 聊天屏蔽关键词(同时在群组和私聊中生效, 避免 bot 用户恶意提问导致封号 | 155 | | CHATGPT_BLOCK_WORDS | "VPN" | "WORD1,WORD2,WORD3" | ChatGPT回复屏蔽词, 如果ChatGPT的回复中包含了屏蔽词, 则不回复 | 156 | 157 | ## 📝 使用自定义ChatGPT API 158 | > https://github.com/fuergaosi233/openai-proxy 159 | ```shell 160 | # 克隆项目 161 | git clone https://github.com/fuergaosi233/openai-proxy 162 | # 安装依赖 163 | npm install && npm install -g wrangler && npm run build 164 | # 部署到 CloudFlare Workers 165 | npm run deploy 166 | # 自定义域名(可选) 167 | 添加 `Route`` 到 `wrangler.toml` 168 | routes = [ 169 | { pattern = "Your Custom Domain", custom_domain = true }, 170 | ] 171 | ``` 172 | 173 | ## ⌨️ 命令 174 | > 在微信聊天框中输入 175 | ```shell 176 | /cmd help # 显示帮助信息 177 | /cmd prompt # 设置ChatGPT Prompt 178 | /cmd clear # 清除WeChat-ChatGPT保存的会话记录 179 | ``` 180 | 181 | ## ✨ Contributor 182 | 183 | 184 | 185 | 186 | 187 | ## 🤝 为项目添砖加瓦 188 | 189 | 欢迎提出 Contributions, issues 与 feature requests!
190 | 随时查看 [issues page](https://github.com/fuergaosi233/wechat-chatgpt/issues). 191 | 192 | ## 感谢支持 🙏 193 | 194 | 如果这个项目对你产生了一点的帮助,请为这个项目点上一颗 ⭐️ -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | wechat-chatgpt: 4 | image: wechat-chatgpt 5 | build: . 6 | volumes: 7 | - ./data/wechat-assistant.memory-card.json:/app/wechat-assistant.memory-card.json 8 | env_file: 9 | - .env -------------------------------------------------------------------------------- /docs/images/railway-deployed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuergaosi233/wechat-chatgpt/e96349ffa2173b20135809601eb7ee7b996a3111/docs/images/railway-deployed.png -------------------------------------------------------------------------------- /docs/images/railway-deploying.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuergaosi233/wechat-chatgpt/e96349ffa2173b20135809601eb7ee7b996a3111/docs/images/railway-deploying.png -------------------------------------------------------------------------------- /docs/images/railway-succeed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuergaosi233/wechat-chatgpt/e96349ffa2173b20135809601eb7ee7b996a3111/docs/images/railway-succeed.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat-chatgpt", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/main.js", 6 | "export": "dist/main.js", 7 | "scripts": { 8 | "dev": "nodemon --exec node --loader ts-node/esm src/main.ts", 9 | "build": "tsc" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "async-retry": "^1.3.3", 15 | "dotenv": "^16.0.3", 16 | "execa": "^6.1.0", 17 | "gpt3-tokenizer": "^1.1.5", 18 | "openai": "^3.2.1", 19 | "qrcode": "^1.5.1", 20 | "uuid": "^9.0.0", 21 | "wechaty": "^1.20.2", 22 | "wechaty-puppet-wechat": "^1.18.4" 23 | }, 24 | "devDependencies": { 25 | "@types/async-retry": "^1.4.5", 26 | "@types/qrcode": "^1.5.0", 27 | "@types/uuid": "^9.0.0", 28 | "nodemon": "^2.0.20", 29 | "ts-node": "^10.9.1" 30 | }, 31 | "nodemonConfig": { 32 | "watch": [ 33 | "src/*.ts" 34 | ], 35 | "ignore": [ 36 | "src/main.ts" 37 | ], 38 | "ext": "ts", 39 | "exec": "node --loader ts-node/esm src/main.ts", 40 | "delay": 500 41 | }, 42 | "type": "module" 43 | } 44 | -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /src/bot.ts: -------------------------------------------------------------------------------- 1 | import { config } from "./config.js"; 2 | import {ContactImpl, ContactInterface, RoomImpl, RoomInterface} from "wechaty/impls"; 3 | import { Message } from "wechaty"; 4 | import {FileBox} from "file-box"; 5 | import {chatgpt, dalle, whisper} from "./openai.js"; 6 | import DBUtils from "./data.js"; 7 | import { regexpEncode } from "./utils.js"; 8 | enum MessageType { 9 | Unknown = 0, 10 | Attachment = 1, // Attach(6), 11 | Audio = 2, // Audio(1), Voice(34) 12 | Contact = 3, // ShareCard(42) 13 | ChatHistory = 4, // ChatHistory(19) 14 | Emoticon = 5, // Sticker: Emoticon(15), Emoticon(47) 15 | Image = 6, // Img(2), Image(3) 16 | Text = 7, // Text(1) 17 | Location = 8, // Location(48) 18 | MiniProgram = 9, // MiniProgram(33) 19 | GroupNote = 10, // GroupNote(53) 20 | Transfer = 11, // Transfers(2000) 21 | RedEnvelope = 12, // RedEnvelopes(2001) 22 | Recalled = 13, // Recalled(10002) 23 | Url = 14, // Url(5) 24 | Video = 15, // Video(4), Video(43) 25 | Post = 16, // Moment, Channel, Tweet, etc 26 | } 27 | const SINGLE_MESSAGE_MAX_SIZE = 500; 28 | type Speaker = RoomImpl | ContactImpl; 29 | interface ICommand{ 30 | name:string; 31 | description:string; 32 | exec: (talker:Speaker, text:string) => Promise; 33 | } 34 | export class ChatGPTBot { 35 | chatPrivateTriggerKeyword = config.chatPrivateTriggerKeyword; 36 | chatTriggerRule = config.chatTriggerRule? new RegExp(config.chatTriggerRule): undefined; 37 | disableGroupMessage = config.disableGroupMessage || false; 38 | botName: string = ""; 39 | ready = false; 40 | setBotName(botName: string) { 41 | this.botName = botName; 42 | } 43 | get chatGroupTriggerRegEx(): RegExp { 44 | return new RegExp(`^@${regexpEncode(this.botName)}\\s`); 45 | } 46 | get chatPrivateTriggerRule(): RegExp | undefined { 47 | const { chatPrivateTriggerKeyword, chatTriggerRule } = this; 48 | let regEx = chatTriggerRule 49 | if (!regEx && chatPrivateTriggerKeyword) { 50 | regEx = new RegExp(regexpEncode(chatPrivateTriggerKeyword)) 51 | } 52 | return regEx 53 | } 54 | private readonly commands:ICommand[] = [ 55 | { 56 | name: "help", 57 | description: "显示帮助信息", 58 | exec: async (talker) => { 59 | await this.trySay(talker,"========\n" + 60 | "/cmd help\n" + 61 | "# 显示帮助信息\n" + 62 | "/cmd prompt \n" + 63 | "# 设置当前会话的 prompt \n" + 64 | "/img \n" + 65 | "# 根据 prompt 生成图片\n" + 66 | "/cmd clear\n" + 67 | "# 清除自上次启动以来的所有会话\n" + 68 | "========"); 69 | } 70 | }, 71 | { 72 | name: "prompt", 73 | description: "设置当前会话的prompt", 74 | exec: async (talker, prompt) => { 75 | if (talker instanceof RoomImpl) { 76 | DBUtils.setPrompt(await talker.topic(), prompt); 77 | }else { 78 | DBUtils.setPrompt(talker.name(), prompt); 79 | } 80 | } 81 | }, 82 | { 83 | name: "clear", 84 | description: "清除自上次启动以来的所有会话", 85 | exec: async (talker) => { 86 | if (talker instanceof RoomImpl) { 87 | DBUtils.clearHistory(await talker.topic()); 88 | }else{ 89 | DBUtils.clearHistory(talker.name()); 90 | } 91 | } 92 | } 93 | ] 94 | 95 | /** 96 | * EXAMPLE: 97 | * /cmd help 98 | * /cmd prompt 99 | * /cmd img 100 | * /cmd clear 101 | * @param contact 102 | * @param rawText 103 | */ 104 | async command(contact: any, rawText: string): Promise { 105 | const [commandName, ...args] = rawText.split(/\s+/); 106 | const command = this.commands.find( 107 | (command) => command.name === commandName 108 | ); 109 | if (command) { 110 | await command.exec(contact, args.join(" ")); 111 | } 112 | } 113 | // remove more times conversation and mention 114 | cleanMessage(rawText: string, privateChat: boolean = false): string { 115 | let text = rawText; 116 | const item = rawText.split("- - - - - - - - - - - - - - -"); 117 | if (item.length > 1) { 118 | text = item[item.length - 1]; 119 | } 120 | 121 | const { chatTriggerRule, chatPrivateTriggerRule } = this; 122 | 123 | if (privateChat && chatPrivateTriggerRule) { 124 | text = text.replace(chatPrivateTriggerRule, "") 125 | } else if (!privateChat) { 126 | text = text.replace(this.chatGroupTriggerRegEx, "") 127 | text = chatTriggerRule? text.replace(chatTriggerRule, ""): text 128 | } 129 | // remove more text via - - - - - - - - - - - - - - - 130 | return text 131 | } 132 | async getGPTMessage(talkerName: string,text: string): Promise { 133 | let gptMessage = await chatgpt(talkerName,text); 134 | if (gptMessage !=="") { 135 | DBUtils.addAssistantMessage(talkerName,gptMessage); 136 | return gptMessage; 137 | } 138 | return "Sorry, please try again later. 😔"; 139 | } 140 | // Check if the message returned by chatgpt contains masked words] 141 | checkChatGPTBlockWords(message: string): boolean { 142 | if (config.chatgptBlockWords.length == 0) { 143 | return false; 144 | } 145 | return config.chatgptBlockWords.some((word) => message.includes(word)); 146 | } 147 | // The message is segmented according to its size 148 | async trySay( 149 | talker: RoomInterface | ContactInterface, 150 | mesasge: string 151 | ): Promise { 152 | const messages: Array = []; 153 | if (this.checkChatGPTBlockWords(mesasge)) { 154 | console.log(`🚫 Blocked ChatGPT: ${mesasge}`); 155 | return; 156 | } 157 | let message = mesasge; 158 | while (message.length > SINGLE_MESSAGE_MAX_SIZE) { 159 | messages.push(message.slice(0, SINGLE_MESSAGE_MAX_SIZE)); 160 | message = message.slice(SINGLE_MESSAGE_MAX_SIZE); 161 | } 162 | messages.push(message); 163 | for (const msg of messages) { 164 | await talker.say(msg); 165 | } 166 | } 167 | // Check whether the ChatGPT processing can be triggered 168 | triggerGPTMessage(text: string, privateChat: boolean = false): boolean { 169 | const { chatTriggerRule } = this; 170 | let triggered = false; 171 | if (privateChat) { 172 | const regEx = this.chatPrivateTriggerRule 173 | triggered = regEx? regEx.test(text): true; 174 | } else { 175 | triggered = this.chatGroupTriggerRegEx.test(text); 176 | // group message support `chatTriggerRule` 177 | if (triggered && chatTriggerRule) { 178 | triggered = chatTriggerRule.test(text.replace(this.chatGroupTriggerRegEx, "")) 179 | } 180 | } 181 | if (triggered) { 182 | console.log(`🎯 Triggered ChatGPT: ${text}`); 183 | } 184 | return triggered; 185 | } 186 | // Check whether the message contains the blocked words. if so, the message will be ignored. if so, return true 187 | checkBlockWords(message: string): boolean { 188 | if (config.blockWords.length == 0) { 189 | return false; 190 | } 191 | return config.blockWords.some((word) => message.includes(word)); 192 | } 193 | // Filter out the message that does not need to be processed 194 | isNonsense( 195 | talker: ContactInterface, 196 | messageType: MessageType, 197 | text: string 198 | ): boolean { 199 | return ( 200 | talker.self() || 201 | // TODO: add doc support 202 | !(messageType == MessageType.Text || messageType == MessageType.Audio) || 203 | talker.name() === "微信团队" || 204 | // 语音(视频)消息 205 | text.includes("收到一条视频/语音聊天消息,请在手机上查看") || 206 | // 红包消息 207 | text.includes("收到红包,请在手机上查看") || 208 | // Transfer message 209 | text.includes("收到转账,请在手机上查看") || 210 | // 位置消息 211 | text.includes("/cgi-bin/mmwebwx-bin/webwxgetpubliclinkimg") || 212 | // 聊天屏蔽词 213 | this.checkBlockWords(text) 214 | ); 215 | } 216 | 217 | async onPrivateMessage(talker: ContactInterface, text: string) { 218 | const gptMessage = await this.getGPTMessage(talker.name(),text); 219 | await this.trySay(talker, gptMessage); 220 | } 221 | 222 | async onGroupMessage( 223 | talker: ContactInterface, 224 | text: string, 225 | room: RoomInterface 226 | ) { 227 | const gptMessage = await this.getGPTMessage(await room.topic(),text); 228 | const result = `@${talker.name()} ${text}\n\n------\n ${gptMessage}`; 229 | await this.trySay(room, result); 230 | } 231 | async onMessage(message: Message) { 232 | const talker = message.talker(); 233 | const rawText = message.text(); 234 | const room = message.room(); 235 | const messageType = message.type(); 236 | const privateChat = !room; 237 | if (privateChat) { 238 | console.log(`🤵 Contact: ${talker.name()} 💬 Text: ${rawText}`) 239 | } else { 240 | const topic = await room.topic() 241 | console.log(`🚪 Room: ${topic} 🤵 Contact: ${talker.name()} 💬 Text: ${rawText}`) 242 | } 243 | if (this.isNonsense(talker, messageType, rawText)) { 244 | return; 245 | } 246 | if (messageType == MessageType.Audio){ 247 | // 保存语音文件 248 | const fileBox = await message.toFileBox(); 249 | let fileName = "./public/" + fileBox.name; 250 | await fileBox.toFile(fileName, true).catch((e) => { 251 | console.log("保存语音失败",e); 252 | return; 253 | }); 254 | // Whisper 255 | whisper("",fileName).then((text) => { 256 | message.say(text); 257 | }) 258 | return; 259 | } 260 | if (rawText.startsWith("/cmd ")){ 261 | console.log(`🤖 Command: ${rawText}`) 262 | const cmdContent = rawText.slice(5) // 「/cmd 」一共5个字符(注意空格) 263 | if (privateChat) { 264 | await this.command(talker, cmdContent); 265 | }else{ 266 | await this.command(room, cmdContent); 267 | } 268 | return; 269 | } 270 | // 使用DallE生成图片 271 | if (rawText.startsWith("/img")){ 272 | console.log(`🤖 Image: ${rawText}`) 273 | const imgContent = rawText.slice(4) 274 | if (privateChat) { 275 | let url = await dalle(talker.name(), imgContent) as string; 276 | const fileBox = FileBox.fromUrl(url) 277 | message.say(fileBox) 278 | }else{ 279 | let url = await dalle(await room.topic(), imgContent) as string; 280 | const fileBox = FileBox.fromUrl(url) 281 | message.say(fileBox) 282 | } 283 | return; 284 | } 285 | if (this.triggerGPTMessage(rawText, privateChat)) { 286 | const text = this.cleanMessage(rawText, privateChat); 287 | if (privateChat) { 288 | return await this.onPrivateMessage(talker, text); 289 | } else{ 290 | if (!this.disableGroupMessage){ 291 | return await this.onGroupMessage(talker, text, room); 292 | } else { 293 | return; 294 | } 295 | } 296 | } else { 297 | return; 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | dotenv.config(); 3 | import { IConfig } from "./interface"; 4 | 5 | export const config: IConfig = { 6 | api: process.env.API, 7 | openai_api_key: process.env.OPENAI_API_KEY || "123456789", 8 | model: process.env.MODEL || "gpt-3.5-turbo", 9 | chatPrivateTriggerKeyword: process.env.CHAT_PRIVATE_TRIGGER_KEYWORD || "", 10 | chatTriggerRule: process.env.CHAT_TRIGGER_RULE || "", 11 | disableGroupMessage: process.env.DISABLE_GROUP_MESSAGE === "true", 12 | temperature: process.env.TEMPERATURE ? parseFloat(process.env.TEMPERATURE) : 0.6, 13 | blockWords: process.env.BLOCK_WORDS?.split(",") || [], 14 | chatgptBlockWords: process.env.CHATGPT_BLOCK_WORDS?.split(",") || [], 15 | }; 16 | -------------------------------------------------------------------------------- /src/data.ts: -------------------------------------------------------------------------------- 1 | import {ChatCompletionRequestMessage, ChatCompletionRequestMessageRoleEnum} from "openai"; 2 | import {User} from "./interface"; 3 | import {isTokenOverLimit} from "./utils.js"; 4 | 5 | /** 6 | * 使用内存作为数据库 7 | */ 8 | 9 | class DB { 10 | private static data: User[] = []; 11 | 12 | /** 13 | * 添加一个用户, 如果用户已存在则返回已存在的用户 14 | * @param username 15 | */ 16 | public addUser(username: string): User { 17 | let existUser = DB.data.find((user) => user.username === username); 18 | if (existUser) { 19 | console.log(`用户${username}已存在`); 20 | return existUser; 21 | } 22 | const newUser: User = { 23 | username: username, 24 | chatMessage: [ 25 | { 26 | role: ChatCompletionRequestMessageRoleEnum.System, 27 | content: "You are a helpful assistant." 28 | } 29 | ], 30 | }; 31 | DB.data.push(newUser); 32 | return newUser; 33 | } 34 | 35 | /** 36 | * 根据用户名获取用户, 如果用户不存在则添加用户 37 | * @param username 38 | */ 39 | public getUserByUsername(username: string): User { 40 | return DB.data.find((user) => user.username === username) || this.addUser(username); 41 | } 42 | 43 | /** 44 | * 获取用户的聊天记录 45 | * @param username 46 | */ 47 | public getChatMessage(username: string): Array { 48 | return this.getUserByUsername(username).chatMessage; 49 | } 50 | 51 | /** 52 | * 设置用户的prompt 53 | * @param username 54 | * @param prompt 55 | */ 56 | public setPrompt(username: string, prompt: string): void { 57 | const user = this.getUserByUsername(username); 58 | if (user) { 59 | user.chatMessage.find( 60 | (msg) => msg.role === ChatCompletionRequestMessageRoleEnum.System 61 | )!.content = prompt; 62 | } 63 | } 64 | 65 | /** 66 | * 添加用户输入的消息 67 | * @param username 68 | * @param message 69 | */ 70 | public addUserMessage(username: string, message: string): void { 71 | const user = this.getUserByUsername(username); 72 | if (user) { 73 | while (isTokenOverLimit(user.chatMessage)){ 74 | // 删除从第2条开始的消息(因为第一条是prompt) 75 | user.chatMessage.splice(1,1); 76 | } 77 | user.chatMessage.push({ 78 | role: ChatCompletionRequestMessageRoleEnum.User, 79 | content: message, 80 | }); 81 | } 82 | } 83 | 84 | /** 85 | * 添加ChatGPT的回复 86 | * @param username 87 | * @param message 88 | */ 89 | public addAssistantMessage(username: string, message: string): void { 90 | const user = this.getUserByUsername(username); 91 | if (user) { 92 | while (isTokenOverLimit(user.chatMessage)){ 93 | // 删除从第2条开始的消息(因为第一条是prompt) 94 | user.chatMessage.splice(1,1); 95 | } 96 | user.chatMessage.push({ 97 | role: ChatCompletionRequestMessageRoleEnum.Assistant, 98 | content: message, 99 | }); 100 | } 101 | } 102 | 103 | /** 104 | * 清空用户的聊天记录, 并将prompt设置为默认值 105 | * @param username 106 | */ 107 | public clearHistory(username: string): void { 108 | const user = this.getUserByUsername(username); 109 | if (user) { 110 | user.chatMessage = [ 111 | { 112 | role: ChatCompletionRequestMessageRoleEnum.System, 113 | content: "You are a helpful assistant." 114 | } 115 | ]; 116 | } 117 | } 118 | 119 | public getAllData(): User[] { 120 | return DB.data; 121 | } 122 | } 123 | const DBUtils = new DB(); 124 | export default DBUtils; -------------------------------------------------------------------------------- /src/interface.ts: -------------------------------------------------------------------------------- 1 | import {ChatCompletionRequestMessage} from "openai"; 2 | 3 | export interface IConfig { 4 | api?: string; 5 | openai_api_key: string; 6 | model: string; 7 | chatTriggerRule: string; 8 | disableGroupMessage: boolean; 9 | temperature: number; 10 | blockWords: string[]; 11 | chatgptBlockWords: string[]; 12 | chatPrivateTriggerKeyword: string; 13 | } 14 | export interface User { 15 | username: string, 16 | chatMessage: Array, 17 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { WechatyBuilder } from "wechaty"; 2 | import QRCode from "qrcode"; 3 | import { ChatGPTBot } from "./bot.js"; 4 | import {config} from "./config.js"; 5 | const chatGPTBot = new ChatGPTBot(); 6 | 7 | const bot = WechatyBuilder.build({ 8 | name: "wechat-assistant", // generate xxxx.memory-card.json and save login data for the next login 9 | puppet: "wechaty-puppet-wechat", 10 | puppetOptions: { 11 | uos: true 12 | } 13 | }); 14 | async function main() { 15 | const initializedAt = Date.now() 16 | bot 17 | .on("scan", async (qrcode, status) => { 18 | const url = `https://wechaty.js.org/qrcode/${encodeURIComponent(qrcode)}`; 19 | console.log(`Scan QR Code to login: ${status}\n${url}`); 20 | console.log( 21 | await QRCode.toString(qrcode, { type: "terminal", small: true }) 22 | ); 23 | }) 24 | .on("login", async (user) => { 25 | chatGPTBot.setBotName(user.name()); 26 | console.log(`User ${user} logged in`); 27 | console.log(`私聊触发关键词: ${config.chatPrivateTriggerKeyword}`); 28 | console.log(`已设置 ${config.blockWords.length} 个聊天关键词屏蔽. ${config.blockWords}`); 29 | console.log(`已设置 ${config.chatgptBlockWords.length} 个ChatGPT回复关键词屏蔽. ${config.chatgptBlockWords}`); 30 | }) 31 | .on("message", async (message) => { 32 | if (message.date().getTime() < initializedAt) { 33 | return; 34 | } 35 | if (message.text().startsWith("/ping")) { 36 | await message.say("pong"); 37 | return; 38 | } 39 | try { 40 | await chatGPTBot.onMessage(message); 41 | } catch (e) { 42 | console.error(e); 43 | } 44 | }); 45 | try { 46 | await bot.start(); 47 | } catch (e) { 48 | console.error( 49 | `⚠️ Bot start failed, can you log in through wechat on the web?: ${e}` 50 | ); 51 | } 52 | } 53 | main(); 54 | -------------------------------------------------------------------------------- /src/openai.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Configuration, 3 | CreateImageRequestResponseFormatEnum, 4 | CreateImageRequestSizeEnum, 5 | OpenAIApi 6 | } from "openai"; 7 | import fs from "fs"; 8 | import DBUtils from "./data.js"; 9 | import {config} from "./config.js"; 10 | 11 | const configuration = new Configuration({ 12 | apiKey: config.openai_api_key, 13 | basePath: config.api, 14 | }); 15 | const openai = new OpenAIApi(configuration); 16 | 17 | /** 18 | * Get completion from OpenAI 19 | * @param username 20 | * @param message 21 | */ 22 | async function chatgpt(username:string,message: string): Promise { 23 | // 先将用户输入的消息添加到数据库中 24 | DBUtils.addUserMessage(username, message); 25 | const messages = DBUtils.getChatMessage(username); 26 | const response = await openai.createChatCompletion({ 27 | model: "gpt-3.5-turbo", 28 | messages: messages, 29 | temperature: config.temperature, 30 | }); 31 | let assistantMessage = ""; 32 | try { 33 | if (response.status === 200) { 34 | assistantMessage = response.data.choices[0].message?.content.replace(/^\n+|\n+$/g, "") as string; 35 | }else{ 36 | console.log(`Something went wrong,Code: ${response.status}, ${response.statusText}`) 37 | } 38 | }catch (e:any) { 39 | if (e.request){ 40 | console.log("请求出错"); 41 | } 42 | } 43 | return assistantMessage; 44 | } 45 | 46 | /** 47 | * Get image from Dall·E 48 | * @param username 49 | * @param prompt 50 | */ 51 | async function dalle(username:string,prompt: string) { 52 | const response = await openai.createImage({ 53 | prompt: prompt, 54 | n:1, 55 | size: CreateImageRequestSizeEnum._256x256, 56 | response_format: CreateImageRequestResponseFormatEnum.Url, 57 | user: username 58 | }).then((res) => res.data).catch((err) => console.log(err)); 59 | if (response) { 60 | return response.data[0].url; 61 | }else{ 62 | return "Generate image failed" 63 | } 64 | } 65 | 66 | /** 67 | * Speech to text 68 | * @param username 69 | * @param videoPath 70 | */ 71 | async function whisper(username:string,videoPath: string): Promise { 72 | const file:any= fs.createReadStream(videoPath); 73 | const response = await openai.createTranscription(file,"whisper-1") 74 | .then((res) => res.data).catch((err) => console.log(err)); 75 | if (response) { 76 | return response.text; 77 | }else{ 78 | return "Speech to text failed" 79 | } 80 | } 81 | 82 | export {chatgpt,dalle,whisper}; -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import {ChatCompletionRequestMessage} from "openai"; 2 | 3 | import GPT3TokenizerImport from 'gpt3-tokenizer'; 4 | import {config} from "./config.js"; 5 | 6 | export const regexpEncode = (str: string) => str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); 7 | 8 | const GPT3Tokenizer: typeof GPT3TokenizerImport = 9 | typeof GPT3TokenizerImport === 'function' 10 | ? GPT3TokenizerImport 11 | : (GPT3TokenizerImport as any).default; 12 | // https://github.com/chathub-dev/chathub/blob/main/src/app/bots/chatgpt-api/usage.ts 13 | const tokenizer = new GPT3Tokenizer({ type: 'gpt3' }) 14 | function calTokens(chatMessage:ChatCompletionRequestMessage[]):number { 15 | let count = 0 16 | for (const msg of chatMessage) { 17 | count += countTokens(msg.content) 18 | count += countTokens(msg.role) 19 | } 20 | return count + 2 21 | } 22 | 23 | function countTokens(str: string):number { 24 | const encoded = tokenizer.encode(str) 25 | return encoded.bpe.length 26 | } 27 | export function isTokenOverLimit(chatMessage:ChatCompletionRequestMessage[]): boolean { 28 | let limit = 4096; 29 | if (config.model==="gpt-3.5-turbo" || config.model==="gpt-3.5-turbo-0301") { 30 | limit = 4096; 31 | } 32 | return calTokens(chatMessage) > limit; 33 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "esnext", /* Specify what module code is generated. */ 28 | "rootDir": "src", /* Specify the root folder within your source files. */ 29 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | "outDir": "dist", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | 76 | /* Type Checking */ 77 | "strict": true, /* Enable all strict type-checking options. */ 78 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | } 101 | } 102 | --------------------------------------------------------------------------------