master 63d476b63b39 cached
77 files
276.2 KB
60.5k tokens
343 symbols
1 requests
Download .txt
Showing preview only (304K chars total). Download the full file or copy to clipboard to get everything.
Repository: kylelin1998/RssMonitorTelegramBot
Branch: master
Commit: 63d476b63b39
Files: 77
Total size: 276.2 KB

Directory structure:
gitextract_nqpts3aa/

├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── README_en.md
├── docker-push.sh
├── pom.xml
├── run.sh
└── src/
    └── main/
        ├── java/
        │   └── code/
        │       ├── Main.java
        │       ├── config/
        │       │   ├── Config.java
        │       │   ├── ConfigField.java
        │       │   ├── ConfigSettings.java
        │       │   ├── DisplayConfigAnnotation.java
        │       │   ├── ExecutorsConfig.java
        │       │   ├── I18nConfig.java
        │       │   ├── I18nEnum.java
        │       │   ├── I18nLocaleEnum.java
        │       │   ├── MonitorConfigSettings.java
        │       │   ├── MonitorExecutorsConfig.java
        │       │   ├── ProxyTypeEnum.java
        │       │   ├── RequestProxyConfig.java
        │       │   └── TableEnum.java
        │       ├── eneity/
        │       │   ├── MonitorTableEntity.java
        │       │   ├── PageEntity.java
        │       │   ├── SentRecordTableEntity.java
        │       │   ├── WebhookTableEntity.java
        │       │   └── YesOrNoEnum.java
        │       ├── handler/
        │       │   ├── Command.java
        │       │   ├── CommandsHandler.java
        │       │   ├── Handler.java
        │       │   ├── I18nHandle.java
        │       │   ├── MessageHandle.java
        │       │   ├── StepsCenter.java
        │       │   ├── message/
        │       │   │   ├── CallbackBuilder.java
        │       │   │   ├── InlineKeyboardButtonBuilder.java
        │       │   │   ├── InlineKeyboardButtonListBuilder.java
        │       │   │   └── MessageHandle.java
        │       │   ├── steps/
        │       │   │   ├── StepErrorApi.java
        │       │   │   ├── StepExecuteResult.java
        │       │   │   ├── StepHandleApi.java
        │       │   │   ├── StepResult.java
        │       │   │   ├── StepsBuilder.java
        │       │   │   ├── StepsChatSession.java
        │       │   │   ├── StepsChatSessionBuilder.java
        │       │   │   ├── StepsHandler.java
        │       │   │   └── StepsRegisterCenter.java
        │       │   └── store/
        │       │       ├── ChatButtonsStore.java
        │       │       ├── Store.java
        │       │       └── WebhookStore.java
        │       ├── repository/
        │       │   ├── I18nTableRepository.java
        │       │   ├── MonitorTableRepository.java
        │       │   ├── SentRecordTableRepository.java
        │       │   ├── WebhookTableRepository.java
        │       │   └── base/
        │       │       ├── SqlBuilder.java
        │       │       ├── TableEntity.java
        │       │       ├── TableField.java
        │       │       ├── TableName.java
        │       │       └── TableRepository.java
        │       └── util/
        │           ├── BytesUtil.java
        │           ├── DownloadUtil.java
        │           ├── ExceptionUtil.java
        │           ├── GithubUtil.java
        │           ├── ProgramUtil.java
        │           ├── RssUtil.java
        │           ├── Snowflake.java
        │           ├── SqliteUtil.java
        │           ├── TelegraphUtil.java
        │           └── translate/
        │               ├── MicrosoftTranslateHandle.java
        │               ├── Translate.java
        │               ├── YoudaoTranslateHandle.java
        │               └── base/
        │                   ├── TranslateAPI.java
        │                   └── TranslateAuth.java
        └── resources/
            ├── code/
            │   └── config/
            │       ├── telegraph.html
            │       └── webhook.json
            ├── i18n/
            │   ├── i18n_en.properties
            │   └── i18n_zh_CN.properties
            └── logback.xml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/

/logs
/config

.mvn
mvnw
mvnw.cmd

*.imi

================================================
FILE: Dockerfile
================================================
FROM openjdk:8-jdk-alpine
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
# 增加字体,解决验证码没有字体报空指针问题
RUN set -xe \
&& apk --no-cache add ttf-dejavu fontconfig
# 系统编码
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8

ENV BOT_ADMIN_ID=''
ENV BOT_NAME=''
ENV BOT_TOKEN=''
ENV PROXY=false
ENV PROXY_HOST=127.0.0.1
ENV PROXY_PORT=7890

WORKDIR /app
COPY rss-monitor-for-telegram-universal.jar rss-monitor-for-telegram-universal.jar
COPY run.sh run.sh

ENTRYPOINT ["sh", "run.sh"]

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2023 kylelin1998

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
### 简体中文 | [English](./README_en.md)

