MiraiForum

    • 注册
    • 登录
    • 搜索
    • 热门
    • 最新
    • 未解决
    • 标签
    • 群组
    • 友情链接
    1. 主页
    2. wssy001
    3. 帖子
    • 资料
    • 关注 0
    • 粉丝 0
    • 主题 5
    • 帖子 86
    • 最佳 9
    • 有争议的 0
    • 群组 1

    wssy001 发布的帖子

    • RE: Java + Springboot 2.X+ mirai最新RELEASE の采坑记录

      ——————————这是分隔符——————————
      第四天开发报告 (摸鱼记录):
      年初我就一直期待mirai对群文件的支持,正好我手上就有个业务需要远程传送文件。文件这块内容在官方文档中还未来得及更新,故我仅依靠着对代码中相关注释的理解进行开发,若有纰漏之处望不吝赐教。

      准备开始之前,必须打开 RemoteFile.kt 或在IDEA中 搜索 net.mamoe.mirai.utils.RemoteFile,请仔细阅读作者给出的注释,它能给你开发思路!!!

      关于群文件的操作,我只举例上传与下载,其余的可以翻阅源码,食用方法注释写得很详细。

      上文说过,我写了一个handler用于处理群消息,下文是一个栗子

      @CheckPermission(isAdminOnly = true)
      public void onMessage(@NotNull GroupMessageEvent event) {
          //发送人信息
          Member sender = event.getSender();
          //目标消息所在群
          Group group = event.getSource().getGroup();
          //消息文本
          String msg = event.getMessage().contentToString();
      }
      

      group.getFilesRoot()方法可以让我们定位到群文件的根目录“/”,亦是我们在客户端中点开群文件第一个显示的目录界面。

      resolve()方法可以将操作定位到我们需要前去的目录或所需要操作的文件,详见注释。

      我们可以这样遍历群文件,不过在键入group.getFilesRoot().listFilesIterator(false).var后回车,IDEA自动创建的变量的类型直接为Iterator,兴许是还没这么智能,建议手动补全为Iterator<RemoteFile>。善于挖掘源码的你也一定注意到了,listFilesIterator()方法支持懒加载。

      if (msg.contains("遍历群文件")) {
          if (!permission) return;
          Iterator<RemoteFile> iterator = group.getFilesRoot().listFilesIterator(false);
          List<RemoteFile> list = group.getFilesRoot().listFilesCollection();
      }
      

      RemoteFile还提供了俩方法 isFile()和isDirectory()用于判断目标类型。反正要么是文件,要么是文档,任君选择。

      getDownloadInfo()方法会返回一个叫DownloadInfo的bean,其中包含了该文件在群文件的路径,ID,下载链接等一系列信息,值得一提的是,它提供了md5和sha1,方便我们对下载的文件进行完整性校验。下文便是一个文件下载栗子,文件下载轮子我用的是hutool,GAV附上

      <dependency>
          <groupId>cn.hutool</groupId>
          <artifactId>hutool-http</artifactId>
          <version>5.5.1</version>
      </dependency>
      
      List<RemoteFile> list = group.getFilesRoot().listFilesCollection();
      //一系列的判断,校验
      String url = files.get(0).getDownloadInfo().getUrl();
      file = HttpUtil.downloadFileFromUrl(url, "temp");
      

      mirai更新2.6.3了,只需要更新一下版本号即可,GAV:

      <dependency>
          <groupId>net.mamoe</groupId>
          <artifactId>mirai-core-jvm</artifactId>
          <version>2.6.3</version>
      </dependency>
      

      然后就是文件上传,文件上传这块感觉和OSS(对象存储)很相似,不过暂时mirai还不能做到查看所给的“文件名”,自动创建不存在的文件夹。废话不多说,上代码

      if (msg.startsWith("文件上传")) {
          File file = new File("C:\\deviceInfo.json");
          RemoteFile remoteFile = group.getFilesRoot().resolve("/test").resolve(file.getName());
          remoteFile.uploadAndSend(file);
      }
      

      其实在文件上传这块 resolve()方法其实相当于是设置你上传的文件的“文件名”,如果给定的“文件名”包含一个路径(例 “\test\Test.java”),则自动将上传的Test.java放入根目录文件中的test文件夹。如果这个“test”文件夹不存在,mirai不会自动创建之,取而代之的是一个报错。
      我给出一个方案

      if (msg.startsWith("文件上传")) {
          File file = new File("C:\\deviceInfo.json");
      //  这是一个不存在的文件夹
          String path = "/fileTestDocument";
          RemoteFile resolve = group.getFilesRoot().resolve(path);
          if (!resolve.exists()) {
              resolve.mkdir();
          }
          resolve.resolve(file.getName()).uploadAndSend(file);
      }
      

      上述方案中,我选择exists()方法作为目标文件夹是否存在的判断方法,如果你的bot在这个业务中具有管理员权限,那可以直接调用mkdir()
      第四天补充完成,我当前的业务是以群文件为主。后续的话,新的消息我可能用不了多少,但是会逐步完善功能。
      ——————————这是分隔符——————————

      发布在 精华主题
      wssy001
      wssy001
    • RE: Java + Springboot 2.X+ mirai最新RELEASE の采坑记录

      写在开头:mirai 2.6.2出了,下文mirai版本为:2.6.2,同样的,滑动登录模块也更新了,这里一同贴出
      maven:

      <dependency>
          <groupId>net.mamoe</groupId>
          <artifactId>mirai-core-jvm</artifactId>
          <version>2.6.2</version>
      </dependency>
      <dependency>
          <groupId>net.mamoe</groupId>
          <artifactId>mirai-login-solver-selenium</artifactId>
          <version>1.0-dev-17</version>
      </dependency>
      

      ——————————这是分隔符——————————

      完成了IOC,就要用AOP来完成鉴权了,大概需要几步

      • 1:写一个注解
      • 2:写一个bean
      • 3:写一个切面

      记得先添加Spring AOP的依赖,最好是用AspectJ进行切面,毕竟基于动态代理的Spring AOP局限性太大!!!!
      GAV坐标:

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
      

      先写一个注解,我写的注解是可以加在类上和方法上的,用于鉴权。我在其他权限管理项目中可遇到过要求某用户具有A权限,但没有B权限的业务,故有了这俩。未来打算适配Spring Security

      @Target({ElementType.METHOD, ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface CheckPermission {
      
          String[] roles() default {};
      
          String[] nonRoles() default {};
      
          boolean isGroupOwnerOnly() default false;
      
          boolean isAdminOnly() default false;
      
          String description() default "QQRobot自定义权限校验注解";
      }
      

      然后就是切面

      @Aspect
      @Component
      @RequiredArgsConstructor
      @Slf4j
      public class CheckPermissionAspect {
      
          @Pointcut("@annotation(cyou.wssy001.qqrobot.annotation.CheckPermission)")
          public void annotatedMethods() {
          }
      
          @Pointcut("@within(cyou.wssy001.qqrobot.annotation.CheckPermission)")
          public void annotatedClasses() {
          }
          
      
          @Before("annotatedClasses() || annotatedMethods()")
          public Object checkPermission(JoinPoint point) {
              Object[] args = point.getArgs();
              if (args == null) throw new RuntimeException("无发送者!!!");
              //这个event中可以获取发送者的信息,以便下文鉴权
              GroupMessageEvent event = (GroupMessageEvent) args[1];
      
              MethodSignature signature = (MethodSignature) point.getSignature();
              CheckPermission methodAnnotation = signature.getMethod().getAnnotation(CheckPermission.class);
              Class<?> aClass = point.getSignature().getDeclaringType();
              CheckPermission classAnnotation = aClass.getAnnotation(CheckPermission.class);
              Permission permission = getCheckPermission(methodAnnotation, classAnnotation);
      
              //这里的checkA()方法和checkB()方法是具体的判断逻辑
              if (checkA() && checkB()) {
                  return point.getArgs();
              } else {
                  throw new RuntimeException("无权访问!");
              }
          }
      
          private Permission getCheckPermission(CheckPermission methodAnnotation, CheckPermission classAnnotation) {
              Permission permission = new Permission();
              CheckPermission check = methodAnnotation != null ? methodAnnotation : classAnnotation;
              if (check != null) {
                  permission.getRoles().addAll(Arrays.asList(check.roles()));
                  permission.getNonRoles().addAll(Arrays.asList(check.nonRoles()));
                  permission.setAdminOnly(check.isAdminOnly());
                  permission.setGroupOwnerOnly(check.isGroupOwnerOnly());
              }
              return permission;
          }
      }
      

      我写了个Bean用于存储注解包含的信息

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class Permission {
          //存放所需要的角色
          Set<String> roles;
      
          //存放所不需要的角色
          Set<String> nonRoles;
      
          //是否仅限于群主
          boolean isGroupOwnerOnly;
      
          //是否仅限于管理(这里考虑的是 管理+群主,毕竟群主具有最高权限)
          boolean isAdminOnly;
      }
      

      使用的方法也很简单,举个栗子:

      @Component
      @Slf4j
      @RequiredArgsConstructor
      public class GroupMessageHandler {
          private final AdminMapService adminMapService;
      
          @CheckPermission(isAdminOnly = true)
          public void onMessage(@NotNull GroupMessageEvent event) throws Exception {
              Member sender = event.getSender();
              Group group = event.getSource().getGroup();
      
              StringBuilder reply = new StringBuilder();
                                              ………………这是省略号………………
      

      这样一来 所有调用onMessage(@NotNull GroupMessageEvent event)的方法都会先判断调用者是不是管理员 or 群主。

      最后别忘启用切面

      @EnableAspectJAutoProxy(exposeProxy = true)
      

      第三天内容补完了,后续就要进入实际业务开发了,我会优先展示群文件的相关操作,主要手头上刚好有项工作需要和群文件打交道

      发布在 精华主题
      wssy001
      wssy001
    • RE: Java + Springboot 2.X+ mirai最新RELEASE の采坑记录

      开发第三天 (其实是对第二天的代码补充):

      先说说将bot加载至IOC,这个其实很简单,考虑到个别springboot新手,故贴一下代码 (这么好的水贴例子怎么能放过呢?)

      @Bean
      public Bot bot() {
          return BotFactory.INSTANCE.newBot(yourQQNumber, "password", new BotConfiguration() {
              {
                  fileBasedDeviceInfo("deviceInfo.json");
                  setProtocol(MiraiProtocol.ANDROID_PHONE);
              }
          });
      }
      

      如果需要设置事件监听,可以这样:

      @Bean
      public Bot bot() {
          Bot bot = BotFactory.INSTANCE.newBot(yourQQNumber, "password", new BotConfiguration() {
              {
                  fileBasedDeviceInfo("deviceInfo.json");
                  setProtocol(MiraiProtocol.ANDROID_PHONE);
              }
          });
          bot.getEventChannel().registerListenerHost(groupMessageHandler);       
          return bot;
      }
      

      这里我调用了一个handler来处理群消息,详见:wiki-->使用 @EventHandler 注解标注的方法监听事件。

      (P.S. Spring已经不推荐使用@Autowired注入,IDEA也明确给了一个warning,推荐使用构造器的方式注入,配合Lombok神器,构造器注入比前者更简单)

      然后再写了个QQRobotService,完成机器人登录。

      @PostConstruct
      public void init() {
          bot.login();
      }
      
      发布在 精华主题
      wssy001
      wssy001
    • RE: Java + Springboot 2.X+ mirai最新RELEASE の采坑记录

      别忘了在主POM加上滑动验证模块,

      <dependency>
            <groupId>net.mamoe</groupId>
            <artifactId>mirai-login-solver-selenium</artifactId>
            <version>1.0-dev-16</version>
       </dependency>
      

      貌似MAC环境下貌似无法自动化操作,但是WIN下OK,到时候把deviceInfo.json ctrl cv过去即可

      ———————————————————————————————————————————————————

      开发第二天:
      解决完项目创建,就要监听游戏中的事件了,逛了wiki-->Event发现如今的事件注册变成了

      bot.getEventChannel().registerListenerHost(groupMessageHandler);
      

      但如果想使用自定义鉴权注解,事件注册不能采取前一者(暂时还未能解决@EventHandler与自定义注解共同作用于同一方法,@EventHandler失效的问题),而是:

      bot.getEventChannel().subscribeAlways(GroupMessageEvent.class, groupMessageHandler::onMessage);
      

      而且要注意,注册完事件再执行

      bot.join();
      

      别问我是怎么知道了,我是不会告诉你我因为无法注册事件查了几小时的wiki,CSDN,etc....

      Spring最大的核心是AOP & IOC。实际应用中,难免少不了鉴权,AOP就能很好滴防止我们在handler中每个方法都写上一个if(!permission(...));对于IOC,可以将bot对象放入,然后controller中注入,直接调用bot对象,也可以写一个BotService,封装常用方法,凭君喜爱……
      ———————————————————————————————————————————————————

      发布在 精华主题
      wssy001
      wssy001
    • RE: Java + Springboot 2.X+ mirai最新RELEASE の采坑记录

      边写代码 边采坑 获得了解决方案就更新

      发布在 精华主题
      wssy001
      wssy001
    • Java + Springboot 2.X+ mirai最新RELEASE の采坑记录

      环境:
      SpringBoot 2.2.11.RELEASE
      Mirai-core-jvm 2.6.0(时刻试水最新release版)
      Open JDK 11

      Spring Cloud版
      开发第一天:
      新建了Spring Boot项目,鉴于前几次mirai的开发,我轻车熟路地加上了这个

      <dependency>
              <groupId>net.mamoe</groupId>
              <artifactId>mirai-core-all</artifactId>
              <version>2.6.0</version>
      </dependency>
      

      当然成功翻车。
      所以说 拥有一个良好的看wiki-->使用 Maven习惯是很重要的!

      下载了最新的依赖后,鉴于是springboot项目,还需要做的一件事是查看mirai依赖的kotlin版本,可以直接去wiki找,也可以自己点开依赖挖

      别忘了在主POM加上Mirai所依赖的kotlin版本

      <properties>
          <kotlin.version>1.4.32</kotlin.version>
      </properties>
      
      发布在 精华主题
      wssy001
      wssy001
    • 1
    • 2
    • 3
    • 4
    • 5
    • 5 / 5