-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
144 lines (120 loc) · 6.93 KB
/
main.py
File metadata and controls
144 lines (120 loc) · 6.93 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
import random
import time
import sys
import sqlite3
from config import ApiConfig, BotStrategy, StealthConfig, Validation
from src.utils.logger import log
from src.database.manager import DatabaseManager
from src.services.gemini_client import GeminiClient
from src.services.crypto_price_client import CryptoPriceClient # Import new client
from src.bot.controller import BotController
def run_bot_cycle():
"""
Initializes all components and runs the main bot loop,
including Burst Mode and Fallback Logic.
"""
log.info("=============================================")
log.info("=== Reddit Bot v2.2 - Starting Engine... ===") # Version bump for new features
log.info("=============================================")
is_valid, message = Validation.validate_config()
if not is_valid:
log.critical(f"Configuration Error: {message}\nBot cannot start. Please check your config.py file.")
sys.exit(1)
log.info("Configuration validated successfully.")
db_manager = None
crypto_price_cli = None # Initialize here for finally block
try:
db_manager = DatabaseManager()
gemini_client = GeminiClient()
if ApiConfig.ENABLE_CRYPTO_PRICE_API: # Initialize crypto client if enabled
crypto_price_cli = CryptoPriceClient()
log.info("Crypto Price Client initialized.")
else:
log.info("Crypto Price API is disabled in config.")
if not gemini_client.model:
log.critical("Failed to initialize Gemini Client. Check API Key and model name. Bot cannot start.")
sys.exit(1) # Critical failure
except Exception as e:
log.critical(f"Failed to initialize core services: {e}", exc_info=True)
if db_manager: db_manager.close()
sys.exit(1)
log.info("Core services (Database, Gemini Client, Crypto Price Client) initialized.")
try:
while True:
log.info("--- Initiating new bot operational cycle ---")
if not ApiConfig.REDDIT_ACCOUNTS:
log.critical("No Reddit accounts configured. Halting.");
break
selected_account = random.choice(ApiConfig.REDDIT_ACCOUNTS)
# Pass crypto_price_cli to BotController
bot_instance = BotController(selected_account, db_manager, gemini_client, crypto_price_cli)
cycle_actions_performed_count = 0
# --- Determine if this is a Burst Mode cycle ---
is_burst_cycle = False
if BotStrategy.ENABLE_BURST_MODE and random.random() < BotStrategy.BURST_MODE_PROBABILITY:
is_burst_cycle = True
log.info(f"*** Burst Mode Engaged! Attempting up to {BotStrategy.ACTIONS_PER_BURST} actions. ***")
actions_to_attempt = BotStrategy.ACTIONS_PER_BURST if is_burst_cycle else 2 # 2 attempts for normal (1 primary + 1 fallback)
last_chosen_action = None
for attempt in range(actions_to_attempt):
if is_burst_cycle:
log.info(f"Burst Mode: Action {attempt + 1} of {actions_to_attempt}")
elif attempt > 0: # This is the fallback attempt in a normal cycle
log.info(f"Attempt 2 (Fallback) for account '{selected_account['username']}' in current cycle.")
else: # First attempt in a normal cycle
log.info(f"Attempt 1 for account '{selected_account['username']}' in current cycle.")
try:
# In burst mode, we don't want to exclude actions unless truly necessary (e.g., to avoid repetition)
# For simplicity, if an action fails in burst, it just tries another weighted random one next.
# The fallback (exclude_action) is more for the 2-attempt normal cycle.
exclude_this_action = last_chosen_action if not is_burst_cycle and attempt > 0 else None
action_successful, chosen_action = bot_instance.run(exclude_action=exclude_this_action)
if action_successful:
cycle_actions_performed_count += 1
if not is_burst_cycle: # If normal mode and first action succeeded, no fallback needed.
break
else:
log.warning(f"Action '{chosen_action}' was not productive in attempt {attempt + 1}.")
if not is_burst_cycle and attempt == 0: # First attempt failed in normal cycle
last_chosen_action = chosen_action # Store to exclude in fallback
elif is_burst_cycle and attempt == BotStrategy.ACTIONS_PER_BURST - 1: # Last attempt in burst
log.info("End of Burst Mode attempts for this cycle.")
except Exception as e:
log.error(f"An unexpected error occurred in bot action attempt {attempt + 1}: {e}", exc_info=True)
# Break from burst/fallback attempts on major error in controller.run()
break
if is_burst_cycle and attempt < BotStrategy.ACTIONS_PER_BURST - 1:
burst_cooldown = random.uniform(
BotStrategy.COOLDOWN_BETWEEN_BURST_ACTIONS_MIN_SECONDS,
BotStrategy.COOLDOWN_BETWEEN_BURST_ACTIONS_MAX_SECONDS
)
log.info(f"Short cooldown within Burst Mode. Sleeping for {burst_cooldown:.2f} seconds.")
time.sleep(burst_cooldown)
elif not is_burst_cycle and attempt == 0 and cycle_actions_performed_count == 0: # First attempt in normal cycle failed
time.sleep(random.uniform(5, 15)) # Very short delay before fallback
elif not is_burst_cycle and cycle_actions_performed_count > 0: # Successful action on first attempt of normal cycle
break
if cycle_actions_performed_count == 0:
log.info("No productive action performed in this full operational cycle.")
else:
log.info(f"Total of {cycle_actions_performed_count} productive action(s) performed in this cycle.")
# --- Main Cooldown ---
main_cooldown_seconds = random.uniform(
StealthConfig.COOLDOWN_MIN_MINUTES * 60,
StealthConfig.COOLDOWN_MAX_MINUTES * 60
)
log.info(f"Main cooldown for this cycle initiated. Sleeping for {main_cooldown_seconds / 60:.2f} minutes.")
time.sleep(main_cooldown_seconds)
finally:
if db_manager:
db_manager.close()
if __name__ == "__main__":
try:
run_bot_cycle()
except KeyboardInterrupt:
log.info("Shutdown signal received (Ctrl+C). Exiting gracefully.")
except Exception as e:
log.critical(f"A critical unhandled error occurred at the top level: {e}", exc_info=True)
finally:
log.info("Bot shutdown sequence complete.")
sys.exit(0)