![License](https://img.shields.io/badge/license-MIT-green)
[![release](https://img.shields.io/github/v/release/kylelin1998/RssMonitorTelegramBot)](https://github.com/kylelin1998/RssMonitorTelegramBot/releases/latest)

## 简介
RSS监控最新文章, 如果有监控到最新文章会通知到您设置好的Telegram群聊, 频道, 或者个人号上

支持自定义消息通知, 由你掌控内容

支持RSS同步到Telegraph

支持自定义Webhook调用

## 具体功能
#### 通知
不仅可以通知到TG, 你还可以自定义Webhook通知到任意你想要的地方
#### 通知对象
拥有全局通知对象, 方案本身也可单独配置通知对象, 如果方案本身没有通知对象配置默认采用全局通知对象, 非常自由
#### 无限方案
你可以添加任意数量监控RSS方案, 不限制, 好管理
#### 通知模板变量
高度可配置通知文本, 支持众多通知模板变量
#### 抓取资源
你可以为你的方案配置开启抓取资源功能, 开启之后默认会抓取RSS内容的一张图片结合通知文本发送到TG
#### 翻译你想翻译的
模板变量支持翻译到你想要的, 比如你可以翻译标题成英文, 泰语, 俄语, 德语...等等
#### 好升级
机器人内部内置升级功能, 一次部署, 后续升级在机器人内点击升级即可

## 部署
机器人的部署步骤是基于 Docker 的,其机器人升级功能也基于 Docker,因此请使用 Docker 进行部署,以防出现错误

### 部署方式1 (推荐)
⭐ Youtube: https://youtu.be/mNg6TFyozZk

⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1qF411f7pg/

#### 一键部署
```
docker run --name rssb -d -v $(pwd)/config:/app/config -e BOT_ADMIN_ID=管理者的ChatId -e BOT_NAME=机器人的username -e BOT_TOKEN=机器人token --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-tg-bot
```
#### 一键部署(开启代理)
```
docker run --name rssb -d -v $(pwd)/config:/app/config -e BOT_ADMIN_ID=管理者的ChatId -e BOT_NAME=机器人的username -e BOT_TOKEN=机器人token -e PROXY=true -e PROXY_HOST=127.0.0.1 -e PROXY_PORT=7890 --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-tg-bot
```

### 部署方式2 (不推荐)
Youtube:https://youtu.be/CiDxb1ESijQ

哔哩哔哩: https://www.bilibili.com/video/BV1Ts4y1S7bn/

首先,在您的服务器上创建一个文件夹

然后,在其中创建名为 config 的另一个文件夹,config文件夹下必须包含名为 config.json 的JSON文件

接着,将 rss-monitor-for-telegram-universal.jar, run.sh 和 Dockerfile 传输到该文件夹中

### config.json
```json
{
  "debug": false,
  "on_proxy": false,
  "proxy_host": "127.0.0.1",
  "proxy_port": 7890,
  "bot_admin_id": "xxxx",
  "bot_name": "xxx",
  "bot_token": "xxx",
  "interval_minute": 10,
  "chatIdArray": [
    "xxxxx"
  ],
  "permission_chat_id_array": [
    "xxxx"
  ]
}
```
bot admin主要作用是设置成只有你才能触发命令
* on_proxy -> 是否开启代理
* bot_admin_id -> Bot的管理者chat id
* bot_name -> Bot 用户名
* bot_token -> Bot token
* interval_minute -> 监控间隔(分钟)
* chatIdArray -> 需要发送的Chat Id列表
* permission_chat_id_array -> 你只能允许列表下的这些chat id使用机器人, 可以填写个人的,或者是群的chat id
* translate_youdao_key -> 有道翻译应用id
* translate_youdao_secret -> 有道翻译密钥

### 第一步:
编译镜像
```
docker build -t rssb .
```

### 第二步:
运行容器镜像
```
docker run --name rssb -d -v $(pwd):/app --restart=always rssb
```

## 使用说明
**机器人命令:**
```
create - 创建计划
list - 计划列表
exit - 退出
language - 切换语言
admin - 管理命令
restart - 重启机器人
upgrade - 升级机器人
help - 帮助
```

监控部分属性说明
* webPagePreview -> 消息web预览
* notification -> 通知开关
* zeroDelay -> 零延迟监控开关, 不受间隔时间限制

template说明:
支持自定义发送通知消息文本
* ${link} -> 文章地址
* ${title} -> 文章标题
* ${author} -> 文章作者
* ${telegraph} -> Telegraph文章地址
* ${description} -> 文章内容
* ${translate|zh|title} -> 将标题翻译成中文
* ${translate|zh|description} -> 将描述翻译成中文
* ${translate|en|title} -> 将标题翻译成英文
* ${translate|en|description} -> 将描述翻译成英文
* 翻译中间的代码可以更改为自己想要翻译的...以此类推...

例子, 会自动替换对应内容:
```
${title}

Telegraph: ${telegraph}

原文: ${link}
```

![d529dad53bfee8844d66330e837912a869f427af.png](https://openimg.kylelin1998.com/img/d529dad53bfee8844d66330e837912a869f427af.png)


================================================
FILE: README_en.md
================================================
### [简体中文](./README.md) | English

![License](https://img.shields.io/badge/license-MIT-green)
[![release](https://img.shields.io/github/v/release/kylelin1998/RssMonitorTelegramBot)](https://github.com/kylelin1998/RssMonitorTelegramBot/releases/latest)

## Introduction
Monitor articles for RSS

Send messages of an up-to-date article to your set-up Telegram channel,  group, or personal if you have up-to-date.

Support custom message content to your decision.

Support RSS article auto sync to Telegraph platform.

Support custom Webhook invocation.

## Specific Features
#### Notifications
Not only can you receive notifications on Telegram, but you can also customize webhook notifications to any platform you desire.
#### Notification Targets
You have the option to set global notification targets, and each individual plan can also have its unique notification targets. If a plan doesn't have specific notification targets configured, it will default to the global ones. This provides great flexibility.
#### Unlimited Plans
You can add an unlimited number of RSS monitoring plans, with no restrictions, for easy management.
#### Notification Template Variables
Highly configurable notification text, with support for numerous template variables.
#### Resource Crawling
You can enable resource crawling for your plans. Once enabled, it will automatically grab an image from the RSS content, combining it with the notification text and sending it to Telegram.
#### Translation of Your Choice
Template variables support translation to the language of your choice. For example, you can translate the title into English, Thai, Russian, German, etc.
#### Convenient Upgrades
The bot includes a built-in upgrade feature. After initial deployment, you can simply click on the upgrade button within the bot for future upgrades.

## Deploy
The bot's deploy steps based on the Docker, its upgrade feature also based on the Docker, so please use the Docker to deploy it in case appear error.

### Deployment method 1 (recommended)
⭐ Youtube: https://youtu.be/mNg6TFyozZk

⭐ 哔哩哔哩: https://www.bilibili.com/video/BV1qF411f7pg/

#### One-click deployment
```
docker run --name rssb -d -v $(pwd)/config:/app/config -e BOT_ADMIN_ID=AdminChatId -e BOT_NAME=BotUsername -e BOT_TOKEN=BotToken --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-tg-bot
```
#### One-click deployment (with proxy enabled)
```
docker run --name rssb -d -v $(pwd)/config:/app/config -e BOT_ADMIN_ID=AdminChatId -e BOT_NAME=BotUsername -e BOT_TOKEN=BotToken -e PROXY=true -e PROXY_HOST=127.0.0.1 -e PROXY_PORT=7890 --log-opt max-size=10MB --log-opt max-file=5 --restart=always kylelin1998/rss-tg-bot
```

### Deployment method 2 (not recommended)
Youtube:https://youtu.be/CiDxb1ESijQ

哔哩哔哩: https://www.bilibili.com/video/BV1Ts4y1S7bn/

### Prepare

To start, create a folder named whatever you prefer on your server.

Then create another folder named config and the config folder must contains a json file named config.json in, then transfer rss-monitor-for-telegram-universal.jar, run.sh and Dockerfile to the folder.

### config.json
```
{
  "debug": false,
  "on_proxy": false,
  "proxy_host": "127.0.0.1",
  "proxy_port": 7890,
  "bot_admin_id": "xxxx",
  "bot_name": "xxx",
  "bot_token": "xxx",
  "interval_minute": 10,
  "chatIdArray": [
    "xxxxx"
  ],
  "permission_chat_id_array": [
    "xxxx"
  ]
}
```
bot admin's main role is to set it so that only you can trigger commands.
* on_proxy -> Enable proxy or not
* bot_admin_id -> Chat ID of the Bot administrator
* bot_name -> Bot username
* bot_token -> Bot token
* interval_minute -> Monitoring interval (in minutes)
* chatIdArray -> List of Chat IDs to send notifications to
* permission_chat_id_array -> Only allow the use of the bot by the chat IDs in this list. You can fill in your personal chat ID or group chat IDs.
* translate_youdao_key -> Youdao translation application ID
* translate_youdao_secret -> Youdao translation secret key

### First step:
Build a docker image for use.
```
docker build -t rssb .
```

### Second step:
Run the docker image of just then build.
```
docker run --name rssb -d -v $(pwd):/app --restart=always rssb
```

## Usage
**Commands:**
```
create - Create plan
list - Plan list
exit - Exit
language - Change language
admin - Admin
restart - Restart the bot
upgrade - Upgrade the bot
help - Help
```

Monitor config description
* webPagePreview -> Web page preview
* notification -> Notification switch
* zeroDelay -> Zero delays to monitor

Template content:
Support custom message content
* ${link} -> Article website URL
* ${title} -> Article title
* ${author} -> Article author
* ${telegraph} -> Telegraph URL
* ${description} -> Article description
* ${translate|zh|title} -> Translate the title into Chinese
* ${translate|zh|description} -> Translate the description into Chinese
* ${translate|en|title} -> Translate the title into English
* ${translate|en|description} -> Translate the description into English
* You can modify the code in between to translate whatever you want... and so on...

For example, automatically replace the variable:
```
${title}

Telegraph: ${telegraph}

Original article: ${link}
```

![3fdb60f99c4cb66084eea0a8116b7342d96a1039.png](https://openimg.kylelin1998.com/img/3fdb60f99c4cb66084eea0a8116b7342d96a1039.png)


================================================
FILE: docker-push.sh
================================================
#!/bin/sh

sudo apt install -y qemu-user-static binfmt-support
docker buildx create --use
docker buildx ls

docker buildx build --platform linux/amd64,linux/386,linux/arm64 -t kylelin1998/rss-tg-bot . --push

================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.kylelin1998</groupId>
    <artifactId>rss-monitor-for-telegram</artifactId>
    <version>universal</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <repositories>
        <repository>
            <id>nexus-tencentyun</id>
            <name>Nexus tencentyun</name>
            <url>http://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url>
            <layout>default</layout>
            <!-- 是否开启发布版构件下载 -->
            <releases>
                <enabled>true</enabled>
            </releases>
            <!-- 是否开启快照版构件下载 -->
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>central</id>
            <name>aliyun maven</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <layout>default</layout>
            <!-- 是否开启发布版构件下载 -->
            <releases>
                <enabled>true</enabled>
            </releases>
            <!-- 是否开启快照版构件下载 -->
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.23</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>

        <!-- logback+slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.28</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.3</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
            <type>jar</type>
        </dependency>

        <dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambots</artifactId>
            <version>6.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambotsextensions</artifactId>
            <version>6.5.0</version>
        </dependency>

        <dependency>
            <groupId>com.vladsch.flexmark</groupId>
            <artifactId>flexmark-all</artifactId>
            <version>0.64.0</version>
        </dependency>

        <dependency>
            <groupId>com.rometools</groupId>
            <artifactId>rome</artifactId>
            <version>1.18.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.40.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.konghq</groupId>
            <artifactId>unirest-java</artifactId>
            <version>3.14.1</version>
        </dependency>

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5</artifactId>
            <version>5.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5-fluent</artifactId>
            <version>5.1.3</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>code.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

================================================
FILE: run.sh
================================================
#!/bin/sh

temp=temp.jar
app=/app/rss-monitor-for-telegram-universal.jar
temp_jar=/app/temp.jar

if [ -e $temp ]
then
    echo "updating..."
    rm -rf $app
    mv $temp_jar $app
    echo "updated..."
fi

java -Djava.security.egd=file:/dev/./urandom -DbotAdminId=$BOT_ADMIN_ID -DbotName=$BOT_NAME -DbotToken=$BOT_TOKEN -DbotProxy=$PROXY -DbotProxyHost=$PROXY_HOST -DbotProxyPort=$PROXY_PORT -jar $app

================================================
FILE: src/main/java/code/Main.java
================================================
package code;

import code.config.Config;
import code.config.ConfigSettings;
import code.config.I18nEnum;
import code.config.RequestProxyConfig;
import code.handler.CommandsHandler;
import code.handler.Handler;
import code.handler.I18nHandle;
import code.handler.MessageHandle;
import code.handler.store.Store;
import code.repository.*;
import code.util.ExceptionUtil;
import code.util.Snowflake;
import com.alibaba.fastjson2.JSON;
import kong.unirest.Unirest;
import lombok.extern.slf4j.Slf4j;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;

/**
 *
 *    ,---.  ,--.                                   ,--.          ,--.,--.
 *   /  O  \ |  |,--.   ,--. ,--,--.,--. ,--.,---.  |  |-.  ,---. |  |`--' ,---.,--.  ,--.,---.
 *  |  .-.  ||  ||  |.'.|  |' ,-.  | \  '  /(  .-'  | .-. '| .-. :|  |,--.| .-. :\  `'  /| .-. :
 *  |  | |  ||  ||   .'.   |\ '-'  |  \   ' .-'  `) | `-' |\   --.|  ||  |\   --. \    / \   --.
 *  `--' `--'`--''--'   '--' `--`--'.-'  /  `----'   `---'  `----'`--'`--' `----'  `--'   `----'
 *                                  `---'
 *  Always believe that something wonderful is about to happen
 *
 *  If you have any additional features you'd like to suggest or if you have any feedback,
 *  you can reach me at my email address: email@kylelin1998.com
 */
@Slf4j
public class Main {
    public static CommandsHandler Bot = null;
    public static volatile ConfigSettings GlobalConfig = Config.initConfig();
    public final static code.repository.SentRecordTableRepository SentRecordTableRepository = new SentRecordTableRepository();
    public final static code.repository.I18nTableRepository I18nTableRepository = new I18nTableRepository();
    public final static code.repository.MonitorTableRepository MonitorTableRepository = new MonitorTableRepository();
    public final static code.repository.WebhookTableRepository WebhookTableRepository = new WebhookTableRepository();
    public final static Snowflake Snowflake = new Snowflake(997);

    public static void main(String[] args) {
        log.info(String.format("Main args: %s", JSON.toJSONString(args)));
        log.info(String.format("System properties: %s", System.getProperties()));
        log.info(String.format("Config: %s", JSON.toJSONString(GlobalConfig)));

        Unirest
                .config()
                .enableCookieManagement(false)
                .verifySsl(GlobalConfig.getVerifySsl())
        ;

        new Thread(() -> {
            while (true) {
                try {
                    GlobalConfig = Config.readConfig();

                    Thread.sleep(5000);
                } catch (InterruptedException e) {}
            }
        }).start();

        Store.init();
        Handler.init();

        log.info("Program is running");

        try {
            TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);

            if (GlobalConfig.getOnProxy()) {
                Bot = new CommandsHandler(RequestProxyConfig.create().buildDefaultBotOptions());
            } else {
                Bot = new CommandsHandler();
            }

            botsApi.registerBot(Bot);

            MessageHandle.sendMessage(GlobalConfig.getBotAdminId(), I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.BotStartSucceed) + I18nHandle.getText(GlobalConfig.getBotAdminId(), I18nEnum.CurrentVersion) + ": " + Config.MetaData.CurrentVersion, false);

            Config.oldDataConvert();

        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
    }
}


================================================
FILE: src/main/java/code/config/Config.java
================================================
package code.config;

import code.eneity.MonitorTableEntity;
import code.eneity.YesOrNoEnum;
import code.util.ExceptionUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;

import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

import static code.Main.*;

@Slf4j
public class Config {

    public static final String CurrentDir = System.getProperty("user.dir") + File.separator + "config";
    public static final String MonitorDir = CurrentDir + File.separator + "monitor";

    public static final String SettingsPath = CurrentDir + File.separator + "config.json";

    public static final String DBPath = CurrentDir + File.separator + "db.db";

    public final static String TempDir = CurrentDir + File.separator + "temp";

    public static String TelegraphHtml = new BufferedReader(new InputStreamReader(Config.class.getResourceAsStream("telegraph.html"), StandardCharsets.UTF_8))
            .lines()
            .collect(Collectors.joining("\n"));
    public static String WebhookJson = new BufferedReader(new InputStreamReader(Config.class.getResourceAsStream("webhook.json"), StandardCharsets.UTF_8))
            .lines()
            .collect(Collectors.joining("\n"));

    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

    public final static class MetaData {
        public final static String CurrentVersion = "1.1.5";
        public final static String GitOwner = "kylelin1998";
        public final static String GitRepo = "RssMonitorTelegramBot";
        public final static String ProcessName = "rss-monitor-for-telegram-universal.jar";
        public final static String JarName = "rss-monitor-for-telegram-universal.jar";
    }

    static {
        mkdirs(CurrentDir, MonitorDir, TempDir);

        new Thread(() -> {
            while (true) {
                try {
                    File file = new File(TempDir);
                    ArrayList<File> files = new ArrayList<>();
                    file.list((File dir, String name) -> {
                        File file1 = new File(dir, name);
                        try {
                            BasicFileAttributes attributes = Files.readAttributes(file1.toPath(), BasicFileAttributes.class);
                            FileTime fileTime = attributes.creationTime();
                            long millis = System.currentTimeMillis() - fileTime.toMillis();
                            if (millis > 3600000) {
                                files.add(file1);
                            }
                        } catch (IOException e) {
                            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                        }

                        return true;
                    });

                    for (File df : files) {
                        df.delete();
                    }
                } catch (Exception e) {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                }
                try {
                    TimeUnit.MINUTES.sleep(30);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

    private static void mkdirs(String... dirs) {
        for (String dir : dirs) {
            File file = new File(dir);
            if (!file.exists()) {
                file.mkdirs();
            }
        }
    }

    public static ConfigSettings initConfig() {
        File file = new File(SettingsPath);
        if (!file.exists()) {
            Properties properties = System.getProperties();

            ConfigSettings configSettings = new ConfigSettings();
            configSettings.setBotAdminId(properties.getProperty("botAdminId", ""));
            configSettings.setBotName(properties.getProperty("botName", ""));
            configSettings.setBotToken(properties.getProperty("botToken", ""));
            configSettings.setOnProxy(Boolean.valueOf(properties.getProperty("botProxy", "false")));
            configSettings.setProxyHost(properties.getProperty("botProxyHost", "127.0.0.1"));
            configSettings.setProxyPort(Integer.valueOf(properties.getProperty("botProxyPort", "7890")));

            saveConfig(handle(configSettings));
        }
        return readConfig();
    }

    private static ConfigSettings handle(ConfigSettings configSettings) {
        Boolean debug = configSettings.getDebug();
        if (null == debug) {
            configSettings.setDebug(false);
        }
        String[] permissionChatIdArray = configSettings.getPermissionChatIdArray();
        if (null == permissionChatIdArray) {
            configSettings.setPermissionChatIdArray(new String[]{ configSettings.getBotAdminId() });
        }
        String[] chatIdArray = configSettings.getChatIdArray();
        if (null == chatIdArray) {
            configSettings.setChatIdArray(new String[]{ configSettings.getBotAdminId() });
        }
        Integer intervalMinute = configSettings.getIntervalMinute();
        if (null == intervalMinute) {
            configSettings.setIntervalMinute(5);
        }
        Boolean hideCopyrightTips = configSettings.getHideCopyrightTips();
        if (null == hideCopyrightTips) {
            configSettings.setHideCopyrightTips(false);
        }
        Boolean verifySsl = configSettings.getVerifySsl();
        if (null == verifySsl) {
            configSettings.setVerifySsl(true);
        }
        List<String> excludeKeywords = configSettings.getExcludeKeywords();
        if (null == excludeKeywords) {
            configSettings.setExcludeKeywords(new ArrayList<>());
        }
        List<String> excludeKeywordsRegex = configSettings.getExcludeKeywordsRegex();
        if (null == excludeKeywordsRegex) {
            configSettings.setExcludeKeywordsRegex(new ArrayList<>());
        }
        List<String> includeKeywords = configSettings.getIncludeKeywords();
        if (null == includeKeywords) {
            configSettings.setIncludeKeywords(new ArrayList<>());
        }
        List<String> includeKeywordsRegex = configSettings.getIncludeKeywordsRegex();
        if (null == includeKeywordsRegex) {
            configSettings.setIncludeKeywordsRegex(new ArrayList<>());
        }
        return configSettings;
    }

    public static ConfigSettings readConfig() {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        readLock.lock();
        try {
            File file = new File(SettingsPath);
            boolean exists = file.exists();
            if (exists) {
                String text = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
                ConfigSettings configSettings = JSON.parseObject(text, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch);
                return handle(configSettings);
            } else {
                log.warn("Settings file not found, " + SettingsPath);
            }
        } catch (IOException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e), SettingsPath);
        } finally {
            readLock.unlock();
        }
        return null;
    }

    public static ConfigSettings verifyConfig(String configJson) {
        if (StringUtils.isBlank(configJson)) {
            return null;
        }
        ConfigSettings configSettings = null;
        try {
            configSettings = JSON.parseObject(configJson, ConfigSettings.class, JSONReader.Feature.SupportSmartMatch);
        } catch (JSONException e) {
        }
        if (null == configSettings) {
            return null;
        }
        for (Field field : configSettings.getClass().getDeclaredFields()) {
            ConfigField configField = field.getAnnotation(ConfigField.class);
            if (null == configField) {
                continue;
            }
            if (configField.isNotNull()) {
                try {
                    field.setAccessible(true);
                    Object o = field.get(configSettings);
                    if (null == o) {
                        return null;
                    }
                } catch (IllegalAccessException e) {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    return null;
                }
            }
        }

        return configSettings;
    }

    public synchronized static void oldDataConvert() {
        File file = new File(MonitorDir);
        file.list((File dir, String name) -> {
            File monitorFile = new File(dir, name);
            try {
                if (monitorFile.isFile()) {
                    String text = FileUtils.readFileToString(monitorFile, StandardCharsets.UTF_8);
                    MonitorConfigSettings configSettings = JSON.parseObject(text, MonitorConfigSettings.class, JSONReader.Feature.SupportSmartMatch);
                    configSettings.setFilename(name);
                    configSettings.setFileBasename(StringUtils.removeEnd(name, ".json"));
                    if (null == configSettings.getNotification()) {
                        configSettings.setNotification(true);
                    }
                    if (null == configSettings.getZeroDelay()) {
                        configSettings.setZeroDelay(false);
                    }

                    MonitorTableEntity monitorTableEntity = new MonitorTableEntity();
                    monitorTableEntity.setId(Snowflake.nextIdToStr());
                    monitorTableEntity.setChatId(GlobalConfig.botAdminId);
                    monitorTableEntity.setEnable(YesOrNoEnum.toInt(configSettings.getOn()));
                    monitorTableEntity.setName(configSettings.getFileBasename());
                    monitorTableEntity.setNotification(YesOrNoEnum.toInt(configSettings.getNotification()));
                    monitorTableEntity.setUrl(configSettings.getUrl());
                    monitorTableEntity.setTemplate(configSettings.getTemplate());
                    monitorTableEntity.setZeroDelay(YesOrNoEnum.toInt(configSettings.getZeroDelay()));
                    monitorTableEntity.setCreateTime(System.currentTimeMillis());
                    monitorTableEntity.setChatIdArrayJson(JSON.toJSONString(configSettings.getChatIdArray()));
                    monitorTableEntity.setWebPagePreview(YesOrNoEnum.toInt(configSettings.getWebPagePreview()));
                    monitorTableEntity.setCaptureFlag(YesOrNoEnum.No.getNum());
                    MonitorTableRepository.insert(monitorTableEntity);

                    monitorFile.delete();
                }
            } catch (IOException e) {
                log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
            }
            return true;
        });
    }

    public static boolean saveConfig(ConfigSettings configSettings) {
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        writeLock.lock();
        try {
            File file = new File(SettingsPath);
            FileUtils.write(file, JSON.toJSONString(configSettings, JSONWriter.Feature.PrettyFormat), StandardCharsets.UTF_8);
            return true;
        } catch (IOException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        } finally {
            writeLock.unlock();
        }
        return false;
    }

}


================================================
FILE: src/main/java/code/config/ConfigField.java
================================================
package code.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ConfigField {

    boolean isNotNull();

}


================================================
FILE: src/main/java/code/config/ConfigSettings.java
================================================
package code.config;

import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;

import java.util.List;

@Data
public class ConfigSettings {

    @ConfigField(isNotNull = true)
    public Boolean debug;

    @JSONField(name = "on_proxy")
    @ConfigField(isNotNull = true)
    public Boolean onProxy;
    @JSONField(name = "proxy_host")
    public String proxyHost;
    @JSONField(name = "proxy_port")
    public Integer proxyPort;

    @JSONField(name = "bot_admin_id")
    @ConfigField(isNotNull = true)
    public String botAdminId;
    @JSONField(name = "permission_chat_id_array")
    @ConfigField(isNotNull = true)
    private String[] permissionChatIdArray;

    @JSONField(name = "bot_name")
    @ConfigField(isNotNull = true)
    public String botName;
    @JSONField(name = "bot_token")
    @ConfigField(isNotNull = true)
    public String botToken;

    @JSONField(name = "interval_minute")
    @ConfigField(isNotNull = true)
    private Integer intervalMinute;

    @JSONField(name = "chatId_array")
    @ConfigField(isNotNull = true)
    private String[] chatIdArray;

    @JSONField(name = "chat_buttons")
    private String chatButtons;

    @JSONField(name = "hide_copyright_tips")
    private Boolean hideCopyrightTips;

    @JSONField(name = "verify_ssl")
    private Boolean verifySsl;

    @JSONField(name = "exclude_keywords")
    private List<String> excludeKeywords;
    @JSONField(name = "exclude_keywords_regex")
    private List<String> excludeKeywordsRegex;

    @JSONField(name = "include_keywords")
    private List<String> includeKeywords;
    @JSONField(name = "include_keywords_regex")
    private List<String> includeKeywordsRegex;

    @JSONField(name = "translate_youdao_key")
    public String translateYoudaoKey;
    @JSONField(name = "translate_youdao_secret")
    public String translateYoudaoSecret;

}


================================================
FILE: src/main/java/code/config/DisplayConfigAnnotation.java
================================================
package code.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisplayConfigAnnotation {

    String i18n();

    boolean set();

}


================================================
FILE: src/main/java/code/config/ExecutorsConfig.java
================================================
package code.config;

import lombok.extern.slf4j.Slf4j;
import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;

import java.util.concurrent.*;

@Slf4j
public class ExecutorsConfig {

    private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(false).setNameFormat("handle-pool-%d").build();

    private static ExecutorService fixedThreadPool = new ThreadPoolExecutor(
            5,
            20,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(200),
            threadFactory,
            (Runnable r, ThreadPoolExecutor executor) -> {
                log.error(r.toString() + " is Rejected");
            }
    );

    public static void submit(Runnable task) {
        fixedThreadPool.submit(task);
    }

}


================================================
FILE: src/main/java/code/config/I18nConfig.java
================================================
package code.config;

import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class I18nConfig {

    private static Map<String, Map<String, String>> cacheMap = new LinkedHashMap<>();

    static {
        ResourceBundle.Control control = new ResourceBundle.Control() {
            @Override
            public ResourceBundle newBundle(String baseName, java.util.Locale locale, String format, ClassLoader loader, boolean reload) throws IOException {
                String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
                InputStream inputStream = null;
                try {
                    inputStream = loader.getResourceAsStream(resourceName);
                    if (inputStream != null) {
                        return new PropertyResourceBundle(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                    }
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                }
                return null;
            }
        };
        for (I18nLocaleEnum value : I18nLocaleEnum.values()) {
            ResourceBundle bundle = ResourceBundle.getBundle("i18n/i18n", value.getLocale(), control);

            HashMap<String, String> hashMap = new HashMap<>();
            for (String s : bundle.keySet()) {
                hashMap.put(s, bundle.getString(s));
            }

            cacheMap.put(value.getAlias(), hashMap);
        }
    }

    public static String getText(String i18nAlias, String key) {
        Map<String, String> map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias());
        return map.get(key);
    }

    public static String getText(String i18nAlias, I18nEnum i18nEnum) {
        Map<String, String> map = cacheMap.get(StringUtils.isNotBlank(i18nAlias) ? i18nAlias : I18nLocaleEnum.ZH_CN.getAlias());
        return map.get(i18nEnum.getKey());
    }

}


================================================
FILE: src/main/java/code/config/I18nEnum.java
================================================
package code.config;

import lombok.Getter;

@Getter
public enum I18nEnum {

    BotStartSucceed("bot_start_succeed"),
    HelpText("help_text"),

    InvalidCommand("invalid_command"),
    MonitorList("monitor_list"),
    NothingHere("nothing_here"),

    On("on"),
    Off("off"),
    Test("test"),
    Update("update"),
    NotFound("not_found"),
    UnknownError("unknown_error"),
    NothingAtAll("nothing_at_all"),
    CancelSucceeded("cancel_succeeded"),
    Confirm("confirm"),
    Cancel("cancel"),
    Delete("delete"),
    Finish("finish"),
    ExitSucceeded("exit_succeeded"),
    Getting("getting"),
    Downloading("downloading"),
    ForceRecord("force_record"),

    OnMonitor("on_monitor"),
    OffMonitor("off_monitor"),

    LanguageList("language_list"),
    ChangeLanguageFinish("change_language_finish"),

    MonitorExists("monitor_exists"),

    CreateNameTooLong("create_name_too_long"),
    CreateMonitor1("create_monitor_1"),
    CreateMonitor2("create_monitor_2"),
    CreateMonitor3("create_monitor_3"),
    CreateMonitor4("create_monitor_4"),
    CreateMonitor5("create_monitor_5"),
    CreateMonitor6("create_monitor_6"),
    CreateMonitor7("create_monitor_7"),
    CreateMonitor8("create_monitor_8"),
    CreateMonitorFinish("create_monitor_finish"),


    TestMonitor("test_monitor"),
    ForceRecordSucceeded("force_record_succeeded"),


    UpdateMonitor1("update_monitor_1"),
    UpdateMonitor2("update_monitor_2"),
    UpdateMonitor3("update_monitor_3"),
    UpdateMonitor4("update_monitor_4"),
    UpdateFieldError("update_field_error"),
    UpdateMonitorFinish("update_monitor_finish"),

    DeleteMonitorConfirm("delete_monitor_confirm"),
    DeleteMonitorFinish("delete_monitor_finish"),


    ConfigDisplayOn("config_display_on"),
    ConfigDisplayWebPagePreview("config_display_web_page_preview"),
    ConfigDisplayNotification("config_display_notification"),
    ConfigDisplayZeroDelay("config_display_zero_delay"),
    ConfigDisplayUrl("config_display_url"),
    ConfigDisplayTemplate("config_display_template"),
    ConfigDisplayChatIdArray("config_display_chat_id_array"),
    ConfigDisplayFileBasename("config_display_file_basename"),

    YouAreNotAnAdmin("you_are_not_an_admin"),
    AreYouSureToRestartRightNow("are_you_sure_to_restart_right_now"),
    Restarting("restarting"),
    GettingUpdateData("getting_update_data"),
    AreYouSureToUpgradeThisBotRightNow("are_you_sure_to_upgrade_this_bot_right_now"),
    TargetVersion("target_version"),
    CurrentVersion("current_version"),
    UpdateLogs("update_logs"),
    Updating("updating"),
    Downloaded("downloaded"),

    AreYouSureToUpdateTheConfig("are_you_sure_to_update_the_config"),
    PleaseSendMeConfigContent("please_send_me_config_content"),
    UpdateConfigFail("update_config_fail"),

    UpdateSucceeded("update_succeeded"),
    UpdateFailed("update_failed"),

    SetChatButtons("set_chat_buttons"),
    UpdateConfig("update_config"),
    Restart("restart"),
    Upgrade("upgrade"),

    PleaseSendMeChatButtons("please_send_me_chat_buttons"),
    FormatError("format_error"),

    SettingWebhook("setting_webhook"),
    HideCopyrightTips("hide_copyright_tips"),
    AreYouSureYouWantToHideCopyrightTips("are_you_sure_you_want_to_hide_copyright_tips"),
    CurrentSetting("current_setting"),
    PleaseSendMeWebhookSettings("please_send_me_webhook_settings"),

    Back("back"),
    Refresh("refresh"),
    ExcludeKeywords("exclude_keywords"),
    ExcludeKeywordsRegex("exclude_keywords_regex"),
    PleaseSendMeExcludeKeywords("please_send_me_exclude_keywords"),
    PleaseSendMeExcludeKeywordsRegex("please_send_me_exclude_keywords_regex"),

    VerifySsl("verify_ssl"),
    AreYouSureYouWantToSetVerifySsl("are_you_sure_you_want_to_set_verify_ssl"),
    Enable("enable"),
    Disable("disable"),
    NeedToRestartBot("need_to_restart_bot"),

    IncludeKeywords("include_keywords"),
    IncludeKeywordsRegex("include_keywords_regex"),
    PleaseSendMeIncludeKeywords("please_send_me_include_keywords"),
    PleaseSendMeIncludeKeywordsRegex("please_send_me_include_keywords_regex"),

    CaptureFlag("capture_flag"),
    TranslationLanguage("translation_language"),
    SetCaptureFlag("set_capture_flag"),
    SetCaptureFlagOnNote("set_capture_flag_on_note"),
    SetCaptureFlagOffNote("set_capture_flag_off_note"),

    ;

    private String key;

    I18nEnum(String key) {
        this.key = key;
    }

}


================================================
FILE: src/main/java/code/config/I18nLocaleEnum.java
================================================
package code.config;

import lombok.Getter;

import java.util.Locale;

@Getter
public enum I18nLocaleEnum {
    ZH_CN(Locale.SIMPLIFIED_CHINESE, "zh-cn", "简体中文"),
    EN(Locale.US, "en", "English"),

    ;

    private Locale locale;
    private String alias;
    private String displayText;

    I18nLocaleEnum(Locale locale, String alias, String displayText) {
        this.locale = locale;
        this.alias = alias;
        this.displayText = displayText;
    }

    public static I18nLocaleEnum getI18nLocaleEnumByAlias(String alias) {
        for (I18nLocaleEnum value : I18nLocaleEnum.values()) {
            if (value.getAlias().equals(alias)) {
                return value;
            }
        }
        return null;
    }

}


================================================
FILE: src/main/java/code/config/MonitorConfigSettings.java
================================================
package code.config;

import lombok.Data;

@Data
public class MonitorConfigSettings {

    @DisplayConfigAnnotation(i18n = "config_display_on", set = false)
    private Boolean on;

    private String filename;
    @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = false)
    private String fileBasename;

    @DisplayConfigAnnotation(i18n = "config_display_web_page_preview", set = true)
    private Boolean webPagePreview;
    @DisplayConfigAnnotation(i18n = "config_display_notification", set = true)
    private Boolean notification;
    @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = true)
    private Boolean zeroDelay;

    @DisplayConfigAnnotation(i18n = "config_display_url", set = true)
    private String url;
    @DisplayConfigAnnotation(i18n = "config_display_template", set = true)
    private String template;

    @DisplayConfigAnnotation(i18n = "config_display_chat_id_array", set = true)
    private String[] chatIdArray;

}


================================================
FILE: src/main/java/code/config/MonitorExecutorsConfig.java
================================================
package code.config;

import lombok.extern.slf4j.Slf4j;
import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;

import java.util.concurrent.*;

@Slf4j
public class MonitorExecutorsConfig {

    private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(false).setNameFormat("monitor-pool-%d").build();

    private static ExecutorService fixedThreadPool = new ThreadPoolExecutor(
            2,
            10,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100000),
            threadFactory,
            (Runnable r, ThreadPoolExecutor executor) -> {
                log.error(r.toString()+" is Rejected");
            }
    );

    public static void submit(Runnable task) {
        fixedThreadPool.submit(task);
    }

}


================================================
FILE: src/main/java/code/config/ProxyTypeEnum.java
================================================
package code.config;

import lombok.Getter;

@Getter
public enum ProxyTypeEnum {

    NotOpen(0, "Not Open"),
    HttpProxy(1, "Http Proxy"),

    ;

    private int type;
    private String alias;

    ProxyTypeEnum(int type, String alias) {
        this.type = type;
        this.alias = alias;
    }

    public static ProxyTypeEnum getDefault() {
        return NotOpen;
    }

}


================================================
FILE: src/main/java/code/config/RequestProxyConfig.java
================================================
package code.config;

import kong.unirest.HttpRequest;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.HttpHost;
import org.telegram.telegrambots.bots.DefaultBotOptions;

import static code.Main.GlobalConfig;

public class RequestProxyConfig {

    private ProxyTypeEnum type;
    private String hostName;
    private Integer port;

    public static RequestProxyConfig create() {
        RequestProxyConfig config = new RequestProxyConfig();
        if (null != GlobalConfig && GlobalConfig.getOnProxy()) {
            config.type = ProxyTypeEnum.HttpProxy;
            config.hostName = GlobalConfig.getProxyHost();
            config.port = GlobalConfig.getProxyPort();
        } else {
            config.type = ProxyTypeEnum.getDefault();
        }
        return config;
    }

    public void viaProxy(HttpRequest request) {
        switch (this.type) {
            case HttpProxy:
                request.proxy(this.hostName, this.port);
                break;
        }
    }

    public void viaProxy(Request request) {
        switch (this.type) {
            case HttpProxy:
                request.viaProxy(new HttpHost(this.hostName, this.port));
                break;
        }
    }

    public DefaultBotOptions buildDefaultBotOptions() {
        switch (this.type) {
            case HttpProxy:
                DefaultBotOptions botOptions = new DefaultBotOptions();

                botOptions.setProxyHost(this.hostName);
                botOptions.setProxyPort(this.port);
                botOptions.setProxyType(DefaultBotOptions.ProxyType.HTTP);
                return botOptions;
        }
        return null;
    }

}


================================================
FILE: src/main/java/code/config/TableEnum.java
================================================
package code.config;

import lombok.Getter;

@Getter
public enum TableEnum {

    SentRecordTable("sent_record_table"),
    I18nTable("i18n_table"),

    ;

    private String name;

    TableEnum(String name) {
        this.name = name;
    }

}


================================================
FILE: src/main/java/code/eneity/MonitorTableEntity.java
================================================
package code.eneity;

import code.config.DisplayConfigAnnotation;
import code.repository.base.TableEntity;
import code.repository.base.TableField;
import code.repository.base.TableName;
import lombok.Data;

@TableName(name = "monitor_table")
@Data
public class MonitorTableEntity implements TableEntity {

    @TableField(name = "id", sql = "id varchar(55) primary key")
    private String id;

    @TableField(name = "chat_id", sql = "chat_id varchar(100)")
    private String chatId;

    @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = false)
    @TableField(name = "name", sql = "name varchar(50)")
    private String name;

    @DisplayConfigAnnotation(i18n = "config_display_chat_id_array", set = true)
    @TableField(name = "chat_id_array_json", sql = "chat_id_array_json text")
    private String chatIdArrayJson;

    @TableField(name = "create_time", sql = "create_time timestamp")
    private Long createTime;
    @TableField(name = "update_time", sql = "update_time timestamp")
    private Long updateTime;

    @DisplayConfigAnnotation(i18n = "config_display_notification", set = true)
    @TableField(name = "notification", sql = "notification int(2)")
    private Integer notification;

    @DisplayConfigAnnotation(i18n = "config_display_on", set = false)
    @TableField(name = "enable", sql = "enable int(2)")
    private Integer enable;

    @DisplayConfigAnnotation(i18n = "config_display_template", set = true)
    @TableField(name = "template", sql = "template text")
    private String template;

    @DisplayConfigAnnotation(i18n = "config_display_url", set = true)
    @TableField(name = "url", sql = "url text")
    private String url;

    @DisplayConfigAnnotation(i18n = "config_display_web_page_preview", set = true)
    @TableField(name = "web_page_preview", sql = "web_page_preview int(2)")
    private Integer webPagePreview;

    @DisplayConfigAnnotation(i18n = "config_display_zero_delay", set = true)
    @TableField(name = "zero_delay", sql = "zero_delay int(2)")
    private Integer zeroDelay;

    @DisplayConfigAnnotation(i18n = "capture_flag", set = false)
    @TableField(name = "capture_flag", sql = "capture_flag int(2)")
    private Integer captureFlag;

    @DisplayConfigAnnotation(i18n = "translation_language", set = false)
    @TableField(name = "translation_language", sql = "translation_language text")
    private String translationLanguage;

}


================================================
FILE: src/main/java/code/eneity/PageEntity.java
================================================
package code.eneity;

import lombok.Data;

import java.util.ArrayList;
import java.util.List;

@Data
public class PageEntity<T> {

    private int total;
    private int remainder;
    private int page;
    private int count;
    private int current;
    private boolean hasNext;
    private boolean hasPrev;
    private List<T> list = new ArrayList<>();

    private PageEntity() {}

    public PageEntity(int total, int page, int current) {
        this.total = total;
        this.remainder = total % page;
        this.page = page;
        this.count = remainder > 0 ? ((total / page) + 1) : (total / page);
        this.current = current;

        this.hasNext = current < this.count;
        this.hasPrev = current > 1;
    }

    public static PageEntity empty() {
        return new PageEntity();
    }

}


================================================
FILE: src/main/java/code/eneity/SentRecordTableEntity.java
================================================
package code.eneity;

import code.repository.base.TableEntity;
import code.repository.base.TableField;
import code.repository.base.TableName;
import lombok.Data;

@TableName(name = "sent_record_202312_table")
@Data
public class SentRecordTableEntity implements TableEntity {

    @TableField(name = "id", sql = "id varchar(100) primary key")
    private String id;

    @TableField(name = "chat_id", sql = "chat_id varchar(100)")
    private String chatId;

    @TableField(name = "name", sql = "name varchar(50)")
    private String name;

    @TableField(name = "uri", sql = "uri text")
    private String uri;

    @TableField(name = "create_time", sql = "create_time timestamp")
    private Long createTime;

}


================================================
FILE: src/main/java/code/eneity/WebhookTableEntity.java
================================================
package code.eneity;

import code.repository.base.TableEntity;
import code.repository.base.TableField;
import code.repository.base.TableName;
import lombok.Data;

@TableName(name = "webhook_table")
@Data
public class WebhookTableEntity implements TableEntity {

    @TableField(name = "id", sql = "id varchar(100) primary key")
    private String id;

    @TableField(name = "chat_id", sql = "chat_id varchar(100)")
    private String chatId;

    @TableField(name = "settings_json", sql = "settings_json text")
    private String settingsJson;

}


================================================
FILE: src/main/java/code/eneity/YesOrNoEnum.java
================================================
package code.eneity;

import lombok.Getter;

import java.util.Optional;

@Getter
public enum YesOrNoEnum {

    Yes(1, true),
    No(0, false),

    ;

    private int num;
    private boolean bool;

    YesOrNoEnum(int num, boolean bool) {
        this.num = num;
        this.bool = bool;
    }

    public static int toInt(boolean bool) {
        return bool ? Yes.getNum() : No.getNum();
    }

    public static Optional<Boolean> toBoolean(int num) {
        for (YesOrNoEnum value : values()) {
            if (num == value.getNum()) {
                return Optional.of(value.isBool());
            }
        }
        return Optional.empty();
    }

    public static Optional<YesOrNoEnum> get(int num) {
        for (YesOrNoEnum value : values()) {
            if (value.getNum() == num) {
                return Optional.of(value);
            }
        }
        return Optional.empty();
    }

}


================================================
FILE: src/main/java/code/handler/Command.java
================================================
package code.handler;

import lombok.Getter;

@Getter
public enum Command {

    Start("start"),
    Help("help"),
    Create("create"),
    List("list"),
    Get("get"),
    Update("update"),
    Test("test"),
    ForceRecord("fr"),
    On("on"),
    Off("off"),
    Delete("delete"),

    Admin("admin"),
    Exit("exit"),
    Language("language"),
    Restart("restart"),
    UpdateConfig("uc"),
    Upgrade("upgrade"),

    Webhook("webhook"),

    SetVerifySsl("rssb01"),
    SetExcludeKeywords("rssb02"),
    SetExcludeKeywordsRegex("rssb03"),
    SetIncludeKeywords("rssb04"),
    SetIncludeKeywordsRegex("rssb05"),
    SetCaptureFlag("rssb06"),
    SetTranslationLanguage("rssb07"),
    SetChatButtons("rssb11"),
    HideCopyrightTips("rssb10"),

    ;

    private String cmd;

    Command(String cmd) {
        this.cmd = cmd;
    }

    public static Command toCmd(String cmd) {
        for (Command value : Command.values()) {
            if (value.getCmd().equals(cmd)) {
                return value;
            }
        }
        return null;
    }

    public static boolean exist(String cmd) {
        return null != toCmd(cmd);
    }

}


================================================
FILE: src/main/java/code/handler/CommandsHandler.java
================================================
package code.handler;

import code.handler.message.CallbackBuilder;
import code.handler.steps.StepsChatSession;
import code.handler.steps.StepsChatSessionBuilder;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot;
import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;

import static code.Main.GlobalConfig;

@Slf4j
public class CommandsHandler extends TelegramLongPollingCommandBot {

    public CommandsHandler() {
        super();
    }

    public CommandsHandler(DefaultBotOptions botOptions) {
        super(botOptions);
    }

    @Override
    public String getBotUsername() {
        return GlobalConfig.getBotName();
    }

    @Override
    public String getBotToken() {
        return GlobalConfig.getBotToken();
    }

    @Override
    public void processNonCommandUpdate(Update update) {
        if (GlobalConfig.getDebug()) {
            log.info(JSON.toJSONString(update));
        }

        CallbackQuery callbackQuery = update.getCallbackQuery();
        if (null != callbackQuery) {
            String data = callbackQuery.getData();
            CallbackBuilder.CallbackData callbackData = CallbackBuilder.parseCallbackData(data);
            if (null == callbackData) {
                MessageHandle.sendMessage(String.valueOf(callbackQuery.getMessage().getChatId()), "Error...", false);
                return;
            }

            StepsChatSession session = StepsChatSessionBuilder
                    .create(callbackQuery)
                    .setText(callbackData.getText())
                    .build();

            if (!session.getSessionId().equals(String.valueOf(callbackData.getId()))) {
                return;
            }

            if (StringUtils.isNotBlank(data)) {
                StepsCenter.cmdHandle(callbackData, session);
                return;
            }
        }

        Message message = update.getMessage();
        if (null == message) {
            return;
        }
        String text = message.getText();
        if (StringUtils.isNotEmpty(text)) {
            boolean handle = StepsCenter.cmdHandle(StepsChatSessionBuilder.create(message).build());
            if (!handle) {
                StepsCenter.textHandle(StepsChatSessionBuilder.create(message).build());
            }
        }
    }

}


================================================
FILE: src/main/java/code/handler/Handler.java
================================================
package code.handler;

import code.config.*;
import code.eneity.*;
import code.handler.message.CallbackBuilder;
import code.handler.message.InlineKeyboardButtonBuilder;
import code.handler.message.InlineKeyboardButtonListBuilder;
import code.handler.steps.StepResult;
import code.handler.steps.StepsBuilder;
import code.handler.steps.StepsChatSession;
import code.handler.store.ChatButtonsStore;
import code.handler.store.WebhookStore;
import code.util.*;
import code.util.translate.Translate;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.rometools.rome.feed.synd.SyndContent;
import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndPerson;
import kong.unirest.HttpResponse;
import kong.unirest.RequestBodyEntity;
import kong.unirest.Unirest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.media.InputMedia;
import org.telegram.telegrambots.meta.api.objects.media.InputMediaPhoto;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;

import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static code.Main.*;

@Slf4j
public class Handler {

    private static boolean isAdmin(String fromId) {
        return GlobalConfig.getBotAdminId().equals(fromId);
    }

    public static void init() {
        new Thread(() -> {
            while (true) {
                try {
                    MonitorTableEntity where = new MonitorTableEntity();
                    where.setZeroDelay(YesOrNoEnum.No.getNum());
                    where.setEnable(YesOrNoEnum.Yes.getNum());
                    List<MonitorTableEntity> list = MonitorTableRepository.selectList(where);
                    for (MonitorTableEntity entity : list) {
                        rssMessageHandle(null, entity, false, false);
                    }

                    TimeUnit.MINUTES.sleep(GlobalConfig.getIntervalMinute());
                } catch (Exception e) {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                }
            }
        }).start();

        new Thread(() -> {
            while (true) {
                try {
                    long startMillis = System.currentTimeMillis();
                    if (GlobalConfig.getDebug()) {
                        log.info("Zero delay, start timestamp: {}", startMillis);
                    }

                    MonitorTableEntity where = new MonitorTableEntity();
                    where.setZeroDelay(YesOrNoEnum.Yes.getNum());
                    where.setEnable(YesOrNoEnum.Yes.getNum());
                    List<MonitorTableEntity> list = MonitorTableRepository.selectList(where);
                    if (list.isEmpty()) {
                        log.info("Zero delay, monitor list is empty!");
                        TimeUnit.MINUTES.sleep(2);
                    } else {
                        CountDownLatch countDownLatch = new CountDownLatch(list.size());

                        for (MonitorTableEntity entity : list) {
                            MonitorExecutorsConfig.submit(() -> {
                                try {
                                    rssMessageHandle(null, entity, false, false);
                                } finally {
                                    countDownLatch.countDown();
                                }
                            });
                        }
                        countDownLatch.await();

                        long endMillis = System.currentTimeMillis();
                        if (GlobalConfig.getDebug()) {
                            log.info("Zero delay, end timestamp: {}, total time: {}", endMillis, endMillis - startMillis);
                        }
                        TimeUnit.SECONDS.sleep(5);
                    }
                } catch (Exception e) {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                }
            }
        }).start();

        StepsBuilder
                .create()
                .bindCommand(Command.Start, Command.Help)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.HelpText), false);
                    return StepResult.end();
                })
                .build();

        // Create
        StepsBuilder
                .create()
                .bindCommand(Command.Create)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor1), false);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (session.getText().length() > 50) {
                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateNameTooLong), false);
                        return StepResult.reject();
                    }
                    Integer count = MonitorTableRepository.selectCountByName(session.getFromId(), session.getText());
                    if (count > 0) {
                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.MonitorExists), false);
                        return StepResult.reject();
                    }

                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor2, session.getText()), false);
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor3), false);

                    context.put("name", session.getText());

                    return StepResult.ok();
                }, (StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor4), false);
                    SyndFeed feed = RssUtil.getFeed(RequestProxyConfig.create(), session.getText());
                    if (null == feed) {
                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor5), false);
                        return StepResult.reject();
                    }

                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor6, session.getText()), false);
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor7), false);

                    context.put("url", session.getText());

                    return StepResult.ok();
                }, (StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor8, session.getText()), false);

                    String name = (String) context.get("name");
                    String url = (String) context.get("url");

                    String id = Snowflake.nextIdToStr();

                    MonitorTableEntity settings = new MonitorTableEntity();
                    settings.setId(id);
                    settings.setCreateTime(System.currentTimeMillis());
                    settings.setChatId(session.getFromId());
                    settings.setName(name);
                    settings.setTemplate(session.getText());
                    settings.setEnable(YesOrNoEnum.No.getNum());
                    settings.setUrl(url);
                    settings.setWebPagePreview(YesOrNoEnum.Yes.getNum());
                    settings.setChatIdArrayJson(JSON.toJSONString(new ArrayList<String>()));
                    settings.setNotification(YesOrNoEnum.Yes.getNum());
                    settings.setZeroDelay(YesOrNoEnum.No.getNum());

                    MonitorTableRepository.insert(settings);

                    showMonitorHandle(session, id);
