from __future__ import annotations from dataclasses import dataclass, field @dataclass class MattermostAccount: account_id: str server_url: str = "" bot_token: str = "" nick: str = "ForcePilot" username: str = "" realname: str = "" channels: list[str] = field(default_factory=list) nickserv_password: str = "" nickserv_register_email: str = "" enabled: bool = True @property def configured(self) -> bool: return bool(self.server_url and self.bot_token) @classmethod def from_config_entry(cls, account_id: str, entry: dict) -> MattermostAccount: return cls( account_id=account_id, server_url=entry.get("server_url", "") or entry.get("host", ""), bot_token=entry.get("bot_token", "") or entry.get("token", ""), nick=entry.get("nick", "ForcePilot"), username=entry.get("username", ""), realname=entry.get("realname", ""), channels=entry.get("channels", []), nickserv_password=entry.get("nickserv_password", ""), nickserv_register_email=entry.get("nickserv_register_email", ""), enabled=entry.get("enabled", True), ) DEFAULT_ACCOUNT_ID = "default" @dataclass class MultiAccountConfig: accounts: dict[str, MattermostAccount] = field(default_factory=dict) default_account_id: str = DEFAULT_ACCOUNT_ID @classmethod def from_config(cls, config: dict) -> MultiAccountConfig: accounts_data = config.get("accounts", {}) if not accounts_data: account = MattermostAccount.from_config_entry( DEFAULT_ACCOUNT_ID, { "server_url": config.get("server_url", ""), "bot_token": config.get("bot_token", ""), "nick": config.get("nick", "ForcePilot"), "username": config.get("username", ""), "realname": config.get("realname", ""), "channels": config.get("channels", []), "nickserv_password": config.get("nickserv_password", ""), "nickserv_register_email": config.get("nickserv_register_email", ""), "enabled": True, }, ) return cls(accounts={DEFAULT_ACCOUNT_ID: account}, default_account_id=DEFAULT_ACCOUNT_ID) accounts = {} for aid, entry in accounts_data.items(): accounts[aid] = MattermostAccount.from_config_entry(aid, entry) default_id = config.get("default_account_id", DEFAULT_ACCOUNT_ID) if default_id not in accounts and accounts: default_id = next(iter(accounts)) return cls(accounts=accounts, default_account_id=default_id) def resolve_account(self, account_id: str | None = None) -> MattermostAccount | None: if account_id and account_id in self.accounts: return self.accounts[account_id] if self.default_account_id in self.accounts: return self.accounts[self.default_account_id] return None def list_account_ids(self) -> list[str]: return list(self.accounts.keys()) def list_configured_accounts(self) -> list[MattermostAccount]: return [a for a in self.accounts.values() if a.configured and a.enabled] def merge_config( top_level: dict, account_config: dict, ) -> dict: merged = {**top_level, **account_config} top_commands = top_level.get("commands", {}) account_commands = account_config.get("commands", {}) if top_commands or account_commands: merged["commands"] = {**top_commands, **account_commands} top_interactions = top_level.get("interactions", {}) account_interactions = account_config.get("interactions", {}) if top_interactions or account_interactions: merged["interactions"] = {**top_interactions, **account_interactions} return merged