Skip to content

Commit

Permalink
fix(*): stay in topic group even when the "General" topic is closed
Browse files Browse the repository at this point in the history
Signed-off-by: Rongrong <[email protected]>
  • Loading branch information
Rongronggg9 committed Jan 7, 2024
1 parent 986e4ea commit 44d3411
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 34 deletions.
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

- **HTML list support improvement**: Now `<menu>` and `<dir>` are treated the same as `<ul>` (unordered list). In addition, orphan `<li>` (list item) without a valid list parent tag are no longer ignored but treated as an item in an single unordered list.

### Bug fixes

- **Stay in topic group even when the "General" topic is closed**: Now that topic groups are not fully supported, the bot can only send messages in the "General" topic. Previously, the bot would only send an error message to the bot manager if the "General" topic is closed. Now the bot will leave the topic group, without disturbing the bot manager, if the "General" topic is closed. This is a temporary limitation before topic groups are fully supported.

## v2.4.1: Minor enhancements, bug fixes, and Happy New Year!

### Enhancements
Expand Down
4 changes: 4 additions & 0 deletions docs/CHANGELOG.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

- **改进了对 HTML 列表的支持**: 现在,`<menu>``<dir>` 被视为 `<ul>` (无序列表)。此外,没有有效列表父标签的孤立 `<li>` (列表项) 不再被忽略,而是被视为单个无序列表中的一个项目。

### Bug 修复

- **即使 “General” 话题已关闭,也留在话题群组中**: 由于话题群组尚未被完全支持,bot 只能在 “General” 话题中发送消息。先前,如果 “General” 话题被关闭,bot 只会向 bot 管理员发送错误消息。现在,如果 “General” 话题被关闭,bot 将离开话题群组,而不再打扰 bot 管理员。这是在话题群组完全受支持之前的一个临时限制。

## v2.4.1: 次要的增强和 bug 修复,以及新年快乐!

### 增强
Expand Down
2 changes: 2 additions & 0 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ If you want to allow a certain channel/group to use the bot, you should promote
**A**: Once the bot finds itself lacking the permission to send messages (not granted or being blocked), it will immediately unsubscribe all subscriptions in this chat. Meanwhile, if this chat is a channel or group and the bot is still a member of it, it will leave the channel/group.
Make sure to grant the bot enough permission (sending messages) in channel/group.

A special case is that the bot will leave a topic group if the "General" topic is closed. This is a temporary limitation before topic groups are fully supported.

### **Q**: My bot is not responding. I checked the log and saw Telethon complaining "Server sent a very new message with ID...", "Server replied with a wrong session ID...", or "Could not find a matching Constructor ID for the TLObject...".

