Repository: fuergaosi233/wechat-chatgpt
Branch: main
Commit: e96349ffa217
Files: 19
Total size: 59.2 KB
Directory structure:
gitextract_52g6r067/
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ └── config.yml
│ └── workflows/
│ └── publish-docker-hub.yml
├── .gitignore
├── Dockerfile
├── README.md
├── README_ZH.md
├── docker-compose.yml
├── package.json
├── public/
│ └── .gitignore
├── src/
│ ├── bot.ts
│ ├── config.ts
│ ├── data.ts
│ ├── interface.ts
│ ├── main.ts
│ ├── openai.ts
│ └── utils.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
# End of https://www.toptal.com/developers/gitignore/api/node
n
*memory-card.json
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
docs/
# End of https://www.toptal.com/developers/gitignore/api/python
n
config.json
cache.json
config.yaml
.vscode
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
title: "[Bug]: "
labels: ["bug"]
description: "Create a report to help us improve."
body:
- type: checkboxes
id: terms
attributes:
label: Welcome
options:
- label: Yes, I've searched similar issues on GitHub and didn't find any.
required: true
- label: Yes, I've included all information below (version, **FULL** config, **FULL** log, etc).
required: true
- type: checkboxes
id: platform
attributes:
label: Deployment Platform
options:
- label: Railway
- label: Fly.io
- label: Docker & Docker Compose
- label: Node.js
- type: textarea
id: problem
attributes:
label: Description of the problem
placeholder: Your problem description
validations:
required: true
- type: textarea
id: log
attributes:
label: wechat-chatgpt operation log
value: |-
```console
# Paste output here
```
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Discord Chat
url: https://discord.gg/8xWdppS7bE
about: Join our Discord server to chat with the community and get help with your issue.
================================================
FILE: .github/workflows/publish-docker-hub.yml
================================================
name: Publish Docker image
on:
# Test pub with dev tag
push:
branches:
- ci/fix-muti-platform
release:
types: [published]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: holegots/wechat-chatgpt
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
# FIXME: Chrome doesn't seem to install on ARM, so skip it for now
# platforms: linux/amd64,linux/arm64
labels: ${{ steps.meta.outputs.labels }}
================================================
FILE: .gitignore
================================================
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
# SvelteKit build / generate output
.svelte-kit
# End of https://www.toptal.com/developers/gitignore/api/node
n
*memory-card.json
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# End of https://www.toptal.com/developers/gitignore/api/python
n
config.json
cache.json
config.yaml
.vscode
data/
================================================
FILE: Dockerfile
================================================
FROM node:19 AS app
# We don't need the standalone Chromium
RUN apt-get install -y wget \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \
&& apt-get update && apt-get -y install google-chrome-stable chromium xvfb\
&& rm -rf /var/lib/apt/lists/* \
&& echo "Chrome: " && google-chrome --version
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD xvfb-run --server-args="-screen 0 1280x800x24 -ac -nolisten tcp -dpi 96 +extension RANDR" npm run dev
================================================
FILE: README.md
================================================
!!!! Project Archived 📦 !!!!
> This project has been archived. Thank you to everyone who contributed! 🙌😔
Welcome to wechat-chatgpt 👋
> Use ChatGPT On Wechat via wechaty
> English | [中文文档](README_ZH.md)
[](https://railway.app/template/dMLG70?referralCode=bIYugQ)
## 🌟 Features
- Interact with WeChat and ChatGPT:
- Use ChatGPT on WeChat with [wechaty](https://github.com/wechaty/wechaty) and [Official API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis)
- Add conversation support
- Support command setting
- Deployment and configuration options:
- Add Dockerfile, deployable with [docker](#use-with-docker)
- Support deployment using [docker compose](#use-with-docker-compose)
- Support [Railway](#use-with-railway) and [Fly.io](#use-with-flyio) deployment
- Other features:
- Support [Dall·E](https://labs.openai.com/)
- Support [whisper](https://openai.com/blog/introducing-chatgpt-and-whisper-apis)
- Support setting prompt
- Support proxy (in development)
## 🚀 Usage
- [Use with Railway](#use-with-railway)(PaaS, Free, Stable, ✅Recommended)
- [Use with Fly.io](#use-with-flyio)(Paas, Free, ✅Recommended)
- [Use with docker](#use-with-docker)(Self-hosted, Stable, ✅Recommended)
- [Use with docker compose](#use-with-docker-compose)(Self-hosted, Stable, ✅Recommended)
- [Use with nodejs](#use-with-nodejs)(Self-hosted)
## Use with Railway
> Railway offers $5 or 500 hours of runtime per month
1. Click the [Railway](https://railway.app/template/dMLG70?referralCode=bIYugQ) button to go to the Railway deployment page
2. Click the `Deploy Now` button to enter the Railway deployment page
3. Fill in the repository name and `OPENAI_API_KEY` (need to link GitHub account)
4. Click the `Deploy` button
5. Click the `View Logs` button and wait for the deployment to complete
## Use with Fly.io
> Please allocate 512MB memory for the application to meet the application requirements
> fly.io offers free bills up to $5(Free Allowances 3 256MB are not included in the bill)
1. Install [flyctl](https://fly.io/docs/getting-started/installing-flyctl/)
```shell
# macOS
brew install flyctl
# Windows
scoop install flyctl
# Linux
curl https://fly.io/install.sh | sh
```
2. Clone the project and enter the project directory
```shell
git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt
```
3. Create a new app
```shell
➜ flyctl launch
? Would you like to copy its configuration to the new app? No
? App Name (leave blank to use an auto-generated name):
? Select region:
? Would you like to setup a Postgresql database now? No
? Would you like to deploy now? No
```
4. Configure the environment variables
```shell
flyctl secrets set OPENAI_API_KEY="" MODEL=""
```
5. Deploy the app
```shell
flyctl deploy
```
## Use with docker
```sh
# pull image
docker pull holegots/wechat-chatgpt
# run container
docker run -d --name wechat-chatgpt \
-e OPENAI_API_KEY= \
-e MODEL="gpt-3.5-turbo" \
-e CHAT_PRIVATE_TRIGGER_KEYWORD="" \
-v $(pwd)/data:/app/data/wechat-assistant.memory-card.json \
holegots/wechat-chatgpt:latest
# View the QR code to log in to wechat
docker logs -f wechat-chatgpt
```
> How to get OPENAI API KEY? [Click here](https://platform.openai.com/account/api-keys)
## Use with docker compose
```sh
# Copy the configuration file according to the template
cp .env.example .env
# Edit the configuration file
vim .env
# Start the container
docker-compose up -d
# View the QR code to log in to wechat
docker logs -f wechat-chatgpt
```
## Use with nodejs
> You need NodeJS 18.0.0 version and above
```sh
# Clone the project
git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt
# Install dependencies
npm install
# Copy the configuration file according to the template
cp .env.example .env
# Edit the configuration file
vim .env
# Start project
npm run dev
```
> Please make sure your WeChat account can log in [WeChat on web](https://wx.qq.com/)
## 📝 Environment Variables
| name | description |
|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| API | API endpoint of ChatGPT |
| OPENAI_API_KEY | [create new secret key](https://platform.openai.com/account/api-keys) |
| MODEL | ID of the model to use. Currently, only gpt-3.5-turbo and gpt-3.5-turbo-0301 are supported. |
| 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. |
| CHAT_TRIGGER_RULE | Private chat triggering rules. |
| DISABLE_GROUP_MESSAGE | Prohibited to use ChatGPT in group chat. |
| CHAT_PRIVATE_TRIGGER_KEYWORD | Keyword to trigger ChatGPT reply in WeChat private chat |
| BLOCK_WORDS | Chat blocker words, (works for both private and group chats, Use, Split) |
| CHATGPT_BLOCK_WORDS | The blocked words returned by ChatGPT(works for both private and group chats, Use, Split) |
## 📝 Using Custom ChatGPT API
> https://github.com/fuergaosi233/openai-proxy
```shell
# Clone the project
git clone https://github.com/fuergaosi233/openai-proxy
# Install dependencies
npm install && npm install -g wrangler && npm run build
# Deploy to CloudFlare Workers
npm run deploy
# Custom domain (optional)
Add `Route` to `wrangler.toml`
routes = [
{ pattern = "Your Custom Domain", custom_domain = true },
]
```
## ⌨️ Commands
> Enter in the WeChat chat box
```shell
/cmd help # Show help
/cmd prompt # Set prompt
/cmd clear # Clear all sessions since last boot
```
## ✨ Contributor
## 🤝 Contributing
Contributions, issues and feature requests are welcome!
Feel free to
check [issues page](https://github.com/fuergaosi233/wechat-chatgpt/issues).
## Show your support
Give a ⭐️ if this project helped you!
================================================
FILE: README_ZH.md
================================================
欢迎使用 wechat-chatgpt 👋
> 在微信上迅速接入 ChatGPT,让它成为你最好的助手!
> [English](README.md) | 中文文档
[](https://railway.app/template/dMLG70?referralCode=bIYugQ)
## 🌟 功能点
- 使用 WeChat 和 ChatGPT 进行互动:
- 基于 [wechaty](https://github.com/wechaty/wechaty) 和 [Official API](https://openai.com/blog/introducing-chatgpt-and-whisper-apis) 在微信中使用 ChatGPT
- 支持多轮对话
- 支持[命令](#-命令)设置
- 部署和配置选项:
- 提供 Dockerfile,可以通过 [docker](#通过docker使用) 进行部署
- 支持使用 [docker compose](#通过docker-compose使用) 进行部署
- 支持在 [Railway](#使用railway进行部署) 和 [Fly.io](#通过flyio进行部署) 上部署
- 其他功能:
- 支持 [Dall·E](https://labs.openai.com/)
- 支持 [whisper](https://openai.com/blog/introducing-chatgpt-and-whisper-apis)
- 支持设置 prompt
- 支持代理(开发中)
## 🚀 使用
- [在 Railway 部署](#使用railway进行部署)(PaaS, 免费, 稳定, ✅推荐)
- [在 Fly.io 部署](#通过flyio进行部署)(PaaS, 免费, ✅推荐)
- [使用 Docker 部署](#通过docker使用)(自托管, 稳定, ✅推荐)
- [使用 Docker Compose 部署](#通过docker-compose使用)(自托管, 稳定, ✅推荐)
- [使用 NodeJS 部署](#使用nodejs运行)
## 使用Railway进行部署
> Railway 是一个免费的 PaaS 平台,5刀以内的账单免费或者每个月500小时的运行时间
1. 点击 [Railway](https://railway.app/template/dMLG70?referralCode=bIYugQ) 按钮,进入 Railway 部署页面
2. 点击 `Deploy Now` 按钮,进入 Railway 部署页面
3. 填写 仓库名称和 `OPENAI_API_KEY`(需要连接 GitHub 账号)
4. 点击 `Deploy` 按钮
5. 点击 `View Logs` 按钮,等待部署完成
## 通过Fly.io进行部署
> 请为应用程序分配 512 MB 内存,否则可能会出现内存溢出
> Fly.io 5刀以内的账单免费(免费计划的3个256MB的应用不在账单内)也就是可以同时可以部署 `1*512MB + 3*256MB`
1. 安装 [flyctl](https://fly.io/docs/getting-started/installing-flyctl/)
```shell
# macOS
brew install flyctl
# Windows
scoop install flyctl
# Linux
curl https://fly.io/install.sh | sh
```
2. 克隆项目并进入项目目录
```shell
git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt
```
3. 创建应用
```shell
➜ flyctl launch
? Would you like to copy its configuration to the new app? No
? App Name (leave blank to use an auto-generated name):
? Select region:
? Would you like to setup a Postgresql database now? No
? Would you like to deploy now? No
```
4. 配置环境变量
```shell
flyctl secrets set OPENAI_API_KEY="" MODEL=""
```
5. 部署应用
```shell
flyctl deploy
```
## 通过Docker使用
```sh
# 拉取镜像
docker pull holegots/wechat-chatgpt:latest
# 运行容器
docker run -it --name wechat-chatgpt \
-e OPENAI_API_KEY= \
-e MODEL="gpt-3.5-turbo" \
-e CHAT_PRIVATE_TRIGGER_KEYWORD="" \
-v $(pwd)/data:/app/data/wechat-assistant.memory-card.json \
holegots/wechat-chatgpt:latest
# 使用二维码登陆
docker logs -f wechat-chatgpt
```
> 如何获取 OPENAI API KEY?请参考 [OpenAI API](https://platform.openai.com/account/api-keys)。
## 通过docker compose使用
```sh
# 根据模板拷贝配置文件
cp .env.example .env
# 使用你喜欢的文本编辑器修改配置文件
vim .env
# 在Linux或WindowsPowerShell上运行如下命令
docker compose up -d
# 使用二维码登陆
docker logs -f wechat-chatgpt
```
## 使用NodeJS运行
> 请确认安装的NodeJS版本为18.0.0以上
```sh
# 克隆项目
git clone https://github.com/fuergaosi233/wechat-chatgpt.git && cd wechat-chatgpt
# 安装依赖
npm install
# 编辑配置
cp .env.example .env
vim .env # 使用你喜欢的文本编辑器修改配置文件
# 启动项目
npm run dev
# 如果您是初次登陆,那么需要扫描二维码
```
> 请确保您的账号可以登陆 [网页版微信](https://wx.qq.com/)。
## 📝 Environment Variables
| name | default | example | description |
|--------------------------|------------------------|------------------------------------------------|-------------------------------------------------------------|
| API | https://api.openai.com | | 自定义ChatGPT API 地址 |
| OPENAI_API_KEY | 123456789 | sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | [创建你的 API 密钥](https://platform.openai.com/account/api-keys) |
| MODEL | gpt-3.5-turbo | | 要使用的模型ID, 目前仅支持`gpt-3.5-turbo` 和 `gpt-3.5-turbo-0301` |
| TEMPERATURE | 0.6 | | 在0和2之间。较高的数值如0.8会使 ChatGPT 输出更加随机,而较低的数值如0.2会使其更加稳定。 |
| CHAT_TRIGGER_RULE | | | 私聊触发规则 |
| DISABLE_GROUP_MESSAGE | true | | 禁用在群聊里使用ChatGPT |
| CHAT_PRIVATE_TRIGGER_KEYWORD | | | 在私聊中触发ChatGPT的关键词, 默认是无需关键词即可触发 |
| BLOCK_WORDS | "VPN" | "WORD1,WORD2,WORD3" | 聊天屏蔽关键词(同时在群组和私聊中生效, 避免 bot 用户恶意提问导致封号 |
| CHATGPT_BLOCK_WORDS | "VPN" | "WORD1,WORD2,WORD3" | ChatGPT回复屏蔽词, 如果ChatGPT的回复中包含了屏蔽词, 则不回复 |
## 📝 使用自定义ChatGPT API
> https://github.com/fuergaosi233/openai-proxy
```shell
# 克隆项目
git clone https://github.com/fuergaosi233/openai-proxy
# 安装依赖
npm install && npm install -g wrangler && npm run build
# 部署到 CloudFlare Workers
npm run deploy
# 自定义域名(可选)
添加 `Route`` 到 `wrangler.toml`
routes = [
{ pattern = "Your Custom Domain", custom_domain = true },
]
```
## ⌨️ 命令
> 在微信聊天框中输入
```shell
/cmd help # 显示帮助信息
/cmd prompt # 设置ChatGPT Prompt
/cmd clear # 清除WeChat-ChatGPT保存的会话记录
```
## ✨ Contributor
## 🤝 为项目添砖加瓦
欢迎提出 Contributions, issues 与 feature requests!
随时查看 [issues page](https://github.com/fuergaosi233/wechat-chatgpt/issues).
## 感谢支持 🙏
如果这个项目对你产生了一点的帮助,请为这个项目点上一颗 ⭐️
================================================
FILE: docker-compose.yml
================================================
version: '3'
services:
wechat-chatgpt:
image: wechat-chatgpt
build: .
volumes:
- ./data/wechat-assistant.memory-card.json:/app/wechat-assistant.memory-card.json
env_file:
- .env
================================================
FILE: package.json
================================================
{
"name": "wechat-chatgpt",
"version": "1.0.0",
"description": "",
"main": "dist/main.js",
"export": "dist/main.js",
"scripts": {
"dev": "nodemon --exec node --loader ts-node/esm src/main.ts",
"build": "tsc"
},
"author": "",
"license": "ISC",
"dependencies": {
"async-retry": "^1.3.3",
"dotenv": "^16.0.3",
"execa": "^6.1.0",
"gpt3-tokenizer": "^1.1.5",
"openai": "^3.2.1",
"qrcode": "^1.5.1",
"uuid": "^9.0.0",
"wechaty": "^1.20.2",
"wechaty-puppet-wechat": "^1.18.4"
},
"devDependencies": {
"@types/async-retry": "^1.4.5",
"@types/qrcode": "^1.5.0",
"@types/uuid": "^9.0.0",
"nodemon": "^2.0.20",
"ts-node": "^10.9.1"
},
"nodemonConfig": {
"watch": [
"src/*.ts"
],
"ignore": [
"src/main.ts"
],
"ext": "ts",
"exec": "node --loader ts-node/esm src/main.ts",
"delay": 500
},
"type": "module"
}
================================================
FILE: public/.gitignore
================================================
# Ignore everything in this directory
*
# Except this file
!.gitignore
================================================
FILE: src/bot.ts
================================================
import { config } from "./config.js";
import {ContactImpl, ContactInterface, RoomImpl, RoomInterface} from "wechaty/impls";
import { Message } from "wechaty";
import {FileBox} from "file-box";
import {chatgpt, dalle, whisper} from "./openai.js";
import DBUtils from "./data.js";
import { regexpEncode } from "./utils.js";
enum MessageType {
Unknown = 0,
Attachment = 1, // Attach(6),
Audio = 2, // Audio(1), Voice(34)
Contact = 3, // ShareCard(42)
ChatHistory = 4, // ChatHistory(19)
Emoticon = 5, // Sticker: Emoticon(15), Emoticon(47)
Image = 6, // Img(2), Image(3)
Text = 7, // Text(1)
Location = 8, // Location(48)
MiniProgram = 9, // MiniProgram(33)
GroupNote = 10, // GroupNote(53)
Transfer = 11, // Transfers(2000)
RedEnvelope = 12, // RedEnvelopes(2001)
Recalled = 13, // Recalled(10002)
Url = 14, // Url(5)
Video = 15, // Video(4), Video(43)
Post = 16, // Moment, Channel, Tweet, etc
}
const SINGLE_MESSAGE_MAX_SIZE = 500;
type Speaker = RoomImpl | ContactImpl;
interface ICommand{
name:string;
description:string;
exec: (talker:Speaker, text:string) => Promise;
}
export class ChatGPTBot {
chatPrivateTriggerKeyword = config.chatPrivateTriggerKeyword;
chatTriggerRule = config.chatTriggerRule? new RegExp(config.chatTriggerRule): undefined;
disableGroupMessage = config.disableGroupMessage || false;
botName: string = "";
ready = false;
setBotName(botName: string) {
this.botName = botName;
}
get chatGroupTriggerRegEx(): RegExp {
return new RegExp(`^@${regexpEncode(this.botName)}\\s`);
}
get chatPrivateTriggerRule(): RegExp | undefined {
const { chatPrivateTriggerKeyword, chatTriggerRule } = this;
let regEx = chatTriggerRule
if (!regEx && chatPrivateTriggerKeyword) {
regEx = new RegExp(regexpEncode(chatPrivateTriggerKeyword))
}
return regEx
}
private readonly commands:ICommand[] = [
{
name: "help",
description: "显示帮助信息",
exec: async (talker) => {
await this.trySay(talker,"========\n" +
"/cmd help\n" +
"# 显示帮助信息\n" +
"/cmd prompt \n" +
"# 设置当前会话的 prompt \n" +
"/img \n" +
"# 根据 prompt 生成图片\n" +
"/cmd clear\n" +
"# 清除自上次启动以来的所有会话\n" +
"========");
}
},
{
name: "prompt",
description: "设置当前会话的prompt",
exec: async (talker, prompt) => {
if (talker instanceof RoomImpl) {
DBUtils.setPrompt(await talker.topic(), prompt);
}else {
DBUtils.setPrompt(talker.name(), prompt);
}
}
},
{
name: "clear",
description: "清除自上次启动以来的所有会话",
exec: async (talker) => {
if (talker instanceof RoomImpl) {
DBUtils.clearHistory(await talker.topic());
}else{
DBUtils.clearHistory(talker.name());
}
}
}
]
/**
* EXAMPLE:
* /cmd help
* /cmd prompt
* /cmd img
* /cmd clear
* @param contact
* @param rawText
*/
async command(contact: any, rawText: string): Promise {
const [commandName, ...args] = rawText.split(/\s+/);
const command = this.commands.find(
(command) => command.name === commandName
);
if (command) {
await command.exec(contact, args.join(" "));
}
}
// remove more times conversation and mention
cleanMessage(rawText: string, privateChat: boolean = false): string {
let text = rawText;
const item = rawText.split("- - - - - - - - - - - - - - -");
if (item.length > 1) {
text = item[item.length - 1];
}
const { chatTriggerRule, chatPrivateTriggerRule } = this;
if (privateChat && chatPrivateTriggerRule) {
text = text.replace(chatPrivateTriggerRule, "")
} else if (!privateChat) {
text = text.replace(this.chatGroupTriggerRegEx, "")
text = chatTriggerRule? text.replace(chatTriggerRule, ""): text
}
// remove more text via - - - - - - - - - - - - - - -
return text
}
async getGPTMessage(talkerName: string,text: string): Promise {
let gptMessage = await chatgpt(talkerName,text);
if (gptMessage !=="") {
DBUtils.addAssistantMessage(talkerName,gptMessage);
return gptMessage;
}
return "Sorry, please try again later. 😔";
}
// Check if the message returned by chatgpt contains masked words]
checkChatGPTBlockWords(message: string): boolean {
if (config.chatgptBlockWords.length == 0) {
return false;
}
return config.chatgptBlockWords.some((word) => message.includes(word));
}
// The message is segmented according to its size
async trySay(
talker: RoomInterface | ContactInterface,
mesasge: string
): Promise {
const messages: Array = [];
if (this.checkChatGPTBlockWords(mesasge)) {
console.log(`🚫 Blocked ChatGPT: ${mesasge}`);
return;
}
let message = mesasge;
while (message.length > SINGLE_MESSAGE_MAX_SIZE) {
messages.push(message.slice(0, SINGLE_MESSAGE_MAX_SIZE));
message = message.slice(SINGLE_MESSAGE_MAX_SIZE);
}
messages.push(message);
for (const msg of messages) {
await talker.say(msg);
}
}
// Check whether the ChatGPT processing can be triggered
triggerGPTMessage(text: string, privateChat: boolean = false): boolean {
const { chatTriggerRule } = this;
let triggered = false;
if (privateChat) {
const regEx = this.chatPrivateTriggerRule
triggered = regEx? regEx.test(text): true;
} else {
triggered = this.chatGroupTriggerRegEx.test(text);
// group message support `chatTriggerRule`
if (triggered && chatTriggerRule) {
triggered = chatTriggerRule.test(text.replace(this.chatGroupTriggerRegEx, ""))
}
}
if (triggered) {
console.log(`🎯 Triggered ChatGPT: ${text}`);
}
return triggered;
}
// Check whether the message contains the blocked words. if so, the message will be ignored. if so, return true
checkBlockWords(message: string): boolean {
if (config.blockWords.length == 0) {
return false;
}
return config.blockWords.some((word) => message.includes(word));
}
// Filter out the message that does not need to be processed
isNonsense(
talker: ContactInterface,
messageType: MessageType,
text: string
): boolean {
return (
talker.self() ||
// TODO: add doc support
!(messageType == MessageType.Text || messageType == MessageType.Audio) ||
talker.name() === "微信团队" ||
// 语音(视频)消息
text.includes("收到一条视频/语音聊天消息,请在手机上查看") ||
// 红包消息
text.includes("收到红包,请在手机上查看") ||
// Transfer message
text.includes("收到转账,请在手机上查看") ||
// 位置消息
text.includes("/cgi-bin/mmwebwx-bin/webwxgetpubliclinkimg") ||
// 聊天屏蔽词
this.checkBlockWords(text)
);
}
async onPrivateMessage(talker: ContactInterface, text: string) {
const gptMessage = await this.getGPTMessage(talker.name(),text);
await this.trySay(talker, gptMessage);
}
async onGroupMessage(
talker: ContactInterface,
text: string,
room: RoomInterface
) {
const gptMessage = await this.getGPTMessage(await room.topic(),text);
const result = `@${talker.name()} ${text}\n\n------\n ${gptMessage}`;
await this.trySay(room, result);
}
async onMessage(message: Message) {
const talker = message.talker();
const rawText = message.text();
const room = message.room();
const messageType = message.type();
const privateChat = !room;
if (privateChat) {
console.log(`🤵 Contact: ${talker.name()} 💬 Text: ${rawText}`)
} else {
const topic = await room.topic()
console.log(`🚪 Room: ${topic} 🤵 Contact: ${talker.name()} 💬 Text: ${rawText}`)
}
if (this.isNonsense(talker, messageType, rawText)) {
return;
}
if (messageType == MessageType.Audio){
// 保存语音文件
const fileBox = await message.toFileBox();
let fileName = "./public/" + fileBox.name;
await fileBox.toFile(fileName, true).catch((e) => {
console.log("保存语音失败",e);
return;
});
// Whisper
whisper("",fileName).then((text) => {
message.say(text);
})
return;
}
if (rawText.startsWith("/cmd ")){
console.log(`🤖 Command: ${rawText}`)
const cmdContent = rawText.slice(5) // 「/cmd 」一共5个字符(注意空格)
if (privateChat) {
await this.command(talker, cmdContent);
}else{
await this.command(room, cmdContent);
}
return;
}
// 使用DallE生成图片
if (rawText.startsWith("/img")){
console.log(`🤖 Image: ${rawText}`)
const imgContent = rawText.slice(4)
if (privateChat) {
let url = await dalle(talker.name(), imgContent) as string;
const fileBox = FileBox.fromUrl(url)
message.say(fileBox)
}else{
let url = await dalle(await room.topic(), imgContent) as string;
const fileBox = FileBox.fromUrl(url)
message.say(fileBox)
}
return;
}
if (this.triggerGPTMessage(rawText, privateChat)) {
const text = this.cleanMessage(rawText, privateChat);
if (privateChat) {
return await this.onPrivateMessage(talker, text);
} else{
if (!this.disableGroupMessage){
return await this.onGroupMessage(talker, text, room);
} else {
return;
}
}
} else {
return;
}
}
}
================================================
FILE: src/config.ts
================================================
import * as dotenv from "dotenv";
dotenv.config();
import { IConfig } from "./interface";
export const config: IConfig = {
api: process.env.API,
openai_api_key: process.env.OPENAI_API_KEY || "123456789",
model: process.env.MODEL || "gpt-3.5-turbo",
chatPrivateTriggerKeyword: process.env.CHAT_PRIVATE_TRIGGER_KEYWORD || "",
chatTriggerRule: process.env.CHAT_TRIGGER_RULE || "",
disableGroupMessage: process.env.DISABLE_GROUP_MESSAGE === "true",
temperature: process.env.TEMPERATURE ? parseFloat(process.env.TEMPERATURE) : 0.6,
blockWords: process.env.BLOCK_WORDS?.split(",") || [],
chatgptBlockWords: process.env.CHATGPT_BLOCK_WORDS?.split(",") || [],
};
================================================
FILE: src/data.ts
================================================
import {ChatCompletionRequestMessage, ChatCompletionRequestMessageRoleEnum} from "openai";
import {User} from "./interface";
import {isTokenOverLimit} from "./utils.js";
/**
* 使用内存作为数据库
*/
class DB {
private static data: User[] = [];
/**
* 添加一个用户, 如果用户已存在则返回已存在的用户
* @param username
*/
public addUser(username: string): User {
let existUser = DB.data.find((user) => user.username === username);
if (existUser) {
console.log(`用户${username}已存在`);
return existUser;
}
const newUser: User = {
username: username,
chatMessage: [
{
role: ChatCompletionRequestMessageRoleEnum.System,
content: "You are a helpful assistant."
}
],
};
DB.data.push(newUser);
return newUser;
}
/**
* 根据用户名获取用户, 如果用户不存在则添加用户
* @param username
*/
public getUserByUsername(username: string): User {
return DB.data.find((user) => user.username === username) || this.addUser(username);
}
/**
* 获取用户的聊天记录
* @param username
*/
public getChatMessage(username: string): Array {
return this.getUserByUsername(username).chatMessage;
}
/**
* 设置用户的prompt
* @param username
* @param prompt
*/
public setPrompt(username: string, prompt: string): void {
const user = this.getUserByUsername(username);
if (user) {
user.chatMessage.find(
(msg) => msg.role === ChatCompletionRequestMessageRoleEnum.System
)!.content = prompt;
}
}
/**
* 添加用户输入的消息
* @param username
* @param message
*/
public addUserMessage(username: string, message: string): void {
const user = this.getUserByUsername(username);
if (user) {
while (isTokenOverLimit(user.chatMessage)){
// 删除从第2条开始的消息(因为第一条是prompt)
user.chatMessage.splice(1,1);
}
user.chatMessage.push({
role: ChatCompletionRequestMessageRoleEnum.User,
content: message,
});
}
}
/**
* 添加ChatGPT的回复
* @param username
* @param message
*/
public addAssistantMessage(username: string, message: string): void {
const user = this.getUserByUsername(username);
if (user) {
while (isTokenOverLimit(user.chatMessage)){
// 删除从第2条开始的消息(因为第一条是prompt)
user.chatMessage.splice(1,1);
}
user.chatMessage.push({
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: message,
});
}
}
/**
* 清空用户的聊天记录, 并将prompt设置为默认值
* @param username
*/
public clearHistory(username: string): void {
const user = this.getUserByUsername(username);
if (user) {
user.chatMessage = [
{
role: ChatCompletionRequestMessageRoleEnum.System,
content: "You are a helpful assistant."
}
];
}
}
public getAllData(): User[] {
return DB.data;
}
}
const DBUtils = new DB();
export default DBUtils;
================================================
FILE: src/interface.ts
================================================
import {ChatCompletionRequestMessage} from "openai";
export interface IConfig {
api?: string;
openai_api_key: string;
model: string;
chatTriggerRule: string;
disableGroupMessage: boolean;
temperature: number;
blockWords: string[];
chatgptBlockWords: string[];
chatPrivateTriggerKeyword: string;
}
export interface User {
username: string,
chatMessage: Array,
}
================================================
FILE: src/main.ts
================================================
import { WechatyBuilder } from "wechaty";
import QRCode from "qrcode";
import { ChatGPTBot } from "./bot.js";
import {config} from "./config.js";
const chatGPTBot = new ChatGPTBot();
const bot = WechatyBuilder.build({
name: "wechat-assistant", // generate xxxx.memory-card.json and save login data for the next login
puppet: "wechaty-puppet-wechat",
puppetOptions: {
uos: true
}
});
async function main() {
const initializedAt = Date.now()
bot
.on("scan", async (qrcode, status) => {
const url = `https://wechaty.js.org/qrcode/${encodeURIComponent(qrcode)}`;
console.log(`Scan QR Code to login: ${status}\n${url}`);
console.log(
await QRCode.toString(qrcode, { type: "terminal", small: true })
);
})
.on("login", async (user) => {
chatGPTBot.setBotName(user.name());
console.log(`User ${user} logged in`);
console.log(`私聊触发关键词: ${config.chatPrivateTriggerKeyword}`);
console.log(`已设置 ${config.blockWords.length} 个聊天关键词屏蔽. ${config.blockWords}`);
console.log(`已设置 ${config.chatgptBlockWords.length} 个ChatGPT回复关键词屏蔽. ${config.chatgptBlockWords}`);
})
.on("message", async (message) => {
if (message.date().getTime() < initializedAt) {
return;
}
if (message.text().startsWith("/ping")) {
await message.say("pong");
return;
}
try {
await chatGPTBot.onMessage(message);
} catch (e) {
console.error(e);
}
});
try {
await bot.start();
} catch (e) {
console.error(
`⚠️ Bot start failed, can you log in through wechat on the web?: ${e}`
);
}
}
main();
================================================
FILE: src/openai.ts
================================================
import {
Configuration,
CreateImageRequestResponseFormatEnum,
CreateImageRequestSizeEnum,
OpenAIApi
} from "openai";
import fs from "fs";
import DBUtils from "./data.js";
import {config} from "./config.js";
const configuration = new Configuration({
apiKey: config.openai_api_key,
basePath: config.api,
});
const openai = new OpenAIApi(configuration);
/**
* Get completion from OpenAI
* @param username
* @param message
*/
async function chatgpt(username:string,message: string): Promise {
// 先将用户输入的消息添加到数据库中
DBUtils.addUserMessage(username, message);
const messages = DBUtils.getChatMessage(username);
const response = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: messages,
temperature: config.temperature,
});
let assistantMessage = "";
try {
if (response.status === 200) {
assistantMessage = response.data.choices[0].message?.content.replace(/^\n+|\n+$/g, "") as string;
}else{
console.log(`Something went wrong,Code: ${response.status}, ${response.statusText}`)
}
}catch (e:any) {
if (e.request){
console.log("请求出错");
}
}
return assistantMessage;
}
/**
* Get image from Dall·E
* @param username
* @param prompt
*/
async function dalle(username:string,prompt: string) {
const response = await openai.createImage({
prompt: prompt,
n:1,
size: CreateImageRequestSizeEnum._256x256,
response_format: CreateImageRequestResponseFormatEnum.Url,
user: username
}).then((res) => res.data).catch((err) => console.log(err));
if (response) {
return response.data[0].url;
}else{
return "Generate image failed"
}
}
/**
* Speech to text
* @param username
* @param videoPath
*/
async function whisper(username:string,videoPath: string): Promise {
const file:any= fs.createReadStream(videoPath);
const response = await openai.createTranscription(file,"whisper-1")
.then((res) => res.data).catch((err) => console.log(err));
if (response) {
return response.text;
}else{
return "Speech to text failed"
}
}
export {chatgpt,dalle,whisper};
================================================
FILE: src/utils.ts
================================================
import {ChatCompletionRequestMessage} from "openai";
import GPT3TokenizerImport from 'gpt3-tokenizer';
import {config} from "./config.js";
export const regexpEncode = (str: string) => str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
const GPT3Tokenizer: typeof GPT3TokenizerImport =
typeof GPT3TokenizerImport === 'function'
? GPT3TokenizerImport
: (GPT3TokenizerImport as any).default;
// https://github.com/chathub-dev/chathub/blob/main/src/app/bots/chatgpt-api/usage.ts
const tokenizer = new GPT3Tokenizer({ type: 'gpt3' })
function calTokens(chatMessage:ChatCompletionRequestMessage[]):number {
let count = 0
for (const msg of chatMessage) {
count += countTokens(msg.content)
count += countTokens(msg.role)
}
return count + 2
}
function countTokens(str: string):number {
const encoded = tokenizer.encode(str)
return encoded.bpe.length
}
export function isTokenOverLimit(chatMessage:ChatCompletionRequestMessage[]): boolean {
let limit = 4096;
if (config.model==="gpt-3.5-turbo" || config.model==="gpt-3.5-turbo-0301") {
limit = 4096;
}
return calTokens(chatMessage) > limit;
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "esnext", /* Specify what module code is generated. */
"rootDir": "src", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
"resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "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. */
"outDir": "dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}