2 |
3 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 | 📖Docs 27 |
28 | 29 | ## 介绍 30 | 31 | FloraBot是一个功能强大的聊天机器人,旨在为用户提供智能、便捷的聊天服务。以下是FloraBot的主要特点: 32 | 33 | - **方便快捷**:FloraBot支持多个平台,旨在提供更加简便快捷的服务 34 | - **拓展方便**: Florabot允许用户自行开发和安装插件来拓展功能,并且开发难度低 35 | 36 | ## 快速开始 37 | 38 | 1. **安装Python** 39 | - Windows用户前往[Python官网](https://www.python.org/downloads)下载不低于`Python3.11`的版本进行安装。 40 | - Linux用户运行命令`apt install python3`,可将`python3`改为指定版本,如`python3.11`。 41 | 42 | 2. **下载FloraBot** 43 | - 点击GitHub仓库右上角的绿色`Code`按钮,选择`Download ZIP`下载,或通过链接下载[FloraBot源码](https://github.com/FloraBotTeam/FloraBot/archive/main.zip)。 44 | 45 | 3. **解压文件** 46 | - 解压下载的文件后,可删除`PluginTemplate`文件夹(开发者可保留)。 47 | 48 | 4. **创建启动脚本** 49 | - **Windows用户**:在`FloraBot.py`同级目录创建文本文档,内容为`python FloraBot.py`,保存后将后缀名改为`.bat`,双击运行。若无法运行Python,需将脚本内容改为Python.exe的绝对路径。 50 | - **Linux用户**:在`FloraBot.py`同级目录创建`.sh`文件,内容为`python3 FloraBot.py`,可将`python3`改为指定版本,如`python3.11`。 51 | 52 | 5. **安装必要库** 53 | - 打开终端(Windows使用CMD或PowerShell),运行以下命令安装所有必要库: 54 | ```Shell 55 | pip install -r requirements.txt文件所在路径 56 | ``` 57 | - 若手动安装,依次运行以下命令: 58 | ```Shell 59 | pip install flask 60 | pip install requests 61 | pip install websocket-server 62 | pip install colorama 63 | ``` 64 | - 若`pip`命令无法运行,可尝试将`pip`替换为`python3 -m pip`。 65 | 66 | 6. **首次启动与配置** 67 | - 首次启动会失败,但会在`FloraBot.py`同级目录生成`Config.json`文件,编辑该文件进行配置。 68 | 69 | ## 配置说明 70 | 71 | `Config.json`文件的键值对照表如下: 72 | 73 | ```Json 74 | { 75 | "AutoInstallLibraries": true, 76 | "ConnectionType": "HTTP", 77 | "FloraHost": "127.0.0.1", 78 | "FloraPort": 3003, 79 | "FrameworkAddress": "127.0.0.1:3000", 80 | "BotID": 0, 81 | "Administrator": [0] 82 | } 83 | ``` 84 | 85 | - **`AutoInstallLibraries`**:是否自动安装pip安装所需的第三方库,默认为`true`。 86 | - **`ConnectionType`**:Bot与框架的连接方式,可选`HTTP`和`WebSocket`,默认为`HTTP`。 87 | - **`FloraHost`**:Bot监听的IP地址,默认为`127.0.0.1`。 88 | - **`FloraPort`**:Bot监听的端口号,默认为`3003`。 89 | - **`FrameworkAddress`**:框架的Http协议监听地址,格式为`IP地址:端口号`,默认为`127.0.0.1:3000`。 90 | - **`BotID`**:登录的Bot账号的ID,默认为`0`。 91 | - **`Administrator`**:管理员/所有者/主人的ID列表,格式为`[ID, ID, ...]`,默认为`[0]`。 92 | 93 | ## 再次启动 94 | 95 | 完成配置后,再次启动FloraBot,若无意外,已可正常使用。 96 | 97 | ## 注意事项 98 | 99 | 部分框架使用WebSocket协议进行连接,Bot可能会警告WebSocket相关的问题。如果能够正常收发消息,请忽略警告;如果不能,则可能是框架的问题。 100 | 101 | ## 框架配置 102 | 103 | - **Http协议配置**:框架需要开启Http服务,启用Http事件上报,关闭Http心跳(避免出现Bug),并在事件上报地址中添加Bot的监听地址。若框架支持消息上报格式,设置为`CQ码`即可。 104 | - **WebSocket协议配置**:Bot仅支持反向WebSocket,因此框架应配置反向WebSocket,其他部分与HTTP协议配置相同。 105 | 106 | ## 添加插件 107 | 108 | **重要声明**:插件为第三方内容,请自行分辨是否为恶意插件。若因恶意插件导致设备受损或信息泄露,FloraBotTeam概不负责。 109 | 110 | 首次启动会创建一个名为`FloraBot`的文件夹,进入该文件夹,再进入`Plugins`文件夹,将插件文件夹放入其中。若插件为压缩包,需解压后再放入。 111 | 112 | 文件结构示例: 113 | ```File 114 | FloraBot 115 | \-Plugins 116 | |-插件文件夹 117 | | |-Plugin.json 118 | | \-插件.py 119 | \-插件文件夹 120 | |-Plugin.json 121 | \-插件.py 122 | Config.json 123 | FloraBot.py 124 | 启动脚本 125 | ``` 126 | 127 | ## 内置功能 128 | 129 | 与Bot账号私聊或在Bot加入的群聊中发送指令触发: 130 | 131 | - **`/重载插件`**:重新加载插件,若在FloraBot运行中添加/删除/修改了插件文件,请发送该指令重新加载。 132 | - **`/插件列表`**:发送该指令后,Bot会自动发送当前已添加的所有插件的列表,包括插件的状态等。 133 | - **`/启用插件 + [空格] + [插件名]`**:若插件被禁用,可使用该指令启用插件,插件名可通过`/插件列表`指令查询。 134 | - **`/禁用插件 + [空格] + [插件名]`**:若不想要该插件的功能,可使用该指令禁用插件,插件名可通过`/插件列表`指令查询。 135 | - **`/echo + [空格] + [内容]`**:让Bot复读一遍内容,用于调试(Debug),此指令复读方式为回复。 136 | - **`/echo1 + [空格] + [内容]`**:与`/echo`功能相同,只是复读方式不为回复。 137 | - **`/帮助`**:查看FloraBot可使用的指令。 138 | - **`/帮助 + [空格] + [插件名]`**:可查看对应插件的帮助。 139 | 140 | ## 插件开发 141 | 142 | **要求**: 143 | - 会Python的基础知识。 144 | - 会使用Python解析Json。 145 | - 如果会CQ码会更好。 146 | 147 | **示例模板**:可在仓库中的`PluginTemplate`文件夹内找到模板。 148 | 149 | **`Plugin.json`(必要)**: 150 | ```Json 151 | { 152 | "PluginName": "插件名", 153 | "PluginVersion": "插件版本", 154 | "DependentLibraries": null, 155 | "IsLibraries": false, 156 | "PluginIcon": null, 157 | "PluginAuthor": "插件作者名", 158 | "MainPyName": "插件主.py文件名,不要带上.py后缀名", 159 | "PluginDescription": "插件描述", 160 | "EnablePlugin": true, 161 | 162 | "Help": [ 163 | { 164 | "Class": "分类", 165 | "Commands": [ 166 | { 167 | "Command": "指令", 168 | "Content": "指令介绍" 169 | } 170 | ] 171 | } 172 | ] 173 | } 174 | ``` 175 | 176 | **`Plugin.json`文件键值对照表**: 177 | - **`PluginName`**:插件名。 178 | - **`PluginVersion`**:插件版本。 179 | - **`DependentLibraries`**:依赖的第三方库的名称,若`AutoInstallLibraries`值为`true`,则会尝试自动安装这些库,格式为`["库名", "库名", ...]`。 180 | - **`IsLibraries`**:是否为依赖库插件。 181 | - **`PluginIcon`**:插件的图标,格式为`xxx.png`(要带上后缀,主流文件格式即可,可以在文件夹下,但相对路径是从`Plugin.json`文件所在的目录开始的)。 182 | - **`PluginAuthor`**:插件作者名。 183 | - **`MainPyName`**:插件主.py文件名,不要带上.py后缀名,另外py文件里不要赋值`__name__`变量! 184 | - **`PluginDescription`**:插件描述。 185 | - **`EnablePlugin`**:是否启用插件,这是一个标志,用于启用和禁用插件,默认值为`true`即可。 186 | - **`Help`**:帮助菜单内容列表。 187 | - **`Class`**:指令分类。 188 | - **`Commands`**:分类的指令列表。 189 | - **`Command`**:指令。 190 | - **`Content`**:指令介绍。 191 | 192 | **Python文件(必要)示例**: 193 | 194 | ```Python 195 | # 前言,这里用不到的函数可以不定义,可以直接删去,包括API也可以删去不定义,不会报错的 196 | 197 | flora_api = {} # 顾名思义,FloraBot的API,载入(若插件已设为禁用则不载入)后会赋值上 198 | 199 | 200 | def occupying_function(*values): # 该函数仅用于占位,并没有任何意义 201 | pass 202 | 203 | 204 | send_msg = occupying_function 205 | 206 | 207 | def init(): # 插件初始化函数,在载入(若插件已设为禁用则不载入)或启用插件时会调用一次,API可能没有那么快更新,可等待,无传入参数 208 | global send_msg 209 | print(flora_api) 210 | send_msg = flora_api.get("SendMsg") 211 | print("FloraBot插件模板 加载成功") 212 | 213 | 214 | def api_update_event(): # 在API更新时会调用一次(若插件已设为禁用则不调用),可及时获得最新的API内容,无传入参数 215 | print(flora_api) 216 | 217 | 218 | def event(data: dict): # 事件函数,FloraBot每收到一个事件都会调用这个函数(若插件已设为禁用则不调用),传入原消息JSON参数 219 | print(data) 220 | send_type = data.get("SendType") 221 | send_address = data.get("SendAddress") 222 | ws_client = send_address.get("WebSocketClient") 223 | ws_server = send_address.get("WebSocketServer") 224 | send_host = send_address.get("SendHost") 225 | send_port = send_address.get("SendPort") 226 | uid = data.get("user_id") # 事件对象QQ号 227 | gid = data.get("group_id") # 事件对象群号 228 | mid = data.get("message_id") # 消息ID 229 | msg = data.get("raw_message") # 消息内容 230 | if msg is not None: 231 | msg = msg.replace("[", "[").replace("]", "]").replace("&", "&").replace(",", ",") # 消息需要将URL编码替换到正确内容 232 | print(send_type, uid, gid, mid, msg, ws_client, ws_server, send_host, send_port) 233 | ``` 234 | 235 | **注意事项**: 236 | - 这些函数以及`flora_api`变量都不是必要的,少了也不会报错。 237 | - `init`函数里获取API内容的话,`PluginsDict`和`PluginsInfoDict`还不是正确的,推荐放到`api_update_event`函数中处理。 238 | 239 | ## 插件API 240 | 241 | 这些在插件中都可以使用`flora_api.get()`获取到: 242 | 243 | - **`FloraPath`**:FloraBot.py文件所在的绝对路径,不是这个文件的路径,而是所在目录。 244 | - **`FloraHost`**:Bot监听的IP地址。 245 | - **`FloraPort`**:Bot监听的端口号。 246 | - **`FrameworkAddress`**:QQ框架的Http协议监听地址。 247 | - **`BotID`**:登录的Bot账号的ID。 248 | - **`Administrator`**:管理员/所有者/主人的ID列表。 249 | - **`FloraVersion`**:Bot的版本号。 250 | - **`FloraServer`**:Bot的Flask实例。 251 | - **`UpdateFloraApi`**:更新`flora_api`的函数,调用了会同时调用插件中的`api_update_event`函数,无参数。 252 | - **`LoadPlugins`**:加载/重载插件函数,会调用`UpdateFloraApi`函数,无参数。 253 | - **`BroadcastEvent`**:广播消息函数,向所有插件包括内置功能广播基于OneBot协议的数据,参数如下方注释解释: 254 | ```Python 255 | def broadcast_event(data: dict, send_type: str, ws_client=None, ws_server=None, send_host: str = "", send_port: int | str = ""): 256 | # 广播消息函数,data: 基于OneBot协议的数据 257 | # send_type: 发送类型,告诉插件是用HTTP还是WebSocket发送消息 258 | # ws_client: WebSocket连接实例,ws_server: WebSocket服务端实例(若发送类型为WebSocket这两个参数必填) 259 | # send_host: HTTP协议发送地址,send_port: HTTP协议发送端口(若填这两个参数则使用自定义地址发送) 260 | ``` 261 | **调用示例**: 262 | - HTTP: 263 | ```Python 264 | broadcast_event(data, "HTTP") 265 | ``` 266 | - WebSocket: 267 | ```Python 268 | broadcast_event(data, "WebSocket", ws_client, ws_server) 269 | ``` 270 | - **`SendMsg`**:发送信息函数,也可以发送事件,只要你会CQ码,参数如下方注释解释: 271 | ```Python 272 | def send_msg(msg: str, uid: str | int, gid: str | int | None, mid: str | int | None = None, ws_client=None, ws_server=None, send_host: str = "", send_port: int | str = ""): 273 | # 发送消息函数,send_type: 发送类型,决定是用HTTP还是WebSocket发送消息 274 | # msg: 正文,uid: ID,gid: 群号,mid: 消息编号 275 | # ws_client: WebSocket连接实例,ws_server: WebSocket服务端实例(若发送类型为WebSocket这两个参数必填) 276 | # send_host: HTTP协议发送地址,send_port: HTTP协议发送端口(若填这两个参数则使用自定义地址发送) 277 | ``` 278 | **关于`SendMsg`的补充**:默认传入参数如下即可: 279 | ```Python 280 | send_msg(send_type, "正文", uid, gid, mid, ws_client, ws_server, send_host, send_port) 281 | ``` 282 | 默认为回复信息,如果不需要回复将参数`mid`改成`None`即可。调用函数后会返回相应的信息(dict类型),有需求可获取其中的数据(如获取该消息的mid,可用于撤回该消息)。 283 | - **`CallApi`**:向框架调用API,会返回dict类型的数据,参数与`SendMsg`差不多,函数如下: 284 | ```Python 285 | def call_api(send_type: str, api: str, params: dict, ws_client=None, ws_server=None, send_host: str = "", send_port: int | str = ""): 286 | # 发送消息函数,send_type: 发送类型,决定是用HTTP还是WebSocket发送消息 287 | # api: 接口/终结点去掉"/"(str类型),data: 数据(dict类型) 288 | # ws_client: WebSocket连接实例,ws_server: WebSocket服务端实例(若发送类型为WebSocket这两个参数必填) 289 | # send_host: HTTP协议发送地址,send_port: HTTP协议发送端口(若填这两个参数则使用自定义地址发送) 290 | ``` 291 | **API(终结点)以及该API的参数查阅OneBot文档调用**,调用函数后会返回相应的信息(dict类型),有需求可获取其中的数据。以下是调用示例: 292 | ```Python 293 | print(call_api(send_type, "API", {}, ws_client, ws_server, send_host, send_port)) 294 | ``` 295 | - **`HelpInfoDict`**:所有插件包括FloraBot的帮助字典。 296 | - **`CallApiReturned`**:调用框架API时,如果为WebSocket协议,这里将会记录返回的数据。这个逻辑也相对地引出了新的Bug,当插件以WebSocket协议广播消息时,若其他插件会发送消息或调用API时会等待WebSocket返回调用参数返回的数据,因为是插件广播的,所以会一直永远的等待下去。若插件有这个需求,还请查看源代码进行开发。作者已经尽力了(一般绝对用不上,要用请参考源代码)。 297 | - **`PluginsDict`**:插件对象字典,使用对应的插件名获取,并赋值给变量(或不赋值直接调用),即可将对应的插件当作库来调用。 298 | - **`PluginsInfoDict`**:插件信息字典,使用对应的插件名获取,可获取到对应插件的`Plugin.json`已转换为Python对象的内容。 299 | - **`ThePluginPath`**:插件对于`FloraBot.py`文件所在的目录的相对路径。由于是将插件导入再调用的,所以任何相对路径都是从`FloraBot.py`文件所在的目录开始的。这非常重要,不推荐使用自己手动定义到插件资源的相对路径,而是推荐使用`ThePluginPath` + 插件相对于资源的相对路径(因为可能会出现种种原因导致你手动定义到插件资源的相对路径不能正确使用插件文件夹中的文件)。示例:我有一个叫做Test.json的文件,在插件目录中的文件夹Test中(即Test/Test.json),那么获取`ThePluginPath`的值拼接到路径"/Test/Test.json"的前面即可获得`FloraBot.py`与该文件的相对路径,现在就可以在插件中正确地使用这个文件了(希望不会那么拗口:))。 300 | **如果还是不能理解`ThePluginPath`的话,直接上代码**: 301 | ```Python 302 | import json 303 | 304 | group_white_list = [] 305 | 306 | 307 | def init(): 308 | global group_white_list 309 | with open(f"./{flora_api.get('ThePluginPath')}/Plugin.json", "r", encoding="UTF-8") as plugin_config: 310 | group_white_list = json.loads(plugin_config.read()).get("GroupWhiteList") 311 | ``` 312 | 上述代码使用`ThePluginPath`拼接了当前插件配置文件的路径,并且读取并获取了当中的`GroupWhiteList`键的值。 313 | 另外,如果要发送图片等本地文件需要绝对路径可以这么写: 314 | ```Python 315 | flora_api = {} 316 | 317 | 318 | def occupying_function(*values): # 该函数仅用于占位,并没有任何意义 319 | pass 320 | 321 | 322 | send_msg = occupying_function 323 | 324 | 325 | def init(): 326 | global send_msg 327 | send_msg = flora_api.get("SendMsg") 328 | 329 | 330 | def event(data: dict): # 事件函数,FloraBot每收到一个事件都会调用这个函数(若插件已设为禁用则不调用),传入原消息JSON参数 331 | print(data) 332 | send_type = data.get("SendType") 333 | send_address = data.get("SendAddress") 334 | ws_client = send_address.get("WebSocketClient") 335 | ws_server = send_address.get("WebSocketServer") 336 | send_host = send_address.get("SendHost") 337 | send_port = data.get("SendPort") 338 | uid = data.get("user_id") # 事件对象QQ号 339 | gid = data.get("group_id") # 事件对象群号 340 | mid = data.get("message_id") # 消息ID 341 | msg = data.get("raw_message") # 消息内容 342 | if msg is not None: 343 | msg = msg.replace("[", "[").replace("]", "]").replace("&", "&").replace(",", ",") # 消息需要将URL编码替换到正确内容 344 | if msg == "TestSendImage": 345 | send_msg(send_type, f"[CQ:image,file=file:///{flora_api.get('FloraPath')}/{flora_api.get('ThePluginPath')}/Test.png]", uid, gid, mid, ws_client, ws_server, send_host, send_port) 346 | ``` 347 | 上述代码发送图片时使用了`FloraPath`和`ThePluginPath`拼接了当前插件文件夹下的Test.png的绝对路径,或获取相对路径的文件的绝对路径。 348 | **注意!!!**:拼接路径请使用`/`而不是`\`,因为如果路径中出现了`\`则只能在Windows中使用,而`/`则是全平台,Windows支持使用`/`拼接路径。 349 | 350 | ## 推荐QQ框架 351 | 352 | - **[NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ)** 353 | 354 | ## 关于 355 | 356 | **QQ群(仅供开发者加入):[994825372](http://qm.qq.com/cgi-bin/qm/qr?group_code=994825372)** 357 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 2 | requests 3 | flask 4 | websocket-server 5 | colorama --------------------------------------------------------------------------------