` tag takes up a whole line.
+- **Unnecessary image fallback**: The bot will no longer fall back all images to file if at least one image needs to be sent as a file.
+- **Web retry**: Added a need-to-retry exception.
+- **Webpage decode error**: `cchardet` is not robust enough to handle all feeds. Now the bot will try to detect the encoding of the webpage according to the XML encoding declaration. Also, if `cchardet` returns a not-supported encoding, the bot will try to decode the webpage using UTF-8. Any character that cannot be decoded will be replaced with `�`.
+- **Extracting image dimension from Exif thumbnail**: Some images may contain a thumbnail in the Exif data. The bot will now avoid extracting the dimension from the thumbnail.
+- **Minor bug fixes**
+
## Custom format, new l10n, improved media fallback, and more (v2.1.0)
Official public bot [@RSStT_Bot](https://t.me/RSStT_Bot) is always using the `dev` branch. If you are using it, you may have noticed the new features. Since new commands are added, please use `/lang` command once again and select your language to let the bot update your command list.
diff --git a/docs/CHANGELOG.zh.md b/docs/CHANGELOG.zh.md
index e97899f994..28be23c89e 100644
--- a/docs/CHANGELOG.zh.md
+++ b/docs/CHANGELOG.zh.md
@@ -1,5 +1,47 @@
# 更新日志
+## 频道远程管理、更多格式化选项和更多 (v2.2.0)
+
+### 新特性
+
+#### 亮点
+
+- **频道/群组远程管理**: 现在你可以在和 bot 的私聊里管理你的频道/群组的订阅。支持使用大部分命令。只需以类似于 `/sub @username https://exmaple.com` 或 `/sub -10010000000000 https://exmaple.com` 的格式发送命令。(`@username` 是频道/群组的用户名, `@` 是不可缺少的; `-10010000000000` 是频道/群组的 ID, 必须以 `-100` 开头)
+- **更多格式化选项**:
+ - **媒体**: 你可以选择让 Telegram 消息不带任何媒体 (只有文字)。也可以选择让 Telegram 消息只带有媒体和元数据 (没有内容);只有当有媒体附加到文章时才可如此,否则,它们仍会带有内容。
+ - **链接预览**: 现在你可以强制关闭 Telegram 消息的链接预览。
+ - **来源**: 更多来源格式化选项。阅读 [格式设置指南](formatting-settings.md) 以获取详细信息。
+- **部署到 Heroku**: Bot 现在可以部署到 Heroku。阅读 [部署指南](deployment-guide.md) 以获取详细信息。
+- **用户权限管理**: Bot 管理员现在可以使用 `/user_info` 命令来管理 bot 用户 (用户/频道/群组) 的权限。这样管理员就可以设置谁可以使用 bot,即使禁用了多用户模式。
+
+#### 其他新特性
+
+- **单列表格支持**: 先前,所有 HTML 表格都被丢弃。现在,只含有单列的表格将被渲染为多行文本。请注意,多列表格仍会被丢弃。
+- **适用于 [lizhi.fm](https://www.lizhi.fm) 的音频回落**: 如果高音质音频超出了文件大小限制,自动回落到更低音质的音频。仅适用于 [lizhi.fm](https://www.lizhi.fm)。
+
+### 增强
+
+- **Telegraph 文章美化**: Telegraph 文章的格式美化。除此之外,所有图片和视频都使用媒体中继服务器来规避防盗链。
+- **非 HTTP 超链接**: Telegram 不支持非 HTTP 超链接。Bot 会自动将它们转换为裸 URL。
+- **Enclosure 清理**: 如果一个附件包含非 HTTP URL 且文章中的链接已包含它,它将被移除。
+- **懒惰的媒体验证器**: 媒体验证器现在是懒惰的。它只有在一篇文章可能作为 Telegram 消息发送时才会运行。这将减少 CPU 使用量和网络流量。
+- **增强的图片尺寸提取**: 图片尺寸提取现在更快速和灵活。如果提取失败,bot 会尝试使用 [images.weserv.nl](https://images.weserv.nl) 来提取。
+- **本地化更新**: 土耳其语 (Türkçe) 本地化文件已更新。 (英语 / English 、简体中文 、繁体中文 / 正體中文 和 粤语 / 廣東話 永远是最新的。)
+- **改进的 Docker 构建缓存**: 如果依赖未改变,无需再重新拉取完整的 Docker 镜像。只需使用缓存的依赖并拉取最新的源码。
+- **从 Railway.app 的环境变量中提取 git 信息**: Railway.app 上的部署现在可以识别 git 信息。
+- **次要的增强**
+
+### Bug 修复
+
+- **Python 3.7 兼容性**: 上一个版本破坏了与 Python 3.8 的兼容性,现在已经修复了。请注意,仅支持 x86 和 amd64 架构。对于 arm64,最小的 Python 版本要求是 3.8。
+- **EntitiesTooLongError**: 含有大量文本超链接的文章可引起 Telegram API 抛出这个错误。现在 bot 会尝试通过更激进的文章分割来修复这个错误。
+- `
`: Bot 现在会确保每个 `
` 标签都占据一整行。
+- **不必要的图片回落**: 如果至少有一张图片需要作为文件发送,bot 不再会将所有图片都回落成文件。
+- **网络重试**: 增加了一个需要进行重试的异常。
+- **网页解码错误**: `cchardet` 并不足够健壮以处理所有源。现在 bot 会尝试从 XML 编码声明中探测网页编码。同时,如果 `cchardet` 返回了不支持的编码,bot 会尝试使用 UTF-8 来解码网页。任何无法被解码的字符都会被替换为 `�`。
+- **从 Exif 缩略图中提取图片尺寸**: 一些图片在 Exif 数据中含有缩略图。Bot 现在会避免从缩略图中提取尺寸。
+- **次要的 bug 修复**
+
## 自定义格式、新本地化、改进的媒体回落和更多 (v2.1.0)
官方的公开 bot [@RSStT_Bot](https://t.me/RSStT_Bot) 一直使用 `dev` 分支。如果你正在使用它,你可能已经注意到新功能了。由于添加了新的命令,请使用 `/lang` 命令再一次选择你的语言,让 bot 更新你的命令列表。
diff --git a/docs/advanced-settings.md b/docs/advanced-settings.md
index d145dde29c..d6fd5298e4 100644
--- a/docs/advanced-settings.md
+++ b/docs/advanced-settings.md
@@ -33,7 +33,7 @@
| `R_PROXY` | Proxy used to fetch feeds [^3] | `socks5://172.17.0.1:1080` | |
| `PROXY_BYPASS_PRIVATE` | Pypass proxy for private IPs or not? | `1` | `0` |
| `PROXY_BYPASS_DOMAINS` | Pypass proxy for listed domains | `example.com;example.net` [^2] | |
-| `USER_AGENT` | User-Agent | `Mozilla/5.0` | `RSStT/2.0 RSS Reader` |
+| `USER_AGENT` | User-Agent | `Mozilla/5.0` | `RSStT/2.2 RSS Reader` |
| `IPV6_PRIOR` | Enforce fetching feeds over IPv6 firstly or not? [^4] | `1` | `0` |
### Misc settings
diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md
index 1a4111ed2c..e391225143 100644
--- a/docs/deployment-guide.md
+++ b/docs/deployment-guide.md
@@ -87,7 +87,7 @@ Turn to [Kaffeine](https://kaffeine.herokuapp.com/), filling your Heroku app nam
## Option 4: Dirty run
-Minimal: Python 3.7+
+Minimal: Python 3.7+ (x86 / amd64), Python 3.8+ (arm64)
Recommended: Python 3.9+
```sh
diff --git a/docs/formatting-settings.md b/docs/formatting-settings.md
index 759f404538..87c42514e8 100644
--- a/docs/formatting-settings.md
+++ b/docs/formatting-settings.md
@@ -37,8 +37,8 @@
- **Feed title and link**: display a link to the post and the feed title. If **style** is **RSStT**, they will be displayed together as a text link at the end of the message. _(note that if the link does not exist, only the feed title will be displayed)_
- **Feed title and link displayed as post title**: display the link and post title together as a text link at the beginning of the message, but not to omit the feed title _(note that if the link does not exist, only the post title will be displayed)_
- **No feed title, link displayed as post title**: display the link and post title together as a text link at the beginning of the message, omitting the feed title _(note that if the link does not exist, fall back to **completely disable**)_
- - **No feed title, text link at the end**: display a text link to the post at the end of the message, just like [`source`](https://git.io/RSStT) _(note that if the link does not exist, fall back to **completely disable**)_
- - **No feed title, bare link at the end**: display a bare link to the post at the end of the message, just like [`https://git.io/RSStT`](https://git.io/RSStT) _(note that if the link does not exist, fall back to **completely disable**)_
+ - **No feed title, hyperlink at the end**: display a text hyperlink to the post at the end of the message, just like [`source`](https://git.io/RSStT) _(note that if the link does not exist, fall back to **completely disable**)_
+ - **No feed title, bare URL at the end**: display a bare URL to the post at the end of the message, just like [`https://git.io/RSStT`](https://git.io/RSStT) _(note that if the link does not exist, fall back to **completely disable**)_
- **Completely disable**: do not display anything about the source
- **Author**:
- **Auto**: display author name if it is not fully contained in the feed title
diff --git a/docs/translation-guide.md b/docs/translation-guide.md
index 900facd363..51f66030bf 100644
--- a/docs/translation-guide.md
+++ b/docs/translation-guide.md
@@ -42,7 +42,7 @@ https://hosted.weblate.org/projects/rss-to-telegram-bot/glossary/
| [it] / Italian / Italiano | [@Alfy] ([@AlfyT96]) | [![it_s]][it_w] |
| [ca] / Catalan / Català | [@maite.guix] | [![ca_s]][ca_w] |
| [tr] / Turkish / Türkçe | [@wiseweb-works] | [![tr_s]][tr_w] |
-| [fr] / French / français | [@Edanas] | [![fr_s]][fr_w] |
+| [fr] / French / français | [@Edanas] | [![fr_s]][fr_w] |
[i18n]: ../src/i18n
diff --git a/src/env.py b/src/env.py
index 15bb663166..707bccc778 100644
--- a/src/env.py
+++ b/src/env.py
@@ -152,7 +152,7 @@ def __list_parser(var: Optional[str]) -> list[str]:
PROXY_BYPASS_PRIVATE: Final = __bool_parser(os.environ.get('PROXY_BYPASS_PRIVATE'))
PROXY_BYPASS_DOMAINS: Final = __list_parser(os.environ.get('PROXY_BYPASS_DOMAINS'))
-USER_AGENT: Final = os.environ.get('USER_AGENT') or 'RSStT/2.1 RSS Reader'
+USER_AGENT: Final = os.environ.get('USER_AGENT') or 'RSStT/2.2 RSS Reader'
IPV6_PRIOR: Final = __bool_parser(os.environ.get('IPV6_PRIOR'))
# ----- img relay server config -----
diff --git a/src/i18n/en.json b/src/i18n/en.json
index be6c253912..a08362c1bd 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -114,10 +114,10 @@
"display_author_0": "Auto",
"display_author_1": "Enable",
"display_via": "Source",
- "display_via_-4": "No feed title, bare link at the end",
+ "display_via_-4": "No feed title, bare URL at the end",
"display_via_-3": "No feed title, link displayed as post title",
"display_via_-2": "Completely disable",
- "display_via_-1": "No feed title, text link at the end",
+ "display_via_-1": "No feed title, hyperlink at the end",
"display_via_0": "Feed title and link",
"display_via_1": "Feed title and link displayed as post title",
"display_title": "Post title",
diff --git a/src/i18n/yue.json b/src/i18n/yue.json
index 3b5b693ad4..442773f383 100644
--- a/src/i18n/yue.json
+++ b/src/i18n/yue.json
@@ -114,10 +114,10 @@
"display_author_0": "自動",
"display_author_1": "開",
"display_via": "來源",
- "display_via_-4": "冇 RSS 標題,裸連結喺最後",
+ "display_via_-4": "冇 RSS 標題,裸 URL 擺喺最後",
"display_via_-3": "冇 RSS 標題,連結展示做文章標題",
"display_via_-2": "閂晒",
- "display_via_-1": "冇 RSS 標題,文字連結喺最後",
+ "display_via_-1": "冇 RSS 標題,超連結擺喺最後",
"display_via_0": "RSS 標題同埋連結",
"display_via_1": "RSS 標題同埋展示做文章標題嘅連結",
"display_title": "文章標題",
diff --git a/src/i18n/zh-Hans.json b/src/i18n/zh-Hans.json
index e5b7576a2b..8a6972b2d6 100644
--- a/src/i18n/zh-Hans.json
+++ b/src/i18n/zh-Hans.json
@@ -114,10 +114,10 @@
"display_author_0": "自动",
"display_author_1": "启用",
"display_via": "来源",
- "display_via_-4": "无源标题,裸链接置于结尾",
+ "display_via_-4": "无源标题,裸 URL 置于结尾",
"display_via_-3": "无源标题,链接显示为文章标题",
"display_via_-2": "完全禁用",
- "display_via_-1": "无源标题,文本链接置于结尾",
+ "display_via_-1": "无源标题,超链接置于结尾",
"display_via_0": "源标题和链接",
"display_via_1": "源标题和显示为文章标题的链接",
"display_title": "文章标题",
diff --git a/src/i18n/zh-Hant.json b/src/i18n/zh-Hant.json
index 3a1827c267..cd00e9721b 100644
--- a/src/i18n/zh-Hant.json
+++ b/src/i18n/zh-Hant.json
@@ -101,7 +101,7 @@
"send_mode_2": "强制 Telegram 消息",
"length_limit": "長度限制 (僅限自動模式)",
"length_limit_unlimited": "無限",
- "link_preview": "鏈接預覽",
+ "link_preview": "連結預覽",
"link_preview_-1": "禁用",
"link_preview_0": "自動",
"link_preview_1": "啟用",
@@ -114,12 +114,12 @@
"display_author_0": "自動",
"display_author_1": "啟用",
"display_via": "來源",
- "display_via_-4": "無源標題,裸鏈接附于尾部",
- "display_via_-3": "無源標題,鏈接顯示爲文章標題",
+ "display_via_-4": "無源標題,裸 URL 附于尾部",
+ "display_via_-3": "無源標題,連結顯示爲文章標題",
"display_via_-2": "完全禁用",
- "display_via_-1": "無源標題,文本鏈接附于尾部",
- "display_via_0": "源標題和鏈接",
- "display_via_1": "源標題和顯示爲文章標題的鏈接",
+ "display_via_-1": "無源標題,超連結附于尾部",
+ "display_via_0": "源標題和連結",
+ "display_via_1": "源標題和顯示爲文章標題的連結",
"display_title": "文章標題",
"display_title_-1": "禁用",
"display_title_0": "自動",