Commit 5c04ba44 authored by jiahua.liu's avatar jiahua.liu

Group management Update

parent 30d41f6e
package net.mamoe.mirai.qqandroid package net.mamoe.mirai.qqandroid
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.FriendNameRemark import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.PreviousNameList import net.mamoe.mirai.data.PreviousNameList
...@@ -23,6 +25,7 @@ import net.mamoe.mirai.utils.io.PlatformSocket ...@@ -23,6 +25,7 @@ import net.mamoe.mirai.utils.io.PlatformSocket
import net.mamoe.mirai.utils.io.discardExact import net.mamoe.mirai.utils.io.discardExact
import net.mamoe.mirai.utils.unsafeWeakRef import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.properties.Delegates
internal abstract class ContactImpl : Contact internal abstract class ContactImpl : Contact
...@@ -108,15 +111,106 @@ internal class MemberImpl( ...@@ -108,15 +111,106 @@ internal class MemberImpl(
} }
/**
* 对GroupImpl
* 中name/announcement的更改会直接向服务器异步汇报
*/
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
internal class GroupImpl( internal class GroupImpl(
bot: QQAndroidBot, override val coroutineContext: CoroutineContext, bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
override val id: Long, override val id: Long,
val uin: Long, val uin: Long,
override var name: String, initName: String,
override var announcement: String, initAnnouncement: String,
initAllowMemberInvite: Boolean,
initConfessTalk: Boolean,
initMuteAll: Boolean,
initAutoApprove: Boolean,
initAnonymousChat: Boolean,
override var members: ContactList<Member> override var members: ContactList<Member>
) : ContactImpl(), Group { ) : ContactImpl(), Group {
override var name by Delegates.observable(initName) { _, oldValue, newValue ->
if (this.botPermission != MemberPermission.MEMBER && oldValue != newValue) {
this.bot.launch {
bot.network.run {
TroopManagement.updateGroupInfo.name(
client = bot.client,
groupCode = id,
newName = newValue
).sendWithoutExpect()
}
}
}
}
override var announcement: String by Delegates.observable(initAnnouncement) { _, oldValue, newValue ->
if (this.botPermission != MemberPermission.MEMBER && oldValue != newValue) {
this.bot.launch {
bot.network.run {
TroopManagement.updateGroupInfo.memo(
client = bot.client,
groupCode = id,
newMemo = newValue
).sendWithoutExpect()
}
}
}
}
override var allowMemberInvite: Boolean by Delegates.observable(initAllowMemberInvite) { _, oldValue, newValue ->
if (this.botPermission != MemberPermission.MEMBER && oldValue != newValue) {
this.bot.launch {
bot.network.run {
TroopManagement.updateGroupInfo.allowMemberInvite(
client = bot.client,
groupCode = id,
switch = newValue
).sendWithoutExpect()
}
}
}
}
override var autoApprove: Boolean by Delegates.observable(initAutoApprove) { _, oldValue, newValue ->
//暂时也不能改
}
override val anonymousChat: Boolean by Delegates.observable(initAnonymousChat) { _, oldValue, newValue ->
//暂时不能改
}
override var confessTalk: Boolean by Delegates.observable(initConfessTalk) { _, oldValue, newValue ->
if (this.botPermission != MemberPermission.MEMBER && oldValue != newValue) {
this.bot.launch {
bot.network.run {
TroopManagement.updateGroupInfo.confessTalk(
client = bot.client,
groupCode = id,
switch = newValue
).sendWithoutExpect()
}
}
}
}
override var muteAll: Boolean by Delegates.observable(initMuteAll) { _, oldValue, newValue ->
if (this.botPermission != MemberPermission.MEMBER && oldValue != newValue) {
this.bot.launch {
bot.network.run {
TroopManagement.updateGroupInfo.muteAll(
client = bot.client,
groupCode = id,
switch = newValue
).sendWithoutExpect()
}
}
}
}
override lateinit var owner: Member override lateinit var owner: Member
override var botPermission: MemberPermission = MemberPermission.MEMBER override var botPermission: MemberPermission = MemberPermission.MEMBER
...@@ -124,6 +218,7 @@ internal class GroupImpl( ...@@ -124,6 +218,7 @@ internal class GroupImpl(
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }
override operator fun get(id: Long): Member { override operator fun get(id: Long): Member {
return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("for group id $id") return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("for group id $id")
} }
......
...@@ -24,6 +24,7 @@ import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent ...@@ -24,6 +24,7 @@ import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent
import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.* import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
...@@ -167,8 +168,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -167,8 +168,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
coroutineContext = this.coroutineContext, coroutineContext = this.coroutineContext,
id = it.groupCode, id = it.groupCode,
uin = it.groupUin, uin = it.groupUin,
name = it.groupName, initName = it.groupName,
announcement = it.groupMemo, initAnnouncement = it.groupMemo,
initAllowMemberInvite = false,
initConfessTalk = false,
initMuteAll = false,
initAutoApprove = false,
members = contactList members = contactList
) )
group.owner = group.owner =
...@@ -238,6 +243,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -238,6 +243,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect() MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
} }
suspend fun getGroupInfo(uin: Long) {
val data = TroopManagement.getGroupInfo(
client = bot.client,
groupCode = uin
).sendAndExpect<TroopManagement.getGroupInfo.Response>(timeoutMillis = 3000)
}
suspend fun getTroopMemberList(group: GroupImpl, list: ContactList<Member>, owner: Long): ContactList<Member> { suspend fun getTroopMemberList(group: GroupImpl, list: ContactList<Member>, owner: Long): ContactList<Member> {
bot.logger.info("开始获取群[${group.uin}]成员列表") bot.logger.info("开始获取群[${group.uin}]成员列表")
var size = 0 var size = 0
......
...@@ -127,7 +127,8 @@ internal object KnownPacketFactories { ...@@ -127,7 +127,8 @@ internal object KnownPacketFactories {
LongConn.OffPicDown, LongConn.OffPicDown,
TroopManagement.EditNametag, TroopManagement.EditNametag,
TroopManagement.Mute, TroopManagement.Mute,
TroopManagement.MuteAll TroopManagement.updateGroupInfo,
TroopManagement.getGroupInfo
) )
object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf( object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf(
......
...@@ -3,10 +3,14 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat ...@@ -3,10 +3,14 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import kotlinx.io.core.toByteArray
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.io.serialization.writeProtoBuf import net.mamoe.mirai.qqandroid.io.serialization.writeProtoBuf
import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Oidb0x88d
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Oidb0x89a
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.OidbSso import net.mamoe.mirai.qqandroid.network.protocol.data.proto.OidbSso
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
...@@ -14,6 +18,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory ...@@ -14,6 +18,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.utils.daysToSeconds import net.mamoe.mirai.utils.daysToSeconds
import net.mamoe.mirai.utils.io.debugPrintThis
internal object TroopManagement { internal object TroopManagement {
...@@ -53,31 +58,210 @@ internal object TroopManagement { ...@@ -53,31 +58,210 @@ internal object TroopManagement {
} }
internal object MuteAll : OutgoingPacketFactory<MuteAll.Response>("OidbSvc.0x89a_0") { internal object getGroupInfo : OutgoingPacketFactory<getGroupInfo.Response>("OidbSvc.0x88d_7") {
class Response() : Packet
operator fun invoke(
client: QQAndroidClient,
groupCode: Long
): OutgoingPacket {
return buildOutgoingUniPacket(client) {
writeProtoBuf(
OidbSso.OIDBSSOPkg.serializer(),
OidbSso.OIDBSSOPkg(
command = 2189,
serviceType = 7,
result = 0,
bodybuffer = Oidb0x88d.ReqBody(
appid = 537062845,
stzreqgroupinfo = listOf(
Oidb0x88d.ReqGroupInfo(
stgroupinfo = Oidb0x88d.GroupInfo(
groupFlagExt = 0,
groupFlagext4 = 0,
groupFlag = 0,
groupFlagext3 = 0,//获取confess
noFingerOpenFlag = 0,
cmduinFlagEx2 = 0,
groupTypeFlag = 0,
appPrivilegeFlag = 0,
cmduinFlagEx = 0,
cmduinNewMobileFlag = 0,
cmduinUinFlag = 0,
createSourceFlag = 0,
noCodeFingerOpenFlag = 0,
ingGroupQuestion = EMPTY_BYTE_ARRAY,
ingGroupAnswer = EMPTY_BYTE_ARRAY
),
groupCode = groupCode
)
)
).toByteArray(Oidb0x88d.ReqBody.serializer())
)
)
}
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
debugPrintThis()
return Response()
}
}
internal object updateGroupInfo : OutgoingPacketFactory<updateGroupInfo.Response>("OidbSvc.0x89a_0") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
return Response return Response
} }
operator fun invoke( fun muteAll(
client: QQAndroidClient, client: QQAndroidClient,
groupCode: Long groupCode: Long,
switch: Boolean
): OutgoingPacket { ): OutgoingPacket {
return buildOutgoingUniPacket(client) { return buildOutgoingUniPacket(client) {
writeProtoBuf( writeProtoBuf(
OidbSso.OIDBSSOPkg.serializer(), OidbSso.OIDBSSOPkg.serializer(),
OidbSso.OIDBSSOPkg( OidbSso.OIDBSSOPkg(
command = 2202, command = 2202,
serviceType = 0, bodybuffer = Oidb0x89a.ReqBody(
result = 0, groupCode = groupCode,
bodybuffer = EMPTY_BYTE_ARRAY//TODO stGroupInfo = Oidb0x89a.Groupinfo(
shutupTime = if (switch) {
1
} else {
0
}
)
).toByteArray(Oidb0x89a.ReqBody.serializer())
)
)
}
}
fun confessTalk(
client: QQAndroidClient,
groupCode: Long,
switch: Boolean
): OutgoingPacket {
return buildOutgoingUniPacket(client) {
writeProtoBuf(
OidbSso.OIDBSSOPkg.serializer(),
OidbSso.OIDBSSOPkg(
command = 2202,
bodybuffer = Oidb0x89a.ReqBody(
groupCode = groupCode,
stGroupInfo = Oidb0x89a.Groupinfo(
groupFlagext3Mask = 8192,
groupFlagext3 = if (switch) {
0
} else {
8192
}
)
).toByteArray(Oidb0x89a.ReqBody.serializer())
)
)
}
}
fun autoApprove(
client: QQAndroidClient,
groupCode: Long,
switch: Boolean
): OutgoingPacket {
return buildOutgoingUniPacket(client) {
writeProtoBuf(
OidbSso.OIDBSSOPkg.serializer(),
OidbSso.OIDBSSOPkg(
command = 2202,
bodybuffer = Oidb0x89a.ReqBody(
groupCode = groupCode,
stGroupInfo = Oidb0x89a.Groupinfo(
groupFlagext3 = if (switch) {
0x00100000
} else {
0x00000000
}//暂时无效
)
).toByteArray(Oidb0x89a.ReqBody.serializer())
)
)
}
}
fun name(
client: QQAndroidClient,
groupCode: Long,
newName: String
): OutgoingPacket {
return buildOutgoingUniPacket(client) {
writeProtoBuf(
OidbSso.OIDBSSOPkg.serializer(),
OidbSso.OIDBSSOPkg(
command = 2202,
bodybuffer = Oidb0x89a.ReqBody(
groupCode = groupCode,
stGroupInfo = Oidb0x89a.Groupinfo(
ingGroupName = newName.toByteArray()
)
).toByteArray(Oidb0x89a.ReqBody.serializer())
) )
) )
} }
} }
fun memo(
client: QQAndroidClient,
groupCode: Long,
newMemo: String
): OutgoingPacket {
return buildOutgoingUniPacket(client) {
writeProtoBuf(
OidbSso.OIDBSSOPkg.serializer(),
OidbSso.OIDBSSOPkg(
command = 2202,
bodybuffer = Oidb0x89a.ReqBody(
groupCode = groupCode,
stGroupInfo = Oidb0x89a.Groupinfo(
ingGroupMemo = newMemo.toByteArray()
)
).toByteArray(Oidb0x89a.ReqBody.serializer())
)
)
}
}
fun allowMemberInvite(
client: QQAndroidClient,
groupCode: Long,
switch: Boolean
): OutgoingPacket {
return buildOutgoingUniPacket(client) {
writeProtoBuf(
OidbSso.OIDBSSOPkg.serializer(),
OidbSso.OIDBSSOPkg(
command = 2202,
bodybuffer = Oidb0x89a.ReqBody(
groupCode = groupCode,
stGroupInfo = Oidb0x89a.Groupinfo(
allowMemberInvite = if (switch) {
1
} else {
0
}
)
).toByteArray(Oidb0x89a.ReqBody.serializer())
)
)
}
}
object Response : Packet object Response : Packet
} }
internal object EditNametag : OutgoingPacketFactory<LoginPacket.LoginPacketResponse>("OidbSvc.0x8fc_2") { internal object EditNametag : OutgoingPacketFactory<LoginPacket.LoginPacketResponse>("OidbSvc.0x8fc_2") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacket.LoginPacketResponse { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacket.LoginPacketResponse {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
......
...@@ -6,7 +6,7 @@ import java.io.File ...@@ -6,7 +6,7 @@ import java.io.File
fun main() { fun main() {
println( println(
File("""/Users/jiahua.liu/Desktop/QQAndroid-F/app/src/main/java/tencent/im/s2c/msgtype0x210/submsgtype0xc7/bussinfo/mutualmark""") File("""/Users/jiahua.liu/Desktop/QQAndroid-F/app/src/main/java/tencent/im/oidb/cmd0x88d/""")
.generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n") .generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n")
) )
} }
......
...@@ -9,6 +9,51 @@ import kotlinx.coroutines.CoroutineScope ...@@ -9,6 +9,51 @@ import kotlinx.coroutines.CoroutineScope
* 群. 在 QQ Android 中叫做 "Troop" * 群. 在 QQ Android 中叫做 "Troop"
*/ */
interface Group : Contact, CoroutineScope { interface Group : Contact, CoroutineScope {
/**
* ====以下字段在更新值的时候会自动异步上报服务器更改群信息====
*/
/**
* 群名称
* [可查可改已完成]
*/
var name: String
/**
* 入群公告, 没有时为空字符串
* [可查可改已完成]
*/
var announcement: String
/**
* 全体禁言状态
* [可改已完成]
*/
var muteAll: Boolean
/**
* 坦白说状态
* [可查可改已完成]
*/
var confessTalk: Boolean
/**
* 允许群员拉人状态
* [可查可改已完成]
*/
var allowMemberInvite: Boolean
/**
* QQ中的自动加群审批
* [可查已完成]
*/
val autoApprove: Boolean
/**
* 匿名聊天
* [可查已完成]
*/
val anonymousChat: Boolean
/**
* ====以上字段在更新值的时候会自动异步上报服务器更改群信息====
*/
/** /**
* 同为 groupCode, 用户看到的群号码. * 同为 groupCode, 用户看到的群号码.
*/ */
...@@ -22,22 +67,12 @@ interface Group : Contact, CoroutineScope { ...@@ -22,22 +67,12 @@ interface Group : Contact, CoroutineScope {
val botPermission: MemberPermission val botPermission: MemberPermission
/**
* 群名称 (同步事件更新)
*/
val name: String
/**
* 入群公告, 没有时为空字符串. (同步事件更新)
*/
val announcement: String
/** /**
* 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新 * 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新
*/ */
val members: ContactList<Member> val members: ContactList<Member>
/** /**
* 获取群成员实例. 若此 id 的成员不存在, 则会抛出 [kotlin.NoSuchElementException] * 获取群成员实例. 若此 id 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
*/ */
...@@ -58,5 +93,6 @@ interface Group : Contact, CoroutineScope { ...@@ -58,5 +93,6 @@ interface Group : Contact, CoroutineScope {
*/ */
suspend fun quit(): Boolean suspend fun quit(): Boolean
fun toFullString(): String = "Group(id=${this.id}, name=$name, owner=${owner.id}, members=${members.idContentString})" fun toFullString(): String = "Group(id=${this.id}, name=$name, owner=${owner.id}, members=${members.idContentString})"
} }
\ 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