Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/dev' into maven-central

# Conflicts:
#	.github/workflows/bintray.yml
#	.github/workflows/release.yml
Karlatemp 5 rokov pred
rodič
commit
32ef89add1
32 zmenil súbory, kde vykonal 285 pridanie a 187 odobranie
  1. 6 6
      .github/workflows/build.yml
  2. 8 8
      .github/workflows/release.yml
  3. 7 41
      binary-compatibility-validator/api/binary-compatibility-validator.api
  4. 11 1
      build.gradle.kts
  5. 1 1
      buildSrc/src/main/kotlin/Versions.kt
  6. 4 5
      docs/README.md
  7. 1 3
      mirai-core-api/src/commonMain/kotlin/Bot.kt
  8. 1 14
      mirai-core-api/src/commonMain/kotlin/IMirai.kt
  9. 1 16
      mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt
  10. 2 9
      mirai-core-api/src/commonMain/kotlin/contact/Contact.kt
  11. 1 3
      mirai-core-api/src/commonMain/kotlin/contact/Friend.kt
  12. 1 5
      mirai-core-api/src/commonMain/kotlin/contact/Group.kt
  13. 1 1
      mirai-core-api/src/commonMain/kotlin/contact/Member.kt
  14. 1 4
      mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt
  15. 1 3
      mirai-core-api/src/commonMain/kotlin/contact/Stranger.kt
  16. 1 3
      mirai-core-api/src/commonMain/kotlin/contact/User.kt
  17. 1 3
      mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt
  18. 3 0
      mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt
  19. 7 1
      mirai-core-api/src/commonMain/kotlin/message/data/RichMessageOrigin.kt
  20. 10 4
      mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt
  21. 5 2
      mirai-core/src/commonMain/kotlin/AbstractBot.kt
  22. 5 1
      mirai-core/src/commonMain/kotlin/QQAndroidBot.kt
  23. 3 0
      mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt
  24. 1 1
      mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt
  25. 34 28
      mirai-core/src/commonMain/kotlin/message/lightApp.kt
  26. 1 1
      mirai-core/src/commonMain/kotlin/network/ContactListCache.kt
  27. 2 5
      mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt
  28. 120 0
      mirai-core/src/commonMain/kotlin/network/handler/BdhSessionSyncer.kt
  29. 9 13
      mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt
  30. 1 1
      mirai-core/src/commonMain/kotlin/network/highway/Highway.kt
  31. 4 1
      mirai-core/src/commonMain/kotlin/network/keys.kt
  32. 31 3
      mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt

+ 6 - 6
.github/workflows/build.yml

@@ -19,13 +19,13 @@ jobs:
         run: chmod -R 777 *
 
       - name: Init gradle project
-        run: ./gradlew clean --info --stacktrace
+        run: ./gradlew clean --scan
 
       - name: Build mirai-core series
-        run: ./gradlew assemble --info --stacktrace
+        run: ./gradlew assemble --scan
 
       - name: mirai-core Tests
-        run: ./gradlew check --info --stacktrace
+        run: ./gradlew check --scan
 
   build-all:
     runs-on: ubuntu-latest
@@ -45,10 +45,10 @@ jobs:
         run: chmod -R 777 *
 
       - name: Init gradle project
-        run: ./gradlew clean --info --stacktrace
+        run: ./gradlew clean --scan
 
       - name: Build all
-        run: ./gradlew assemble --info --stacktrace
+        run: ./gradlew assemble --scan
 
       - name: All Tests
-        run: ./gradlew check --info --stacktrace
+        run: ./gradlew check --scan

+ 8 - 8
.github/workflows/release.yml

@@ -51,7 +51,7 @@ jobs:
           BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
 
       - name: Init gradle project
-        run: ./gradlew clean --info
+        run: ./gradlew clean --scan
 
       - name: Check keys
         run: >
@@ -68,25 +68,25 @@ jobs:
       - name: fillBuildConstants
         run: >
           ./gradlew
-          fillBuildConstants --info --stacktrace
+          fillBuildConstants --scan
 
       - name: Assemble
-        run: ./gradlew assemble --info --stacktrace
+        run: ./gradlew assemble --scan
 
       - name: Check
-        run: ./gradlew check --info --stacktrace
+        run: ./gradlew check --scan
 
       - name: Gradle :mirai-core-utils:publish
         run: >
-          ./gradlew :mirai-core-utils:publish --info --stacktrace
+          ./gradlew :mirai-core-utils:publish --scan
 
       - name: Gradle :mirai-core-api:publish
         run: >
-          ./gradlew :mirai-core-api:publish --info --stacktrace
+          ./gradlew :mirai-core-api:publish --scan
 
       - name: Gradle :mirai-core:publish
         run: >
-          ./gradlew :mirai-core:publish --info --stacktrace
+          ./gradlew :mirai-core:publish --scan
 
       - name: Gradle :mirai-core-all:publish
         run: >
@@ -115,7 +115,7 @@ jobs:
       - name: Publish Gradle plugin
         run: >
           ./gradlew
-          :mirai-console-gradle:publishPlugins --info --stacktrace
+          :mirai-console-gradle:publishPlugins --scan
           -Dgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} -Pgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }}
           -Dgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} -Pgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }}
 

+ 7 - 41
binary-compatibility-validator/api/binary-compatibility-validator.api

