-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathtest-toolsV2.py
More file actions
180 lines (152 loc) · 7.1 KB
/
test-toolsV2.py
File metadata and controls
180 lines (152 loc) · 7.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import asyncio
import logging
import os
import signal
import sys
from typing import Optional, Dict, Any
from dotenv import load_dotenv
# Import Axiom Trade API
try:
from axiomtradeapi import AxiomTradeClient
from axiomtradeapi.tools import login_with_email_otp
except ImportError:
print("CRITICAL ERROR: 'axiomtradeapi' not found. Please install it using: pip install axiomtradeapi")
sys.exit(1)
# Configure logging structure for a professional application
# We create a specific logger for our bot to separate it from library logs
logger = logging.getLogger("UserMonitorBot")
logger.setLevel(logging.INFO)
# Console handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# File handler (keep a record of execution)
file_handler = logging.FileHandler("bot.log", encoding='utf-8')
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
class ActiveUserMonitorBot:
"""
A professional-grade bot to monitor active users on Axiom.
Features:
- Robust configuration loading
- Graceful shutdown handling
- Automatic token management
- Structured logging
- Continuous monitoring
"""
def __init__(self):
self.client: Optional[AxiomTradeClient] = None
self.is_running = False
self._load_config()
def _load_config(self):
"""Load configuration from environment variables."""
load_dotenv()
self.email = os.getenv('email') or os.getenv('EMAIL_ADDRESS')
self.axiom_password = os.getenv('AXIOM_PASSWORD')
self.email_app_password = os.getenv('EMAIL_APP_PASSWORD')
async def initialize(self):
"""Initialize the client and session."""
logger.info("Initializing UserMonitorBot...")
# Storage for session persistence
storage_dir = os.path.join(os.getcwd(), ".chipadev_data")
os.makedirs(storage_dir, exist_ok=True)
# Initialize client with storage to persist session
self.client = AxiomTradeClient(
username=self.email, # Needed for auto-relogin if implemented in lib
password=self.axiom_password, # Needed for optional login flows
storage_dir=storage_dir,
use_saved_tokens=True # Try to load from disk first
)
if not self.client.is_authenticated():
logger.info("No valid session found. Attempting login flow...")
await self._perform_login()
else:
logger.info("✅ Successfully resumed existing session.")
# Simulate browser connection to register as an active user
logger.info("🌍 Simulating full browser connection sequence...")
token_address = "8P5kBTzvG7xyjTZRzi4ftzpy6mnL74AHLtHDqyDq44ST"
# Run synchronous connect method in executor
loop = asyncio.get_event_loop()
await loop.run_in_executor(
None,
lambda: self.client.connect(token_address=token_address)
)
logger.info("✅ Browser connection simulation complete.")
async def _perform_login(self):
"""Handle the login process if not authenticated."""
if not self.email or not self.axiom_password or not self.email_app_password:
logger.error("Authentication required but credentials missing in .env.")
logger.error("Please provide EMAIL_ADDRESS, AXIOM_PASSWORD, and EMAIL_APP_PASSWORD.")
sys.exit(1)
logger.info(f"Attempting login for user: {self.email}")
# Use the tool provided by the library for email OTP login
# This is blocking in the example, so we wrap or call directly if it supports async
# Looking at examples/test-loginsystem.py, it seems synchronous or we need to check if it returns a coroutine.
# Most likely synchronous based on the example structure.
# We run this in a thread executor to avoid blocking the async loop if it's synchronous IO
loop = asyncio.get_event_loop()
try:
# Assuming login_with_email_otp is the function from the example
result = await loop.run_in_executor(
None,
lambda: login_with_email_otp(self.client, self.email_app_password)
)
if result and self.client.is_authenticated():
logger.info("✅ Login successful!")
else:
logger.error("❌ Login failed. Check credentials and OTP.")
sys.exit(1)
except Exception as e:
logger.error(f"Login process encountered an error: {e}")
sys.exit(1)
async def _user_count_callback(self, count: int):
"""Callback function triggered when user count updates."""
# Here you could implement logic to:
# - Save to database
# - Send alert if count drops below threshold
# - Update a dashboard
logger.info(f"👥 Current Active Users: {count}")
async def run(self):
"""Main execution loop."""
await self.initialize()
self.is_running = True
logger.info("🚀 Bot started. Press Ctrl+C to stop.")
# Setup signal handlers for graceful shutdown (PC only - Windows has limitations with add_signal_handler in loops)
# We'll use a try/finally block in the main loop instead for better cross-platform support generic
try:
while self.is_running:
logger.info("Starting monitoring cycle...")
# We monitor in chunks (e.g., 1 hour) to allow for periodic re-checks or restarts if needed
# or pass a very long duration if the library supports it.
# Just using a large number like 3600 (1 hour) for now.
try:
await self.client.get_active_axiom_users(
callback=self._user_count_callback,
duration=3600, # Monitor for 1 hour
token_address="8P5kBTzvG7xyjTZRzi4ftzpy6mnL74AHLtHDqyDq44ST"
)
except Exception as e:
logger.error(f"Connection lost or error in monitoring: {e}")
logger.info("Attempting to reconnect in 5 seconds...")
await asyncio.sleep(5)
except asyncio.CancelledError:
logger.info("Bot stopping...")
finally:
await self.shutdown()
async def shutdown(self):
"""Cleanup resources."""
logger.info("Shutting down gracefully...")
self.is_running = False
# If the client has a close method, call it here.
# if hasattr(self.client, 'close'): await self.client.close()
logger.info("👋 Goodbye!")
if __name__ == "__main__":
bot = ActiveUserMonitorBot()
try:
asyncio.run(bot.run())
except KeyboardInterrupt:
# This catches the Ctrl+C at the top level
pass