mirror of
https://github.com/LiteyukiStudio/LiteyukiBot.git
synced 2024-11-22 20:17:39 +08:00
feat: 配置项目的热修改
This commit is contained in:
parent
f23567194c
commit
aed63c34c9
69
README.md
69
README.md
@ -10,78 +10,11 @@
|
|||||||
- 全新可视化`npm`包管理,支持一键安装插件
|
- 全新可视化`npm`包管理,支持一键安装插件
|
||||||
- 支持OneBotv11/12标准通信,且使用`Alconna`命令解析,不再局限于OneBot
|
- 支持OneBotv11/12标准通信,且使用`Alconna`命令解析,不再局限于OneBot
|
||||||
|
|
||||||
## 1.安装和部署
|
### [文档](https://bot.liteyuki.icu)
|
||||||
|
|
||||||
1. 安装 `Git` 和 `Python3.10+` 环境
|
|
||||||
2. 克隆项目 `git clone https://github.com/snowykami/LiteyukiBot` (无法连接可以用`https://gitee.com/snowykami/LiteyukiBot`)
|
|
||||||
3. 切换目录`cd LiteyukiBot`
|
|
||||||
4. 安装依赖`pip install -r requirements.txt`(如果多个Python环境请指定后安装`pythonx -m pip install -r requirements.txt`)
|
|
||||||
5. 启动`python main.py`
|
|
||||||
|
|
||||||
## 2. 配置
|
|
||||||
|
|
||||||
### 轻雪配置项(Nonebot插件配置项也可以写在此,与dotenv格式不同,应为小写)
|
|
||||||
|
|
||||||
如果不确定字段的含义,请不要修改(部分在自动生成配置文件中未列出,需手动添加)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# 生成文件的配置项
|
|
||||||
command_start: [ "/", " " ] # 指令前缀
|
|
||||||
host: 127.0.0.1 # 监听地址
|
|
||||||
port: 20216 # 绑定端口
|
|
||||||
nickname: [ "liteyuki" ] # 机器人昵称
|
|
||||||
superusers: [ "1919810" ] # 超级用户
|
|
||||||
# 未列出的配置项(如要自定义请手动修改)
|
|
||||||
onebot_access_token: "" # Onebot访问令牌[具体请看](https://onebot.adapters.nonebot.dev/docs/guide/configuration)
|
|
||||||
default_language: "zh-CN" # 默认语言
|
|
||||||
log_level: "INFO" # 日志等级
|
|
||||||
log_icon: true # 是否显示日志等级图标(某些控制台不可用)
|
|
||||||
auto_report: true # 是否自动上报问题给轻雪服务器,仅包含硬件信息和运行软件版本
|
|
||||||
|
|
||||||
# 其他Nonebot插件的配置项
|
|
||||||
custom_config_1: "custom_value1"
|
|
||||||
custom_config_2: "custom_value2"
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Onebot实现端配置
|
|
||||||
|
|
||||||
不同的实现端给出的字段可能不同,但是基本上都是一样的,这里给出一个参考值
|
|
||||||
|
|
||||||
| 字段 | 参考值 | 说明 |
|
|
||||||
|-------------|--------------------------|----------------------------------|
|
|
||||||
| 协议 | 反向WebSocket | 推荐使用反向ws协议进行通信,即轻雪作为服务端 |
|
|
||||||
| 地址 | ws://`address`/onebot/v11 | 地址取决于配置文件,本机默认为`127.0.0.1:20216` |
|
|
||||||
| AccessToken | `""` | 如果你给轻雪配置了`AccessToken`,请在此填写相同的值 |
|
|
||||||
|
|
||||||
## 3.其他
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
- 设备上Python环境太乱了,pip和python不对应怎么办?
|
|
||||||
- 请使用`/path/to/python -m pip install -r requirements.txt`来安装依赖,
|
|
||||||
然后用`/path/to/python main.py`来启动Bot,
|
|
||||||
其中`/path/to/python`是你要用来运行Bot可执行文件
|
|
||||||
- 为什么我启动后机器人没有反应?
|
|
||||||
- 请检查配置文件的`command_start`或`superusers`,确认你有权限使用命令并按照正确的命令发送
|
|
||||||
|
|
||||||
- 怎么登录QQ等聊天平台
|
|
||||||
- 你有这个问题说明你不是很了解这个项目,本项目不负责实现登录功能,只负责处理消息
|
|
||||||
你需要使用Onebot标准的实现端来连接到轻雪并将消息上报给轻雪,下面已经列出一些推荐的实现端
|
|
||||||
|
|
||||||
#### 推荐方案(QQ)
|
|
||||||
|
|
||||||
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core),目前点按交互目前仅支持Lagrange
|
|
||||||
2. [LiteLoaderNTQQ+LLOneBot](https://github.com/LLOneBot/LLOneBot),基于NTQQ的Onebot实现
|
|
||||||
3. 云崽的`icqq-plugin`和`ws-plugin`进行通信
|
|
||||||
4. `Go-cqhttp`(目前已经半死不活了)
|
|
||||||
5. 人工实现的`Onebot`协议,自己整一个WebSocket客户端,看着QQ的消息,然后给轻雪传输数据
|
|
||||||
|
|
||||||
#### 推荐方案(Minecraft)
|
|
||||||
|
|
||||||
1. 我们有专门为Minecraft开发的服务器Bot,支持OnebotV11/12标准,详细请看[MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot)
|
|
||||||
|
|
||||||
使用其他项目连接请先自行查阅文档,若有困难请联系对应开发者而不是Liteyuki的开发者
|
|
||||||
|
|
||||||
## 4.用户协议
|
## 4.用户协议
|
||||||
|
|
||||||
|
37
docs/deployment/fandq.md
Normal file
37
docs/deployment/fandq.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: 答疑
|
||||||
|
icon: object-group
|
||||||
|
order: 3
|
||||||
|
category: 使用指南
|
||||||
|
tag:
|
||||||
|
- 配置
|
||||||
|
- 部署
|
||||||
|
---
|
||||||
|
|
||||||
|
### 常见问题
|
||||||
|
|
||||||
|
- 设备上Python环境太乱了,pip和python不对应怎么办?
|
||||||
|
- 请使用`/path/to/python -m pip install -r requirements.txt`来安装依赖,
|
||||||
|
然后用`/path/to/python main.py`来启动Bot,
|
||||||
|
其中`/path/to/python`是你要用来运行Bot的可执行文件
|
||||||
|
- 为什么我启动后机器人没有反应?
|
||||||
|
- 请检查配置文件的`command_start`或`superusers`,确认你有权限使用命令并按照正确的命令发送
|
||||||
|
|
||||||
|
- 怎么登录聊天平台,例如QQ
|
||||||
|
- 你有这个问题说明你不是很了解这个项目,本项目不负责实现登录功能,只负责处理和回应消息,登录功能由实现端(协议端)提供,
|
||||||
|
实现端本身不负责处理响应逻辑,将消息按照OneBot标准处理好上报给轻雪
|
||||||
|
你需要使用Onebot标准的实现端来连接到轻雪并将消息上报给轻雪,下面已经列出一些推荐的实现端
|
||||||
|
|
||||||
|
#### 推荐方案(QQ)
|
||||||
|
|
||||||
|
1. [Lagrange.OneBot](https://github.com/KonataDev/Lagrange.Core),目前Markdown点按交互目前仅支持Lagrange
|
||||||
|
2. [LiteLoaderNTQQ+LLOneBot](https://github.com/LLOneBot/LLOneBot),基于NTQQ的Onebot实现
|
||||||
|
3. 云崽的`icqq-plugin`和`ws-plugin`进行通信
|
||||||
|
4. `Go-cqhttp`(目前已经半死不活了)
|
||||||
|
5. 人工实现的`Onebot`协议,自己整一个WebSocket客户端,看着QQ的消息,然后给轻雪传输数据
|
||||||
|
|
||||||
|
#### 推荐方案(Minecraft)
|
||||||
|
|
||||||
|
1. 我们有专门为Minecraft开发的服务器Bot,支持OnebotV11/12标准,详细请看[MinecraftOneBot](https://github.com/snowykami/MinecraftOnebot)
|
||||||
|
|
||||||
|
使用其他项目连接请先自行查阅文档,若有困难请联系对应开发者而不是Liteyuki的开发者
|
@ -5,34 +5,41 @@ order: 1
|
|||||||
category: 使用手册
|
category: 使用手册
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 基础插件命令
|
||||||
|
|
||||||
## 内置插件命令
|
#### 命令前有[S]的表示仅超级用户可用,[O]和[A]分别为群主和群管可用
|
||||||
|
|
||||||
### 轻雪`liteyuki`
|
### 轻雪`liteyuki`
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
reload-liteyuki # 重载轻雪
|
[S]reload-liteyuki # 重载轻雪
|
||||||
update-liteyuki # 更新轻雪
|
[S]update-liteyuki # 更新轻雪
|
||||||
liteyuki # 查看轻雪信息
|
[S]liteyuki # 查看轻雪信息
|
||||||
|
[S]config set <key> value # 添加配置项,若存在则会覆盖,输入值会被执行,以便于转换为正确的值,"10"和10是不一样的
|
||||||
|
[S]config get [key] # 查询配置项,不带key返回配置项列表,推荐私聊使用
|
||||||
|
# 上述两个命令修改的配置项在数据库中保存,但是优先级低于配置文件,如果配置文件中存在相同的配置项,将会使用配置文件中的配置
|
||||||
|
------
|
||||||
|
别名: reload-liteyuki 重启轻雪, update-liteyuki 更新轻雪, config 配置, set 设置, get 查询
|
||||||
```
|
```
|
||||||
|
|
||||||
### 轻雪Nonebot插件管理 `liteyuki_npm`
|
### 轻雪Nonebot插件管理 `liteyuki_npm`
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm update # 更新插件索引
|
[S]npm update # 更新插件索引
|
||||||
npm install <plugin_name> # 安装插件
|
[S]npm install <plugin_name> # 安装插件
|
||||||
npm uninstall <plugin_name> # 卸载插件
|
[S]npm uninstall <plugin_name> # 卸载插件
|
||||||
npm search <keywords...> # 搜索插件
|
[S]npm search <keywords...> # 通过关键词搜索插件
|
||||||
------
|
------
|
||||||
别名: npm 插件, update 更新, install 安装, uninstall 卸载, search 搜索
|
别名: npm 插件, update 更新, install 安装, uninstall 卸载, search 搜索
|
||||||
```
|
```
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
enable <plugin_name> # 启用插件
|
[SOA]enable <plugin_name> # 启用插件
|
||||||
disable <plugin_name> # 禁用插件
|
[SOA]disable <plugin_name> # 禁用插件
|
||||||
enable-global <plugin_name> # 全局启用插件
|
[S]enable-global <plugin_name> # 全局启用插件
|
||||||
disable-global <plugin_name> # 全局禁用插件
|
[S]disable-global <plugin_name> # 全局禁用插件
|
||||||
list-plugin # 列出所有插件
|
list-plugin # 列出所有插件
|
||||||
|
# 受限于Nonebot的钩子函数,目前只能阻断消息事件的传入,对于主动推送消息的插件,无法将其阻止
|
||||||
------
|
------
|
||||||
别名: enable 启用, disable 停用, enable-global 全局启用, disable-global 全局停用, list-plugin 列出插件/插件列表
|
别名: enable 启用, disable 停用, enable-global 全局启用, disable-global 全局停用, list-plugin 列出插件/插件列表
|
||||||
```
|
```
|
||||||
|
@ -3,4 +3,19 @@ title: 功能命令
|
|||||||
icon: laptop-code
|
icon: laptop-code
|
||||||
order: 2
|
order: 2
|
||||||
category: 使用手册
|
category: 使用手册
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## 功能插件命令
|
||||||
|
|
||||||
|
### 轻雪天气`liteyuki_weather`
|
||||||
|
配置项
|
||||||
|
```yaml
|
||||||
|
weather-key # 和风天气的天气key
|
||||||
|
```
|
||||||
|
命令
|
||||||
|
```shell
|
||||||
|
weather <keywords...> # 查询目标地天气,例如:"天气 北京 海淀", "weather Tokyo Shinjuku"
|
||||||
|
bind-city <keywords...> # 绑定查询城市,个人全局生效
|
||||||
|
别名:weather 天气
|
||||||
|
```
|
@ -1,17 +1,23 @@
|
|||||||
|
import json
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import aiofiles
|
||||||
|
import yaml
|
||||||
from nonebot import require
|
from nonebot import require
|
||||||
from nonebot.permission import SUPERUSER
|
from nonebot.permission import SUPERUSER
|
||||||
from git import Repo
|
from git import Repo
|
||||||
|
|
||||||
from liteyuki.utils.config import config
|
from liteyuki.utils.config import config, load_from_yaml
|
||||||
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent
|
||||||
|
|
||||||
from liteyuki.utils.language import get_user_lang
|
from liteyuki.utils.language import get_user_lang
|
||||||
from liteyuki.utils.message import Markdown as md, send_markdown
|
from liteyuki.utils.message import Markdown as md, send_markdown
|
||||||
|
|
||||||
from .reloader import Reloader
|
from .reloader import Reloader
|
||||||
|
from liteyuki.utils.data_manager import StoredConfig, common_db
|
||||||
|
|
||||||
require("nonebot_plugin_alconna")
|
require("nonebot_plugin_alconna")
|
||||||
from nonebot_plugin_alconna import on_alconna, Alconna
|
from nonebot_plugin_alconna import on_alconna, Alconna, Args, Subcommand, Arparma
|
||||||
|
|
||||||
cmd_liteyuki = on_alconna(
|
cmd_liteyuki = on_alconna(
|
||||||
Alconna(
|
Alconna(
|
||||||
@ -34,6 +40,24 @@ reload_liteyuki = on_alconna(
|
|||||||
permission=SUPERUSER
|
permission=SUPERUSER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cmd_config = on_alconna(
|
||||||
|
Alconna(
|
||||||
|
["config", "配置"],
|
||||||
|
Subcommand(
|
||||||
|
"set",
|
||||||
|
Args["key", str]["value", Any],
|
||||||
|
alias=["设置"],
|
||||||
|
|
||||||
|
),
|
||||||
|
Subcommand(
|
||||||
|
"get",
|
||||||
|
Args["key", str, None],
|
||||||
|
alias=["查询"]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
permission=SUPERUSER
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@cmd_liteyuki.handle()
|
@cmd_liteyuki.handle()
|
||||||
async def _(bot: T_Bot):
|
async def _(bot: T_Bot):
|
||||||
@ -64,3 +88,36 @@ async def _(bot: T_Bot, event: T_MessageEvent):
|
|||||||
async def _():
|
async def _():
|
||||||
await reload_liteyuki.send("Liteyuki reloading")
|
await reload_liteyuki.send("Liteyuki reloading")
|
||||||
Reloader.reload(3)
|
Reloader.reload(3)
|
||||||
|
|
||||||
|
|
||||||
|
@cmd_config.handle()
|
||||||
|
async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot):
|
||||||
|
ulang = get_user_lang(str(event.user_id))
|
||||||
|
stored_config: StoredConfig = common_db.first(StoredConfig(), default=StoredConfig())
|
||||||
|
if result.subcommands.get("set"):
|
||||||
|
key, value = result.subcommands.get("set").args.get("key"), result.subcommands.get("set").args.get("value")
|
||||||
|
try:
|
||||||
|
value = eval(value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
stored_config.config[key] = value
|
||||||
|
common_db.upsert(stored_config)
|
||||||
|
await cmd_config.finish(f"{ulang.get('liteyuki.config_set_success', KEY=key, VAL=value)}")
|
||||||
|
elif result.subcommands.get("get"):
|
||||||
|
key = result.subcommands.get("get").args.get("key")
|
||||||
|
file_config = load_from_yaml("config.yml")
|
||||||
|
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||||
|
if key:
|
||||||
|
reply += f"```dotenv\n{key}={file_config.get(key, stored_config.config.get(key))}\n```"
|
||||||
|
else:
|
||||||
|
reply = f"{ulang.get('liteyuki.current_config')}"
|
||||||
|
reply += f"\n{ulang.get('liteyuki.static_config')}\n```dotenv"
|
||||||
|
for k, v in file_config.items():
|
||||||
|
reply += f"\n{k}={v}"
|
||||||
|
reply += "\n```"
|
||||||
|
if len(stored_config.config) > 0:
|
||||||
|
reply += f"\n{ulang.get('liteyuki.stored_config')}\n```dotenv"
|
||||||
|
for k, v in stored_config.config.items():
|
||||||
|
reply += f"\n{k}={v}"
|
||||||
|
reply += "\n```"
|
||||||
|
await send_markdown(reply, bot, event=event)
|
||||||
|
@ -9,6 +9,7 @@ log.success=Success
|
|||||||
liteyuki.restart=Restart
|
liteyuki.restart=Restart
|
||||||
liteyuki.restart_now=Restart now
|
liteyuki.restart_now=Restart now
|
||||||
liteyuki.update_restart=Update completely, You can {RESTART} or later to apply the changes
|
liteyuki.update_restart=Update completely, You can {RESTART} or later to apply the changes
|
||||||
|
liteyuki.current_config=Current Config
|
||||||
|
|
||||||
main.current_language=Current config language: {LANG}
|
main.current_language=Current config language: {LANG}
|
||||||
main.enable_webdash=Web dashboard is enabled: {URL}
|
main.enable_webdash=Web dashboard is enabled: {URL}
|
||||||
|
@ -9,6 +9,10 @@ log.success=成功
|
|||||||
liteyuki.restart=重启
|
liteyuki.restart=重启
|
||||||
liteyuki.restart_now=立即重启
|
liteyuki.restart_now=立即重启
|
||||||
liteyuki.update_restart=更新完成,你可以{RESTART}或稍后手动重启以应用这些更新
|
liteyuki.update_restart=更新完成,你可以{RESTART}或稍后手动重启以应用这些更新
|
||||||
|
liteyuki.current_config=当前配置项如下
|
||||||
|
liteyuki.static_config=静态文件配置项
|
||||||
|
liteyuki.stored_config=储存的配置项
|
||||||
|
liteyuki.config_set_success=配置项 {KEY}={VAL} 设置成功
|
||||||
|
|
||||||
main.current_language=当前配置语言为: {LANG}
|
main.current_language=当前配置语言为: {LANG}
|
||||||
main.enable_webdash=已启用网页监控面板: {URL}
|
main.enable_webdash=已启用网页监控面板: {URL}
|
||||||
|
@ -15,9 +15,9 @@ class LiteModel(BaseModel):
|
|||||||
|
|
||||||
def dump(self, *args, **kwargs):
|
def dump(self, *args, **kwargs):
|
||||||
if pydantic.__version__ < "1.8.2":
|
if pydantic.__version__ < "1.8.2":
|
||||||
return self.dict(by_alias=True)
|
return self.dict(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return self.model_dump(by_alias=True)
|
return self.model_dump(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
@ -30,7 +30,7 @@ class Database:
|
|||||||
self.conn = sqlite3.connect(db_name)
|
self.conn = sqlite3.connect(db_name)
|
||||||
self.cursor = self.conn.cursor()
|
self.cursor = self.conn.cursor()
|
||||||
|
|
||||||
def first(self, model: LiteModel, condition: str, *args: Any, default: Any = None) -> LiteModel | Any | None:
|
def first(self, model: LiteModel, condition: str = "", *args: Any, default: Any = None) -> LiteModel | Any | None:
|
||||||
"""查询第一个
|
"""查询第一个
|
||||||
Args:
|
Args:
|
||||||
model: 数据模型实例
|
model: 数据模型实例
|
||||||
|
@ -43,9 +43,14 @@ class GlobalPlugin(LiteModel):
|
|||||||
enabled: bool = Field(True, alias="enabled")
|
enabled: bool = Field(True, alias="enabled")
|
||||||
|
|
||||||
|
|
||||||
|
class StoredConfig(LiteModel):
|
||||||
|
TABLE_NAME = "stored_config"
|
||||||
|
config: dict = {}
|
||||||
|
|
||||||
|
|
||||||
def auto_migrate():
|
def auto_migrate():
|
||||||
print("Migrating databases...")
|
|
||||||
user_db.auto_migrate(User())
|
user_db.auto_migrate(User())
|
||||||
group_db.auto_migrate(Group())
|
group_db.auto_migrate(Group())
|
||||||
plugin_db.auto_migrate(InstalledPlugin(), GlobalPlugin())
|
plugin_db.auto_migrate(InstalledPlugin(), GlobalPlugin())
|
||||||
common_db.auto_migrate(GlobalPlugin())
|
common_db.auto_migrate(GlobalPlugin(), StoredConfig())
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from .ly_typing import T_Bot, T_MessageEvent
|
|||||||
|
|
||||||
async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, session_id: str | int = None, event: T_MessageEvent = None, **kwargs) -> dict[
|
async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, session_id: str | int = None, event: T_MessageEvent = None, **kwargs) -> dict[
|
||||||
str, Any]:
|
str, Any]:
|
||||||
formatted_md = v11.unescape(markdown).replace("\n", r"\n").replace("\"", r'\\\"')
|
formatted_md = v11.unescape(markdown).replace("\n", r"\n").replace('"', r'\\\"')
|
||||||
if event is not None and message_type is None:
|
if event is not None and message_type is None:
|
||||||
message_type = event.message_type
|
message_type = event.message_type
|
||||||
session_id = event.user_id if event.message_type == "private" else event.group_id
|
session_id = event.user_id if event.message_type == "private" else event.group_id
|
||||||
|
Loading…
Reference in New Issue
Block a user