├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── README_en.md
├── docker-push.sh
├── pom.xml
├── run.sh
└── src
└── main
├── java
└── code
│ ├── Main.java
│ ├── commands
│ ├── HelpCommand.java
│ └── StartCommand.java
│ ├── config
│ ├── Config.java
│ ├── ConfigField.java
│ ├── ConfigSettings.java
│ ├── ExecutorsConfig.java
│ ├── I18nConfig.java
│ ├── I18nEnum.java
│ ├── I18nLocaleEnum.java
│ ├── ProxyTypeEnum.java
│ ├── RequestProxyConfig.java
│ └── TableEnum.java
│ ├── eneity
│ ├── GptTokenTableEntity.java
│ ├── RecordTableEntity.java
│ └── cons
│ │ ├── GptTokenStatusEnum.java
│ │ └── YesOrNoEnum.java
│ ├── handler
│ ├── Command.java
│ ├── CommandsHandler.java
│ ├── Handler.java
│ ├── I18nHandle.java
│ ├── StepsCenter.java
│ ├── commands
│ │ └── AdminCommands.java
│ ├── message
│ │ ├── InlineKeyboardButtonBuilder.java
│ │ ├── InlineKeyboardButtonListBuilder.java
│ │ └── MessageHandle.java
│ ├── steps
│ │ ├── StepErrorApi.java
│ │ ├── StepExecuteResult.java
│ │ ├── StepHandleApi.java
│ │ ├── StepResult.java
│ │ ├── StepsBuilder.java
│ │ ├── StepsChatSession.java
│ │ ├── StepsChatSessionBuilder.java
│ │ ├── StepsHandler.java
│ │ └── StepsRegisterCenter.java
│ └── store
│ │ ├── ChatButtonsStore.java
│ │ ├── GptTokenStore.java
│ │ └── Store.java
│ ├── repository
│ ├── GptTokenTableRepository.java
│ ├── I18nTableRepository.java
│ ├── RecordTableRepository.java
│ └── mapper
│ │ ├── SqlBuilder.java
│ │ ├── TableEntity.java
│ │ ├── TableField.java
│ │ ├── TableName.java
│ │ └── TableRepository.java
│ └── util
│ ├── BytesUtil.java
│ ├── CompressUtil.java
│ ├── DownloadUtil.java
│ ├── ExceptionUtil.java
│ ├── FFmpegUtil.java
│ ├── GPTUtil.java
│ ├── GithubUtil.java
│ ├── ProgramUtil.java
│ ├── SqliteUtil.java
│ ├── ThreadUtil.java
│ ├── ffmpeg
│ └── FfmpegDownloadUrl.java
│ └── gpt
│ ├── GPTCallback.java
│ ├── GPTModel.java
│ ├── GPTRole.java
│ ├── GPTTranscriptionsModel.java
│ ├── parameter
│ ├── GPTChatParameter.java
│ ├── GPTCreateImageParameter.java
│ ├── GPTMessage.java
│ └── GPTTranscriptionsParameter.java
│ └── response
│ ├── GPTCallbackChatContent.java
│ ├── GPTChatContentChoices.java
│ ├── GPTChatContentChoicesDelta.java
│ ├── GPTChatResponse.java
│ ├── GPTCreateImage.java
│ ├── GPTCreateImageResponse.java
│ └── GPTTranscriptionsResponse.java
└── resources
├── i18n
├── i18n_en.properties
└── i18n_zh_CN.properties
└── logback.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
35 | /logs
36 | /config
37 |
38 | .mvn
39 | mvnw
40 | mvnw.cmd
41 |
42 | *.imi
43 | /ChatGPTForTelegram-universal.jar
44 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-jdk-alpine
2 | ENV TZ=Asia/Shanghai
3 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
4 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
5 | # 增加字体,解决验证码没有字体报空指针问题
6 | RUN set -xe \
7 | && apk --no-cache add ttf-dejavu fontconfig
8 | # 系统编码
9 | ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
10 |
11 | ENV GPT_TOKEN=''
12 | ENV BOT_ADMIN_ID=''
13 | ENV BOT_NAME=''
14 | ENV BOT_TOKEN=''
15 | ENV PROXY=false
16 | ENV PROXY_HOST=127.0.0.1
17 | ENV PROXY_PORT=7890
18 |
19 | WORKDIR /app
20 | COPY ChatGPTForTelegram-universal.jar ChatGPTForTelegram-universal.jar
21 | COPY run.sh run.sh
22 |
23 | ENTRYPOINT ["sh", "run.sh"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 kylelin1998
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 简体中文 | [English](./README_en.md)
2 |
3 | 
4 | [](https://github.com/kylelin1998/ChatGPTForTelegram/releases/latest)
5 |
6 | ## 介绍
7 | - Youtube: https://youtu.be/a_WLFtRWGzY
8 | - 哔哩哔哩: https://www.bilibili.com/video/BV1qX4y1z7Dt/
9 |
10 | ChatGPT机器人, 这是一个开源项目, 你可以基于它搭建属于自己的机器人
11 |
12 | 使用机器人可以让你轻松进行对话, 后续机器人的更新升级一个命令即可搞定, 无需再上服务器进行升级机器人
13 |
14 | 1. 支持各种场景文本对话
15 | 2. 语音对话
16 | 3. 录制重放对话
17 | 4. 支持生成图片
18 |
19 | 你可以事先录制一段对话, 后面用到的时候直接快速重放出来, 就不用每次建立新对话就要发送一堆引导文本了
20 |
21 | ## 最近更新日志
22 | 开源ChatGPT TG机器人 v1.0.50 更新说明视频
23 | - ⭐ Youtube: https://youtu.be/9hczaDzOvGA
24 | - ⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1wV41137Yf/
25 |
26 | 1. 支持多ChatGPT Api Key, 随机切换使用, 死号自动通知管理员
27 | 2. 支持精简回复, 开启后部分命令会去除回复的问题显示和退出提示, 仅显示回复内容
28 | 3. 超级加强引导, 录制重放对话支持替换变量, 直接发送文本即可, 无需再增加文本来引导
29 |
30 | ## 部署
31 | 机器人的部署步骤是基于 Docker 的,其机器人升级功能也基于 Docker,因此请使用 Docker 进行部署,以防出现错误
32 |
33 | ### 部署方式1 (推荐)
34 | - ⭐ Youtube: https://youtu.be/mNg6TFyozZk
35 | - ⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1qF411f7pg/
36 |
37 | #### 一键部署
38 | ```
39 | docker run --name gpttb -d -v $(pwd)/config:/app/config -e GPT_TOKEN=你的GPTApiKey -e BOT_ADMIN_ID=管理者的ChatId -e BOT_NAME=机器人的username -e BOT_TOKEN=机器人token --restart=always kylelin1998/chatgpt-tg-bot
40 | ```
41 | #### 一键部署(开启代理)
42 | ```
43 | docker run --name gpttb -d -v $(pwd)/config:/app/config -e GPT_TOKEN=你的GPTApiKey -e BOT_ADMIN_ID=管理者的ChatId -e BOT_NAME=机器人的username -e BOT_TOKEN=机器人token -e PROXY=true -e PROXY_HOST=127.0.0.1 -e PROXY_PORT=7890 --restart=always kylelin1998/chatgpt-tg-bot
44 | ```
45 |
46 | ### 部署方式2 (不推荐)
47 | - Youtube:https://youtu.be/CiDxb1ESijQ
48 | - 哔哩哔哩: https://www.bilibili.com/video/BV1Ts4y1S7bn/
49 |
50 | ### 准备
51 | 
52 |
53 | 首先,在您的服务器上创建一个文件夹
54 |
55 | 然后,在其中创建名为 config 的另一个文件夹,config文件夹下必须包含名为 config.json 的JSON文件
56 |
57 | 接着,将 ChatGPTForTelegram-universal.jar, run.sh 和 Dockerfile 传输到该文件夹中
58 |
59 | ### config.json
60 | ```
61 | {
62 | "open": false,
63 | "on_proxy": false,
64 | "proxy_host": "127.0.0.1",
65 | "proxy_port": 7890,
66 | "bot_admin_id": "xxxx",
67 | "bot_name": "xxx_bot",
68 | "bot_token": "xxxx",
69 | "debug": false,
70 | "gpt_token": "xxxxx",
71 | "gpt_model": "gpt-3.5-turbo",
72 | "permission_chat_id_array": [
73 | "xxxx"
74 | ],
75 | "block_chat_id_array": []
76 | }
77 | ```
78 | ```
79 | open ->
80 | 开放状态:任何人都可以使用这个机器人。
81 | 关闭状态:只有permission chat id列表才能使用这个bot。
82 | on proxy 代表是否开启代理
83 | bot admin id 就是你要指定那个号是管理员, 这个id是chat id
84 | bot name, 和 bot token 就是机器人创建好就有的,你肯定知道
85 | gpt token -> 就是 gpt的token
86 | permission chat id array -> 这个就是代表你只能允许列表下的这些chat id使用机器人, 可以填写个人的,或者是群的chat id
87 | block_chat_id_array -> 不允许使用机器人的chat id列表
88 | ```
89 |
90 | ### 第一步:
91 | 编译镜像
92 | ```
93 | docker build -t gptft .
94 | ```
95 |
96 | ### 第二步:
97 | 运行容器镜像
98 | ```
99 | docker run --name gptft -d -v $(pwd):/app --restart=always gptft
100 | ```
101 |
102 | ## 使用
103 | 机器人命令:
104 | ```
105 | chat - 发起对话
106 | c - 发起对话
107 | ask - 单次提问
108 | a - 单次提问
109 | cml - 发起消息限制对话
110 | nccm - 发起无上下文对话
111 | image - 生成图片
112 | record - 录制对话
113 | p - 重放对话
114 | record_list - 录制对话管理
115 | exit - 退出
116 | language - 切换语言
117 | admin - 管理员命令
118 | restart - 重启机器人
119 | upgrade - 升级机器人
120 | help - 帮助
121 | ```
122 | 
123 | 
124 |
--------------------------------------------------------------------------------
/README_en.md:
--------------------------------------------------------------------------------
1 | ### [简体中文](./README.md) | English
2 |
3 | 
4 | [](https://github.com/kylelin1998/ChatGPTForTelegram/releases/latest)
5 |
6 | ## Introduction
7 | Youtube: https://youtu.be/a_WLFtRWGzY
8 |
9 | 哔哩哔哩: https://www.bilibili.com/video/BV1qX4y1z7Dt/
10 |
11 | The ChatGPT Telegram Bot based on OpenAI's ChatGPT. It can let you easily to chat or ask.
12 |
13 | The bot has some commands that consider different scenes for chatting.
14 |
15 | This project is an open-source project that you can trust. Once deployed, the bot's version is easy with a single command to upgrade.
16 |
17 | 1. Support the voice chat with the bot.
18 | 2. Added record & playback mode feature.
19 | 3. Support text chat with the bot.
20 | 4. Support image generation
21 |
22 | The record & playback mode allows you to record chats with the bot, then send 'end_record' to stop and save the recording. Afterward, you can playback the recorded chat. It can help your work and is a nice feature.
23 |
24 | ## Recent Update Log
25 | Open-source ChatGPT TG Robot v1.0.50 Update Notes Video
26 | - ⭐ Youtube: https://youtu.be/9hczaDzOvGA
27 | - ⭐ Bilibili: https://www.bilibili.com/video/BV1wV41137Yf/
28 |
29 | 1. Added support for multiple ChatGPT API keys, which switch randomly. Invalid keys are automatically notified to the administrator.
30 | 2. Added support for streamlined replies. When enabled, certain commands will remove the display of the question and exit prompts, only showing the reply content.
31 | 3. Enhanced onboarding process. The recording and playback of conversations now support variable substitution. Simply send the text and no longer need to add additional text for guidance.
32 |
33 | ## Deploy
34 | The bot's deploy steps based on the Docker, its upgrade feature also based on the Docker, so please use the Docker to deploy it in case appear error.
35 |
36 | ### Deployment method 1 (recommended)
37 | ⭐ Youtube: https://youtu.be/mNg6TFyozZk
38 |
39 | ⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1qF411f7pg/
40 |
41 | #### One-click deployment
42 | ```
43 | docker run --name gpttb -d -v $(pwd)/config:/app/config -e GPT_TOKEN=YourGPTApiKey -e BOT_ADMIN_ID=AdminChatId -e BOT_NAME=BotUsername -e BOT_TOKEN=BotToken --restart=always kylelin1998/chatgpt-tg-bot
44 | ```
45 | #### One-click deployment (with proxy enabled)
46 | ```
47 | docker run --name gpttb -d -v $(pwd)/config:/app/config -e GPT_TOKEN=YourGPTApiKey -e BOT_ADMIN_ID=AdminChatId -e BOT_NAME=BotUsername -e BOT_TOKEN=BotToken -e PROXY=true -e PROXY_HOST=127.0.0.1 -e PROXY_PORT=7890 --restart=always kylelin1998/chatgpt-tg-bot
48 | ```
49 |
50 | ### Deployment method 2 (not recommended)
51 | Youtube:https://youtu.be/CiDxb1ESijQ
52 |
53 | 哔哩哔哩: https://www.bilibili.com/video/BV1Ts4y1S7bn/
54 |
55 | ### Prepare
56 | 
57 |
58 | To start, create a folder named whatever you prefer on your server.
59 | Then create another folder named config and the config folder must contains a json file named config.json in, then transfer ChatGPTForTelegram-universal.jar, run.sh and Dockerfile to the folder.
60 | ### config.json
61 | ```
62 | {
63 | "open": false,
64 | "on_proxy": false,
65 | "proxy_host": "127.0.0.1",
66 | "proxy_port": 7890,
67 | "bot_admin_id": "xxxx",
68 | "bot_name": "xxx_bot",
69 | "bot_token": "xxxx",
70 | "debug": false,
71 | "gpt_token": "xxxxx",
72 | "gpt_model": "gpt-3.5-turbo",
73 | "permission_chat_id_array": [
74 | "xxxx"
75 | ],
76 | "block_chat_id_array": []
77 | }
78 | ```
79 | ```
80 | open ->
81 | Open status: Anyone can use this bot.
82 | Close status: Only permission chat id list can use this bot.
83 | on proxy -> Whether to open proxy
84 | bot admin id -> Bot's admin, the id is chat id of Telegram.
85 | bot name, and token you -> @BotFather has given bot name, bot token
86 | permission chat id array -> Allow using the bot.
87 | block_chat_id_array -> Not allow using the bot.
88 | ```
89 |
90 | ### First step:
91 | Build a docker image for use.
92 | ```
93 | docker build -t gptft .
94 | ```
95 |
96 | ### Second step:
97 | Run the docker image of just then build.
98 | ```
99 | docker run --name gptft -d -v $(pwd):/app --restart=always gptft
100 | ```
101 |
102 | ## Usage
103 | How to use the bot's commands:
104 | ```
105 | chat - The current mode is continuous chat mode
106 | c - The current mode is continuous chat mode
107 | ask - Ask a problem
108 | a - Ask a problem
109 | cml - The current mode is chat message limit mode
110 | nccm - The current mode is none of message context mode
111 | image - Create a image for you
112 | record - Record chat messages.
113 | p - Playback chat messages.
114 | record_list - Manage the record list.
115 | exit - Exit chat
116 | language - Change language
117 | admin - Admin
118 | restart - Restart the bot
119 | upgrade - Upgrade the bot
120 | help - Help
121 | ```
122 |
123 | 
124 | 
125 |
--------------------------------------------------------------------------------
/docker-push.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | sudo apt install -y qemu-user-static binfmt-support
4 | docker buildx create --use
5 | docker buildx ls
6 |
7 | docker buildx build --platform linux/amd64,linux/386,linux/arm64 -t kylelin1998/chatgpt-tg-bot . --push
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.kylelin1998
8 | ChatGPTForTelegram
9 | universal
10 |
11 |
12 | 8
13 | 8
14 | UTF-8
15 |
16 |
17 |
18 |
19 | nexus-tencentyun
20 | Nexus tencentyun
21 | http://mirrors.cloud.tencent.com/nexus/repository/maven-public/
22 | default
23 |
24 |
25 | true
26 |
27 |
28 |
29 | false
30 |
31 |
32 |
33 | central
34 | aliyun maven
35 | http://maven.aliyun.com/nexus/content/groups/public/
36 | default
37 |
38 |
39 | true
40 |
41 |
42 |
43 | false
44 |
45 |
46 |
47 |
48 |
49 |
50 | com.alibaba.fastjson2
51 | fastjson2
52 | 2.0.23
53 |
54 |
55 |
56 | org.projectlombok
57 | lombok
58 | 1.18.22
59 | provided
60 |
61 |
62 |
63 | commons-io
64 | commons-io
65 | 2.11.0
66 |
67 |
68 | org.apache.commons
69 | commons-lang3
70 | 3.12.0
71 |
72 |
73 | com.konghq
74 | unirest-java
75 | 3.14.1
76 |
77 |
78 | org.jsoup
79 | jsoup
80 | 1.11.2
81 |
82 |
83 |
84 |
85 | org.slf4j
86 | slf4j-api
87 | 1.7.28
88 | jar
89 |
90 |
91 | ch.qos.logback
92 | logback-core
93 | 1.2.3
94 | jar
95 |
96 |
97 | ch.qos.logback
98 | logback-classic
99 | 1.2.3
100 | jar
101 |
102 |
103 |
104 | org.telegram
105 | telegrambots
106 | 6.5.0
107 |
108 |
109 | org.telegram
110 | telegrambotsextensions
111 | 6.5.0
112 |
113 |
114 |
115 |
116 | org.xerial
117 | sqlite-jdbc
118 | 3.40.0.0
119 |
120 |
121 |
122 | org.apache.httpcomponents.client5
123 | httpclient5
124 | 5.1.3
125 |
126 |
127 | org.apache.httpcomponents.client5
128 | httpclient5-fluent
129 | 5.1.3
130 |
131 |
132 |
133 | org.codehaus.plexus
134 | plexus-archiver
135 | 4.6.3
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | org.apache.maven.plugins
144 | maven-shade-plugin
145 | 2.3
146 |
147 | false
148 |
149 |
150 |
151 | package
152 |
153 | shade
154 |
155 |
156 |
157 |
158 | code.Main
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | temp=temp.jar
4 | app=/app/ChatGPTForTelegram-universal.jar
5 | temp_jar=/app/temp.jar
6 |
7 | if [ -e $temp ]
8 | then
9 | echo "updating..."
10 | rm -rf $app
11 | mv $temp_jar $app
12 | echo "updated..."
13 | fi
14 |
15 | java -Djava.security.egd=file:/dev/./urandom -DgptToken=$GPT_TOKEN -DbotAdminId=$BOT_ADMIN_ID -DbotName=$BOT_NAME -DbotToken=$BOT_TOKEN -DbotProxy=$PROXY -DbotProxyHost=$PROXY_HOST -DbotProxyPort=$PROXY_PORT -jar $app
--------------------------------------------------------------------------------
/src/main/java/code/Main.java:
--------------------------------------------------------------------------------
1 | package code;
2 |
3 | import code.config.Config;
4 | import code.config.ConfigSettings;
5 | import code.config.I18nEnum;
6 | import code.config.RequestProxyConfig;
7 | import code.handler.CommandsHandler;
8 | import code.handler.Handler;
9 | import code.handler.I18nHandle;
10 | import code.handler.message.MessageHandle;
11 | import code.handler.store.Store;
12 | import code.repository.GptTokenTableRepository;
13 | import code.repository.I18nTableRepository;
14 | import code.repository.RecordTableRepository;
15 | import code.util.ExceptionUtil;
16 | import com.alibaba.fastjson2.JSON;
17 | import kong.unirest.Unirest;
18 | import lombok.extern.slf4j.Slf4j;
19 | import org.telegram.telegrambots.meta.TelegramBotsApi;
20 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
21 | import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
22 |
23 | @Slf4j
24 | public class Main {
25 | public static volatile CommandsHandler Bot = null;
26 | public static volatile ConfigSettings GlobalConfig = Config.initConfig();
27 | public static volatile code.repository.I18nTableRepository I18nTableRepository = new I18nTableRepository();
28 | public static volatile code.repository.RecordTableRepository RecordTableRepository = new RecordTableRepository();
29 | public static volatile code.repository.GptTokenTableRepository GptTokenTableRepository = new GptTokenTableRepository();
30 |
31 | public static void main(String[] args) throws InterruptedException {
32 | log.info(String.format("Main args: %s", JSON.toJSONString(args)));
33 | log.info(String.format("System properties: %s", System.getProperties()));
34 | log.info(String.format("Config: %s", JSON.toJSONString(GlobalConfig)));
35 |
36 | Unirest
37 | .config()
38 | .enableCookieManagement(false)
39 | ;
40 |
41 | new Thread(() -> {
42 | while (true) {
43 | try {
44 | GlobalConfig = Config.readConfig();
45 | Thread.sleep(5000);
46 | } catch (InterruptedException e) {}
47 | }
48 | }).start();
49 | Handler.init();
50 |
51 | log.info("Program is running");
52 |
53 | try {
54 | TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
55 |
56 | if (GlobalConfig.getOnProxy()) {
57 | Bot = new CommandsHandler(RequestProxyConfig.create().buildDefaultBotOptions());
58 | } else {
59 | Bot = new CommandsHandler();
60 | }
61 |
62 | botsApi.registerBot(Bot);
63 | Store.init();
64 | MessageHandle.sendMessage(GlobalConfig.getBotAdminId(), I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.BotStartSucceed) + I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.CurrentVersion) + ": " + Config.MetaData.CurrentVersion, false);
65 |
66 | } catch (TelegramApiException e) {
67 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/code/commands/HelpCommand.java:
--------------------------------------------------------------------------------
1 | package code.commands;
2 |
3 | import code.config.I18nEnum;
4 | import code.handler.I18nHandle;
5 | import code.handler.message.MessageHandle;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand;
9 | import org.telegram.telegrambots.meta.api.objects.Chat;
10 | import org.telegram.telegrambots.meta.api.objects.Message;
11 | import org.telegram.telegrambots.meta.api.objects.User;
12 | import org.telegram.telegrambots.meta.bots.AbsSender;
13 |
14 | import static code.Main.GlobalConfig;
15 |
16 | @Slf4j
17 | public class HelpCommand extends BotCommand {
18 |
19 | public HelpCommand() {
20 | super("help", "");
21 | }
22 |
23 | @Override
24 | public void processMessage(AbsSender absSender, Message message, String[] arguments) {
25 | execute(absSender, message, arguments);
26 | }
27 |
28 | public void execute(AbsSender absSender, Message message, String[] arguments) {
29 | String chatId = message.getChat().getId().toString();
30 | String fromId = String.valueOf(message.getFrom().getId());
31 |
32 | if (StringUtils.isBlank(GlobalConfig.getHelpText())) {
33 | MessageHandle.sendMessage(chatId, I18nHandle.getText(fromId, I18nEnum.HelpText), false);
34 | } else {
35 | MessageHandle.sendMessage(chatId, GlobalConfig.getHelpText(), false);
36 | }
37 | }
38 |
39 | @Override
40 | public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/code/commands/StartCommand.java:
--------------------------------------------------------------------------------
1 | package code.commands;
2 |
3 | import code.config.I18nEnum;
4 | import code.handler.I18nHandle;
5 | import code.handler.message.MessageHandle;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand;
9 | import org.telegram.telegrambots.meta.api.objects.Chat;
10 | import org.telegram.telegrambots.meta.api.objects.Message;
11 | import org.telegram.telegrambots.meta.api.objects.User;
12 | import org.telegram.telegrambots.meta.bots.AbsSender;
13 |
14 | import static code.Main.GlobalConfig;
15 |
16 | @Slf4j
17 | public class StartCommand extends BotCommand {
18 |
19 | public StartCommand() {
20 | super("start", "");
21 | }
22 |
23 | @Override
24 | public void processMessage(AbsSender absSender, Message message, String[] arguments) {
25 | execute(absSender, message, arguments);
26 | }
27 |
28 | public void execute(AbsSender absSender, Message message, String[] arguments) {
29 | String chatId = message.getChat().getId().toString();
30 | String fromId = String.valueOf(message.getFrom().getId());
31 |
32 | if (StringUtils.isBlank(GlobalConfig.getStartText())) {
33 | MessageHandle.sendMessage(chatId, I18nHandle.getText(fromId, I18nEnum.HelpText), false);
34 | } else {
35 | MessageHandle.sendMessage(chatId, GlobalConfig.getStartText(), false);
36 | }
37 | }
38 |
39 | @Override
40 | public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
41 |
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/code/config/Config.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import code.util.ExceptionUtil;
4 | import code.util.gpt.GPTModel;
5 | import com.alibaba.fastjson2.JSON;
6 | import com.alibaba.fastjson2.JSONException;
7 | import com.alibaba.fastjson2.JSONReader;
8 | import com.alibaba.fastjson2.JSONWriter;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.apache.commons.io.FileUtils;
11 | import org.apache.commons.lang3.StringUtils;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.lang.reflect.Field;
16 | import java.nio.charset.StandardCharsets;
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Properties;
20 | import java.util.concurrent.locks.ReentrantReadWriteLock;
21 | import java.util.stream.Collectors;
22 |
23 | @Slf4j
24 | public class Config {
25 |
26 | private final static String UserDir = System.getProperty("user.dir");
27 |
28 | public final static String CurrentDir = (UserDir.equals("/") ? "" : UserDir) + File.separator + "config";
29 |
30 | public final static String SettingsPath = CurrentDir + File.separator + "config.json";
31 |
32 | public final static String TempDir = CurrentDir + File.separator + "temp";
33 |
34 | public final static String DBPath = CurrentDir + File.separator + "db.db";
35 |
36 | public final static String FFMPEGDir = CurrentDir + File.separator + "ffmpeg";
37 | public final static String FFMPEGPath = FFMPEGDir + File.separator + (System.getProperty("os.name").toLowerCase().contains("windows") ? "bin"+File.separator+"ffmpeg.exe" : "ffmpeg");
38 |
39 | private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
40 |
41 | public static class MetaData {
42 | public final static String CurrentVersion = "1.0.62";
43 | public final static String GitOwner = "kylelin1998";
44 | public final static String GitRepo = "ChatGPTForTelegram";
45 | public final static String ProcessName = "ChatGPTForTelegram-universal.jar";
46 | public final static String JarName = "ChatGPTForTelegram-universal.jar";
47 | }
48 |
49 | static {
50 | mkdirs(CurrentDir);
51 |
52 | List list = new ArrayList<>();
53 | list.add(UserDir);
54 | list.add(CurrentDir);
55 | list.add(SettingsPath);
56 | list.add(TempDir);
57 | log.info(list.stream().collect(Collectors.joining("\n")));
58 | }
59 |
60 | private static void mkdirs(String... path) {
61 | for (String s : path) {
62 | File file = new File(s);
63 | if (!file.exists()) {
64 | file.mkdirs();
65 | }
66 | }
67 | }
68 |
69 | public static ConfigSettings initConfig() {
70 | File file = new File(SettingsPath);
71 | if (!file.exists()) {
72 | Properties properties = System.getProperties();
73 |
74 | ConfigSettings configSettings = new ConfigSettings();
75 | configSettings.setGptToken(properties.getProperty("gptToken", ""));
76 | configSettings.setBotAdminId(properties.getProperty("botAdminId", ""));
77 | configSettings.setBotName(properties.getProperty("botName", ""));
78 | configSettings.setBotToken(properties.getProperty("botToken", ""));
79 | configSettings.setOnProxy(Boolean.valueOf(properties.getProperty("botProxy", "false")));
80 | configSettings.setProxyHost(properties.getProperty("botProxyHost", "127.0.0.1"));
81 | configSettings.setProxyPort(Integer.valueOf(properties.getProperty("botProxyPort", "7890")));
82 |
83 | saveConfig(handle(configSettings));
84 | }
85 | return readConfig();
86 | }
87 |
88 | private static ConfigSettings handle(ConfigSettings configSettings) {
89 | Boolean debug = configSettings.getDebug();
90 | if (null == debug) {
91 | configSettings.setDebug(false);
92 | }
93 | Boolean open = configSettings.getOpen();
94 | if (null == open) {
95 | configSettings.setOpen(false);
96 | }
97 | Boolean conciseReplies = configSettings.getConciseReplies();
98 | if (null == conciseReplies) {
99 | configSettings.setConciseReplies(false);
100 | }
101 | Boolean voice = configSettings.getVoice();
102 | if (null == voice) {
103 | configSettings.setVoice(false);
104 | }
105 | String[] blockChatIdArray = configSettings.getBlockChatIdArray();
106 | if (null == blockChatIdArray) {
107 | configSettings.setBlockChatIdArray(new String[]{});
108 | }
109 | String[] permissionChatIdArray = configSettings.getPermissionChatIdArray();
110 | if (null == permissionChatIdArray) {
111 | configSettings.setPermissionChatIdArray(new String[]{});
112 | }
113 | String gptModel = configSettings.getGptModel();
114 | if (StringUtils.isBlank(gptModel)) {
115 | configSettings.setGptModel(GPTModel.Gpt3_5Turbo.getModel());
116 | }
117 | String openaiAPIPrefix = configSettings.getOpenaiAPIPrefix();
118 | if (StringUtils.isBlank(openaiAPIPrefix)) {
119 | configSettings.setOpenaiAPIPrefix("https://api.openai.com");
120 | }
121 | return configSettings;
122 | }
123 |
124 | public static ConfigSettings readConfig() {
125 | ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
126 | readLock.lock();
127 | try {
128 | File file = new File(SettingsPath);
129 | boolean exists = file.exists();
130 | if (exists) {
131 | String text = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
132 | ConfigSettings configSettings = JSON.parseObject(text, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch);
133 | return handle(configSettings);
134 | } else {
135 | log.warn("Settings file not found, " + SettingsPath);
136 | }
137 | } catch (IOException e) {
138 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e), SettingsPath);
139 | } finally {
140 | readLock.unlock();
141 | }
142 | return null;
143 | }
144 |
145 | public static ConfigSettings verifyConfig(String configJson) {
146 | if (StringUtils.isBlank(configJson)) {
147 | return null;
148 | }
149 | ConfigSettings configSettings = null;
150 | try {
151 | configSettings = JSON.parseObject(configJson, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch);
152 | } catch (JSONException e) {
153 | }
154 | if (null == configSettings) {
155 | return null;
156 | }
157 | for (Field field : configSettings.getClass().getDeclaredFields()) {
158 | ConfigField configField = field.getAnnotation(ConfigField.class);
159 | if (null == configField) {
160 | continue;
161 | }
162 | if (configField.isNotNull()) {
163 | try {
164 | field.setAccessible(true);
165 | Object o = field.get(configSettings);
166 | if (null == o) {
167 | return null;
168 | }
169 | } catch (IllegalAccessException e) {
170 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
171 | return null;
172 | }
173 | }
174 | }
175 |
176 | return configSettings;
177 | }
178 |
179 | public static boolean saveConfig(ConfigSettings configSettings) {
180 | ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
181 | writeLock.lock();
182 | try {
183 | File file = new File(SettingsPath);
184 | FileUtils.write(file, JSON.toJSONString(configSettings, JSONWriter.Feature.PrettyFormat), StandardCharsets.UTF_8);
185 | return true;
186 | } catch (IOException e) {
187 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
188 | } finally {
189 | writeLock.unlock();
190 | }
191 | return false;
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/src/main/java/code/config/ConfigField.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target(value = ElementType.FIELD)
9 | @Retention(value = RetentionPolicy.RUNTIME)
10 | public @interface ConfigField {
11 |
12 | boolean isNotNull();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/code/config/ConfigSettings.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import com.alibaba.fastjson2.annotation.JSONField;
4 | import lombok.Data;
5 |
6 | @Data
7 | public class ConfigSettings {
8 |
9 | @ConfigField(isNotNull = true)
10 | private Boolean debug;
11 |
12 | @JSONField(name = "on_proxy")
13 | @ConfigField(isNotNull = true)
14 | private Boolean onProxy;
15 | @JSONField(name = "proxy_host")
16 | private String proxyHost;
17 | @JSONField(name = "proxy_port")
18 | private Integer proxyPort;
19 |
20 | @JSONField(name = "bot_admin_id")
21 | @ConfigField(isNotNull = true)
22 | private String botAdminId;
23 |
24 | @ConfigField(isNotNull = true)
25 | private Boolean voice;
26 |
27 | @ConfigField(isNotNull = true)
28 | private Boolean open;
29 | @JSONField(name = "permission_chat_id_array")
30 | private String[] permissionChatIdArray;
31 | @JSONField(name = "block_chat_id_array")
32 | private String[] blockChatIdArray;
33 |
34 | @JSONField(name = "bot_name")
35 | @ConfigField(isNotNull = true)
36 | private String botName;
37 | @JSONField(name = "bot_token")
38 | @ConfigField(isNotNull = true)
39 | private String botToken;
40 |
41 | @JSONField(name = "gpt_token")
42 | @ConfigField(isNotNull = true)
43 | private String gptToken;
44 | @JSONField(name = "gpt_model")
45 | @ConfigField(isNotNull = true)
46 | private String gptModel;
47 |
48 | @JSONField(name = "chat_buttons")
49 | @ConfigField(isNotNull = false)
50 | private String chatButtons;
51 |
52 | @JSONField(name = "concise_replies")
53 | @ConfigField(isNotNull = false)
54 | private Boolean conciseReplies;
55 |
56 | @JSONField(name = "openai_api_prefix")
57 | @ConfigField(isNotNull = true)
58 | private String openaiAPIPrefix;
59 |
60 | @JSONField(name = "start_text")
61 | @ConfigField(isNotNull = false)
62 | private String startText;
63 |
64 | @JSONField(name = "help_text")
65 | @ConfigField(isNotNull = false)
66 | private String helpText;
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/code/config/ExecutorsConfig.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
5 |
6 | import java.util.concurrent.*;
7 |
8 | @Slf4j
9 | public class ExecutorsConfig {
10 |
11 | private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(false).setNameFormat("handle-pool-%d").build();
12 |
13 | private static ExecutorService fixedThreadPool = new ThreadPoolExecutor(
14 | 5,
15 | 20,
16 | 60,
17 | TimeUnit.SECONDS,
18 | new ArrayBlockingQueue<>(200),
19 | threadFactory,
20 | (Runnable r, ThreadPoolExecutor executor) -> {
21 | log.error(r.toString() + " is Rejected");
22 | }
23 | );
24 |
25 | public static void submit(Runnable task) {
26 | fixedThreadPool.submit(task);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/code/config/I18nConfig.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | import java.util.HashMap;
6 | import java.util.LinkedHashMap;
7 | import java.util.Map;
8 | import java.util.ResourceBundle;
9 |
10 | public class I18nConfig {
11 |
12 | private static Map> cacheMap = new LinkedHashMap<>();
13 |
14 | static {
15 | for (I18nLocaleEnum value : I18nLocaleEnum.values()) {
16 | ResourceBundle bundle = ResourceBundle.getBundle("i18n/i18n", value.getLocale());
17 | HashMap hashMap = new HashMap<>();
18 | for (String s : bundle.keySet()) {
19 | hashMap.put(s, bundle.getString(s));
20 | }
21 |
22 | cacheMap.put(value.getAlias(), hashMap);
23 | }
24 | }
25 |
26 | public static String getText(String i18nAlias, String key) {
27 | Map map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias());
28 | return map.get(key);
29 | }
30 |
31 | public static String getText(String i18nAlias, I18nEnum i18nEnum) {
32 | Map map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias());
33 | return map.get(i18nEnum.getKey());
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/code/config/I18nEnum.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import lombok.Getter;
4 |
5 | @Getter
6 | public enum I18nEnum {
7 |
8 | BotStartSucceed("bot_start_succeed"),
9 | HelpText("help_text"),
10 |
11 | InvalidCommand("invalid_command"),
12 | On("on"),
13 | Off("off"),
14 | Test("test"),
15 | Update("update"),
16 | NotFound("not_found"),
17 | UnknownError("unknown_error"),
18 | NothingAtAll("nothing_at_all"),
19 | CancelSucceeded("cancel_succeeded"),
20 | Confirm("confirm"),
21 | Cancel("cancel"),
22 | Delete("delete"),
23 | Finish("finish"),
24 | ExitSucceeded("exit_succeeded"),
25 | Getting("getting"),
26 | Downloading("downloading"),
27 | UpdateConfig("update_config"),
28 | Restart("restart"),
29 | Upgrade("upgrade"),
30 | UpdateSucceeded("update_succeeded"),
31 | UpdateFailed("update_failed"),
32 | Open("open"),
33 | Close("close"),
34 | Processing("processing"),
35 |
36 | LanguageList("language_list"),
37 | ChangeLanguageFinish("change_language_finish"),
38 |
39 | ThisChatIsANewChat("this_chat_is_a_new_chat"),
40 | AreYouSureToUpdateTheConfig("are_you_sure_to_update_the_config"),
41 | PleaseSendMeConfigContent("please_send_me_config_content"),
42 | UpdateConfigFail("update_config_fail"),
43 | PleaseSendMeAProblemThatYouWantToAsk("please_send_me_a_problem_that_you_want_to_ask"),
44 | RequestingOpenAiApi("requesting_open_ai_api"),
45 | TheCurrentModeIsContinuousChatMode("the_current_mode_is_continuous_chat_mode"),
46 | AnErrorOccurredOfRequestingOpenAiApiFailed("an_error_occurred_of_requesting_open_ai_api_failed"),
47 | ContinueThisChat("continue_this_chat"),
48 | AskChatEnded("ask_chat_ended"),
49 | TheCurrentModeIsChatMessageLimitMode("the_current_mode_is_chat_message_limit_mode"),
50 | CmlChatEnded("cml_chat_ended"),
51 | CmlContinueThisChat("cml_continue_this_chat"),
52 | TheCurrentModeIsNoneOfMessageContextMode("the_current_mode_is_none_of_message_context_mode"),
53 |
54 | YouAreNotAnAdmin("you_are_not_an_admin"),
55 | AreYouSureToRestartRightNow("are_you_sure_to_restart_right_now"),
56 | Restarting("restarting"),
57 | GettingUpdateData("getting_update_data"),
58 | AreYouSureToUpgradeThisBotRightNow("are_you_sure_to_upgrade_this_bot_right_now"),
59 | TargetVersion("target_version"),
60 | CurrentVersion("current_version"),
61 | UpdateLogs("update_logs"),
62 | Updating("updating"),
63 | Downloaded("downloaded"),
64 | PleaseSendMeAnImageDescription("please_send_me_an_image_description"),
65 | ImageDescriptionTextCharacterCountMoreThan("image_description_text_character_count_more_than"),
66 |
67 | ChatHasTooManyConversations("chat_has_too_many_conversations"),
68 | SetOpenStatus("set_open_status"),
69 | ChooseOpenStatus("choose_open_status"),
70 | Model("model"),
71 | ChangeModel("change_model"),
72 | PleaseSendMeTheModelYouWantToChange("please_send_me_the_model_you_want_to_change"),
73 |
74 | SetVoiceStatus("set_voice_status"),
75 | Unzipping("unzipping"),
76 | PleaseSendMeThePlaybackAlias("please_send_me_the_playback_alias"),
77 | XXXNotFound("xxx_not_found"),
78 | RecordModeOpened("record_mode_opened"),
79 | PleaseSendMeRecordAlias("please_send_me_record_alias"),
80 | InvalidAlias("invalid_alias"),
81 | PleaseSendMeRecordExplanations("Please_send_me_record_explanations"),
82 | InvalidExplanations("invalid_explanations"),
83 | Saving("saving"),
84 | SaveSucceeded("save_succeeded"),
85 | SaveFailed("save_failed"),
86 | RecordAlias("record_alias"),
87 | RecordExplanations("record_explanations"),
88 | YouCanUseRecordToStartRecoding("you_can_use_record_to_start_recoding"),
89 | DeleteSucceeded("delete_succeeded"),
90 | DeleteFailed("delete_failed"),
91 | ChooseVoiceStatus("choose_voice_status"),
92 | DownloadingXXX("downloading_xxx"),
93 |
94 | PleaseSendMeChatButtons("please_send_me_chat_buttons"),
95 | FormatError("format_error"),
96 | SetChatButtons("set_chat_buttons"),
97 |
98 | Tip429("tip_429"),
99 | SetGptToken("set_gpt_token"),
100 | PleaseSendMeGptToken("please_send_me_gpt_token"),
101 | Alive("alive"),
102 | Die("die"),
103 | PleaseChooseConciseReplies("please_choose_concise_replies"),
104 | SetConciseReplies("set_concise_replies"),
105 | SetOpenAIAPIPrefix("set_openai_api_prefix"),
106 | SetStartText("set_start_text"),
107 | SetHelpText("set_help_text"),
108 | PleaseSendMeStartText("please_send_me_start_text"),
109 | PleaseSendMeHelpText("please_send_me_help_text"),
110 |
111 | ;
112 |
113 | private String key;
114 |
115 | I18nEnum(String key) {
116 | this.key = key;
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/code/config/I18nLocaleEnum.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import lombok.Getter;
4 |
5 | import java.util.Locale;
6 |
7 | @Getter
8 | public enum I18nLocaleEnum {
9 | ZH_CN(Locale.SIMPLIFIED_CHINESE, "zh-cn", "简体中文"),
10 | EN(Locale.US, "en", "English"),
11 |
12 | ;
13 |
14 | private Locale locale;
15 | private String alias;
16 | private String displayText;
17 |
18 | I18nLocaleEnum(Locale locale, String alias, String displayText) {
19 | this.locale = locale;
20 | this.alias = alias;
21 | this.displayText = displayText;
22 | }
23 |
24 | public static I18nLocaleEnum getI18nLocaleEnumByAlias(String alias) {
25 | for (I18nLocaleEnum value : I18nLocaleEnum.values()) {
26 | if (value.getAlias().equals(alias)) {
27 | return value;
28 | }
29 | }
30 | return null;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/code/config/ProxyTypeEnum.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import lombok.Getter;
4 |
5 | @Getter
6 | public enum ProxyTypeEnum {
7 |
8 | NotOpen(0, "Not Open"),
9 | HttpProxy(1, "Http Proxy"),
10 |
11 | ;
12 |
13 | private int type;
14 | private String alias;
15 |
16 | ProxyTypeEnum(int type, String alias) {
17 | this.type = type;
18 | this.alias = alias;
19 | }
20 |
21 | public static ProxyTypeEnum getDefault() {
22 | return NotOpen;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/code/config/RequestProxyConfig.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import kong.unirest.HttpRequest;
4 | import org.apache.hc.client5.http.fluent.Request;
5 | import org.apache.hc.core5.http.HttpHost;
6 | import org.telegram.telegrambots.bots.DefaultBotOptions;
7 |
8 | import static code.Main.GlobalConfig;
9 |
10 | public class RequestProxyConfig {
11 |
12 | private ProxyTypeEnum type;
13 | private String hostName;
14 | private Integer port;
15 |
16 | public static RequestProxyConfig create() {
17 | RequestProxyConfig config = new RequestProxyConfig();
18 | if (null != GlobalConfig && GlobalConfig.getOnProxy()) {
19 | config.type = ProxyTypeEnum.HttpProxy;
20 | config.hostName = GlobalConfig.getProxyHost();
21 | config.port = GlobalConfig.getProxyPort();
22 | } else {
23 | config.type = ProxyTypeEnum.getDefault();
24 | }
25 | return config;
26 | }
27 |
28 | public void viaProxy(HttpRequest request) {
29 | switch (this.type) {
30 | case HttpProxy:
31 | request.proxy(this.hostName, this.port);
32 | break;
33 | }
34 | }
35 |
36 | public void viaProxy(Request request) {
37 | switch (this.type) {
38 | case HttpProxy:
39 | request.viaProxy(new HttpHost(this.hostName, this.port));
40 | break;
41 | }
42 | }
43 |
44 | public DefaultBotOptions buildDefaultBotOptions() {
45 | switch (this.type) {
46 | case HttpProxy:
47 | DefaultBotOptions botOptions = new DefaultBotOptions();
48 |
49 | botOptions.setProxyHost(this.hostName);
50 | botOptions.setProxyPort(this.port);
51 | botOptions.setProxyType(DefaultBotOptions.ProxyType.HTTP);
52 | return botOptions;
53 | }
54 | return null;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/code/config/TableEnum.java:
--------------------------------------------------------------------------------
1 | package code.config;
2 |
3 | import lombok.Getter;
4 |
5 | @Getter
6 | public enum TableEnum {
7 |
8 | UserGeoTable("user_geo_table"),
9 | I18nTable("i18n_table"),
10 |
11 | ;
12 |
13 | private String name;
14 |
15 | TableEnum(String name) {
16 | this.name = name;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/code/eneity/GptTokenTableEntity.java:
--------------------------------------------------------------------------------
1 | package code.eneity;
2 |
3 | import code.repository.mapper.TableEntity;
4 | import code.repository.mapper.TableField;
5 | import code.repository.mapper.TableName;
6 | import lombok.Data;
7 |
8 | @TableName(name = "gpt_token_table")
9 | @Data
10 | public class GptTokenTableEntity implements TableEntity {
11 | @TableField(name = "token", sql = "token varchar(100) primary key")
12 | private String token;
13 |
14 | @TableField(name = "status", sql = "status int comment '1.正常 2.死亡'")
15 | private Integer status;
16 |
17 | @TableField(name = "send", sql = "send int comment '1.未发送 2.已发送'")
18 | private Integer send;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/code/eneity/RecordTableEntity.java:
--------------------------------------------------------------------------------
1 | package code.eneity;
2 |
3 | import code.repository.mapper.TableEntity;
4 | import code.repository.mapper.TableField;
5 | import code.repository.mapper.TableName;
6 | import lombok.Data;
7 |
8 | @TableName(name = "record_table")
9 | @Data
10 | public class RecordTableEntity implements TableEntity {
11 |
12 | @TableField(name = "id", sql = "id varchar(55) primary key")
13 | private String id;
14 |
15 | @TableField(name = "record_alias", sql = "record_alias varchar(50)")
16 | private String recordAlias;
17 |
18 | @TableField(name = "record_explains", sql = "record_explains varchar(255)")
19 | private String recordExplains;
20 |
21 | @TableField(name = "create_time", sql = "create_time timestamp")
22 | private Long createTime;
23 | @TableField(name = "update_time", sql = "update_time timestamp")
24 | private Long updateTime;
25 |
26 | @TableField(name = "chat_id", sql = "chat_id varchar(88)")
27 | private String chatId;
28 |
29 | @TableField(name = "chat_template_json", sql = "chat_template_json text")
30 | private String chatTemplateJson;
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/code/eneity/cons/GptTokenStatusEnum.java:
--------------------------------------------------------------------------------
1 | package code.eneity.cons;
2 |
3 | import lombok.Getter;
4 |
5 | import java.util.Optional;
6 |
7 | @Getter
8 | public enum GptTokenStatusEnum {
9 |
10 | Alive(1, "正常"),
11 | Die(2, "死亡"),
12 |
13 | ;
14 |
15 | private int num;
16 | private String name;
17 | GptTokenStatusEnum(int num, String name) {
18 | this.num = num;
19 | this.name = name;
20 | }
21 |
22 | public static Optional get(int num) {
23 | for (GptTokenStatusEnum value : values()) {
24 | if (value.getNum() == num) {
25 | return Optional.of(value);
26 | }
27 | }
28 | return Optional.empty();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/code/eneity/cons/YesOrNoEnum.java:
--------------------------------------------------------------------------------
1 | package code.eneity.cons;
2 |
3 | import lombok.Getter;
4 |
5 | import java.util.Optional;
6 |
7 | @Getter
8 | public enum YesOrNoEnum {
9 |
10 | Yes(2, true),
11 | No(1, false),
12 |
13 | ;
14 |
15 | private int num;
16 | private boolean bool;
17 |
18 | YesOrNoEnum(int num, boolean bool) {
19 | this.num = num;
20 | this.bool = bool;
21 | }
22 |
23 | public static int toInt(boolean bool) {
24 | return bool ? Yes.getNum() : No.getNum();
25 | }
26 |
27 | public static Optional toBoolean(int num) {
28 | for (YesOrNoEnum value : values()) {
29 | if (num == value.getNum()) {
30 | return Optional.of(value.isBool());
31 | }
32 | }
33 | return Optional.empty();
34 | }
35 |
36 | public static Optional get(int num) {
37 | for (YesOrNoEnum value : values()) {
38 | if (value.getNum() == num) {
39 | return Optional.of(value);
40 | }
41 | }
42 | return Optional.empty();
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/Command.java:
--------------------------------------------------------------------------------
1 | package code.handler;
2 |
3 | import lombok.Getter;
4 | import org.apache.commons.lang3.StringUtils;
5 |
6 | import java.util.regex.Matcher;
7 | import java.util.regex.Pattern;
8 |
9 | @Getter
10 | public enum Command {
11 |
12 | Chat("chat", false),
13 | ChatShorter("c", false),
14 | Ask("ask", false),
15 | AskShorter("a", false),
16 | ChatMsgLimit("cml", false),
17 | NoneOfContextChatMessage("nccm", false),
18 | Exit("exit", false),
19 | Admin("admin", false),
20 | Image("image", false),
21 |
22 | Record("record", false),
23 | Playback("p", false),
24 | PlaybackRegex("^[,,]", true),
25 | RecordList("record_list", false),
26 | GetRecord("get_record", false),
27 | DeleteRecord("delete_record", false),
28 |
29 | SetChatButtons("set_chat_buttons", false),
30 | SetVoiceStatus("set_voice_status", false),
31 | ChangeModel("change_model", false),
32 | SetOpenStatus("set_open_status", false),
33 | SetGptToken("set_gpt_token", false),
34 | SetConciseReplies("set_concise_replies", false),
35 | SetOpenAIAPIPrefix("set_openai_api_prefix", false),
36 | SetStartText("set_start_text", false),
37 | SetHelpText("set_help_text", false),
38 | UpdateConfig("uc", false),
39 | Language("language", false),
40 | Restart("restart", false),
41 | Upgrade("upgrade", false),
42 |
43 | ;
44 |
45 | private String cmd;
46 | private boolean regex;
47 |
48 | Command(String cmd, boolean regex) {
49 | this.cmd = cmd;
50 | this.regex = regex;
51 | }
52 |
53 | public static Command toCmd(String cmd) {
54 | for (Command value : Command.values()) {
55 | if (value.getCmd().equals(cmd)) {
56 | return value;
57 | }
58 | }
59 | return null;
60 | }
61 | public static Command regexMatch(String cmd) {
62 | if (StringUtils.isBlank(cmd)) {
63 | return null;
64 | }
65 |
66 | for (Command value : values()) {
67 | if (value.isRegex()) {
68 | Pattern pattern = Pattern.compile(value.getCmd());
69 | Matcher matcher = pattern.matcher(cmd);
70 | if (matcher.find()) {
71 | return value;
72 | }
73 | }
74 | }
75 | return null;
76 | }
77 |
78 | public static boolean exist(String cmd) {
79 | return null != toCmd(cmd);
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/CommandsHandler.java:
--------------------------------------------------------------------------------
1 | package code.handler;
2 |
3 | import code.commands.*;
4 | import code.handler.message.MessageHandle;
5 | import code.handler.steps.StepsChatSession;
6 | import code.handler.steps.StepsChatSessionBuilder;
7 | import com.alibaba.fastjson2.JSON;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.telegram.telegrambots.bots.DefaultBotOptions;
11 | import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot;
12 | import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
13 | import org.telegram.telegrambots.meta.api.objects.Message;
14 | import org.telegram.telegrambots.meta.api.objects.Update;
15 |
16 | import static code.Main.GlobalConfig;
17 |
18 | @Slf4j
19 | public class CommandsHandler extends TelegramLongPollingCommandBot {
20 |
21 | public CommandsHandler() {
22 | super();
23 | start();
24 | }
25 |
26 | public CommandsHandler(DefaultBotOptions botOptions) {
27 | super(botOptions);
28 | start();
29 | }
30 |
31 | public void start() {
32 | register(new HelpCommand());
33 | register(new StartCommand());
34 | }
35 |
36 | @Override
37 | public String getBotUsername() {
38 | return GlobalConfig.getBotName();
39 | }
40 |
41 | @Override
42 | public String getBotToken() {
43 | return GlobalConfig.getBotToken();
44 | }
45 |
46 | @Override
47 | public void processNonCommandUpdate(Update update) {
48 | if (GlobalConfig.getDebug()) {
49 | log.info(JSON.toJSONString(update));
50 | }
51 |
52 | CallbackQuery callbackQuery = update.getCallbackQuery();
53 | if (null != callbackQuery) {
54 | String data = callbackQuery.getData();
55 | StepsCenter.CallbackData callbackData = StepsCenter.parseCallbackData(data);
56 | if (null == callbackData) {
57 | MessageHandle.sendMessage(String.valueOf(callbackQuery.getMessage().getChatId()), "Error...", false);
58 | return;
59 | }
60 |
61 | StepsChatSession session = StepsChatSessionBuilder
62 | .create(callbackQuery)
63 | .setText(callbackData.getText())
64 | .build();
65 |
66 | if (!session.getFromId().equals(String.valueOf(callbackData.getFromId()))) {
67 | return;
68 | }
69 |
70 | if (StringUtils.isNotBlank(data)) {
71 | StepsCenter.cmdHandle(callbackData, session);
72 | return;
73 | }
74 | }
75 |
76 | Message message = update.getMessage();
77 | if (null == message) {
78 | return;
79 | }
80 | if (StringUtils.isNotEmpty(message.getText()) || null != message.getVoice()) {
81 | boolean handle = StepsCenter.cmdHandle(StepsChatSessionBuilder.create(message).build());
82 | if (!handle) {
83 | StepsCenter.messageHandle(StepsChatSessionBuilder.create(message).build());
84 | }
85 | }
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/I18nHandle.java:
--------------------------------------------------------------------------------
1 | package code.handler;
2 |
3 | import code.config.I18nConfig;
4 | import code.config.I18nEnum;
5 | import code.config.I18nLocaleEnum;
6 | import org.apache.commons.lang3.StringUtils;
7 |
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | import static code.Main.I18nTableRepository;
12 |
13 | public class I18nHandle {
14 |
15 | private static Map cacheMap = new HashMap<>();
16 |
17 | public static String getText(String chatId, String key) {
18 | return getText(chatId, key, null);
19 | }
20 |
21 | public static String getText(String chatId, String key, Object... args) {
22 | String alias = cacheMap.get(chatId);
23 | if (StringUtils.isBlank(alias)) {
24 | alias = I18nTableRepository.selectI18nAlias(chatId);
25 | cacheMap.put(chatId, alias);
26 | }
27 |
28 | String text = I18nConfig.getText(alias, key);
29 | if (null != args && args.length > 0) {
30 | return String.format(text, args);
31 | }
32 | return text;
33 | }
34 |
35 | public static String getText(String chatId, I18nEnum i18nEnum) {
36 | return getText(chatId, i18nEnum, null);
37 | }
38 | public static String getText(String chatId, I18nEnum i18nEnum, Object... args) {
39 | String alias = cacheMap.get(chatId);
40 | if (StringUtils.isBlank(alias)) {
41 | alias = I18nTableRepository.selectI18nAlias(chatId);
42 | cacheMap.put(chatId, alias);
43 | }
44 |
45 | String text = I18nConfig.getText(alias, i18nEnum);
46 | if (null != args && args.length > 0) {
47 | return String.format(text, args);
48 | }
49 | return text;
50 | }
51 |
52 | public static void save(String chatId, I18nLocaleEnum i18nLocaleEnum) {
53 | I18nTableRepository.save(chatId, i18nLocaleEnum.getAlias());
54 | cacheMap.put(chatId, i18nLocaleEnum.getAlias());
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/StepsCenter.java:
--------------------------------------------------------------------------------
1 | package code.handler;
2 |
3 | import code.config.ExecutorsConfig;
4 | import code.handler.message.MessageHandle;
5 | import code.handler.steps.StepsChatSession;
6 | import code.handler.steps.StepsHandler;
7 | import code.handler.steps.StepsRegisterCenter;
8 | import code.util.ExceptionUtil;
9 | import lombok.Data;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.apache.commons.lang3.StringUtils;
12 |
13 | import java.util.Collection;
14 | import java.util.stream.Collectors;
15 | import java.util.stream.Stream;
16 |
17 | import static code.Main.GlobalConfig;
18 |
19 | @Slf4j
20 | public class StepsCenter {
21 |
22 | @Data
23 | public static class CallbackData {
24 | private boolean init;
25 | private String fromId;
26 | private Command command;
27 | private String text;
28 | }
29 |
30 | public static String buildCallbackData(boolean init, StepsChatSession session, Command command, String text) {
31 | StringBuilder builder = new StringBuilder();
32 | builder.append("f[" + session.getFromId() + "]");
33 | builder.append(command.getCmd());
34 | builder.append(" ");
35 | builder.append(init);
36 | builder.append(" ");
37 | builder.append(text);
38 | return builder.toString();
39 | }
40 | public static CallbackData parseCallbackData(String callbackData) {
41 | try {
42 | CallbackData data = new CallbackData();
43 | data.setFromId(StringUtils.substringBetween(callbackData, "f[", "]"));
44 |
45 | String s = StringUtils.replace(callbackData, "f[" + data.getFromId() + "]", "");
46 | String[] arguments = s.split(" ");
47 |
48 | data.setCommand(Command.toCmd(arguments[0]));
49 | data.setInit(Boolean.valueOf(arguments[1]));
50 | data.setText(arguments.length > 2 ? arguments[2] : null);
51 |
52 | return data;
53 | } catch (Exception e) {
54 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
55 | }
56 | return null;
57 | }
58 |
59 | public static boolean cmdHandle(StepsChatSession session) {
60 | if (StringUtils.isNotBlank(session.getText())) {
61 | if (session.getText().startsWith("/")) {
62 | String s = StringUtils.remove(session.getText(), "/");
63 | String[] split = s.split(" ");
64 | if (split.length > 0) {
65 | String cmd = split[0];
66 | cmd = StringUtils.replace(cmd, "@" + GlobalConfig.getBotName(), "");
67 | if (Command.exist(cmd)) {
68 | split[0] = cmd;
69 | session.setText(Stream.of(split).skip(1).collect(Collectors.joining(" ")));
70 | cmdHandle(
71 | Command.toCmd(cmd),
72 | false,
73 | session,
74 | null
75 | );
76 | return true;
77 | }
78 | }
79 | } else {
80 | Command command = Command.regexMatch(session.getText());
81 | if (null != command) {
82 | session.setText(session.getText().replaceFirst(command.getCmd(), ""));
83 | cmdHandle(
84 | command,
85 | false,
86 | session,
87 | null
88 | );
89 | return true;
90 | }
91 | }
92 | }
93 | return false;
94 | }
95 |
96 | public static void cmdHandle(CallbackData callbackData, StepsChatSession stepsChatSession) {
97 | cmdHandle(callbackData.getCommand(), true, stepsChatSession, callbackData);
98 | }
99 |
100 | public static void cmdHandle(Command command, StepsChatSession stepsChatSession) {
101 | cmdHandle(command, false, stepsChatSession, null);
102 | }
103 |
104 | private static void cmdHandle(Command command, boolean isCall, StepsChatSession stepsChatSession, CallbackData callbackData) {
105 | Boolean open = GlobalConfig.getOpen();
106 | if (!open) {
107 | boolean permission = false;
108 |
109 | String botAdminId = GlobalConfig.getBotAdminId();
110 | if (botAdminId.equals(stepsChatSession.getChatId()) || botAdminId.equals(stepsChatSession.getFromId())) {
111 | permission = true;
112 | }
113 | for (String s : GlobalConfig.getPermissionChatIdArray()) {
114 | if (s.equals(stepsChatSession.getChatId()) || s.equals(stepsChatSession.getFromId())) {
115 | permission = true;
116 | break;
117 | }
118 | }
119 |
120 | if (!permission) {
121 | MessageHandle.sendMessage(stepsChatSession.getChatId(), stepsChatSession.getReplyToMessageId(), "你没有使用权限, 不过你可以自己搭建一个\nhttps://github.com/kylelin1998/ChatGPTForTelegram", false);
122 | return;
123 | }
124 | }
125 | for (String s : GlobalConfig.getBlockChatIdArray()) {
126 | if (s.equals(stepsChatSession.getChatId()) || s.equals(stepsChatSession.getFromId())) {
127 | MessageHandle.sendMessage(stepsChatSession.getChatId(), stepsChatSession.getReplyToMessageId(), "系统限制, 你没有使用权限, 不过你可以自己搭建一个\nhttps://github.com/kylelin1998/ChatGPTForTelegram", false);
128 | return;
129 | }
130 | }
131 |
132 | if (null != callbackData){
133 | StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd());
134 | if (!callbackData.isInit() && !handler.hasInit(stepsChatSession)) {
135 | return;
136 | }
137 | }
138 |
139 | ExecutorsConfig.submit(() -> {
140 | StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd());
141 | if (null != handler.getInitStep() && (!handler.hasInit(stepsChatSession) || !isCall)) {
142 | handler.init(stepsChatSession);
143 | } else {
144 | handler.step(stepsChatSession);
145 | }
146 | });
147 | }
148 |
149 | public static void messageHandle(StepsChatSession stepsChatSession) {
150 | StepsHandler handler = StepsRegisterCenter.getPriority(stepsChatSession);
151 | if (null == handler) {
152 | return;
153 | }
154 | ExecutorsConfig.submit(() -> {
155 | handler.step(stepsChatSession);
156 | });
157 | }
158 |
159 | public static void exit(StepsChatSession stepsChatSession) {
160 | Collection list = StepsRegisterCenter.getRegisterList();
161 | for (StepsHandler handler : list) {
162 | handler.exit(stepsChatSession);
163 | }
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/message/InlineKeyboardButtonBuilder.java:
--------------------------------------------------------------------------------
1 | package code.handler.message;
2 |
3 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class InlineKeyboardButtonBuilder {
9 |
10 | private List inlineKeyboardButtonList;
11 |
12 | private String callbackData;
13 |
14 | private InlineKeyboardButtonBuilder() {}
15 |
16 | public static InlineKeyboardButtonBuilder create() {
17 | InlineKeyboardButtonBuilder builder = new InlineKeyboardButtonBuilder();
18 | builder.inlineKeyboardButtonList = new ArrayList<>();
19 | return builder;
20 | }
21 |
22 | public InlineKeyboardButtonBuilder setCallbackData(String callbackData) {
23 | this.callbackData = callbackData;
24 | return this;
25 | }
26 |
27 | public InlineKeyboardButtonBuilder add(String text, String callbackData) {
28 | InlineKeyboardButton button = new InlineKeyboardButton();
29 | button.setText(text);
30 | button.setCallbackData(callbackData);
31 | inlineKeyboardButtonList.add(button);
32 | return this;
33 | }
34 |
35 | public InlineKeyboardButtonBuilder add(String text) {
36 | InlineKeyboardButton button = new InlineKeyboardButton();
37 | button.setText(text);
38 | button.setCallbackData(this.callbackData);
39 | inlineKeyboardButtonList.add(button);
40 | return this;
41 | }
42 |
43 | public InlineKeyboardButtonBuilder add(InlineKeyboardButton button) {
44 | inlineKeyboardButtonList.add(button);
45 | return this;
46 | }
47 |
48 | public List build() {
49 | return inlineKeyboardButtonList;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/message/InlineKeyboardButtonListBuilder.java:
--------------------------------------------------------------------------------
1 | package code.handler.message;
2 |
3 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class InlineKeyboardButtonListBuilder {
9 | private List> keyboard;
10 | private InlineKeyboardButtonListBuilder() {}
11 |
12 | public static InlineKeyboardButtonListBuilder create() {
13 | InlineKeyboardButtonListBuilder builder = new InlineKeyboardButtonListBuilder();
14 | builder.keyboard = new ArrayList<>();
15 | return builder;
16 | }
17 |
18 | public InlineKeyboardButtonListBuilder add(List inlineKeyboardButtonList) {
19 | this.keyboard.add(inlineKeyboardButtonList);
20 | return this;
21 | }
22 |
23 | public List> build() {
24 | return keyboard;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/message/MessageHandle.java:
--------------------------------------------------------------------------------
1 | package code.handler.message;
2 |
3 | import code.util.ExceptionUtil;
4 | import com.alibaba.fastjson2.JSON;
5 | import lombok.Data;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.telegram.telegrambots.meta.api.methods.ParseMode;
9 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
10 | import org.telegram.telegrambots.meta.api.methods.send.SendPhoto;
11 | import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage;
12 | import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText;
13 | import org.telegram.telegrambots.meta.api.objects.InputFile;
14 | import org.telegram.telegrambots.meta.api.objects.Message;
15 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
16 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
17 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
18 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
19 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
20 |
21 | import java.io.File;
22 | import java.io.InputStream;
23 | import java.util.ArrayList;
24 | import java.util.Arrays;
25 | import java.util.List;
26 |
27 | import static code.Main.Bot;
28 |
29 | @Slf4j
30 | public class MessageHandle {
31 |
32 | public enum MessageError {
33 | BotWasBlockedByTheUser,
34 |
35 | ;
36 | }
37 |
38 | @Data
39 | public static class MessageResponse {
40 | private boolean ok;
41 | private Message message;
42 | private MessageError messageError;
43 | }
44 |
45 | public static Message sendImage(String chatId, Integer replyToMessageId, String text, InputStream image) {
46 | SendPhoto sendPhoto = new SendPhoto();
47 | sendPhoto.setChatId(chatId);
48 | sendPhoto.setReplyToMessageId(replyToMessageId);
49 | sendPhoto.setCaption(text);
50 | sendPhoto.setPhoto(new InputFile(image, "image.png"));
51 |
52 | try {
53 | return Bot.execute(sendPhoto);
54 | } catch (TelegramApiException e) {
55 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
56 | }
57 | return null;
58 | }
59 |
60 | public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image) {
61 | SendPhoto sendPhoto = new SendPhoto();
62 | sendPhoto.setChatId(chatId);
63 | sendPhoto.setReplyToMessageId(replyToMessageId);
64 | sendPhoto.setCaption(text);
65 | sendPhoto.setPhoto(new InputFile(image));
66 |
67 | try {
68 | return Bot.execute(sendPhoto);
69 | } catch (TelegramApiException e) {
70 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
71 | }
72 | return null;
73 | }
74 |
75 | public static Message sendInlineKeyboard(String chatId, String text, InlineKeyboardButton... inlineKeyboardButtonList) {
76 | return sendInlineKeyboard(chatId, text, Arrays.asList(inlineKeyboardButtonList));
77 | }
78 |
79 | public static Message sendInlineKeyboardList(String chatId, String text, List> keyboard) {
80 | return sendInlineKeyboardList(chatId, null, text, keyboard);
81 | }
82 |
83 | public static Message sendInlineKeyboardList(String chatId, Integer replyToMessageId, String text, List> keyboard) {
84 | SendMessage message = new SendMessage();
85 | message.setChatId(chatId);
86 | message.setText(text);
87 | message.setReplyToMessageId(replyToMessageId);
88 | message.setDisableWebPagePreview(true);
89 |
90 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
91 |
92 | inlineKeyboardMarkup.setKeyboard(keyboard);
93 | message.setReplyMarkup(inlineKeyboardMarkup);
94 |
95 | try {
96 | return Bot.execute(message);
97 | } catch (TelegramApiException e) {
98 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
99 | }
100 | return null;
101 | }
102 |
103 | public static Message sendInlineKeyboard(String chatId, String text, List inlineKeyboardButtonList) {
104 | SendMessage message = new SendMessage();
105 | message.setChatId(chatId);
106 | message.setText(text);
107 |
108 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
109 | List> keyboard = new ArrayList<>();
110 |
111 | for (InlineKeyboardButton button : inlineKeyboardButtonList) {
112 | List list = new ArrayList<>();
113 | list.add(button);
114 | keyboard.add(list);
115 | }
116 |
117 | inlineKeyboardMarkup.setKeyboard(keyboard);
118 | message.setReplyMarkup(inlineKeyboardMarkup);
119 |
120 | try {
121 | return Bot.execute(message);
122 | } catch (TelegramApiException e) {
123 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
124 | }
125 | return null;
126 | }
127 |
128 | public static Message sendCustomKeyboard(String chatId, String text, KeyboardRow row) {
129 | List list = new ArrayList<>();
130 | list.add(row);
131 |
132 | return sendCustomKeyboard(chatId, text, list);
133 | }
134 |
135 | public static Message sendCustomKeyboard(String chatId, String text, List keyboard) {
136 | SendMessage message = new SendMessage();
137 | message.setChatId(chatId);
138 | message.setText(text);
139 |
140 | ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();
141 |
142 | keyboardMarkup.setKeyboard(keyboard);
143 | message.setReplyMarkup(keyboardMarkup);
144 |
145 | try {
146 | return Bot.execute(message);
147 | } catch (TelegramApiException e) {
148 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
149 | }
150 | return null;
151 | }
152 |
153 | public static MessageResponse sendMsg(String chatId, String text, boolean webPagePreview) {
154 | MessageResponse response = new MessageResponse();
155 | response.setOk(false);
156 |
157 | SendMessage sendMessage = new SendMessage();
158 | sendMessage.setChatId(chatId);
159 | sendMessage.setText(text);
160 | sendMessage.setParseMode(ParseMode.HTML);
161 | if (!webPagePreview) {
162 | sendMessage.disableWebPagePreview();
163 | }
164 | try {
165 | Message execute = Bot.execute(sendMessage);
166 | response.setOk(true);
167 | response.setMessage(execute);
168 | return response;
169 | } catch (Exception e) {
170 | String message = e.getMessage();
171 | if (message.contains("bot was blocked by the user")) {
172 | response.setMessageError(MessageError.BotWasBlockedByTheUser);
173 | } else {
174 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage)));
175 | }
176 | }
177 |
178 | return response;
179 | }
180 |
181 | public static Message sendMessage(String chatId, String text, boolean webPagePreview) {
182 | return sendMessage(chatId, null, text, webPagePreview, true, null);
183 | }
184 | public static Message sendMessage(String chatId, String text, boolean webPagePreview, boolean notification) {
185 | return sendMessage(chatId, null, text, webPagePreview, notification, null);
186 | }
187 | public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview) {
188 | return sendMessage(chatId, replyToMessageId, text, webPagePreview, true, null);
189 | }
190 | public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview, boolean notification, List> buttons) {
191 | SendMessage sendMessage = new SendMessage();
192 | sendMessage.setChatId(chatId);
193 | sendMessage.setReplyToMessageId(replyToMessageId);
194 | sendMessage.setText(text);
195 | sendMessage.setParseMode(ParseMode.HTML);
196 | if (!notification) {
197 | sendMessage.disableNotification();
198 | }
199 | if (!webPagePreview) {
200 | sendMessage.disableWebPagePreview();
201 | }
202 | if (null != buttons && !buttons.isEmpty()) {
203 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
204 | inlineKeyboardMarkup.setKeyboard(buttons);
205 | sendMessage.setReplyMarkup(inlineKeyboardMarkup);
206 | }
207 | return sendMessage(sendMessage);
208 | }
209 |
210 | public static Message sendMessage(String chatId, String text, boolean webPagePreview, List> buttons) {
211 | return sendMessage(chatId, null, text, webPagePreview, true, null);
212 | }
213 |
214 | public static Message sendMessage(SendMessage sendMessage) {
215 | try {
216 | String text = sendMessage.getText();
217 | if (StringUtils.isNotBlank(text)) {
218 | text = StringUtils.replace(text, "<", "<");
219 | text = StringUtils.replace(text, ">", ">");
220 | sendMessage.setText(text);
221 | }
222 |
223 | Message execute = Bot.execute(sendMessage);
224 | return execute;
225 | } catch (Exception e) {
226 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage)));
227 | }
228 | return null;
229 | }
230 |
231 | public static boolean editMessage(Message message, String text) {
232 | try {
233 | EditMessageText editMessageText = new EditMessageText();
234 | editMessageText.setChatId(message.getChatId());
235 | editMessageText.setMessageId(message.getMessageId());
236 | editMessageText.setText(text);
237 |
238 | Bot.execute(editMessageText);
239 | return true;
240 | } catch (Exception e) {
241 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(message)));
242 | }
243 | return false;
244 | }
245 |
246 | public static boolean editMessage(Message message, String text, List> buttons) {
247 | try {
248 | EditMessageText editMessageText = new EditMessageText();
249 | editMessageText.setChatId(message.getChatId());
250 | editMessageText.setMessageId(message.getMessageId());
251 | editMessageText.setText(text);
252 |
253 | if (null != buttons && !buttons.isEmpty()) {
254 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
255 | inlineKeyboardMarkup.setKeyboard(buttons);
256 | editMessageText.setReplyMarkup(inlineKeyboardMarkup);
257 | }
258 |
259 | Bot.execute(editMessageText);
260 | return true;
261 | } catch (Exception e) {
262 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(message)));
263 | }
264 | return false;
265 | }
266 |
267 | public static boolean deleteMessage(Message message) {
268 | if (null == message) {
269 | return false;
270 | }
271 |
272 | DeleteMessage deleteMessage = new DeleteMessage();
273 | deleteMessage.setChatId(message.getChatId());
274 | deleteMessage.setMessageId(message.getMessageId());
275 |
276 | try {
277 | Boolean execute = Bot.execute(deleteMessage);
278 | return null == execute ? false : execute;
279 | } catch (Exception e) {
280 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage)));
281 | }
282 | return false;
283 | }
284 |
285 | public static boolean deleteMessage(DeleteMessage deleteMessage) {
286 | try {
287 | Boolean execute = Bot.execute(deleteMessage);
288 | return null == execute ? false : execute;
289 | } catch (Exception e) {
290 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage)));
291 | }
292 | return false;
293 | }
294 |
295 | }
296 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepErrorApi.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | public interface StepErrorApi {
4 |
5 | void callback(Exception e, StepsChatSession stepsChatSession);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepExecuteResult.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | @Data
7 | @AllArgsConstructor
8 | public class StepExecuteResult {
9 |
10 | private boolean init;
11 | private StepResult stepResult;
12 | private boolean isWork;
13 |
14 | public static StepExecuteResult not() {
15 | return new StepExecuteResult(false, null, false);
16 | }
17 |
18 | public static StepExecuteResult work() {
19 | return new StepExecuteResult(true, null, true);
20 | }
21 |
22 | public static StepExecuteResult ok(StepResult stepResult) {
23 | return new StepExecuteResult(true, stepResult, true);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepHandleApi.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | public interface StepHandleApi {
7 |
8 | StepResult execute(StepsChatSession stepsChatSession, int index, List list, Map context);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepResult.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | @Data
7 | @AllArgsConstructor
8 | public class StepResult {
9 |
10 | private boolean ok;
11 |
12 | private boolean next;
13 | private String text;
14 |
15 | private boolean end;
16 |
17 | public static StepResult ok() {
18 | return new StepResult(true, false, null, false);
19 | }
20 |
21 | public static StepResult reject() {
22 | return new StepResult(false, false, null, false);
23 | }
24 |
25 | public static StepResult next() {
26 | return new StepResult(true, true, null, false);
27 | }
28 |
29 | public static StepResult next(String text) {
30 | return new StepResult(true, true, text, false);
31 | }
32 |
33 | public static StepResult end() {
34 | return new StepResult(true, false, null, true);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepsBuilder.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import code.handler.Command;
4 |
5 | public class StepsBuilder {
6 |
7 | private Command[] commands;
8 | private boolean debug = true;
9 | private StepErrorApi errorApi;
10 | private StepHandleApi initStep;
11 | private StepHandleApi[] steps;
12 |
13 | private StepsBuilder() {}
14 |
15 | public static StepsBuilder create() {
16 | return new StepsBuilder();
17 | }
18 |
19 | public StepsBuilder bindCommand(Command... commands) {
20 | this.commands = commands;
21 | return this;
22 | }
23 | public StepsBuilder debug(boolean debug) {
24 | this.debug = debug;
25 | return this;
26 | }
27 | public StepsBuilder error(StepErrorApi errorApi) {
28 | this.errorApi = errorApi;
29 | return this;
30 | }
31 | public StepsBuilder init(StepHandleApi initStep) {
32 | this.initStep = initStep;
33 | return this;
34 | }
35 | public StepsBuilder steps(StepHandleApi... steps) {
36 | this.steps = steps;
37 | return this;
38 | }
39 |
40 | public StepsHandler build() {
41 | return StepsHandler.build(debug, errorApi, initStep, steps).bindCommand(commands);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepsChatSession.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import lombok.Data;
4 | import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
5 | import org.telegram.telegrambots.meta.api.objects.Message;
6 | import org.telegram.telegrambots.meta.api.objects.Voice;
7 |
8 | @Data
9 | public class StepsChatSession {
10 |
11 | private String sessionId;
12 | private String chatId;
13 | private String fromId;
14 |
15 | private Integer replyToMessageId;
16 |
17 | private Message message;
18 | private CallbackQuery callbackQuery;
19 | private String text;
20 | private Voice voice;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepsChatSessionBuilder.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
4 | import org.telegram.telegrambots.meta.api.objects.Message;
5 |
6 | public class StepsChatSessionBuilder {
7 | private StepsChatSession session;
8 |
9 | private StepsChatSessionBuilder(StepsChatSession session) {
10 | this.session = session;
11 | }
12 |
13 | public static StepsChatSessionBuilder clone(StepsChatSession session) {
14 | return create(session.getMessage());
15 | }
16 | public static StepsChatSessionBuilder create(CallbackQuery callbackQuery) {
17 | String chatId = String.valueOf(callbackQuery.getMessage().getChat().getId());
18 | String fromId = String.valueOf(callbackQuery.getFrom().getId());
19 |
20 | StepsChatSession session = new StepsChatSession();
21 | session.setChatId(chatId);
22 | session.setFromId(fromId);
23 | session.setSessionId(chatId + "_" + fromId);
24 | session.setCallbackQuery(callbackQuery);
25 | return new StepsChatSessionBuilder(session);
26 | }
27 | public static StepsChatSessionBuilder create(Message message) {
28 | String chatId = message.getChat().getId().toString();
29 | String fromId = String.valueOf(message.getFrom().getId());
30 | String text = message.getText();
31 |
32 | StepsChatSession session = new StepsChatSession();
33 | session.setChatId(chatId);
34 | session.setFromId(fromId);
35 | session.setSessionId(chatId + "_" + fromId);
36 | session.setText(text);
37 | session.setReplyToMessageId(message.getMessageId());
38 | session.setMessage(message);
39 | session.setVoice(message.getVoice());
40 | return new StepsChatSessionBuilder(session);
41 | }
42 | public StepsChatSessionBuilder setText(String text) {
43 | session.setText(text);
44 | return this;
45 | }
46 |
47 | public StepsChatSessionBuilder setText(String[] arguments) {
48 | session.setText(String.join(" ", arguments));
49 | return this;
50 | }
51 |
52 | public StepsChatSession build() {
53 | return session;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepsHandler.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import code.handler.Command;
4 | import com.alibaba.fastjson2.JSON;
5 | import lombok.Getter;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.commons.lang3.StringUtils;
8 |
9 | import java.util.*;
10 | import java.util.concurrent.ConcurrentHashMap;
11 | import java.util.concurrent.atomic.AtomicInteger;
12 |
13 | @Getter
14 | @Slf4j
15 | public class StepsHandler {
16 |
17 | private static volatile AtomicInteger IdAtomic = new AtomicInteger(1);
18 |
19 | private Command[] commands;
20 |
21 | private boolean debug;
22 | private StepErrorApi errorApi;
23 | private StepHandleApi initStep;
24 | private StepHandleApi[] stepHandleApis;
25 |
26 | private Map> context = new ConcurrentHashMap<>();
27 |
28 | private Map> message = new ConcurrentHashMap<>();
29 |
30 | private Map stepWorkStatus = new ConcurrentHashMap<>();
31 | private Map stepId = new ConcurrentHashMap<>();
32 |
33 | private StepsHandler() {
34 | }
35 |
36 | public StepsHandler bindCommand(Command[] commands) {
37 | this.commands = commands;
38 | for (Command command : commands) {
39 | StepsRegisterCenter.register(command.getCmd(), this);
40 | }
41 | return this;
42 | }
43 |
44 | public static StepsHandler build(boolean debug, StepErrorApi errorApi, StepHandleApi step) {
45 | return build(debug, errorApi, null, step);
46 | }
47 |
48 | public static StepsHandler build(boolean debug, StepErrorApi errorApi, StepHandleApi initStep, StepHandleApi... steps) {
49 | StepsHandler handler = new StepsHandler();
50 |
51 | handler.debug = debug;
52 | handler.errorApi = errorApi;
53 | handler.initStep = initStep;
54 | handler.stepHandleApis = steps;
55 |
56 | return handler;
57 | }
58 |
59 | public boolean hasInit(StepsChatSession stepsChatSession) {
60 | return stepId.containsKey(stepsChatSession.getSessionId());
61 | }
62 | public boolean isInit() {
63 | return null != initStep;
64 | }
65 |
66 | public void init(StepsChatSession stepsChatSession) {
67 | StepResult execute = null;
68 | try {
69 | StepsRegisterCenter.priority(stepsChatSession, this);
70 | String sessionId = stepsChatSession.getSessionId();
71 |
72 | Boolean stepsWorkStatusBool = stepWorkStatus.get(sessionId);
73 | if (null != stepsWorkStatusBool && stepsWorkStatusBool) {
74 | return;
75 | }
76 |
77 | stepWorkStatus.put(sessionId, true);
78 |
79 | List list = Collections.synchronizedList(new ArrayList<>());
80 | ConcurrentHashMap contextMap = new ConcurrentHashMap<>();
81 | if (null != initStep) {
82 | execute = initStep.execute(stepsChatSession, 0, list, contextMap);
83 | }
84 | if ((null != execute && execute.isOk()) || null == initStep) {
85 | context.remove(sessionId);
86 | message.remove(sessionId);
87 | stepId.remove(sessionId);
88 | list.add(stepsChatSession.getText());
89 | message.put(sessionId, list);
90 | context.put(sessionId, contextMap);
91 | stepId.put(sessionId, IdAtomic.incrementAndGet());
92 | if (debug) {
93 | log.info("Steps init, id: {}, chat id: {}, text: {}, list: {}", stepId.get(sessionId), stepsChatSession.getChatId(), stepsChatSession.getText(), JSON.toJSONString(list));
94 | }
95 | }
96 | } catch (Exception e) {
97 | errorApi.callback(e, stepsChatSession);
98 | } finally {
99 | stepWorkStatus.put(stepsChatSession.getSessionId(), false);
100 |
101 | if (null != execute) {
102 | if (execute.isNext()) {
103 | step(stepsChatSession);
104 | }
105 | if (execute.isEnd()) {
106 | exit(stepsChatSession);
107 | }
108 | }
109 | }
110 | }
111 |
112 | public void next(StepsChatSession stepsChatSession) {
113 | step(stepsChatSession);
114 | }
115 |
116 | public StepExecuteResult step(StepsChatSession stepsChatSession) {
117 | StepResult execute = null;
118 | try {
119 | String sessionId = stepsChatSession.getSessionId();
120 | if (!hasInit(stepsChatSession) && !isInit()) {
121 | init(stepsChatSession);
122 | }
123 |
124 | Boolean stepsWorkStatusBool = stepWorkStatus.get(sessionId);
125 | if (null != stepsWorkStatusBool && stepsWorkStatusBool) {
126 | return StepExecuteResult.work();
127 | }
128 | stepWorkStatus.put(sessionId, true);
129 |
130 | if (!message.containsKey(sessionId)) {
131 | return StepExecuteResult.not();
132 | }
133 |
134 | List list = message.get(sessionId);
135 | Map contextMap = context.get(sessionId);
136 | execute = this.stepHandleApis[list.size() - 1].execute(stepsChatSession, list.size(), list, contextMap);
137 | if (execute.isOk()) {
138 | list.add(stepsChatSession.getText());
139 | }
140 | if (debug) {
141 | log.info("Step, id: {}, chat id: {}, text: {}, list: {}, context: {}", stepId.get(sessionId), stepsChatSession.getChatId(), stepsChatSession.getText(), JSON.toJSONString(list), JSON.toJSONString(contextMap));
142 | }
143 | if ((list.size() - 1) >= this.stepHandleApis.length) {
144 | if (debug) {
145 | log.info("Step finish, id: {}, chat id: {}, text: {}, list: {}, context: {}", stepId.get(sessionId), stepsChatSession.getChatId(), stepsChatSession.getText(), JSON.toJSONString(list), JSON.toJSONString(contextMap));
146 | }
147 | exit(stepsChatSession);
148 | }
149 | } catch (Exception e) {
150 | errorApi.callback(e, stepsChatSession);
151 | } finally {
152 | stepWorkStatus.put(stepsChatSession.getSessionId(), false);
153 |
154 | if (null != execute) {
155 | if (execute.isEnd()) {
156 | exit(stepsChatSession);
157 | } else if (execute.isNext()) {
158 | next(
159 | StepsChatSessionBuilder.clone(stepsChatSession).setText(execute.getText()).build()
160 | );
161 | }
162 | }
163 | }
164 | return StepExecuteResult.ok(execute);
165 | }
166 |
167 | public void exit(StepsChatSession stepsChatSession) {
168 | String sessionId = stepsChatSession.getSessionId();
169 |
170 | message.remove(sessionId);
171 | context.remove(sessionId);
172 | stepWorkStatus.remove(sessionId);
173 | stepId.remove(sessionId);
174 | StepsRegisterCenter.finish(stepsChatSession);
175 | }
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/steps/StepsRegisterCenter.java:
--------------------------------------------------------------------------------
1 | package code.handler.steps;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 |
5 | import java.util.*;
6 | import java.util.concurrent.ConcurrentHashMap;
7 |
8 | @Slf4j
9 | public class StepsRegisterCenter {
10 |
11 | private static Map stepsHandlerMap = new HashMap<>();
12 |
13 | private static volatile Map priorityMap = new ConcurrentHashMap<>();
14 |
15 | public static void register(String cmd, StepsHandler handler) {
16 | stepsHandlerMap.put(cmd, handler);
17 | }
18 |
19 | public static StepsHandler getRegister(String cmd) {
20 | return stepsHandlerMap.get(cmd);
21 | }
22 | public static Collection getRegisterList() {
23 | return stepsHandlerMap.values();
24 | }
25 |
26 | public synchronized static void priority(StepsChatSession stepsChatSession, StepsHandler stepsHandler) {
27 | for (StepsHandler value : stepsHandlerMap.values()) {
28 | value.exit(stepsChatSession);
29 | }
30 |
31 | priorityMap.put(stepsChatSession.getSessionId(), stepsHandler);
32 | }
33 | public synchronized static void finish(StepsChatSession stepsChatSession) {
34 | priorityMap.remove(stepsChatSession.getSessionId());
35 | }
36 | public static StepsHandler getPriority(StepsChatSession stepsChatSession) {
37 | return priorityMap.get(stepsChatSession.getSessionId());
38 | }
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/store/ChatButtonsStore.java:
--------------------------------------------------------------------------------
1 | package code.handler.store;
2 |
3 | import lombok.Data;
4 | import org.apache.commons.lang3.StringUtils;
5 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
6 |
7 | import java.util.*;
8 |
9 | public class ChatButtonsStore {
10 |
11 | private volatile static ChatButtonsToInlineKeyboardButtons buttons;
12 |
13 | public static void set(String chatButtons) {
14 | Optional keyboardButtons = chatButtonsToInlineKeyboardButtons(chatButtons);
15 | buttons = keyboardButtons.orElse(null);
16 | }
17 |
18 | public static Optional verify(String chatButtons) {
19 | return chatButtonsToInlineKeyboardButtons(chatButtons);
20 | }
21 |
22 | public static Optional get() {
23 | return Optional.ofNullable(buttons);
24 | }
25 |
26 | @Data
27 | public static class ChatButtonsToInlineKeyboardButtons {
28 | private boolean isAll;
29 |
30 | private Map> map;
31 |
32 | public Optional> getButtons(String chatId) {
33 | if (isAll) {
34 | return Optional.ofNullable(map.get("all"));
35 | }
36 | for (Map.Entry> entry : map.entrySet()) {
37 | if (entry.getKey().equals(chatId)) {
38 | return Optional.ofNullable(entry.getValue());
39 | }
40 | }
41 | return Optional.empty();
42 | }
43 | }
44 | private static Optional chatButtonsToInlineKeyboardButtons(String chatButtons) {
45 | if (StringUtils.isBlank(chatButtons)) {
46 | return Optional.empty();
47 | }
48 |
49 | String[] split = chatButtons.split("---");
50 | if (split.length == 0) {
51 | return Optional.empty();
52 | }
53 | ChatButtonsToInlineKeyboardButtons chatButtonsToInlineKeyboardButtons = new ChatButtonsToInlineKeyboardButtons();
54 | Map> map = new LinkedHashMap<>();
55 | for (String s : split) {
56 | s = StringUtils.removeStart(s, "\n");
57 | List buttons = new ArrayList<>();
58 | String[] dataSplit = s.split("\n");
59 | if (dataSplit.length < 2) {
60 | return Optional.empty();
61 | }
62 | for (int i = 1; i < dataSplit.length; i++) {
63 | String data = dataSplit[i];
64 | if (StringUtils.isBlank(data)) {
65 | return Optional.empty();
66 | }
67 | String[] buttonSplit = data.split(" ");
68 | if (buttonSplit.length != 2) {
69 | return Optional.empty();
70 | }
71 | String url = buttonSplit[1];
72 | if (!StringUtils.startsWith(url, "http")) {
73 | return Optional.empty();
74 | }
75 |
76 | InlineKeyboardButton button = new InlineKeyboardButton();
77 | button.setText(buttonSplit[0]);
78 | button.setUrl(url);
79 | buttons.add(button);
80 | }
81 |
82 | String chatId = dataSplit[0];
83 | chatButtonsToInlineKeyboardButtons.setAll(chatId.equals("all"));
84 | map.put(chatId, buttons);
85 | if (chatButtonsToInlineKeyboardButtons.isAll()) {
86 | break;
87 | }
88 | }
89 | if (chatButtonsToInlineKeyboardButtons.isAll()) {
90 | List deleteKeys = new ArrayList<>();
91 | for (Map.Entry> entry : map.entrySet()) {
92 | if (!entry.getKey().equals("all")) {
93 | deleteKeys.add(entry.getKey());
94 | }
95 | }
96 | for (String key : deleteKeys) {
97 | map.remove(key);
98 | }
99 | }
100 | chatButtonsToInlineKeyboardButtons.setMap(map);
101 |
102 | return Optional.of(chatButtonsToInlineKeyboardButtons);
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/store/GptTokenStore.java:
--------------------------------------------------------------------------------
1 | package code.handler.store;
2 |
3 | import code.config.I18nEnum;
4 | import code.eneity.GptTokenTableEntity;
5 | import code.eneity.cons.GptTokenStatusEnum;
6 | import code.handler.I18nHandle;
7 | import code.handler.message.MessageHandle;
8 | import code.util.gpt.response.GPTChatResponse;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.telegram.telegrambots.meta.api.objects.Message;
11 |
12 | import java.util.List;
13 |
14 | import static code.Main.GlobalConfig;
15 | import static code.Main.GptTokenTableRepository;
16 |
17 | public class GptTokenStore {
18 | public static String getRandomToken() {
19 | GptTokenTableEntity entity = GptTokenTableRepository.selectOneByRand(GptTokenStatusEnum.Alive);
20 | return null == entity ? GlobalConfig.getGptToken() : entity.getToken();
21 | }
22 | public static String getListText(String chatId) {
23 | StringBuilder builder = new StringBuilder();
24 | List gptTokenTableEntities = GptTokenTableRepository.selectListByStatus(GptTokenStatusEnum.Alive);
25 | builder.append(I18nHandle.getText(chatId, I18nEnum.Alive) + ": " + gptTokenTableEntities.size());
26 | builder.append("\n");
27 | for (GptTokenTableEntity entity : gptTokenTableEntities) {
28 | builder.append(entity.getToken());
29 | builder.append("\n");
30 | }
31 | builder.append("\n");
32 | List gptTokenTableEntities2 = GptTokenTableRepository.selectListByStatus(GptTokenStatusEnum.Die);
33 | builder.append(I18nHandle.getText(chatId, I18nEnum.Die) + ": " + gptTokenTableEntities2.size());
34 | builder.append("\n");
35 | for (GptTokenTableEntity entity : gptTokenTableEntities2) {
36 | builder.append(entity.getToken());
37 | builder.append("\n");
38 | }
39 |
40 | return builder.toString();
41 | }
42 | public static void forceSave(List tokens) {
43 | GptTokenTableRepository.forceSave(tokens);
44 | }
45 | public static void deleteAll() {
46 | GptTokenTableRepository.deleteAll();
47 | }
48 | public static void handle(String token, GPTChatResponse response) {
49 | if (StringUtils.isBlank(token)) {
50 | return;
51 | }
52 | if (null == response || response.isOk()) {
53 | return;
54 | }
55 | if (response.getStatusCode() == 429) {
56 | if (response.getResponse().contains("You exceeded your current quota, please check your plan and billing details")) {
57 | send(token);
58 | }
59 | } else if (response.getStatusCode() == 401) {
60 | send(token);
61 | }
62 | }
63 | private static void send(String token) {
64 | String text = token + " " + I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.Tip429);
65 | Message message = MessageHandle.sendMessage(GlobalConfig.getBotAdminId(), text, false);
66 | if (null != message) {
67 | GptTokenTableRepository.dieAndSend(token);
68 | }
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/code/handler/store/Store.java:
--------------------------------------------------------------------------------
1 | package code.handler.store;
2 |
3 | import static code.Main.GlobalConfig;
4 |
5 | public class Store {
6 |
7 | public static void init() {
8 | ChatButtonsStore.set(GlobalConfig.getChatButtons());
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/GptTokenTableRepository.java:
--------------------------------------------------------------------------------
1 | package code.repository;
2 |
3 | import code.config.Config;
4 | import code.eneity.GptTokenTableEntity;
5 | import code.eneity.cons.GptTokenStatusEnum;
6 | import code.eneity.cons.YesOrNoEnum;
7 | import code.repository.mapper.TableRepository;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.apache.commons.lang3.BooleanUtils;
10 |
11 | import java.util.List;
12 |
13 | @Slf4j
14 | public class GptTokenTableRepository extends TableRepository {
15 |
16 | public GptTokenTableRepository() {
17 | super(Config.DBPath);
18 | }
19 |
20 | public void deleteAll() {
21 | super.deleteAll(GptTokenTableEntity.class);
22 | }
23 | public synchronized void forceSave(List tokens) {
24 | super.deleteAll(GptTokenTableEntity.class);
25 | for (String token : tokens) {
26 | GptTokenTableEntity entity = new GptTokenTableEntity();
27 | entity.setToken(token);
28 | entity.setStatus(GptTokenStatusEnum.Alive.getNum());
29 | entity.setSend(YesOrNoEnum.No.getNum());
30 | super.insert(entity);
31 | }
32 | }
33 |
34 | public List selectListByStatus(GptTokenStatusEnum gptTokenStatusEnum) {
35 | GptTokenTableEntity where = new GptTokenTableEntity();
36 | where.setStatus(gptTokenStatusEnum.getNum());
37 | return super.selectList(where);
38 | }
39 |
40 | public GptTokenTableEntity selectOneByRand(GptTokenStatusEnum gptTokenStatusEnum) {
41 | GptTokenTableEntity where = new GptTokenTableEntity();
42 | where.setStatus(gptTokenStatusEnum.getNum());
43 | return super.selectOneByRand(where);
44 | }
45 |
46 | public boolean die(String gptToken) {
47 | GptTokenTableEntity where = new GptTokenTableEntity();
48 | where.setToken(gptToken);
49 |
50 | GptTokenTableEntity entity = new GptTokenTableEntity();
51 | entity.setStatus(GptTokenStatusEnum.Die.getNum());
52 |
53 | return BooleanUtils.toBooleanDefaultIfNull(super.update(entity, where), false);
54 | }
55 | public boolean dieAndSend(String gptToken) {
56 | GptTokenTableEntity where = new GptTokenTableEntity();
57 | where.setToken(gptToken);
58 |
59 | GptTokenTableEntity entity = new GptTokenTableEntity();
60 | entity.setStatus(GptTokenStatusEnum.Die.getNum());
61 | entity.setSend(YesOrNoEnum.Yes.getNum());
62 |
63 | return BooleanUtils.toBooleanDefaultIfNull(super.update(entity, where), false);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/I18nTableRepository.java:
--------------------------------------------------------------------------------
1 | package code.repository;
2 |
3 | import code.config.Config;
4 | import code.config.TableEnum;
5 | import code.repository.mapper.TableRepository;
6 | import code.util.ExceptionUtil;
7 | import lombok.extern.slf4j.Slf4j;
8 |
9 | import java.sql.ResultSet;
10 |
11 | @Slf4j
12 | public class I18nTableRepository extends TableRepository {
13 |
14 | public I18nTableRepository() {
15 | super(Config.DBPath, TableEnum.I18nTable.getName());
16 | }
17 |
18 | @Override
19 | public String getCreateTableSql() {
20 | return String.format("create table if not exists %s (chat_id varchar(88), i18n_alias varchar(20))", super.getTableName());
21 | }
22 |
23 | public String selectI18nAlias(String chatId) {
24 | try {
25 | String i18nAlias = (String) execute((statement) -> {
26 | String sql = String.format("select i18n_alias from %s where chat_id = '%s'", super.getTableName(), chatId);
27 | ResultSet query = statement.executeQuery(sql);
28 | return query.getString("i18n_alias");
29 | });
30 | return i18nAlias;
31 | } catch (Exception e) {
32 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
33 | }
34 | return null;
35 | }
36 |
37 | public synchronized boolean save(String chatId, String i18nAlias) {
38 | String sql = String.format("insert into %s values('%s', '%s')", super.getTableName(), chatId, i18nAlias);
39 | try {
40 | execute((statement) -> {
41 | statement.executeUpdate(String.format("delete from %s where chat_id = '%s'", super.getTableName(), chatId));
42 | statement.executeUpdate(sql);
43 | return null;
44 | });
45 | return true;
46 | } catch (Exception e) {
47 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
48 | return false;
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/RecordTableRepository.java:
--------------------------------------------------------------------------------
1 | package code.repository;
2 |
3 | import code.config.Config;
4 | import code.eneity.RecordTableEntity;
5 | import code.repository.mapper.TableRepository;
6 | import lombok.extern.slf4j.Slf4j;
7 |
8 | import java.util.List;
9 |
10 | @Slf4j
11 | public class RecordTableRepository extends TableRepository {
12 |
13 | public RecordTableRepository() {
14 | super(Config.DBPath);
15 | }
16 |
17 | public List selectListByChatId(String chatId) {
18 | RecordTableEntity where = new RecordTableEntity();
19 | where.setChatId(chatId);
20 | return super.selectList(where);
21 | }
22 |
23 | public RecordTableEntity selectOne(String id, String chatId) {
24 | RecordTableEntity where = new RecordTableEntity();
25 | where.setId(id);
26 | where.setChatId(chatId);
27 | return super.selectOne(where);
28 | }
29 |
30 | public RecordTableEntity selectOneByAlias(String alias, String chatId) {
31 | RecordTableEntity where = new RecordTableEntity();
32 | where.setRecordAlias(alias);
33 | where.setChatId(chatId);
34 | return super.selectOne(where);
35 | }
36 |
37 | public Boolean delete(String id, String chatId) {
38 | RecordTableEntity where = new RecordTableEntity();
39 | where.setId(id);
40 | where.setChatId(chatId);
41 | return super.delete(where);
42 | }
43 |
44 | public Integer selectCount(String chatId, String alias) {
45 | RecordTableEntity where = new RecordTableEntity();
46 | where.setChatId(chatId);
47 | where.setRecordAlias(alias);
48 | return super.selectCount(where);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/mapper/SqlBuilder.java:
--------------------------------------------------------------------------------
1 | package code.repository.mapper;
2 |
3 | import code.util.ExceptionUtil;
4 | import lombok.extern.slf4j.Slf4j;
5 |
6 | import java.lang.reflect.Field;
7 | import java.util.StringJoiner;
8 |
9 | @Slf4j
10 | public class SqlBuilder {
11 |
12 | public static String getTableName(TableEntity tableEntity) {
13 | return getTableName(tableEntity.getClass());
14 | }
15 | public static String getTableName(Class extends TableEntity> tableClass) {
16 | return tableClass.getAnnotation(TableName.class).name();
17 | }
18 |
19 | public static String buildCreateTableSql(Class extends TableEntity> tableClass) {
20 | String tableName = getTableName(tableClass);
21 | StringJoiner joiner = new StringJoiner(", ", "(", ")");
22 |
23 | for (Field field : tableClass.getDeclaredFields()) {
24 | TableField tableField = field.getAnnotation(TableField.class);
25 | if (null == tableField) {
26 | continue;
27 | }
28 | joiner.add(tableField.sql());
29 | }
30 |
31 | String sql = "create table if not exists " + tableName + " " + joiner;
32 | return sql;
33 | }
34 |
35 | private static String buildFieldValueSql(TableEntity tableEntity, String delimiter, String prefix, String suffix) {
36 | Class extends TableEntity> entityClass = tableEntity.getClass();
37 | StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix);
38 | for (Field field : entityClass.getDeclaredFields()) {
39 | try {
40 | TableField tableField = field.getAnnotation(TableField.class);
41 | if (null == tableField) {
42 | continue;
43 | }
44 | field.setAccessible(true);
45 | Object o = field.get(tableEntity);
46 | if (null == o) {
47 | continue;
48 | }
49 |
50 | if (o instanceof String) {
51 | joiner.add(tableField.name() + "=" + "'" + o + "'");
52 | } else {
53 | joiner.add(tableField.name() + "=" + o);
54 | }
55 | } catch (IllegalAccessException e) {
56 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
57 | }
58 | }
59 | return joiner.toString();
60 | }
61 |
62 | public static String buildFieldSql(Class extends TableEntity> tableClass, String prefix, String suffix) {
63 | StringJoiner joiner = new StringJoiner(", ", prefix, suffix);
64 |
65 | for (Field field : tableClass.getDeclaredFields()) {
66 | TableField tableField = field.getAnnotation(TableField.class);
67 | if (null == tableField) {
68 | continue;
69 | }
70 | joiner.add(tableField.name());
71 | }
72 | return joiner.toString();
73 | }
74 | public static String buildFieldSql(TableEntity tableEntity, String prefix, String suffix) {
75 | StringJoiner joiner = new StringJoiner(", ", prefix, suffix);
76 |
77 | Class extends TableEntity> entityClass = tableEntity.getClass();
78 | for (Field field : entityClass.getDeclaredFields()) {
79 | TableField tableField = field.getAnnotation(TableField.class);
80 | if (null == tableField) {
81 | continue;
82 | }
83 | joiner.add(tableField.name());
84 | }
85 | return joiner.toString();
86 | }
87 |
88 | public static String buildWhereSql(TableEntity tableEntity) {
89 | return buildFieldValueSql(tableEntity, " and ", " where ", "");
90 | }
91 |
92 | public static String buildSelectSql(Class extends TableEntity> tableClass) {
93 | String tableName = getTableName(tableClass);
94 |
95 | String sql = "select" + buildFieldSql(tableClass, " ", " ") + "from " + tableName;
96 | return sql;
97 | }
98 | public static String buildSelectSql(TableEntity tableEntity) {
99 | String tableName = getTableName(tableEntity);
100 |
101 | String sql = "select" + buildFieldSql(tableEntity, " ", " ") + "from " + tableName + buildWhereSql(tableEntity);
102 | return sql;
103 | }
104 | public static String buildSelectOneRandSql(TableEntity tableEntity) {
105 | String tableName = getTableName(tableEntity);
106 |
107 | String sql = "select" + buildFieldSql(tableEntity, " ", " ") + "from " + tableName + buildWhereSql(tableEntity);
108 | sql = sql + " order by random() limit 1";
109 | return sql;
110 | }
111 |
112 | public static String buildDeleteAllSql(TableEntity entity) {
113 | String tableName = getTableName(entity);
114 |
115 | String sql = "delete from " + tableName;
116 | return sql;
117 | }
118 | public static String buildDeleteSql(TableEntity where) {
119 | String tableName = getTableName(where);
120 |
121 | String sql = "delete from " + tableName + buildWhereSql(where);
122 | return sql;
123 | }
124 |
125 | public static String buildSelectCountSql(Class extends TableEntity> tableClass) {
126 | String tableName = getTableName(tableClass);
127 |
128 | String sql = "select count(*) as total from " + tableName;
129 | return sql;
130 | }
131 |
132 | public static String buildSelectCountSql(TableEntity where) {
133 | String tableName = getTableName(where);
134 |
135 | String sql = "select count(*) as total from " + tableName + buildWhereSql(where);
136 | return sql;
137 | }
138 |
139 | public static String buildInsertSql(TableEntity tableEntity, boolean isForceInsertNullValue) {
140 | String tableName = getTableName(tableEntity);
141 |
142 | Class extends TableEntity> entityClass = tableEntity.getClass();
143 | StringJoiner prefixJoiner = new StringJoiner(", ", "(", ")");
144 | StringJoiner suffixJoiner = new StringJoiner(", ", "values (", ")");
145 | for (Field field : entityClass.getDeclaredFields()) {
146 | try {
147 | TableField tableField = field.getAnnotation(TableField.class);
148 | if (null == tableField) {
149 | continue;
150 | }
151 | field.setAccessible(true);
152 | Object o = field.get(tableEntity);
153 | if (null == o && !isForceInsertNullValue) {
154 | continue;
155 | }
156 | prefixJoiner.add(tableField.name());
157 | if (null == o) {
158 | suffixJoiner.add(null);
159 | }
160 | else if (o instanceof String) {
161 | suffixJoiner.add("'" + o + "'");
162 | } else {
163 | suffixJoiner.add(String.valueOf(o));
164 | }
165 | } catch (IllegalAccessException e) {
166 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
167 | }
168 | }
169 |
170 | String sql = "insert into " + tableName + " " + prefixJoiner + " " + suffixJoiner;
171 | return sql;
172 | }
173 |
174 | public static String buildUpdateSql(TableEntity tableEntity, TableEntity where) {
175 | String tableName = getTableName(tableEntity);
176 |
177 | String sql = "update " + tableName + buildFieldValueSql(tableEntity, ", ", " set ", "") + buildWhereSql(where);
178 | return sql;
179 | }
180 |
181 | }
182 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/mapper/TableEntity.java:
--------------------------------------------------------------------------------
1 | package code.repository.mapper;
2 |
3 | public interface TableEntity {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/mapper/TableField.java:
--------------------------------------------------------------------------------
1 | package code.repository.mapper;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target(value = ElementType.FIELD)
9 | @Retention(value = RetentionPolicy.RUNTIME)
10 | public @interface TableField {
11 |
12 | String sql();
13 |
14 | String name();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/mapper/TableName.java:
--------------------------------------------------------------------------------
1 | package code.repository.mapper;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target(ElementType.TYPE)
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface TableName {
11 |
12 | String name();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/code/repository/mapper/TableRepository.java:
--------------------------------------------------------------------------------
1 | package code.repository.mapper;
2 |
3 | import code.util.ExceptionUtil;
4 | import code.util.SqliteUtil;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.apache.commons.lang3.BooleanUtils;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.sqlite.jdbc4.JDBC4ResultSet;
9 |
10 | import java.lang.reflect.Field;
11 | import java.lang.reflect.ParameterizedType;
12 | import java.lang.reflect.Type;
13 | import java.sql.ResultSet;
14 | import java.sql.SQLException;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | @Slf4j
19 | public abstract class TableRepository {
20 |
21 | private String tableName;
22 | private String dbPath;
23 |
24 | public TableRepository(String dbPath, String tableName) {
25 | this.tableName = tableName;
26 | this.dbPath = dbPath;
27 | try {
28 | SqliteUtil.execute(dbPath, (statement) -> {
29 | String sql = getCreateTableSql();
30 | if (StringUtils.isNotBlank(sql)) {
31 | statement.execute(sql);
32 | }
33 | return null;
34 | });
35 | } catch (Exception e) {
36 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
37 | }
38 | }
39 |
40 | public TableRepository(String dbPath) {
41 | this.tableName = getT().getAnnotation(TableName.class).name();
42 | this.dbPath = dbPath;
43 | try {
44 | SqliteUtil.execute(dbPath, (statement) -> {
45 | String sql = SqlBuilder.buildCreateTableSql(getT());
46 | if (StringUtils.isNotBlank(sql)) {
47 | statement.execute(sql);
48 | }
49 | return null;
50 | });
51 | } catch (Exception e) {
52 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
53 | }
54 | }
55 |
56 | private Class getT() {
57 | ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
58 | Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
59 | String typeName = actualTypeArgument.getTypeName();
60 | try {
61 | return (Class) Class.forName(typeName);
62 | } catch (ClassNotFoundException e) {
63 | throw new RuntimeException(e);
64 | }
65 | }
66 |
67 | public String getTableName() {
68 | return this.tableName;
69 | }
70 | public String sql(String sql) {
71 | return sql(sql, null);
72 | }
73 | public String sql(String sql, String[] args) {
74 | String table = StringUtils.replace(sql, "$table", this.getTableName());
75 | return String.format(table, args);
76 | }
77 |
78 | public Object execute(SqliteUtil.SqliteInterface sqliteInterface) throws Exception {
79 | return SqliteUtil.execute(this.dbPath, sqliteInterface);
80 | }
81 | public Object executeWithTryCatch(SqliteUtil.SqliteInterface sqliteInterface) {
82 | try {
83 | return SqliteUtil.execute(this.dbPath, sqliteInterface);
84 | } catch (Exception e) {
85 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
86 | }
87 | return null;
88 | }
89 |
90 | public boolean delete(T where) {
91 | try {
92 | return (boolean) execute((statement) -> {
93 | int update = statement.executeUpdate(SqlBuilder.buildDeleteSql(where));
94 | return update > 0;
95 | });
96 | } catch (Exception e) {
97 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
98 | }
99 | return false;
100 | }
101 | public boolean deleteAll(Class tClass) {
102 | try {
103 | return (boolean) execute((statement) -> {
104 | int update = statement.executeUpdate(SqlBuilder.buildDeleteAllSql(tClass.newInstance()));
105 | return update > 0;
106 | });
107 | } catch (Exception e) {
108 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
109 | }
110 | return false;
111 | }
112 |
113 | public boolean update(T entity, T where) {
114 | try {
115 | return (boolean) execute((statement) -> {
116 | int update = statement.executeUpdate(SqlBuilder.buildUpdateSql(entity, where));
117 | return update > 0;
118 | });
119 | } catch (Exception e) {
120 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
121 | }
122 | return false;
123 | }
124 |
125 | public boolean insert(T entity) {
126 | try {
127 | return (boolean) execute((statement) -> {
128 | int update = statement.executeUpdate(SqlBuilder.buildInsertSql(entity, false));
129 | return update > 0;
130 | });
131 | } catch (Exception e) {
132 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
133 | }
134 | return false;
135 | }
136 |
137 | public T getOne(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException {
138 | JDBC4ResultSet jdbc4ResultSet = (JDBC4ResultSet) resultSet;
139 | if (jdbc4ResultSet.emptyResultSet) {
140 | return null;
141 | }
142 |
143 | Class t = getT();
144 | T instance = t.newInstance();
145 | for (Field field : t.getDeclaredFields()) {
146 | TableField tableField = field.getAnnotation(TableField.class);
147 | if (null == tableField) {
148 | continue;
149 | }
150 | field.setAccessible(true);
151 | field.set(instance, resultSet.getObject(tableField.name()));
152 | }
153 | return instance;
154 | }
155 |
156 | public List getList(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException {
157 | List list = new ArrayList<>();
158 |
159 | JDBC4ResultSet jdbc4ResultSet = (JDBC4ResultSet) resultSet;
160 | if (jdbc4ResultSet.emptyResultSet) {
161 | return list;
162 | }
163 |
164 | Class t = getT();
165 | while (resultSet.next()) {
166 | T instance = t.newInstance();
167 | for (Field field : t.getDeclaredFields()) {
168 | TableField tableField = field.getAnnotation(TableField.class);
169 | if (null == tableField) {
170 | continue;
171 | }
172 | field.setAccessible(true);
173 | field.set(instance, resultSet.getObject(tableField.name()));
174 | }
175 | list.add(instance);
176 | }
177 | return list;
178 | }
179 |
180 | public T selectOne(T where) {
181 | try {
182 | return (T) execute((statement) -> {
183 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where));
184 |
185 | return getOne(resultSet);
186 | });
187 | } catch (Exception e) {
188 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
189 | }
190 | return null;
191 | }
192 | public T selectOneByRand(T where) {
193 | try {
194 | return (T) execute((statement) -> {
195 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectOneRandSql(where));
196 |
197 | return getOne(resultSet);
198 | });
199 | } catch (Exception e) {
200 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
201 | }
202 | return null;
203 | }
204 |
205 | public List selectList() {
206 | try {
207 | return (List) execute((statement) -> {
208 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(getT()));
209 |
210 | return getList(resultSet);
211 | });
212 | } catch (Exception e) {
213 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
214 | }
215 | return new ArrayList<>();
216 | }
217 |
218 | public List selectList(T where) {
219 | try {
220 | return (List) execute((statement) -> {
221 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where));
222 |
223 | return getList(resultSet);
224 | });
225 | } catch (Exception e) {
226 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
227 | }
228 | return new ArrayList<>();
229 | }
230 |
231 | public Integer selectCount() {
232 | Integer total = null;
233 | try {
234 | total = (int) execute((statement) -> {
235 | ResultSet query = statement.executeQuery(SqlBuilder.buildSelectCountSql(getT()));
236 | return query.getInt("total");
237 | });
238 | } catch (Exception e) {
239 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
240 | }
241 | return total;
242 | }
243 |
244 | public Integer selectCount(T where) {
245 | Integer total = null;
246 | try {
247 | total = (int) execute((statement) -> {
248 | ResultSet query = statement.executeQuery(SqlBuilder.buildSelectCountSql(where));
249 | return query.getInt("total");
250 | });
251 | } catch (Exception e) {
252 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
253 | }
254 | return total;
255 | }
256 |
257 | public Boolean exist(String field, String value) {
258 | Integer total = null;
259 | try {
260 | total = (int) execute((statement) -> {
261 | String sql = String.format("select count(*) as total from %s where %s = '%s'", this.tableName, field, value);
262 | ResultSet query = statement.executeQuery(sql);
263 | return query.getInt("total");
264 | });
265 | } catch (Exception e) {
266 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
267 | }
268 | if (null == total) {
269 | return null;
270 | }
271 | return total > 0;
272 | }
273 |
274 | public String getCreateTableSql() {
275 | return null;
276 | }
277 |
278 | }
279 |
--------------------------------------------------------------------------------
/src/main/java/code/util/BytesUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import java.math.BigDecimal;
4 |
5 | public class BytesUtil {
6 |
7 | private final static int KB = 1024;
8 | private final static int MB = 1024 * 1024;
9 | private final static int GB = 1024 * 1024 * 1024;
10 |
11 | private static BigDecimal divide(long size, int unit) {
12 | return new BigDecimal(size).divide(new BigDecimal(unit)).setScale(2, BigDecimal.ROUND_DOWN);
13 | }
14 |
15 | public static String toDisplayStr(long size) {
16 | if (size < KB) {
17 | return size + "B";
18 | } else if (size >= KB && size < MB) {
19 | return divide(size, KB) + "KB";
20 | } else if (size >= MB && size < GB) {
21 | return divide(size, MB) + "MB";
22 | } else if (size >= GB) {
23 | return divide(size, GB) + "GB";
24 | }
25 | return null;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/code/util/CompressUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.codehaus.plexus.archiver.tar.TarXZUnArchiver;
5 |
6 | import java.io.*;
7 | import java.util.zip.ZipEntry;
8 | import java.util.zip.ZipInputStream;
9 |
10 | @Slf4j
11 | public class CompressUtil {
12 |
13 | public static void unzip(String fileZip, File destDir) throws IOException {
14 | byte[] buffer = new byte[1024];
15 | ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip));
16 | try {
17 | ZipEntry zipEntry = zis.getNextEntry();
18 | while (zipEntry != null) {
19 | File newFile = newFile(destDir, zipEntry);
20 | if (zipEntry.isDirectory()) {
21 | if (!newFile.isDirectory() && !newFile.mkdirs()) {
22 | throw new IOException("Failed to create directory " + newFile);
23 | }
24 | } else {
25 | // fix for Windows-created archives
26 | File parent = newFile.getParentFile();
27 | if (!parent.isDirectory() && !parent.mkdirs()) {
28 | throw new IOException("Failed to create directory " + parent);
29 | }
30 |
31 | // write file content
32 | FileOutputStream fos = new FileOutputStream(newFile);
33 | int len;
34 | while ((len = zis.read(buffer)) > 0) {
35 | fos.write(buffer, 0, len);
36 | }
37 | fos.close();
38 | }
39 | zipEntry = zis.getNextEntry();
40 | }
41 |
42 | } finally {
43 | zis.closeEntry();
44 | zis.close();
45 | }
46 | }
47 |
48 | private static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
49 | File destFile = new File(destinationDir, zipEntry.getName());
50 |
51 | String destDirPath = destinationDir.getCanonicalPath();
52 | String destFilePath = destFile.getCanonicalPath();
53 |
54 | if (!destFilePath.startsWith(destDirPath + File.separator)) {
55 | throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
56 | }
57 |
58 | return destFile;
59 | }
60 |
61 | public static void tarXZUnArchiver(String in, String out) {
62 | final TarXZUnArchiver ua = new TarXZUnArchiver();
63 |
64 | ua.setSourceFile(new File(in));
65 | ua.setDestDirectory(new File(out));
66 | ua.extract();
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/code/util/DownloadUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import code.config.RequestProxyConfig;
4 | import kong.unirest.GetRequest;
5 | import kong.unirest.HttpResponse;
6 | import kong.unirest.ProgressMonitor;
7 | import kong.unirest.Unirest;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.apache.hc.client5.http.fluent.Request;
10 | import org.apache.hc.core5.util.Timeout;
11 |
12 | import java.io.File;
13 | import java.io.InputStream;
14 | import java.nio.file.StandardCopyOption;
15 | import java.util.concurrent.TimeUnit;
16 |
17 | @Slf4j
18 | public class DownloadUtil {
19 |
20 | public static InputStream download(RequestProxyConfig requestProxyConfig, String url) {
21 | try {
22 | Request request = Request
23 | .get(url)
24 | .connectTimeout(Timeout.ofSeconds(30))
25 | .responseTimeout(Timeout.ofSeconds(60));
26 | requestProxyConfig.viaProxy(request);
27 | return request.execute().returnContent().asStream();
28 |
29 | } catch (Exception e) {
30 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
31 | }
32 | return null;
33 | }
34 |
35 | public static boolean download(RequestProxyConfig requestProxyConfig, String url, String file, ProgressMonitor progressMonitor) {
36 | try {
37 | GetRequest request = Unirest
38 | .get(url)
39 | .downloadMonitor(progressMonitor)
40 | .connectTimeout((int) TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS))
41 | ;
42 | requestProxyConfig.viaProxy(request);
43 |
44 | HttpResponse response = request.asFile(file, StandardCopyOption.REPLACE_EXISTING);
45 | return response.getStatus() == 200;
46 | } catch (Exception e) {
47 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
48 | }
49 | return false;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/code/util/ExceptionUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import java.io.IOException;
4 | import java.io.PrintWriter;
5 | import java.io.StringWriter;
6 |
7 | public class ExceptionUtil {
8 |
9 | public static String getStackTraceWithCustomInfoToStr(Exception e, String description) {
10 | return String.format("description: %s, class name: %s, stacktrace: %s", description, e.getClass().getName(), getStackTraceToStr(e));
11 | }
12 |
13 | public static String getStackTraceWithCustomInfoToStr(Exception e) {
14 | return String.format("class name: %s, stacktrace: %s", e.getClass().getName(), getStackTraceToStr(e));
15 | }
16 |
17 | public static String getStackTraceToStr(Exception e) {
18 | StringWriter stringWriter = null;
19 | PrintWriter printWriter = null;
20 | try {
21 | stringWriter = new StringWriter();
22 | printWriter = new PrintWriter(stringWriter);
23 |
24 | e.printStackTrace(printWriter);
25 |
26 | printWriter.flush();
27 | stringWriter.flush();
28 | } finally {
29 | if (null != stringWriter) {
30 | try {
31 | stringWriter.close();
32 | } catch (IOException ioException) {
33 | ioException.printStackTrace();
34 | }
35 | }
36 | if (null != printWriter) {
37 | printWriter.close();
38 | }
39 | }
40 | return stringWriter.toString();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/code/util/FFmpegUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import java.util.concurrent.*;
13 |
14 | @Slf4j
15 | public class FFmpegUtil {
16 |
17 | private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(false).setNameFormat("ffmpeg-pool-%d").build();
18 |
19 | private static ExecutorService fixedThreadPool = new ThreadPoolExecutor(
20 | 5,
21 | 20,
22 | 60,
23 | TimeUnit.SECONDS,
24 | new ArrayBlockingQueue<>(200),
25 | threadFactory,
26 | (Runnable r, ThreadPoolExecutor executor) -> {
27 | log.error(r.toString() + " is Rejected");
28 | }
29 | );
30 | public static void oggFileToMp3(String ffmpegPath, String filePath, String outPath) {
31 | List commend = new ArrayList();
32 | commend.add(ffmpegPath);
33 | commend.add("-i");
34 | commend.add(filePath);
35 | commend.add("-acodec");
36 | commend.add("libmp3lame");
37 | commend.add("-y");
38 | commend.add(outPath);
39 |
40 | try {
41 | ProcessBuilder builder = new ProcessBuilder(commend);
42 | builder.command(commend);
43 | log.info("ffmpeg command: {}", String.join(" ", commend));
44 | Process p = builder.start();
45 |
46 | // 获取外部程序标准输出流
47 | fixedThreadPool.submit(new OutputHandlerRunnable(p.getInputStream(), false));
48 | // 获取外部程序标准错误流
49 | fixedThreadPool.submit(new OutputHandlerRunnable(p.getErrorStream(), true));
50 | int code = p.waitFor();
51 | log.info("ffmpeg command: {} result: {}", String.join(" ", commend), code);
52 |
53 | p.destroy();
54 | } catch (Exception e) {
55 | e.printStackTrace();
56 | }
57 | }
58 | private static class OutputHandlerRunnable implements Runnable {
59 | private InputStream in;
60 |
61 | private boolean error;
62 |
63 | public OutputHandlerRunnable(InputStream in, boolean error) {
64 | this.in = in;
65 | this.error = error;
66 | }
67 |
68 | @Override
69 | public void run() {
70 | try (BufferedReader bufr = new BufferedReader(new InputStreamReader(this.in))) {
71 | String line = null;
72 | while ((line = bufr.readLine()) != null) {
73 | if (error) {
74 | System.out.println(line);
75 | }
76 | }
77 | } catch (IOException e) {
78 | e.printStackTrace();
79 | }
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/code/util/GPTUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import code.config.RequestProxyConfig;
4 | import code.util.gpt.GPTCallback;
5 | import code.util.gpt.parameter.GPTChatParameter;
6 | import code.util.gpt.parameter.GPTCreateImageParameter;
7 | import code.util.gpt.parameter.GPTTranscriptionsParameter;
8 | import code.util.gpt.response.*;
9 | import com.alibaba.fastjson2.JSON;
10 | import com.alibaba.fastjson2.JSONReader;
11 | import lombok.extern.slf4j.Slf4j;
12 | import org.apache.commons.io.IOUtils;
13 | import org.apache.commons.lang3.StringUtils;
14 | import org.apache.hc.client5.http.entity.mime.HttpMultipartMode;
15 | import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
16 | import org.apache.hc.client5.http.fluent.Content;
17 | import org.apache.hc.client5.http.fluent.Request;
18 | import org.apache.hc.client5.http.fluent.Response;
19 | import org.apache.hc.core5.http.ContentType;
20 | import org.apache.hc.core5.http.HttpEntity;
21 | import org.apache.hc.core5.util.Timeout;
22 |
23 | import java.io.BufferedInputStream;
24 | import java.io.BufferedReader;
25 | import java.io.InputStream;
26 | import java.io.InputStreamReader;
27 | import java.nio.charset.StandardCharsets;
28 |
29 | @Slf4j
30 | public class GPTUtil {
31 |
32 | public static GPTCreateImageResponse createImage(RequestProxyConfig requestProxyConfig, String token, String apiPrefix, GPTCreateImageParameter parameter) {
33 | GPTCreateImageResponse gptCreateImageResponse = new GPTCreateImageResponse();
34 | gptCreateImageResponse.setOk(false);
35 | try {
36 | Request request = Request
37 | .post(apiPrefix + "/v1/images/generations")
38 | .setHeader("Authorization", "Bearer " + token)
39 | .bodyString(JSON.toJSONString(parameter), ContentType.APPLICATION_JSON)
40 | .connectTimeout(Timeout.ofSeconds(30))
41 | .responseTimeout(Timeout.ofSeconds(60));
42 | requestProxyConfig.viaProxy(request);
43 | Response response = request.execute();
44 |
45 | Content content = response.returnContent();
46 | String s = content.asString();
47 | if (StringUtils.isNotBlank(s)) {
48 | GPTCreateImageResponse gptCreateImageResponse2 = JSON.parseObject(s, GPTCreateImageResponse.class, JSONReader.Feature.SupportSmartMatch);
49 | if (null != gptCreateImageResponse2 && null != gptCreateImageResponse2.getData() && !gptCreateImageResponse2.getData().isEmpty()) {
50 | gptCreateImageResponse2.setOk(true);
51 | return gptCreateImageResponse2;
52 | }
53 | }
54 |
55 | } catch (Exception e) {
56 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
57 | }
58 | return gptCreateImageResponse;
59 | }
60 |
61 | public static GPTTranscriptionsResponse transcriptions(RequestProxyConfig requestProxyConfig, String token, String apiPrefix, GPTTranscriptionsParameter parameter) {
62 | GPTTranscriptionsResponse gptTranscriptionsResponse = new GPTTranscriptionsResponse();
63 | gptTranscriptionsResponse.setOk(false);
64 | try {
65 | MultipartEntityBuilder builder = MultipartEntityBuilder.create();
66 | builder.setMode(HttpMultipartMode.LEGACY);
67 | builder.addBinaryBody("file", parameter.getFile(), ContentType.DEFAULT_BINARY, parameter.getFile().getName());
68 | builder.addTextBody("model", parameter.getModel(), ContentType.DEFAULT_BINARY);
69 | HttpEntity entity = builder.build();
70 |
71 | Request request = Request
72 | .post(apiPrefix + "/v1/audio/transcriptions")
73 | .body(entity)
74 | .setHeader("Authorization", "Bearer " + token)
75 | .connectTimeout(Timeout.ofSeconds(30))
76 | .responseTimeout(Timeout.ofSeconds(60));
77 | requestProxyConfig.viaProxy(request);
78 | Response response = request.execute();
79 |
80 | Content content = response.returnContent();
81 | String s = content.asString(StandardCharsets.UTF_8);
82 | if (StringUtils.isNotBlank(s)) {
83 | GPTTranscriptionsResponse gptTranscriptionsResponse2 = JSON.parseObject(s, GPTTranscriptionsResponse.class, JSONReader.Feature.SupportSmartMatch);
84 | if (null != gptTranscriptionsResponse2 && StringUtils.isNotBlank(gptTranscriptionsResponse2.getText())) {
85 | gptTranscriptionsResponse2.setOk(true);
86 | return gptTranscriptionsResponse2;
87 | }
88 | }
89 |
90 | } catch (Exception e) {
91 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
92 | }
93 | return gptTranscriptionsResponse;
94 | }
95 |
96 | public static GPTChatResponse chat(RequestProxyConfig requestProxyConfig, String token, String apiPrefix, GPTChatParameter parameter, GPTCallback callback) {
97 | GPTChatResponse chatResponse = new GPTChatResponse();
98 | chatResponse.setOk(false);
99 |
100 | try {
101 | Request request = Request
102 | .post(apiPrefix + "/v1/chat/completions")
103 | .setHeader("Authorization", "Bearer " + token)
104 | .setHeader("accept", "text/event-stream")
105 | .bodyString(JSON.toJSONString(parameter), org.apache.hc.core5.http.ContentType.APPLICATION_JSON)
106 | .connectTimeout(Timeout.ofSeconds(30))
107 | .responseTimeout(Timeout.ofSeconds(30))
108 | ;
109 | requestProxyConfig.viaProxy(request);
110 | Response response = request.execute();
111 |
112 | StringBuilder builder = new StringBuilder();
113 | StringBuilder responseBuilder = new StringBuilder();
114 | int code = response.handleResponse((classicHttpResponse) -> {
115 | InputStream inputStream = classicHttpResponse.getEntity().getContent();
116 |
117 | try (BufferedInputStream in = IOUtils.buffer(inputStream)) {
118 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
119 | String line = null;
120 | while((line = reader.readLine()) != null) {
121 | responseBuilder.append(line);
122 | String s = StringUtils.substringAfter(line, "data: ");
123 | if (StringUtils.isNotEmpty(s)) {
124 | if ("[DONE]".equals(s)) {
125 | GPTCallbackChatContent content = new GPTCallbackChatContent();
126 | content.setDone(true);
127 | callback.call(content);
128 | } else {
129 | GPTCallbackChatContent content = JSON.parseObject(s, GPTCallbackChatContent.class, JSONReader.Feature.SupportSmartMatch);
130 | GPTChatContentChoicesDelta delta = content.getChoices().get(0).getDelta();
131 | if (StringUtils.isNotEmpty(delta.getContent())) {
132 | builder.append(content.getChoices().get(0).getDelta().getContent());
133 | content.setDone(false);
134 | content.setContent(builder.toString());
135 | callback.call(content);
136 | }
137 | }
138 | }
139 | }
140 | }
141 | } catch (Exception e) {
142 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
143 | }
144 |
145 | return classicHttpResponse.getCode();
146 | });
147 |
148 | chatResponse.setStatusCode(code);
149 |
150 | String s = builder.toString();
151 | chatResponse.setOk(StringUtils.isNotEmpty(s));
152 | chatResponse.setContent(s);
153 | chatResponse.setResponse(responseBuilder.toString());
154 | } catch (Exception e) {
155 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
156 | }
157 |
158 | return chatResponse;
159 | }
160 |
161 | }
162 |
--------------------------------------------------------------------------------
/src/main/java/code/util/GithubUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import code.config.RequestProxyConfig;
4 | import com.alibaba.fastjson2.JSON;
5 | import com.alibaba.fastjson2.JSONReader;
6 | import lombok.Data;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.apache.hc.client5.http.fluent.Request;
9 | import org.apache.hc.client5.http.fluent.Response;
10 | import org.apache.hc.core5.http.ContentType;
11 | import org.apache.hc.core5.http.HttpResponse;
12 | import org.apache.hc.core5.util.Timeout;
13 |
14 | import java.nio.charset.Charset;
15 | import java.util.List;
16 | import java.util.concurrent.TimeUnit;
17 |
18 | @Slf4j
19 | public class GithubUtil {
20 |
21 | private final static String ApiVersion = "2022-11-28";
22 |
23 | @Data
24 | public static class LatestReleaseResponse {
25 | private boolean ok;
26 | private String htmlUrl;
27 | private String tagName;
28 | private String name;
29 | private String body;
30 | private List assets;
31 | }
32 | @Data
33 | public static class LatestReleaseAsset {
34 | private String name;
35 | private String browserDownloadUrl;
36 | }
37 |
38 | public static LatestReleaseResponse getLatestRelease(RequestProxyConfig requestProxyConfig, String owner, String repo) {
39 | String url = String.format("https://api.github.com/repos/%s/%s/releases/latest", owner, repo);
40 | try {
41 | Request request = Request
42 | .get(url)
43 | .setHeader("Accept", "application/vnd.github+json")
44 | .setHeader("X-GitHub-Api-Version", ApiVersion)
45 | .connectTimeout(Timeout.of(15, TimeUnit.SECONDS))
46 | .responseTimeout(Timeout.of(60, TimeUnit.SECONDS));
47 | requestProxyConfig.viaProxy(request);
48 | Response execute = request.execute();
49 |
50 | LatestReleaseResponse releaseAssetResponse = JSON.parseObject(execute.returnContent().asString(Charset.forName("UTF-8")), LatestReleaseResponse.class, JSONReader.Feature.SupportSmartMatch);
51 | releaseAssetResponse.setOk(true);
52 | return releaseAssetResponse;
53 | } catch (Exception e) {
54 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
55 | }
56 | LatestReleaseResponse response = new LatestReleaseResponse();
57 | response.setOk(false);
58 | return response;
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/code/util/ProgramUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 |
5 | import java.io.IOException;
6 | import java.util.stream.Collectors;
7 | import java.util.stream.Stream;
8 |
9 | @Slf4j
10 | public class ProgramUtil {
11 |
12 | public static void restart(String processName) {
13 | try {
14 | String[] strings = {"java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/" + processName};
15 | log.info(Stream.of(strings).collect(Collectors.joining(" ")));
16 | Runtime.getRuntime().exec(strings);
17 | System.exit(1);
18 | } catch (IOException e) {
19 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
20 | }
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/code/util/SqliteUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 |
5 | import java.io.File;
6 | import java.sql.Connection;
7 | import java.sql.DriverManager;
8 | import java.sql.SQLException;
9 | import java.sql.Statement;
10 |
11 | @Slf4j
12 | public class SqliteUtil {
13 |
14 | public interface SqliteInterface {
15 | Object execute(Statement statement) throws SQLException, IllegalAccessException, InstantiationException;
16 | }
17 |
18 | public static Object execute(String dbPath, SqliteInterface sqliteInterface) throws Exception {
19 | Connection connection = null;
20 | try {
21 | File file = new File(dbPath);
22 | boolean exists = file.exists();
23 | if (!exists) {
24 | file.createNewFile();
25 | }
26 | connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
27 | Statement statement = connection.createStatement();
28 | statement.setQueryTimeout(30);
29 | return sqliteInterface.execute(statement);
30 | } catch (Exception e) {
31 | throw e;
32 | } finally {
33 | if (null != connection) {
34 | connection.close();
35 | }
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/code/util/ThreadUtil.java:
--------------------------------------------------------------------------------
1 | package code.util;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 |
5 | import java.util.concurrent.TimeUnit;
6 | import java.util.concurrent.atomic.AtomicInteger;
7 |
8 | @Slf4j
9 | public class ThreadUtil {
10 |
11 | private static String name = "Thread-Util-";
12 | private static AtomicInteger atomicInteger = new AtomicInteger(0);
13 |
14 | public interface IntervalCallback {
15 | void run();
16 | }
17 |
18 | public static synchronized void newIntervalWithTryCatch(IntervalCallback callback, int unit, TimeUnit timeUnit) {
19 | new Thread(() -> {
20 | while (true) {
21 | try {
22 | callback.run();
23 | } catch (Exception e) {
24 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
25 | }
26 |
27 | try {
28 | timeUnit.sleep(unit);
29 | } catch (InterruptedException e) {
30 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
31 | throw new RuntimeException(e);
32 | }
33 | }
34 | }, name + atomicInteger.incrementAndGet()).start();
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/code/util/ffmpeg/FfmpegDownloadUrl.java:
--------------------------------------------------------------------------------
1 | package code.util.ffmpeg;
2 |
3 | import lombok.Getter;
4 |
5 | import java.util.Properties;
6 |
7 | @Getter
8 | public enum FfmpegDownloadUrl {
9 |
10 | Windows("https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n6.0-latest-win64-gpl-6.0.zip"),
11 |
12 | LINUX_AMD64("https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz"),
13 | LINUX_I686("https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-i686-static.tar.xz"),
14 | LINUX_ARM64("https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-arm64-static.tar.xz"),
15 | LINUX_ARMHF("https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-armhf-static.tar.xz"),
16 | LINUX_ARMEL("https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-armel-static.tar.xz"),
17 |
18 | ;
19 |
20 | private String url;
21 |
22 | FfmpegDownloadUrl(String url) {
23 | this.url = url;
24 | }
25 |
26 | public static FfmpegDownloadUrl getFfmpegDownloadUrl() {
27 | Properties properties = System.getProperties();
28 | String name = properties.getProperty("os.name");
29 | String arch = properties.getProperty("os.arch");
30 |
31 | if (name.toLowerCase().contains("windows")) {
32 | return Windows;
33 | } else {
34 | if ("amd64".equals(arch)) {
35 | return LINUX_AMD64;
36 | } else if ("i686".equals(arch)) {
37 | return LINUX_I686;
38 | } else if ("arm64".equals(arch)) {
39 | return LINUX_ARM64;
40 | } else if ("armhf".equals(arch)) {
41 | return LINUX_ARMHF;
42 | } else if ("armel".equals(arch)) {
43 | return LINUX_ARMEL;
44 | } else {
45 | return LINUX_AMD64;
46 | }
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/GPTCallback.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt;
2 |
3 | import code.util.gpt.response.GPTCallbackChatContent;
4 |
5 | public interface GPTCallback {
6 |
7 | void call(GPTCallbackChatContent content);
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/GPTModel.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt;
2 |
3 | import lombok.Getter;
4 |
5 | @Getter
6 | public enum GPTModel {
7 |
8 | Gpt3_5Turbo("gpt-3.5-turbo"),
9 |
10 | ;
11 |
12 | private String model;
13 |
14 | GPTModel(String model) {
15 | this.model = model;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/GPTRole.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt;
2 |
3 | import lombok.Getter;
4 | import lombok.ToString;
5 |
6 | @Getter
7 | public enum GPTRole {
8 |
9 | User("user"),
10 | System("system"),
11 | Assistant("assistant"),
12 |
13 | ;
14 |
15 | private String role;
16 |
17 | GPTRole(String role) {
18 | this.role = role;
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/GPTTranscriptionsModel.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt;
2 |
3 | import lombok.Getter;
4 |
5 | @Getter
6 | public enum GPTTranscriptionsModel {
7 |
8 | Whisper_1("whisper-1"),
9 |
10 | ;
11 |
12 | private String model;
13 |
14 | GPTTranscriptionsModel(String model) {
15 | this.model = model;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/parameter/GPTChatParameter.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.parameter;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | @Data
8 | public class GPTChatParameter {
9 |
10 | private String model;
11 | private boolean stream;
12 | private List messages;
13 | private String user;
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/parameter/GPTCreateImageParameter.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.parameter;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class GPTCreateImageParameter {
7 |
8 | private String prompt;
9 |
10 | private String user;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/parameter/GPTMessage.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.parameter;
2 |
3 | import code.util.gpt.GPTRole;
4 | import lombok.Data;
5 |
6 | @Data
7 | public class GPTMessage {
8 |
9 | private String role;
10 | private String content;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/parameter/GPTTranscriptionsParameter.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.parameter;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.File;
6 |
7 | @Data
8 | public class GPTTranscriptionsParameter {
9 |
10 | private File file;
11 |
12 | private String model;
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/response/GPTCallbackChatContent.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.response;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | @Data
8 | public class GPTCallbackChatContent {
9 |
10 | private boolean done;
11 | private String content;
12 |
13 | private String id;
14 |
15 | private String object;
16 | private Long created;
17 | private String model;
18 | private List choices;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/response/GPTChatContentChoices.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.response;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class GPTChatContentChoices {
7 |
8 | private GPTChatContentChoicesDelta delta;
9 |
10 | private Integer index;
11 | private String finishReason;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/response/GPTChatContentChoicesDelta.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.response;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class GPTChatContentChoicesDelta {
7 |
8 | private String role;
9 |
10 | private String content;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/response/GPTChatResponse.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.response;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class GPTChatResponse {
7 | private boolean ok;
8 |
9 | private int statusCode;
10 |
11 | private String content;
12 |
13 | private String response;
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/response/GPTCreateImage.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.response;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class GPTCreateImage {
7 |
8 | private String url;
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/response/GPTCreateImageResponse.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.response;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | @Data
8 | public class GPTCreateImageResponse {
9 |
10 | private boolean ok;
11 |
12 | private Long created;
13 |
14 | private List data;
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/code/util/gpt/response/GPTTranscriptionsResponse.java:
--------------------------------------------------------------------------------
1 | package code.util.gpt.response;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.List;
6 |
7 | @Data
8 | public class GPTTranscriptionsResponse {
9 |
10 | private boolean ok;
11 |
12 | private String text;
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/resources/i18n/i18n_en.properties:
--------------------------------------------------------------------------------
1 | bot_start_succeed=Bot program start succeed!
2 |
3 | help_text=\
4 | Document\n \
5 | https://github.com/kylelin1998/ChatGPTForTelegram/blob/master/README_en.md
6 |
7 | invalid_command=Invalid command, you are not admin
8 | on=On
9 | off=Off
10 | test=Test
11 | update=Update
12 | not_found=Not found
13 | unknown_error=System unknown error.
14 | nothing_at_all=Nothing at all
15 | cancel_succeeded=Cancel succeed.
16 | confirm=Confirm
17 | cancel=Cancel
18 | delete=Delete
19 | exit_succeeded=Exit Succeeded
20 | getting=Getting...
21 | downloading=Downloading...
22 | update_config=Update Config
23 | restart=Restart
24 | upgrade=Upgrade
25 | update_succeeded=Updating succeeded.
26 | update_failed=Updating failed.
27 | open=Open
28 | close=Close
29 | processing=Processing...
30 |
31 | language_list=Please choose one language below.
32 | change_language_finish=Language changed success.
33 |
34 | this_chat_is_a_new_chat=This chat is a new chat.
35 | please_send_me_a_problem_that_you_want_to_ask=Please send me a problem that you want to ask...
36 | requesting_open_ai_api=%s\n---\n%s\nOrganizing language...
37 | the_current_mode_is_continuous_chat_mode=The current mode is continuous chat mode
38 | an_error_occurred_of_requesting_open_ai_api_failed=An error occurred of requesting open ai API failed. HTTP response status code: %s
39 | continue_this_chat=You can continue this chat if you\u2019d like, or use /exit command to exit chat.
40 | ask_chat_ended=Chat ended or use /ask command to ask again if you wish
41 | the_current_mode_is_chat_message_limit_mode=The current mode is chat message limit mode
42 | cml_chat_ended=Chat ended, current chat message count: %s / %s, or you can use /cml command to chat again if you want to.
43 | cml_continue_this_chat=You can continue this chat if you\u2019d like, current chat message count: %s / %s, or use /exit command to exit chat.
44 | the_current_mode_is_none_of_message_context_mode=The current mode is none of message context mode
45 |
46 | you_are_not_an_admin=Invalid use of this command because you are not an admin.
47 | are_you_sure_to_restart_right_now=Are you sure to restart the bot right now?
48 | restarting=Restarting...
49 | getting_update_data=Getting update data...
50 | are_you_sure_to_upgrade_this_bot_right_now=Are you sure to upgrade this bot right now ?
51 | are_you_sure_to_update_the_config=Are you sure to update the config ?
52 | please_send_me_config_content=Please send me the config content you want to set.
53 | update_config_fail=Updating the config failed. This is because the config content does not meet certain standards.
54 | target_version=Target version
55 | current_version=Current version
56 | update_logs=Update logs
57 | updating=Updating
58 | downloaded=Downloaded: %s, Total size: %s
59 | please_send_me_an_image_description=Please send me an image description of what you want to create
60 | image_description_text_character_count_more_than=Image description text character count more than 1000 characters.
61 |
62 | chat_has_too_many_conversations=Oops! This chat has too many conversations. Please invoke /chat command for a new chat again.
63 |
64 | set_open_status=Set open usage status
65 | choose_open_status=Open status:\n\
66 | Anyone can use this bot.\n\n\
67 | Close status:\n\
68 | Only permission chat id list can use this bot.\n\n\
69 | Please choose open status or close status, and you can choose cancel to cancel.
70 |
71 | model=Model
72 | change_model=Change Model
73 | please_send_me_the_model_you_want_to_change=\u8BF7\u53D1\u9001\u4F60\u60F3\u8981\u66F4\u6539\u7684\u6A21\u578B
74 |
75 | set_voice_status=Set voice usage status
76 | saving=Saving...
77 | save_succeeded=Save succeeded.
78 | save_failed=Save failed.
79 | unzipping=Unzipping...
80 | delete_succeeded=Delete succeeded.
81 | delete_failed=Delete failed.
82 | please_send_me_the_playback_alias=Please resend the command /p
83 | xxx_not_found='%s' not found.
84 | record_mode_opened=Recording mode has been activated. Please chat with this chatbot to record the conversation. If you want to end the recording, send "end_record" to me. If you only want to record a single dialogue, please include the variable "${content}". The recording will end immediately when this variable is detected. More explanation: When replaying the single dialogue, the variable "${content}" will be automatically replaced with your input text, minimizing the need to input characters each time.
85 | please_send_me_record_alias=Please send me this record chat alias.
86 | invalid_alias=Invalid alias. The alias length must less than 10. Please send me again.
87 | Please_send_me_record_explanations=Please send me the record chat explanations.
88 | invalid_explanations=Invalid explanations. The explanations length must less than 100. Please send me again.
89 | record_alias=Record Alias
90 | record_explanations=Record Explanations
91 | you_can_use_record_to_start_recoding=You haven't created any records yet. You can use the /record command to start recording.
92 | choose_voice_status=Please choose open status or close status, and you can choose cancel to cancel.
93 | downloading_xxx=Downloading %s program...
94 | please_send_me_chat_buttons=Please send me chat buttons. If you don't want to set them, you can send '-1' to me.\n\n\
95 | The "<>" symbol is merely for emphasis. Please refrain from including this symbol when submitting formal data.\n\
96 | Sample\uFF1A\n\
97 | \n\
98 |