Skip to content

Commit f3b712b

Browse files
konardclaude
andcommitted
Implement robust network reconnection handling for VK Bot
- Add NetworkHandler class with automatic retry logic and exponential backoff - Implement VkNetworkMixin to enhance Bot class with network resilience - Add connection health monitoring with background health checks - Update Bot and UserBot classes to use network error handling - Include comprehensive error logging and connection statistics - Add test scripts and examples demonstrating network resilience features This fixes the issue where the bot loses connection with VK during network changes or temporary disconnections. The bot now automatically detects network issues and reconnects gracefully. Fixes #70 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent cec30b3 commit f3b712b

File tree

6 files changed

+788
-10
lines changed

6 files changed

+788
-10
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""Example demonstrating the network resilience features of the VK Bot.
4+
5+
This example shows how the enhanced bot handles network disconnections,
6+
timeouts, and connection recovery automatically.
7+
"""
8+
9+
import sys
10+
import os
11+
import time
12+
13+
# Add python module path
14+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'python'))
15+
16+
try:
17+
from network_handler import NetworkHandler
18+
from __main__ import Bot
19+
from tokens import BOT_TOKEN
20+
import config
21+
except ImportError as e:
22+
print(f"Import error: {e}")
23+
print("This example requires the bot dependencies to be installed.")
24+
sys.exit(1)
25+
26+
27+
def demonstrate_network_resilience():
28+
"""Demonstrate network resilience features."""
29+
30+
print("=== VK Bot Network Resilience Demo ===")
31+
print()
32+
33+
# Create bot instance with network resilience
34+
print("1. Creating bot with network resilience...")
35+
bot = Bot(token=BOT_TOKEN, group_id=config.BOT_GROUP_ID, debug=True)
36+
print("✓ Bot created with automatic network error handling")
37+
print()
38+
39+
# Show network handler configuration
40+
handler = bot.network_handler
41+
print("2. Network Handler Configuration:")
42+
print(f" - Max retries: {handler.max_retries}")
43+
print(f" - Backoff factor: {handler.backoff_factor}")
44+
print(f" - Connection timeout: {handler.connection_timeout}s")
45+
print(f" - Read timeout: {handler.read_timeout}s")
46+
print(f" - Health check interval: {handler.health_check_interval}s")
47+
print()
48+
49+
# Show connection statistics
50+
print("3. Connection Statistics:")
51+
stats = handler.get_connection_stats()
52+
print(f" - Connected: {stats['is_connected']}")
53+
print(f" - Connection failures: {stats['connection_failures']}")
54+
print(f" - Last successful request: {stats['last_successful_request']}")
55+
print(f" - Time since last success: {stats['time_since_last_success']:.1f}s")
56+
print()
57+
58+
# Test API call
59+
print("4. Testing API call with network resilience...")
60+
try:
61+
# This call will automatically retry on network errors
62+
result = bot.call_method('users.get', {'user_ids': 1})
63+
if 'response' in result:
64+
user = result['response'][0]
65+
print(f"✓ API call successful: User ID {user.get('id')} retrieved")
66+
elif 'error' in result:
67+
print(f"⚠ VK API error: {result['error']['error_msg']}")
68+
else:
69+
print(f"? Unexpected response: {result}")
70+
except Exception as e:
71+
print(f"✗ Network error (after all retries): {e}")
72+
print()
73+
74+
# Monitor connection for a short time
75+
print("5. Monitoring connection health for 10 seconds...")
76+
start_time = time.time()
77+
while time.time() - start_time < 10:
78+
time.sleep(2)
79+
if handler.is_connected():
80+
print(" ✓ Connection healthy")
81+
else:
82+
print(" ⚠ Connection issues detected")
83+
print()
84+
85+
print("6. Features provided by network resilience:")
86+
print(" ✓ Automatic retry on connection errors")
87+
print(" ✓ Exponential backoff for retry delays")
88+
print(" ✓ Background health check monitoring")
89+
print(" ✓ Connection statistics tracking")
90+
print(" ✓ Detailed error logging")
91+
print(" ✓ Graceful handling of network changes")
92+
print()
93+
94+
print("Demo completed. The bot is now resilient to network issues!")
95+
96+
97+
if __name__ == '__main__':
98+
demonstrate_network_resilience()

experiments/network_test.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""Network connectivity test script to simulate connection issues.
4+
5+
This script helps understand how the current VK bot handles network disconnections
6+
and connection failures.
7+
"""
8+
import sys
9+
import os
10+
import time
11+
import signal
12+
import threading
13+
from unittest.mock import patch, MagicMock
14+
15+
# Add python module path to import Bot
16+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'python'))
17+
18+
try:
19+
import requests
20+
from saya import Vk
21+
from python.__main__ import Bot
22+
from python.tokens import BOT_TOKEN
23+
import python.config as config
24+
except ImportError as e:
25+
print(f"Import error: {e}")
26+
print("This is expected in the isolated test environment")
27+
sys.exit(1)
28+
29+
30+
class NetworkTestBot(Bot):
31+
"""Test bot with network failure simulation."""
32+
33+
def __init__(self, *args, **kwargs):
34+
self.connection_lost_count = 0
35+
self.reconnection_attempts = 0
36+
self.max_reconnection_attempts = 5
37+
self.reconnection_delay = 2 # seconds
38+
super().__init__(*args, **kwargs)
39+
40+
def call_method(self, method, params=None):
41+
"""Override call_method to simulate network failures."""
42+
try:
43+
return super().call_method(method, params)
44+
except (requests.exceptions.ConnectionError,
45+
requests.exceptions.Timeout,
46+
requests.exceptions.HTTPError) as e:
47+
print(f"Network error detected: {type(e).__name__}: {e}")
48+
self.connection_lost_count += 1
49+
return self._handle_network_error(method, params, e)
50+
51+
def _handle_network_error(self, method, params, error):
52+
"""Handle network errors with reconnection logic."""
53+
if self.reconnection_attempts >= self.max_reconnection_attempts:
54+
print(f"Max reconnection attempts ({self.max_reconnection_attempts}) reached. Giving up.")
55+
raise error
56+
57+
self.reconnection_attempts += 1
58+
print(f"Attempting reconnection {self.reconnection_attempts}/{self.max_reconnection_attempts}")
59+
60+
# Exponential backoff
61+
delay = self.reconnection_delay * (2 ** (self.reconnection_attempts - 1))
62+
print(f"Waiting {delay} seconds before retry...")
63+
time.sleep(delay)
64+
65+
try:
66+
result = super().call_method(method, params)
67+
print(f"Reconnection successful on attempt {self.reconnection_attempts}")
68+
self.reconnection_attempts = 0 # Reset on success
69+
return result
70+
except Exception as e:
71+
print(f"Reconnection attempt {self.reconnection_attempts} failed: {e}")
72+
return self._handle_network_error(method, params, e)
73+
74+
75+
def simulate_network_failure():
76+
"""Simulate network failures by patching requests."""
77+
78+
def failing_request(*args, **kwargs):
79+
"""Mock function that always raises connection error."""
80+
raise requests.exceptions.ConnectionError("Simulated network failure")
81+
82+
# Patch requests to simulate network failure
83+
with patch.object(requests.Session, 'post', side_effect=failing_request):
84+
with patch.object(requests.Session, 'get', side_effect=failing_request):
85+
print("Network failure simulation active")
86+
87+
try:
88+
# Create test bot instance
89+
bot = NetworkTestBot(token=BOT_TOKEN, group_id=config.BOT_GROUP_ID, debug=True)
90+
91+
# Test method call that should fail
92+
print("Testing API call with simulated network failure...")
93+
result = bot.call_method('users.get', {'user_ids': 1})
94+
print(f"Unexpected success: {result}")
95+
96+
except Exception as e:
97+
print(f"Final error after all retry attempts: {type(e).__name__}: {e}")
98+
99+
100+
def test_current_bot_resilience():
101+
"""Test how the current bot handles network issues."""
102+
print("Testing current bot network resilience...")
103+
104+
# Mock network issues
105+
def intermittent_failure(*args, **kwargs):
106+
"""Randomly fail some requests."""
107+
import random
108+
if random.random() < 0.7: # 70% failure rate
109+
raise requests.exceptions.ConnectionError("Intermittent network failure")
110+
return MagicMock()
111+
112+
with patch.object(requests.Session, 'post', side_effect=intermittent_failure):
113+
try:
114+
bot = Bot(token=BOT_TOKEN, group_id=config.BOT_GROUP_ID, debug=True)
115+
116+
# Test multiple calls
117+
for i in range(5):
118+
try:
119+
print(f"Attempt {i+1}: Making API call...")
120+
result = bot.call_method('users.get', {'user_ids': 1})
121+
print(f"Success: {result}")
122+
except Exception as e:
123+
print(f"Failed: {type(e).__name__}: {e}")
124+
time.sleep(1)
125+
126+
except Exception as e:
127+
print(f"Bot initialization failed: {e}")
128+
129+
130+
if __name__ == '__main__':
131+
print("=== VK Bot Network Resilience Test ===")
132+
print("This script tests how the bot handles network disconnections and failures.")
133+
print()
134+
135+
print("1. Testing with simulated complete network failure:")
136+
simulate_network_failure()
137+
print()
138+
139+
print("2. Testing with intermittent network failures:")
140+
test_current_bot_resilience()
141+
print()
142+
143+
print("Test completed.")

0 commit comments

Comments
 (0)