89 lines
2.7 KiB
Python
89 lines
2.7 KiB
Python
import platform
|
|
import re
|
|
from pathlib import Path
|
|
from typing import *
|
|
|
|
import yaml
|
|
|
|
|
|
class ConfigError(RuntimeError):
|
|
pass
|
|
|
|
|
|
class Fallbackdict(dict):
|
|
"""Like defaultdict, but not implicitly creating entries."""
|
|
|
|
def __init__(self, default):
|
|
super().__init__()
|
|
self._default = default
|
|
|
|
def __missing__(self, key):
|
|
return self._default
|
|
|
|
|
|
class Config:
|
|
def __init__(self, path):
|
|
self.load(path)
|
|
|
|
def load(self, path):
|
|
with open(path) as fp:
|
|
self.config = yaml.safe_load(fp)
|
|
|
|
self.command_prefix = self.get("command_prefix")
|
|
|
|
# Logging
|
|
self.loglevel = self.get("loglevel", default="INFO")
|
|
|
|
# Storage
|
|
self.store_path = Path(self.get("storage.store_path"))
|
|
|
|
# Matrix
|
|
self.user_id = self.get("matrix.user_id")
|
|
if not re.fullmatch("@.*:.*", self.user_id):
|
|
raise ConfigError("matrix.user_id must be in the form @name:domain")
|
|
|
|
self.device_id_path = self.store_path / f"{self.user_id}.device_id"
|
|
try:
|
|
self.device_id = self.device_id_path.read_text()
|
|
except FileNotFoundError:
|
|
self.device_id = ""
|
|
|
|
self.device_name = self.get(
|
|
"matrix.session_name", default=f"hotdog/{platform.node()}"
|
|
)
|
|
self.display_name = self.get("matrix.nick_name")
|
|
self.homeserver_url = self.get("matrix.homeserver_url")
|
|
self.password = self.get("matrix.password")
|
|
|
|
# Development
|
|
self.is_dev = self.get("dev.active", False)
|
|
self.dev_room = self.get("dev.room", "")
|
|
|
|
# Location / l6n
|
|
l6n_default = self.get("l6n.default")
|
|
l6n = Fallbackdict(l6n_default)
|
|
for room in self.get("l6n.rooms"):
|
|
l6n[room["id"]] = {**l6n_default, **room} # XXX in py3.9 use |-operator
|
|
self.l6n = l6n
|
|
|
|
def get(self, path: Union[str, List[str]], default: Any = None) -> Any:
|
|
if isinstance(path, str):
|
|
path = path.split(".")
|
|
config = self.config
|
|
for part in path:
|
|
config = config.get(part)
|
|
if config is None:
|
|
if default is not None:
|
|
return default
|
|
raise ConfigError(f"Config value is missing: {'.'.join(path)}")
|
|
return config
|
|
|
|
def set(self, path: Union[str, List[str]], value: Any):
|
|
if isinstance(path, str):
|
|
path = path.split(".")
|
|
config = self.config
|
|
for part in path[:-1]:
|
|
if part in config and not isinstance(config[part], dict):
|
|
raise ConfigError(f"Conflicting value exists: {'.'.join(path)}")
|
|
config = config.get(part, {})
|
|
config[path[-1]] = value
|