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