//                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitorFinish), false);
//                    rssMessageHandle(session, settings, true, false);

                    return StepResult.ok();
                })
                .build();

        // Update
        StepsBuilder
                .create()
                .bindCommand(Command.Update)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId());
                    if (null == settings) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound, session.getText()), false);
                        return StepResult.end();
                    }

                    InlineKeyboardButtonListBuilder listBuilder = InlineKeyboardButtonListBuilder.create();
                    List<InlineKeyboardButton> inlineKeyboardButtonArrayList = new ArrayList<>();
                    for (Field field : settings.getClass().getDeclaredFields()) {
                        DisplayConfigAnnotation annotation = field.getAnnotation(DisplayConfigAnnotation.class);
                        if (null != annotation && annotation.set()) {
                            listBuilder.add(
                                    InlineKeyboardButtonBuilder
                                            .create()
                                            .add(I18nHandle.getText(session.getFromId(), annotation.i18n()), CallbackBuilder.buildCallbackData(false, session, Command.Update, field.getName()))
                                            .build()
                            );
                        }
                    }
                    listBuilder.add(
                            InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Back), CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId()))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Refresh), CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId()))
                                    .build()
                    );


                    MessageHandle.updateInlineKeyboardList(session.getCallbackQuery().getMessage(), session.getChatId(), session.getCallbackQuery().getMessage().getText(), listBuilder.build());

                    context.put("id", session.getText());
                    context.put("session", session);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String id = (String) context.get("id");

                    MonitorTableEntity settings = MonitorTableRepository.selectOne(id, session.getFromId());
                    Field[] declaredFields = settings.getClass().getDeclaredFields();
                    Field field = null;
                    for (Field declaredField : declaredFields) {
                        DisplayConfigAnnotation annotation = declaredField.getAnnotation(DisplayConfigAnnotation.class);
                        if (null != annotation && annotation.set()) {
                            if (declaredField.getName().equals(session.getText())) {
                                field = declaredField;
                                break;
                            }
                        }
                    }
                    if (null == field) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor2, session.getText()), false);
                        return StepResult.reject();
                    }

                    context.put("field", session.getText());

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor3, session.getText()), false);

                    if (session.getText().equals("notification") || session.getText().equals("enable")
                            || session.getText().equals("webPagePreview") || session.getText().equals("zeroDelay")) {
                        InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                        inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.On));
                        inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Update, String.valueOf(YesOrNoEnum.Yes.getNum())));

                        InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
                        inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Off));
                        inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Update, String.valueOf(YesOrNoEnum.No.getNum())));

                        Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor4), inlineKeyboardButton, inlineKeyboardButton2);
                        putDeleteMessage(context, message);
                        return StepResult.ok();
                    }

                    if (session.getText().equals("template")) {
                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor7), false);
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor4), false);
                    }
                    return StepResult.ok();
                }, (StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String id = (String) context.get("id");
                    String fieldName = (String) context.get("field");

                    try {
                        MonitorTableEntity settings = MonitorTableRepository.selectOne(id, session.getFromId());
                        Field[] declaredFields = settings.getClass().getDeclaredFields();
                        Field field = null;
                        for (Field declaredField : declaredFields) {
                            DisplayConfigAnnotation annotation = declaredField.getAnnotation(DisplayConfigAnnotation.class);
                            if (null != annotation && annotation.set()) {
                                if (declaredField.getName().equals(fieldName)) {
                                    field = declaredField;
                                    break;
                                }
                            }
                        }
                        if (null == field) {
                            MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitor2, session.getText()), false);
                            return StepResult.reject();
                        }

                        if (fieldName.equals("zeroDelay")) {
                            settings.setZeroDelay(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum());
                        } else if (fieldName.equals("webPagePreview")) {
                            settings.setWebPagePreview(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum());
                        } else if (fieldName.equals("enable")) {
                            settings.setEnable(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum());
                        } else if (fieldName.equals("notification")) {
                            settings.setNotification(YesOrNoEnum.get(Integer.valueOf(session.getText())).get().getNum());
                        } else if (fieldName.equals("chatIdArrayJson")) {
                            String[] s = StringUtils.split(session.getText(), " ");
                            if (s.length == 0) {
                                MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateFieldError), false);
                                return StepResult.reject();
                            }
                            settings.setChatIdArrayJson(JSON.toJSONString(s));
                        } else if (fieldName.equals("template")) {
                            settings.setTemplate(session.getText());
                        } else if (fieldName.equals("url")) {
                            settings.setUrl(session.getText());
                        }

                        settings.setUpdateTime(System.currentTimeMillis());
                        MonitorTableRepository.update(settings);

                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateMonitorFinish), false);
                        showMonitorHandle((StepsChatSession) context.get("session"), id);
                        deleteMessage(context);

                    } catch (IllegalArgumentException e) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateFieldError), false);
                        return StepResult.reject();
                    }

                    return StepResult.end();
                })
                .build();

        // Delete
        StepsBuilder
                .create()
                .bindCommand(Command.Delete)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                    inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm));
                    inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Delete, session.getText()));

                    InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
                    inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel));
                    inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Delete, ""));

                    Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.DeleteMonitorConfirm), inlineKeyboardButton, inlineKeyboardButton2);
                    putDeleteMessage(context, message);
                    context.put("session", session);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (StringUtils.isNotBlank(session.getText())) {
                        MonitorTableEntity entity = MonitorTableRepository.selectOne(list.get(0), session.getFromId());
                        if (null != entity) {
                            SentRecordTableRepository.delete(entity.getName(), entity.getChatId());
                            MonitorTableRepository.delete(entity.getId());
                        }
                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.DeleteMonitorFinish), false);
                        showMonitorListHandle((StepsChatSession) context.get("session"));
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false);
                    }
                    deleteMessage(context);
                    return StepResult.end();
                })
                .build();

        // Exit
        StepsBuilder
                .create()
                .bindCommand(Command.Exit)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    StepsCenter.exit(session);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.ExitSucceeded), false);
                    return StepResult.end();
                })
                .build();

        // List
        StepsBuilder
                .create()
                .bindCommand(Command.List)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {

                    showMonitorListHandle(session);

                    return StepResult.end();
                })
                .build();

        // Get
        StepsBuilder
                .create()
                .bindCommand(Command.Get)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {

                    showMonitorHandle(session, session.getText());

                    return StepResult.end();
                })
                .build();

        // On
        StepsBuilder
                .create()
                .bindCommand(Command.On)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {

                    MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId());
                    if (null != settings) {
                        settings.setEnable(YesOrNoEnum.Yes.getNum());
                        MonitorTableRepository.update(settings);
                        showMonitorHandle(session, session.getText());
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false);
                    }

                    return StepResult.end();
                })
                .build();

        // Off
        StepsBuilder
                .create()
                .bindCommand(Command.Off)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {

                    MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId());
                    if (null != settings) {
                        settings.setEnable(YesOrNoEnum.No.getNum());
                        MonitorTableRepository.update(settings);
                        showMonitorHandle(session, session.getText());
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false);
                    }

                    return StepResult.end();
                })
                .build();

        StepsBuilder
                .create()
                .bindCommand(Command.SetCaptureFlag)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId());
                    if (null != settings) {
                        if (null == settings.getCaptureFlag()) {
                            settings.setCaptureFlag(YesOrNoEnum.No.getNum());
                        }
                        boolean captureFlag = !YesOrNoEnum.get(settings.getCaptureFlag()).get().isBool();
                        if (captureFlag) {
                            settings.setCaptureFlag(YesOrNoEnum.Yes.getNum());
                            MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.SetCaptureFlagOnNote), false);
                        } else {
                            settings.setCaptureFlag(YesOrNoEnum.No.getNum());
                            MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.SetCaptureFlagOffNote), false);
                        }
                        MonitorTableRepository.update(settings);
                        showMonitorHandle(session, session.getText());
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false);
                    }
                    return StepResult.end();
                })
                .build();

        // Test
        StepsBuilder
                .create()
                .bindCommand(Command.Test)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {

                    MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId());
                    if (null == settings) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.TestMonitor, session.getText()), false);
                        return StepResult.end();
                    }
                    rssMessageHandle(session, settings, true, false);

                    return StepResult.end();
                })
                .build();

        // Force Record
        StepsBuilder
                .create()
                .bindCommand(Command.ForceRecord)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {

                    MonitorTableEntity settings = MonitorTableRepository.selectOne(session.getText(), session.getFromId());
                    if (null == settings) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.TestMonitor, session.getText()), false);
                        return StepResult.end();
                    }
                    rssMessageHandle(session, settings, false, true);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.ForceRecordSucceeded, session.getText()), false);

                    return StepResult.end();
                })
                .build();

        // Language
        StepsBuilder
                .create()
                .bindCommand(Command.Language)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    ArrayList<InlineKeyboardButton> inlineKeyboardButtons = new ArrayList<>();
                    for (I18nLocaleEnum value : I18nLocaleEnum.values()) {
                        InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                        inlineKeyboardButton.setText(value.getDisplayText());
                        inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Language, value.getAlias()));

                        inlineKeyboardButtons.add(inlineKeyboardButton);
                    }

                    MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.LanguageList), inlineKeyboardButtons);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    I18nLocaleEnum alias = I18nLocaleEnum.getI18nLocaleEnumByAlias(session.getText());

                    I18nHandle.save(session.getFromId(), alias);

                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.ChangeLanguageFinish), false);

                    return StepResult.end();
                })
                .build();

        // Admin
        StepsBuilder
                .create()
                .bindCommand(Command.Admin)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    ConfigSettings config = Config.readConfig();
                    List<List<InlineKeyboardButton>> keyboardButton = InlineKeyboardButtonListBuilder
                            .create()
                            .add(InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.SetChatButtons), CallbackBuilder.buildCallbackData(true, session, Command.SetChatButtons, null))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.SettingWebhook), CallbackBuilder.buildCallbackData(true, session, Command.Webhook, null))
                                    .build()
                            )
                            .add(InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.UpdateConfig), CallbackBuilder.buildCallbackData(true, session, Command.UpdateConfig, null))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.HideCopyrightTips), CallbackBuilder.buildCallbackData(true, session, Command.HideCopyrightTips, null))
                                    .build()
                            )
                            .add(InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.VerifySsl) + String.format("(%s)", config.getVerifySsl() ? I18nHandle.getText(session.getFromId(), I18nEnum.Enable) : I18nHandle.getText(session.getFromId(), I18nEnum.Disable)), CallbackBuilder.buildCallbackData(true, session, Command.SetVerifySsl, null))
                                    .build()
                            )
                            .add(InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.ExcludeKeywords), CallbackBuilder.buildCallbackData(true, session, Command.SetExcludeKeywords, null))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.ExcludeKeywordsRegex), CallbackBuilder.buildCallbackData(true, session, Command.SetExcludeKeywordsRegex, null))
                                    .build()
                            )
                            .add(InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.IncludeKeywords), CallbackBuilder.buildCallbackData(true, session, Command.SetIncludeKeywords, null))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.IncludeKeywordsRegex), CallbackBuilder.buildCallbackData(true, session, Command.SetIncludeKeywordsRegex, null))
                                    .build()
                            )
                            .add(InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Restart), CallbackBuilder.buildCallbackData(true, session, Command.Restart, null))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Upgrade), CallbackBuilder.buildCallbackData(true, session, Command.Upgrade, null))
                                    .build()
                            )
                            .build();

                    Properties properties = System.getProperties();

                    StringBuilder builder = new StringBuilder();
                    builder.append("os.name: ");
                    builder.append(properties.getProperty("os.name"));
                    builder.append("\n");
                    builder.append("os.arch: ");
                    builder.append(properties.getProperty("os.arch"));

                    code.handler.message.MessageHandle.sendInlineKeyboardList(session.getFromId(), builder.toString(),  keyboardButton);

                    return StepResult.end();
                })
                .build();

        StepsBuilder
                .create()
                .bindCommand(Command.SetExcludeKeywordsRegex)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    ConfigSettings config = Config.readConfig();
                    List<String> excludeKeywordsRegex = config.getExcludeKeywordsRegex();
                    if (null != excludeKeywordsRegex && !excludeKeywordsRegex.isEmpty()) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), excludeKeywordsRegex.stream().collect(Collectors.joining("\n")), false);
                    }

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeExcludeKeywordsRegex), false);
                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    if (StringUtils.isBlank(text)) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false);
                        return StepResult.reject();
                    }
                    List<String> excludeKeywordsRegex = new ArrayList<>();
                    if (!text.equals("-1")) {
                        String[] split = StringUtils.split(text, "\n");
                        for (String s : split) {
                            if (StringUtils.isNotBlank(s)) {
                                excludeKeywordsRegex.add(s);
                            }
                        }
                    }
                    ConfigSettings config = Config.readConfig();
                    config.setExcludeKeywordsRegex(excludeKeywordsRegex);
                    Config.saveConfig(config);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + excludeKeywordsRegex.stream().collect(Collectors.joining("\n")), false);
                    return StepResult.ok();
                })
                .build();

        // Set Exclude Keywords
        StepsBuilder
                .create()
                .bindCommand(Command.SetExcludeKeywords)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    ConfigSettings config = Config.readConfig();
                    List<String> excludeKeywords = config.getExcludeKeywords();
                    if (null != excludeKeywords && !excludeKeywords.isEmpty()) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), excludeKeywords.stream().collect(Collectors.joining("\n")), false);
                    }

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeExcludeKeywords), false);
                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    if (StringUtils.isBlank(text)) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false);
                        return StepResult.reject();
                    }
                    List<String> excludeKeywords = new ArrayList<>();
                    if (!text.equals("-1")) {
                        String[] split = StringUtils.split(text, "\n");
                        for (String s : split) {
                            if (StringUtils.isNotBlank(s)) {
                                excludeKeywords.add(s);
                            }
                        }
                    }
                    ConfigSettings config = Config.readConfig();
                    config.setExcludeKeywords(excludeKeywords);
                    Config.saveConfig(config);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + excludeKeywords.stream().collect(Collectors.joining("\n")), false);
                    return StepResult.ok();
                })
                .build();

        StepsBuilder
                .create()
                .bindCommand(Command.SetIncludeKeywordsRegex)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    ConfigSettings config = Config.readConfig();
                    List<String> includeKeywordsRegex = config.getIncludeKeywordsRegex();
                    if (null != includeKeywordsRegex && !includeKeywordsRegex.isEmpty()) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), includeKeywordsRegex.stream().collect(Collectors.joining("\n")), false);
                    }

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeIncludeKeywordsRegex), false);
                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    if (StringUtils.isBlank(text)) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false);
                        return StepResult.reject();
                    }
                    List<String> includeKeywordsRegex = new ArrayList<>();
                    if (!text.equals("-1")) {
                        String[] split = StringUtils.split(text, "\n");
                        for (String s : split) {
                            if (StringUtils.isNotBlank(s)) {
                                includeKeywordsRegex.add(s);
                            }
                        }
                    }
                    ConfigSettings config = Config.readConfig();
                    config.setIncludeKeywordsRegex(includeKeywordsRegex);
                    Config.saveConfig(config);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + includeKeywordsRegex.stream().collect(Collectors.joining("\n")), false);
                    return StepResult.ok();
                })
                .build();

        StepsBuilder
                .create()
                .bindCommand(Command.SetIncludeKeywords)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    ConfigSettings config = Config.readConfig();
                    List<String> includeKeywords = config.getIncludeKeywords();
                    if (null != includeKeywords && !includeKeywords.isEmpty()) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), includeKeywords.stream().collect(Collectors.joining("\n")), false);
                    }

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeIncludeKeywords), false);
                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    if (StringUtils.isBlank(text)) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false);
                        return StepResult.reject();
                    }
                    List<String> includeKeywords = new ArrayList<>();
                    if (!text.equals("-1")) {
                        String[] split = StringUtils.split(text, "\n");
                        for (String s : split) {
                            if (StringUtils.isNotBlank(s)) {
                                includeKeywords.add(s);
                            }
                        }
                    }
                    ConfigSettings config = Config.readConfig();
                    config.setIncludeKeywords(includeKeywords);
                    Config.saveConfig(config);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + "\n\n" + includeKeywords.stream().collect(Collectors.joining("\n")), false);
                    return StepResult.ok();
                })
                .build();

        // Set Chat Buttons
        StepsBuilder
                .create()
                .bindCommand(Command.SetChatButtons)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    ConfigSettings config = Config.readConfig();
                    String chatButtons = config.getChatButtons();
                    if (StringUtils.isNotBlank(chatButtons)) {
                        code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), chatButtons, false);
                    }

                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeChatButtons), false);
                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    if (StringUtils.isBlank(text)) {
                        code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false);
                        return StepResult.reject();
                    }
                    if (!text.equals("-1")) {
                        Optional<ChatButtonsStore.ChatButtonsToInlineKeyboardButtons> buttons = ChatButtonsStore.verify(text);
                        if (!buttons.isPresent()) {
                            code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.FormatError), false);
                            return StepResult.reject();
                        }
                        ChatButtonsStore.ChatButtonsToInlineKeyboardButtons keyboardButtons = buttons.get();

                        for (Map.Entry<String, List<InlineKeyboardButton>> entry : keyboardButtons.getMap().entrySet()) {
                            List<List<InlineKeyboardButton>> build = InlineKeyboardButtonListBuilder
                                    .create()
                                    .add(entry.getValue())
                                    .build();
                            code.handler.message.MessageHandle.sendInlineKeyboardList(session.getChatId(), session.getReplyToMessageId(), entry.getKey(), build);
                        }
                    }
                    ChatButtonsStore.set(text);

                    ConfigSettings config = Config.readConfig();
                    config.setChatButtons(text);
                    Config.saveConfig(config);
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false);
                    return StepResult.ok();
                })
                .build();

        // Hide Copyright Tips
        StepsBuilder
                .create()
                .bindCommand(Command.HideCopyrightTips)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                    inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm));
                    inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.HideCopyrightTips, "true"));

                    InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
                    inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel));
                    inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.HideCopyrightTips, "false"));

                    Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureYouWantToHideCopyrightTips), inlineKeyboardButton, inlineKeyboardButton2);
                    putDeleteMessage(context, message);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    Boolean of = Boolean.valueOf(session.getText());
                    ConfigSettings configSettings = Config.readConfig();
                    configSettings.setHideCopyrightTips(of);
                    Config.saveConfig(configSettings);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false);
                    deleteMessage(context);

                    return StepResult.end();
                })
                .build();

        StepsBuilder
                .create()
                .bindCommand(Command.SetVerifySsl)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                    inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Enable));
                    inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.SetVerifySsl, "true"));

                    InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
                    inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Disable));
                    inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.SetVerifySsl, "false"));

                    Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureYouWantToSetVerifySsl), inlineKeyboardButton, inlineKeyboardButton2);
                    putDeleteMessage(context, message);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    Boolean of = Boolean.valueOf(session.getText());
                    ConfigSettings configSettings = Config.readConfig();
                    configSettings.setVerifySsl(of);
                    Config.saveConfig(configSettings);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded) + ", " + I18nHandle.getText(session.getFromId(), I18nEnum.NeedToRestartBot), false);
                    deleteMessage(context);

                    return StepResult.end();
                })
                .build();

        // Webhook
        StepsBuilder
                .create()
                .bindCommand(Command.Webhook)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    StringBuilder builder = new StringBuilder();
                    builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.CurrentSetting) + ": \n\n");
                    WebhookTableEntity webhookTableEntity = WebhookTableRepository.selectOne(session.getFromId());
                    if (null == webhookTableEntity) {
                        builder.append(Config.WebhookJson);
                    } else {
                        builder.append(webhookTableEntity.getSettingsJson());
                    }
                    builder.append("\n\n");
                    builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToUpdateTheConfig));

                    InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                    inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm));
                    inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Webhook, "true"));

                    InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
                    inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel));
                    inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Webhook, "false"));

                    Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), builder.toString(), inlineKeyboardButton, inlineKeyboardButton2);
                    putDeleteMessage(context, message);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    Boolean of = Boolean.valueOf(session.getText());
                    if (of) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeWebhookSettings), false);
                        return StepResult.ok();
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false);
                        deleteMessage(context);
                        return StepResult.end();
                    }
                }, (StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    boolean verify = WebhookStore.verify(text);
                    if (!verify) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateConfigFail), false);
                        return StepResult.reject();
                    }
                    WebhookStore.Webhook webhook = WebhookStore.get(text).get();

                    WebhookTableEntity webhookTableEntity = new WebhookTableEntity();
                    webhookTableEntity.setChatId(session.getFromId());
                    webhookTableEntity.setSettingsJson(JSON.toJSONString(webhook, JSONWriter.Feature.PrettyFormat));
                    WebhookTableRepository.save(webhookTableEntity);

                    MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false);
                    deleteMessage(context);

                    return StepResult.end();
                })
                .build();

        // Restart
        StepsBuilder
                .create()
                .bindCommand(Command.Restart)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                    inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm));
                    inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Restart, "true"));

                    InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
                    inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel));
                    inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Restart, "false"));

                    MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToRestartRightNow), inlineKeyboardButton, inlineKeyboardButton2);

                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    Boolean of = Boolean.valueOf(session.getText());
                    if (of) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.Restarting), false);
                        ProgramUtil.restart(Config.MetaData.ProcessName);
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false);
                    }
                    return StepResult.end();
                })
                .build();

        // Update config
        StepsBuilder
                .create()
                .bindCommand(Command.UpdateConfig)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession session) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    code.handler.message.MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    List<InlineKeyboardButton> buttons = InlineKeyboardButtonBuilder
                            .create()
                            .add(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm), CallbackBuilder.buildCallbackData(false, session, Command.UpdateConfig, "confirm"))
                            .add(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel), CallbackBuilder.buildCallbackData(false, session, Command.UpdateConfig, "cancel"))
                            .build();
                    ConfigSettings config = Config.readConfig();

                    MessageHandle.sendMessage(session.getFromId(), JSON.toJSONString(config, JSONWriter.Feature.PrettyFormat), false);
                    Message message = MessageHandle.sendInlineKeyboard(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToUpdateTheConfig), buttons);
                    putDeleteMessage(context, message);
                    return StepResult.ok();
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    if (text.equals("confirm")) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.PleaseSendMeConfigContent), false);
                        return StepResult.ok();
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false);
                        deleteMessage(context);
                        return StepResult.end();
                    }
                }, (StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    String text = session.getText();
                    ConfigSettings configSettings = Config.verifyConfig(text);
                    if (null == configSettings) {
                        code.handler.message.MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateConfigFail), false);
                        return StepResult.reject();
                    }

                    boolean b = Config.saveConfig(configSettings);
                    if (b) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateSucceeded), false);
                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UpdateFailed), false);
                    }
                    deleteMessage(context);
                    return StepResult.end();
                })
                .build();

        // Upgrade
        StepsBuilder
                .create()
                .bindCommand(Command.Upgrade)
                .debug(GlobalConfig.getDebug())
                .error((Exception e, StepsChatSession stepsChatSession) -> {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    MessageHandle.sendMessage(stepsChatSession.getChatId(), I18nHandle.getText(stepsChatSession.getFromId(), I18nEnum.UnknownError), false);
                })
                .init((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    if (!isAdmin(session.getFromId())) {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.YouAreNotAnAdmin), false);
                        return StepResult.end();
                    }

                    Message message = MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.GettingUpdateData), false);
                    GithubUtil.LatestReleaseResponse release = GithubUtil.getLatestRelease(RequestProxyConfig.create(), Config.MetaData.GitOwner, Config.MetaData.GitRepo);
                    if (release.isOk()) {
                        StringBuilder builder = new StringBuilder();
                        builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.AreYouSureToUpgradeThisBotRightNow));
                        builder.append("\n");
                        builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.TargetVersion) + ": ");
                        builder.append(release.getTagName());
                        builder.append("\n");
                        builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.CurrentVersion) + ": ");
                        builder.append(Config.MetaData.CurrentVersion);
                        builder.append("\n");
                        builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.UpdateLogs) + ": ");
                        builder.append("\n");
                        builder.append(release.getBody());

                        InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
                        inlineKeyboardButton.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Confirm));
                        inlineKeyboardButton.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Upgrade, "true"));

                        InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
                        inlineKeyboardButton2.setText(I18nHandle.getText(session.getFromId(), I18nEnum.Cancel));
                        inlineKeyboardButton2.setCallbackData(CallbackBuilder.buildCallbackData(false, session, Command.Upgrade, "false"));

                        MessageHandle.sendInlineKeyboard(session.getChatId(), builder.toString(), inlineKeyboardButton, inlineKeyboardButton2);

                        String url = "";
                        for (GithubUtil.LatestReleaseAsset asset : release.getAssets()) {
                            if (Config.MetaData.JarName.equals(asset.getName())) {
                                url = asset.getBrowserDownloadUrl();
                                break;
                            }
                        }

                        context.put("url", url);

                        return StepResult.ok();
                    } else {
                        MessageHandle.editMessage(message, I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError));
                        return StepResult.end();
                    }
                })
                .steps((StepsChatSession session, int index, List<String> list, Map<String, Object> context) -> {
                    Boolean of = Boolean.valueOf(session.getText());
                    if (of) {
                        Message message = MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.Updating), false);
                        String url = (String) context.get("url");

                        AtomicInteger count = new AtomicInteger();
                        String temp = System.getProperty("user.dir") + "/temp.jar";
                        log.info("temp: " + temp);
                        boolean b = DownloadUtil.download(
                                RequestProxyConfig.create(),
                                url,
                                temp,
                                (String var1, String var2, Long var3, Long var4) -> {
                                    if ((var4 - var3) > 0) {
                                        count.incrementAndGet();
                                        if (count.get() == 100) {
                                            MessageHandle.editMessage(message, I18nHandle.getText(session.getFromId(), I18nEnum.Downloaded, BytesUtil.toDisplayStr(var3), BytesUtil.toDisplayStr(var4)));
                                            count.set(0);
                                        }
                                    }
                                }
                        );

                        if (b) {
                            System.exit(1);
                        } else {
                            MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.UnknownError), false);
                        }

                    } else {
                        MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.CancelSucceeded), false);
                    }
                    return StepResult.end();
                })
                .build();
    }

    private static void putDeleteMessage(Map<String, Object> context, Message message) {
        context.put("delete", message);
    }
    private static void deleteMessage(Map<String, Object> context) {
        try {
            if (context.containsKey("delete")) {
                MessageHandle.deleteMessage((Message) context.get("delete"));
            }
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
    }

    private static void showMonitorHandle(StepsChatSession session, String id) {
        MonitorTableEntity settings = MonitorTableRepository.selectOne(id, session.getFromId());
        if (null != settings) {
            if (null == settings.getCaptureFlag()) {
                settings.setCaptureFlag(YesOrNoEnum.No.getNum());
            }
            boolean captureFlag = YesOrNoEnum.get(settings.getCaptureFlag()).get().isBool();

            List<List<InlineKeyboardButton>> build = InlineKeyboardButtonListBuilder
                    .create()
                    .add(
                            InlineKeyboardButtonBuilder
                                    .create()
                                    .add((YesOrNoEnum.get(settings.getEnable()).get().isBool() ? "✅ " : "") + I18nHandle.getText(session.getFromId(), I18nEnum.On), CallbackBuilder.buildCallbackData(true, session, Command.On, settings.getId()))
                                    .add((!YesOrNoEnum.get(settings.getEnable()).get().isBool() ? "❌ " : "") + I18nHandle.getText(session.getFromId(), I18nEnum.Off), CallbackBuilder.buildCallbackData(true, session, Command.Off, settings.getId()))
                                    .build()
                    )
                    .add(
                            InlineKeyboardButtonBuilder
                                    .create()
                                    .add((captureFlag ? "✅ " : "❌ ") + I18nHandle.getText(session.getFromId(), I18nEnum.SetCaptureFlag), CallbackBuilder.buildCallbackData(true, session, Command.SetCaptureFlag, settings.getId()))
                                    .build()
                    )
                    .add(
                            InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Test), CallbackBuilder.buildCallbackData(true, session, Command.Test, settings.getId()))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.ForceRecord), CallbackBuilder.buildCallbackData(true, session, Command.ForceRecord, settings.getId()))
                                    .build()
                    )
                    .add(
                            InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Update), CallbackBuilder.buildCallbackData(true, session, Command.Update, settings.getId()))
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Delete), CallbackBuilder.buildCallbackData(true, session, Command.Delete, settings.getId()))
                                    .build()
                    )
                    .add(
                            InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Refresh), CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId()))
                                    .build()
                    )
                    .add(
                            InlineKeyboardButtonBuilder
                                    .create()
                                    .add(I18nHandle.getText(session.getFromId(), I18nEnum.Back), CallbackBuilder.buildCallbackData(true, session, Command.List, ""))
                                    .build()
                    )
                    .build();

            if (null == session.getCallbackQuery()) {
                MessageHandle.sendInlineKeyboardList(session.getChatId(), getMonitorData(session, settings), build);
            } else {
                MessageHandle.updateInlineKeyboardList(session.getCallbackQuery().getMessage(), session.getChatId(), getMonitorData(session, settings), build);
            }
        } else {
            MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NotFound), false);
        }
    }

    private static void showMonitorListHandle(StepsChatSession session) {
        MonitorTableEntity where = new MonitorTableEntity();
        where.setChatId(session.getFromId());
        PageEntity page = MonitorTableRepository.page(where, 5, NumberUtils.toInt(session.getText(), 1), "order by create_time desc");
        if (!page.isHasNext() && page.getList().isEmpty()) {
            page = MonitorTableRepository.page(where, 5, 1, "order by create_time desc");
        }

        List<MonitorTableEntity> entityList = page.getList();
        if (entityList.size() > 0) {
            StringBuilder builder = new StringBuilder();
            ArrayList<InlineKeyboardButton> inlineKeyboardButtons = new ArrayList<>();

            for (MonitorTableEntity settings : entityList) {
                builder.append(I18nHandle.getText(session.getFromId(), I18nEnum.MonitorList, settings.getName(), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(settings.getEnable()).get())));
                builder.append("\n\n");

                InlineKeyboardButton button = new InlineKeyboardButton();
                button.setText(settings.getName());
                button.setCallbackData(CallbackBuilder.buildCallbackData(true, session, Command.Get, settings.getId()));
                inlineKeyboardButtons.add(button);
            }

            List<List<InlineKeyboardButton>> build = InlineKeyboardButtonListBuilder
                    .create()
                    .add(inlineKeyboardButtons)
                    .pagination(page, session, Command.List)
                    .build();
            if (null == session.getCallbackQuery()) {
                MessageHandle.sendInlineKeyboardList(session.getChatId(), builder.toString(), build);
            } else {
                MessageHandle.updateInlineKeyboardList(session.getCallbackQuery().getMessage(), session.getChatId(), builder.toString(), build);
            }
        } else {
            if (null != session.getCallbackQuery()) {
                MessageHandle.deleteMessage(session.getCallbackQuery().getMessage());
            }
            MessageHandle.sendMessage(session.getChatId(), session.getReplyToMessageId(), I18nHandle.getText(session.getFromId(), I18nEnum.NothingHere), false);
        }
    }

    private static void rssMessageHandle(StepsChatSession session, MonitorTableEntity entity, boolean isTest, boolean forceRecord) {
        try {
            Boolean on = YesOrNoEnum.toBoolean(entity.getEnable()).get();
            String name = entity.getName();
            if ((null != on && on) || isTest || forceRecord) {
                SyndFeed feed = RssUtil.getFeed(RequestProxyConfig.create(), entity.getUrl());
                if (null == feed) {
                    if (isTest) MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.CreateMonitor5), false);
                    return;
                }
                List<SyndEntry> entries = feed.getEntries();
                if (null == entries || entries.isEmpty()) {
                    if (isTest) MessageHandle.sendMessage(session.getChatId(), I18nHandle.getText(session.getFromId(), I18nEnum.NothingAtAll), false);
                    return;
                }
                if (!SentRecordTableRepository.exists(name, entity.getChatId()) || forceRecord) {
                    for (int i = 0; i < entries.size(); i++) {
                        SyndEntry entry = entries.get(i);

                        String uri = entry.getUri();
                        if (StringUtils.isBlank(uri)) {
                            uri = entry.getLink();
                        }

                        SentRecordTableEntity sentRecordTableEntity = new SentRecordTableEntity();
                        sentRecordTableEntity.setId(Snowflake.nextIdToStr());
                        sentRecordTableEntity.setCreateTime(System.currentTimeMillis());
                        sentRecordTableEntity.setName(name);
                        sentRecordTableEntity.setChatId(entity.getChatId());
                        sentRecordTableEntity.setUri(uri);
                        SentRecordTableRepository.save(sentRecordTableEntity);
                    }
                }

                String template = entity.getTemplate();
                for (int i = 0; i < entries.size(); i++) {
                    SyndEntry entry = entries.get(i);

                    String uri = entry.getUri();
                    if (StringUtils.isBlank(uri)) {
                        uri = entry.getLink();
                    }

                    if (SentRecordTableRepository.exists(uri, name, entity.getChatId()) && !isTest) {
                        continue;
                    }

                    String text = replaceTemplate(template, feed, entry);
                    if (StringUtils.isNotBlank(text)) {
                        List<String> images = null;
                        Integer captureFlag = (null == entity.getCaptureFlag() ? YesOrNoEnum.No.getNum() : entity.getCaptureFlag());
                        Optional<Boolean> captureFlagBoolean = YesOrNoEnum.toBoolean(captureFlag);
                        if (captureFlagBoolean.isPresent() && captureFlagBoolean.get()) {
                            images = getImages(entry);
                        }

                        if (!isTest) {
                            List<String> chatIdArray = JSON.parseArray(entity.getChatIdArrayJson(), String.class);
                            if (null == chatIdArray || chatIdArray.isEmpty()) {
                                chatIdArray = Arrays.asList(GlobalConfig.getChatIdArray());
                            }
                            for (String s : chatIdArray) {
                                if (!containsExcludeKeywords(text)) {
                                    if (isEnableIncludeKeywords()) {
                                        if (containsIncludeKeywords(text)) {
                                            sendRss(s, session, entity, text, images);
                                        }
                                    } else {
                                        sendRss(s, session, entity, text, images);
                                    }
                                }
                            }

                            if (chatIdArray.size() > 0) {
                                SentRecordTableEntity sentRecordTableEntity = new SentRecordTableEntity();
                                sentRecordTableEntity.setId(Snowflake.nextIdToStr());
                                sentRecordTableEntity.setCreateTime(System.currentTimeMillis());
                                sentRecordTableEntity.setName(name);
                                sentRecordTableEntity.setChatId(entity.getChatId());
                                sentRecordTableEntity.setUri(uri);
                                SentRecordTableRepository.save(sentRecordTableEntity);
                            }
                        } else {
                            sendRss(session.getChatId(), session, entity, text, images);
                            if (i >= 2) {
                                break;
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
            if (isTest) MessageHandle.sendMessage(GlobalConfig.getBotAdminId(), e.getMessage(), false);
        }
    }

    private static boolean isEnableIncludeKeywords() {
        ConfigSettings configSettings = Config.readConfig();
        if (configSettings.getIncludeKeywords().isEmpty() && configSettings.getIncludeKeywordsRegex().isEmpty()) {
            return false;
        }
        return true;
    }
    private static boolean containsIncludeKeywords(String text) {
        try {
            if (StringUtils.isNotBlank(text)) {
                ConfigSettings configSettings = Config.readConfig();
                for (String includeKeywords : configSettings.getIncludeKeywords()) {
                    if (StringUtils.containsIgnoreCase(text, includeKeywords)) {
                        return true;
                    }
                }
                for (String includeKeywordsRegex : configSettings.getIncludeKeywordsRegex()) {
                    Pattern pattern = Pattern.compile(includeKeywordsRegex);
                    Matcher matcher = pattern.matcher(text);
                    if (matcher.find()) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return false;
    }
    private static boolean containsExcludeKeywords(String text) {
        try {
            if (StringUtils.isNotBlank(text)) {
                ConfigSettings configSettings = Config.readConfig();
                for (String excludeKeyword : configSettings.getExcludeKeywords()) {
                    if (StringUtils.containsIgnoreCase(text, excludeKeyword)) {
                        return true;
                    }
                }
                for (String excludeKeywordsRegex : configSettings.getExcludeKeywordsRegex()) {
                    Pattern pattern = Pattern.compile(excludeKeywordsRegex);
                    Matcher matcher = pattern.matcher(text);
                    if (matcher.find()) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return false;
    }

    private static void sendRss(String chatId, StepsChatSession session, MonitorTableEntity entity, String text, List<String> images) {
        List<List<InlineKeyboardButton>> build = null;
        Optional<ChatButtonsStore.ChatButtonsToInlineKeyboardButtons> buttons = ChatButtonsStore.get();
        if (buttons.isPresent()) {
            Optional<List<InlineKeyboardButton>> inlineKeyboardButtonList = buttons.get().getButtons(session.getChatId());
            if (inlineKeyboardButtonList.isPresent()) {
                build = InlineKeyboardButtonListBuilder
                        .create()
                        .add(inlineKeyboardButtonList.get())
                        .build();
            }
        }

        boolean sendText = true;
        if (null != images && !images.isEmpty()) {
            try {
                boolean sendSingleImage = true;
                if (images.size() > 1) {
                    List<InputMedia> inputMedia = new ArrayList<>();
                    AtomicInteger countAtomic = new AtomicInteger(0);
                    for (String image : images) {
                        if (inputMedia.size() >= 10) {
                            break;
                        }
                        String name = UUID.randomUUID().toString();
                        String temp = Config.TempDir + File.separator + name + ".png";
                        boolean download = DownloadUtil.download(RequestProxyConfig.create(), image, temp);
                        if (download) {
                            int count = countAtomic.addAndGet(1);

                            InputMediaPhoto inputMediaPhoto = new InputMediaPhoto();
                            inputMediaPhoto.setMedia(new File(temp), name);
                            if (count == 1) {
                                inputMediaPhoto.setCaption(text);
                            }
                            inputMedia.add(inputMediaPhoto);
                        }
                    }
                    if (inputMedia.size() >= 2) {
                        List<Message> messages = MessageHandle.sendMediaGroup(chatId, inputMedia, YesOrNoEnum.toBoolean(entity.getNotification()).get());
                        if (null != messages && !messages.isEmpty()) {
                            sendSingleImage = false;
                            sendText = false;
                        }
                    }
                }
                if (sendSingleImage) {
                    String image = images.get(0);
                    String temp = Config.TempDir + File.separator + UUID.randomUUID() + ".png";
                    boolean download = DownloadUtil.download(RequestProxyConfig.create(), image, temp);
                    if (download) {
                        Message message = MessageHandle.sendImage(chatId, null, text, new File(temp), build);
                        if (null != message) {
                            sendText = false;
                        }
                    }
                }
            } catch (Exception e) {
                log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
            }
        }
        if (sendText) {
            MessageHandle.sendMessage(chatId, null, text, YesOrNoEnum.toBoolean(entity.getWebPagePreview()).get(), YesOrNoEnum.toBoolean(entity.getNotification()).get(), build);
        }

        WebhookTableEntity webhookTableEntity = WebhookTableRepository.selectOne(entity.getChatId());
        if (null != webhookTableEntity) {
            Optional<WebhookStore.Webhook> webhookOptional = WebhookStore.get(webhookTableEntity.getSettingsJson());
            if (webhookOptional.isPresent()) {
                WebhookStore.Webhook webhook = webhookOptional.get();
                if (webhook.isEnable()) {
                    try {
                        for (WebhookStore.WebhookRequest request : webhook.getList()) {
                            String body = JSON.toJSONString(request.getBody());
                            String str = JSON.toJSONString(text);
                            str = StringUtils.removeStart(str, "\"");
                            str = StringUtils.removeEnd(str, "\"");
                            body = StringUtils.replace(body, "${text}", str);

                            RequestBodyEntity requestBody = Unirest
                                    .request(request.getMethod(), request.getUrl())
                                    .headers(request.getHeaders())
                                    .body(body)
                                    .connectTimeout((int) TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS))
                                    .socketTimeout((int) TimeUnit.MILLISECONDS.convert(40, TimeUnit.SECONDS));
                            if (GlobalConfig.getOnProxy()) {
                                requestBody.proxy(GlobalConfig.getProxyHost(), GlobalConfig.getProxyPort());
                            }
                            HttpResponse<String> rsp = requestBody.asString();
                            log.info("Webhook request, url: {}, body: {}, rsp: {}", request.getUrl(), body, rsp.getBody());
                        }
                    } catch (Exception e) {
                        log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                    }
                }
            }
        }
    }

    private static List<String> getImages(SyndEntry entry) {
        List<String> list = new ArrayList<>();
        if (null != entry) {
            SyndContent description = entry.getDescription();
            if ("text/html".equals(description.getType())) {
                try {
                    Document document = Jsoup.parse(description.getValue());
                    if (null != document) {
                        Elements images = document.select("img");
                        for (Element image : images) {
                            String imageUrl = image.attr("src");
                            if (StringUtils.isNotBlank(imageUrl)) {
                                list.add(imageUrl);
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                }
            }
        }
        return list;
    }
    public static String getDescription(SyndEntry entry) {
        if (null != entry) {
            SyndContent description = entry.getDescription();
            if ("text/html".equals(description.getType())) {
                return getDescription(description.getValue());
            } else {
                return StringUtils.defaultIfBlank(description.getValue(), "");
            }
        }
        return "";
    }
    public static String getDescription(String html) {
        if (null != html) {
            try {
                Document document = Jsoup.parse(html);
                if (null != document) {
                    Elements br = document.select("br");
                    for (Element element : br) {
                        element.html("\n");
                    }
                    String text = document.wholeText();
                    return StringUtils.defaultIfBlank(text, "");
                }
            } catch (Exception e) {
                log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
            }
        }
        return "";
    }
    private static String replaceTemplate(String template, SyndFeed feed, SyndEntry entry) {
        try {
            if (StringUtils.isBlank(template) || null == entry) {
                return null;
            }

            String s = new String(template);

            if (template.contains("${translate")) {
                try {
                    String pattern = "\\$\\{translate\\|(\\w+-\\w+|\\w+)\\|\\w+\\}";

                    Pattern regex = Pattern.compile(pattern);
                    Matcher matcher = regex.matcher(s);

                    while (matcher.find()) {
                        String variable = matcher.group();
                        if (StringUtils.isNotBlank(variable)) {
                            String variableEdit = StringUtils.removeStart(variable, "${");
                            variableEdit = StringUtils.removeEnd(variableEdit, "}");
                            String[] split = StringUtils.split(variableEdit, "|");
                            if (split.length == 3) {
                                String s1 = split[0];
                                String s2 = split[1];
                                String s3 = split[2];

                                String text = "";
                                if ("title".equals(s3)) {
                                    text = entry.getTitle();
                                } else if ("description".equals(s3)) {
                                    text = getDescription(entry);
                                }
                                String translate = Translate.translate(text, "auto", s2);
                                s = StringUtils.replace(s, variable, translate);
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
                }
            }

            if (template.contains("${link}")) {
                s = StringUtils.replace(s, "${link}", entry.getLink());
            }
            if (template.contains("${title}")) {
                s = StringUtils.replace(s, "${title}", entry.getTitle());
            }
            if (template.contains("${description}")) {
                s = StringUtils.replace(s, "${description}", getDescription(entry));
            }
            String author = entry.getAuthor();
            if (StringUtils.isBlank(author)) {
                author = feed.getAuthor();
            }
            if (StringUtils.isBlank(author)) {
                List<SyndPerson> authors = feed.getAuthors();
                author = authors.size() > 0 ? authors.get(0).getName() : "";
            }
            if (template.contains("${author}")) {
                s = StringUtils.replace(s, "${author}", author);
            }
            if (template.contains("${telegraph}")) {
                String html = null;

                List<SyndContent> contents = entry.getContents();
                if (contents.size() > 0) {
                    String value = contents.get(0).getValue();
                    if (StringUtils.isNotBlank(value)) {
                        html = value;
                    }
                }

                if (StringUtils.isBlank(html)) {
                    SyndContent description = entry.getDescription();
                    if (null != description) {
                        String value = description.getValue();
                        if (StringUtils.isNotBlank(value)) {
                            html = value;
                        }
                    }
                }

                if (StringUtils.isNotBlank(html)) {
                    String telegraphHtml = null;
                    if (!GlobalConfig.getHideCopyrightTips()) {
                        telegraphHtml = replaceTelegraphHtml(entry.getLink(), entry.getTitle());
                    }

                    TelegraphUtil.SaveResponse response =
                            TelegraphUtil.save(RequestProxyConfig.create(), entry.getTitle(), author, html, telegraphHtml);
                    if (response.isOk()) {
                        s = StringUtils.replace(s, "${telegraph}", response.getUrl());
                    } else {
                        s = StringUtils.replace(s, "${telegraph}", "");
                    }
                } else {
                    s = StringUtils.replace(s, "${telegraph}", "");
                }
            }

            return s;
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    private static String replaceTelegraphHtml(String link, String title) {
        String s = StringUtils.replace(Config.TelegraphHtml, "${link}", link);
        return StringUtils.replace(s, "${title}", title);
    }

    private static String getEnableDisplayI18nText(String fromId, YesOrNoEnum yesOrNoEnum) {
        switch (yesOrNoEnum) {
            case Yes:
                return I18nHandle.getText(fromId, I18nEnum.On);
            case No:
                return I18nHandle.getText(fromId, I18nEnum.Off);
            default:
                return "";
        }
    }

    private static String getMonitorData(StepsChatSession session, MonitorTableEntity entity) {
        String chatIdArrayStr = "";
        List<String> chatIdArray = JSON.parseArray(entity.getChatIdArrayJson(), String.class);
        if (null == chatIdArray || chatIdArray.isEmpty()) {
            chatIdArrayStr = StringUtils.join(GlobalConfig.getChatIdArray(), " ");
        } else {
            chatIdArrayStr = StringUtils.join(chatIdArray, " ");
        }

        StringBuilder builder = new StringBuilder();

        builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayFileBasename), entity.getName()));
        builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayOn), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getEnable()).get())));
        builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayWebPagePreview), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getWebPagePreview()).get())));
        builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayNotification), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getNotification()).get())));
        builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayZeroDelay), getEnableDisplayI18nText(session.getFromId(), YesOrNoEnum.get(entity.getZeroDelay()).get())));
        builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayUrl), entity.getUrl()));
        builder.append(String.format("%s: %s\n", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayChatIdArray), chatIdArrayStr));
        builder.append(String.format("%s: \n%s", I18nHandle.getText(session.getFromId(), I18nEnum.ConfigDisplayTemplate), entity.getTemplate()));

        return builder.toString();
    }

}


