0.3.9.2.a4
This commit is contained in:
		
							parent
							
								
									b152440241
								
							
						
					
					
						commit
						979a5ecd4f
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -10,6 +10,7 @@ data/
 | 
				
			|||||||
data.json
 | 
					data.json
 | 
				
			||||||
data.json.bak
 | 
					data.json.bak
 | 
				
			||||||
settings*.conf
 | 
					settings*.conf
 | 
				
			||||||
 | 
					NOTES.md
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Tools
 | 
					# Tools
 | 
				
			||||||
wrapper/
 | 
					wrapper/
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										80
									
								
								bot.py
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								bot.py
									
									
									
									
									
								
							@ -8,44 +8,29 @@ from modules.common.boot_notice import post_boot_notice
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Version consists of:
 | 
					# Version consists of:
 | 
				
			||||||
# Major.Enhancement.Minor.Patch.Test  (Test is alphanumeric; doesn’t trigger auto update)
 | 
					# Major.Enhancement.Minor.Patch.Test  (Test is alphanumeric; doesn’t trigger auto update)
 | 
				
			||||||
VERSION = "0.3.9.2.a3"
 | 
					VERSION = "0.3.9.2.a4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ---------- Env & config loading ----------
 | 
					# ---------- Env & config loading ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
load_dotenv()
 | 
					load_dotenv()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TOKEN = os.getenv('DISCORD_TOKEN', '').strip()
 | 
					def _get_env(name: str, default: str = "") -> str:
 | 
				
			||||||
CONFIG_PATH = os.getenv('SHAI_CONFIG', '/config/settings.conf')
 | 
					    v = os.getenv(name, "")
 | 
				
			||||||
 | 
					    # normalize stray quotes/whitespace Portainer sometimes injects
 | 
				
			||||||
 | 
					    v = (v or "").strip().strip('"').strip("'")
 | 
				
			||||||
 | 
					    return v if v else default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config = ConfigParser()
 | 
					TOKEN = _get_env("DISCORD_TOKEN")
 | 
				
			||||||
read_files = config.read(CONFIG_PATH)
 | 
					DATA_FILE = _get_env("SHAI_DATA") or _get_env("SHAI_DATA_FILE") or "/data/data.json"
 | 
				
			||||||
if not read_files:
 | 
					CHECK_TIME_UTC = _get_env("CHECK_TIME_UTC", "03:00")
 | 
				
			||||||
    print(f"[Config] INFO: no config at {CONFIG_PATH} (or unreadable). Will rely on env + defaults.")
 | 
					IGNORE_TEST_LEVEL = int(_get_env("IGNORE_TEST_LEVEL", "1"))
 | 
				
			||||||
