From 540b50c4d86fa6385a60f434befaffa566262b91 Mon Sep 17 00:00:00 2001 From: grassblock Date: Thu, 31 Jul 2025 10:50:19 +0800 Subject: [PATCH 1/3] fix: only repeat the message when it appears continuously --- adapters/tg.py | 2 +- core/repeater.py | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/adapters/tg.py b/adapters/tg.py index 539041d..954469a 100644 --- a/adapters/tg.py +++ b/adapters/tg.py @@ -53,7 +53,7 @@ class TelegramAdapter: # link 模块 router.message(F.text.regexp(r'https?:\/\/(?:www\.)?([-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b)*(\/[\/\d\w\.-]*)*(?:[\?])*(.+)*'))(handle_links) # repeater 模块 - router.message(F.text & (F.chat.type.in_({'group', 'supergroup'})))(MessageRepeater().handle_message) + router.message(F.chat.type.in_({'group', 'supergroup'}))(MessageRepeater().handle_message) # 捕获所有其他消息 router.message(F.chat.type.in_({'group', 'supergroup'}))(dummy_handler) diff --git a/core/repeater.py b/core/repeater.py index 60ef3d5..a5229db 100644 --- a/core/repeater.py +++ b/core/repeater.py @@ -10,32 +10,37 @@ class MessageRepeater: self.message_counts = defaultdict(lambda: defaultdict(int)) self.repeated_messages = defaultdict(set) self.message_timestamps = defaultdict(lambda: defaultdict(float)) + self.last_messages = defaultdict(str) # Track last message in each chat self.expiry_seconds = message_expiry_seconds async def handle_message(self, message: aiogram.types.Message): """Handle incoming messages and repeat when a threshold is met""" chat_id = message.chat.id - text = message.text + content = '' + if message.text: + content = message.text + elif message.sticker: + content = message.sticker.file_id if not config.is_feature_enabled('repeater', message.chat.id): return - if not text: - return - # Clean expired messages self._clean_expired_messages(chat_id) # Increment message count - self.message_counts[chat_id][text] += 1 - self.message_timestamps[chat_id][text] = time.time() + self.message_counts[chat_id][content] += 1 + self.message_timestamps[chat_id][content] = time.time() # If a message appears twice and hasn't been repeated yet - if (self.message_counts[chat_id][text] >= 2 and - text not in self.repeated_messages[chat_id]): + if (content == self.last_messages[chat_id] and + self.message_counts[chat_id][content] >= 2 and + content not in self.repeated_messages[chat_id]): # Mark as repeated and send the message - self.repeated_messages[chat_id].add(text) - await message.answer(text) + self.repeated_messages[chat_id].add(content) + await message.copy_to(chat_id) + + self.last_messages[chat_id] = content def _clean_expired_messages(self, chat_id: int): """Remove messages older than expiry_seconds""" From 3b087369fa144babf26c830cefa71e183224a657 Mon Sep 17 00:00:00 2001 From: grassblock Date: Thu, 31 Jul 2025 11:44:44 +0800 Subject: [PATCH 2/3] feat: display anonymous channel/group name instead of 'Telegram'/'Group' --- core/actions.py | 15 ++++++++++----- core/middleware/stats.py | 11 ++++++++++- core/stats.py | 4 +--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/core/actions.py b/core/actions.py index 245959e..518ad80 100644 --- a/core/actions.py +++ b/core/actions.py @@ -7,17 +7,22 @@ async def handle_actions(message: Message) -> None: if not config.is_feature_enabled('actions', message.chat.id): return rawtext = message.text + from_user = message.from_user.mention_html(message.sender_chat.title) if message.sender_chat else message.from_user.mention_html() + replied_user = message.from_user.mention_html(message.reply_to_message.sender_chat.title) if message.reply_to_message and message.reply_to_message.sender_chat else message.reply_to_message.from_user.mention_html() # 防止识别成命令而被误触发 - if rawtext.replace('/','',1).isalpha() or '@' in rawtext: + if rawtext.replace('/','',1).isascii() or '@' in rawtext: + print(rawtext.replace('/','',1).isascii()) return elif " " in message.text: - if rawtext.split(" ")[0].replace('/','',1).isalpha(): + if rawtext.split(" ")[0].replace('/','',1).isascii(): return - await message.reply(f"{message.from_user.mention_html()} {rawtext.split(" ")[0].replace('/','')}了 {message.reply_to_message.from_user.mention_html() if message.reply_to_message else '自己' } {''.join(rawtext.split(" ")[1:])}!",disable_web_page_preview=True) + await message.reply(f"{from_user} {rawtext.split(" ")[0].replace('/','')}了 {replied_user if message.reply_to_message else '自己' } {''.join(rawtext.split(" ")[1:])}!",disable_web_page_preview=True) else: - await message.reply(f"{message.from_user.mention_html()} {message.text.replace('/','')}了 {message.reply_to_message.from_user.mention_html() if message.reply_to_message else '自己'}!",disable_web_page_preview=True) + await message.reply(f"{from_user} {message.text.replace('/','')}了 {replied_user if message.reply_to_message else '自己'}!",disable_web_page_preview=True) async def handle_reverse_actions(message: Message) -> None: + from_user = message.from_user.mention_html(message.sender_chat.title) if message.sender_chat else message.from_user.mention_html() + replied_user = message.from_user.mention_html(message.reply_to_message.sender_chat.title) if message.reply_to_message and message.reply_to_message.sender_chat else message.reply_to_message.from_user.mention_html() if not config.is_feature_enabled('actions', message.chat.id): return - await message.reply(f"{message.from_user.mention_html()} 被 {message.reply_to_message.from_user.mention_html()} {message.text.replace('\\','')}了!",disable_web_page_preview=True) \ No newline at end of file + await message.reply(f"{from_user} 被 {replied_user if message.reply_to_message else '自己'} {message.text.replace('\\','')}了!",disable_web_page_preview=True) \ No newline at end of file diff --git a/core/middleware/stats.py b/core/middleware/stats.py index 77f08ec..c01c3c4 100644 --- a/core/middleware/stats.py +++ b/core/middleware/stats.py @@ -28,10 +28,19 @@ class MessageStatsMiddleware(BaseMiddleware): } if user_id not in self.stats[chat_id]['users']: + name = 'Unknown' + if event.sender_chat: + if event.sender_chat.type in ['group','supergroup']: + # 如果是频道/群组匿名管理员消息,使用频道名称 + name = f"{event.sender_chat.title} [admin]" + # 如果是频道/群组匿名管理员消息,使用频道名称 + name = f"{event.sender_chat.title} [channel]" + elif event.from_user: + name = event.from_user.full_name self.stats[chat_id]['users'][user_id] = { 'message_count': 0, 'username': event.from_user.username if event.from_user else 'Unknown', - 'first_name': event.from_user.first_name if event.from_user else 'Unknown' + 'name': name } # 更新统计 diff --git a/core/stats.py b/core/stats.py index ffca6f7..bafffa1 100644 --- a/core/stats.py +++ b/core/stats.py @@ -18,8 +18,6 @@ async def handle_stats_command(message: Message): except (FileNotFoundError, json.JSONDecodeError): stats = {}.get(chat_id) - print(stats) - if not stats: await message.reply("暂无统计数据") return @@ -38,7 +36,7 @@ async def handle_stats_command(message: Message): text += "🏆 发言排行榜:\n" for i, (user_id, user_data) in enumerate(sorted_users[:10], 1): - name = user_data['first_name'] or user_data['username'] or str(user_id) + name = user_data['name'] or user_data['username'] or str(user_id) text += f"{i}. {name}: {user_data['message_count']} 条\n" await message.reply(text) \ No newline at end of file From 26e526d12476c7d5782a39629b6c53b227c276fb Mon Sep 17 00:00:00 2001 From: grassblock Date: Thu, 31 Jul 2025 11:45:10 +0800 Subject: [PATCH 3/3] chore: clean up --- adapters/tg.py | 1 - core/unpin.py | 1 - 2 files changed, 2 deletions(-) diff --git a/adapters/tg.py b/adapters/tg.py index 954469a..3b2470f 100644 --- a/adapters/tg.py +++ b/adapters/tg.py @@ -79,7 +79,6 @@ class TelegramAdapter: ) await self.dp.start_polling(bot) - print(await bot.get_updates()) async def main() -> None: diff --git a/core/unpin.py b/core/unpin.py index b96d05c..7a5fde7 100644 --- a/core/unpin.py +++ b/core/unpin.py @@ -9,7 +9,6 @@ async def handle_unpin_channel_message(message: Message): return try: regex_pattern = config.get_feature_config('unpin', message.chat.id)['regex'] - print(regex_pattern) # If a regex pattern exists, check if the message matches if regex_pattern: import re