================================================
FILE: src/main/java/code/handler/I18nHandle.java
================================================
package code.handler;

import code.config.I18nConfig;
import code.config.I18nEnum;
import code.config.I18nLocaleEnum;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

import static code.Main.I18nTableRepository;

public class I18nHandle {

    private static Map<String, String> cacheMap = new HashMap<>();

    public static String getText(String chatId, String key) {
        return getText(chatId, key, null);
    }

    public static String getText(String chatId, String key, Object... args) {
        String alias = cacheMap.get(chatId);
        if (StringUtils.isBlank(alias)) {
            alias = I18nTableRepository.selectI18nAlias(chatId);
            cacheMap.put(chatId, alias);
        }

        String text = I18nConfig.getText(alias, key);
        if (null != args && args.length > 0) {
            return String.format(text, args);
        }
        return text;
    }

    public static String getText(String chatId, I18nEnum i18nEnum) {
        return getText(chatId, i18nEnum, null);
    }
    public static String getText(String chatId, I18nEnum i18nEnum, Object... args) {
        String alias = cacheMap.get(chatId);
        if (StringUtils.isBlank(alias)) {
            alias = I18nTableRepository.selectI18nAlias(chatId);
            cacheMap.put(chatId, alias);
        }

        String text = I18nConfig.getText(alias, i18nEnum);
        if (null != args && args.length > 0) {
            return String.format(text, args);
        }
        return text;
    }

