Commit eec820c1 authored by ryoii's avatar ryoii

Support for approving requests of adding friend and joining group, close #91

parent 5c95ab29
......@@ -32,6 +32,8 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.*
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.MessageRecallEvent
import net.mamoe.mirai.event.events.NewFriendEvent
import net.mamoe.mirai.event.events.NewGroupEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.contact.MemberInfoImpl
......@@ -73,7 +75,61 @@ internal class QQAndroidBot constructor(
context: Context,
account: BotAccount,
configuration: BotConfiguration
) : QQAndroidBotBase(context, account, configuration)
) : QQAndroidBotBase(context, account, configuration) {
override suspend fun acceptNewFriend(event: NewFriendEvent) {
network.run {
NewContact.SystemMsgNewFriend.Action(
bot.client,
event,
accept = true
).sendWithoutExpect()
}
}
override suspend fun rejectNewFriend(event: NewFriendEvent, blackList: Boolean) {
network.run {
NewContact.SystemMsgNewFriend.Action(
bot.client,
event,
accept = false,
blackList = blackList
).sendWithoutExpect()
}
}
override suspend fun acceptNewGroup(event: NewGroupEvent) {
network.run {
NewContact.SystemMsgNewGroup.Action(
bot.client,
event,
accept = true
).sendWithoutExpect()
}
}
override suspend fun rejectNewGroup(event: NewGroupEvent, blackList: Boolean) {
network.run {
NewContact.SystemMsgNewGroup.Action(
bot.client,
event,
accept = false,
blackList = blackList
).sendWithoutExpect()
}
}
override suspend fun ignoreNewGroup(event: NewGroupEvent, blackList: Boolean) {
network.run {
NewContact.SystemMsgNewGroup.Action(
bot.client,
event,
accept = null,
blackList = blackList
).sendWithoutExpect()
}
}
}
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal abstract class QQAndroidBotBase constructor(
......
......@@ -15,6 +15,7 @@ import net.mamoe.mirai.event.Event
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.MultiMsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
......@@ -28,16 +29,12 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.WtLogin
import net.mamoe.mirai.qqandroid.network.readUShortLVByteArray
import net.mamoe.mirai.qqandroid.utils.*
import net.mamoe.mirai.qqandroid.utils.ByteArrayPool
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import net.mamoe.mirai.qqandroid.utils.cryptor.TEA
import net.mamoe.mirai.qqandroid.utils.cryptor.adjustToPublicKey
import net.mamoe.mirai.qqandroid.utils.io.readPacketExact
import net.mamoe.mirai.qqandroid.utils.io.readString
import net.mamoe.mirai.qqandroid.utils.io.useBytes
import net.mamoe.mirai.qqandroid.utils.io.withUse
import net.mamoe.mirai.qqandroid.utils.toReadPacket
import net.mamoe.mirai.qqandroid.utils.toUHexString
import net.mamoe.mirai.utils.*
import kotlin.jvm.JvmName
......@@ -150,7 +147,10 @@ internal object KnownPacketFactories {
TroopManagement.Kick,
Heartbeat.Alive,
PbMessageSvc.PbMsgWithDraw,
MultiMsg.ApplyUp
MultiMsg.ApplyUp,
NewContact.SystemMsgNewFriend,
NewContact.SystemMsgNewGroup,
NewContact.Del
)
object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf(
......
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
import net.mamoe.mirai.event.events.NewFriendEvent
import net.mamoe.mirai.event.events.NewGroupEvent
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Structmsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
internal class NewContact {
internal object SystemMsgNewFriend :
OutgoingPacketFactory<NewFriendEvent?>("ProfileService.Pb.ReqSystemMsgNew.Friend") {
operator fun invoke(client: QQAndroidClient) = buildOutgoingUniPacket(client) {
writeProtoBuf(
Structmsg.ReqSystemMsgNew.serializer(),
Structmsg.ReqSystemMsgNew(
checktype = 2,
flag = Structmsg.FlagInfo(
frdMsgDiscuss2ManyChat = 1,
frdMsgGetBusiCard = 1,
frdMsgNeedWaitingMsg = 1,
frdMsgUint32NeedAllUnreadMsg = 1,
grpMsgMaskInviteAutoJoin = 1
),
friendMsgTypeFlag = 1,
isGetFrdRibbon = false,
isGetGrpRibbon = false,
msgNum = 20,
version = 1000
)
)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): NewFriendEvent? {
readBytes().loadAs(Structmsg.RspSystemMsgNew.serializer()).run {
val struct = friendmsgs?.firstOrNull()
return if (struct == null) null else {
struct.msg?.run {
NewFriendEvent(
bot,
struct.msgSeq,
msgAdditional,
struct.reqUin,
groupName,
reqUinNick
)
}
}
}
}
internal object Action : OutgoingPacketFactory<Nothing?>("ProfileService.Pb.ReqSystemMsgAction.Friend") {
operator fun invoke(
client: QQAndroidClient,
event: NewFriendEvent,
accept: Boolean,
blackList: Boolean = false
) =
buildOutgoingUniPacket(client) {
writeProtoBuf(
Structmsg.ReqSystemMsgAction.serializer(),
Structmsg.ReqSystemMsgAction(
actionInfo = Structmsg.SystemMsgActionInfo(
type = if (accept) 2 else 3,
addFrdSNInfo = Structmsg.AddFrdSNInfo(),
msg = "",
remark = "",
blacklist = !accept && blackList
),
msgSeq = event.seq,
reqUin = event.id,
srcId = 6,
subSrcId = 7,
subType = 1
)
)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot) = null
}
}
internal object SystemMsgNewGroup :
OutgoingPacketFactory<NewGroupEvent?>("ProfileService.Pb.ReqSystemMsgNew.Group") {
operator fun invoke(client: QQAndroidClient) = buildOutgoingUniPacket(client) {
writeProtoBuf(
Structmsg.ReqSystemMsgNew.serializer(),
Structmsg.ReqSystemMsgNew(
checktype = 3,
flag = Structmsg.FlagInfo(
frdMsgDiscuss2ManyChat = 1,
frdMsgGetBusiCard = 0,
frdMsgNeedWaitingMsg = 1,
frdMsgUint32NeedAllUnreadMsg = 1,
grpMsgGetC2cInviteJoinGroup = 1,
grpMsgMaskInviteAutoJoin = 1,
grpMsgGetDisbandedByAdmin = 1,
grpMsgGetOfficialAccount = 1,
grpMsgGetPayInGroup = 1,
grpMsgGetQuitPayGroupMsgFlag = 1,
grpMsgGetTransferGroupMsgFlag = 1,
grpMsgHiddenGrp = 1,
grpMsgKickAdmin = 1,
grpMsgNeedAutoAdminWording = 1,
grpMsgNotAllowJoinGrpInviteNotFrd = 1,
grpMsgSupportInviteAutoJoin = 1,
grpMsgWordingDown = 1
),
friendMsgTypeFlag = 1,
isGetFrdRibbon = false,
isGetGrpRibbon = false,
msgNum = 5,
version = 1000
)
)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): NewGroupEvent? {
readBytes().loadAs(Structmsg.RspSystemMsgNew.serializer()).run {
val struct = groupmsgs?.firstOrNull()
return if (struct == null) null else {
struct.msg?.run {
NewGroupEvent(
bot,
struct.msgSeq,
msgAdditional,
struct.reqUin,
groupCode,
groupName,
reqUinNick
)
}
}
}
}
internal object Action : OutgoingPacketFactory<Nothing?>("ProfileService.Pb.ReqSystemMsgAction.Group") {
operator fun invoke(
client: QQAndroidClient,
event: NewGroupEvent,
accept: Boolean?,
blackList: Boolean = false
) =
buildOutgoingUniPacket(client) {
writeProtoBuf(
Structmsg.ReqSystemMsgAction.serializer(),
Structmsg.ReqSystemMsgAction(
actionInfo = Structmsg.SystemMsgActionInfo(
type = when (accept) {
null -> 14 // ignore
true -> 11 // accept
false -> 12 // reject
},
groupCode = event.groupId,
msg = "",
remark = "",
blacklist = blackList
),
groupMsgType = 1,
language = 1000,
msgSeq = event.seq,
reqUin = event.id,
srcId = 3,
subSrcId = 31,
subType = 1
)
)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot) = null
}
}
internal object Del : OutgoingPacketFactory<Nothing?>("MessageSvc.PbDeleteMsg") {
internal operator fun invoke(client: QQAndroidClient, header: MsgComm.MsgHead) = buildOutgoingUniPacket(client) {
writeProtoBuf(
MsgSvc.PbDeleteMsgReq.serializer(),
MsgSvc.PbDeleteMsgReq(
msgItems = listOf(
MsgSvc.PbDeleteMsgReq.MsgItem(
fromUin = header.fromUin,
toUin = header.toUin,
// 群为84、好友为187。但是群通过其他方法删除,测试通过187也能删除群消息。
msgType = 187,
msgSeq = header.msgSeq,
msgUid = header.msgUid
)
)
)
)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot) = null
}
}
......@@ -46,6 +46,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.utils.io.serialization.decodeUniPacket
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
......@@ -235,6 +236,24 @@ internal class MessageSvc {
} else return@mapNotNull null
}
}
84 -> { // 群验证
bot.network.run {
NewContact.SystemMsgNewGroup(bot.client).sendWithoutExpect()
// 处理后要向服务器提交已阅,否则登陆时会重复收到事件
NewContact.Del(bot.client, msg.msgHead).sendWithoutExpect()
}
return@mapNotNull null
}
187 -> { // 好友验证
bot.network.run {
NewContact.SystemMsgNewFriend(bot.client).sendWithoutExpect()
// 处理后要向服务器提交已阅,否则登陆时会重复收到事件
NewContact.Del(bot.client, msg.msgHead).sendWithoutExpect()
}
return@mapNotNull null
}
else -> return@mapNotNull null
}
}
......
......@@ -6,6 +6,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.event.events.NewFriendEvent
import net.mamoe.mirai.event.events.NewGroupEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.MessageChain
......@@ -199,4 +201,47 @@ actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIA
@OptIn(LowLevelAPI::class, MiraiExperimentalAPI::class)
actual final override fun toString(): String = "Bot($id)"
/**
* 通过好友验证
*
* @param event 好友验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptNewFriend(event: NewFriendEvent)
/**
* 拒绝好友验证
*
* @param event 好友验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectNewFriend(event: NewFriendEvent, blackList: Boolean)
/**
* 通过加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptNewGroup(event: NewGroupEvent)
/**
* 拒绝加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectNewGroup(event: NewGroupEvent, blackList: Boolean)
/**
* 忽略加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
* @param blackList 忽略后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun ignoreNewGroup(event: NewGroupEvent, blackList: Boolean)
}
\ No newline at end of file
......@@ -18,6 +18,8 @@ import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.launch
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.event.events.NewFriendEvent
import net.mamoe.mirai.event.events.NewGroupEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.network.BotNetworkHandler
......@@ -198,6 +200,55 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
@MiraiExperimentalAPI("未支持")
abstract suspend fun addFriend(id: Long, message: String? = null, remark: String? = null): AddFriendResult
/**
* 通过好友验证
*
* @param event 好友验证的事件对象
*/
@SinceMirai("0.35.0")
@JvmSynthetic
abstract suspend fun acceptNewFriend(event: NewFriendEvent)
/**
* 拒绝好友验证
*
* @param event 好友验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@SinceMirai("0.35.0")
@JvmSynthetic
abstract suspend fun rejectNewFriend(event: NewFriendEvent, blackList: Boolean = false)
/**
* 通过加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
*/
@SinceMirai("0.35.0")
@JvmSynthetic
abstract suspend fun acceptNewGroup(event: NewGroupEvent)
/**
* 拒绝加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@SinceMirai("0.35.0")
@JvmSynthetic
abstract suspend fun rejectNewGroup(event: NewGroupEvent, blackList: Boolean = false)
/**
* 忽略加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
* @param blackList 忽略后是否拉入黑名单
*/
@SinceMirai("0.35.0")
@JvmSynthetic
abstract suspend fun ignoreNewGroup(event: NewGroupEvent, blackList: Boolean = false)
// endregion
/**
......
......@@ -24,6 +24,7 @@ import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
@Suppress("unused")
......@@ -471,3 +472,35 @@ data class MemberUnmuteEvent(
// endregion
// endregion
// region 好友、群认证
@SinceMirai("0.35.0")
data class NewFriendEvent(
override val bot: Bot,
val seq: Long, // 事件唯一识别
val additional: String,
val id: Long,
val groupName: String,
val nick: String
) : BotEvent, Packet {
suspend fun accept() = bot.acceptNewFriend(this)
suspend fun reject(blackList: Boolean = false) = bot.rejectNewFriend(this, blackList)
}
@SinceMirai("0.35.0")
data class NewGroupEvent(
override val bot: Bot,
val seq: Long, // 事件唯一识别
val additional: String,
val id: Long,
val groupId: Long,
val groupName: String,
val nick: String
) : BotEvent, Packet {
suspend fun accept() = bot.acceptNewGroup(this)
suspend fun reject(blackList: Boolean = false) = bot.rejectNewGroup(this, blackList)
suspend fun ignore(blackList: Boolean = false) = bot.ignoreNewGroup(this, blackList)
}
// endregion 好友、群认证
\ No newline at end of file
......@@ -6,6 +6,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.event.events.NewFriendEvent
import net.mamoe.mirai.event.events.NewGroupEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.network.BotNetworkHandler
......@@ -207,4 +209,47 @@ actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIA
@OptIn(LowLevelAPI::class, MiraiExperimentalAPI::class)
actual final override fun toString(): String = "Bot($id)"
/**
* 通过好友验证
*
* @param event 好友验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptNewFriend(event: NewFriendEvent)
/**
* 拒绝好友验证
*
* @param event 好友验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectNewFriend(event: NewFriendEvent, blackList: Boolean)
/**
* 通过加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptNewGroup(event: NewGroupEvent)
/**
* 拒绝加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectNewGroup(event: NewGroupEvent, blackList: Boolean)
/**
* 忽略加群验证(需管理员权限)
*
* @param event 加群验证的事件对象
* @param blackList 忽略后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun ignoreNewGroup(event: NewGroupEvent, blackList: Boolean)
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment