From 4f0413c67a931370a56909b07914e9b44c41b6fc Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 14 Sep 2025 02:56:04 +0300 Subject: [PATCH 1/3] Initial commit with task details for issue #53 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/Bot/issues/53 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..93a188dc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/Bot/issues/53 +Your prepared branch: issue-53-084e60a3 +Your prepared working directory: /tmp/gh-issue-solver-1757807761503 + +Proceed. \ No newline at end of file From ab95590bf38d383d591350436653114cdf6ff713 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 14 Sep 2025 03:02:00 +0300 Subject: [PATCH 2/3] Implement vote logging feature for Bot issue #53 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add vote_log_subscription property to user data model - Create patterns for enable/disable vote log commands (Russian & English) - Implement enable_vote_log() and disable_vote_log() command methods - Add comprehensive vote logging system that sends notifications to subscribed users - Include detailed vote information: timestamp, chat, voter, target, karma changes - Support both personal karma transfers and collective votes - Prevent self-notifications (voters and recipients don't get their own logs) - Add vote log commands to help message documentation - Maintain decentralized, tamper-proof vote log distribution 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- python/__main__.py | 4 +- python/modules/commands.py | 113 +++++++++++++++++++++++++++++ python/modules/commands_builder.py | 10 ++- python/modules/data_service.py | 1 + python/patterns.py | 6 ++ 5 files changed, 130 insertions(+), 4 deletions(-) diff --git a/python/__main__.py b/python/__main__.py index cdcbf7f6..c68850a4 100644 --- a/python/__main__.py +++ b/python/__main__.py @@ -63,7 +63,9 @@ def __init__( (patterns.WHAT_IS, self.commands.what_is), (patterns.WHAT_MEAN, self.commands.what_is), (patterns.APPLY_KARMA, self.commands.apply_karma), - (patterns.GITHUB_COPILOT, self.commands.github_copilot) + (patterns.GITHUB_COPILOT, self.commands.github_copilot), + (patterns.ENABLE_VOTE_LOG, self.commands.enable_vote_log), + (patterns.DISABLE_VOTE_LOG, self.commands.disable_vote_log) ) def message_new( diff --git a/python/modules/commands.py b/python/modules/commands.py index 93d99817..63cd0cf9 100644 --- a/python/modules/commands.py +++ b/python/modules/commands.py @@ -226,6 +226,9 @@ def apply_karma(self) -> NoReturn: user_karma_change, selected_user_karma_change, voters), self.peer_id) self.vk_instance.delete_message(self.peer_id, self.msg_id) + + # Send vote log to subscribed users + self.send_vote_log(user_karma_change, selected_user_karma_change, voters, operator, amount) def apply_karma_change( self, @@ -379,6 +382,116 @@ def github_copilot(self) -> NoReturn: f'Пожалуйста, подождите {round(config.GITHUB_COPILOT_TIMEOUT - (now - self.now))} секунд', self.peer_id ) + def enable_vote_log(self) -> NoReturn: + """Enable vote log subscription for the user""" + if self.from_id > 0: + self.current_user.vote_log_subscription = True + self.data_service.save_user(self.current_user) + self.vk_instance.send_msg( + "✅ Лог голосований включен. Вы будете получать уведомления о всех голосованиях.", + self.peer_id + ) + + def disable_vote_log(self) -> NoReturn: + """Disable vote log subscription for the user""" + if self.from_id > 0: + self.current_user.vote_log_subscription = False + self.data_service.save_user(self.current_user) + self.vk_instance.send_msg( + "❌ Лог голосований отключен. Вы больше не будете получать уведомления о голосованиях.", + self.peer_id + ) + + def send_vote_log( + self, + user_karma_change: Optional[Tuple[int, str, int, int]], + selected_user_karma_change: Optional[Tuple[int, str, int, int]], + voters: Optional[List[int]], + operator: str, + amount: int + ) -> NoReturn: + """Send vote log to subscribed users""" + # Get all users with vote log subscription enabled + subscribed_users = self.data_service.get_users( + ["uid", "vote_log_subscription"], + None, + False + ) + + # Filter users that have subscription enabled + subscribed_users = [user for user in subscribed_users if user.get("vote_log_subscription", False)] + + if not subscribed_users: + return + + # Build log message + log_message = self.build_vote_log_message( + user_karma_change, selected_user_karma_change, voters, operator, amount + ) + + # Send to all subscribed users + for user in subscribed_users: + user_id = user["uid"] + # Don't send log to the voter themselves or the recipient + if user_id != self.from_id and (not selected_user_karma_change or user_id != selected_user_karma_change[0]): + try: + self.vk_instance.send_msg(log_message, user_id) + except Exception as e: + # Continue if failed to send to one user + print(f"Failed to send vote log to user {user_id}: {e}") + continue + + def build_vote_log_message( + self, + user_karma_change: Optional[Tuple[int, str, int, int]], + selected_user_karma_change: Optional[Tuple[int, str, int, int]], + voters: Optional[List[int]], + operator: str, + amount: int + ) -> str: + """Build vote log message""" + timestamp = datetime.now().strftime("%H:%M:%S") + chat_name = f"chat_{self.peer_id}" if self.peer_id >= 2e9 else "ЛС" + + voter_name = self.current_user.name + if selected_user_karma_change: + target_name = selected_user_karma_change[1] + old_karma = selected_user_karma_change[2] + new_karma = selected_user_karma_change[3] + else: + return f"📊 [{timestamp}] Голосование в {chat_name} не привело к изменению кармы" + + if amount > 0: + # Personal karma transfer + log_message = (f"📊 [{timestamp}] Голосование в {chat_name}:\n" + f"👤 {voter_name} передал {amount} кармы " + f"→ {target_name}\n" + f"🔄 Карма {target_name}: {old_karma} → {new_karma}") + else: + # Collective vote + vote_type = "👍 поддержка" if operator == "+" else "👎 осуждение" + + if voters: + # Vote completed, karma changed + voters_count = len(voters) + karma_change = new_karma - old_karma + change_symbol = "+" if karma_change > 0 else "" + + log_message = (f"📊 [{timestamp}] Голосование в {chat_name}:\n" + f"🗳 Коллективное голосование завершено ({vote_type})\n" + f"👥 Голосов: {voters_count}\n" + f"👤 {target_name}: {old_karma} → {new_karma} ({change_symbol}{karma_change})") + else: + # Vote in progress + current_votes = len(getattr(self.user, 'supporters' if operator == '+' else 'opponents', [])) + required_votes = config.POSITIVE_VOTES_PER_KARMA if operator == '+' else config.NEGATIVE_VOTES_PER_KARMA + + log_message = (f"📊 [{timestamp}] Голосование в {chat_name}:\n" + f"🗳 {vote_type} за {target_name}\n" + f"📈 Голосов: {current_votes}/{required_votes}") + + return log_message + def match_command( self, pattern: Pattern diff --git a/python/modules/commands_builder.py b/python/modules/commands_builder.py index 29dc739f..4853bcca 100644 --- a/python/modules/commands_builder.py +++ b/python/modules/commands_builder.py @@ -20,16 +20,20 @@ def build_help_message( - {karma} - is karma enabled in chat. """ documentation_link = "vk.cc/c9TNs3" + vote_log_commands = ("\n\n📊 Лог голосований:\n" + "• \"включить лог голосований\" - получать уведомления о всех голосованиях\n" + "• \"отключить лог голосований\" - отключить уведомления") + if 0 < peer_id < 2e9: return ("Вы находитесь в личных сообщениях бота.\n" - f"Документация — {documentation_link}") + f"Документация — {documentation_link}" + vote_log_commands) elif peer_id > 2e9: if karma: return ("Вы находитесь в беседе с включённой кармой.\n" - f"Документация — {documentation_link}") + f"Документация — {documentation_link}" + vote_log_commands) else: return (f"Вы находитесь в беседе (#{peer_id}) с выключенной кармой.\n" - f"Документация — {documentation_link}") + f"Документация — {documentation_link}" + vote_log_commands) @staticmethod def build_info_message( diff --git a/python/modules/data_service.py b/python/modules/data_service.py index 45a0faeb..7b64d99f 100644 --- a/python/modules/data_service.py +++ b/python/modules/data_service.py @@ -19,6 +19,7 @@ def __init__(self, db_name: str = "users"): self.base.addPattern("supporters", []) self.base.addPattern("opponents", []) self.base.addPattern("karma", 0) + self.base.addPattern("vote_log_subscription", False) def get_or_create_user( self, diff --git a/python/patterns.py b/python/patterns.py index 1834c72c..1d0e8bf2 100644 --- a/python/patterns.py +++ b/python/patterns.py @@ -65,3 +65,9 @@ GITHUB_COPILOT = recompile( r'\A\s*(code|код)\s+(?P(' + COPILOT_LANGUAGES + r'))(?P[\S\s]+)\Z', IGNORECASE) + +ENABLE_VOTE_LOG = recompile( + r'\A\s*(включить лог голосований|enable vote log)\s*\Z', IGNORECASE) + +DISABLE_VOTE_LOG = recompile( + r'\A\s*(отключить лог голосований|disable vote log)\s*\Z', IGNORECASE) From c48f9132f975cb1160fa5a9f509a840c839e1527 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 14 Sep 2025 03:02:48 +0300 Subject: [PATCH 3/3] Remove CLAUDE.md - Claude command completed --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 93a188dc..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/Bot/issues/53 -Your prepared branch: issue-53-084e60a3 -Your prepared working directory: /tmp/gh-issue-solver-1757807761503 - -Proceed. \ No newline at end of file