Private voice support (#1310)

* Support voice for private messaging

* typo

* Add extension functions for `uploadAsVoice`; Update KDocs

* Update KDoc

* Update error message

* Fix `C2CPttStoreBDH` error

* Deprecated `ExternalResource.uploadAsVoice(Contact): Voice`

* Update mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt

* apiDump

Co-authored-by: Him188 <Him188@mamoe.net>
This commit is contained in:
Karlatemp 2021-06-26 10:02:23 +08:00 committed by GitHub
parent d349c19c2e
commit ae54a5fb41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 848 additions and 57 deletions

View File

@ -330,7 +330,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 {
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/VoiceSupported {
public fun delete ()V
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getId ()J
@ -345,7 +345,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 {
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 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
@ -371,8 +371,6 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin
public fun setEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;)Z
public abstract fun setEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun setName (Ljava/lang/String;)V
public fun 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 {
@ -571,6 +569,11 @@ 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 class net/mamoe/mirai/data/Announcement {
public static final field Companion Lnet/mamoe/mirai/data/Announcement$Companion;
public static final fun create (JLjava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/data/AnnouncementParameters;)Lnet/mamoe/mirai/data/Announcement;
@ -5399,8 +5402,14 @@ 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;
@ -5468,8 +5477,14 @@ 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;

View File

@ -330,7 +330,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 {
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/VoiceSupported {
public fun delete ()V
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getId ()J
@ -345,7 +345,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 {
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 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
@ -371,8 +371,6 @@ public abstract interface class net/mamoe/mirai/contact/Group : kotlinx/coroutin
public fun setEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;)Z
public abstract fun setEssenceMessage (Lnet/mamoe/mirai/message/data/MessageSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun setName (Ljava/lang/String;)V
public fun 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 {
@ -571,6 +569,11 @@ 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 class net/mamoe/mirai/data/Announcement {
public static final field Companion Lnet/mamoe/mirai/data/Announcement$Companion;
public static final fun create (JLjava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/data/AnnouncementParameters;)Lnet/mamoe/mirai/data/Announcement;
@ -5399,8 +5402,14 @@ 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;
@ -5468,8 +5477,14 @@ 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;

View File

@ -32,7 +32,7 @@ import net.mamoe.mirai.message.data.toPlainText
* @see FriendMessageEvent
*/
@JvmBlockingBridge
public interface Friend : User, CoroutineScope {
public interface Friend : User, CoroutineScope, VoiceSupported {
/**
* QQ 号码
*/

View File

@ -32,7 +32,7 @@ import net.mamoe.mirai.utils.OverFileSizeMaxException
* .
*/
@JvmBlockingBridge
public interface Group : Contact, CoroutineScope, FileSupported {
public interface Group : Contact, CoroutineScope, FileSupported, VoiceSupported {
/**
* 群名称.
*
@ -157,18 +157,6 @@ public interface Group : Contact, CoroutineScope, FileSupported {
public override suspend fun sendMessage(message: String): MessageReceipt<Group> =
this.sendMessage(message.toPlainText())
/**
* 上传一个语音消息以备发送.
*
* - **请手动关闭 [resource]**
* - 请使用 amr silk 格式
*
* @see ExternalResource.uploadAsVoice
*
* @throws EventCancelledException 当发送消息事件被取消
* @throws OverFileSizeMaxException 当语音文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
*/
public suspend fun uploadVoice(resource: ExternalResource): Voice
/**

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.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.OverFileSizeMaxException
/**
* 支持发送语音的 [Contact]
*
* @since 2.7
*/
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
}

View File

@ -11,6 +11,7 @@ package net.mamoe.mirai.message.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsVoice
@ -39,6 +40,10 @@ public abstract class PttMessage : MessageContent {
@MiraiExperimentalApi
public abstract val fileSize: Long
@MiraiInternalApi
@Transient
public var pttInternalInstance: Any? = null
}
/**

View File

@ -19,7 +19,7 @@ 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.Group
import net.mamoe.mirai.contact.VoiceSupported
import net.mamoe.mirai.internal.utils.ExternalResourceImplByByteArray
import net.mamoe.mirai.internal.utils.ExternalResourceImplByFile
import net.mamoe.mirai.message.MessageReceipt
@ -384,15 +384,52 @@ public interface ExternalResource : Closeable {
*
* **服务器仅支持音频格式 `silk` `amr`**. 需要调用方手动[关闭资源][ExternalResource.close].
*
* 目前仅支持发送给群.
*
* @throws OverFileSizeMaxException
* @since 2.7
* @see VoiceSupported.uploadVoice
*/
@JvmBlockingBridge
@JvmStatic
public suspend fun ExternalResource.uploadAsVoice(contact: Contact): Voice {
if (contact is Group) return contact.uploadVoice(this)
else throw UnsupportedOperationException("Uploading Voice is only supported for Group yet.")
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)
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) }
}
}
}

View File

@ -21,16 +21,29 @@ package net.mamoe.mirai.internal.contact
import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.data.FriendInfo
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.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.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.*
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.Voice
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.recoverCatchingSuppressed
import net.mamoe.mirai.utils.toByteArray
import net.mamoe.mirai.utils.toUHexString
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
@ -81,4 +94,63 @@ 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,
""
)
kotlin.runCatching {
val resp = Highway.uploadResourceBdh(
bot = bot,
resource = resource,
kind = ResourceKind.PRIVATE_VOICE,
commandId = 26,
extendInfo = PttStore.C2C.createC2CPttStoreBDHExt(bot, this@FriendImpl.uin, resource)
.toByteArray(Cmd0x346.ReqBody.serializer())
)
// resp._miraiContentToString("UV resp")
val c346resp = resp.extendInfo!!.loadAs(Cmd0x346.RspBody.serializer())
if (c346resp.msgApplyUploadRsp == null) {
error("Upload failed")
}
voice.pttInternalInstance = 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>()) {
is PttStore.GroupPttUp.Response.RequireUpload -> {
tryServersUpload(
bot,
resp.uploadIpList.zip(resp.uploadPortList),
resource.size,
ResourceKind.GROUP_VOICE,
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(),
fileMd5 = resource.md5,
fileName = resource.md5 + ".amr".toByteArray(),
fileSize = resource.size.toInt(),
boolValid = true,
)
}
}
}.getOrThrow()
return voice
}
}

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.internal.message
import io.ktor.util.*
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUInt
import kotlinx.io.core.readUShort
@ -520,5 +519,5 @@ internal object ReceiveMessageTransformer {
fileSize.toLong(),
format,
kotlinx.io.core.String(downPara)
)
).also { it.pttInternalInstance = this }
}

View File

@ -0,0 +1,588 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("unused", "SpellCheckingInspection")
package net.mamoe.mirai.internal.network.protocol.data.proto
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import net.mamoe.mirai.internal.utils.io.ProtoBuf
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
@Serializable
internal class Cmd0x346 : ProtoBuf {
@Serializable
internal class AddrList(
@JvmField @ProtoNumber(2) val strIp: List<String> = emptyList(),
@JvmField @ProtoNumber(3) val strDomain: String = "",
@JvmField @ProtoNumber(4) val port: Int = 0
) : ProtoBuf
@Serializable
internal class ApplyCleanTrafficReq : ProtoBuf
@Serializable
internal class ApplyCleanTrafficRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = ""
) : ProtoBuf
@Serializable
internal class ApplyCopyFromReq(
@JvmField @ProtoNumber(10) val srcUin: Long = 0L,
@JvmField @ProtoNumber(20) val srcGroup: Long = 0L,
@JvmField @ProtoNumber(30) val srcSvcid: Int = 0,
@JvmField @ProtoNumber(40) val srcParentfolder: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(50) val srcUuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(60) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val dstUin: Long = 0L,
@JvmField @ProtoNumber(80) val fileSize: Long = 0L,
@JvmField @ProtoNumber(90) val fileName: String = "",
@JvmField @ProtoNumber(100) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(110) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyCopyFromRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(40) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyCopyToReq(
@JvmField @ProtoNumber(10) val dstId: Long = 0L,
@JvmField @ProtoNumber(20) val dstUin: Long = 0L,
@JvmField @ProtoNumber(30) val dstSvcid: Int = 0,
@JvmField @ProtoNumber(40) val srcUin: Long = 0L,
@JvmField @ProtoNumber(50) val fileSize: Long = 0L,
@JvmField @ProtoNumber(60) val fileName: String = "",
@JvmField @ProtoNumber(70) val localFilepath: String = "",
@JvmField @ProtoNumber(80) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class ApplyCopyToRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val fileKey: String = ""
) : ProtoBuf
@Serializable
internal class ApplyDownloadAbsReq(
@JvmField @ProtoNumber(10) val uin: Long = 0L,
@JvmField @ProtoNumber(20) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class ApplyDownloadAbsRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val msgDownloadInfo: Cmd0x346.DownloadInfo? = null
) : ProtoBuf
@Serializable
internal class ApplyDownloadReq(
@JvmField @ProtoNumber(10) val uin: Long = 0L,
@JvmField @ProtoNumber(20) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(30) val ownerType: Int = 0,
@JvmField @ProtoNumber(500) val extUintype: Int = 0,
@JvmField @ProtoNumber(501) val needHttpsUrl: Int = 0
) : ProtoBuf
@Serializable
internal class ApplyDownloadRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val msgDownloadInfo: Cmd0x346.DownloadInfo? = null,
@JvmField @ProtoNumber(40) val msgFileInfo: Cmd0x346.FileInfo? = null
) : ProtoBuf
@Serializable
internal class ApplyForwardFileReq(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(40) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(50) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyForwardFileRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val totalSpace: Long = 0L,
@JvmField @ProtoNumber(40) val usedSpace: Long = 0L,
@JvmField @ProtoNumber(50) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class ApplyGetTrafficReq : ProtoBuf
@Serializable
internal class ApplyGetTrafficRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val useFileSize: Long = 0L,
@JvmField @ProtoNumber(40) val useFileNum: Int = 0,
@JvmField @ProtoNumber(50) val allFileSize: Long = 0L,
@JvmField @ProtoNumber(60) val allFileNum: Int = 0
) : ProtoBuf
@Serializable
internal class ApplyListDownloadReq(
@JvmField @ProtoNumber(10) val uin: Long = 0L,
@JvmField @ProtoNumber(20) val beginIndex: Int = 0,
@JvmField @ProtoNumber(30) val reqCount: Int = 0
) : ProtoBuf
@Serializable
internal class ApplyListDownloadRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val totalCount: Int = 0,
@JvmField @ProtoNumber(40) val beginIndex: Int = 0,
@JvmField @ProtoNumber(50) val rspCount: Int = 0,
@JvmField @ProtoNumber(60) val isEnd: Int = 0,
@JvmField @ProtoNumber(70) val msgFileList: List<Cmd0x346.FileInfo> = emptyList()
) : ProtoBuf
@Serializable
internal class ApplyUploadHitReq(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val fileSize: Long = 0L,
@JvmField @ProtoNumber(40) val fileName: String = "",
@JvmField @ProtoNumber(50) val _10mMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(60) val localFilepath: String = "",
@JvmField @ProtoNumber(70) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(80) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyUploadHitReqV2(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val fileSize: Long = 0L,
@JvmField @ProtoNumber(40) val fileName: String = "",
@JvmField @ProtoNumber(50) val _10mMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(60) val _3sha: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val sha: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(80) val localFilepath: String = "",
@JvmField @ProtoNumber(90) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(100) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyUploadHitReqV3(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val fileSize: Long = 0L,
@JvmField @ProtoNumber(40) val fileName: String = "",
@JvmField @ProtoNumber(50) val _10mMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(60) val sha: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val localFilepath: String = "",
@JvmField @ProtoNumber(80) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(90) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyUploadHitRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val uploadIp: String = "",
@JvmField @ProtoNumber(40) val uploadPort: Int = 0,
@JvmField @ProtoNumber(50) val uploadDomain: String = "",
@JvmField @ProtoNumber(60) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val uploadKey: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(80) val totalSpace: Long = 0L,
@JvmField @ProtoNumber(90) val usedSpace: Long = 0L,
@JvmField @ProtoNumber(100) val uploadDns: String = ""
) : ProtoBuf
@Serializable
internal class ApplyUploadHitRspV2(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val uploadIp: String = "",
@JvmField @ProtoNumber(40) val uploadPort: Int = 0,
@JvmField @ProtoNumber(50) val uploadDomain: String = "",
@JvmField @ProtoNumber(60) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val uploadKey: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(80) val totalSpace: Long = 0L,
@JvmField @ProtoNumber(90) val usedSpace: Long = 0L,
@JvmField @ProtoNumber(100) val uploadHttpsPort: Int = 443,
@JvmField @ProtoNumber(110) val uploadHttpsDomain: String = "",
@JvmField @ProtoNumber(120) val uploadDns: String = ""
) : ProtoBuf
@Serializable
internal class ApplyUploadHitRspV3(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val uploadIp: String = "",
@JvmField @ProtoNumber(40) val uploadPort: Int = 0,
@JvmField @ProtoNumber(50) val uploadDomain: String = "",
@JvmField @ProtoNumber(60) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val uploadKey: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(80) val totalSpace: Long = 0L,
@JvmField @ProtoNumber(90) val usedSpace: Long = 0L,
@JvmField @ProtoNumber(100) val uploadDns: String = ""
) : ProtoBuf
@Serializable
internal class ApplyUploadReq(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val fileType: Int = 0,
@JvmField @ProtoNumber(40) val fileSize: Long = 0L,
@JvmField @ProtoNumber(50) val fileName: ByteArray = EMPTY_BYTE_ARRAY, //String = "",
@JvmField @ProtoNumber(60) val _10mMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val localFilepath: String = "",
@JvmField @ProtoNumber(80) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(90) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyUploadReqV2(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val fileSize: Long = 0L,
@JvmField @ProtoNumber(40) val fileName: String = "",
@JvmField @ProtoNumber(50) val _10mMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(60) val _3sha: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val localFilepath: String = "",
@JvmField @ProtoNumber(80) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(90) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyUploadReqV3(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val fileSize: Long = 0L,
@JvmField @ProtoNumber(40) val fileName: String = "",
@JvmField @ProtoNumber(50) val _10mMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(60) val sha: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(70) val localFilepath: String = "",
@JvmField @ProtoNumber(80) val dangerLevel: Int = 0,
@JvmField @ProtoNumber(90) val totalSpace: Long = 0L
) : ProtoBuf
@Serializable
internal class ApplyUploadRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val totalSpace: Long = 0L,
@JvmField @ProtoNumber(40) val usedSpace: Long = 0L,
@JvmField @ProtoNumber(50) val uploadedSize: Long = 0L,
@JvmField @ProtoNumber(60) val uploadIp: String = "",
@JvmField @ProtoNumber(70) val uploadDomain: String = "",
@JvmField @ProtoNumber(80) val uploadPort: Int = 0,
@JvmField @ProtoNumber(90) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(100) val uploadKey: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(110) val boolFileExist: Boolean = false,
@JvmField @ProtoNumber(120) val packSize: Int = 0,
@JvmField @ProtoNumber(130) val strUploadipList: List<String> = emptyList(),
@JvmField @ProtoNumber(140) val uploadDns: String = ""
) : ProtoBuf
@Serializable
internal class ApplyUploadRspV2(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val totalSpace: Long = 0L,
@JvmField @ProtoNumber(40) val usedSpace: Long = 0L,
@JvmField @ProtoNumber(50) val uploadedSize: Long = 0L,
@JvmField @ProtoNumber(60) val uploadIp: String = "",
@JvmField @ProtoNumber(70) val uploadDomain: String = "",
@JvmField @ProtoNumber(80) val uploadPort: Int = 0,
@JvmField @ProtoNumber(90) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(100) val uploadKey: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(110) val boolFileExist: Boolean = false,
@JvmField @ProtoNumber(120) val packSize: Int = 0,
@JvmField @ProtoNumber(130) val strUploadipList: List<String> = emptyList(),
@JvmField @ProtoNumber(140) val httpsvrApiVer: Int = 0,
@JvmField @ProtoNumber(141) val sha: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(142) val uploadHttpsPort: Int = 443,
@JvmField @ProtoNumber(143) val uploadHttpsDomain: String = "",
@JvmField @ProtoNumber(150) val uploadDns: String = "",
@JvmField @ProtoNumber(160) val uploadLanip: String = ""
) : ProtoBuf
@Serializable
internal class ApplyUploadRspV3(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val totalSpace: Long = 0L,
@JvmField @ProtoNumber(40) val usedSpace: Long = 0L,
@JvmField @ProtoNumber(50) val uploadedSize: Long = 0L,
@JvmField @ProtoNumber(60) val uploadIp: String = "",
@JvmField @ProtoNumber(70) val uploadDomain: String = "",
@JvmField @ProtoNumber(80) val uploadPort: Int = 0,
@JvmField @ProtoNumber(90) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(100) val uploadKey: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(110) val boolFileExist: Boolean = false,
@JvmField @ProtoNumber(120) val packSize: Int = 0,
@JvmField @ProtoNumber(130) val strUploadipList: List<String> = emptyList(),
@JvmField @ProtoNumber(140) val uploadHttpsPort: Int = 443,
@JvmField @ProtoNumber(150) val uploadHttpsDomain: String = "",
@JvmField @ProtoNumber(160) val uploadDns: String = "",
@JvmField @ProtoNumber(170) val uploadLanip: String = ""
) : ProtoBuf
@Serializable
internal class DeleteFileReq(
@JvmField @ProtoNumber(10) val uin: Long = 0L,
@JvmField @ProtoNumber(20) val peerUin: Long = 0L,
@JvmField @ProtoNumber(30) val deleteType: Int = 0,
@JvmField @ProtoNumber(40) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class DeleteFileRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = ""
) : ProtoBuf
@Serializable
internal class DelMessageReq(
@JvmField @ProtoNumber(1) val uinSender: Long = 0L,
@JvmField @ProtoNumber(2) val uinReceiver: Long = 0L,
@JvmField @ProtoNumber(10) val msgTime: Int = 0,
@JvmField @ProtoNumber(20) val msgRandom: Int = 0,
@JvmField @ProtoNumber(30) val msgSeqNo: Int = 0
) : ProtoBuf
@Serializable
internal class DownloadInfo(
@JvmField @ProtoNumber(10) val downloadKey: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(20) val downloadIp: String = "",
@JvmField @ProtoNumber(30) val downloadDomain: String = "",
@JvmField @ProtoNumber(40) val port: Int = 0,
@JvmField @ProtoNumber(50) val downloadUrl: String = "",
@JvmField @ProtoNumber(60) val strDownloadipList: List<String> = emptyList(),
@JvmField @ProtoNumber(70) val cookie: String = "",
@JvmField @ProtoNumber(80) val httpsPort: Int = 443,
@JvmField @ProtoNumber(90) val httpsDownloadDomain: String = "",
@JvmField @ProtoNumber(110) val downloadDns: String = ""
) : ProtoBuf
@Serializable
internal class DownloadSuccReq(
@JvmField @ProtoNumber(10) val uin: Long = 0L,
@JvmField @ProtoNumber(20) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class DownloadSuccRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val int32DownStat: Int = 0
) : ProtoBuf
@Serializable
internal class ExtensionReq(
@JvmField @ProtoNumber(1) val id: Long = 0L,
@JvmField @ProtoNumber(2) val type: Long = 0L,
@JvmField @ProtoNumber(3) val dstPhonenum: String = "",
@JvmField @ProtoNumber(4) val int32PhoneConvertType: Int = 0,
@JvmField @ProtoNumber(20) val sig: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(100) val routeId: Long = 0L,
@JvmField @ProtoNumber(90100) val msgDelMessageReq: Cmd0x346.DelMessageReq? = null,
@JvmField @ProtoNumber(90200) val downloadUrlType: Int = 0,
@JvmField @ProtoNumber(90300) val pttFormat: Int = 0,
@JvmField @ProtoNumber(90400) val isNeedInnerIp: Int = 0,
@JvmField @ProtoNumber(90500) val netType: Int = 255,
@JvmField @ProtoNumber(90600) val voiceType: Int = 0,
@JvmField @ProtoNumber(90700) val fileType: Int = 0,
@JvmField @ProtoNumber(90800) val pttTime: Int = 0,
@JvmField @ProtoNumber(90900) val bdhCmdid: Int = 0,
@JvmField @ProtoNumber(91000) val reqTransferType: Int = 0,
@JvmField @ProtoNumber(91100) val isAuto: Int = 0
) : ProtoBuf
@Serializable
internal class ExtensionRsp(
@JvmField @ProtoNumber(1) val transferType: Int = 0,
@JvmField @ProtoNumber(2) val channelType: Int = 0,
@JvmField @ProtoNumber(3) val allowRetry: Int = 0,
@JvmField @ProtoNumber(4) val serverAddrIpv6List: Cmd0x346.AddrList? = null
) : ProtoBuf
@Serializable
internal class FileInfo(
@JvmField @ProtoNumber(1) val uin: Long = 0L,
@JvmField @ProtoNumber(2) val dangerEvel: Int = 0,
@JvmField @ProtoNumber(3) val fileSize: Long = 0L,
@JvmField @ProtoNumber(4) val lifeTime: Int = 0,
@JvmField @ProtoNumber(5) val uploadTime: Int = 0,
@JvmField @ProtoNumber(6) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(7) val fileName: String = "",
@JvmField @ProtoNumber(90) val absFileType: Int = 0,
@JvmField @ProtoNumber(100) val _10mMd5: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(101) val sha: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(110) val clientType: Int = 0,
@JvmField @ProtoNumber(120) val ownerUin: Long = 0L,
@JvmField @ProtoNumber(121) val peerUin: Long = 0L,
@JvmField @ProtoNumber(130) val expireTime: Int = 0
) : ProtoBuf
@Serializable
internal class FileQueryReq(
@JvmField @ProtoNumber(10) val uin: Long = 0L,
@JvmField @ProtoNumber(20) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class FileQueryRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val msgFileInfo: Cmd0x346.FileInfo? = null
) : ProtoBuf
@Serializable
internal class RecallFileReq(
@JvmField @ProtoNumber(1) val uin: Long = 0L,
@JvmField @ProtoNumber(2) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class RecallFileRsp(
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(2) val retMsg: String = ""
) : ProtoBuf
@Serializable
internal class RecvListQueryReq(
@JvmField @ProtoNumber(1) val uin: Long = 0L,
@JvmField @ProtoNumber(2) val beginIndex: Int = 0,
@JvmField @ProtoNumber(3) val reqCount: Int = 0
) : ProtoBuf
@Serializable
internal class RecvListQueryRsp(
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(2) val retMsg: String = "",
@JvmField @ProtoNumber(3) val fileTotCount: Int = 0,
@JvmField @ProtoNumber(4) val beginIndex: Int = 0,
@JvmField @ProtoNumber(5) val rspFileCount: Int = 0,
@JvmField @ProtoNumber(6) val isEnd: Int = 0,
@JvmField @ProtoNumber(7) val msgFileList: List<Cmd0x346.FileInfo> = emptyList()
) : ProtoBuf
@Serializable
internal class RenewFileReq(
@JvmField @ProtoNumber(1) val uin: Long = 0L,
@JvmField @ProtoNumber(2) val uuid: ByteArray = EMPTY_BYTE_ARRAY,
@JvmField @ProtoNumber(3) val addTtl: Int = 0
) : ProtoBuf
@Serializable
internal class RenewFileRsp(
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(2) val retMsg: String = ""
) : ProtoBuf
@Serializable
internal class ReqBody(
@JvmField @ProtoNumber(1) val cmd: Int = 0,
@JvmField @ProtoNumber(2) val seq: Int = 0,
@JvmField @ProtoNumber(3) val msgRecvListQueryReq: Cmd0x346.RecvListQueryReq? = null,
@JvmField @ProtoNumber(4) val msgSendListQueryReq: Cmd0x346.SendListQueryReq? = null,
@JvmField @ProtoNumber(5) val msgRenewFileReq: Cmd0x346.RenewFileReq? = null,
@JvmField @ProtoNumber(6) val msgRecallFileReq: Cmd0x346.RecallFileReq? = null,
@JvmField @ProtoNumber(7) val msgApplyUploadReq: Cmd0x346.ApplyUploadReq? = null,
@JvmField @ProtoNumber(8) val msgApplyUploadHitReq: Cmd0x346.ApplyUploadHitReq? = null,
@JvmField @ProtoNumber(9) val msgApplyForwardFileReq: Cmd0x346.ApplyForwardFileReq? = null,
@JvmField @ProtoNumber(10) val msgUploadSuccReq: Cmd0x346.UploadSuccReq? = null,
@JvmField @ProtoNumber(11) val msgDeleteFileReq: Cmd0x346.DeleteFileReq? = null,
@JvmField @ProtoNumber(12) val msgDownloadSuccReq: Cmd0x346.DownloadSuccReq? = null,
@JvmField @ProtoNumber(13) val msgApplyDownloadAbsReq: Cmd0x346.ApplyDownloadAbsReq? = null,
@JvmField @ProtoNumber(14) val msgApplyDownloadReq: Cmd0x346.ApplyDownloadReq? = null,
@JvmField @ProtoNumber(15) val msgApplyListDownloadReq: Cmd0x346.ApplyListDownloadReq? = null,
@JvmField @ProtoNumber(16) val msgFileQueryReq: Cmd0x346.FileQueryReq? = null,
@JvmField @ProtoNumber(17) val msgApplyCopyFromReq: Cmd0x346.ApplyCopyFromReq? = null,
@JvmField @ProtoNumber(18) val msgApplyUploadReqV2: Cmd0x346.ApplyUploadReqV2? = null,
@JvmField @ProtoNumber(19) val msgApplyUploadReqV3: Cmd0x346.ApplyUploadReqV3? = null,
@JvmField @ProtoNumber(20) val msgApplyUploadHitReqV2: Cmd0x346.ApplyUploadHitReqV2? = null,
@JvmField @ProtoNumber(21) val msgApplyUploadHitReqV3: Cmd0x346.ApplyUploadHitReqV3? = null,
@JvmField @ProtoNumber(101) val businessId: Int = 0,
@JvmField @ProtoNumber(102) val clientType: Int = 0,
@JvmField @ProtoNumber(90000) val msgApplyCopyToReq: Cmd0x346.ApplyCopyToReq? = null,
@JvmField @ProtoNumber(90001) val msgApplyCleanTrafficReq: Cmd0x346.ApplyCleanTrafficReq? = null,
@JvmField @ProtoNumber(90002) val msgApplyGetTrafficReq: Cmd0x346.ApplyGetTrafficReq? = null,
@JvmField @ProtoNumber(99999) val msgExtensionReq: Cmd0x346.ExtensionReq? = null
) : ProtoBuf
@Serializable
internal class RspBody(
@JvmField @ProtoNumber(1) val cmd: Int = 0,
@JvmField @ProtoNumber(2) val seq: Int = 0,
@JvmField @ProtoNumber(3) val msgRecvListQueryRsp: Cmd0x346.RecvListQueryRsp? = null,
@JvmField @ProtoNumber(4) val msgSendListQueryRsp: Cmd0x346.SendListQueryRsp? = null,
@JvmField @ProtoNumber(5) val msgRenewFileRsp: Cmd0x346.RenewFileRsp? = null,
@JvmField @ProtoNumber(6) val msgRecallFileRsp: Cmd0x346.RecallFileRsp? = null,
@JvmField @ProtoNumber(7) val msgApplyUploadRsp: Cmd0x346.ApplyUploadRsp? = null,
@JvmField @ProtoNumber(8) val msgApplyUploadHitRsp: Cmd0x346.ApplyUploadHitRsp? = null,
@JvmField @ProtoNumber(9) val msgApplyForwardFileRsp: Cmd0x346.ApplyForwardFileRsp? = null,
@JvmField @ProtoNumber(10) val msgUploadSuccRsp: Cmd0x346.UploadSuccRsp? = null,
@JvmField @ProtoNumber(11) val msgDeleteFileRsp: Cmd0x346.DeleteFileRsp? = null,
@JvmField @ProtoNumber(12) val msgDownloadSuccRsp: Cmd0x346.DownloadSuccRsp? = null,
@JvmField @ProtoNumber(13) val msgApplyDownloadAbsRsp: Cmd0x346.ApplyDownloadAbsRsp? = null,
@JvmField @ProtoNumber(14) val msgApplyDownloadRsp: Cmd0x346.ApplyDownloadRsp? = null,
@JvmField @ProtoNumber(15) val msgApplyListDownloadRsp: Cmd0x346.ApplyListDownloadRsp? = null,
@JvmField @ProtoNumber(16) val msgFileQueryRsp: Cmd0x346.FileQueryRsp? = null,
@JvmField @ProtoNumber(17) val msgApplyCopyFromRsp: Cmd0x346.ApplyCopyFromRsp? = null,
@JvmField @ProtoNumber(18) val msgApplyUploadRspV2: Cmd0x346.ApplyUploadRspV2? = null,
@JvmField @ProtoNumber(19) val msgApplyUploadRspV3: Cmd0x346.ApplyUploadRspV3? = null,
@JvmField @ProtoNumber(20) val msgApplyUploadHitRspV2: Cmd0x346.ApplyUploadHitRspV2? = null,
@JvmField @ProtoNumber(21) val msgApplyUploadHitRspV3: Cmd0x346.ApplyUploadHitRspV3? = null,
@JvmField @ProtoNumber(90000) val msgApplyCopyToRsp: Cmd0x346.ApplyCopyToRsp? = null,
@JvmField @ProtoNumber(90001) val msgApplyCleanTrafficRsp: Cmd0x346.ApplyCleanTrafficRsp? = null,
@JvmField @ProtoNumber(90002) val msgApplyGetTrafficRsp: Cmd0x346.ApplyGetTrafficRsp? = null,
@JvmField @ProtoNumber(99999) val msgExtensionRsp: Cmd0x346.ExtensionRsp? = null
) : ProtoBuf
@Serializable
internal class SendListQueryReq(
@JvmField @ProtoNumber(1) val uin: Long = 0L,
@JvmField @ProtoNumber(2) val beginIndex: Int = 0,
@JvmField @ProtoNumber(3) val reqCount: Int = 0
) : ProtoBuf
@Serializable
internal class SendListQueryRsp(
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(2) val retMsg: String = "",
@JvmField @ProtoNumber(3) val fileTotCount: Int = 0,
@JvmField @ProtoNumber(4) val beginIndex: Int = 0,
@JvmField @ProtoNumber(5) val rspFileCount: Int = 0,
@JvmField @ProtoNumber(6) val isEnd: Int = 0,
@JvmField @ProtoNumber(7) val totLimit: Long = 0L,
@JvmField @ProtoNumber(8) val usedLimit: Long = 0L,
@JvmField @ProtoNumber(9) val msgFileList: List<Cmd0x346.FileInfo> = emptyList()
) : ProtoBuf
@Serializable
internal class UploadSuccReq(
@JvmField @ProtoNumber(10) val senderUin: Long = 0L,
@JvmField @ProtoNumber(20) val recverUin: Long = 0L,
@JvmField @ProtoNumber(30) val uuid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class UploadSuccRsp(
@JvmField @ProtoNumber(10) val int32RetCode: Int = 0,
@JvmField @ProtoNumber(20) val retMsg: String = "",
@JvmField @ProtoNumber(30) val msgFileInfo: Cmd0x346.FileInfo? = null
) : ProtoBuf
}

View File

@ -51,11 +51,8 @@ import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
import net.mamoe.mirai.message.data.MessageSourceKind
import net.mamoe.mirai.message.data.MessageSourceKind.STRANGER
import net.mamoe.mirai.message.data.MessageSourceKind.TEMP
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.buildMessageChain
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.MessageSourceKind.*
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.read
@ -447,7 +444,11 @@ internal suspend fun MsgComm.Msg.transform(bot: QQAndroidBot, fromSync: Boolean
}
208 -> {
// friend ptt
return null
val target = bot.getFriend(msgHead.fromUin)
?: return null
val lsc = listOf(this).toMessageChainOnline(bot, 0, FRIEND)
return FriendMessageEvent(target, lsc, msgHead.msgTime)
}
529 -> {

View File

@ -145,6 +145,25 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
return response
}
internal fun PttMessage.toPtt() = run {
(this.pttInternalInstance as? ImMsgBody.Ptt)?.let { return it }
ImMsgBody.Ptt(
fileName = fileName.toByteArray(),
fileMd5 = md5,
boolValid = true,
fileSize = fileSize.toInt(),
fileType = 4,
pbReserve = byteArrayOf(0),
format = let {
if (it is Voice) {
it.codec
} else {
0
}
}
)
}
/**
* 发送陌生人消息
*/
@ -221,10 +240,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
return buildOutgoingMessageCommon(
client = client,
message = message,
fragmentTranslator = {
fragmentTranslator = { subChain ->
ImMsgBody.MsgBody(
richText = ImMsgBody.RichText(
elems = it.toRichTextElems(messageTarget = targetFriend, withGeneralFlags = true)
elems = subChain.toRichTextElems(messageTarget = targetFriend, withGeneralFlags = true),
ptt = subChain[PttMessage]?.toPtt(),
)
)
},
@ -341,23 +361,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
ImMsgBody.MsgBody(
richText = ImMsgBody.RichText(
elems = subChain.toRichTextElems(messageTarget = targetGroup, withGeneralFlags = true),
ptt = subChain[PttMessage]?.run {
ImMsgBody.Ptt(
fileName = fileName.toByteArray(),
fileMd5 = md5,
boolValid = true,
fileSize = fileSize.toInt(),
fileType = 4,
pbReserve = byteArrayOf(0),
format = let {
if (it is Voice) {
it.codec
} else {
0
}
}
)
}
ptt = subChain[PttMessage]?.toPtt()
)
)

View File

@ -11,8 +11,10 @@ package net.mamoe.mirai.internal.network.protocol.packet.chat.voice
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x346
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x388
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
@ -190,4 +192,32 @@ internal class PttStore {
}
}
object C2C {
fun createC2CPttStoreBDHExt(
bot: QQAndroidBot,
uin: Long,
resource: ExternalResource
): Cmd0x346.ReqBody {
return Cmd0x346.ReqBody(
cmd = 500,
businessId = 17,
clientType = 104,
msgExtensionReq = Cmd0x346.ExtensionReq(
id = 3,
pttFormat = 1,
netType = 3,
voiceType = 1,
pttTime = 1,
),
msgApplyUploadReq = Cmd0x346.ApplyUploadReq(
senderUin = bot.uin,
recverUin = uin,
fileType = 2,
fileSize = resource.size,
fileName = resource.md5,
_10mMd5 = resource.md5,
),
)
}
}
}