# 多账号 > 核心`1.8.0`开始支持多账号功能。 >[warning] 并不是每一个插件都可以完美支持多账号功能的全部特性,具体注意事项以组件的具体说明为准。 由于多账号功能更新后有不少改动,因此专门使用单独章节介绍。 >[info] 一般来讲,注册一个账号信息需要两个值:账号与请求地址。而大多数情况下,账号是不必要的,因为如果你的请求地址正确,再验证的时候会自动获取到你的账号信息。但是假如你指定了账号的值,那么在获取到信息后会在对**你指定的值**与**获取到的值**进行一次匹配。 ## **账号注册** 在启动的时候,至少需要注册一个BOT。而在程序运行期间,有可能需要额外注册一些BOT。那么综上所述,账号注册的方式存在两种:**初始注册** 与 **动态注册**。 ### **初始注册** 初始注册即启动的时候就会注册的账号。初始注册的方式有三种,但是都与配置有关,所以你可以在[配置](./配置文件.md)中找到关于账号注册的相关信息。这里我将它们单独拿出来再简单介绍一下: #### **代码配置** 代码配置中共有6种方法,两个方法名,分别是`registerBot` 与 `registerBotAsDefault` ```java /** * 注册一个机器人的信息。 * * @param botCode bot账号 * @param path 上报地址,为一个完整的请求路径,例如:http://127.0.0.1:12345 */ public void registerBot(String botCode, String path); /** * 注册一个机器人的信息。 一般来讲最终拼接为:http://{ip}:{port}{path} * @param botCode bot账号 * @param ip 上报地址的ip * @param port 上报地址的端口 * @param path 如果存在,上报地址的路径 */ public void registerBot(String botCode, String ip, int port, String path); /** * 仅仅注册一个路径信息,需要是一个完整路径,例如一个http路径或者一个ws的连接路径。 * 在启动后需要通过此路径来验证或者连接。 * 一般来说直接使用这个就行了。 * * @param path 上报路径 */ public void registerBot(String path); /** * 仅注册一个路径信息,信息分为ip、端口、一个可能存在的额外路径。 * 开发者如果需要实现对于ip、端口、额外路径的转化规则,尝试重写{@link #toHttpPath(String, int, String)}方法。默认情况下为转化为http协议路径。 * @param ip ip地址 * @param port 端口 * @param path nullable */ public void registerBot(String ip, int port, String path); /** * 注册一个机器人的信息。与普通的registerBot不同,此处注册的机器人会直接覆盖当前的默认机器人信息 * * @param botCode bot账号 * @param path 上报地址,为一个完整的请求路径 */ public void registerBotAsDefault(String botCode, String path); /** * 注册一个机器人的信息。 * @param botCode bot账号 * @param ip 上报地址的ip * @param port 上报地址的端口 * @param path 如果存在,上报地址的路径 */ public void registerBotAsDefault(String botCode, String ip, int port, String path); /** * 仅仅注册一个路径信息,需要是一个完整路径,例如一个http路径或者一个ws的连接路径。 * 在启动后需要通过此路径来验证或者连接 * * @param path 上报路径 */ public void registerBotAsDefault(String path); /** * <pre> 仅注册一个路径信息,信息分为ip、端口、一个可能存在的额外路径。 * <pre> 开发者如果需要实现对于ip、端口、额外路径的转化规则,尝试重写{@link #toHttpPath(String, int, String)}方法。默认情况下为转化为http协议路径。 * @param ip ip地址 * @param port 端口 * @param path nullable */ public void registerBotAsDefault(String ip, int port, String path); ``` 虽然方法很多,但是最终都是同一个结果:注册了一个账号与请求路径的对应信息。其中,账号可以省略。 #### **文件配置** 文件配置中,多账号的配置项名称为`core.bots`, 起始状态所注册的bot账号列表的格式:`{code}:{path},{code}:{path},....`, 其中{code}可以是空的。 其中,`{code}:{path}`为一组信息。 一组账号与地址使用冒号分割,多组信息使用逗号分割 如果为空,则默认注册一个本地ip地址:`:http://127.0.0.1:5700` **例1**(不省略账号信息): `11111111:http://127.0.0.1:8080,2222222:http://192.168.0.1:7777` **例2**(省略账号信息): `:http://127.0.0.1:8080,:http://192.168.0.1:7777` >[warning] 在省略了账号信息的时候,请记住它们中间的那个冒号`:`不可以省略哦 配置文件的例子: ```java core.bots=:http://127.0.0.1:5700 ``` #### **注解配置** 注解配置是核心`1.8.0`的新功能,其配置映射格式与文件配置相同,这里举个例子: ```java @SimpleRobotConfiguration({ @ConfigurationProperty(key = "core.bots", value = ":http://127.0.0.1:5700"), // 其他配置...... }) public class Test1 { // do some.. } ``` ### **动态注册** 当程序运行期间,你想要注册一个BOT,你也有那么几个选择。 #### **BotRuntime** `BotRuntime`类是一个框架启动完成后被初始化的类,你可以直接使用`BotRuntime.getRuntime()`来获取他的实例,然后你可以通过它的`getBotManager()`方法来获取一个`BotManager`对象。 #### **BotManager** 除了上述的使用`BotRuntime`类来获取`BotManager`对象以外,理论上你可以直接将`BotManager`对象作为依赖注入的参数放在类字段上或者监听函数的参数上。哦,不要忘记了`@Depend`注解。 当你得到`BotManager`类实例的时候,你会发现他其中包括了5个`registerBot`方法的重载。这5个方法的含义与上述的**代码配置**中提到的注册方法大致相同,所以,直接注册即可。 如果最终验证成功,你便可以使用了。 <br> ## **BotManager** 在接着向下介绍之前,先介绍一下`BotManager`类。 `BotManager`是对多BOT的管理中心,它是一个接口类型,其中所定义的全部API如下: ```java /** * 大多数情况下,可能不一定必须指定一个bot,则此方法规定获取一个默认的bot * @return 获取一个默认bot */ BotInfo defaultBot(); /** * 设置默认bot的账号信息 * @param botCode 默认bot的账号信息 */ void setDefaultBot(String botCode); /** * 通过bot的code获取一个Bot的信息 * 参数有可能为null * @param botCode 账号 * @return bot信息 */ BotInfo getBot(String botCode); /** * 获取全部的bot信息 * @return bots */ BotInfo[] bots(); /** * 注册一个botInfo。在实现的时候需要注意线程安全问题,概率较小,但是不是没有可能 * @param info bot信息,作为key的code信息将会从其中获取。info中的各项参数不可为null * @return 是否注册成功 */ boolean registerBot(BotInfo info); /** * 注册一个bot。 * @param code 账号, 可以为null * @param ip ip地址 * @param port 端口号 * @param path 请求路径 * @return 是否注册成功 */ boolean registerBot(String code, String ip, int port, String path); /** * 注册一个bot。 * @param code 账号, 可以为null * @param fullPath 完整路径 * @return 是否注册成功 */ boolean registerBot(String code, String fullPath); /** * 注册一个bot。code为null * @param ip ip地址 * @param port 端口号 * @param path 请求路径 * @return 是否注册成功 */ boolean registerBot(String ip, int port, String path); /** * 注册一个bot。code为null * @param fullPath 完整路径 * @return 是否注册成功 */ boolean registerBot(String fullPath); /** * 获取注册用的验证函数 * * @return 验证函数 */ VerifyFunction getVerifyFunction(); /** * 获取路径拼接函数 * @return 拼接函数 */ PathAssembler getPathAssembler(); ``` ## **使用** 使用多账号来发送消息有很多种办法。一般想要使用指定的BOT来发送消息,首先需要获得这个BOT验证过的`BotInfo`对象。 ### **BotInfo** `BotInfo`对象是对一个已经注册的BOT的信息封装,其中包括这个BOT的账号(`code`)、上报地址(`path`)、登录信息(`LoginInfo`)以及其对应的三大送信器(`BotSender`)。 `BotInfo`为接口类型,其定义如下: ```java /** * 获取Bot的账号信息 * @return Bot账号信息 */ String getBotCode(); /** * 获取此bot的上报信息 * @return bot上报信息 */ String getPath(); /** * 获取此账号的登录信息 * @return 获取登录信息 */ LoginInfo getInfo(); /** * 获取当前bot所对应的送信器 * @return 当前账号送信器 */ BotSender getSender(); ``` ### **BotSender** `BotSender`就是一个BOT专属版的`MsgSender`,这个类里只有三个公共常量: `SENDER`、`GETTER`、`SETTER` 是不是很眼熟?就像平时那样用就好了。 ```java // 获取botInfo BotInfo bot = botManager.getBot("111"); // 获取BotSender BotSender sender = bot.getSender(); // 发送消息 sender.SENDER.sendPrivateMsg("22222", "hi"); // 或者,连起来 botManager.getBot("111").getSender().SENDER.sendPrivateMsg("22222", "hi"); ``` <br> ***** >[success] 那么我应该如何获取上面提到的`BotInfo`和`BotSender`呢? #### **BotManager** 当你得到`BotManager`类实例的时候(上面**动态注册**提到过如何获取了),可以通过以下方法获得一个或多个`BotInfo`对象: ```java /** * 大多数情况下,可能不一定必须指定一个bot,则此方法规定获取一个默认的bot * @return 获取一个默认bot */ BotInfo defaultBot(); /** * 通过bot的code获取一个Bot的信息 * 参数有可能为null * @param botCode 账号 * @return bot信息 */ BotInfo getBot(String botCode); /** * 获取全部的bot信息 * @return bots */ BotInfo[] bots(); ``` #### **MsgSender** 送信器`MsgSender`中增加了4个方法以获取`BotInfo`或`BotSender`: ```java /** * 获取一个指定的Bot对象 * @param botCode botCode * @return {@link BotInfo} bot信息 */ public BotInfo bot(String botCode); /** * 获取一个默认的Bot对象 * @return {@link BotInfo} bot信息 */ public BotInfo bot(); /** * 获取一个指定bot的送信器 * @param botCode bot账号 * @return bot送信器 */ public BotSender botSender(String botCode); /** * 获取默认的Bot送信器 * @return bot送信器 */ public BotSender botSender(); ``` 直接使用就好喽: ```java @Listen(MsgGetTypes.privateMsg) @Filter("hello.*") public void testListen1(PrivateMsg msg, MsgSender sender) { sender.botSender("22222").SENDER.sendPrivateMsg("66666", "hello~"); } ``` ## **送信器机制变动** 看到这里不知道你有没有一些小小的疑问或猜想:如果在`MsgSender`中使用原本的送信器而不去获取指定bot的送信器,那么我到底是哪个BOT发送的消息呢? <br> 举个例子: ```java @Listen(MsgGetTypes.privateMsg) @Filter("hello.*") public void testListen1(PrivateMsg msg, MsgSender sender) { sender.SENDER.sendPrivateMsg("66666", "hello~"); } ``` > 这种情况下,我到底是用谁发送的消息呢? ### ThisCodeAble 在核心`1.7.0`之后,我在[监听消息API](./监听消息API.md)中的信息携带者里增加了一个携带者:`接收者信息携带者 ThisCodeAble`。 简单来说,`ThisCodeAble`是一个接口类型,它定义了两个方法: ```java /** * 此消息获取的时候,代表的是哪个账号获取到的消息。 * @return 接收到此消息的账号。 */ String getThisCode(); /** * 允许重新定义Code以实现在存在多个机器人的时候切换处理。 * @param code code */ void setThisCode(String code); ``` 允许你去获取或者设置当前消息是由哪个BOT接收到的。而这个接口被`MsgGet`接口所继承。如果有仔细读我的文档的话应该会知道,`MsgGet`是所有监听消息封装类的父类接口,因此所有的监听消息均得到了获取到自身BOT的能力,并且你可以重新设置它。 >[info] 比如最常见的`GroupMsg`与`PrivateMsg`。 为什么要提到这个呢?因为在`1.8.0`更新后,`MsgSender`在一般情况下会随着当前消息的BOT账号的变化而变化。 简单举个例子: ```java @Listen(MsgGetTypes.privateMsg) public void testListen1(PrivateMsg msg, MsgSender sender) { System.out.println(msg.getThisCode()); // 假设当前BOT账号为11111 // 此时,由账号"11111"向好友"12345"发送了一个"hello" sender.SENDER.sendPrivateMsg("12345", "hello"); // 重新设置BOT账号为22222 msg.setThisCode("22222"); // 此时,由账号"22222"向好友"12345"发送了一个"hello" sender.SENDER.sendPrivateMsg("12345", "hello"); } ``` 即,当前消息中的Msg中的`ThisCode`发生变化的时候,`MsgSender`内部默认的BOT账号会一同发生变化。 >[danger] 上述特性取决于插件是否支持在上报数据中存在BOT信息。CQ HTTP API插件支持上述特性,但是LEMOC无法支持上述特性。 >[warning] 当插件无法支持上述特性的时候,一般来讲`MsgSender`所使用的默认送信BOT为注册的时候所指定的默认BOT。如果没有指定,则为注册的第一个BOT。