Upload files to "/"
parent
6ba99c9a57
commit
72fb3f2a7d
|
@ -0,0 +1,129 @@
|
|||
# bots.py
|
||||
import os
|
||||
import json
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
import traceback
|
||||
import time
|
||||
from collections import Counter
|
||||
from discord.ext import commands
|
||||
from dotenv import load_dotenv
|
||||
from bot_discord import DiscordBot
|
||||
from bot_twitch import TwitchBot
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Load bot configuration
|
||||
CONFIG_PATH = "config.json"
|
||||
try:
|
||||
with open(CONFIG_PATH, "r") as f:
|
||||
config_data = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print("Error: config.json not found.")
|
||||
sys.exit(1)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error parsing config.json: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Global settings
|
||||
ERROR_LOG_FILE = "error_log.txt"
|
||||
DISCORD_USER_ID = os.getenv("DISCORD_OWNER_ID") # Your Discord user ID for DM alerts
|
||||
DISCORD_BOT_TOKEN = os.getenv("DISCORD_BOT_TOKEN") # Discord bot token
|
||||
ERROR_CACHE = [] # Stores pending errors
|
||||
LAST_ERROR_SENT = 0 # Timestamp of last Discord error message
|
||||
ERROR_COOLDOWN = 900 # 15 minutes (900 sec) cooldown between Discord reports
|
||||
ERROR_BATCH_TIMEOUT = 5 # 5 seconds batching delay before sending
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||
stream=sys.stdout
|
||||
)
|
||||
logger = logging.getLogger("Main")
|
||||
|
||||
###############################
|
||||
# Error Handling & Discord Alerts
|
||||
###############################
|
||||
|
||||
async def send_discord_error_report(bot):
|
||||
"""
|
||||
Sends cached errors as a Discord DM with cooldowns and batching.
|
||||
If it fails, logs to error_log.txt instead.
|
||||
"""
|
||||
global ERROR_CACHE, LAST_ERROR_SENT
|
||||
|
||||
if not ERROR_CACHE:
|
||||
return # No errors to send
|
||||
|
||||
# Wait for batching timeout
|
||||
await asyncio.sleep(ERROR_BATCH_TIMEOUT)
|
||||
|
||||
# Compress duplicate errors
|
||||
error_counter = Counter(ERROR_CACHE)
|
||||
compressed_errors = [f"{err} (Occurred {count} times)" if count > 1 else err for err, count in error_counter.items()]
|
||||
error_message = "\n".join(compressed_errors)
|
||||
|
||||
# Ensure message fits in Discord limits
|
||||
if len(error_message) > 2000:
|
||||
error_message = error_message[:1990] + "\n... (truncated)"
|
||||
|
||||
# Update timestamp
|
||||
LAST_ERROR_SENT = time.time()
|
||||
|
||||
try:
|
||||
user = await bot.fetch_user(DISCORD_USER_ID)
|
||||
await user.send(f"**Bot Error Report**\n\n{error_message}")
|
||||
logger.info(f"[ERROR REPORT] Sent {len(ERROR_CACHE)} errors at {time.strftime('%H:%M:%S')} | Discord Send Status: Success")
|
||||
except Exception as e:
|
||||
# If Discord DM fails, log errors to a file
|
||||
with open(ERROR_LOG_FILE, "a") as f:
|
||||
timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
|
||||
f.write(f"\n----- {len(ERROR_CACHE)} Errors Occurred at {timestamp} -----\n")
|
||||
for err in ERROR_CACHE:
|
||||
f.write(f"{err}\n")
|
||||
|
||||
logger.error(f"[ERROR REPORT] Failed to send Discord DM. Errors saved to {ERROR_LOG_FILE}")
|
||||
|
||||
# Clear cache after sending
|
||||
ERROR_CACHE = []
|
||||
|
||||
def log_error(error_msg):
|
||||
"""
|
||||
Handles error logging with cooldowns.
|
||||
If multiple errors occur in a short time, they are cached and sent in batch.
|
||||
"""
|
||||
global ERROR_CACHE, LAST_ERROR_SENT
|
||||
|
||||
ERROR_CACHE.append(error_msg)
|
||||
if time.time() - LAST_ERROR_SENT >= ERROR_COOLDOWN:
|
||||
try:
|
||||
discord_bot
|
||||
except NameError:
|
||||
discord_bot = DiscordBot(config_data, logger)
|
||||
asyncio.create_task(send_discord_error_report(discord_bot))
|
||||
|
||||
###############################
|
||||
# Main Event Loop
|
||||
###############################
|
||||
|
||||
async def main():
|
||||
bot_start_time = time.time()
|
||||
global discord_bot, twitch_bot
|
||||
|
||||
discord_bot = DiscordBot(config_data, logger)
|
||||
twitch_bot = TwitchBot(config_data, logger)
|
||||
|
||||
discord_task = asyncio.create_task(discord_bot.run(DISCORD_BOT_TOKEN))
|
||||
twitch_task = asyncio.create_task(twitch_bot.run())
|
||||
|
||||
await asyncio.gather(discord_task, twitch_task)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except Exception as e:
|
||||
error_trace = traceback.format_exc()
|
||||
log_error(f"Fatal Error: {e}\n{error_trace}")
|
Loading…
Reference in New Issue