mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-05 07:30:09 +08:00
Introduce Audio
for new API, revert changes on Voice
This commit is contained in:
parent
cf1c5d4d81
commit
8d8aca3f1c
@ -176,6 +176,11 @@ public abstract interface class net/mamoe/mirai/contact/AnonymousMember : net/ma
|
||||
public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/AudioSupported : net/mamoe/mirai/contact/Contact {
|
||||
public fun uploadAudio (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public abstract fun uploadAudio (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/contact/BotIsBeingMutedException : java/lang/RuntimeException {
|
||||
public fun <init> (Lnet/mamoe/mirai/contact/Group;)V
|
||||
public final fun getTarget ()Lnet/mamoe/mirai/contact/Group;
|
||||
@ -326,7 +331,7 @@ public abstract interface class net/mamoe/mirai/contact/FileSupported : net/mamo
|
||||
public abstract fun getFilesRoot ()Lnet/mamoe/mirai/utils/RemoteFile;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/VoiceSupported {
|
||||
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User {
|
||||
public fun delete ()V
|
||||
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun nudge ()Lnet/mamoe/mirai/message/action/FriendNudge;
|
||||
@ -338,7 +343,7 @@ public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/corouti
|
||||
public abstract fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/Contact, net/mamoe/mirai/contact/FileSupported, net/mamoe/mirai/contact/VoiceSupported {
|
||||
public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/Contact, net/mamoe/mirai/contact/FileSupported {
|
||||
public static final field Companion Lnet/mamoe/mirai/contact/Group$Companion;
|
||||
public abstract fun contains (J)Z
|
||||
public fun contains (Lnet/mamoe/mirai/contact/NormalMember;)Z
|
||||
@ -365,6 +370,8 @@ 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 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;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/contact/Group$Companion {
|
||||
@ -560,11 +567,6 @@ public abstract interface class net/mamoe/mirai/contact/UserOrBot : net/mamoe/mi
|
||||
public abstract fun nudge ()Lnet/mamoe/mirai/message/action/Nudge;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/VoiceSupported : net/mamoe/mirai/contact/Contact {
|
||||
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;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/announcement/Announcement {
|
||||
public static final field Companion Lnet/mamoe/mirai/contact/announcement/Announcement$Companion;
|
||||
public abstract fun getContent ()Ljava/lang/String;
|
||||
@ -3158,6 +3160,41 @@ public final class net/mamoe/mirai/message/data/AtAll : net/mamoe/mirai/message/
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/Audio : net/mamoe/mirai/message/data/MessageContent {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/Audio$Key;
|
||||
public fun contentToString ()Ljava/lang/String;
|
||||
public abstract fun getCodec ()Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public abstract fun getExtraData ()[B
|
||||
public abstract fun getFileMd5 ()[B
|
||||
public abstract fun getFileSize ()J
|
||||
public abstract fun getFilename ()Ljava/lang/String;
|
||||
public abstract fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Audio$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/AudioCodec : java/lang/Enum {
|
||||
public static final field AMR Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final field Companion Lnet/mamoe/mirai/message/data/AudioCodec$Companion;
|
||||
public static final field SILK Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromFormatName (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromFormatNameOrNull (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromId (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromIdOrNull (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun getFormatName ()Ljava/lang/String;
|
||||
public final fun getId ()I
|
||||
public static fun valueOf (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static fun values ()[Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/AudioCodec$Companion {
|
||||
public final fun fromFormatName (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun fromFormatNameOrNull (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun fromId (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun fromIdOrNull (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/ConstrainSingle : net/mamoe/mirai/message/data/SingleMessage {
|
||||
public abstract fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey;
|
||||
}
|
||||
@ -4499,6 +4536,8 @@ public final class net/mamoe/mirai/message/data/MessageUtils {
|
||||
public static final synthetic fun At (Lnet/mamoe/mirai/contact/UserOrBot;)Lnet/mamoe/mirai/message/data/At;
|
||||
public static final synthetic fun FileMessage (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
public static final synthetic fun Image (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
|
||||
public static final synthetic fun OfflineAudio (Ljava/lang/String;[BJLnet/mamoe/mirai/message/data/AudioCodec;[B)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public static final synthetic fun OfflineAudio (Lnet/mamoe/mirai/message/data/OnlineAudio;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public static final synthetic fun UnsupportedMessage ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
|
||||
public static final synthetic fun at (Lnet/mamoe/mirai/contact/Member;)Lnet/mamoe/mirai/message/data/At;
|
||||
public static final fun buildMessageChain (ILkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;
|
||||
@ -4541,6 +4580,7 @@ public final class net/mamoe/mirai/message/data/MessageUtils {
|
||||
public static final synthetic fun toMessageChain ([Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/data/MessageChain;
|
||||
public static final fun toOfflineMessageSource (Lnet/mamoe/mirai/message/data/OnlineMessageSource;)Lnet/mamoe/mirai/message/data/OfflineMessageSource;
|
||||
public static final synthetic fun toPlainText (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/PlainText;
|
||||
public static final synthetic fun toVoice (Lnet/mamoe/mirai/message/data/Audio;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/MusicKind : java/lang/Enum {
|
||||
@ -4604,6 +4644,26 @@ public final class net/mamoe/mirai/message/data/MusicShare$Key : net/mamoe/mirai
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/OfflineAudio : net/mamoe/mirai/message/data/Audio {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OfflineAudio$Key;
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/OfflineAudio$Factory {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/OfflineAudio$Factory$INSTANCE;
|
||||
public abstract fun create (Ljava/lang/String;[BJLnet/mamoe/mirai/message/data/AudioCodec;[B)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public fun from (Lnet/mamoe/mirai/message/data/OnlineAudio;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/OfflineAudio$Factory$INSTANCE : net/mamoe/mirai/message/data/OfflineAudio$Factory {
|
||||
public fun create (Ljava/lang/String;[BJLnet/mamoe/mirai/message/data/AudioCodec;[B)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public fun from (Lnet/mamoe/mirai/message/data/OnlineAudio;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/OfflineAudio$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract class net/mamoe/mirai/message/data/OfflineMessageSource : net/mamoe/mirai/message/data/MessageSource {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OfflineMessageSource$Key;
|
||||
public fun <init> ()V
|
||||
@ -4613,6 +4673,17 @@ public abstract class net/mamoe/mirai/message/data/OfflineMessageSource : net/ma
|
||||
public final class net/mamoe/mirai/message/data/OfflineMessageSource$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/OnlineAudio : net/mamoe/mirai/message/data/Audio {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OnlineAudio$Key;
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
public abstract fun getLength ()J
|
||||
public abstract fun getUrlForDownload ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/OnlineAudio$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract class net/mamoe/mirai/message/data/OnlineMessageSource : net/mamoe/mirai/message/data/MessageSource {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OnlineMessageSource$Key;
|
||||
public abstract fun getBot ()Lnet/mamoe/mirai/Bot;
|
||||
@ -5090,32 +5161,39 @@ public final class net/mamoe/mirai/message/data/VipFace$Kind$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
||||
public class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
public synthetic fun <init> (ILjava/lang/String;[BJILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;[BJILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun contentToString ()Ljava/lang/String;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getCodec ()I
|
||||
public static final fun fromAudio (Lnet/mamoe/mirai/message/data/Audio;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public fun getFileName ()Ljava/lang/String;
|
||||
public fun getFileSize ()J
|
||||
public fun getMd5 ()[B
|
||||
public final fun getUrl ()Ljava/lang/String;
|
||||
public fun getUrl ()Ljava/lang/String;
|
||||
public final fun get_codec ()I
|
||||
public fun hashCode ()I
|
||||
public final fun toAudio ()Lnet/mamoe/mirai/message/data/Audio;
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$Serializer : kotlinx/serialization/KSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$Serializer;
|
||||
public final class net/mamoe/mirai/message/data/Voice$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$$serializer;
|
||||
public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/Voice;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public final fun fromAudio (Lnet/mamoe/mirai/message/data/Audio;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
||||
@ -5429,14 +5507,8 @@ public abstract interface class net/mamoe/mirai/utils/ExternalResource : java/io
|
||||
public static fun uploadAsImage (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsImage (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;)Lnet/mamoe/mirai/message/data/Image;
|
||||
public static fun uploadAsImage (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
public static fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
@ -5504,14 +5576,8 @@ public final class net/mamoe/mirai/utils/ExternalResource$Companion {
|
||||
public static synthetic fun uploadAsImage$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/File;Lnet/mamoe/mirai/contact/Contact;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
|
||||
public static synthetic fun uploadAsImage$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/InputStream;Lnet/mamoe/mirai/contact/Contact;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/message/data/Image;
|
||||
public static synthetic fun uploadAsImage$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/InputStream;Lnet/mamoe/mirai/contact/Contact;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
public final fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
|
@ -176,6 +176,11 @@ public abstract interface class net/mamoe/mirai/contact/AnonymousMember : net/ma
|
||||
public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/AudioSupported : net/mamoe/mirai/contact/Contact {
|
||||
public fun uploadAudio (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public abstract fun uploadAudio (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/contact/BotIsBeingMutedException : java/lang/RuntimeException {
|
||||
public fun <init> (Lnet/mamoe/mirai/contact/Group;)V
|
||||
public final fun getTarget ()Lnet/mamoe/mirai/contact/Group;
|
||||
@ -326,7 +331,7 @@ public abstract interface class net/mamoe/mirai/contact/FileSupported : net/mamo
|
||||
public abstract fun getFilesRoot ()Lnet/mamoe/mirai/utils/RemoteFile;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/VoiceSupported {
|
||||
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User {
|
||||
public fun delete ()V
|
||||
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public fun nudge ()Lnet/mamoe/mirai/message/action/FriendNudge;
|
||||
@ -338,7 +343,7 @@ public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/corouti
|
||||
public abstract fun sendMessage (Lnet/mamoe/mirai/message/data/Message;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/Contact, net/mamoe/mirai/contact/FileSupported, net/mamoe/mirai/contact/VoiceSupported {
|
||||
public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/Contact, net/mamoe/mirai/contact/FileSupported {
|
||||
public static final field Companion Lnet/mamoe/mirai/contact/Group$Companion;
|
||||
public abstract fun contains (J)Z
|
||||
public fun contains (Lnet/mamoe/mirai/contact/NormalMember;)Z
|
||||
@ -365,6 +370,8 @@ 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 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;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/contact/Group$Companion {
|
||||
@ -560,11 +567,6 @@ public abstract interface class net/mamoe/mirai/contact/UserOrBot : net/mamoe/mi
|
||||
public abstract fun nudge ()Lnet/mamoe/mirai/message/action/Nudge;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/VoiceSupported : net/mamoe/mirai/contact/Contact {
|
||||
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;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/contact/announcement/Announcement {
|
||||
public static final field Companion Lnet/mamoe/mirai/contact/announcement/Announcement$Companion;
|
||||
public abstract fun getContent ()Ljava/lang/String;
|
||||
@ -3158,6 +3160,41 @@ public final class net/mamoe/mirai/message/data/AtAll : net/mamoe/mirai/message/
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/Audio : net/mamoe/mirai/message/data/MessageContent {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/Audio$Key;
|
||||
public fun contentToString ()Ljava/lang/String;
|
||||
public abstract fun getCodec ()Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public abstract fun getExtraData ()[B
|
||||
public abstract fun getFileMd5 ()[B
|
||||
public abstract fun getFileSize ()J
|
||||
public abstract fun getFilename ()Ljava/lang/String;
|
||||
public abstract fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Audio$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/AudioCodec : java/lang/Enum {
|
||||
public static final field AMR Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final field Companion Lnet/mamoe/mirai/message/data/AudioCodec$Companion;
|
||||
public static final field SILK Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromFormatName (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromFormatNameOrNull (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromId (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static final fun fromIdOrNull (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun getFormatName ()Ljava/lang/String;
|
||||
public final fun getId ()I
|
||||
public static fun valueOf (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public static fun values ()[Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/AudioCodec$Companion {
|
||||
public final fun fromFormatName (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun fromFormatNameOrNull (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun fromId (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
public final fun fromIdOrNull (I)Lnet/mamoe/mirai/message/data/AudioCodec;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/ConstrainSingle : net/mamoe/mirai/message/data/SingleMessage {
|
||||
public abstract fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey;
|
||||
}
|
||||
@ -4499,6 +4536,8 @@ public final class net/mamoe/mirai/message/data/MessageUtils {
|
||||
public static final synthetic fun At (Lnet/mamoe/mirai/contact/UserOrBot;)Lnet/mamoe/mirai/message/data/At;
|
||||
public static final synthetic fun FileMessage (Ljava/lang/String;ILjava/lang/String;J)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
public static final synthetic fun Image (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
|
||||
public static final synthetic fun OfflineAudio (Ljava/lang/String;[BJLnet/mamoe/mirai/message/data/AudioCodec;[B)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public static final synthetic fun OfflineAudio (Lnet/mamoe/mirai/message/data/OnlineAudio;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public static final synthetic fun UnsupportedMessage ([B)Lnet/mamoe/mirai/message/data/UnsupportedMessage;
|
||||
public static final synthetic fun at (Lnet/mamoe/mirai/contact/Member;)Lnet/mamoe/mirai/message/data/At;
|
||||
public static final fun buildMessageChain (ILkotlin/jvm/functions/Function1;)Lnet/mamoe/mirai/message/data/MessageChain;
|
||||
@ -4541,6 +4580,7 @@ public final class net/mamoe/mirai/message/data/MessageUtils {
|
||||
public static final synthetic fun toMessageChain ([Lnet/mamoe/mirai/message/data/Message;)Lnet/mamoe/mirai/message/data/MessageChain;
|
||||
public static final fun toOfflineMessageSource (Lnet/mamoe/mirai/message/data/OnlineMessageSource;)Lnet/mamoe/mirai/message/data/OfflineMessageSource;
|
||||
public static final synthetic fun toPlainText (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/PlainText;
|
||||
public static final synthetic fun toVoice (Lnet/mamoe/mirai/message/data/Audio;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/MusicKind : java/lang/Enum {
|
||||
@ -4604,6 +4644,26 @@ public final class net/mamoe/mirai/message/data/MusicShare$Key : net/mamoe/mirai
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/OfflineAudio : net/mamoe/mirai/message/data/Audio {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OfflineAudio$Key;
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/OfflineAudio$Factory {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/OfflineAudio$Factory$INSTANCE;
|
||||
public abstract fun create (Ljava/lang/String;[BJLnet/mamoe/mirai/message/data/AudioCodec;[B)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public fun from (Lnet/mamoe/mirai/message/data/OnlineAudio;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/OfflineAudio$Factory$INSTANCE : net/mamoe/mirai/message/data/OfflineAudio$Factory {
|
||||
public fun create (Ljava/lang/String;[BJLnet/mamoe/mirai/message/data/AudioCodec;[B)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
public fun from (Lnet/mamoe/mirai/message/data/OnlineAudio;)Lnet/mamoe/mirai/message/data/OfflineAudio;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/OfflineAudio$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract class net/mamoe/mirai/message/data/OfflineMessageSource : net/mamoe/mirai/message/data/MessageSource {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OfflineMessageSource$Key;
|
||||
public fun <init> ()V
|
||||
@ -4613,6 +4673,17 @@ public abstract class net/mamoe/mirai/message/data/OfflineMessageSource : net/ma
|
||||
public final class net/mamoe/mirai/message/data/OfflineMessageSource$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/OnlineAudio : net/mamoe/mirai/message/data/Audio {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OnlineAudio$Key;
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
public abstract fun getLength ()J
|
||||
public abstract fun getUrlForDownload ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/OnlineAudio$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
}
|
||||
|
||||
public abstract class net/mamoe/mirai/message/data/OnlineMessageSource : net/mamoe/mirai/message/data/MessageSource {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/OnlineMessageSource$Key;
|
||||
public abstract fun getBot ()Lnet/mamoe/mirai/Bot;
|
||||
@ -5090,32 +5161,39 @@ public final class net/mamoe/mirai/message/data/VipFace$Kind$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
||||
public class net/mamoe/mirai/message/data/Voice : net/mamoe/mirai/message/data/PttMessage {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/Voice$Key;
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
public synthetic fun <init> (ILjava/lang/String;[BJILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;[BJILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun contentToString ()Ljava/lang/String;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getCodec ()I
|
||||
public static final fun fromAudio (Lnet/mamoe/mirai/message/data/Audio;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public fun getFileName ()Ljava/lang/String;
|
||||
public fun getFileSize ()J
|
||||
public fun getMd5 ()[B
|
||||
public final fun getUrl ()Ljava/lang/String;
|
||||
public fun getUrl ()Ljava/lang/String;
|
||||
public final fun get_codec ()I
|
||||
public fun hashCode ()I
|
||||
public final fun toAudio ()Lnet/mamoe/mirai/message/data/Audio;
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$Serializer : kotlinx/serialization/KSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$Serializer;
|
||||
public final class net/mamoe/mirai/message/data/Voice$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/Voice$$serializer;
|
||||
public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/Voice;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/Voice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey {
|
||||
public final fun fromAudio (Lnet/mamoe/mirai/message/data/Audio;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
||||
@ -5429,14 +5507,8 @@ public abstract interface class net/mamoe/mirai/utils/ExternalResource : java/io
|
||||
public static fun uploadAsImage (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsImage (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;)Lnet/mamoe/mirai/message/data/Image;
|
||||
public static fun uploadAsImage (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public static fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
public static fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public static fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
@ -5504,14 +5576,8 @@ public final class net/mamoe/mirai/utils/ExternalResource$Companion {
|
||||
public static synthetic fun uploadAsImage$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/File;Lnet/mamoe/mirai/contact/Contact;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
|
||||
public static synthetic fun uploadAsImage$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/InputStream;Lnet/mamoe/mirai/contact/Contact;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/message/data/Image;
|
||||
public static synthetic fun uploadAsImage$default (Lnet/mamoe/mirai/utils/ExternalResource$Companion;Ljava/io/InputStream;Lnet/mamoe/mirai/contact/Contact;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Ljava/io/File;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Ljava/io/InputStream;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/Contact;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;)Lnet/mamoe/mirai/message/data/Voice;
|
||||
public final fun uploadAsVoice (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/contact/VoiceSupported;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
public final fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun uploadTo (Ljava/io/File;Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;)Lnet/mamoe/mirai/message/data/FileMessage;
|
||||
|
@ -15,9 +15,7 @@ import kotlinx.coroutines.Job
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.*
|
||||
import net.mamoe.mirai.message.data.Voice
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import net.mamoe.mirai.utils.WeakRef
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
@ -219,18 +217,4 @@ public interface LowLevelApiAccessor {
|
||||
groupId: Long,
|
||||
seconds: Int,
|
||||
)
|
||||
|
||||
/**
|
||||
* 序列化 [Voice.pttInternalInstance]
|
||||
*/
|
||||
@LowLevelApi
|
||||
@MiraiInternalApi // For Voice serialize
|
||||
public fun serializePttElem(ptt: Any?): String
|
||||
|
||||
/**
|
||||
* 反序列化 [Voice.pttInternalInstance]
|
||||
*/
|
||||
@LowLevelApi
|
||||
@MiraiInternalApi // For Voice serialize
|
||||
public fun deserializePttElem(ptt: String): Any?
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmBlockingBridge
|
||||
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.message.data.Audio
|
||||
import net.mamoe.mirai.message.data.OfflineAudio
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
|
||||
/**
|
||||
* 支持发送语音的 [Contact]
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface AudioSupported : Contact {
|
||||
/**
|
||||
* 上传一个语音文件以备发送. [resource] 需要调用方[关闭][ExternalResource.close].
|
||||
*
|
||||
* 多次调用 [uploadAudio] 使用同一个 [resource] 时, 将会发生多次上传, 且有可能产生不同的 [OfflineAudio] 对象, 因为服务器不会提供有关文件是否已经存在于服务器的信息.
|
||||
*
|
||||
* 返回的 [OfflineAudio] 支持序列化, 可以保存后在将来使用, 而不需要立即[发送][Contact.sendMessage]. 但不建议保存太久, 无法确定服务器保留一个文件的时间.
|
||||
*
|
||||
* 建议使用同一个 [Contact] 进行 [uploadAudio] 和 [sendMessage]. 目标对象不同时的行为是不确定的.
|
||||
*
|
||||
* 要获取更多语音相关的信息, 参阅 [Audio].
|
||||
*
|
||||
* @throws OverFileSizeMaxException 当语音文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
|
||||
* **注意**: 由于服务器不一定会检查大小, 该异常就不一定会因大小超过 1MB 而抛出.
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
public suspend fun uploadAudio(resource: ExternalResource): OfflineAudio
|
||||
}
|
@ -34,7 +34,7 @@ import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
* @see FriendMessageEvent
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface Friend : User, CoroutineScope, VoiceSupported {
|
||||
public interface Friend : User, CoroutineScope, AudioSupported {
|
||||
/**
|
||||
* 向这个对象发送消息.
|
||||
*
|
||||
|
@ -19,6 +19,7 @@ import net.mamoe.mirai.contact.announcement.Announcements
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
|
||||
@ -52,7 +53,7 @@ import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
* ##
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface Group : Contact, CoroutineScope, FileSupported, VoiceSupported {
|
||||
public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported {
|
||||
/**
|
||||
* 群名称.
|
||||
*
|
||||
@ -184,6 +185,17 @@ public interface Group : Contact, CoroutineScope, FileSupported, VoiceSupported
|
||||
this.sendMessage(message.toPlainText())
|
||||
|
||||
|
||||
/**
|
||||
* 上传一个语音消息以备发送. 该方法已弃用且将在未来版本删除, 请使用 [uploadAudio].
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@Deprecated(
|
||||
"use uploadAudio",
|
||||
replaceWith = ReplaceWith("uploadAudio(resource)"),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
public suspend fun uploadVoice(resource: ExternalResource): Voice
|
||||
|
||||
/**
|
||||
* 将一条消息设置为群精华消息, 需要管理员或群主权限.
|
||||
* 操作成功返回 `true`.
|
||||
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.message.data.Voice
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsVoice
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||
|
||||
/**
|
||||
* 支持发送语音的 [Contact]
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface VoiceSupported : Contact {
|
||||
/**
|
||||
* 上传一个语音消息以备发送.
|
||||
*
|
||||
* - **请手动关闭 [resource]**
|
||||
* - 请使用 amr 或 silk 格式
|
||||
*
|
||||
* @since 2.7
|
||||
* @see ExternalResource.uploadAsVoice
|
||||
* @throws OverFileSizeMaxException 当语音文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public suspend fun uploadVoice(resource: ExternalResource): Voice
|
||||
|
||||
}
|
@ -13,14 +13,12 @@ import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.modules.PolymorphicModuleBuilder
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.overwriteWith
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.*
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.message.MessageSerializers
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.lateinitMutableProperty
|
||||
import net.mamoe.mirai.utils.map
|
||||
import net.mamoe.mirai.utils.takeElementsFrom
|
||||
import kotlin.reflect.KClass
|
||||
@ -121,6 +119,7 @@ private val builtInSerializersModule by lazy {
|
||||
subclass(SimpleServiceMessage::class, SimpleServiceMessage.serializer())
|
||||
|
||||
// subclass(PttMessage::class, PttMessage.serializer())
|
||||
@Suppress("DEPRECATION")
|
||||
subclass(Voice::class, Voice.serializer())
|
||||
|
||||
// subclass(HummerMessage::class, HummerMessage.serializer())
|
||||
@ -188,31 +187,58 @@ private val builtInSerializersModule by lazy {
|
||||
// Tests:
|
||||
// net.mamoe.mirai.internal.message.data.MessageSerializationTest
|
||||
internal object MessageSerializersImpl : MessageSerializers {
|
||||
@Volatile
|
||||
private var serializersModuleField: SerializersModule? = null
|
||||
private var serializersModuleField: SerializersModule by lateinitMutableProperty {
|
||||
builtInSerializersModule
|
||||
}
|
||||
|
||||
override val serializersModule: SerializersModule
|
||||
get() {
|
||||
Mirai // ensure registered, for tests
|
||||
return serializersModuleField ?: builtInSerializersModule
|
||||
return serializersModuleField
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun <M : SingleMessage> registerSerializer(type: KClass<M>, serializer: KSerializer<M>) {
|
||||
serializersModuleField = serializersModule.overwriteWith(SerializersModule {
|
||||
// contextual(type, serializer)
|
||||
for (superclass in type.allSuperclasses) {
|
||||
if (superclass.isFinal) continue
|
||||
if (!superclass.isSubclassOf(SingleMessage::class)) continue
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
polymorphic(superclass as KClass<Any>) {
|
||||
subclass(type, serializer)
|
||||
}
|
||||
}
|
||||
})
|
||||
serializersModuleField = serializersModule.overwritePolymorphicWith(type, serializer)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun registerSerializers(serializersModule: SerializersModule) {
|
||||
serializersModuleField = serializersModule.overwriteWith(serializersModule)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <M : Any> SerializersModule.overwritePolymorphicWith(
|
||||
type: KClass<M>,
|
||||
serializer: KSerializer<M>
|
||||
): SerializersModule {
|
||||
return overwriteWith(SerializersModule {
|
||||
// contextual(type, serializer)
|
||||
for (superclass in type.allSuperclasses) {
|
||||
if (superclass.isFinal) continue
|
||||
if (!superclass.isSubclassOf(SingleMessage::class)) continue
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
polymorphic(superclass as KClass<Any>) {
|
||||
subclass(type, serializer)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private inline fun <reified M : SingleMessage> SerializersModuleBuilder.hierarchicallyPolymorphic(serializer: KSerializer<M>) =
|
||||
hierarchicallyPolymorphic(M::class, serializer)
|
||||
|
||||
private fun <M : SingleMessage> SerializersModuleBuilder.hierarchicallyPolymorphic(
|
||||
type: KClass<M>,
|
||||
serializer: KSerializer<M>
|
||||
) {
|
||||
// contextual(type, serializer)
|
||||
for (superclass in type.allSuperclasses) {
|
||||
if (superclass.isFinal) continue
|
||||
if (!superclass.isSubclassOf(SingleMessage::class)) continue
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
polymorphic(superclass as KClass<Any>) {
|
||||
subclass(type, serializer)
|
||||
}
|
||||
}
|
||||
}
|
274
mirai-core-api/src/commonMain/kotlin/message/data/Audio.kt
Normal file
274
mirai-core-api/src/commonMain/kotlin/message/data/Audio.kt
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import net.mamoe.mirai.contact.AudioSupported
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.message.MessageSerializers
|
||||
import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString
|
||||
import net.mamoe.mirai.utils.*
|
||||
|
||||
/**
|
||||
* 语音消息.
|
||||
*
|
||||
* [Audio] 分为 [OnlineAudio] 与 [OfflineAudio]. 在本地上传的, 或手动构造的语音为 [OfflineAudio]. 从服务器接收的语音为 [OnlineAudio].
|
||||
*
|
||||
* ## 上传和发送语音
|
||||
*
|
||||
* 使用 [AudioSupported.uploadAudio] 上传语音到服务器并取得 [Audio] 消息实例, 然后通过 [Contact.sendMessage] 发送.
|
||||
*
|
||||
* Java 示例:
|
||||
* ```
|
||||
* Audio audio;
|
||||
* try {
|
||||
* audio = group.uploadAudio(resource); // 上传文件得到语音实例
|
||||
* } finally {
|
||||
* resource.close(); // 保证资源正常关闭
|
||||
* }
|
||||
* group.sendMessage(audio); // 发送语音消息
|
||||
* ```
|
||||
*
|
||||
* ## 下载语音
|
||||
*
|
||||
* 使用 [OnlineAudio.urlForDownload] 获取文件下载链接.
|
||||
*
|
||||
* ## [Audio] 与 [Voice] 的转换
|
||||
*
|
||||
* 原 [Voice] 已弃用故不推荐进行兼容转换. [Audio] 将有稳定性保证, 请尽量使用新的 [Audio].
|
||||
*
|
||||
* 将 [Audio] 转为 [Voice]: [Voice.fromAudio]
|
||||
* 将 [Voice] 转为 [Audio]: [Voice.toAudio]
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
public sealed interface Audio : MessageContent {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<MessageContent, Audio>(MessageContent, { it.safeCast() })
|
||||
|
||||
/**
|
||||
* 文件名称. 通常为 `XXX.amr`. 服务器要求文件名后缀必须为 ".amr", 但其[编码方式][codec]也有可能是非 [AudioCodec.AMR].
|
||||
*/
|
||||
public val filename: String
|
||||
|
||||
/**
|
||||
* 文件 MD5. 16 bytes.
|
||||
*/
|
||||
public val fileMd5: ByteArray
|
||||
|
||||
/**
|
||||
* 文件大小 bytes. 官方客户端支持最大文件大小约为 1MB, 过大的文件**可能**可以正常上传, 但在官方客户端无法收听 (显示文件损坏).
|
||||
*/
|
||||
public val fileSize: Long
|
||||
|
||||
/**
|
||||
* 编码方式.
|
||||
*
|
||||
* - 若语音文件真实编码方式为 [AudioCodec.SILK], 而该属性为 [AudioCodec.AMR], 语音文件将会被服务器压缩为低音质 [AudioCodec.AMR] 格式.
|
||||
* - 若语音文件真实编码方式为 [AudioCodec.AMR], 而该属性为 [AudioCodec.SILK], 语音也可以正常发送并在客户端收听, 音质随文件真实格式而决定.
|
||||
*
|
||||
* 因此在发送时 [codec] 通常可以总是使用 [AudioCodec.SILK] (这也是 [AudioSupported.uploadAudio] 的默认行为).
|
||||
*/
|
||||
public val codec: AudioCodec
|
||||
|
||||
/**
|
||||
* 文件的额外数据. 该数据由服务器提供, 可能会影响语音音质等属性.
|
||||
* [extraData] 为 `null` 时也可以发送语音, 但不确定发送和客户端收听是否会正常.
|
||||
*
|
||||
* [extraData] 可能随服务器更新而更新, 因此请不要尝试解析该数据.
|
||||
*
|
||||
* [extraData] 向下兼容, 即旧版本的 [extraData] 可以在新版本构造 [OfflineAudio] ([OfflineAudio.Factory.create]).
|
||||
*/
|
||||
public val extraData: ByteArray?
|
||||
|
||||
/**
|
||||
* @return `"[mirai:audio:${filename}]"`
|
||||
*/
|
||||
public override fun toString(): String
|
||||
public override fun contentToString(): String = "[语音消息]"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 在线语音消息, 即从消息事件中接收到的语音消息.
|
||||
*
|
||||
* [OnlineAudio] 可以获取[语音长度][length]以及[下载链接][urlForDownload].
|
||||
*
|
||||
* [OnlineAudio] 仅可以从事件中的[消息链][MessageChain]接收, 不可手动构造. 若需要手动构造, 请使用 [OfflineAudio.Factory.create] 构造 [离线语音][OfflineAudio].
|
||||
*
|
||||
* ### 序列化支持
|
||||
*
|
||||
* [OnlineAudio] 支持序列化. 可使用 [MessageChain.serializeToJsonString] 以及 [MessageChain.deserializeFromJsonString].
|
||||
* 也可以在 [MessageSerializers.serializersModule] 获取到 [OnlineAudio] 的 [KSerializer].
|
||||
*
|
||||
* 要获取更多有关序列化的信息, 参阅 [MessageSerializers].
|
||||
*
|
||||
* ### 不建议自行实现该接口
|
||||
*
|
||||
* [OnlineAudio] 不稳定, 将来可能会增加新的抽象属性或方法而导致不兼容. 仅可以使用该接口而不能继承或实现它.
|
||||
*
|
||||
* @since 2.7
|
||||
* @see OfflineAudio
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface OnlineAudio : Audio { // 协议实现
|
||||
/**
|
||||
* 下载链接 HTTP URL.
|
||||
* @return `"http://xxx"`
|
||||
*/
|
||||
public val urlForDownload: String
|
||||
|
||||
/**
|
||||
* 语音长度秒数
|
||||
*/
|
||||
public val length: Long
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Audio, OnlineAudio>(Audio, { it.safeCast() }) {
|
||||
|
||||
public const val SERIAL_NAME: String = "OnlineAudio"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 离线语音消息.
|
||||
*
|
||||
* [OfflineAudio] 仅拥有协议上必要的五个属性:
|
||||
* - 文件名 [filename]
|
||||
* - 文件 MD5 [fileMd5]
|
||||
* - 文件大小 [fileSize]
|
||||
* - 编码方式 [codec]
|
||||
* - 额外数据 [extraData]
|
||||
*
|
||||
* [OfflineAudio] 可由本地 [ExternalResource] 经过 [AudioSupported.uploadAudio] 上传到服务器得到, 故无[下载链接][OnlineAudio.urlForDownload].
|
||||
*
|
||||
* [OfflineAudio] 同时还可以用做自定义构造 [Audio] 实例, 使用 [OfflineAudio.Factory.create] 可通过上述五个必要参数获得 [OfflineAudio] 实例.
|
||||
*
|
||||
* ### 序列化支持
|
||||
*
|
||||
* [OfflineAudio] 支持序列化. 可使用 [MessageChain.serializeToJsonString] 以及 [MessageChain.deserializeFromJsonString].
|
||||
* 也可以在 [MessageSerializers.serializersModule] 获取到 [OfflineAudio] 的 [KSerializer].
|
||||
*
|
||||
* 要获取更多有关序列化的信息, 参阅 [MessageSerializers].
|
||||
*
|
||||
* ### 不建议自行实现该接口
|
||||
*
|
||||
* [OfflineAudio] 不稳定, 将来可能会增加新的抽象属性或方法而导致不兼容. 仅可以使用该接口而不能继承或实现它.
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
@NotStableForInheritance
|
||||
public interface OfflineAudio : Audio {
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<Audio, OfflineAudio>(Audio, { it.safeCast() }) {
|
||||
public const val SERIAL_NAME: String = "OfflineAudio"
|
||||
}
|
||||
|
||||
public interface Factory {
|
||||
/**
|
||||
* 构造 [OfflineAudio]. 有关参数的含义, 参考 [Audio].
|
||||
*
|
||||
* 在 Kotlin 可以使用类构造器的函数 [OfflineAudio]: `OfflineAudio(...)`
|
||||
*/
|
||||
public fun create(
|
||||
filename: String,
|
||||
fileMd5: ByteArray,
|
||||
fileSize: Long,
|
||||
codec: AudioCodec,
|
||||
extraData: ByteArray?,
|
||||
): OfflineAudio
|
||||
|
||||
/**
|
||||
* 使用 [OnlineAudio] 的信息构造 [OfflineAudio].
|
||||
*
|
||||
* 在 Kotlin 可以使用类构造器的函数 [OfflineAudio]: `OfflineAudio(...)`
|
||||
*/
|
||||
public fun from(onlineAudio: OnlineAudio): OfflineAudio = onlineAudio.run {
|
||||
create(filename, fileMd5, fileSize, codec, extraData)
|
||||
}
|
||||
|
||||
public companion object INSTANCE :
|
||||
Factory by loadService("net.mamoe.mirai.internal.message.OfflineAudioFactoryImpl")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 [OfflineAudio]. 有关参数的含义, 参考 [Audio].
|
||||
* @since 2.7
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun OfflineAudio(
|
||||
filename: String,
|
||||
fileMd5: ByteArray,
|
||||
fileSize: Long,
|
||||
codec: AudioCodec,
|
||||
extraData: ByteArray?,
|
||||
): OfflineAudio = OfflineAudio.Factory.create(filename, fileMd5, fileSize, codec, extraData)
|
||||
|
||||
/**
|
||||
* 使用 [OnlineAudio] 的信息构造 [OfflineAudio].
|
||||
* @since 2.7
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun OfflineAudio(
|
||||
onlineAudio: OnlineAudio
|
||||
): OfflineAudio = OfflineAudio.Factory.from(onlineAudio)
|
||||
|
||||
|
||||
/**
|
||||
* 语音编码方式.
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
@Serializable(AudioCodec.AsIntSerializer::class)
|
||||
public enum class AudioCodec(
|
||||
public val id: Int,
|
||||
public val formatName: String,
|
||||
) {
|
||||
/**
|
||||
* 低音质编码格式
|
||||
*/
|
||||
AMR(0, "amr"),
|
||||
|
||||
/**
|
||||
* 高音质编码格式
|
||||
*/
|
||||
SILK(1, "silk");
|
||||
|
||||
public companion object {
|
||||
private val VALUES = values()
|
||||
|
||||
@JvmStatic
|
||||
public fun fromId(id: Int): AudioCodec = VALUES.first { it.id == id }
|
||||
|
||||
@JvmStatic
|
||||
public fun fromFormatName(formatName: String): AudioCodec = VALUES.first { it.formatName == formatName }
|
||||
|
||||
@JvmStatic
|
||||
public fun fromIdOrNull(id: Int): AudioCodec? = VALUES.find { it.id == id }
|
||||
|
||||
@JvmStatic
|
||||
public fun fromFormatNameOrNull(formatName: String): AudioCodec? =
|
||||
VALUES.find { it.formatName == formatName }
|
||||
}
|
||||
|
||||
internal object AsIntSerializer : KSerializer<AudioCodec> by Int.serializer().map(
|
||||
Int.serializer().descriptor,
|
||||
deserialize = { fromId(it) },
|
||||
serialize = { id }
|
||||
)
|
||||
}
|
@ -7,16 +7,20 @@
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("MessageUtils")
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsVoice
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
|
||||
/**
|
||||
@ -49,56 +53,68 @@ public abstract class PttMessage : MessageContent {
|
||||
@MiraiInternalApi
|
||||
@Transient
|
||||
public var pttInternalInstance: Any? = null
|
||||
set(value) {
|
||||
field = value
|
||||
_pttInternalInstanceSerializeCache = null
|
||||
}
|
||||
|
||||
@MiraiInternalApi
|
||||
protected val pttInternalInstanceSerializeCache: String
|
||||
get() {
|
||||
_pttInternalInstanceSerializeCache?.let { return it }
|
||||
return Mirai.serializePttElem(pttInternalInstance).also {
|
||||
_pttInternalInstanceSerializeCache = it
|
||||
}
|
||||
}
|
||||
|
||||
@Transient
|
||||
private var _pttInternalInstanceSerializeCache: String? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 语音消息, 目前只支持接收和转发
|
||||
* 已弃用的旧版本语音消息.
|
||||
*
|
||||
* 目前, 使用 [Voice] 类型是稳定的, 但调用 [Voice] 中的属性 [fileName], [md5], [fileSize] 是不稳定的. 语音的序列化也可能会在未来有变动.
|
||||
* [Voice] 由于有设计缺陷已弃用且可能会在将来版本删除, 请使用 [Audio].
|
||||
*
|
||||
* ## 使用语音
|
||||
* ## 迁移指南
|
||||
*
|
||||
* 可以通过 [ExternalResource.uploadAsVoice] 或者 [Group.uploadVoice] 上传语音文件到服务器, 得到 [Voice] 实例. 但这不会发送给目标群.
|
||||
* 上传后需要通过 [Group.sendMessage] 发送 [Voice] 实例.
|
||||
*
|
||||
* [Voice] 实例可以通过序列化方式保存. 下次可以用它发送因而不需要上传. 但可能由于未来服务器更新, 这项功能就不稳定. 因此建议总是上传音频文件而不要保存 [Voice].
|
||||
* - 将使用的 [Voice] 类型替换为 [Audio] 类型
|
||||
* - 将 [Group.uploadVoice] 替换为 [Group.uploadAudio]
|
||||
* - 如果有必须使用旧 [Voice] 类型的情况, 请使用 [Audio.toVoice]
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
@Serializable(Voice.Serializer::class) // experimental
|
||||
@Suppress("DuplicatedCode", "DEPRECATION")
|
||||
@Serializable
|
||||
@SerialName(Voice.SERIAL_NAME)
|
||||
public class Voice @MiraiInternalApi constructor(
|
||||
@Deprecated(
|
||||
"Please use Audio instead.",
|
||||
replaceWith = ReplaceWith("Audio", "net.mamoe.mirai.message.data.Audio"),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
public open class Voice @MiraiInternalApi constructor(
|
||||
@MiraiExperimentalApi public override val fileName: String,
|
||||
@MiraiExperimentalApi public override val md5: ByteArray,
|
||||
@MiraiExperimentalApi public override val fileSize: Long,
|
||||
|
||||
@MiraiInternalApi public val codec: Int = 0,
|
||||
@SerialName("codec") @MiraiInternalApi public val _codec: Int = 0,
|
||||
private val _url: String
|
||||
) : PttMessage() {
|
||||
|
||||
public companion object Key : AbstractPolymorphicMessageKey<PttMessage, Voice>(PttMessage, { it.safeCast() }) {
|
||||
public const val SERIAL_NAME: String = "Voice"
|
||||
|
||||
/**
|
||||
* 将 2.7 新增的 [Audio] 转为旧版本的 [Voice], 以兼容某些情况.
|
||||
*
|
||||
* @see Audio.toVoice
|
||||
* @since 2.7
|
||||
*/
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated(
|
||||
"Please consider migrating to Audio",
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
@JvmStatic
|
||||
public fun fromAudio(audio: Audio): Voice {
|
||||
audio.run {
|
||||
return Voice(
|
||||
filename,
|
||||
fileMd5,
|
||||
fileSize,
|
||||
codec.id,
|
||||
if (this is OnlineAudio) kotlin.runCatching { urlForDownload }.getOrElse { "" } else ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载链接 HTTP URL.
|
||||
*/
|
||||
public val url: String?
|
||||
public open val url: String?
|
||||
get() = when {
|
||||
_url.isBlank() -> null
|
||||
_url.startsWith("http") -> _url
|
||||
@ -115,69 +131,54 @@ public class Voice @MiraiInternalApi constructor(
|
||||
|
||||
public override fun contentToString(): String = "[语音消息]"
|
||||
|
||||
/**
|
||||
* 转换为 2.7 新增的 [Audio], 以兼容某些无法迁移的情况.
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
public fun toAudio(): Audio {
|
||||
val voice = this
|
||||
return OfflineAudio(
|
||||
voice.fileName,
|
||||
voice.md5,
|
||||
voice.fileSize,
|
||||
AudioCodec.fromIdOrNull(voice._codec) ?: AudioCodec.SILK,
|
||||
byteArrayOf()
|
||||
)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Voice) return false
|
||||
|
||||
if (this.pttInternalInstance != null && other.pttInternalInstance != null) {
|
||||
if (this.pttInternalInstance == other.pttInternalInstance)
|
||||
return true
|
||||
// strict
|
||||
return this.pttInternalInstanceSerializeCache == other.pttInternalInstanceSerializeCache
|
||||
}
|
||||
|
||||
if (fileName != other.fileName) return false
|
||||
if (!md5.contentEquals(other.md5)) return false
|
||||
if (fileSize != other.fileSize) return false
|
||||
if (codec != other.codec) return false
|
||||
if (_codec != other._codec) return false
|
||||
if (_url != other._url) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
if (pttInternalInstance != null)
|
||||
return pttInternalInstanceSerializeCache.hashCode()
|
||||
|
||||
var result = fileName.hashCode()
|
||||
result = 12 * result + md5.contentHashCode()
|
||||
result = 54 * result + fileSize.hashCode()
|
||||
result = 33 * result + codec
|
||||
result = 33 * result + _codec
|
||||
result = 15 * result + _url.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public object Serializer : KSerializer<Voice> by VoiceS.serializer().map(
|
||||
resultantDescriptor = VoiceS.serializer().descriptor.copy(SERIAL_NAME),
|
||||
deserialize = {
|
||||
Voice(
|
||||
fileName = it.fileName,
|
||||
md5 = it.md5,
|
||||
fileSize = it.fileSize,
|
||||
codec = it.codec,
|
||||
_url = it._url,
|
||||
).also { v -> v.pttInternalInstance = Mirai.deserializePttElem(it.ptt) }
|
||||
},
|
||||
serialize = {
|
||||
VoiceS(
|
||||
fileName = it.fileName,
|
||||
md5 = it.md5,
|
||||
fileSize = it.fileSize,
|
||||
_url = it._url,
|
||||
codec = it.codec,
|
||||
ptt = Mirai.serializePttElem(it.pttInternalInstance)
|
||||
)
|
||||
}
|
||||
) {
|
||||
@Serializable
|
||||
@SerialName(SERIAL_NAME)
|
||||
private class VoiceS(
|
||||
val fileName: String,
|
||||
val md5: ByteArray,
|
||||
val fileSize: Long,
|
||||
val codec: Int,
|
||||
val _url: String,
|
||||
val ptt: String = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 将 2.7 新增的 [Audio] 转为旧版本的 [Voice], 以兼容某些情况.
|
||||
*
|
||||
* @since 2.7
|
||||
*/
|
||||
@Suppress("DEPRECATION", "DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated(
|
||||
"Please migrate to Audio",
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
@JvmSynthetic
|
||||
public inline fun Audio.toVoice(): Voice = Voice.fromAudio(this)
|
@ -19,13 +19,12 @@ import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Contact.Companion.sendImage
|
||||
import net.mamoe.mirai.contact.Contact.Companion.uploadImage
|
||||
import net.mamoe.mirai.contact.FileSupported
|
||||
import net.mamoe.mirai.contact.VoiceSupported
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.internal.utils.ExternalResourceImplByByteArray
|
||||
import net.mamoe.mirai.internal.utils.ExternalResourceImplByFile
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.FileMessage
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.Voice
|
||||
import net.mamoe.mirai.message.data.sendTo
|
||||
import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo
|
||||
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
|
||||
@ -416,58 +415,15 @@ public interface ExternalResource : Closeable {
|
||||
// region uploadAsVoice
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 将文件作为语音上传后构造 [Voice]. 上传后只会得到 [Voice] 实例, 而不会将语音发送到目标群或好友.
|
||||
*
|
||||
* **服务器仅支持音频格式 `silk` 或 `amr`**. 需要调用方手动[关闭资源][ExternalResource.close].
|
||||
*
|
||||
* @throws OverFileSizeMaxException
|
||||
* @since 2.7
|
||||
* @see VoiceSupported.uploadVoice
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@JvmBlockingBridge
|
||||
@JvmStatic
|
||||
public suspend fun ExternalResource.uploadAsVoice(contact: VoiceSupported): Voice {
|
||||
return contact.uploadVoice(this)
|
||||
}
|
||||
|
||||
@JvmBlockingBridge
|
||||
@JvmStatic
|
||||
@Deprecated("For binary compatibility", level = DeprecationLevel.WARNING)
|
||||
@JvmName("uploadAsVoice")
|
||||
public suspend fun ExternalResource.uploadAsVoice(contact: Contact): Voice {
|
||||
if (contact is VoiceSupported) return contact.uploadVoice(this)
|
||||
@Deprecated("Use `contact.uploadAudio(resource)` instead", level = DeprecationLevel.WARNING)
|
||||
public suspend fun ExternalResource.uploadAsVoice(contact: Contact): net.mamoe.mirai.message.data.Voice {
|
||||
@Suppress("DEPRECATION")
|
||||
if (contact is Group) return contact.uploadVoice(this)
|
||||
else throw UnsupportedOperationException("Contact `$contact` is not supported uploading voice")
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 [InputStream] 到临时文件并将其作为语音上传至 [contact] 后构造 [Voice],
|
||||
* 上传后只会得到 [Voice] 实例, 而不会将语音发送到目标群或好友
|
||||
*
|
||||
* 注意:本函数不会关闭流.
|
||||
*
|
||||
* @since 2.7
|
||||
* @throws OverFileSizeMaxException
|
||||
* @see VoiceSupported.uploadVoice
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmBlockingBridge
|
||||
public suspend fun InputStream.uploadAsVoice(contact: VoiceSupported): Voice =
|
||||
runBIO { toExternalResource() }.withUse { uploadAsVoice(contact) }
|
||||
|
||||
/**
|
||||
* 将文件作为语音上传后构造 [Voice].
|
||||
* 上传后只会得到 [Voice] 实例, 而不会将语音发送到目标群或好友
|
||||
*
|
||||
* @since 2.7
|
||||
* @throws OverFileSizeMaxException
|
||||
* @see VoiceSupported.uploadVoice
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmBlockingBridge
|
||||
public suspend fun File.uploadAsVoice(contact: VoiceSupported): Voice =
|
||||
toExternalResource().withUse { uploadAsVoice(contact) }
|
||||
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ public fun UByteArray.toUHexString(separator: String = " ", offset: Int = 0, len
|
||||
}
|
||||
|
||||
public inline fun ByteArray.encodeToString(offset: Int = 0, charset: Charset = Charsets.UTF_8): String =
|
||||
kotlinx.io.core.String(this, charset = charset, offset = offset, length = this.size - offset)
|
||||
String(this, charset = charset, offset = offset, length = this.size - offset)
|
||||
|
||||
public expect fun ByteArray.encodeBase64(): String
|
||||
public expect fun String.decodeBase64(): ByteArray
|
||||
|
@ -124,6 +124,14 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
||||
UnsupportedMessageImpl::class,
|
||||
UnsupportedMessageImpl.serializer()
|
||||
)
|
||||
MessageSerializers.registerSerializer(
|
||||
OnlineAudioImpl::class,
|
||||
OnlineAudioImpl.serializer()
|
||||
)
|
||||
MessageSerializers.registerSerializer(
|
||||
OfflineAudioImpl::class,
|
||||
OfflineAudioImpl.serializer()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -965,14 +973,4 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun serializePttElem(ptt: Any?): String {
|
||||
if (ptt !is ImMsgBody.Ptt) return ""
|
||||
return ptt.toByteArray(ImMsgBody.Ptt.serializer()).toUHexString()
|
||||
}
|
||||
|
||||
override fun deserializePttElem(ptt: String): Any? {
|
||||
if (ptt.isBlank()) return null
|
||||
return ptt.hexToBytes().loadAs(ImMsgBody.Ptt.serializer())
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,7 @@
|
||||
|
||||
@file:OptIn(LowLevelApi::class)
|
||||
@file:Suppress(
|
||||
"EXPERIMENTAL_API_USAGE",
|
||||
"DEPRECATION_ERROR",
|
||||
"NOTHING_TO_INLINE",
|
||||
"INVISIBLE_MEMBER",
|
||||
"INVISIBLE_REFERENCE"
|
||||
)
|
||||
|
||||
package net.mamoe.mirai.internal.contact
|
||||
@ -28,18 +24,19 @@ import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
|
||||
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
|
||||
import net.mamoe.mirai.internal.message.OfflineAudioImpl
|
||||
import net.mamoe.mirai.internal.network.highway.*
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x346
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.audioCodec
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
|
||||
import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.Voice
|
||||
import net.mamoe.mirai.message.data.OfflineAudio
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.recoverCatchingSuppressed
|
||||
import net.mamoe.mirai.utils.toByteArray
|
||||
@ -78,10 +75,9 @@ internal class FriendImpl(
|
||||
"Friend ${this.id} had already been deleted"
|
||||
}
|
||||
bot.network.run {
|
||||
FriendList.DelFriend.invoke(bot.client, this@FriendImpl)
|
||||
.sendAndExpect<FriendList.DelFriend.Response>().also {
|
||||
check(it.isSuccess) { "delete friend failed: ${it.resultCode}" }
|
||||
}
|
||||
FriendList.DelFriend.invoke(bot.client, this@FriendImpl).sendAndExpect().also {
|
||||
check(it.isSuccess) { "delete friend failed: ${it.resultCode}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,19 +91,13 @@ internal class FriendImpl(
|
||||
|
||||
override fun toString(): String = "Friend($id)"
|
||||
|
||||
override suspend fun uploadVoice(resource: ExternalResource): Voice = bot.network.run {
|
||||
val voice = Voice(
|
||||
"${resource.md5.toUHexString("")}.amr",
|
||||
resource.md5,
|
||||
resource.size,
|
||||
resource.voiceCodec,
|
||||
""
|
||||
)
|
||||
override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio = bot.network.run {
|
||||
var audio: OfflineAudioImpl? = null
|
||||
kotlin.runCatching {
|
||||
val resp = Highway.uploadResourceBdh(
|
||||
bot = bot,
|
||||
resource = resource,
|
||||
kind = ResourceKind.PRIVATE_VOICE,
|
||||
kind = ResourceKind.PRIVATE_AUDIO,
|
||||
commandId = 26,
|
||||
extendInfo = PttStore.C2C.createC2CPttStoreBDHExt(bot, this@FriendImpl.uin, resource)
|
||||
.toByteArray(Cmd0x346.ReqBody.serializer())
|
||||
@ -117,14 +107,20 @@ internal class FriendImpl(
|
||||
if (c346resp.msgApplyUploadRsp == null) {
|
||||
error("Upload failed")
|
||||
}
|
||||
voice.pttInternalInstance = ImMsgBody.Ptt(
|
||||
fileType = 4,
|
||||
srcUin = bot.uin,
|
||||
fileUuid = c346resp.msgApplyUploadRsp.uuid,
|
||||
audio = OfflineAudioImpl(
|
||||
filename = "${resource.md5.toUHexString("")}.amr",
|
||||
fileMd5 = resource.md5,
|
||||
fileName = resource.md5 + ".amr".toByteArray(),
|
||||
fileSize = resource.size.toInt(),
|
||||
boolValid = true,
|
||||
fileSize = resource.size,
|
||||
codec = resource.audioCodec,
|
||||
originalPtt = ImMsgBody.Ptt(
|
||||
fileType = 4,
|
||||
srcUin = bot.uin,
|
||||
fileUuid = c346resp.msgApplyUploadRsp.uuid,
|
||||
fileMd5 = resource.md5,
|
||||
fileName = resource.md5 + ".amr".toByteArray(),
|
||||
fileSize = resource.size.toInt(),
|
||||
boolValid = true,
|
||||
)
|
||||
)
|
||||
}.recoverCatchingSuppressed {
|
||||
when (val resp = PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect<Any>()) {
|
||||
@ -133,24 +129,30 @@ internal class FriendImpl(
|
||||
bot,
|
||||
resp.uploadIpList.zip(resp.uploadPortList),
|
||||
resource.size,
|
||||
ResourceKind.GROUP_VOICE,
|
||||
ResourceKind.GROUP_AUDIO,
|
||||
ChannelKind.HTTP
|
||||
) { ip, port ->
|
||||
Mirai.Http.postPtt(ip, port, resource, resp.uKey, resp.fileKey)
|
||||
}
|
||||
voice.pttInternalInstance = ImMsgBody.Ptt(
|
||||
fileType = 4,
|
||||
srcUin = bot.uin,
|
||||
fileUuid = resp.fileId.toByteArray(),
|
||||
audio = OfflineAudioImpl(
|
||||
filename = "${resource.md5.toUHexString("")}.amr",
|
||||
fileMd5 = resource.md5,
|
||||
fileName = resource.md5 + ".amr".toByteArray(),
|
||||
fileSize = resource.size.toInt(),
|
||||
boolValid = true,
|
||||
fileSize = resource.size,
|
||||
codec = resource.audioCodec,
|
||||
originalPtt = ImMsgBody.Ptt(
|
||||
fileType = 4,
|
||||
srcUin = bot.uin,
|
||||
fileUuid = resp.fileId.toByteArray(),
|
||||
fileMd5 = resource.md5,
|
||||
fileName = resource.md5 + ".amr".toByteArray(),
|
||||
fileSize = resource.size.toInt(),
|
||||
boolValid = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}.getOrThrow()
|
||||
|
||||
return voice
|
||||
return audio!!
|
||||
}
|
||||
}
|
||||
|
@ -23,22 +23,25 @@ import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.announcement.AnnouncementsImpl
|
||||
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
|
||||
import net.mamoe.mirai.internal.message.OfflineAudioImpl
|
||||
import net.mamoe.mirai.internal.message.OfflineGroupImage
|
||||
import net.mamoe.mirai.internal.network.components.BdhSession
|
||||
import net.mamoe.mirai.internal.network.handler.NetworkHandler
|
||||
import net.mamoe.mirai.internal.network.handler.logger
|
||||
import net.mamoe.mirai.internal.network.highway.ChannelKind
|
||||
import net.mamoe.mirai.internal.network.highway.Highway
|
||||
import net.mamoe.mirai.internal.network.highway.ResourceKind.GROUP_AUDIO
|
||||
import net.mamoe.mirai.internal.network.highway.ResourceKind.GROUP_IMAGE
|
||||
import net.mamoe.mirai.internal.network.highway.ResourceKind.GROUP_VOICE
|
||||
import net.mamoe.mirai.internal.network.highway.postPtt
|
||||
import net.mamoe.mirai.internal.network.highway.tryServersUpload
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x388
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopEssenceMsgManager
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.audioCodec
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.list.ProfileService
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
|
||||
import net.mamoe.mirai.internal.utils.GroupPkgMsgParsingCache
|
||||
import net.mamoe.mirai.internal.utils.RemoteFileImpl
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||
@ -204,32 +207,10 @@ internal class GroupImpl(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
||||
override suspend fun uploadVoice(resource: ExternalResource): Voice {
|
||||
return bot.network.run {
|
||||
kotlin.runCatching {
|
||||
val (_) = Highway.uploadResourceBdh(
|
||||
bot = bot,
|
||||
resource = resource,
|
||||
kind = GROUP_VOICE,
|
||||
commandId = 29,
|
||||
extendInfo = PttStore.GroupPttUp.createTryUpPttPack(bot.id, id, resource)
|
||||
.toByteArray(Cmd0x388.ReqBody.serializer()),
|
||||
)
|
||||
}.recoverCatchingSuppressed {
|
||||
when (val resp = PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect<Any>()) {
|
||||
is PttStore.GroupPttUp.Response.RequireUpload -> {
|
||||
tryServersUpload(
|
||||
bot,
|
||||
resp.uploadIpList.zip(resp.uploadPortList),
|
||||
resource.size,
|
||||
GROUP_VOICE,
|
||||
ChannelKind.HTTP
|
||||
) { ip, port ->
|
||||
Mirai.Http.postPtt(ip, port, resource, resp.uKey, resp.fileKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.getOrThrow()
|
||||
uploadAudioResource(resource)
|
||||
|
||||
// val body = resp?.loadAs(Cmd0x388.RspBody.serializer())
|
||||
// ?.msgTryupPttRsp
|
||||
@ -243,6 +224,51 @@ internal class GroupImpl(
|
||||
""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun uploadAudioResource(resource: ExternalResource) {
|
||||
kotlin.runCatching {
|
||||
val (_) = Highway.uploadResourceBdh(
|
||||
bot = bot,
|
||||
resource = resource,
|
||||
kind = GROUP_AUDIO,
|
||||
commandId = 29,
|
||||
extendInfo = PttStore.GroupPttUp.createTryUpPttPack(bot.id, id, resource)
|
||||
.toByteArray(Cmd0x388.ReqBody.serializer()),
|
||||
)
|
||||
}.recoverCatchingSuppressed {
|
||||
when (val resp = PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect(bot)) {
|
||||
is PttStore.GroupPttUp.Response.RequireUpload -> {
|
||||
tryServersUpload(
|
||||
bot,
|
||||
resp.uploadIpList.zip(resp.uploadPortList),
|
||||
resource.size,
|
||||
GROUP_AUDIO,
|
||||
ChannelKind.HTTP
|
||||
) { ip, port ->
|
||||
Mirai.Http.postPtt(ip, port, resource, resp.uKey, resp.fileKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.getOrThrow()
|
||||
}
|
||||
|
||||
override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio {
|
||||
return bot.network.run {
|
||||
uploadAudioResource(resource)
|
||||
|
||||
// val body = resp?.loadAs(Cmd0x388.RspBody.serializer())
|
||||
// ?.msgTryupPttRsp
|
||||
// ?.singleOrNull()?.fileKey ?: error("Group voice highway transfer succeed but failed to find fileKey")
|
||||
|
||||
OfflineAudioImpl(
|
||||
filename = "${resource.md5.toUHexString("")}.amr",
|
||||
fileMd5 = resource.md5,
|
||||
fileSize = resource.size,
|
||||
codec = resource.audioCodec,
|
||||
originalPtt = null,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
302
mirai-core/src/commonMain/kotlin/message/OnlineAudioImpl.kt
Normal file
302
mirai-core/src/commonMain/kotlin/message/OnlineAudioImpl.kt
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.message
|
||||
|
||||
import kotlinx.io.core.toByteArray
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.copy
|
||||
import net.mamoe.mirai.utils.map
|
||||
|
||||
|
||||
/**
|
||||
* ## Audio Implementation Overview
|
||||
*
|
||||
* ```
|
||||
* (api)Audio
|
||||
* |
|
||||
* /------------------\
|
||||
* (api)OnlineAudio (api)OfflineAudio
|
||||
* | |
|
||||
* | |
|
||||
* (core)OnlineAudioImpl (core)OfflineAudioImpl
|
||||
* ```
|
||||
*
|
||||
* - [OnlineAudioImpl]: 实现从 [ImMsgBody.Ptt] 解析
|
||||
* - [OfflineAudioImpl]: 支持用户手动构造
|
||||
*
|
||||
* ## Equality
|
||||
*
|
||||
* - [OnlineAudio] != [OfflineAudio]
|
||||
*
|
||||
* ## Converting [Audio] to [ImMsgBody.Ptt]
|
||||
*
|
||||
* Always call [Audio.toPtt]
|
||||
*/
|
||||
internal interface AudioPttSupport : MessageContent { // Audio is sealed in mirai-core-api
|
||||
/**
|
||||
* 原协议数据. 用于在接受到其他用户发送的语音时能按照原样发回.
|
||||
*/
|
||||
val originalPtt: ImMsgBody.Ptt?
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal class AudioExtraData(
|
||||
@ProtoNumber(1) val ptt: ImMsgBody.Ptt?,
|
||||
) : ProtoBuf {
|
||||
fun toByteArray(): ByteArray {
|
||||
return Wrapper(CURRENT_VERSION, this).toByteArray(Wrapper.serializer())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Serializable
|
||||
class Wrapper(
|
||||
@ProtoNumber(1) val version: Int,
|
||||
@ProtoNumber(2) val v1: AudioExtraData,
|
||||
) : ProtoBuf
|
||||
|
||||
private const val CURRENT_VERSION = 1
|
||||
|
||||
|
||||
fun loadFrom(byteArray: ByteArray?): AudioExtraData? {
|
||||
byteArray ?: return null
|
||||
return kotlin.runCatching {
|
||||
byteArray.loadAs(Wrapper.serializer()).v1
|
||||
}.getOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Audio.toPtt(): ImMsgBody.Ptt {
|
||||
if (this is AudioPttSupport) {
|
||||
this.originalPtt?.let { return it }
|
||||
}
|
||||
return ImMsgBody.Ptt(
|
||||
fileName = this.filename.toByteArray(),
|
||||
fileMd5 = this.fileMd5,
|
||||
boolValid = true,
|
||||
fileSize = this.fileSize.toInt(),
|
||||
fileType = 4,
|
||||
pbReserve = byteArrayOf(0),
|
||||
format = this.codec.id
|
||||
)
|
||||
}
|
||||
|
||||
@SerialName(OnlineAudio.SERIAL_NAME)
|
||||
@Serializable(OnlineAudioImpl.Serializer::class)
|
||||
internal class OnlineAudioImpl(
|
||||
override val filename: String,
|
||||
override val fileMd5: ByteArray,
|
||||
override val fileSize: Long,
|
||||
override val codec: AudioCodec,
|
||||
url: String,
|
||||
override val length: Long,
|
||||
override val originalPtt: ImMsgBody.Ptt?,
|
||||
) : OnlineAudio, AudioPttSupport {
|
||||
private val _url = refineUrl(url)
|
||||
|
||||
override val extraData: ByteArray? by lazy {
|
||||
AudioExtraData(originalPtt).toByteArray()
|
||||
}
|
||||
|
||||
override val urlForDownload: String
|
||||
get() = _url.takeIf { it.isNotBlank() }
|
||||
?: throw UnsupportedOperationException("Could not fetch URL for audio $filename")
|
||||
|
||||
private val _stringValue: String by lazy { "[mirai:audio:${filename}]" }
|
||||
override fun toString(): String = _stringValue
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as OnlineAudioImpl
|
||||
|
||||
if (filename != other.filename) return false
|
||||
if (!fileMd5.contentEquals(other.fileMd5)) return false
|
||||
if (fileSize != other.fileSize) return false
|
||||
if (_url != other._url) return false
|
||||
if (codec != other.codec) return false
|
||||
if (length != other.length) return false
|
||||
if (originalPtt != other.originalPtt) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + filename.hashCode()
|
||||
result = 31 * result + fileMd5.contentHashCode()
|
||||
result = 31 * result + fileSize.hashCode()
|
||||
result = 31 * result + _url.hashCode()
|
||||
result = 31 * result + codec.hashCode()
|
||||
result = 31 * result + length.hashCode()
|
||||
result = 31 * result + originalPtt.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun refineUrl(url: String) = when {
|
||||
url.isBlank() -> ""
|
||||
url.startsWith("http") -> url
|
||||
url.startsWith("/") -> "$DOWNLOAD_URL$url"
|
||||
else -> "$DOWNLOAD_URL/$url"
|
||||
}
|
||||
|
||||
@Suppress("HttpUrlsUsage")
|
||||
const val DOWNLOAD_URL = "http://grouptalk.c2c.qq.com"
|
||||
}
|
||||
|
||||
object Serializer : KSerializer<OnlineAudioImpl> by Surrogate.serializer().map(
|
||||
resultantDescriptor = Surrogate.serializer().descriptor.copy(OnlineAudio.SERIAL_NAME),
|
||||
deserialize = {
|
||||
OnlineAudioImpl(
|
||||
filename = filename,
|
||||
fileMd5 = fileMd5,
|
||||
fileSize = fileSize,
|
||||
url = urlForDownload,
|
||||
codec = codec,
|
||||
length = length,
|
||||
originalPtt = AudioExtraData.loadFrom(extraData)?.ptt
|
||||
)
|
||||
},
|
||||
serialize = {
|
||||
Surrogate(
|
||||
filename = filename,
|
||||
fileMd5 = fileMd5,
|
||||
fileSize = fileSize,
|
||||
urlForDownload = urlForDownload,
|
||||
codec = codec,
|
||||
length = length,
|
||||
extraData = extraData
|
||||
)
|
||||
}
|
||||
) {
|
||||
@Serializable
|
||||
@SerialName(OnlineAudio.SERIAL_NAME)
|
||||
private class Surrogate(
|
||||
override val filename: String,
|
||||
override val fileMd5: ByteArray,
|
||||
override val fileSize: Long,
|
||||
override val codec: AudioCodec,
|
||||
override val length: Long,
|
||||
override val extraData: ByteArray?,
|
||||
override val urlForDownload: String,
|
||||
) : OnlineAudio {
|
||||
override fun toString(): String {
|
||||
return "Surrogate(filename='$filename', fileMd5=${fileMd5.contentToString()}, fileSize=$fileSize, codec=$codec, length=$length, extraData=${extraData.contentToString()}, urlForDownload='$urlForDownload')"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SerialName(OfflineAudio.SERIAL_NAME)
|
||||
@Serializable(OfflineAudioImpl.Serializer::class)
|
||||
internal class OfflineAudioImpl(
|
||||
override val filename: String,
|
||||
override val fileMd5: ByteArray,
|
||||
override val fileSize: Long,
|
||||
override val codec: AudioCodec,
|
||||
override val originalPtt: ImMsgBody.Ptt?,
|
||||
) : OfflineAudio, AudioPttSupport {
|
||||
constructor(
|
||||
filename: String,
|
||||
fileMd5: ByteArray,
|
||||
fileSize: Long,
|
||||
codec: AudioCodec,
|
||||
extraData: ByteArray?,
|
||||
) : this(filename, fileMd5, fileSize, codec, AudioExtraData.loadFrom(extraData)?.ptt)
|
||||
|
||||
override val extraData: ByteArray? by lazy {
|
||||
AudioExtraData(originalPtt).toByteArray()
|
||||
}
|
||||
|
||||
private val _stringValue: String by lazy { "[mirai:audio:${filename}]" }
|
||||
override fun toString(): String = _stringValue
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as OfflineAudioImpl
|
||||
|
||||
if (filename != other.filename) return false
|
||||
if (!fileMd5.contentEquals(other.fileMd5)) return false
|
||||
if (fileSize != other.fileSize) return false
|
||||
if (codec != other.codec) return false
|
||||
if (originalPtt != other.originalPtt) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = filename.hashCode()
|
||||
result = 31 * result + fileMd5.contentHashCode()
|
||||
result = 31 * result + fileSize.hashCode()
|
||||
result = 31 * result + codec.hashCode()
|
||||
result = 31 * result + originalPtt.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
object Serializer : KSerializer<OfflineAudioImpl> by Surrogate.serializer().map(
|
||||
resultantDescriptor = Surrogate.serializer().descriptor.copy(OfflineAudio.SERIAL_NAME),
|
||||
deserialize = {
|
||||
OfflineAudioImpl(
|
||||
filename = filename,
|
||||
fileMd5 = fileMd5,
|
||||
fileSize = fileSize,
|
||||
codec = codec,
|
||||
extraData = extraData,
|
||||
)
|
||||
},
|
||||
serialize = {
|
||||
Surrogate(
|
||||
filename = filename,
|
||||
fileMd5 = fileMd5,
|
||||
fileSize = fileSize,
|
||||
codec = codec,
|
||||
extraData = extraData,
|
||||
)
|
||||
}
|
||||
) {
|
||||
@Serializable
|
||||
@SerialName(OfflineAudio.SERIAL_NAME)
|
||||
private class Surrogate(
|
||||
override val filename: String,
|
||||
override val fileMd5: ByteArray,
|
||||
override val fileSize: Long,
|
||||
override val codec: AudioCodec,
|
||||
override val extraData: ByteArray?,
|
||||
) : OfflineAudio {
|
||||
override fun toString(): String {
|
||||
return "OfflineAudio(filename='$filename', fileMd5=${fileMd5.contentToString()}, fileSize=$fileSize, codec=$codec, extraData=${extraData.contentToString()})"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal class OfflineAudioFactoryImpl : OfflineAudio.Factory {
|
||||
override fun create(
|
||||
filename: String,
|
||||
fileMd5: ByteArray,
|
||||
fileSize: Long,
|
||||
codec: AudioCodec,
|
||||
extraData: ByteArray?
|
||||
): OfflineAudio = OfflineAudioImpl(filename, fileMd5, fileSize, codec, extraData)
|
||||
}
|
@ -18,15 +18,12 @@ import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
|
||||
import net.mamoe.mirai.internal.message.LightMessageRefiner.refineLight
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.cleanupRubbishMessageElements
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.joinToMessageChain
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.toVoice
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.toAudio
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.*
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.encodeToString
|
||||
import net.mamoe.mirai.utils.read
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import net.mamoe.mirai.utils.unzip
|
||||
import net.mamoe.mirai.utils.*
|
||||
|
||||
/**
|
||||
* 只在手动构造 [OfflineMessageSource] 时调用
|
||||
@ -91,7 +88,7 @@ private fun List<MsgComm.Msg>.toMessageChain(
|
||||
joinToMessageChain(elements, groupIdOrZero, messageSourceKind, bot, builder)
|
||||
|
||||
for (msg in messageList) {
|
||||
msg.msgBody.richText.ptt?.toVoice()?.let { builder.add(it) }
|
||||
msg.msgBody.richText.ptt?.toAudio()?.let { builder.add(it) }
|
||||
}
|
||||
|
||||
return builder.build().cleanupRubbishMessageElements()
|
||||
@ -516,11 +513,13 @@ internal object ReceiveMessageTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
fun ImMsgBody.Ptt.toVoice() = Voice(
|
||||
kotlinx.io.core.String(fileName),
|
||||
fileMd5,
|
||||
fileSize.toLong(),
|
||||
format,
|
||||
kotlinx.io.core.String(downPara)
|
||||
).also { it.pttInternalInstance = this }
|
||||
fun ImMsgBody.Ptt.toAudio() = OnlineAudioImpl(
|
||||
filename = fileName.encodeToString(),
|
||||
fileMd5 = fileMd5,
|
||||
fileSize = fileSize.toLongUnsigned(),
|
||||
codec = AudioCodec.fromId(format),
|
||||
url = downPara.encodeToString(),
|
||||
length = time.toLongUnsigned(),
|
||||
originalPtt = this,
|
||||
)
|
||||
}
|
@ -122,8 +122,8 @@ internal enum class ResourceKind(
|
||||
) {
|
||||
PRIVATE_IMAGE("private image"),
|
||||
GROUP_IMAGE("group image"),
|
||||
PRIVATE_VOICE("private voice"),
|
||||
GROUP_VOICE("group voice"),
|
||||
PRIVATE_AUDIO("private audio"),
|
||||
GROUP_AUDIO("group audio"),
|
||||
|
||||
GROUP_FILE("group file"),
|
||||
|
||||
|
@ -765,7 +765,69 @@ internal class ImMsgBody : ProtoBuf {
|
||||
@ProtoNumber(30) @JvmField val pbReserve: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoNumber(31) @JvmField val bytesPttUrls: List<ByteArray> = emptyList(),
|
||||
@ProtoNumber(32) @JvmField val downloadFlag: Int = 0,
|
||||
) : ProtoBuf
|
||||
) : ProtoBuf {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Ptt
|
||||
|
||||
if (fileType != other.fileType) return false
|
||||
if (srcUin != other.srcUin) return false
|
||||
if (!fileUuid.contentEquals(other.fileUuid)) return false
|
||||
if (!fileMd5.contentEquals(other.fileMd5)) return false
|
||||
if (!fileName.contentEquals(other.fileName)) return false
|
||||
if (fileSize != other.fileSize) return false
|
||||
if (!reserve.contentEquals(other.reserve)) return false
|
||||
if (fileId != other.fileId) return false
|
||||
if (serverIp != other.serverIp) return false
|
||||
if (serverPort != other.serverPort) return false
|
||||
if (boolValid != other.boolValid) return false
|
||||
if (!signature.contentEquals(other.signature)) return false
|
||||
if (!shortcut.contentEquals(other.shortcut)) return false
|
||||
if (!fileKey.contentEquals(other.fileKey)) return false
|
||||
if (magicPttIndex != other.magicPttIndex) return false
|
||||
if (voiceSwitch != other.voiceSwitch) return false
|
||||
if (!pttUrl.contentEquals(other.pttUrl)) return false
|
||||
if (!groupFileKey.contentEquals(other.groupFileKey)) return false
|
||||
if (time != other.time) return false
|
||||
if (!downPara.contentEquals(other.downPara)) return false
|
||||
if (format != other.format) return false
|
||||
if (!pbReserve.contentEquals(other.pbReserve)) return false
|
||||
if (bytesPttUrls != other.bytesPttUrls) return false
|
||||
if (downloadFlag != other.downloadFlag) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = fileType
|
||||
result = 31 * result + srcUin.hashCode()
|
||||
result = 31 * result + fileUuid.contentHashCode()
|
||||
result = 31 * result + fileMd5.contentHashCode()
|
||||
result = 31 * result + fileName.contentHashCode()
|
||||
result = 31 * result + fileSize
|
||||
result = 31 * result + reserve.contentHashCode()
|
||||
result = 31 * result + fileId
|
||||
result = 31 * result + serverIp
|
||||
result = 31 * result + serverPort
|
||||
result = 31 * result + boolValid.hashCode()
|
||||
result = 31 * result + signature.contentHashCode()
|
||||
result = 31 * result + shortcut.contentHashCode()
|
||||
result = 31 * result + fileKey.contentHashCode()
|
||||
result = 31 * result + magicPttIndex
|
||||
result = 31 * result + voiceSwitch
|
||||
result = 31 * result + pttUrl.contentHashCode()
|
||||
result = 31 * result + groupFileKey.contentHashCode()
|
||||
result = 31 * result + time
|
||||
result = 31 * result + downPara.contentHashCode()
|
||||
result = 31 * result + format
|
||||
result = 31 * result + pbReserve.contentHashCode()
|
||||
result = 31 * result + bytesPttUrls.hashCode()
|
||||
result = 31 * result + downloadFlag
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
internal class PubAccInfo(
|
||||
|
@ -145,7 +145,8 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
return response
|
||||
}
|
||||
|
||||
internal fun PttMessage.toPtt() = run {
|
||||
// old Voice
|
||||
private fun PttMessage.toPtt() = run {
|
||||
(this.pttInternalInstance as? ImMsgBody.Ptt)?.let { return it }
|
||||
ImMsgBody.Ptt(
|
||||
fileName = fileName.toByteArray(),
|
||||
@ -155,8 +156,9 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
fileType = 4,
|
||||
pbReserve = byteArrayOf(0),
|
||||
format = let {
|
||||
@Suppress("DEPRECATION")
|
||||
if (it is Voice) {
|
||||
it.codec
|
||||
it._codec
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@ -244,7 +246,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = subChain.toRichTextElems(messageTarget = targetFriend, withGeneralFlags = true),
|
||||
ptt = subChain[PttMessage]?.toPtt(),
|
||||
ptt = subChain.findPtt(),
|
||||
)
|
||||
)
|
||||
},
|
||||
@ -361,7 +363,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = subChain.toRichTextElems(messageTarget = targetGroup, withGeneralFlags = true),
|
||||
ptt = subChain[PttMessage]?.toPtt()
|
||||
ptt = subChain.findPtt()
|
||||
|
||||
)
|
||||
)
|
||||
@ -409,6 +411,9 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
||||
doFragmented = fragmented
|
||||
)
|
||||
}
|
||||
|
||||
private fun MessageChain.findPtt() =
|
||||
findIsInstance<Audio>()?.toPtt() ?: this[PttMessage]?.toPtt()
|
||||
/*
|
||||
= buildOutgoingUniPacket(client) {
|
||||
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
|
||||
|
@ -22,18 +22,21 @@ import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
|
||||
import net.mamoe.mirai.internal.utils.toIpV4AddressString
|
||||
import net.mamoe.mirai.message.data.AudioCodec
|
||||
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.utils.ExternalResource
|
||||
import net.mamoe.mirai.utils.encodeToString
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
|
||||
internal val ExternalResource.voiceCodec: Int
|
||||
internal inline val ExternalResource.voiceCodec: Int get() = audioCodec.id
|
||||
|
||||
internal val ExternalResource.audioCodec: AudioCodec
|
||||
get() {
|
||||
return when (formatName) {
|
||||
// 实际上 amr 是 0, 但用 1 也可以发. 为了避免 silk 错被以 amr 发送导致降音质就都用 1
|
||||
"amr" -> 1 // amr
|
||||
"silk" -> 1 // silk V3
|
||||
else -> 1 // use amr by default
|
||||
"amr" -> AudioCodec.SILK
|
||||
"silk" -> AudioCodec.SILK
|
||||
else -> AudioCodec.AMR // use amr by default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
#
|
||||
# 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/dev/LICENSE
|
||||
#
|
||||
|
||||
net.mamoe.mirai.internal.message.OfflineAudioFactoryImpl
|
75
mirai-core/src/commonTest/kotlin/message/AudioTest.kt
Normal file
75
mirai-core/src/commonTest/kotlin/message/AudioTest.kt
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.message
|
||||
|
||||
import net.mamoe.mirai.internal.message.OnlineAudioImpl.Companion.DOWNLOAD_URL
|
||||
import net.mamoe.mirai.internal.message.OnlineAudioImpl.Companion.refineUrl
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.test.AbstractTest
|
||||
import net.mamoe.mirai.message.data.AudioCodec
|
||||
import net.mamoe.mirai.message.data.OfflineAudio
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertContentEquals
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
internal class AudioTest : AbstractTest() {
|
||||
|
||||
@Test
|
||||
fun `test factory`() {
|
||||
assertEquals(
|
||||
OfflineAudio("name", byteArrayOf(), 1, AudioCodec.SILK, byteArrayOf()),
|
||||
OfflineAudio("name", byteArrayOf(), 1, AudioCodec.SILK, byteArrayOf())
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `invalid extraData is refreshed`() {
|
||||
assertContentEquals(
|
||||
OfflineAudio("test", byteArrayOf(), 1, AudioCodec.SILK, null).extraData,
|
||||
OfflineAudio("test", byteArrayOf(), 1, AudioCodec.SILK, byteArrayOf(1, 2, 3)).extraData,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test equality`() {
|
||||
assertEquals(
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, null),
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, null)
|
||||
)
|
||||
assertEquals(
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, null),
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, null)
|
||||
)
|
||||
assertEquals(
|
||||
OfflineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, originalPtt = null),
|
||||
OfflineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, originalPtt = null)
|
||||
)
|
||||
assertEquals(
|
||||
OfflineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, byteArrayOf()),
|
||||
OfflineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, byteArrayOf())
|
||||
)
|
||||
assertEquals(
|
||||
OfflineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, ImMsgBody.Ptt(srcUin = 2)),
|
||||
OfflineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, ImMsgBody.Ptt(srcUin = 2))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test refineUrl`() {
|
||||
assertFalse { DOWNLOAD_URL.endsWith("/") }
|
||||
|
||||
assertEquals("", refineUrl(""))
|
||||
assertEquals("$DOWNLOAD_URL/test", refineUrl("/test"))
|
||||
assertEquals("$DOWNLOAD_URL/test", refineUrl("test"))
|
||||
assertEquals("https://custom.com", refineUrl("https://custom.com"))
|
||||
assertEquals("http://localhost", refineUrl("http://localhost"))
|
||||
}
|
||||
}
|
@ -12,13 +12,12 @@ package net.mamoe.mirai.internal.message.data
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Polymorphic
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.serializer
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.internal.message.FileMessageImpl
|
||||
import net.mamoe.mirai.internal.message.MarketFaceImpl
|
||||
import net.mamoe.mirai.internal.message.OnlineAudioImpl
|
||||
import net.mamoe.mirai.internal.message.UnsupportedMessageImpl
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.utils._miraiContentToString
|
||||
@ -220,39 +219,100 @@ internal class MessageSerializationTest {
|
||||
|
||||
@Serializable
|
||||
data class V(
|
||||
val msg: Voice
|
||||
val msg: Audio
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class AudioTestStandard(
|
||||
val online: OnlineAudio,
|
||||
val offline: OfflineAudio,
|
||||
val onlineAsRec: Audio,
|
||||
val offlineAsRec: Audio,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test Voice serialization`() {
|
||||
val v = V(Voice("4517", byteArrayOf(14), 50, 3, "https://github.com"))
|
||||
println(v.serialize(V.serializer()))
|
||||
fun `test Audio standard`() {
|
||||
val origin = AudioTestStandard(
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, null),
|
||||
OfflineAudio("test", byteArrayOf(), 1, AudioCodec.SILK, null),
|
||||
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, null),
|
||||
OfflineAudio("test", byteArrayOf(), 1, AudioCodec.SILK, null),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
v.serialize(V.serializer()),
|
||||
v.serialize(V.serializer())
|
||||
.deserialize(V.serializer())
|
||||
.serialize(V.serializer())
|
||||
AudioCodec.SILK.id,
|
||||
format.encodeToJsonElement(origin).jsonObject["offline"]!!.jsonObject["codec"]!!.jsonPrimitive.content.toInt()
|
||||
) // use custom serializer
|
||||
|
||||
assertEquals(
|
||||
AudioCodec.SILK.id,
|
||||
format.encodeToJsonElement(origin).jsonObject["online"]!!.jsonObject["codec"]!!.jsonPrimitive.content.toInt()
|
||||
) // use custom serializer
|
||||
|
||||
assertEquals(
|
||||
"OnlineAudio",
|
||||
format.encodeToJsonElement(origin).jsonObject["online"]!!.jsonObject["type"]!!.jsonPrimitive.content
|
||||
)
|
||||
assertEquals(
|
||||
v,
|
||||
v.serialize(V.serializer()).deserialize(V.serializer())
|
||||
"OfflineAudio",
|
||||
format.encodeToJsonElement(origin).jsonObject["offline"]!!.jsonObject["type"]!!.jsonPrimitive.content
|
||||
)
|
||||
v.msg.pttInternalInstance = ImMsgBody.Ptt(
|
||||
srcUin = 1234567890,
|
||||
fileMd5 = byteArrayOf(14, 81, 37, 14),
|
||||
boolValid = true,
|
||||
format = 90,
|
||||
)
|
||||
println(v.serialize(V.serializer()))
|
||||
|
||||
assertEquals(
|
||||
v.serialize(V.serializer()),
|
||||
v.serialize(V.serializer())
|
||||
.deserialize(V.serializer())
|
||||
.serialize(V.serializer())
|
||||
"OnlineAudio",
|
||||
format.encodeToJsonElement(origin).jsonObject["onlineAsRec"]!!.jsonObject["type"]!!.jsonPrimitive.content
|
||||
)
|
||||
assertEquals(
|
||||
v,
|
||||
v.serialize(V.serializer()).deserialize(V.serializer())
|
||||
"OfflineAudio",
|
||||
format.encodeToJsonElement(origin).jsonObject["offlineAsRec"]!!.jsonObject["type"]!!.jsonPrimitive.content
|
||||
)
|
||||
|
||||
val result = origin.serialize().deserialize<AudioTestStandard>()
|
||||
|
||||
assertEquals(origin.online::class, result.online::class)
|
||||
assertEquals(origin.offline::class, result.offline::class)
|
||||
assertEquals(origin.onlineAsRec::class, result.onlineAsRec::class)
|
||||
assertEquals(origin.offlineAsRec::class, result.offlineAsRec::class)
|
||||
|
||||
assertEquals(origin.online, result.online)
|
||||
assertEquals(origin.offline, result.offline)
|
||||
assertEquals(origin.onlineAsRec, result.onlineAsRec)
|
||||
assertEquals(origin.offlineAsRec, result.offlineAsRec)
|
||||
|
||||
assertEquals(origin, result)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class AudioTestWithPtt(
|
||||
val online: OnlineAudio,
|
||||
val offline: OfflineAudio,
|
||||
val onlineAsRec: Audio,
|
||||
val offlineAsRec: Audio,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test Audio with ptt`() {
|
||||
val origin = AudioTestWithPtt(
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, ImMsgBody.Ptt(1)),
|
||||
OfflineAudio("test", byteArrayOf(), 1, AudioCodec.SILK, byteArrayOf(1, 2)),
|
||||
|
||||
OnlineAudioImpl("name", byteArrayOf(), 1, AudioCodec.SILK, "url", 2, ImMsgBody.Ptt(1)),
|
||||
OfflineAudio("test", byteArrayOf(), 1, AudioCodec.SILK, byteArrayOf(1, 2)),
|
||||
)
|
||||
|
||||
val result = origin.serialize().deserialize<AudioTestWithPtt>()
|
||||
|
||||
assertEquals(origin.online::class, result.online::class)
|
||||
assertEquals(origin.offline::class, result.offline::class)
|
||||
assertEquals(origin.onlineAsRec::class, result.onlineAsRec::class)
|
||||
assertEquals(origin.offlineAsRec::class, result.offlineAsRec::class)
|
||||
|
||||
assertEquals(origin.online, result.online)
|
||||
assertEquals(origin.offline, result.offline)
|
||||
assertEquals(origin.onlineAsRec, result.onlineAsRec)
|
||||
assertEquals(origin.offlineAsRec, result.offlineAsRec)
|
||||
|
||||
assertEquals(origin, result)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user