if 'DEFAULT' not in config:
 | 
					SHAI_HOME_GUILD_ID = _get_env("SHAI_HOME_GUILD_ID")
 | 
				
			||||||
    config['DEFAULT'] = {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _overlay_env_into_config(cfg: ConfigParser):
 | 
					print("[Config] DISCORD_TOKEN set:", bool(TOKEN))
 | 
				
			||||||
    d = cfg['DEFAULT']
 | 
					print("[Config] DATA_FILE:", DATA_FILE)
 | 
				
			||||||
    for k, v in os.environ.items():
 | 
					print("[Config] CHECK_TIME_UTC:", CHECK_TIME_UTC)
 | 
				
			||||||
        if not k.startswith('SHAI_'):
 | 
					print("[Config] IGNORE_TEST_LEVEL:", IGNORE_TEST_LEVEL)
 | 
				
			||||||
            continue
 | 
					print("[Config] SHAI_HOME_GUILD_ID:", SHAI_HOME_GUILD_ID or "(unset)")
 | 
				
			||||||
        key = k[5:].lower()
 | 
					 | 
				
			||||||
        if key == 'data':
 | 
					 | 
				
			||||||
            key = 'data_file'
 | 
					 | 
				
			||||||
        if v is None:
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        vv = str(v).strip().strip('"').strip("'")
 | 
					 | 
				
			||||||
        if key == 'data_file' and not vv:
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        d[key] = vv
 | 
					 | 
				
			||||||
    if not d.get('data_file', '').strip():
 | 
					 | 
				
			||||||
        d['data_file'] = '/data/data.json'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
print("[Config] SHAI_CONFIG:", os.getenv('SHAI_CONFIG', '(unset)'))
 | 
					 | 
				
			||||||
print("[Config] DEFAULT keys now:", list(config['DEFAULT'].keys()))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# IMPORTANT: apply env overlay BEFORE we read values from config
 | 
					 | 
				
			||||||
_overlay_env_into_config(config)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ---------- Discord intents ----------
 | 
					# ---------- Discord intents ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -58,16 +43,25 @@ intents.emojis_and_stickers = True
 | 
				
			|||||||
intents.voice_states = True
 | 
					intents.voice_states = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ---------- Bot + DataManager ----------
 | 
					# ---------- Bot + DataManager ----------
 | 
				
			||||||
 | 
					 | 
				
			||||||
ddefault = config['DEFAULT']
 | 
					 | 
				
			||||||
data_file = (ddefault.get('data_file', '') or '').strip() or '/data/data.json'
 | 
					 | 
				
			||||||
if not TOKEN:
 | 
					if not TOKEN:
 | 
				
			||||||
    print("[Config] WARNING: DISCORD_TOKEN not set (env). Bot will fail to log in.")
 | 
					    print("[Config] WARNING: DISCORD_TOKEN is empty. The bot will fail to log in.")
 | 
				
			||||||
print(f"[Config] Using data_file: {data_file}")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
bot = commands.Bot(command_prefix='!', intents=intents)
 | 
					bot = commands.Bot(command_prefix='!', intents=intents)
 | 
				
			||||||
bot.config = config
 | 
					
 | 
				
			||||||
bot.data_manager = DataManager(data_file)
 | 
					# If you still want to pass “config” around, put a tiny dict on bot:
 | 
				
			||||||
 | 
					bot.config = {
 | 
				
			||||||
 | 
					    "check_time_utc": CHECK_TIME_UTC,
 | 
				
			||||||
 | 
					    "ignore_test_level": IGNORE_TEST_LEVEL,
 | 
				
			||||||
 | 
					    "home_guild_id": SHAI_HOME_GUILD_ID,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Ensure the data path exists and file is seeded if missing
 | 
				
			||||||
 | 
					os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
 | 
				
			||||||
 | 
					if not os.path.exists(DATA_FILE):
 | 
				
			||||||
 | 
					    with open(DATA_FILE, "w", encoding="utf-8") as f:
 | 
				
			||||||
 | 
					        f.write("{}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bot.data_manager = DataManager(DATA_FILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ---------- Self-check helpers ----------
 | 
					# ---------- Self-check helpers ----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -112,12 +106,11 @@ async def on_ready():
 | 
				
			|||||||
          "/ message_content:", bot.intents.message_content,
 | 
					          "/ message_content:", bot.intents.message_content,
 | 
				
			||||||
          "/ voice_states:", bot.intents.voice_states)
 | 
					          "/ voice_states:", bot.intents.voice_states)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Per-guild permission sanity checks (console log)
 | 
					    await asyncio.gather(*[_guild_selfcheck(g, bot.config) for g in bot.guilds])
 | 
				
			||||||
    await asyncio.gather(*[_guild_selfcheck(g, bot.config['DEFAULT']) for g in bot.guilds])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Slash command sync
 | 
					    # Slash sync — now reading from dict
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        dev_gid = bot.config['DEFAULT'].get('dev_guild_id')
 | 
					        dev_gid = bot.config.get('dev_guild_id')
 | 
				
			||||||
        if dev_gid:
 | 
					        if dev_gid:
 | 
				
			||||||
            guild = bot.get_guild(int(dev_gid))
 | 
					            guild = bot.get_guild(int(dev_gid))
 | 
				
			||||||
            if guild:
 | 
					            if guild:
 | 
				
			||||||
@ -132,7 +125,6 @@ async def on_ready():
 | 
				
			|||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
        print("[Slash] Sync failed:", repr(e))
 | 
					        print("[Slash] Sync failed:", repr(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Post boot status message (wrapper/env-driven or RSS fallback)
 | 
					 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        await post_boot_notice(bot)
 | 
					        await post_boot_notice(bot)
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
@ -156,7 +148,7 @@ def _install_signal_handlers(loop, bot):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            loop.add_signal_handler(s, _graceful)
 | 
					            loop.add_signal_handler(s, _graceful)
 | 
				
			||||||
        except NotImplementedError:
 | 
					        except NotImplementedError:
 | 
				
			||||||
            pass  # Windows
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def main():
 | 
					async def main():
 | 
				
			||||||
    async with bot:
 | 
					    async with bot:
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user