├── .gitignore ├── LICENSE ├── README.md ├── permission.md ├── pom.xml ├── src └── main │ ├── Resource │ └── META-INF │ │ └── MANIFEST.MF │ ├── java │ └── net │ │ └── diyigemt │ │ └── miraiboot │ │ ├── annotation │ │ ├── AutoInit.java │ │ ├── ConsoleCommand.java │ │ ├── EventHandler.java │ │ ├── EventHandlerComponent.java │ │ ├── EventHandlerHelp.java │ │ ├── ExceptionHandler.java │ │ ├── ExceptionHandlerComponent.java │ │ ├── MessageFilter.java │ │ ├── MessageFilters.java │ │ ├── MessagePreProcessor.java │ │ ├── MiraiBootApplication.java │ │ └── MiraiBootComponent.java │ │ ├── autoconfig │ │ ├── GlobalLoader.java │ │ ├── JarPluginLoader.java │ │ ├── MiraiApplication.java │ │ ├── MiraiBootPlugin.java │ │ └── PluginLoader.java │ │ ├── constant │ │ ├── ConstantException.java │ │ ├── ConstantGlobal.java │ │ ├── ConstantHttp.java │ │ ├── ConstantSQL.java │ │ ├── EventHandlerType.java │ │ ├── EventType.java │ │ ├── FunctionId.java │ │ ├── MessageFilterMatchType.java │ │ └── MessagePreProcessorMessageType.java │ │ ├── core │ │ ├── MiraiBootConsole.java │ │ ├── PluginMgr.java │ │ └── RegisterProcess.java │ │ ├── dao │ │ └── PermissionDAO.java │ │ ├── entity │ │ ├── AutoInitItem.java │ │ ├── BaseEventPack.java │ │ ├── BotEventPack.java │ │ ├── ConfigFile.java │ │ ├── ConfigFileBot.java │ │ ├── ConfigFileBotConfiguration.java │ │ ├── ConfigFileBotPassword.java │ │ ├── ConfigFileLogger.java │ │ ├── ConfigFileMain.java │ │ ├── ConsoleHandlerItem.java │ │ ├── EnhancedMessageChain.java │ │ ├── EventHandlerItem.java │ │ ├── EventHandlerNextItem.java │ │ ├── ExceptionHandlerItem.java │ │ ├── HttpProperties.java │ │ ├── MessageEventPack.java │ │ ├── MessageFilterImp.java │ │ ├── MessageFilterItem.java │ │ ├── MessageProcessorImp.java │ │ ├── MiraiBootHandlerItem.java │ │ ├── PermissionItem.java │ │ ├── PluginItem.java │ │ └── PreProcessorData.java │ │ ├── exception │ │ ├── EventHandlerInvokeException.java │ │ └── MultipleParameterException.java │ │ ├── function │ │ ├── Alias.java │ │ ├── CreateGlobalException.java │ │ ├── ExceptionA.java │ │ ├── TestBotEvent.java │ │ ├── TestException.java │ │ ├── TestExceptionHandler.java │ │ ├── TestFilter.java │ │ ├── TestFunction.java │ │ ├── TestNext.java │ │ ├── TestTmp.java │ │ ├── TextFileMsgBuilder.java │ │ ├── TextImgMsgBuilder.java │ │ ├── TextSendMsgLibrary.java │ │ ├── TextVocMsgBuilder.java │ │ ├── console │ │ │ ├── ConsoleExit.java │ │ │ ├── ConsoleLoad.java │ │ │ ├── ConsolePlugin.java │ │ │ ├── ConsolePluginList.java │ │ │ └── ConsoleUnloadPlugin.java │ │ ├── testfilter │ │ │ ├── TestFilter.java │ │ │ └── TestFilterA.java │ │ ├── testpreprocess │ │ │ ├── TestDataA.java │ │ │ ├── TestPreProcess.java │ │ │ └── TestPreProcessA.java │ │ └── text.java │ │ ├── interfaces │ │ ├── EventHandlerNext.java │ │ ├── IMessageFilter.java │ │ ├── IMessagePreProcessor.java │ │ └── UnloadHandler.java │ │ ├── listener │ │ ├── BotEventListener.java │ │ ├── ExceptionListener.java │ │ └── MessageEventListener.java │ │ ├── mirai │ │ └── MiraiMain.java │ │ ├── permission │ │ ├── AuthMgr.java │ │ ├── CheckPermission.java │ │ ├── Permission.java │ │ ├── PermissionCheck.java │ │ └── TempPermission.java │ │ ├── sql │ │ └── DatabaseHelper.java │ │ └── utils │ │ ├── BotManager.java │ │ ├── CommandUtil.java │ │ ├── EventHandlerManager.java │ │ ├── ExceptionHandlerManager.java │ │ ├── FileUtil.java │ │ ├── GlobalConfig.java │ │ ├── HttpUtil.java │ │ ├── PermissionUtil.java │ │ ├── SendMessageLib.java │ │ └── builder │ │ ├── ExternalResourceBuilder.java │ │ ├── FileMessageBuilder.java │ │ ├── ImageMessageBuilder.java │ │ └── VoiceMessageBuilder.java │ └── resources │ ├── META-INF │ └── MANIFEST.MF │ ├── application-example.yml │ └── banner.txt └── 介绍.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /.gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | out/ 11 | .DS_Store 12 | /build 13 | /captures 14 | /gradle 15 | .externalNativeBuild 16 | .cxx 17 | local.properties 18 | /.idea 19 | /target/ 20 | /data/ 21 | /cache/ 22 | /config/ -------------------------------------------------------------------------------- /permission.md: -------------------------------------------------------------------------------- 1 | ## 权限管理 2 | 3 | 通过对消息事件处理器添加`@CheckPermission`注解,可对该指令启用动态权限管理功能。 4 | 5 | ```java 6 | @EventHandlerComponent 7 | public class Test { 8 | @EventHandler(target = "复读") 9 | @CheckPermission(blocks = "123123123", isAdminOnly = true, functionId = 2) 10 | public void test(MessageEventPack eventPack, PreProcessorData data) { 11 | eventPack.reply(eventPack.getMessage()); 12 | } 13 | } 14 | ``` 15 | 16 | 上述指令将忽略对qq号为123123123的响应,同时只允许管理员操作,并指定指令对应的权限id为2。 17 | 18 | 该注解的所有参数如下: 19 | 20 | #### `@CheckPermission`: 21 | 22 | | 类型 | 属性名 | 默认值 | 说明 | 23 | | -------- | ------------------ | ------ | ------------------------------------------------ | 24 | | String[] | allows | {} | 允许操作的qq号列表,非空将会只允许列表中的人操作 | 25 | | String[] | blocks | {} | 禁止操作的qq号列表,为空表示不检查 | 26 | | boolean | isStrictRestricted | false | | 27 | | boolean | isGroupOwnerOnly | false | 是否只允许群主操作 | 28 | | boolean | isAdminOnly | false | 是否只允许管理员操作(包括群主) | 29 | | int | functionId | | | 30 | 31 | 以上参数的优先级为: 32 | 33 | permission table -> blocks -> allows -> isStrictRestricted -> isGroupOwnerOnly -> isAdminOnly -------------------------------------------------------------------------------- /src/main/Resource/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: net.diyigemt.miraiboot.Main 3 | 4 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/AutoInit.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import net.diyigemt.miraiboot.dao.PermissionDAO; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | *

对被注释类启用自动初始化

9 | * 样例:{@link PermissionDAO}
10 | * 自动初始化将会在程序启动完成(bot登录前执行) 11 | *

被注解的类中必须有一个静态方法

