import sopel
import requests
import time
import threading
import os
import logging
import traceback
import sys
from datetime import timedelta
class YnMWebPlugin:
def __init__(self, bot):
self.bot = bot
try:
self.api_url = bot.config.ynmweb.api_url
self.api_key = bot.config.ynmweb.api_key
except AttributeError:
logging.error("YnM Web configuration not found.")
return
if not self.api_url or not self.api_key:
logging.error("Invalid YnM Web configuration: URL or API key is missing")
return
# Configure detailed logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(os.path.join(bot.config.core.homedir, 'ynmweb_debug.log')),
logging.StreamHandler(sys.stdout)
]
)
self.logger = logging.getLogger('sopel.plugins.ynmweb')
# Verzió küldés egyszeri kezdeményezése a bot indításakor
self.send_version()
# Start background threads
self.stop_event = threading.Event()
threading.Thread(target=self.api_loop, daemon=True, name="YnMWebAPILoop").start()
threading.Thread(target=self.update_channels, daemon=True, name="YnMWebChannelUpdate").start()
threading.Thread(target=self.update_uptime, daemon=True, name="YnMWebUptimeUpdate").start()
threading.Thread(target=self.update_server_uptime, daemon=True, name="YnMWebServerUptimeUpdate").start()
def send_version(self):
"""Elküldi a bot verzióját egyszer, indításkor."""
try:
sopel_version = sopel.__version__ # Sopel verzió
python_version = sys.version # Python verzió
# Az API számára elküldendő verzió string összeállítása
version_info = f"Sopel: {sopel_version} | Python: {python_version}"
# API kérés verzióval
self._make_api_request('version', version=version_info)
except Exception as e:
self.logger.error(f"Version Send Error: {e}")
def _make_api_request(self, command, **params):
full_params = {
'key': self.api_key,
'command': command,
**params
}
self.logger.debug(f"API Request Details:")
self.logger.debug(f"URL: {self.api_url}")
self.logger.debug(f"Command: {command}")
for key, value in full_params.items():
if key == 'key':
# Mask API key in logs
self.logger.debug(f"Param {key}: {'*' * len(str(value))}")
else:
self.logger.debug(f"Param {key}: {value}")
try:
response = requests.post(
self.api_url,
data=full_params,
timeout=10,
verify=True # Ensure SSL certificate verification
)
self.logger.debug(f"Response Status Code: {response.status_code}")
self.logger.debug(f"Response Headers: {response.headers}")
self.logger.debug(f"Response Content: {response.text}")
response.raise_for_status() # Raise exception for bad HTTP status
return response.json()
except requests.exceptions.RequestException as e:
self.logger.error(f"Network Error: {e}")
self.logger.error(traceback.format_exc())
except ValueError as e:
self.logger.error(f"JSON Parsing Error: {e}")
except Exception as e:
self.logger.error(f"Unexpected Error: {e}")
self.logger.error(traceback.format_exc())
return None
def api_loop(self):
while not self.stop_event.is_set():
try:
response = self._make_api_request('fetch')
if response and 'message' in response:
self._process_messages(response['message'])
except Exception as e:
self.logger.error(f"API Loop Error: {e}")
time.sleep(180)
def _process_messages(self, messages):
for msg in messages:
command = msg.get('command', '').lower()
msg_id = msg.get('id')
handlers = {
'rehash': self._handle_rehash,
'restart': self._handle_restart,
'die': self._handle_die,
'join': self._handle_join,
'part': self._handle_part
}
handler = handlers.get(command)
if handler:
handler(msg_id, msg.get('arguments', ''))
def _pickup(self, msg_id, success, msg=''):
self._make_api_request('pickup', action=msg_id, success=success, message=msg)
def _handle_join(self, msg_id, channel):
if channel.startswith('#'):
try:
self.bot.join(channel)
self._pickup(msg_id, 1, f"Add {channel}")
except Exception as e:
self._pickup(msg_id, 0, str(e))
else:
self._pickup(msg_id, 0, f"Invalid channel name: {channel}")
def _handle_part(self, msg_id, channel):
if channel.startswith('#'):
try:
self.bot.part(channel)
self._pickup(msg_id, 1, f"Del {channel}")
except Exception as e:
self._pickup(msg_id, 0, str(e))
else:
self._pickup(msg_id, 0, f"Invalid channel name: {channel}")
def _handle_rehash(self, msg_id, _):
self._pickup(msg_id, 1, "Reloading configuration...")
setup(self.bot) # Újrahívja a plugin betöltését
def _handle_restart(self, msg_id, _):
self._pickup(msg_id, 1, "Restarting bot...")
self.bot.restart()
def _handle_die(self, msg_id, _):
self._pickup(msg_id, 1, "Shutting down bot...")
self.stop_event.set() # Háttérszálak leállítása
self.bot.quit("Bot shutting down...") # IRC kapcsolat normálisan bontása
time.sleep(1) # Várunk kicsit, hogy befejezze
sys.exit(0) # Tiszta kilépés
def update_channels(self):
while not self.stop_event.is_set():
try:
channels = self.bot.channels
channel_list = ','.join(channels)
self._make_api_request('updatechannels', channels=channel_list)
except Exception as e:
self.logger.error(f"Channel Update Error: {e}")
time.sleep(300)
def update_uptime(self):
while not self.stop_event.is_set():
try:
current_time = int(time.time())
uptime = current_time - self.bot.startup_time # Sopel saját uptime-ja
readable_uptime = str(timedelta(seconds=uptime))
self._make_api_request('ontime', on_time=readable_uptime)
except Exception as e:
self.logger.error(f"Uptime Update Error: {e}")
time.sleep(90)
def update_server_uptime(self):
while not self.stop_event.is_set():
try:
with open('/proc/uptime', 'r') as f:
uptime_seconds = float(f.read().split()[0])
readable_uptime = str(timedelta(seconds=int(uptime_seconds)))
self._make_api_request('uptime', server_uptime=readable_uptime)
except Exception as e:
self.logger.error(f"Server Uptime Update Error: {e}")
time.sleep(90)
def configure(config):
config.define_section('ynmweb', 'YnM Web Configuration')
config.ynmweb.configure_setting('api_url', 'Enter the YnM Web API URL')
config.ynmweb.configure_setting('api_key', 'Enter the YnM Web API Key')
def setup(bot):
bot.startup_time = int(time.time()) # Indulási idő rögzítése
bot.ynm_plugin = YnMWebPlugin(bot)
def shutdown(bot):
if hasattr(bot, 'ynm_plugin'):
bot.ynm_plugin.stop_event.set()