**A:** Telethon is protecting you from potential attacks. For details, please refer to [Telethon FAQ](https://docs.telethon.dev/en/stable/quick-references/faq.html#what-does-server-sent-a-very-new-message-with-id-mean). If you believe that it is caused by misconfiguration instead of attacks, and the bot is not deployed on a PaaS platform (e.g. Heroku, Railway), you may stop RSStT, delete the session file (`config/bot.session`), and restart it to solve the problem.
2 changes: 2 additions & 0 deletions docs/FAQ.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
**A**: 一旦 bot 发现自身缺乏发送消息的权限(未授予或被屏蔽),它将立即退订该聊天中的所有订阅。与此同时,如果该聊天是频道或群组且 bot 仍是成员,它将退出该频道/群组。
确保在群组/频道中授予 bot 足够的权限(发送消息)。

一个特例是,如果「General」话题被关闭,bot 将退出话题群组。这是在话题群组完全受支持之前的一个临时限制。

### **Q**: 我的 bot 无响应。查看日志后发现 Telethon 在抱怨 "Server sent a very new message with ID...","Server replied with a wrong session ID...",或者 "Could not find a matching Constructor ID for the TLObject..."。

**A**: Telethon 正保护您免受潜在的攻击。欲知详情,请参阅 [Telethon FAQ](https://docs.telethon.dev/en/stable/quick-references/faq.html#what-does-server-sent-a-very-new-message-with-id-mean)。如果你确信问题并非由攻击引起,而只是由错误的配置引发,且 bot 并非部署于 PaaS 平台上(如 Heroku, Railway),你可以通过停止 RSStT,删除会话文件(`config/bot.session`),然后重启 RSStT 以解决这个问题。
5 changes: 2 additions & 3 deletions src/command/inner/sub.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,8 @@ async def unsubs(user_id: int,

async def unsub_all(user_id: int, lang: Optional[str] = None) \
-> Optional[dict[str, Union[dict[str, Union[int, str, db.Sub, None]], str]]]:
user_sub_list = await db.Sub.filter(user=user_id)
sub_ids = tuple(_sub.id for _sub in user_sub_list)
return await unsubs(user_id, sub_ids=sub_ids, lang=lang)
user_sub_list = await db.Sub.filter(user=user_id).values_list('id', flat=True)
return await unsubs(user_id, sub_ids=user_sub_list, lang=lang) if user_sub_list else None


async def export_opml(user_id: int) -> Optional[bytes]:
Expand Down
47 changes: 24 additions & 23 deletions src/command/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
from collections import defaultdict, Counter
from itertools import islice
from traceback import format_exc
from telethon.errors import BadRequestError

from . import inner
from .utils import escape_html, leave_chat
from .utils import escape_html, unsub_all_and_leave_chat
from .inner.utils import update_interval, deactivate_feed, calculate_update
from .. import log, db, env, web, locks
from ..errors_collection import EntityNotFoundError, UserBlockedErrors
Expand Down Expand Up @@ -263,37 +264,23 @@ async def __notify_all(feed: db.Feed, subs: Iterable[db.Sub], entry: MutableMapp

async def __send(sub: db.Sub, post: Union[str, Post]):
user_id = sub.user_id
entity = None
try:
try:
try:
entity = await env.bot.get_input_entity(user_id) # verify that the input entity can be gotten first
except ValueError as e: # cannot get the input entity, the bot may be banned by the user
raise EntityNotFoundError(user_id) from e
await env.bot.get_input_entity(user_id) # verify that the input entity can be gotten first
except ValueError: # cannot get the input entity, the bot may be banned by the user
return await __locked_unsub_all_and_leave_chat(user_id=user_id, err_msg=type(EntityNotFoundError).__name__)
try:
if isinstance(post, str):
await env.bot.send_message(user_id, post, parse_mode='html', silent=not sub.notify)
return
await post.send_formatted_post_according_to_sub(sub)
if __user_blocked_counter[user_id]: # reset the counter if success
del __user_blocked_counter[user_id]
except UserBlockedErrors as e:
user_unsub_all_lock = __user_unsub_all_lock_bucket[user_id]
if user_unsub_all_lock.locked():
return # no need to unsub twice!
async with user_unsub_all_lock:
if __user_blocked_counter[user_id] < 5:
__user_blocked_counter[user_id] += 1
return # skip once
# fail for 5 times, consider been banned
del __user_blocked_counter[user_id]
tasks = []
logger.error(f'User blocked ({type(e).__name__}): {user_id}')
if await inner.utils.have_subs(user_id):
tasks.append(inner.sub.unsub_all(user_id))
if user_id < 0 and entity: # it is a group and can get the entity
tasks.append(leave_chat(user_id))
if tasks:
await asyncio.gather(*tasks)
return await __locked_unsub_all_and_leave_chat(user_id=user_id, err_msg=type(e).__name__)
except BadRequestError as e:
if e.message == 'TOPIC_CLOSED':
return await __locked_unsub_all_and_leave_chat(user_id=user_id, err_msg=e.message)
except Exception as e:
logger.error(f'Failed to send {post.link} (feed: {post.feed_link}, user: {sub.user_id}):', exc_info=e)
try:
Expand All @@ -311,6 +298,20 @@ async def __send(sub: db.Sub, post: Union[str, Post]):
await env.bot.send_message(env.MANAGER, 'An sending error message cannot be sent, please check the logs.')


async def __locked_unsub_all_and_leave_chat(user_id: int, err_msg: str):
user_unsub_all_lock = __user_unsub_all_lock_bucket[user_id]
if user_unsub_all_lock.locked():
return # no need to unsub twice!
async with user_unsub_all_lock:
if __user_blocked_counter[user_id] < 5:
__user_blocked_counter[user_id] += 1
return # skip once
# fail for 5 times, consider been banned
del __user_blocked_counter[user_id]
logger.error(f'User blocked ({err_msg}): {user_id}')
await unsub_all_and_leave_chat(user_id)


async def __deactivate_feed_and_notify_all(feed: db.Feed,
subs: Iterable[db.Sub],
reason: Union[web.WebError, str] = None):
Expand Down
19 changes: 11 additions & 8 deletions src/command/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ async def leave_chat(chat_id: hints.EntityLike) -> bool:
return False


async def unsub_all_and_leave_chat(user_id: hints.EntityLike):
await asyncio.gather(
inner.sub.unsub_all(user_id),
leave_chat(user_id)
)


def command_gatekeeper(func: Optional[Callable] = None,
*,
only_manager: bool = False,
Expand Down Expand Up @@ -537,19 +544,15 @@ async def execute():
elif isinstance(e, (EntitiesTooLongError, MessageTooLongError)):
await respond_or_answer(event, 'ERROR: ' + i18n[lang]['message_too_long_prompt'])
elif isinstance(e, errors_collection.UserBlockedErrors):
tasks = []
if await inner.utils.have_subs(chat_id):
tasks.append(inner.sub.unsub_all(chat_id))
if chat_id < 0: # it is a group
tasks.append(leave_chat(chat_id))
if tasks:
await asyncio.gather(*tasks)
await unsub_all_and_leave_chat(chat_id)
elif isinstance(e, BadRequestError) and e.message == 'TOPIC_CLOSED':
await unsub_all_and_leave_chat(chat_id)
else:
await respond_or_answer(event, 'ERROR: ' + i18n[lang]['uncaught_internal_error'])
except (FloodError, MessageNotModifiedError, locks.ContextTimeoutError):
pass # we can do nothing but be a pessimism to drop it
except Exception as e:
logger.error('Uncaught error occurred when dealing with an uncaught error', exc_info=e)
logger.error('Uncaught error occurred when dealing with another uncaught error', exc_info=e)
finally:
raise events.StopPropagation

Expand Down

0 comments on commit 44d3411

Please sign in to comment.