12 | * *@param config 配置文件
13 | * public static void init(ConfigFile config) {//your code}
14 | * value指初始化的优先级 数字越大越先进行初始化 15 | * @author diyigemt 16 | * @since 1.0.0 17 | */ 18 | @Target({ElementType.TYPE}) 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @Documented 21 | public @interface AutoInit { 22 | int value() default 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/ConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | *

注册一个控制台指令

7 | * @author diyigemt 8 | */ 9 | @Target({ElementType.METHOD}) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | public @interface ConsoleCommand { 13 | /** 14 | * 指令名称 15 | */ 16 | String value(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/EventHandler.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import net.diyigemt.miraiboot.constant.EventHandlerType; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | 6 | import java.lang.annotation.*; 7 | 8 | 9 | /** 10 | *

将受到注解的方法注册为事件handler

11 | * 注意, 目前最多支持2参数的方法 多余的参数将会传入null 12 | * 受到注解的方法必须为public的 13 | *

14 | * 例如 15 | *

16 |  * {@code
17 |  * 在群消息中发送任何一条含有aaa的消息就会被触发
18 |  * @EventHandler(target = "aaa")
19 |  * public void test1() { your code }
20 |  * 在群消息中发送任何一条含有/aaa的消息就会被触发
21 |  * @EventHandler(start = "/", target = "aaa")
22 |  * public void test1(MessageEventPack eventPack) { your code }
23 |  * 任何消息均会触发该事件处理器
24 |  * !!此时消息中所有纯文本内容均会作为参数被解析后放入data.args中!
25 |  * @EventHandler(isAny = true)
26 |  * public void test1(MessageEventPack eventPack, PreProcessData data) { your code }
27 |  * }
28 |  * 
29 | *

30 | * 关于PreProcessData的内容, 请参考{@link PreProcessorData} 31 | * @author diyigemt 32 | * @since 1.0.0 33 | */ 34 | @Target({ElementType.METHOD}) 35 | @Retention(RetentionPolicy.RUNTIME) 36 | @Documented 37 | public @interface EventHandler { 38 | /** 39 | * 是否为常驻消息处理器
40 | * 设置为true时无论如何该消息处理器均会触发 41 | * @since 1.0.0 42 | */ 43 | boolean isAny() default false; 44 | /** 45 | * 匹配的指令开头 为空表示忽略
46 | * 如:@MessageEventFilter(start = "/") 47 | * 表示将从 '/'之后的内容获取指令名和参数
48 | * 如:消息 '一些乱七八糟的内容 /help asd'
49 | * 将会获取到指令 help 和参数 asd 50 | * 当配置文件中存在DEFAULT_COMMAND_START非空配置项且start值为空时 将使用配置项代替 51 | * @since 1.0.0 52 | */ 53 | String start() default ""; 54 | /** 55 | *

参数分隔符 用于获取指令之后的文本参数

56 | * 值为正则表达式 默认使用'\\s+' 即以至少1个空格为分割
57 | * 如:@EventHandler(start = "/", split = "\\s+")
58 | * 处理消息 '一些乱七八糟的内容 /help asd ssd'
59 | * 将会得到 指令 help 和参数 asd与ssd 60 | * @since 1.0.0 61 | */ 62 | String split() default "\\s+"; 63 | /** 64 | * 触发handler的指令 为空时取受到注解的方法名 65 | * @since 1.0.0 66 | */ 67 | String target() default ""; 68 | /** 69 | * 处理类型 群消息或好友消息或者全部
70 | * 更多消息类型在做了 71 | * @since 1.0.0 72 | * @see EventHandlerType 73 | */ 74 | EventHandlerType[] type() default EventHandlerType.MESSAGE_HANDLER_ALL; 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/EventHandlerComponent.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | *

在受到注解的类中扫描事件handler

7 | *

未受到该注解的类 扫描时将会被忽略

8 | * @author diyigemt 9 | * @since 1.0.0 10 | */ 11 | @Target({ElementType.TYPE}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | public @interface EventHandlerComponent { 15 | /** 16 | *

默认优先级

17 | * 当@CheckPermission的permissionIndex未设置(为默认的0)时取该值 18 | */ 19 | int value() default 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/EventHandlerHelp.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | 4 | import java.lang.annotation.*; 5 | 6 | /** 7 | *

为指令添加帮助信息

8 | *

例如:

9 |
10 |  {@code
11 |  @EventHandler(target = "乐", start = "/")
12 |  @EventHandlerHelp(value = 1, description = "发送一个乐子", detail = {/乐 随机发送一个乐子", "/乐 1  发送编号为1的乐子"})
13 |  public void happy(MessageEventPack eventPack, PreProcessData data) { your code }
14 |  }
15 | 
16 | * 以上将会在用户发送"帮助"的时候回复:"可用指令:1./乐"
17 | * 在用户发送"帮助 1"的时候回复:"1./乐 随机发送一个乐子/n2./乐 1 发送编号为1的乐子" 18 | * @author diyigemt 19 | * @since 1.1.0 20 | */ 21 | @Target({ElementType.METHOD}) 22 | @Retention(RetentionPolicy.RUNTIME) 23 | @Documented 24 | public @interface EventHandlerHelp { 25 | /** 26 | *

是否录入帮助信息

27 | * 为false时不会将被注解的方法录入帮助信息中 28 | */ 29 | boolean enable() default true; 30 | /** 31 | *

指令对应的帮助id

32 | * 会在用户输入 帮助 帮助id时,将id对应的指令具体触发方式描述打印 33 | */ 34 | int value(); 35 | /** 36 | *

指令功能描述

37 | */ 38 | String description() default ""; 39 | 40 | /** 41 | *

指令的具体触发方式描述

42 | */ 43 | String[] detail() default {}; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | 4 | import java.lang.annotation.*; 5 | 6 | /** 7 | *

将收到注解的方法注册为异常Handler

8 | * 受到注解的方法至多有一个参数 即[Throwable]本身 多余的参数将会被传入null
9 | * 方法返回值可以为void 也可以为boolean 返回boolean且为true时将会阻止低优先级的异常处理器的触发
10 | * 返回值不正确的方法将不会被注册成异常处理器!!
11 | * 注解的value为空也不会被注册为异常处理器!!
12 | *
13 |  * {@code
14 |  * @ExceptionHandler
15 |  * public void testHandler1(Throwable e) {your code}
16 |  *
17 |  * @ExceptionHandler(priority = 1)
18 |  * public boolean testHandler2(Throwable e) {your code}
19 |  *
20 |  * @ExceptionHandler(priority = 1)
21 |  * public boolean testHandler3() {your code}
22 |  * }
23 |  * 
24 | */ 25 | @Target({ElementType.METHOD}) 26 | @Retention(RetentionPolicy.RUNTIME) 27 | @Documented 28 | public @interface ExceptionHandler { 29 | /** 30 | *

处理器名

31 | * 可根据该名字主动移除一个异常处理器 32 | */ 33 | String name() default ""; 34 | /** 35 | *

要处理的异常列表

36 | */ 37 | Class value(); 38 | 39 | /** 40 | *

处理优先级

41 | */ 42 | int priority() default 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/ExceptionHandlerComponent.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | 4 | import java.lang.annotation.*; 5 | 6 | /** 7 | *

在受到注解的类中扫描异常handler

8 | *

未受到该注解的类 扫描时将会被忽略

9 | * @author diyigemt 10 | * @since 1.0.0 11 | */ 12 | @Target({ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface ExceptionHandlerComponent { 16 | /** 17 | *

默认优先级

18 | * 当@ExceptionHandler的priority未设置(为默认的0)时取该值 19 | */ 20 | int value() default 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/MessageFilter.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import net.diyigemt.miraiboot.constant.MessageFilterMatchType; 4 | import net.diyigemt.miraiboot.entity.MessageFilterImp; 5 | import net.diyigemt.miraiboot.interfaces.IMessageFilter; 6 | 7 | import java.lang.annotation.*; 8 | 9 | /** 10 | *

消息事件过滤器

11 | * 在所有规则通过时EventHandler才会被触发
12 | * 其实和@EventHandler有点重复了
13 | * 可以重复注解 此时将会对所有进行匹配 (更多匹配规则在做了) 14 | * @author diyigemt 15 | * @since 1.0.0 16 | */ 17 | @Target({ElementType.METHOD}) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Repeatable(MessageFilters.class) 20 | @Documented 21 | public @interface MessageFilter { 22 | 23 | /** 24 | * 匹配关键词的内容 为空表示忽略 25 | * @since 1.0.0 26 | */ 27 | String value() default ""; 28 | /** 29 | * 匹配类型 详见MessageEventFilterMatchType 30 | * @see MessageFilterMatchType 31 | * @since 1.0.0 32 | */ 33 | MessageFilterMatchType matchType() default MessageFilterMatchType.NULL; 34 | /** 35 | * 匹配的账号列表 36 | * 若消息发送着不在列表内则不做响应 37 | * 为空则允许所有 38 | * @since 1.0.0 39 | */ 40 | String[] accounts() default {}; 41 | /** 42 | * 匹配的群列表 43 | * 若消息发送着不在列表内则不做响应 44 | * 为空则允许所有 45 | * @since 1.0.0 46 | */ 47 | String[] groups() default {}; 48 | /** 49 | * 匹配的机器人qq号列表 50 | * 若消息接受的bot不在列表内则不做响应 51 | * 为空则允许所有 52 | * @since 1.0.0 53 | */ 54 | String[] bots() default {}; 55 | /** 56 | * 是否当bot被at时才触发
57 | * 好友消息时忽略 58 | * @since 1.0.0 59 | */ 60 | boolean isAt() default false; 61 | /** 62 | * 是否at全体时才触发
63 | * 好友消息时忽略 64 | * @since 1.0.0 65 | */ 66 | boolean isAtAll() default false; 67 | /** 68 | * 是否有人被at时才触发 69 | * 不一定是bot被at
70 | * 好友消息时忽略 71 | * @since 1.0.0 72 | */ 73 | boolean isAtAny() default false; 74 | 75 | /** 76 | * 自定义消息过滤器
77 | * 不为默认值时将与以上设置共存 78 | */ 79 | Class filter() default MessageFilterImp.class; 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/MessageFilters.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | *

消息事件过滤器组

7 | * @author diyigemt 8 | * @since 1.0.0 9 | * @see MessageFilter 10 | */ 11 | @Target({ElementType.METHOD}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | public @interface MessageFilters { 15 | MessageFilter[] value() default {}; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/MessagePreProcessor.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import net.diyigemt.miraiboot.constant.MessagePreProcessorMessageType; 4 | import net.diyigemt.miraiboot.entity.MessageProcessorImp; 5 | import net.diyigemt.miraiboot.entity.PreProcessorData; 6 | import net.diyigemt.miraiboot.interfaces.IMessagePreProcessor; 7 | 8 | import java.lang.annotation.*; 9 | 10 | /** 11 | *

消息事件预处理器

12 | * 对收到的消息事件进行预处理
13 | * 例如 14 | *
15 |  * {@code
16 |  * 以下配置将会响应 搜图 指令 并将消息中的图片过滤至 data.classified中
17 |  * @EventHandler(target = "搜图")
18 |  * @MessagePreProcessor(filterType = MessagePreProcessorMessageType.IMAGE)
19 |  * public void searchImage(MessageEvent event, PreProcessData data) { your code }
20 |  * }
21 |  * 
22 | * @author diyigemt 23 | * @since 1.0.0 24 | */ 25 | @Target({ElementType.METHOD}) 26 | @Retention(RetentionPolicy.RUNTIME) 27 | @Documented 28 | public @interface MessagePreProcessor { 29 | /** 30 | * 将所有纯文本消息提取出来 31 | * 保存在PreProcessorData.text中
32 | * 其实无论是true还是false此项均会起作用 33 | * @see PreProcessorData 34 | * @since 1.0.0 35 | */ 36 | boolean textProcessor() default false; 37 | /** 38 | * 将对应类型的消息提取出来 39 | * 保存在PreProcessorData.classified中 40 | * @see PreProcessorData 41 | * @see MessagePreProcessorMessageType 42 | * @since 1.0.0 43 | */ 44 | MessagePreProcessorMessageType[] filterType() default {}; 45 | 46 | /** 47 | * 自定义消息过滤器, 默认使用内置实现 48 | * @since 1.0.5 49 | */ 50 | Class> filter() default MessageProcessorImp.class; 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/MiraiBootApplication.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 开玩笑的 没什么用 7 | * @author diyigemt 8 | * @since 1.0.0 9 | */ 10 | @Target({ElementType.TYPE}) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | public @interface MiraiBootApplication { 14 | /** 15 | * 初始化时控制台打印信息 将会在banner后打印 16 | */ 17 | String description() default ""; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/annotation/MiraiBootComponent.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | *

将一个类标注为miraiboot组件

7 | * @author diyigemt 8 | */ 9 | @Target({ElementType.TYPE}) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | public @interface MiraiBootComponent { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/autoconfig/GlobalLoader.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.autoconfig; 2 | 3 | import java.io.FileFilter; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.lang.annotation.Annotation; 7 | import java.lang.reflect.Method; 8 | import java.net.JarURLConnection; 9 | import java.net.URL; 10 | import java.net.URLDecoder; 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.ArrayList; 13 | import java.util.Enumeration; 14 | import java.util.List; 15 | import java.util.jar.JarEntry; 16 | import java.util.jar.JarFile; 17 | 18 | /** 19 | *

全局加载器 用于包扫描

20 | * @author diyigemt 21 | * @since 1.0.0 22 | */ 23 | public class GlobalLoader { 24 | // private static final char SYSTEM_PATH_DIV = File.separatorChar; 25 | private static final char SYSTEM_PATH_DIV = '/'; 26 | 27 | public static List> getClassHasAnnotation(Class annotation, String packageName) { 28 | List> classes = getClasses(packageName); 29 | if (classes.isEmpty()) return null; 30 | List> res = new ArrayList>(); 31 | for (Class aClass : classes) { 32 | Method[] methods = aClass.getMethods(); 33 | if (aClass.getAnnotation(annotation) != null) { 34 | res.add(aClass); 35 | } 36 | } 37 | return res; 38 | } 39 | 40 | /** 41 | * 从包package中获取所有的Class 42 | * 43 | * @param packageName 包名 44 | * @return CLass LIst 45 | */ 46 | public static List> getClasses(String packageName) { 47 | 48 | //第一个class类的集合 49 | List> classes = new ArrayList>(); 50 | //是否循环迭代 51 | boolean recursive = true; 52 | //获取包的名字 并进行替换 53 | String packageDirName = packageName.replace('.', SYSTEM_PATH_DIV); 54 | //定义一个枚举的集合 并进行循环来处理这个目录下的things 55 | Enumeration dirs; 56 | try { 57 | dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); 58 | //循环迭代下去 59 | while (dirs.hasMoreElements()) { 60 | //获取下一个元素 61 | URL url = dirs.nextElement(); 62 | //得到协议的名称 63 | String protocol = url.getProtocol(); 64 | //如果是以文件的形式保存在服务器上 65 | if ("file".equals(protocol)) { 66 | //获取包的物理路径 67 | String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8); 68 | //以文件的方式扫描整个包下的文件 并添加到集合中 69 | findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); 70 | } else if ("jar".equals(protocol)) { 71 | //如果是jar包文件 72 | //定义一个JarFile 73 | JarFile jar; 74 | try { 75 | //获取jar 76 | jar = ((JarURLConnection) url.openConnection()).getJarFile(); 77 | //从此jar包 得到一个枚举类 78 | Enumeration entries = jar.entries(); 79 | //同样的进行循环迭代 80 | while (entries.hasMoreElements()) { 81 | //获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件 82 | JarEntry entry = entries.nextElement(); 83 | String name = entry.getName(); 84 | //如果是以/开头的 85 | if (name.charAt(0) == SYSTEM_PATH_DIV) { 86 | //获取后面的字符串 87 | name = name.substring(1); 88 | } 89 | //如果前半部分和定义的包名相同 90 | if (name.startsWith(packageDirName)) { 91 | int idx = name.lastIndexOf(SYSTEM_PATH_DIV); 92 | //如果以"/"结尾 是一个包 93 | if (idx != -1) { 94 | //获取包名 把"/"替换成"." 95 | packageName = name.substring(0, idx).replace(SYSTEM_PATH_DIV, '.'); 96 | } 97 | //如果可以迭代下去 并且是一个包 98 | if ((idx != -1) || recursive) { 99 | //如果是一个.class文件 而且不是目录 100 | if (name.endsWith(".class") && !entry.isDirectory()) { 101 | //去掉后面的".class" 获取真正的类名 102 | String className = name.substring(packageName.length() + 1, name.length() - 6); 103 | try { 104 | //添加到classes 105 | String clazzName = packageName + '.' + className; 106 | classes.add(Class.forName(clazzName.replace(SYSTEM_PATH_DIV, '.'))); 107 | } catch (ClassNotFoundException e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | } 112 | } 113 | } 114 | } catch (IOException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | } 119 | } catch (IOException e) { 120 | e.printStackTrace(); 121 | } 122 | 123 | return classes; 124 | } 125 | 126 | /** 127 | * 以文件的形式来获取包下的所有Class 128 | * 129 | * @param packageName 全包名 130 | * @param packagePath 包路径 131 | * @param recursive 是否递归 132 | * @param classes 包列表 133 | */ 134 | public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List> classes) { 135 | //获取此包的目录 建立一个File 136 | File dir = new File(packagePath); 137 | //如果不存在或者 也不是目录就直接返回 138 | if (!dir.exists() || !dir.isDirectory()) { 139 | return; 140 | } 141 | //如果存在 就获取包下的所有文件 包括目录 142 | //自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件) 143 | File[] dirFiles = dir.listFiles(file -> (recursive && file.isDirectory()) || (file.getName().endsWith(".class"))); 144 | if (dirFiles == null) return; 145 | //循环所有文件 146 | for (File file : dirFiles) { 147 | //如果是目录 则继续扫描 148 | if (file.isDirectory()) { 149 | findAndAddClassesInPackageByFile(packageName + "." + file.getName(), 150 | file.getAbsolutePath(), 151 | recursive, 152 | classes); 153 | } else { 154 | //如果是java类文件 去掉后面的.class 只留下类名 155 | String className = file.getName().substring(0, file.getName().length() - 6); 156 | try { 157 | //添加到集合中去 158 | String clazzName = packageName + '.' + className; 159 | classes.add(Class.forName(clazzName.replace(SYSTEM_PATH_DIV, '.'))); 160 | } catch (ClassNotFoundException e) { 161 | e.printStackTrace(); 162 | } 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/autoconfig/JarPluginLoader.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.autoconfig; 2 | 3 | import java.io.IOException; 4 | import java.net.URL; 5 | import java.net.URLClassLoader; 6 | 7 | /** 8 | *

插件ClassLoader

9 | *

每个插件都有一个该ClassLoader实例

10 | * @author Haythem 11 | */ 12 | public class JarPluginLoader extends URLClassLoader { 13 | 14 | public JarPluginLoader(URL[] urls){ 15 | super(urls); 16 | } 17 | 18 | public JarPluginLoader(ClassLoader parent){ 19 | super(new URL[0], parent); 20 | } 21 | 22 | @Override 23 | public void close() throws IOException { 24 | super.close(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/autoconfig/MiraiApplication.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.autoconfig; 2 | 3 | import net.diyigemt.miraiboot.annotation.*; 4 | import net.diyigemt.miraiboot.constant.ConstantGlobal; 5 | import net.diyigemt.miraiboot.constant.EventHandlerType; 6 | import net.diyigemt.miraiboot.constant.FunctionId; 7 | import net.diyigemt.miraiboot.core.MiraiBootConsole; 8 | import net.diyigemt.miraiboot.core.PluginMgr; 9 | import net.diyigemt.miraiboot.core.RegisterProcess; 10 | import net.diyigemt.miraiboot.dao.PermissionDAO; 11 | import net.diyigemt.miraiboot.entity.*; 12 | import net.diyigemt.miraiboot.function.Alias; 13 | import net.diyigemt.miraiboot.function.console.ConsoleExit; 14 | import net.diyigemt.miraiboot.listener.BotEventListener; 15 | import net.diyigemt.miraiboot.listener.MessageEventListener; 16 | import net.diyigemt.miraiboot.mirai.MiraiMain; 17 | import net.diyigemt.miraiboot.permission.AuthMgr; 18 | import net.diyigemt.miraiboot.permission.CheckPermission; 19 | import net.diyigemt.miraiboot.utils.*; 20 | import net.mamoe.mirai.Bot; 21 | import net.mamoe.mirai.BotFactory; 22 | import net.mamoe.mirai.event.events.BotEvent; 23 | import net.mamoe.mirai.event.events.MessageEvent; 24 | import org.yaml.snakeyaml.Yaml; 25 | 26 | import java.io.*; 27 | import java.lang.reflect.InvocationTargetException; 28 | import java.lang.reflect.Method; 29 | import java.nio.charset.StandardCharsets; 30 | import java.util.*; 31 | import java.util.concurrent.atomic.AtomicBoolean; 32 | 33 | /** 34 | *

主实现逻辑

35 | * @author diyigemt 36 | * @since 1.0.0 37 | */ 38 | public class MiraiApplication { 39 | 40 | public static ConfigFile config; 41 | public static void run(Class mainClass, String... args) { 42 | // 打印banner 43 | InputStream banner = mainClass.getResourceAsStream("/banner.txt"); 44 | if (banner != null) { 45 | BufferedReader bannerReader = new BufferedReader(new InputStreamReader(banner)); 46 | String line; 47 | try { 48 | while ((line = bannerReader.readLine()) != null) { 49 | System.out.println(line); 50 | } 51 | } catch (IOException e) { 52 | e.printStackTrace(); 53 | } finally { 54 | try { 55 | banner.close(); 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | } 61 | // AutoConfiguration 62 | // 打印描述 63 | MiraiBootApplication miraiBootApplication = mainClass.getAnnotation(MiraiBootApplication.class); 64 | if (miraiBootApplication == null) { 65 | MiraiMain.logger.error("没有找到主类! 请将主类加上@MiraiBootApplication注解"); 66 | return; 67 | } 68 | MiraiMain.logger.info(miraiBootApplication.description()); 69 | // 初始化FileUtil 70 | FileUtil.init(mainClass); 71 | // 尝试读取配置文件 72 | MiraiMain.logger.info("开始读取配置文件"); 73 | File configFile = FileUtil.getInstance().getConfigFile(); 74 | if (configFile == null) { 75 | MiraiMain.logger.warning("未找到配置文件, 请自行在新创建的配置文件中修改"); 76 | configFile = FileUtil.getInstance().createConfigFile(); 77 | if (configFile == null) { 78 | MiraiMain.logger.error("配置文件创建失败"); 79 | } 80 | return; 81 | } 82 | try { 83 | config = new Yaml().loadAs(new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8), ConfigFile.class); 84 | } catch (FileNotFoundException e) { 85 | e.printStackTrace(); 86 | MiraiMain.logger.error("配置文件读取出错"); 87 | return; 88 | } 89 | // 注册全局配置 90 | GlobalConfig.getInstance().putAll(config.getMiraiboot().getConfigs()); 91 | GlobalConfig.getInstance().init(); 92 | MiraiMain.logger.info("配置文件读取成功"); 93 | // 开始自动包扫描 注册event handler 94 | String packageName = mainClass.getPackageName(); 95 | List> classes = GlobalLoader.getClasses(packageName); 96 | // 添加权限管理命令和别名命令 97 | classes.add(Alias.class); 98 | classes.add(AuthMgr.class); 99 | // 初始化permission数据库 100 | classes.add(PermissionDAO.class); 101 | // 初始化自带控制台指令 102 | List> consoleCommand = GlobalLoader.getClasses(ConsoleExit.class.getPackageName()); 103 | classes.addAll(consoleCommand); 104 | classes.addAll(PluginLoader.getPluginClasses(mainClass)); 105 | // 开始处理事件handler和autoInit 106 | List inits = RegisterProcess.AnnotationScanner(classes); 107 | //事件注册完成,释放所有List 108 | classes.clear(); 109 | // 开始读取配置文件 110 | ConfigFileMain miraiboot = config.getMiraiboot(); 111 | final boolean isNetwork = miraiboot.getLogger().isNetwork(); 112 | // 设置事件监听的logger是否启用 113 | MessageEventListener.eventLoggerEnable = miraiboot.getLogger().isEventStatus(); 114 | BotEventListener.eventLoggerEnable = miraiboot.getLogger().isEventStatus(); 115 | // 注册Bot 116 | for (ConfigFileBot configFileBot : miraiboot.getBots()) { 117 | long account = configFileBot.getAccount(); 118 | if (account == 123L) continue; 119 | String value = configFileBot.getPassword().getValue(); 120 | if (value.equals("pwd")) continue; 121 | Bot bot = BotFactory.INSTANCE.newBot(account, value, botConfiguration -> { 122 | ConfigFileBotConfiguration configuration = configFileBot.getConfiguration(); 123 | botConfiguration.fileBasedDeviceInfo(FileUtil.getInstance().getBotDeviceFilePath(account, configuration.getDevice())); 124 | botConfiguration.setProtocol(configuration.getProtocol()); 125 | if (!isNetwork) { 126 | botConfiguration.noNetworkLog(); 127 | } 128 | }); 129 | // 注册统一的事件监听器 130 | bot.getEventChannel().subscribeAlways(BotEvent.class, new BotEventListener()); 131 | bot.getEventChannel().subscribeAlways(MessageEvent.class, new MessageEventListener()); 132 | BotManager.getInstance().register(configFileBot.getAccount(), bot); 133 | } 134 | // 注册配置文件中的指令别名 135 | EventHandlerManager.getInstance().registerAlias(miraiboot.getAlias()); 136 | // 开始自动初始化 137 | Collections.sort(inits); 138 | for (AutoInitItem item : inits) { 139 | int parameterCount = item.getHandler().getParameterCount(); 140 | Object[] param = null; 141 | if (parameterCount != 0) { 142 | param = new Object[parameterCount]; 143 | param[0] = config; 144 | } 145 | try { 146 | if (parameterCount == 0) { 147 | item.getHandler().invoke(null); 148 | } else { 149 | item.getHandler().invoke(null, param); 150 | } 151 | } catch (IllegalAccessException | InvocationTargetException e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | inits.clear();//初始化完成后释放所有记载 156 | System.gc();//清理插件加载时因各种失败而变得无用的class 157 | // 初始化完成 统一登录 158 | MiraiMain.logger.info("初始化完成 开始登录bot"); 159 | BotManager.getInstance().loginAll(); 160 | MiraiMain.logger.info("bot登录成功 系统启动完成"); 161 | // 阻塞主线程 162 | // Map> text = PluginMgr.manifests; 163 | // int i = 0; 164 | MiraiBootConsole.getInstance().listenLoop(); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/autoconfig/MiraiBootPlugin.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.autoconfig; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.jar.JarFile; 6 | 7 | /** 8 | *

插件类

9 | *

在主类上继承该类,输出的JAR即为插件

10 | * @author Haythem 11 | */ 12 | public class MiraiBootPlugin { 13 | 14 | private List PluginDependencies = new ArrayList<>(); 15 | 16 | /** 17 | *

UEFI模式

18 | *

仅加载主类所在的包,其余的包均不加载,大幅提升加载速度

19 | *

(前提是将必须的依赖打入插件包中)

20 | */ 21 | public boolean UEFIMode = true; 22 | 23 | /** 24 | *

插件被加载前执行的方法

25 | */ 26 | public void onLoad(){} 27 | 28 | /** 29 | *

单独加载依赖

30 | *

在onLoad方法中使用

31 | *

注:此方法并不是UEFI启动

32 | */ 33 | protected void addDependencies(List file){ 34 | PluginDependencies.addAll(file); 35 | PluginLoader.LoadPluginDependencies(PluginDependencies); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/ConstantException.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | /** 4 | * 存储有关异常处理的常量 5 | * @since 1.0.5 6 | * @author diyigemt 7 | */ 8 | public final class ConstantException { 9 | public static final int DEFAULT_PRIORITY = 0; 10 | public static final int MAX_PARAM_COUNT = 3; 11 | public static final String OTHER_EVENT_EXCEPTION_HANDLER_NAME = "other_exception_handler"; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/ConstantGlobal.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | /** 4 | *

全局变量默认值以及变量名

5 | * @author diyigemt 6 | * @since 1.0.0 7 | */ 8 | public class ConstantGlobal { 9 | /** 10 | * 配置文件中配置指令默认开头的字段名 11 | */ 12 | public static final String DEFAULT_COMMAND_START = "DEFAULT_COMMAND_START"; 13 | /** 14 | * 配置文件中配置上下文监听超时时间的字段名 15 | */ 16 | public static final String DEFAULT_EVENT_NET_TIMEOUT = "DEFAULT_EVENT_NET_TIMEOUT"; 17 | /** 18 | * 默认上下文监听超时时间 可在配置文件中更改 19 | */ 20 | public static final long DEFAULT_EVENT_NET_TIMEOUT_TIME = 5 * 60 * 1000L; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/ConstantHttp.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | /** 4 | *

HttpUtil用常量

5 | * @author Heythem723 6 | * @since 1.0.0 7 | */ 8 | public class ConstantHttp { 9 | /** 10 | * 反爬header 11 | */ 12 | public static final String HEADER_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"; 13 | } -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/ConstantSQL.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | /** 4 | *

sqlDAO结果常量

5 | * @author diyigemt 6 | * @since 1.0.0 7 | */ 8 | public class ConstantSQL { 9 | public static final int ERROR = -1; 10 | public static final int SUCCESS = 0; 11 | public static final int INSERT_CREATE = 1; 12 | public static final int INSERT_UPDATE = 2; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/EventHandlerType.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | 5 | /** 6 | *

消息处理Handler类型

7 | * 目前仅有群消息 好友消息和全部
8 | * 更多消息类型在做了 9 | * @author diyigemt 10 | * @since 1.0.0 11 | * @see EventHandler 12 | */ 13 | public enum EventHandlerType { 14 | /** 15 | * 处理好友消息 16 | * @since 1.0.0 17 | */ 18 | MESSAGE_HANDLER_FRIEND, 19 | 20 | /** 21 | * 处理临时会话 22 | */ 23 | MESSAGE_HANDLER_TEMP, 24 | 25 | /** 26 | * 处理群消息 27 | * @since 1.0.0 28 | */ 29 | MESSAGE_HANDLER_GROUP, 30 | 31 | /** 32 | * 处理所有消息 33 | * @since 1.0.0 34 | */ 35 | MESSAGE_HANDLER_ALL, 36 | 37 | /** 38 | * 处理其他事件 39 | * @since 1.0.0 40 | */ 41 | OTHER_HANDLER; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/EventType.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | /** 4 | *

事件类型

5 | * @author diyigemt 6 | * @since 1.0.0 7 | */ 8 | 9 | public enum EventType { 10 | /** 11 | * 群消息事件 12 | * @since 1.0.0 13 | */ 14 | GROUP_MESSAGE_EVENT, 15 | /** 16 | * 好友消息事件 17 | * @since 1.0.0 18 | */ 19 | FRIEND_MESSAGE_EVENT, 20 | 21 | /** 22 | * 通过群发送的临时消息事件 23 | * @since 1.0.0 24 | */ 25 | GROUP_TMP_MESSAGE_EVENT, 26 | 27 | /** 28 | * 其他的事件 29 | * @since 1.0.0 30 | */ 31 | OTHER_EVENT; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/FunctionId.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | *

管理EventHandler在权限管理中对应的id

7 | * 所有功能都需要在这注册ID号,依次顺延
8 | * 建用 管理员功能使用负数ID 9 | * 对外开放功能使用正数ID
10 | * 极值±127(Tinyint),需要扩充请去修改permission表属性,记得注释也更新一下
11 | * (不用尝试枚举了,注解里不会生效的233)
12 | * ⬆大概是指注解里不能用枚举? 13 | * @author Haythem723 14 | * @author diyigemt 15 | */ 16 | 17 | public class FunctionId { 18 | 19 | //用来查询ID 20 | public static final Map map = new HashMap<>(); 21 | 22 | public static void put(String key, int value) { 23 | map.put(key, value); 24 | } 25 | 26 | /** 27 | * 28 | * @param target 29 | * 提供原始命令 30 | * @return 返回ID值 31 | */ 32 | public static int getMap(String target){ 33 | Integer integer = map.get(target); 34 | return integer == null ? 0 : integer; 35 | } 36 | 37 | public static String getKey(int value){ 38 | Set set = map.entrySet(); 39 | Iterator it = set.iterator(); 40 | while(it.hasNext()) { 41 | Map.Entry entry = (Map.Entry) it.next(); 42 | if(entry.getValue().equals(value)) { 43 | return entry.getKey().toString(); 44 | } 45 | } 46 | return null; 47 | } 48 | 49 | public static void registerAlias(String target, String alias) { 50 | Integer permissionIndex = getMap(target); 51 | if (permissionIndex == null) return; 52 | map.put(alias, permissionIndex); 53 | } 54 | 55 | // 默认permissionIndex 56 | public static final int DEFAULT_INDEX = 0; 57 | 58 | /** 59 | * 给注解用的 60 | */ 61 | 62 | //对外开放功能 63 | public static final int reply = 1; 64 | 65 | //管理员功能 66 | public static final int permit = -1; 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/MessageFilterMatchType.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | import net.diyigemt.miraiboot.annotation.MessageFilter; 4 | 5 | /** 6 | *

消息过滤类型

7 | * @author diyigemt 8 | * @since 1.0.0 9 | * @see MessageFilter 10 | */ 11 | public enum MessageFilterMatchType { 12 | /** 13 | * 忽略 14 | */ 15 | NULL, 16 | /** 17 | * 相同匹配 18 | */ 19 | EQUALS, 20 | /** 21 | * 忽略大小写的相同匹配 22 | */ 23 | EQUALS_IGNORE_CASE, 24 | /** 25 | * 包含匹配 26 | */ 27 | CONTAINS, 28 | /** 29 | * 开头匹配 30 | */ 31 | STARTS_WITH, 32 | /** 33 | * 结尾匹配 34 | */ 35 | ENDS_WITH, 36 | /** 37 | * 正则全文匹配 38 | */ 39 | REGEX_MATCHES, 40 | /** 41 | * 正则查找匹配 42 | */ 43 | REGEX_FIND 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/constant/MessagePreProcessorMessageType.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.constant; 2 | 3 | import net.diyigemt.miraiboot.annotation.MessageFilter; 4 | 5 | /** 6 | *

消息预处理器过滤类型

7 | * 与mirai核心的消息类型完全对应 8 | * @author diyigemt 9 | * @since 1.0.0 10 | * @see MessageFilter 11 | */ 12 | public enum MessagePreProcessorMessageType { 13 | /** 14 | * 纯文本
15 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt 16 | * @see net.mamoe.mirai.message.data.PlainText 17 | */ 18 | PlainText, 19 | /** 20 | * 图片 21 | * 包括群图片 好友图片
22 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt 23 | * @see net.mamoe.mirai.message.data.GroupImage 24 | * @see net.mamoe.mirai.message.data.FriendImage 25 | */ 26 | Image, 27 | /** 28 | * 艾特(@)人的消息
29 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/At.kt 30 | * @see net.mamoe.mirai.message.data.At 31 | */ 32 | At, 33 | /** 34 | * 艾特(@)全体的消息
35 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt 36 | * @see net.mamoe.mirai.message.data.AtAll 37 | */ 38 | AtAll, 39 | /** 40 | * 自带的表情栏的表情
41 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt 42 | * @see net.mamoe.mirai.message.data.Face 43 | */ 44 | Face, 45 | /** 46 | * 闪照
47 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt 48 | * @see net.mamoe.mirai.message.data.FlashImage 49 | */ 50 | FlashImage, 51 | /** 52 | * 戳一戳
53 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/PokeMessage.kt 54 | * @see net.mamoe.mirai.message.data.PokeMessage 55 | */ 56 | PokeMessage, 57 | /** 58 | * vip才能用的表情
59 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/VipFace.kt 60 | * @see net.mamoe.mirai.message.data.VipFace 61 | */ 62 | VipFace, 63 | /** 64 | * 快应用
65 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt 66 | * @see net.mamoe.mirai.message.data.LightApp 67 | */ 68 | LightApp, 69 | /** 70 | * 语音
71 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt 72 | * @see net.mamoe.mirai.message.data.Audio 73 | */ 74 | Audio, 75 | /** 76 | * 表情市场中的表情
77 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt 78 | * @see net.mamoe.mirai.message.data.MarketFace 79 | */ 80 | MarketFace, 81 | /** 82 | * 合并转发的消息
83 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt 84 | * @see net.mamoe.mirai.message.data.ForwardMessage 85 | */ 86 | ForwardMessage, 87 | /** 88 | * 另一种消息类型 不常用
89 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt 90 | * @see net.mamoe.mirai.message.data.SimpleServiceMessage 91 | */ 92 | SimpleServiceMessage, 93 | /** 94 | * 音乐分享
95 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt 96 | * @see net.mamoe.mirai.message.data.MusicShare 97 | */ 98 | MusicShare, 99 | /** 100 | * 骰子消息
101 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt 102 | * @see net.mamoe.mirai.message.data.Dice 103 | */ 104 | Dice, 105 | /** 106 | * 文件消息
107 | * https://github.com/mamoe/mirai/blob/dev/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt 108 | * @see net.mamoe.mirai.message.data.FileMessage 109 | */ 110 | FileMessage 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/core/MiraiBootConsole.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.core; 2 | 3 | import net.diyigemt.miraiboot.annotation.ConsoleCommand; 4 | import net.diyigemt.miraiboot.annotation.EventHandler; 5 | import net.diyigemt.miraiboot.entity.ConsoleHandlerItem; 6 | import net.diyigemt.miraiboot.entity.PluginItem; 7 | import net.diyigemt.miraiboot.interfaces.UnloadHandler; 8 | import net.diyigemt.miraiboot.mirai.MiraiMain; 9 | import net.diyigemt.miraiboot.utils.CommandUtil; 10 | import net.diyigemt.miraiboot.utils.ExceptionHandlerManager; 11 | 12 | import java.lang.annotation.Annotation; 13 | import java.lang.reflect.InvocationTargetException; 14 | import java.lang.reflect.Method; 15 | import java.util.*; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | /** 20 | *

MiraiBoot控制台

21 | * 22 | * @author diyigemt 23 | * @author Haythem 24 | */ 25 | public final class MiraiBootConsole implements UnloadHandler { 26 | 27 | private static final MiraiBootConsole INSTANCE = new MiraiBootConsole(); 28 | 29 | private static final Map store = new HashMap<>(); 30 | 31 | private static final Pattern EMPTY_SOURCE = Pattern.compile("^\\s+$"); 32 | 33 | public static MiraiBootConsole getInstance() { 34 | return INSTANCE; 35 | } 36 | 37 | /** 38 | *

注册一条控制台指令

39 | * 40 | * @param target 指令名 41 | * @param invoker 操作类 42 | * @param handler 操作方法 43 | * @return 是否注册成功, 当存在同名指令时返回false 44 | */ 45 | public boolean on(String target, Class invoker, Method handler) { 46 | ConsoleHandlerItem one = store.get(target); 47 | if (one != null) return false; 48 | String name = CommandUtil.getInstance().parseHandlerBaseName(invoker); 49 | ConsoleHandlerItem item = new ConsoleHandlerItem(name, invoker, handler); 50 | store.put(target, item); 51 | return true; 52 | } 53 | 54 | /** 55 | *

根据指令执行对应的方法

56 | * @param target 指令名 57 | * @param args 指令的参数 58 | * @return 有对应的处理方法且执行成功返回true 59 | */ 60 | public boolean emit(String target, String... args) { 61 | List arg = Arrays.asList(args); 62 | ConsoleHandlerItem item = store.get(target); 63 | if (item == null) return false; 64 | Method method = item.getHandler(); 65 | Class invoker = item.getInvoker(); 66 | int count = method.getParameterCount(); 67 | Object[] param = null; 68 | if (count != 0) { 69 | param = new Object[count]; 70 | param[0] = arg; 71 | } 72 | try { 73 | if (count != 0) { 74 | method.invoke(invoker.getDeclaredConstructor().newInstance(), param); 75 | } else { 76 | method.invoke(invoker.getDeclaredConstructor().newInstance()); 77 | } 78 | } catch (IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchMethodException e) { 79 | boolean res = ExceptionHandlerManager.getInstance().emit(e, null, null); 80 | if (!res) e.printStackTrace(); 81 | return false; 82 | } 83 | return true; 84 | } 85 | 86 | private String[] parsArgs(String source) { 87 | Matcher matcher = EMPTY_SOURCE.matcher(source); 88 | if (source.equals("") || matcher.matches()) return null; 89 | return source.split("\\s+"); 90 | } 91 | 92 | 93 | /** 94 | *

控制台输入监听

95 | */ 96 | public void listenLoop() { 97 | Scanner scanner = new Scanner(System.in); 98 | while (true) { 99 | String source = scanner.nextLine(); 100 | String[] strings = parsArgs(source); 101 | if (strings == null) continue; 102 | boolean emit = emit(strings[0], Arrays.copyOfRange(strings, 1, strings.length, String[].class)); 103 | if (!emit) { 104 | MiraiMain.logger.error("命令:" + "\"" + source + "\"" + " 执行失败或不是有效的MiraiBoot命令"); 105 | } 106 | } 107 | } 108 | 109 | /** 110 | *

根据指令移除一个指令

111 | * @param command 指令 112 | * @return 被移除的指令 113 | */ 114 | public ConsoleHandlerItem removeByCommand(String command) { 115 | return store.remove(command); 116 | } 117 | 118 | /** 119 | *

根据名字移除一个控制台指令

120 | * @param name 指令名 121 | * @return 被移除的指令 122 | */ 123 | public ConsoleHandlerItem removeByClassName(String name) { 124 | if (store.isEmpty()) return null; 125 | String t = null; 126 | for (Map.Entry next : store.entrySet()) { 127 | if (next.getValue().getName().equals(name)) { 128 | t = next.getKey(); 129 | break; 130 | } 131 | } 132 | if (t == null) return null; 133 | return store.remove(t); 134 | } 135 | 136 | @Override 137 | public void onUnload(List pluginItems) { 138 | for (PluginItem item : pluginItems) { 139 | Annotation annotationData = item.getAnnotationData(); 140 | if (!(annotationData instanceof ConsoleCommand)) { 141 | continue; 142 | } 143 | ConsoleCommand annotation = (ConsoleCommand) annotationData; 144 | String value = annotation.value(); 145 | removeByCommand(value); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/core/PluginMgr.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.core; 2 | 3 | import net.diyigemt.miraiboot.autoconfig.JarPluginLoader; 4 | import net.diyigemt.miraiboot.autoconfig.PluginLoader; 5 | import net.diyigemt.miraiboot.entity.PluginItem; 6 | import net.diyigemt.miraiboot.mirai.MiraiMain; 7 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 8 | import net.diyigemt.miraiboot.utils.ExceptionHandlerManager; 9 | 10 | import java.io.IOException; 11 | import java.net.JarURLConnection; 12 | import java.util.*; 13 | 14 | public class PluginMgr { 15 | 16 | private static List Plugin_Cache = new ArrayList<>(); 17 | 18 | /** 19 | *

加载器集合

20 | */ 21 | private static List> loaders = new ArrayList<>(); 22 | 23 | private static Map> manifests = new HashMap<>(); 24 | 25 | // private static List unLoadControllers = new ArrayList<>(); 26 | 27 | public static void addPluginConnection(JarURLConnection connection){ 28 | Plugin_Cache.add(connection); 29 | } 30 | 31 | // public static void addUnloadController(Method UnloadController){ 32 | // unLoadControllers.add(UnloadController); 33 | // } 34 | 35 | public static void addPluginManifest(String jarName, List pluginItemList){ 36 | manifests.put(jarName, pluginItemList); 37 | } 38 | 39 | public static void addLoader(JarPluginLoader loader, String jarName){ 40 | Map map = new HashMap<>(); 41 | map.put(loader, jarName); 42 | } 43 | 44 | private static void RemoveLoader(String jarName) throws IOException { 45 | for(Map map : loaders){ 46 | JarPluginLoader loader = map.get(jarName); 47 | if(loader != null){ 48 | loader.close();//关闭URL链接 49 | loader = null;//释放该loader 50 | loaders.remove(map);//将loader从List中移除 51 | return; 52 | } 53 | } 54 | } 55 | 56 | public static List getPluginConnectionList(){ 57 | List PluginList = new ArrayList<>(); 58 | try{ 59 | for(JarURLConnection connection : Plugin_Cache){ 60 | PluginList.add(connection.getJarFile().getName()); 61 | } 62 | }catch (IOException e){ 63 | e.printStackTrace(); 64 | } 65 | 66 | return PluginList; 67 | } 68 | 69 | public static void unLoadPlugin(String jarFileName){ 70 | if(unloadProcess(jarFileName)){ 71 | MiraiMain.logger.info("插件:" + jarFileName + " 卸载成功"); 72 | } 73 | } 74 | 75 | public static void reLoadPlugin(String jarFileName){ 76 | String path = Objects.requireNonNull(getConnection(jarFileName)).getJarFileURL().getPath(); 77 | if(unloadProcess(jarFileName)){ 78 | PluginLoader.LoadPlugin(path); 79 | } 80 | 81 | } 82 | 83 | private static JarURLConnection getConnection(String jarFileName){ 84 | try{ 85 | for(JarURLConnection connection : Plugin_Cache){ 86 | if(connection.getJarFile().getName().contains(jarFileName)){ 87 | return connection; 88 | } 89 | } 90 | }catch (IOException e){ 91 | e.printStackTrace(); 92 | } 93 | 94 | return null; 95 | } 96 | 97 | private static boolean unloadProcess(String jarFileName){ 98 | JarURLConnection connection = getConnection(jarFileName); 99 | if(connection == null){ 100 | MiraiMain.logger.error("未找到插件:" + jarFileName); 101 | return false; 102 | } 103 | try{ 104 | List pluginItem = manifests.get(jarFileName); 105 | connection.getJarFile().close();//关闭插件jar打开状态 106 | RemoveLoader(jarFileName);//销毁该插件的ClassLoader 107 | //TODO: 释放该插件的实例化 108 | EventHandlerManager.getInstance().onUnload(pluginItem);//注销EventHandler 109 | ExceptionHandlerManager.getInstance().onUnload(pluginItem);//注销ExceptionHandler 110 | MiraiBootConsole.getInstance().onUnload(pluginItem);//注销控制台指令 111 | // List> list = loaders; 112 | System.gc();//让JVM启动垃圾回收 113 | Plugin_Cache.remove(connection);//从当前清单中除名 114 | }catch (IOException e){ 115 | MiraiMain.logger.error("插件:" + jarFileName + " 卸载失败,请关闭本程序后手动删除插件"); 116 | return false; 117 | } 118 | 119 | return true; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/dao/PermissionDAO.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.dao; 2 | 3 | import com.j256.ormlite.dao.Dao; 4 | import com.j256.ormlite.dao.Dao.CreateOrUpdateStatus; 5 | import com.j256.ormlite.stmt.DeleteBuilder; 6 | import com.j256.ormlite.stmt.PreparedQuery; 7 | import com.j256.ormlite.stmt.QueryBuilder; 8 | import com.j256.ormlite.stmt.Where; 9 | import net.diyigemt.miraiboot.annotation.AutoInit; 10 | import net.diyigemt.miraiboot.constant.ConstantSQL; 11 | import net.diyigemt.miraiboot.entity.ConfigFile; 12 | import net.diyigemt.miraiboot.entity.PermissionItem; 13 | import net.diyigemt.miraiboot.sql.DatabaseHelper; 14 | 15 | import java.sql.SQLException; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | /** 21 | *

sqlite数据库权限管理的DAO

22 | * 一般并不需要关心 23 | * @author diyigemt 24 | * @author Heythem723 25 | * @since 1.0.0 26 | */ 27 | @AutoInit 28 | public class PermissionDAO { 29 | private static final PermissionDAO INSTANCE = new PermissionDAO(); 30 | private static Dao dao; 31 | 32 | public static void init(ConfigFile config) { 33 | dao = DatabaseHelper.getInstance().getDAO("permission", PermissionItem.class, Integer.class); 34 | } 35 | 36 | public static PermissionDAO getInstance() { return INSTANCE; } 37 | 38 | public QueryBuilder getQueryBuilder() { 39 | return dao.queryBuilder(); 40 | } 41 | 42 | public PermissionItem selectById(int id) { 43 | try { 44 | return dao.queryForId(id); 45 | } catch (SQLException throwables) { 46 | throwables.printStackTrace(); 47 | } 48 | return null; 49 | } 50 | 51 | public List selectAll() { 52 | try { 53 | return dao.queryForAll(); 54 | } catch (SQLException throwables) { 55 | throwables.printStackTrace(); 56 | } 57 | return null; 58 | } 59 | 60 | public List selectByPrepared(PreparedQuery query) { 61 | try { 62 | return dao.query(query); 63 | } catch (SQLException throwables) { 64 | throwables.printStackTrace(); 65 | } 66 | return null; 67 | } 68 | 69 | public List selectForFieldValuesArgs(Map args) { 70 | try { 71 | return dao.queryForFieldValuesArgs(args); 72 | } catch (SQLException throwables) { 73 | throwables.printStackTrace(); 74 | } 75 | return null; 76 | } 77 | 78 | public int insert(PermissionItem item) { 79 | int res = ConstantSQL.SUCCESS; 80 | QueryBuilder builder = dao.queryBuilder(); 81 | Map args = new HashMap(); 82 | long senderId = item.getSenderId(); 83 | int commandId = item.getCommandId(); 84 | int remain = item.getRemain(); 85 | int permits = item.getPermits(); 86 | args.put("sender_id", senderId); 87 | args.put("command_id", commandId); 88 | List permissionItems = selectForFieldValuesArgs(args); 89 | if (permissionItems != null && !permissionItems.isEmpty()) { 90 | item = permissionItems.get(0); 91 | item.setSenderId(String.valueOf(senderId)); 92 | item.setCommandId(commandId); 93 | item.setRemain(remain); 94 | item.setPermits(permits); 95 | } 96 | try { 97 | CreateOrUpdateStatus status = dao.createOrUpdate(item); 98 | if (status.isCreated()) res = ConstantSQL.INSERT_CREATE; 99 | if (status.isUpdated()) res = ConstantSQL.INSERT_UPDATE; 100 | } catch (SQLException e) { 101 | res = ConstantSQL.ERROR; 102 | e.printStackTrace(); 103 | } 104 | return res; 105 | } 106 | 107 | public int update(PermissionItem item) { 108 | int res = ConstantSQL.SUCCESS; 109 | try { 110 | res = dao.update(item); 111 | } catch (SQLException e) { 112 | e.printStackTrace(); 113 | res = ConstantSQL.ERROR; 114 | } 115 | return res; 116 | } 117 | 118 | public int delete(PermissionItem item) { 119 | int res = ConstantSQL.SUCCESS; 120 | DeleteBuilder builder = dao.deleteBuilder(); 121 | Where where = builder.where(); 122 | try { 123 | Where eq = where.eq("sender_id", item.getSenderId()).and().eq("command_id", item.getCommandId()); 124 | builder.setWhere(eq); 125 | res = dao.delete(builder.prepare()); 126 | } catch (SQLException throwables) { 127 | throwables.printStackTrace(); 128 | res = ConstantSQL.ERROR; 129 | } 130 | return res; 131 | } 132 | 133 | public int delete(int id) { 134 | int res = ConstantSQL.SUCCESS; 135 | try { 136 | res = dao.deleteById(id); 137 | } catch (SQLException e) { 138 | res = ConstantSQL.ERROR; 139 | e.printStackTrace(); 140 | } 141 | return res; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/AutoInitItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import net.diyigemt.miraiboot.annotation.AutoInit; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | *

自动初始化信息存储类

13 | * @author diyigem 14 | * @see AutoInit 15 | */ 16 | @Data 17 | public class AutoInitItem implements Comparable { 18 | private final String name; 19 | private final int priority; 20 | private final Method handler; 21 | 22 | @Override 23 | public int compareTo(@NotNull AutoInitItem o) { 24 | return -Integer.compare(this.priority, o.priority); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/BaseEventPack.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | /** 4 | * 用于泛型约束 5 | */ 6 | public class BaseEventPack { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/BotEventPack.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import net.mamoe.mirai.event.events.BotEvent; 4 | 5 | /** 6 | *

对Bot事件的简单封装

7 | * @author diyigemt 8 | * @since 1.0.0 9 | */ 10 | public class BotEventPack extends BaseEventPack{ 11 | 12 | private BotEvent event; 13 | 14 | public BotEventPack(BotEvent event) { 15 | this.event = event; 16 | } 17 | 18 | public BotEvent getEvent() { 19 | return event; 20 | } 21 | 22 | public void setEvent(BotEvent event) { 23 | this.event = event; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ConfigFile.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | *

配置文件存储类

9 | * @since 1.0.0 10 | * @author diyiegmt 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class ConfigFile { 16 | private ConfigFileMain miraiboot; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ConfigFileBot.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class ConfigFileBot { 9 | private long account; 10 | private ConfigFileBotPassword password; 11 | private ConfigFileBotConfiguration configuration; 12 | 13 | public ConfigFileBot() { 14 | this.password = new ConfigFileBotPassword(); 15 | this.configuration = new ConfigFileBotConfiguration(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ConfigFileBotConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import net.mamoe.mirai.utils.BotConfiguration; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | public class ConfigFileBotConfiguration { 10 | private BotConfiguration.MiraiProtocol protocol; 11 | private String device; 12 | 13 | public ConfigFileBotConfiguration() { 14 | this.protocol = BotConfiguration.MiraiProtocol.ANDROID_PHONE; 15 | this.device = "device.json"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ConfigFileBotPassword.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class ConfigFileBotPassword { 9 | private PasswordKind kind; 10 | private String value; 11 | 12 | public ConfigFileBotPassword() { 13 | this.kind = PasswordKind.PLAIN; 14 | } 15 | public enum PasswordKind { 16 | PLAIN, MD5; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ConfigFileLogger.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | public class ConfigFileLogger { 10 | private boolean network; 11 | private boolean eventStatus; 12 | private boolean debug; 13 | public ConfigFileLogger() { 14 | this.network = false; 15 | this.eventStatus = true; 16 | this.debug = false; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ConfigFileMain.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class ConfigFileMain { 14 | List bots; 15 | ConfigFileLogger logger; 16 | Map configs; 17 | Map alias; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ConsoleHandlerItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Getter; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | @Getter 8 | public final class ConsoleHandlerItem extends MiraiBootHandlerItem{ 9 | public ConsoleHandlerItem(String name, Class invoker, Method handler) { 10 | super(name, invoker, handler); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/EnhancedMessageChain.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Data; 4 | import net.mamoe.mirai.message.data.MessageChain; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | 12 | /** 13 | *

加强消息链

14 | *

相比MessageChain,该消息链可以添加多个语音,群文件等特殊Message

15 | * @author Haythem 16 | * @since 1.0.0 17 | */ 18 | @Data 19 | public class EnhancedMessageChain implements Iterable{ 20 | 21 | private List EnhancedMsgChain = new ArrayList<>(); 22 | 23 | /** 24 | *

加强消息链添加消息方法

25 | *

支持以下类型输入:

26 | *

27 | *

1: MessageChain消息链

28 | *

2: EnhancedMessageChain加强的消息链

29 | * 30 | * @param messageChain 当前类型: MessageChain 消息链 31 | */ 32 | public void append(MessageChain messageChain){ 33 | this.EnhancedMsgChain.add(messageChain); 34 | } 35 | 36 | /** 37 | *

加强消息链添加消息方法

38 | *

支持以下类型输入:

39 | *

40 | *

1: MessageChain消息链

41 | *

2: EnhancedMessageChain加强的消息链

42 | * 43 | * @param messageChain 当前类型: EnhancedMessageChain 特殊消息链 44 | */ 45 | public void append(EnhancedMessageChain messageChain){ 46 | this.EnhancedMsgChain.addAll(messageChain.getEnhancedMsgChain()); 47 | } 48 | 49 | /** 50 | * 迭代器,用于支持foreach循环 51 | */ 52 | @NotNull 53 | @Override 54 | public Iterator iterator() { 55 | return new MsgChainIterator(); 56 | } 57 | 58 | private class MsgChainIterator implements Iterator{ 59 | int current; 60 | 61 | @Override 62 | public boolean hasNext() { 63 | return current != EnhancedMsgChain.size(); 64 | } 65 | 66 | @Override 67 | public MessageChain next() { 68 | return EnhancedMsgChain.get(current++); 69 | } 70 | } 71 | 72 | public int size() { return EnhancedMsgChain.size(); } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/EventHandlerItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.Getter; 6 | import net.diyigemt.miraiboot.annotation.EventHandler; 7 | import net.diyigemt.miraiboot.constant.EventHandlerType; 8 | 9 | import java.lang.reflect.Method; 10 | import java.util.List; 11 | 12 | /** 13 | *

EventHandler信息存储类

14 | * @see EventHandler 15 | * @author diyigemt 16 | * @since 1.0.0 17 | */ 18 | @Getter 19 | public final class EventHandlerItem extends MiraiBootHandlerItem { 20 | private final EventHandlerType[] type; 21 | /** 22 | * 同一个类中处理异常的方法 23 | */ 24 | private final List exceptionHandlers; 25 | 26 | public EventHandlerItem(String name, Class invoker, Method handler, EventHandlerType[] type, List list) { 27 | super(name, invoker, handler); 28 | this.type = type; 29 | this.exceptionHandlers = list; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/EventHandlerNextItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Data; 4 | import net.diyigemt.miraiboot.constant.ConstantGlobal; 5 | import net.diyigemt.miraiboot.interfaces.EventHandlerNext; 6 | import net.mamoe.mirai.event.ListeningStatus; 7 | 8 | import java.util.Timer; 9 | import java.util.TimerTask; 10 | 11 | /** 12 | *

上下文监听信息存储类

13 | * 在计时中如果被触发 将会重新开始计时 14 | * @author diyigemt 15 | * @since 1.0.0 16 | */ 17 | @Data 18 | public class EventHandlerNextItem> { 19 | /** 20 | * 超时时间 21 | * @see ConstantGlobal -> DEFAULT_EVENT_NET_TIMEOUT_TIME 22 | */ 23 | private long timeOut; 24 | /** 25 | * 最高触发次数 26 | */ 27 | private int triggerCount; 28 | /** 29 | * 存储信息的handler类 30 | * @see EventHandlerNext 31 | */ 32 | private K handler; 33 | /** 34 | * timer用 35 | */ 36 | private TimerTask task; 37 | /** 38 | * 计时的timer 39 | */ 40 | private Timer timer; 41 | /** 42 | * 最后一次被触发时的event
43 | * 给超时函数 次数耗尽函数 和销毁函数用 44 | */ 45 | private MessageEventPack lastEventPack; 46 | /** 47 | * 最后一次触发时的data内容
48 | * 给超时函数 次数耗尽函数 和销毁函数用 49 | */ 50 | private PreProcessorData lastData; 51 | 52 | public EventHandlerNextItem() { 53 | this.timeOut = -1; 54 | this.triggerCount = -1; 55 | this.handler = null; 56 | this.task = null; 57 | } 58 | 59 | public EventHandlerNextItem(K handler) { 60 | this.handler = handler; 61 | } 62 | 63 | public EventHandlerNextItem(K handler, long timeOut, int triggerCount) { 64 | this(handler); 65 | this.timeOut = timeOut; 66 | this.triggerCount = triggerCount; 67 | if (timeOut != -1 || triggerCount != -1) this.timer = new Timer(); 68 | } 69 | 70 | public ListeningStatus onNext(MessageEventPack eventPack, PreProcessorData data) { 71 | PreProcessorData cast = (PreProcessorData) data; 72 | this.lastEventPack = eventPack; 73 | this.lastData = cast; 74 | if (this.triggerCount != -1) data.setTriggerCount(this.triggerCount); 75 | return onNextSelf(eventPack, cast); 76 | } 77 | 78 | public ListeningStatus onNextSelf(MessageEventPack eventPack, PreProcessorData data) { 79 | try { 80 | return handler.onNext(eventPack, data); 81 | } catch (Throwable e) { 82 | return handlerException(e); 83 | } 84 | } 85 | 86 | public void onTimeOut() { 87 | try { 88 | handler.onTimeOut(this.lastEventPack, this.lastData); 89 | } catch (Throwable e) { 90 | handlerException(e); 91 | } 92 | } 93 | 94 | public void onTriggerOut() { 95 | try { 96 | handler.onTriggerOut(this.lastEventPack, this.lastData); 97 | } catch (Throwable e) { 98 | handlerException(e); 99 | } 100 | } 101 | 102 | public void onDestroy() { 103 | try { 104 | handler.onDestroy(this.lastEventPack, this.lastData); 105 | } catch (Throwable e) { 106 | handlerException(e); 107 | } 108 | } 109 | 110 | public boolean check() { 111 | if (this.triggerCount == -1 && this.timeOut == -1) return true; 112 | if (this.triggerCount != -1) this.triggerCount--; 113 | if (this.triggerCount == 0) { 114 | return false; 115 | } 116 | if (this.timeOut != -1) { 117 | this.timer.cancel(); 118 | this.timer = new Timer(); 119 | } 120 | return true; 121 | } 122 | 123 | public void start(TimerTask task) { 124 | if (this.timeOut != -1) { 125 | this.task = task; 126 | this.timer.schedule(task, this.timeOut); 127 | } 128 | } 129 | 130 | public void cancel() { 131 | this.timer.cancel(); 132 | } 133 | 134 | private ListeningStatus handlerException(Throwable e) { 135 | return handler.onException(e, lastEventPack, lastData); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/ExceptionHandlerItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.Getter; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | *

异常处理信息存储类

12 | * 优先级越高越先触发!! 13 | * @author diyigemt 14 | * @since 1.0.0 15 | */ 16 | @Getter 17 | public final class ExceptionHandlerItem extends MiraiBootHandlerItem implements Comparable { 18 | private final Class target; 19 | private final int priority; 20 | 21 | public ExceptionHandlerItem(String name, Class invoker, Method handler, Class target, int priority) { 22 | super(name, invoker, handler); 23 | this.target = target; 24 | this.priority = priority; 25 | } 26 | 27 | @Override 28 | public int compareTo(@NotNull ExceptionHandlerItem o) { 29 | return -Integer.compare(priority, o.priority); 30 | } 31 | 32 | public boolean check(Class t) { 33 | return target.isAssignableFrom(t); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/HttpProperties.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.LinkedHashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | *

自定义HTTP属性

11 | *

实例化后在Builder中作为参数传入

12 | *

构造函数已为您添加:

13 | *

  最大请求时间:3000

14 | *

  请求模式:GET

15 | *

  引擎伪装

16 | *

  保持连接

17 | *

18 | *

如有需求,重写对应属性即可

19 | * @author Haythem 20 | * @since 1.0.0 21 | */ 22 | 23 | public class HttpProperties { 24 | 25 | @Getter 26 | private Map RequestProperties = new LinkedHashMap<>(); 27 | 28 | @Getter 29 | @Setter 30 | private int Timeout = 3000; 31 | 32 | @Setter 33 | @Getter 34 | private String RequestMethod = "GET"; 35 | 36 | /** 37 | *

自定义HTTP属性

38 | *

实例化后在Builder中作为参数传入

39 | *

构造函数已为您添加保持连接和引擎伪装,如有需求重写对应参数即可覆盖

40 | */ 41 | public HttpProperties(){ 42 | RequestProperties.put("Connection", "keep-alive"); 43 | RequestProperties.put("User-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"); 44 | } 45 | 46 | /** 47 | *

获取RequestProperties长度

48 | */ 49 | public int getRequestPropertiesSize(){ 50 | return RequestProperties.size(); 51 | } 52 | 53 | /** 54 | *

添加HTTP属性

55 | *

提供属性键值对

56 | *

构造函数已为您添加保持连接和引擎伪装,如有需求重写对应参数即可覆盖

57 | * @param key 键 58 | * @param value 值 59 | */ 60 | public void setRequestProperties(String key, String value){ 61 | if(RequestProperties.containsKey(key)){ 62 | RequestProperties.replace(key, value); 63 | }else { 64 | RequestProperties.put(key, value); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/MessageFilterImp.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import net.diyigemt.miraiboot.interfaces.IMessageFilter; 4 | 5 | public class MessageFilterImp implements IMessageFilter { 6 | @Override 7 | public boolean check(String source, MessageEventPack eventPack, MessageFilterItem item) { 8 | return item.check(eventPack, source); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/MessageFilterItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Data; 4 | import net.diyigemt.miraiboot.annotation.MessageFilter; 5 | import net.diyigemt.miraiboot.constant.EventType; 6 | import net.diyigemt.miraiboot.constant.MessageFilterMatchType; 7 | import net.mamoe.mirai.message.data.At; 8 | import net.mamoe.mirai.message.data.AtAll; 9 | import net.mamoe.mirai.message.data.SingleMessage; 10 | 11 | import java.util.Arrays; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | *

用于保存MessageFilter的参数

18 | * @see MessageFilter 19 | * @author diyiegmt 20 | * @since 1.0.0 21 | */ 22 | @Data 23 | public class MessageFilterItem { 24 | private String value; 25 | private MessageFilterMatchType matchType; 26 | private Set accounts; 27 | private Set groups; 28 | private Set bots; 29 | private boolean isAt; 30 | private boolean isAtAll; 31 | private boolean isAtAny; 32 | 33 | public MessageFilterItem() { 34 | this.accounts = new HashSet(); 35 | this.groups = new HashSet(); 36 | this.bots = new HashSet(); 37 | } 38 | 39 | public MessageFilterItem(MessageFilter filter) { 40 | this(); 41 | value = filter.value(); 42 | matchType = filter.matchType(); 43 | addAccounts(filter.accounts()); 44 | addGroups(filter.groups()); 45 | addBots(filter.bots()); 46 | isAt = filter.isAt(); 47 | isAtAll = filter.isAtAll(); 48 | isAtAny = filter.isAtAny(); 49 | } 50 | 51 | public void addAccounts(String[] accounts) { 52 | this.accounts.addAll(Arrays.asList(accounts)); 53 | } 54 | 55 | public void addGroups(String[] groups) { 56 | this.groups.addAll(Arrays.asList(groups)); 57 | } 58 | 59 | public void addBots(String[] bots) { 60 | this.bots.addAll(Arrays.asList(bots)); 61 | } 62 | 63 | public boolean check(MessageEventPack eventPack, String source) { 64 | if (!bots.isEmpty() && !bots.contains(String.valueOf(eventPack.getBotId()))) return false; 65 | boolean res = false; 66 | switch (matchType) { 67 | case NULL: 68 | res = true; 69 | break; 70 | case EQUALS: 71 | res = source.equals(value); 72 | break; 73 | case EQUALS_IGNORE_CASE: 74 | res = source.equalsIgnoreCase(value); 75 | break; 76 | case CONTAINS: 77 | res = source.contains(value); 78 | break; 79 | case STARTS_WITH: 80 | res = source.startsWith(value); 81 | break; 82 | case ENDS_WITH: 83 | res = source.endsWith(value); 84 | break; 85 | case REGEX_MATCHES: 86 | Pattern p1 = Pattern.compile(value); 87 | res = p1.matcher(source).matches(); 88 | break; 89 | case REGEX_FIND: 90 | Pattern p2 = Pattern.compile(value); 91 | res = p2.matcher(source).find(); 92 | break; 93 | } 94 | if (!res) return false; 95 | String id = String.valueOf(eventPack.getSenderId()); 96 | if (!accounts.isEmpty() && !accounts.contains(id)) return false; 97 | if (eventPack.getEventType() == EventType.GROUP_MESSAGE_EVENT) return checkGroup(eventPack); 98 | return true; 99 | } 100 | 101 | public boolean checkGroup(MessageEventPack pack) { 102 | String group = String.valueOf(pack.getSubject().getId()); 103 | if (!groups.isEmpty() && !groups.contains(group)) return false; 104 | boolean at = false; 105 | boolean atAll = false; 106 | boolean atAny = false; 107 | for (SingleMessage message : pack.getMessage()) { 108 | if (message instanceof At) { 109 | atAny = true; 110 | if (at) continue; 111 | at = ((At) message).getTarget() == pack.getBotId(); 112 | } 113 | if (message instanceof AtAll) { 114 | if (atAll) continue; 115 | atAll = true; 116 | } 117 | } 118 | if (isAt && !at) return false; 119 | if (isAtAll && !atAll) return false; 120 | if (isAtAny && !atAny) return false; 121 | return true; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/MessageProcessorImp.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Data; 4 | import net.diyigemt.miraiboot.annotation.MessagePreProcessor; 5 | import net.diyigemt.miraiboot.constant.MessagePreProcessorMessageType; 6 | import net.diyigemt.miraiboot.interfaces.IMessagePreProcessor; 7 | import net.mamoe.mirai.message.data.*; 8 | 9 | import java.util.*; 10 | 11 | /** 12 | *

默认的消息预处理器

13 | * @see MessagePreProcessor 14 | * @author diyiegmt 15 | * @since 1.0.0 16 | */ 17 | @Data 18 | public class MessageProcessorImp implements IMessagePreProcessor { 19 | private final boolean isTextProcessor = true; 20 | private final Set filterType; 21 | 22 | public MessageProcessorImp() { 23 | this.filterType = new HashSet<>(); 24 | } 25 | 26 | public void addFilterType(MessagePreProcessorMessageType[] type) { 27 | this.filterType.addAll(Arrays.asList(type)); 28 | } 29 | 30 | public List parseMessage(MessageEventPack eventPack) { 31 | if (this.filterType.isEmpty()) return eventPack.getMessage(); 32 | List> classes = new ArrayList<>(); 33 | for (MessagePreProcessorMessageType type : filterType) { 34 | switch (type) { 35 | case PlainText: 36 | classes.add(PlainText.class); 37 | break; 38 | case Image: 39 | classes.add(Image.class); 40 | break; 41 | case At: 42 | classes.add(At.class); 43 | break; 44 | case AtAll: 45 | classes.add(AtAll.class); 46 | break; 47 | case Face: 48 | classes.add(Face.class); 49 | break; 50 | case FlashImage: 51 | classes.add(FlashImage.class); 52 | break; 53 | case PokeMessage: 54 | classes.add(PokeMessage.class); 55 | break; 56 | case VipFace: 57 | classes.add(VipFace.class); 58 | break; 59 | case LightApp: 60 | classes.add(LightApp.class); 61 | break; 62 | case Audio: 63 | classes.add(Voice.class); 64 | break; 65 | case MarketFace: 66 | classes.add(MarketFace.class); 67 | break; 68 | case ForwardMessage: 69 | classes.add(ForwardMessage.class); 70 | break; 71 | case SimpleServiceMessage: 72 | classes.add(SimpleServiceMessage.class); 73 | break; 74 | case MusicShare: 75 | classes.add(MusicShare.class); 76 | break; 77 | case Dice: 78 | classes.add(Dice.class); 79 | break; 80 | case FileMessage: 81 | classes.add(FileMessage.class); 82 | break; 83 | } 84 | } 85 | List res = new ArrayList<>(); 86 | for (SingleMessage message : eventPack.getMessage()) { 87 | Class clazz = message.getClass(); 88 | for (Class messageClass : classes) { 89 | if (messageClass.isAssignableFrom(clazz)) res.add(message); 90 | } 91 | } 92 | return res; 93 | } 94 | 95 | @Override 96 | public PreProcessorData parseMessage(String source, MessageEventPack eventPack, PreProcessorData data) { 97 | PreProcessorData res = new PreProcessorData<>(); 98 | res.setClassified(parseMessage(eventPack)); 99 | return res; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/MiraiBootHandlerItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | *

handler的基类

10 | * @author diyigemt 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | public class MiraiBootHandlerItem { 15 | protected final String name; 16 | protected final Class invoker; 17 | protected final Method handler; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/PermissionItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | /** 7 | *

用于权限操作的数据类

8 | * @author diyigemt 9 | * @since 1.0.0 10 | */ 11 | @DatabaseTable(tableName = "permission") 12 | public class PermissionItem { 13 | // 主键id 14 | @DatabaseField(generatedId = true, columnName = "id", unique = true) 15 | private int id; 16 | // EventHandler注解中的permissionIndex 17 | @DatabaseField(columnName = "command_id") 18 | private int commandId; 19 | // 发送者qq号 20 | @DatabaseField(columnName = "sender_id") 21 | private String senderId; 22 | // @DatabaseField(columnName = "permit", defaultValue = "false") 23 | // private boolean permit; 24 | // 权限等级 25 | @DatabaseField(columnName = "permit", defaultValue = "-1") 26 | private int permits = 0; 27 | // 剩余使用次数 默认-1代表无限次 用于临时权限 28 | @DatabaseField(columnName = "remain") 29 | private int remain = -1; 30 | 31 | public PermissionItem() {} 32 | 33 | public PermissionItem(long senderId, String commandId) { 34 | this.senderId = String.valueOf(senderId); 35 | this.commandId = Integer.parseInt(commandId); 36 | } 37 | 38 | public PermissionItem(String senderId, String commandId) { 39 | this.senderId = senderId; 40 | this.commandId = Integer.parseInt(commandId); 41 | } 42 | 43 | 44 | public PermissionItem(long senderId, int commandId, int permits) { 45 | this.senderId = String.valueOf(senderId); 46 | this.commandId = commandId; 47 | this.permits = permits; 48 | } 49 | 50 | public PermissionItem(long senderId, int commandId, int permits, int remain) { 51 | this.senderId = String.valueOf(senderId); 52 | this.commandId = commandId; 53 | this.permits = permits; 54 | this.remain = remain; 55 | } 56 | 57 | public int getId() { 58 | return id; 59 | } 60 | 61 | public void setId(int id) { 62 | this.id = id; 63 | } 64 | 65 | public int getCommandId() { 66 | return commandId; 67 | } 68 | 69 | public void setCommandId(int commandId) { 70 | this.commandId = commandId; 71 | } 72 | 73 | public long getSenderId() { 74 | return Long.parseLong(senderId); 75 | } 76 | 77 | public void setSenderId(String senderId) { 78 | this.senderId = senderId; 79 | } 80 | 81 | // public String isPermit() { 82 | // return this.permit ? "true" : "false"; 83 | // } 84 | // 85 | // public void setPermit(boolean permit) { 86 | // this.permit = permit; 87 | // } 88 | 89 | public int getRemain() { 90 | return remain; 91 | } 92 | 93 | public void setRemain(int remain) { 94 | this.remain = remain; 95 | } 96 | 97 | public int getPermits() { 98 | return permits; 99 | } 100 | 101 | public void setPermits(int permits) { 102 | this.permits = permits; 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/PluginItem.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.lang.annotation.Annotation; 6 | 7 | /** 8 | *

插件信息统计类

9 | * @author Haythem 10 | */ 11 | @Data 12 | public class PluginItem { 13 | 14 | private String PackageName = null; 15 | 16 | private String ClassName = null; 17 | 18 | private Annotation AnnotationData = null; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/entity/PreProcessorData.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.entity; 2 | 3 | import lombok.Data; 4 | import net.diyigemt.miraiboot.annotation.MessagePreProcessor; 5 | import net.mamoe.mirai.message.data.SingleMessage; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /** 12 | *

用于存放参数之类的预处理信息

13 | * @see MessagePreProcessor 14 | * @author diyigemt 15 | * @since 1.0.0 16 | */ 17 | @Data 18 | public class PreProcessorData { 19 | /** 20 | * 匹配到的包含指令开头所有内容
21 | * 例如: start: "/" target: "help"
22 | * 对于输入 "一些乱七八糟的/help 123 sss ddd"
23 | * commandText = "/help 123 sss ddd" 24 | */ 25 | private String commandText; 26 | /** 27 | * 匹配到的指令 28 | */ 29 | private String command; 30 | /** 31 | * 匹配到的参数 32 | */ 33 | private List args; 34 | /** 35 | * 消息纯文本 36 | */ 37 | private String text; 38 | /** 39 | * 由@MessagePreProcessor过滤出的消息内容 40 | * @see MessagePreProcessor 41 | */ 42 | private List classified; 43 | 44 | /** 45 | * 上下文触发剩余次数 46 | */ 47 | private int triggerCount; 48 | 49 | /** 50 | * 用户自定义预处理器处理结果 51 | * @since 1.0.5 52 | */ 53 | private T data; 54 | 55 | public PreProcessorData() { 56 | this.args = new ArrayList(); 57 | this.classified = new ArrayList(); 58 | } 59 | 60 | public void addArgs(String[] args) { 61 | this.args.addAll(Arrays.asList(args)); 62 | } 63 | 64 | public void addClassified(List messages) { 65 | this.classified.addAll(messages); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/exception/EventHandlerInvokeException.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.exception; 2 | 3 | import net.diyigemt.miraiboot.entity.MessageEventPack; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | /** 9 | *

消息事件处理器本身引发的异常

10 | * @since 1.0.5 11 | */ 12 | public class EventHandlerInvokeException extends RuntimeException { 13 | private final InvocationTargetException targetException; 14 | private final MessageEventPack eventPack; 15 | private final PreProcessorData data; 16 | 17 | public EventHandlerInvokeException(MessageEventPack eventPack, PreProcessorData data, InvocationTargetException targetException) { 18 | this.eventPack = eventPack; 19 | this.data = data; 20 | this.targetException = targetException; 21 | } 22 | 23 | /** 24 | * 获取invocation异常本身 25 | * @return 异常本身 26 | */ 27 | public InvocationTargetException getTargetException() { 28 | return targetException; 29 | } 30 | 31 | /** 32 | * 获取引发异常的事件 33 | * @return 事件 34 | */ 35 | public MessageEventPack getEventPack() { 36 | return eventPack; 37 | } 38 | 39 | /** 40 | * 获取异常事件的预处理数据 41 | * @return 数据 42 | */ 43 | public PreProcessorData getData() { 44 | return data; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/exception/MultipleParameterException.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.exception; 2 | 3 | /** 4 | *

参数多余异常

5 | * @author Haythem 6 | * @since 1.0.0 7 | */ 8 | public class MultipleParameterException extends RuntimeException { 9 | public MultipleParameterException(){ 10 | super(); 11 | } 12 | 13 | public MultipleParameterException(String message){ 14 | super(message); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/Alias.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 5 | import net.diyigemt.miraiboot.entity.PreProcessorData; 6 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 7 | import net.diyigemt.miraiboot.entity.MessageEventPack; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 13 | */ 14 | @EventHandlerComponent 15 | public class Alias { 16 | 17 | @EventHandler(target = "alias") 18 | public void testAlias(MessageEventPack eventPack, PreProcessorData data) { 19 | List args = data.getArgs(); 20 | if (args == null || args.isEmpty()) { 21 | eventPack.reply("参数获取失败"); 22 | return; 23 | } 24 | if (args.size() < 2) { 25 | eventPack.reply("参数不足"); 26 | return; 27 | } 28 | String target = args.get(0); 29 | String alias = args.get(1); 30 | EventHandlerManager.getInstance().registerAlias(target, alias); 31 | eventPack.reply("为指令:" + target + " 添加别名:" + alias); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/CreateGlobalException.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 5 | 6 | public class CreateGlobalException { 7 | @EventHandler(target = "create") 8 | public void createException() { 9 | throw new ExceptionA("create"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/ExceptionA.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | public class ExceptionA extends RuntimeException{ 4 | public ExceptionA(String msg) { 5 | super(msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TestBotEvent.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.entity.BotEventPack; 4 | import net.mamoe.mirai.event.events.BotEvent; 5 | import net.mamoe.mirai.event.events.MemberCardChangeEvent; 6 | import net.mamoe.mirai.message.data.Dice; 7 | import net.diyigemt.miraiboot.annotation.EventHandler; 8 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 9 | import net.diyigemt.miraiboot.entity.MessageEventPack; 10 | 11 | public class TestBotEvent { 12 | 13 | public void testBotEvent(BotEventPack eventPack) { 14 | BotEvent event = eventPack.getEvent(); 15 | if (event instanceof MemberCardChangeEvent) { 16 | String aNew = ((MemberCardChangeEvent) event).getNew(); 17 | event.getBot().getGroup(1002484182).sendMessage("change to: " + aNew); 18 | } 19 | } 20 | 21 | @EventHandler(target = "dice") 22 | public void testDice(MessageEventPack eventPack) { 23 | eventPack.reply(new Dice(4)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TestException.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.mamoe.mirai.event.ListeningStatus; 4 | import net.diyigemt.miraiboot.annotation.EventHandler; 5 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 6 | import net.diyigemt.miraiboot.annotation.ExceptionHandler; 7 | import net.diyigemt.miraiboot.annotation.ExceptionHandlerComponent; 8 | import net.diyigemt.miraiboot.entity.MessageEventPack; 9 | import net.diyigemt.miraiboot.entity.PreProcessorData; 10 | import net.diyigemt.miraiboot.interfaces.EventHandlerNext; 11 | import net.diyigemt.miraiboot.utils.BotManager; 12 | 13 | public class TestException { 14 | @EventHandler(target = "error1") 15 | public void testSendError1(MessageEventPack eventPack) { 16 | throw new RuntimeException("测试"); 17 | } 18 | 19 | @EventHandler(target = "error2") 20 | public void testSendError2(MessageEventPack eventPack, PreProcessorData data) { 21 | eventPack.onNext(new EventHandlerNext() { 22 | @Override 23 | public ListeningStatus onNext(MessageEventPack eventPack, PreProcessorData data) { 24 | if (data.getText().contains("error2")) { 25 | throw new IllegalArgumentException("测试error"); 26 | } 27 | return ListeningStatus.STOPPED; 28 | } 29 | 30 | @Override 31 | public ListeningStatus onException(Throwable e, MessageEventPack eventPack, PreProcessorData data) { 32 | eventPack.reply("事件处理器收到error:" + e.getMessage()); 33 | return ListeningStatus.STOPPED; 34 | } 35 | 36 | @Override 37 | public void onDestroy(MessageEventPack eventPack, PreProcessorData data) { 38 | eventPack.reply("停止监听"); 39 | } 40 | }, data); 41 | } 42 | 43 | @EventHandler(target = "error3") 44 | public void testSendError3(MessageEventPack eventPack, PreProcessorData data) { 45 | eventPack.onNext(new EventHandlerNext() { 46 | @Override 47 | public ListeningStatus onNext(MessageEventPack eventPack, PreProcessorData data) { 48 | if (data.getText().contains("error2")) { 49 | throw new IllegalArgumentException("测试error"); 50 | } 51 | return ListeningStatus.STOPPED; 52 | } 53 | 54 | @Override 55 | public ListeningStatus onException(Throwable e, MessageEventPack eventPack, PreProcessorData data) { 56 | eventPack.reply("事件处理器收到error:" + e.getMessage()); 57 | return ListeningStatus.LISTENING; 58 | } 59 | }, 1000L, data); 60 | } 61 | 62 | @ExceptionHandler(RuntimeException.class) 63 | public void handlerError(RuntimeException e, MessageEventPack eventPack) { 64 | eventPack.reply("收到error: " + e.getMessage()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TestExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.ExceptionHandler; 4 | import net.diyigemt.miraiboot.annotation.ExceptionHandlerComponent; 5 | import net.diyigemt.miraiboot.entity.MessageEventPack; 6 | 7 | public class TestExceptionHandler { 8 | 9 | @ExceptionHandler(RuntimeException.class) 10 | public void testException(RuntimeException e, MessageEventPack eventPack) { 11 | eventPack.reply("处理自定义RuntimeException:" + e.getMessage()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TestFilter.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | 4 | import net.diyigemt.miraiboot.annotation.EventHandler; 5 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 6 | import net.diyigemt.miraiboot.annotation.MessageFilter; 7 | import net.diyigemt.miraiboot.constant.MessageFilterMatchType; 8 | import net.diyigemt.miraiboot.entity.PreProcessorData; 9 | import net.diyigemt.miraiboot.entity.MessageEventPack; 10 | import net.diyigemt.miraiboot.permission.CheckPermission; 11 | 12 | public class TestFilter { 13 | 14 | @EventHandler(target = "filter1") 15 | @CheckPermission(isAdminOnly = true, functionId = 3) 16 | @MessageFilter(accounts = "1355247243") 17 | public void testFilter1(MessageEventPack eventPack, PreProcessorData data) { 18 | eventPack.getSubject().sendMessage("filter1"); 19 | } 20 | 21 | @EventHandler(target = "filter2") 22 | @CheckPermission(isAdminOnly = true) 23 | @MessageFilter(isAt = true) 24 | public void testFilter2(MessageEventPack eventPack, PreProcessorData data) { 25 | eventPack.getSubject().sendMessage("filter2"); 26 | } 27 | 28 | @EventHandler(target = "filter3") 29 | @CheckPermission(isAdminOnly = true) 30 | @MessageFilter(isAtAll = true) 31 | public void testFilter3(MessageEventPack eventPack, PreProcessorData data) { 32 | eventPack.getSubject().sendMessage("filter3"); 33 | } 34 | 35 | @EventHandler(target = "filter4") 36 | @CheckPermission(isAdminOnly = true) 37 | @MessageFilter(isAtAny = true) 38 | public void testFilter4(MessageEventPack eventPack, PreProcessorData data) { 39 | eventPack.getSubject().sendMessage("filter4"); 40 | } 41 | 42 | @EventHandler(target = "filter5") 43 | @CheckPermission(isAdminOnly = true) 44 | @MessageFilter(value = "filter5", matchType = MessageFilterMatchType.EQUALS) 45 | public void testFilter5(MessageEventPack eventPack, PreProcessorData data) { 46 | eventPack.getSubject().sendMessage("filter5"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TestFunction.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 6 | import net.diyigemt.miraiboot.constant.FunctionId; 7 | import net.diyigemt.miraiboot.entity.MessageEventPack; 8 | import net.diyigemt.miraiboot.permission.CheckPermission; 9 | 10 | public class TestFunction{ 11 | 12 | @EventHandler(target = "reply") 13 | @CheckPermission(isAdminOnly = true, functionId = FunctionId.reply) 14 | public void testReply(MessageEventPack eventPack, PreProcessorData data) { 15 | eventPack.getSubject().sendMessage(eventPack.getMessage()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TestNext.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | 4 | import net.diyigemt.miraiboot.annotation.MessagePreProcessor; 5 | import net.diyigemt.miraiboot.constant.MessagePreProcessorMessageType; 6 | import net.mamoe.mirai.event.ListeningStatus; 7 | import net.mamoe.mirai.message.data.PlainText; 8 | import net.mamoe.mirai.message.data.SingleMessage; 9 | import net.diyigemt.miraiboot.annotation.EventHandler; 10 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 11 | import net.diyigemt.miraiboot.entity.MessageEventPack; 12 | import net.diyigemt.miraiboot.entity.PreProcessorData; 13 | import net.diyigemt.miraiboot.interfaces.EventHandlerNext; 14 | 15 | import java.util.List; 16 | 17 | public class TestNext { 18 | 19 | @EventHandler(target = "搜图") 20 | @MessagePreProcessor(filterType = MessagePreProcessorMessageType.Image) 21 | public void testTimeOut(MessageEventPack eventPack, PreProcessorData data) { 22 | List filter = data.getClassified(); 23 | if (filter.isEmpty()) { 24 | eventPack.reply("图呢"); 25 | eventPack.onNext(new EventHandlerNext() { 26 | @Override 27 | @MessagePreProcessor(filterType = MessagePreProcessorMessageType.Image) 28 | public ListeningStatus onNext(MessageEventPack eventPack, PreProcessorData nextData) { 29 | if (nextData.getText().contains("没有")) { 30 | eventPack.reply("停止监听"); 31 | return ListeningStatus.STOPPED; 32 | } 33 | List filter = nextData.getClassified(); 34 | if (filter.isEmpty()) { 35 | eventPack.reply("等着呢 还是没有"); 36 | return ListeningStatus.LISTENING; 37 | } 38 | SingleMessage image = filter.get(0); 39 | eventPack.reply(new PlainText("接收到图片\n"), image); 40 | return ListeningStatus.STOPPED; 41 | } 42 | 43 | @Override 44 | public void onTimeOut(MessageEventPack eventPack, PreProcessorData nextData) { 45 | eventPack.reply("已经超时 停止监听"); 46 | } 47 | }, 5* 1000L, -1, data); 48 | return; 49 | } 50 | SingleMessage image = filter.get(0); 51 | eventPack.reply(new PlainText("接收到图片\n"), image); 52 | } 53 | 54 | @EventHandler(target = "trigger") 55 | public void testTriggerOut(MessageEventPack eventPack, PreProcessorData data) { 56 | eventPack.reply("开始监听"); 57 | eventPack.onNext(new EventHandlerNext() { 58 | @Override 59 | public ListeningStatus onNext(MessageEventPack eventPack, PreProcessorData data) { 60 | eventPack.reply("剩余次数: " + data.getTriggerCount()); 61 | if (data.getText().contains("没有")) { 62 | eventPack.reply("主动停止监听"); 63 | return ListeningStatus.STOPPED; 64 | } 65 | return ListeningStatus.LISTENING; 66 | } 67 | @Override 68 | public void onTriggerOut(MessageEventPack eventPack, PreProcessorData data) { 69 | eventPack.reply("次数耗尽 停止监听"); 70 | } 71 | 72 | @Override 73 | public void onTimeOut(MessageEventPack eventPack, PreProcessorData data) { 74 | eventPack.reply("已经超时 停止监听"); 75 | } 76 | }, -1, 3); 77 | } 78 | 79 | @EventHandler(target = "myhandler") 80 | public void test(MessageEventPack eventPack, PreProcessorData data) { 81 | eventPack.reply("开始了"); 82 | eventPack.onNext(new MyEventHandlerNext(), data); 83 | } 84 | 85 | class MyEventHandlerNext extends EventHandlerNext { 86 | private int index = 1; 87 | @Override 88 | public ListeningStatus onNext(MessageEventPack eventPack, PreProcessorData data) { 89 | eventPack.reply("这是自建的handler" + index); 90 | return ListeningStatus.STOPPED; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TestTmp.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 6 | import net.diyigemt.miraiboot.entity.MessageEventPack; 7 | 8 | public class TestTmp { 9 | @EventHandler(target = "tmp") 10 | public void testTmp(MessageEventPack eventPack, PreProcessorData data) { 11 | eventPack.getGroup().get(1355247243L).sendMessage("heello"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TextFileMsgBuilder.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 5 | import net.diyigemt.miraiboot.entity.PreProcessorData; 6 | import net.mamoe.mirai.message.data.MessageChain; 7 | import net.mamoe.mirai.message.data.MessageChainBuilder; 8 | import net.diyigemt.miraiboot.entity.EnhancedMessageChain; 9 | import net.diyigemt.miraiboot.entity.HttpProperties; 10 | import net.diyigemt.miraiboot.entity.MessageEventPack; 11 | import net.diyigemt.miraiboot.permission.CheckPermission; 12 | import net.diyigemt.miraiboot.utils.builder.FileMessageBuilder; 13 | import net.diyigemt.miraiboot.utils.builder.VoiceMessageBuilder; 14 | 15 | import java.io.File; 16 | 17 | public class TextFileMsgBuilder { 18 | 19 | private static final String FileLocalPath = "fff";//TODO: 群文件素材本地路径,自己填 20 | private static final String VoiceLocalPath = "fff";//TODO: 语音素材本地路径,自己填 21 | private static final String URLPath = "https://api.btstu.cn/sjbz/?lx=dongman";//素材URL 22 | private static final MessageChain messageChain = new MessageChainBuilder().append("fff").build();//已经构造好需要接龙的普通消息链 23 | private static final File file = new File(FileLocalPath); 24 | 25 | @EventHandler(target = "FileMsg") 26 | @CheckPermission(isAdminOnly = true) 27 | public static void textVoiceBuilder(MessageEventPack eventPack, PreProcessorData data){ 28 | EnhancedMessageChain chain = new FileMessageBuilder(eventPack) 29 | .add("1234")//纯文本消息 30 | .add("1234","5678")//支持多个字符串参数输入 31 | .add(FileLocalPath) 32 | .add(URLPath)//(目前只有此Builder支持重定向) 33 | .add(messageChain) 34 | .add(file) 35 | .build();//构造但不发送 36 | 37 | HttpProperties properties = new HttpProperties(); 38 | properties.setRequestProperties("Connection", "keep-alive");//TODO:添加属性,支持重写 39 | 40 | EnhancedMessageChain chains = new VoiceMessageBuilder(eventPack, properties)//接龙 41 | .add(chain) 42 | .add(VoiceLocalPath) 43 | .send();//构造并发送 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TextImgMsgBuilder.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 5 | import net.diyigemt.miraiboot.entity.PreProcessorData; 6 | import net.mamoe.mirai.message.data.MessageChain; 7 | import net.mamoe.mirai.message.data.MessageChainBuilder; 8 | import net.diyigemt.miraiboot.entity.EnhancedMessageChain; 9 | import net.diyigemt.miraiboot.entity.HttpProperties; 10 | import net.diyigemt.miraiboot.entity.MessageEventPack; 11 | import net.diyigemt.miraiboot.permission.CheckPermission; 12 | import net.diyigemt.miraiboot.utils.builder.ImageMessageBuilder; 13 | 14 | import java.io.File; 15 | 16 | public class TextImgMsgBuilder { 17 | 18 | private static final String ImageLocalPath = "";//TODO: 图片素材本地路径,自己填 19 | private static final String URLPath = "https://i0.hdslb.com/bfs/article/0a53e07dd26d946adfe9fe843263bc12ef441bf8.jpg@860w_482h.jpg";//素材URL 20 | private static final MessageChain messageChain = new MessageChainBuilder().append("fff").build();//已经构造好需要接龙的普通消息链 21 | private static final File file = new File(ImageLocalPath); 22 | 23 | @EventHandler(target = "ImgMsg") 24 | @CheckPermission(isAdminOnly = true) 25 | public static void textImgBuilder(MessageEventPack eventPack, PreProcessorData data){ 26 | EnhancedMessageChain chains = new ImageMessageBuilder(eventPack) 27 | .add("1234")//纯文本消息 28 | .add("1234","5678")//支持多个字符串参数输入 29 | .add(ImageLocalPath) 30 | .add(URLPath) 31 | .add(messageChain) 32 | .add(file) 33 | .build();//构造但不发送 34 | 35 | HttpProperties properties = new HttpProperties(); 36 | properties.setRequestProperties("Connection", "keep-alive");//TODO:添加属性,支持重写 37 | 38 | EnhancedMessageChain messageChain = new ImageMessageBuilder(eventPack, properties)//接龙 39 | .add("Start") 40 | .add(chains) 41 | .add("end") 42 | .send();//构造并发送 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TextSendMsgLibrary.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | import net.mamoe.mirai.message.data.MessageChain; 6 | import net.mamoe.mirai.message.data.MessageChainBuilder; 7 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 8 | import net.diyigemt.miraiboot.entity.MessageEventPack; 9 | import net.diyigemt.miraiboot.utils.SendMessageLib; 10 | 11 | import java.io.File; 12 | 13 | public class TextSendMsgLibrary { 14 | private static final String ImageLocalPath = "";//TODO: 图片素材本地路径,自己填 15 | private static final String URLPath = "https://i0.hdslb.com/bfs/article/0a53e07dd26d946adfe9fe843263bc12ef441bf8.jpg@860w_482h.jpg";//素材URL 16 | private static final String soundURL = "https://meamea.moe/voices/01-1.mp3"; 17 | private static final MessageChain messageChain = new MessageChainBuilder().append("fff").build();//已经构造好需要接龙的普通消息链 18 | private static final File file = new File(ImageLocalPath); 19 | 20 | @EventHandler(target = "MsgLib") 21 | public static void textSendMsgLib(MessageEventPack eventPack, PreProcessorData data){ 22 | SendMessageLib.ImageMessageSender(eventPack, ImageLocalPath);// 23 | SendMessageLib.ImageMessageSender_asc(eventPack, "message", URLPath); 24 | SendMessageLib.ImageMessageSender_multiImg(eventPack, new String[]{"Path1", "Path2", "..."}); 25 | SendMessageLib.VoiceMsgSender(eventPack, soundURL); 26 | //TODO:还有更多,详情请看开发文档。 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/TextVocMsgBuilder.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | import net.mamoe.mirai.message.data.MessageChain; 6 | import net.mamoe.mirai.message.data.MessageChainBuilder; 7 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 8 | import net.diyigemt.miraiboot.entity.EnhancedMessageChain; 9 | import net.diyigemt.miraiboot.entity.HttpProperties; 10 | import net.diyigemt.miraiboot.entity.MessageEventPack; 11 | import net.diyigemt.miraiboot.permission.CheckPermission; 12 | import net.diyigemt.miraiboot.utils.builder.VoiceMessageBuilder; 13 | 14 | import java.io.File; 15 | 16 | public class TextVocMsgBuilder { 17 | private static final String LocalPath = "fff";//TODO: 素材本地路径,自己填 18 | private static final String URLPath = "https://meamea.moe/voices/01-1.mp3";//素材URL 19 | private static final MessageChain messageChain = new MessageChainBuilder().append("fff").build();//已经构造好需要接龙的普通消息链 20 | private static final File file = new File(LocalPath); 21 | 22 | @EventHandler(target = "VoiceMsg") 23 | @CheckPermission(isAdminOnly = true) 24 | public static void textVoiceBuilder(MessageEventPack eventPack, PreProcessorData data){ 25 | EnhancedMessageChain chain = new VoiceMessageBuilder(eventPack) 26 | .add("1234")//纯文本消息 27 | .add("1234","5678")//支持多个字符串参数输入 28 | .add(LocalPath) 29 | .add(URLPath) 30 | .add(messageChain) 31 | .add(file) 32 | .build();//构造但不发送 33 | 34 | HttpProperties properties = new HttpProperties(); 35 | properties.setRequestProperties("Connection", "keep-alive");//TODO:添加属性,支持重写 36 | 37 | EnhancedMessageChain chains = new VoiceMessageBuilder(eventPack, properties)//接龙 38 | .add(chain) 39 | .add("end") 40 | .add(URLPath) 41 | .send();//构造并发送 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/console/ConsoleExit.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.console; 2 | 3 | import net.diyigemt.miraiboot.annotation.ConsoleCommand; 4 | import net.diyigemt.miraiboot.annotation.MiraiBootComponent; 5 | import net.diyigemt.miraiboot.utils.BotManager; 6 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 7 | 8 | @MiraiBootComponent 9 | public class ConsoleExit { 10 | 11 | @ConsoleCommand("exit") 12 | public void exit() { 13 | EventHandlerManager.getInstance().cancelAll(); 14 | BotManager.getInstance().logoutAll(); 15 | System.exit(0); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/console/ConsoleLoad.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.console; 2 | 3 | import net.diyigemt.miraiboot.annotation.ConsoleCommand; 4 | import net.diyigemt.miraiboot.annotation.MiraiBootComponent; 5 | import net.diyigemt.miraiboot.autoconfig.PluginLoader; 6 | import net.diyigemt.miraiboot.mirai.MiraiMain; 7 | 8 | import java.util.List; 9 | 10 | @MiraiBootComponent 11 | public class ConsoleLoad { 12 | 13 | @ConsoleCommand("load") 14 | public void load(List args){ 15 | if (args == null || args.isEmpty()) { 16 | MiraiMain.logger.error("参数:[Path] 不能为空"); 17 | return; 18 | } 19 | String PluginName = args.get(0); 20 | if(PluginName.matches("^.+\\.jar")){ 21 | PluginLoader.LoadPlugin(PluginName); 22 | } else { 23 | MiraiMain.logger.error("参数:[Path] 不是有效路径"); 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/console/ConsolePlugin.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.console; 2 | 3 | import net.diyigemt.miraiboot.annotation.ConsoleCommand; 4 | import net.diyigemt.miraiboot.annotation.MiraiBootComponent; 5 | import net.diyigemt.miraiboot.autoconfig.PluginLoader; 6 | import net.diyigemt.miraiboot.core.PluginMgr; 7 | import net.diyigemt.miraiboot.mirai.MiraiMain; 8 | 9 | import java.util.List; 10 | 11 | @MiraiBootComponent 12 | public class ConsolePlugin { 13 | 14 | @ConsoleCommand("plugin") 15 | public void plugin(List args){ 16 | if(args == null || args.isEmpty()){ 17 | MiraiMain.logger.error("命令:“\"plugin\"" + "缺少参数"); 18 | return; 19 | } 20 | 21 | switch (args.get(0)){ 22 | case "list": 23 | List res = PluginMgr.getPluginConnectionList(); 24 | MiraiMain.logger.info("已加载插件:"); 25 | for (String s : res) { 26 | MiraiMain.logger.info(s); 27 | } 28 | break; 29 | case "unload": 30 | if(args.get(1) == null){ 31 | MiraiMain.logger.error("参数: [PluginName] 不能为空"); 32 | break; 33 | } 34 | String PluginName = args.get(1); 35 | if(PluginName.matches("^.+\\.jar")){ 36 | PluginMgr.unLoadPlugin(PluginName); 37 | } else { 38 | MiraiMain.logger.error("非法的MiraiBoot插件名称"); 39 | } 40 | break; 41 | case "load": 42 | if(args.get(1) == null){ 43 | MiraiMain.logger.error("参数: [PluginName] 不能为空"); 44 | break; 45 | } 46 | String Path = args.get(1); 47 | if(Path.matches("^.+\\.jar")){ 48 | PluginLoader.LoadPlugin(Path); 49 | } else { 50 | MiraiMain.logger.error("参数:[Path] 不是有效插件名称"); 51 | } 52 | break; 53 | 54 | // case "reload": 55 | // if(args.get(1) == null){ 56 | // MiraiMain.logger.error("参数: [PluginName] 不能为空"); 57 | // break; 58 | // } 59 | // Path = args.get(1); 60 | // if(Path.matches("^.+\\.jar")){ 61 | // PluginLoader.LoadPlugin(Path); 62 | // } else { 63 | // MiraiMain.logger.error("参数:[Path] 不是有效插件名称"); 64 | // } 65 | // break; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/console/ConsolePluginList.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.console; 2 | 3 | import net.diyigemt.miraiboot.annotation.ConsoleCommand; 4 | import net.diyigemt.miraiboot.annotation.MiraiBootComponent; 5 | import net.diyigemt.miraiboot.core.PluginMgr; 6 | import net.diyigemt.miraiboot.mirai.MiraiMain; 7 | import net.diyigemt.miraiboot.utils.BotManager; 8 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 9 | 10 | import java.util.List; 11 | 12 | @MiraiBootComponent 13 | public class ConsolePluginList { 14 | 15 | 16 | public void plugin() { 17 | List res = PluginMgr.getPluginConnectionList(); 18 | MiraiMain.logger.info("已加载插件:"); 19 | for (String s : res) { 20 | MiraiMain.logger.info(s); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/console/ConsoleUnloadPlugin.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.console; 2 | 3 | import net.diyigemt.miraiboot.annotation.ConsoleCommand; 4 | import net.diyigemt.miraiboot.annotation.MiraiBootComponent; 5 | import net.diyigemt.miraiboot.core.PluginMgr; 6 | import net.diyigemt.miraiboot.mirai.MiraiMain; 7 | import net.diyigemt.miraiboot.utils.BotManager; 8 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 9 | 10 | import java.util.List; 11 | import java.util.Scanner; 12 | 13 | @MiraiBootComponent 14 | public class ConsoleUnloadPlugin { 15 | 16 | @ConsoleCommand("unload") 17 | public void unload(List arg) { 18 | if (arg == null || arg.isEmpty()) { 19 | MiraiMain.logger.error("非法的MiraiBoot插件名称"); 20 | return; 21 | } 22 | String PluginName = arg.get(0); 23 | if(PluginName.matches("^.+\\.jar")){ 24 | PluginMgr.unLoadPlugin(PluginName); 25 | } else { 26 | MiraiMain.logger.error("非法的MiraiBoot插件名称"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/testfilter/TestFilter.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.testfilter; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 5 | import net.diyigemt.miraiboot.annotation.MessageFilter; 6 | import net.diyigemt.miraiboot.entity.MessageEventPack; 7 | 8 | public class TestFilter { 9 | 10 | @EventHandler(isAny = true) 11 | @MessageFilter(filter = TestFilterA.class) 12 | public void testFilter(MessageEventPack eventPack) { 13 | eventPack.reply("通过"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/testfilter/TestFilterA.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.testfilter; 2 | 3 | import net.diyigemt.miraiboot.entity.MessageEventPack; 4 | import net.diyigemt.miraiboot.entity.MessageFilterItem; 5 | import net.diyigemt.miraiboot.interfaces.IMessageFilter; 6 | 7 | public class TestFilterA implements IMessageFilter { 8 | 9 | @Override 10 | public boolean check(String source, MessageEventPack eventPack, MessageFilterItem item) { 11 | if (!source.contains("测试")) throw new IllegalArgumentException("测试"); 12 | return source.contains("测试"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/testpreprocess/TestDataA.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.testpreprocess; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class TestDataA { 7 | private String source; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/testpreprocess/TestPreProcess.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.testpreprocess; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 5 | import net.diyigemt.miraiboot.annotation.MessagePreProcessor; 6 | import net.diyigemt.miraiboot.entity.MessageEventPack; 7 | import net.diyigemt.miraiboot.entity.PreProcessorData; 8 | 9 | public class TestPreProcess { 10 | 11 | @EventHandler(target = "预处理测试") 12 | @MessagePreProcessor(filter = TestPreProcessA.class) 13 | public void testPreProcess1(MessageEventPack eventPack, PreProcessorData data) { 14 | eventPack.reply("自定义预处理器数据: " + data.getData().getSource()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/testpreprocess/TestPreProcessA.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function.testpreprocess; 2 | 3 | 4 | import net.diyigemt.miraiboot.entity.MessageEventPack; 5 | import net.diyigemt.miraiboot.entity.PreProcessorData; 6 | import net.diyigemt.miraiboot.interfaces.IMessagePreProcessor; 7 | 8 | public class TestPreProcessA implements IMessagePreProcessor { 9 | @Override 10 | public PreProcessorData parseMessage(String source, MessageEventPack eventPack, PreProcessorData data) { 11 | TestDataA dataA = new TestDataA(); 12 | dataA.setSource(source); 13 | data.setData(dataA); 14 | return data; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/function/text.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.function; 2 | 3 | import net.diyigemt.miraiboot.annotation.EventHandler; 4 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 5 | import net.diyigemt.miraiboot.entity.EnhancedMessageChain; 6 | import net.diyigemt.miraiboot.entity.MessageEventPack; 7 | import net.diyigemt.miraiboot.entity.PreProcessorData; 8 | import net.diyigemt.miraiboot.utils.builder.ImageMessageBuilder; 9 | 10 | import java.io.File; 11 | 12 | public class text { 13 | @EventHandler(target = "textSend") 14 | public void Text(MessageEventPack eventPack, PreProcessorData data){ 15 | File resourceFile = new File("G:\\素材\\QQBot\\素材\\未标题-1.png"); 16 | String path = resourceFile.getPath();//相对路径 17 | String AbsPath = resourceFile.getAbsolutePath();//绝对路径 18 | 19 | // SendMessageLib.ImageMessageSender(eventPack, path); 20 | // eventPack.reply("分割线"); 21 | // SendMessageLib.ImageMessageSender(eventPack, AbsPath); 22 | // eventPack.reply("分割线"); 23 | EnhancedMessageChain chain1 = new ImageMessageBuilder(eventPack) 24 | .add("G:\\素材\\成品\\QQ截图20210215191854.png") 25 | .build(); 26 | EnhancedMessageChain chain2 = new ImageMessageBuilder(eventPack) 27 | .add(chain1) 28 | .add("G:\\素材\\header.jpg") 29 | .build(); 30 | EnhancedMessageChain chain3 = new ImageMessageBuilder(eventPack) 31 | .add(chain2) 32 | .add("G:\\素材\\成品\\QQ截图20210215191854.png") 33 | .build(); 34 | EnhancedMessageChain chain4 = new ImageMessageBuilder(eventPack) 35 | .add(chain3) 36 | .add("G:\\素材\\header.jpg") 37 | .send(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/interfaces/EventHandlerNext.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.interfaces; 2 | 3 | import net.diyigemt.miraiboot.entity.PreProcessorData; 4 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 5 | import net.diyigemt.miraiboot.utils.ExceptionHandlerManager; 6 | import net.mamoe.mirai.event.ListeningStatus; 7 | import net.diyigemt.miraiboot.entity.MessageEventPack; 8 | 9 | /** 10 | *

用于上下文监听的类

11 | * 使用时需要至少实现onNext方法 12 | * 用于指代预处理数据中用户自定义的数据类型 13 | *
14 |  * {@code
15 |  * onNext方法也可以使用@MessagePreProcessor注解
16 |  * 销毁时调用顺序为 onTimeOut | onTriggerOut -> onDestroy
17 |  * 传入的MessageEvent和PreProcessorData均为最后一次触发时的event和data
18 |  * 默认创建时倒计时和剩余次数均不会动作 仅在监听器第一次触发后倒计时和剩余次数才起作用 例如:
19 |  *
20 |  * EventHandlerManager.getInstance().onNext(监听目标qq号, new EventHandlerNext(),
21 |  * 超时时间 不合理时取全局配置或者默认5min, 剩余触发次数 不合理时取1)
22 |  *
23 |  * 除了以上四个参数, 还需要额外传入MessageEvent和PreProcessorData用于销毁和超时事件调用
24 |  * }
25 |  * 
26 | * 详细用法请参考{@link EventHandlerManager#onNext}
27 | * @author diyigemt 28 | * @since 1.0.0 29 | */ 30 | public abstract class EventHandlerNext { 31 | 32 | /** 33 | *

上下文监听器方法

34 | * 该方法可以使用@MessageEventFilter注解来过滤信息 35 | * @param eventPack 事件本身 36 | * @param data 预处理器 37 | * @return 返回是否继续监听事件 ListeningStatus.LISTENING表示继续监听 STOPPED表示停止监听 38 | */ 39 | public abstract ListeningStatus onNext(MessageEventPack eventPack, PreProcessorData data); 40 | 41 | /** 42 | *

监听器销毁时调用

43 | * @param eventPack 最后一次触发监听器的事件Event 44 | * @param data 最后一次触发监听器的PreProcessorData 45 | */ 46 | public void onDestroy(MessageEventPack eventPack, PreProcessorData data) { } 47 | 48 | /** 49 | *

监听器超时时调用

50 | * @param eventPack 最后一次触发监听器的事件Event 51 | * @param data 最后一次触发监听器的PreProcessorData 52 | */ 53 | public void onTimeOut(MessageEventPack eventPack, PreProcessorData data) { } 54 | 55 | /** 56 | *

监听器触发次数耗尽时调用

57 | * @param eventPack 最后一次触发监听器的事件Event 58 | * @param data 最后一次触发监听器的PreProcessorData 59 | */ 60 | public void onTriggerOut(MessageEventPack eventPack, PreProcessorData data) { } 61 | 62 | /** 63 | *

在监听器处理过程中如果抛出异常由此方法处理

64 | * 默认直接交给全局异常处理器处理 65 | * @param e 异常 66 | * @param eventPack 触发的事件 67 | * @param data 触发事件的数据 68 | * @return 是否继续监听 69 | */ 70 | public ListeningStatus onException(Throwable e, MessageEventPack eventPack, PreProcessorData data) { 71 | ExceptionHandlerManager.getInstance().emit(e, eventPack, data); 72 | return ListeningStatus.STOPPED; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/interfaces/IMessageFilter.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.interfaces; 2 | 3 | import net.diyigemt.miraiboot.entity.MessageEventPack; 4 | import net.diyigemt.miraiboot.entity.MessageFilterItem; 5 | 6 | /** 7 | *

消息过滤器的接口

8 | * 所有实现了该接口的类均可以作为消息过滤器 9 | * @since 1.0.5 10 | * @author diyigemt 11 | */ 12 | public interface IMessageFilter { 13 | 14 | /** 15 | * 具体实现 16 | * @param source 消息纯文本 17 | * @param eventPack 消息事件封装 18 | * @param item 同注解下的其他配置 19 | * @return 是否通过, true:通过 20 | */ 21 | boolean check(String source, MessageEventPack eventPack, MessageFilterItem item); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/interfaces/IMessagePreProcessor.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.interfaces; 2 | 3 | import net.diyigemt.miraiboot.entity.MessageEventPack; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | 6 | /** 7 | *

消息预处理器的接口

8 | * 所有实现了该接口的类均可以作为消息预处理器 9 | * @param 处理结果的类 10 | * @since 1.0.5 11 | */ 12 | public interface IMessagePreProcessor { 13 | /** 14 | * 对消息进行预处理 15 | * @param source 消息纯文本 16 | * @param eventPack 消息事件的封装 17 | * @param data 预处理结果存放类 18 | * @return 预处理结果 19 | */ 20 | PreProcessorData parseMessage(String source, MessageEventPack eventPack, PreProcessorData data); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/interfaces/UnloadHandler.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.interfaces; 2 | 3 | import net.diyigemt.miraiboot.entity.PluginItem; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | *

插件卸载接口

9 | * @author Haythem 10 | */ 11 | public interface UnloadHandler { 12 | /** 13 | *

当插件被卸载时调用的方法

14 | * @param pluginItems PluginMgr提供的类清单 15 | */ 16 | void onUnload(List pluginItems); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/listener/BotEventListener.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.listener; 2 | 3 | import net.diyigemt.miraiboot.entity.BotEventPack; 4 | import net.diyigemt.miraiboot.mirai.MiraiMain; 5 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 6 | import net.mamoe.mirai.event.events.BotEvent; 7 | import net.mamoe.mirai.event.events.FriendMessageEvent; 8 | import net.mamoe.mirai.event.events.GroupMessageEvent; 9 | import net.mamoe.mirai.event.events.GroupTempMessageEvent; 10 | 11 | import java.util.function.Consumer; 12 | 13 | /** 14 | * BotEvent监听器 15 | * @author diyigemt 16 | * @since 1.0.0 17 | */ 18 | public class BotEventListener implements Consumer { 19 | public static boolean eventLoggerEnable = true; 20 | 21 | @Override 22 | public void accept(BotEvent botEvent) { 23 | if (botEvent instanceof GroupMessageEvent) return; 24 | if (botEvent instanceof FriendMessageEvent) return; 25 | if (botEvent instanceof GroupTempMessageEvent) return; 26 | BotEventPack eventPack = new BotEventPack(botEvent); 27 | String res = EventHandlerManager.getInstance().emitOther("", eventPack); 28 | if (res != null && eventLoggerEnable) { 29 | MiraiMain.logger.error(res); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/listener/ExceptionListener.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.listener; 2 | 3 | 4 | import net.diyigemt.miraiboot.mirai.MiraiMain; 5 | import net.diyigemt.miraiboot.utils.ExceptionHandlerManager; 6 | 7 | /** 8 | *

异常监听器

9 | * 触发关键词为异常的全类名 10 | * @author diyigemt 11 | * @since 1.0.0 12 | */ 13 | public class ExceptionListener implements Thread.UncaughtExceptionHandler { 14 | @Override 15 | public void uncaughtException(Thread t, Throwable e) { 16 | boolean res = ExceptionHandlerManager.getInstance().emit(e); 17 | if (!res) e.printStackTrace(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/listener/MessageEventListener.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.listener; 2 | 3 | import net.diyigemt.miraiboot.utils.CommandUtil; 4 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 5 | import net.mamoe.mirai.event.events.*; 6 | import net.mamoe.mirai.message.data.PlainText; 7 | import net.diyigemt.miraiboot.entity.MessageEventPack; 8 | import net.diyigemt.miraiboot.mirai.MiraiMain; 9 | import net.diyigemt.miraiboot.utils.BotManager; 10 | 11 | import java.util.List; 12 | import java.util.function.Consumer; 13 | 14 | /** 15 | *

处理消息事件的监听器 目前只能处理群消息和好友消息

16 | * 其他消息处理在做了 17 | * @author diyigemt 18 | * @since 1.0.0 19 | */ 20 | public class MessageEventListener implements Consumer { 21 | public static boolean eventLoggerEnable = true; 22 | @Override 23 | public void accept(MessageEvent messageEvent) { 24 | // 目前只能处理群消息 好友消息 以及通过群发送的临时会话 25 | if (!(messageEvent instanceof GroupMessageEvent || messageEvent instanceof FriendMessageEvent || messageEvent instanceof GroupTempMessageEvent)) return; 26 | // 如果是机器人自己的消息事件 忽略 27 | if (BotManager.getInstance().get(messageEvent.getSender().getId()) != null) return; 28 | // 封装 29 | MessageEventPack eventPack = new MessageEventPack(messageEvent); 30 | // 提取纯文本 31 | 32 | List collect = eventPack.getMessageByType(PlainText.class); 33 | StringBuffer sb = new StringBuffer(); 34 | collect.forEach(item -> { 35 | sb.append(item.contentToString()); 36 | }); 37 | String source = sb.toString().trim(); 38 | // 执行监听中的特定对话 39 | EventHandlerManager.getInstance().emitNext(eventPack.getSenderId(), eventPack, source); 40 | // 执行强制触发EventHandler 41 | String res = null; 42 | res = EventHandlerManager.getInstance().emitAny(eventPack, source); 43 | // if (res != null && eventLoggerEnable) { 44 | // MiraiMain.logger.error(res); 45 | // } 46 | // 获取指令 47 | String command = CommandUtil.getInstance().parseCommand(source); 48 | if (command.equals("")) return; 49 | // 执行指令对应的EventHandler 50 | res = EventHandlerManager.getInstance().emit(command, eventPack, source); 51 | if (res != null && eventLoggerEnable) { 52 | MiraiMain.logger.error(res); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/mirai/MiraiMain.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.mirai; 2 | 3 | import net.diyigemt.miraiboot.constant.EventType; 4 | import net.mamoe.mirai.contact.Friend; 5 | import net.mamoe.mirai.contact.Group; 6 | import net.mamoe.mirai.contact.NormalMember; 7 | import net.mamoe.mirai.event.events.FriendMessageEvent; 8 | import net.mamoe.mirai.event.events.GroupMessageEvent; 9 | import net.mamoe.mirai.event.events.MessageEvent; 10 | import net.mamoe.mirai.message.data.*; 11 | import net.mamoe.mirai.utils.MiraiLogger; 12 | import net.diyigemt.miraiboot.entity.MessageEventPack; 13 | 14 | import java.util.Arrays; 15 | 16 | /** 17 | * <h2>全局实例 用于回复消息和打印log</h2> 18 | * @author diyigemt 19 | * @since 1.0.0 20 | */ 21 | public class MiraiMain { 22 | public static final MiraiLogger logger = MiraiLogger.create("mirai-boot-status"); 23 | // 全局唯一实例 24 | public static final MiraiMain INSTANCE = new MiraiMain(); 25 | 26 | /** 27 | * <h2>获取实例</h2> 28 | * @return 全局实例 29 | */ 30 | public static MiraiMain getInstance() { 31 | return INSTANCE; 32 | } 33 | 34 | /** 35 | * <h2>回复消息并at发送者</h2> 36 | * @param eventPack 消息封装 37 | * @param messages 消息文本内容 38 | */ 39 | public void reply(MessageEventPack eventPack, String... messages) { 40 | reply(eventPack, true, messages); 41 | } 42 | 43 | /** 44 | * <h2>回复消息并at发送者</h2> 45 | * @param eventPack 消息封装 46 | * @param messages 消息内容 47 | */ 48 | public void reply(MessageEventPack eventPack, SingleMessage... messages) { 49 | reply(eventPack, true, messages); 50 | } 51 | 52 | /** 53 | * <h2>回复消息并at发送者</h2> 54 | * @param eventPack 消息封装 55 | * @param chain 构建好的消息链 56 | */ 57 | public void reply(MessageEventPack eventPack, MessageChain chain) { 58 | reply(eventPack, chain, true); 59 | } 60 | 61 | /** 62 | * <h2>回复消息不at发送者</h2> 63 | * @param eventPack 消息封装 64 | * @param messages 消息文本内容 65 | */ 66 | public void replyNotAt(MessageEventPack eventPack, String... messages) { 67 | reply(eventPack, false, messages); 68 | } 69 | 70 | /** 71 | * <h2>回复消息不at发送者</h2> 72 | * @param eventPack 消息封装 73 | * @param messages 消息内容 74 | */ 75 | public void replyNotAt(MessageEventPack eventPack, SingleMessage... messages) { 76 | reply(eventPack, false, messages); 77 | } 78 | 79 | /** 80 | * <h2>回复消息不at发送者</h2> 81 | * @param eventPack 消息封装 82 | * @param chain 构建好的消息链 83 | */ 84 | public void replyNotAt(MessageEventPack eventPack, MessageChain chain) { 85 | reply(eventPack, chain, false); 86 | } 87 | 88 | /** 89 | * <h2>构建回复消息并根据at判断是否at发送者</h2> 90 | * @param eventPack 消息封装 91 | * @param at 是否at 92 | * @param msg 消息内容 93 | */ 94 | private void reply(MessageEventPack eventPack, boolean at, SingleMessage... msg) { 95 | MessageChainBuilder builder = new MessageChainBuilder(); 96 | Arrays.asList(msg).forEach(builder::append); 97 | reply(eventPack, builder.build(), at); 98 | } 99 | 100 | /** 101 | * <h2>构建回复消息并根据at判断是否at发送者</h2> 102 | * @param eventPack 消息封装 103 | * @param at 是否at 104 | * @param msg 消息文本内容 105 | */ 106 | private void reply(MessageEventPack eventPack, boolean at, String... msg) { 107 | MessageChainBuilder builder = new MessageChainBuilder(); 108 | Arrays.asList(msg).forEach(builder::append); 109 | reply(eventPack, builder.build(), at); 110 | } 111 | 112 | /** 113 | * <h2>用构建好的消息回复</h2> 114 | * @param eventPack 消息封装 115 | * @param chain 构建好的消息链 116 | * @param at 是否at 117 | */ 118 | private void reply(MessageEventPack eventPack, MessageChain chain, boolean at) { 119 | if (at && eventPack.getEventType() == EventType.GROUP_MESSAGE_EVENT) replySelf(eventPack, chain); 120 | else replySelfNotAt(eventPack, chain); 121 | } 122 | 123 | /** 124 | * <h2>用构建好的消息回复并at发送者</h2> 125 | * @param eventPack 消息封装 126 | * @param chain 构建好的消息链 127 | */ 128 | private void replySelf(MessageEventPack eventPack, MessageChain chain) { 129 | MessageChainBuilder builder = new MessageChainBuilder() 130 | .append(new At(eventPack.getSenderId())) 131 | .append("\n") 132 | .append(chain); 133 | eventPack.getSubject().sendMessage(builder.build()); 134 | } 135 | 136 | /** 137 | * <h2>用构建好的消息回复不at发送者</h2> 138 | * @param eventPack 消息封装 139 | * @param chain 构建好的消息链 140 | */ 141 | private void replySelfNotAt(MessageEventPack eventPack, MessageChain chain) { 142 | eventPack.getSubject().sendMessage(chain); 143 | } 144 | 145 | /** 146 | * <h2>向指定的好友发送消息</h2> 147 | * @param friend 指定的好友 148 | * @param msg 消息 149 | */ 150 | public void sendFriendMessage(Friend friend, String... msg) { 151 | MessageChainBuilder builder = new MessageChainBuilder(); 152 | Arrays.asList(msg).forEach(builder::append); 153 | sendFriendMessage(friend, builder.build()); 154 | } 155 | 156 | 157 | /** 158 | * <h2>向指定的好友发送消息</h2> 159 | * @param friend 指定的好友 160 | * @param msg 消息 161 | */ 162 | public void sendFriendMessage(Friend friend, SingleMessage... msg) { 163 | MessageChainBuilder builder = new MessageChainBuilder(); 164 | Arrays.asList(msg).forEach(builder::append); 165 | sendFriendMessage(friend, builder.build()); 166 | } 167 | 168 | /** 169 | * <h2>向指定的好友发送消息</h2> 170 | * @param friend 指定的好友 171 | * @param chain 构造好的消息链 172 | */ 173 | public void sendFriendMessage(Friend friend, MessageChain chain) { 174 | friend.sendMessage(chain); 175 | } 176 | 177 | /** 178 | * <h2>向指定的群发送消息</h2> 179 | * @param group 指定的群 180 | * @param msg 消息 181 | */ 182 | public void sendGroupMessage(Group group, String... msg) { 183 | MessageChainBuilder builder = new MessageChainBuilder(); 184 | Arrays.asList(msg).forEach(builder::append); 185 | sendGroupMessage(group, builder.build()); 186 | } 187 | 188 | 189 | /** 190 | * <h2>向指定的群发送消息</h2> 191 | * @param group 指定的群 192 | * @param msg 消息 193 | */ 194 | public void sendGroupMessage(Group group, SingleMessage... msg) { 195 | MessageChainBuilder builder = new MessageChainBuilder(); 196 | Arrays.asList(msg).forEach(builder::append); 197 | sendGroupMessage(group, builder.build()); 198 | } 199 | 200 | /** 201 | * <h2>向指定的群发送消息</h2> 202 | * @param group 指定的群 203 | * @param chain 构造好的消息链 204 | */ 205 | public void sendGroupMessage(Group group, MessageChain chain) { 206 | group.sendMessage(chain); 207 | } 208 | 209 | /** 210 | * <h2>向群中指定的人发送临时消息</h2> 211 | * @param member 指定的人 212 | * @param msg 消息 213 | */ 214 | public void sendTempMessage(NormalMember member, String... msg) { 215 | MessageChainBuilder builder = new MessageChainBuilder(); 216 | Arrays.asList(msg).forEach(builder::append); 217 | sendTempMessage(member, builder.build()); 218 | } 219 | 220 | 221 | /** 222 | * <h2>向群中指定的人发送临时消息</h2> 223 | * @param member 指定的人 224 | * @param msg 消息 225 | */ 226 | public void sendTempMessage(NormalMember member, SingleMessage... msg) { 227 | MessageChainBuilder builder = new MessageChainBuilder(); 228 | Arrays.asList(msg).forEach(builder::append); 229 | sendTempMessage(member, builder.build()); 230 | } 231 | 232 | /** 233 | * <h2>向群中指定的人发送临时消息</h2> 234 | * @param member 指定的人 235 | * @param chain 构造好的消息链 236 | */ 237 | public void sendTempMessage(NormalMember member, MessageChain chain) { 238 | member.sendMessage(chain); 239 | } 240 | 241 | /** 242 | * <h2>快速回复消息</h2> 243 | * 使用列 244 | * @param event 消息事件 245 | * @param msg 要回复的消息内容 246 | */ 247 | @Deprecated 248 | public void quickReply(MessageEvent event, String msg) { 249 | long senderId = event.getSender().getId(); 250 | MessageChain chain = null; 251 | if (event instanceof GroupMessageEvent) { 252 | // 如果是群事件 回复时@本人 253 | chain = new MessageChainBuilder().append(new At(senderId)).append("\n").append(msg).build(); 254 | } 255 | if (event instanceof FriendMessageEvent) { 256 | chain = new MessageChainBuilder().append(msg).build(); 257 | } 258 | this.quickReply(event, chain); 259 | } 260 | 261 | /** 262 | * <h2>快速回复消息</h2> 263 | * @param event 消息事件 264 | * @param messages 消息数组 265 | */ 266 | @Deprecated 267 | public void quickReply (MessageEvent event, SingleMessage... messages) { 268 | MessageChainBuilder builder = new MessageChainBuilder(); 269 | if (event instanceof GroupMessageEvent) { 270 | // 如果是群事件 回复时@本人 271 | long senderId = event.getSender().getId(); 272 | builder.append(new At(senderId)).append("\n"); 273 | } 274 | for (SingleMessage message : messages) { 275 | builder.append(message); 276 | } 277 | this.quickReply(event, builder.build()); 278 | } 279 | 280 | /** 281 | * <h2>快速回复消息</h2> 282 | * @param event 消息事件 283 | * @param chain 消息链 284 | */ 285 | @Deprecated 286 | public void quickReply(MessageEvent event, MessageChain chain) { 287 | event.getSubject().sendMessage(chain); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/permission/AuthMgr.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.permission; 2 | 3 | import net.diyigemt.miraiboot.annotation.MessagePreProcessor; 4 | import net.diyigemt.miraiboot.constant.MessagePreProcessorMessageType; 5 | import net.mamoe.mirai.message.data.At; 6 | import net.mamoe.mirai.message.data.SingleMessage; 7 | import net.diyigemt.miraiboot.annotation.EventHandler; 8 | import net.diyigemt.miraiboot.annotation.EventHandlerComponent; 9 | import net.diyigemt.miraiboot.constant.FunctionId; 10 | import net.diyigemt.miraiboot.entity.MessageEventPack; 11 | import net.diyigemt.miraiboot.entity.PermissionItem; 12 | import net.diyigemt.miraiboot.entity.PreProcessorData; 13 | import net.diyigemt.miraiboot.utils.PermissionUtil; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * <h2>权限控制台</h2> 19 | * <p>此功能仅限群主和管理员使用,普通群员没有足够权限使用该命令</p> 20 | * <p>此功能强制开启严格限制模式</p> 21 | * <p>此功能不能通过临时权限授权</p> 22 | * @author Haythem 23 | * @since 1.0.0 24 | */ 25 | 26 | @EventHandlerComponent 27 | public class AuthMgr { 28 | 29 | @EventHandler(target = "permit") 30 | @CheckPermission(isAdminOnly = true, isStrictRestricted = true, functionId = FunctionId.permit) 31 | @MessagePreProcessor(filterType = MessagePreProcessorMessageType.At) 32 | public void authorityManager(MessageEventPack eventPack, PreProcessorData data){ 33 | List<String> args = data.getArgs(); 34 | if(args == null){ 35 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "获取参数出错"); 36 | eventPack.reply("获取参数出错"); 37 | return; 38 | } 39 | List<SingleMessage> classified = data.getClassified(); 40 | long senderId = -1L; 41 | for (SingleMessage message : classified) { 42 | senderId = ((At) message).getTarget(); 43 | if (senderId != eventPack.getBotId()) break; 44 | } 45 | if(senderId == -1L){ 46 | eventPack.reply("@成员不存在"); 47 | return; 48 | } 49 | int commandId = 0; 50 | 51 | //临时权限剩余次数限制 52 | int remain = -2;//-1代表无限制 53 | try{ 54 | remain = Integer.parseInt(args.get(2)); 55 | if(remain == 0 || remain < 0){ 56 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "参数:次数限制必须 > 0"); 57 | eventPack.reply("参数:次数限制必须 > 0"); 58 | return; 59 | } 60 | }catch (IndexOutOfBoundsException e){ 61 | remain = -1; 62 | }catch (NumberFormatException e){ 63 | eventPack.reply("参数:次数限制输入非法"); 64 | return; 65 | } 66 | try{ 67 | if(args.get(0).contains("assign") || args.get(0).contains("cancel")){ 68 | commandId = FunctionId.getMap(args.get(1)); 69 | }else{ 70 | commandId = FunctionId.getMap(args.get(0)); 71 | } 72 | }catch (NullPointerException e){ 73 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "一个或多个参数无效"); 74 | eventPack.reply("一个或多个参数无效"); 75 | return; 76 | } 77 | // 说明指令并没有被CheckPermission注解 不能进行权限判断 78 | if (commandId == 0) return; 79 | 80 | int permit = 2; 81 | if(args.get(1).equals("off")){ 82 | permit = 0; 83 | }else if(args.get(1).equals("on")){ 84 | permit = 1; 85 | }else if(args.get(0).contains("assign") || args.get(0).contains("cancel")){ 86 | if(args.get(0).equals("assign")){ 87 | permit = -1; 88 | TempPermission.tempAuthProcess(eventPack, commandId, remain); 89 | } 90 | else if(args.get(0).equals("cancel")){ 91 | permit = -1; 92 | TempPermission.cancelAuthProcess(eventPack, commandId); 93 | } 94 | } 95 | if(permit == 2 || (senderId == -1L) || senderId == eventPack.getBotId()){ 96 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "命令:permit 无法将“permit”项识别为 函数、脚本文件或可运行程序的名称,请检查参数的拼写。"); 97 | eventPack.reply("命令:permit 无法将“permit”项识别为 函数、脚本文件或可运行程序的名称,请检查参数的拼写。"); 98 | permit = 2; 99 | return; 100 | } 101 | if(permit == 0){ 102 | PermissionItem permissionItem = PermissionUtil.getInstance().getPermissionItem(senderId, String.valueOf(commandId)); 103 | try{ 104 | if(permissionItem.getSenderId() == senderId && permissionItem.getPermits() > 0){//数据库中存在临时权限 105 | permissionItem.setPermits(0); 106 | PermissionUtil.getInstance().updatePermissionItem(permissionItem); 107 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "对用户" + senderId + "的" + args.get(0) + "功能,禁用完成,临时权限已取消"); 108 | eventPack.reply("对用户" + senderId + "的" + args.get(0) + "功能,禁用完成,临时权限已取消"); 109 | }else { 110 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "该用户已被禁用,无需操作"); 111 | eventPack.reply("该用户已被禁用,无需操作"); 112 | } 113 | }catch (NullPointerException e){//数据库没有记录 114 | PermissionUtil.getInstance().addPermissionItem(senderId, commandId); 115 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "对用户" + senderId + "的" + args.get(0) + "功能,禁用完成"); 116 | eventPack.reply("对用户" + senderId + "的" + args.get(0) + "功能,禁用完成"); 117 | } 118 | 119 | }else if(permit == 1){ 120 | PermissionItem pi = PermissionUtil.getInstance().getPermissionItem(senderId, String.valueOf(commandId)); 121 | if(pi == null){ 122 | eventPack.reply("该用户并没有被禁用相关功能"); 123 | return; 124 | } 125 | PermissionUtil.getInstance().removePermissionItem(senderId, commandId); 126 | // MiraiMain.getInstance().quickReply(eventPack.getEvent(), "对用户" + senderId + "的" + args.get(0) + "功能,解锁完成"); 127 | eventPack.reply("对用户" + senderId + "的" + args.get(0) + "功能,解锁完成"); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/permission/CheckPermission.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.permission; 2 | 3 | import java.lang.annotation.*; 4 | 5 | import static net.diyigemt.miraiboot.constant.FunctionId.DEFAULT_INDEX; 6 | 7 | /** 8 | * <h2>群消息权限规定</h2> 9 | * <p>将此注解加在有@EventHandler注解的方法上,将对此方法加入权限管理属性</p> 10 | * <p>优先级 at-> permission table -> blocks -> allows -> isStrictRestricted -> isGroupOwnerOnly -> isAdminOnly</p> 11 | * <p>allows: 允许某人使用,其余的成员均无法使用</p> 12 | * <p>blocks: 禁止某人使用,其余的成员均可以使用</p> 13 | * <p>isStrictRestricted: 严格管理模式,开启时将严格划分权限级别,详情看【1】</p> 14 | * <p>isGroupOwnerOnly: 群主独占功能,开启后此功能仅限群主使用</p> 15 | * <p>isAdminOnly: 管理层独占功能,开启后此功能仅限群主和管理员使用</p> 16 | * <p>FunctionID: 注册功能ID号,管理层独占功能为负数,普通功能为正数。1和-1已被占用</p> 17 | * <p></p> 18 | * <p>注:</p> 19 | * <p>【1】 严格管理模式:不允许越级操作(包括同级别权限)</p> 20 | * <p>权限划分:</p> 21 | * <p>群主 -> 管理员 -> 普通群员</p> 22 | * <p>2. 严格管理模式对permit指令强制打开,无法修改</p> 23 | * @author diyigemt 24 | * @since 1.0.0 25 | */ 26 | @Target({ElementType.METHOD, ElementType.TYPE}) 27 | @Retention(RetentionPolicy.RUNTIME) 28 | @Documented 29 | public @interface CheckPermission { 30 | 31 | //允许列表 32 | String[] allows() default {}; 33 | 34 | //禁止列表 35 | String[] blocks() default {}; 36 | 37 | //是否严格检查身份(群主和管理员之间的权限将严格划分)防止下克上 38 | boolean isStrictRestricted() default false; 39 | 40 | //是否只允许群主 41 | boolean isGroupOwnerOnly() default false; 42 | 43 | //是否允许管理员(包括群主) 44 | boolean isAdminOnly() default false; 45 | 46 | //注册功能ID号,管理层独占功能为负数,普通功能为正数。1和-1已被占用 47 | int functionId() default DEFAULT_INDEX; 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/permission/Permission.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.permission; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * <h2>用于存储@CheckPermission信息</h2> 8 | * 优先级 at-> permission table -> blocks -> allows -> isStrictRestricted -> isGroupOwnerOnly -> isAdminOnly 9 | * @author diyigemt 10 | * @since 1.0.0 11 | * @see CheckPermission 12 | */ 13 | public class Permission { 14 | 15 | //允许操作的用户id 16 | private Set<String> allows; 17 | 18 | //禁止操作的用户id 19 | private Set<String> blocks; 20 | 21 | //是否严格检查身份(群主和管理员之间的权限将严格划分) 22 | boolean isStrictRestricted; 23 | 24 | //是否只允许群主操作 25 | private boolean isGroupOwnerOnly; 26 | 27 | //是否只允许管理员操作 28 | private boolean isAdminOnly; 29 | 30 | public Permission() { 31 | this.allows = new HashSet<String>(); 32 | this.blocks = new HashSet<String>(); 33 | this.isGroupOwnerOnly = false; 34 | this.isAdminOnly = false; 35 | } 36 | 37 | public Set<String> getAllows() { 38 | return allows; 39 | } 40 | 41 | public void setAllows(Set<String> allows) { 42 | this.allows = allows; 43 | } 44 | 45 | public Set<String> getBlocks() { 46 | return blocks; 47 | } 48 | 49 | public void setBlocks(Set<String> blocks) { 50 | this.blocks = blocks; 51 | } 52 | 53 | public boolean isGroupOwnerOnly() { 54 | return isGroupOwnerOnly; 55 | } 56 | 57 | public void setGroupOwnerOnly(boolean groupOwnerOnly) { 58 | isGroupOwnerOnly = groupOwnerOnly; 59 | } 60 | 61 | public boolean isAdminOnly() { 62 | return isAdminOnly; 63 | } 64 | 65 | public void setAdminOnly(boolean adminOnly) { 66 | isAdminOnly = adminOnly; 67 | } 68 | 69 | public boolean isStrictRestricted() { 70 | return isStrictRestricted; 71 | } 72 | 73 | public void setStrictRestricted(boolean strictRestricted) { 74 | isStrictRestricted = strictRestricted; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/permission/PermissionCheck.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.permission; 2 | 3 | import net.diyigemt.miraiboot.constant.EventType; 4 | import net.diyigemt.miraiboot.entity.PreProcessorData; 5 | import net.diyigemt.miraiboot.utils.EventHandlerManager; 6 | import net.mamoe.mirai.contact.MemberPermission; 7 | import net.mamoe.mirai.message.data.At; 8 | import net.mamoe.mirai.message.data.MessageChain; 9 | import net.mamoe.mirai.message.data.SingleMessage; 10 | import net.diyigemt.miraiboot.entity.EventHandlerItem; 11 | import net.diyigemt.miraiboot.entity.MessageEventPack; 12 | import net.diyigemt.miraiboot.entity.PermissionItem; 13 | import net.diyigemt.miraiboot.utils.PermissionUtil; 14 | 15 | import java.lang.reflect.Method; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | 19 | /** 20 | * <h2>权限检查工具类</h2> 21 | * 22 | * @author diyigemt, Haythem 23 | * @since 1.0.0 24 | */ 25 | 26 | public class PermissionCheck { 27 | 28 | /** 29 | * <h2>数据库权限检查</h2> 30 | * <p>此检查优先级最高,可以覆盖所有其它限制的检查(除了permit)</p> 31 | * 32 | * @param eventPack 消息事件,私聊或群聊 33 | * @param commandId 命令ID 34 | * @return 查询结果 false为通过,true为拦截 35 | * @author Haythem 36 | * @since 1.0.0 37 | */ 38 | public static boolean checkGroupPermission(MessageEventPack eventPack, int commandId) { 39 | ; 40 | // 数据库动态权限检查 41 | if (commandId == 0) return false;//默认值不查 42 | PermissionItem permissionItem; 43 | try { 44 | permissionItem = PermissionUtil.getInstance().getPermissionItem(eventPack.getSenderId(), String.valueOf(commandId)); 45 | if (permissionItem.getPermits() > 0) {//被授予临时权限 46 | if (permissionItem.getRemain() > 0) {//存在次数限制 47 | int remain = permissionItem.getRemain(); 48 | if (remain - 1 == 0) { 49 | //次数没了 50 | PermissionUtil.getInstance().removePermissionItem(permissionItem.getSenderId(), permissionItem.getCommandId()); 51 | permissionItem.setPermits(0); 52 | eventPack.reply("提示:本次操作是最后一次, 使用完成后系统将回收您的使用权"); 53 | } else { 54 | permissionItem.setRemain(permissionItem.getRemain() - 1); 55 | } 56 | PermissionUtil.getInstance().updatePermissionItem(permissionItem); 57 | return true; 58 | } 59 | return false; 60 | } 61 | } catch (NullPointerException e) {//数据库没相关记录,说明没有任何禁用和授权 62 | return false; 63 | } 64 | if (permissionItem.getPermits() <= 0) { 65 | EventHandlerManager.SQLNonTempAuth = true; 66 | eventPack.reply("您的管理员已禁止您使用该功能"); 67 | return true; 68 | } 69 | return false; 70 | } 71 | 72 | /** 73 | * <h2>群员身份权限检查</h2> 74 | * <p>isAdminOnly和isGroupOwnerOnly的实现方法</p> 75 | * 76 | * @param eventPack 消息事件,私聊或群聊 77 | * @param item 信息存储类 78 | * @return 查询结果 true为通过,false为拦截 79 | * @author diyigemt 80 | * @since 1.0.0 81 | */ 82 | public static boolean identityCheck(EventHandlerItem item, MessageEventPack eventPack) {//群员身份检查,优先级低 83 | if (eventPack.getEventType() != EventType.GROUP_MESSAGE_EVENT) return false; 84 | MemberPermission memberPermission = eventPack.getSenderPermission(); 85 | Method handler = item.getHandler(); 86 | Class<?> aClass = item.getInvoker(); 87 | CheckPermission classAnnotation = aClass.getAnnotation(CheckPermission.class); 88 | CheckPermission methodAnnotation = null; 89 | if (handler != null) methodAnnotation = handler.getAnnotation(CheckPermission.class); 90 | Permission permission = PermissionCheck.getGroupPermission(methodAnnotation, classAnnotation); 91 | if (permission.isGroupOwnerOnly()) return memberPermission == MemberPermission.OWNER; 92 | if (permission.isAdminOnly()) 93 | return memberPermission == MemberPermission.OWNER || memberPermission == MemberPermission.ADMINISTRATOR; 94 | return true; 95 | } 96 | 97 | /** 98 | * <h2>权限检查信息存储构造器</h2> 99 | * 100 | * @param methodAnnotation 方法注解 101 | * @param classAnnotation 类注解 102 | * @return 构造完成的Permission类 103 | * @author diyigemt 104 | * @since 1.0.0 105 | */ 106 | private static Permission getGroupPermission(CheckPermission methodAnnotation, CheckPermission classAnnotation) { 107 | Permission permission = new Permission(); 108 | CheckPermission check = methodAnnotation != null ? methodAnnotation : classAnnotation; 109 | if (check != null) { 110 | permission.getAllows().addAll(Arrays.asList(check.allows())); 111 | permission.getBlocks().addAll(Arrays.asList(check.blocks())); 112 | permission.setAdminOnly(check.isAdminOnly()); 113 | permission.setGroupOwnerOnly(check.isGroupOwnerOnly()); 114 | } 115 | return permission; 116 | } 117 | 118 | /** 119 | * <h2>严格模式工作流程</h2> 120 | * <p>isStrictRestricted的实现方法</p> 121 | * 122 | * @param eventPack 消息事件,群聊或私聊 123 | * @return 检查结果, true为通过,false为拦截 124 | * @author Haythem 125 | * @since 1.0.0 126 | */ 127 | public static boolean strictRestrictedCheck(MessageEventPack eventPack) { 128 | if (eventPack.getEventType() != EventType.GROUP_MESSAGE_EVENT) return false; 129 | MemberPermission senderPermissions = eventPack.getSenderPermission(); 130 | int senderAuthLevel = senderPermissions.ordinal(); 131 | MessageChain content = eventPack.getMessage(); 132 | long botId = eventPack.getBot().getId(); 133 | long targetId = -1L; 134 | for (SingleMessage s : content) { 135 | if (s instanceof At && ((At) s).getTarget() != botId) { 136 | targetId = ((At) s).getTarget(); 137 | } 138 | } 139 | if (targetId == -1) { 140 | eventPack.reply("宁禁我干嘛?"); 141 | return false; 142 | } 143 | MemberPermission targetPermission = eventPack.getGroup().get(targetId).getPermission(); 144 | int targetAuthLevel = targetPermission.ordinal(); 145 | if (senderAuthLevel <= targetAuthLevel) { 146 | return false; 147 | } 148 | return true; 149 | } 150 | 151 | /** 152 | * <h2>个例权限检查工作流程</h2> 153 | * <p>blocks和allows的实现方法</p> 154 | * <p></p> 155 | * <p>注:</p> 156 | * <p>优先级:allows -> blocks</p> 157 | * 158 | * @param eventPack 消息事件,群聊或私聊 159 | * @param item 信息存储类 160 | * @return 检查结果, true为通过,false为拦截 161 | * @author Haythem 162 | * @since 1.0.0 163 | */ 164 | public static boolean individualAuthCheck(EventHandlerItem item, MessageEventPack eventPack) { 165 | Method method = item.getHandler(); 166 | String[] allows = method.getAnnotation(CheckPermission.class).allows(); 167 | String[] blocks = method.getAnnotation(CheckPermission.class).blocks(); 168 | long senderId = eventPack.getSenderId(); 169 | if (allows.length != 0) { 170 | for (String i : allows) { 171 | if (Long.parseLong(i) == senderId) { 172 | return true; 173 | } 174 | } 175 | return false; 176 | } 177 | if (blocks.length != 0) { 178 | for (String i : blocks) { 179 | if (Long.parseLong(i) == senderId) { 180 | return false; 181 | } 182 | } 183 | return true; 184 | } 185 | 186 | return true; 187 | } 188 | 189 | /** 190 | * <h2>@成员合法性检查(固定检查,无法取消)</h2> 191 | * <p>@成员合法性检查的实现方法</p> 192 | * 193 | * @param data 消息预处理器,获得消息中所有@元素 194 | * @return 检查结果, true为通过,false为拦截 195 | * @author Haythem 196 | * @since 1.0.0 197 | */ 198 | public static boolean atValidationCheck(PreProcessorData data) { 199 | List<SingleMessage> args = data.getClassified(); 200 | if (args.size() == 0) return true; 201 | if (!args.contains("[Mirai:")) { 202 | return false; 203 | } 204 | return true; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/permission/TempPermission.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.permission; 2 | 3 | import net.diyigemt.miraiboot.constant.EventType; 4 | import net.diyigemt.miraiboot.constant.FunctionId; 5 | import net.diyigemt.miraiboot.entity.MessageEventPack; 6 | import net.diyigemt.miraiboot.entity.PermissionItem; 7 | import net.diyigemt.miraiboot.utils.PermissionUtil; 8 | 9 | /** 10 | * 临时权限授权和处理类 11 | * @author Haythem 12 | * @since 1.0.0 13 | */ 14 | 15 | public class TempPermission { 16 | 17 | /** 18 | * <h2>临时权限授权方法</h2> 19 | * <p>临时权限分为两种:</p> 20 | * <p>带次数限制的临时权限</p> 21 | * <p>permit assign 命令名称/别名 限制次数 @目标成员</p> 22 | * <p></p> 23 | * <p>无次数限制的临时权限</p> 24 | * <p>permit assign 命令名称/别名 @目标成员</p> 25 | * <p></p> 26 | * <p>注:</p> 27 | * <p>1. 临时权限的权限级别低于permit off的权限级别,如果管理员使用了permit off会自动解除临时权限</p> 28 | * <p>2. 当管理员使用assign授予临时权限时,也会自动解除permit off的限制</p> 29 | * <p>3. assign允许提升或降级临时权限级别,从有限次数升为无限次数或从无限次数降为有限次数,只需执行对应的有限或无限次数的assign指令即可</p> 30 | * <p>4. assign不能授予permit命令使用权,如有需求请联系群主设置目标为管理员</p> 31 | * @param eventPack 消息事件,私聊或群聊 32 | * @param commandId 命令ID 33 | * @param remain 规定的次数,默认为-1,即为无限制次数 34 | */ 35 | public static void tempAuthProcess(MessageEventPack eventPack, int commandId, int remain){ 36 | if (eventPack.getEventType() != EventType.GROUP_MESSAGE_EVENT) return; 37 | long senderId = eventPack.getSenderId(); 38 | PermissionItem permissionItem = null; 39 | permissionItem = PermissionUtil.getInstance().getPermissionItem(senderId, String.valueOf(commandId)); 40 | if(permissionItem == null){// 数据库里没有记录,开始授权 41 | PermissionUtil.getInstance().addPermissionItem(senderId, commandId, 1, remain); 42 | eventPack.reply("对用户" + senderId + "的" + FunctionId.getKey(commandId) + "功能,临时授权成功"); 43 | return; 44 | } 45 | else if(permissionItem.getPermits() <= 0 && permissionItem.getSenderId() == senderId){//数据库中有被禁止使用当前功能的记录,取消禁用并授权 46 | permissionItem.setPermits(1); 47 | permissionItem.setRemain(remain); 48 | PermissionUtil.getInstance().updatePermissionItem(permissionItem); 49 | eventPack.reply("对用户" + senderId + "的" + FunctionId.getKey(commandId) + "功能,临时授权成功, 禁用已解除"); 50 | return; 51 | } 52 | else if(permissionItem.getRemain() < 0 && permissionItem.getSenderId() == senderId){//数据库存在高级权限 53 | permissionItem.setRemain(remain); 54 | PermissionUtil.getInstance().updatePermissionItem(permissionItem); 55 | eventPack.reply("对用户" + senderId + "的" + FunctionId.getKey(commandId) + "功能,降低权限成功, 次数限制已生效"); 56 | } 57 | else if(permissionItem.getRemain() > 0 && permissionItem.getSenderId() == senderId){//数据库存在低级权限 58 | permissionItem.setRemain(-1); 59 | PermissionUtil.getInstance().updatePermissionItem(permissionItem); 60 | eventPack.reply("对用户" + senderId + "的" + FunctionId.getKey(commandId) + "功能,提升权限成功, 次数限制已解除"); 61 | } 62 | else { 63 | eventPack.reply("该用户已授权"); 64 | } 65 | } 66 | 67 | /** 68 | * <h2>解除临时权限授权方法</h2> 69 | * <p>解除目标用户的临时使用权</p> 70 | * <p>permit cancel 命令名称/别名 @目标成员</p> 71 | * <p></p> 72 | * <p>注:</p> 73 | * <p>解除临时权限不会解除permit off的限制</p> 74 | * @param eventPack 消息事件,私聊或群聊 75 | * @param commandId 命令ID 76 | */ 77 | public static void cancelAuthProcess(MessageEventPack eventPack, int commandId){ 78 | if (eventPack.getEventType() != EventType.GROUP_MESSAGE_EVENT) return; 79 | long senderId = eventPack.getSender().getId(); 80 | PermissionItem permissionItem = PermissionUtil.getInstance().getPermissionItem(senderId, String.valueOf(commandId)); 81 | if(permissionItem != null && permissionItem.getSenderId() == eventPack.getSenderId() && permissionItem.getPermits() != 0){ 82 | PermissionUtil.getInstance().removePermissionItem(eventPack.getSender().getId(), commandId); 83 | eventPack.reply("对用户" + senderId + "的" + FunctionId.getKey(commandId) + "功能,解除授权成功"); 84 | } else { 85 | eventPack.reply("该用户并没有授予相关权限"); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/sql/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.sql; 2 | 3 | import com.j256.ormlite.dao.Dao; 4 | import com.j256.ormlite.dao.DaoManager; 5 | import com.j256.ormlite.jdbc.JdbcConnectionSource; 6 | import com.j256.ormlite.table.TableUtils; 7 | import net.diyigemt.miraiboot.entity.PermissionItem; 8 | 9 | import java.sql.SQLException; 10 | 11 | /** 12 | * <h2>sqlite数据库连接类</h2> 13 | * @author diyigemt 14 | * @since 1.0.0 15 | */ 16 | public class DatabaseHelper { 17 | private static final DatabaseHelper INSTANCE = new DatabaseHelper(); 18 | // 数据库文件存放位置 19 | private static final String SQL_URL_BASE = "jdbc:sqlite:./config/dbs/"; 20 | 21 | 22 | public static DatabaseHelper getInstance() { return INSTANCE; } 23 | 24 | /** 25 | * <h2>获取指定数据库名字的文件存放相对位置</h2> 26 | * <p>当数据库文件或者表不存在时会自动创建 27 | * <p>使用例子请百度或@see 28 | * @see PermissionItem 29 | * @param dBName 数据库文件吗名 30 | * @return 一个连接池 31 | * @throws SQLException 连接池创建失败 32 | */ 33 | public JdbcConnectionSource getDataSourceURL(String dBName) throws SQLException { 34 | if (dBName.contains(".db")) dBName = dBName.replace(".db", ""); 35 | String url = SQL_URL_BASE + dBName + ".db"; 36 | return new JdbcConnectionSource(url); 37 | } 38 | 39 | /** 40 | * <h2>根据数据库文件名 对应的实体类 和表key对应的实体类获取数据库DAO</h2> 41 | * @param dBName 数据库文件吗名 42 | * @param entity 实体类 43 | * @param key 主键类 44 | * @param <T> 实体类 45 | * @param <TD> 主键类 46 | * @return 一个连接池失败时返回null 47 | */ 48 | public <T, TD> Dao<T, TD> getDAO(String dBName, Class<T> entity, Class<TD> key) { 49 | Dao<T, TD> dao = null; 50 | try { 51 | dao = DaoManager.createDao(getDataSourceURL(dBName), entity); 52 | if (!dao.isTableExists()) TableUtils.createTable(dao); 53 | } catch (SQLException e) { 54 | e.printStackTrace(); 55 | return null; 56 | } 57 | return dao; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/BotManager.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils; 2 | 3 | import net.mamoe.mirai.Bot; 4 | 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * <h2>用于bot的统一管理</h2> 11 | * STORE这个HashMap中存储着所有配置文件中注册的bot的实例<br/> 12 | * 对应关系为qq号(long) - Bot实例 13 | * @author diyigemt 14 | * @since 1.0.0 15 | */ 16 | public class BotManager { 17 | private static final BotManager INSTANCE = new BotManager(); 18 | private static final Map<Long, Bot> STORE = new HashMap<Long, Bot>(); 19 | 20 | /** 21 | * <h2>Manager获取单例对象</h2> 22 | * @return Manager实例 23 | */ 24 | public static BotManager getInstance() { return INSTANCE; } 25 | 26 | /** 27 | * <h2>根据qq号获取Bot实例</h2> 28 | * @param key qq号 29 | * @return 对应的Bot实例 没有时为null 30 | */ 31 | public Bot get(long key) { 32 | return STORE.get(key); 33 | } 34 | 35 | /** 36 | * <h2>得到所有注册的Bot实例</h2> 37 | * @return 所有注册的Bot实例 38 | */ 39 | public Collection<Bot> getAllBot() { 40 | return STORE.values(); 41 | } 42 | 43 | /** 44 | * <h2>注册一个Bot实例</h2> 45 | * @param key qq号 46 | * @param bot Bot实例 47 | */ 48 | public void register(long key, Bot bot) { 49 | STORE.put(key, bot); 50 | } 51 | 52 | /** 53 | * <h2>根据qq号删除一个Bot实例</h2> 54 | * @param key qq号 55 | * @return 被删除的Bot实例 没有时为null 56 | */ 57 | public Bot remove(long key) { 58 | return STORE.remove(key); 59 | } 60 | 61 | /** 62 | * <h2>将qq号对应的Bot实例登出</h2> 63 | * @param key qq号 64 | * @return 登出的Bot实例 65 | */ 66 | public Bot logout(long key) { 67 | Bot remove = STORE.get(key); 68 | remove.close(); 69 | return remove; 70 | } 71 | 72 | /** 73 | * <h2>将所有注册的Bot实例登出</h2> 74 | */ 75 | public void logoutAll() { 76 | STORE.forEach((qq, bot) -> { 77 | bot.close(); 78 | }); 79 | } 80 | 81 | /** 82 | * <h2>将qq号对应的Bot实例登录</h2> 83 | * @param key qq号 84 | * @return 登录的Bot实例 85 | */ 86 | public boolean login(long key) { 87 | Bot bot = STORE.get(key); 88 | if (bot == null) return false; 89 | bot.login(); 90 | return true; 91 | } 92 | 93 | /** 94 | * <h2>将所有注册的Bot实例登入</h2> 95 | */ 96 | public void loginAll() { 97 | STORE.forEach((qq, bot) -> { 98 | bot.login(); 99 | }); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/CommandUtil.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils; 2 | 3 | import net.diyigemt.miraiboot.annotation.MessagePreProcessor; 4 | import net.diyigemt.miraiboot.interfaces.IMessageFilter; 5 | import net.diyigemt.miraiboot.interfaces.IMessagePreProcessor; 6 | import net.mamoe.mirai.message.data.SingleMessage; 7 | import net.diyigemt.miraiboot.annotation.EventHandler; 8 | import net.diyigemt.miraiboot.annotation.MessageFilter; 9 | import net.diyigemt.miraiboot.constant.ConstantGlobal; 10 | import net.diyigemt.miraiboot.entity.MessageEventPack; 11 | import net.diyigemt.miraiboot.entity.MessageFilterItem; 12 | import net.diyigemt.miraiboot.entity.MessageProcessorImp; 13 | import net.diyigemt.miraiboot.entity.PreProcessorData; 14 | 15 | import java.lang.reflect.Method; 16 | import java.util.Arrays; 17 | import java.util.HashSet; 18 | import java.util.List; 19 | import java.util.Set; 20 | import java.util.regex.Matcher; 21 | import java.util.regex.Pattern; 22 | 23 | /** 24 | * <h2>用于处理指令和参数以及注解</h2> 25 | * @author diyigemt 26 | * @since 1.0.0 27 | */ 28 | public class CommandUtil { 29 | /** 30 | * 全局唯一实例 31 | */ 32 | private static final CommandUtil INSTANCE = new CommandUtil(); 33 | /** 34 | * <h2>所有注册的指令开头的集合</h2> 35 | * 静态的, 当配置文件载入后<strong>不再改变</strong> 当然如果运行时需要添加新的指令开头也可以<br/> 36 | * 例如<br/> 37 | * CommandUtil.getInstance().registerCommandStart(String start);<br/> 38 | * 注册完成后调用<br/> 39 | * CommandUtil.getInstance().compileCommandPattern();<br/> 40 | * 即可完成新指令开头的注册 41 | */ 42 | private static final Set<String> COMMAND_START_SET = new HashSet<String>(); 43 | /** 44 | * <h2>基本正则 用于匹配指令本身</h2> 45 | * 注意:<strong>指令仅支持纯中文或者纯英文与阿拉伯数字组合两种形式 不能中英混用</strong> 46 | */ 47 | private static final String BASE_PATTERN = "?([\\u4e00-\\u9fa5]+|[a-zA-Z0-9]+))"; 48 | /** 49 | * 正则中的特殊字符 正则编译时需要转义 50 | */ 51 | private static final List<String> SPECIAL_CHARACTER = Arrays.asList("^", "$", "[", "(", ")", "{", "\\", "?", ".", "*", "|", "+"); 52 | /** 53 | * <h2>匹配指令及其参数的正则</h2> 54 | * 会在注册完成指令开头后调用compileCommandPattern()方法编译<br/> 55 | * 因此 在运行时注册完指令开头后<strong>需要</strong>调用compileCommandPattern()进行重新编译 56 | */ 57 | private static Pattern commandPattern; 58 | 59 | 60 | /** 61 | * 获取全局唯一实例 62 | * @return CommandUtil的全局唯一实例 63 | */ 64 | public static CommandUtil getInstance() { return INSTANCE; } 65 | 66 | public String parseTargetAndStart(EventHandler annotation, String defaultName) { 67 | String targetName = annotation.target(); 68 | String start = annotation.start(); 69 | if (targetName.equals("")) { 70 | targetName = defaultName; 71 | } 72 | if (start.equals("")) { 73 | Object o = GlobalConfig.getInstance().get(ConstantGlobal.DEFAULT_COMMAND_START); 74 | if (!o.toString().equals("")) targetName = o + targetName; 75 | } else { 76 | targetName = start + targetName; 77 | // 注册指令开头 78 | CommandUtil.getInstance().registerCommandStart(start); 79 | } 80 | return targetName; 81 | } 82 | 83 | /** 84 | * <h2>注册一个指令开头</h2> 85 | * <p>什么是指令开头呢 就比如"/搜图" 那么指令本身是"搜图" 而"/"就是指令开头了</p> 86 | * <p>支持同指令不同开头对应不同EventHandler 比如注册两种指令开头"/"和"."</p> 87 | * <p>那么"/搜图"和".搜图"可以分别指向不同的EventHandler</p> 88 | * @param start 要注册的指令开头 89 | */ 90 | public void registerCommandStart(String start) { 91 | COMMAND_START_SET.add(start); 92 | compileCommandPattern(); 93 | } 94 | 95 | /** 96 | * <h2>将指令开头编译至指令分离正则中</h2> 97 | * <h2>因此 在运行时注册完指令开头后务必重新调用compileCommandPattern()进行编译</h2> 98 | */ 99 | public void compileCommandPattern() { 100 | StringBuilder sb = new StringBuilder(); 101 | sb.append("(("); 102 | if (!COMMAND_START_SET.isEmpty()) { 103 | COMMAND_START_SET.forEach(item -> { 104 | //判断是否是特殊字符需要转义 105 | if (SPECIAL_CHARACTER.contains(item)) { 106 | sb.append("\\").append(item).append("|"); 107 | } else { 108 | sb.append(item).append("|"); 109 | } 110 | }); 111 | sb.replace(sb.length() - 1, sb.length(), ""); 112 | } 113 | sb.append(")"); 114 | sb.append(BASE_PATTERN); 115 | commandPattern = Pattern.compile(sb.toString()); 116 | } 117 | 118 | /** 119 | * <h2>将指令字符串通过正则分离出指令</h2>> 120 | * @param source 原字符串 121 | * @return 分离出的指令 122 | */ 123 | public String parseCommand(String source) { 124 | // 默认为空指令 125 | String command = ""; 126 | Matcher matcher = commandPattern.matcher(source); 127 | if (matcher.find() && matcher.groupCount() >= 3) { 128 | command = matcher.group(1); 129 | } 130 | return command; 131 | } 132 | 133 | /** 134 | * <h2>从纯文本中分理出参数和被检测到属于指令的内容</h2> 135 | * @param source 消息纯文本 136 | * @param command 指令 由于指令别名的存在没有从方法的注解中获取 而是采用在parseCommand中获取的指令名 137 | * @param handler 触发的方法 138 | * @param data 存储处理结果 139 | * @return 处理结果 140 | */ 141 | public PreProcessorData parseArgs(String source, String command, Method handler, PreProcessorData data) { 142 | EventHandler eventHandlerAnnotation = handler.getAnnotation(EventHandler.class); 143 | String start = eventHandlerAnnotation.start(); 144 | if (start.equals("")) { 145 | Object o = GlobalConfig.getInstance().get(ConstantGlobal.DEFAULT_COMMAND_START); 146 | if (!o.toString().equals("")) start = o.toString(); 147 | } 148 | String remove = start + command; 149 | String s = source; 150 | // 如果是强制执行handler command将会是 "" 151 | // 那么substring的结果将会是 "" 需要做个选择 152 | if (!command.equals("")) s = source.substring(source.indexOf(remove) + remove.length()).trim(); 153 | String split = eventHandlerAnnotation.split(); 154 | if (split.equals("")) split = "\\s+"; 155 | String[] res = s.split(split); 156 | data.addArgs(res); 157 | data.setCommandText(s); 158 | return data; 159 | } 160 | 161 | /** 162 | * <h2>检查@MessageFiler是否通过</h2> 163 | * @param eventPack 要检查的事件 164 | * @param handler 要检查的事件Handler 165 | * @param source 消息纯文本 166 | * @return 是否通过 167 | */ 168 | public boolean checkFilter(MessageEventPack eventPack, Method handler, String source) { 169 | MessageFilter[] filters = handler.getDeclaredAnnotationsByType(MessageFilter.class); 170 | for (MessageFilter filter : filters) { 171 | MessageFilterItem item = new MessageFilterItem(filter); 172 | Class<? extends IMessageFilter> self = filter.filter(); 173 | try { 174 | IMessageFilter iMessageFilter = self.getDeclaredConstructor().newInstance(); 175 | boolean check = iMessageFilter.check(source, eventPack, item); 176 | if (!check) return false; 177 | } catch (Throwable e) { 178 | ExceptionHandlerManager.getInstance().emit(e); 179 | return false; 180 | } 181 | } 182 | return true; 183 | } 184 | 185 | /** 186 | * <h2>进行消息预处理</h2> 187 | * @param eventPack 要处理的事件 188 | * @param data 存放结果 189 | * @param handler 要处理的事件Handler 190 | * @param source 消息纯文本 191 | * @return 结果 192 | */ 193 | public PreProcessorData<?> parsePreProcessor(MessageEventPack eventPack, PreProcessorData<?> data, Method handler, String source) { 194 | MessagePreProcessor annotation = handler.getAnnotation(MessagePreProcessor.class); 195 | MessageProcessorImp preProcessorItem = new MessageProcessorImp(); 196 | preProcessorItem.addFilterType(annotation.filterType()); 197 | List<SingleMessage> singleMessages = preProcessorItem.parseMessage(eventPack); 198 | data.addClassified(singleMessages); 199 | Class<? extends IMessagePreProcessor<?>> filter = annotation.filter(); 200 | if (filter != MessageProcessorImp.class) { 201 | try { 202 | IMessagePreProcessor messageProcessor = filter.getDeclaredConstructor().newInstance(); 203 | data = messageProcessor.parseMessage(source, eventPack, data); 204 | } catch (Throwable e) { 205 | ExceptionHandlerManager.getInstance().emit(e); 206 | } 207 | } 208 | return data; 209 | } 210 | 211 | /** 212 | * <h2>构建Handler的基础Name</h2> 213 | * @param clazz 类名 214 | * @return 基础name 215 | */ 216 | public String parseHandlerBaseName(Class<?> clazz) { 217 | return clazz.getName(); 218 | } 219 | 220 | /** 221 | * <h2>构建Handler的基础Name</h2> 222 | * @param className 类名 223 | * @param packageName 包名 224 | * @return 基础name 225 | */ 226 | public String getHandlerBaseName(String packageName, String className) { 227 | return packageName + "." + className; 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/ExceptionHandlerManager.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils; 2 | 3 | import net.diyigemt.miraiboot.annotation.ExceptionHandler; 4 | import net.diyigemt.miraiboot.entity.*; 5 | import net.diyigemt.miraiboot.interfaces.UnloadHandler; 6 | import net.diyigemt.miraiboot.mirai.MiraiMain; 7 | import net.mamoe.mirai.event.events.BotEvent; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | import java.util.*; 13 | import java.util.stream.Collectors; 14 | 15 | import static net.diyigemt.miraiboot.constant.ConstantException.MAX_PARAM_COUNT; 16 | 17 | /** 18 | * <h2>异常总处理器</h2> 19 | * 管理着所有通过@ExceptionHandler注册的方法 20 | * 21 | * @author diyigemt 22 | * @since 1.0.0 23 | */ 24 | public class ExceptionHandlerManager implements UnloadHandler { 25 | 26 | /** 27 | * 全局唯一实例 28 | */ 29 | private static final ExceptionHandlerManager INSTANCE = new ExceptionHandlerManager(); 30 | /** 31 | * 存储所有通过@ExceptionHandler注册的方法 32 | */ 33 | private static final List<ExceptionHandlerItem> STORE = new ArrayList<>(); 34 | 35 | /** 36 | * 获取全局唯一实例 37 | * 38 | * @return 全局唯一实例 39 | */ 40 | public static ExceptionHandlerManager getInstance() { 41 | return INSTANCE; 42 | } 43 | 44 | /** 45 | * 注册一个异常监听器 46 | * @param item 监听器本体 47 | */ 48 | public void on(ExceptionHandlerItem item) { 49 | STORE.add(item); 50 | } 51 | 52 | /** 53 | * 触发异常类对应的异常处理器 54 | * @param e 异常本体 55 | * @param <T> 异常泛型 56 | * @return 处理结果 正常返回null 异常返回信息字符串 57 | */ 58 | public <T extends Throwable> boolean emit(T e) { 59 | return emit(e, null, null); 60 | } 61 | 62 | /** 63 | * 触发异常类对应的异常处理器 64 | * 65 | * @param e 异常本体 66 | * @param eventPack 引发的事件 可以为null 67 | * @param data 引发事件的数据 可以为null 68 | * @param <T> 异常泛型 69 | * @return 处理结果 正常返回null 异常返回信息字符串 70 | */ 71 | public <T extends Throwable, K extends BaseEventPack> boolean emit(T e, K eventPack, PreProcessorData<?> data) { 72 | return handleException(e, eventPack, data, STORE); 73 | } 74 | 75 | /** 76 | * 根据异常处理器的名称移除一个异常处理器 77 | * @param name 要移除的异常处理器的名称 78 | * @return 被移除的异常处理器 79 | */ 80 | public ExceptionHandlerItem remove(String name) { 81 | for (int i = 0; i < STORE.size(); i++) { 82 | ExceptionHandlerItem item = STORE.get(i); 83 | if (item.getName().equals(name)) { 84 | return STORE.remove(i); 85 | } 86 | } 87 | return null; 88 | } 89 | 90 | /** 91 | * 处理异常 92 | * @param e 异常 93 | * @param eventPack 发生异常的事件 94 | * @param data 发生异常的预处理数据 95 | * @param handlers 存储处理该异常handler的list 96 | * @param <T> 异常类 97 | * @param <K> 异常事件类型 98 | * @return 有对应的异常处理类并且执行成功返回true 99 | */ 100 | public <T extends Throwable, K extends BaseEventPack> boolean handleException(T e, K eventPack, PreProcessorData<?> data, List<ExceptionHandlerItem> handlers) { 101 | if (handlers == null || handlers.isEmpty()) return false; 102 | List<ExceptionHandlerItem> collect = handlers.stream().filter(item -> item.check(e.getClass())).collect(Collectors.toList()); 103 | if (collect.isEmpty()) return false; 104 | // 排序 105 | Collections.sort(collect); 106 | int priority = 0; 107 | boolean isBlock = false; 108 | boolean lock = true; 109 | for (ExceptionHandlerItem item : collect) { 110 | if (isBlock && item.getPriority() < priority) continue; 111 | Class<?> invoker = item.getInvoker(); 112 | Method handler = item.getHandler(); 113 | int parameterCount = handler.getParameterCount(); 114 | Object[] param = new Object[parameterCount]; 115 | Object[] pre = new Object[]{e, eventPack, data}; 116 | for (int i = 0; i < parameterCount; i++) { 117 | if (i >= MAX_PARAM_COUNT) { 118 | param[i] = null; 119 | continue; 120 | } 121 | param[i] = pre[i]; 122 | } 123 | priority = item.getPriority(); 124 | try { 125 | Object o = handler.invoke(invoker.getDeclaredConstructor().newInstance(), param); 126 | if (o != null && lock) { 127 | isBlock = Boolean.parseBoolean(o.toString()); 128 | if (isBlock) { 129 | lock = false; 130 | } 131 | } 132 | } catch (IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchMethodException ex) { 133 | ex.printStackTrace(); 134 | return false; 135 | } 136 | } 137 | return true; 138 | } 139 | 140 | /** 141 | * 卸载插件 142 | * @param pluginItems PluginMgr提供的类清单 143 | */ 144 | @Override 145 | public void onUnload(List<PluginItem> pluginItems) { 146 | for(PluginItem item : pluginItems){ 147 | Annotation annotationData = item.getAnnotationData(); 148 | if (!(annotationData instanceof ExceptionHandler)) continue; 149 | ExceptionHandler annotation = (ExceptionHandler) annotationData; 150 | String name = annotation.name(); 151 | if (name.equals("")) { 152 | String className = item.getClassName(); 153 | String packageName = item.getPackageName(); 154 | name = CommandUtil.getInstance().getHandlerBaseName(packageName, className); 155 | } 156 | remove(name); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * <h2>文件管理工具类</h2> 7 | * 单列实例 8 | * <p>用于获取资源文件夹下的资源文件</p> 9 | * <p>具体使用请参阅方法注释</p> 10 | * @author diyigemt 11 | * @since 1.0.0 12 | */ 13 | public class FileUtil { 14 | /** 15 | * 资源文件根目录 16 | */ 17 | private static String RESOURCE_ROOT_FOLDER_PATH; 18 | /** 19 | * 配置文件根目录 20 | */ 21 | private static String CONFIG_ROOT_FOLDER_PATH; 22 | /** 23 | * 插件文件根目录 24 | */ 25 | private static String PLUGIN_ROOT_FOLDER_PATH; 26 | /** 27 | * Bot设备信息根目录 28 | */ 29 | private static String BOT_DEVICE_FOLDER_PATH; 30 | // 系统目录分隔符 31 | /* linux: "/" windows: "\\" */ 32 | public static String SYSTEM_PATH_DIV; 33 | 34 | private static final FileUtil INSTANCE = new FileUtil(); 35 | 36 | /** 37 | * <h2>初始化工具类</h2> 38 | * 初始化和创建文件夹 39 | * @param mainClass 主程序入口 40 | */ 41 | public static void init(Class<?> mainClass) { 42 | SYSTEM_PATH_DIV = File.separator; 43 | String base = getJARRootPath(mainClass) + SYSTEM_PATH_DIV; 44 | RESOURCE_ROOT_FOLDER_PATH = base + "data"; 45 | CONFIG_ROOT_FOLDER_PATH = base + "config"; 46 | PLUGIN_ROOT_FOLDER_PATH = RESOURCE_ROOT_FOLDER_PATH + SYSTEM_PATH_DIV + "plugin"; 47 | String dBRootFolderPath = CONFIG_ROOT_FOLDER_PATH + SYSTEM_PATH_DIV + "dbs"; 48 | // 初始化资源文件夹 49 | File resourceFileDir = new File(RESOURCE_ROOT_FOLDER_PATH); 50 | if (!resourceFileDir.exists()) resourceFileDir.mkdirs(); 51 | File voicesFileDir = new File(RESOURCE_ROOT_FOLDER_PATH + SYSTEM_PATH_DIV + "voices"); 52 | if (!voicesFileDir.exists()) voicesFileDir.mkdir(); 53 | File imagesFileDir = new File(RESOURCE_ROOT_FOLDER_PATH + SYSTEM_PATH_DIV + "images"); 54 | if (!imagesFileDir.exists()) imagesFileDir.mkdir(); 55 | // 初始化配置文件夹 56 | File configFileDir = new File(CONFIG_ROOT_FOLDER_PATH); 57 | if (!configFileDir.exists()) configFileDir.mkdirs(); 58 | // 初始化插件文件夹 59 | File pluginFileDir = new File(PLUGIN_ROOT_FOLDER_PATH); 60 | if (!pluginFileDir.exists()) pluginFileDir.mkdir(); 61 | // 初始化机器人device文件位置文件夹 62 | BOT_DEVICE_FOLDER_PATH = CONFIG_ROOT_FOLDER_PATH + SYSTEM_PATH_DIV + "bots"; 63 | File botDeviceFileDir = new File(BOT_DEVICE_FOLDER_PATH); 64 | if (!botDeviceFileDir.exists()) botDeviceFileDir.mkdirs(); 65 | //初始化db文件夹 66 | File dBFileDir = new File(dBRootFolderPath); 67 | if (!dBFileDir.exists()) dBFileDir.mkdirs(); 68 | } 69 | 70 | /** 71 | * <h2>获取全局实例</h2> 72 | * @return 全局实例 73 | */ 74 | public static FileUtil getInstance() { 75 | return INSTANCE; 76 | } 77 | 78 | /** 79 | * <h2>根据文件名获取资源文件</h2> 80 | * @param fileName 文件名(包括文件拓展名) 81 | * @return 获取到的文件 82 | */ 83 | public File getResourceFile(String fileName) { 84 | return getResourceFile(fileName, null); 85 | } 86 | 87 | /** 88 | * <h2>根据文件名获取音频资源文件</h2> 89 | * @param fileName 文件名(包括文件拓展名) 90 | * @return 获取到的文件 91 | */ 92 | public File getVoiceResourceFile(String fileName) { 93 | return getResourceFile(fileName, "voices"); 94 | } 95 | 96 | /** 97 | * <h2>根据文件名获取音频资源文件</h2> 98 | * @param fileName 文件名(包括文件拓展名) 99 | * @param fold 子文件夹 100 | * @return 获取到的文件 101 | */ 102 | public File getVoiceResourceFile(String fileName, String fold) { 103 | if (fold == null) return getResourceFile(fileName, "voices"); 104 | return getResourceFile(fileName, "voices" + SYSTEM_PATH_DIV + fold); 105 | } 106 | 107 | /** 108 | * <h2>根据文件名获取图片资源文件</h2> 109 | * @param fileName 文件名(包括文件拓展名) 110 | * @return 获取到的文件 111 | */ 112 | public File getImageResourceFile(String fileName) { 113 | return getResourceFile(fileName, "images"); 114 | } 115 | 116 | /** 117 | * <h2>根据文件名获取图片资源文件</h2> 118 | * @param fileName 文件名(包括文件拓展名) 119 | * @param fold 所在的子文件夹 120 | * @return 获取到的文件 121 | */ 122 | public File getImageResourceFile(String fileName, String fold) { 123 | return getResourceFile(fileName, "images" + SYSTEM_PATH_DIV + fold); 124 | } 125 | 126 | /** 127 | * <h2>根据文件名获取资源文件</h2> 128 | * @param fileName 文件名(包括文件拓展名) 129 | * @param fold 所在的子文件夹 130 | * @return 获取到的文件 131 | */ 132 | public File getResourceFile(String fileName, String fold) { 133 | if (fold == null) return new File(RESOURCE_ROOT_FOLDER_PATH + SYSTEM_PATH_DIV + fileName); 134 | return new File(RESOURCE_ROOT_FOLDER_PATH + SYSTEM_PATH_DIV + fold + SYSTEM_PATH_DIV + fileName); 135 | } 136 | 137 | /** 138 | * <h2>根据Bot的qq号获取设备文件</h2> 139 | * @param botId Bot的qq号 140 | * @param deviceFileName 设备文件名(包括文件拓展名) 141 | * @return 文件相对路径 142 | */ 143 | public String getBotDeviceFilePath(long botId, String deviceFileName) { 144 | String s = BOT_DEVICE_FOLDER_PATH + SYSTEM_PATH_DIV + botId; 145 | File botDeviceFile = new File(s); 146 | if (!botDeviceFile.exists()) botDeviceFile.mkdir(); 147 | return s + SYSTEM_PATH_DIV + deviceFileName; 148 | } 149 | 150 | /** 151 | * <h2>获取配置文件</h2> 152 | * @return 配置文件 没有获取到为null 153 | */ 154 | public File getConfigFile() { 155 | File configFile = new File(CONFIG_ROOT_FOLDER_PATH + "/application.yml"); 156 | return configFile.exists() ? configFile : null; 157 | } 158 | 159 | /** 160 | * <h2>创建配置文件</h2> 161 | * 没有找到配置文件时调用 162 | * @return 创建好的配置文件 163 | */ 164 | public File createConfigFile() { 165 | File configFile = new File(CONFIG_ROOT_FOLDER_PATH + "/application.yml"); 166 | if (!configFile.exists()) { 167 | try { 168 | if (!configFile.createNewFile()) return null; 169 | InputStream resourceAsStream = FileUtil.class.getResourceAsStream("/application-example.yml"); 170 | FileOutputStream stream = new FileOutputStream(configFile); 171 | byte[] buf = new byte[4096]; 172 | int len; 173 | while ((len = resourceAsStream.read(buf)) > 0) { 174 | stream.write(buf, 0, len); 175 | } 176 | stream.close(); 177 | resourceAsStream.close(); 178 | } catch (IOException e) { 179 | e.printStackTrace(); 180 | return null; 181 | } catch (NullPointerException ignored) { 182 | 183 | } 184 | } 185 | return configFile; 186 | } 187 | 188 | /** 189 | * <h2>获取jar包所在的全路径</h2> 190 | * @param target 需要获取的jar包里的class 191 | * @return 全路径 192 | */ 193 | public static String getJARRootPath(Class<?> target) { 194 | String path = target.getProtectionDomain().getCodeSource().getLocation().getPath(); 195 | if (System.getProperty("os.name").contains("dows")) { 196 | path = path.substring(1); 197 | } 198 | if (path.contains("jar")) { 199 | path = path.substring(0, path.lastIndexOf(".")); 200 | return path.substring(0, path.lastIndexOf("/")); 201 | } 202 | return path.replace("target/classes/", ""); 203 | } 204 | 205 | /** 206 | * <h2>获取插件文件夹下的所有jar文件</h2> 207 | */ 208 | public File[] getPlugins() { 209 | return new File(PLUGIN_ROOT_FOLDER_PATH).listFiles(); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/GlobalConfig.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils; 2 | 3 | import net.diyigemt.miraiboot.constant.ConstantGlobal; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * <h2>全局配置</h2> 10 | * 保存着默认配置和从配置文件中config项读取到的配置<br/> 11 | * 单例 12 | * @author diyigemt 13 | * @since 1.0.0 14 | */ 15 | public class GlobalConfig { 16 | /** 17 | * 唯一实例 18 | */ 19 | private static final GlobalConfig INSTANCE = new GlobalConfig(); 20 | /** 21 | * 存储所有配置的Hash表 22 | */ 23 | private static final Map<String, Object> STORE = new HashMap<String, Object>(); 24 | 25 | /** 26 | * <h2>获取单例</h2> 27 | * @return 单例 28 | */ 29 | public static GlobalConfig getInstance() { return INSTANCE; } 30 | 31 | /** 32 | * <h2>根据key获取内容</h2> 33 | * @param key 键 34 | * @return 值 35 | */ 36 | public Object get(String key) { return STORE.get(key); } 37 | 38 | /** 39 | * <h2>根据key获取内容</h2> 40 | * @param key 键 41 | * @return 值 42 | */ 43 | public <T> T get(String key, T defaultValue) { 44 | Object o = STORE.get(key); 45 | T res = defaultValue; 46 | try { 47 | res = (T) o; 48 | } catch (ClassCastException e) { 49 | ExceptionHandlerManager.getInstance().emit(e); 50 | } 51 | return o == null ? defaultValue : res; 52 | } 53 | 54 | /** 55 | * <h2>存入一个配置</h2> 56 | * @param key 键 57 | * @param o 值 58 | */ 59 | public void put(String key, Object o) { STORE.put(key, o); } 60 | 61 | /** 62 | * <h2>根据key获取boolean内容</h2> 63 | * @param key 键 64 | * @param defaultValue 默认值 获取失败时返回 65 | * @return 值 66 | */ 67 | public boolean getBoolean(String key, boolean defaultValue) { 68 | Object o = STORE.get(key); 69 | return o == null ? defaultValue : Boolean.parseBoolean(o.toString()); 70 | } 71 | 72 | /** 73 | * <h2>批量存入键值对</h2> 74 | * @param map 键值对 75 | */ 76 | public void putAll(Map<String, Object> map) { STORE.putAll(map); } 77 | 78 | /** 79 | * <h2>读取配置文件后调用</h2> 80 | * 目前只是拿来注册全局指令开头的 81 | */ 82 | public void init() { 83 | // 注册全局指令头 84 | Object o = get(ConstantGlobal.DEFAULT_COMMAND_START); 85 | if (o != null && !o.toString().equals("")) { 86 | CommandUtil.getInstance().registerCommandStart(o.toString()); 87 | } else { 88 | CommandUtil.getInstance().compileCommandPattern(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils; 2 | 3 | import net.diyigemt.miraiboot.entity.HttpProperties; 4 | import net.diyigemt.miraiboot.utils.builder.FileMessageBuilder; 5 | 6 | import javax.net.ssl.HttpsURLConnection; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.net.URL; 10 | import java.util.Iterator; 11 | import java.util.Map; 12 | import java.util.Set; 13 | import java.util.StringTokenizer; 14 | 15 | /** 16 | * <p>HTTP请求工具类</p> 17 | * 18 | * @author Haythem, diyigemt 19 | * @since 1.0.0 20 | */ 21 | 22 | public class HttpUtil { 23 | 24 | /** 25 | * <h2>带高级设置的HTTP请求</h2> 26 | * <p>通过实例化注解或添加注解来增加HOST等设置</p> 27 | * 28 | * @param urlString 目标URL 29 | * @param properties 注解实例化结果 30 | * @return 目标文件输入流 31 | */ 32 | public static InputStream getInputStream_advanced(String urlString, HttpProperties properties) { 33 | InputStream inputStream = null; 34 | try { 35 | URL url = new URL(urlString); 36 | HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 37 | connection.setConnectTimeout(properties.getTimeout()); 38 | connection.setRequestMethod(properties.getRequestMethod()); 39 | Map<String, String> map = properties.getRequestProperties(); 40 | Set set = map.entrySet(); 41 | Iterator it=set.iterator(); 42 | while(it.hasNext()) { 43 | Map.Entry entry=(Map.Entry)it.next(); 44 | String key = entry.getKey().toString(); 45 | String value = entry.getValue().toString(); 46 | connection.setRequestProperty(key, value); 47 | } 48 | 49 | connection.connect(); 50 | inputStream = connection.getInputStream(); 51 | URL su = connection.getURL(); 52 | FileMessageBuilder.FileName = getFileName(su.getPath()); 53 | } catch (IOException e) { 54 | e.printStackTrace(); 55 | } 56 | return inputStream; 57 | } 58 | 59 | /** 60 | * <h2>使用默认配置的HTTP请求</h2> 61 | * <p>此为裸连请求,不适合用来请求外网资源</p> 62 | * 63 | * @param urlString 目标URL 64 | * @return 目标文件输入流 65 | */ 66 | public static InputStream getInputStream(String urlString) { 67 | InputStream inputStream = null; 68 | try { 69 | URL url = new URL(urlString); 70 | HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 71 | inputStream = connection.getInputStream(); 72 | URL su = connection.getURL(); 73 | FileMessageBuilder.FileName = getFileName(su.getPath()); 74 | } catch (IOException e) { 75 | e.printStackTrace(); 76 | } 77 | return inputStream; 78 | } 79 | 80 | private static String getFileName(String file) { 81 | StringTokenizer st = new StringTokenizer(file, "/"); 82 | while (st.hasMoreTokens()) { 83 | file = st.nextToken(); 84 | } 85 | return file; 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/PermissionUtil.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils; 2 | 3 | import net.diyigemt.miraiboot.permission.CheckPermission; 4 | import net.diyigemt.miraiboot.dao.PermissionDAO; 5 | import net.diyigemt.miraiboot.entity.PermissionItem; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * <h2>权限管理的工具类</h2> 13 | * 单例 一般用不到 14 | * @author diyigemt 15 | * @since 1.0.0 16 | */ 17 | public class PermissionUtil { 18 | private static final PermissionUtil INSTANCE = new PermissionUtil(); 19 | 20 | public PermissionUtil() {} 21 | 22 | public static PermissionUtil getInstance() { return INSTANCE; } 23 | 24 | /** 25 | * <h2>获取权限数据库中的记录</h2> 26 | * 根据发送者qq号和EventHandler的permissionIndex获取记录 27 | * @param senderId 发送者qq号 28 | * @param commandId EventHandler注解中的permissionIndex 29 | * @return 对应的记录 没有记录时返回null 30 | * @see CheckPermission 31 | * @see PermissionItem 32 | */ 33 | public PermissionItem getPermissionItem(long senderId, String commandId) { 34 | Map<String, Object> args = new HashMap<String, Object>(); 35 | args.put("sender_id", senderId); 36 | args.put("command_id", commandId); 37 | List<PermissionItem> permissionItems = PermissionDAO.getInstance().selectForFieldValuesArgs(args); 38 | if (permissionItems != null && !permissionItems.isEmpty()) return permissionItems.get(0); 39 | return null; 40 | } 41 | 42 | /** 43 | * <h2>向数据库中添加一天记录</h2> 44 | * @param targetId 发送者qq号 45 | * @param commandId EventHandler注解中的permissionIndex 46 | * @see CheckPermission 47 | * @see PermissionItem 48 | */ 49 | public void addPermissionItem(long targetId, int commandId) { 50 | PermissionItem item = new PermissionItem(targetId, String.valueOf(commandId)); 51 | updatePermissionItem(item); 52 | } 53 | 54 | /** 55 | * <h2>向数据库中添加一条记录</h2> 56 | * @param targetId 发送者qq号 57 | * @param commandId EventHandler注解中的permissionIndex 58 | * @param permits 权限等级 防止下克上 59 | * @param remain 剩余次数 60 | * @see CheckPermission 61 | * @see PermissionItem 62 | */ 63 | public void addPermissionItem(long targetId, int commandId, int permits, int remain) { 64 | PermissionItem item = new PermissionItem(targetId, commandId, permits, remain); 65 | updatePermissionItem(item); 66 | } 67 | 68 | /** 69 | * <h2>从数据库中移除一条记录</h2> 70 | * 根据发送者qq号和permissionIndex从数据库中移除一条记录 71 | * @param targetId 发送者qq号 72 | * @param commandId EventHandler注解中的permissionIndex 73 | * @see CheckPermission 74 | * @see PermissionItem 75 | */ 76 | public void removePermissionItem(long targetId, int commandId) { 77 | PermissionItem item = new PermissionItem(targetId, String.valueOf(commandId)); 78 | PermissionDAO.getInstance().delete(item); 79 | } 80 | 81 | /** 82 | * <h2>启用权限</h2> 83 | * 根据发送者qq号和permissionIndex从数据库中启用一条记录 84 | * @param targetId 发送者qq号 85 | * @param commandId EventHandler注解中的permissionIndex 86 | * @see CheckPermission 87 | * @see PermissionItem 88 | */ 89 | public void enablePermissionItem(long targetId, int commandId) { 90 | PermissionItem item = new PermissionItem(targetId, String.valueOf(commandId)); 91 | updatePermissionItem(item); 92 | } 93 | 94 | /** 95 | * <h2>启用权限</h2> 96 | * 根据发送者qq号和permissionIndex从数据库中启用一条记录 97 | * @param targetId 发送者qq号 98 | * @param commandId EventHandler注解中的permissionIndex 99 | * @param permits 权限等级 100 | * @see CheckPermission 101 | * @see PermissionItem 102 | */ 103 | public void enablePermissionItem(long targetId, int commandId, int permits) { 104 | PermissionItem item = new PermissionItem(targetId, commandId, permits); 105 | updatePermissionItem(item); 106 | } 107 | 108 | /** 109 | * <h2>关闭权限而不删除记录</h2> 110 | * 根据发送者qq号和permissionIndex从数据库中关闭一条记录 111 | * @param targetId 发送者qq号 112 | * @param commandId EventHandler注解中的permissionIndex 113 | * @see CheckPermission 114 | * @see PermissionItem 115 | */ 116 | public void disablePermissionItem(long targetId, int commandId) { 117 | PermissionItem item = new PermissionItem(targetId, String.valueOf(commandId)); 118 | updatePermissionItem(item); 119 | } 120 | 121 | /** 122 | * <h2>关闭权限而不删除记录</h2> 123 | * 根据发送者qq号和permissionIndex从数据库中关闭一条记录 124 | * @param targetId 发送者qq号 125 | * @param commandId EventHandler注解中的permissionIndex 126 | * @param permits 权限等级 防止下克上 127 | * @see CheckPermission 128 | * @see PermissionItem 129 | */ 130 | public void disablePermissionItem(long targetId, int commandId, int permits) { 131 | PermissionItem item = new PermissionItem(targetId, commandId, permits); 132 | updatePermissionItem(item); 133 | } 134 | 135 | /** 136 | * <h2>更新数据库中的权限信息</h2> 137 | * @param item 权限存储类 138 | */ 139 | public void updatePermissionItem(PermissionItem item) { 140 | PermissionDAO.getInstance().insert(item); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/builder/ExternalResourceBuilder.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils.builder; 2 | 3 | import net.diyigemt.miraiboot.entity.HttpProperties; 4 | import net.mamoe.mirai.Mirai; 5 | import net.mamoe.mirai.utils.ExternalResource; 6 | import net.diyigemt.miraiboot.utils.HttpUtil; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | 12 | /** 13 | * <h2>外部资源构造器类</h2> 14 | * <p>请不要用该class声明变量</p> 15 | * @author Haythem 16 | * @since 1.0.0 17 | */ 18 | public class ExternalResourceBuilder { 19 | 20 | /** 21 | * <h2>外部资源构造器</h2> 22 | * <p>用于构造URL来源的输入,支持HTTP高级设置</p> 23 | */ 24 | public ExternalResource ExtResourceBuilder(String path, HttpProperties properties){ 25 | ExternalResource externalResource = null; 26 | try{ 27 | if(path.contains("http")){//URL 28 | InputStream inputStream = null; 29 | if(properties != null){ 30 | inputStream = HttpUtil.getInputStream_advanced(path, properties); 31 | }else{ 32 | inputStream = HttpUtil.getInputStream(path); 33 | } 34 | externalResource = Mirai.getInstance().getFileCacheStrategy().newCache(inputStream); 35 | }else {//LOCAL 36 | File file = new File(path); 37 | externalResource = ExternalResource.create(file); 38 | FileMessageBuilder.FileName = file.getName(); 39 | } 40 | }catch (IOException e){ 41 | externalResource = null; 42 | }catch (Exception e){ 43 | e.printStackTrace(); 44 | } 45 | return externalResource; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/builder/FileMessageBuilder.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils.builder; 2 | 3 | import net.diyigemt.miraiboot.entity.EnhancedMessageChain; 4 | import net.diyigemt.miraiboot.entity.HttpProperties; 5 | import net.mamoe.mirai.event.events.MessageEvent; 6 | import net.mamoe.mirai.message.data.MessageChain; 7 | import net.mamoe.mirai.message.data.MessageChainBuilder; 8 | import net.mamoe.mirai.utils.ExternalResource; 9 | import net.diyigemt.miraiboot.entity.MessageEventPack; 10 | 11 | import java.io.File; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * <h2>自定义群文件消息构造器</h2> 17 | * <p>样例:</p> 18 | * <p>EnhancedMessageChain chains = new FileMessageBuilder(MessageEventPack eventPack).build();</p> 19 | * <p></p> 20 | * <p><b>注:请不要用此类创建变量</b></p> 21 | * 22 | * @author Haythem 23 | * @since 1.0.0 24 | */ 25 | 26 | public class FileMessageBuilder { 27 | 28 | private final Pattern windowsPattern = Pattern.compile("[A-z]:\\\\([A-Za-z0-9_\u4e00-\u9fa5]+\\\\)*"); 29 | 30 | private final Pattern linuxPattern = Pattern.compile("/([A-Za-z0-9_\u4e00-\u9fa5]+/?)+"); 31 | 32 | private MessageEvent event = null; 33 | 34 | private MessageEventPack messageEventPack = null; 35 | 36 | private EnhancedMessageChain chains = new EnhancedMessageChain(); 37 | 38 | private boolean isUTTPRequestSuccess = true; 39 | 40 | public static String FileName = null; 41 | 42 | public HttpProperties properties = null; 43 | 44 | 45 | /** 46 | * <h2>自定义语音消息构造器</h2> 47 | * <p>可以自定义语音和文字消息构成</p> 48 | * <p>样例:</p> 49 | * <p>EnhancedMessageChain chains = new FileMessageBuilder(eventPack)</p> 50 | * <p>&nbsp;&nbsp;.add("1234\n")</p> 51 | * <p>&nbsp;&nbsp;.add("1234\n", "5678\n")</p> 52 | * <p>&nbsp;&nbsp;.add(enhancedMessageChain)</p> 53 | * <p>&nbsp;&nbsp;.add(LocalFilePath)</p> 54 | * <p>&nbsp;&nbsp;.add(urlPath)</p> 55 | * <p>&nbsp;&nbsp;.add(file)</p> 56 | * <p>&nbsp;&nbsp;.send();(或.build();)</p> 57 | * <p>}</p> 58 | * 59 | * @param eventPack 事件封装 60 | * @author Haythem 61 | * @since 1.0.0 62 | */ 63 | public FileMessageBuilder(MessageEventPack eventPack) { 64 | this.messageEventPack = eventPack; 65 | this.event = eventPack.getEvent(); 66 | } 67 | 68 | /** 69 | * <h2>自定义语音消息构造器</h2> 70 | * <p>可以自定义语音和文字消息构成</p> 71 | * <p>样例:</p> 72 | * <p>EnhancedMessageChain chains = new FileMessageBuilder(eventPack)</p> 73 | * <p>&nbsp;&nbsp;.add("1234\n")</p> 74 | * <p>&nbsp;&nbsp;.add("1234\n", "5678\n")</p> 75 | * <p>&nbsp;&nbsp;.add(enhancedMessageChain)</p> 76 | * <p>&nbsp;&nbsp;.add(LocalFilePath)</p> 77 | * <p>&nbsp;&nbsp;.add(urlPath)</p> 78 | * <p>&nbsp;&nbsp;.add(file)</p> 79 | * <p>&nbsp;&nbsp;.send();(或.build();)</p> 80 | * <p>}</p> 81 | * 82 | * @param eventPack 事件封装 83 | * @author Haythem 84 | * @since 1.0.0 85 | */ 86 | public FileMessageBuilder(MessageEventPack eventPack, HttpProperties properties){ 87 | this.messageEventPack = eventPack; 88 | this.event = eventPack.getEvent(); 89 | this.properties = properties; 90 | } 91 | 92 | /** 93 | * <h2>添加群文件消息方法</h2> 94 | * <p>支持以下类型输入:</p> 95 | * <p></p> 96 | * <p>1: MessageChain消息链</p> 97 | * <p>2: EnhancedMessageChain加强的消息链</p> 98 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 99 | * <p>4: File 打开的文件类</p> 100 | * <p></p> 101 | * <p>注:</p> 102 | * <p>1: 请勿上传大小为0字节的文件</p> 103 | * <p>2: URL支持重定向</p> 104 | * 105 | * @param messageChain 当前类型: MessageChain 消息链 106 | */ 107 | public FileMessageBuilder add(MessageChain messageChain) { 108 | this.chains.append(messageChain); 109 | return this; 110 | } 111 | 112 | /** 113 | * <h2>添加群文件消息方法</h2> 114 | * <p>支持以下类型输入:</p> 115 | * <p></p> 116 | * <p>1: MessageChain消息链</p> 117 | * <p>2: EnhancedMessageChain加强的消息链</p> 118 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 119 | * <p>4: File 打开的文件类</p> 120 | * <p></p> 121 | * <p>注:</p> 122 | * <p>1: 请勿上传大小为0字节的文件</p> 123 | * <p>2: URL支持重定向</p> 124 | * 125 | * @param messageChain 当前类型: EnhancedMessageChain 加强消息链 126 | */ 127 | public FileMessageBuilder add(EnhancedMessageChain messageChain){ 128 | this.chains.append(messageChain); 129 | return this; 130 | } 131 | 132 | /** 133 | * <h2>添加群文件消息方法</h2> 134 | * <p>支持以下类型输入:</p> 135 | * <p></p> 136 | * <p>1: MessageChain消息链</p> 137 | * <p>2: EnhancedMessageChain加强的消息链</p> 138 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 139 | * <p>4: File 打开的文件类</p> 140 | * <p></p> 141 | * <p>注:</p> 142 | * <p>1: 请勿上传大小为0字节的文件</p> 143 | * <p>2: URL支持重定向</p> 144 | * 145 | * @param file 当前类型: File 打开的文件类 146 | */ 147 | public FileMessageBuilder add(File file) { 148 | ExternalResource resource = ExternalResource.create(file); 149 | MessageChain chain = new MessageChainBuilder().build(); 150 | chain = chain.plus(ExternalResource.uploadAsFile(resource, messageEventPack.getGroup(), "/" + file.getName())); 151 | this.chains.append(chain); 152 | return this; 153 | } 154 | 155 | /** 156 | * <h2>添加群文件消息方法</h2> 157 | * <p>支持以下类型输入:</p> 158 | * <p></p> 159 | * <p>1: MessageChain消息链</p> 160 | * <p>2: EnhancedMessageChain加强的消息链</p> 161 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 162 | * <p>4: File 打开的文件类</p> 163 | * <p></p> 164 | * <p>注:</p> 165 | * <p>1: 请勿上传大小为0字节的文件</p> 166 | * <p>2: URL支持重定向</p> 167 | * <p>3: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 168 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 169 | * 170 | * @param s 当前类型: String...可变长字符串 171 | */ 172 | public FileMessageBuilder add(String... s) { 173 | for (String i : s) { 174 | MessageChain chain = new MessageChainBuilder().build(); 175 | Matcher windowsMatcher = windowsPattern.matcher(i); 176 | Matcher linuxMatcher = linuxPattern.matcher(i); 177 | if (!(windowsMatcher.find() || linuxMatcher.find()) && !i.contains("http")) { 178 | chain = chain.plus(i); 179 | this.chains.append(chain); 180 | }else { 181 | ExternalResource resource = ExtResBuilder(i); 182 | if (isUTTPRequestSuccess) { 183 | chain = chain.plus(ExternalResource.uploadAsFile(resource, messageEventPack.getGroup(), "/" + FileName)); 184 | this.chains.append(chain); 185 | } else { 186 | chain = chain.plus("联网获取数据失败"); 187 | this.chains.append(chain); 188 | } 189 | } 190 | } 191 | return this; 192 | } 193 | 194 | /** 195 | * <h2>构造器结尾</h2> 196 | * <p>该方法返回构造完成的加强消息链</p> 197 | * @return EnhancedMessageChain 加强消息链 198 | */ 199 | public EnhancedMessageChain build() { 200 | return this.chains; 201 | } 202 | 203 | /** 204 | * <h2>构造器结尾</h2> 205 | * <p>该方法返回并自动发送构造完成的加强消息链</p> 206 | * <p>注:当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 207 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 208 | * 209 | * @return EnhancedMessageChain 加强消息链 210 | */ 211 | public EnhancedMessageChain send() { 212 | for (MessageChain messageChain : chains) { 213 | event.getSubject().sendMessage(messageChain); 214 | try{ 215 | Thread.sleep(500); 216 | } catch (InterruptedException e) { 217 | continue; 218 | } 219 | } 220 | return this.chains; 221 | } 222 | 223 | private ExternalResource ExtResBuilder(String path){ 224 | ExternalResource resource = new ExternalResourceBuilder().ExtResourceBuilder(path, properties); 225 | if(resource == null){ 226 | isUTTPRequestSuccess = false; 227 | } 228 | return resource; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/builder/ImageMessageBuilder.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils.builder; 2 | 3 | import net.diyigemt.miraiboot.entity.EnhancedMessageChain; 4 | import net.diyigemt.miraiboot.entity.HttpProperties; 5 | import net.mamoe.mirai.event.events.MessageEvent; 6 | import net.mamoe.mirai.message.data.EmptyMessageChain; 7 | import net.mamoe.mirai.message.data.MessageChain; 8 | import net.mamoe.mirai.message.data.MessageChainBuilder; 9 | import net.mamoe.mirai.utils.ExternalResource; 10 | import net.diyigemt.miraiboot.entity.MessageEventPack; 11 | 12 | import java.io.File; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * <h2>自定义图片消息构造器</h2> 18 | * <p>样例:</p> 19 | * <p>EnhancedMessageChain chains = new ImageMessageBuilder(MessageEventPack eventPack).build();</p> 20 | * <p></p> 21 | * <p><b>注:请不要用此类创建变量</b></p> 22 | * @author Haythem 23 | * @since 1.0.0 24 | */ 25 | public class ImageMessageBuilder { 26 | 27 | private final Pattern windowsPattern = Pattern.compile("[A-z]:\\\\([A-Za-z0-9_\u4e00-\u9fa5]+\\\\)*"); 28 | 29 | private final Pattern linuxPattern = Pattern.compile("/([A-Za-z0-9_\u4e00-\u9fa5]+/?)+"); 30 | 31 | private MessageEvent event = null; 32 | 33 | private MessageEventPack messageEventPack = null; 34 | 35 | private MessageChain chain = new MessageChainBuilder().build(); 36 | 37 | private EnhancedMessageChain chains = new EnhancedMessageChain(); 38 | 39 | private boolean isUTTPRequestSuccess = true; 40 | 41 | private HttpProperties properties = null; 42 | 43 | /** 44 | * <h2>自定义图片消息构造器</h2> 45 | * <p>可以自定义图文消息构成</p> 46 | * <p>样例:</p> 47 | * <p>EnhancedMessageChain chain = new ImageMessageBuilder(MessageEventPack)</p> 48 | * <p>&nbsp;&nbsp;.add(messageChain)</p> 49 | * <p>&nbsp;&nbsp;.add("1234\n")</p> 50 | * <p>&nbsp;&nbsp;.add("1234\n", "5678\n")</p> 51 | * <p>&nbsp;&nbsp;.add(enhancedMessageChain)</p> 52 | * <p>&nbsp;&nbsp;.add(LocalFilePath)</p> 53 | * <p>&nbsp;&nbsp;.add(urlPath)</p> 54 | * <p>&nbsp;&nbsp;.add(file)</p> 55 | * <p>&nbsp;&nbsp;.send();(或.build();)</p> 56 | *<p>}</p> 57 | * @param eventPack 事件封装 58 | * @author Haythem 59 | * @since 1.0.0 60 | */ 61 | public ImageMessageBuilder(MessageEventPack eventPack){ 62 | this.messageEventPack = eventPack; 63 | this.event = eventPack.getEvent(); 64 | } 65 | 66 | /** 67 | * <h2>自定义图片消息构造器</h2> 68 | * <p>可以自定义图文消息构成</p> 69 | * <p>样例:</p> 70 | * <p>EnhancedMessageChain chain = new ImageMessageBuilder(MessageEventPack)</p> 71 | * <p>&nbsp;&nbsp;.add(messageChain)</p> 72 | * <p>&nbsp;&nbsp;.add("1234\n")</p> 73 | * <p>&nbsp;&nbsp;.add("1234\n", "5678\n")</p> 74 | * <p>&nbsp;&nbsp;.add(enhancedMessageChain)</p> 75 | * <p>&nbsp;&nbsp;.add(LocalFilePath)</p> 76 | * <p>&nbsp;&nbsp;.add(urlPath)</p> 77 | * <p>&nbsp;&nbsp;.add(file)</p> 78 | * <p>&nbsp;&nbsp;.send();(或.build();)</p> 79 | *<p>}</p> 80 | * @param eventPack 事件封装 81 | * @author Haythem 82 | * @since 1.0.0 83 | */ 84 | public ImageMessageBuilder(MessageEventPack eventPack, HttpProperties properties){ 85 | this.messageEventPack = eventPack; 86 | this.event = eventPack.getEvent(); 87 | this.properties = properties; 88 | } 89 | 90 | /** 91 | * <h2>添加图文消息方法</h2> 92 | * <p>支持以下类型输入:</p> 93 | * <p></p> 94 | * <p>1: MessageChain消息链</p> 95 | * <p>2: EnhancedMessageChain加强的消息链</p> 96 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 97 | * <p>4: File 打开的文件类</p> 98 | * <p></p> 99 | * <p>注:</p> 100 | * <p>1: 请不要插入和图片无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 101 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 102 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 103 | * @param messageChain 当前类型: 消息链 104 | */ 105 | public ImageMessageBuilder add(MessageChain messageChain){ 106 | chain = chain.plus(messageChain); 107 | return this; 108 | } 109 | 110 | /** 111 | * <h2>添加图文消息方法</h2> 112 | * <p>支持以下类型输入:</p> 113 | * <p></p> 114 | * <p>1: MessageChain消息链</p> 115 | * <p>2: EnhancedMessageChain加强的消息链</p> 116 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 117 | * <p>4: File 打开的文件类</p> 118 | * <p></p> 119 | * <p>注:</p> 120 | * <p>1: 请不要插入和图片无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 121 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 122 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 123 | * @param messageChain 当前类型: 加强消息链 124 | */ 125 | public ImageMessageBuilder add(EnhancedMessageChain messageChain){ 126 | if(!(chain instanceof EmptyMessageChain)){ 127 | chains.append(chain); 128 | } 129 | chains.append(messageChain); 130 | this.chain = new MessageChainBuilder().build(); 131 | return this; 132 | } 133 | 134 | /** 135 | * <h2>添加图文消息方法</h2> 136 | * <p>支持以下类型输入:</p> 137 | * <p></p> 138 | * <p>1: MessageChain消息链</p> 139 | * <p>2: EnhancedMessageChain加强的消息链</p> 140 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 141 | * <p>4: File 打开的文件类</p> 142 | * <p></p> 143 | * <p>注:</p> 144 | * <p>1: 请不要插入和图片无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 145 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 146 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 147 | * @param s 当前类型: String... 可变长字符串 148 | */ 149 | public ImageMessageBuilder add(String... s){ 150 | for(String i : s){ 151 | Matcher windowsMatcher = windowsPattern.matcher(i); 152 | Matcher linuxMatcher = linuxPattern.matcher(i); 153 | if(!(windowsMatcher.find() || linuxMatcher.find()) && !i.contains("http")){ 154 | chain = chain.plus(i); 155 | }else { 156 | ExternalResource resource = ExtResBuilder(i); 157 | if(isUTTPRequestSuccess){ 158 | chain = chain.plus(ExternalResource.Companion.uploadAsImage(resource, event.getSubject())); 159 | }else { 160 | chain = chain.plus("联网获取数据失败"); 161 | } 162 | } 163 | } 164 | 165 | return this; 166 | } 167 | 168 | /** 169 | * <h2>添加图文消息方法</h2> 170 | * <p>支持以下类型输入:</p> 171 | * <p></p> 172 | * <p>1: MessageChain消息链</p> 173 | * <p>2: EnhancedMessageChain加强的消息链</p> 174 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 175 | * <p>4: File 打开的文件类</p> 176 | * <p></p> 177 | * <p>注:</p> 178 | * <p>1: 请不要插入和图片无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 179 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 180 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 181 | * @param file 当前类型: 文件类 182 | */ 183 | public ImageMessageBuilder add(File file){ 184 | chain = chain.plus(ExternalResource.Companion.uploadAsImage(file, event.getSubject())); 185 | return this; 186 | } 187 | 188 | /** 189 | * <h2>构造器结尾</h2> 190 | * <p>该方法返回构造完成的加强消息链</p> 191 | * @return EnhancedMessageChain 加强消息链 192 | */ 193 | public EnhancedMessageChain build(){ 194 | this.chains.append(chain); 195 | return chains; 196 | } 197 | 198 | /** 199 | * <h2>构造器结尾</h2> 200 | * <p>该方法返回并自动发送构造完成的加强消息链</p> 201 | * <p>注:当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 202 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 203 | * @return EnhancedMessageChain 加强消息链 204 | */ 205 | 206 | public EnhancedMessageChain send(){ 207 | this.chains.append(chain); 208 | for (MessageChain chain : chains){ 209 | event.getSubject().sendMessage(chain); 210 | try{ 211 | Thread.sleep(500); 212 | } catch (InterruptedException e) { 213 | continue; 214 | } 215 | } 216 | return chains; 217 | } 218 | 219 | private ExternalResource ExtResBuilder(String path){ 220 | ExternalResource resource = new ExternalResourceBuilder().ExtResourceBuilder(path, properties); 221 | if(resource == null){ 222 | isUTTPRequestSuccess = false; 223 | } 224 | return resource; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/net/diyigemt/miraiboot/utils/builder/VoiceMessageBuilder.java: -------------------------------------------------------------------------------- 1 | package net.diyigemt.miraiboot.utils.builder; 2 | 3 | import net.diyigemt.miraiboot.entity.EnhancedMessageChain; 4 | import net.diyigemt.miraiboot.entity.HttpProperties; 5 | import net.mamoe.mirai.contact.Group; 6 | import net.mamoe.mirai.event.events.MessageEvent; 7 | import net.mamoe.mirai.message.data.MessageChain; 8 | import net.mamoe.mirai.message.data.MessageChainBuilder; 9 | import net.mamoe.mirai.utils.ExternalResource; 10 | import net.diyigemt.miraiboot.entity.MessageEventPack; 11 | 12 | import java.io.File; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * <h2>自定义语音消息构造器</h2> 18 | * <p>样例:</p> 19 | * <p>EnhancedMessageChain = new VoiceMessageBuilder(MessageEventPack eventPack).build();</p> 20 | * <p></p> 21 | * <p><b>注:请不要用此类创建变量</b></p> 22 | * @author Haythem 23 | * @since 1.0.0 24 | */ 25 | 26 | public class VoiceMessageBuilder { 27 | 28 | private final Pattern windowsPattern = Pattern.compile("[A-z]:\\\\([A-Za-z0-9_\u4e00-\u9fa5]+\\\\)*"); 29 | 30 | private final Pattern linuxPattern = Pattern.compile("/([A-Za-z0-9_\u4e00-\u9fa5]+/?)+"); 31 | 32 | private MessageEvent event = null; 33 | 34 | private MessageEventPack messageEventPack = null; 35 | 36 | private EnhancedMessageChain chains = new EnhancedMessageChain(); 37 | 38 | private boolean isUTTPRequestSuccess = true; 39 | 40 | private HttpProperties properties = null; 41 | 42 | /** 43 | * <h2>自定义语音消息构造器</h2> 44 | * <p>可以自定义语音和文字消息构成</p> 45 | * <p>样例:</p> 46 | * <p>EnhancedMessageChain chains = new VoiceMessageBuilder(eventPack)</p> 47 | * <p>&nbsp;&nbsp;.add("1234\n")</p> 48 | * <p>&nbsp;&nbsp;.add("1234\n", "5678\n")</p> 49 | * <p>&nbsp;&nbsp;.add(enhancedMessageChain)</p> 50 | * <p>&nbsp;&nbsp;.add(LocalFilePath)</p> 51 | * <p>&nbsp;&nbsp;.add(urlPath)</p> 52 | * <p>&nbsp;&nbsp;.add(file)</p> 53 | * <p>&nbsp;&nbsp;.send();(或.build();)</p> 54 | *<p>}</p> 55 | * @param eventPack 事件封装 56 | * @author Haythem 57 | * @since 1.0.0 58 | */ 59 | public VoiceMessageBuilder(MessageEventPack eventPack){ 60 | this.messageEventPack = eventPack; 61 | this.event = messageEventPack.getEvent(); 62 | } 63 | 64 | /** 65 | * <h2>自定义语音消息构造器</h2> 66 | * <p>可以自定义语音和文字消息构成</p> 67 | * <p>可以使用HTTP高级属性</p> 68 | * <p>样例:</p> 69 | * <p>EnhancedMessageChain chains = new VoiceMessageBuilder(eventPack)</p> 70 | * <p>&nbsp;&nbsp;.add("1234\n")</p> 71 | * <p>&nbsp;&nbsp;.add("1234\n", "5678\n")</p> 72 | * <p>&nbsp;&nbsp;.add(enhancedMessageChain)</p> 73 | * <p>&nbsp;&nbsp;.add(LocalFilePath)</p> 74 | * <p>&nbsp;&nbsp;.add(urlPath)</p> 75 | * <p>&nbsp;&nbsp;.add(file)</p> 76 | * <p>&nbsp;&nbsp;.send();(或.build();)</p> 77 | *<p>}</p> 78 | * @param eventPack 事件封装 79 | * @author Haythem 80 | * @since 1.0.0 81 | */ 82 | public VoiceMessageBuilder(MessageEventPack eventPack, HttpProperties properties){ 83 | this.messageEventPack = eventPack; 84 | this.event = messageEventPack.getEvent(); 85 | this.properties = properties; 86 | } 87 | 88 | /** 89 | * <h2>添加语音消息方法</h2> 90 | * <p>支持以下类型输入:</p> 91 | * <p></p> 92 | * <p>1: MessageChain消息链</p> 93 | * <p>2: EnhancedMessageChain加强的消息链</p> 94 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 95 | * <p>4: File 打开的文件类</p> 96 | * <p></p> 97 | * <p>注:</p> 98 | * <p>1: 请不要插入和语音无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 99 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 100 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 101 | * @param messageChain 当前类型: MessageChain 消息链 102 | */ 103 | public VoiceMessageBuilder add(MessageChain messageChain){ 104 | this.chains.append(messageChain); 105 | return this; 106 | } 107 | 108 | /** 109 | * <h2>添加语音消息方法</h2> 110 | * <p>支持以下类型输入:</p> 111 | * <p></p> 112 | * <p>1: MessageChain消息链</p> 113 | * <p>2: EnhancedMessageChain加强的消息链</p> 114 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 115 | * <p>4: File 打开的文件类</p> 116 | * <p></p> 117 | * <p>注:</p> 118 | * <p>1: 请不要插入和语音无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 119 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 120 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 121 | * @param messageChain 当前类型: EnhancedMessageChain 消息链 122 | */ 123 | public VoiceMessageBuilder add(EnhancedMessageChain messageChain){ 124 | this.chains.append(messageChain); 125 | return this; 126 | } 127 | 128 | /** 129 | * <h2>添加语音消息方法</h2> 130 | * <p>支持以下类型输入:</p> 131 | * <p></p> 132 | * <p>1: MessageChain消息链</p> 133 | * <p>2: EnhancedMessageChain加强的消息链</p> 134 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 135 | * <p>4: File 打开的文件类</p> 136 | * <p></p> 137 | * <p>注:</p> 138 | * <p>1: 请不要插入和语音无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 139 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 140 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 141 | * @param s 当前类型: String... 可变长字符串 142 | */ 143 | public VoiceMessageBuilder add(String... s){ 144 | for(String i : s){ 145 | MessageChain chain = new MessageChainBuilder().build(); 146 | Matcher windowsMatcher = windowsPattern.matcher(i); 147 | Matcher linuxMatcher = linuxPattern.matcher(i); 148 | if(!(windowsMatcher.find() || linuxMatcher.find()) && !i.contains("http")){ 149 | chain = chain.plus(i); 150 | this.chains.append(chain); 151 | }else { 152 | ExternalResource resource = ExtResBuilder(i); 153 | if(isUTTPRequestSuccess){ 154 | chain = chain.plus(this.messageEventPack.uploadAudio(resource)); 155 | this.chains.append(chain); 156 | }else { 157 | chain = chain.plus("联网获取数据失败"); 158 | this.chains.append(chain); 159 | } 160 | } 161 | } 162 | return this; 163 | } 164 | 165 | /** 166 | * <h2>添加语音消息方法</h2> 167 | * <p>支持以下类型输入:</p> 168 | * <p></p> 169 | * <p>1: MessageChain消息链</p> 170 | * <p>2: EnhancedMessageChain加强的消息链</p> 171 | * <p>3: String...可变长字符串,字符串支持本地路径、URL和文字消息</p> 172 | * <p>4: File 打开的文件类</p> 173 | * <p></p> 174 | * <p>注:</p> 175 | * <p>1: 请不要插入和语音无关的素材,如有需求,请使用与素材类型对应的其它Builder</p> 176 | * <p>2: 当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 177 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 178 | * @param file 当前类型: File 打开的文件类 179 | */ 180 | public VoiceMessageBuilder add(File file){ 181 | MessageChain chain = new MessageChainBuilder().build(); 182 | chain = chain.plus(this.messageEventPack.uploadAudio(ExternalResource.create(file))); 183 | chains.append(chain); 184 | return this; 185 | } 186 | 187 | /** 188 | * <h2>构造器结尾</h2> 189 | * <p>该方法返回构造完成的加强消息链</p> 190 | * @return EnhancedMessageChain 加强消息链 191 | */ 192 | public EnhancedMessageChain build(){ 193 | return this.chains; 194 | } 195 | 196 | /** 197 | * <h2>构造器结尾</h2> 198 | * <p>该方法返回并自动发送构造完成的加强消息链</p> 199 | * <p>注:当使用URL素材时,如果网络不佳未能获得素材会发送以下纯文本消息:</p> 200 | * <p>&nbsp;&nbsp;"联网获取素材失败"</p> 201 | * @return EnhancedMessageChain 加强消息链 202 | */ 203 | public EnhancedMessageChain send(){ 204 | for (MessageChain messageChain : chains){ 205 | event.getSubject().sendMessage(messageChain); 206 | try{ 207 | Thread.sleep(500); 208 | } catch (InterruptedException e) { 209 | continue; 210 | } 211 | } 212 | return this.chains; 213 | } 214 | 215 | private ExternalResource ExtResBuilder(String path){ 216 | ExternalResource resource = new ExternalResourceBuilder().ExtResourceBuilder(path, properties); 217 | if(resource == null){ 218 | isUTTPRequestSuccess = false; 219 | } 220 | return resource; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: net.diyigemt.miraiboot.Main 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/application-example.yml: -------------------------------------------------------------------------------- 1 | miraiboot: 2 | bots: 3 | - 4 | account: 123 # qq号 5 | password: 6 | kind: PLAIN # 密码类型 目前仅支持PLAIN纯文本类型 7 | value: pwd # qq密码 8 | configuration: 9 | protocol: ANDROID_PHONE # qq登录协议 支持 ANDROID_PHONE ANDROID_PAD ANDROID_WATCH 10 | device: device.json # 设备信息文件名 11 | # 可以配置多个 在@EventFilter中修改bot字段可以指定不同的bot处理事件 12 | - account: 123 # qq号 13 | password: 14 | kind: PLAIN # 密码类型 目前仅支持PLAIN纯文本类型 15 | value: pwd # qq密码 16 | configuration: 17 | protocol: ANDROID_PHONE # qq登录协议 支持 ANDROID_PHONE ANDROID_PAD ANDROID_WATCH 18 | device: device.json # 设备信息文件名 19 | logger: 20 | network: false # 是否在控制台显示bot的网络信息 21 | eventStatus: true # 是否在控制台打印事件执行失败信息 暂时不起作用 22 | debug: false # 是否在控制台打印事件解析信息 暂时不起作用 23 | alias: # 指令别名 将会自动注册 配置方式为 指令全名:别名 24 | configs: # 全局配置 所有全局配置均可以主动从GlobalConfig类中拿到 具体请看注释 25 | DEFAULT_COMMAND_START: "" # 默认指令开头 当@EventHandler中没有指定start时使用改配置 26 | DEFAULT_EVENT_NET_TIMEOUT: 300000 # 默认的上下文监听超时时间 5分钟 27 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ██████ ██████ ███ ███ ███████████ █████ 2 | ░░██████ ██████ ░░░ ░░░ ░░███░░░░░███ ░░███ 3 | ░███░█████░███ ████ ████████ ██████ ████ ░███ ░███ ██████ ██████ ███████ 4 | ░███░░███ ░███ ░░███ ░░███░░███ ░░░░░███ ░░███ ░██████████ ███░░███ ███░░███░░░███░ 5 | ░███ ░░░ ░███ ░███ ░███ ░░░ ███████ ░███ ░███░░░░░███░███ ░███░███ ░███ ░███ 6 | ░███ ░███ ░███ ░███ ███░░███ ░███ ░███ ░███░███ ░███░███ ░███ ░███ ███ 7 | █████ █████ █████ █████ ░░████████ █████ ███████████ ░░██████ ░░██████ ░░█████ 8 | ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░░ 9 | -------------------------------------------------------------------------------- /介绍.md: -------------------------------------------------------------------------------- 1 | 在开发基于指令和权限的机器人的过程中感觉mirai-console-plugin的开发方式很难受(没办法debug) 于是有了开发一个包含mirai-core项目的想法 2 | 然后觉得判断消息事件太麻烦逐渐发展成了一个框架 3 | 在这里感谢以下[xiangming-bot](https://github.com/Chuanwise/xiaoming-bot)这个项目 让这个框架多了一个粗糙的上下文交互的功能 4 | **先占个坑 项目这两天测试完就发布** 5 | 当然也可以去仓库自己拉下来complie一个体验版2333 6 | 7 | 仓库在这里->[miraiboot](https://github.com/diyigemt/mirai-boot) 8 | 9 | ## 介绍 10 | 11 | miraiboot是是对mirai框架的简单Java封装。 12 | 13 | 目的是为了让Java开发者更方便地开发基于指令响应的机器人。 14 | 15 | ## 特点 16 | 17 | 1. 不用关心mirai-core的代码 18 | 19 | miraiboot提供了一系列方便的工具类对mirai-core的核心功能依赖进行封装,如消息回复、语音、图片等本地文件的发送等,对于简单的qq机器人开发,Java开发者不需要在去接触kotlin代码,更适合于Java初级开发者。 20 | 21 | 2. 自带简单的消息过滤器和权限管理模块 22 | 23 | miraiboot提供了一系列的工具,可以方便地对消息事件进行过滤和权限管理。 24 | 25 | 权限管理基于SQLite且已经进行了封装,开发者无需考虑实现。 26 | 27 | 3. 注解驱动开发 28 | 29 | miraiboot所有的事件和异常处理都通过注解完成,开发者只需要对处理方法加上对应的注解,其余的都交由miraiboot进行管理,让开发者专注于功能的实现。 30 | 31 | ## 一些展示 32 | 33 | 权限功能: 34 | 35 | ![权限功能](https://lychee.diyigemt.net/uploads/small/0eb5b76858f250f09d07c7b3cd8accd5.jpg) 36 | 37 | ExceptionHandler: 38 | 39 | ![ExceptionHandler](https://lychee.diyigemt.net/uploads/small/193a0d32129274a882f414bf7529d91c.jpg) 40 | 41 | 上下文交互: 42 | 43 | ![1](https://lychee.diyigemt.net/uploads/small/6f7ced06aadf185ce6e09906c470c164.jpg) 44 | 45 | ![2](https://lychee.diyigemt.net/uploads/small/be25f01034ad8c57dffd1aed2e4f0486.jpg) 46 | 47 | ![3](https://lychee.diyigemt.net/uploads/small/9c1424689d52114302cefb637e758b8d.jpg) 48 | 49 | 仓库在这里->[miraiboot](https://github.com/diyigemt/mirai-boot) 50 | 51 | --------------------------------------------------------------------------------