    public static void save(String chatId, I18nLocaleEnum i18nLocaleEnum) {
        I18nTableRepository.save(chatId, i18nLocaleEnum.getAlias());
        cacheMap.put(chatId, i18nLocaleEnum.getAlias());
    }

}


================================================
FILE: src/main/java/code/handler/MessageHandle.java
================================================
package code.handler;

import code.util.ExceptionUtil;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.telegram.telegrambots.meta.api.methods.ParseMode;
import org.telegram.telegrambots.meta.api.methods.send.SendMediaGroup;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.methods.send.SendPhoto;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText;
import org.telegram.telegrambots.meta.api.objects.InputFile;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.media.InputMedia;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static code.Main.Bot;

@Slf4j
public class MessageHandle {

    public enum MessageError {
        BotWasBlockedByTheUser,

        ;
    }

    @Data
    public static class MessageResponse {
        private boolean ok;
        private Message message;
        private MessageError messageError;
    }

    public static Message sendImage(String chatId, Integer replyToMessageId, String text, InputStream image) {
        SendPhoto sendPhoto = new SendPhoto();
        sendPhoto.setChatId(chatId);
        sendPhoto.setReplyToMessageId(replyToMessageId);
        sendPhoto.setCaption(text);
        sendPhoto.setPhoto(new InputFile(image, "image.png"));

        try {
            return Bot.execute(sendPhoto);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image) {
        SendPhoto sendPhoto = new SendPhoto();
        sendPhoto.setChatId(chatId);
        sendPhoto.setReplyToMessageId(replyToMessageId);
        sendPhoto.setCaption(text);
        sendPhoto.setPhoto(new InputFile(image));

        try {
            return Bot.execute(sendPhoto);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image, List<List<InlineKeyboardButton>> keyboard) {
        SendPhoto sendPhoto = new SendPhoto();
        sendPhoto.setChatId(chatId);
        sendPhoto.setReplyToMessageId(replyToMessageId);
        sendPhoto.setCaption(text);
        sendPhoto.setPhoto(new InputFile(image));
        if (null != keyboard) {
            InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
            inlineKeyboardMarkup.setKeyboard(keyboard);
            sendPhoto.setReplyMarkup(inlineKeyboardMarkup);
        }
        try {
            return Bot.execute(sendPhoto);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static void updateInlineKeyboardList(Message message, String chatId, String text, List<List<InlineKeyboardButton>> keyboard) {
        EditMessageText editMessageText = new EditMessageText();
        editMessageText.setChatId(chatId);
        editMessageText.setText(text);
        editMessageText.setDisableWebPagePreview(true);
        editMessageText.setMessageId(message.getMessageId());

        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
        inlineKeyboardMarkup.setKeyboard(keyboard);
        editMessageText.setReplyMarkup(inlineKeyboardMarkup);
        try {
            Bot.execute(editMessageText);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
    }

    public static Message sendInlineKeyboardList(String chatId, String text, List<List<InlineKeyboardButton>> keyboard) {
        return sendInlineKeyboardList(chatId, null, text, keyboard);
    }

    public static Message sendInlineKeyboardList(String chatId, Integer replyToMessageId, String text, List<List<InlineKeyboardButton>> keyboard) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText(text);
        message.setReplyToMessageId(replyToMessageId);
        message.setDisableWebPagePreview(true);

        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();

        inlineKeyboardMarkup.setKeyboard(keyboard);
        message.setReplyMarkup(inlineKeyboardMarkup);

        try {
            return Bot.execute(message);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendInlineKeyboard(String chatId, String text, InlineKeyboardButton... inlineKeyboardButtonList) {
        return sendInlineKeyboard(chatId, text, Arrays.asList(inlineKeyboardButtonList));
    }

    public static Message sendInlineKeyboard(String chatId, String text, List<InlineKeyboardButton> inlineKeyboardButtonList) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText(text);

        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
        List<List<InlineKeyboardButton>> keyboard = new ArrayList<>();

        for (InlineKeyboardButton button : inlineKeyboardButtonList) {
            List<InlineKeyboardButton> list = new ArrayList<>();
            list.add(button);
            keyboard.add(list);
        }

        inlineKeyboardMarkup.setKeyboard(keyboard);
        message.setReplyMarkup(inlineKeyboardMarkup);

        try {
            return Bot.execute(message);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendCustomKeyboard(String chatId, String text, KeyboardRow row) {
        List<KeyboardRow> list = new ArrayList<>();
        list.add(row);

        return sendCustomKeyboard(chatId, text, list);
    }

    public static Message sendCustomKeyboard(String chatId, String text, List<KeyboardRow> keyboard) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText(text);

        ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();

        keyboardMarkup.setKeyboard(keyboard);
        message.setReplyMarkup(keyboardMarkup);

        try {
            return Bot.execute(message);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static MessageResponse sendMsg(String chatId, String text, boolean webPagePreview) {
        MessageResponse response = new MessageResponse();
        response.setOk(false);

        SendMessage sendMessage = new SendMessage();
        sendMessage.setChatId(chatId);
        sendMessage.setText(text);
        sendMessage.setParseMode(ParseMode.HTML);
        if (!webPagePreview) {
            sendMessage.disableWebPagePreview();
        }
        try {
            Message execute = Bot.execute(sendMessage);
            response.setOk(true);
            response.setMessage(execute);
            return response;
        } catch (Exception e) {
            String message = e.getMessage();
            if (message.contains("bot was blocked by the user")) {
                response.setMessageError(MessageError.BotWasBlockedByTheUser);
            } else {
                log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage)));
            }
        }

        return response;
    }

    public static List<Message> sendMediaGroup(String chatId, List<InputMedia> mediaList, boolean notification) {
        SendMediaGroup sendMediaGroup = new SendMediaGroup();
        sendMediaGroup.setChatId(chatId);
        sendMediaGroup.setMedias(mediaList);
        if (!notification) {
            sendMediaGroup.disableNotification();
        }
        try {
            List<Message> execute = Bot.execute(sendMediaGroup);
            return execute;
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendMessage(String chatId, String text, boolean webPagePreview) {
        return sendMessage(chatId, null, text, webPagePreview, true, null);
    }
    public static Message sendMessage(String chatId, String text, boolean webPagePreview, boolean notification) {
        return sendMessage(chatId, null, text, webPagePreview, notification, null);
    }
    public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview) {
        return sendMessage(chatId, replyToMessageId, text, webPagePreview, true, null);
    }
    public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview, boolean notification, List<List<InlineKeyboardButton>> buttons) {
        SendMessage sendMessage = new SendMessage();
        sendMessage.setChatId(chatId);
        sendMessage.setReplyToMessageId(replyToMessageId);
        sendMessage.setText(text);
        sendMessage.setParseMode(ParseMode.HTML);
        if (!notification) {
            sendMessage.disableNotification();
        }
        if (!webPagePreview) {
            sendMessage.disableWebPagePreview();
        }
        if (null != buttons && !buttons.isEmpty()) {
            InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
            inlineKeyboardMarkup.setKeyboard(buttons);
            sendMessage.setReplyMarkup(inlineKeyboardMarkup);
        }
        return sendMessage(sendMessage);
    }

    public static Message sendMessage(SendMessage sendMessage) {
        try {
            Message execute = Bot.execute(sendMessage);
            return execute;
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage)));
        }
        return null;
    }

    public static boolean editMessage(Message message, String text) {
        try {
            EditMessageText editMessageText = new EditMessageText();
            editMessageText.setChatId(message.getChatId());
            editMessageText.setMessageId(message.getMessageId());
            editMessageText.setText(text);

            Bot.execute(editMessageText);
            return true;
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(message)));
        }
        return false;
    }

