Skip to content

Commit 5b959a0

Browse files
konardclaude
andcommitted
Implement rules management feature for VK bot
- Add GitHub Gist monitoring for chat rules - Auto-update pinned messages when rules change - Support English and Russian commands - Add comprehensive error handling and testing Features: - set rules <gist_url> - Set rules from GitHub Gist - remove rules - Remove rules monitoring - rules status - Check rules configuration status - Background monitoring every 5 minutes - Automatic pinned message updates 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 1ee1baa commit 5b959a0

File tree

8 files changed

+822
-6
lines changed

8 files changed

+822
-6
lines changed

python/RULES_FEATURE.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Rules Management Feature
2+
3+
This document describes the new rules management functionality added to the VK bot.
4+
5+
## Overview
6+
7+
The bot now supports monitoring GitHub Gists containing chat rules and automatically updating pinned messages when rules change.
8+
9+
## Features
10+
11+
### 1. Set Rules from GitHub Gist
12+
- **Command**: `set rules <gist_url>` or `установить правила <gist_url>`
13+
- **Description**: Sets a GitHub Gist as the source for chat rules
14+
- **Example**: `set rules https://gist.github.com/Konard/a7cd43f91c035e412037cbb3de75d540`
15+
- **Requirements**:
16+
- Only works in group chats (peer_id > 2e9)
17+
- Admin permissions recommended (simplified check in current implementation)
18+
- Gist must be publicly accessible
19+
20+
### 2. Remove Rules Monitoring
21+
- **Command**: `remove rules` or `убрать правила`
22+
- **Description**: Removes rules monitoring for the current chat
23+
- **Requirements**: Only works in group chats
24+
25+
### 3. Check Rules Status
26+
- **Command**: `rules status` or `статус правил`
27+
- **Description**: Shows current rules configuration status
28+
- **Shows**:
29+
- Source gist URL
30+
- Who set the rules
31+
- When rules were set
32+
- Last check timestamp
33+
34+
## How It Works
35+
36+
### Initial Setup
37+
1. Admin uses `set rules <gist_url>` command
38+
2. Bot validates the gist URL and checks accessibility
39+
3. Bot fetches current rules content from the gist
40+
4. Bot posts rules as a pinned message in the chat
41+
5. Bot stores configuration for monitoring
42+
43+
### Automatic Monitoring
44+
1. Background thread checks all configured chats every 5 minutes
45+
2. For each chat, bot checks if gist content has changed
46+
3. If content changed:
47+
- Bot tries to edit the existing pinned message
48+
- If editing fails, bot creates a new pinned message
49+
- Bot notifies the chat about the update
50+
51+
### Data Storage
52+
Rules configuration is stored in `chat_rules.json` with the following structure:
53+
```json
54+
{
55+
"chat_id": {
56+
"gist_url": "https://gist.github.com/user/gist_id",
57+
"gist_id": "gist_id",
58+
"set_by": user_id,
59+
"set_at": "2023-01-01T12:00:00",
60+
"last_check": "2023-01-01T12:05:00",
61+
"last_content_hash": 12345,
62+
"pinned_message_id": 67890
63+
}
64+
}
65+
```
66+
67+
## Implementation Details
68+
69+
### New Files
70+
- `modules/rules_service.py` - Core rules management service
71+
- `python/test_patterns_only.py` - Test script for patterns and GitHub API
72+
- `python/RULES_FEATURE.md` - This documentation
73+
74+
### Modified Files
75+
- `python/patterns.py` - Added new command patterns
76+
- `python/__main__.py` - Added VK API methods and monitoring thread
77+
- `python/modules/commands.py` - Added rules command handlers
78+
- `python/modules/commands_builder.py` - Updated help message
79+
80+
### New VK API Methods
81+
- `pin_message()` - Pin a message in chat
82+
- `unpin_message()` - Unpin a message in chat
83+
- `send_and_pin_message()` - Send and immediately pin a message
84+
- `edit_message()` - Edit an existing message
85+
86+
### Error Handling
87+
- Network errors when fetching gist content
88+
- VK API errors when pinning/editing messages
89+
- Invalid gist URLs
90+
- Gist access permission issues
91+
- Missing or malformed configuration files
92+
93+
## Usage Examples
94+
95+
### Setting Rules
96+
```
97+
set rules https://gist.github.com/Konard/a7cd43f91c035e412037cbb3de75d540
98+
```
99+
Bot response: "✅ Правила успешно установлены и закреплены!"
100+
101+
### Checking Status
102+
```
103+
rules status
104+
```
105+
Bot response:
106+
```
107+
📊 Статус правил:
108+
🔗 Источник: https://gist.github.com/Konard/a7cd43f91c035e412037cbb3de75d540
109+
👤 Установил: John Doe
110+
📅 Дата установки: 2023-01-01
111+
🔄 Последняя проверка: 2023-01-01T12:05:00
112+
```
113+
114+
### Removing Rules
115+
```
116+
remove rules
117+
```
118+
Bot response: "✅ Мониторинг правил отключен для этого чата."
119+
120+
## Benefits
121+
122+
1. **Centralized Rules Management**: Rules are stored in GitHub Gists, making them easy to edit and version control
123+
2. **Automatic Updates**: No need to manually update pinned messages when rules change
124+
3. **Multi-language Support**: Commands work in both English and Russian
125+
4. **Persistent Configuration**: Settings are saved and restored between bot restarts
126+
5. **Error Resilience**: Comprehensive error handling for network and API issues
127+
128+
## Future Enhancements
129+
130+
1. **Admin Permission Checks**: Implement proper VK admin permission verification
131+
2. **Multiple Gists**: Support multiple gist sources per chat
132+
3. **Custom Update Intervals**: Allow chats to configure monitoring frequency
133+
4. **Rich Text Formatting**: Support Markdown or HTML formatting in rules
134+
5. **Notification Settings**: Allow chats to configure update notifications
135+
6. **Backup/Restore**: Export/import rules configurations

