Repository: kylelin1998/RssMonitorTelegramBot Branch: master Commit: 63d476b63b39 Files: 77 Total size: 276.2 KB Directory structure: gitextract_nqpts3aa/ ├── .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 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ HELP.md target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ build/ !**/src/main/**/build/ !**/src/test/**/build/ ### VS Code ### .vscode/ /logs /config .mvn mvnw mvnw.cmd *.imi ================================================ FILE: Dockerfile ================================================ FROM openjdk:8-jdk-alpine ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories # 增加字体,解决验证码没有字体报空指针问题 RUN set -xe \ && apk --no-cache add ttf-dejavu fontconfig # 系统编码 ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 ENV BOT_ADMIN_ID='' ENV BOT_NAME='' ENV BOT_TOKEN='' ENV PROXY=false ENV PROXY_HOST=127.0.0.1 ENV PROXY_PORT=7890 WORKDIR /app COPY rss-monitor-for-telegram-universal.jar rss-monitor-for-telegram-universal.jar COPY run.sh run.sh ENTRYPOINT ["sh", "run.sh"] ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 kylelin1998 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ### 简体中文 | [English](./README_en.md)  [](https://github.com/kylelin1998/RssMonitorTelegramBot/releases/latest) ## 简介 RSS监控最新文章, 如果有监控到最新文章会通知到您设置好的Telegram群聊, 频道, 或者个人号上 支持自定义消息通知, 由你掌控内容 支持RSS同步到Telegraph 支持自定义Webhook调用 ## 具体功能 #### 通知 不仅可以通知到TG, 你还可以自定义Webhook通知到任意你想要的地方 #### 通知对象 拥有全局通知对象, 方案本身也可单独配置通知对象, 如果方案本身没有通知对象配置默认采用全局通知对象, 非常自由 #### 无限方案 你可以添加任意数量监控RSS方案, 不限制, 好管理 #### 通知模板变量 高度可配置通知文本, 支持众多通知模板变量 #### 抓取资源 你可以为你的方案配置开启抓取资源功能, 开启之后默认会抓取RSS内容的一张图片结合通知文本发送到TG #### 翻译你想翻译的 模板变量支持翻译到你想要的, 比如你可以翻译标题成英文, 泰语, 俄语, 德语...等等 #### 好升级 机器人内部内置升级功能, 一次部署, 后续升级在机器人内点击升级即可 ## 部署 机器人的部署步骤是基于 Docker 的,其机器人升级功能也基于 Docker,因此请使用 Docker 进行部署,以防出现错误 ### 部署方式1 (推荐) ⭐ Youtube: https://youtu.be/mNg6TFyozZk ⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1qF411f7pg/ #### 一键部署 ``` 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 ``` #### 一键部署(开启代理) ``` 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 ``` ### 部署方式2 (不推荐) Youtube:https://youtu.be/CiDxb1ESijQ 哔哩哔哩: https://www.bilibili.com/video/BV1Ts4y1S7bn/ 首先,在您的服务器上创建一个文件夹 然后,在其中创建名为 config 的另一个文件夹,config文件夹下必须包含名为 config.json 的JSON文件 接着,将 rss-monitor-for-telegram-universal.jar, run.sh 和 Dockerfile 传输到该文件夹中 ### config.json ```json { "debug": false, "on_proxy": false, "proxy_host": "127.0.0.1", "proxy_port": 7890, "bot_admin_id": "xxxx", "bot_name": "xxx", "bot_token": "xxx", "interval_minute": 10, "chatIdArray": [ "xxxxx" ], "permission_chat_id_array": [ "xxxx" ] } ``` bot admin主要作用是设置成只有你才能触发命令 * on_proxy -> 是否开启代理 * bot_admin_id -> Bot的管理者chat id * bot_name -> Bot 用户名 * bot_token -> Bot token * interval_minute -> 监控间隔(分钟) * chatIdArray -> 需要发送的Chat Id列表 * permission_chat_id_array -> 你只能允许列表下的这些chat id使用机器人, 可以填写个人的,或者是群的chat id * translate_youdao_key -> 有道翻译应用id * translate_youdao_secret -> 有道翻译密钥 ### 第一步: 编译镜像 ``` docker build -t rssb . ``` ### 第二步: 运行容器镜像 ``` docker run --name rssb -d -v $(pwd):/app --restart=always rssb ``` ## 使用说明 **机器人命令:** ``` create - 创建计划 list - 计划列表 exit - 退出 language - 切换语言 admin - 管理命令 restart - 重启机器人 upgrade - 升级机器人 help - 帮助 ``` 监控部分属性说明 * webPagePreview -> 消息web预览 * notification -> 通知开关 * zeroDelay -> 零延迟监控开关, 不受间隔时间限制 template说明: 支持自定义发送通知消息文本 * ${link} -> 文章地址 * ${title} -> 文章标题 * ${author} -> 文章作者 * ${telegraph} -> Telegraph文章地址 * ${description} -> 文章内容 * ${translate|zh|title} -> 将标题翻译成中文 * ${translate|zh|description} -> 将描述翻译成中文 * ${translate|en|title} -> 将标题翻译成英文 * ${translate|en|description} -> 将描述翻译成英文 * 翻译中间的代码可以更改为自己想要翻译的...以此类推... 例子, 会自动替换对应内容: ``` ${title} Telegraph: ${telegraph} 原文: ${link} ```  ================================================ FILE: README_en.md ================================================ ### [简体中文](./README.md) | English  [](https://github.com/kylelin1998/RssMonitorTelegramBot/releases/latest) ## Introduction Monitor articles for RSS Send messages of an up-to-date article to your set-up Telegram channel, group, or personal if you have up-to-date. Support custom message content to your decision. Support RSS article auto sync to Telegraph platform. Support custom Webhook invocation. ## Specific Features #### Notifications Not only can you receive notifications on Telegram, but you can also customize webhook notifications to any platform you desire. #### Notification Targets 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. #### Unlimited Plans You can add an unlimited number of RSS monitoring plans, with no restrictions, for easy management. #### Notification Template Variables Highly configurable notification text, with support for numerous template variables. #### Resource Crawling 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. #### Translation of Your Choice Template variables support translation to the language of your choice. For example, you can translate the title into English, Thai, Russian, German, etc. #### Convenient Upgrades 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. ## Deploy 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. ### Deployment method 1 (recommended) ⭐ Youtube: https://youtu.be/mNg6TFyozZk ⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1qF411f7pg/ #### One-click deployment ``` 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 ``` #### One-click deployment (with proxy enabled) ``` 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 ``` ### Deployment method 2 (not recommended) Youtube:https://youtu.be/CiDxb1ESijQ 哔哩哔哩: https://www.bilibili.com/video/BV1Ts4y1S7bn/ ### Prepare To start, create a folder named whatever you prefer on your server. 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. ### config.json ``` { "debug": false, "on_proxy": false, "proxy_host": "127.0.0.1", "proxy_port": 7890, "bot_admin_id": "xxxx", "bot_name": "xxx", "bot_token": "xxx", "interval_minute": 10, "chatIdArray": [ "xxxxx" ], "permission_chat_id_array": [ "xxxx" ] } ``` bot admin's main role is to set it so that only you can trigger commands. * on_proxy -> Enable proxy or not * bot_admin_id -> Chat ID of the Bot administrator * bot_name -> Bot username * bot_token -> Bot token * interval_minute -> Monitoring interval (in minutes) * chatIdArray -> List of Chat IDs to send notifications to * 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. * translate_youdao_key -> Youdao translation application ID * translate_youdao_secret -> Youdao translation secret key ### First step: Build a docker image for use. ``` docker build -t rssb . ``` ### Second step: Run the docker image of just then build. ``` docker run --name rssb -d -v $(pwd):/app --restart=always rssb ``` ## Usage **Commands:** ``` create - Create plan list - Plan list exit - Exit language - Change language admin - Admin restart - Restart the bot upgrade - Upgrade the bot help - Help ``` Monitor config description * webPagePreview -> Web page preview * notification -> Notification switch * zeroDelay -> Zero delays to monitor Template content: Support custom message content * ${link} -> Article website URL * ${title} -> Article title * ${author} -> Article author * ${telegraph} -> Telegraph URL * ${description} -> Article description * ${translate|zh|title} -> Translate the title into Chinese * ${translate|zh|description} -> Translate the description into Chinese * ${translate|en|title} -> Translate the title into English * ${translate|en|description} -> Translate the description into English * You can modify the code in between to translate whatever you want... and so on... For example, automatically replace the variable: ``` ${title} Telegraph: ${telegraph} Original article: ${link} ```  ================================================ FILE: docker-push.sh ================================================ #!/bin/sh sudo apt install -y qemu-user-static binfmt-support docker buildx create --use docker buildx ls docker buildx build --platform linux/amd64,linux/386,linux/arm64 -t kylelin1998/rss-tg-bot . --push ================================================ FILE: pom.xml ================================================ 4.0.0 com.kylelin1998 rss-monitor-for-telegram universal 8 8 UTF-8 nexus-tencentyun Nexus tencentyun http://mirrors.cloud.tencent.com/nexus/repository/maven-public/ default true false central aliyun maven http://maven.aliyun.com/nexus/content/groups/public/ default true false com.alibaba.fastjson2 fastjson2 2.0.23 org.projectlombok lombok 1.18.22 provided commons-io commons-io 2.11.0 org.apache.commons commons-lang3 3.12.0 org.slf4j slf4j-api 1.7.28 jar ch.qos.logback logback-core 1.2.3 jar ch.qos.logback logback-classic 1.2.3 jar org.telegram telegrambots 6.5.0 org.telegram telegrambotsextensions 6.5.0 com.vladsch.flexmark flexmark-all 0.64.0 com.rometools rome 1.18.0 org.xerial sqlite-jdbc 3.40.0.0 com.konghq unirest-java 3.14.1 org.jsoup jsoup 1.11.2 org.apache.httpcomponents.client5 httpclient5 5.1.3 org.apache.httpcomponents.client5 httpclient5-fluent 5.1.3 org.apache.maven.plugins maven-shade-plugin 2.3 false package shade code.Main ================================================ FILE: run.sh ================================================ #!/bin/sh temp=temp.jar app=/app/rss-monitor-for-telegram-universal.jar temp_jar=/app/temp.jar if [ -e $temp ] then echo "updating..." rm -rf $app mv $temp_jar $app echo "updated..." fi 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 ================================================ FILE: src/main/java/code/Main.java ================================================ package code; import code.config.Config; import code.config.ConfigSettings; import code.config.I18nEnum; import code.config.RequestProxyConfig; import code.handler.CommandsHandler; import code.handler.Handler; import code.handler.I18nHandle; import code.handler.MessageHandle; import code.handler.store.Store; import code.repository.*; import code.util.ExceptionUtil; import code.util.Snowflake; import com.alibaba.fastjson2.JSON; import kong.unirest.Unirest; import lombok.extern.slf4j.Slf4j; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; /** * * ,---. ,--. ,--. ,--.,--. * / O \ | |,--. ,--. ,--,--.,--. ,--.,---. | |-. ,---. | |`--' ,---.,--. ,--.,---. * | .-. || || |.'.| |' ,-. | \ ' /( .-' | .-. '| .-. :| |,--.| .-. :\ `' /| .-. : * | | | || || .'. |\ '-' | \ ' .-' `) | `-' |\ --.| || |\ --. \ / \ --. * `--' `--'`--''--' '--' `--`--'.-' / `----' `---' `----'`--'`--' `----' `--' `----' * `---' * Always believe that something wonderful is about to happen * * If you have any additional features you'd like to suggest or if you have any feedback, * you can reach me at my email address: email@kylelin1998.com */ @Slf4j public class Main { public static CommandsHandler Bot = null; public static volatile ConfigSettings GlobalConfig = Config.initConfig(); public final static code.repository.SentRecordTableRepository SentRecordTableRepository = new SentRecordTableRepository(); public final static code.repository.I18nTableRepository I18nTableRepository = new I18nTableRepository(); public final static code.repository.MonitorTableRepository MonitorTableRepository = new MonitorTableRepository(); public final static code.repository.WebhookTableRepository WebhookTableRepository = new WebhookTableRepository(); public final static Snowflake Snowflake = new Snowflake(997); public static void main(String[] args) { log.info(String.format("Main args: %s", JSON.toJSONString(args))); log.info(String.format("System properties: %s", System.getProperties())); log.info(String.format("Config: %s", JSON.toJSONString(GlobalConfig))); Unirest .config() .enableCookieManagement(false) .verifySsl(GlobalConfig.getVerifySsl()) ; new Thread(() -> { while (true) { try { GlobalConfig = Config.readConfig(); Thread.sleep(5000); } catch (InterruptedException e) {} } }).start(); Store.init(); Handler.init(); log.info("Program is running"); try { TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class); if (GlobalConfig.getOnProxy()) { Bot = new CommandsHandler(RequestProxyConfig.create().buildDefaultBotOptions()); } else { Bot = new CommandsHandler(); } botsApi.registerBot(Bot); MessageHandle.sendMessage(GlobalConfig.getBotAdminId(), I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.BotStartSucceed) + I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.CurrentVersion) + ": " + Config.MetaData.CurrentVersion, false); Config.oldDataConvert(); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } } ================================================ FILE: src/main/java/code/config/Config.java ================================================ package code.config; import code.eneity.MonitorTableEntity; import code.eneity.YesOrNoEnum; import code.util.ExceptionUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONException; import com.alibaba.fastjson2.JSONReader; import com.alibaba.fastjson2.JSONWriter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import java.io.*; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import static code.Main.*; @Slf4j public class Config { public static final String CurrentDir = System.getProperty("user.dir") + File.separator + "config"; public static final String MonitorDir = CurrentDir + File.separator + "monitor"; public static final String SettingsPath = CurrentDir + File.separator + "config.json"; public static final String DBPath = CurrentDir + File.separator + "db.db"; public final static String TempDir = CurrentDir + File.separator + "temp"; public static String TelegraphHtml = new BufferedReader(new InputStreamReader(Config.class.getResourceAsStream("telegraph.html"), StandardCharsets.UTF_8)) .lines() .collect(Collectors.joining("\n")); public static String WebhookJson = new BufferedReader(new InputStreamReader(Config.class.getResourceAsStream("webhook.json"), StandardCharsets.UTF_8)) .lines() .collect(Collectors.joining("\n")); private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); public final static class MetaData { public final static String CurrentVersion = "1.1.5"; public final static String GitOwner = "kylelin1998"; public final static String GitRepo = "RssMonitorTelegramBot"; public final static String ProcessName = "rss-monitor-for-telegram-universal.jar"; public final static String JarName = "rss-monitor-for-telegram-universal.jar"; } static { mkdirs(CurrentDir, MonitorDir, TempDir); new Thread(() -> { while (true) { try { File file = new File(TempDir); ArrayList files = new ArrayList<>(); file.list((File dir, String name) -> { File file1 = new File(dir, name); try { BasicFileAttributes attributes = Files.readAttributes(file1.toPath(), BasicFileAttributes.class); FileTime fileTime = attributes.creationTime(); long millis = System.currentTimeMillis() - fileTime.toMillis(); if (millis > 3600000) { files.add(file1); } } catch (IOException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return true; }); for (File df : files) { df.delete(); } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } try { TimeUnit.MINUTES.sleep(30); } catch (InterruptedException e) { throw new RuntimeException(e); } } }).start(); } private static void mkdirs(String... dirs) { for (String dir : dirs) { File file = new File(dir); if (!file.exists()) { file.mkdirs(); } } } public static ConfigSettings initConfig() { File file = new File(SettingsPath); if (!file.exists()) { Properties properties = System.getProperties(); ConfigSettings configSettings = new ConfigSettings(); configSettings.setBotAdminId(properties.getProperty("botAdminId", "")); configSettings.setBotName(properties.getProperty("botName", "")); configSettings.setBotToken(properties.getProperty("botToken", "")); configSettings.setOnProxy(Boolean.valueOf(properties.getProperty("botProxy", "false"))); configSettings.setProxyHost(properties.getProperty("botProxyHost", "127.0.0.1")); configSettings.setProxyPort(Integer.valueOf(properties.getProperty("botProxyPort", "7890"))); saveConfig(handle(configSettings)); } return readConfig(); } private static ConfigSettings handle(ConfigSettings configSettings) { Boolean debug = configSettings.getDebug(); if (null == debug) { configSettings.setDebug(false); } String[] permissionChatIdArray = configSettings.getPermissionChatIdArray(); if (null == permissionChatIdArray) { configSettings.setPermissionChatIdArray(new String[]{ configSettings.getBotAdminId() }); } String[] chatIdArray = configSettings.getChatIdArray(); if (null == chatIdArray) { configSettings.setChatIdArray(new String[]{ configSettings.getBotAdminId() }); } Integer intervalMinute = configSettings.getIntervalMinute(); if (null == intervalMinute) { configSettings.setIntervalMinute(5); } Boolean hideCopyrightTips = configSettings.getHideCopyrightTips(); if (null == hideCopyrightTips) { configSettings.setHideCopyrightTips(false); } Boolean verifySsl = configSettings.getVerifySsl(); if (null == verifySsl) { configSettings.setVerifySsl(true); } List excludeKeywords = configSettings.getExcludeKeywords(); if (null == excludeKeywords) { configSettings.setExcludeKeywords(new ArrayList<>()); } List excludeKeywordsRegex = configSettings.getExcludeKeywordsRegex(); if (null == excludeKeywordsRegex) { configSettings.setExcludeKeywordsRegex(new ArrayList<>()); } List includeKeywords = configSettings.getIncludeKeywords(); if (null == includeKeywords) { configSettings.setIncludeKeywords(new ArrayList<>()); } List includeKeywordsRegex = configSettings.getIncludeKeywordsRegex(); if (null == includeKeywordsRegex) { configSettings.setIncludeKeywordsRegex(new ArrayList<>()); } return configSettings; } public static ConfigSettings readConfig() { ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock(); readLock.lock(); try { File file = new File(SettingsPath); boolean exists = file.exists(); if (exists) { String text = FileUtils.readFileToString(file, StandardCharsets.UTF_8); ConfigSettings configSettings = JSON.parseObject(text, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch); return handle(configSettings); } else { log.warn("Settings file not found, " + SettingsPath); } } catch (IOException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e), SettingsPath); } finally { readLock.unlock(); } return null; } public static ConfigSettings verifyConfig(String configJson) { if (StringUtils.isBlank(configJson)) { return null; } ConfigSettings configSettings = null; try { configSettings = JSON.parseObject(configJson, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch); } catch (JSONException e) { } if (null == configSettings) { return null; } for (Field field : configSettings.getClass().getDeclaredFields()) { ConfigField configField = field.getAnnotation(ConfigField.class); if (null == configField) { continue; } if (configField.isNotNull()) { try { field.setAccessible(true); Object o = field.get(configSettings); if (null == o) { return null; } } catch (IllegalAccessException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); return null; } } } return configSettings; } public synchronized static void oldDataConvert() { File file = new File(MonitorDir); file.list((File dir, String name) -> { File monitorFile = new File(dir, name); try { if (monitorFile.isFile()) { String text = FileUtils.readFileToString(monitorFile, StandardCharsets.UTF_8); MonitorConfigSettings configSettings = JSON.parseObject(text, MonitorConfigSettings.class, JSONReader.Feature.SupportSmartMatch); configSettings.setFilename(name); configSettings.setFileBasename(StringUtils.removeEnd(name, ".json")); if (null == configSettings.getNotification()) { configSettings.setNotification(true); } if (null == configSettings.getZeroDelay()) { configSettings.setZeroDelay(false); } MonitorTableEntity monitorTableEntity = new MonitorTableEntity(); monitorTableEntity.setId(Snowflake.nextIdToStr()); monitorTableEntity.setChatId(GlobalConfig.botAdminId); monitorTableEntity.setEnable(YesOrNoEnum.toInt(configSettings.getOn())); monitorTableEntity.setName(configSettings.getFileBasename()); monitorTableEntity.setNotification(YesOrNoEnum.toInt(configSettings.getNotification())); monitorTableEntity.setUrl(configSettings.getUrl()); monitorTableEntity.setTemplate(configSettings.getTemplate()); monitorTableEntity.setZeroDelay(YesOrNoEnum.toInt(configSettings.getZeroDelay())); monitorTableEntity.setCreateTime(System.currentTimeMillis()); monitorTableEntity.setChatIdArrayJson(JSON.toJSONString(configSettings.getChatIdArray())); monitorTableEntity.setWebPagePreview(YesOrNoEnum.toInt(configSettings.getWebPagePreview())); monitorTableEntity.setCaptureFlag(YesOrNoEnum.No.getNum()); MonitorTableRepository.insert(monitorTableEntity); monitorFile.delete(); } } catch (IOException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return true; }); } public static boolean saveConfig(ConfigSettings configSettings) { ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock(); writeLock.lock(); try { File file = new File(SettingsPath); FileUtils.write(file, JSON.toJSONString(configSettings, JSONWriter.Feature.PrettyFormat), StandardCharsets.UTF_8); return true; } catch (IOException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } finally { writeLock.unlock(); } return false; } } ================================================ FILE: src/main/java/code/config/ConfigField.java ================================================ package code.config; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.FIELD) @Retention(value = RetentionPolicy.RUNTIME) public @interface ConfigField { boolean isNotNull(); } ================================================ FILE: src/main/java/code/config/ConfigSettings.java ================================================ package code.config; import com.alibaba.fastjson2.annotation.JSONField; import lombok.Data; import java.util.List; @Data public class ConfigSettings { @ConfigField(isNotNull = true) public Boolean debug; @JSONField(name = "on_proxy") @ConfigField(isNotNull = true) public Boolean onProxy; @JSONField(name = "proxy_host") public String proxyHost; @JSONField(name = "proxy_port") public Integer proxyPort; @JSONField(name = "bot_admin_id") @ConfigField(isNotNull = true) public String botAdminId; @JSONField(name = "permission_chat_id_array") @ConfigField(isNotNull = true) private String[] permissionChatIdArray; @JSONField(name = "bot_name") @ConfigField(isNotNull = true) public String botName; @JSONField(name = "bot_token") @ConfigField(isNotNull = true) public String botToken; @JSONField(name = "interval_minute") @ConfigField(isNotNull = true) private Integer intervalMinute; @JSONField(name = "chatId_array") @ConfigField(isNotNull = true) private String[] chatIdArray; @JSONField(name = "chat_buttons") private String chatButtons; @JSONField(name = "hide_copyright_tips") private Boolean hideCopyrightTips; @JSONField(name = "verify_ssl") private Boolean verifySsl; @JSONField(name = "exclude_keywords") private List excludeKeywords; @JSONField(name = "exclude_keywords_regex") private List excludeKeywordsRegex; @JSONField(name = "include_keywords") private List includeKeywords; @JSONField(name = "include_keywords_regex") private List includeKeywordsRegex; @JSONField(name = "translate_youdao_key") public String translateYoudaoKey; @JSONField(name = "translate_youdao_secret") public String translateYoudaoSecret; } ================================================ FILE: src/main/java/code/config/DisplayConfigAnnotation.java ================================================ package code.config; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface DisplayConfigAnnotation { String i18n(); boolean set(); } ================================================ FILE: src/main/java/code/config/ExecutorsConfig.java ================================================ package code.config; import lombok.extern.slf4j.Slf4j; import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import java.util.concurrent.*; @Slf4j public class ExecutorsConfig { private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(false).setNameFormat("handle-pool-%d").build(); private static ExecutorService fixedThreadPool = new ThreadPoolExecutor( 5, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), threadFactory, (Runnable r, ThreadPoolExecutor executor) -> { log.error(r.toString() + " is Rejected"); } ); public static void submit(Runnable task) { fixedThreadPool.submit(task); } } ================================================ FILE: src/main/java/code/config/I18nConfig.java ================================================ package code.config; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.*; public class I18nConfig { private static Map> cacheMap = new LinkedHashMap<>(); static { ResourceBundle.Control control = new ResourceBundle.Control() { @Override public ResourceBundle newBundle(String baseName, java.util.Locale locale, String format, ClassLoader loader, boolean reload) throws IOException { String resourceName = toResourceName(toBundleName(baseName, locale), "properties"); InputStream inputStream = null; try { inputStream = loader.getResourceAsStream(resourceName); if (inputStream != null) { return new PropertyResourceBundle(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); } } finally { if (inputStream != null) { inputStream.close(); } } return null; } }; for (I18nLocaleEnum value : I18nLocaleEnum.values()) { ResourceBundle bundle = ResourceBundle.getBundle("i18n/i18n", value.getLocale(), control); HashMap hashMap = new HashMap<>(); for (String s : bundle.keySet()) { hashMap.put(s, bundle.getString(s)); } cacheMap.put(value.getAlias(), hashMap); } } public static String getText(String i18nAlias, String key) { Map map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias()); return map.get(key); } public static String getText(String i18nAlias, I18nEnum i18nEnum) { Map map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias()); return map.get(i18nEnum.getKey()); } } ================================================ FILE: src/main/java/code/config/I18nEnum.java ================================================ package code.config; import lombok.Getter; @Getter public enum I18nEnum { BotStartSucceed("bot_start_succeed"), HelpText("help_text"), InvalidCommand("invalid_command"), MonitorList("monitor_list"), NothingHere("nothing_here"), On("on"), Off("off"), Test("test"), Update("update"), NotFound("not_found"), UnknownError("unknown_error"), NothingAtAll("nothing_at_all"), CancelSucceeded("cancel_succeeded"), Confirm("confirm"), Cancel("cancel"), Delete("delete"), Finish("finish"), ExitSucceeded("exit_succeeded"), Getting("getting"), Downloading("downloading"), ForceRecord("force_record"), OnMonitor("on_monitor"), OffMonitor("off_monitor"), LanguageList("language_list"), ChangeLanguageFinish("change_language_finish"), MonitorExists("monitor_exists"), CreateNameTooLong("create_name_too_long"), CreateMonitor1("create_monitor_1"), CreateMonitor2("create_monitor_2"), CreateMonitor3("create_monitor_3"), CreateMonitor4("create_monitor_4"), CreateMonitor5("create_monitor_5"), CreateMonitor6("create_monitor_6"), CreateMonitor7("create_monitor_7"), CreateMonitor8("create_monitor_8"), CreateMonitorFinish("create_monitor_finish"), TestMonitor("test_monitor"), ForceRecordSucceeded("force_record_succeeded"), UpdateMonitor1("update_monitor_1"), UpdateMonitor2("update_monitor_2"), UpdateMonitor3("update_monitor_3"), UpdateMonitor4("update_monitor_4"), UpdateFieldError("update_field_error"), UpdateMonitorFinish("update_monitor_finish"), DeleteMonitorConfirm("delete_monitor_confirm"), DeleteMonitorFinish("delete_monitor_finish"), ConfigDisplayOn("config_display_on"), ConfigDisplayWebPagePreview("config_display_web_page_preview"), ConfigDisplayNotification("config_display_notification"), ConfigDisplayZeroDelay("config_display_zero_delay"), ConfigDisplayUrl("config_display_url"), ConfigDisplayTemplate("config_display_template"), ConfigDisplayChatIdArray("config_display_chat_id_array"), ConfigDisplayFileBasename("config_display_file_basename"), YouAreNotAnAdmin("you_are_not_an_admin"), AreYouSureToRestartRightNow("are_you_sure_to_restart_right_now"), Restarting("restarting"), GettingUpdateData("getting_update_data"), AreYouSureToUpgradeThisBotRightNow("are_you_sure_to_upgrade_this_bot_right_now"), TargetVersion("target_version"), CurrentVersion("current_version"), UpdateLogs("update_logs"), Updating("updating"), Downloaded("downloaded"), AreYouSureToUpdateTheConfig("are_you_sure_to_update_the_config"), PleaseSendMeConfigContent("please_send_me_config_content"), UpdateConfigFail("update_config_fail"), UpdateSucceeded("update_succeeded"), UpdateFailed("update_failed"), SetChatButtons("set_chat_buttons"), UpdateConfig("update_config"), Restart("restart"), Upgrade("upgrade"), PleaseSendMeChatButtons("please_send_me_chat_buttons"), FormatError("format_error"), SettingWebhook("setting_webhook"), HideCopyrightTips("hide_copyright_tips"), AreYouSureYouWantToHideCopyrightTips("are_you_sure_you_want_to_hide_copyright_tips"), CurrentSetting("current_setting"), PleaseSendMeWebhookSettings("please_send_me_webhook_settings"), Back("back"), Refresh("refresh"), ExcludeKeywords("exclude_keywords"), ExcludeKeywordsRegex("exclude_keywords_regex"), PleaseSendMeExcludeKeywords("please_send_me_exclude_keywords"), PleaseSendMeExcludeKeywordsRegex("please_send_me_exclude_keywords_regex"), VerifySsl("verify_ssl"), AreYouSureYouWantToSetVerifySsl("are_you_sure_you_want_to_set_verify_ssl"), Enable("enable"), Disable("disable"), NeedToRestartBot("need_to_restart_bot"), IncludeKeywords("include_keywords"), IncludeKeywordsRegex("include_keywords_regex"), PleaseSendMeIncludeKeywords("please_send_me_include_keywords"), PleaseSendMeIncludeKeywordsRegex("please_send_me_include_keywords_regex"), CaptureFlag("capture_flag"), TranslationLanguage("translation_language"), SetCaptureFlag("set_capture_flag"), SetCaptureFlagOnNote("set_capture_flag_on_note"), SetCaptureFlagOffNote("set_capture_flag_off_note"), ; private String key; I18nEnum(String key) { this.key = key; } } ================================================ FILE: src/main/java/code/config/I18nLocaleEnum.java ================================================ package code.config; import lombok.Getter; import java.util.Locale; @Getter public enum I18nLocaleEnum { ZH_CN(Locale.SIMPLIFIED_CHINESE, "zh-cn", "简体中文"), EN(Locale.US, "en", "English"), ; private Locale locale; private String alias; private String displayText; I18nLocaleEnum(Locale locale, String alias, String displayText) { this.locale = locale; this.alias = alias; this.displayText = displayText; } public static I18nLocaleEnum getI18nLocaleEnumByAlias(String alias) { for (I18nLocaleEnum value : I18nLocaleEnum.values()) { if (value.getAlias().equals(alias)) { return value; } } return null; } } ================================================ FILE: src/main/java/code/config/MonitorConfigSettings.java ================================================ package code.config; import lombok.Data; @Data public class MonitorConfigSettings { @DisplayConfigAnnotation(i18n = "config_display_on", set = false) private Boolean on; private String filename; @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = false) private String fileBasename; @DisplayConfigAnnotation(i18n = "config_display_web_page_preview", set = true) private Boolean webPagePreview; @DisplayConfigAnnotation(i18n = "config_display_notification", set = true) private Boolean notification; @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = true) private Boolean zeroDelay; @DisplayConfigAnnotation(i18n = "config_display_url", set = true) private String url; @DisplayConfigAnnotation(i18n = "config_display_template", set = true) private String template; @DisplayConfigAnnotation(i18n = "config_display_chat_id_array", set = true) private String[] chatIdArray; } ================================================ FILE: src/main/java/code/config/MonitorExecutorsConfig.java ================================================ package code.config; import lombok.extern.slf4j.Slf4j; import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import java.util.concurrent.*; @Slf4j public class MonitorExecutorsConfig { private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(false).setNameFormat("monitor-pool-%d").build(); private static ExecutorService fixedThreadPool = new ThreadPoolExecutor( 2, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100000), threadFactory, (Runnable r, ThreadPoolExecutor executor) -> { log.error(r.toString()+" is Rejected"); } ); public static void submit(Runnable task) { fixedThreadPool.submit(task); } } ================================================ FILE: src/main/java/code/config/ProxyTypeEnum.java ================================================ package code.config; import lombok.Getter; @Getter public enum ProxyTypeEnum { NotOpen(0, "Not Open"), HttpProxy(1, "Http Proxy"), ; private int type; private String alias; ProxyTypeEnum(int type, String alias) { this.type = type; this.alias = alias; } public static ProxyTypeEnum getDefault() { return NotOpen; } } ================================================ FILE: src/main/java/code/config/RequestProxyConfig.java ================================================ package code.config; import kong.unirest.HttpRequest; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.http.HttpHost; import org.telegram.telegrambots.bots.DefaultBotOptions; import static code.Main.GlobalConfig; public class RequestProxyConfig { private ProxyTypeEnum type; private String hostName; private Integer port; public static RequestProxyConfig create() { RequestProxyConfig config = new RequestProxyConfig(); if (null != GlobalConfig && GlobalConfig.getOnProxy()) { config.type = ProxyTypeEnum.HttpProxy; config.hostName = GlobalConfig.getProxyHost(); config.port = GlobalConfig.getProxyPort(); } else { config.type = ProxyTypeEnum.getDefault(); } return config; } public void viaProxy(HttpRequest request) { switch (this.type) { case HttpProxy: request.proxy(this.hostName, this.port); break; } } public void viaProxy(Request request) { switch (this.type) { case HttpProxy: request.viaProxy(new HttpHost(this.hostName, this.port)); break; } } public DefaultBotOptions buildDefaultBotOptions() { switch (this.type) { case HttpProxy: DefaultBotOptions botOptions = new DefaultBotOptions(); botOptions.setProxyHost(this.hostName); botOptions.setProxyPort(this.port); botOptions.setProxyType(DefaultBotOptions.ProxyType.HTTP); return botOptions; } return null; } } ================================================ FILE: src/main/java/code/config/TableEnum.java ================================================ package code.config; import lombok.Getter; @Getter public enum TableEnum { SentRecordTable("sent_record_table"), I18nTable("i18n_table"), ; private String name; TableEnum(String name) { this.name = name; } } ================================================ FILE: src/main/java/code/eneity/MonitorTableEntity.java ================================================ package code.eneity; import code.config.DisplayConfigAnnotation; import code.repository.base.TableEntity; import code.repository.base.TableField; import code.repository.base.TableName; import lombok.Data; @TableName(name = "monitor_table") @Data public class MonitorTableEntity implements TableEntity { @TableField(name = "id", sql = "id varchar(55) primary key") private String id; @TableField(name = "chat_id", sql = "chat_id varchar(100)") private String chatId; @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = false) @TableField(name = "name", sql = "name varchar(50)") private String name; @DisplayConfigAnnotation(i18n = "config_display_chat_id_array", set = true) @TableField(name = "chat_id_array_json", sql = "chat_id_array_json text") private String chatIdArrayJson; @TableField(name = "create_time", sql = "create_time timestamp") private Long createTime; @TableField(name = "update_time", sql = "update_time timestamp") private Long updateTime; @DisplayConfigAnnotation(i18n = "config_display_notification", set = true) @TableField(name = "notification", sql = "notification int(2)") private Integer notification; @DisplayConfigAnnotation(i18n = "config_display_on", set = false) @TableField(name = "enable", sql = "enable int(2)") private Integer enable; @DisplayConfigAnnotation(i18n = "config_display_template", set = true) @TableField(name = "template", sql = "template text") private String template; @DisplayConfigAnnotation(i18n = "config_display_url", set = true) @TableField(name = "url", sql = "url text") private String url; @DisplayConfigAnnotation(i18n = "config_display_web_page_preview", set = true) @TableField(name = "web_page_preview", sql = "web_page_preview int(2)") private Integer webPagePreview; @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = true) @TableField(name = "zero_delay", sql = "zero_delay int(2)") private Integer zeroDelay; @DisplayConfigAnnotation(i18n = "capture_flag", set = false) @TableField(name = "capture_flag", sql = "capture_flag int(2)") private Integer captureFlag; @DisplayConfigAnnotation(i18n = "translation_language", set = false) @TableField(name = "translation_language", sql = "translation_language text") private String translationLanguage; } ================================================ FILE: src/main/java/code/eneity/PageEntity.java ================================================ package code.eneity; import lombok.Data; import java.util.ArrayList; import java.util.List; @Data public class PageEntity { private int total; private int remainder; private int page; private int count; private int current; private boolean hasNext; private boolean hasPrev; private List list = new ArrayList<>(); private PageEntity() {} public PageEntity(int total, int page, int current) { this.total = total; this.remainder = total % page; this.page = page; this.count = remainder > 0 ? ((total / page) + 1) : (total / page); this.current = current; this.hasNext = current < this.count; this.hasPrev = current > 1; } public static PageEntity empty() { return new PageEntity(); } } ================================================ FILE: src/main/java/code/eneity/SentRecordTableEntity.java ================================================ package code.eneity; import code.repository.base.TableEntity; import code.repository.base.TableField; import code.repository.base.TableName; import lombok.Data; @TableName(name = "sent_record_202312_table") @Data public class SentRecordTableEntity implements TableEntity { @TableField(name = "id", sql = "id varchar(100) primary key") private String id; @TableField(name = "chat_id", sql = "chat_id varchar(100)") private String chatId; @TableField(name = "name", sql = "name varchar(50)") private String name; @TableField(name = "uri", sql = "uri text") private String uri; @TableField(name = "create_time", sql = "create_time timestamp") private Long createTime; } ================================================ FILE: src/main/java/code/eneity/WebhookTableEntity.java ================================================ package code.eneity; import code.repository.base.TableEntity; import code.repository.base.TableField; import code.repository.base.TableName; import lombok.Data; @TableName(name = "webhook_table") @Data public class WebhookTableEntity implements TableEntity { @TableField(name = "id", sql = "id varchar(100) primary key") private String id; @TableField(name = "chat_id", sql = "chat_id varchar(100)") private String chatId; @TableField(name = "settings_json", sql = "settings_json text") private String settingsJson; } ================================================ FILE: src/main/java/code/eneity/YesOrNoEnum.java ================================================ package code.eneity; import lombok.Getter; import java.util.Optional; @Getter public enum YesOrNoEnum { Yes(1, true), No(0, false), ; private int num; private boolean bool; YesOrNoEnum(int num, boolean bool) { this.num = num; this.bool = bool; } public static int toInt(boolean bool) { return bool ? Yes.getNum() : No.getNum(); } public static Optional toBoolean(int num) { for (YesOrNoEnum value : values()) { if (num == value.getNum()) { return Optional.of(value.isBool()); } } return Optional.empty(); } public static Optional get(int num) { for (YesOrNoEnum value : values()) { if (value.getNum() == num) { return Optional.of(value); } } return Optional.empty(); } } ================================================ FILE: src/main/java/code/handler/Command.java ================================================ package code.handler; import lombok.Getter; @Getter public enum Command { Start("start"), Help("help"), Create("create"), List("list"), Get("get"), Update("update"), Test("test"), ForceRecord("fr"), On("on"), Off("off"), Delete("delete"), Admin("admin"), Exit("exit"), Language("language"), Restart("restart"), UpdateConfig("uc"), Upgrade("upgrade"), Webhook("webhook"), SetVerifySsl("rssb01"), SetExcludeKeywords("rssb02"), SetExcludeKeywordsRegex("rssb03"), SetIncludeKeywords("rssb04"), SetIncludeKeywordsRegex("rssb05"), SetCaptureFlag("rssb06"), SetTranslationLanguage("rssb07"), SetChatButtons("rssb11"), HideCopyrightTips("rssb10"), ; private String cmd; Command(String cmd) { this.cmd = cmd; } public static Command toCmd(String cmd) { for (Command value : Command.values()) { if (value.getCmd().equals(cmd)) { return value; } } return null; } public static boolean exist(String cmd) { return null != toCmd(cmd); } } ================================================ FILE: src/main/java/code/handler/CommandsHandler.java ================================================ package code.handler; import code.handler.message.CallbackBuilder; import code.handler.steps.StepsChatSession; import code.handler.steps.StepsChatSessionBuilder; import com.alibaba.fastjson2.JSON; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.telegram.telegrambots.bots.DefaultBotOptions; import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot; import org.telegram.telegrambots.meta.api.objects.CallbackQuery; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.Update; import static code.Main.GlobalConfig; @Slf4j public class CommandsHandler extends TelegramLongPollingCommandBot { public CommandsHandler() { super(); } public CommandsHandler(DefaultBotOptions botOptions) { super(botOptions); } @Override public String getBotUsername() { return GlobalConfig.getBotName(); } @Override public String getBotToken() { return GlobalConfig.getBotToken(); } @Override public void processNonCommandUpdate(Update update) { if (GlobalConfig.getDebug()) { log.info(JSON.toJSONString(update)); } CallbackQuery callbackQuery = update.getCallbackQuery(); if (null != callbackQuery) { String data = callbackQuery.getData(); CallbackBuilder.CallbackData callbackData = CallbackBuilder.parseCallbackData(data); if (null == callbackData) { MessageHandle.sendMessage(String.valueOf(callbackQuery.getMessage().getChatId()), "Error...", false); return; } StepsChatSession session = StepsChatSessionBuilder .create(callbackQuery) .setText(callbackData.getText()) .build(); if (!session.getSessionId().equals(String.valueOf(callbackData.getId()))) { return; } if (StringUtils.isNotBlank(data)) { StepsCenter.cmdHandle(callbackData, session); return; } } Message message = update.getMessage(); if (null == message) { return; } String text = message.getText(); if (StringUtils.isNotEmpty(text)) { boolean handle = StepsCenter.cmdHandle(StepsChatSessionBuilder.create(message).build()); if (!handle) { StepsCenter.textHandle(StepsChatSessionBuilder.create(message).build()); } } } } ================================================ FILE: src/main/java/code/handler/Handler.java ================================================ package code.handler; import code.config.*; import code.eneity.*; import code.handler.message.CallbackBuilder; import code.handler.message.InlineKeyboardButtonBuilder; import code.handler.message.InlineKeyboardButtonListBuilder; import code.handler.steps.StepResult; import code.handler.steps.StepsBuilder; import code.handler.steps.StepsChatSession; import code.handler.store.ChatButtonsStore; import code.handler.store.WebhookStore; import code.util.*; import code.util.translate.Translate; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONWriter; import com.rometools.rome.feed.synd.SyndContent; import com.rometools.rome.feed.synd.SyndEntry; import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.feed.synd.SyndPerson; import kong.unirest.HttpResponse; import kong.unirest.RequestBodyEntity; import kong.unirest.Unirest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.media.InputMedia; import org.telegram.telegrambots.meta.api.objects.media.InputMediaPhoto; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import java.io.File; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static code.Main.*; @Slf4j public class Handler { private static boolean isAdmin(String fromId) { return GlobalConfig.getBotAdminId().equals(fromId); } public static void init() { new Thread(() -> { while (true) { try { MonitorTableEntity where = new MonitorTableEntity(); where.setZeroDelay(YesOrNoEnum.No.getNum()); where.setEnable(YesOrNoEnum.Yes.getNum()); List list = MonitorTableRepository.selectList(where); for (MonitorTableEntity entity : list) { rssMessageHandle(null, entity, false, false); } TimeUnit.MINUTES.sleep(GlobalConfig.getIntervalMinute()); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } }).start(); new Thread(() -> { while (true) { try { long startMillis = System.currentTimeMillis(); if (GlobalConfig.getDebug()) { log.info("Zero delay, start timestamp: {}", startMillis); } MonitorTableEntity where = new MonitorTableEntity(); where.setZeroDelay(YesOrNoEnum.Yes.getNum()); where.setEnable(YesOrNoEnum.Yes.getNum()); List list = MonitorTableRepository.selectList(where); if (list.isEmpty()) { log.info("Zero delay, monitor list is empty!"); TimeUnit.MINUTES.sleep(2); } else { CountDownLatch countDownLatch = new CountDownLatch(list.size()); for (MonitorTableEntity entity : list) { MonitorExecutorsConfig.submit(() -> { try { rssMessageHandle(null, entity, false, false); } finally { countDownLatch.countDown(); } }); } countDownLatch.await(); long endMillis = System.currentTimeMillis(); if (GlobalConfig.getDebug()) { log.info("Zero delay, end timestamp: {}, total time: {}", endMillis, endMillis - startMillis); } TimeUnit.SECONDS.sleep(5); } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } }).start(); StepsBuilder .create() .bindCommand(Command.Start, Command.Help) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.HelpText), false); return StepResult.end(); }) .build(); // Create StepsBuilder .create() .bindCommand(Command.Create) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor1), false); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { if (session.getText().length() > 50) { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateNameTooLong), false); return StepResult.reject(); } Integer count = MonitorTableRepository.selectCountByName(session.getFromId(), session.getText()); if (count > 0) { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.MonitorExists), false); return StepResult.reject(); } MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor2, session.getText()), false); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor3), false); context.put("name", session.getText()); return StepResult.ok(); }, (StepsChatSession session, int index, List list, Map context) -> { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor4), false); SyndFeed feed = RssUtil.getFeed(RequestProxyConfig.create(), session.getText()); if (null == feed) { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor5), false); return StepResult.reject(); } MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor6, session.getText()), false); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor7), false); context.put("url", session.getText()); return StepResult.ok(); }, (StepsChatSession session, int index, List list, Map context) -> { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor8, session.getText()), false); String name = (String) context.get("name"); String url = (String) context.get("url"); String id = Snowflake.nextIdToStr(); MonitorTableEntity settings = new MonitorTableEntity(); settings.setId(id); settings.setCreateTime(System.currentTimeMillis()); settings.setChatId(session.getFromId()); settings.setName(name); settings.setTemplate(session.getText()); settings.setEnable(YesOrNoEnum.No.getNum()); settings.setUrl(url); settings.setWebPagePreview(YesOrNoEnum.Yes.getNum()); settings.setChatIdArrayJson(JSON.toJSONString(new ArrayList())); settings.setNotification(YesOrNoEnum.Yes.getNum()); settings.setZeroDelay(YesOrNoEnum.No.getNum()); MonitorTableRepository.insert(settings); showMonitorHandle(session, id); // MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitorFinish), false); // rssMessageHandle(session, settings, true, false); return StepResult.ok(); }) .build(); // Update StepsBuilder .create() .bindCommand(Command.Update) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId()); if (null == settings) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound, session.getText()), false); return StepResult.end(); } InlineKeyboardButtonListBuilder listBuilder = InlineKeyboardButtonListBuilder.create(); List inlineKeyboardButtonArrayList = new ArrayList<>(); for (Field field : settings.getClass().getDeclaredFields()) { DisplayConfigAnnotation annotation = field.getAnnotation(DisplayConfigAnnotation.class); if (null != annotation && annotation.set()) { listBuilder.add( InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), annotation.i18n()), CallbackBuilder.buildCallbackData(false, session, Command.Update, field.getName())) .build() ); } } listBuilder.add( InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.Back), CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId())) .add(I18nHandle.getText(session.getFromId(), I18nEnum.Refresh), CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId())) .build() ); MessageHandle.updateInlineKeyboardList(session.getCallbackQuery().getMessage(), session.getChatId(), session.getCallbackQuery().getMessage().getText(), listBuilder.build()); context.put("id", session.getText()); context.put("session", session); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { String id = (String) context.get("id"); MonitorTableEntity settings = MonitorTableRepository.selectOne(id, session.getFromId()); Field[] declaredFields = settings.getClass().getDeclaredFields(); Field field = null; for (Field declaredField : declaredFields) { DisplayConfigAnnotation annotation = declaredField.getAnnotation(DisplayConfigAnnotation.class); if (null != annotation && annotation.set()) { if (declaredField.getName().equals(session.getText())) { field = declaredField; break; } } } if (null == field) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor2, session.getText()), false); return StepResult.reject(); } context.put("field", session.getText()); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor3, session.getText()), false); if (session.getText().equals("notification") || session.getText().equals("enable") || session.getText().equals("webPagePreview") || session.getText().equals("zeroDelay")) { InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.On)); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Update, String.valueOf(YesOrNoEnum.Yes.getNum()))); InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton(); inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Off)); inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Update, String.valueOf(YesOrNoEnum.No.getNum()))); Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor4), inlineKeyboardButton, inlineKeyboardButton2); putDeleteMessage(context, message); return StepResult.ok(); } if (session.getText().equals("template")) { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor7), false); } else { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor4), false); } return StepResult.ok(); }, (StepsChatSession session, int index, List list, Map context) -> { String id = (String) context.get("id"); String fieldName = (String) context.get("field"); try { MonitorTableEntity settings = MonitorTableRepository.selectOne(id, session.getFromId()); Field[] declaredFields = settings.getClass().getDeclaredFields(); Field field = null; for (Field declaredField : declaredFields) { DisplayConfigAnnotation annotation = declaredField.getAnnotation(DisplayConfigAnnotation.class); if (null != annotation && annotation.set()) { if (declaredField.getName().equals(fieldName)) { field = declaredField; break; } } } if (null == field) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor2, session.getText()), false); return StepResult.reject(); } if (fieldName.equals("zeroDelay")) { settings.setZeroDelay(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum()); } else if (fieldName.equals("webPagePreview")) { settings.setWebPagePreview(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum()); } else if (fieldName.equals("enable")) { settings.setEnable(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum()); } else if (fieldName.equals("notification")) { settings.setNotification(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum()); } else if (fieldName.equals("chatIdArrayJson")) { String[] s = StringUtils.split(session.getText(), " "); if (s.length == 0) { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateFieldError), false); return StepResult.reject(); } settings.setChatIdArrayJson(JSON.toJSONString(s)); } else if (fieldName.equals("template")) { settings.setTemplate(session.getText()); } else if (fieldName.equals("url")) { settings.setUrl(session.getText()); } settings.setUpdateTime(System.currentTimeMillis()); MonitorTableRepository.update(settings); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitorFinish), false); showMonitorHandle((StepsChatSession) context.get("session"), id); deleteMessage(context); } catch (IllegalArgumentException e) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateFieldError), false); return StepResult.reject(); } return StepResult.end(); }) .build(); // Delete StepsBuilder .create() .bindCommand(Command.Delete) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm)); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Delete, session.getText())); InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton(); inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel)); inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Delete, "")); Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.DeleteMonitorConfirm), inlineKeyboardButton, inlineKeyboardButton2); putDeleteMessage(context, message); context.put("session", session); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { if (StringUtils.isNotBlank(session.getText())) { MonitorTableEntity entity = MonitorTableRepository.selectOne(list.get(0), session.getFromId()); if (null != entity) { SentRecordTableRepository.delete(entity.getName(), entity.getChatId()); MonitorTableRepository.delete(entity.getId()); } MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.DeleteMonitorFinish), false); showMonitorListHandle((StepsChatSession) context.get("session")); } else { MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false); } deleteMessage(context); return StepResult.end(); }) .build(); // Exit StepsBuilder .create() .bindCommand(Command.Exit) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { StepsCenter.exit(session); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.ExitSucceeded), false); return StepResult.end(); }) .build(); // List StepsBuilder .create() .bindCommand(Command.List) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { showMonitorListHandle(session); return StepResult.end(); }) .build(); // Get StepsBuilder .create() .bindCommand(Command.Get) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { showMonitorHandle(session, session.getText()); return StepResult.end(); }) .build(); // On StepsBuilder .create() .bindCommand(Command.On) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId()); if (null != settings) { settings.setEnable(YesOrNoEnum.Yes.getNum()); MonitorTableRepository.update(settings); showMonitorHandle(session, session.getText()); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false); } return StepResult.end(); }) .build(); // Off StepsBuilder .create() .bindCommand(Command.Off) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId()); if (null != settings) { settings.setEnable(YesOrNoEnum.No.getNum()); MonitorTableRepository.update(settings); showMonitorHandle(session, session.getText()); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false); } return StepResult.end(); }) .build(); StepsBuilder .create() .bindCommand(Command.SetCaptureFlag) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId()); if (null != settings) { if (null == settings.getCaptureFlag()) { settings.setCaptureFlag(YesOrNoEnum.No.getNum()); } boolean captureFlag = !YesOrNoEnum.get(settings.getCaptureFlag()).get().isBool(); if (captureFlag) { settings.setCaptureFlag(YesOrNoEnum.Yes.getNum()); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.SetCaptureFlagOnNote), false); } else { settings.setCaptureFlag(YesOrNoEnum.No.getNum()); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.SetCaptureFlagOffNote), false); } MonitorTableRepository.update(settings); showMonitorHandle(session, session.getText()); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false); } return StepResult.end(); }) .build(); // Test StepsBuilder .create() .bindCommand(Command.Test) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId()); if (null == settings) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.TestMonitor, session.getText()), false); return StepResult.end(); } rssMessageHandle(session, settings, true, false); return StepResult.end(); }) .build(); // Force Record StepsBuilder .create() .bindCommand(Command.ForceRecord) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId()); if (null == settings) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.TestMonitor, session.getText()), false); return StepResult.end(); } rssMessageHandle(session, settings, false, true); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.ForceRecordSucceeded, session.getText()), false); return StepResult.end(); }) .build(); // Language StepsBuilder .create() .bindCommand(Command.Language) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { ArrayList inlineKeyboardButtons = new ArrayList<>(); for (I18nLocaleEnum value : I18nLocaleEnum.values()) { InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(value.getDisplayText()); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Language, value.getAlias())); inlineKeyboardButtons.add(inlineKeyboardButton); } MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.LanguageList), inlineKeyboardButtons); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { I18nLocaleEnum alias = I18nLocaleEnum.getI18nLocaleEnumByAlias(session.getText()); I18nHandle.save(session.getFromId(), alias); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.ChangeLanguageFinish), false); return StepResult.end(); }) .build(); // Admin StepsBuilder .create() .bindCommand(Command.Admin) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .steps((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } ConfigSettings config = Config.readConfig(); List> keyboardButton = InlineKeyboardButtonListBuilder .create() .add(InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.SetChatButtons), CallbackBuilder.buildCallbackData(true, session, Command.SetChatButtons, null)) .add(I18nHandle.getText(session.getFromId(), I18nEnum.SettingWebhook), CallbackBuilder.buildCallbackData(true, session, Command.Webhook, null)) .build() ) .add(InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.UpdateConfig), CallbackBuilder.buildCallbackData(true, session, Command.UpdateConfig, null)) .add(I18nHandle.getText(session.getFromId(), I18nEnum.HideCopyrightTips), CallbackBuilder.buildCallbackData(true, session, Command.HideCopyrightTips, null)) .build() ) .add(InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.VerifySsl) + String.format("(%s)", config.getVerifySsl() ? I18nHandle.getText(session.getFromId(), I18nEnum.Enable) : I18nHandle.getText(session.getFromId(), I18nEnum.Disable)), CallbackBuilder.buildCallbackData(true, session, Command.SetVerifySsl, null)) .build() ) .add(InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.ExcludeKeywords), CallbackBuilder.buildCallbackData(true, session, Command.SetExcludeKeywords, null)) .add(I18nHandle.getText(session.getFromId(), I18nEnum.ExcludeKeywordsRegex), CallbackBuilder.buildCallbackData(true, session, Command.SetExcludeKeywordsRegex, null)) .build() ) .add(InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.IncludeKeywords), CallbackBuilder.buildCallbackData(true, session, Command.SetIncludeKeywords, null)) .add(I18nHandle.getText(session.getFromId(), I18nEnum.IncludeKeywordsRegex), CallbackBuilder.buildCallbackData(true, session, Command.SetIncludeKeywordsRegex, null)) .build() ) .add(InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.Restart), CallbackBuilder.buildCallbackData(true, session, Command.Restart, null)) .add(I18nHandle.getText(session.getFromId(), I18nEnum.Upgrade), CallbackBuilder.buildCallbackData(true, session, Command.Upgrade, null)) .build() ) .build(); Properties properties = System.getProperties(); StringBuilder builder = new StringBuilder(); builder.append("os.name: "); builder.append(properties.getProperty("os.name")); builder.append("\n"); builder.append("os.arch: "); builder.append(properties.getProperty("os.arch")); code.handler.message.MessageHandle.sendInlineKeyboardList(session.getFromId(), builder.toString(), keyboardButton); return StepResult.end(); }) .build(); StepsBuilder .create() .bindCommand(Command.SetExcludeKeywordsRegex) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } ConfigSettings config = Config.readConfig(); List excludeKeywordsRegex = config.getExcludeKeywordsRegex(); if (null != excludeKeywordsRegex && !excludeKeywordsRegex.isEmpty()) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), excludeKeywordsRegex.stream().collect(Collectors.joining("\n")), false); } MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeExcludeKeywordsRegex), false); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); if (StringUtils.isBlank(text)) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false); return StepResult.reject(); } List excludeKeywordsRegex = new ArrayList<>(); if (!text.equals("-1")) { String[] split = StringUtils.split(text, "\n"); for (String s : split) { if (StringUtils.isNotBlank(s)) { excludeKeywordsRegex.add(s); } } } ConfigSettings config = Config.readConfig(); config.setExcludeKeywordsRegex(excludeKeywordsRegex); Config.saveConfig(config); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + excludeKeywordsRegex.stream().collect(Collectors.joining("\n")), false); return StepResult.ok(); }) .build(); // Set Exclude Keywords StepsBuilder .create() .bindCommand(Command.SetExcludeKeywords) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } ConfigSettings config = Config.readConfig(); List excludeKeywords = config.getExcludeKeywords(); if (null != excludeKeywords && !excludeKeywords.isEmpty()) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), excludeKeywords.stream().collect(Collectors.joining("\n")), false); } MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeExcludeKeywords), false); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); if (StringUtils.isBlank(text)) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false); return StepResult.reject(); } List excludeKeywords = new ArrayList<>(); if (!text.equals("-1")) { String[] split = StringUtils.split(text, "\n"); for (String s : split) { if (StringUtils.isNotBlank(s)) { excludeKeywords.add(s); } } } ConfigSettings config = Config.readConfig(); config.setExcludeKeywords(excludeKeywords); Config.saveConfig(config); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + excludeKeywords.stream().collect(Collectors.joining("\n")), false); return StepResult.ok(); }) .build(); StepsBuilder .create() .bindCommand(Command.SetIncludeKeywordsRegex) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } ConfigSettings config = Config.readConfig(); List includeKeywordsRegex = config.getIncludeKeywordsRegex(); if (null != includeKeywordsRegex && !includeKeywordsRegex.isEmpty()) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), includeKeywordsRegex.stream().collect(Collectors.joining("\n")), false); } MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeIncludeKeywordsRegex), false); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); if (StringUtils.isBlank(text)) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false); return StepResult.reject(); } List includeKeywordsRegex = new ArrayList<>(); if (!text.equals("-1")) { String[] split = StringUtils.split(text, "\n"); for (String s : split) { if (StringUtils.isNotBlank(s)) { includeKeywordsRegex.add(s); } } } ConfigSettings config = Config.readConfig(); config.setIncludeKeywordsRegex(includeKeywordsRegex); Config.saveConfig(config); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + includeKeywordsRegex.stream().collect(Collectors.joining("\n")), false); return StepResult.ok(); }) .build(); StepsBuilder .create() .bindCommand(Command.SetIncludeKeywords) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } ConfigSettings config = Config.readConfig(); List includeKeywords = config.getIncludeKeywords(); if (null != includeKeywords && !includeKeywords.isEmpty()) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), includeKeywords.stream().collect(Collectors.joining("\n")), false); } MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeIncludeKeywords), false); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); if (StringUtils.isBlank(text)) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false); return StepResult.reject(); } List includeKeywords = new ArrayList<>(); if (!text.equals("-1")) { String[] split = StringUtils.split(text, "\n"); for (String s : split) { if (StringUtils.isNotBlank(s)) { includeKeywords.add(s); } } } ConfigSettings config = Config.readConfig(); config.setIncludeKeywords(includeKeywords); Config.saveConfig(config); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + includeKeywords.stream().collect(Collectors.joining("\n")), false); return StepResult.ok(); }) .build(); // Set Chat Buttons StepsBuilder .create() .bindCommand(Command.SetChatButtons) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } ConfigSettings config = Config.readConfig(); String chatButtons = config.getChatButtons(); if (StringUtils.isNotBlank(chatButtons)) { code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), chatButtons, false); } code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeChatButtons), false); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); if (StringUtils.isBlank(text)) { code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false); return StepResult.reject(); } if (!text.equals("-1")) { Optional buttons = ChatButtonsStore.verify(text); if (!buttons.isPresent()) { code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false); return StepResult.reject(); } ChatButtonsStore.ChatButtonsToInlineKeyboardButtons keyboardButtons = buttons.get(); for (Map.Entry> entry : keyboardButtons.getMap().entrySet()) { List> build = InlineKeyboardButtonListBuilder .create() .add(entry.getValue()) .build(); code.handler.message.MessageHandle.sendInlineKeyboardList(session.getChatId(), session.getReplyToMessageId(), entry.getKey(), build); } } ChatButtonsStore.set(text); ConfigSettings config = Config.readConfig(); config.setChatButtons(text); Config.saveConfig(config); code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false); return StepResult.ok(); }) .build(); // Hide Copyright Tips StepsBuilder .create() .bindCommand(Command.HideCopyrightTips) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm)); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.HideCopyrightTips, "true")); InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton(); inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel)); inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.HideCopyrightTips, "false")); Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureYouWantToHideCopyrightTips), inlineKeyboardButton, inlineKeyboardButton2); putDeleteMessage(context, message); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { Boolean of = Boolean.valueOf(session.getText()); ConfigSettings configSettings = Config.readConfig(); configSettings.setHideCopyrightTips(of); Config.saveConfig(configSettings); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false); deleteMessage(context); return StepResult.end(); }) .build(); StepsBuilder .create() .bindCommand(Command.SetVerifySsl) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Enable)); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.SetVerifySsl, "true")); InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton(); inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Disable)); inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.SetVerifySsl, "false")); Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureYouWantToSetVerifySsl), inlineKeyboardButton, inlineKeyboardButton2); putDeleteMessage(context, message); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { Boolean of = Boolean.valueOf(session.getText()); ConfigSettings configSettings = Config.readConfig(); configSettings.setVerifySsl(of); Config.saveConfig(configSettings); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + ", " + I18nHandle.getText(session.getFromId(), I18nEnum.NeedToRestartBot), false); deleteMessage(context); return StepResult.end(); }) .build(); // Webhook StepsBuilder .create() .bindCommand(Command.Webhook) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } StringBuilder builder = new StringBuilder(); builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.CurrentSetting) + ": \n\n"); WebhookTableEntity webhookTableEntity = WebhookTableRepository.selectOne(session.getFromId()); if (null == webhookTableEntity) { builder.append(Config.WebhookJson); } else { builder.append(webhookTableEntity.getSettingsJson()); } builder.append("\n\n"); builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToUpdateTheConfig)); InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm)); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Webhook, "true")); InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton(); inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel)); inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Webhook, "false")); Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), builder.toString(), inlineKeyboardButton, inlineKeyboardButton2); putDeleteMessage(context, message); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { Boolean of = Boolean.valueOf(session.getText()); if (of) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeWebhookSettings), false); return StepResult.ok(); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false); deleteMessage(context); return StepResult.end(); } }, (StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); boolean verify = WebhookStore.verify(text); if (!verify) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateConfigFail), false); return StepResult.reject(); } WebhookStore.Webhook webhook = WebhookStore.get(text).get(); WebhookTableEntity webhookTableEntity = new WebhookTableEntity(); webhookTableEntity.setChatId(session.getFromId()); webhookTableEntity.setSettingsJson(JSON.toJSONString(webhook, JSONWriter.Feature.PrettyFormat)); WebhookTableRepository.save(webhookTableEntity); MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false); deleteMessage(context); return StepResult.end(); }) .build(); // Restart StepsBuilder .create() .bindCommand(Command.Restart) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm)); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Restart, "true")); InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton(); inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel)); inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Restart, "false")); MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToRestartRightNow), inlineKeyboardButton, inlineKeyboardButton2); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { Boolean of = Boolean.valueOf(session.getText()); if (of) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.Restarting), false); ProgramUtil.restart(Config.MetaData.ProcessName); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false); } return StepResult.end(); }) .build(); // Update config StepsBuilder .create() .bindCommand(Command.UpdateConfig) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession session) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } List buttons = InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm), CallbackBuilder.buildCallbackData(false, session, Command.UpdateConfig, "confirm")) .add(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel), CallbackBuilder.buildCallbackData(false, session, Command.UpdateConfig, "cancel")) .build(); ConfigSettings config = Config.readConfig(); MessageHandle.sendMessage(session.getFromId(), JSON.toJSONString(config, JSONWriter.Feature.PrettyFormat), false); Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToUpdateTheConfig), buttons); putDeleteMessage(context, message); return StepResult.ok(); }) .steps((StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); if (text.equals("confirm")) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeConfigContent), false); return StepResult.ok(); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false); deleteMessage(context); return StepResult.end(); } }, (StepsChatSession session, int index, List list, Map context) -> { String text = session.getText(); ConfigSettings configSettings = Config.verifyConfig(text); if (null == configSettings) { code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateConfigFail), false); return StepResult.reject(); } boolean b = Config.saveConfig(configSettings); if (b) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateFailed), false); } deleteMessage(context); return StepResult.end(); }) .build(); // Upgrade StepsBuilder .create() .bindCommand(Command.Upgrade) .debug(GlobalConfig.getDebug()) .error((Exception e, StepsChatSession stepsChatSession) -> { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); MessageHandle.sendMessage(stepsChatSession.getChatId(), I18nHandle.getText(stepsChatSession.getFromId(), I18nEnum.UnknownError), false); }) .init((StepsChatSession session, int index, List list, Map context) -> { if (!isAdmin(session.getFromId())) { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false); return StepResult.end(); } Message message = MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.GettingUpdateData), false); GithubUtil.LatestReleaseResponse release = GithubUtil.getLatestRelease(RequestProxyConfig.create(), Config.MetaData.GitOwner, Config.MetaData.GitRepo); if (release.isOk()) { StringBuilder builder = new StringBuilder(); builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToUpgradeThisBotRightNow)); builder.append("\n"); builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.TargetVersion) + ": "); builder.append(release.getTagName()); builder.append("\n"); builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.CurrentVersion) + ": "); builder.append(Config.MetaData.CurrentVersion); builder.append("\n"); builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.UpdateLogs) + ": "); builder.append("\n"); builder.append(release.getBody()); InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton(); inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm)); inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Upgrade, "true")); InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton(); inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel)); inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Upgrade, "false")); MessageHandle.sendInlineKeyboard(session.getChatId(), builder.toString(), inlineKeyboardButton, inlineKeyboardButton2); String url = ""; for (GithubUtil.LatestReleaseAsset asset : release.getAssets()) { if (Config.MetaData.JarName.equals(asset.getName())) { url = asset.getBrowserDownloadUrl(); break; } } context.put("url", url); return StepResult.ok(); } else { MessageHandle.editMessage(message, I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError)); return StepResult.end(); } }) .steps((StepsChatSession session, int index, List list, Map context) -> { Boolean of = Boolean.valueOf(session.getText()); if (of) { Message message = MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.Updating), false); String url = (String) context.get("url"); AtomicInteger count = new AtomicInteger(); String temp = System.getProperty("user.dir") + "/temp.jar"; log.info("temp: " + temp); boolean b = DownloadUtil.download( RequestProxyConfig.create(), url, temp, (String var1, String var2, Long var3, Long var4) -> { if ((var4 - var3) > 0) { count.incrementAndGet(); if (count.get() == 100) { MessageHandle.editMessage(message, I18nHandle.getText(session.getFromId(), I18nEnum.Downloaded, BytesUtil.toDisplayStr(var3), BytesUtil.toDisplayStr(var4))); count.set(0); } } } ); if (b) { System.exit(1); } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false); } } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false); } return StepResult.end(); }) .build(); } private static void putDeleteMessage(Map context, Message message) { context.put("delete", message); } private static void deleteMessage(Map context) { try { if (context.containsKey("delete")) { MessageHandle.deleteMessage((Message) context.get("delete")); } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } private static void showMonitorHandle(StepsChatSession session, String id) { MonitorTableEntity settings = MonitorTableRepository.selectOne(id, session.getFromId()); if (null != settings) { if (null == settings.getCaptureFlag()) { settings.setCaptureFlag(YesOrNoEnum.No.getNum()); } boolean captureFlag = YesOrNoEnum.get(settings.getCaptureFlag()).get().isBool(); List> build = InlineKeyboardButtonListBuilder .create() .add( InlineKeyboardButtonBuilder .create() .add((YesOrNoEnum.get(settings.getEnable()).get().isBool() ? "✅ " : "") + I18nHandle.getText(session.getFromId(), I18nEnum.On), CallbackBuilder.buildCallbackData(true, session, Command.On, settings.getId())) .add((!YesOrNoEnum.get(settings.getEnable()).get().isBool() ? "❌ " : "") + I18nHandle.getText(session.getFromId(), I18nEnum.Off), CallbackBuilder.buildCallbackData(true, session, Command.Off, settings.getId())) .build() ) .add( InlineKeyboardButtonBuilder .create() .add((captureFlag ? "✅ " : "❌ ") + I18nHandle.getText(session.getFromId(), I18nEnum.SetCaptureFlag), CallbackBuilder.buildCallbackData(true, session, Command.SetCaptureFlag, settings.getId())) .build() ) .add( InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.Test), CallbackBuilder.buildCallbackData(true, session, Command.Test, settings.getId())) .add(I18nHandle.getText(session.getFromId(), I18nEnum.ForceRecord), CallbackBuilder.buildCallbackData(true, session, Command.ForceRecord, settings.getId())) .build() ) .add( InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.Update), CallbackBuilder.buildCallbackData(true, session, Command.Update, settings.getId())) .add(I18nHandle.getText(session.getFromId(), I18nEnum.Delete), CallbackBuilder.buildCallbackData(true, session, Command.Delete, settings.getId())) .build() ) .add( InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.Refresh), CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId())) .build() ) .add( InlineKeyboardButtonBuilder .create() .add(I18nHandle.getText(session.getFromId(), I18nEnum.Back), CallbackBuilder.buildCallbackData(true, session, Command.List, "")) .build() ) .build(); if (null == session.getCallbackQuery()) { MessageHandle.sendInlineKeyboardList(session.getChatId(), getMonitorData(session, settings), build); } else { MessageHandle.updateInlineKeyboardList(session.getCallbackQuery().getMessage(), session.getChatId(), getMonitorData(session, settings), build); } } else { MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false); } } private static void showMonitorListHandle(StepsChatSession session) { MonitorTableEntity where = new MonitorTableEntity(); where.setChatId(session.getFromId()); PageEntity page = MonitorTableRepository.page(where, 5, NumberUtils.toInt(session.getText(), 1), "order by create_time desc"); if (!page.isHasNext() && page.getList().isEmpty()) { page = MonitorTableRepository.page(where, 5, 1, "order by create_time desc"); } List entityList = page.getList(); if (entityList.size() > 0) { StringBuilder builder = new StringBuilder(); ArrayList inlineKeyboardButtons = new ArrayList<>(); for (MonitorTableEntity settings : entityList) { builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.MonitorList, settings.getName(), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(settings.getEnable()).get()))); builder.append("\n\n"); InlineKeyboardButton button = new InlineKeyboardButton(); button.setText(settings.getName()); button.setCallbackData(CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId())); inlineKeyboardButtons.add(button); } List> build = InlineKeyboardButtonListBuilder .create() .add(inlineKeyboardButtons) .pagination(page, session, Command.List) .build(); if (null == session.getCallbackQuery()) { MessageHandle.sendInlineKeyboardList(session.getChatId(), builder.toString(), build); } else { MessageHandle.updateInlineKeyboardList(session.getCallbackQuery().getMessage(), session.getChatId(), builder.toString(), build); } } else { if (null != session.getCallbackQuery()) { MessageHandle.deleteMessage(session.getCallbackQuery().getMessage()); } MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NothingHere), false); } } private static void rssMessageHandle(StepsChatSession session, MonitorTableEntity entity, boolean isTest, boolean forceRecord) { try { Boolean on = YesOrNoEnum.toBoolean(entity.getEnable()).get(); String name = entity.getName(); if ((null != on && on) || isTest || forceRecord) { SyndFeed feed = RssUtil.getFeed(RequestProxyConfig.create(), entity.getUrl()); if (null == feed) { if (isTest) MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor5), false); return; } List entries = feed.getEntries(); if (null == entries || entries.isEmpty()) { if (isTest) MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.NothingAtAll), false); return; } if (!SentRecordTableRepository.exists(name, entity.getChatId()) || forceRecord) { for (int i = 0; i < entries.size(); i++) { SyndEntry entry = entries.get(i); String uri = entry.getUri(); if (StringUtils.isBlank(uri)) { uri = entry.getLink(); } SentRecordTableEntity sentRecordTableEntity = new SentRecordTableEntity(); sentRecordTableEntity.setId(Snowflake.nextIdToStr()); sentRecordTableEntity.setCreateTime(System.currentTimeMillis()); sentRecordTableEntity.setName(name); sentRecordTableEntity.setChatId(entity.getChatId()); sentRecordTableEntity.setUri(uri); SentRecordTableRepository.save(sentRecordTableEntity); } } String template = entity.getTemplate(); for (int i = 0; i < entries.size(); i++) { SyndEntry entry = entries.get(i); String uri = entry.getUri(); if (StringUtils.isBlank(uri)) { uri = entry.getLink(); } if (SentRecordTableRepository.exists(uri, name, entity.getChatId()) && !isTest) { continue; } String text = replaceTemplate(template, feed, entry); if (StringUtils.isNotBlank(text)) { List images = null; Integer captureFlag = (null == entity.getCaptureFlag() ? YesOrNoEnum.No.getNum() : entity.getCaptureFlag()); Optional captureFlagBoolean = YesOrNoEnum.toBoolean(captureFlag); if (captureFlagBoolean.isPresent() && captureFlagBoolean.get()) { images = getImages(entry); } if (!isTest) { List chatIdArray = JSON.parseArray(entity.getChatIdArrayJson(), String.class); if (null == chatIdArray || chatIdArray.isEmpty()) { chatIdArray = Arrays.asList(GlobalConfig.getChatIdArray()); } for (String s : chatIdArray) { if (!containsExcludeKeywords(text)) { if (isEnableIncludeKeywords()) { if (containsIncludeKeywords(text)) { sendRss(s, session, entity, text, images); } } else { sendRss(s, session, entity, text, images); } } } if (chatIdArray.size() > 0) { SentRecordTableEntity sentRecordTableEntity = new SentRecordTableEntity(); sentRecordTableEntity.setId(Snowflake.nextIdToStr()); sentRecordTableEntity.setCreateTime(System.currentTimeMillis()); sentRecordTableEntity.setName(name); sentRecordTableEntity.setChatId(entity.getChatId()); sentRecordTableEntity.setUri(uri); SentRecordTableRepository.save(sentRecordTableEntity); } } else { sendRss(session.getChatId(), session, entity, text, images); if (i >= 2) { break; } } } } } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); if (isTest) MessageHandle.sendMessage(GlobalConfig.getBotAdminId(), e.getMessage(), false); } } private static boolean isEnableIncludeKeywords() { ConfigSettings configSettings = Config.readConfig(); if (configSettings.getIncludeKeywords().isEmpty() && configSettings.getIncludeKeywordsRegex().isEmpty()) { return false; } return true; } private static boolean containsIncludeKeywords(String text) { try { if (StringUtils.isNotBlank(text)) { ConfigSettings configSettings = Config.readConfig(); for (String includeKeywords : configSettings.getIncludeKeywords()) { if (StringUtils.containsIgnoreCase(text, includeKeywords)) { return true; } } for (String includeKeywordsRegex : configSettings.getIncludeKeywordsRegex()) { Pattern pattern = Pattern.compile(includeKeywordsRegex); Matcher matcher = pattern.matcher(text); if (matcher.find()) { return true; } } } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return false; } private static boolean containsExcludeKeywords(String text) { try { if (StringUtils.isNotBlank(text)) { ConfigSettings configSettings = Config.readConfig(); for (String excludeKeyword : configSettings.getExcludeKeywords()) { if (StringUtils.containsIgnoreCase(text, excludeKeyword)) { return true; } } for (String excludeKeywordsRegex : configSettings.getExcludeKeywordsRegex()) { Pattern pattern = Pattern.compile(excludeKeywordsRegex); Matcher matcher = pattern.matcher(text); if (matcher.find()) { return true; } } } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return false; } private static void sendRss(String chatId, StepsChatSession session, MonitorTableEntity entity, String text, List images) { List> build = null; Optional buttons = ChatButtonsStore.get(); if (buttons.isPresent()) { Optional> inlineKeyboardButtonList = buttons.get().getButtons(session.getChatId()); if (inlineKeyboardButtonList.isPresent()) { build = InlineKeyboardButtonListBuilder .create() .add(inlineKeyboardButtonList.get()) .build(); } } boolean sendText = true; if (null != images && !images.isEmpty()) { try { boolean sendSingleImage = true; if (images.size() > 1) { List inputMedia = new ArrayList<>(); AtomicInteger countAtomic = new AtomicInteger(0); for (String image : images) { if (inputMedia.size() >= 10) { break; } String name = UUID.randomUUID().toString(); String temp = Config.TempDir + File.separator + name + ".png"; boolean download = DownloadUtil.download(RequestProxyConfig.create(), image, temp); if (download) { int count = countAtomic.addAndGet(1); InputMediaPhoto inputMediaPhoto = new InputMediaPhoto(); inputMediaPhoto.setMedia(new File(temp), name); if (count == 1) { inputMediaPhoto.setCaption(text); } inputMedia.add(inputMediaPhoto); } } if (inputMedia.size() >= 2) { List messages = MessageHandle.sendMediaGroup(chatId, inputMedia, YesOrNoEnum.toBoolean(entity.getNotification()).get()); if (null != messages && !messages.isEmpty()) { sendSingleImage = false; sendText = false; } } } if (sendSingleImage) { String image = images.get(0); String temp = Config.TempDir + File.separator + UUID.randomUUID() + ".png"; boolean download = DownloadUtil.download(RequestProxyConfig.create(), image, temp); if (download) { Message message = MessageHandle.sendImage(chatId, null, text, new File(temp), build); if (null != message) { sendText = false; } } } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } if (sendText) { MessageHandle.sendMessage(chatId, null, text, YesOrNoEnum.toBoolean(entity.getWebPagePreview()).get(), YesOrNoEnum.toBoolean(entity.getNotification()).get(), build); } WebhookTableEntity webhookTableEntity = WebhookTableRepository.selectOne(entity.getChatId()); if (null != webhookTableEntity) { Optional webhookOptional = WebhookStore.get(webhookTableEntity.getSettingsJson()); if (webhookOptional.isPresent()) { WebhookStore.Webhook webhook = webhookOptional.get(); if (webhook.isEnable()) { try { for (WebhookStore.WebhookRequest request : webhook.getList()) { String body = JSON.toJSONString(request.getBody()); String str = JSON.toJSONString(text); str = StringUtils.removeStart(str, "\""); str = StringUtils.removeEnd(str, "\""); body = StringUtils.replace(body, "${text}", str); RequestBodyEntity requestBody = Unirest .request(request.getMethod(), request.getUrl()) .headers(request.getHeaders()) .body(body) .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) .socketTimeout((int) TimeUnit.MILLISECONDS.convert(40, TimeUnit.SECONDS)); if (GlobalConfig.getOnProxy()) { requestBody.proxy(GlobalConfig.getProxyHost(), GlobalConfig.getProxyPort()); } HttpResponse rsp = requestBody.asString(); log.info("Webhook request, url: {}, body: {}, rsp: {}", request.getUrl(), body, rsp.getBody()); } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } } } } private static List getImages(SyndEntry entry) { List list = new ArrayList<>(); if (null != entry) { SyndContent description = entry.getDescription(); if ("text/html".equals(description.getType())) { try { Document document = Jsoup.parse(description.getValue()); if (null != document) { Elements images = document.select("img"); for (Element image : images) { String imageUrl = image.attr("src"); if (StringUtils.isNotBlank(imageUrl)) { list.add(imageUrl); } } } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } } return list; } public static String getDescription(SyndEntry entry) { if (null != entry) { SyndContent description = entry.getDescription(); if ("text/html".equals(description.getType())) { return getDescription(description.getValue()); } else { return StringUtils.defaultIfBlank(description.getValue(), ""); } } return ""; } public static String getDescription(String html) { if (null != html) { try { Document document = Jsoup.parse(html); if (null != document) { Elements br = document.select("br"); for (Element element : br) { element.html("\n"); } String text = document.wholeText(); return StringUtils.defaultIfBlank(text, ""); } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } return ""; } private static String replaceTemplate(String template, SyndFeed feed, SyndEntry entry) { try { if (StringUtils.isBlank(template) || null == entry) { return null; } String s = new String(template); if (template.contains("${translate")) { try { String pattern = "\\$\\{translate\\|(\\w+-\\w+|\\w+)\\|\\w+\\}"; Pattern regex = Pattern.compile(pattern); Matcher matcher = regex.matcher(s); while (matcher.find()) { String variable = matcher.group(); if (StringUtils.isNotBlank(variable)) { String variableEdit = StringUtils.removeStart(variable, "${"); variableEdit = StringUtils.removeEnd(variableEdit, "}"); String[] split = StringUtils.split(variableEdit, "|"); if (split.length == 3) { String s1 = split[0]; String s2 = split[1]; String s3 = split[2]; String text = ""; if ("title".equals(s3)) { text = entry.getTitle(); } else if ("description".equals(s3)) { text = getDescription(entry); } String translate = Translate.translate(text, "auto", s2); s = StringUtils.replace(s, variable, translate); } } } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } if (template.contains("${link}")) { s = StringUtils.replace(s, "${link}", entry.getLink()); } if (template.contains("${title}")) { s = StringUtils.replace(s, "${title}", entry.getTitle()); } if (template.contains("${description}")) { s = StringUtils.replace(s, "${description}", getDescription(entry)); } String author = entry.getAuthor(); if (StringUtils.isBlank(author)) { author = feed.getAuthor(); } if (StringUtils.isBlank(author)) { List authors = feed.getAuthors(); author = authors.size() > 0 ? authors.get(0).getName() : ""; } if (template.contains("${author}")) { s = StringUtils.replace(s, "${author}", author); } if (template.contains("${telegraph}")) { String html = null; List contents = entry.getContents(); if (contents.size() > 0) { String value = contents.get(0).getValue(); if (StringUtils.isNotBlank(value)) { html = value; } } if (StringUtils.isBlank(html)) { SyndContent description = entry.getDescription(); if (null != description) { String value = description.getValue(); if (StringUtils.isNotBlank(value)) { html = value; } } } if (StringUtils.isNotBlank(html)) { String telegraphHtml = null; if (!GlobalConfig.getHideCopyrightTips()) { telegraphHtml = replaceTelegraphHtml(entry.getLink(), entry.getTitle()); } TelegraphUtil.SaveResponse response = TelegraphUtil.save(RequestProxyConfig.create(), entry.getTitle(), author, html, telegraphHtml); if (response.isOk()) { s = StringUtils.replace(s, "${telegraph}", response.getUrl()); } else { s = StringUtils.replace(s, "${telegraph}", ""); } } else { s = StringUtils.replace(s, "${telegraph}", ""); } } return s; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } private static String replaceTelegraphHtml(String link, String title) { String s = StringUtils.replace(Config.TelegraphHtml, "${link}", link); return StringUtils.replace(s, "${title}", title); } private static String getEnableDisplayI18nText(String fromId, YesOrNoEnum yesOrNoEnum) { switch (yesOrNoEnum) { case Yes: return I18nHandle.getText(fromId, I18nEnum.On); case No: return I18nHandle.getText(fromId, I18nEnum.Off); default: return ""; } } private static String getMonitorData(StepsChatSession session, MonitorTableEntity entity) { String chatIdArrayStr = ""; List chatIdArray = JSON.parseArray(entity.getChatIdArrayJson(), String.class); if (null == chatIdArray || chatIdArray.isEmpty()) { chatIdArrayStr = StringUtils.join(GlobalConfig.getChatIdArray(), " "); } else { chatIdArrayStr = StringUtils.join(chatIdArray, " "); } StringBuilder builder = new StringBuilder(); builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayFileBasename), entity.getName())); builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayOn), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getEnable()).get()))); builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayWebPagePreview), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getWebPagePreview()).get()))); builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayNotification), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getNotification()).get()))); builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayZeroDelay), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getZeroDelay()).get()))); builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayUrl), entity.getUrl())); builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayChatIdArray), chatIdArrayStr)); builder.append(String.format("%s: \n%s", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayTemplate), entity.getTemplate())); return builder.toString(); } } ================================================ FILE: src/main/java/code/handler/I18nHandle.java ================================================ package code.handler; import code.config.I18nConfig; import code.config.I18nEnum; import code.config.I18nLocaleEnum; import org.apache.commons.lang3.StringUtils; import java.util.HashMap; import java.util.Map; import static code.Main.I18nTableRepository; public class I18nHandle { private static Map cacheMap = new HashMap<>(); public static String getText(String chatId, String key) { return getText(chatId, key, null); } public static String getText(String chatId, String key, Object... args) { String alias = cacheMap.get(chatId); if (StringUtils.isBlank(alias)) { alias = I18nTableRepository.selectI18nAlias(chatId); cacheMap.put(chatId, alias); } String text = I18nConfig.getText(alias, key); if (null != args && args.length > 0) { return String.format(text, args); } return text; } public static String getText(String chatId, I18nEnum i18nEnum) { return getText(chatId, i18nEnum, null); } public static String getText(String chatId, I18nEnum i18nEnum, Object... args) { String alias = cacheMap.get(chatId); if (StringUtils.isBlank(alias)) { alias = I18nTableRepository.selectI18nAlias(chatId); cacheMap.put(chatId, alias); } String text = I18nConfig.getText(alias, i18nEnum); if (null != args && args.length > 0) { return String.format(text, args); } return text; } public static void save(String chatId, I18nLocaleEnum i18nLocaleEnum) { I18nTableRepository.save(chatId, i18nLocaleEnum.getAlias()); cacheMap.put(chatId, i18nLocaleEnum.getAlias()); } } ================================================ FILE: src/main/java/code/handler/MessageHandle.java ================================================ package code.handler; import code.util.ExceptionUtil; import com.alibaba.fastjson2.JSON; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.telegram.telegrambots.meta.api.methods.ParseMode; import org.telegram.telegrambots.meta.api.methods.send.SendMediaGroup; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.methods.send.SendPhoto; import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage; import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText; import org.telegram.telegrambots.meta.api.objects.InputFile; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.media.InputMedia; import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup; import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static code.Main.Bot; @Slf4j public class MessageHandle { public enum MessageError { BotWasBlockedByTheUser, ; } @Data public static class MessageResponse { private boolean ok; private Message message; private MessageError messageError; } public static Message sendImage(String chatId, Integer replyToMessageId, String text, InputStream image) { SendPhoto sendPhoto = new SendPhoto(); sendPhoto.setChatId(chatId); sendPhoto.setReplyToMessageId(replyToMessageId); sendPhoto.setCaption(text); sendPhoto.setPhoto(new InputFile(image, "image.png")); try { return Bot.execute(sendPhoto); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image) { SendPhoto sendPhoto = new SendPhoto(); sendPhoto.setChatId(chatId); sendPhoto.setReplyToMessageId(replyToMessageId); sendPhoto.setCaption(text); sendPhoto.setPhoto(new InputFile(image)); try { return Bot.execute(sendPhoto); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image, List> keyboard) { SendPhoto sendPhoto = new SendPhoto(); sendPhoto.setChatId(chatId); sendPhoto.setReplyToMessageId(replyToMessageId); sendPhoto.setCaption(text); sendPhoto.setPhoto(new InputFile(image)); if (null != keyboard) { InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); inlineKeyboardMarkup.setKeyboard(keyboard); sendPhoto.setReplyMarkup(inlineKeyboardMarkup); } try { return Bot.execute(sendPhoto); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static void updateInlineKeyboardList(Message message, String chatId, String text, List> keyboard) { EditMessageText editMessageText = new EditMessageText(); editMessageText.setChatId(chatId); editMessageText.setText(text); editMessageText.setDisableWebPagePreview(true); editMessageText.setMessageId(message.getMessageId()); InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); inlineKeyboardMarkup.setKeyboard(keyboard); editMessageText.setReplyMarkup(inlineKeyboardMarkup); try { Bot.execute(editMessageText); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } public static Message sendInlineKeyboardList(String chatId, String text, List> keyboard) { return sendInlineKeyboardList(chatId, null, text, keyboard); } public static Message sendInlineKeyboardList(String chatId, Integer replyToMessageId, String text, List> keyboard) { SendMessage message = new SendMessage(); message.setChatId(chatId); message.setText(text); message.setReplyToMessageId(replyToMessageId); message.setDisableWebPagePreview(true); InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); inlineKeyboardMarkup.setKeyboard(keyboard); message.setReplyMarkup(inlineKeyboardMarkup); try { return Bot.execute(message); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendInlineKeyboard(String chatId, String text, InlineKeyboardButton... inlineKeyboardButtonList) { return sendInlineKeyboard(chatId, text, Arrays.asList(inlineKeyboardButtonList)); } public static Message sendInlineKeyboard(String chatId, String text, List inlineKeyboardButtonList) { SendMessage message = new SendMessage(); message.setChatId(chatId); message.setText(text); InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); List> keyboard = new ArrayList<>(); for (InlineKeyboardButton button : inlineKeyboardButtonList) { List list = new ArrayList<>(); list.add(button); keyboard.add(list); } inlineKeyboardMarkup.setKeyboard(keyboard); message.setReplyMarkup(inlineKeyboardMarkup); try { return Bot.execute(message); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendCustomKeyboard(String chatId, String text, KeyboardRow row) { List list = new ArrayList<>(); list.add(row); return sendCustomKeyboard(chatId, text, list); } public static Message sendCustomKeyboard(String chatId, String text, List keyboard) { SendMessage message = new SendMessage(); message.setChatId(chatId); message.setText(text); ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup(); keyboardMarkup.setKeyboard(keyboard); message.setReplyMarkup(keyboardMarkup); try { return Bot.execute(message); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static MessageResponse sendMsg(String chatId, String text, boolean webPagePreview) { MessageResponse response = new MessageResponse(); response.setOk(false); SendMessage sendMessage = new SendMessage(); sendMessage.setChatId(chatId); sendMessage.setText(text); sendMessage.setParseMode(ParseMode.HTML); if (!webPagePreview) { sendMessage.disableWebPagePreview(); } try { Message execute = Bot.execute(sendMessage); response.setOk(true); response.setMessage(execute); return response; } catch (Exception e) { String message = e.getMessage(); if (message.contains("bot was blocked by the user")) { response.setMessageError(MessageError.BotWasBlockedByTheUser); } else { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage))); } } return response; } public static List sendMediaGroup(String chatId, List mediaList, boolean notification) { SendMediaGroup sendMediaGroup = new SendMediaGroup(); sendMediaGroup.setChatId(chatId); sendMediaGroup.setMedias(mediaList); if (!notification) { sendMediaGroup.disableNotification(); } try { List execute = Bot.execute(sendMediaGroup); return execute; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendMessage(String chatId, String text, boolean webPagePreview) { return sendMessage(chatId, null, text, webPagePreview, true, null); } public static Message sendMessage(String chatId, String text, boolean webPagePreview, boolean notification) { return sendMessage(chatId, null, text, webPagePreview, notification, null); } public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview) { return sendMessage(chatId, replyToMessageId, text, webPagePreview, true, null); } public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview, boolean notification, List> buttons) { SendMessage sendMessage = new SendMessage(); sendMessage.setChatId(chatId); sendMessage.setReplyToMessageId(replyToMessageId); sendMessage.setText(text); sendMessage.setParseMode(ParseMode.HTML); if (!notification) { sendMessage.disableNotification(); } if (!webPagePreview) { sendMessage.disableWebPagePreview(); } if (null != buttons && !buttons.isEmpty()) { InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); inlineKeyboardMarkup.setKeyboard(buttons); sendMessage.setReplyMarkup(inlineKeyboardMarkup); } return sendMessage(sendMessage); } public static Message sendMessage(SendMessage sendMessage) { try { Message execute = Bot.execute(sendMessage); return execute; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage))); } return null; } public static boolean editMessage(Message message, String text) { try { EditMessageText editMessageText = new EditMessageText(); editMessageText.setChatId(message.getChatId()); editMessageText.setMessageId(message.getMessageId()); editMessageText.setText(text); Bot.execute(editMessageText); return true; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(message))); } return false; } public static boolean deleteMessage(Message message) { if (null == message) { return false; } DeleteMessage deleteMessage = new DeleteMessage(); deleteMessage.setChatId(message.getChatId()); deleteMessage.setMessageId(message.getMessageId()); try { Boolean execute = Bot.execute(deleteMessage); return null == execute ? false : execute; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage))); } return false; } public static boolean deleteMessage(DeleteMessage deleteMessage) { try { Boolean execute = Bot.execute(deleteMessage); return null == execute ? false : execute; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage))); } return false; } } ================================================ FILE: src/main/java/code/handler/StepsCenter.java ================================================ package code.handler; import code.config.ExecutorsConfig; import code.handler.message.CallbackBuilder; import code.handler.steps.StepsChatSession; import code.handler.steps.StepsHandler; import code.handler.steps.StepsRegisterCenter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.util.Collection; import java.util.stream.Collectors; import java.util.stream.Stream; import static code.Main.GlobalConfig; @Slf4j public class StepsCenter { public static boolean cmdHandle(StepsChatSession session) { if (StringUtils.isNotBlank(session.getText()) && session.getText().startsWith("/")) { String s = StringUtils.remove(session.getText(), "/"); String[] split = s.split(" "); if (split.length > 0) { String cmd = split[0]; cmd = StringUtils.replace(cmd, "@" + GlobalConfig.getBotName(), ""); if (Command.exist(cmd)) { split[0] = cmd; session.setText(Stream.of(split).skip(1).collect(Collectors.joining(" "))); cmdHandle( Command.toCmd(cmd), false, session, null ); return true; } } } return false; } public static void cmdHandle(CallbackBuilder.CallbackData callbackData, StepsChatSession stepsChatSession) { cmdHandle(callbackData.getCommand(), true, stepsChatSession, callbackData); } public static void cmdHandle(Command command, StepsChatSession stepsChatSession) { cmdHandle(command, false, stepsChatSession, null); } private static void cmdHandle(Command command, boolean isCall, StepsChatSession stepsChatSession, CallbackBuilder.CallbackData callbackData) { boolean permission = false; String botAdminId = GlobalConfig.getBotAdminId(); if (botAdminId.equals(stepsChatSession.getChatId()) || botAdminId.equals(stepsChatSession.getFromId())) { permission = true; } for (String s : GlobalConfig.getPermissionChatIdArray()) { if (s.equals(stepsChatSession.getChatId()) || s.equals(stepsChatSession.getFromId())) { permission = true; break; } } if (!permission) { MessageHandle.sendMessage(stepsChatSession.getChatId(), stepsChatSession.getReplyToMessageId(), "你没有使用权限, 不过你可以自己搭建一个\nhttps://github.com/kylelin1998/RssMonitorTelegramBot", false); return; } if (null != callbackData){ StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd()); if (!callbackData.isInit() && !handler.hasInit(stepsChatSession)) { return; } } ExecutorsConfig.submit(() -> { StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd()); if (null != handler.getInitStep() && (!handler.hasInit(stepsChatSession) || !isCall)) { handler.init(stepsChatSession); } else { handler.step(stepsChatSession); } }); } public static void textHandle(StepsChatSession stepsChatSession) { StepsHandler handler = StepsRegisterCenter.getPriority(stepsChatSession); if (null == handler) { return; } ExecutorsConfig.submit(() -> { handler.step(stepsChatSession); }); } public static void exit(StepsChatSession stepsChatSession) { Collection list = StepsRegisterCenter.getRegisterList(); for (StepsHandler handler : list) { handler.exit(stepsChatSession); } } } ================================================ FILE: src/main/java/code/handler/message/CallbackBuilder.java ================================================ package code.handler.message; import code.handler.Command; import code.handler.steps.StepsChatSession; import code.util.ExceptionUtil; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @Slf4j public class CallbackBuilder { @Data public static class CallbackData { private boolean init; private String id; private Command command; private String text; } public static String buildCallbackData(boolean init, StepsChatSession session, Command command, String text) { StringBuilder builder = new StringBuilder(); builder.append("f[" + session.getSessionId() + "]"); builder.append(command.getCmd()); builder.append(" "); builder.append(init); builder.append(" "); builder.append(text); return builder.toString(); } public static CallbackData parseCallbackData(String callbackData) { try { CallbackData data = new CallbackData(); data.setId(StringUtils.substringBetween(callbackData, "f[", "]")); String s = StringUtils.replace(callbackData, "f[" + data.getId() + "]", ""); String[] arguments = s.split(" "); data.setCommand(Command.toCmd(arguments[0])); data.setInit(Boolean.valueOf(arguments[1])); data.setText(arguments.length > 2 ? arguments[2] : null); return data; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } } ================================================ FILE: src/main/java/code/handler/message/InlineKeyboardButtonBuilder.java ================================================ package code.handler.message; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import java.util.ArrayList; import java.util.List; public class InlineKeyboardButtonBuilder { private List inlineKeyboardButtonList; private String callbackData; private InlineKeyboardButtonBuilder() {} public static InlineKeyboardButtonBuilder create() { InlineKeyboardButtonBuilder builder = new InlineKeyboardButtonBuilder(); builder.inlineKeyboardButtonList = new ArrayList<>(); return builder; } public InlineKeyboardButtonBuilder setCallbackData(String callbackData) { this.callbackData = callbackData; return this; } public InlineKeyboardButtonBuilder add(String text, String callbackData) { InlineKeyboardButton button = new InlineKeyboardButton(); button.setText(text); button.setCallbackData(callbackData); inlineKeyboardButtonList.add(button); return this; } public InlineKeyboardButtonBuilder add(String text) { InlineKeyboardButton button = new InlineKeyboardButton(); button.setText(text); button.setCallbackData(this.callbackData); inlineKeyboardButtonList.add(button); return this; } public InlineKeyboardButtonBuilder add(InlineKeyboardButton button) { inlineKeyboardButtonList.add(button); return this; } public List build() { return inlineKeyboardButtonList; } } ================================================ FILE: src/main/java/code/handler/message/InlineKeyboardButtonListBuilder.java ================================================ package code.handler.message; import code.eneity.PageEntity; import code.handler.Command; import code.handler.steps.StepsChatSession; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import java.util.ArrayList; import java.util.List; public class InlineKeyboardButtonListBuilder { private List> keyboard; private InlineKeyboardButtonListBuilder() {} public static InlineKeyboardButtonListBuilder create() { InlineKeyboardButtonListBuilder builder = new InlineKeyboardButtonListBuilder(); builder.keyboard = new ArrayList<>(); return builder; } public InlineKeyboardButtonListBuilder add(List inlineKeyboardButtonList) { this.keyboard.add(inlineKeyboardButtonList); return this; } public InlineKeyboardButtonListBuilder pagination(PageEntity entity, StepsChatSession session, Command command) { int count = entity.getCount(); if (count > 1) { InlineKeyboardButtonBuilder builder = InlineKeyboardButtonBuilder .create(); if (entity.isHasPrev()) { builder.add("⬅️", CallbackBuilder.buildCallbackData(true, session, command, "" + (entity.getCurrent() - 1))); } if (entity.isHasNext()) { builder.add("➡️", CallbackBuilder.buildCallbackData(true, session, command, "" + (entity.getCurrent() + 1))); } this.keyboard.add(builder.build()); } return this; } public List> build() { return keyboard; } } ================================================ FILE: src/main/java/code/handler/message/MessageHandle.java ================================================ package code.handler.message; import code.util.ExceptionUtil; import com.alibaba.fastjson2.JSON; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.telegram.telegrambots.meta.api.methods.ParseMode; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.methods.send.SendPhoto; import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage; import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText; import org.telegram.telegrambots.meta.api.objects.InputFile; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup; import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static code.Main.Bot; @Slf4j public class MessageHandle { public enum MessageError { BotWasBlockedByTheUser, ; } @Data public static class MessageResponse { private boolean ok; private Message message; private MessageError messageError; } public static Message sendImage(String chatId, Integer replyToMessageId, String text, InputStream image) { SendPhoto sendPhoto = new SendPhoto(); sendPhoto.setChatId(chatId); sendPhoto.setReplyToMessageId(replyToMessageId); sendPhoto.setCaption(text); sendPhoto.setPhoto(new InputFile(image, "image.png")); try { return Bot.execute(sendPhoto); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image) { SendPhoto sendPhoto = new SendPhoto(); sendPhoto.setChatId(chatId); sendPhoto.setReplyToMessageId(replyToMessageId); sendPhoto.setCaption(text); sendPhoto.setPhoto(new InputFile(image)); try { return Bot.execute(sendPhoto); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendInlineKeyboard(String chatId, String text, InlineKeyboardButton... inlineKeyboardButtonList) { return sendInlineKeyboard(chatId, text, Arrays.asList(inlineKeyboardButtonList)); } public static Message sendInlineKeyboardList(String chatId, String text, List> keyboard) { return sendInlineKeyboardList(chatId, null, text, keyboard); } public static Message sendInlineKeyboardList(String chatId, Integer replyToMessageId, String text, List> keyboard) { SendMessage message = new SendMessage(); message.setChatId(chatId); message.setText(text); message.setReplyToMessageId(replyToMessageId); message.setDisableWebPagePreview(true); InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); inlineKeyboardMarkup.setKeyboard(keyboard); message.setReplyMarkup(inlineKeyboardMarkup); try { return Bot.execute(message); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendInlineKeyboard(String chatId, String text, List inlineKeyboardButtonList) { SendMessage message = new SendMessage(); message.setChatId(chatId); message.setText(text); InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); List> keyboard = new ArrayList<>(); for (InlineKeyboardButton button : inlineKeyboardButtonList) { List list = new ArrayList<>(); list.add(button); keyboard.add(list); } inlineKeyboardMarkup.setKeyboard(keyboard); message.setReplyMarkup(inlineKeyboardMarkup); try { return Bot.execute(message); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static Message sendCustomKeyboard(String chatId, String text, KeyboardRow row) { List list = new ArrayList<>(); list.add(row); return sendCustomKeyboard(chatId, text, list); } public static Message sendCustomKeyboard(String chatId, String text, List keyboard) { SendMessage message = new SendMessage(); message.setChatId(chatId); message.setText(text); ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup(); keyboardMarkup.setKeyboard(keyboard); message.setReplyMarkup(keyboardMarkup); try { return Bot.execute(message); } catch (TelegramApiException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static MessageResponse sendMsg(String chatId, String text, boolean webPagePreview) { MessageResponse response = new MessageResponse(); response.setOk(false); SendMessage sendMessage = new SendMessage(); sendMessage.setChatId(chatId); sendMessage.setText(text); sendMessage.setParseMode(ParseMode.HTML); if (!webPagePreview) { sendMessage.disableWebPagePreview(); } try { Message execute = Bot.execute(sendMessage); response.setOk(true); response.setMessage(execute); return response; } catch (Exception e) { String message = e.getMessage(); if (message.contains("bot was blocked by the user")) { response.setMessageError(MessageError.BotWasBlockedByTheUser); } else { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage))); } } return response; } public static Message sendMessage(String chatId, String text, boolean webPagePreview) { return sendMessage(chatId, null, text, webPagePreview, true, null); } public static Message sendMessage(String chatId, String text, boolean webPagePreview, boolean notification) { return sendMessage(chatId, null, text, webPagePreview, notification, null); } public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview) { return sendMessage(chatId, replyToMessageId, text, webPagePreview, true, null); } public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview, boolean notification, List> buttons) { SendMessage sendMessage = new SendMessage(); sendMessage.setChatId(chatId); sendMessage.setReplyToMessageId(replyToMessageId); sendMessage.setText(text); sendMessage.setParseMode(ParseMode.HTML); if (!notification) { sendMessage.disableNotification(); } if (!webPagePreview) { sendMessage.disableWebPagePreview(); } if (null != buttons && !buttons.isEmpty()) { InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); inlineKeyboardMarkup.setKeyboard(buttons); sendMessage.setReplyMarkup(inlineKeyboardMarkup); } return sendMessage(sendMessage); } public static Message sendMessage(String chatId, String text, boolean webPagePreview, List> buttons) { return sendMessage(chatId, null, text, webPagePreview, true, null); } public static Message sendMessage(SendMessage sendMessage) { try { String text = sendMessage.getText(); if (StringUtils.isNotBlank(text)) { text = StringUtils.replace(text, "<", "<"); text = StringUtils.replace(text, ">", ">"); sendMessage.setText(text); } Message execute = Bot.execute(sendMessage); return execute; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage))); } return null; } public static boolean editMessage(Message message, String text) { try { EditMessageText editMessageText = new EditMessageText(); editMessageText.setChatId(message.getChatId()); editMessageText.setMessageId(message.getMessageId()); editMessageText.setText(text); Bot.execute(editMessageText); return true; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(message))); } return false; } public static boolean editMessage(Message message, String text, List> buttons) { try { EditMessageText editMessageText = new EditMessageText(); editMessageText.setChatId(message.getChatId()); editMessageText.setMessageId(message.getMessageId()); editMessageText.setText(text); if (null != buttons && !buttons.isEmpty()) { InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup(); inlineKeyboardMarkup.setKeyboard(buttons); editMessageText.setReplyMarkup(inlineKeyboardMarkup); } Bot.execute(editMessageText); return true; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(message))); } return false; } public static boolean deleteMessage(Message message) { if (null == message) { return false; } DeleteMessage deleteMessage = new DeleteMessage(); deleteMessage.setChatId(message.getChatId()); deleteMessage.setMessageId(message.getMessageId()); try { Boolean execute = Bot.execute(deleteMessage); return null == execute ? false : execute; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage))); } return false; } public static boolean deleteMessage(DeleteMessage deleteMessage) { try { Boolean execute = Bot.execute(deleteMessage); return null == execute ? false : execute; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage))); } return false; } } ================================================ FILE: src/main/java/code/handler/steps/StepErrorApi.java ================================================ package code.handler.steps; public interface StepErrorApi { void callback(Exception e, StepsChatSession stepsChatSession); } ================================================ FILE: src/main/java/code/handler/steps/StepExecuteResult.java ================================================ package code.handler.steps; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class StepExecuteResult { private boolean init; private StepResult stepResult; private boolean isWork; public static StepExecuteResult not() { return new StepExecuteResult(false, null, false); } public static StepExecuteResult work() { return new StepExecuteResult(true, null, true); } public static StepExecuteResult ok(StepResult stepResult) { return new StepExecuteResult(true, stepResult, true); } } ================================================ FILE: src/main/java/code/handler/steps/StepHandleApi.java ================================================ package code.handler.steps; import java.util.List; import java.util.Map; public interface StepHandleApi { StepResult execute(StepsChatSession stepsChatSession, int index, List list, Map context); } ================================================ FILE: src/main/java/code/handler/steps/StepResult.java ================================================ package code.handler.steps; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class StepResult { private boolean ok; private boolean next; private String text; private boolean end; public static StepResult ok() { return new StepResult(true, false, null, false); } public static StepResult reject() { return new StepResult(false, false, null, false); } public static StepResult next() { return new StepResult(true, true, null, false); } public static StepResult next(String text) { return new StepResult(true, true, text, false); } public static StepResult end() { return new StepResult(true, false, null, true); } } ================================================ FILE: src/main/java/code/handler/steps/StepsBuilder.java ================================================ package code.handler.steps; import code.handler.Command; public class StepsBuilder { private Command[] commands; private boolean debug = true; private StepErrorApi errorApi; private StepHandleApi initStep; private StepHandleApi[] steps; private StepsBuilder() {} public static StepsBuilder create() { return new StepsBuilder(); } public StepsBuilder bindCommand(Command... commands) { this.commands = commands; return this; } public StepsBuilder debug(boolean debug) { this.debug = debug; return this; } public StepsBuilder error(StepErrorApi errorApi) { this.errorApi = errorApi; return this; } public StepsBuilder init(StepHandleApi initStep) { this.initStep = initStep; return this; } public StepsBuilder steps(StepHandleApi... steps) { this.steps = steps; return this; } public StepsHandler build() { return StepsHandler.build(debug, errorApi, initStep, steps).bindCommand(commands); } } ================================================ FILE: src/main/java/code/handler/steps/StepsChatSession.java ================================================ package code.handler.steps; import lombok.Data; import org.telegram.telegrambots.meta.api.objects.CallbackQuery; import org.telegram.telegrambots.meta.api.objects.Message; @Data public class StepsChatSession { private String sessionId; private String chatId; private String fromId; private Integer replyToMessageId; private Message message; private CallbackQuery callbackQuery; private String text; } ================================================ FILE: src/main/java/code/handler/steps/StepsChatSessionBuilder.java ================================================ package code.handler.steps; import org.telegram.telegrambots.meta.api.objects.CallbackQuery; import org.telegram.telegrambots.meta.api.objects.Message; public class StepsChatSessionBuilder { private StepsChatSession session; private StepsChatSessionBuilder(StepsChatSession session) { this.session = session; } public static StepsChatSessionBuilder clone(StepsChatSession session) { return create(session.getMessage()); } public static StepsChatSessionBuilder create(CallbackQuery callbackQuery) { String chatId = String.valueOf(callbackQuery.getMessage().getChat().getId()); String fromId = String.valueOf(callbackQuery.getFrom().getId()); StepsChatSession session = new StepsChatSession(); session.setChatId(chatId); session.setFromId(fromId); session.setSessionId(chatId + "_" + fromId); session.setCallbackQuery(callbackQuery); return new StepsChatSessionBuilder(session); } public static StepsChatSessionBuilder create(Message message) { String chatId = message.getChat().getId().toString(); String fromId = String.valueOf(message.getFrom().getId()); String text = message.getText(); StepsChatSession session = new StepsChatSession(); session.setChatId(chatId); session.setFromId(fromId); session.setSessionId(chatId + "_" + fromId); session.setText(text); session.setReplyToMessageId(message.getMessageId()); session.setMessage(message); return new StepsChatSessionBuilder(session); } public StepsChatSessionBuilder setText(String text) { session.setText(text); return this; } public StepsChatSessionBuilder setText(String[] arguments) { session.setText(String.join(" ", arguments)); return this; } public StepsChatSession build() { return session; } } ================================================ FILE: src/main/java/code/handler/steps/StepsHandler.java ================================================ package code.handler.steps; import code.handler.Command; import com.alibaba.fastjson2.JSON; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @Getter @Slf4j public class StepsHandler { private static volatile AtomicInteger IdAtomic = new AtomicInteger(1); private Command[] commands; private boolean debug; private StepErrorApi errorApi; private StepHandleApi initStep; private StepHandleApi[] stepHandleApis; private Map> context = new ConcurrentHashMap<>(); private Map> message = new ConcurrentHashMap<>(); private Map stepWorkStatus = new ConcurrentHashMap<>(); private Map stepId = new ConcurrentHashMap<>(); private StepsHandler() { } public StepsHandler bindCommand(Command[] commands) { this.commands = commands; for (Command command : commands) { StepsRegisterCenter.register(command.getCmd(), this); } return this; } public static StepsHandler build(boolean debug, StepErrorApi errorApi, StepHandleApi step) { return build(debug, errorApi, null, step); } public static StepsHandler build(boolean debug, StepErrorApi errorApi, StepHandleApi initStep, StepHandleApi... steps) { StepsHandler handler = new StepsHandler(); handler.debug = debug; handler.errorApi = errorApi; handler.initStep = initStep; handler.stepHandleApis = steps; return handler; } public boolean hasInit(StepsChatSession stepsChatSession) { return stepId.containsKey(stepsChatSession.getSessionId()); } public boolean isInit() { return null != initStep; } public void init(StepsChatSession stepsChatSession) { StepsRegisterCenter.priority(stepsChatSession, this); String sessionId = stepsChatSession.getSessionId(); Boolean stepsWorkStatusBool = stepWorkStatus.get(sessionId); if (null != stepsWorkStatusBool && stepsWorkStatusBool) { return; } stepWorkStatus.put(sessionId, true); StepResult execute = null; try { List list = Collections.synchronizedList(new ArrayList<>()); ConcurrentHashMap contextMap = new ConcurrentHashMap<>(); if (null != initStep) { execute = initStep.execute(stepsChatSession, 0, list, contextMap); } if ((null != execute && execute.isOk()) || null == initStep) { context.remove(sessionId); message.remove(sessionId); stepId.remove(sessionId); list.add(stepsChatSession.getText()); message.put(sessionId, list); context.put(sessionId, contextMap); stepId.put(sessionId, IdAtomic.incrementAndGet()); if (debug) { log.info("Steps init, id: {}, chat id: {}, text: {}, list: {}", stepId.get(sessionId), stepsChatSession.getChatId(), stepsChatSession.getText(), JSON.toJSONString(list)); } } } catch (Exception e) { errorApi.callback(e, stepsChatSession); } finally { stepWorkStatus.put(sessionId, false); if (null != execute) { if (execute.isNext()) { step(stepsChatSession); } if (execute.isEnd()) { exit(stepsChatSession); } } } } public void next(StepsChatSession stepsChatSession) { step(stepsChatSession); } public StepExecuteResult step(StepsChatSession stepsChatSession) { String sessionId = stepsChatSession.getSessionId(); if (!hasInit(stepsChatSession) && !isInit()) { init(stepsChatSession); } Boolean stepsWorkStatusBool = stepWorkStatus.get(sessionId); if (null != stepsWorkStatusBool && stepsWorkStatusBool) { return StepExecuteResult.work(); } stepWorkStatus.put(sessionId, true); StepResult execute = null; try { if (!message.containsKey(sessionId)) { return StepExecuteResult.not(); } List list = message.get(sessionId); Map contextMap = context.get(sessionId); execute = this.stepHandleApis[list.size() - 1].execute(stepsChatSession, list.size(), list, contextMap); if (execute.isOk()) { list.add(stepsChatSession.getText()); } if (debug) { log.info("Step, id: {}, chat id: {}, text: {}, list: {}, context: {}", stepId.get(sessionId), stepsChatSession.getChatId(), stepsChatSession.getText(), JSON.toJSONString(list), JSON.toJSONString(contextMap)); } if ((list.size() - 1) >= this.stepHandleApis.length) { if (debug) { log.info("Step finish, id: {}, chat id: {}, text: {}, list: {}, context: {}", stepId.get(sessionId), stepsChatSession.getChatId(), stepsChatSession.getText(), JSON.toJSONString(list), JSON.toJSONString(contextMap)); } exit(stepsChatSession); } } catch (Exception e) { errorApi.callback(e, stepsChatSession); } finally { stepWorkStatus.put(sessionId, false); if (null != execute) { if (execute.isEnd()) { exit(stepsChatSession); } else if (execute.isNext()) { next( StepsChatSessionBuilder.clone(stepsChatSession).setText(execute.getText()).build() ); } } } return StepExecuteResult.ok(execute); } public void exit(StepsChatSession stepsChatSession) { String sessionId = stepsChatSession.getSessionId(); message.remove(sessionId); context.remove(sessionId); stepWorkStatus.remove(sessionId); stepId.remove(sessionId); StepsRegisterCenter.finish(stepsChatSession); } } ================================================ FILE: src/main/java/code/handler/steps/StepsRegisterCenter.java ================================================ package code.handler.steps; import lombok.extern.slf4j.Slf4j; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Slf4j public class StepsRegisterCenter { private static Map stepsHandlerMap = new HashMap<>(); private static volatile Map priorityMap = new ConcurrentHashMap<>(); public static void register(String cmd, StepsHandler handler) { stepsHandlerMap.put(cmd, handler); } public static StepsHandler getRegister(String cmd) { return stepsHandlerMap.get(cmd); } public static Collection getRegisterList() { return stepsHandlerMap.values(); } public synchronized static void priority(StepsChatSession stepsChatSession, StepsHandler stepsHandler) { for (StepsHandler value : stepsHandlerMap.values()) { value.exit(stepsChatSession); } priorityMap.put(stepsChatSession.getSessionId(), stepsHandler); } public synchronized static void finish(StepsChatSession stepsChatSession) { priorityMap.remove(stepsChatSession.getSessionId()); } public static StepsHandler getPriority(StepsChatSession stepsChatSession) { return priorityMap.get(stepsChatSession.getSessionId()); } } ================================================ FILE: src/main/java/code/handler/store/ChatButtonsStore.java ================================================ package code.handler.store; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import java.util.*; public class ChatButtonsStore { private volatile static ChatButtonsToInlineKeyboardButtons buttons; public static void set(String chatButtons) { Optional keyboardButtons = chatButtonsToInlineKeyboardButtons(chatButtons); buttons = keyboardButtons.orElse(null); } public static Optional verify(String chatButtons) { return chatButtonsToInlineKeyboardButtons(chatButtons); } public static Optional get() { return Optional.ofNullable(buttons); } @Data public static class ChatButtonsToInlineKeyboardButtons { private boolean isAll; private Map> map; public Optional> getButtons(String chatId) { for (Map.Entry> entry : map.entrySet()) { if (entry.getKey().equals(chatId)) { return Optional.ofNullable(entry.getValue()); } } if (isAll) { return Optional.ofNullable(map.get("all")); } return Optional.empty(); } } private static Optional chatButtonsToInlineKeyboardButtons(String chatButtons) { if (StringUtils.isBlank(chatButtons)) { return Optional.empty(); } String[] split = chatButtons.split("---"); if (split.length == 0) { return Optional.empty(); } ChatButtonsToInlineKeyboardButtons chatButtonsToInlineKeyboardButtons = new ChatButtonsToInlineKeyboardButtons(); Map> map = new LinkedHashMap<>(); for (String s : split) { s = StringUtils.removeStart(s, "\n"); List buttons = new ArrayList<>(); String[] dataSplit = s.split("\n"); if (dataSplit.length < 2) { return Optional.empty(); } for (int i = 1; i < dataSplit.length; i++) { String data = dataSplit[i]; if (StringUtils.isBlank(data)) { return Optional.empty(); } String[] buttonSplit = data.split(" "); if (buttonSplit.length != 2) { return Optional.empty(); } String url = buttonSplit[1]; if (!StringUtils.startsWith(url, "http")) { return Optional.empty(); } InlineKeyboardButton button = new InlineKeyboardButton(); button.setText(buttonSplit[0]); button.setUrl(url); buttons.add(button); } String chatId = dataSplit[0]; chatButtonsToInlineKeyboardButtons.setAll(chatId.equals("all")); map.put(chatId, buttons); if (chatButtonsToInlineKeyboardButtons.isAll()) { break; } } if (chatButtonsToInlineKeyboardButtons.isAll()) { List deleteKeys = new ArrayList<>(); for (Map.Entry> entry : map.entrySet()) { if (!entry.getKey().equals("all")) { deleteKeys.add(entry.getKey()); } } for (String key : deleteKeys) { map.remove(key); } } chatButtonsToInlineKeyboardButtons.setMap(map); return Optional.of(chatButtonsToInlineKeyboardButtons); } } ================================================ FILE: src/main/java/code/handler/store/Store.java ================================================ package code.handler.store; import static code.Main.GlobalConfig; public class Store { public static void init() { ChatButtonsStore.set(GlobalConfig.getChatButtons()); } } ================================================ FILE: src/main/java/code/handler/store/WebhookStore.java ================================================ package code.handler.store; import com.alibaba.fastjson2.JSON; import lombok.Data; import org.apache.commons.lang3.StringUtils; import java.util.List; import java.util.Map; import java.util.Optional; public class WebhookStore { @Data public static class Webhook { private boolean enable; private List list; } @Data public static class WebhookRequest { private String url; private String method; private Map headers; private Map body; } public static boolean verify(String webhookJson) { if (StringUtils.isBlank(webhookJson)) { return false; } Optional webhookOptional = get(webhookJson); if (!webhookOptional.isPresent()) { return false; } Webhook webhook = webhookOptional.get(); if (!webhook.isEnable()) { return true; } else { List list = webhook.getList(); if (null == list || list.isEmpty()) { return false; } } for (WebhookRequest request : webhook.list) { String url = request.getUrl(); if (!StringUtils.startsWith(url, "http")) { return false; } else if (StringUtils.isBlank(request.getMethod())) { return false; } } return true; } public static Optional get(String webhookJson) { try { Webhook webhook = JSON.parseObject(webhookJson, Webhook.class); return Optional.ofNullable(webhook); } catch (Exception e) { } return Optional.empty(); } } ================================================ FILE: src/main/java/code/repository/I18nTableRepository.java ================================================ package code.repository; import code.config.Config; import code.config.TableEnum; import code.repository.base.TableRepository; import code.util.ExceptionUtil; import lombok.extern.slf4j.Slf4j; import java.sql.ResultSet; @Slf4j public class I18nTableRepository extends TableRepository { public I18nTableRepository() { super(Config.DBPath, TableEnum.I18nTable.getName()); } @Override public String getCreateTableSql() { return String.format("create table if not exists %s (chat_id varchar(88), i18n_alias varchar(20))", super.getTableName()); } public String selectI18nAlias(String chatId) { try { String i18nAlias = (String) execute((statement) -> { String sql = String.format("select i18n_alias from %s where chat_id = '%s'", super.getTableName(), chatId); ResultSet query = statement.executeQuery(sql); return query.getString("i18n_alias"); }); return i18nAlias; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public synchronized boolean save(String chatId, String i18nAlias) { String sql = String.format("insert into %s values('%s', '%s')", super.getTableName(), chatId, i18nAlias); try { execute((statement) -> { statement.executeUpdate(String.format("delete from %s where chat_id = '%s'", super.getTableName(), chatId)); statement.executeUpdate(sql); return null; }); return true; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); return false; } } } ================================================ FILE: src/main/java/code/repository/MonitorTableRepository.java ================================================ package code.repository; import code.config.Config; import code.eneity.MonitorTableEntity; import code.repository.base.TableRepository; import lombok.extern.slf4j.Slf4j; import java.util.List; @Slf4j public class MonitorTableRepository extends TableRepository { public MonitorTableRepository() { super(Config.DBPath, true); } public MonitorTableEntity selectOne(String id, String chatId) { MonitorTableEntity where = new MonitorTableEntity(); where.setId(id); where.setChatId(chatId); return super.selectOne(where); } public Integer selectCountByName(String chatId, String name) { MonitorTableEntity where = new MonitorTableEntity(); where.setName(name); where.setChatId(chatId); return super.selectCount(where); } public Boolean delete(String id) { MonitorTableEntity where = new MonitorTableEntity(); where.setId(id); return super.delete(where); } public Boolean update(MonitorTableEntity entity) { MonitorTableEntity where = new MonitorTableEntity(); where.setId(entity.getId()); return super.update(entity, where); } public List selectList(String chatId) { MonitorTableEntity where = new MonitorTableEntity(); where.setChatId(chatId); return super.selectList(where); } } ================================================ FILE: src/main/java/code/repository/SentRecordTableRepository.java ================================================ package code.repository; import code.config.Config; import code.eneity.SentRecordTableEntity; import code.repository.base.TableRepository; import lombok.extern.slf4j.Slf4j; @Slf4j public class SentRecordTableRepository extends TableRepository { public SentRecordTableRepository() { super(Config.DBPath, false); } public void save(SentRecordTableEntity entity) { SentRecordTableEntity where = new SentRecordTableEntity(); where.setId(entity.getId()); Integer count = super.selectCount(where); if (count == 0) { super.insert(entity); } } public Boolean delete(String name, String chatId) { SentRecordTableEntity where = new SentRecordTableEntity(); where.setChatId(chatId); where.setName(name); return super.delete(where); } public Boolean exists(String name, String chatId) { return exists(null, name, chatId); } public Boolean exists(String uri, String name, String chatId) { SentRecordTableEntity where = new SentRecordTableEntity(); where.setUri(uri); where.setName(name); where.setChatId(chatId); Integer count = super.selectCount(where); if (null == count) { return null; } return count > 0; } } ================================================ FILE: src/main/java/code/repository/WebhookTableRepository.java ================================================ package code.repository; import code.config.Config; import code.eneity.WebhookTableEntity; import code.repository.base.TableRepository; import lombok.extern.slf4j.Slf4j; import java.util.List; @Slf4j public class WebhookTableRepository extends TableRepository { public WebhookTableRepository() { super(Config.DBPath, false); } public WebhookTableEntity selectOne(String chatId) { WebhookTableEntity where = new WebhookTableEntity(); where.setChatId(chatId); return super.selectOne(where); } public List selectList(String chatId) { WebhookTableEntity where = new WebhookTableEntity(); where.setChatId(chatId); return super.selectList(where); } public synchronized Boolean save(WebhookTableEntity entity) { WebhookTableEntity rsp = selectOne(entity.getChatId()); if (null == rsp) { return super.insert(entity); } else { WebhookTableEntity where = new WebhookTableEntity(); where.setChatId(entity.getChatId()); return super.update(entity, where); } } public Boolean delete(String chatId) { WebhookTableEntity where = new WebhookTableEntity(); where.setChatId(chatId); return super.delete(where); } } ================================================ FILE: src/main/java/code/repository/base/SqlBuilder.java ================================================ package code.repository.base; import code.util.ExceptionUtil; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.StringJoiner; @Slf4j public class SqlBuilder { public static String getTableName(TableEntity tableEntity) { return getTableName(tableEntity.getClass()); } public static String getTableName(Class extends TableEntity> tableClass) { return tableClass.getAnnotation(TableName.class).name(); } public static List getNameList(Class extends TableEntity> tableClass) { ArrayList list = new ArrayList<>(); for (Field field : tableClass.getDeclaredFields()) { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } list.add(tableField); } return list; } public static String buildCreateTableSql(Class extends TableEntity> tableClass) { String tableName = getTableName(tableClass); StringJoiner joiner = new StringJoiner(", ", "(", ")"); for (Field field : tableClass.getDeclaredFields()) { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } joiner.add(tableField.sql()); } String sql = "create table if not exists " + tableName + " " + joiner; return sql; } public static String buildAlterTableAddColumnNameSql(String tableName, String column) { return String.format("ALTER TABLE %s ADD COLUMN %s", tableName, column); } private static String buildFieldValueSql(TableEntity tableEntity, String delimiter, String prefix, String suffix) { Class extends TableEntity> entityClass = tableEntity.getClass(); StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix); for (Field field : entityClass.getDeclaredFields()) { try { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } field.setAccessible(true); Object o = field.get(tableEntity); if (null == o) { continue; } if (o instanceof String) { joiner.add(tableField.name() + "=" + "'" + o + "'"); } else { joiner.add(tableField.name() + "=" + o); } } catch (IllegalAccessException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } return joiner.toString(); } public static String buildFieldSql(Class extends TableEntity> tableClass, String prefix, String suffix) { StringJoiner joiner = new StringJoiner(", ", prefix, suffix); for (Field field : tableClass.getDeclaredFields()) { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } joiner.add(tableField.name()); } return joiner.toString(); } public static String buildFieldSql(TableEntity tableEntity, String prefix, String suffix) { StringJoiner joiner = new StringJoiner(", ", prefix, suffix); Class extends TableEntity> entityClass = tableEntity.getClass(); for (Field field : entityClass.getDeclaredFields()) { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } joiner.add(tableField.name()); } return joiner.toString(); } public static String buildWhereSql(TableEntity tableEntity) { return buildFieldValueSql(tableEntity, " and ", " where ", ""); } public static String buildSelectSql(Class extends TableEntity> tableClass) { String tableName = getTableName(tableClass); String sql = "select" + buildFieldSql(tableClass, " ", " ") + "from " + tableName; return sql; } public static String buildSelectSql(TableEntity tableEntity) { String tableName = getTableName(tableEntity); String sql = "select" + buildFieldSql(tableEntity, " ", " ") + "from " + tableName + buildWhereSql(tableEntity); return sql; } public static String buildSelectSql(TableEntity tableEntity, int page, int current, String orderBy) { String tableName = getTableName(tableEntity); String sql = "select" + buildFieldSql(tableEntity, " ", " ") + "from " + tableName + buildWhereSql(tableEntity); sql += String.format(" %s limit %s, %s", orderBy, ((current * page) - page), page); return sql; } public static String buildDeleteSql(TableEntity where) { String tableName = getTableName(where); String sql = "delete from " + tableName + buildWhereSql(where); return sql; } public static String buildSelectCountSql(Class extends TableEntity> tableClass) { String tableName = getTableName(tableClass); String sql = "select count(*) as total from " + tableName; return sql; } public static String buildSelectCountSql(TableEntity where) { String tableName = getTableName(where); String sql = "select count(*) as total from " + tableName + buildWhereSql(where); return sql; } public static String buildInsertSql(TableEntity tableEntity, boolean isForceInsertNullValue) { String tableName = getTableName(tableEntity); Class extends TableEntity> entityClass = tableEntity.getClass(); StringJoiner prefixJoiner = new StringJoiner(", ", "(", ")"); StringJoiner suffixJoiner = new StringJoiner(", ", "values (", ")"); for (Field field : entityClass.getDeclaredFields()) { try { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } field.setAccessible(true); Object o = field.get(tableEntity); if (null == o && !isForceInsertNullValue) { continue; } prefixJoiner.add(tableField.name()); if (null == o) { suffixJoiner.add(null); } else if (o instanceof String) { suffixJoiner.add("'" + o + "'"); } else { suffixJoiner.add(String.valueOf(o)); } } catch (IllegalAccessException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } String sql = "insert into " + tableName + " " + prefixJoiner + " " + suffixJoiner; return sql; } public static String buildUpdateSql(TableEntity tableEntity, TableEntity where) { String tableName = getTableName(tableEntity); String sql = "update " + tableName + buildFieldValueSql(tableEntity, ", ", " set ", "") + buildWhereSql(where); return sql; } } ================================================ FILE: src/main/java/code/repository/base/TableEntity.java ================================================ package code.repository.base; public interface TableEntity { } ================================================ FILE: src/main/java/code/repository/base/TableField.java ================================================ package code.repository.base; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.FIELD) @Retention(value = RetentionPolicy.RUNTIME) public @interface TableField { String sql(); String name(); } ================================================ FILE: src/main/java/code/repository/base/TableName.java ================================================ package code.repository.base; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TableName { String name(); } ================================================ FILE: src/main/java/code/repository/base/TableRepository.java ================================================ package code.repository.base; import code.eneity.PageEntity; import code.util.ExceptionUtil; import code.util.SqliteUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.sqlite.jdbc4.JDBC4ResultSet; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @Slf4j public abstract class TableRepository { private String tableName; private String dbPath; private boolean checkColumn = false; public TableRepository(String dbPath, String tableName) { this.tableName = tableName; this.dbPath = dbPath; createTableHandle(); } public TableRepository(String dbPath, String tableName, boolean checkColumn) { this.tableName = tableName; this.dbPath = dbPath; this.checkColumn = checkColumn; createTableHandle(); checkColumnHandle(); } public TableRepository(String dbPath, boolean checkColumn) { this.tableName = getT().getAnnotation(TableName.class).name(); this.dbPath = dbPath; this.checkColumn = checkColumn; createTableHandle(); checkColumnHandle(); } private void createTableHandle() { try { SqliteUtil.execute(dbPath, (statement) -> { String createTableSql = getCreateTableSql(); if (StringUtils.isBlank(createTableSql)) { createTableSql = SqlBuilder.buildCreateTableSql(getT()); } if (StringUtils.isNotBlank(createTableSql)) { statement.execute(createTableSql); } return null; }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } private void checkColumnHandle() { try { if (this.checkColumn) { SqliteUtil.execute(dbPath, (statement) -> { Class t = getT(); DatabaseMetaData metaData = statement.getConnection().getMetaData(); List nameList = SqlBuilder.getNameList(t); for (TableField name : nameList) { ResultSet rs = metaData.getColumns(null, null, tableName, name.name()); if (!rs.next()) { String sql = SqlBuilder.buildAlterTableAddColumnNameSql(tableName, name.sql()); if (StringUtils.isNotBlank(sql)) { statement.execute(sql); } } } return null; }); } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } private Class getT() { ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass(); Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0]; String typeName = actualTypeArgument.getTypeName(); try { return (Class) Class.forName(typeName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public String getTableName() { return this.tableName; } public String sql(String sql) { return sql(sql, null); } public String sql(String sql, String[] args) { String table = StringUtils.replace(sql, "$table", this.getTableName()); return String.format(table, args); } public Object execute(SqliteUtil.SqliteInterface sqliteInterface) throws Exception { return SqliteUtil.execute(this.dbPath, sqliteInterface); } public Object executeWithTryCatch(SqliteUtil.SqliteInterface sqliteInterface) { try { return SqliteUtil.execute(this.dbPath, sqliteInterface); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public Boolean delete(T where) { try { return (boolean) execute((statement) -> { int update = statement.executeUpdate(SqlBuilder.buildDeleteSql(where)); return update > 0; }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public Boolean update(T entity, T where) { try { return (boolean) execute((statement) -> { int update = statement.executeUpdate(SqlBuilder.buildUpdateSql(entity, where)); return update > 0; }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public Boolean insert(T entity) { try { return (boolean) execute((statement) -> { int update = statement.executeUpdate(SqlBuilder.buildInsertSql(entity, false)); return update > 0; }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public T getOne(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException { JDBC4ResultSet jdbc4ResultSet = (JDBC4ResultSet) resultSet; if (jdbc4ResultSet.emptyResultSet) { return null; } Class t = getT(); T instance = t.newInstance(); for (Field field : t.getDeclaredFields()) { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } field.setAccessible(true); field.set(instance, resultSet.getObject(tableField.name())); } return instance; } public List getList(ResultSet resultSet) throws SQLException, IllegalAccessException, InstantiationException { List list = new ArrayList<>(); JDBC4ResultSet jdbc4ResultSet = (JDBC4ResultSet) resultSet; if (jdbc4ResultSet.emptyResultSet) { return list; } Class t = getT(); while (resultSet.next()) { T instance = t.newInstance(); for (Field field : t.getDeclaredFields()) { TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { continue; } field.setAccessible(true); field.set(instance, resultSet.getObject(tableField.name())); } list.add(instance); } return list; } public T selectOne(T where) { try { return (T) execute((statement) -> { ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where)); return getOne(resultSet); }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public List selectList() { try { return (List) execute((statement) -> { ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(getT())); return getList(resultSet); }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return new ArrayList<>(); } public List selectList(T where) { try { return (List) execute((statement) -> { ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where)); return getList(resultSet); }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return new ArrayList<>(); } public PageEntity page(T where, int page, int current, String orderBy) { try { Integer count = this.selectCount(where); PageEntity entity = new PageEntity<>(count, page, current); List list = (List) execute((statement) -> { ResultSet resultSet = statement.executeQuery(SqlBuilder.buildSelectSql(where, page, current, orderBy)); return getList(resultSet); }); entity.setList(list); return entity; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return PageEntity.empty(); } public Integer selectCount() { Integer total = null; try { total = (int) execute((statement) -> { ResultSet query = statement.executeQuery(SqlBuilder.buildSelectCountSql(getT())); return query.getInt("total"); }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return total; } public Integer selectCount(T where) { Integer total = null; try { total = (int) execute((statement) -> { ResultSet query = statement.executeQuery(SqlBuilder.buildSelectCountSql(where)); return query.getInt("total"); }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return total; } public Boolean exist(String field, String value) { Integer total = null; try { total = (int) execute((statement) -> { String sql = String.format("select count(*) as total from %s where %s = '%s'", this.tableName, field, value); ResultSet query = statement.executeQuery(sql); return query.getInt("total"); }); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } if (null == total) { return null; } return total > 0; } public String getCreateTableSql() { return null; } } ================================================ FILE: src/main/java/code/util/BytesUtil.java ================================================ package code.util; import java.math.BigDecimal; public class BytesUtil { private final static int KB = 1024; private final static int MB = 1024 * 1024; private final static int GB = 1024 * 1024 * 1024; private static BigDecimal divide(long size, int unit) { return new BigDecimal(size).divide(new BigDecimal(unit)).setScale(2, BigDecimal.ROUND_DOWN); } public static String toDisplayStr(long size) { if (size < KB) { return size + "B"; } else if (size >= KB && size < MB) { return divide(size, KB) + "KB"; } else if (size >= MB && size < GB) { return divide(size, MB) + "MB"; } else if (size >= GB) { return divide(size, GB) + "GB"; } return null; } } ================================================ FILE: src/main/java/code/util/DownloadUtil.java ================================================ package code.util; import code.config.RequestProxyConfig; import kong.unirest.GetRequest; import kong.unirest.HttpResponse; import kong.unirest.ProgressMonitor; import kong.unirest.Unirest; import lombok.extern.slf4j.Slf4j; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.util.Timeout; import java.io.File; import java.io.InputStream; import java.nio.file.StandardCopyOption; import java.util.concurrent.TimeUnit; @Slf4j public class DownloadUtil { public static InputStream download(RequestProxyConfig requestProxyConfig, String url) { try { Request request = Request .get(url) .connectTimeout(Timeout.ofSeconds(30)) .responseTimeout(Timeout.ofSeconds(60)); requestProxyConfig.viaProxy(request); return request.execute().returnContent().asStream(); } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } public static boolean download(RequestProxyConfig requestProxyConfig, String url, String file) { try { GetRequest request = Unirest .get(url) .connectTimeout((int) TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS)) ; requestProxyConfig.viaProxy(request); HttpResponse response = request.asFile(file, StandardCopyOption.REPLACE_EXISTING); return response.getStatus() == 200; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return false; } public static boolean download(RequestProxyConfig requestProxyConfig, String url, String file, ProgressMonitor progressMonitor) { try { GetRequest request = Unirest .get(url) .downloadMonitor(progressMonitor) .connectTimeout((int) TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS)) ; requestProxyConfig.viaProxy(request); HttpResponse response = request.asFile(file, StandardCopyOption.REPLACE_EXISTING); return response.getStatus() == 200; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return false; } } ================================================ FILE: src/main/java/code/util/ExceptionUtil.java ================================================ package code.util; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; public class ExceptionUtil { public static String getStackTraceWithCustomInfoToStr(Exception e, String description) { return String.format("description: %s, class name: %s, stacktrace: %s", description, e.getClass().getName(), getStackTraceToStr(e)); } public static String getStackTraceWithCustomInfoToStr(Exception e) { return String.format("class name: %s, stacktrace: %s", e.getClass().getName(), getStackTraceToStr(e)); } public static String getStackTraceToStr(Exception e) { StringWriter stringWriter = null; PrintWriter printWriter = null; try { stringWriter = new StringWriter(); printWriter = new PrintWriter(stringWriter); e.printStackTrace(printWriter); printWriter.flush(); stringWriter.flush(); } finally { if (null != stringWriter) { try { stringWriter.close(); } catch (IOException ioException) { ioException.printStackTrace(); } } if (null != printWriter) { printWriter.close(); } } return stringWriter.toString(); } } ================================================ FILE: src/main/java/code/util/GithubUtil.java ================================================ package code.util; import code.config.RequestProxyConfig; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONReader; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.client5.http.fluent.Response; import org.apache.hc.core5.util.Timeout; import java.nio.charset.Charset; import java.util.List; import java.util.concurrent.TimeUnit; @Slf4j public class GithubUtil { private final static String ApiVersion = "2022-11-28"; @Data public static class LatestReleaseResponse { private boolean ok; private String htmlUrl; private String tagName; private String name; private String body; private List assets; } @Data public static class LatestReleaseAsset { private String name; private String browserDownloadUrl; } public static LatestReleaseResponse getLatestRelease(RequestProxyConfig requestProxyConfig, String owner, String repo) { String url = String.format("https://api.github.com/repos/%s/%s/releases/latest", owner, repo); try { Request request = Request .get(url) .setHeader("Accept", "application/vnd.github+json") .setHeader("X-GitHub-Api-Version", ApiVersion) .connectTimeout(Timeout.of(15, TimeUnit.SECONDS)) .responseTimeout(Timeout.of(60, TimeUnit.SECONDS)); requestProxyConfig.viaProxy(request); Response execute = request.execute(); LatestReleaseResponse releaseAssetResponse = JSON.parseObject(execute.returnContent().asString(Charset.forName("UTF-8")), LatestReleaseResponse.class, JSONReader.Feature.SupportSmartMatch); releaseAssetResponse.setOk(true); return releaseAssetResponse; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } LatestReleaseResponse response = new LatestReleaseResponse(); response.setOk(false); return response; } } ================================================ FILE: src/main/java/code/util/ProgramUtil.java ================================================ package code.util; import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.util.stream.Collectors; import java.util.stream.Stream; @Slf4j public class ProgramUtil { public static void restart(String processName) { try { String[] strings = {"java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/" + processName}; log.info(Stream.of(strings).collect(Collectors.joining(" "))); Runtime.getRuntime().exec(strings); System.exit(1); } catch (IOException e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } } ================================================ FILE: src/main/java/code/util/RssUtil.java ================================================ package code.util; import code.config.RequestProxyConfig; import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.XmlReader; import kong.unirest.GetRequest; import kong.unirest.HttpResponse; import kong.unirest.Unirest; import lombok.extern.slf4j.Slf4j; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; @Slf4j public class RssUtil { public static SyndFeed getFeed(RequestProxyConfig proxyConfig, String url) { try { GetRequest request = Unirest .get(url) .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) .socketTimeout((int) TimeUnit.MILLISECONDS.convert(40, TimeUnit.SECONDS)) ; proxyConfig.viaProxy(request); HttpResponse response = request.asString(); String body = response.getBody(); InputStream inputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); SyndFeed syndFeed = new SyndFeedInput().build(new XmlReader(inputStream)); return syndFeed; } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return null; } } ================================================ FILE: src/main/java/code/util/Snowflake.java ================================================ package code.util; import java.net.NetworkInterface; import java.security.SecureRandom; import java.time.Instant; import java.util.Enumeration; /** * Distributed Sequence Generator. * Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010 * * This class should be used as a Singleton. * Make sure that you create and reuse a Single instance of Snowflake per node in your distributed system cluster. */ public class Snowflake { private static final int UNUSED_BITS = 1; // Sign bit, Unused (always set to 0) private static final int EPOCH_BITS = 41; private static final int NODE_ID_BITS = 10; private static final int SEQUENCE_BITS = 12; private static final long maxNodeId = (1L << NODE_ID_BITS) - 1; private static final long maxSequence = (1L << SEQUENCE_BITS) - 1; // Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z) private static final long DEFAULT_CUSTOM_EPOCH = 1420070400000L; private final long nodeId; private final long customEpoch; private volatile long lastTimestamp = -1L; private volatile long sequence = 0L; // Create Snowflake with a nodeId and custom epoch public Snowflake(long nodeId, long customEpoch) { if(nodeId < 0 || nodeId > maxNodeId) { throw new IllegalArgumentException(String.format("NodeId must be between %d and %d", 0, maxNodeId)); } this.nodeId = nodeId; this.customEpoch = customEpoch; } // Create Snowflake with a nodeId public Snowflake(long nodeId) { this(nodeId, DEFAULT_CUSTOM_EPOCH); } // Let Snowflake generate a nodeId public Snowflake() { this.nodeId = createNodeId(); this.customEpoch = DEFAULT_CUSTOM_EPOCH; } public synchronized String nextIdToStr() { return String.valueOf(nextId()); } public synchronized long nextId() { long currentTimestamp = timestamp(); if(currentTimestamp < lastTimestamp) { throw new IllegalStateException("Invalid System Clock!"); } if (currentTimestamp == lastTimestamp) { sequence = (sequence + 1) & maxSequence; if(sequence == 0) { // Sequence Exhausted, wait till next millisecond. currentTimestamp = waitNextMillis(currentTimestamp); } } else { // reset sequence to start with zero for the next millisecond sequence = 0; } lastTimestamp = currentTimestamp; long id = currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS) | (nodeId << SEQUENCE_BITS) | sequence; return id; } // Get current timestamp in milliseconds, adjust for the custom epoch. private long timestamp() { return Instant.now().toEpochMilli() - customEpoch; } // Block and wait till next millisecond private long waitNextMillis(long currentTimestamp) { while (currentTimestamp == lastTimestamp) { currentTimestamp = timestamp(); } return currentTimestamp; } private long createNodeId() { long nodeId; try { StringBuilder sb = new StringBuilder(); Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); byte[] mac = networkInterface.getHardwareAddress(); if (mac != null) { for(byte macPort: mac) { sb.append(String.format("%02X", macPort)); } } } nodeId = sb.toString().hashCode(); } catch (Exception ex) { nodeId = (new SecureRandom().nextInt()); } nodeId = nodeId & maxNodeId; return nodeId; } public long[] parse(long id) { long maskNodeId = ((1L << NODE_ID_BITS) - 1) << SEQUENCE_BITS; long maskSequence = (1L << SEQUENCE_BITS) - 1; long timestamp = (id >> (NODE_ID_BITS + SEQUENCE_BITS)) + customEpoch; long nodeId = (id & maskNodeId) >> SEQUENCE_BITS; long sequence = id & maskSequence; return new long[]{timestamp, nodeId, sequence}; } @Override public String toString() { return "Snowflake Settings [EPOCH_BITS=" + EPOCH_BITS + ", NODE_ID_BITS=" + NODE_ID_BITS + ", SEQUENCE_BITS=" + SEQUENCE_BITS + ", CUSTOM_EPOCH=" + customEpoch + ", NodeId=" + nodeId + "]"; } } ================================================ FILE: src/main/java/code/util/SqliteUtil.java ================================================ package code.util; import lombok.extern.slf4j.Slf4j; import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; @Slf4j public class SqliteUtil { public interface SqliteInterface { Object execute(Statement statement) throws SQLException, IllegalAccessException, InstantiationException; } public static Object execute(String dbPath, SqliteInterface sqliteInterface) throws Exception { Connection connection = null; try { File file = new File(dbPath); boolean exists = file.exists(); if (!exists) { file.createNewFile(); } connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); return sqliteInterface.execute(statement); } catch (Exception e) { throw e; } finally { if (null != connection) { connection.close(); } } } } ================================================ FILE: src/main/java/code/util/TelegraphUtil.java ================================================ package code.util; import code.config.RequestProxyConfig; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import kong.unirest.ContentType; import kong.unirest.HttpResponse; import kong.unirest.MultipartBody; import kong.unirest.Unirest; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.*; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @Slf4j public class TelegraphUtil { @Data private static class TelegraphNode { private String tag; private Map attrs; private List children; } @Data public static class SaveResponse { private boolean ok; private String pageId; private String url; } public static SaveResponse save(RequestProxyConfig proxyConfig, String title, String author, String html, String appendHtml) { SaveResponse saveResponse = new SaveResponse(); saveResponse.setOk(false); try { String telegraphContent = getTelegraphContent(html, appendHtml); if (StringUtils.isBlank(telegraphContent)) { log.warn("Telegraph content is null, title: {}", title); return saveResponse; } for (int i = 0; i < 3; i++) { MultipartBody multipartBody = Unirest.post("https://edit.telegra.ph/save") .header("origin", " https://telegra.ph") .header("referer", " https://telegra.ph/") .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") .field("Data", new ByteArrayInputStream(telegraphContent.getBytes(StandardCharsets.UTF_8)), ContentType.create("text/html", StandardCharsets.UTF_8), "content.html") .field("title", title) .field("author", StringUtils.isBlank(author) ? "Unknown" : author) .field("page_id", "0") .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) .socketTimeout((int) TimeUnit.MILLISECONDS.convert(40, TimeUnit.SECONDS)) ; proxyConfig.viaProxy(multipartBody); HttpResponse response = multipartBody.asString(); String body = response.getBody(); log.info("title: {}, response: {}", title, body); JSONObject jsonObject = JSON.parseObject(body); String path = jsonObject.getString("path"); if (StringUtils.isNotBlank(path)) { saveResponse.setOk(true); saveResponse.setPageId(path); saveResponse.setUrl("https://telegra.ph/" + path); return saveResponse; } String errorDetails = jsonObject.getString("error_details"); if (StringUtils.isNotBlank(errorDetails) && "PATH_NUM_NOT_FOUND".equals(errorDetails)) { title = title + "-r" + RandomUtils.nextInt(1, 999); } } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } return saveResponse; } private static String getTelegraphContent(String html, String appendHtml) { Document doc = Jsoup.parse(html); Element body = doc.body(); if (StringUtils.isNotBlank(appendHtml)) { body.append(appendHtml); } TelegraphNode telegraphNode = (TelegraphNode) buildNote(body); return JSON.toJSONString(telegraphNode.getChildren()); } private static Object buildNote(Node node) { if (node instanceof TextNode) { return ((TextNode) node).text(); } if (!(node instanceof Element)) { return false; } TelegraphNode telegraphNode = new TelegraphNode(); String nodeName = node.nodeName(); if ("figure".equals(nodeName) || "div".equals(nodeName)) { nodeName = "p"; } if ("td".equals(nodeName)) { String aClass = node.attr("class"); if ("gutter".equals(aClass)) { return false; } } telegraphNode.setTag(nodeName); HashMap attributeMap = new HashMap<>(); for (Attribute attribute : node.attributes()) { String key = attribute.getKey(); if ("href".equals(key) || "src".equals(key)) { attributeMap.put(key, attribute.getValue()); } } telegraphNode.setAttrs(attributeMap); if (node.childNodeSize() > 0) { telegraphNode.setChildren(new ArrayList<>()); for (Node childNode : node.childNodes()) { telegraphNode.getChildren().add(buildNote(childNode)); } } return telegraphNode; } } ================================================ FILE: src/main/java/code/util/translate/MicrosoftTranslateHandle.java ================================================ package code.util.translate; import code.util.translate.base.TranslateAPI; import code.util.translate.base.TranslateAuth; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import kong.unirest.HttpResponse; import kong.unirest.Unirest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import static code.Main.GlobalConfig; @Slf4j public class MicrosoftTranslateHandle implements TranslateAPI { @Override public boolean hasAuth() { return false; } @Override public TranslateAuth auth() { return null; } @Override public String translate(String text, String from, String to) { String url = "https://api.microsofttranslator.com/v2/Http.svc/Translate?appId=A4D660A48A6A97CCA791C34935E4C02BBB1BEC1C&from=%s&to=%s&text=%s"; url = String.format(url, "auto".equals(from) ? "" : from, to, encode(text)); HttpResponse response = Unirest .get(url) .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) .socketTimeout((int) TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS)) .asString(); if (response.getStatus() == 200) { String body = response.getBody(); String string = StringUtils.substringBetween(body,"", ""); if (StringUtils.isNotBlank(string)) { return string; } } return null; } private String encode(String text) { try { return URLEncoder.encode(text, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/main/java/code/util/translate/Translate.java ================================================ package code.util.translate; import code.util.ExceptionUtil; import code.util.translate.base.TranslateAPI; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.List; @Slf4j public class Translate { private static List handleList = new ArrayList<>(); static { handleList.add(new YoudaoTranslateHandle()); handleList.add(new MicrosoftTranslateHandle()); } public static List translateAll(String text, String from, String to) { ArrayList list = new ArrayList<>(); for (TranslateAPI api : handleList) { try { if (api.hasAuth()) { if (null == api.auth()) { continue; } } String translate = api.translate(text, from, to); if (StringUtils.isNotBlank(translate)) { list.add(translate); } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } return list; } public static String translate(String text, String from, String to) { for (TranslateAPI api : handleList) { try { if (api.hasAuth()) { if (null == api.auth()) { continue; } } String translate = api.translate(text, from, to); if (StringUtils.isNotBlank(translate)) { return translate; } } catch (Exception e) { log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e)); } } return ""; } } ================================================ FILE: src/main/java/code/util/translate/YoudaoTranslateHandle.java ================================================ package code.util.translate; import code.util.translate.base.TranslateAPI; import code.util.translate.base.TranslateAuth; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import kong.unirest.HttpResponse; import kong.unirest.Unirest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import static code.Main.GlobalConfig; @Slf4j public class YoudaoTranslateHandle implements TranslateAPI { @Override public boolean hasAuth() { return true; } @Override public TranslateAuth auth() { String translateYoudaoKey = GlobalConfig.getTranslateYoudaoKey(); String translateYoudaoSecret = GlobalConfig.getTranslateYoudaoSecret(); if (StringUtils.isNotBlank(translateYoudaoKey) && StringUtils.isNotBlank(translateYoudaoSecret)) { TranslateAuth translateAuth = new TranslateAuth(); translateAuth.setKey(translateYoudaoKey); translateAuth.setSecret(translateYoudaoSecret); return translateAuth; } return null; } @Override public String translate(String text, String from, String to) { TranslateAuth auth = auth(); String salt = UUID.randomUUID().toString(); Long curtime = System.currentTimeMillis() / 1000; String raw = auth.getKey() + getInput(text) + salt + curtime + auth.getSecret(); String sign = DigestUtils.sha256Hex(raw); Map map = new HashMap(); map.put("q", text); map.put("from", from); map.put("to", to); map.put("appKey", auth.getKey()); map.put("salt", salt); map.put("sign", sign); map.put("signType", "v3"); map.put("curtime", curtime); HttpResponse response = Unirest .post("https://openapi.youdao.com/api") .fields(map) .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS)) .socketTimeout((int) TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS)) .asString(); if (response.getStatus() == 200) { String body = response.getBody(); JSONObject object = JSON.parseObject(body); if ("0".equals(object.getString("errorCode"))) { JSONArray translation = object.getJSONArray("translation"); if (null != translation && translation.size() > 0) { return translation.getString(0); } } } return null; } private static String getInput(String input) { if (input == null) { return null; } String result; int len = input.length(); if (len <= 20) { result = input; } else { String startStr = input.substring(0, 10); String endStr = input.substring(len - 10, len); result = startStr + len + endStr; } return result; } } ================================================ FILE: src/main/java/code/util/translate/base/TranslateAPI.java ================================================ package code.util.translate.base; public interface TranslateAPI { boolean hasAuth(); TranslateAuth auth(); String translate(String text, String from, String to); } ================================================ FILE: src/main/java/code/util/translate/base/TranslateAuth.java ================================================ package code.util.translate.base; import lombok.Data; @Data public class TranslateAuth { private String key; private String secret; } ================================================ FILE: src/main/resources/code/config/telegraph.html ================================================ 本文由 RssMonitorTelegramBot 自动生成, 版权归源站点所有。 原文: ${title} This article was generated by RssMonitorTelegramBot All copyright rights belong to the source site. Original article: ${title} ================================================ FILE: src/main/resources/code/config/webhook.json ================================================ { "enable":false, "list":[ { "url":"https://open.larksuite.com/open-apis/bot/v2/hook/...", "method":"POST", "headers":{ "User-Agent":"PostmanRuntime/7.31.3", "Content-Type":"application/json" }, "body":{ "msg_type":"text", "content":{ "text":"哈喽 ${text}" } } }, { "url":"https://open.larksuite.com/open-apis/bot/v2/hook/...", "method":"POST", "headers":{ "User-Agent":"PostmanRuntime/7.31.3", "Content-Type":"application/json" }, "body":{ "msg_type":"text", "content":{ "text":"Hello ${text}" } } } ] } ================================================ FILE: src/main/resources/i18n/i18n_en.properties ================================================ bot_start_succeed=Bot program starting succeeded\uFF01 help_text=Welcome to using this bot! invalid_command=Invalid command, you are not admin monitor_list=Name: %s, Open status: %s nothing_here=Nothing here. You can use /create command to create a new grab plan. on_monitor=Saving succeeded. The grab plan is set to "on" status and is ready to work. off_monitor=Saving succeeded. The grab plan is set to "off" status. on=On off=Off test=Test update=Update not_found=Not found. unknown_error=Program has occurred an unknown error. nothing_at_all=Nothing at all cancel_succeeded=Canceling succeeded. confirm=Confirm cancel=Cancel delete=Delete exit_succeeded=Exiting succeeded. getting=Getting... downloading=Downloading... force_record=Force record language_list=Please choose a language that you understand from the options below. change_language_finish=Your language setting has changed. monitor_exists=Your name of grab plan already exists, please input a new name and try again. create_name_too_long=Name is too long. Please send me a name that is 50 characters or less. create_monitor_1=Please send me a new grab plan name, I will create it. create_monitor_2=Grab plan: %s, has created. create_monitor_3=Please continue to send me a RSS feed URL. create_monitor_4=Verifying the RSS feed URL. Please be patient while we proceed. create_monitor_5=For now, program only supports XML RSS content. Please send me a new RSS feed URL again. create_monitor_6=RSS feed URL: %s. create_monitor_7=Please continue sending me the template content...\n\n\ Template Variable Explanation:\n\ You can customize the notification message text with the following variables\n\ ${link} -> Article URL\n\ ${title} -> Article title\n\ ${author} -> Article author\n\ ${telegraph} -> Telegraph article URL\n\ ${description} -> Article description\n\ ${translate|zh|title} -> Translate the title into Chinese\n\ ${translate|zh|description} -> Translate the description into Chinese\n\ ${translate|en|title} -> Translate the title into English\n\ ${translate|en|description} -> Translate the description into English\n\ You can modify the code in between to translate whatever you want... and so on...\n create_monitor_8=Message template content: \n%s. create_monitor_finish=Creating succeeded. Requesting the RSS feed URL. Please be patient while we proceed. test_monitor=Grab plan: %s, not found, please send me again. force_record_succeeded=Force record succeeded. update_monitor_1=Please continue to send me the field name you want to set. update_monitor_2=Filed name: %s not found. Please resend it to me. update_monitor_3=Field name: %s update_monitor_4=Please continue to send me the value of the field name. update_field_error=Apologies, an error occurred while setting the field value. update_monitor_finish=Updating succeeded. delete_monitor_confirm=Do you want to delete this monitor? delete_monitor_finish=Deleting succeeded. config_display_on=On config_display_web_page_preview=Web Page Preview config_display_notification=Notification config_display_zero_delay=Zero Delay config_display_url=RSS feed Url config_display_template=Template Content config_display_chat_id_array=Send Target List(Chat Id) config_display_file_basename=Name you_are_not_an_admin=Invalid use of this command because you are not an admin. are_you_sure_to_restart_right_now=Are you sure to restart the bot right now? restarting=Restarting... getting_update_data=Getting update data... are_you_sure_to_upgrade_this_bot_right_now=Are you sure to upgrade this bot right now ? target_version=Target version current_version=Current version update_logs=Update logs updating=Updating downloaded=Downloaded: %s, Total size: %s are_you_sure_to_update_the_config=Are you sure to update the config ? please_send_me_config_content=Please send me the config content you want to set. update_config_fail=Updating the config failed. This is because the config content does not meet certain standards. update_succeeded=Updating succeeded. update_failed=Updating failed. set_chat_buttons=Set Chat Buttons update_config=Update Config restart=Restart upgrade=Upgrade 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\ Sample\uFF1A\n\ \n\ \n\ \n\ ---\n\ \n\ \n\n\n\ All Sample:\n\ all\n\