Skip to content

Commit 0571d99

Browse files
konardclaude
andcommitted
Add callback buttons for top command pagination
- Implement VkKeyboard utility class for creating VK inline keyboards - Add TopPaginationKeyboard specialized class for top command pagination - Add message_event handler to process callback button events - Modify top command to support pagination with callback buttons - Add send_msg_with_keyboard and send_callback_answer methods - Maintain backward compatibility for top command with number parameter - Support pagination for top, bottom, and people commands - Display 10 users per page with Previous/Next navigation buttons 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 7276e83 commit 0571d99

File tree

4 files changed

+398
-13
lines changed

4 files changed

+398
-13
lines changed

python/__main__.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import requests
99

1010
from modules import (
11-
BetterBotBaseDataService, Commands
11+
BetterBotBaseDataService, Commands, VkKeyboard, TopPaginationKeyboard
1212
)
1313
from tokens import BOT_TOKEN
1414
from userbot import UserBot
@@ -112,6 +112,26 @@ def message_new(
112112
except Exception as e:
113113
print(e)
114114

115+
def message_event(
116+
self,
117+
event: Dict[str, Any]
118+
) -> NoReturn:
119+
"""Handling callback button events.
120+
"""
121+
event_data = event["object"]
122+
peer_id = event_data["peer_id"]
123+
from_id = event_data["user_id"]
124+
payload = event_data.get("payload", {})
125+
event_id = event_data["event_id"]
126+
127+
# Get user for callback handling
128+
user = self.data.get_user(from_id, self) if from_id > 0 else None
129+
130+
try:
131+
self.commands.process_callback(
132+
payload, peer_id, from_id, event_id, user)
133+
except Exception as e:
134+
print(f"Error handling callback: {e}")
115135

116136
def delete_message(
117137
self,
@@ -168,6 +188,46 @@ def send_msg(
168188
message=msg, peer_id=peer_id,
169189
disable_mentions=1, random_id=0))
170190

191+
def send_msg_with_keyboard(
192+
self,
193+
msg: str,
194+
peer_id: int,
195+
keyboard: str
196+
) -> NoReturn:
197+
"""Sends message with keyboard to chat with {peer_id}.
198+
199+
:param msg: message text
200+
:param peer_id: chat ID
201+
:param keyboard: JSON keyboard string
202+
"""
203+
self.call_method(
204+
'messages.send',
205+
dict(
206+
message=msg, peer_id=peer_id,
207+
keyboard=keyboard,
208+
disable_mentions=1, random_id=0))
209+
210+
def send_callback_answer(
211+
self,
212+
event_id: str,
213+
peer_id: int,
214+
event_data: Dict[str, Any] = None
215+
) -> NoReturn:
216+
"""Send answer to callback query.
217+
218+
:param event_id: Event ID from callback
219+
:param peer_id: chat ID
220+
:param event_data: Optional event data (for snackbar, etc.)
221+
"""
222+
params = {
223+
"event_id": event_id,
224+
"peer_id": peer_id
225+
}
226+
if event_data:
227+
params["event_data"] = event_data
228+
229+
self.call_method('messages.sendMessageEventAnswer', params)
230+
171231
def get_user_name(
172232
self,
173233
uid: int,

python/modules/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .data_service import BetterBotBaseDataService
55
from .data_builder import DataBuilder
66
from .vk_instance import VkInstance
7+
from .keyboard_utils import VkKeyboard, TopPaginationKeyboard
78
from .utils import (
89
get_default_programming_language,
910
contains_string,

python/modules/commands.py

Lines changed: 127 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from datetime import datetime
44
from time import time
55
import os
6+
import json
67

78
from regex import Pattern, Match, split, match, search, IGNORECASE, sub
89
from requests import post
@@ -13,6 +14,7 @@
1314
from .commands_builder import CommandsBuilder
1415
from .data_service import BetterBotBaseDataService
1516
from .data_builder import DataBuilder
17+
from .keyboard_utils import TopPaginationKeyboard
1618
from .utils import (
1719
get_default_programming_language,
1820
contains_all_strings,
@@ -124,22 +126,28 @@ def top(
124126
self,
125127
reverse: bool = False
126128
) -> NoReturn:
127-
"""Sends users top."""
129+
"""Sends users top with pagination."""
128130
if self.peer_id < 2e9:
129131
return
130132
maximum_users = self.matched.group("maximum_users")
131-
maximum_users = int(maximum_users) if maximum_users else -1
132-
users = DataBuilder.get_users_sorted_by_karma(
133-
self.vk_instance, self.data_service, self.peer_id)
134-
users = [i for i in users if
135-
(i["karma"] != 0) or
136-
("programming_languages" in i and len(i["programming_languages"]) > 0)
137-
]
138-
self.vk_instance.send_msg(
139-
CommandsBuilder.build_top_users(
133+
134+
# If specific maximum_users is requested, use old behavior without pagination
135+
if maximum_users:
136+
maximum_users = int(maximum_users)
137+
users = DataBuilder.get_users_sorted_by_karma(
138+
self.vk_instance, self.data_service, self.peer_id)
139+
users = [i for i in users if
140+
(i["karma"] != 0) or
141+
("programming_languages" in i and len(i["programming_languages"]) > 0)
142+
]
143+
message = CommandsBuilder.build_top_users(
140144
users, self.data_service, reverse,
141-
self.karma_enabled, maximum_users),
142-
self.peer_id)
145+
self.karma_enabled, maximum_users)
146+
if message:
147+
self.vk_instance.send_msg(message, self.peer_id)
148+
else:
149+
# Use new paginated version
150+
self.top_paginated(reverse=reverse, page=0)
143151

144152
def top_langs(
145153
self,
@@ -436,3 +444,110 @@ def process(
436444
if self.matched:
437445
action()
438446
return
447+
448+
def process_callback(
449+
self,
450+
payload: Dict[str, Any],
451+
peer_id: int,
452+
from_id: int,
453+
event_id: str,
454+
user: BetterUser
455+
) -> NoReturn:
456+
"""Process callback button events
457+
458+
:param payload: Callback payload data
459+
:param peer_id: chat ID
460+
:param from_id: user ID
461+
:param event_id: callback event ID
462+
:param user: user object
463+
"""
464+
self.peer_id = peer_id
465+
self.from_id = from_id
466+
self.karma_enabled = peer_id in config.CHATS_KARMA_WHITELIST
467+
self.current_user = user
468+
469+
if from_id < 0:
470+
return
471+
472+
try:
473+
# Parse payload if it's a string
474+
if isinstance(payload, str):
475+
payload = json.loads(payload)
476+
477+
action = payload.get("action")
478+
479+
if action == "paginate":
480+
self.handle_top_pagination(payload, event_id)
481+
elif action == "page_info":
482+
# Just acknowledge the page info button click
483+
self.vk_instance.send_callback_answer(event_id, peer_id)
484+
485+
except Exception as e:
486+
print(f"Error processing callback: {e}")
487+
# Send empty callback answer to acknowledge the button press
488+
self.vk_instance.send_callback_answer(event_id, peer_id)
489+
490+
def handle_top_pagination(
491+
self,
492+
payload: Dict[str, Any],
493+
event_id: str
494+
) -> NoReturn:
495+
"""Handle pagination for top command"""
496+
command = payload.get("command", "top")
497+
page = payload.get("page", 0)
498+
reverse = payload.get("reverse", False)
499+
500+
# Execute the appropriate top command with pagination
501+
if command == "top":
502+
self.top_paginated(reverse=reverse, page=page)
503+
elif command == "bottom":
504+
self.top_paginated(reverse=True, page=page)
505+
elif command == "people":
506+
self.top_paginated(reverse=reverse, page=page)
507+
508+
# Acknowledge the callback
509+
self.vk_instance.send_callback_answer(event_id, self.peer_id)
510+
511+
def top_paginated(
512+
self,
513+
reverse: bool = False,
514+
page: int = 0
515+
) -> NoReturn:
516+
"""Sends paginated users top with callback buttons."""
517+
if self.peer_id < 2e9:
518+
return
519+
520+
users = DataBuilder.get_users_sorted_by_karma(
521+
self.vk_instance, self.data_service, self.peer_id)
522+
users = [i for i in users if
523+
(i["karma"] != 0) or
524+
("programming_languages" in i and len(i["programming_languages"]) > 0)
525+
]
526+
527+
if reverse:
528+
users = list(reversed(users))
529+
530+
# Calculate pagination
531+
pagination_info = TopPaginationKeyboard.calculate_pagination(len(users))
532+
total_pages = pagination_info["total_pages"]
533+
534+
# Ensure page is within valid range
535+
page = max(0, min(page, total_pages - 1))
536+
537+
# Get users for current page
538+
page_users = TopPaginationKeyboard.get_page_users(users, page)
539+
540+
# Build message
541+
command_type = "bottom" if reverse else "top"
542+
message = CommandsBuilder.build_top_users(
543+
page_users, self.data_service, False,
544+
self.karma_enabled, -1)
545+
546+
if message:
547+
# Create pagination keyboard
548+
keyboard = TopPaginationKeyboard.create_pagination_keyboard(
549+
page, total_pages, command_type, reverse)
550+
551+
self.vk_instance.send_msg_with_keyboard(message, self.peer_id, keyboard)
552+
else:
553+
self.vk_instance.send_msg("Пользователи не найдены.", self.peer_id)

0 commit comments

Comments
 (0)