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(
......
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import kotlinx.serialization.protobuf.ProtoId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf
@Serializable
class QPayReminderMsg : ProtoBuf {
@Serializable
class GetInfoReq(
@ProtoId(1) val scene: String = "",
@ProtoId(2) val subCmd: String = "",
@ProtoId(3) val infoDate: String = ""
) : ProtoBuf
@Serializable
class GetInfoRsp(
@ProtoId(1) val resultCode: Int = 0,
@ProtoId(2) val resultInfo: String = "",
@ProtoId(3) val urgency: Int = 0,
@ProtoId(4) val templateNo: Int = 0,
@ProtoId(5) val content: String = "",
@ProtoId(6) val infoDate: String = ""
) : ProtoBuf
}
@Serializable
class Structmsg : ProtoBuf {
@Serializable
class AddFrdSNInfo(
@ProtoId(1) val notSeeDynamic: Int = 0,
@ProtoId(2) val setSn: Int = 0
) : ProtoBuf
@Serializable
class FlagInfo(
@ProtoId(1) val grpMsgKickAdmin: Int = 0,
@ProtoId(2) val grpMsgHiddenGrp: Int = 0,
@ProtoId(3) val grpMsgWordingDown: Int = 0,
@ProtoId(4) val frdMsgGetBusiCard: Int = 0,
@ProtoId(5) val grpMsgGetOfficialAccount: Int = 0,
@ProtoId(6) val grpMsgGetPayInGroup: Int = 0,
@ProtoId(7) val frdMsgDiscuss2ManyChat: Int = 0,
@ProtoId(8) val grpMsgNotAllowJoinGrpInviteNotFrd: Int = 0,
@ProtoId(9) val frdMsgNeedWaitingMsg: Int = 0,
@ProtoId(10) val frdMsgUint32NeedAllUnreadMsg: Int = 0,
@ProtoId(11) val grpMsgNeedAutoAdminWording: Int = 0,
@ProtoId(12) val grpMsgGetTransferGroupMsgFlag: Int = 0,
@ProtoId(13) val grpMsgGetQuitPayGroupMsgFlag: Int = 0,
@ProtoId(14) val grpMsgSupportInviteAutoJoin: Int = 0,
@ProtoId(15) val grpMsgMaskInviteAutoJoin: Int = 0,
@ProtoId(16) val grpMsgGetDisbandedByAdmin: Int = 0,
@ProtoId(17) val grpMsgGetC2cInviteJoinGroup: Int = 0
) : ProtoBuf
@Serializable
class FriendInfo(
@ProtoId(1) val msgJointFriend: String = "",
@ProtoId(2) val msgBlacklist: String = ""
) : ProtoBuf
@Serializable
class GroupInfo(
@ProtoId(1) val groupAuthType: Int = 0,
@ProtoId(2) val displayAction: Int = 0,
@ProtoId(3) val msgAlert: String = "",
@ProtoId(4) val msgDetailAlert: String = "",
@ProtoId(5) val msgOtherAdminDone: String = "",
@ProtoId(6) val appPrivilegeFlag: Int = 0
) : ProtoBuf
@Serializable
class MsgInviteExt(
@ProtoId(1) val srcType: Int = 0,
@ProtoId(2) val srcCode: Long = 0L,
@ProtoId(3) val waitState: Int = 0
) : ProtoBuf
@Serializable
class MsgPayGroupExt(
@ProtoId(1) val joinGrpTime: Long = 0L,
@ProtoId(2) val quitGrpTime: Long = 0L
) : ProtoBuf
@Serializable
class ReqNextSystemMsg(
@ProtoId(1) val msgNum: Int = 0,
@ProtoId(2) val followingFriendSeq: Long = 0L,
@ProtoId(3) val followingGroupSeq: Long = 0L,
@ProtoId(4) val checktype: Int /* enum */ = 1,
@ProtoId(5) val flag: Structmsg.FlagInfo? = null,
@ProtoId(6) val language: Int = 0,
@ProtoId(7) val version: Int = 0,
@ProtoId(8) val friendMsgTypeFlag: Long = 0L
) : ProtoBuf
@Serializable
class ReqSystemMsg(
@ProtoId(1) val msgNum: Int = 0,
@ProtoId(2) val latestFriendSeq: Long = 0L,
@ProtoId(3) val latestGroupSeq: Long = 0L,
@ProtoId(4) val version: Int = 0,
@ProtoId(5) val language: Int = 0
) : ProtoBuf
@Serializable
class ReqSystemMsgAction(
@ProtoId(1) val msgType: Int /* enum */ = 1,
@ProtoId(2) val msgSeq: Long = 0L,
@ProtoId(3) val reqUin: Long = 0L,
@ProtoId(4) val subType: Int = 0,
@ProtoId(5) val srcId: Int = 0,
@ProtoId(6) val subSrcId: Int = 0,
@ProtoId(7) val groupMsgType: Int = 0,
@ProtoId(8) val actionInfo: Structmsg.SystemMsgActionInfo? = null,
@ProtoId(9) val language: Int = 0
) : ProtoBuf
@Serializable
class ReqSystemMsgNew(
@ProtoId(1) val msgNum: Int = 0,
@ProtoId(2) val latestFriendSeq: Long = 0L,
@ProtoId(3) val latestGroupSeq: Long = 0L,
@ProtoId(4) val version: Int = 0,
@ProtoId(5) val checktype: Int /* enum */ = 1,
@ProtoId(6) val flag: Structmsg.FlagInfo? = null,
@ProtoId(7) val language: Int = 0,
@ProtoId(8) val isGetFrdRibbon: Boolean = true,
@ProtoId(9) val isGetGrpRibbon: Boolean = true,
@ProtoId(10) val friendMsgTypeFlag: Long = 0L
) : ProtoBuf
@Serializable
class ReqSystemMsgRead(
@ProtoId(1) val latestFriendSeq: Long = 0L,
@ProtoId(2) val latestGroupSeq: Long = 0L,
@ProtoId(3) val type: Int = 0,
@ProtoId(4) val checktype: Int /* enum */ = 1
) : ProtoBuf
@Serializable
class RspHead(
@ProtoId(1) val result: Int = 0,
@ProtoId(2) val msgFail: String = ""
) : ProtoBuf
@Serializable
class RspNextSystemMsg(
@ProtoId(1) val head: Structmsg.RspHead? = null,
@ProtoId(2) val msgs: List<Structmsg.StructMsg>? = null,
@ProtoId(3) val followingFriendSeq: Long = 0L,
@ProtoId(4) val followingGroupSeq: Long = 0L,
@ProtoId(5) val checktype: Int /* enum */ = 1,
@ProtoId(100) val gameNick: String = "",
@ProtoId(101) val undecidForQim: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(102) val unReadCount3: Int = 0
) : ProtoBuf
@Serializable
class RspSystemMsg(
@ProtoId(1) val head: Structmsg.RspHead? = null,
@ProtoId(2) val msgs: List<Structmsg.StructMsg>? = null,
@ProtoId(3) val unreadCount: Int = 0,
@ProtoId(4) val latestFriendSeq: Long = 0L,
@ProtoId(5) val latestGroupSeq: Long = 0L,
@ProtoId(6) val followingFriendSeq: Long = 0L,
@ProtoId(7) val followingGroupSeq: Long = 0L,
@ProtoId(8) val msgDisplay: String = ""
) : ProtoBuf
@Serializable
class RspSystemMsgAction(
@ProtoId(1) val head: Structmsg.RspHead? = null,
@ProtoId(2) val msgDetail: String = "",
@ProtoId(3) val type: Int = 0,
@ProtoId(5) val msgInvalidDecided: String = "",
@ProtoId(6) val remarkResult: Int = 0
) : ProtoBuf
@Serializable
class RspSystemMsgNew(
@ProtoId(1) val head: Structmsg.RspHead? = null,
@ProtoId(2) val unreadFriendCount: Int = 0,
@ProtoId(3) val unreadGroupCount: Int = 0,
@ProtoId(4) val latestFriendSeq: Long = 0L,
@ProtoId(5) val latestGroupSeq: Long = 0L,
@ProtoId(6) val followingFriendSeq: Long = 0L,
@ProtoId(7) val followingGroupSeq: Long = 0L,
@ProtoId(9) val friendmsgs: List<Structmsg.StructMsg>? = null,
@ProtoId(10) val groupmsgs: List<Structmsg.StructMsg>? = null,
@ProtoId(11) val msgRibbonFriend: Structmsg.StructMsg? = null,
@ProtoId(12) val msgRibbonGroup: Structmsg.StructMsg? = null,
@ProtoId(13) val msgDisplay: String = "",
@ProtoId(14) val grpMsgDisplay: String = "",
@ProtoId(15) val over: Int = 0,
@ProtoId(20) val checktype: Int /* enum */ = 1,
@ProtoId(100) val gameNick: String = "",
@ProtoId(101) val undecidForQim: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(102) val unReadCount3: Int = 0
) : ProtoBuf
@Serializable
class RspSystemMsgRead(
@ProtoId(1) val head: Structmsg.RspHead? = null,
@ProtoId(2) val type: Int = 0,
@ProtoId(3) val checktype: Int /* enum */ = 1
) : ProtoBuf
@Serializable
class StructMsg(
@ProtoId(1) val version: Int = 0,
@ProtoId(2) val msgType: Int /* enum */ = 1,
@ProtoId(3) val msgSeq: Long = 0L,
@ProtoId(4) val msgTime: Long = 0L,
@ProtoId(5) val reqUin: Long = 0L,
@ProtoId(6) val unreadFlag: Int = 0,
@ProtoId(50) val msg: Structmsg.SystemMsg? = null
) : ProtoBuf
@Serializable
class SystemMsg(
@ProtoId(1) val subType: Int = 0,
@ProtoId(2) val msgTitle: String = "",
@ProtoId(3) val msgDescribe: String = "",
@ProtoId(4) val msgAdditional: String = "",
@ProtoId(5) val msgSource: String = "",
@ProtoId(6) val msgDecided: String = "",
@ProtoId(7) val srcId: Int = 0,
@ProtoId(8) val subSrcId: Int = 0,
@ProtoId(9) val actions: List<Structmsg.SystemMsgAction>? = null,
@ProtoId(10) val groupCode: Long = 0L,
@ProtoId(11) val actionUin: Long = 0L,
@ProtoId(12) val groupMsgType: Int = 0,
@ProtoId(13) val groupInviterRole: Int = 0,
@ProtoId(14) val friendInfo: Structmsg.FriendInfo? = null,
@ProtoId(15) val groupInfo: Structmsg.GroupInfo? = null,
@ProtoId(16) val actorUin: Long = 0L,
@ProtoId(17) val msgActorDescribe: String = "",
@ProtoId(18) val msgAdditionalList: String = "",
@ProtoId(19) val relation: Int = 0,
@ProtoId(20) val reqsubtype: Int = 0,
@ProtoId(21) val cloneUin: Long = 0L,
@ProtoId(22) val discussUin: Long = 0L,
@ProtoId(23) val eimGroupId: Long = 0L,
@ProtoId(24) val msgInviteExtinfo: Structmsg.MsgInviteExt? = null,
@ProtoId(25) val msgPayGroupExtinfo: Structmsg.MsgPayGroupExt? = null,
@ProtoId(26) val sourceFlag: Int = 0,
@ProtoId(27) val gameNick: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(28) val gameMsg: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(29) val groupFlagext3: Int = 0,
@ProtoId(30) val groupOwnerUin: Long = 0L,
@ProtoId(31) val doubtFlag: Int = 0,
@ProtoId(32) val warningTips: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(33) val nameMore: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(50) val reqUinFaceid: Int = 0,
@ProtoId(51) val reqUinNick: String = "",
@ProtoId(52) val groupName: String = "",
@ProtoId(53) val actionUinNick: String = "",
@ProtoId(54) val msgQna: String = "",
@ProtoId(55) val msgDetail: String = "",
@ProtoId(57) val groupExtFlag: Int = 0,
@ProtoId(58) val actorUinNick: String = "",
@ProtoId(59) val picUrl: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(60) val cloneUinNick: String = "",
@ProtoId(61) val reqUinBusinessCard: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(63) val eimGroupIdName: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(64) val reqUinPreRemark: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(65) val actionUinQqNick: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(66) val actionUinRemark: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(67) val reqUinGender: Int = 0,
@ProtoId(68) val reqUinAge: Int = 0,
@ProtoId(69) val c2cInviteJoinGroupFlag: Int = 0,
@ProtoId(101) val cardSwitch: Int = 0
) : ProtoBuf
@Serializable
class SystemMsgAction(
@ProtoId(1) val name: String = "",
@ProtoId(2) val result: String = "",
@ProtoId(3) val action: Int = 0,
@ProtoId(4) val actionInfo: Structmsg.SystemMsgActionInfo? = null,
@ProtoId(5) val detailName: String = ""
) : ProtoBuf
@Serializable
class SystemMsgActionInfo(
@ProtoId(1) val type: Int /* enum */ = 1,
@ProtoId(2) val groupCode: Long = 0L,
@ProtoId(3) val sig: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(50) val msg: String = "",
@ProtoId(51) val groupId: Int = 0,
@ProtoId(52) val remark: String = "",
@ProtoId(53) val blacklist: Boolean = false,
@ProtoId(54) val addFrdSNInfo: Structmsg.AddFrdSNInfo? = null
) : ProtoBuf
}
@Serializable
class Youtu : ProtoBuf {
@Serializable
class NameCardOcrRsp(
@ProtoId(1) val errorcode: Int = 0,
@ProtoId(2) val errormsg: String = "",
@ProtoId(3) val uin: String = "",
@ProtoId(4) val uinConfidence: Float = 0.0F,
@ProtoId(5) val phone: String = "",
@ProtoId(6) val phoneConfidence: Float = 0.0F,
@ProtoId(7) val name: String = "",
@ProtoId(8) val nameConfidence: Float = 0.0F,
@ProtoId(9) val image: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(10) val sessionId: String = ""
) : ProtoBuf
}
......@@ -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")
......@@ -470,4 +471,36 @@ data class MemberUnmuteEvent(
// endregion
// endregion
\ No newline at end of file
// 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