Skip to content

Commit 32c1b41

Browse files
konardclaude
andcommitted
Implement upvote functionality for "+" messages
- Add UPVOTE_PREVIOUS pattern to match simple "+" messages - Add get_conversation_messages method to retrieve message history - Implement upvote_previous method to find and upvote previous non-command messages - Integrate with existing karma system using collective vote mechanism - Add comprehensive pattern tests Fixes #7 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent fffcbe3 commit 32c1b41

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

experiments/test_upvote_pattern.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""Test script for the new UPVOTE_PREVIOUS pattern."""
4+
import sys
5+
import os
6+
7+
# Add python directory to path
8+
python_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'python')
9+
sys.path.append(python_dir)
10+
11+
from patterns import UPVOTE_PREVIOUS
12+
from regex import match
13+
14+
def test_upvote_pattern():
15+
"""Test the UPVOTE_PREVIOUS pattern."""
16+
17+
test_cases = [
18+
# Should match
19+
("+", True),
20+
(" + ", True),
21+
(" + ", True),
22+
(" +", True),
23+
("+ ", True),
24+
25+
# Should not match
26+
("++", False),
27+
("+ hello", False),
28+
("hello +", False),
29+
("+5", False),
30+
("+-", False),
31+
("", False),
32+
("hello", False),
33+
]
34+
35+
print("Testing UPVOTE_PREVIOUS pattern...")
36+
37+
all_passed = True
38+
for test_input, expected in test_cases:
39+
result = bool(match(UPVOTE_PREVIOUS, test_input))
40+
status = "PASS" if result == expected else "FAIL"
41+
42+
if result != expected:
43+
all_passed = False
44+
45+
print(f" '{test_input}' -> {result} (expected {expected}) [{status}]")
46+
47+
print(f"\nOverall: {'PASS' if all_passed else 'FAIL'}")
48+
return all_passed
49+
50+
if __name__ == "__main__":
51+
test_upvote_pattern()

python/__main__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def __init__(
6363
(patterns.WHAT_IS, self.commands.what_is),
6464
(patterns.WHAT_MEAN, self.commands.what_is),
6565
(patterns.APPLY_KARMA, self.commands.apply_karma),
66+
(patterns.UPVOTE_PREVIOUS, self.commands.upvote_previous),
6667
(patterns.GITHUB_COPILOT, self.commands.github_copilot)
6768
)
6869

@@ -198,6 +199,20 @@ def get_messages(
198199
reply_message = event.get("reply_message", {})
199200
return [reply_message] if reply_message else event.get("fwd_messages", [])
200201

202+
def get_conversation_messages(
203+
self,
204+
peer_id: int,
205+
count: int = 10
206+
) -> List[Dict[str, Any]]:
207+
"""Returns recent messages from conversation.
208+
"""
209+
response = self.call_method(
210+
'messages.getHistory',
211+
dict(peer_id=peer_id, count=count))
212+
if "error" in response:
213+
return []
214+
return response["response"]["items"]
215+
201216

202217
if __name__ == '__main__':
203218
vk = Bot(token=BOT_TOKEN, group_id=config.BOT_GROUP_ID, debug=True)

python/modules/commands.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,98 @@ def github_copilot(self) -> NoReturn:
379379
f'Пожалуйста, подождите {round(config.GITHUB_COPILOT_TIMEOUT - (now - self.now))} секунд', self.peer_id
380380
)
381381

382+
def upvote_previous(self) -> NoReturn:
383+
"""Upvotes the previous non-command message."""
384+
if self.peer_id < 2e9 or not self.karma_enabled:
385+
return
386+
387+
# Get recent conversation messages
388+
recent_messages = self.vk_instance.get_conversation_messages(self.peer_id, 20)
389+
390+
# Find the previous non-command message
391+
current_msg_found = False
392+
for message in recent_messages:
393+
# Skip until we find current message
394+
if not current_msg_found:
395+
if message["conversation_message_id"] == self.msg_id:
396+
current_msg_found = True
397+
continue
398+
399+
# Skip messages from bots or system
400+
if message["from_id"] <= 0:
401+
continue
402+
403+
message_text = message["text"].lstrip("/")
404+
405+
# Check if this message matches any command pattern
406+
is_command = False
407+
for cmd_pattern in Commands.cmds.keys():
408+
if match(cmd_pattern, message_text):
409+
is_command = True
410+
break
411+
412+
# If not a command, this is our target message
413+
if not is_command and message_text.strip():
414+
target_user_id = message["from_id"]
415+
target_user = self.data_service.get_user(target_user_id, self.vk_instance)
416+
417+
# Don't allow self-upvote
418+
if target_user_id == self.from_id:
419+
self.vk_instance.send_msg(
420+
'Нельзя голосовать за свои сообщения.', self.peer_id)
421+
return
422+
423+
# Apply upvote (equivalent to +0 karma - collective vote)
424+
self.user = target_user
425+
utcnow = datetime.utcnow()
426+
427+
# Check if already voted for this user
428+
if self.current_user.uid in target_user.supporters:
429+
self.vk_instance.send_msg(
430+
(f'Вы уже голосовали за [id{target_user.uid}|'
431+
f'{self.vk_instance.get_user_name(target_user.uid, "acc")}].'),
432+
self.peer_id
433+
)
434+
return
435+
436+
# Check collective vote time limit
437+
utclast = datetime.fromtimestamp(
438+
float(self.current_user.last_collective_vote))
439+
difference = utcnow - utclast
440+
hours_difference = difference.total_seconds() / 3600
441+
hours_limit = karma_limit(self.current_user.karma)
442+
443+
if hours_difference < hours_limit:
444+
self.vk_instance.delete_message(self.peer_id, self.msg_id)
445+
self.vk_instance.send_msg(
446+
CommandsBuilder.build_not_enough_hours(
447+
self.current_user, self.data_service,
448+
hours_limit, difference.total_seconds() / 60),
449+
self.peer_id)
450+
return
451+
452+
# Apply the upvote
453+
user_karma_change, selected_user_karma_change, collective_vote_applied, voters = self.apply_karma_change(
454+
"+", 0)
455+
456+
if collective_vote_applied:
457+
self.current_user.last_collective_vote = int(utcnow.timestamp())
458+
self.data_service.save_user(self.current_user)
459+
460+
if user_karma_change:
461+
self.data_service.save_user(target_user)
462+
463+
self.vk_instance.send_msg(
464+
CommandsBuilder.build_karma_change(
465+
user_karma_change, selected_user_karma_change, voters),
466+
self.peer_id)
467+
self.vk_instance.delete_message(self.peer_id, self.msg_id)
468+
return
469+
470+
# No previous non-command message found
471+
self.vk_instance.send_msg(
472+
'Не найдено предыдущее сообщение для голосования.', self.peer_id)
473+
382474
def match_command(
383475
self,
384476
pattern: Pattern

python/patterns.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,6 @@
6565
GITHUB_COPILOT = recompile(
6666
r'\A\s*(code|код)\s+(?P<lang>(' + COPILOT_LANGUAGES +
6767
r'))(?P<text>[\S\s]+)\Z', IGNORECASE)
68+
69+
UPVOTE_PREVIOUS = recompile(
70+
r'\A\s*\+\s*\Z', IGNORECASE)

0 commit comments

Comments
 (0)