python/__main__.py

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
"""Main Bot module.
33
"""
44
from datetime import datetime, timedelta
5-
from typing import NoReturn, List, Dict, Any
5+
from typing import NoReturn, List, Dict, Any, Optional
6+
import threading
7+
import time
68

79
from saya import Vk
810
import requests
911

1012
from modules import (
1113
BetterBotBaseDataService, Commands
1214
)
15+
from modules.rules_service import RulesService
1316
from tokens import BOT_TOKEN
1417
from userbot import UserBot
1518
import patterns
@@ -38,7 +41,8 @@ def __init__(
3841
self.messages_to_delete = {}
3942
self.userbot = UserBot()
4043
self.data = BetterBotBaseDataService()
41-
self.commands = Commands(self, self.data)
44+
self.rules_service = RulesService()
45+
self.commands = Commands(self, self.data, self.rules_service)
4246
self.commands.register_cmds(
4347
(patterns.HELP, self.commands.help_message),
4448
(patterns.INFO, self.commands.info_message),
@@ -63,8 +67,15 @@ def __init__(
6367
(patterns.WHAT_IS, self.commands.what_is),
6468
(patterns.WHAT_MEAN, self.commands.what_is),
6569
(patterns.APPLY_KARMA, self.commands.apply_karma),
66-
(patterns.GITHUB_COPILOT, self.commands.github_copilot)
70+
(patterns.GITHUB_COPILOT, self.commands.github_copilot),
71+
(patterns.SET_RULES_GIST, self.commands.set_rules_gist),
72+
(patterns.REMOVE_RULES_GIST, self.commands.remove_rules_gist),
73+
(patterns.GET_RULES_STATUS, self.commands.get_rules_status)
6774
)
75+
76+
# Start rules monitoring thread
77+
self.rules_monitor_thread = threading.Thread(target=self._monitor_rules, daemon=True)
78+
self.rules_monitor_thread.start()
6879

6980
def message_new(
7081
self,
@@ -167,6 +178,83 @@ def send_msg(
167178
dict(
168179
message=msg, peer_id=peer_id,
169180
disable_mentions=1, random_id=0))
181+
182+
def pin_message(
183+
self,
184+
peer_id: int,
185+
message_id: int
186+
) -> dict:
187+
"""Pin a message in chat
188+
189+
:param peer_id: chat ID
190+
:param message_id: message ID to pin
191+
"""
192+
return self.call_method(
193+
'messages.pin',
194+
dict(peer_id=peer_id, message_id=message_id))
195+
196+
def unpin_message(
197+
self,
198+
peer_id: int,
199+
message_id: int
200+
) -> dict:
201+
"""Unpin a message in chat
202+
203+
:param peer_id: chat ID
204+
:param message_id: message ID to unpin
205+
"""
206+
return self.call_method(
207+
'messages.unpin',
208+
dict(peer_id=peer_id, message_id=message_id))
209+
210+
def send_and_pin_message(
211+
self,
212+
msg: str,
213+
peer_id: int
214+
) -> Optional[int]:
215+
"""Send a message and pin it
216+
217+
:param msg: message text
218+
:param peer_id: chat ID
219+
:return: message ID if successful, None otherwise
220+
"""
221+
try:
222+
# Send message
223+
response = self.call_method(
224+
'messages.send',
225+
dict(
226+
message=msg, peer_id=peer_id,
227+
disable_mentions=1, random_id=0))
228+
229+
if 'response' in response:
230+
message_id = response['response']
231+
# Pin the message
232+
pin_response = self.pin_message(peer_id, message_id)
233+
if 'response' in pin_response:
234+
return message_id
235+
return None
236+
except Exception as e:
237+
print(f"Error sending and pinning message: {e}")
238+
return None
239+
240+
def edit_message(
241+
self,
242+
peer_id: int,
243+
message_id: int,
244+
message: str
245+
) -> dict:
246+
"""Edit a message
247+
248+
:param peer_id: chat ID
249+
:param message_id: message ID to edit
250+
:param message: new message text
251+
"""
252+
return self.call_method(
253+
'messages.edit',
254+
dict(
255+
peer_id=peer_id,
256+
message_id=message_id,
257+
message=message))
170258

171259
def get_user_name(
172260
self,
@@ -197,6 +285,69 @@ def get_messages(
197285
"""
198286
reply_message = event.get("reply_message", {})
199287
return [reply_message] if reply_message else event.get("fwd_messages", [])
288+
289+
def _monitor_rules(self) -> NoReturn:
290+
"""Background thread to monitor rules changes"""
291+
while True:
292+
try:
293+
# Check every 5 minutes
294+
time.sleep(300)
295+
296+
# Get all monitored chats
297+
monitored_chats = self.rules_service.get_all_monitored_chats()
298+
299+
for peer_id_str, config in monitored_chats.items():
300+
peer_id = int(peer_id_str)
301+
302+
# Check for updates
303+
updated_content = self.rules_service.check_gist_updates(peer_id)
304+
305+
if updated_content:
306+
# Rules have been updated
307+
new_rules_message = f"📋 Правила чата:\n\n{updated_content}"
308+
309+
# Try to edit existing pinned message
310+
pinned_message_id = config.get("pinned_message_id")
311+
312+
if pinned_message_id:
313+
try:
314+
# Try to edit the existing pinned message
315+
edit_response = self.edit_message(
316+
peer_id, pinned_message_id, new_rules_message)
317+
318+
if 'response' in edit_response:
319+
# Successfully edited
320+
self.send_msg(
321+
"🔄 Правила чата обновлены в закрепленном сообщении!",
322+
peer_id)
323+
else:
324+
# Failed to edit, create new pinned message
325+
self._create_new_pinned_rules(peer_id, new_rules_message)
326+
except Exception as e:
327+
print(f"Error editing pinned message: {e}")
328+
self._create_new_pinned_rules(peer_id, new_rules_message)
329+
else:
330+
# No existing pinned message, create new one
331+
self._create_new_pinned_rules(peer_id, new_rules_message)
332+
333+
except Exception as e:
334+
print(f"Error in rules monitoring: {e}")
335+
336+
def _create_new_pinned_rules(self, peer_id: int, rules_message: str) -> NoReturn:
337+
"""Helper method to create and pin new rules message"""
338+
try:
339+
message_id = self.send_and_pin_message(rules_message, peer_id)
340+
if message_id:
341+
self.rules_service.update_pinned_message_id(peer_id, message_id)
342+
self.send_msg(
343+
"🔄 Правила чата обновлены и закреплено новое сообщение!",
344+
peer_id)
345+
else:
346+
self.send_msg(
347+
"🔄 Правила чата обновлены, но не удалось закрепить сообщение.",
348+
peer_id)
349+
except Exception as e:
350+
print(f"Error creating new pinned rules: {e}")
200351

201352

202353
if __name__ == '__main__':

0 commit comments

Comments
 (0)