MiraiForum

    • 注册
    • 登录
    • 搜索
    • 热门
    • 最新
    • 未解决
    • 标签
    • 群组
    • 友情链接

    获取回复消息(QuoteReply)指向原文的实现思路

    技术交流板块
    3
    6
    751
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • MrXiaoM
      MrXiaoM 童心未泯 最后由 MrXiaoM 编辑

      总所周知,腾讯服务器给你返回的 QuoteReply 的源消息并不是富文本消息,也就是说,图片会“退化”为[图片]等等。如果我们想做回复消息搜图之类的功能,这给我们带来了比较多的麻烦。

      但是,我们可以取到源消息的 MessageSource,只要你本地存了聊天记录,就能够通过 MessageSource 获取相应消息。按照这个思路,我们只要跟QQ客户端一样,把消息存到本地,需要时获取即可。

      具体实现

      本帖中将使用 Mirai Hibernate Plugin 插件储存聊天记录,演示获取回复消息原文的方法。

      引用依赖

      // build.gradle(.kts)
      
      repositories {
          mavenCentral()
      }
      
      dependencies {
          compileOnly("xyz.cssxsh.mirai:mirai-hibernate-plugin:依赖版本")
      }
      
      // hibernate 6 和 HikariCP 5 需要 jdk11
      mirai {
          jvmTarget = JavaVersion.VERSION_11
      }
      
      //author("MrXiaoM")
      // 在主类插件信息处声明插件依赖关系
      dependsOn("xyz.cssxsh.mirai.plugin.mirai-hibernate-plugin", false)
      

      Kotlin

      val QuoteReply.originalMessageFromLocal: MessageChain
          get() = MiraiHibernateRecorder[source].firstOrNull()?.toMessageChain() ?: source.originalMessage
      
      // 使用示例
      @EventHandler
      fun GroupMessageEvent.listen() {
          // 检测回复消息
          message[QuoteReply.Key]?.run { 
              val original = originalMessageFromLocal
              // do sth.
          }
      }
      

      Java

          public static MessageChain getOriginalMessageFromLocal(QuoteReply quote) {
              Optional<MessageRecord> record = MiraiHibernateRecorder.INSTANCE.get(quote.getSource()).stream().findFirst();
              return record.map(MessageRecord::toMessageChain).orElseGet(() -> quote.getSource().getOriginalMessage());
          }
          // 使用示例
          @EventHandler
          public void onGroupMessage(GroupMessageEvent event) {
              // 检测回复消息
              QuoteReply quote = event.getMessage().get(QuoteReply.Key);
              if (quote != null) {
                  MessageChain original = getOriginalMessageFromLocal(quote);
                  // do sth.
              }
          }
      
      hisou 1 条回复 最后回复 回复 引用 1
      • Referenced by  Z zsd123ss 
      • N
        nannanness 最后由 编辑

        您好,请教一下,我启动后MiraiHibernateRecorder.INSTANCE是null,然后我看readme里面说需要手动对 xyz.cssxsh.mirai.hibernate.factory 进行初始化,和对 MiraiHibernateRecorder 进行注册,但是我没找到factory,也不知道怎么初始化

        MrXiaoM 1 条回复 最后回复 回复 引用 0
        • MrXiaoM
          MrXiaoM 童心未泯 @nannanness 最后由 编辑

          @nannanness 但是 MiraiHibernateRecorder 是单例类,它的实例不可能为 null。
          说的手动注册,是指需要在 EventChannel 注册它为 listenerHost

          1 条回复 最后回复 回复 引用 0
          • N
            nannanness 最后由 编辑

            你好,我注册了MiraiHibernateRecorder,但是调用的时候报错了,好像是实例化的问题,能帮忙看下吗
            Exception in thread "DefaultDispatcher-worker-2" java.lang.RuntimeException: Exception while trying to handle coroutine exception
            at kotlinx.coroutines.CoroutineExceptionHandlerKt.handlerException(CoroutineExceptionHandler.kt:38)
            at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:29)
            at kotlinx.coroutines.StandaloneCoroutine.handleJobException(Builders.common.kt:196)
            at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:229)
            at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:906)
            at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:863)
            at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:828)
            at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
            at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
            at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
            Suppressed: kotlin.UninitializedPropertyAccessException: lateinit property factory has not been initialized
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateUtilsKt.getFactory(MiraiHibernateUtils.kt:74)
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder.merge(MiraiHibernateRecorder.kt:30)
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder.access$merge(MiraiHibernateRecorder.kt:28)
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder$record$11.invokeSuspend(MiraiHibernateRecorder.kt:152)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
            ... 5 more
            Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [net.mamoe.mirai.event.SimpleListenerHost$special$$inlined$CoroutineExceptionHandler$1@58551237, StandaloneCoroutine{Cancelling}@6e8d0dfb, Dispatchers.Default]
            Caused by: java.lang.NoClassDefFoundError: jakarta/persistence/PersistenceException
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder.handleException(MiraiHibernateRecorder.kt:545)
            at net.mamoe.mirai.event.SimpleListenerHost$special$$inlined$CoroutineExceptionHandler$1.handleException(CoroutineExceptionHandler.kt:111)
            at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:25)
            ... 12 more
            Caused by: java.lang.ClassNotFoundException: jakarta.persistence.PersistenceException
            at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
            at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
            at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
            ... 15 more
            Exception in thread "DefaultDispatcher-worker-7" java.lang.RuntimeException: Exception while trying to handle coroutine exception
            at kotlinx.coroutines.CoroutineExceptionHandlerKt.handlerException(CoroutineExceptionHandler.kt:38)
            at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:29)
            at kotlinx.coroutines.StandaloneCoroutine.handleJobException(Builders.common.kt:196)
            at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:229)
            at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:906)
            at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:863)
            at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:828)
            at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
            at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
            at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
            at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
            Suppressed: kotlin.UninitializedPropertyAccessException: lateinit property factory has not been initialized
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateUtilsKt.getFactory(MiraiHibernateUtils.kt:74)
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder.merge(MiraiHibernateRecorder.kt:30)
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder.access$merge(MiraiHibernateRecorder.kt:28)
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder$record$1.invokeSuspend(MiraiHibernateRecorder.kt:44)
            at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
            ... 5 more
            Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [net.mamoe.mirai.event.SimpleListenerHost$special$$inlined$CoroutineExceptionHandler$1@58551237, StandaloneCoroutine{Cancelling}@2e304ce3, Dispatchers.Default]
            Caused by: java.lang.NoClassDefFoundError: jakarta/persistence/PersistenceException
            at xyz.cssxsh.mirai.hibernate.MiraiHibernateRecorder.handleException(MiraiHibernateRecorder.kt:545)
            at net.mamoe.mirai.event.SimpleListenerHost$special$$inlined$CoroutineExceptionHandler$1.handleException(CoroutineExceptionHandler.kt:111)
            at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:25)
            ... 12 more
            Caused by: java.lang.ClassNotFoundException: jakarta.persistence.PersistenceException
            ... 15 more
            2023-11-28 19:43:16.075 [SpringApplicationShutdownHook] INFO o.s.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'

            MrXiaoM 1 条回复 最后回复 回复 引用 0
            • MrXiaoM
              MrXiaoM 童心未泯 @nannanness 最后由 编辑

              @nannanness

              Caused by: java.lang.ClassNotFoundException: jakarta.persistence.PersistenceException
              

              明显运行时环境缺东西了

              1 条回复 最后回复 回复 引用 0
              • hisou
                hisou @MrXiaoM 最后由 编辑

                @MrXiaoM 在 获取回复消息(QuoteReplay)指向原文的实现思路 中说:

                return record.map(MessageRecord::toMessageChain).orElseGet(() -> quote.getSource().getOriginalMessage());

                测了多次,疑似发现 当原消息所含图片的isEmoji=true时,有这几种情况:

                1. 用户A在PC端发送图片1,用户A在PC端引用回复图片1且触发bot指令,后台还是[图片]而不是富文本

                2. 用户A在PC端发送图片1,用户A在手机端引用回复图片1且触发bot指令,后台能获得富文本

                3. 另一用户B发送图片1(设备任意),用户A在PC端引用回复图片1且触发bot指令,后台能获得富文本

                主要就是1.那个用户自己引用回复自己的图没法解析出来就挺不理解的……应该有八九成的复现率,剩下的例外概率自己也搞不清楚了……
                Mirai Hibernate Plugin 那边还没有研究明白没去看实际聊天记录数据库里存的是什么样子

                1 条回复 最后回复 回复 引用 0
                • 1 / 1
                • First post
                  Last post
                Powered by Mamoe Technologies & NodeBB | 友情链接 | 服务监控 | Contact