v0.3.9.a2
Added experimental bot wrapper functionality for completely automated updates and restarts
This commit is contained in:
parent
9fcd55ec4b
commit
dee0c4a5b4
4
.gitignore
vendored
4
.gitignore
vendored
@ -10,3 +10,7 @@ data/
|
|||||||
data.json
|
data.json
|
||||||
data.json.bak
|
data.json.bak
|
||||||
settings*.conf
|
settings*.conf
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
wrapper/
|
||||||
|
wrapper/tools/
|
53
bot.py
53
bot.py
@ -1,15 +1,14 @@
|
|||||||
import os
|
|
||||||
import asyncio
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from data_manager import DataManager
|
from data_manager import DataManager
|
||||||
|
from modules.common.boot_notice import post_boot_notice
|
||||||
import pathlib
|
import pathlib
|
||||||
import os, asyncio, xml.etree.ElementTree as ET
|
import os, signal, asyncio, xml.etree.ElementTree as ET
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
VERSION="0.0.9"
|
VERSION="0.3.9.a2"
|
||||||
|
|
||||||
# ---------- Env & config loading ----------
|
# ---------- Env & config loading ----------
|
||||||
|
|
||||||
@ -130,6 +129,33 @@ async def _fetch_latest_from_rss(url: str):
|
|||||||
|
|
||||||
# ---------- boot notice ----------
|
# ---------- boot notice ----------
|
||||||
|
|
||||||
|
async def _maybe_post_boot_notice(bot):
|
||||||
|
status = os.getenv("SHAI_BOOT_STATUS", "")
|
||||||
|
if not status:
|
||||||
|
return
|
||||||
|
desc = os.getenv("SHAI_BOOT_DESC", "")
|
||||||
|
old_v = os.getenv("SHAI_BOOT_OLD", "")
|
||||||
|
new_v = os.getenv("SHAI_BOOT_NEW", "")
|
||||||
|
|
||||||
|
if status == "fetched_new":
|
||||||
|
line = f"Successfully fetched, cached, and booted new version: v{old_v or '0.0.0.0'} -> v{new_v}"
|
||||||
|
elif status == "cached_no_update":
|
||||||
|
line = f"Successfully booted from cached version: v{new_v}. No new update found"
|
||||||
|
else:
|
||||||
|
line = f"Successfully booted from cached version: v{new_v}. Program repository not accessible!"
|
||||||
|
|
||||||
|
ch_id = int(bot.config['DEFAULT'].get('modlog_channel_id', "0") or 0)
|
||||||
|
ch = None
|
||||||
|
for g in bot.guilds:
|
||||||
|
ch = g.get_channel(ch_id)
|
||||||
|
if ch: break
|
||||||
|
if ch:
|
||||||
|
try:
|
||||||
|
msg = line if not desc else f"{line}\n_{desc}_"
|
||||||
|
await ch.send(msg, allowed_mentions=discord.AllowedMentions.none())
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
async def _post_boot_notice():
|
async def _post_boot_notice():
|
||||||
|
|
||||||
msg = f"Self-update and reboot successful! (v.{VERSION})"
|
msg = f"Self-update and reboot successful! (v.{VERSION})"
|
||||||
@ -176,8 +202,11 @@ async def on_ready():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("[Slash] Sync failed:", repr(e))
|
print("[Slash] Sync failed:", repr(e))
|
||||||
|
|
||||||
# Boot notice in modlog
|
# Post BSM if present
|
||||||
await _post_boot_notice()
|
try:
|
||||||
|
await post_boot_notice(bot)
|
||||||
|
except Exception as e:
|
||||||
|
print("[BootNotice] failed:", repr(e))
|
||||||
|
|
||||||
# ---------- Auto-discover extensions ----------
|
# ---------- Auto-discover extensions ----------
|
||||||
|
|
||||||
@ -190,6 +219,16 @@ for folder in modules_path.iterdir():
|
|||||||
continue
|
continue
|
||||||
extensions.append(f"modules.{folder.name}.{file.stem}")
|
extensions.append(f"modules.{folder.name}.{file.stem}")
|
||||||
|
|
||||||
|
def _install_signal_handlers(loop, bot):
|
||||||
|
def _graceful(*_):
|
||||||
|
# ask discord.py to close cleanly
|
||||||
|
loop.create_task(bot.close())
|
||||||
|
for s in (signal.SIGTERM, signal.SIGINT):
|
||||||
|
try:
|
||||||
|
loop.add_signal_handler(s, _graceful)
|
||||||
|
except NotImplementedError:
|
||||||
|
pass # Windows
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
async with bot:
|
async with bot:
|
||||||
for ext in extensions:
|
for ext in extensions:
|
||||||
@ -198,6 +237,8 @@ async def main():
|
|||||||
print(f"[Modules] Loaded: {ext}")
|
print(f"[Modules] Loaded: {ext}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Modules] Failed to load {ext}:", repr(e))
|
print(f"[Modules] Failed to load {ext}:", repr(e))
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
_install_signal_handlers(loop, bot)
|
||||||
await bot.start(TOKEN)
|
await bot.start(TOKEN)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
45
modules/common/boot_notice.py
Normal file
45
modules/common/boot_notice.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# modules/common/boot_notice.py
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import discord
|
||||||
|
|
||||||
|
async def post_boot_notice(bot: discord.Client):
|
||||||
|
# Needs modlog_channel_id in config/env
|
||||||
|
cfg = bot.config['DEFAULT']
|
||||||
|
ch_raw = cfg.get('modlog_channel_id') or os.getenv('SHAI_MODLOG_CHANNEL_ID')
|
||||||
|
if not ch_raw:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
ch_id = int(ch_raw)
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
ch = None
|
||||||
|
for g in bot.guilds:
|
||||||
|
ch = g.get_channel(ch_id)
|
||||||
|
if ch:
|
||||||
|
break
|
||||||
|
if not ch:
|
||||||
|
return
|
||||||
|
|
||||||
|
status = os.getenv("SHAI_BOOT_STATUS", "").strip()
|
||||||
|
oldver = os.getenv("SHAI_BOOT_OLDVER", "").strip()
|
||||||
|
newver = os.getenv("SHAI_BOOT_NEWVER", "").strip()
|
||||||
|
commit = os.getenv("SHAI_BUILD_COMMIT", "").strip()
|
||||||
|
subject = os.getenv("SHAI_BUILD_SUBJECT", "").strip()
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return
|
||||||
|
|
||||||
|
parts = [f"**Boot**: {status}"]
|
||||||
|
if oldver or newver:
|
||||||
|
parts.append(f"**Version**: {oldver or '?'} → {newver or '?'}")
|
||||||
|
if commit:
|
||||||
|
parts.append(f"**Commit**: `{commit}`")
|
||||||
|
if subject and len(subject) > 5:
|
||||||
|
parts.append(f"**Note**: {subject}")
|
||||||
|
|
||||||
|
msg = " | ".join(parts) + f" — <t:{int(time.time())}:R>"
|
||||||
|
try:
|
||||||
|
await ch.send(msg, allowed_mentions=discord.AllowedMentions.none())
|
||||||
|
except Exception:
|
||||||
|
pass
|
@ -87,6 +87,10 @@ class NickNudgeCog(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
if not guild:
|
if not guild:
|
||||||
return
|
return
|
||||||
|
# If a pending review already exists for this user in this guild, do nothing
|
||||||
|
for r in self.bot.data_manager.get('nick_reviews'):
|
||||||
|
if r.get('guild_id') == guild.id and r.get('user_id') == member.id and r.get('status') == 'pending':
|
||||||
|
return
|
||||||
mod_ch = guild.get_channel(self.mod_channel_id)
|
mod_ch = guild.get_channel(self.mod_channel_id)
|
||||||
if not mod_ch:
|
if not mod_ch:
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user