0.4.0.0.a1

- Fully implemented swagger-like docs site
  - Full search functionality
  - Command type tags
  - Usage examples
  - Mobile-friendly
  - Command details available through the "Details" panel
  - Dynamically keeps information up-to-date
This commit is contained in:
Franz Rolfsvaag 2025-08-13 12:19:09 +02:00
parent 21a79194dd
commit 47cc285919
6 changed files with 962 additions and 223 deletions

1
.gitignore vendored
View File

@ -12,6 +12,7 @@ data.json.bak
settings*.conf
NOTES.md
sanity/
.offline_data.json
# Tools
wrapper/

View File

@ -0,0 +1 @@
{}

7
assets/docs/no.svg Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 22 16">
<title>Flag of Norway</title>
<rect width="22" height="16" fill="#ba0c2f"/>
<path d="M0,8h22M8,0v16" stroke="#fff" stroke-width="4"/>
<path d="M0,8h22M8,0v16" stroke="#00205b" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 319 B

2
bot.py
View File

@ -9,7 +9,7 @@ from modules.common.boot_notice import post_boot_notice
# Version consists of:
# Major.Enhancement.Minor.Patch.Test (Test is alphanumeric; doesnt trigger auto update)
VERSION = "0.3.9.8.a1"
VERSION = "0.4.0.0.a1"
# ---------- Env loading ----------

148
dev/offline_preview.py Normal file
View File

@ -0,0 +1,148 @@
# offline_preview.py
"""
ShaiWatcher offline preview (Discord-less)
Run from anywhere:
# optional (forces root if your layout is odd)
# export SHAI_PROJECT_ROOT=/absolute/path/to/shaiwatcher
# export SHAI_DOCS_HOST=127.0.0.1
# export SHAI_DOCS_PORT=8911
# export SHAI_OFFLINE=1
python3 offline_preview.py
"""
import os
import sys
import asyncio
import pathlib
import traceback
VERSION = "offline-preview-3"
# ---------- repo root discovery ----------
def _find_project_root() -> pathlib.Path:
cand = []
env = os.environ.get("SHAI_PROJECT_ROOT")
if env:
cand.append(pathlib.Path(env).resolve())
here = pathlib.Path(__file__).resolve().parent
cand.extend([
pathlib.Path.cwd().resolve(), # current working dir
here, # folder containing this file
here.parent, # one level up
here.parent.parent, # two levels up
])
# Also walk upwards from CWD a few levels to be forgiving
cur = pathlib.Path.cwd().resolve()
for _ in range(5):
cand.append(cur)
cur = cur.parent
tried = []
for c in cand:
tried.append(str(c))
if (c / "modules").is_dir() and (c / "modules" / "common").is_dir():
return c
raise FileNotFoundError(
"Could not locate project root with a 'modules/common' folder.\n"
f"Tried:\n - " + "\n - ".join(tried) +
"\nTip: set SHAI_PROJECT_ROOT=/absolute/path/to/repo"
)
PROJECT_ROOT = _find_project_root()
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
# ---------- now safe to import project modules ----------
import discord # type: ignore
from discord.ext import commands # type: ignore
# Optional: your config helper if cogs expect it to exist
try:
from modules.common.settings import cfg as cfg_helper # noqa: F401
except Exception as e:
print("[OFFLINE] Warning: couldn't import cfg helper:", repr(e))
def _discover_extensions(project_root: pathlib.Path):
modules_path = project_root / "modules"
exts = []
for folder in modules_path.iterdir():
if not folder.is_dir():
continue
if folder.name == "common": # match your prod loader
continue
for file in folder.glob("*.py"):
if file.name == "__init__.py":
continue
exts.append(f"modules.{folder.name}.{file.stem}")
return exts
async def main():
print(f"[OFFLINE] ShaiWatcher offline preview v{VERSION}")
print(f"[OFFLINE] Project root -> {PROJECT_ROOT}")
# Keep intents minimal; we never connect anyway
intents = discord.Intents.none()
intents.guilds = True
bot = commands.Bot(command_prefix="!", intents=intents)
# Mark environment as offline for any cogs that check it
os.environ.setdefault("SHAI_OFFLINE", "1")
# Bind docs to localhost by default while testing
os.environ.setdefault("SHAI_DOCS_HOST", "127.0.0.1")
os.environ.setdefault("SHAI_DOCS_PORT", "8911")
os.environ.setdefault("SHAI_DOCS_TITLE", "ShaiWatcher (Offline Preview)")
# Optional: isolate data file so we don't touch prod paths
data_file = os.environ.get("SHAI_DATA", str(PROJECT_ROOT / ".offline_data.json"))
try:
from data_manager import DataManager # if your project has this at root
os.makedirs(os.path.dirname(data_file) or ".", 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)
print(f"[OFFLINE] DATA_FILE -> {data_file}")
except Exception as e:
print("[OFFLINE] DataManager unavailable/failed:", repr(e))
os.environ.setdefault("SHAI_OFFLINE", "1") # before loading cogs
# Load extensions exactly like prod
failures = 0
for ext in _discover_extensions(PROJECT_ROOT):
try:
await bot.load_extension(ext)
print(f"[Modules] Loaded: {ext}")
except Exception as e:
failures += 1
print(f"[Modules] Failed to load {ext}: {e}")
traceback.print_exc()
if failures:
print(f"[OFFLINE] Loaded with {failures} module error(s). See logs above.")
docs = bot.get_cog("DocsSite")
if docs and hasattr(docs, "force_ready"):
docs.force_ready(True)
# Make is_ready() == True so DocsSite serves immediately
try:
# discord.py sets this in login/READY; we emulate it
if not hasattr(bot, "_ready") or bot._ready is None: # type: ignore[attr-defined]
bot._ready = asyncio.Event() # type: ignore[attr-defined]
bot._ready.set() # type: ignore[attr-defined]
except Exception:
pass
print("[OFFLINE] Docs: http://%s:%s/"
% (os.environ.get("SHAI_DOCS_HOST", "127.0.0.1"),
os.environ.get("SHAI_DOCS_PORT", "8911")))
print("[OFFLINE] This runner does NOT connect to Discord.")
# Idle forever; DocsSite runs in its own daemon thread
await asyncio.Event().wait()
if __name__ == "__main__":
asyncio.run(main())

File diff suppressed because it is too large Load Diff