@@ -89,25 +89,16 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
 	public fun calculateGroupUinByGroupCode (J)J
 	public abstract fun constructMessageSource (JLnet/mamoe/mirai/message/data/MessageSourceKind;JJ[II[ILnet/mamoe/mirai/message/data/MessageChain;)Lnet/mamoe/mirai/message/data/OfflineMessageSource;
 	public abstract fun createImage (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
-	public synthetic fun deleteGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)Lkotlin/Unit;
-	public fun deleteGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)V
 	public fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Ljava/util/List;
 	public abstract fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/MessageChain;
 	public abstract fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public abstract fun getBotFactory ()Lnet/mamoe/mirai/BotFactory;
 	public abstract fun getFileCacheStrategy ()Lnet/mamoe/mirai/utils/FileCacheStrategy;
-	public fun getGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)Lnet/mamoe/mirai/data/GroupAnnouncement;
-	public fun getGroupVoiceDownloadUrl (Lnet/mamoe/mirai/Bot;[BJJ)Ljava/lang/String;
 	public abstract fun getHttp ()Lio/ktor/client/HttpClient;
 	public fun getOnlineOtherClientsList (Lnet/mamoe/mirai/Bot;Z)Ljava/util/List;
 	public abstract fun getOnlineOtherClientsList (Lnet/mamoe/mirai/Bot;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public static synthetic fun getOnlineOtherClientsList$default (Lnet/mamoe/mirai/IMirai;Lnet/mamoe/mirai/Bot;ZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
-	public fun getRawGroupActiveData (Lnet/mamoe/mirai/Bot;JI)Lnet/mamoe/mirai/data/GroupActiveData;
-	public fun getRawGroupAnnouncements (Lnet/mamoe/mirai/Bot;JII)Lnet/mamoe/mirai/data/GroupAnnouncementList;
-	public fun getRawGroupHonorListData (Lnet/mamoe/mirai/Bot;JLnet/mamoe/mirai/data/GroupHonorType;)Lnet/mamoe/mirai/data/GroupHonorListData;
-	public fun getRawGroupList (Lnet/mamoe/mirai/Bot;)Lkotlin/sequences/Sequence;
-	public fun getRawGroupMemberList (Lnet/mamoe/mirai/Bot;JJJ)Lkotlin/sequences/Sequence;
 	public fun getUin (Lnet/mamoe/mirai/contact/ContactOrBot;)J
 	public synthetic fun ignoreInvitedJoinGroupRequest (Lnet/mamoe/mirai/event/events/BotInvitedJoinGroupRequestEvent;)Lkotlin/Unit;
 	public fun ignoreInvitedJoinGroupRequest (Lnet/mamoe/mirai/event/events/BotInvitedJoinGroupRequestEvent;)V
@@ -116,15 +107,10 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
 	public fun ignoreMemberJoinRequest (Lnet/mamoe/mirai/event/events/MemberJoinRequestEvent;Z)V
 	public abstract fun ignoreMemberJoinRequest (Lnet/mamoe/mirai/event/events/MemberJoinRequestEvent;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public static synthetic fun ignoreMemberJoinRequest$default (Lnet/mamoe/mirai/IMirai;Lnet/mamoe/mirai/event/events/MemberJoinRequestEvent;ZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
-	public synthetic fun muteAnonymousMember (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Ljava/lang/String;JI)Lkotlin/Unit;
-	public fun muteAnonymousMember (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Ljava/lang/String;JI)V
 	public fun queryImageUrl (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/data/Image;)Ljava/lang/String;
 	public abstract fun queryImageUrl (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/data/Image;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun queryProfile (Lnet/mamoe/mirai/Bot;J)Lnet/mamoe/mirai/data/UserProfile;
 	public abstract fun queryProfile (Lnet/mamoe/mirai/Bot;JLkotlin/coroutines/Continuation;)Ljava/lang/Object;
-	public fun recallFriendMessageRaw (Lnet/mamoe/mirai/Bot;J[I[II)Z
-	public fun recallGroupMessageRaw (Lnet/mamoe/mirai/Bot;J[I[I)Z
-	public fun recallGroupTempMessageRaw (Lnet/mamoe/mirai/Bot;JJ[I[II)Z
 	public synthetic fun recallMessage (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/data/MessageSource;)Lkotlin/Unit;
 	public fun recallMessage (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/data/MessageSource;)V
 	public abstract fun recallMessage (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -136,17 +122,10 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
 	public fun rejectNewFriendRequest (Lnet/mamoe/mirai/event/events/NewFriendRequestEvent;Z)V
 	public abstract fun rejectNewFriendRequest (Lnet/mamoe/mirai/event/events/NewFriendRequestEvent;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public static synthetic fun rejectNewFriendRequest$default (Lnet/mamoe/mirai/IMirai;Lnet/mamoe/mirai/event/events/NewFriendRequestEvent;ZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
-	public fun sendGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLnet/mamoe/mirai/data/GroupAnnouncement;)Ljava/lang/String;
 	public fun sendNudge (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/action/Nudge;Lnet/mamoe/mirai/contact/Contact;)Z
 	public abstract fun sendNudge (Lnet/mamoe/mirai/Bot;Lnet/mamoe/mirai/message/action/Nudge;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public abstract fun setFileCacheStrategy (Lnet/mamoe/mirai/utils/FileCacheStrategy;)V
 	public abstract fun setHttp (Lio/ktor/client/HttpClient;)V
-	public synthetic fun solveBotInvitedJoinGroupRequestEvent (Lnet/mamoe/mirai/Bot;JJJZ)Lkotlin/Unit;
-	public fun solveBotInvitedJoinGroupRequestEvent (Lnet/mamoe/mirai/Bot;JJJZ)V
-	public synthetic fun solveMemberJoinRequestEvent (Lnet/mamoe/mirai/Bot;JJLjava/lang/String;JLjava/lang/Boolean;ZLjava/lang/String;)Lkotlin/Unit;
-	public fun solveMemberJoinRequestEvent (Lnet/mamoe/mirai/Bot;JJLjava/lang/String;JLjava/lang/Boolean;ZLjava/lang/String;)V
-	public synthetic fun solveNewFriendRequestEvent (Lnet/mamoe/mirai/Bot;JJLjava/lang/String;ZZ)Lkotlin/Unit;
-	public fun solveNewFriendRequestEvent (Lnet/mamoe/mirai/Bot;JJLjava/lang/String;ZZ)V
 }
 
 public abstract interface annotation class net/mamoe/mirai/LowLevelApi : java/lang/annotation/Annotation {
@@ -183,6 +162,8 @@ public abstract interface class net/mamoe/mirai/LowLevelApiAccessor {
 	public abstract fun recallGroupMessageRaw (Lnet/mamoe/mirai/Bot;J[I[ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun recallGroupTempMessageRaw (Lnet/mamoe/mirai/Bot;JJ[I[II)Z
 	public abstract fun recallGroupTempMessageRaw (Lnet/mamoe/mirai/Bot;JJ[I[IILkotlin/coroutines/Continuation;)Ljava/lang/Object;
+	public synthetic fun refreshKeys (Lnet/mamoe/mirai/Bot;)Lkotlin/Unit;
+	public fun refreshKeys (Lnet/mamoe/mirai/Bot;)V
 	public abstract fun refreshKeys (Lnet/mamoe/mirai/Bot;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun sendGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLnet/mamoe/mirai/data/GroupAnnouncement;)Ljava/lang/String;
 	public abstract fun sendGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLnet/mamoe/mirai/data/GroupAnnouncement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -205,12 +186,9 @@ public final class net/mamoe/mirai/Mirai {
 
 public abstract interface class net/mamoe/mirai/contact/AnonymousMember : net/mamoe/mirai/contact/Member {
 	public abstract fun getAnonymousId ()Ljava/lang/String;
-	public synthetic fun mute (I)Lkotlin/Unit;
-	public fun mute (I)V
 	public fun nudge ()Lnet/mamoe/mirai/message/action/MemberNudge;
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/Nudge;
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/UserNudge;
-	public fun queryProfile ()Lnet/mamoe/mirai/data/UserProfile;
 	public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -373,12 +351,10 @@ public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/corouti
 	public fun nudge ()Lnet/mamoe/mirai/message/action/FriendNudge;
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/Nudge;
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/UserNudge;
-	public fun queryProfile ()Lnet/mamoe/mirai/data/UserProfile;
 	public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public abstract fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image;
 }
 
 public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/Contact {
@@ -405,7 +381,6 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin
 	public fun setEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;)Z
 	public abstract fun setEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public abstract fun setName (Ljava/lang/String;)V
-	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image;
 	public fun uploadVoice (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Voice;
 	public abstract fun uploadVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 }
@@ -441,10 +416,10 @@ public abstract interface class net/mamoe/mirai/contact/Member : net/mamoe/mirai
 	public fun mute (I)V
 	public abstract fun mute (ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public abstract fun nudge ()Lnet/mamoe/mirai/message/action/MemberNudge;
-	public fun queryProfile ()Lnet/mamoe/mirai/data/UserProfile;
+	public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public abstract fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public abstract fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image;
 }
 
 public final class net/mamoe/mirai/contact/MemberKt {
@@ -490,12 +465,9 @@ public abstract interface class net/mamoe/mirai/contact/NormalMember : net/mamoe
 	public synthetic fun kick (Ljava/lang/String;)Lkotlin/Unit;
 	public fun kick (Ljava/lang/String;)V
 	public abstract fun kick (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-	public synthetic fun mute (I)Lkotlin/Unit;
-	public fun mute (I)V
 	public fun nudge ()Lnet/mamoe/mirai/message/action/MemberNudge;
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/Nudge;
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/UserNudge;
-	public fun queryProfile ()Lnet/mamoe/mirai/data/UserProfile;
 	public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt;
@@ -505,7 +477,6 @@ public abstract interface class net/mamoe/mirai/contact/NormalMember : net/mamoe
 	public synthetic fun unmute ()Lkotlin/Unit;
 	public fun unmute ()V
 	public abstract fun unmute (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image;
 }
 
 public final class net/mamoe/mirai/contact/NormalMemberKt {
@@ -518,7 +489,6 @@ public abstract interface class net/mamoe/mirai/contact/OtherClient : net/mamoe/
 	public abstract fun getBot ()Lnet/mamoe/mirai/Bot;
 	public fun getId ()J
 	public abstract fun getInfo ()Lnet/mamoe/mirai/contact/OtherClientInfo;
-	public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 }
@@ -575,12 +545,10 @@ public abstract interface class net/mamoe/mirai/contact/Stranger : kotlinx/corou
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/Nudge;
 	public fun nudge ()Lnet/mamoe/mirai/message/action/StrangerNudge;
 	public synthetic fun nudge ()Lnet/mamoe/mirai/message/action/UserNudge;
-	public fun queryProfile ()Lnet/mamoe/mirai/data/UserProfile;
 	public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public abstract fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image;
 }
 
 public final class net/mamoe/mirai/contact/StrangerKt {
@@ -589,10 +557,6 @@ public final class net/mamoe/mirai/contact/StrangerKt {
 }
 
 public abstract interface class net/mamoe/mirai/contact/TempUser : net/mamoe/mirai/contact/User {
-	public fun queryProfile ()Lnet/mamoe/mirai/data/UserProfile;
-	public fun sendMessage (Ljava/lang/String;)Lnet/mamoe/mirai/message/MessageReceipt;
-	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt;
-	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image;
 }
 
 public abstract interface class net/mamoe/mirai/contact/User : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/Contact, net/mamoe/mirai/contact/UserOrBot {
@@ -606,7 +570,6 @@ public abstract interface class net/mamoe/mirai/contact/User : kotlinx/coroutine
 	public fun sendMessage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
 	public fun sendMessage (Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/MessageReceipt;
 	public abstract fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
-	public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image;
 }
 
 public final class net/mamoe/mirai/contact/UserKt {
@@ -5087,6 +5050,7 @@ public final class net/mamoe/mirai/message/data/RichMessage$Key : net/mamoe/mira
 public final class net/mamoe/mirai/message/data/RichMessageKind : java/lang/Enum {
 	public static final field FORWARD Lnet/mamoe/mirai/message/data/RichMessageKind;
 	public static final field LONG Lnet/mamoe/mirai/message/data/RichMessageKind;
+	public static final field MUSIC_SHARE Lnet/mamoe/mirai/message/data/RichMessageKind;
 	public static fun valueOf (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/RichMessageKind;
 	public static fun values ()[Lnet/mamoe/mirai/message/data/RichMessageKind;
 }
@@ -5401,6 +5365,7 @@ public class net/mamoe/mirai/utils/BotConfiguration {
 	public static synthetic fun fileBasedDeviceInfo$default (Lnet/mamoe/mirai/utils/BotConfiguration;Ljava/lang/String;ILjava/lang/Object;)V
 	public final fun getAutoReconnectOnForceOffline ()Z
 	public final fun getBotLoggerSupplier ()Lkotlin/jvm/functions/Function1;
+	public final fun getCacheDirSupplier ()Lkotlin/jvm/functions/Function0;
 	public static final fun getDefault ()Lnet/mamoe/mirai/utils/BotConfiguration;
 	public final fun getDeviceInfo ()Lkotlin/jvm/functions/Function1;
 	public final fun getFirstReconnectDelayMillis ()J
@@ -5442,6 +5407,7 @@ public class net/mamoe/mirai/utils/BotConfiguration {
 	public static synthetic fun redirectNetworkLogToFile$default (Lnet/mamoe/mirai/utils/BotConfiguration;Ljava/io/File;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
 	public final fun setAutoReconnectOnForceOffline (Z)V
 	public final fun setBotLoggerSupplier (Lkotlin/jvm/functions/Function1;)V
+	public final fun setCacheDirSupplier (Lkotlin/jvm/functions/Function0;)V
 	public final fun setDeviceInfo (Lkotlin/jvm/functions/Function1;)V
 	public final fun setFirstReconnectDelayMillis (J)V
 	public final fun setFriendListCache (Lnet/mamoe/mirai/utils/BotConfiguration$FriendListCache;)V

+ 11 - 1
build.gradle.kts

@@ -140,11 +140,21 @@ subprojects {
 tasks.register("cleanExceptIntellij") {
     group = "build"
     allprojects.forEach { proj ->
-        if (proj.name != "mirai-console-intellij")
+        if (proj.name != "mirai-console-intellij") {
+
+            // Type mismatch
+            // proj.tasks.findByName("clean")?.let(::dependsOn)
+
             proj.tasks.findByName("clean")?.let { dependsOn(it) }
+        }
     }
 }
 
+extensions.findByName("buildScan")?.withGroovyBuilder {
+    setProperty("termsOfServiceUrl", "https://gradle.com/terms-of-service")
+    setProperty("termsOfServiceAgree", "yes")
+}
+
 fun Project.useIr() {
     kotlinCompilations?.forEach { kotlinCompilation ->
         kotlinCompilation.kotlinOptions.freeCompilerArgs += "-Xuse-ir"

+ 1 - 1
buildSrc/src/main/kotlin/Versions.kt

@@ -32,7 +32,7 @@ object Versions {
     const val io = "0.1.16"
     const val coroutinesIo = "0.1.16"
 
-    const val blockingBridge = "1.7.4"
+    const val blockingBridge = "1.10.0"
 
     const val androidGradlePlugin = "3.5.3"
 

+ 4 - 5
docs/README.md

@@ -22,7 +22,6 @@
 [NoneBot]: https://github.com/nonebot/nonebot2
 [RedBeanN/node-mirai]: https://github.com/RedBeanN/node-mirai
 [Logiase/gomirai]: https://github.com/Logiase/gomirai
-[StageGuard/mirai-rhinojs-sdk]: https://github.com/StageGuard/mirai-rhinojs-sdk
 [cyanray/mirai-cpp]: https://github.com/cyanray/mirai-cpp
 [Chlorie/miraipp]: https://github.com/Chlorie/miraipp-template
 [Executor-Cheng/mirai-CSharp]: https://github.com/Executor-Cheng/mirai-CSharp
@@ -36,29 +35,29 @@
 
 [Rhino]: https://github.com/mozilla/rhino
 [OneBot]: https://github.com/howmanybots/onebot
-[Nambers/MiraiCP]: https://github.com/Nambers/MiraiCP
 
 | 技术                | 实现                          | 维护者及项目地址                               |
 |:-------------------|:-----------------------------|:--------------------------------------------|
 | ***Mirai Http***   | Mirai 标准                    | [mamoe/mirai-api-http]                      |
 | *OneBot Http*      | [OneBot] 标准                 | [yyuueexxiinngg/onebot-kotlin]              |
-| `C++`              | JNI                           | [Nambers/MiraiCP]
 | `Kotlin Scripting` | JVM                          | [iTXTech/mirai-kts]                         |
 | `Python`           | *Mirai Http*                 | [Graia Framework][GraiaProject/Application] |
 | `Python`           | *Mirai Http* / *OneBot Http* | [NoneBot]                                   |
+| `C++`              | JNI                          | [Nambers/MiraiCP]                           |
 | `C++`              | *Mirai Http*                 | [cyanray/mirai-cpp]                         |
 | `C++`              | *Mirai Http*                 | [Chlorie/miraipp]                           |
 | `C#`               | *Mirai Http*                 | [Executor-Cheng/mirai-CSharp]               |
+| `C#`               | *Mirai Http*                 | [Hyperai][theGravityLab/ProjHyperai]        |
 | `Rust`             | *Mirai Http*                 | [HoshinoTented/mirai-rs]                    |
 | `JavaScript`       | [Rhino] / JVM                | [iTXTech/mirai-js]                          |
 | `JavaScript`       | Node.js / *Mirai Http*       | [RedBeanN/node-mirai]                       |
 | `JavaScript`       | TypeScript / *Mirai Http*    | [YunYouJun/mirai-ts]                        |
-| `JavaScript`       | Node.js / *Mirai Http*    | [drinkal/Mirai-js]                        |
-| `.Net/C#`          | *Mirai Http*                 | [Hyperai][theGravityLab/ProjHyperai]        |
+| `JavaScript`       | Node.js / *Mirai Http*       | [drinkal/Mirai-js]                          |
 | `Go`               | *Mirai Http*                 | [Logiase/gomirai]                           |
 | `易语言`            | *Mirai Http*                 | [only52607/e-mirai]                         |
 | *酷 Q DLL 插件*     | JNI                          | [iTXTech/mirai-native]                      |
 
+> 排名不分先后  
 > *想在这里添加你的项目?欢迎[提交 PR](https://github.com/mamoe/mirai/edit/dev/docs/README.md)。*
 
 特别地,有一些 SDK 直接基于 mirai-core 开发,不需要 [`mirai-console`]:

+ 1 - 3
mirai-core-api/src/commonMain/kotlin/Bot.kt

@@ -43,6 +43,7 @@ public suspend inline fun <B : Bot> B.alsoLogin(): B = also { login() }
  *
  * @see BotFactory 构造 [Bot] 的工厂, [Bot] 唯一的构造方式.
  */
+@JvmBlockingBridge
 public interface Bot : CoroutineScope, ContactOrBot, UserOrBot {
     /**
      * Bot 配置
@@ -161,7 +162,6 @@ public interface Bot : CoroutineScope, ContactOrBot, UserOrBot {
      * @throws LoginFailedException 正常登录失败时抛出
      * @see alsoLogin `.apply { login() }` 捷径
      */
-    @JvmBlockingBridge
     public suspend fun login()
 
     /**
@@ -232,7 +232,6 @@ public interface Bot : CoroutineScope, ContactOrBot, UserOrBot {
      * 挂起协程直到 [Bot] 协程被关闭 ([Bot.close]).
      * 即使 [Bot] 离线, 也会等待直到协程关闭.
      */
-    @JvmBlockingBridge
     public suspend fun join(): Unit = supervisorJob.join()
 
 
@@ -243,7 +242,6 @@ public interface Bot : CoroutineScope, ContactOrBot, UserOrBot {
      *
      * @param cause 原因. 为 null 时视为正常关闭, 非 null 时视为异常关闭
      */
-    @JvmBlockingBridge
     public suspend fun closeAndJoin(cause: Throwable? = null) {
         close(cause)
         join()

+ 1 - 14
mirai-core-api/src/commonMain/kotlin/IMirai.kt

@@ -41,6 +41,7 @@ public val Mirai: IMirai by lazy { findMiraiInstance() }
  *
  * @see Mirai 获取实例
  */
+@JvmBlockingBridge
 public interface IMirai : LowLevelApiAccessor {
     /**
      * 请优先使用 [BotFactory.INSTANCE]
@@ -121,13 +122,11 @@ public interface IMirai : LowLevelApiAccessor {
      * @see IMirai.recallMessage (扩展函数) 接受参数 [MessageChain]
      * @see MessageSource.recall 撤回消息扩展
      */
-    @JvmBlockingBridge
     public suspend fun recallMessage(bot: Bot, source: MessageSource)
 
     /**
      * 发送戳一戳消息
      */
-    @JvmBlockingBridge
     public suspend fun sendNudge(bot: Bot, nudge: Nudge, receiver: Contact): Boolean
 
     /**
@@ -143,7 +142,6 @@ public interface IMirai : LowLevelApiAccessor {
      *
      * @see Image.queryUrl [Image] 的扩展函数
      */
-    @JvmBlockingBridge
     public suspend fun queryImageUrl(bot: Bot, image: Image): String
 
     /**
@@ -151,7 +149,6 @@ public interface IMirai : LowLevelApiAccessor {
      *
      * @since 2.1
      */
-    @JvmBlockingBridge
     public suspend fun queryProfile(bot: Bot, targetId: Long): UserProfile
 
     /**
@@ -173,7 +170,6 @@ public interface IMirai : LowLevelApiAccessor {
     /**
      * @since 2.3
      */
-    @JvmBlockingBridge
     public suspend fun downloadLongMessage(
         bot: Bot,
         resourceId: String,
@@ -182,7 +178,6 @@ public interface IMirai : LowLevelApiAccessor {
     /**
      * @since 2.3
      */
-    @JvmBlockingBridge
     public suspend fun downloadForwardMessage(
         bot: Bot,
         resourceId: String,
@@ -193,7 +188,6 @@ public interface IMirai : LowLevelApiAccessor {
      *
      * @param event 好友验证的事件对象
      */
-    @JvmBlockingBridge
     public suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent)
 
     /**
@@ -202,7 +196,6 @@ public interface IMirai : LowLevelApiAccessor {
      * @param event 好友验证的事件对象
      * @param blackList 拒绝后是否拉入黑名单
      */
-    @JvmBlockingBridge
     public suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean = false)
 
     /**
@@ -210,7 +203,6 @@ public interface IMirai : LowLevelApiAccessor {
      *
      * @param event 加群验证的事件对象
      */
-    @JvmBlockingBridge
     public suspend fun acceptMemberJoinRequest(event: MemberJoinRequestEvent)
 
     /**
@@ -219,7 +211,6 @@ public interface IMirai : LowLevelApiAccessor {
      * @param event 加群验证的事件对象
      * @param blackList 拒绝后是否拉入黑名单
      */
-    @JvmBlockingBridge
     public suspend fun rejectMemberJoinRequest(
         event: MemberJoinRequestEvent,
         blackList: Boolean = false,
@@ -230,7 +221,6 @@ public interface IMirai : LowLevelApiAccessor {
      * 获取在线的 [OtherClient] 列表
      * @param mayIncludeSelf 服务器返回的列表可能包含 [Bot] 自己. [mayIncludeSelf] 为 `false` 会排除自己
      */
-    @JvmBlockingBridge
     public suspend fun getOnlineOtherClientsList(
         bot: Bot,
         mayIncludeSelf: Boolean = false
@@ -242,7 +232,6 @@ public interface IMirai : LowLevelApiAccessor {
      * @param event 加群验证的事件对象
      * @param blackList 忽略后是否拉入黑名单
      */
-    @JvmBlockingBridge
     public suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean = false)
 
     /**
@@ -250,7 +239,6 @@ public interface IMirai : LowLevelApiAccessor {
      *
      * @param event 邀请入群的事件对象
      */
-    @JvmBlockingBridge
     public suspend fun acceptInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent)
 
     /**
@@ -258,7 +246,6 @@ public interface IMirai : LowLevelApiAccessor {
      *
      * @param event 邀请入群的事件对象
      */
-    @JvmBlockingBridge
     public suspend fun ignoreInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent)
 }
 

+ 1 - 16
mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt

@@ -36,6 +36,7 @@ public annotation class LowLevelApi
  * **警告**: 所有的低级 API 都可能在任意时刻不经过任何警告和迭代就被修改. 因此非常不建议在任何情况下使用这些 API.
  */
 @LowLevelApi
+@JvmBlockingBridge
 public interface LowLevelApiAccessor {
     /**
      * 主动刷新 keys, 如 SKey, PSKey 等.
@@ -70,7 +71,6 @@ public interface LowLevelApiAccessor {
      * @see recallMessage
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun recallGroupMessageRaw(
         bot: Bot,
         groupCode: Long,
@@ -83,7 +83,6 @@ public interface LowLevelApiAccessor {
      * @see recallMessage
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun recallFriendMessageRaw(
         bot: Bot,
         targetId: Long,
@@ -97,7 +96,6 @@ public interface LowLevelApiAccessor {
      * @see recallMessage
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun recallGroupTempMessageRaw(
         bot: Bot,
         groupUin: Long,
@@ -111,7 +109,6 @@ public interface LowLevelApiAccessor {
      * 向服务器查询群列表. 返回值高 32 bits 为 uin, 低 32 bits 为 groupCode
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun getRawGroupList(bot: Bot): Sequence<Long>
 
     /**
@@ -123,7 +120,6 @@ public interface LowLevelApiAccessor {
      * @see IMirai.calculateGroupUinByGroupCode 使用 groupCode 计算 groupUin
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun getRawGroupMemberList(
         bot: Bot,
         groupUin: Long,
@@ -137,7 +133,6 @@ public interface LowLevelApiAccessor {
      */
     @LowLevelApi
     @MiraiExperimentalApi
-    @JvmBlockingBridge
     public suspend fun getRawGroupAnnouncements(
         bot: Bot,
         groupId: Long,
@@ -151,7 +146,6 @@ public interface LowLevelApiAccessor {
      * @return 公告的fid
      */
     @LowLevelApi
-    @JvmBlockingBridge
     @MiraiExperimentalApi
     public suspend fun sendGroupAnnouncement(
         bot: Bot,
@@ -165,7 +159,6 @@ public interface LowLevelApiAccessor {
      * @param fid [GroupAnnouncement.fid]
      */
     @LowLevelApi
-    @JvmBlockingBridge
     @MiraiExperimentalApi
     public suspend fun deleteGroupAnnouncement(
         bot: Bot,
@@ -178,7 +171,6 @@ public interface LowLevelApiAccessor {
      * @param fid [GroupAnnouncement.fid]
      */
     @LowLevelApi
-    @JvmBlockingBridge
     @MiraiExperimentalApi
     public suspend fun getGroupAnnouncement(
         bot: Bot,
@@ -193,7 +185,6 @@ public interface LowLevelApiAccessor {
      * page从0开始传入可以得到发言列表
      */
     @LowLevelApi
-    @JvmBlockingBridge
     @MiraiExperimentalApi
     public suspend fun getRawGroupActiveData(bot: Bot, groupId: Long, page: Int = -1): GroupActiveData
 
@@ -203,7 +194,6 @@ public interface LowLevelApiAccessor {
      */
     @LowLevelApi
     @MiraiExperimentalApi
-    @JvmBlockingBridge
     public suspend fun getRawGroupHonorListData(
         bot: Bot,
         groupId: Long,
@@ -214,7 +204,6 @@ public interface LowLevelApiAccessor {
     /**
      * 处理一个账号请求添加机器人为好友的事件
      */
-    @JvmBlockingBridge
     @LowLevelApi
     public suspend fun solveNewFriendRequestEvent(
         bot: Bot,
@@ -229,7 +218,6 @@ public interface LowLevelApiAccessor {
      * 处理被邀请加入一个群请求事件
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun solveBotInvitedJoinGroupRequestEvent(
         bot: Bot,
         eventId: Long,
@@ -242,7 +230,6 @@ public interface LowLevelApiAccessor {
      * 处理账号请求加入群事件
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun solveMemberJoinRequestEvent(
         bot: Bot,
         eventId: Long,
@@ -258,7 +245,6 @@ public interface LowLevelApiAccessor {
      * 查询语音的下载连接
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun getGroupVoiceDownloadUrl(
         bot: Bot,
         md5: ByteArray,
@@ -272,7 +258,6 @@ public interface LowLevelApiAccessor {
      * @param anonymousId [AnonymousMember.anonymousId]
      */
     @LowLevelApi
-    @JvmBlockingBridge
     public suspend fun muteAnonymousMember(
         bot: Bot,
         anonymousId: String,

+ 2 - 9
mirai-core-api/src/commonMain/kotlin/contact/Contact.kt

@@ -27,6 +27,7 @@ import java.io.InputStream
 /**
  * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group].
  */
+@JvmBlockingBridge
 public interface Contact : ContactOrBot, CoroutineScope {
     /**
      * 这个联系对象所属 [Bot].
@@ -57,14 +58,12 @@ public interface Contact : ContactOrBot, CoroutineScope {
      *
      * @return 消息回执. 可 [引用][MessageReceipt.quote] 或 [撤回][MessageReceipt.recall] 这条消息.
      */
-    @JvmBlockingBridge
     public suspend fun sendMessage(message: Message): MessageReceipt<Contact>
 
     /**
      * 发送纯文本消息
      * @see sendMessage
      */
-    @JvmBlockingBridge
     public suspend fun sendMessage(message: String): MessageReceipt<Contact> = this.sendMessage(message.toPlainText())
 
     /**
@@ -84,9 +83,9 @@ public interface Contact : ContactOrBot, CoroutineScope {
      * @throws EventCancelledException 当发送消息事件被取消时抛出
      * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时抛出. (最大大小约为 20 MB, 但 mirai 限制的大小为 30 MB)
      */
-    @JvmBlockingBridge
     public suspend fun uploadImage(resource: ExternalResource): Image
 
+    @JvmBlockingBridge
     public companion object {
         /**
          * 读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
@@ -98,7 +97,6 @@ public interface Contact : ContactOrBot, CoroutineScope {
          * @see FileCacheStrategy
          */
         @JvmStatic
-        @JvmBlockingBridge
         @JvmOverloads
         public suspend fun <C : Contact> C.sendImage(
             imageStream: InputStream,
@@ -112,7 +110,6 @@ public interface Contact : ContactOrBot, CoroutineScope {
          * @see FileCacheStrategy
          */
         @JvmStatic
-        @JvmBlockingBridge
         @JvmOverloads
         public suspend fun <C : Contact> C.sendImage(
             file: File,
@@ -124,7 +121,6 @@ public interface Contact : ContactOrBot, CoroutineScope {
          *
          * @see Contact.sendMessage 最终调用, 发送消息.
          */
-        @JvmBlockingBridge
         @JvmStatic
         public suspend fun <C : Contact> C.sendImage(resource: ExternalResource): MessageReceipt<C> =
             resource.sendAsImageTo(this)
@@ -139,7 +135,6 @@ public interface Contact : ContactOrBot, CoroutineScope {
          * @throws OverFileSizeMaxException
          */
         @JvmStatic
-        @JvmBlockingBridge
         @JvmOverloads
         public suspend fun Contact.uploadImage(
             imageStream: InputStream,
@@ -152,7 +147,6 @@ public interface Contact : ContactOrBot, CoroutineScope {
          * @throws OverFileSizeMaxException
          */
         @JvmStatic
-        @JvmBlockingBridge
         @JvmOverloads
         public suspend fun Contact.uploadImage(
             file: File,
@@ -165,7 +159,6 @@ public interface Contact : ContactOrBot, CoroutineScope {
          */
         @Throws(OverFileSizeMaxException::class)
         @JvmStatic
-        @JvmBlockingBridge
         @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXTENSION_SHADOWED_BY_MEMBER")
         @kotlin.internal.LowPriorityInOverloadResolution // for better Java API
         public suspend fun Contact.uploadImage(resource: ExternalResource): Image = this.uploadImage(resource)

+ 1 - 3
mirai-core-api/src/commonMain/kotlin/contact/Friend.kt

@@ -31,6 +31,7 @@ import net.mamoe.mirai.message.data.toPlainText
  *
  * @see FriendMessageEvent
  */
+@JvmBlockingBridge
 public interface Friend : User, CoroutineScope {
     /**
      * QQ 号码
@@ -62,7 +63,6 @@ public interface Friend : User, CoroutineScope {
      *
      * @return 消息回执. 可 [引用][MessageReceipt.quote] 或 [撤回][MessageReceipt.recall] 这条消息.
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: Message): MessageReceipt<Friend>
 
     /**
@@ -70,14 +70,12 @@ public interface Friend : User, CoroutineScope {
      *
      * @see FriendDeleteEvent 好友删除事件
      */
-    @JvmBlockingBridge
     public suspend fun delete()
 
     /**
      * 发送纯文本消息
      * @see sendMessage
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: String): MessageReceipt<Friend> =
         this.sendMessage(message.toPlainText())
 

+ 1 - 5
mirai-core-api/src/commonMain/kotlin/contact/Group.kt

@@ -25,6 +25,7 @@ import net.mamoe.mirai.utils.OverFileSizeMaxException
 /**
  * 群.
  */
+@JvmBlockingBridge
 public interface Group : Contact, CoroutineScope {
     /**
      * 群名称.
@@ -123,7 +124,6 @@ public interface Group : Contact, CoroutineScope {
      * @throws IllegalStateException 当机器人为群主时
      * @return 退出成功时 true; 已经退出时 false
      */
-    @JvmBlockingBridge
     public suspend fun quit(): Boolean
 
     /**
@@ -141,14 +141,12 @@ public interface Group : Contact, CoroutineScope {
      *
      * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: Message): MessageReceipt<Group>
 
     /**
      * 发送纯文本消息
      * @see sendMessage
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: String): MessageReceipt<Group> =
         this.sendMessage(message.toPlainText())
 
@@ -163,7 +161,6 @@ public interface Group : Contact, CoroutineScope {
      * @throws EventCancelledException 当发送消息事件被取消
      * @throws OverFileSizeMaxException 当语音文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
      */
-    @JvmBlockingBridge
     public suspend fun uploadVoice(resource: ExternalResource): Voice
 
 
@@ -175,7 +172,6 @@ public interface Group : Contact, CoroutineScope {
      *
      * @since 2.2
      */
-    @JvmBlockingBridge
     public suspend fun setEssenceMessage(source: MessageSource): Boolean
 
     public companion object {

+ 1 - 1
mirai-core-api/src/commonMain/kotlin/contact/Member.kt

@@ -34,6 +34,7 @@ import net.mamoe.mirai.utils.WeakRefProperty
  * - [Member.asFriend] 转换为 [Friend]
  * - [Member.asStranger] 转换为 [Stranger]
  */
+@JvmBlockingBridge
 public interface Member : User {
     /**
      * 所在的群.
@@ -79,7 +80,6 @@ public interface Member : User {
      *
      * @see Member.mute 支持 Kotlin [kotlin.time.Duration] 的扩展
      */
-    @JvmBlockingBridge
     public suspend fun mute(durationSeconds: Int)
 
     /**

+ 1 - 4
mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt

@@ -27,6 +27,7 @@ import kotlin.time.ExperimentalTime
  * 群成员可能也是好友, 但他们在对象类型上不同.
  * 群成员可以通过 [asFriend] 得到相关好友对象.
  */
+@JvmBlockingBridge
 public interface NormalMember : Member {
     /**
      * 群名片. 可能为空.
@@ -88,7 +89,6 @@ public interface NormalMember : Member {
      *
      * @throws PermissionDeniedException 无权限修改时抛出
      */
-    @JvmBlockingBridge
     public suspend fun unmute()
 
     /**
@@ -100,7 +100,6 @@ public interface NormalMember : Member {
      * @throws PermissionDeniedException 无权限修改时
      *
      */
-    @JvmBlockingBridge
     public suspend fun kick(message: String)
 
     /**
@@ -122,14 +121,12 @@ public interface NormalMember : Member {
      *
      * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: Message): MessageReceipt<NormalMember>
 
     /**
      * 发送纯文本消息
      * @see sendMessage
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: String): MessageReceipt<NormalMember> =
         this.sendMessage(message.toPlainText())
 

+ 1 - 3
mirai-core-api/src/commonMain/kotlin/contact/Stranger.kt

@@ -39,6 +39,7 @@ import net.mamoe.mirai.message.data.toPlainText
  *
  * @see StrangerMessageEvent
  */
+@JvmBlockingBridge
 public interface Stranger : User, CoroutineScope {
     /**
      * QQ 号码
@@ -66,7 +67,6 @@ public interface Stranger : User, CoroutineScope {
      *
      * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: Message): MessageReceipt<Stranger>
 
     /**
@@ -74,14 +74,12 @@ public interface Stranger : User, CoroutineScope {
      *
      * @see StrangerRelationChangeEvent.Deleted 陌生人删除事件
      */
-    @JvmBlockingBridge
     public suspend fun delete()
 
     /**
      * 发送纯文本消息
      * @see sendMessage
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: String): MessageReceipt<Stranger> =
         this.sendMessage(message.toPlainText())
 

+ 1 - 3
mirai-core-api/src/commonMain/kotlin/contact/User.kt

@@ -36,6 +36,7 @@ import net.mamoe.mirai.message.data.toPlainText
  *
  * 对于同一个 [Bot] 任何一个人的 [User] 实例都是单一的.
  */
+@JvmBlockingBridge
 public interface User : Contact, UserOrBot, CoroutineScope {
     /**
      * QQ 号码
@@ -72,14 +73,12 @@ public interface User : Contact, UserOrBot, CoroutineScope {
      *
      * @return 消息回执. 可 [引用][MessageReceipt.quote] 或 [撤回][MessageReceipt.recall] 这条消息.
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: Message): MessageReceipt<User>
 
     /**
      * 发送纯文本消息
      * @see sendMessage
      */
-    @JvmBlockingBridge
     public override suspend fun sendMessage(message: String): MessageReceipt<User> =
         this.sendMessage(message.toPlainText())
 
@@ -95,7 +94,6 @@ public interface User : Contact, UserOrBot, CoroutineScope {
      *
      * @since 2.1
      */
-    @JvmBlockingBridge
     public suspend fun queryProfile(): UserProfile = Mirai.queryProfile(bot, this.id)
 }
 

+ 1 - 3
mirai-core-api/src/commonMain/kotlin/message/MessageReceipt.kt

@@ -39,6 +39,7 @@ import net.mamoe.mirai.utils.MiraiInternalApi
  * @see MessageReceipt.sourceIds 源 ids
  * @see MessageReceipt.sourceTime 源时间
  */
+@JvmBlockingBridge
 public open class MessageReceipt<out C : Contact> @MiraiInternalApi constructor(
     /**
      * 指代发送出去的消息.
@@ -59,7 +60,6 @@ public open class MessageReceipt<out C : Contact> @MiraiInternalApi constructor(
      *
      * @see IMirai.recallMessage
      */
-    @JvmBlockingBridge
     public suspend inline fun recall() {
         return Mirai.recallMessage(target.bot, source)
     }
@@ -82,7 +82,6 @@ public open class MessageReceipt<out C : Contact> @MiraiInternalApi constructor(
      * 引用这条消息并回复.
      * @see MessageSource.quote 引用一条消息
      */
-    @JvmBlockingBridge
     public suspend inline fun quoteReply(message: Message): MessageReceipt<C> {
         @Suppress("UNCHECKED_CAST")
         return target.sendMessage(this.quote() + message) as MessageReceipt<C>
@@ -92,7 +91,6 @@ public open class MessageReceipt<out C : Contact> @MiraiInternalApi constructor(
      * 引用这条消息并回复.
      * @see MessageSource.quote 引用一条消息
      */
-    @JvmBlockingBridge
     public suspend inline fun quoteReply(message: String): MessageReceipt<C> {
         return this.quoteReply(PlainText(message))
     }

+ 3 - 0
mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt

@@ -98,6 +98,9 @@ public interface RichMessage : MessageContent, ConstrainSingle {
 @Serializable
 @SerialName(LightApp.SERIAL_NAME)
 public data class LightApp(override val content: String) : RichMessage, CodableMessage {
+    // implementation notes: LightApp is always decoded as LightAppInternal
+    // which are transformed as RefinableMessage to LightApp
+
     public companion object Key : AbstractMessageKey<LightApp>({ it.safeCast() }) {
         public const val SERIAL_NAME: String = "LightApp"
     }

+ 7 - 1
mirai-core-api/src/commonMain/kotlin/message/data/RichMessageOrigin.kt

@@ -108,8 +108,14 @@ public enum class RichMessageKind {
 
     /**
      * 合并转发
+     * @see ForwardMessage
      */
     FORWARD,
 
-    // TODO: 2021/2/3 MusicShare  RichMessageKind
+    /**
+     * 音乐分享
+     * @see MusicShare
+     * @since 2.4
+     */
+    MUSIC_SHARE,
 }

+ 10 - 4
mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt

@@ -56,6 +56,11 @@ public open class BotConfiguration { // open for Java
      */
     public var workingDir: File = File(".")
 
+    /**
+     * 缓存数据目录
+     */
+    public var cacheDirSupplier: (() -> File) = { workingDir.resolve("cache") }
+
     /**
      * Json 序列化器, 使用 'kotlinx.serialization'
      */
@@ -396,11 +401,11 @@ public open class BotConfiguration { // open for Java
      */
     public class FriendListCache @JvmOverloads constructor(
         /**
-         * 缓存文件位置, 相对于 [workingDir] 的路径.
+         * 缓存文件位置, 相对于 [cacheDirSupplier] 的路径.
          *
          * 注意: 保存的文件仅供内部使用, 将来可能会变化.
          */
-        public val cacheFile: File = File("cache/friends.json"),
+        public val cacheFile: File = File("friends.json"),
         /**
          * 在有好友列表修改时自动保存间隔
          */
@@ -432,11 +437,11 @@ public open class BotConfiguration { // open for Java
      */
     public class GroupMemberListCache @JvmOverloads constructor(
         /**
-         * 缓存目录位置, 相对于 [workingDir] 的路径.
+         * 缓存目录位置, 相对于 [cacheDirSupplier] 的路径.
          *
          * 注意: 保存的文件仅供内部使用, 将来可能会变化.
          */
-        public val cacheDir: File = File("cache/groups"),
+        public val cacheDir: File = File("groups"),
         /**
          * 在有成员列表修改时自动保存间隔
          */
@@ -470,6 +475,7 @@ public open class BotConfiguration { // open for Java
         return BotConfiguration().also { new ->
             // To structural order
             new.workingDir = workingDir
+            new.cacheDirSupplier = cacheDirSupplier
             new.json = json
             new.parentCoroutineContext = parentCoroutineContext
             new.heartbeatPeriodMillis = heartbeatPeriodMillis

+ 5 - 2
mirai-core/src/commonMain/kotlin/AbstractBot.kt

@@ -68,7 +68,7 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
     }
 
     // region network
-    internal val serverList: MutableList<Pair<String, Int>> = DefaultServerList.toMutableList()
+    internal val serverList: MutableList<Pair<String, Int>> = mutableListOf()
 
     val network: N get() = _network
 
@@ -149,7 +149,10 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
 
                 bot.asQQAndroidBot().client.run {
                     if (serverList.isEmpty()) {
-                        serverList.addAll(DefaultServerList)
+                        bot.asQQAndroidBot().bdhSyncer.loadServerListFromCache()
+                        if (serverList.isEmpty()) {
+                            serverList.addAll(DefaultServerList)
+                        } else Unit
                     } else serverList.removeAt(0)
                 }
 

+ 5 - 1
mirai-core/src/commonMain/kotlin/QQAndroidBot.kt

@@ -25,6 +25,7 @@ import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
 import net.mamoe.mirai.internal.contact.uin
 import net.mamoe.mirai.internal.message.*
 import net.mamoe.mirai.internal.network.*
+import net.mamoe.mirai.internal.network.handler.BdhSessionSyncer
 import net.mamoe.mirai.internal.network.handler.QQAndroidBotNetworkHandler
 import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
 import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
@@ -57,6 +58,8 @@ internal class QQAndroidBot constructor(
     private val account: BotAccount,
     configuration: BotConfiguration
 ) : AbstractBot<QQAndroidBotNetworkHandler>(configuration, account.id) {
+    val bdhSyncer: BdhSessionSyncer = BdhSessionSyncer(this)
+
     var client: QQAndroidClient = initClient()
         private set
 
@@ -79,7 +82,7 @@ internal class QQAndroidBot constructor(
 
     val friendListCache: FriendListCache? by lazy {
         configuration.friendListCache?.cacheFile?.let { cacheFile ->
-            val ret = configuration.workingDir.resolve(cacheFile).loadAs(FriendListCache.serializer(), JsonForCache) ?: FriendListCache()
+            val ret = configuration.cacheDirSupplier().resolve(cacheFile).loadAs(FriendListCache.serializer(), JsonForCache) ?: FriendListCache()
 
             @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
             bot.eventChannel.parentScope(this@QQAndroidBot)
@@ -130,6 +133,7 @@ internal class QQAndroidBot constructor(
     @ThisApiMustBeUsedInWithConnectionLockBlock
     @Throws(LoginFailedException::class) // only
     override suspend fun relogin(cause: Throwable?) {
+        bdhSyncer.loadFromCache()
         client.useNextServers { host, port ->
             network.closeEverythingAndRelogin(host, port, cause, 0)
         }

+ 3 - 0
mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt

@@ -87,6 +87,9 @@ internal data class ForwardMessageInternal(override val content: String, val res
 
 internal interface RefinableMessage : SingleMessage {
 
+    /**
+     * This message [RefinableMessage] will be replaced by return value of [refine]
+     */
     suspend fun refine(
         contact: Contact,
         context: MessageChain,

+ 1 - 1
mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt

@@ -278,7 +278,7 @@ private object ReceiveMessageTransformer {
             }
         }
 
-        list.add(LightApp(content).refine())
+        list.add(LightAppInternal(content))
     }
 
     private fun decodeCustomElem(

+ 34 - 28
mirai-core/src/commonMain/kotlin/message/lightApp.kt

@@ -12,42 +12,48 @@ package net.mamoe.mirai.internal.message
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.Json
-import net.mamoe.mirai.message.data.LightApp
-import net.mamoe.mirai.message.data.MusicKind
-import net.mamoe.mirai.message.data.MusicShare
-import net.mamoe.mirai.message.data.SingleMessage
+import net.mamoe.mirai.contact.Contact
+import net.mamoe.mirai.message.data.*
+import net.mamoe.mirai.utils.safeCast
 
-private val json = Json {
-    ignoreUnknownKeys = true
-}
+internal data class LightAppInternal(
+    override val content: String
+) : RichMessage, RefinableMessage {
+    companion object Key :
+        AbstractPolymorphicMessageKey<RichMessage, LightAppInternal>(RichMessage, { it.safeCast() })
 
-internal fun LightApp.tryDeserialize(): LightAppStruct? {
-    return kotlin.runCatching {
-        json.decodeFromString(LightAppStruct.serializer(), this.content)
-    }.getOrNull()
-}
-
-/**
- * 识别 app 内容, 如果有必要
- */
-internal fun LightApp.refine(): SingleMessage {
-    val struct = tryDeserialize() ?: return this
-    struct.run {
-        if (meta.music != null) {
-            MusicKind.values().find { it.appId.toInt() == meta.music.appid }?.let { musicType ->
-                meta.music.run {
-                    return MusicShare(
-                        kind = musicType, title = title, summary = desc,
-                        jumpUrl = jumpUrl, pictureUrl = preview, musicUrl = musicUrl, brief = prompt
-                    )
+    override suspend fun refine(contact: Contact, context: MessageChain): Message {
+        val struct = tryDeserialize() ?: return LightApp(content)
+        struct.run {
+            if (meta.music != null) {
+                MusicKind.values().find { it.appId.toInt() == meta.music.appid }?.let { musicType ->
+                    meta.music.run {
+                        return RichMessageOrigin(
+                            LightApp(content),
+                            null,
+                            RichMessageKind.MUSIC_SHARE
+                        ) + MusicShare(
+                            kind = musicType, title = title, summary = desc,
+                            jumpUrl = jumpUrl, pictureUrl = preview, musicUrl = musicUrl, brief = prompt
+                        )
+                    }
                 }
             }
         }
 
-
+        return LightApp(content)
     }
+}
 
-    return this
+private val json = Json {
+    ignoreUnknownKeys = true
+    isLenient = true
+}
+
+internal fun LightAppInternal.tryDeserialize(): LightAppStruct? {
+    return kotlin.runCatching {
+        json.decodeFromString(LightAppStruct.serializer(), this.content)
+    }.getOrNull()
 }
 
 /*

+ 1 - 1
mirai-core/src/commonMain/kotlin/network/ContactListCache.kt

@@ -84,7 +84,7 @@ internal class GroupMemberListCaches(
     }
 
     private val cacheDir by lazy {
-        bot.configuration.groupMemberListCache!!.cacheDir.let { bot.configuration.workingDir.resolve(it) }
+        bot.configuration.groupMemberListCache!!.cacheDir.let { bot.configuration.cacheDirSupplier().resolve(it) }
     }
 
     private fun resolveCacheFile(groupCode: Long): File {

+ 2 - 5
mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt

@@ -19,6 +19,7 @@ import kotlinx.io.core.BytePacketBuilder
 import kotlinx.io.core.String
 import kotlinx.io.core.toByteArray
 import kotlinx.io.core.writeFully
+import kotlinx.serialization.Serializable
 import net.mamoe.mirai.data.OnlineStatus
 import net.mamoe.mirai.internal.BotAccount
 import net.mamoe.mirai.internal.QQAndroidBot
@@ -281,11 +282,6 @@ internal open class QQAndroidClient(
 
     lateinit var t104: ByteArray
 
-    /**
-     * from ConfigPush.PushReq
-     */
-    @JvmField
-    val bdhSession: CompletableDeferred<BdhSession> = CompletableDeferred()
 }
 
 internal fun BytePacketBuilder.writeLoginExtraData(loginExtraData: LoginExtraData) {
@@ -298,6 +294,7 @@ internal fun BytePacketBuilder.writeLoginExtraData(loginExtraData: LoginExtraDat
     }
 }
 
+@Serializable
 internal class BdhSession(
     val sigSession: ByteArray,
     val sessionKey: ByteArray,

+ 120 - 0
mirai-core/src/commonMain/kotlin/network/handler/BdhSessionSyncer.kt

@@ -0,0 +1,120 @@
+/*
+ * Copyright 2019-2021 Mamoe Technologies and contributors.
+ *
+ *  此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ *  Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ *  https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+package net.mamoe.mirai.internal.network.handler
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.builtins.ListSerializer
+import kotlinx.serialization.builtins.PairSerializer
+import kotlinx.serialization.builtins.serializer
+import net.mamoe.mirai.internal.QQAndroidBot
+import net.mamoe.mirai.internal.network.BdhSession
+import net.mamoe.mirai.internal.network.JsonForCache
+import java.io.File
+
+private val ServerListSerializer: KSerializer<List<Pair<String, Int>>> =
+    ListSerializer(PairSerializer(String.serializer(), Int.serializer()))
+
+@OptIn(ExperimentalCoroutinesApi::class)
+internal class BdhSessionSyncer(
+    private val bot: QQAndroidBot
+) {
+    var bdhSession: CompletableDeferred<BdhSession> = CompletableDeferred()
+    val hasSession: Boolean get() = bdhSession.isCompleted
+
+    fun overrideSession(
+        session: BdhSession,
+        doSave: Boolean = true
+    ) {
+        bdhSession.complete(session)
+        bdhSession = CompletableDeferred(session)
+        if (doSave) {
+            saveToCache()
+        }
+    }
+
+    private val sessionCacheFile: File
+        get() = bot.configuration.cacheDirSupplier().resolve("session.json")
+    private val serverListCacheFile: File
+        get() = bot.configuration.cacheDirSupplier().resolve("serverlist.json")
+
+    fun loadServerListFromCache() {
+        val serverListCacheFile = this.serverListCacheFile
+        if (serverListCacheFile.isFile) {
+            bot.network.logger.verbose("Loading server list from cache.")
+            kotlin.runCatching {
+                val list = JsonForCache.decodeFromString(ServerListSerializer, serverListCacheFile.readText())
+                bot.serverList.clear()
+                bot.serverList.addAll(list)
+            }.onFailure {
+                bot.network.logger.warning("Error in loading server list from cache", it)
+            }
+        } else {
+            bot.network.logger.verbose("No server list cached.")
+        }
+    }
+
+    fun loadFromCache() {
+        val sessionCacheFile = this.sessionCacheFile
+        if (sessionCacheFile.isFile) {
+            bot.network.logger.verbose("Loading BdhSession from cache file")
+            kotlin.runCatching {
+                overrideSession(
+                    JsonForCache.decodeFromString(BdhSession.serializer(), sessionCacheFile.readText()),
+                    doSave = false
+                )
+            }.onFailure {
+                bot.network.logger.warning("Error in loading BdhSession from cache", it)
+            }
+        } else {
+            bot.network.logger.verbose("No BdhSession cache")
+        }
+    }
+
+    fun saveServerListToCache() {
+        val serverListCacheFile = this.serverListCacheFile
+        serverListCacheFile.parentFile?.mkdirs()
+
+        bot.network.logger.verbose("Saving server list to cache")
+        kotlin.runCatching {
+            serverListCacheFile.writeText(
+                JsonForCache.encodeToString(
+                    ServerListSerializer,
+                    bot.serverList
+                )
+            )
+        }.onFailure {
+            bot.network.logger.warning("Error in saving ServerList to cache.", it)
+        }
+    }
+
+    fun saveToCache() {
+        val sessionCacheFile = this.sessionCacheFile
+        sessionCacheFile.parentFile?.mkdirs()
+        if (bdhSession.isCompleted) {
+            bot.network.logger.verbose("Saving bdh session to cache")
+            kotlin.runCatching {
+                sessionCacheFile.writeText(
+                    JsonForCache.encodeToString(
+                        BdhSession.serializer(),
+                        bdhSession.getCompleted()
+                    )
+                )
+            }.onFailure {
+                bot.network.logger.warning("Error in saving BdhSession to cache.", it)
+            }
+        } else {
+            sessionCacheFile.delete()
+            bot.network.logger.verbose("No BdhSession to save to cache")
+        }
+
+    }
+}

+ 9 - 13
mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt

@@ -359,24 +359,20 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
         logger.info { "Awaiting ConfigPushSvc.PushReq." }
         when (val resp: ConfigPushSvc.PushReq.PushReqResponse? = nextEventOrNull(20_000)) {
             null -> {
-                kotlin.runCatching { bot.client.bdhSession.completeExceptionally(CancellationException("Timeout waiting for ConfigPushSvc.PushReq")) }
-                logger.warning { "Missing ConfigPushSvc.PushReq. File uploading may be affected." }
+                val hasSession = bot.bdhSyncer.hasSession
+                kotlin.runCatching { bot.bdhSyncer.bdhSession.completeExceptionally(CancellationException("Timeout waiting for ConfigPushSvc.PushReq")) }
+                if (!hasSession) {
+                    logger.warning { "Missing ConfigPushSvc.PushReq. File uploading may be affected." }
+                } else {
+                    logger.warning { "Missing ConfigPushSvc.PushReq. Using latest response. File uploading may be affected." }
+                }
             }
             is ConfigPushSvc.PushReq.PushReqResponse.Success -> {
                 logger.info { "ConfigPushSvc.PushReq: Success." }
             }
             is ConfigPushSvc.PushReq.PushReqResponse.ChangeServer -> {
-                bot.logger.info { "Server requires reconnect." }
-                bot.logger.info { "Server list: ${resp.serverList.joinToString()}." }
-
-                if (resp.serverList.isNotEmpty()) {
-                    bot.serverList.clear()
-                    resp.serverList.shuffled().forEach {
-                        bot.serverList.add(it.host to it.port)
-                    }
-                }
-
-                bot.launch { BotOfflineEvent.RequireReconnect(bot).broadcast() }
+                logger.info { "ConfigPushSvc.PushReq: Require reconnect" }
+                // handled in ConfigPushSvc
                 return@launch
             }
         }

+ 1 - 1
mirai-core/src/commonMain/kotlin/network/highway/Highway.kt

@@ -59,7 +59,7 @@ internal object Highway {
         fallbackSession: (Throwable) -> BdhSession = { throw IllegalStateException("Failed to get bdh session", it) }
     ): BdhUploadResponse {
         val bdhSession = kotlin.runCatching {
-            val deferred = bot.client.bdhSession
+            val deferred = bot.bdhSyncer.bdhSession
             // no need to care about timeout. proceed by bot init
             @OptIn(ExperimentalCoroutinesApi::class)
             if (noBdhAwait) deferred.getCompleted() else deferred.await()

+ 4 - 1
mirai-core/src/commonMain/kotlin/network/keys.kt

@@ -200,7 +200,10 @@ internal open class KeyWithCreationTime(
 
 internal suspend inline fun QQAndroidClient.useNextServers(crossinline block: suspend (host: String, port: Int) -> Unit) {
     if (bot.serverList.isEmpty()) {
-        bot.serverList.addAll(DefaultServerList)
+        bot.bdhSyncer.loadServerListFromCache()
+        if (bot.serverList.isEmpty()) {
+            bot.serverList.addAll(DefaultServerList)
+        }
     }
     retryCatchingExceptions(bot.serverList.size, except = LoginFailedException::class) l@{
         val pair = bot.serverList[0]

+ 31 - 3
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt

@@ -9,10 +9,14 @@
 
 package net.mamoe.mirai.internal.network.protocol.packet.login
 
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.serialization.Serializable
 import net.mamoe.mirai.event.AbstractEvent
 import net.mamoe.mirai.event.Event
+import net.mamoe.mirai.event.broadcast
+import net.mamoe.mirai.event.events.BotOfflineEvent
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.message.contextualBugReportException
 import net.mamoe.mirai.internal.network.BdhSession
@@ -30,6 +34,7 @@ import net.mamoe.mirai.internal.utils.io.serialization.loadAs
 import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket
 import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
 import net.mamoe.mirai.internal.utils.io.serialization.writeJceStruct
+import net.mamoe.mirai.utils.info
 import net.mamoe.mirai.utils.toUHexString
 import java.lang.IllegalStateException
 import net.mamoe.mirai.internal.network.protocol.data.jce.PushReq as PushReqJceStruct
@@ -110,7 +115,7 @@ internal class ConfigPushSvc {
 
                 val bigDataChannel = fileStoragePushFSSvcList.bigDataChannel
                 if (bigDataChannel?.vBigdataPbBuf == null) {
-                    client.bdhSession.completeExceptionally(IllegalStateException("BdhSession not received."))
+                    bot.bdhSyncer.bdhSession.completeExceptionally(IllegalStateException("BdhSession not received."))
                     return
                 }
 
@@ -134,16 +139,35 @@ internal class ConfigPushSvc {
                     session
                 }.fold(
                     onSuccess = {
-                        client.bdhSession.complete(it)
+                        bdhSyncer.overrideSession(it)
                     },
                     onFailure = { cause ->
                         val e = IllegalStateException("Failed to decode BdhSession", cause)
-                        client.bdhSession.completeExceptionally(e)
+                        bdhSyncer.bdhSession.completeExceptionally(e)
                         logger.error(e)
                     }
                 )
             }
 
+            fun handleRequireReconnect(resp: PushReqResponse.ChangeServer) {
+                bot.logger.info { "Server requires reconnect." }
+                bot.logger.info { "Server list: ${resp.serverList.joinToString()}." }
+
+                if (resp.serverList.isNotEmpty()) {
+                    bot.serverList.clear()
+                    resp.serverList.shuffled().forEach {
+                        bot.serverList.add(it.host to it.port)
+                    }
+                }
+                bot.bdhSyncer.saveToCache()
+                bot.bdhSyncer.saveServerListToCache()
+
+                bot.launch {
+                    delay(1000)
+                    BotOfflineEvent.RequireReconnect(bot).broadcast()
+                }
+            }
+
             when (packet) {
                 is PushReqResponse.Success -> {
                     handleSuccess(packet)
@@ -172,6 +196,10 @@ internal class ConfigPushSvc {
                         // writePacket(this.build().debugPrintThis())
                     }
                 }
+                is PushReqResponse.ChangeServer -> {
+                    handleRequireReconnect(packet)
+                    return null
+                }
                 else -> {
                     // handled in QQABot
                     return null