    public static boolean deleteMessage(Message message) {
        if (null == message) {
            return false;
        }

        DeleteMessage deleteMessage = new DeleteMessage();
        deleteMessage.setChatId(message.getChatId());
        deleteMessage.setMessageId(message.getMessageId());

        try {
            Boolean execute = Bot.execute(deleteMessage);
            return null == execute ? false : execute;
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage)));
        }
        return false;
    }

    public static boolean deleteMessage(DeleteMessage deleteMessage) {
        try {
            Boolean execute = Bot.execute(deleteMessage);
            return null == execute ? false : execute;
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(deleteMessage)));
        }
        return false;
    }

}


================================================
FILE: src/main/java/code/handler/StepsCenter.java
================================================
package code.handler;

import code.config.ExecutorsConfig;
import code.handler.message.CallbackBuilder;
import code.handler.steps.StepsChatSession;
import code.handler.steps.StepsHandler;
import code.handler.steps.StepsRegisterCenter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static code.Main.GlobalConfig;

@Slf4j
public class StepsCenter {

    public static boolean cmdHandle(StepsChatSession session) {
        if (StringUtils.isNotBlank(session.getText()) && session.getText().startsWith("/")) {
            String s = StringUtils.remove(session.getText(), "/");
            String[] split = s.split(" ");
            if (split.length > 0) {
                String cmd = split[0];
                cmd = StringUtils.replace(cmd, "@" + GlobalConfig.getBotName(), "");
                if (Command.exist(cmd)) {
                    split[0] = cmd;
                    session.setText(Stream.of(split).skip(1).collect(Collectors.joining(" ")));
                    cmdHandle(
                            Command.toCmd(cmd),
                            false,
                            session,
                            null
                    );
                    return true;
                }
            }
        }
        return false;
    }

    public static void cmdHandle(CallbackBuilder.CallbackData callbackData, StepsChatSession stepsChatSession) {
        cmdHandle(callbackData.getCommand(), true, stepsChatSession, callbackData);
    }

    public static void cmdHandle(Command command, StepsChatSession stepsChatSession) {
        cmdHandle(command, false, stepsChatSession, null);
    }

    private static void cmdHandle(Command command, boolean isCall, StepsChatSession stepsChatSession, CallbackBuilder.CallbackData callbackData) {
        boolean permission = false;

        String botAdminId = GlobalConfig.getBotAdminId();
        if (botAdminId.equals(stepsChatSession.getChatId()) || botAdminId.equals(stepsChatSession.getFromId())) {
            permission = true;
        }
        for (String s : GlobalConfig.getPermissionChatIdArray()) {
            if (s.equals(stepsChatSession.getChatId()) || s.equals(stepsChatSession.getFromId())) {
                permission = true;
                break;
            }
        }

        if (!permission) {
            MessageHandle.sendMessage(stepsChatSession.getChatId(), stepsChatSession.getReplyToMessageId(), "你没有使用权限, 不过你可以自己搭建一个\nhttps://github.com/kylelin1998/RssMonitorTelegramBot", false);
            return;
        }

        if (null != callbackData){
            StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd());
            if (!callbackData.isInit() && !handler.hasInit(stepsChatSession)) {
                return;
            }
        }

        ExecutorsConfig.submit(() -> {
            StepsHandler handler = StepsRegisterCenter.getRegister(command.getCmd());
            if (null != handler.getInitStep() && (!handler.hasInit(stepsChatSession) || !isCall)) {
                handler.init(stepsChatSession);
            } else {
                handler.step(stepsChatSession);
            }
        });
    }

    public static void textHandle(StepsChatSession stepsChatSession) {
        StepsHandler handler = StepsRegisterCenter.getPriority(stepsChatSession);
        if (null == handler) {
            return;
        }
        ExecutorsConfig.submit(() -> {
            handler.step(stepsChatSession);
        });
    }

    public static void exit(StepsChatSession stepsChatSession) {
        Collection<StepsHandler> list = StepsRegisterCenter.getRegisterList();
        for (StepsHandler handler : list) {
            handler.exit(stepsChatSession);
        }
    }

}


================================================
FILE: src/main/java/code/handler/message/CallbackBuilder.java
================================================
package code.handler.message;

