2022-01-18 23:46:10 +08:00
|
|
|
|
"""本模块定义了 NoneBot 本身运行所需的配置项。
|
2020-08-20 16:34:07 +08:00
|
|
|
|
|
2023-06-24 14:47:35 +08:00
|
|
|
|
NoneBot 使用 [`pydantic`](https://pydantic-docs.helpmanual.io/) 以及
|
|
|
|
|
[`python-dotenv`](https://saurabh-kumar.com/python-dotenv/) 来读取配置。
|
2020-08-20 16:34:07 +08:00
|
|
|
|
|
2023-06-24 14:47:35 +08:00
|
|
|
|
配置项需符合特殊格式或 json 序列化格式
|
|
|
|
|
详情见 [`pydantic Field Type`](https://pydantic-docs.helpmanual.io/usage/types/) 文档。
|
2022-01-16 11:30:09 +08:00
|
|
|
|
|
|
|
|
|
FrontMatter:
|
|
|
|
|
sidebar_position: 1
|
|
|
|
|
description: nonebot.config 模块
|
2020-08-20 16:34:07 +08:00
|
|
|
|
"""
|
2023-06-24 14:47:35 +08:00
|
|
|
|
|
2020-08-07 11:18:02 +08:00
|
|
|
|
import os
|
2024-01-15 13:24:55 +08:00
|
|
|
|
import abc
|
2024-01-26 11:12:57 +08:00
|
|
|
|
import json
|
2024-01-15 13:24:55 +08:00
|
|
|
|
from pathlib import Path
|
2020-08-06 17:54:55 +08:00
|
|
|
|
from datetime import timedelta
|
2020-07-04 22:51:10 +08:00
|
|
|
|
from ipaddress import IPv4Address
|
2024-01-26 11:12:57 +08:00
|
|
|
|
from typing_extensions import TypeAlias, get_args, get_origin
|
2024-01-15 13:24:55 +08:00
|
|
|
|
from typing import (
|
|
|
|
|
TYPE_CHECKING,
|
|
|
|
|
Any,
|
|
|
|
|
Set,
|
|
|
|
|
Dict,
|
|
|
|
|
List,
|
|
|
|
|
Type,
|
|
|
|
|
Tuple,
|
|
|
|
|
Union,
|
|
|
|
|
Mapping,
|
|
|
|
|
Optional,
|
|
|
|
|
)
|
2020-07-04 22:51:10 +08:00
|
|
|
|
|
2024-01-15 13:24:55 +08:00
|
|
|
|
from dotenv import dotenv_values
|
2024-01-26 11:12:57 +08:00
|
|
|
|
from pydantic import Field, BaseModel
|
|
|
|
|
from pydantic.networks import IPvAnyAddress
|
2020-08-07 11:18:02 +08:00
|
|
|
|
|
2021-12-27 02:26:02 +08:00
|
|
|
|
from nonebot.log import logger
|
2024-01-26 11:12:57 +08:00
|
|
|
|
from nonebot.typing import origin_is_union
|
|
|
|
|
from nonebot.utils import deep_update, type_is_complex, lenient_issubclass
|
|
|
|
|
from nonebot.compat import (
|
|
|
|
|
PYDANTIC_V2,
|
|
|
|
|
ConfigDict,
|
|
|
|
|
ModelField,
|
|
|
|
|
PydanticUndefined,
|
|
|
|
|
PydanticUndefinedType,
|
|
|
|
|
model_config,
|
|
|
|
|
model_fields,
|
|
|
|
|
)
|
2021-12-27 02:26:02 +08:00
|
|
|
|
|
2024-01-15 13:24:55 +08:00
|
|
|
|
DOTENV_TYPE: TypeAlias = Union[
|
|
|
|
|
Path, str, List[Union[Path, str]], Tuple[Union[Path, str], ...]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
ENV_FILE_SENTINEL = Path("")
|
|
|
|
|
|
|
|
|
|
|
2024-02-06 12:48:23 +08:00
|
|
|
|
class SettingsError(ValueError): ...
|
2024-01-15 13:24:55 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BaseSettingsSource(abc.ABC):
|
|
|
|
|
def __init__(self, settings_cls: Type["BaseSettings"]) -> None:
|
|
|
|
|
self.settings_cls = settings_cls
|
|
|
|
|
|
|
|
|
|
@property
|
2024-01-26 11:12:57 +08:00
|
|
|
|
def config(self) -> "SettingsConfig":
|
|
|
|
|
return model_config(self.settings_cls)
|
2024-01-15 13:24:55 +08:00
|
|
|
|
|
|
|
|
|
@abc.abstractmethod
|
|
|
|
|
def __call__(self) -> Dict[str, Any]:
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InitSettingsSource(BaseSettingsSource):
|
|
|
|
|
__slots__ = ("init_kwargs",)
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self, settings_cls: Type["BaseSettings"], init_kwargs: Dict[str, Any]
|
|
|
|
|
) -> None:
|
|
|
|
|
self.init_kwargs = init_kwargs
|
|
|
|
|
super().__init__(settings_cls)
|
|
|
|
|
|
|
|
|
|
def __call__(self) -> Dict[str, Any]:
|
|
|
|
|
return self.init_kwargs
|
|
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return f"InitSettingsSource(init_kwargs={self.init_kwargs!r})"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DotEnvSettingsSource(BaseSettingsSource):
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
settings_cls: Type["BaseSettings"],
|
|
|
|
|
env_file: Optional[DOTENV_TYPE] = ENV_FILE_SENTINEL,
|
|
|
|
|
env_file_encoding: Optional[str] = None,
|
|
|
|
|
case_sensitive: Optional[bool] = None,
|
|
|
|
|
env_nested_delimiter: Optional[str] = None,
|
|
|
|
|
) -> None:
|
|
|
|
|
super().__init__(settings_cls)
|
|
|
|
|
self.env_file = (
|
2024-01-26 11:12:57 +08:00
|
|
|
|
env_file
|
|
|
|
|
if env_file is not ENV_FILE_SENTINEL
|
|
|
|
|
else self.config.get("env_file", (".env",))
|
2024-01-15 13:24:55 +08:00
|
|
|
|
)
|
|
|
|
|
self.env_file_encoding = (
|
|
|
|
|
env_file_encoding
|
|
|
|
|
if env_file_encoding is not None
|
2024-01-26 11:12:57 +08:00
|
|
|
|
else self.config.get("env_file_encoding", "utf-8")
|
2024-01-15 13:24:55 +08:00
|
|
|
|
)
|
|
|
|
|
self.case_sensitive = (
|
2024-01-26 11:12:57 +08:00
|
|
|
|
case_sensitive
|
|
|
|
|
if case_sensitive is not None
|
|
|
|
|
else self.config.get("case_sensitive", False)
|
2024-01-15 13:24:55 +08:00
|
|
|
|
)
|
|
|
|
|
self.env_nested_delimiter = (
|
|
|
|
|
env_nested_delimiter
|
|
|
|
|
if env_nested_delimiter is not None
|
2024-01-26 11:12:57 +08:00
|
|
|
|
else self.config.get("env_nested_delimiter", None)
|
2024-01-15 13:24:55 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _apply_case_sensitive(self, var_name: str) -> str:
|
|
|
|
|
return var_name if self.case_sensitive else var_name.lower()
|
|
|
|
|
|
|
|
|
|
def _field_is_complex(self, field: ModelField) -> Tuple[bool, bool]:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
if type_is_complex(field.annotation):
|
2024-01-15 13:24:55 +08:00
|
|
|
|
return True, False
|
2024-01-26 11:12:57 +08:00
|
|
|
|
elif origin_is_union(get_origin(field.annotation)) and any(
|
|
|
|
|
type_is_complex(arg) for arg in get_args(field.annotation)
|
2024-01-15 13:24:55 +08:00
|
|
|
|
):
|
|
|
|
|
return True, True
|
|
|
|
|
return False, False
|
|
|
|
|
|
|
|
|
|
def _parse_env_vars(
|
|
|
|
|
self, env_vars: Mapping[str, Optional[str]]
|
|
|
|
|
) -> Dict[str, Optional[str]]:
|
|
|
|
|
return {
|
|
|
|
|
self._apply_case_sensitive(key): value for key, value in env_vars.items()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def _read_env_file(self, file_path: Path) -> Dict[str, Optional[str]]:
|
|
|
|
|
file_vars = dotenv_values(file_path, encoding=self.env_file_encoding)
|
|
|
|
|
return self._parse_env_vars(file_vars)
|
|
|
|
|
|
|
|
|
|
def _read_env_files(self) -> Dict[str, Optional[str]]:
|
|
|
|
|
env_files = self.env_file
|
|
|
|
|
if env_files is None:
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
if isinstance(env_files, (str, os.PathLike)):
|
|
|
|
|
env_files = [env_files]
|
|
|
|
|
|
|
|
|
|
dotenv_vars: Dict[str, Optional[str]] = {}
|
|
|
|
|
for env_file in env_files:
|
|
|
|
|
env_path = Path(env_file).expanduser()
|
|
|
|
|
if env_path.is_file():
|
|
|
|
|
dotenv_vars.update(self._read_env_file(env_path))
|
|
|
|
|
return dotenv_vars
|
|
|
|
|
|
|
|
|
|
def _next_field(
|
|
|
|
|
self, field: Optional[ModelField], key: str
|
|
|
|
|
) -> Optional[ModelField]:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
if not field or origin_is_union(get_origin(field.annotation)):
|
2024-01-15 13:24:55 +08:00
|
|
|
|
return None
|
2024-01-26 11:12:57 +08:00
|
|
|
|
elif field.annotation and lenient_issubclass(field.annotation, BaseModel):
|
|
|
|
|
for field in model_fields(field.annotation):
|
|
|
|
|
if field.name == key:
|
|
|
|
|
return field
|
2024-01-15 13:24:55 +08:00
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def _explode_env_vars(
|
|
|
|
|
self,
|
|
|
|
|
field: ModelField,
|
|
|
|
|
env_vars: Dict[str, Optional[str]],
|
|
|
|
|
env_file_vars: Dict[str, Optional[str]],
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
if self.env_nested_delimiter is None:
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
prefix = f"{field.name}{self.env_nested_delimiter}"
|
|
|
|
|
result: Dict[str, Any] = {}
|
|
|
|
|
for env_name, env_val in env_vars.items():
|
|
|
|
|
if not env_name.startswith(prefix):
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# delete from file vars when used
|
|
|
|
|
if env_name in env_file_vars:
|
|
|
|
|
del env_file_vars[env_name]
|
|
|
|
|
|
|
|
|
|
_, *keys, last_key = env_name.split(self.env_nested_delimiter)
|
|
|
|
|
env_var = result
|
|
|
|
|
target_field: Optional[ModelField] = field
|
|
|
|
|
for key in keys:
|
|
|
|
|
target_field = self._next_field(target_field, key)
|
|
|
|
|
env_var = env_var.setdefault(key, {})
|
|
|
|
|
|
|
|
|
|
target_field = self._next_field(target_field, last_key)
|
|
|
|
|
if target_field and env_val:
|
|
|
|
|
is_complex, allow_parse_failure = self._field_is_complex(target_field)
|
|
|
|
|
if is_complex:
|
|
|
|
|
try:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
env_val = json.loads(env_val)
|
2024-01-15 13:24:55 +08:00
|
|
|
|
except ValueError as e:
|
|
|
|
|
if not allow_parse_failure:
|
|
|
|
|
raise SettingsError(
|
|
|
|
|
f'error parsing env var "{env_name}"'
|
|
|
|
|
) from e
|
|
|
|
|
|
|
|
|
|
env_var[last_key] = env_val
|
2020-08-07 11:18:02 +08:00
|
|
|
|
|
2024-01-15 13:24:55 +08:00
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def __call__(self) -> Dict[str, Any]:
|
2023-06-24 14:47:35 +08:00
|
|
|
|
"""从环境变量和 dotenv 配置文件中读取配置项。"""
|
|
|
|
|
|
2022-10-14 09:58:44 +08:00
|
|
|
|
d: Dict[str, Any] = {}
|
2020-08-07 11:18:02 +08:00
|
|
|
|
|
2024-01-15 13:24:55 +08:00
|
|
|
|
env_vars = self._parse_env_vars(os.environ)
|
|
|
|
|
env_file_vars = self._read_env_files()
|
2022-10-14 09:58:44 +08:00
|
|
|
|
env_vars = {**env_file_vars, **env_vars}
|
2020-08-07 11:18:02 +08:00
|
|
|
|
|
2024-01-26 11:12:57 +08:00
|
|
|
|
for field in model_fields(self.settings_cls):
|
|
|
|
|
field_name = field.name
|
|
|
|
|
env_name = self._apply_case_sensitive(field_name)
|
|
|
|
|
|
2024-01-15 13:24:55 +08:00
|
|
|
|
# try get values from env vars
|
2024-01-26 11:12:57 +08:00
|
|
|
|
env_val = env_vars.get(env_name, PydanticUndefined)
|
2024-01-15 13:24:55 +08:00
|
|
|
|
# delete from file vars when used
|
|
|
|
|
if env_name in env_file_vars:
|
|
|
|
|
del env_file_vars[env_name]
|
2020-08-07 11:18:02 +08:00
|
|
|
|
|
2024-01-15 13:24:55 +08:00
|
|
|
|
is_complex, allow_parse_failure = self._field_is_complex(field)
|
2022-10-14 09:58:44 +08:00
|
|
|
|
if is_complex:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
if isinstance(env_val, PydanticUndefinedType):
|
2023-07-04 10:45:55 +08:00
|
|
|
|
# field is complex but no value found so far, try explode_env_vars
|
2024-01-15 13:24:55 +08:00
|
|
|
|
if env_val_built := self._explode_env_vars(
|
|
|
|
|
field, env_vars, env_file_vars
|
|
|
|
|
):
|
2024-01-26 11:12:57 +08:00
|
|
|
|
d[field_name] = env_val_built
|
2023-07-04 10:45:55 +08:00
|
|
|
|
elif env_val is None:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
d[field_name] = env_val
|
2022-10-14 09:58:44 +08:00
|
|
|
|
else:
|
2023-06-24 14:47:35 +08:00
|
|
|
|
# field is complex and there's a value
|
|
|
|
|
# decode that as JSON, then add explode_env_vars
|
2022-09-09 11:52:57 +08:00
|
|
|
|
try:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
env_val = json.loads(env_val)
|
2022-09-09 11:52:57 +08:00
|
|
|
|
except ValueError as e:
|
2022-10-14 09:58:44 +08:00
|
|
|
|
if not allow_parse_failure:
|
|
|
|
|
raise SettingsError(
|
2024-01-15 13:24:55 +08:00
|
|
|
|
f'error parsing env var "{env_name}"'
|
2022-10-14 09:58:44 +08:00
|
|
|
|
) from e
|
|
|
|
|
|
|
|
|
|
if isinstance(env_val, dict):
|
2024-01-15 13:24:55 +08:00
|
|
|
|
# field value is a dict
|
|
|
|
|
# try explode_env_vars to find more sub-values
|
2024-01-26 11:12:57 +08:00
|
|
|
|
d[field_name] = deep_update(
|
2024-01-15 13:24:55 +08:00
|
|
|
|
env_val,
|
|
|
|
|
self._explode_env_vars(field, env_vars, env_file_vars),
|
2022-09-09 11:52:57 +08:00
|
|
|
|
)
|
2022-10-14 09:58:44 +08:00
|
|
|
|
else:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
d[field_name] = env_val
|
|
|
|
|
elif env_val is not PydanticUndefined:
|
2023-06-24 14:47:35 +08:00
|
|
|
|
# simplest case, field is not complex
|
|
|
|
|
# we only need to add the value if it was found
|
2024-01-26 11:12:57 +08:00
|
|
|
|
d[field_name] = env_val
|
2022-10-14 09:58:44 +08:00
|
|
|
|
|
|
|
|
|
# remain user custom config
|
|
|
|
|
for env_name in env_file_vars:
|
|
|
|
|
env_val = env_vars[env_name]
|
|
|
|
|
if env_val and (val_striped := env_val.strip()):
|
|
|
|
|
# there's a value, decode that as JSON
|
|
|
|
|
try:
|
2024-01-26 11:12:57 +08:00
|
|
|
|
env_val = json.loads(val_striped)
|
2023-06-24 14:47:35 +08:00
|
|
|
|
except ValueError:
|
2022-10-14 09:58:44 +08:00
|
|
|
|
logger.trace(
|
|
|
|
|
"Error while parsing JSON for "
|
|
|
|
|
f"{env_name!r}={val_striped!r}. "
|
|
|
|
|
"Assumed as string."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# explode value when it's a nested dict
|
|
|
|
|
env_name, *nested_keys = env_name.split(self.env_nested_delimiter)
|
|
|
|
|
if nested_keys and (env_name not in d or isinstance(d[env_name], dict)):
|
|
|
|
|
result = {}
|
|
|
|
|
*keys, last_key = nested_keys
|
|
|
|
|
_tmp = result
|
|
|
|
|
for key in keys:
|
|
|
|
|
_tmp = _tmp.setdefault(key, {})
|
|
|
|
|
_tmp[last_key] = env_val
|
|
|
|
|
d[env_name] = deep_update(d.get(env_name, {}), result)
|
|
|
|
|
elif not nested_keys:
|
2020-08-07 11:18:02 +08:00
|
|
|
|
d[env_name] = env_val
|
|
|
|
|
|
|
|
|
|
return d
|
2020-07-04 22:51:10 +08:00
|
|
|
|
|
2021-02-28 00:35:40 +08:00
|
|
|
|
|
2024-01-26 11:12:57 +08:00
|
|
|
|
if PYDANTIC_V2: # pragma: pydantic-v2
|
|
|
|
|
|
|
|
|
|
class SettingsConfig(ConfigDict, total=False):
|
|
|
|
|
env_file: Optional[DOTENV_TYPE]
|
|
|
|
|
env_file_encoding: str
|
|
|
|
|
case_sensitive: bool
|
|
|
|
|
env_nested_delimiter: Optional[str]
|
|
|
|
|
|
|
|
|
|
else: # pragma: pydantic-v1
|
|
|
|
|
|
|
|
|
|
class SettingsConfig(ConfigDict):
|
|
|
|
|
env_file: Optional[DOTENV_TYPE]
|
|
|
|
|
env_file_encoding: str
|
|
|
|
|
case_sensitive: bool
|
|
|
|
|
env_nested_delimiter: Optional[str]
|
2024-01-15 13:24:55 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BaseSettings(BaseModel):
|
2022-01-27 11:15:44 +08:00
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
# dummy getattr for pylance checking, actually not used
|
|
|
|
|
def __getattr__(self, name: str) -> Any: # pragma: no cover
|
|
|
|
|
return self.__dict__.get(name)
|
2020-08-20 15:07:05 +08:00
|
|
|
|
|
2024-01-26 11:12:57 +08:00
|
|
|
|
if PYDANTIC_V2: # pragma: pydantic-v2
|
|
|
|
|
model_config: SettingsConfig = SettingsConfig(
|
|
|
|
|
extra="allow",
|
|
|
|
|
env_file=".env",
|
|
|
|
|
env_file_encoding="utf-8",
|
|
|
|
|
case_sensitive=False,
|
|
|
|
|
env_nested_delimiter="__",
|
|
|
|
|
)
|
|
|
|
|
else: # pragma: pydantic-v1
|
|
|
|
|
|
|
|
|
|
class Config(SettingsConfig):
|
|
|
|
|
extra = "allow" # type: ignore
|
|
|
|
|
env_file = ".env"
|
|
|
|
|
env_file_encoding = "utf-8"
|
|
|
|
|
case_sensitive = False
|
|
|
|
|
env_nested_delimiter = "__"
|
2024-01-15 13:24:55 +08:00
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
__settings_self__, # pyright: ignore[reportSelfClsParameterName]
|
|
|
|
|
_env_file: Optional[DOTENV_TYPE] = ENV_FILE_SENTINEL,
|
|
|
|
|
_env_file_encoding: Optional[str] = None,
|
|
|
|
|
_env_nested_delimiter: Optional[str] = None,
|
|
|
|
|
**values: Any,
|
|
|
|
|
) -> None:
|
|
|
|
|
super().__init__(
|
|
|
|
|
**__settings_self__._settings_build_values(
|
|
|
|
|
values,
|
|
|
|
|
env_file=_env_file,
|
|
|
|
|
env_file_encoding=_env_file_encoding,
|
|
|
|
|
env_nested_delimiter=_env_nested_delimiter,
|
2021-11-22 23:21:26 +08:00
|
|
|
|
)
|
2024-01-15 13:24:55 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _settings_build_values(
|
|
|
|
|
self,
|
|
|
|
|
init_kwargs: Dict[str, Any],
|
|
|
|
|
env_file: Optional[DOTENV_TYPE] = None,
|
|
|
|
|
env_file_encoding: Optional[str] = None,
|
|
|
|
|
env_nested_delimiter: Optional[str] = None,
|
|
|
|
|
) -> Dict[str, Any]:
|
|
|
|
|
init_settings = InitSettingsSource(self.__class__, init_kwargs=init_kwargs)
|
|
|
|
|
env_settings = DotEnvSettingsSource(
|
|
|
|
|
self.__class__,
|
|
|
|
|
env_file=env_file,
|
|
|
|
|
env_file_encoding=env_file_encoding,
|
|
|
|
|
env_nested_delimiter=env_nested_delimiter,
|
|
|
|
|
)
|
|
|
|
|
return deep_update(env_settings(), init_settings())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Env(BaseSettings):
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""运行环境配置。大小写不敏感。
|
2020-08-20 15:07:05 +08:00
|
|
|
|
|
2023-06-24 14:47:35 +08:00
|
|
|
|
将会从 **环境变量** > **dotenv 配置文件** 的优先级读取环境信息。
|
2020-08-20 15:07:05 +08:00
|
|
|
|
"""
|
|
|
|
|
|
2020-07-04 22:51:10 +08:00
|
|
|
|
environment: str = "prod"
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""当前环境名。
|
|
|
|
|
|
|
|
|
|
NoneBot 将从 `.env.{environment}` 文件中加载配置。
|
2020-08-20 15:07:05 +08:00
|
|
|
|
"""
|
2020-07-04 22:51:10 +08:00
|
|
|
|
|
|
|
|
|
|
2024-01-15 13:24:55 +08:00
|
|
|
|
class Config(BaseSettings):
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""NoneBot 主要配置。大小写不敏感。
|
2020-08-07 11:18:02 +08:00
|
|
|
|
|
2022-01-12 18:19:21 +08:00
|
|
|
|
除了 NoneBot 的配置项外,还可以自行添加配置项到 `.env.{environment}` 文件中。
|
|
|
|
|
这些配置将会在 json 反序列化后一起带入 `Config` 类中。
|
2022-01-18 12:31:08 +08:00
|
|
|
|
|
2023-06-01 14:18:16 +08:00
|
|
|
|
配置方法参考: [配置](https://nonebot.dev/docs/appendices/config)
|
2020-08-07 11:18:02 +08:00
|
|
|
|
"""
|
2021-11-22 23:21:26 +08:00
|
|
|
|
|
2024-01-26 11:12:57 +08:00
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
_env_file: Optional[DOTENV_TYPE] = ".env", ".env.prod"
|
2022-01-04 14:36:32 +08:00
|
|
|
|
|
2020-08-01 22:03:40 +08:00
|
|
|
|
# nonebot configs
|
2021-12-25 14:57:29 +08:00
|
|
|
|
driver: str = "~fastapi"
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""NoneBot 运行所使用的 `Driver` 。继承自 {ref}`nonebot.drivers.Driver` 。
|
2021-06-20 23:38:42 +08:00
|
|
|
|
|
2022-01-12 19:41:42 +08:00
|
|
|
|
配置格式为 `<module>[:<Driver>][+<module>[:<Mixin>]]*`。
|
2021-12-25 14:57:29 +08:00
|
|
|
|
|
2022-01-12 19:41:42 +08:00
|
|
|
|
`~` 为 `nonebot.drivers.` 的缩写。
|
2023-06-24 14:47:35 +08:00
|
|
|
|
|
|
|
|
|
配置方法参考: [配置驱动器](https://nonebot.dev/docs/advanced/driver#%E9%85%8D%E7%BD%AE%E9%A9%B1%E5%8A%A8%E5%99%A8)
|
2020-08-20 15:07:05 +08:00
|
|
|
|
"""
|
2020-08-18 17:24:49 +08:00
|
|
|
|
host: IPvAnyAddress = IPv4Address("127.0.0.1") # type: ignore
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""NoneBot {ref}`nonebot.drivers.ReverseDriver` 服务端监听的 IP/主机名。"""
|
2023-01-25 12:31:26 +08:00
|
|
|
|
port: int = Field(default=8080, ge=1, le=65535)
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""NoneBot {ref}`nonebot.drivers.ReverseDriver` 服务端监听的端口。"""
|
2021-12-28 15:19:53 +08:00
|
|
|
|
log_level: Union[int, str] = "INFO"
|
2023-06-24 14:47:35 +08:00
|
|
|
|
"""NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。
|
2022-01-18 12:31:08 +08:00
|
|
|
|
|
2023-06-24 14:47:35 +08:00
|
|
|
|
参考 [记录日志](https://nonebot.dev/docs/appendices/log),[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
|
2022-01-18 12:31:08 +08:00
|
|
|
|
|
|
|
|
|
:::tip 提示
|
|
|
|
|
日志等级名称应为大写,如 `INFO`。
|
|
|
|
|
:::
|
2021-04-19 21:15:10 +08:00
|
|
|
|
|
2022-01-12 18:53:30 +08:00
|
|
|
|
用法:
|
|
|
|
|
```conf
|
2021-04-19 21:15:10 +08:00
|
|
|
|
LOG_LEVEL=25
|
|
|
|
|
LOG_LEVEL=INFO
|
2022-01-12 18:53:30 +08:00
|
|
|
|
```
|
2021-04-19 21:15:10 +08:00
|
|
|
|
"""
|
2020-07-04 22:51:10 +08:00
|
|
|
|
|
2020-08-01 22:03:40 +08:00
|
|
|
|
# bot connection configs
|
2021-11-22 23:21:26 +08:00
|
|
|
|
api_timeout: Optional[float] = 30.0
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""API 请求超时时间,单位: 秒。"""
|
2020-08-01 22:03:40 +08:00
|
|
|
|
|
|
|
|
|
# bot runtime configs
|
2020-12-17 21:09:30 +08:00
|
|
|
|
superusers: Set[str] = set()
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""机器人超级用户。
|
2020-11-30 11:08:00 +08:00
|
|
|
|
|
2022-01-12 18:53:30 +08:00
|
|
|
|
用法:
|
|
|
|
|
```conf
|
2021-01-05 23:06:36 +08:00
|
|
|
|
SUPERUSERS=["12345789"]
|
2022-01-12 18:53:30 +08:00
|
|
|
|
```
|
2020-08-20 16:34:07 +08:00
|
|
|
|
"""
|
2020-10-06 22:29:05 +08:00
|
|
|
|
nickname: Set[str] = set()
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""机器人昵称。"""
|
2020-08-17 16:09:41 +08:00
|
|
|
|
command_start: Set[str] = {"/"}
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""命令的起始标记,用于判断一条消息是不是命令。
|
|
|
|
|
|
2023-06-24 14:47:35 +08:00
|
|
|
|
参考[命令响应规则](https://nonebot.dev/docs/advanced/matcher#command)。
|
|
|
|
|
|
2022-01-18 12:31:08 +08:00
|
|
|
|
用法:
|
|
|
|
|
```conf
|
|
|
|
|
COMMAND_START=["/", ""]
|
|
|
|
|
```
|
2020-08-20 16:34:07 +08:00
|
|
|
|
"""
|
2020-08-17 16:09:41 +08:00
|
|
|
|
command_sep: Set[str] = {"."}
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""命令的分隔标记,用于将文本形式的命令切分为元组(实际的命令名)。
|
|
|
|
|
|
2023-06-24 14:47:35 +08:00
|
|
|
|
参考[命令响应规则](https://nonebot.dev/docs/advanced/matcher#command)。
|
|
|
|
|
|
2022-01-18 12:31:08 +08:00
|
|
|
|
用法:
|
|
|
|
|
```conf
|
|
|
|
|
COMMAND_SEP=["."]
|
|
|
|
|
```
|
2020-08-20 16:34:07 +08:00
|
|
|
|
"""
|
2020-08-06 17:54:55 +08:00
|
|
|
|
session_expire_timeout: timedelta = timedelta(minutes=2)
|
2022-01-18 12:31:08 +08:00
|
|
|
|
"""等待用户回复的超时时间。
|
2020-11-30 11:08:00 +08:00
|
|
|
|
|
2022-01-12 18:53:30 +08:00
|
|
|
|
用法:
|
|
|
|
|
```conf
|
2020-08-20 16:34:07 +08:00
|
|
|
|
SESSION_EXPIRE_TIMEOUT=120 # 单位: 秒
|
|
|
|
|
SESSION_EXPIRE_TIMEOUT=[DD ][HH:MM]SS[.ffffff]
|
|
|
|
|
SESSION_EXPIRE_TIMEOUT=P[DD]DT[HH]H[MM]M[SS]S # ISO 8601
|
2022-01-12 18:53:30 +08:00
|
|
|
|
```
|
2020-08-20 16:34:07 +08:00
|
|
|
|
"""
|
2020-07-04 22:51:10 +08:00
|
|
|
|
|
2021-01-12 17:55:27 +08:00
|
|
|
|
# adapter configs
|
|
|
|
|
# adapter configs are defined in adapter/config.py
|
|
|
|
|
|
2020-08-01 22:03:40 +08:00
|
|
|
|
# custom configs
|
2020-08-07 11:18:02 +08:00
|
|
|
|
# custom configs can be assigned during nonebot.init
|
|
|
|
|
# or from env file using json loads
|
2020-07-04 22:51:10 +08:00
|
|
|
|
|
2024-01-26 11:12:57 +08:00
|
|
|
|
if PYDANTIC_V2: # pragma: pydantic-v2
|
|
|
|
|
model_config = SettingsConfig(env_file=(".env", ".env.prod"))
|
|
|
|
|
else: # pragma: pydantic-v1
|
|
|
|
|
|
|
|
|
|
class Config(SettingsConfig):
|
|
|
|
|
env_file = ".env", ".env.prod"
|
2022-01-18 12:31:08 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__autodoc__ = {
|
2024-01-15 13:24:55 +08:00
|
|
|
|
"SettingsError": False,
|
|
|
|
|
"BaseSettingsSource": False,
|
|
|
|
|
"InitSettingsSource": False,
|
|
|
|
|
"DotEnvSettingsSource": False,
|
|
|
|
|
"SettingsConfig": False,
|
|
|
|
|
"BaseSettings": False,
|
2022-01-18 12:31:08 +08:00
|
|
|
|
}
|