Files
DarkflameServer/tools/chat-bridge/chat_bridge/__main__.py
Aaron Kimbrell 2d1ef0e370 feat: web dashboard and playground work
The NexusDashboard-parity dashboard (dDashboardServer) and everything built on it on the experimental branch:
accounts, characters, properties and moderation tools, permissions shared with in-game slash commands, economy
reports, World 3D and property 3D views with client scenery, scheduled events (features, vanity changes, live
events, announcements, restarts), vanity files and events, the CDClient browser, the message inspector with saved
captures, chat filter tools, community challenges, live ops, the AI moderator helper, and the server-side changes
they need.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:33:10 -05:00

85 lines
3.1 KiB
Python

"""python3 -m chat_bridge --config config.json [--check] [-v]"""
from __future__ import annotations
import argparse
import asyncio
import logging
import os
import sys
from .adapters import ADAPTERS
from .bridge import Bridge, Link
from .config import load_config
from .dashboard import ApiError, Dashboard
class _Subscribed(Exception):
pass
def _stop() -> None:
raise _Subscribed
async def check(dashboard: Dashboard) -> int:
"""Try the token: who it signs in as, and whether it can read and subscribe (it doesn't post anything)."""
me = await dashboard.me()
if not me.get("valid"):
print("token: not valid")
return 1
print(f"token: {me['username']} (account {me['accountId']}, GM {me['gmLevel']})")
ok = True
try:
page = await dashboard.chat_page({"limit": "1"})
print(f"read chat (chat_view): yes, newest id {page.get('last_id')}")
except ApiError as e:
print(f"read chat (chat_view): no ({e})")
ok = False
try:
await asyncio.wait_for(dashboard.watch_chat(lambda _e: None, _stop), 10)
except _Subscribed:
print("live chat (WebSocket): yes")
except ImportError:
print("live chat (WebSocket): no, the websockets package isn't installed (the bridge will poll)")
except Exception as e:
print(f"live chat (WebSocket): no ({e}); the bridge will poll")
print("send chat (chat_send): not tried here; see the Permissions page")
return 0 if ok else 1
def main() -> int:
parser = argparse.ArgumentParser(prog="chat_bridge", description="Bridge game chat to Discord and other services through the dashboard API")
parser.add_argument("--config", default=os.environ.get("CHAT_BRIDGE_CONFIG", "config.json"))
parser.add_argument("--check", action="store_true", help="check the token and permissions, then exit")
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args()
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO, stream=sys.stderr,
format="%(asctime)s %(levelname)s %(name)s: %(message)s")
# websockets' debug log prints request headers, which carry the API token
logging.getLogger("websockets").setLevel(logging.INFO)
config = load_config(args.config, {name: cls.default_label for name, cls in ADAPTERS.items()})
if not os.path.isabs(config.state_file):
config.state_file = os.path.join(os.path.dirname(os.path.abspath(args.config)), config.state_file)
dashboard = Dashboard(config.url, config.token)
if args.check:
return asyncio.run(check(dashboard))
links = []
for link_cfg in config.links:
if link_cfg.adapter not in ADAPTERS:
parser.error(f"unknown adapter {link_cfg.adapter!r}; known: {', '.join(ADAPTERS)}")
links.append(Link(link_cfg, ADAPTERS[link_cfg.adapter](link_cfg.settings)))
try:
asyncio.run(Bridge(config, dashboard, links).run())
except KeyboardInterrupt:
pass
except RuntimeError as e:
logging.error("%s", e)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())