├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── README_en.md ├── docker-push.sh ├── pom.xml ├── run.sh └── src └── main ├── java └── code │ ├── Main.java │ ├── config │ ├── Config.java │ ├── ConfigField.java │ ├── ConfigSettings.java │ ├── DisplayConfigAnnotation.java │ ├── ExecutorsConfig.java │ ├── I18nConfig.java │ ├── I18nEnum.java │ ├── I18nLocaleEnum.java │ ├── MonitorConfigSettings.java │ ├── MonitorExecutorsConfig.java │ ├── ProxyTypeEnum.java │ ├── RequestProxyConfig.java │ └── TableEnum.java │ ├── eneity │ ├── MonitorTableEntity.java │ ├── PageEntity.java │ ├── SentRecordTableEntity.java │ ├── WebhookTableEntity.java │ └── YesOrNoEnum.java │ ├── handler │ ├── Command.java │ ├── CommandsHandler.java │ ├── Handler.java │ ├── I18nHandle.java │ ├── MessageHandle.java │ ├── StepsCenter.java │ ├── message │ │ ├── CallbackBuilder.java │ │ ├── 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 │ │ ├── Store.java │ │ └── WebhookStore.java │ ├── repository │ ├── I18nTableRepository.java │ ├── MonitorTableRepository.java │ ├── SentRecordTableRepository.java │ ├── WebhookTableRepository.java │ └── base │ │ ├── SqlBuilder.java │ │ ├── TableEntity.java │ │ ├── TableField.java │ │ ├── TableName.java │ │ └── TableRepository.java │ └── util │ ├── BytesUtil.java │ ├── DownloadUtil.java │ ├── ExceptionUtil.java │ ├── GithubUtil.java │ ├── ProgramUtil.java │ ├── RssUtil.java │ ├── Snowflake.java │ ├── SqliteUtil.java │ ├── TelegraphUtil.java │ └── translate │ ├── MicrosoftTranslateHandle.java │ ├── Translate.java │ ├── YoudaoTranslateHandle.java │ └── base │ ├── TranslateAPI.java │ └── TranslateAuth.java └── resources ├── code └── config │ ├── telegraph.html │ └── webhook.json ├── 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 -------------------------------------------------------------------------------- /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 BOT_ADMIN_ID='' 12 | ENV BOT_NAME='' 13 | ENV BOT_TOKEN='' 14 | ENV PROXY=false 15 | ENV PROXY_HOST=127.0.0.1 16 | ENV PROXY_PORT=7890 17 | 18 | WORKDIR /app 19 | COPY rss-monitor-for-telegram-universal.jar rss-monitor-for-telegram-universal.jar 20 | COPY run.sh run.sh 21 | 22 | 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/RssMonitorTelegramBot/releases/latest) 5 | 6 | ## 简介 7 | RSS监控最新文章, 如果有监控到最新文章会通知到您设置好的Telegram群聊, 频道, 或者个人号上 8 | 9 | 支持自定义消息通知, 由你掌控内容 10 | 11 | 支持RSS同步到Telegraph 12 | 13 | 支持自定义Webhook调用 14 | 15 | ## 具体功能 16 | #### 通知 17 | 不仅可以通知到TG, 你还可以自定义Webhook通知到任意你想要的地方 18 | #### 通知对象 19 | 拥有全局通知对象, 方案本身也可单独配置通知对象, 如果方案本身没有通知对象配置默认采用全局通知对象, 非常自由 20 | #### 无限方案 21 | 你可以添加任意数量监控RSS方案, 不限制, 好管理 22 | #### 通知模板变量 23 | 高度可配置通知文本, 支持众多通知模板变量 24 | #### 抓取资源 25 | 你可以为你的方案配置开启抓取资源功能, 开启之后默认会抓取RSS内容的一张图片结合通知文本发送到TG 26 | #### 翻译你想翻译的 27 | 模板变量支持翻译到你想要的, 比如你可以翻译标题成英文, 泰语, 俄语, 德语...等等 28 | #### 好升级 29 | 机器人内部内置升级功能, 一次部署, 后续升级在机器人内点击升级即可 30 | 31 | ## 部署 32 | 机器人的部署步骤是基于 Docker 的,其机器人升级功能也基于 Docker,因此请使用 Docker 进行部署,以防出现错误 33 | 34 | ### 部署方式1 (推荐) 35 | ⭐ Youtube: https://youtu.be/mNg6TFyozZk 36 | 37 | ⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1qF411f7pg/ 38 | 39 | #### 一键部署 40 | ``` 41 | docker run --name rssb -d -v $(pwd)/config:/app/config -e BOT_ADMIN_ID=管理者的ChatId -e BOT_NAME=机器人的username -e BOT_TOKEN=机器人token --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-tg-bot 42 | ``` 43 | #### 一键部署(开启代理) 44 | ``` 45 | docker run --name rssb -d -v $(pwd)/config:/app/config -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 --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-tg-bot 46 | ``` 47 | 48 | ### 部署方式2 (不推荐) 49 | Youtube:https://youtu.be/CiDxb1ESijQ 50 | 51 | 哔哩哔哩: https://www.bilibili.com/video/BV1Ts4y1S7bn/ 52 | 53 | 首先,在您的服务器上创建一个文件夹 54 | 55 | 然后,在其中创建名为 config 的另一个文件夹,config文件夹下必须包含名为 config.json 的JSON文件 56 | 57 | 接着,将 rss-monitor-for-telegram-universal.jar, run.sh 和 Dockerfile 传输到该文件夹中 58 | 59 | ### config.json 60 | ```json 61 | { 62 | "debug": 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", 68 | "bot_token": "xxx", 69 | "interval_minute": 10, 70 | "chatIdArray": [ 71 | "xxxxx" 72 | ], 73 | "permission_chat_id_array": [ 74 | "xxxx" 75 | ] 76 | } 77 | ``` 78 | bot admin主要作用是设置成只有你才能触发命令 79 | * on_proxy -> 是否开启代理 80 | * bot_admin_id -> Bot的管理者chat id 81 | * bot_name -> Bot 用户名 82 | * bot_token -> Bot token 83 | * interval_minute -> 监控间隔(分钟) 84 | * chatIdArray -> 需要发送的Chat Id列表 85 | * permission_chat_id_array -> 你只能允许列表下的这些chat id使用机器人, 可以填写个人的,或者是群的chat id 86 | * translate_youdao_key -> 有道翻译应用id 87 | * translate_youdao_secret -> 有道翻译密钥 88 | 89 | ### 第一步: 90 | 编译镜像 91 | ``` 92 | docker build -t rssb . 93 | ``` 94 | 95 | ### 第二步: 96 | 运行容器镜像 97 | ``` 98 | docker run --name rssb -d -v $(pwd):/app --restart=always rssb 99 | ``` 100 | 101 | ## 使用说明 102 | **机器人命令:** 103 | ``` 104 | create - 创建计划 105 | list - 计划列表 106 | exit - 退出 107 | language - 切换语言 108 | admin - 管理命令 109 | restart - 重启机器人 110 | upgrade - 升级机器人 111 | help - 帮助 112 | ``` 113 | 114 | 监控部分属性说明 115 | * webPagePreview -> 消息web预览 116 | * notification -> 通知开关 117 | * zeroDelay -> 零延迟监控开关, 不受间隔时间限制 118 | 119 | template说明: 120 | 支持自定义发送通知消息文本 121 | * ${link} -> 文章地址 122 | * ${title} -> 文章标题 123 | * ${author} -> 文章作者 124 | * ${telegraph} -> Telegraph文章地址 125 | * ${description} -> 文章内容 126 | * ${translate|zh|title} -> 将标题翻译成中文 127 | * ${translate|zh|description} -> 将描述翻译成中文 128 | * ${translate|en|title} -> 将标题翻译成英文 129 | * ${translate|en|description} -> 将描述翻译成英文 130 | * 翻译中间的代码可以更改为自己想要翻译的...以此类推... 131 | 132 | 例子, 会自动替换对应内容: 133 | ``` 134 | ${title} 135 | 136 | Telegraph: ${telegraph} 137 | 138 | 原文: ${link} 139 | ``` 140 | 141 |  142 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | ### [简体中文](./README.md) | English 2 | 3 |  4 | [](https://github.com/kylelin1998/RssMonitorTelegramBot/releases/latest) 5 | 6 | ## Introduction 7 | Monitor articles for RSS 8 | 9 | Send messages of an up-to-date article to your set-up Telegram channel, group, or personal if you have up-to-date. 10 | 11 | Support custom message content to your decision. 12 | 13 | Support RSS article auto sync to Telegraph platform. 14 | 15 | Support custom Webhook invocation. 16 | 17 | ## Specific Features 18 | #### Notifications 19 | Not only can you receive notifications on Telegram, but you can also customize webhook notifications to any platform you desire. 20 | #### Notification Targets 21 | You have the option to set global notification targets, and each individual plan can also have its unique notification targets. If a plan doesn't have specific notification targets configured, it will default to the global ones. This provides great flexibility. 22 | #### Unlimited Plans 23 | You can add an unlimited number of RSS monitoring plans, with no restrictions, for easy management. 24 | #### Notification Template Variables 25 | Highly configurable notification text, with support for numerous template variables. 26 | #### Resource Crawling 27 | You can enable resource crawling for your plans. Once enabled, it will automatically grab an image from the RSS content, combining it with the notification text and sending it to Telegram. 28 | #### Translation of Your Choice 29 | Template variables support translation to the language of your choice. For example, you can translate the title into English, Thai, Russian, German, etc. 30 | #### Convenient Upgrades 31 | The bot includes a built-in upgrade feature. After initial deployment, you can simply click on the upgrade button within the bot for future upgrades. 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 rssb -d -v $(pwd)/config:/app/config -e BOT_ADMIN_ID=AdminChatId -e BOT_NAME=BotUsername -e BOT_TOKEN=BotToken --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-tg-bot 44 | ``` 45 | #### One-click deployment (with proxy enabled) 46 | ``` 47 | docker run --name rssb -d -v $(pwd)/config:/app/config -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 --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-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 | To start, create a folder named whatever you prefer on your server. 58 | 59 | Then create another folder named config and the config folder must contains a json file named config.json in, then transfer rss-monitor-for-telegram-universal.jar, run.sh and Dockerfile to the folder. 60 | 61 | ### config.json 62 | ``` 63 | { 64 | "debug": false, 65 | "on_proxy": false, 66 | "proxy_host": "127.0.0.1", 67 | "proxy_port": 7890, 68 | "bot_admin_id": "xxxx", 69 | "bot_name": "xxx", 70 | "bot_token": "xxx", 71 | "interval_minute": 10, 72 | "chatIdArray": [ 73 | "xxxxx" 74 | ], 75 | "permission_chat_id_array": [ 76 | "xxxx" 77 | ] 78 | } 79 | ``` 80 | bot admin's main role is to set it so that only you can trigger commands. 81 | * on_proxy -> Enable proxy or not 82 | * bot_admin_id -> Chat ID of the Bot administrator 83 | * bot_name -> Bot username 84 | * bot_token -> Bot token 85 | * interval_minute -> Monitoring interval (in minutes) 86 | * chatIdArray -> List of Chat IDs to send notifications to 87 | * permission_chat_id_array -> Only allow the use of the bot by the chat IDs in this list. You can fill in your personal chat ID or group chat IDs. 88 | * translate_youdao_key -> Youdao translation application ID 89 | * translate_youdao_secret -> Youdao translation secret key 90 | 91 | ### First step: 92 | Build a docker image for use. 93 | ``` 94 | docker build -t rssb . 95 | ``` 96 | 97 | ### Second step: 98 | Run the docker image of just then build. 99 | ``` 100 | docker run --name rssb -d -v $(pwd):/app --restart=always rssb 101 | ``` 102 | 103 | ## Usage 104 | **Commands:** 105 | ``` 106 | create - Create plan 107 | list - Plan list 108 | exit - Exit 109 | language - Change language 110 | admin - Admin 111 | restart - Restart the bot 112 | upgrade - Upgrade the bot 113 | help - Help 114 | ``` 115 | 116 | Monitor config description 117 | * webPagePreview -> Web page preview 118 | * notification -> Notification switch 119 | * zeroDelay -> Zero delays to monitor 120 | 121 | Template content: 122 | Support custom message content 123 | * ${link} -> Article website URL 124 | * ${title} -> Article title 125 | * ${author} -> Article author 126 | * ${telegraph} -> Telegraph URL 127 | * ${description} -> Article description 128 | * ${translate|zh|title} -> Translate the title into Chinese 129 | * ${translate|zh|description} -> Translate the description into Chinese 130 | * ${translate|en|title} -> Translate the title into English 131 | * ${translate|en|description} -> Translate the description into English 132 | * You can modify the code in between to translate whatever you want... and so on... 133 | 134 | For example, automatically replace the variable: 135 | ``` 136 | ${title} 137 | 138 | Telegraph: ${telegraph} 139 | 140 | Original article: ${link} 141 | ``` 142 | 143 |  144 | -------------------------------------------------------------------------------- /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/rss-tg-bot . --push -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.kylelin1998 8 | rss-monitor-for-telegram 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 | 74 | 75 | org.slf4j 76 | slf4j-api 77 | 1.7.28 78 | jar 79 | 80 | 81 | ch.qos.logback 82 | logback-core 83 | 1.2.3 84 | jar 85 | 86 | 87 | ch.qos.logback 88 | logback-classic 89 | 1.2.3 90 | jar 91 | 92 | 93 | 94 | org.telegram 95 | telegrambots 96 | 6.5.0 97 | 98 | 99 | org.telegram 100 | telegrambotsextensions 101 | 6.5.0 102 | 103 | 104 | 105 | com.vladsch.flexmark 106 | flexmark-all 107 | 0.64.0 108 | 109 | 110 | 111 | com.rometools 112 | rome 113 | 1.18.0 114 | 115 | 116 | 117 | 118 | org.xerial 119 | sqlite-jdbc 120 | 3.40.0.0 121 | 122 | 123 | 124 | com.konghq 125 | unirest-java 126 | 3.14.1 127 | 128 | 129 | 130 | org.jsoup 131 | jsoup 132 | 1.11.2 133 | 134 | 135 | 136 | org.apache.httpcomponents.client5 137 | httpclient5 138 | 5.1.3 139 | 140 | 141 | org.apache.httpcomponents.client5 142 | httpclient5-fluent 143 | 5.1.3 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | org.apache.maven.plugins 152 | maven-shade-plugin 153 | 2.3 154 | 155 | false 156 | 157 | 158 | 159 | package 160 | 161 | shade 162 | 163 | 164 | 165 | 166 | code.Main 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | temp=temp.jar 4 | app=/app/rss-monitor-for-telegram-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 -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.MessageHandle; 11 | import code.handler.store.Store; 12 | import code.repository.*; 13 | import code.util.ExceptionUtil; 14 | import code.util.Snowflake; 15 | import com.alibaba.fastjson2.JSON; 16 | import kong.unirest.Unirest; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.telegram.telegrambots.meta.TelegramBotsApi; 19 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 20 | import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; 21 | 22 | /** 23 | * 24 | * ,---. ,--. ,--. ,--.,--. 25 | * / O \ | |,--. ,--. ,--,--.,--. ,--.,---. | |-. ,---. | |`--' ,---.,--. ,--.,---. 26 | * | .-. || || |.'.| |' ,-. | \ ' /( .-' | .-. '| .-. :| |,--.| .-. :\ `' /| .-. : 27 | * | | | || || .'. |\ '-' | \ ' .-' `) | `-' |\ --.| || |\ --. \ / \ --. 28 | * `--' `--'`--''--' '--' `--`--'.-' / `----' `---' `----'`--'`--' `----' `--' `----' 29 | * `---' 30 | * Always believe that something wonderful is about to happen 31 | * 32 | * If you have any additional features you'd like to suggest or if you have any feedback, 33 | * you can reach me at my email address: email@kylelin1998.com 34 | */ 35 | @Slf4j 36 | public class Main { 37 | public static CommandsHandler Bot = null; 38 | public static volatile ConfigSettings GlobalConfig = Config.initConfig(); 39 | public final static code.repository.SentRecordTableRepository SentRecordTableRepository = new SentRecordTableRepository(); 40 | public final static code.repository.I18nTableRepository I18nTableRepository = new I18nTableRepository(); 41 | public final static code.repository.MonitorTableRepository MonitorTableRepository = new MonitorTableRepository(); 42 | public final static code.repository.WebhookTableRepository WebhookTableRepository = new WebhookTableRepository(); 43 | public final static Snowflake Snowflake = new Snowflake(997); 44 | 45 | public static void main(String[] args) { 46 | log.info(String.format("Main args: %s", JSON.toJSONString(args))); 47 | log.info(String.format("System properties: %s", System.getProperties())); 48 | log.info(String.format("Config: %s", JSON.toJSONString(GlobalConfig))); 49 | 50 | Unirest 51 | .config() 52 | .enableCookieManagement(false) 53 | .verifySsl(GlobalConfig.getVerifySsl()) 54 | ; 55 | 56 | new Thread(() -> { 57 | while (true) { 58 | try { 59 | GlobalConfig = Config.readConfig(); 60 | 61 | Thread.sleep(5000); 62 | } catch (InterruptedException e) {} 63 | } 64 | }).start(); 65 | 66 | Store.init(); 67 | Handler.init(); 68 | 69 | log.info("Program is running"); 70 | 71 | try { 72 | TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class); 73 | 74 | if (GlobalConfig.getOnProxy()) { 75 | Bot = new CommandsHandler(RequestProxyConfig.create().buildDefaultBotOptions()); 76 | } else { 77 | Bot = new CommandsHandler(); 78 | } 79 | 80 | botsApi.registerBot(Bot); 81 | 82 | MessageHandle.sendMessage(GlobalConfig.getBotAdminId(), I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.BotStartSucceed) + I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.CurrentVersion) + ": " + Config.MetaData.CurrentVersion, false); 83 | 84 | Config.oldDataConvert(); 85 | 86 | } catch (TelegramApiException e) { 87 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/code/config/Config.java: -------------------------------------------------------------------------------- 1 | package code.config; 2 | 3 | import code.eneity.MonitorTableEntity; 4 | import code.eneity.YesOrNoEnum; 5 | import code.util.ExceptionUtil; 6 | import com.alibaba.fastjson2.JSON; 7 | import com.alibaba.fastjson2.JSONException; 8 | import com.alibaba.fastjson2.JSONReader; 9 | import com.alibaba.fastjson2.JSONWriter; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.io.FileUtils; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import java.io.*; 15 | import java.lang.reflect.Field; 16 | import java.nio.charset.StandardCharsets; 17 | 18 | import java.nio.file.Files; 19 | import java.nio.file.attribute.BasicFileAttributes; 20 | import java.nio.file.attribute.FileTime; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Properties; 24 | import java.util.concurrent.TimeUnit; 25 | import java.util.concurrent.locks.ReentrantReadWriteLock; 26 | import java.util.stream.Collectors; 27 | 28 | import static code.Main.*; 29 | 30 | @Slf4j 31 | public class Config { 32 | 33 | public static final String CurrentDir = System.getProperty("user.dir") + File.separator + "config"; 34 | public static final String MonitorDir = CurrentDir + File.separator + "monitor"; 35 | 36 | public static final String SettingsPath = CurrentDir + File.separator + "config.json"; 37 | 38 | public static final String DBPath = CurrentDir + File.separator + "db.db"; 39 | 40 | public final static String TempDir = CurrentDir + File.separator + "temp"; 41 | 42 | public static String TelegraphHtml = new BufferedReader(new InputStreamReader(Config.class.getResourceAsStream("telegraph.html"), StandardCharsets.UTF_8)) 43 | .lines() 44 | .collect(Collectors.joining("\n")); 45 | public static String WebhookJson = new BufferedReader(new InputStreamReader(Config.class.getResourceAsStream("webhook.json"), StandardCharsets.UTF_8)) 46 | .lines() 47 | .collect(Collectors.joining("\n")); 48 | 49 | private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); 50 | 51 | public final static class MetaData { 52 | public final static String CurrentVersion = "1.1.5"; 53 | public final static String GitOwner = "kylelin1998"; 54 | public final static String GitRepo = "RssMonitorTelegramBot"; 55 | public final static String ProcessName = "rss-monitor-for-telegram-universal.jar"; 56 | public final static String JarName = "rss-monitor-for-telegram-universal.jar"; 57 | } 58 | 59 | static { 60 | mkdirs(CurrentDir, MonitorDir, TempDir); 61 | 62 | new Thread(() -> { 63 | while (true) { 64 | try { 65 | File file = new File(TempDir); 66 | ArrayList files = new ArrayList<>(); 67 | file.list((File dir, String name) -> { 68 | File file1 = new File(dir, name); 69 | try { 70 | BasicFileAttributes attributes = Files.readAttributes(file1.toPath(), BasicFileAttributes.class); 71 | FileTime fileTime = attributes.creationTime(); 72 | long millis = System.currentTimeMillis() - fileTime.toMillis(); 73 | if (millis > 3600000) { 74 | files.add(file1); 75 | } 76 | } catch (IOException e) { 77 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 78 | } 79 | 80 | return true; 81 | }); 82 | 83 | for (File df : files) { 84 | df.delete(); 85 | } 86 | } catch (Exception e) { 87 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 88 | } 89 | try { 90 | TimeUnit.MINUTES.sleep(30); 91 | } catch (InterruptedException e) { 92 | throw new RuntimeException(e); 93 | } 94 | } 95 | }).start(); 96 | } 97 | 98 | private static void mkdirs(String... dirs) { 99 | for (String dir : dirs) { 100 | File file = new File(dir); 101 | if (!file.exists()) { 102 | file.mkdirs(); 103 | } 104 | } 105 | } 106 | 107 | public static ConfigSettings initConfig() { 108 | File file = new File(SettingsPath); 109 | if (!file.exists()) { 110 | Properties properties = System.getProperties(); 111 | 112 | ConfigSettings configSettings = new ConfigSettings(); 113 | configSettings.setBotAdminId(properties.getProperty("botAdminId", "")); 114 | configSettings.setBotName(properties.getProperty("botName", "")); 115 | configSettings.setBotToken(properties.getProperty("botToken", "")); 116 | configSettings.setOnProxy(Boolean.valueOf(properties.getProperty("botProxy", "false"))); 117 | configSettings.setProxyHost(properties.getProperty("botProxyHost", "127.0.0.1")); 118 | configSettings.setProxyPort(Integer.valueOf(properties.getProperty("botProxyPort", "7890"))); 119 | 120 | saveConfig(handle(configSettings)); 121 | } 122 | return readConfig(); 123 | } 124 | 125 | private static ConfigSettings handle(ConfigSettings configSettings) { 126 | Boolean debug = configSettings.getDebug(); 127 | if (null == debug) { 128 | configSettings.setDebug(false); 129 | } 130 | String[] permissionChatIdArray = configSettings.getPermissionChatIdArray(); 131 | if (null == permissionChatIdArray) { 132 | configSettings.setPermissionChatIdArray(new String[]{ configSettings.getBotAdminId() }); 133 | } 134 | String[] chatIdArray = configSettings.getChatIdArray(); 135 | if (null == chatIdArray) { 136 | configSettings.setChatIdArray(new String[]{ configSettings.getBotAdminId() }); 137 | } 138 | Integer intervalMinute = configSettings.getIntervalMinute(); 139 | if (null == intervalMinute) { 140 | configSettings.setIntervalMinute(5); 141 | } 142 | Boolean hideCopyrightTips = configSettings.getHideCopyrightTips(); 143 | if (null == hideCopyrightTips) { 144 | configSettings.setHideCopyrightTips(false); 145 | } 146 | Boolean verifySsl = configSettings.getVerifySsl(); 147 | if (null == verifySsl) { 148 | configSettings.setVerifySsl(true); 149 | } 150 | List excludeKeywords = configSettings.getExcludeKeywords(); 151 | if (null == excludeKeywords) { 152 | configSettings.setExcludeKeywords(new ArrayList<>()); 153 | } 154 | List excludeKeywordsRegex = configSettings.getExcludeKeywordsRegex(); 155 | if (null == excludeKeywordsRegex) { 156 | configSettings.setExcludeKeywordsRegex(new ArrayList<>()); 157 | } 158 | List includeKeywords = configSettings.getIncludeKeywords(); 159 | if (null == includeKeywords) { 160 | configSettings.setIncludeKeywords(new ArrayList<>()); 161 | } 162 | List includeKeywordsRegex = configSettings.getIncludeKeywordsRegex(); 163 | if (null == includeKeywordsRegex) { 164 | configSettings.setIncludeKeywordsRegex(new ArrayList<>()); 165 | } 166 | return configSettings; 167 | } 168 | 169 | public static ConfigSettings readConfig() { 170 | ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock(); 171 | readLock.lock(); 172 | try { 173 | File file = new File(SettingsPath); 174 | boolean exists = file.exists(); 175 | if (exists) { 176 | String text = FileUtils.readFileToString(file, StandardCharsets.UTF_8); 177 | ConfigSettings configSettings = JSON.parseObject(text, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch); 178 | return handle(configSettings); 179 | } else { 180 | log.warn("Settings file not found, " + SettingsPath); 181 | } 182 | } catch (IOException e) { 183 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e), SettingsPath); 184 | } finally { 185 | readLock.unlock(); 186 | } 187 | return null; 188 | } 189 | 190 | public static ConfigSettings verifyConfig(String configJson) { 191 | if (StringUtils.isBlank(configJson)) { 192 | return null; 193 | } 194 | ConfigSettings configSettings = null; 195 | try { 196 | configSettings = JSON.parseObject(configJson, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch); 197 | } catch (JSONException e) { 198 | } 199 | if (null == configSettings) { 200 | return null; 201 | } 202 | for (Field field : configSettings.getClass().getDeclaredFields()) { 203 | ConfigField configField = field.getAnnotation(ConfigField.class); 204 | if (null == configField) { 205 | continue; 206 | } 207 | if (configField.isNotNull()) { 208 | try { 209 | field.setAccessible(true); 210 | Object o = field.get(configSettings); 211 | if (null == o) { 212 | return null; 213 | } 214 | } catch (IllegalAccessException e) { 215 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 216 | return null; 217 | } 218 | } 219 | } 220 | 221 | return configSettings; 222 | } 223 | 224 | public synchronized static void oldDataConvert() { 225 | File file = new File(MonitorDir); 226 | file.list((File dir, String name) -> { 227 | File monitorFile = new File(dir, name); 228 | try { 229 | if (monitorFile.isFile()) { 230 | String text = FileUtils.readFileToString(monitorFile, StandardCharsets.UTF_8); 231 | MonitorConfigSettings configSettings = JSON.parseObject(text, MonitorConfigSettings.class, JSONReader.Feature.SupportSmartMatch); 232 | configSettings.setFilename(name); 233 | configSettings.setFileBasename(StringUtils.removeEnd(name, ".json")); 234 | if (null == configSettings.getNotification()) { 235 | configSettings.setNotification(true); 236 | } 237 | if (null == configSettings.getZeroDelay()) { 238 | configSettings.setZeroDelay(false); 239 | } 240 | 241 | MonitorTableEntity monitorTableEntity = new MonitorTableEntity(); 242 | monitorTableEntity.setId(Snowflake.nextIdToStr()); 243 | monitorTableEntity.setChatId(GlobalConfig.botAdminId); 244 | monitorTableEntity.setEnable(YesOrNoEnum.toInt(configSettings.getOn())); 245 | monitorTableEntity.setName(configSettings.getFileBasename()); 246 | monitorTableEntity.setNotification(YesOrNoEnum.toInt(configSettings.getNotification())); 247 | monitorTableEntity.setUrl(configSettings.getUrl()); 248 | monitorTableEntity.setTemplate(configSettings.getTemplate()); 249 | monitorTableEntity.setZeroDelay(YesOrNoEnum.toInt(configSettings.getZeroDelay())); 250 | monitorTableEntity.setCreateTime(System.currentTimeMillis()); 251 | monitorTableEntity.setChatIdArrayJson(JSON.toJSONString(configSettings.getChatIdArray())); 252 | monitorTableEntity.setWebPagePreview(YesOrNoEnum.toInt(configSettings.getWebPagePreview())); 253 | monitorTableEntity.setCaptureFlag(YesOrNoEnum.No.getNum()); 254 | MonitorTableRepository.insert(monitorTableEntity); 255 | 256 | monitorFile.delete(); 257 | } 258 | } catch (IOException e) { 259 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 260 | } 261 | return true; 262 | }); 263 | } 264 | 265 | public static boolean saveConfig(ConfigSettings configSettings) { 266 | ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock(); 267 | writeLock.lock(); 268 | try { 269 | File file = new File(SettingsPath); 270 | FileUtils.write(file, JSON.toJSONString(configSettings, JSONWriter.Feature.PrettyFormat), StandardCharsets.UTF_8); 271 | return true; 272 | } catch (IOException e) { 273 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 274 | } finally { 275 | writeLock.unlock(); 276 | } 277 | return false; 278 | } 279 | 280 | } 281 | -------------------------------------------------------------------------------- /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 | import java.util.List; 7 | 8 | @Data 9 | public class ConfigSettings { 10 | 11 | @ConfigField(isNotNull = true) 12 | public Boolean debug; 13 | 14 | @JSONField(name = "on_proxy") 15 | @ConfigField(isNotNull = true) 16 | public Boolean onProxy; 17 | @JSONField(name = "proxy_host") 18 | public String proxyHost; 19 | @JSONField(name = "proxy_port") 20 | public Integer proxyPort; 21 | 22 | @JSONField(name = "bot_admin_id") 23 | @ConfigField(isNotNull = true) 24 | public String botAdminId; 25 | @JSONField(name = "permission_chat_id_array") 26 | @ConfigField(isNotNull = true) 27 | private String[] permissionChatIdArray; 28 | 29 | @JSONField(name = "bot_name") 30 | @ConfigField(isNotNull = true) 31 | public String botName; 32 | @JSONField(name = "bot_token") 33 | @ConfigField(isNotNull = true) 34 | public String botToken; 35 | 36 | @JSONField(name = "interval_minute") 37 | @ConfigField(isNotNull = true) 38 | private Integer intervalMinute; 39 | 40 | @JSONField(name = "chatId_array") 41 | @ConfigField(isNotNull = true) 42 | private String[] chatIdArray; 43 | 44 | @JSONField(name = "chat_buttons") 45 | private String chatButtons; 46 | 47 | @JSONField(name = "hide_copyright_tips") 48 | private Boolean hideCopyrightTips; 49 | 50 | @JSONField(name = "verify_ssl") 51 | private Boolean verifySsl; 52 | 53 | @JSONField(name = "exclude_keywords") 54 | private List excludeKeywords; 55 | @JSONField(name = "exclude_keywords_regex") 56 | private List excludeKeywordsRegex; 57 | 58 | @JSONField(name = "include_keywords") 59 | private List includeKeywords; 60 | @JSONField(name = "include_keywords_regex") 61 | private List includeKeywordsRegex; 62 | 63 | @JSONField(name = "translate_youdao_key") 64 | public String translateYoudaoKey; 65 | @JSONField(name = "translate_youdao_secret") 66 | public String translateYoudaoSecret; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/code/config/DisplayConfigAnnotation.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(ElementType.FIELD) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface DisplayConfigAnnotation { 11 | 12 | String i18n(); 13 | 14 | boolean set(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /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.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.*; 10 | 11 | public class I18nConfig { 12 | 13 | private static Map> cacheMap = new LinkedHashMap<>(); 14 | 15 | static { 16 | ResourceBundle.Control control = new ResourceBundle.Control() { 17 | @Override 18 | public ResourceBundle newBundle(String baseName, java.util.Locale locale, String format, ClassLoader loader, boolean reload) throws IOException { 19 | String resourceName = toResourceName(toBundleName(baseName, locale), "properties"); 20 | InputStream inputStream = null; 21 | try { 22 | inputStream = loader.getResourceAsStream(resourceName); 23 | if (inputStream != null) { 24 | return new PropertyResourceBundle(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); 25 | } 26 | } finally { 27 | if (inputStream != null) { 28 | inputStream.close(); 29 | } 30 | } 31 | return null; 32 | } 33 | }; 34 | for (I18nLocaleEnum value : I18nLocaleEnum.values()) { 35 | ResourceBundle bundle = ResourceBundle.getBundle("i18n/i18n", value.getLocale(), control); 36 | 37 | HashMap hashMap = new HashMap<>(); 38 | for (String s : bundle.keySet()) { 39 | hashMap.put(s, bundle.getString(s)); 40 | } 41 | 42 | cacheMap.put(value.getAlias(), hashMap); 43 | } 44 | } 45 | 46 | public static String getText(String i18nAlias, String key) { 47 | Map map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias()); 48 | return map.get(key); 49 | } 50 | 51 | public static String getText(String i18nAlias, I18nEnum i18nEnum) { 52 | Map map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias()); 53 | return map.get(i18nEnum.getKey()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /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 | MonitorList("monitor_list"), 13 | NothingHere("nothing_here"), 14 | 15 | On("on"), 16 | Off("off"), 17 | Test("test"), 18 | Update("update"), 19 | NotFound("not_found"), 20 | UnknownError("unknown_error"), 21 | NothingAtAll("nothing_at_all"), 22 | CancelSucceeded("cancel_succeeded"), 23 | Confirm("confirm"), 24 | Cancel("cancel"), 25 | Delete("delete"), 26 | Finish("finish"), 27 | ExitSucceeded("exit_succeeded"), 28 | Getting("getting"), 29 | Downloading("downloading"), 30 | ForceRecord("force_record"), 31 | 32 | OnMonitor("on_monitor"), 33 | OffMonitor("off_monitor"), 34 | 35 | LanguageList("language_list"), 36 | ChangeLanguageFinish("change_language_finish"), 37 | 38 | MonitorExists("monitor_exists"), 39 | 40 | CreateNameTooLong("create_name_too_long"), 41 | CreateMonitor1("create_monitor_1"), 42 | CreateMonitor2("create_monitor_2"), 43 | CreateMonitor3("create_monitor_3"), 44 | CreateMonitor4("create_monitor_4"), 45 | CreateMonitor5("create_monitor_5"), 46 | CreateMonitor6("create_monitor_6"), 47 | CreateMonitor7("create_monitor_7"), 48 | CreateMonitor8("create_monitor_8"), 49 | CreateMonitorFinish("create_monitor_finish"), 50 | 51 | 52 | TestMonitor("test_monitor"), 53 | ForceRecordSucceeded("force_record_succeeded"), 54 | 55 | 56 | UpdateMonitor1("update_monitor_1"), 57 | UpdateMonitor2("update_monitor_2"), 58 | UpdateMonitor3("update_monitor_3"), 59 | UpdateMonitor4("update_monitor_4"), 60 | UpdateFieldError("update_field_error"), 61 | UpdateMonitorFinish("update_monitor_finish"), 62 | 63 | DeleteMonitorConfirm("delete_monitor_confirm"), 64 | DeleteMonitorFinish("delete_monitor_finish"), 65 | 66 | 67 | ConfigDisplayOn("config_display_on"), 68 | ConfigDisplayWebPagePreview("config_display_web_page_preview"), 69 | ConfigDisplayNotification("config_display_notification"), 70 | ConfigDisplayZeroDelay("config_display_zero_delay"), 71 | ConfigDisplayUrl("config_display_url"), 72 | ConfigDisplayTemplate("config_display_template"), 73 | ConfigDisplayChatIdArray("config_display_chat_id_array"), 74 | ConfigDisplayFileBasename("config_display_file_basename"), 75 | 76 | YouAreNotAnAdmin("you_are_not_an_admin"), 77 | AreYouSureToRestartRightNow("are_you_sure_to_restart_right_now"), 78 | Restarting("restarting"), 79 | GettingUpdateData("getting_update_data"), 80 | AreYouSureToUpgradeThisBotRightNow("are_you_sure_to_upgrade_this_bot_right_now"), 81 | TargetVersion("target_version"), 82 | CurrentVersion("current_version"), 83 | UpdateLogs("update_logs"), 84 | Updating("updating"), 85 | Downloaded("downloaded"), 86 | 87 | AreYouSureToUpdateTheConfig("are_you_sure_to_update_the_config"), 88 | PleaseSendMeConfigContent("please_send_me_config_content"), 89 | UpdateConfigFail("update_config_fail"), 90 | 91 | UpdateSucceeded("update_succeeded"), 92 | UpdateFailed("update_failed"), 93 | 94 | SetChatButtons("set_chat_buttons"), 95 | UpdateConfig("update_config"), 96 | Restart("restart"), 97 | Upgrade("upgrade"), 98 | 99 | PleaseSendMeChatButtons("please_send_me_chat_buttons"), 100 | FormatError("format_error"), 101 | 102 | SettingWebhook("setting_webhook"), 103 | HideCopyrightTips("hide_copyright_tips"), 104 | AreYouSureYouWantToHideCopyrightTips("are_you_sure_you_want_to_hide_copyright_tips"), 105 | CurrentSetting("current_setting"), 106 | PleaseSendMeWebhookSettings("please_send_me_webhook_settings"), 107 | 108 | Back("back"), 109 | Refresh("refresh"), 110 | ExcludeKeywords("exclude_keywords"), 111 | ExcludeKeywordsRegex("exclude_keywords_regex"), 112 | PleaseSendMeExcludeKeywords("please_send_me_exclude_keywords"), 113 | PleaseSendMeExcludeKeywordsRegex("please_send_me_exclude_keywords_regex"), 114 | 115 | VerifySsl("verify_ssl"), 116 | AreYouSureYouWantToSetVerifySsl("are_you_sure_you_want_to_set_verify_ssl"), 117 | Enable("enable"), 118 | Disable("disable"), 119 | NeedToRestartBot("need_to_restart_bot"), 120 | 121 | IncludeKeywords("include_keywords"), 122 | IncludeKeywordsRegex("include_keywords_regex"), 123 | PleaseSendMeIncludeKeywords("please_send_me_include_keywords"), 124 | PleaseSendMeIncludeKeywordsRegex("please_send_me_include_keywords_regex"), 125 | 126 | CaptureFlag("capture_flag"), 127 | TranslationLanguage("translation_language"), 128 | SetCaptureFlag("set_capture_flag"), 129 | SetCaptureFlagOnNote("set_capture_flag_on_note"), 130 | SetCaptureFlagOffNote("set_capture_flag_off_note"), 131 | 132 | ; 133 | 134 | private String key; 135 | 136 | I18nEnum(String key) { 137 | this.key = key; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /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/MonitorConfigSettings.java: -------------------------------------------------------------------------------- 1 | package code.config; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class MonitorConfigSettings { 7 | 8 | @DisplayConfigAnnotation(i18n = "config_display_on", set = false) 9 | private Boolean on; 10 | 11 | private String filename; 12 | @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = false) 13 | private String fileBasename; 14 | 15 | @DisplayConfigAnnotation(i18n = "config_display_web_page_preview", set = true) 16 | private Boolean webPagePreview; 17 | @DisplayConfigAnnotation(i18n = "config_display_notification", set = true) 18 | private Boolean notification; 19 | @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = true) 20 | private Boolean zeroDelay; 21 | 22 | @DisplayConfigAnnotation(i18n = "config_display_url", set = true) 23 | private String url; 24 | @DisplayConfigAnnotation(i18n = "config_display_template", set = true) 25 | private String template; 26 | 27 | @DisplayConfigAnnotation(i18n = "config_display_chat_id_array", set = true) 28 | private String[] chatIdArray; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/code/config/MonitorExecutorsConfig.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 MonitorExecutorsConfig { 10 | 11 | private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(false).setNameFormat("monitor-pool-%d").build(); 12 | 13 | private static ExecutorService fixedThreadPool = new ThreadPoolExecutor( 14 | 2, 15 | 10, 16 | 60, 17 | TimeUnit.SECONDS, 18 | new ArrayBlockingQueue<>(100000), 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/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 | SentRecordTable("sent_record_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/MonitorTableEntity.java: -------------------------------------------------------------------------------- 1 | package code.eneity; 2 | 3 | import code.config.DisplayConfigAnnotation; 4 | import code.repository.base.TableEntity; 5 | import code.repository.base.TableField; 6 | import code.repository.base.TableName; 7 | import lombok.Data; 8 | 9 | @TableName(name = "monitor_table") 10 | @Data 11 | public class MonitorTableEntity implements TableEntity { 12 | 13 | @TableField(name = "id", sql = "id varchar(55) primary key") 14 | private String id; 15 | 16 | @TableField(name = "chat_id", sql = "chat_id varchar(100)") 17 | private String chatId; 18 | 19 | @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = false) 20 | @TableField(name = "name", sql = "name varchar(50)") 21 | private String name; 22 | 23 | @DisplayConfigAnnotation(i18n = "config_display_chat_id_array", set = true) 24 | @TableField(name = "chat_id_array_json", sql = "chat_id_array_json text") 25 | private String chatIdArrayJson; 26 | 27 | @TableField(name = "create_time", sql = "create_time timestamp") 28 | private Long createTime; 29 | @TableField(name = "update_time", sql = "update_time timestamp") 30 | private Long updateTime; 31 | 32 | @DisplayConfigAnnotation(i18n = "config_display_notification", set = true) 33 | @TableField(name = "notification", sql = "notification int(2)") 34 | private Integer notification; 35 | 36 | @DisplayConfigAnnotation(i18n = "config_display_on", set = false) 37 | @TableField(name = "enable", sql = "enable int(2)") 38 | private Integer enable; 39 | 40 | @DisplayConfigAnnotation(i18n = "config_display_template", set = true) 41 | @TableField(name = "template", sql = "template text") 42 | private String template; 43 | 44 | @DisplayConfigAnnotation(i18n = "config_display_url", set = true) 45 | @TableField(name = "url", sql = "url text") 46 | private String url; 47 | 48 | @DisplayConfigAnnotation(i18n = "config_display_web_page_preview", set = true) 49 | @TableField(name = "web_page_preview", sql = "web_page_preview int(2)") 50 | private Integer webPagePreview; 51 | 52 | @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = true) 53 | @TableField(name = "zero_delay", sql = "zero_delay int(2)") 54 | private Integer zeroDelay; 55 | 56 | @DisplayConfigAnnotation(i18n = "capture_flag", set = false) 57 | @TableField(name = "capture_flag", sql = "capture_flag int(2)") 58 | private Integer captureFlag; 59 | 60 | @DisplayConfigAnnotation(i18n = "translation_language", set = false) 61 | @TableField(name = "translation_language", sql = "translation_language text") 62 | private String translationLanguage; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/code/eneity/PageEntity.java: -------------------------------------------------------------------------------- 1 | package code.eneity; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | @Data 9 | public class PageEntity { 10 | 11 | private int total; 12 | private int remainder; 13 | private int page; 14 | private int count; 15 | private int current; 16 | private boolean hasNext; 17 | private boolean hasPrev; 18 | private List list = new ArrayList<>(); 19 | 20 | private PageEntity() {} 21 | 22 | public PageEntity(int total, int page, int current) { 23 | this.total = total; 24 | this.remainder = total % page; 25 | this.page = page; 26 | this.count = remainder > 0 ? ((total / page) + 1) : (total / page); 27 | this.current = current; 28 | 29 | this.hasNext = current < this.count; 30 | this.hasPrev = current > 1; 31 | } 32 | 33 | public static PageEntity empty() { 34 | return new PageEntity(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/code/eneity/SentRecordTableEntity.java: -------------------------------------------------------------------------------- 1 | package code.eneity; 2 | 3 | import code.repository.base.TableEntity; 4 | import code.repository.base.TableField; 5 | import code.repository.base.TableName; 6 | import lombok.Data; 7 | 8 | @TableName(name = "sent_record_202312_table") 9 | @Data 10 | public class SentRecordTableEntity implements TableEntity { 11 | 12 | @TableField(name = "id", sql = "id varchar(100) primary key") 13 | private String id; 14 | 15 | @TableField(name = "chat_id", sql = "chat_id varchar(100)") 16 | private String chatId; 17 | 18 | @TableField(name = "name", sql = "name varchar(50)") 19 | private String name; 20 | 21 | @TableField(name = "uri", sql = "uri text") 22 | private String uri; 23 | 24 | @TableField(name = "create_time", sql = "create_time timestamp") 25 | private Long createTime; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/code/eneity/WebhookTableEntity.java: -------------------------------------------------------------------------------- 1 | package code.eneity; 2 | 3 | import code.repository.base.TableEntity; 4 | import code.repository.base.TableField; 5 | import code.repository.base.TableName; 6 | import lombok.Data; 7 | 8 | @TableName(name = "webhook_table") 9 | @Data 10 | public class WebhookTableEntity implements TableEntity { 11 | 12 | @TableField(name = "id", sql = "id varchar(100) primary key") 13 | private String id; 14 | 15 | @TableField(name = "chat_id", sql = "chat_id varchar(100)") 16 | private String chatId; 17 | 18 | @TableField(name = "settings_json", sql = "settings_json text") 19 | private String settingsJson; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/code/eneity/YesOrNoEnum.java: -------------------------------------------------------------------------------- 1 | package code.eneity; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.Optional; 6 | 7 | @Getter 8 | public enum YesOrNoEnum { 9 | 10 | Yes(1, true), 11 | No(0, 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 | 5 | @Getter 6 | public enum Command { 7 | 8 | Start("start"), 9 | Help("help"), 10 | Create("create"), 11 | List("list"), 12 | Get("get"), 13 | Update("update"), 14 | Test("test"), 15 | ForceRecord("fr"), 16 | On("on"), 17 | Off("off"), 18 | Delete("delete"), 19 | 20 | Admin("admin"), 21 | Exit("exit"), 22 | Language("language"), 23 | Restart("restart"), 24 | UpdateConfig("uc"), 25 | Upgrade("upgrade"), 26 | 27 | Webhook("webhook"), 28 | 29 | SetVerifySsl("rssb01"), 30 | SetExcludeKeywords("rssb02"), 31 | SetExcludeKeywordsRegex("rssb03"), 32 | SetIncludeKeywords("rssb04"), 33 | SetIncludeKeywordsRegex("rssb05"), 34 | SetCaptureFlag("rssb06"), 35 | SetTranslationLanguage("rssb07"), 36 | SetChatButtons("rssb11"), 37 | HideCopyrightTips("rssb10"), 38 | 39 | ; 40 | 41 | private String cmd; 42 | 43 | Command(String cmd) { 44 | this.cmd = cmd; 45 | } 46 | 47 | public static Command toCmd(String cmd) { 48 | for (Command value : Command.values()) { 49 | if (value.getCmd().equals(cmd)) { 50 | return value; 51 | } 52 | } 53 | return null; 54 | } 55 | 56 | public static boolean exist(String cmd) { 57 | return null != toCmd(cmd); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/code/handler/CommandsHandler.java: -------------------------------------------------------------------------------- 1 | package code.handler; 2 | 3 | import code.handler.message.CallbackBuilder; 4 | import code.handler.steps.StepsChatSession; 5 | import code.handler.steps.StepsChatSessionBuilder; 6 | import com.alibaba.fastjson2.JSON; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.telegram.telegrambots.bots.DefaultBotOptions; 10 | import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot; 11 | import org.telegram.telegrambots.meta.api.objects.CallbackQuery; 12 | import org.telegram.telegrambots.meta.api.objects.Message; 13 | import org.telegram.telegrambots.meta.api.objects.Update; 14 | 15 | import static code.Main.GlobalConfig; 16 | 17 | @Slf4j 18 | public class CommandsHandler extends TelegramLongPollingCommandBot { 19 | 20 | public CommandsHandler() { 21 | super(); 22 | } 23 | 24 | public CommandsHandler(DefaultBotOptions botOptions) { 25 | super(botOptions); 26 | } 27 | 28 | @Override 29 | public String getBotUsername() { 30 | return GlobalConfig.getBotName(); 31 | } 32 | 33 | @Override 34 | public String getBotToken() { 35 | return GlobalConfig.getBotToken(); 36 | } 37 | 38 | @Override 39 | public void processNonCommandUpdate(Update update) { 40 | if (GlobalConfig.getDebug()) { 41 | log.info(JSON.toJSONString(update)); 42 | } 43 | 44 | CallbackQuery callbackQuery = update.getCallbackQuery(); 45 | if (null != callbackQuery) { 46 | String data = callbackQuery.getData(); 47 | CallbackBuilder.CallbackData callbackData = CallbackBuilder.parseCallbackData(data); 48 | if (null == callbackData) { 49 | MessageHandle.sendMessage(String.valueOf(callbackQuery.getMessage().getChatId()), "Error...", false); 50 | return; 51 | } 52 | 53 | StepsChatSession session = StepsChatSessionBuilder 54 | .create(callbackQuery) 55 | .setText(callbackData.getText()) 56 | .build(); 57 | 58 | if (!session.getSessionId().equals(String.valueOf(callbackData.getId()))) { 59 | return; 60 | } 61 | 62 | if (StringUtils.isNotBlank(data)) { 63 | StepsCenter.cmdHandle(callbackData, session); 64 | return; 65 | } 66 | } 67 | 68 | Message message = update.getMessage(); 69 | if (null == message) { 70 | return; 71 | } 72 | String text = message.getText(); 73 | if (StringUtils.isNotEmpty(text)) { 74 | boolean handle = StepsCenter.cmdHandle(StepsChatSessionBuilder.create(message).build()); 75 | if (!handle) { 76 | StepsCenter.textHandle(StepsChatSessionBuilder.create(message).build()); 77 | } 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /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/MessageHandle.java: -------------------------------------------------------------------------------- 1 | package code.handler; 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.telegram.telegrambots.meta.api.methods.ParseMode; 8 | import org.telegram.telegrambots.meta.api.methods.send.SendMediaGroup; 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.media.InputMedia; 16 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup; 17 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; 18 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; 19 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow; 20 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 21 | 22 | import java.io.File; 23 | import java.io.InputStream; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | 28 | import static code.Main.Bot; 29 | 30 | @Slf4j 31 | public class MessageHandle { 32 | 33 | public enum MessageError { 34 | BotWasBlockedByTheUser, 35 | 36 | ; 37 | } 38 | 39 | @Data 40 | public static class MessageResponse { 41 | private boolean ok; 42 | private Message message; 43 | private MessageError messageError; 44 | } 45 | 46 | public static Message sendImage(String chatId, Integer replyToMessageId, String text, InputStream image) { 47 | SendPhoto sendPhoto = new SendPhoto(); 48 | sendPhoto.setChatId(chatId); 49 | sendPhoto.setReplyToMessageId(replyToMessageId); 50 | sendPhoto.setCaption(text); 51 | sendPhoto.setPhoto(new InputFile(image, "image.png")); 52 | 53 | try { 54 | return Bot.execute(sendPhoto); 55 | } catch (TelegramApiException e) { 56 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 57 | } 58 | return null; 59 | } 60 | 61 | public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image) { 62 | SendPhoto sendPhoto = new SendPhoto(); 63 | sendPhoto.setChatId(chatId); 64 | sendPhoto.setReplyToMessageId(replyToMessageId); 65 | sendPhoto.setCaption(text); 66 | sendPhoto.setPhoto(new InputFile(image)); 67 | 68 | try { 69 | return Bot.execute(sendPhoto); 70 | } catch (TelegramApiException e) { 71 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 72 | } 73 | return null; 74 | } 75 | 76 | public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image, List> keyboard) { 77 | SendPhoto sendPhoto = new SendPhoto(); 78 | sendPhoto.setChatId(chatId); 79 | sendPhoto.setReplyToMessageId(replyToMessageId); 80 | sendPhoto.setCaption(text); 81 | sendPhoto.setPhoto(new InputFile(image)); 82 | if (null != keyboard) { 83 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); 84 | inlineKeyboardMarkup.setKeyboard(keyboard); 85 | sendPhoto.setReplyMarkup(inlineKeyboardMarkup); 86 | } 87 | try { 88 | return Bot.execute(sendPhoto); 89 | } catch (TelegramApiException e) { 90 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 91 | } 92 | return null; 93 | } 94 | 95 | public static void updateInlineKeyboardList(Message message, String chatId, String text, List> keyboard) { 96 | EditMessageText editMessageText = new EditMessageText(); 97 | editMessageText.setChatId(chatId); 98 | editMessageText.setText(text); 99 | editMessageText.setDisableWebPagePreview(true); 100 | editMessageText.setMessageId(message.getMessageId()); 101 | 102 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); 103 | inlineKeyboardMarkup.setKeyboard(keyboard); 104 | editMessageText.setReplyMarkup(inlineKeyboardMarkup); 105 | try { 106 | Bot.execute(editMessageText); 107 | } catch (TelegramApiException e) { 108 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 109 | } 110 | } 111 | 112 | public static Message sendInlineKeyboardList(String chatId, String text, List> keyboard) { 113 | return sendInlineKeyboardList(chatId, null, text, keyboard); 114 | } 115 | 116 | public static Message sendInlineKeyboardList(String chatId, Integer replyToMessageId, String text, List> keyboard) { 117 | SendMessage message = new SendMessage(); 118 | message.setChatId(chatId); 119 | message.setText(text); 120 | message.setReplyToMessageId(replyToMessageId); 121 | message.setDisableWebPagePreview(true); 122 | 123 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); 124 | 125 | inlineKeyboardMarkup.setKeyboard(keyboard); 126 | message.setReplyMarkup(inlineKeyboardMarkup); 127 | 128 | try { 129 | return Bot.execute(message); 130 | } catch (TelegramApiException e) { 131 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 132 | } 133 | return null; 134 | } 135 | 136 | public static Message sendInlineKeyboard(String chatId, String text, InlineKeyboardButton... inlineKeyboardButtonList) { 137 | return sendInlineKeyboard(chatId, text, Arrays.asList(inlineKeyboardButtonList)); 138 | } 139 | 140 | public static Message sendInlineKeyboard(String chatId, String text, List inlineKeyboardButtonList) { 141 | SendMessage message = new SendMessage(); 142 | message.setChatId(chatId); 143 | message.setText(text); 144 | 145 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); 146 | List> keyboard = new ArrayList<>(); 147 | 148 | for (InlineKeyboardButton button : inlineKeyboardButtonList) { 149 | List list = new ArrayList<>(); 150 | list.add(button); 151 | keyboard.add(list); 152 | } 153 | 154 | inlineKeyboardMarkup.setKeyboard(keyboard); 155 | message.setReplyMarkup(inlineKeyboardMarkup); 156 | 157 | try { 158 | return Bot.execute(message); 159 | } catch (TelegramApiException e) { 160 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 161 | } 162 | return null; 163 | } 164 | 165 | public static Message sendCustomKeyboard(String chatId, String text, KeyboardRow row) { 166 | List list = new ArrayList<>(); 167 | list.add(row); 168 | 169 | return sendCustomKeyboard(chatId, text, list); 170 | } 171 | 172 | public static Message sendCustomKeyboard(String chatId, String text, List keyboard) { 173 | SendMessage message = new SendMessage(); 174 | message.setChatId(chatId); 175 | message.setText(text); 176 | 177 | ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup(); 178 | 179 | keyboardMarkup.setKeyboard(keyboard); 180 | message.setReplyMarkup(keyboardMarkup); 181 | 182 | try { 183 | return Bot.execute(message); 184 | } catch (TelegramApiException e) { 185 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 186 | } 187 | return null; 188 | } 189 | 190 | public static MessageResponse sendMsg(String chatId, String text, boolean webPagePreview) { 191 | MessageResponse response = new MessageResponse(); 192 | response.setOk(false); 193 | 194 | SendMessage sendMessage = new SendMessage(); 195 | sendMessage.setChatId(chatId); 196 | sendMessage.setText(text); 197 | sendMessage.setParseMode(ParseMode.HTML); 198 | if (!webPagePreview) { 199 | sendMessage.disableWebPagePreview(); 200 | } 201 | try { 202 | Message execute = Bot.execute(sendMessage); 203 | response.setOk(true); 204 | response.setMessage(execute); 205 | return response; 206 | } catch (Exception e) { 207 | String message = e.getMessage(); 208 | if (message.contains("bot was blocked by the user")) { 209 | response.setMessageError(MessageError.BotWasBlockedByTheUser); 210 | } else { 211 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage))); 212 | } 213 | } 214 | 215 | return response; 216 | } 217 | 218 | public static List sendMediaGroup(String chatId, List mediaList, boolean notification) { 219 | SendMediaGroup sendMediaGroup = new SendMediaGroup(); 220 | sendMediaGroup.setChatId(chatId); 221 | sendMediaGroup.setMedias(mediaList); 222 | if (!notification) { 223 | sendMediaGroup.disableNotification(); 224 | } 225 | try { 226 | List execute = Bot.execute(sendMediaGroup); 227 | return execute; 228 | } catch (Exception e) { 229 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 230 | } 231 | return null; 232 | } 233 | 234 | public static Message sendMessage(String chatId, String text, boolean webPagePreview) { 235 | return sendMessage(chatId, null, text, webPagePreview, true, null); 236 | } 237 | public static Message sendMessage(String chatId, String text, boolean webPagePreview, boolean notification) { 238 | return sendMessage(chatId, null, text, webPagePreview, notification, null); 239 | } 240 | public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview) { 241 | return sendMessage(chatId, replyToMessageId, text, webPagePreview, true, null); 242 | } 243 | public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview, boolean notification, List> buttons) { 244 | SendMessage sendMessage = new SendMessage(); 245 | sendMessage.setChatId(chatId); 246 | sendMessage.setReplyToMessageId(replyToMessageId); 247 | sendMessage.setText(text); 248 | sendMessage.setParseMode(ParseMode.HTML); 249 | if (!notification) { 250 | sendMessage.disableNotification(); 251 | } 252 | if (!webPagePreview) { 253 | sendMessage.disableWebPagePreview(); 254 | } 255 | if (null != buttons && !buttons.isEmpty()) { 256 | InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); 257 | inlineKeyboardMarkup.setKeyboard(buttons); 258 | sendMessage.setReplyMarkup(inlineKeyboardMarkup); 259 | } 260 | return sendMessage(sendMessage); 261 | } 262 | 263 | public static Message sendMessage(SendMessage sendMessage) { 264 | try { 265 | Message execute = Bot.execute(sendMessage); 266 | return execute; 267 | } catch (Exception e) { 268 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage))); 269 | } 270 | return null; 271 | } 272 | 273 | public static boolean editMessage(Message message, String text) { 274 | try { 275 | EditMessageText editMessageText = new EditMessageText(); 276 | editMessageText.setChatId(message.getChatId()); 277 | editMessageText.setMessageId(message.getMessageId()); 278 | editMessageText.setText(text); 279 | 280 | Bot.execute(editMessageText); 281 | return true; 282 | } catch (Exception e) { 283 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(message))); 284 | } 285 | return false; 286 | } 287 | 288 | public static boolean deleteMessage(Message message) { 289 | if (null == message) { 290 | return false; 291 | } 292 | 293 | DeleteMessage deleteMessage = new DeleteMessage(); 294 | deleteMessage.setChatId(message.getChatId()); 295 | deleteMessage.setMessageId(message.getMessageId()); 296 | 297 | try { 298 | Boolean execute = Bot.execute(deleteMessage); 299 | return null == execute ? false : execute; 300 | } catch (Exception e) { 301 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage))); 302 | } 303 | return false; 304 | } 305 | 306 | public static boolean deleteMessage(DeleteMessage deleteMessage) { 307 | try { 308 | Boolean execute = Bot.execute(deleteMessage); 309 | return null == execute ? false : execute; 310 | } catch (Exception e) { 311 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage))); 312 | } 313 | return false; 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/main/java/code/handler/StepsCenter.java: -------------------------------------------------------------------------------- 1 | package code.handler; 2 | 3 | import code.config.ExecutorsConfig; 4 | import code.handler.message.CallbackBuilder; 5 | import code.handler.steps.StepsChatSession; 6 | import code.handler.steps.StepsHandler; 7 | import code.handler.steps.StepsRegisterCenter; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.commons.lang3.StringUtils; 10 | 11 | import java.util.Collection; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | import static code.Main.GlobalConfig; 16 | 17 | @Slf4j 18 | public class StepsCenter { 19 | 20 | public static boolean cmdHandle(StepsChatSession session) { 21 | if (StringUtils.isNotBlank(session.getText()) && session.getText().startsWith("/")) { 22 | String s = StringUtils.remove(session.getText(), "/"); 23 | String[] split = s.split(" "); 24 | if (split.length > 0) { 25 | String cmd = split[0]; 26 | cmd = StringUtils.replace(cmd, "@" + GlobalConfig.getBotName(), ""); 27 | if (Command.exist(cmd)) { 28 | split[0] = cmd; 29 | session.setText(Stream.of(split).skip(1).collect(Collectors.joining(" "))); 30 | cmdHandle( 31 | Command.toCmd(cmd), 32 | false, 33 | session, 34 | null 35 | ); 36 | return true; 37 | } 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | public static void cmdHandle(CallbackBuilder.CallbackData callbackData, StepsChatSession stepsChatSession) { 44 | cmdHandle(callbackData.getCommand(), true, stepsChatSession, callbackData); 45 | } 46 | 47 | public static void cmdHandle(Command command, StepsChatSession stepsChatSession) { 48 | cmdHandle(command, false, stepsChatSession, null); 49 | } 50 | 51 | private static void cmdHandle(Command command, boolean isCall, StepsChatSession stepsChatSession, CallbackBuilder.CallbackData callbackData) { 52 | boolean permission = false; 53 | 54 | String botAdminId = GlobalConfig.getBotAdminId(); 55 | if (botAdminId.equals(stepsChatSession.getChatId()) || botAdminId.equals(stepsChatSession.getFromId())) { 56 | permission = true; 57 | } 58 | for (String s : GlobalConfig.getPermissionChatIdArray()) { 59 | if (s.equals(stepsChatSession.getChatId()) || s.equals(stepsChatSession.getFromId())) { 60 | permission = true; 61 | break; 62 | } 63 | } 64 | 65 | if (!permission) { 66 | MessageHandle.sendMessage(stepsChatSession.getChatId(), stepsChatSession.getReplyToMessageId(), "你没有使用权限, 不过你可以自己搭建一个\nhttps://github.com/kylelin1998/RssMonitorTelegramBot", false); 67 | return; 68 | } 69 | 70 | if (null != callbackData){ 71 | StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd()); 72 | if (!callbackData.isInit() && !handler.hasInit(stepsChatSession)) { 73 | return; 74 | } 75 | } 76 | 77 | ExecutorsConfig.submit(() -> { 78 | StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd()); 79 | if (null != handler.getInitStep() && (!handler.hasInit(stepsChatSession) || !isCall)) { 80 | handler.init(stepsChatSession); 81 | } else { 82 | handler.step(stepsChatSession); 83 | } 84 | }); 85 | } 86 | 87 | public static void textHandle(StepsChatSession stepsChatSession) { 88 | StepsHandler handler = StepsRegisterCenter.getPriority(stepsChatSession); 89 | if (null == handler) { 90 | return; 91 | } 92 | ExecutorsConfig.submit(() -> { 93 | handler.step(stepsChatSession); 94 | }); 95 | } 96 | 97 | public static void exit(StepsChatSession stepsChatSession) { 98 | Collection list = StepsRegisterCenter.getRegisterList(); 99 | for (StepsHandler handler : list) { 100 | handler.exit(stepsChatSession); 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/code/handler/message/CallbackBuilder.java: -------------------------------------------------------------------------------- 1 | package code.handler.message; 2 | 3 | import code.handler.Command; 4 | import code.handler.steps.StepsChatSession; 5 | import code.util.ExceptionUtil; 6 | import lombok.Data; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | @Slf4j 11 | public class CallbackBuilder { 12 | 13 | @Data 14 | public static class CallbackData { 15 | private boolean init; 16 | private String id; 17 | private Command command; 18 | private String text; 19 | } 20 | 21 | public static String buildCallbackData(boolean init, StepsChatSession session, Command command, String text) { 22 | StringBuilder builder = new StringBuilder(); 23 | builder.append("f[" + session.getSessionId() + "]"); 24 | builder.append(command.getCmd()); 25 | builder.append(" "); 26 | builder.append(init); 27 | builder.append(" "); 28 | builder.append(text); 29 | return builder.toString(); 30 | } 31 | public static CallbackData parseCallbackData(String callbackData) { 32 | try { 33 | CallbackData data = new CallbackData(); 34 | data.setId(StringUtils.substringBetween(callbackData, "f[", "]")); 35 | 36 | String s = StringUtils.replace(callbackData, "f[" + data.getId() + "]", ""); 37 | String[] arguments = s.split(" "); 38 | 39 | data.setCommand(Command.toCmd(arguments[0])); 40 | data.setInit(Boolean.valueOf(arguments[1])); 41 | data.setText(arguments.length > 2 ? arguments[2] : null); 42 | 43 | return data; 44 | } catch (Exception e) { 45 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 46 | } 47 | return null; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /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 code.eneity.PageEntity; 4 | import code.handler.Command; 5 | import code.handler.steps.StepsChatSession; 6 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class InlineKeyboardButtonListBuilder { 12 | private List> keyboard; 13 | private InlineKeyboardButtonListBuilder() {} 14 | 15 | public static InlineKeyboardButtonListBuilder create() { 16 | InlineKeyboardButtonListBuilder builder = new InlineKeyboardButtonListBuilder(); 17 | builder.keyboard = new ArrayList<>(); 18 | return builder; 19 | } 20 | 21 | public InlineKeyboardButtonListBuilder add(List inlineKeyboardButtonList) { 22 | this.keyboard.add(inlineKeyboardButtonList); 23 | return this; 24 | } 25 | 26 | public InlineKeyboardButtonListBuilder pagination(PageEntity entity, StepsChatSession session, Command command) { 27 | int count = entity.getCount(); 28 | if (count > 1) { 29 | InlineKeyboardButtonBuilder builder = InlineKeyboardButtonBuilder 30 | .create(); 31 | if (entity.isHasPrev()) { 32 | builder.add("⬅️", CallbackBuilder.buildCallbackData(true, session, command, "" + (entity.getCurrent() - 1))); 33 | } 34 | if (entity.isHasNext()) { 35 | builder.add("➡️", CallbackBuilder.buildCallbackData(true, session, command, "" + (entity.getCurrent() + 1))); 36 | } 37 | this.keyboard.add(builder.build()); 38 | } 39 | return this; 40 | } 41 | 42 | public List> build() { 43 | return keyboard; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /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 | 7 | @Data 8 | public class StepsChatSession { 9 | 10 | private String sessionId; 11 | private String chatId; 12 | private String fromId; 13 | 14 | private Integer replyToMessageId; 15 | 16 | private Message message; 17 | private CallbackQuery callbackQuery; 18 | private String text; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /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 | return new StepsChatSessionBuilder(session); 40 | } 41 | public StepsChatSessionBuilder setText(String text) { 42 | session.setText(text); 43 | return this; 44 | } 45 | 46 | public StepsChatSessionBuilder setText(String[] arguments) { 47 | session.setText(String.join(" ", arguments)); 48 | return this; 49 | } 50 | 51 | public StepsChatSession build() { 52 | return session; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /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 | StepsRegisterCenter.priority(stepsChatSession, this); 68 | String sessionId = stepsChatSession.getSessionId(); 69 | 70 | Boolean stepsWorkStatusBool = stepWorkStatus.get(sessionId); 71 | if (null != stepsWorkStatusBool && stepsWorkStatusBool) { 72 | return; 73 | } 74 | 75 | stepWorkStatus.put(sessionId, true); 76 | 77 | StepResult execute = null; 78 | try { 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(sessionId, 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 | String sessionId = stepsChatSession.getSessionId(); 118 | if (!hasInit(stepsChatSession) && !isInit()) { 119 | init(stepsChatSession); 120 | } 121 | 122 | Boolean stepsWorkStatusBool = stepWorkStatus.get(sessionId); 123 | if (null != stepsWorkStatusBool && stepsWorkStatusBool) { 124 | return StepExecuteResult.work(); 125 | } 126 | stepWorkStatus.put(sessionId, true); 127 | 128 | StepResult execute = null; 129 | try { 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(sessionId, 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 | for (Map.Entry> entry : map.entrySet()) { 34 | if (entry.getKey().equals(chatId)) { 35 | return Optional.ofNullable(entry.getValue()); 36 | } 37 | } 38 | if (isAll) { 39 | return Optional.ofNullable(map.get("all")); 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/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/handler/store/WebhookStore.java: -------------------------------------------------------------------------------- 1 | package code.handler.store; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import lombok.Data; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Optional; 10 | 11 | public class WebhookStore { 12 | 13 | @Data 14 | public static class Webhook { 15 | private boolean enable; 16 | private List list; 17 | } 18 | @Data 19 | public static class WebhookRequest { 20 | private String url; 21 | private String method; 22 | private Map headers; 23 | private Map body; 24 | } 25 | 26 | public static boolean verify(String webhookJson) { 27 | if (StringUtils.isBlank(webhookJson)) { 28 | return false; 29 | } 30 | 31 | Optional webhookOptional = get(webhookJson); 32 | if (!webhookOptional.isPresent()) { 33 | return false; 34 | } 35 | Webhook webhook = webhookOptional.get(); 36 | if (!webhook.isEnable()) { 37 | return true; 38 | } else { 39 | List list = webhook.getList(); 40 | if (null == list || list.isEmpty()) { 41 | return false; 42 | } 43 | } 44 | for (WebhookRequest request : webhook.list) { 45 | String url = request.getUrl(); 46 | if (!StringUtils.startsWith(url, "http")) { 47 | return false; 48 | } else if (StringUtils.isBlank(request.getMethod())) { 49 | return false; 50 | } 51 | } 52 | return true; 53 | } 54 | 55 | public static Optional get(String webhookJson) { 56 | try { 57 | Webhook webhook = JSON.parseObject(webhookJson, Webhook.class); 58 | return Optional.ofNullable(webhook); 59 | } catch (Exception e) { 60 | } 61 | return Optional.empty(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /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.base.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/MonitorTableRepository.java: -------------------------------------------------------------------------------- 1 | package code.repository; 2 | 3 | import code.config.Config; 4 | import code.eneity.MonitorTableEntity; 5 | import code.repository.base.TableRepository; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.util.List; 9 | 10 | @Slf4j 11 | public class MonitorTableRepository extends TableRepository { 12 | 13 | public MonitorTableRepository() { 14 | super(Config.DBPath, true); 15 | } 16 | 17 | public MonitorTableEntity selectOne(String id, String chatId) { 18 | MonitorTableEntity where = new MonitorTableEntity(); 19 | where.setId(id); 20 | where.setChatId(chatId); 21 | return super.selectOne(where); 22 | } 23 | 24 | public Integer selectCountByName(String chatId, String name) { 25 | MonitorTableEntity where = new MonitorTableEntity(); 26 | where.setName(name); 27 | where.setChatId(chatId); 28 | return super.selectCount(where); 29 | } 30 | public Boolean delete(String id) { 31 | MonitorTableEntity where = new MonitorTableEntity(); 32 | where.setId(id); 33 | return super.delete(where); 34 | } 35 | 36 | public Boolean update(MonitorTableEntity entity) { 37 | MonitorTableEntity where = new MonitorTableEntity(); 38 | where.setId(entity.getId()); 39 | return super.update(entity, where); 40 | } 41 | 42 | public List selectList(String chatId) { 43 | MonitorTableEntity where = new MonitorTableEntity(); 44 | where.setChatId(chatId); 45 | return super.selectList(where); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/code/repository/SentRecordTableRepository.java: -------------------------------------------------------------------------------- 1 | package code.repository; 2 | 3 | import code.config.Config; 4 | import code.eneity.SentRecordTableEntity; 5 | import code.repository.base.TableRepository; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | @Slf4j 9 | public class SentRecordTableRepository extends TableRepository { 10 | 11 | public SentRecordTableRepository() { 12 | super(Config.DBPath, false); 13 | } 14 | 15 | public void save(SentRecordTableEntity entity) { 16 | SentRecordTableEntity where = new SentRecordTableEntity(); 17 | where.setId(entity.getId()); 18 | Integer count = super.selectCount(where); 19 | if (count == 0) { 20 | super.insert(entity); 21 | } 22 | } 23 | 24 | public Boolean delete(String name, String chatId) { 25 | SentRecordTableEntity where = new SentRecordTableEntity(); 26 | where.setChatId(chatId); 27 | where.setName(name); 28 | return super.delete(where); 29 | } 30 | 31 | public Boolean exists(String name, String chatId) { 32 | return exists(null, name, chatId); 33 | } 34 | public Boolean exists(String uri, String name, String chatId) { 35 | SentRecordTableEntity where = new SentRecordTableEntity(); 36 | where.setUri(uri); 37 | where.setName(name); 38 | where.setChatId(chatId); 39 | Integer count = super.selectCount(where); 40 | if (null == count) { 41 | return null; 42 | } 43 | return count > 0; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/code/repository/WebhookTableRepository.java: -------------------------------------------------------------------------------- 1 | package code.repository; 2 | 3 | import code.config.Config; 4 | import code.eneity.WebhookTableEntity; 5 | import code.repository.base.TableRepository; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.util.List; 9 | 10 | @Slf4j 11 | public class WebhookTableRepository extends TableRepository { 12 | 13 | public WebhookTableRepository() { 14 | super(Config.DBPath, false); 15 | } 16 | 17 | public WebhookTableEntity selectOne(String chatId) { 18 | WebhookTableEntity where = new WebhookTableEntity(); 19 | where.setChatId(chatId); 20 | return super.selectOne(where); 21 | } 22 | 23 | public List selectList(String chatId) { 24 | WebhookTableEntity where = new WebhookTableEntity(); 25 | where.setChatId(chatId); 26 | return super.selectList(where); 27 | } 28 | 29 | public synchronized Boolean save(WebhookTableEntity entity) { 30 | WebhookTableEntity rsp = selectOne(entity.getChatId()); 31 | if (null == rsp) { 32 | return super.insert(entity); 33 | } else { 34 | WebhookTableEntity where = new WebhookTableEntity(); 35 | where.setChatId(entity.getChatId()); 36 | return super.update(entity, where); 37 | } 38 | } 39 | 40 | public Boolean delete(String chatId) { 41 | WebhookTableEntity where = new WebhookTableEntity(); 42 | where.setChatId(chatId); 43 | return super.delete(where); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/code/repository/base/SqlBuilder.java: -------------------------------------------------------------------------------- 1 | package code.repository.base; 2 | 3 | import code.util.ExceptionUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.StringJoiner; 10 | 11 | @Slf4j 12 | public class SqlBuilder { 13 | 14 | public static String getTableName(TableEntity tableEntity) { 15 | return getTableName(tableEntity.getClass()); 16 | } 17 | public static String getTableName(Class extends TableEntity> tableClass) { 18 | return tableClass.getAnnotation(TableName.class).name(); 19 | } 20 | 21 | public static List getNameList(Class extends TableEntity> tableClass) { 22 | ArrayList list = new ArrayList<>(); 23 | for (Field field : tableClass.getDeclaredFields()) { 24 | TableField tableField = field.getAnnotation(TableField.class); 25 | if (null == tableField) { 26 | continue; 27 | } 28 | list.add(tableField); 29 | } 30 | return list; 31 | } 32 | 33 | public static String buildCreateTableSql(Class extends TableEntity> tableClass) { 34 | String tableName = getTableName(tableClass); 35 | StringJoiner joiner = new StringJoiner(", ", "(", ")"); 36 | 37 | for (Field field : tableClass.getDeclaredFields()) { 38 | TableField tableField = field.getAnnotation(TableField.class); 39 | if (null == tableField) { 40 | continue; 41 | } 42 | joiner.add(tableField.sql()); 43 | } 44 | 45 | String sql = "create table if not exists " + tableName + " " + joiner; 46 | return sql; 47 | } 48 | 49 | public static String buildAlterTableAddColumnNameSql(String tableName, String column) { 50 | return String.format("ALTER TABLE %s ADD COLUMN %s", tableName, column); 51 | } 52 | 53 | private static String buildFieldValueSql(TableEntity tableEntity, String delimiter, String prefix, String suffix) { 54 | Class extends TableEntity> entityClass = tableEntity.getClass(); 55 | StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix); 56 | for (Field field : entityClass.getDeclaredFields()) { 57 | try { 58 | TableField tableField = field.getAnnotation(TableField.class); 59 | if (null == tableField) { 60 | continue; 61 | } 62 | field.setAccessible(true); 63 | Object o = field.get(tableEntity); 64 | if (null == o) { 65 | continue; 66 | } 67 | 68 | if (o instanceof String) { 69 | joiner.add(tableField.name() + "=" + "'" + o + "'"); 70 | } else { 71 | joiner.add(tableField.name() + "=" + o); 72 | } 73 | } catch (IllegalAccessException e) { 74 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 75 | } 76 | } 77 | return joiner.toString(); 78 | } 79 | 80 | public static String buildFieldSql(Class extends TableEntity> tableClass, String prefix, String suffix) { 81 | StringJoiner joiner = new StringJoiner(", ", prefix, suffix); 82 | 83 | for (Field field : tableClass.getDeclaredFields()) { 84 | TableField tableField = field.getAnnotation(TableField.class); 85 | if (null == tableField) { 86 | continue; 87 | } 88 | joiner.add(tableField.name()); 89 | } 90 | return joiner.toString(); 91 | } 92 | public static String buildFieldSql(TableEntity tableEntity, String prefix, String suffix) { 93 | StringJoiner joiner = new StringJoiner(", ", prefix, suffix); 94 | 95 | Class extends TableEntity> entityClass = tableEntity.getClass(); 96 | for (Field field : entityClass.getDeclaredFields()) { 97 | TableField tableField = field.getAnnotation(TableField.class); 98 | if (null == tableField) { 99 | continue; 100 | } 101 | joiner.add(tableField.name()); 102 | } 103 | return joiner.toString(); 104 | } 105 | 106 | public static String buildWhereSql(TableEntity tableEntity) { 107 | return buildFieldValueSql(tableEntity, " and ", " where ", ""); 108 | } 109 | 110 | public static String buildSelectSql(Class extends TableEntity> tableClass) { 111 | String tableName = getTableName(tableClass); 112 | 113 | String sql = "select" + buildFieldSql(tableClass, " ", " ") + "from " + tableName; 114 | return sql; 115 | } 116 | public static String buildSelectSql(TableEntity tableEntity) { 117 | String tableName = getTableName(tableEntity); 118 | 119 | String sql = "select" + buildFieldSql(tableEntity, " ", " ") + "from " + tableName + buildWhereSql(tableEntity); 120 | return sql; 121 | } 122 | 123 | public static String buildSelectSql(TableEntity tableEntity, int page, int current, String orderBy) { 124 | String tableName = getTableName(tableEntity); 125 | 126 | String sql = "select" + buildFieldSql(tableEntity, " ", " ") + "from " + tableName + buildWhereSql(tableEntity); 127 | sql += String.format(" %s limit %s, %s", orderBy, ((current * page) - page), page); 128 | return sql; 129 | } 130 | 131 | public static String buildDeleteSql(TableEntity where) { 132 | String tableName = getTableName(where); 133 | 134 | String sql = "delete from " + tableName + buildWhereSql(where); 135 | return sql; 136 | } 137 | 138 | public static String buildSelectCountSql(Class extends TableEntity> tableClass) { 139 | String tableName = getTableName(tableClass); 140 | 141 | String sql = "select count(*) as total from " + tableName; 142 | return sql; 143 | } 144 | 145 | public static String buildSelectCountSql(TableEntity where) { 146 | String tableName = getTableName(where); 147 | 148 | String sql = "select count(*) as total from " + tableName + buildWhereSql(where); 149 | return sql; 150 | } 151 | 152 | public static String buildInsertSql(TableEntity tableEntity, boolean isForceInsertNullValue) { 153 | String tableName = getTableName(tableEntity); 154 | 155 | Class extends TableEntity> entityClass = tableEntity.getClass(); 156 | StringJoiner prefixJoiner = new StringJoiner(", ", "(", ")"); 157 | StringJoiner suffixJoiner = new StringJoiner(", ", "values (", ")"); 158 | for (Field field : entityClass.getDeclaredFields()) { 159 | try { 160 | TableField tableField = field.getAnnotation(TableField.class); 161 | if (null == tableField) { 162 | continue; 163 | } 164 | field.setAccessible(true); 165 | Object o = field.get(tableEntity); 166 | if (null == o && !isForceInsertNullValue) { 167 | continue; 168 | } 169 | prefixJoiner.add(tableField.name()); 170 | if (null == o) { 171 | suffixJoiner.add(null); 172 | } 173 | else if (o instanceof String) { 174 | suffixJoiner.add("'" + o + "'"); 175 | } else { 176 | suffixJoiner.add(String.valueOf(o)); 177 | } 178 | } catch (IllegalAccessException e) { 179 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 180 | } 181 | } 182 | 183 | String sql = "insert into " + tableName + " " + prefixJoiner + " " + suffixJoiner; 184 | return sql; 185 | } 186 | 187 | public static String buildUpdateSql(TableEntity tableEntity, TableEntity where) { 188 | String tableName = getTableName(tableEntity); 189 | 190 | String sql = "update " + tableName + buildFieldValueSql(tableEntity, ", ", " set ", "") + buildWhereSql(where); 191 | return sql; 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/code/repository/base/TableEntity.java: -------------------------------------------------------------------------------- 1 | package code.repository.base; 2 | 3 | public interface TableEntity { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/code/repository/base/TableField.java: -------------------------------------------------------------------------------- 1 | package code.repository.base; 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/base/TableName.java: -------------------------------------------------------------------------------- 1 | package code.repository.base; 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/base/TableRepository.java: -------------------------------------------------------------------------------- 1 | package code.repository.base; 2 | 3 | import code.eneity.PageEntity; 4 | import code.util.ExceptionUtil; 5 | import code.util.SqliteUtil; 6 | import lombok.extern.slf4j.Slf4j; 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.DatabaseMetaData; 14 | import java.sql.ResultSet; 15 | import java.sql.SQLException; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | @Slf4j 20 | public abstract class TableRepository { 21 | 22 | private String tableName; 23 | private String dbPath; 24 | private boolean checkColumn = false; 25 | 26 | public TableRepository(String dbPath, String tableName) { 27 | this.tableName = tableName; 28 | this.dbPath = dbPath; 29 | createTableHandle(); 30 | } 31 | public TableRepository(String dbPath, String tableName, boolean checkColumn) { 32 | this.tableName = tableName; 33 | this.dbPath = dbPath; 34 | this.checkColumn = checkColumn; 35 | createTableHandle(); 36 | checkColumnHandle(); 37 | } 38 | public TableRepository(String dbPath, boolean checkColumn) { 39 | this.tableName = getT().getAnnotation(TableName.class).name(); 40 | this.dbPath = dbPath; 41 | this.checkColumn = checkColumn; 42 | createTableHandle(); 43 | checkColumnHandle(); 44 | } 45 | 46 | private void createTableHandle() { 47 | try { 48 | SqliteUtil.execute(dbPath, (statement) -> { 49 | String createTableSql = getCreateTableSql(); 50 | if (StringUtils.isBlank(createTableSql)) { 51 | createTableSql = SqlBuilder.buildCreateTableSql(getT()); 52 | } 53 | if (StringUtils.isNotBlank(createTableSql)) { 54 | statement.execute(createTableSql); 55 | } 56 | return null; 57 | }); 58 | } catch (Exception e) { 59 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 60 | } 61 | } 62 | private void checkColumnHandle() { 63 | try { 64 | if (this.checkColumn) { 65 | SqliteUtil.execute(dbPath, (statement) -> { 66 | Class t = getT(); 67 | DatabaseMetaData metaData = statement.getConnection().getMetaData(); 68 | List nameList = SqlBuilder.getNameList(t); 69 | for (TableField name : nameList) { 70 | ResultSet rs = metaData.getColumns(null, null, tableName, name.name()); 71 | if (!rs.next()) { 72 | String sql = SqlBuilder.buildAlterTableAddColumnNameSql(tableName, name.sql()); 73 | if (StringUtils.isNotBlank(sql)) { 74 | statement.execute(sql); 75 | } 76 | } 77 | } 78 | return null; 79 | }); 80 | } 81 | } catch (Exception e) { 82 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 83 | } 84 | } 85 | 86 | private Class getT() { 87 | ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass(); 88 | Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0]; 89 | String typeName = actualTypeArgument.getTypeName(); 90 | try { 91 | return (Class) Class.forName(typeName); 92 | } catch (ClassNotFoundException e) { 93 | throw new RuntimeException(e); 94 | } 95 | } 96 | 97 | public String getTableName() { 98 | return this.tableName; 99 | } 100 | public String sql(String sql) { 101 | return sql(sql, null); 102 | } 103 | public String sql(String sql, String[] args) { 104 | String table = StringUtils.replace(sql, "$table", this.getTableName()); 105 | return String.format(table, args); 106 | } 107 | 108 | public Object execute(SqliteUtil.SqliteInterface sqliteInterface) throws Exception { 109 | return SqliteUtil.execute(this.dbPath, sqliteInterface); 110 | } 111 | public Object executeWithTryCatch(SqliteUtil.SqliteInterface sqliteInterface) { 112 | try { 113 | return SqliteUtil.execute(this.dbPath, sqliteInterface); 114 | } catch (Exception e) { 115 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 116 | } 117 | return null; 118 | } 119 | public Boolean delete(T where) { 120 | try { 121 | return (boolean) execute((statement) -> { 122 | int update = statement.executeUpdate(SqlBuilder.buildDeleteSql(where)); 123 | return update > 0; 124 | }); 125 | } catch (Exception e) { 126 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 127 | } 128 | return null; 129 | } 130 | 131 | public Boolean update(T entity, T where) { 132 | try { 133 | return (boolean) execute((statement) -> { 134 | int update = statement.executeUpdate(SqlBuilder.buildUpdateSql(entity, where)); 135 | return update > 0; 136 | }); 137 | } catch (Exception e) { 138 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 139 | } 140 | return null; 141 | } 142 | 143 | public Boolean insert(T entity) { 144 | try { 145 | return (boolean) execute((statement) -> { 146 | int update = statement.executeUpdate(SqlBuilder.buildInsertSql(entity, false)); 147 | return update > 0; 148 | }); 149 | } catch (Exception e) { 150 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 151 | } 152 | return null; 153 | } 154 | 155 | public T getOne(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException { 156 | JDBC4ResultSet jdbc4ResultSet = (JDBC4ResultSet) resultSet; 157 | if (jdbc4ResultSet.emptyResultSet) { 158 | return null; 159 | } 160 | 161 | Class t = getT(); 162 | T instance = t.newInstance(); 163 | for (Field field : t.getDeclaredFields()) { 164 | TableField tableField = field.getAnnotation(TableField.class); 165 | if (null == tableField) { 166 | continue; 167 | } 168 | field.setAccessible(true); 169 | field.set(instance, resultSet.getObject(tableField.name())); 170 | } 171 | return instance; 172 | } 173 | 174 | public List getList(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException { 175 | List list = new ArrayList<>(); 176 | 177 | JDBC4ResultSet jdbc4ResultSet = (JDBC4ResultSet) resultSet; 178 | if (jdbc4ResultSet.emptyResultSet) { 179 | return list; 180 | } 181 | 182 | Class t = getT(); 183 | while (resultSet.next()) { 184 | T instance = t.newInstance(); 185 | for (Field field : t.getDeclaredFields()) { 186 | TableField tableField = field.getAnnotation(TableField.class); 187 | if (null == tableField) { 188 | continue; 189 | } 190 | field.setAccessible(true); 191 | field.set(instance, resultSet.getObject(tableField.name())); 192 | } 193 | list.add(instance); 194 | } 195 | return list; 196 | } 197 | 198 | public T selectOne(T where) { 199 | try { 200 | return (T) execute((statement) -> { 201 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where)); 202 | 203 | return getOne(resultSet); 204 | }); 205 | } catch (Exception e) { 206 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 207 | } 208 | return null; 209 | } 210 | 211 | public List selectList() { 212 | try { 213 | return (List) execute((statement) -> { 214 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(getT())); 215 | 216 | return getList(resultSet); 217 | }); 218 | } catch (Exception e) { 219 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 220 | } 221 | return new ArrayList<>(); 222 | } 223 | 224 | public List selectList(T where) { 225 | try { 226 | return (List) execute((statement) -> { 227 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where)); 228 | 229 | return getList(resultSet); 230 | }); 231 | } catch (Exception e) { 232 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 233 | } 234 | return new ArrayList<>(); 235 | } 236 | 237 | public PageEntity page(T where, int page, int current, String orderBy) { 238 | try { 239 | Integer count = this.selectCount(where); 240 | PageEntity entity = new PageEntity<>(count, page, current); 241 | List list = (List) execute((statement) -> { 242 | ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where, page, current, orderBy)); 243 | 244 | return getList(resultSet); 245 | }); 246 | 247 | entity.setList(list); 248 | return entity; 249 | } catch (Exception e) { 250 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 251 | } 252 | return PageEntity.empty(); 253 | } 254 | 255 | public Integer selectCount() { 256 | Integer total = null; 257 | try { 258 | total = (int) execute((statement) -> { 259 | ResultSet query = statement.executeQuery(SqlBuilder.buildSelectCountSql(getT())); 260 | return query.getInt("total"); 261 | }); 262 | } catch (Exception e) { 263 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 264 | } 265 | return total; 266 | } 267 | 268 | public Integer selectCount(T where) { 269 | Integer total = null; 270 | try { 271 | total = (int) execute((statement) -> { 272 | ResultSet query = statement.executeQuery(SqlBuilder.buildSelectCountSql(where)); 273 | return query.getInt("total"); 274 | }); 275 | } catch (Exception e) { 276 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 277 | } 278 | return total; 279 | } 280 | 281 | public Boolean exist(String field, String value) { 282 | Integer total = null; 283 | try { 284 | total = (int) execute((statement) -> { 285 | String sql = String.format("select count(*) as total from %s where %s = '%s'", this.tableName, field, value); 286 | ResultSet query = statement.executeQuery(sql); 287 | return query.getInt("total"); 288 | }); 289 | } catch (Exception e) { 290 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 291 | } 292 | if (null == total) { 293 | return null; 294 | } 295 | return total > 0; 296 | } 297 | 298 | public String getCreateTableSql() { 299 | return null; 300 | } 301 | 302 | } 303 | -------------------------------------------------------------------------------- /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/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) { 36 | try { 37 | GetRequest request = Unirest 38 | .get(url) 39 | .connectTimeout((int) TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS)) 40 | ; 41 | requestProxyConfig.viaProxy(request); 42 | HttpResponse response = request.asFile(file, StandardCopyOption.REPLACE_EXISTING); 43 | return response.getStatus() == 200; 44 | } catch (Exception e) { 45 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 46 | } 47 | return false; 48 | } 49 | 50 | public static boolean download(RequestProxyConfig requestProxyConfig, String url, String file, ProgressMonitor progressMonitor) { 51 | try { 52 | GetRequest request = Unirest 53 | .get(url) 54 | .downloadMonitor(progressMonitor) 55 | .connectTimeout((int) TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS)) 56 | ; 57 | requestProxyConfig.viaProxy(request); 58 | HttpResponse response = request.asFile(file, StandardCopyOption.REPLACE_EXISTING); 59 | return response.getStatus() == 200; 60 | } catch (Exception e) { 61 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 62 | } 63 | return false; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /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/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.util.Timeout; 11 | 12 | import java.nio.charset.Charset; 13 | import java.util.List; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | @Slf4j 17 | public class GithubUtil { 18 | 19 | private final static String ApiVersion = "2022-11-28"; 20 | 21 | @Data 22 | public static class LatestReleaseResponse { 23 | private boolean ok; 24 | private String htmlUrl; 25 | private String tagName; 26 | private String name; 27 | private String body; 28 | private List assets; 29 | } 30 | @Data 31 | public static class LatestReleaseAsset { 32 | private String name; 33 | private String browserDownloadUrl; 34 | } 35 | 36 | public static LatestReleaseResponse getLatestRelease(RequestProxyConfig requestProxyConfig, String owner, String repo) { 37 | String url = String.format("https://api.github.com/repos/%s/%s/releases/latest", owner, repo); 38 | try { 39 | Request request = Request 40 | .get(url) 41 | .setHeader("Accept", "application/vnd.github+json") 42 | .setHeader("X-GitHub-Api-Version", ApiVersion) 43 | .connectTimeout(Timeout.of(15, TimeUnit.SECONDS)) 44 | .responseTimeout(Timeout.of(60, TimeUnit.SECONDS)); 45 | requestProxyConfig.viaProxy(request); 46 | Response execute = request.execute(); 47 | 48 | LatestReleaseResponse releaseAssetResponse = JSON.parseObject(execute.returnContent().asString(Charset.forName("UTF-8")), LatestReleaseResponse.class, JSONReader.Feature.SupportSmartMatch); 49 | releaseAssetResponse.setOk(true); 50 | return releaseAssetResponse; 51 | } catch (Exception e) { 52 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 53 | } 54 | LatestReleaseResponse response = new LatestReleaseResponse(); 55 | response.setOk(false); 56 | return response; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /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/RssUtil.java: -------------------------------------------------------------------------------- 1 | package code.util; 2 | 3 | import code.config.RequestProxyConfig; 4 | import com.rometools.rome.feed.synd.SyndFeed; 5 | import com.rometools.rome.io.SyndFeedInput; 6 | import com.rometools.rome.io.XmlReader; 7 | import kong.unirest.GetRequest; 8 | import kong.unirest.HttpResponse; 9 | import kong.unirest.Unirest; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | import java.io.ByteArrayInputStream; 13 | import java.io.InputStream; 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | @Slf4j 18 | public class RssUtil { 19 | 20 | public static SyndFeed getFeed(RequestProxyConfig proxyConfig, String url) { 21 | try { 22 | GetRequest request = Unirest 23 | .get(url) 24 | .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) 25 | .socketTimeout((int) TimeUnit.MILLISECONDS.convert(40, TimeUnit.SECONDS)) 26 | ; 27 | proxyConfig.viaProxy(request); 28 | HttpResponse response = request.asString(); 29 | String body = response.getBody(); 30 | 31 | InputStream inputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); 32 | 33 | SyndFeed syndFeed = new SyndFeedInput().build(new XmlReader(inputStream)); 34 | return syndFeed; 35 | } catch (Exception e) { 36 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 37 | } 38 | return null; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/code/util/Snowflake.java: -------------------------------------------------------------------------------- 1 | package code.util; 2 | 3 | import java.net.NetworkInterface; 4 | import java.security.SecureRandom; 5 | import java.time.Instant; 6 | import java.util.Enumeration; 7 | 8 | /** 9 | * Distributed Sequence Generator. 10 | * Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010 11 | * 12 | * This class should be used as a Singleton. 13 | * Make sure that you create and reuse a Single instance of Snowflake per node in your distributed system cluster. 14 | */ 15 | public class Snowflake { 16 | 17 | private static final int UNUSED_BITS = 1; // Sign bit, Unused (always set to 0) 18 | private static final int EPOCH_BITS = 41; 19 | private static final int NODE_ID_BITS = 10; 20 | private static final int SEQUENCE_BITS = 12; 21 | 22 | private static final long maxNodeId = (1L << NODE_ID_BITS) - 1; 23 | private static final long maxSequence = (1L << SEQUENCE_BITS) - 1; 24 | 25 | // Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z) 26 | private static final long DEFAULT_CUSTOM_EPOCH = 1420070400000L; 27 | 28 | private final long nodeId; 29 | private final long customEpoch; 30 | 31 | private volatile long lastTimestamp = -1L; 32 | private volatile long sequence = 0L; 33 | 34 | // Create Snowflake with a nodeId and custom epoch 35 | public Snowflake(long nodeId, long customEpoch) { 36 | if(nodeId < 0 || nodeId > maxNodeId) { 37 | throw new IllegalArgumentException(String.format("NodeId must be between %d and %d", 0, maxNodeId)); 38 | } 39 | this.nodeId = nodeId; 40 | this.customEpoch = customEpoch; 41 | } 42 | 43 | // Create Snowflake with a nodeId 44 | public Snowflake(long nodeId) { 45 | this(nodeId, DEFAULT_CUSTOM_EPOCH); 46 | } 47 | 48 | // Let Snowflake generate a nodeId 49 | public Snowflake() { 50 | this.nodeId = createNodeId(); 51 | this.customEpoch = DEFAULT_CUSTOM_EPOCH; 52 | } 53 | 54 | public synchronized String nextIdToStr() { 55 | return String.valueOf(nextId()); 56 | } 57 | 58 | public synchronized long nextId() { 59 | long currentTimestamp = timestamp(); 60 | 61 | if(currentTimestamp < lastTimestamp) { 62 | throw new IllegalStateException("Invalid System Clock!"); 63 | } 64 | 65 | if (currentTimestamp == lastTimestamp) { 66 | sequence = (sequence + 1) & maxSequence; 67 | if(sequence == 0) { 68 | // Sequence Exhausted, wait till next millisecond. 69 | currentTimestamp = waitNextMillis(currentTimestamp); 70 | } 71 | } else { 72 | // reset sequence to start with zero for the next millisecond 73 | sequence = 0; 74 | } 75 | 76 | lastTimestamp = currentTimestamp; 77 | 78 | long id = currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS) 79 | | (nodeId << SEQUENCE_BITS) 80 | | sequence; 81 | 82 | return id; 83 | } 84 | 85 | 86 | // Get current timestamp in milliseconds, adjust for the custom epoch. 87 | private long timestamp() { 88 | return Instant.now().toEpochMilli() - customEpoch; 89 | } 90 | 91 | // Block and wait till next millisecond 92 | private long waitNextMillis(long currentTimestamp) { 93 | while (currentTimestamp == lastTimestamp) { 94 | currentTimestamp = timestamp(); 95 | } 96 | return currentTimestamp; 97 | } 98 | 99 | private long createNodeId() { 100 | long nodeId; 101 | try { 102 | StringBuilder sb = new StringBuilder(); 103 | Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); 104 | while (networkInterfaces.hasMoreElements()) { 105 | NetworkInterface networkInterface = networkInterfaces.nextElement(); 106 | byte[] mac = networkInterface.getHardwareAddress(); 107 | if (mac != null) { 108 | for(byte macPort: mac) { 109 | sb.append(String.format("%02X", macPort)); 110 | } 111 | } 112 | } 113 | nodeId = sb.toString().hashCode(); 114 | } catch (Exception ex) { 115 | nodeId = (new SecureRandom().nextInt()); 116 | } 117 | nodeId = nodeId & maxNodeId; 118 | return nodeId; 119 | } 120 | 121 | public long[] parse(long id) { 122 | long maskNodeId = ((1L << NODE_ID_BITS) - 1) << SEQUENCE_BITS; 123 | long maskSequence = (1L << SEQUENCE_BITS) - 1; 124 | 125 | long timestamp = (id >> (NODE_ID_BITS + SEQUENCE_BITS)) + customEpoch; 126 | long nodeId = (id & maskNodeId) >> SEQUENCE_BITS; 127 | long sequence = id & maskSequence; 128 | 129 | return new long[]{timestamp, nodeId, sequence}; 130 | } 131 | 132 | @Override 133 | public String toString() { 134 | return "Snowflake Settings [EPOCH_BITS=" + EPOCH_BITS + ", NODE_ID_BITS=" + NODE_ID_BITS 135 | + ", SEQUENCE_BITS=" + SEQUENCE_BITS + ", CUSTOM_EPOCH=" + customEpoch 136 | + ", NodeId=" + nodeId + "]"; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /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/TelegraphUtil.java: -------------------------------------------------------------------------------- 1 | package code.util; 2 | 3 | import code.config.RequestProxyConfig; 4 | import com.alibaba.fastjson2.JSON; 5 | import com.alibaba.fastjson2.JSONObject; 6 | import kong.unirest.ContentType; 7 | import kong.unirest.HttpResponse; 8 | import kong.unirest.MultipartBody; 9 | import kong.unirest.Unirest; 10 | import lombok.Data; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.commons.lang3.RandomUtils; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.jsoup.Jsoup; 15 | import org.jsoup.nodes.*; 16 | 17 | import java.io.ByteArrayInputStream; 18 | import java.nio.charset.StandardCharsets; 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | @Slf4j 26 | public class TelegraphUtil { 27 | 28 | @Data 29 | private static class TelegraphNode { 30 | private String tag; 31 | private Map attrs; 32 | private List children; 33 | } 34 | 35 | @Data 36 | public static class SaveResponse { 37 | private boolean ok; 38 | private String pageId; 39 | private String url; 40 | } 41 | 42 | public static SaveResponse save(RequestProxyConfig proxyConfig, String title, String author, String html, String appendHtml) { 43 | SaveResponse saveResponse = new SaveResponse(); 44 | saveResponse.setOk(false); 45 | try { 46 | String telegraphContent = getTelegraphContent(html, appendHtml); 47 | if (StringUtils.isBlank(telegraphContent)) { 48 | log.warn("Telegraph content is null, title: {}", title); 49 | return saveResponse; 50 | } 51 | 52 | for (int i = 0; i < 3; i++) { 53 | MultipartBody multipartBody = Unirest.post("https://edit.telegra.ph/save") 54 | .header("origin", " https://telegra.ph") 55 | .header("referer", " https://telegra.ph/") 56 | .header("user-agent", " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36") 57 | .field("Data", new ByteArrayInputStream(telegraphContent.getBytes(StandardCharsets.UTF_8)), ContentType.create("text/html", StandardCharsets.UTF_8), "content.html") 58 | .field("title", title) 59 | .field("author", StringUtils.isBlank(author) ? "Unknown" : author) 60 | .field("page_id", "0") 61 | .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) 62 | .socketTimeout((int) TimeUnit.MILLISECONDS.convert(40, TimeUnit.SECONDS)) 63 | ; 64 | proxyConfig.viaProxy(multipartBody); 65 | HttpResponse response = multipartBody.asString(); 66 | 67 | String body = response.getBody(); 68 | log.info("title: {}, response: {}", title, body); 69 | JSONObject jsonObject = JSON.parseObject(body); 70 | String path = jsonObject.getString("path"); 71 | if (StringUtils.isNotBlank(path)) { 72 | saveResponse.setOk(true); 73 | saveResponse.setPageId(path); 74 | saveResponse.setUrl("https://telegra.ph/" + path); 75 | return saveResponse; 76 | } 77 | String errorDetails = jsonObject.getString("error_details"); 78 | if (StringUtils.isNotBlank(errorDetails) && "PATH_NUM_NOT_FOUND".equals(errorDetails)) { 79 | title = title + "-r" + RandomUtils.nextInt(1, 999); 80 | } 81 | } 82 | } catch (Exception e) { 83 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 84 | } 85 | return saveResponse; 86 | } 87 | 88 | private static String getTelegraphContent(String html, String appendHtml) { 89 | Document doc = Jsoup.parse(html); 90 | 91 | Element body = doc.body(); 92 | if (StringUtils.isNotBlank(appendHtml)) { 93 | body.append(appendHtml); 94 | } 95 | 96 | TelegraphNode telegraphNode = (TelegraphNode) buildNote(body); 97 | return JSON.toJSONString(telegraphNode.getChildren()); 98 | } 99 | 100 | private static Object buildNote(Node node) { 101 | if (node instanceof TextNode) { 102 | return ((TextNode) node).text(); 103 | } 104 | if (!(node instanceof Element)) { 105 | return false; 106 | } 107 | 108 | TelegraphNode telegraphNode = new TelegraphNode(); 109 | 110 | String nodeName = node.nodeName(); 111 | if ("figure".equals(nodeName) || "div".equals(nodeName)) { 112 | nodeName = "p"; 113 | } 114 | if ("td".equals(nodeName)) { 115 | String aClass = node.attr("class"); 116 | if ("gutter".equals(aClass)) { 117 | return false; 118 | } 119 | } 120 | 121 | telegraphNode.setTag(nodeName); 122 | HashMap attributeMap = new HashMap<>(); 123 | for (Attribute attribute : node.attributes()) { 124 | String key = attribute.getKey(); 125 | if ("href".equals(key) || "src".equals(key)) { 126 | attributeMap.put(key, attribute.getValue()); 127 | } 128 | } 129 | telegraphNode.setAttrs(attributeMap); 130 | if (node.childNodeSize() > 0) { 131 | telegraphNode.setChildren(new ArrayList<>()); 132 | for (Node childNode : node.childNodes()) { 133 | telegraphNode.getChildren().add(buildNote(childNode)); 134 | } 135 | } 136 | 137 | return telegraphNode; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/code/util/translate/MicrosoftTranslateHandle.java: -------------------------------------------------------------------------------- 1 | package code.util.translate; 2 | 3 | import code.util.translate.base.TranslateAPI; 4 | import code.util.translate.base.TranslateAuth; 5 | import com.alibaba.fastjson2.JSON; 6 | import com.alibaba.fastjson2.JSONArray; 7 | import com.alibaba.fastjson2.JSONObject; 8 | import kong.unirest.HttpResponse; 9 | import kong.unirest.Unirest; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.codec.digest.DigestUtils; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import java.io.UnsupportedEncodingException; 15 | import java.net.URLEncoder; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import java.util.UUID; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | import static code.Main.GlobalConfig; 22 | 23 | @Slf4j 24 | public class MicrosoftTranslateHandle implements TranslateAPI { 25 | @Override 26 | public boolean hasAuth() { 27 | return false; 28 | } 29 | 30 | @Override 31 | public TranslateAuth auth() { 32 | return null; 33 | } 34 | 35 | @Override 36 | public String translate(String text, String from, String to) { 37 | String url = "https://api.microsofttranslator.com/v2/Http.svc/Translate?appId=A4D660A48A6A97CCA791C34935E4C02BBB1BEC1C&from=%s&to=%s&text=%s"; 38 | url = String.format(url, "auto".equals(from) ? "" : from, to, encode(text)); 39 | 40 | HttpResponse response = Unirest 41 | .get(url) 42 | .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) 43 | .socketTimeout((int) TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS)) 44 | .asString(); 45 | if (response.getStatus() == 200) { 46 | String body = response.getBody(); 47 | String string = StringUtils.substringBetween(body,"", ""); 48 | if (StringUtils.isNotBlank(string)) { 49 | return string; 50 | } 51 | } 52 | return null; 53 | } 54 | 55 | private String encode(String text) { 56 | try { 57 | return URLEncoder.encode(text, "UTF-8"); 58 | } catch (UnsupportedEncodingException e) { 59 | throw new RuntimeException(e); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/code/util/translate/Translate.java: -------------------------------------------------------------------------------- 1 | package code.util.translate; 2 | 3 | import code.util.ExceptionUtil; 4 | import code.util.translate.base.TranslateAPI; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @Slf4j 12 | public class Translate { 13 | 14 | private static List handleList = new ArrayList<>(); 15 | static { 16 | handleList.add(new YoudaoTranslateHandle()); 17 | handleList.add(new MicrosoftTranslateHandle()); 18 | } 19 | 20 | public static List translateAll(String text, String from, String to) { 21 | ArrayList list = new ArrayList<>(); 22 | for (TranslateAPI api : handleList) { 23 | try { 24 | if (api.hasAuth()) { 25 | if (null == api.auth()) { 26 | continue; 27 | } 28 | } 29 | String translate = api.translate(text, from, to); 30 | if (StringUtils.isNotBlank(translate)) { 31 | list.add(translate); 32 | } 33 | } catch (Exception e) { 34 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 35 | } 36 | } 37 | return list; 38 | } 39 | 40 | public static String translate(String text, String from, String to) { 41 | for (TranslateAPI api : handleList) { 42 | try { 43 | if (api.hasAuth()) { 44 | if (null == api.auth()) { 45 | continue; 46 | } 47 | } 48 | String translate = api.translate(text, from, to); 49 | if (StringUtils.isNotBlank(translate)) { 50 | return translate; 51 | } 52 | } catch (Exception e) { 53 | log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); 54 | } 55 | } 56 | return ""; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/code/util/translate/YoudaoTranslateHandle.java: -------------------------------------------------------------------------------- 1 | package code.util.translate; 2 | 3 | import code.util.translate.base.TranslateAPI; 4 | import code.util.translate.base.TranslateAuth; 5 | import com.alibaba.fastjson2.JSON; 6 | import com.alibaba.fastjson2.JSONArray; 7 | import com.alibaba.fastjson2.JSONObject; 8 | import kong.unirest.HttpResponse; 9 | import kong.unirest.Unirest; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.codec.digest.DigestUtils; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.UUID; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | import static code.Main.GlobalConfig; 20 | 21 | @Slf4j 22 | public class YoudaoTranslateHandle implements TranslateAPI { 23 | @Override 24 | public boolean hasAuth() { 25 | return true; 26 | } 27 | @Override 28 | public TranslateAuth auth() { 29 | String translateYoudaoKey = GlobalConfig.getTranslateYoudaoKey(); 30 | String translateYoudaoSecret = GlobalConfig.getTranslateYoudaoSecret(); 31 | if (StringUtils.isNotBlank(translateYoudaoKey) && StringUtils.isNotBlank(translateYoudaoSecret)) { 32 | TranslateAuth translateAuth = new TranslateAuth(); 33 | translateAuth.setKey(translateYoudaoKey); 34 | translateAuth.setSecret(translateYoudaoSecret); 35 | return translateAuth; 36 | } 37 | return null; 38 | } 39 | 40 | @Override 41 | public String translate(String text, String from, String to) { 42 | TranslateAuth auth = auth(); 43 | String salt = UUID.randomUUID().toString(); 44 | Long curtime = System.currentTimeMillis() / 1000; 45 | String raw = auth.getKey() + getInput(text) + salt + curtime + auth.getSecret(); 46 | String sign = DigestUtils.sha256Hex(raw); 47 | 48 | Map map = new HashMap(); 49 | map.put("q", text); 50 | map.put("from", from); 51 | map.put("to", to); 52 | map.put("appKey", auth.getKey()); 53 | map.put("salt", salt); 54 | map.put("sign", sign); 55 | map.put("signType", "v3"); 56 | map.put("curtime", curtime); 57 | HttpResponse response = Unirest 58 | .post("https://openapi.youdao.com/api") 59 | .fields(map) 60 | .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) 61 | .socketTimeout((int) TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS)) 62 | .asString(); 63 | if (response.getStatus() == 200) { 64 | String body = response.getBody(); 65 | JSONObject object = JSON.parseObject(body); 66 | if ("0".equals(object.getString("errorCode"))) { 67 | JSONArray translation = object.getJSONArray("translation"); 68 | if (null != translation && translation.size() > 0) { 69 | return translation.getString(0); 70 | } 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | private static String getInput(String input) { 77 | if (input == null) { 78 | return null; 79 | } 80 | String result; 81 | int len = input.length(); 82 | if (len <= 20) { 83 | result = input; 84 | } else { 85 | String startStr = input.substring(0, 10); 86 | String endStr = input.substring(len - 10, len); 87 | result = startStr + len + endStr; 88 | } 89 | return result; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/code/util/translate/base/TranslateAPI.java: -------------------------------------------------------------------------------- 1 | package code.util.translate.base; 2 | 3 | public interface TranslateAPI { 4 | boolean hasAuth(); 5 | TranslateAuth auth(); 6 | String translate(String text, String from, String to); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/code/util/translate/base/TranslateAuth.java: -------------------------------------------------------------------------------- 1 | package code.util.translate.base; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TranslateAuth { 7 | private String key; 8 | private String secret; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/code/config/telegraph.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 本文由 5 | RssMonitorTelegramBot 6 | 自动生成, 版权归源站点所有。 7 | 8 | 9 | 10 | 原文: ${title} 11 | 12 | 13 | 14 | This article was generated by 15 | RssMonitorTelegramBot All copyright rights belong to the source site. 18 | 19 | 20 | 21 | Original article: ${title} 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/resources/code/config/webhook.json: -------------------------------------------------------------------------------- 1 | { 2 | "enable":false, 3 | "list":[ 4 | { 5 | "url":"https://open.larksuite.com/open-apis/bot/v2/hook/...", 6 | "method":"POST", 7 | "headers":{ 8 | "User-Agent":"PostmanRuntime/7.31.3", 9 | "Content-Type":"application/json" 10 | }, 11 | "body":{ 12 | "msg_type":"text", 13 | "content":{ 14 | "text":"哈喽 ${text}" 15 | } 16 | } 17 | }, 18 | { 19 | "url":"https://open.larksuite.com/open-apis/bot/v2/hook/...", 20 | "method":"POST", 21 | "headers":{ 22 | "User-Agent":"PostmanRuntime/7.31.3", 23 | "Content-Type":"application/json" 24 | }, 25 | "body":{ 26 | "msg_type":"text", 27 | "content":{ 28 | "text":"Hello ${text}" 29 | } 30 | } 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /src/main/resources/i18n/i18n_en.properties: -------------------------------------------------------------------------------- 1 | bot_start_succeed=Bot program starting succeeded\uFF01 2 | help_text=Welcome to using this bot! 3 | 4 | invalid_command=Invalid command, you are not admin 5 | monitor_list=Name: %s, Open status: %s 6 | nothing_here=Nothing here. You can use /create command to create a new grab plan. 7 | on_monitor=Saving succeeded. The grab plan is set to "on" status and is ready to work. 8 | off_monitor=Saving succeeded. The grab plan is set to "off" status. 9 | on=On 10 | off=Off 11 | test=Test 12 | update=Update 13 | not_found=Not found. 14 | unknown_error=Program has occurred an unknown error. 15 | nothing_at_all=Nothing at all 16 | cancel_succeeded=Canceling succeeded. 17 | confirm=Confirm 18 | cancel=Cancel 19 | delete=Delete 20 | exit_succeeded=Exiting succeeded. 21 | getting=Getting... 22 | downloading=Downloading... 23 | force_record=Force record 24 | 25 | language_list=Please choose a language that you understand from the options below. 26 | change_language_finish=Your language setting has changed. 27 | 28 | monitor_exists=Your name of grab plan already exists, please input a new name and try again. 29 | 30 | create_name_too_long=Name is too long. Please send me a name that is 50 characters or less. 31 | create_monitor_1=Please send me a new grab plan name, I will create it. 32 | create_monitor_2=Grab plan: %s, has created. 33 | create_monitor_3=Please continue to send me a RSS feed URL. 34 | create_monitor_4=Verifying the RSS feed URL. Please be patient while we proceed. 35 | create_monitor_5=For now, program only supports XML RSS content. Please send me a new RSS feed URL again. 36 | create_monitor_6=RSS feed URL: %s. 37 | create_monitor_7=Please continue sending me the template content...\n\n\ 38 | Template Variable Explanation:\n\ 39 | You can customize the notification message text with the following variables\n\ 40 | ${link} -> Article URL\n\ 41 | ${title} -> Article title\n\ 42 | ${author} -> Article author\n\ 43 | ${telegraph} -> Telegraph article URL\n\ 44 | ${description} -> Article description\n\ 45 | ${translate|zh|title} -> Translate the title into Chinese\n\ 46 | ${translate|zh|description} -> Translate the description into Chinese\n\ 47 | ${translate|en|title} -> Translate the title into English\n\ 48 | ${translate|en|description} -> Translate the description into English\n\ 49 | You can modify the code in between to translate whatever you want... and so on...\n 50 | create_monitor_8=Message template content: \n%s. 51 | create_monitor_finish=Creating succeeded. Requesting the RSS feed URL. Please be patient while we proceed. 52 | 53 | test_monitor=Grab plan: %s, not found, please send me again. 54 | force_record_succeeded=Force record succeeded. 55 | 56 | update_monitor_1=Please continue to send me the field name you want to set. 57 | update_monitor_2=Filed name: %s not found. Please resend it to me. 58 | update_monitor_3=Field name: %s 59 | update_monitor_4=Please continue to send me the value of the field name. 60 | update_field_error=Apologies, an error occurred while setting the field value. 61 | update_monitor_finish=Updating succeeded. 62 | 63 | delete_monitor_confirm=Do you want to delete this monitor? 64 | delete_monitor_finish=Deleting succeeded. 65 | 66 | config_display_on=On 67 | config_display_web_page_preview=Web Page Preview 68 | config_display_notification=Notification 69 | config_display_zero_delay=Zero Delay 70 | config_display_url=RSS feed Url 71 | config_display_template=Template Content 72 | config_display_chat_id_array=Send Target List(Chat Id) 73 | config_display_file_basename=Name 74 | 75 | you_are_not_an_admin=Invalid use of this command because you are not an admin. 76 | are_you_sure_to_restart_right_now=Are you sure to restart the bot right now? 77 | restarting=Restarting... 78 | getting_update_data=Getting update data... 79 | are_you_sure_to_upgrade_this_bot_right_now=Are you sure to upgrade this bot right now ? 80 | target_version=Target version 81 | current_version=Current version 82 | update_logs=Update logs 83 | updating=Updating 84 | downloaded=Downloaded: %s, Total size: %s 85 | 86 | are_you_sure_to_update_the_config=Are you sure to update the config ? 87 | please_send_me_config_content=Please send me the config content you want to set. 88 | update_config_fail=Updating the config failed. This is because the config content does not meet certain standards. 89 | 90 | update_succeeded=Updating succeeded. 91 | update_failed=Updating failed. 92 | 93 | set_chat_buttons=Set Chat Buttons 94 | update_config=Update Config 95 | restart=Restart 96 | upgrade=Upgrade 97 | 98 | 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\ 99 | Sample\uFF1A\n\ 100 | \n\ 101 | \n\ 102 | \n\ 103 | ---\n\ 104 | \n\ 105 | \n\n\n\ 106 | All Sample:\n\ 107 | all\n\ 108 | \n\ 109 | 110 | 111 | format_error=Format error. 112 | 113 | setting_webhook=Setting Webhook 114 | hide_copyright_tips=Hide Copyright 115 | are_you_sure_you_want_to_hide_copyright_tips=Are you sure you want to hide the copyright tips? 116 | current_setting=Current Setting 117 | please_send_me_webhook_settings=Please send me the webhook settings you want to set. 118 | back=Back 119 | refresh=Refresh 120 | exclude_keywords=Exclude Keywords 121 | please_send_me_exclude_keywords=Please send me the keywords you want to exclude. Text containing these specified keywords will not be pushed. Please provide one keyword per line, separating them with a line break. If you don't want to set them, you can send me -1. 122 | exclude_keywords_regex=Exclude Keywords Regex 123 | please_send_me_exclude_keywords_regex=Please send me the regular expressions you want to exclude. Each regular expression should be on a separate line, and they should be separated by line breaks. If you don't want to set any exclusions, you can simply send me -1. 124 | verify_ssl=Verify Request SSL Certificate 125 | are_you_sure_you_want_to_set_verify_ssl=Are you confirming the setting to validate SSL certificates? Enabling it means forcing the validation of the requested SSL certificates' validity, while disabling it means not performing the validation. 126 | enable=Enable 127 | disable=Disable 128 | need_to_restart_bot=You'll need to manually restart bot for the configuration to take effect 129 | include_keywords=Include Keywords 130 | include_keywords_regex=Include Keywords Regex 131 | please_send_me_include_keywords=Please send me the desired keywords for the setup. Push notifications will be sent only if the specified keywords are detected in the text. Please provide one keyword per line, separated by line breaks. If you do not wish to set them, you can send -1 132 | please_send_me_include_keywords_regex=Please send me the desired regular expressions for detecting specific patterns in the text in order to trigger push notifications. Please provide one regular expression per line, separated by line breaks. If you do not wish to set them, you can send -1 133 | capture_flag=Capture 134 | translation_language=Translation language 135 | set_capture_flag=Set capture 136 | set_capture_flag_on_note=Resource retrieval has been initiated. Note: When there are image resources in the RSS content, they will be automatically retrieved and sent in TG. 137 | set_capture_flag_off_note=Resource retrieval has been disabled. Note: When there are image resources in the RSS content, they will be automatically retrieved and sent in TG. -------------------------------------------------------------------------------- /src/main/resources/i18n/i18n_zh_CN.properties: -------------------------------------------------------------------------------- 1 | refresh=刷新 2 | bot_start_succeed=机器人启动成功! 3 | help_text=欢迎使用本机器人! 4 | 5 | invalid_command=无效命令, 您不是管理员无法操作此命令 6 | monitor_list=计划名称: %s, 是否开启: %s 7 | nothing_here=暂时还没有抓取计划, 快用 /create 命令创建一个计划吧! 8 | on_monitor=已开启计划 9 | off_monitor=已关闭计划 10 | on=开 11 | off=关 12 | test=测试 13 | update=更新配置 14 | not_found=未找到 15 | unknown_error=系统未知错误 16 | nothing_at_all=啥都没有 17 | cancel_succeeded=撤销操作成功 18 | confirm=确认 19 | cancel=撤销 20 | delete=删除 21 | exit_succeeded=退出成功 22 | getting=正在获取... 23 | downloading=下载中... 24 | force_record=强制同步最新记录 25 | 26 | language_list=请从下方选择一种语言 27 | change_language_finish=更换语言成功 28 | 29 | monitor_exists=计划名称已存在, 请再更换一个 30 | 31 | create_name_too_long=名称太长, 请发送50个字符以下的名称 32 | create_monitor_1=发给我抓取计划的名称, 我会进行创建 33 | create_monitor_2=抓取计划名称 %s, 创建中... 34 | create_monitor_3=请继续发送给我你想要抓取的rss url链接 35 | create_monitor_4=正在验证rss url的合法性...请耐心等待... 36 | create_monitor_5=仅支持rss xml格式, 请再次将rss的新url发送给我 37 | create_monitor_6=rss url: %s. 38 | create_monitor_7=请继续给我发送模板内容...\n\n\ 39 | 模版支持变量说明:\n\ 40 | 支持自定义发送通知消息文本\n\ 41 | ${link} -> 文章地址\n\ 42 | ${title} -> 文章标题\n\ 43 | ${author} -> 文章作者\n\ 44 | ${telegraph} -> telegraph文章地址\n\ 45 | ${description} -> 文章内容\n\ 46 | ${translate|zh|title} -> 将标题翻译成中文\n\ 47 | ${translate|zh|description} -> 将描述翻译成中文\n\ 48 | ${translate|en|title} -> 将标题翻译成英文\n\ 49 | ${translate|en|description} -> 将描述翻译成英文\n\ 50 | 翻译中间的代码可以更改为自己想要翻译的...以此类推...\n 51 | create_monitor_8=模板内容: \n%s. 52 | create_monitor_finish=创建成功! 正在请求测试, 请耐心等待... 53 | 54 | test_monitor=未发现有此抓取计划: %s 55 | force_record_succeeded=强制记录最新文章成功 56 | 57 | update_monitor_1=请继续发送给我你想要更新的属性名 58 | update_monitor_2=属性名 %s, 未找到, 请重新发送给我 59 | update_monitor_3=属性名: %s 60 | update_monitor_4=请继续发送给我你想要更新的属性值 61 | update_field_error=属性更新错误... 62 | update_monitor_finish=更新配置成功! 63 | 64 | delete_monitor_confirm=确认删除此抓取计划吗? 65 | delete_monitor_finish=删除成功 66 | 67 | config_display_on=开启状态 68 | config_display_web_page_preview=web预览开启状态 69 | config_display_notification=通知开启状态 70 | config_display_zero_delay=0延迟监控开启状态 71 | config_display_url=rss url 72 | config_display_template=模板内容 73 | config_display_chat_id_array=发送目标(chatid列表) 74 | config_display_file_basename=监控配置名称 75 | 76 | you_are_not_an_admin=你不是管理员, 无法使用此命令 77 | are_you_sure_to_restart_right_now=确定重启机器人吗? 78 | restarting=重启中... 79 | getting_update_data=正在获取更新数据... 80 | are_you_sure_to_upgrade_this_bot_right_now=确定更新机器人吗? 81 | target_version=目标版本 82 | current_version=当前版本 83 | update_logs=更新日志 84 | updating=更新中... 85 | downloaded=已下载: %s, 总大小: %s 86 | 87 | are_you_sure_to_update_the_config=确定更新配置吗 ? 88 | please_send_me_config_content=请发送给我想要设置的配置内容 89 | update_config_fail=更新失败, 配置内容不符合标准格式 90 | 91 | update_succeeded=更新成功 92 | update_failed=更新失败 93 | 94 | set_chat_buttons=设置聊天下方按钮 95 | update_config=更新配置 96 | restart=重启 97 | upgrade=升级 98 | 99 | please_send_me_chat_buttons=请给我发送聊天按钮。 如果您不想设置它们,可以向我发送 -1\n\n\ 100 | 示例格式:\n\ 101 | \n\ 102 | <按钮名称> <跳转网址>\n\ 103 | <按钮名称> <跳转网址>\n\ 104 | ---\n\ 105 | \n\ 106 | <按钮名称> <跳转网址>\n\n\n\ 107 | 按钮全部生效示例格式:\n\ 108 | all\n\ 109 | <按钮名称> <跳转网址>\n\ 110 | <按钮名称> <跳转网址> 111 | 112 | format_error=格式错误 113 | 114 | setting_webhook=设置webhook 115 | hide_copyright_tips=隐藏版权提示 116 | are_you_sure_you_want_to_hide_copyright_tips=确认隐藏版权提示吗? 117 | current_setting=当前配置 118 | please_send_me_webhook_settings=请发送给我想要设置的webhook配置 119 | back=返回 120 | exclude_keywords=设置排除关键词不推送 121 | please_send_me_exclude_keywords=请发送给我想要排除的关键词, 检测到文本中包含指定的关键词将不会推送, 每行一个关键词, 换行分割。 如果您不想设置它们,可以向我发送 -1 122 | exclude_keywords_regex=设置排除正则不推送 123 | please_send_me_exclude_keywords_regex=请发送给我想要排除的正则表达式, 用正则检测文本是否出现某文本将不推送, 每行一个正则, 换行分割。 如果您不想设置它们,可以向我发送 -1 124 | verify_ssl=验证请求ssl证书 125 | are_you_sure_you_want_to_set_verify_ssl=确认设置验证ssl证书吗? 开启代表强制验证请求ssl证书有效性, 关闭则不验证 126 | enable=开启 127 | disable=关闭 128 | need_to_restart_bot=需要手动重启一下机器人以生效该配置 129 | include_keywords=设置包含关键词才推送 130 | include_keywords_regex=设置包含关键词正则才推送 131 | please_send_me_include_keywords=请发送给我想要设置包含关键词, 检测到文本中包含指定的关键词才会推送, 每行一个关键词, 换行分割。 如果您不想设置它们,可以向我发送 -1 132 | please_send_me_include_keywords_regex=请发送给我想要设置包含关键词的正则表达式, 用正则检测文本是否出现某文本才会推送, 每行一个正则, 换行分割。 如果您不想设置它们,可以向我发送 -1 133 | capture_flag=抓取资源 134 | translation_language=翻译到另一语言 135 | set_capture_flag=设置抓取资源 136 | set_capture_flag_on_note=已开启抓取资源, 说明: 当RSS内容有图片资源时, 会自动抓取并在TG中发送 137 | set_capture_flag_off_note=已关闭抓取资源, 说明: 当RSS内容有图片资源时, 会自动抓取并在TG中发送 -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ${APP_NAME} 10 | 11 | 12 | 13 | 14 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%class.%method:%line] - %msg%n 15 | 16 | 17 | 18 | 19 | 20 | 21 | ${LOG_HOME}/${APP_NAME}/info.log 22 | 23 | 24 | true 25 | 26 | 27 | 28 | INFO 29 | ACCEPT 30 | DENY 31 | 32 | 33 | 34 | 35 | 36 | ${LOG_HOME}/${APP_NAME}/notInfo-%d{yyyy-MM-dd}-%i.log 37 | 39 | 50 40 | 41 | 2MB 42 | 43 | 20MB 44 | 45 | 46 | 47 | 48 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%class.%method:%line] - %msg%n 49 | 50 | 51 | 52 | 53 | 54 | 55 | ${LOG_HOME}/${APP_NAME}/error.log 56 | 57 | 58 | true 59 | 60 | 61 | 62 | ERROR 63 | ACCEPT 64 | DENY 65 | 66 | 67 | 68 | 69 | 70 | ${LOG_HOME}/${APP_NAME}/notError-%d{yyyy-MM-dd}-%i.log 71 | 73 | 50 74 | 75 | 2MB 76 | 77 | 20MB 78 | 79 | 80 | 81 | 82 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%class.%method:%line] - %msg%n 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | --------------------------------------------------------------------------------