import code.handler.Command;
import code.handler.steps.StepsChatSession;
import code.util.ExceptionUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class CallbackBuilder {

    @Data
    public static class CallbackData {
        private boolean init;
        private String id;
        private Command command;
        private String text;
    }

    public static String buildCallbackData(boolean init, StepsChatSession session, Command command, String text) {
        StringBuilder builder = new StringBuilder();
        builder.append("f[" + session.getSessionId() + "]");
        builder.append(command.getCmd());
        builder.append(" ");
        builder.append(init);
        builder.append(" ");
        builder.append(text);
        return builder.toString();
    }
    public static CallbackData parseCallbackData(String callbackData) {
        try {
            CallbackData data = new CallbackData();
            data.setId(StringUtils.substringBetween(callbackData, "f[", "]"));

            String s = StringUtils.replace(callbackData, "f[" + data.getId() + "]", "");
            String[] arguments = s.split(" ");

            data.setCommand(Command.toCmd(arguments[0]));
            data.setInit(Boolean.valueOf(arguments[1]));
            data.setText(arguments.length > 2 ? arguments[2] : null);

            return data;
        } catch (Exception e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

}


================================================
FILE: src/main/java/code/handler/message/InlineKeyboardButtonBuilder.java
================================================
package code.handler.message;

import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;

import java.util.ArrayList;
import java.util.List;

public class InlineKeyboardButtonBuilder {

    private List<InlineKeyboardButton> inlineKeyboardButtonList;

    private String callbackData;

    private InlineKeyboardButtonBuilder() {}

    public static InlineKeyboardButtonBuilder create() {
        InlineKeyboardButtonBuilder builder = new InlineKeyboardButtonBuilder();
        builder.inlineKeyboardButtonList = new ArrayList<>();
        return builder;
    }

    public InlineKeyboardButtonBuilder setCallbackData(String callbackData) {
        this.callbackData = callbackData;
        return this;
    }

    public InlineKeyboardButtonBuilder add(String text, String callbackData) {
        InlineKeyboardButton button = new InlineKeyboardButton();
        button.setText(text);
        button.setCallbackData(callbackData);
        inlineKeyboardButtonList.add(button);
        return this;
    }

    public InlineKeyboardButtonBuilder add(String text) {
        InlineKeyboardButton button = new InlineKeyboardButton();
        button.setText(text);
        button.setCallbackData(this.callbackData);
        inlineKeyboardButtonList.add(button);
        return this;
    }

    public InlineKeyboardButtonBuilder add(InlineKeyboardButton button) {
        inlineKeyboardButtonList.add(button);
        return this;
    }

    public List<InlineKeyboardButton> build() {
        return inlineKeyboardButtonList;
    }

}


================================================
FILE: src/main/java/code/handler/message/InlineKeyboardButtonListBuilder.java
================================================
package code.handler.message;

import code.eneity.PageEntity;
import code.handler.Command;
import code.handler.steps.StepsChatSession;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;

import java.util.ArrayList;
import java.util.List;

public class InlineKeyboardButtonListBuilder {
    private List<List<InlineKeyboardButton>> keyboard;
    private InlineKeyboardButtonListBuilder() {}

    public static InlineKeyboardButtonListBuilder create() {
        InlineKeyboardButtonListBuilder builder = new InlineKeyboardButtonListBuilder();
        builder.keyboard = new ArrayList<>();
        return builder;
    }

    public InlineKeyboardButtonListBuilder add(List<InlineKeyboardButton> inlineKeyboardButtonList) {
        this.keyboard.add(inlineKeyboardButtonList);
        return this;
    }

    public InlineKeyboardButtonListBuilder pagination(PageEntity entity, StepsChatSession session, Command command) {
        int count = entity.getCount();
        if (count > 1) {
            InlineKeyboardButtonBuilder builder = InlineKeyboardButtonBuilder
                    .create();
            if (entity.isHasPrev()) {
                builder.add("⬅️", CallbackBuilder.buildCallbackData(true, session, command, "" + (entity.getCurrent() - 1)));
            }
            if (entity.isHasNext()) {
                builder.add("➡️", CallbackBuilder.buildCallbackData(true, session, command, "" + (entity.getCurrent() + 1)));
            }
            this.keyboard.add(builder.build());
        }
        return this;
    }

    public List<List<InlineKeyboardButton>> build() {
        return keyboard;
    }

}


================================================
FILE: src/main/java/code/handler/message/MessageHandle.java
================================================
package code.handler.message;

import code.util.ExceptionUtil;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.telegram.telegrambots.meta.api.methods.ParseMode;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.methods.send.SendPhoto;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText;
import org.telegram.telegrambots.meta.api.objects.InputFile;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static code.Main.Bot;

@Slf4j
public class MessageHandle {

    public enum MessageError {
        BotWasBlockedByTheUser,

        ;
    }

    @Data
    public static class MessageResponse {
        private boolean ok;
        private Message message;
        private MessageError messageError;
    }

    public static Message sendImage(String chatId, Integer replyToMessageId, String text, InputStream image) {
        SendPhoto sendPhoto = new SendPhoto();
        sendPhoto.setChatId(chatId);
        sendPhoto.setReplyToMessageId(replyToMessageId);
        sendPhoto.setCaption(text);
        sendPhoto.setPhoto(new InputFile(image, "image.png"));

        try {
            return Bot.execute(sendPhoto);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendImage(String chatId, Integer replyToMessageId, String text, File image) {
        SendPhoto sendPhoto = new SendPhoto();
        sendPhoto.setChatId(chatId);
        sendPhoto.setReplyToMessageId(replyToMessageId);
        sendPhoto.setCaption(text);
        sendPhoto.setPhoto(new InputFile(image));

        try {
            return Bot.execute(sendPhoto);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendInlineKeyboard(String chatId, String text, InlineKeyboardButton... inlineKeyboardButtonList) {
        return sendInlineKeyboard(chatId, text, Arrays.asList(inlineKeyboardButtonList));
    }

    public static Message sendInlineKeyboardList(String chatId, String text, List<List<InlineKeyboardButton>> keyboard) {
        return sendInlineKeyboardList(chatId, null, text, keyboard);
    }

    public static Message sendInlineKeyboardList(String chatId, Integer replyToMessageId, String text, List<List<InlineKeyboardButton>> keyboard) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText(text);
        message.setReplyToMessageId(replyToMessageId);
        message.setDisableWebPagePreview(true);

        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();

        inlineKeyboardMarkup.setKeyboard(keyboard);
        message.setReplyMarkup(inlineKeyboardMarkup);

        try {
            return Bot.execute(message);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendInlineKeyboard(String chatId, String text, List<InlineKeyboardButton> inlineKeyboardButtonList) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText(text);

        InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
        List<List<InlineKeyboardButton>> keyboard = new ArrayList<>();

        for (InlineKeyboardButton button : inlineKeyboardButtonList) {
            List<InlineKeyboardButton> list = new ArrayList<>();
            list.add(button);
            keyboard.add(list);
        }

        inlineKeyboardMarkup.setKeyboard(keyboard);
        message.setReplyMarkup(inlineKeyboardMarkup);

        try {
            return Bot.execute(message);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static Message sendCustomKeyboard(String chatId, String text, KeyboardRow row) {
        List<KeyboardRow> list = new ArrayList<>();
        list.add(row);

        return sendCustomKeyboard(chatId, text, list);
    }

    public static Message sendCustomKeyboard(String chatId, String text, List<KeyboardRow> keyboard) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText(text);

        ReplyKeyboardMarkup keyboardMarkup = new ReplyKeyboardMarkup();

        keyboardMarkup.setKeyboard(keyboard);
        message.setReplyMarkup(keyboardMarkup);

        try {
            return Bot.execute(message);
        } catch (TelegramApiException e) {
            log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e));
        }
        return null;
    }

    public static MessageResponse sendMsg(String chatId, String text, boolean webPagePreview) {
        MessageResponse response = new MessageResponse();
        response.setOk(false);

        SendMessage sendMessage = new SendMessage();
        sendMessage.setChatId(chatId);
        sendMessage.setText(text);
        sendMessage.setParseMode(ParseMode.HTML);
        if (!webPagePreview) {
            sendMessage.disableWebPagePreview();
        }
        try {
            Message execute = Bot.execute(sendMessage);
            response.setOk(true);
            response.setMessage(execute);
            return response;
        } catch (Exception e) {
            String message = e.getMessage();
            if (message.contains("bot was blocked by the user")) {
                response.setMessageError(MessageError.BotWasBlockedByTheUser);
            } else {
                log.error(ExceptionUtil.getStackTraceWithCustomInfoToStr(e, JSON.toJSONString(sendMessage)));
            }
        }

        return response;
    }

    public static Message sendMessage(String chatId, String text, boolean webPagePreview) {
        return sendMessage(chatId, null, text, webPagePreview, true, null);
    }
    public static Message sendMessage(String chatId, String text, boolean webPagePreview, boolean notification) {
        return sendMessage(chatId, null, text, webPagePreview, notification, null);
    }
    public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview) {
        return sendMessage(chatId, replyToMessageId, text, webPagePreview, true, null);
    }
    public static Message sendMessage(String chatId, Integer replyToMessageId, String text, boolean webPagePreview, boolean notification, List<List<InlineKeyboardButton>> buttons) {
        SendMessage sendMessage = new SendMessage();
        sendMessage.setChatId(chatId);
        sendMessage.setReplyToMessageId(replyToMessageId);
        sendMessage.setText(text);
        sendMessage.setParseMode(ParseMode.HTML);
        if (!notification) {
            sendMessage.disableNotification();
        }
        if (!webPagePreview) {
            sendMessage.disableWebPagePreview();
        }
        if (null != buttons && !buttons.isEmpty()) {
            InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
            inlineKeyboardMarkup.setKeyboard(buttons);
            sendMessage.setReplyMarkup(inlineKeyboardMarkup);
        }
        return sendMessage(sendMessage);
    }

    public static Message sendMessage(String chatId, String text, boolean webPagePreview, List<List<InlineKeyboardButton>> buttons) {
        return sendMessage(chatId, null, text, webPagePreview, true, null);
    }

    public static Message sendMessage(SendMessage sendMessage) {
        try {
            String text = sendMessage.getText();
            if (StringUtils.isNotBlank(text)) {
                text = StringUtils.replace(text, "<", "&lt;");
                text = StringUtils.replace(text, ">", "&gt;");
                sendMessage.setText(text);
            }

   
Download .txt
gitextract_nqpts3aa/

├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── README_en.md
├── docker-push.sh
├── pom.xml
├── run.sh
└── src/
    └── main/
        ├── java/
        │   └── code/
        │       ├── Main.java
        │       ├── config/
        │       │   ├── Config.java
        │       │   ├── ConfigField.java
        │       │   ├── ConfigSettings.java
        │       │   ├── DisplayConfigAnnotation.java
        │       │   ├── ExecutorsConfig.java
        │       │   ├── I18nConfig.java
        │       │   ├── I18nEnum.java
        │       │   ├── I18nLocaleEnum.java
        │       │   ├── MonitorConfigSettings.java
        │       │   ├── MonitorExecutorsConfig.java
        │       │   ├── ProxyTypeEnum.java
        │       │   ├── RequestProxyConfig.java
        │       │   └── TableEnum.java
        │       ├── eneity/
        │       │   ├── MonitorTableEntity.java
        │       │   ├── PageEntity.java
        │       │   ├── SentRecordTableEntity.java
        │       │   ├── WebhookTableEntity.java
        │       │   └── YesOrNoEnum.java
        │       ├── handler/
        │       │   ├── Command.java
        │       │   ├── CommandsHandler.java
        │       │   ├── Handler.java
        │       │   ├── I18nHandle.java
        │       │   ├── MessageHandle.java
        │       │   ├── StepsCenter.java
        │       │   ├── message/
        │       │   │   ├── CallbackBuilder.java
        │       │   │   ├── InlineKeyboardButtonBuilder.java
        │       │   │   ├── InlineKeyboardButtonListBuilder.java
        │       │   │   └── MessageHandle.java
        │       │   ├── steps/
        │       │   │   ├── StepErrorApi.java
        │       │   │   ├── StepExecuteResult.java
        │       │   │   ├── StepHandleApi.java
        │       │   │   ├── StepResult.java
        │       │   │   ├── StepsBuilder.java
        │       │   │   ├── StepsChatSession.java
        │       │   │   ├── StepsChatSessionBuilder.java
        │       │   │   ├── StepsHandler.java
        │       │   │   └── StepsRegisterCenter.java
        │       │   └── store/
        │       │       ├── ChatButtonsStore.java
        │       │       ├── Store.java
        │       │       └── WebhookStore.java
        │       ├── repository/
        │       │   ├── I18nTableRepository.java
        │       │   ├── MonitorTableRepository.java
        │       │   ├── SentRecordTableRepository.java
        │       │   ├── WebhookTableRepository.java
        │       │   └── base/
        │       │       ├── SqlBuilder.java
        │       │       ├── TableEntity.java
        │       │       ├── TableField.java
        │       │       ├── TableName.java
        │       │       └── TableRepository.java
        │       └── util/
        │           ├── BytesUtil.java
        │           ├── DownloadUtil.java
        │           ├── ExceptionUtil.java
        │           ├── GithubUtil.java
        │           ├── ProgramUtil.java
        │           ├── RssUtil.java
        │           ├── Snowflake.java
        │           ├── SqliteUtil.java
        │           ├── TelegraphUtil.java
        │           └── translate/
        │               ├── MicrosoftTranslateHandle.java
        │               ├── Translate.java
        │               ├── YoudaoTranslateHandle.java
        │               └── base/
        │                   ├── TranslateAPI.java
        │                   └── TranslateAuth.java
        └── resources/
            ├── code/
            │   └── config/
            │       ├── telegraph.html
            │       └── webhook.json
            ├── i18n/
            │   ├── i18n_en.properties
            │   └── i18n_zh_CN.properties
            └── logback.xml
Download .txt
SYMBOL INDEX (343 symbols across 60 files)

FILE: src/main/java/code/Main.java
  class Main (line 35) | @Slf4j
    method main (line 45) | public static void main(String[] args) {

FILE: src/main/java/code/config/Config.java
  class Config (line 30) | @Slf4j
    class MetaData (line 51) | public final static class MetaData {
    method mkdirs (line 98) | private static void mkdirs(String... dirs) {
    method initConfig (line 107) | public static ConfigSettings initConfig() {
    method handle (line 125) | private static ConfigSettings handle(ConfigSettings configSettings) {
    method readConfig (line 169) | public static ConfigSettings readConfig() {
    method verifyConfig (line 190) | public static ConfigSettings verifyConfig(String configJson) {
    method oldDataConvert (line 224) | public synchronized static void oldDataConvert() {
    method saveConfig (line 265) | public static boolean saveConfig(ConfigSettings configSettings) {

FILE: src/main/java/code/config/ConfigSettings.java
  class ConfigSettings (line 8) | @Data

FILE: src/main/java/code/config/ExecutorsConfig.java
  class ExecutorsConfig (line 8) | @Slf4j
    method submit (line 25) | public static void submit(Runnable task) {

FILE: src/main/java/code/config/I18nConfig.java
  class I18nConfig (line 11) | public class I18nConfig {
    method newBundle (line 17) | @Override
    method getText (line 46) | public static String getText(String i18nAlias, String key) {
    method getText (line 51) | public static String getText(String i18nAlias, I18nEnum i18nEnum) {

FILE: src/main/java/code/config/I18nEnum.java
  type I18nEnum (line 5) | @Getter
    method I18nEnum (line 136) | I18nEnum(String key) {

FILE: src/main/java/code/config/I18nLocaleEnum.java
  type I18nLocaleEnum (line 7) | @Getter
    method I18nLocaleEnum (line 18) | I18nLocaleEnum(Locale locale, String alias, String displayText) {
    method getI18nLocaleEnumByAlias (line 24) | public static I18nLocaleEnum getI18nLocaleEnumByAlias(String alias) {

FILE: src/main/java/code/config/MonitorConfigSettings.java
  class MonitorConfigSettings (line 5) | @Data

FILE: src/main/java/code/config/MonitorExecutorsConfig.java
  class MonitorExecutorsConfig (line 8) | @Slf4j
    method submit (line 25) | public static void submit(Runnable task) {

FILE: src/main/java/code/config/ProxyTypeEnum.java
  type ProxyTypeEnum (line 5) | @Getter
    method ProxyTypeEnum (line 16) | ProxyTypeEnum(int type, String alias) {
    method getDefault (line 21) | public static ProxyTypeEnum getDefault() {

FILE: src/main/java/code/config/RequestProxyConfig.java
  class RequestProxyConfig (line 10) | public class RequestProxyConfig {
    method create (line 16) | public static RequestProxyConfig create() {
    method viaProxy (line 28) | public void viaProxy(HttpRequest request) {
    method viaProxy (line 36) | public void viaProxy(Request request) {
    method buildDefaultBotOptions (line 44) | public DefaultBotOptions buildDefaultBotOptions() {

FILE: src/main/java/code/config/TableEnum.java
  type TableEnum (line 5) | @Getter
    method TableEnum (line 15) | TableEnum(String name) {

FILE: src/main/java/code/eneity/MonitorTableEntity.java
  class MonitorTableEntity (line 9) | @TableName(name = "monitor_table")

FILE: src/main/java/code/eneity/PageEntity.java
  class PageEntity (line 8) | @Data
    method PageEntity (line 20) | private PageEntity() {}
    method PageEntity (line 22) | public PageEntity(int total, int page, int current) {
    method empty (line 33) | public static PageEntity empty() {

FILE: src/main/java/code/eneity/SentRecordTableEntity.java
  class SentRecordTableEntity (line 8) | @TableName(name = "sent_record_202312_table")

FILE: src/main/java/code/eneity/WebhookTableEntity.java
  class WebhookTableEntity (line 8) | @TableName(name = "webhook_table")

FILE: src/main/java/code/eneity/YesOrNoEnum.java
  type YesOrNoEnum (line 7) | @Getter
    method YesOrNoEnum (line 18) | YesOrNoEnum(int num, boolean bool) {
    method toInt (line 23) | public static int toInt(boolean bool) {
    method toBoolean (line 27) | public static Optional<Boolean> toBoolean(int num) {
    method get (line 36) | public static Optional<YesOrNoEnum> get(int num) {

FILE: src/main/java/code/handler/Command.java
  type Command (line 5) | @Getter
    method Command (line 43) | Command(String cmd) {
    method toCmd (line 47) | public static Command toCmd(String cmd) {
    method exist (line 56) | public static boolean exist(String cmd) {

FILE: src/main/java/code/handler/CommandsHandler.java
  class CommandsHandler (line 17) | @Slf4j
    method CommandsHandler (line 20) | public CommandsHandler() {
    method CommandsHandler (line 24) | public CommandsHandler(DefaultBotOptions botOptions) {
    method getBotUsername (line 28) | @Override
    method getBotToken (line 33) | @Override
    method processNonCommandUpdate (line 38) | @Override

FILE: src/main/java/code/handler/Handler.java
  class Handler (line 48) | @Slf4j
    method isAdmin (line 51) | private static boolean isAdmin(String fromId) {
    method init (line 55) | public static void init() {
    method putDeleteMessage (line 1261) | private static void putDeleteMessage(Map<String, Object> context, Mess...
    method deleteMessage (line 1264) | private static void deleteMessage(Map<String, Object> context) {
    method showMonitorHandle (line 1274) | private static void showMonitorHandle(StepsChatSession session, String...
    method showMonitorListHandle (line 1335) | private static void showMonitorListHandle(StepsChatSession session) {
    method rssMessageHandle (line 1376) | private static void rssMessageHandle(StepsChatSession session, Monitor...
    method isEnableIncludeKeywords (line 1473) | private static boolean isEnableIncludeKeywords() {
    method containsIncludeKeywords (line 1480) | private static boolean containsIncludeKeywords(String text) {
    method containsExcludeKeywords (line 1502) | private static boolean containsExcludeKeywords(String text) {
    method sendRss (line 1525) | private static void sendRss(String chatId, StepsChatSession session, M...
    method getImages (line 1624) | private static List<String> getImages(SyndEntry entry) {
    method getDescription (line 1647) | public static String getDescription(SyndEntry entry) {
    method getDescription (line 1658) | public static String getDescription(String html) {
    method replaceTemplate (line 1676) | private static String replaceTemplate(String template, SyndFeed feed, ...
    method replaceTelegraphHtml (line 1784) | private static String replaceTelegraphHtml(String link, String title) {
    method getEnableDisplayI18nText (line 1789) | private static String getEnableDisplayI18nText(String fromId, YesOrNoE...
    method getMonitorData (line 1800) | private static String getMonitorData(StepsChatSession session, Monitor...

FILE: src/main/java/code/handler/I18nHandle.java
  class I18nHandle (line 13) | public class I18nHandle {
    method getText (line 17) | public static String getText(String chatId, String key) {
    method getText (line 21) | public static String getText(String chatId, String key, Object... args) {
    method getText (line 35) | public static String getText(String chatId, I18nEnum i18nEnum) {
    method getText (line 38) | public static String getText(String chatId, I18nEnum i18nEnum, Object....
    method save (line 52) | public static void save(String chatId, I18nLocaleEnum i18nLocaleEnum) {

FILE: src/main/java/code/handler/MessageHandle.java
  class MessageHandle (line 30) | @Slf4j
    type MessageError (line 33) | public enum MessageError {
    class MessageResponse (line 39) | @Data
    method sendImage (line 46) | public static Message sendImage(String chatId, Integer replyToMessageI...
    method sendImage (line 61) | public static Message sendImage(String chatId, Integer replyToMessageI...
    method sendImage (line 76) | public static Message sendImage(String chatId, Integer replyToMessageI...
    method updateInlineKeyboardList (line 95) | public static void updateInlineKeyboardList(Message message, String ch...
    method sendInlineKeyboardList (line 112) | public static Message sendInlineKeyboardList(String chatId, String tex...
    method sendInlineKeyboardList (line 116) | public static Message sendInlineKeyboardList(String chatId, Integer re...
    method sendInlineKeyboard (line 136) | public static Message sendInlineKeyboard(String chatId, String text, I...
    method sendInlineKeyboard (line 140) | public static Message sendInlineKeyboard(String chatId, String text, L...
    method sendCustomKeyboard (line 165) | public static Message sendCustomKeyboard(String chatId, String text, K...
    method sendCustomKeyboard (line 172) | public static Message sendCustomKeyboard(String chatId, String text, L...
    method sendMsg (line 190) | public static MessageResponse sendMsg(String chatId, String text, bool...
    method sendMediaGroup (line 218) | public static List<Message> sendMediaGroup(String chatId, List<InputMe...
    method sendMessage (line 234) | public static Message sendMessage(String chatId, String text, boolean ...
    method sendMessage (line 237) | public static Message sendMessage(String chatId, String text, boolean ...
    method sendMessage (line 240) | public static Message sendMessage(String chatId, Integer replyToMessag...
    method sendMessage (line 243) | public static Message sendMessage(String chatId, Integer replyToMessag...
    method sendMessage (line 263) | public static Message sendMessage(SendMessage sendMessage) {
    method editMessage (line 273) | public static boolean editMessage(Message message, String text) {
    method deleteMessage (line 288) | public static boolean deleteMessage(Message message) {
    method deleteMessage (line 306) | public static boolean deleteMessage(DeleteMessage deleteMessage) {

FILE: src/main/java/code/handler/StepsCenter.java
  class StepsCenter (line 17) | @Slf4j
    method cmdHandle (line 20) | public static boolean cmdHandle(StepsChatSession session) {
    method cmdHandle (line 43) | public static void cmdHandle(CallbackBuilder.CallbackData callbackData...
    method cmdHandle (line 47) | public static void cmdHandle(Command command, StepsChatSession stepsCh...
    method cmdHandle (line 51) | private static void cmdHandle(Command command, boolean isCall, StepsCh...
    method textHandle (line 87) | public static void textHandle(StepsChatSession stepsChatSession) {
    method exit (line 97) | public static void exit(StepsChatSession stepsChatSession) {

FILE: src/main/java/code/handler/message/CallbackBuilder.java
  class CallbackBuilder (line 10) | @Slf4j
    class CallbackData (line 13) | @Data
    method buildCallbackData (line 21) | public static String buildCallbackData(boolean init, StepsChatSession ...
    method parseCallbackData (line 31) | public static CallbackData parseCallbackData(String callbackData) {

FILE: src/main/java/code/handler/message/InlineKeyboardButtonBuilder.java
  class InlineKeyboardButtonBuilder (line 8) | public class InlineKeyboardButtonBuilder {
    method InlineKeyboardButtonBuilder (line 14) | private InlineKeyboardButtonBuilder() {}
    method create (line 16) | public static InlineKeyboardButtonBuilder create() {
    method setCallbackData (line 22) | public InlineKeyboardButtonBuilder setCallbackData(String callbackData) {
    method add (line 27) | public InlineKeyboardButtonBuilder add(String text, String callbackDat...
    method add (line 35) | public InlineKeyboardButtonBuilder add(String text) {
    method add (line 43) | public InlineKeyboardButtonBuilder add(InlineKeyboardButton button) {
    method build (line 48) | public List<InlineKeyboardButton> build() {

FILE: src/main/java/code/handler/message/InlineKeyboardButtonListBuilder.java
  class InlineKeyboardButtonListBuilder (line 11) | public class InlineKeyboardButtonListBuilder {
    method InlineKeyboardButtonListBuilder (line 13) | private InlineKeyboardButtonListBuilder() {}
    method create (line 15) | public static InlineKeyboardButtonListBuilder create() {
    method add (line 21) | public InlineKeyboardButtonListBuilder add(List<InlineKeyboardButton> ...
    method pagination (line 26) | public InlineKeyboardButtonListBuilder pagination(PageEntity entity, S...
    method build (line 42) | public List<List<InlineKeyboardButton>> build() {

FILE: src/main/java/code/handler/message/MessageHandle.java
  class MessageHandle (line 29) | @Slf4j
    type MessageError (line 32) | public enum MessageError {
    class MessageResponse (line 38) | @Data
    method sendImage (line 45) | public static Message sendImage(String chatId, Integer replyToMessageI...
    method sendImage (line 60) | public static Message sendImage(String chatId, Integer replyToMessageI...
    method sendInlineKeyboard (line 75) | public static Message sendInlineKeyboard(String chatId, String text, I...
    method sendInlineKeyboardList (line 79) | public static Message sendInlineKeyboardList(String chatId, String tex...
    method sendInlineKeyboardList (line 83) | public static Message sendInlineKeyboardList(String chatId, Integer re...
    method sendInlineKeyboard (line 103) | public static Message sendInlineKeyboard(String chatId, String text, L...
    method sendCustomKeyboard (line 128) | public static Message sendCustomKeyboard(String chatId, String text, K...
    method sendCustomKeyboard (line 135) | public static Message sendCustomKeyboard(String chatId, String text, L...
    method sendMsg (line 153) | public static MessageResponse sendMsg(String chatId, String text, bool...
    method sendMessage (line 181) | public static Message sendMessage(String chatId, String text, boolean ...
    method sendMessage (line 184) | public static Message sendMessage(String chatId, String text, boolean ...
    method sendMessage (line 187) | public static Message sendMessage(String chatId, Integer replyToMessag...
    method sendMessage (line 190) | public static Message sendMessage(String chatId, Integer replyToMessag...
    method sendMessage (line 210) | public static Message sendMessage(String chatId, String text, boolean ...
    method sendMessage (line 214) | public static Message sendMessage(SendMessage sendMessage) {
    method editMessage (line 231) | public static boolean editMessage(Message message, String text) {
    method editMessage (line 246) | public static boolean editMessage(Message message, String text, List<L...
    method deleteMessage (line 267) | public static boolean deleteMessage(Message message) {
    method deleteMessage (line 285) | public static boolean deleteMessage(DeleteMessage deleteMessage) {

FILE: src/main/java/code/handler/steps/StepErrorApi.java
  type StepErrorApi (line 3) | public interface StepErrorApi {
    method callback (line 5) | void callback(Exception e, StepsChatSession stepsChatSession);

FILE: src/main/java/code/handler/steps/StepExecuteResult.java
  class StepExecuteResult (line 6) | @Data
    method not (line 14) | public static StepExecuteResult not() {
    method work (line 18) | public static StepExecuteResult work() {
    method ok (line 22) | public static StepExecuteResult ok(StepResult stepResult) {

FILE: src/main/java/code/handler/steps/StepHandleApi.java
  type StepHandleApi (line 6) | public interface StepHandleApi {
    method execute (line 8) | StepResult execute(StepsChatSession stepsChatSession, int index, List<...

FILE: src/main/java/code/handler/steps/StepResult.java
  class StepResult (line 6) | @Data
    method ok (line 17) | public static StepResult ok() {
    method reject (line 21) | public static StepResult reject() {
    method next (line 25) | public static StepResult next() {
    method next (line 29) | public static StepResult next(String text) {
    method end (line 33) | public static StepResult end() {

FILE: src/main/java/code/handler/steps/StepsBuilder.java
  class StepsBuilder (line 5) | public class StepsBuilder {
    method StepsBuilder (line 13) | private StepsBuilder() {}
    method create (line 15) | public static StepsBuilder create() {
    method bindCommand (line 19) | public StepsBuilder bindCommand(Command... commands) {
    method debug (line 23) | public StepsBuilder debug(boolean debug) {
    method error (line 27) | public StepsBuilder error(StepErrorApi errorApi) {
    method init (line 31) | public StepsBuilder init(StepHandleApi initStep) {
    method steps (line 35) | public StepsBuilder steps(StepHandleApi... steps) {
    method build (line 40) | public StepsHandler build() {

FILE: src/main/java/code/handler/steps/StepsChatSession.java
  class StepsChatSession (line 7) | @Data

FILE: src/main/java/code/handler/steps/StepsChatSessionBuilder.java
  class StepsChatSessionBuilder (line 6) | public class StepsChatSessionBuilder {
    method StepsChatSessionBuilder (line 9) | private StepsChatSessionBuilder(StepsChatSession session) {
    method clone (line 13) | public static StepsChatSessionBuilder clone(StepsChatSession session) {
    method create (line 16) | public static StepsChatSessionBuilder create(CallbackQuery callbackQue...
    method create (line 27) | public static StepsChatSessionBuilder create(Message message) {
    method setText (line 41) | public StepsChatSessionBuilder setText(String text) {
    method setText (line 46) | public StepsChatSessionBuilder setText(String[] arguments) {
    method build (line 51) | public StepsChatSession build() {

FILE: src/main/java/code/handler/steps/StepsHandler.java
  class StepsHandler (line 13) | @Getter
    method StepsHandler (line 33) | private StepsHandler() {
    method bindCommand (line 36) | public StepsHandler bindCommand(Command[] commands) {
    method build (line 44) | public static StepsHandler build(boolean debug, StepErrorApi errorApi,...
    method build (line 48) | public static StepsHandler build(boolean debug, StepErrorApi errorApi,...
    method hasInit (line 59) | public boolean hasInit(StepsChatSession stepsChatSession) {
    method isInit (line 62) | public boolean isInit() {
    method init (line 66) | public void init(StepsChatSession stepsChatSession) {
    method next (line 112) | public void next(StepsChatSession stepsChatSession) {
    method step (line 116) | public StepExecuteResult step(StepsChatSession stepsChatSession) {
    method exit (line 167) | public void exit(StepsChatSession stepsChatSession) {

FILE: src/main/java/code/handler/steps/StepsRegisterCenter.java
  class StepsRegisterCenter (line 8) | @Slf4j
    method register (line 15) | public static void register(String cmd, StepsHandler handler) {
    method getRegister (line 19) | public static StepsHandler getRegister(String cmd) {
    method getRegisterList (line 22) | public static Collection<StepsHandler> getRegisterList() {
    method priority (line 26) | public synchronized static void priority(StepsChatSession stepsChatSes...
    method finish (line 33) | public synchronized static void finish(StepsChatSession stepsChatSessi...
    method getPriority (line 36) | public static StepsHandler getPriority(StepsChatSession stepsChatSessi...

FILE: src/main/java/code/handler/store/ChatButtonsStore.java
  class ChatButtonsStore (line 9) | public class ChatButtonsStore {
    method set (line 13) | public static void set(String chatButtons) {
    method verify (line 18) | public static Optional<ChatButtonsToInlineKeyboardButtons> verify(Stri...
    method get (line 22) | public static Optional<ChatButtonsToInlineKeyboardButtons> get() {
    class ChatButtonsToInlineKeyboardButtons (line 26) | @Data
      method getButtons (line 32) | public Optional<List<InlineKeyboardButton>> getButtons(String chatId) {
    method chatButtonsToInlineKeyboardButtons (line 44) | private static Optional<ChatButtonsToInlineKeyboardButtons> chatButton...

FILE: src/main/java/code/handler/store/Store.java
  class Store (line 5) | public class Store {
    method init (line 7) | public static void init() {

FILE: src/main/java/code/handler/store/WebhookStore.java
  class WebhookStore (line 11) | public class WebhookStore {
    class Webhook (line 13) | @Data
    class WebhookRequest (line 18) | @Data
    method verify (line 26) | public static boolean verify(String webhookJson) {
    method get (line 55) | public static Optional<Webhook> get(String webhookJson) {

FILE: src/main/java/code/repository/I18nTableRepository.java
  class I18nTableRepository (line 11) | @Slf4j
    method I18nTableRepository (line 14) | public I18nTableRepository() {
    method getCreateTableSql (line 18) | @Override
    method selectI18nAlias (line 23) | public String selectI18nAlias(String chatId) {
    method save (line 37) | public synchronized boolean save(String chatId, String i18nAlias) {

FILE: src/main/java/code/repository/MonitorTableRepository.java
  class MonitorTableRepository (line 10) | @Slf4j
    method MonitorTableRepository (line 13) | public MonitorTableRepository() {
    method selectOne (line 17) | public MonitorTableEntity selectOne(String id, String chatId) {
    method selectCountByName (line 24) | public Integer selectCountByName(String chatId, String name) {
    method delete (line 30) | public Boolean delete(String id) {
    method update (line 36) | public Boolean update(MonitorTableEntity entity) {
    method selectList (line 42) | public List<MonitorTableEntity> selectList(String chatId) {

FILE: src/main/java/code/repository/SentRecordTableRepository.java
  class SentRecordTableRepository (line 8) | @Slf4j
    method SentRecordTableRepository (line 11) | public SentRecordTableRepository() {
    method save (line 15) | public void save(SentRecordTableEntity entity) {
    method delete (line 24) | public Boolean delete(String name, String chatId) {
    method exists (line 31) | public Boolean exists(String name, String chatId) {
    method exists (line 34) | public Boolean exists(String uri, String name, String chatId) {

FILE: src/main/java/code/repository/WebhookTableRepository.java
  class WebhookTableRepository (line 10) | @Slf4j
    method WebhookTableRepository (line 13) | public WebhookTableRepository() {
    method selectOne (line 17) | public WebhookTableEntity selectOne(String chatId) {
    method selectList (line 23) | public List<WebhookTableEntity> selectList(String chatId) {
    method save (line 29) | public synchronized Boolean save(WebhookTableEntity entity) {
    method delete (line 40) | public Boolean delete(String chatId) {

FILE: src/main/java/code/repository/base/SqlBuilder.java
  class SqlBuilder (line 11) | @Slf4j
    method getTableName (line 14) | public static String getTableName(TableEntity tableEntity) {
    method getTableName (line 17) | public static String getTableName(Class<? extends TableEntity> tableCl...
    method getNameList (line 21) | public static List<TableField> getNameList(Class<? extends TableEntity...
    method buildCreateTableSql (line 33) | public static String buildCreateTableSql(Class<? extends TableEntity> ...
    method buildAlterTableAddColumnNameSql (line 49) | public static String buildAlterTableAddColumnNameSql(String tableName,...
    method buildFieldValueSql (line 53) | private static String buildFieldValueSql(TableEntity tableEntity, Stri...
    method buildFieldSql (line 80) | public static String buildFieldSql(Class<? extends TableEntity> tableC...
    method buildFieldSql (line 92) | public static String buildFieldSql(TableEntity tableEntity, String pre...
    method buildWhereSql (line 106) | public static String buildWhereSql(TableEntity tableEntity) {
    method buildSelectSql (line 110) | public static String buildSelectSql(Class<? extends TableEntity> table...
    method buildSelectSql (line 116) | public static String buildSelectSql(TableEntity tableEntity) {
    method buildSelectSql (line 123) | public static String buildSelectSql(TableEntity tableEntity, int page,...
    method buildDeleteSql (line 131) | public static String buildDeleteSql(TableEntity where) {
    method buildSelectCountSql (line 138) | public static String buildSelectCountSql(Class<? extends TableEntity> ...
    method buildSelectCountSql (line 145) | public static String buildSelectCountSql(TableEntity where) {
    method buildInsertSql (line 152) | public static String buildInsertSql(TableEntity tableEntity, boolean i...
    method buildUpdateSql (line 187) | public static String buildUpdateSql(TableEntity tableEntity, TableEnti...

FILE: src/main/java/code/repository/base/TableEntity.java
  type TableEntity (line 3) | public interface TableEntity {

FILE: src/main/java/code/repository/base/TableRepository.java
  class TableRepository (line 19) | @Slf4j
    method TableRepository (line 26) | public TableRepository(String dbPath, String tableName) {
    method TableRepository (line 31) | public TableRepository(String dbPath, String tableName, boolean checkC...
    method TableRepository (line 38) | public TableRepository(String dbPath, boolean checkColumn) {
    method createTableHandle (line 46) | private void createTableHandle() {
    method checkColumnHandle (line 62) | private void checkColumnHandle() {
    method getT (line 86) | private Class<T> getT() {
    method getTableName (line 97) | public String getTableName() {
    method sql (line 100) | public String sql(String sql) {
    method sql (line 103) | public String sql(String sql, String[] args) {
    method execute (line 108) | public Object execute(SqliteUtil.SqliteInterface sqliteInterface) thro...
    method executeWithTryCatch (line 111) | public Object executeWithTryCatch(SqliteUtil.SqliteInterface sqliteInt...
    method delete (line 119) | public Boolean delete(T where) {
    method update (line 131) | public Boolean update(T entity, T where) {
    method insert (line 143) | public Boolean insert(T entity) {
    method getOne (line 155) | public T getOne(ResultSet resultSet) throws SQLException, IllegalAcces...
    method getList (line 174) | public List<T> getList(ResultSet resultSet) throws SQLException, Illeg...
    method selectOne (line 198) | public T selectOne(T where) {
    method selectList (line 211) | public List<T> selectList() {
    method selectList (line 224) | public List<T> selectList(T where) {
    method page (line 237) | public PageEntity page(T where, int page, int current, String orderBy) {
    method selectCount (line 255) | public Integer selectCount() {
    method selectCount (line 268) | public Integer selectCount(T where) {
    method exist (line 281) | public Boolean exist(String field, String value) {
    method getCreateTableSql (line 298) | public String getCreateTableSql() {

FILE: src/main/java/code/util/BytesUtil.java
  class BytesUtil (line 5) | public class BytesUtil {
    method divide (line 11) | private static BigDecimal divide(long size, int unit) {
    method toDisplayStr (line 15) | public static String toDisplayStr(long size) {

FILE: src/main/java/code/util/DownloadUtil.java
  class DownloadUtil (line 17) | @Slf4j
    method download (line 20) | public static InputStream download(RequestProxyConfig requestProxyConf...
    method download (line 35) | public static boolean download(RequestProxyConfig requestProxyConfig, ...
    method download (line 50) | public static boolean download(RequestProxyConfig requestProxyConfig, ...

FILE: src/main/java/code/util/ExceptionUtil.java
  class ExceptionUtil (line 7) | public class ExceptionUtil {
    method getStackTraceWithCustomInfoToStr (line 9) | public static String getStackTraceWithCustomInfoToStr(Exception e, Str...
    method getStackTraceWithCustomInfoToStr (line 13) | public static String getStackTraceWithCustomInfoToStr(Exception e) {
    method getStackTraceToStr (line 17) | public static String getStackTraceToStr(Exception e) {

FILE: src/main/java/code/util/GithubUtil.java
  class GithubUtil (line 16) | @Slf4j
    class LatestReleaseResponse (line 21) | @Data
    class LatestReleaseAsset (line 30) | @Data
    method getLatestRelease (line 36) | public static LatestReleaseResponse getLatestRelease(RequestProxyConfi...

FILE: src/main/java/code/util/ProgramUtil.java
  class ProgramUtil (line 9) | @Slf4j
    method restart (line 12) | public static void restart(String processName) {

FILE: src/main/java/code/util/RssUtil.java
  class RssUtil (line 17) | @Slf4j
    method getFeed (line 20) | public static SyndFeed getFeed(RequestProxyConfig proxyConfig, String ...

FILE: src/main/java/code/util/Snowflake.java
  class Snowflake (line 15) | public class Snowflake {
    method Snowflake (line 35) | public Snowflake(long nodeId, long customEpoch) {
    method Snowflake (line 44) | public Snowflake(long nodeId) {
    method Snowflake (line 49) | public Snowflake() {
    method nextIdToStr (line 54) | public synchronized String nextIdToStr() {
    method nextId (line 58) | public synchronized long nextId() {
    method timestamp (line 87) | private long timestamp() {
    method waitNextMillis (line 92) | private long waitNextMillis(long currentTimestamp) {
    method createNodeId (line 99) | private long createNodeId() {
    method parse (line 121) | public long[] parse(long id) {
    method toString (line 132) | @Override

FILE: src/main/java/code/util/SqliteUtil.java
  class SqliteUtil (line 11) | @Slf4j
    type SqliteInterface (line 14) | public interface SqliteInterface {
      method execute (line 15) | Object execute(Statement statement) throws SQLException, IllegalAcce...
    method execute (line 18) | public static Object execute(String dbPath, SqliteInterface sqliteInte...

FILE: src/main/java/code/util/TelegraphUtil.java
  class TelegraphUtil (line 25) | @Slf4j
    class TelegraphNode (line 28) | @Data
    class SaveResponse (line 35) | @Data
    method save (line 42) | public static SaveResponse save(RequestProxyConfig proxyConfig, String...
    method getTelegraphContent (line 88) | private static String getTelegraphContent(String html, String appendHt...
    method buildNote (line 100) | private static Object buildNote(Node node) {

FILE: src/main/java/code/util/translate/MicrosoftTranslateHandle.java
  class MicrosoftTranslateHandle (line 23) | @Slf4j
    method hasAuth (line 25) | @Override
    method auth (line 30) | @Override
    method translate (line 35) | @Override
    method encode (line 55) | private String encode(String text) {

FILE: src/main/java/code/util/translate/Translate.java
  class Translate (line 11) | @Slf4j
    method translateAll (line 20) | public static List<String> translateAll(String text, String from, Stri...
    method translate (line 40) | public static String translate(String text, String from, String to) {

FILE: src/main/java/code/util/translate/YoudaoTranslateHandle.java
  class YoudaoTranslateHandle (line 21) | @Slf4j
    method hasAuth (line 23) | @Override
    method auth (line 27) | @Override
    method translate (line 40) | @Override
    method getInput (line 76) | private static String getInput(String input) {

FILE: src/main/java/code/util/translate/base/TranslateAPI.java
  type TranslateAPI (line 3) | public interface TranslateAPI {
    method hasAuth (line 4) | boolean hasAuth();
    method auth (line 5) | TranslateAuth auth();
    method translate (line 6) | String translate(String text, String from, String to);

FILE: src/main/java/code/util/translate/base/TranslateAuth.java
  class TranslateAuth (line 5) | @Data
Condensed preview — 77 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (302K chars).
[
  {
    "path": ".gitignore",
    "chars": 436,
    "preview": "HELP.md\ntarget/\n!.mvn/wrapper/maven-wrapper.jar\n!**/src/main/**/target/\n!**/src/test/**/target/\n\n### STS ###\n.apt_genera"
  },
  {
    "path": "Dockerfile",
    "chars": 586,
    "preview": "FROM openjdk:8-jdk-alpine\nENV TZ=Asia/Shanghai\nRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/tim"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2023 kylelin1998\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 3229,
    "preview": "### 简体中文 | [English](./README_en.md)\n\n![License](https://img.shields.io/badge/license-MIT-green)\n[![release](https://img"
  },
  {
    "path": "README_en.md",
    "chars": 5343,
    "preview": "### [简体中文](./README.md) | English\n\n![License](https://img.shields.io/badge/license-MIT-green)\n[![release](https://img.sh"
  },
  {
    "path": "docker-push.sh",
    "chars": 207,
    "preview": "#!/bin/sh\n\nsudo apt install -y qemu-user-static binfmt-support\ndocker buildx create --use\ndocker buildx ls\n\ndocker build"
  },
  {
    "path": "pom.xml",
    "chars": 5900,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "run.sh",
    "chars": 400,
    "preview": "#!/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  "
  },
  {
    "path": "src/main/java/code/Main.java",
    "chars": 3707,
    "preview": "package code;\n\nimport code.config.Config;\nimport code.config.ConfigSettings;\nimport code.config.I18nEnum;\nimport code.co"
  },
  {
    "path": "src/main/java/code/config/Config.java",
    "chars": 12007,
    "preview": "package code.config;\n\nimport code.eneity.MonitorTableEntity;\nimport code.eneity.YesOrNoEnum;\nimport code.util.ExceptionU"
  },
  {
    "path": "src/main/java/code/config/ConfigField.java",
    "chars": 324,
    "preview": "package code.config;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.a"
  },
  {
    "path": "src/main/java/code/config/ConfigSettings.java",
    "chars": 1856,
    "preview": "package code.config;\n\nimport com.alibaba.fastjson2.annotation.JSONField;\nimport lombok.Data;\n\nimport java.util.List;\n\n@D"
  },
  {
    "path": "src/main/java/code/config/DisplayConfigAnnotation.java",
    "chars": 334,
    "preview": "package code.config;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.a"
  },
  {
    "path": "src/main/java/code/config/ExecutorsConfig.java",
    "chars": 785,
    "preview": "package code.config;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;"
  },
  {
    "path": "src/main/java/code/config/I18nConfig.java",
    "chars": 2146,
    "preview": "package code.config;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.IOException;\nimport java.io.InputStre"
  },
  {
    "path": "src/main/java/code/config/I18nEnum.java",
    "chars": 4433,
    "preview": "package code.config;\n\nimport lombok.Getter;\n\n@Getter\npublic enum I18nEnum {\n\n    BotStartSucceed(\"bot_start_succeed\"),\n "
  },
  {
    "path": "src/main/java/code/config/I18nLocaleEnum.java",
    "chars": 739,
    "preview": "package code.config;\n\nimport lombok.Getter;\n\nimport java.util.Locale;\n\n@Getter\npublic enum I18nLocaleEnum {\n    ZH_CN(Lo"
  },
  {
    "path": "src/main/java/code/config/MonitorConfigSettings.java",
    "chars": 980,
    "preview": "package code.config;\n\nimport lombok.Data;\n\n@Data\npublic class MonitorConfigSettings {\n\n    @DisplayConfigAnnotation(i18n"
  },
  {
    "path": "src/main/java/code/config/MonitorExecutorsConfig.java",
    "chars": 794,
    "preview": "package code.config;\n\nimport lombok.extern.slf4j.Slf4j;\nimport org.glassfish.jersey.internal.guava.ThreadFactoryBuilder;"
  },
  {
    "path": "src/main/java/code/config/ProxyTypeEnum.java",
    "chars": 384,
    "preview": "package code.config;\n\nimport lombok.Getter;\n\n@Getter\npublic enum ProxyTypeEnum {\n\n    NotOpen(0, \"Not Open\"),\n    HttpPr"
  },
  {
    "path": "src/main/java/code/config/RequestProxyConfig.java",
    "chars": 1679,
    "preview": "package code.config;\n\nimport kong.unirest.HttpRequest;\nimport org.apache.hc.client5.http.fluent.Request;\nimport org.apac"
  },
  {
    "path": "src/main/java/code/config/TableEnum.java",
    "chars": 247,
    "preview": "package code.config;\n\nimport lombok.Getter;\n\n@Getter\npublic enum TableEnum {\n\n    SentRecordTable(\"sent_record_table\"),\n"
  },
  {
    "path": "src/main/java/code/eneity/MonitorTableEntity.java",
    "chars": 2417,
    "preview": "package code.eneity;\n\nimport code.config.DisplayConfigAnnotation;\nimport code.repository.base.TableEntity;\nimport code.r"
  },
  {
    "path": "src/main/java/code/eneity/PageEntity.java",
    "chars": 814,
    "preview": "package code.eneity;\n\nimport lombok.Data;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Data\npublic class PageEn"
  },
  {
    "path": "src/main/java/code/eneity/SentRecordTableEntity.java",
    "chars": 715,
    "preview": "package code.eneity;\n\nimport code.repository.base.TableEntity;\nimport code.repository.base.TableField;\nimport code.repos"
  },
  {
    "path": "src/main/java/code/eneity/WebhookTableEntity.java",
    "chars": 548,
    "preview": "package code.eneity;\n\nimport code.repository.base.TableEntity;\nimport code.repository.base.TableField;\nimport code.repos"
  },
  {
    "path": "src/main/java/code/eneity/YesOrNoEnum.java",
    "chars": 908,
    "preview": "package code.eneity;\n\nimport lombok.Getter;\n\nimport java.util.Optional;\n\n@Getter\npublic enum YesOrNoEnum {\n\n    Yes(1, t"
  },
  {
    "path": "src/main/java/code/handler/Command.java",
    "chars": 1157,
    "preview": "package code.handler;\n\nimport lombok.Getter;\n\n@Getter\npublic enum Command {\n\n    Start(\"start\"),\n    Help(\"help\"),\n    C"
  },
  {
    "path": "src/main/java/code/handler/CommandsHandler.java",
    "chars": 2606,
    "preview": "package code.handler;\n\nimport code.handler.message.CallbackBuilder;\nimport code.handler.steps.StepsChatSession;\nimport c"
  },
  {
    "path": "src/main/java/code/handler/Handler.java",
    "chars": 106453,
    "preview": "package code.handler;\n\nimport code.config.*;\nimport code.eneity.*;\nimport code.handler.message.CallbackBuilder;\nimport c"
  },
  {
    "path": "src/main/java/code/handler/I18nHandle.java",
    "chars": 1746,
    "preview": "package code.handler;\n\nimport code.config.I18nConfig;\nimport code.config.I18nEnum;\nimport code.config.I18nLocaleEnum;\nim"
  },
  {
    "path": "src/main/java/code/handler/MessageHandle.java",
    "chars": 12305,
    "preview": "package code.handler;\n\nimport code.util.ExceptionUtil;\nimport com.alibaba.fastjson2.JSON;\nimport lombok.Data;\nimport lom"
  },
  {
    "path": "src/main/java/code/handler/StepsCenter.java",
    "chars": 3848,
    "preview": "package code.handler;\n\nimport code.config.ExecutorsConfig;\nimport code.handler.message.CallbackBuilder;\nimport code.hand"
  },
  {
    "path": "src/main/java/code/handler/message/CallbackBuilder.java",
    "chars": 1592,
    "preview": "package code.handler.message;\n\nimport code.handler.Command;\nimport code.handler.steps.StepsChatSession;\nimport code.util"
  },
  {
    "path": "src/main/java/code/handler/message/InlineKeyboardButtonBuilder.java",
    "chars": 1567,
    "preview": "package code.handler.message;\n\nimport org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardBut"
  },
  {
    "path": "src/main/java/code/handler/message/InlineKeyboardButtonListBuilder.java",
    "chars": 1665,
    "preview": "package code.handler.message;\n\nimport code.eneity.PageEntity;\nimport code.handler.Command;\nimport code.handler.steps.Ste"
  },
  {
    "path": "src/main/java/code/handler/message/MessageHandle.java",
    "chars": 11392,
    "preview": "package code.handler.message;\n\nimport code.util.ExceptionUtil;\nimport com.alibaba.fastjson2.JSON;\nimport lombok.Data;\nim"
  },
  {
    "path": "src/main/java/code/handler/steps/StepErrorApi.java",
    "chars": 132,
    "preview": "package code.handler.steps;\n\npublic interface StepErrorApi {\n\n    void callback(Exception e, StepsChatSession stepsChatS"
  },
  {
    "path": "src/main/java/code/handler/steps/StepExecuteResult.java",
    "chars": 586,
    "preview": "package code.handler.steps;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\n@Data\n@AllArgsConstructor\npublic cla"
  },
  {
    "path": "src/main/java/code/handler/steps/StepHandleApi.java",
    "chars": 230,
    "preview": "package code.handler.steps;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface StepHandleApi {\n\n    StepRes"
  },
  {
    "path": "src/main/java/code/handler/steps/StepResult.java",
    "chars": 760,
    "preview": "package code.handler.steps;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\n\n@Data\n@AllArgsConstructor\npublic cla"
  },
  {
    "path": "src/main/java/code/handler/steps/StepsBuilder.java",
    "chars": 1076,
    "preview": "package code.handler.steps;\n\nimport code.handler.Command;\n\npublic class StepsBuilder {\n\n    private Command[] commands;\n"
  },
  {
    "path": "src/main/java/code/handler/steps/StepsChatSession.java",
    "chars": 435,
    "preview": "package code.handler.steps;\n\nimport lombok.Data;\nimport org.telegram.telegrambots.meta.api.objects.CallbackQuery;\nimport"
  },
  {
    "path": "src/main/java/code/handler/steps/StepsChatSessionBuilder.java",
    "chars": 1936,
    "preview": "package code.handler.steps;\n\nimport org.telegram.telegrambots.meta.api.objects.CallbackQuery;\nimport org.telegram.telegr"
  },
  {
    "path": "src/main/java/code/handler/steps/StepsHandler.java",
    "chars": 6405,
    "preview": "package code.handler.steps;\n\nimport code.handler.Command;\nimport com.alibaba.fastjson2.JSON;\nimport lombok.Getter;\nimpor"
  },
  {
    "path": "src/main/java/code/handler/steps/StepsRegisterCenter.java",
    "chars": 1289,
    "preview": "package code.handler.steps;\n\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.util.*;\nimport java.util.concurrent.Concurre"
  },
  {
    "path": "src/main/java/code/handler/store/ChatButtonsStore.java",
    "chars": 3900,
    "preview": "package code.handler.store;\n\nimport lombok.Data;\nimport org.apache.commons.lang3.StringUtils;\nimport org.telegram.telegr"
  },
  {
    "path": "src/main/java/code/handler/store/Store.java",
    "chars": 192,
    "preview": "package code.handler.store;\n\nimport static code.Main.GlobalConfig;\n\npublic class Store {\n\n    public static void init() "
  },
  {
    "path": "src/main/java/code/handler/store/WebhookStore.java",
    "chars": 1749,
    "preview": "package code.handler.store;\n\nimport com.alibaba.fastjson2.JSON;\nimport lombok.Data;\nimport org.apache.commons.lang3.Stri"
  },
  {
    "path": "src/main/java/code/repository/I18nTableRepository.java",
    "chars": 1775,
    "preview": "package code.repository;\n\nimport code.config.Config;\nimport code.config.TableEnum;\nimport code.repository.base.TableRepo"
  },
  {
    "path": "src/main/java/code/repository/MonitorTableRepository.java",
    "chars": 1416,
    "preview": "package code.repository;\n\nimport code.config.Config;\nimport code.eneity.MonitorTableEntity;\nimport code.repository.base."
  },
  {
    "path": "src/main/java/code/repository/SentRecordTableRepository.java",
    "chars": 1346,
    "preview": "package code.repository;\n\nimport code.config.Config;\nimport code.eneity.SentRecordTableEntity;\nimport code.repository.ba"
  },
  {
    "path": "src/main/java/code/repository/WebhookTableRepository.java",
    "chars": 1346,
    "preview": "package code.repository;\n\nimport code.config.Config;\nimport code.eneity.WebhookTableEntity;\nimport code.repository.base."
  },
  {
    "path": "src/main/java/code/repository/base/SqlBuilder.java",
    "chars": 7346,
    "preview": "package code.repository.base;\n\nimport code.util.ExceptionUtil;\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.lang.refle"
  },
  {
    "path": "src/main/java/code/repository/base/TableEntity.java",
    "chars": 64,
    "preview": "package code.repository.base;\n\npublic interface TableEntity {\n}\n"
  },
  {
    "path": "src/main/java/code/repository/base/TableField.java",
    "chars": 345,
    "preview": "package code.repository.base;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport ja"
  },
  {
    "path": "src/main/java/code/repository/base/TableName.java",
    "chars": 308,
    "preview": "package code.repository.base;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport ja"
  },
  {
    "path": "src/main/java/code/repository/base/TableRepository.java",
    "chars": 10461,
    "preview": "package code.repository.base;\n\nimport code.eneity.PageEntity;\nimport code.util.ExceptionUtil;\nimport code.util.SqliteUti"
  },
  {
    "path": "src/main/java/code/util/BytesUtil.java",
    "chars": 790,
    "preview": "package code.util;\n\nimport java.math.BigDecimal;\n\npublic class BytesUtil {\n\n    private final static int KB = 1024;\n    "
  },
  {
    "path": "src/main/java/code/util/DownloadUtil.java",
    "chars": 2419,
    "preview": "package code.util;\n\nimport code.config.RequestProxyConfig;\nimport kong.unirest.GetRequest;\nimport kong.unirest.HttpRespo"
  },
  {
    "path": "src/main/java/code/util/ExceptionUtil.java",
    "chars": 1348,
    "preview": "package code.util;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\npublic class E"
  },
  {
    "path": "src/main/java/code/util/GithubUtil.java",
    "chars": 2150,
    "preview": "package code.util;\n\nimport code.config.RequestProxyConfig;\nimport com.alibaba.fastjson2.JSON;\nimport com.alibaba.fastjso"
  },
  {
    "path": "src/main/java/code/util/ProgramUtil.java",
    "chars": 643,
    "preview": "package code.util;\n\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.IOException;\nimport java.util.stream.Collectors;\ni"
  },
  {
    "path": "src/main/java/code/util/RssUtil.java",
    "chars": 1393,
    "preview": "package code.util;\n\nimport code.config.RequestProxyConfig;\nimport com.rometools.rome.feed.synd.SyndFeed;\nimport com.rome"
  },
  {
    "path": "src/main/java/code/util/Snowflake.java",
    "chars": 4665,
    "preview": "package code.util;\n\nimport java.net.NetworkInterface;\nimport java.security.SecureRandom;\nimport java.time.Instant;\nimpor"
  },
  {
    "path": "src/main/java/code/util/SqliteUtil.java",
    "chars": 1124,
    "preview": "package code.util;\n\nimport lombok.extern.slf4j.Slf4j;\n\nimport java.io.File;\nimport java.sql.Connection;\nimport java.sql."
  },
  {
    "path": "src/main/java/code/util/TelegraphUtil.java",
    "chars": 5324,
    "preview": "package code.util;\n\nimport code.config.RequestProxyConfig;\nimport com.alibaba.fastjson2.JSON;\nimport com.alibaba.fastjso"
  },
  {
    "path": "src/main/java/code/util/translate/MicrosoftTranslateHandle.java",
    "chars": 2071,
    "preview": "package code.util.translate;\n\nimport code.util.translate.base.TranslateAPI;\nimport code.util.translate.base.TranslateAut"
  },
  {
    "path": "src/main/java/code/util/translate/Translate.java",
    "chars": 1815,
    "preview": "package code.util.translate;\n\nimport code.util.ExceptionUtil;\nimport code.util.translate.base.TranslateAPI;\nimport lombo"
  },
  {
    "path": "src/main/java/code/util/translate/YoudaoTranslateHandle.java",
    "chars": 3220,
    "preview": "package code.util.translate;\n\nimport code.util.translate.base.TranslateAPI;\nimport code.util.translate.base.TranslateAut"
  },
  {
    "path": "src/main/java/code/util/translate/base/TranslateAPI.java",
    "chars": 177,
    "preview": "package code.util.translate.base;\n\npublic interface TranslateAPI {\n    boolean hasAuth();\n    TranslateAuth auth();\n    "
  },
  {
    "path": "src/main/java/code/util/translate/base/TranslateAuth.java",
    "chars": 144,
    "preview": "package code.util.translate.base;\n\nimport lombok.Data;\n\n@Data\npublic class TranslateAuth {\n    private String key;\n    p"
  },
  {
    "path": "src/main/resources/code/config/telegraph.html",
    "chars": 513,
    "preview": "<div>\n  <hr/>\n  <div>\n    本文由\n    <a href=\"https://github.com/kylelin1998/RssMonitorTelegramBot\"> RssMonitorTelegramBot "
  },
  {
    "path": "src/main/resources/code/config/webhook.json",
    "chars": 696,
    "preview": "{\n  \"enable\":false,\n  \"list\":[\n    {\n      \"url\":\"https://open.larksuite.com/open-apis/bot/v2/hook/...\",\n      \"method\":"
  },
  {
    "path": "src/main/resources/i18n/i18n_en.properties",
    "chars": 6874,
    "preview": "bot_start_succeed=Bot program starting succeeded\\uFF01\nhelp_text=Welcome to using this bot!\n\ninvalid_command=Invalid com"
  },
  {
    "path": "src/main/resources/i18n/i18n_zh_CN.properties",
    "chars": 3695,
    "preview": "refresh=刷新\nbot_start_succeed=机器人启动成功!\nhelp_text=欢迎使用本机器人!\n\ninvalid_command=无效命令, 您不是管理员无法操作此命令\nmonitor_list=计划名称: %s, 是否"
  },
  {
    "path": "src/main/resources/logback.xml",
    "chars": 3350,
    "preview": "<configuration scan=\"true\" scanPeriod=\"60 seconds\" debug=\"false\">\n\n    <shutdownHook class=\"ch.qos.logback.core.hook.Del"
  }
]

About this extraction

This page contains the full source code of the kylelin1998/RssMonitorTelegramBot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 77 files (276.2 KB), approximately 60.5k tokens, and a symbol index with 343 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!