mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-08 06:47:05 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
240183d5a5
@ -11,9 +11,7 @@ package net.mamoe.mirai.qqandroid
|
||||
|
||||
import kotlinx.coroutines.launch
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.FriendNameRemark
|
||||
import net.mamoe.mirai.data.PreviousNameList
|
||||
import net.mamoe.mirai.data.Profile
|
||||
import net.mamoe.mirai.data.*
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
|
||||
@ -24,6 +22,7 @@ import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.NotOnlineImageFromFile
|
||||
import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
|
||||
import net.mamoe.mirai.qqandroid.network.highway.postImage
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
|
||||
@ -33,6 +32,7 @@ import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo
|
||||
|
||||
internal abstract class ContactImpl : Contact {
|
||||
override fun hashCode(): Int {
|
||||
@ -49,10 +49,22 @@ internal abstract class ContactImpl : Contact {
|
||||
}
|
||||
}
|
||||
|
||||
internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: CoroutineContext, override val id: Long) : ContactImpl(), QQ {
|
||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
internal inline class FriendInfoImpl(
|
||||
private val jceFriendInfo: JceFriendInfo
|
||||
) : FriendInfo {
|
||||
override val nick: String get() = jceFriendInfo.nick ?: ""
|
||||
override val uin: Long get() = jceFriendInfo.friendUin
|
||||
}
|
||||
|
||||
override lateinit var nick: String
|
||||
internal class QQImpl(
|
||||
bot: QQAndroidBot,
|
||||
override val coroutineContext: CoroutineContext,
|
||||
override val id: Long,
|
||||
private val friendInfo: FriendInfo
|
||||
) : ContactImpl(), QQ {
|
||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
override val nick: String
|
||||
get() = friendInfo.nick
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
val event = FriendMessageSendEvent(this, message).broadcast()
|
||||
@ -135,14 +147,17 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
|
||||
image.input.close()
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun queryProfile(): Profile {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun queryPreviousNameList(): PreviousNameList {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override suspend fun queryRemark(): FriendNameRemark {
|
||||
TODO("not implemented")
|
||||
}
|
||||
@ -156,24 +171,27 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
|
||||
}
|
||||
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
internal class MemberImpl(
|
||||
qq: QQImpl,
|
||||
var _groupCard: String,
|
||||
var _specialTitle: String,
|
||||
group: GroupImpl,
|
||||
override val coroutineContext: CoroutineContext,
|
||||
override var permission: MemberPermission
|
||||
memberInfo: MemberInfo
|
||||
) : ContactImpl(), Member, QQ by qq {
|
||||
override val group: GroupImpl by group.unsafeWeakRef()
|
||||
val qq: QQImpl by qq.unsafeWeakRef()
|
||||
|
||||
override var permission: MemberPermission = memberInfo.permission
|
||||
internal var _nameCard: String = memberInfo.nameCard
|
||||
internal var _specialTitle: String = memberInfo.specialTitle
|
||||
|
||||
override var nameCard: String
|
||||
get() = _groupCard
|
||||
get() = _nameCard
|
||||
set(newValue) {
|
||||
group.checkBotPermissionOperator()
|
||||
if (_groupCard != newValue) {
|
||||
val oldValue = _groupCard
|
||||
_groupCard = newValue
|
||||
if (_nameCard != newValue) {
|
||||
val oldValue = _nameCard
|
||||
_nameCard = newValue
|
||||
launch {
|
||||
bot.network.run {
|
||||
TroopManagement.EditGroupNametag(
|
||||
@ -223,7 +241,8 @@ internal class MemberImpl(
|
||||
).sendAndExpect<TroopManagement.Mute.Response>()
|
||||
}
|
||||
|
||||
MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
|
||||
@Suppress("RemoveRedundantQualifierName") // or unresolved reference
|
||||
net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -241,7 +260,8 @@ internal class MemberImpl(
|
||||
).sendAndExpect<TroopManagement.Mute.Response>()
|
||||
}
|
||||
|
||||
MemberUnmuteEvent(this@MemberImpl, null).broadcast()
|
||||
@Suppress("RemoveRedundantQualifierName") // or unresolved reference
|
||||
net.mamoe.mirai.event.events.MemberUnmuteEvent(this@MemberImpl, null).broadcast()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -269,25 +289,60 @@ internal class MemberImpl(
|
||||
override fun hashCode(): Int = super.hashCode()
|
||||
}
|
||||
|
||||
internal class MemberInfoImpl(
|
||||
private val jceInfo: StTroopMemberInfo,
|
||||
private val groupOwnerId: Long
|
||||
) : MemberInfo {
|
||||
override val uin: Long get() = jceInfo.memberUin
|
||||
override val nameCard: String get() = jceInfo.sName ?: ""
|
||||
override val nick: String get() = jceInfo.nick
|
||||
override val permission: MemberPermission
|
||||
get() = when {
|
||||
jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER
|
||||
jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR
|
||||
else -> MemberPermission.MEMBER
|
||||
}
|
||||
override val specialTitle: String get() = jceInfo.sSpecialTitle ?: ""
|
||||
}
|
||||
|
||||
/**
|
||||
* 对GroupImpl
|
||||
* 中name/announcement的更改会直接向服务器异步汇报
|
||||
*/
|
||||
@Suppress("PropertyName")
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
internal class GroupImpl(
|
||||
bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
|
||||
override val id: Long,
|
||||
val uin: Long,
|
||||
var _name: String,
|
||||
var _announcement: String,
|
||||
var _allowMemberInvite: Boolean,
|
||||
var _confessTalk: Boolean,
|
||||
var _muteAll: Boolean,
|
||||
var _autoApprove: Boolean,
|
||||
var _anonymousChat: Boolean,
|
||||
override val members: ContactList<Member>
|
||||
groupInfo: GroupInfo,
|
||||
members: Sequence<MemberInfo>
|
||||
) : ContactImpl(), Group {
|
||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
val uin: Long = groupInfo.uin
|
||||
|
||||
override lateinit var owner: Member
|
||||
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
override lateinit var botPermission: MemberPermission
|
||||
|
||||
override val members: ContactList<Member> = ContactList(members.mapNotNull {
|
||||
if (it.uin == bot.uin) {
|
||||
botPermission = it.permission
|
||||
null
|
||||
} else Member(it).also { member ->
|
||||
if (member.permission == MemberPermission.OWNER) {
|
||||
owner = member
|
||||
}
|
||||
}
|
||||
}.toLockFreeLinkedList())
|
||||
|
||||
internal var _name: String = groupInfo.name
|
||||
internal var _announcement: String = groupInfo.memo
|
||||
internal var _allowMemberInvite: Boolean = groupInfo.allowMemberInvite
|
||||
internal var _confessTalk: Boolean = groupInfo.confessTalk
|
||||
internal var _muteAll: Boolean = groupInfo.muteAll
|
||||
internal var _autoApprove: Boolean = groupInfo.autoApprove
|
||||
internal var _anonymousChat: Boolean = groupInfo.allowAnonymousChat
|
||||
|
||||
override var name: String
|
||||
get() = _name
|
||||
@ -304,7 +359,7 @@ internal class GroupImpl(
|
||||
newName = newValue
|
||||
).sendWithoutExpect()
|
||||
}
|
||||
GroupNameChangeEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
|
||||
GroupNameChangeEvent(oldValue, newValue, this@GroupImpl, true).broadcast()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -377,7 +432,7 @@ internal class GroupImpl(
|
||||
switch = newValue
|
||||
).sendWithoutExpect()
|
||||
}
|
||||
GroupAllowConfessTalkEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
|
||||
GroupAllowConfessTalkEvent(oldValue, newValue, this@GroupImpl, true).broadcast()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -403,16 +458,21 @@ internal class GroupImpl(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override lateinit var owner: Member
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
override var botPermission: MemberPermission = MemberPermission.MEMBER
|
||||
|
||||
override suspend fun quit(): Boolean {
|
||||
check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
override fun Member(memberInfo: MemberInfo): Member {
|
||||
return MemberImpl(
|
||||
bot.QQ(memberInfo) as QQImpl,
|
||||
this,
|
||||
this.coroutineContext,
|
||||
memberInfo
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override operator fun get(id: Long): Member {
|
||||
return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("member $id not found in group $uin")
|
||||
@ -426,8 +486,6 @@ internal class GroupImpl(
|
||||
return members.delegate.filteringGetOrNull { it.id == id }
|
||||
}
|
||||
|
||||
override val bot: QQAndroidBot by bot.unsafeWeakRef()
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
val event = GroupMessageSendEvent(this, message).broadcast()
|
||||
if (event.isCancelled) {
|
||||
|
@ -17,11 +17,18 @@ import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.contact.filteringGetOrNull
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
import net.mamoe.mirai.data.FriendInfo
|
||||
import net.mamoe.mirai.data.GroupInfo
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.event.events.BotEvent
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||
import net.mamoe.mirai.utils.*
|
||||
import kotlin.collections.asSequence
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@ -31,22 +38,32 @@ internal expect class QQAndroidBot constructor(
|
||||
configuration: BotConfiguration
|
||||
) : QQAndroidBotBase
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
||||
internal abstract class QQAndroidBotBase constructor(
|
||||
context: Context,
|
||||
account: BotAccount,
|
||||
configuration: BotConfiguration
|
||||
) : BotImpl<QQAndroidBotNetworkHandler>(account, configuration) {
|
||||
val client: QQAndroidClient =
|
||||
QQAndroidClient(context, account, bot = @Suppress("LeakingThis") this as QQAndroidBot, device = configuration.deviceInfo?.invoke(context) ?: SystemDeviceInfo(context))
|
||||
QQAndroidClient(
|
||||
context,
|
||||
account,
|
||||
bot = @Suppress("LeakingThis") this as QQAndroidBot,
|
||||
device = configuration.deviceInfo?.invoke(context) ?: SystemDeviceInfo(context)
|
||||
)
|
||||
internal var firstLoginSucceed: Boolean = false
|
||||
override val uin: Long get() = client.uin
|
||||
override val qqs: ContactList<QQ> = ContactList(LockFreeLinkedList())
|
||||
|
||||
override val selfQQ: QQ by lazy { QQ(uin) }
|
||||
override val selfQQ: QQ by lazy {
|
||||
QQ(object : FriendInfo {
|
||||
override val uin: Long get() = this@QQAndroidBotBase.uin
|
||||
override val nick: String get() = this@QQAndroidBotBase.nick
|
||||
})
|
||||
}
|
||||
|
||||
override fun QQ(id: Long): QQ {
|
||||
return QQImpl(this as QQAndroidBot, coroutineContext, id)
|
||||
override fun QQ(friendInfo: FriendInfo): QQ {
|
||||
return QQImpl(this as QQAndroidBot, coroutineContext, friendInfo.uin, friendInfo)
|
||||
}
|
||||
|
||||
override fun createNetworkHandler(coroutineContext: CoroutineContext): QQAndroidBotNetworkHandler {
|
||||
@ -60,6 +77,45 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
return groups.delegate.filteringGetOrNull { (it as GroupImpl).uin == uin } ?: throw NoSuchElementException("Can not found group with ID=${uin}")
|
||||
}
|
||||
|
||||
fun getGroupByUinOrNull(uin: Long): Group? {
|
||||
return groups.delegate.filteringGetOrNull { (it as GroupImpl).uin == uin }
|
||||
}
|
||||
|
||||
override suspend fun queryGroupList(): Sequence<Long> {
|
||||
return network.run {
|
||||
FriendList.GetTroopListSimplify(bot.client)
|
||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||
}.groups.asSequence().map { it.groupUin.shl(32) and it.groupCode }
|
||||
}
|
||||
|
||||
override suspend fun queryGroupInfo(id: Long): GroupInfo = network.run {
|
||||
TroopManagement.GetGroupInfo(
|
||||
client = bot.client,
|
||||
groupCode = id
|
||||
).sendAndExpect<GroupInfoImpl>()
|
||||
}
|
||||
|
||||
override suspend fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence<MemberInfo> = network.run {
|
||||
var nextUin = 0L
|
||||
var sequence = sequenceOf<MemberInfoImpl>()
|
||||
while (true) {
|
||||
val data = FriendList.GetTroopMemberList(
|
||||
client = bot.client,
|
||||
targetGroupUin = groupUin,
|
||||
targetGroupCode = groupCode,
|
||||
nextUin = nextUin
|
||||
).sendAndExpect<FriendList.GetTroopMemberList.Response>(timeoutMillis = 3000)
|
||||
sequence += data.members.asSequence().map { troopMemberInfo ->
|
||||
MemberInfoImpl(troopMemberInfo, ownerId)
|
||||
}
|
||||
nextUin = data.nextUin
|
||||
if (nextUin == 0L) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return sequence
|
||||
}
|
||||
|
||||
override fun onEvent(event: BotEvent): Boolean {
|
||||
return firstLoginSucceed
|
||||
}
|
||||
|
@ -18,22 +18,19 @@ import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.contact.ContactList
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.data.MultiPacket
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.*
|
||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import net.mamoe.mirai.qqandroid.FriendInfoImpl
|
||||
import net.mamoe.mirai.qqandroid.GroupImpl
|
||||
import net.mamoe.mirai.qqandroid.MemberImpl
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.QQImpl
|
||||
import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
|
||||
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.chat.TroopManagement
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||
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.login.Heartbeat
|
||||
@ -148,7 +145,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
totalFriendCount = data.totalFriendCount
|
||||
data.friendList.forEach {
|
||||
// atomic add
|
||||
bot.qqs.delegate.addLast(QQImpl(bot, bot.coroutineContext, it.friendUin)).also {
|
||||
bot.qqs.delegate.addLast(QQImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it))).also {
|
||||
currentFriendCount++
|
||||
}
|
||||
}
|
||||
@ -171,32 +168,33 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||
|
||||
troopListData.groups.forEach { troopNum ->
|
||||
val contactList = ContactList(LockFreeLinkedList<Member>())
|
||||
val groupInfoResponse =
|
||||
TroopManagement.GetGroupOperationInfo(
|
||||
client = bot.client,
|
||||
groupCode = troopNum.groupCode
|
||||
).sendAndExpect<TroopManagement.GetGroupOperationInfo.Response>()
|
||||
|
||||
val group =
|
||||
GroupImpl(
|
||||
bot = bot,
|
||||
coroutineContext = bot.coroutineContext,
|
||||
id = troopNum.groupCode,
|
||||
uin = troopNum.groupUin,
|
||||
_name = troopNum.groupName,
|
||||
_announcement = troopNum.groupMemo,
|
||||
_allowMemberInvite = groupInfoResponse.allowMemberInvite,
|
||||
_confessTalk = groupInfoResponse.confessTalk,
|
||||
_muteAll = troopNum.dwShutUpTimestamp != 0L,
|
||||
_autoApprove = groupInfoResponse.autoApprove,
|
||||
_anonymousChat = groupInfoResponse.allowAnonymousChat,
|
||||
members = contactList
|
||||
)
|
||||
bot.groups.delegate.addLast(group)
|
||||
launch {
|
||||
try {
|
||||
fillTroopMemberList(group, contactList, troopNum.dwGroupOwnerUin)
|
||||
bot.groups.delegate.addLast(
|
||||
GroupImpl(
|
||||
bot = bot,
|
||||
coroutineContext = bot.coroutineContext,
|
||||
id = troopNum.groupCode,
|
||||
groupInfo = bot.queryGroupInfo(troopNum.groupCode).apply {
|
||||
this as GroupInfoImpl
|
||||
|
||||
if (this.delegate.groupName == null) {
|
||||
this.delegate.groupName = troopNum.groupName
|
||||
}
|
||||
|
||||
if (this.delegate.groupMemo == null) {
|
||||
this.delegate.groupMemo = troopNum.groupMemo
|
||||
}
|
||||
|
||||
if (this.delegate.groupUin == null) {
|
||||
this.delegate.groupUin = troopNum.groupUin
|
||||
}
|
||||
|
||||
this.delegate.groupCode = troopNum.groupCode
|
||||
},
|
||||
members = bot.queryGroupMemberList(troopNum.groupUin, troopNum.groupCode, troopNum.dwGroupOwnerUin)
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
bot.logger.error("群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试")
|
||||
bot.logger.error(e)
|
||||
@ -242,47 +240,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
return lastException
|
||||
}
|
||||
|
||||
suspend fun fillTroopMemberList(group: GroupImpl, list: ContactList<Member>, owner: Long) {
|
||||
bot.logger.verbose("开始获取群[${group.uin}]成员列表")
|
||||
var size = 0
|
||||
var nextUin = 0L
|
||||
while (true) {
|
||||
val data = FriendList.GetTroopMemberList(
|
||||
client = bot.client,
|
||||
targetGroupUin = group.uin,
|
||||
targetGroupCode = group.id,
|
||||
nextUin = nextUin
|
||||
).sendAndExpect<FriendList.GetTroopMemberList.Response>(timeoutMillis = 3000)
|
||||
data.members.forEach { troopMemberInfo ->
|
||||
val member = MemberImpl(
|
||||
qq = (bot.QQ(troopMemberInfo.memberUin) as QQImpl).also { it.nick = troopMemberInfo.nick },
|
||||
_groupCard = troopMemberInfo.sName ?: "",
|
||||
_specialTitle = troopMemberInfo.sSpecialTitle ?: "",
|
||||
group = group,
|
||||
coroutineContext = group.coroutineContext,
|
||||
permission = when {
|
||||
troopMemberInfo.memberUin == owner -> MemberPermission.OWNER
|
||||
troopMemberInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR
|
||||
else -> MemberPermission.MEMBER
|
||||
}
|
||||
)
|
||||
if (member.permission == MemberPermission.OWNER) {
|
||||
group.owner = member
|
||||
}
|
||||
if (troopMemberInfo.memberUin != bot.uin) {
|
||||
list.delegate.addLast(member)
|
||||
} else {
|
||||
group.botPermission = member.permission
|
||||
}
|
||||
size += data.members.size
|
||||
nextUin = data.nextUin
|
||||
}
|
||||
if (nextUin == 0L) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理
|
||||
*/
|
||||
@ -360,7 +317,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
if (packet is CancellableEvent && packet.isCancelled) return
|
||||
}
|
||||
|
||||
bot.logger.info("Received packet: ${packet.toString().replace("\n", """\n""").replace("\r", "")}")
|
||||
bot.logger.info("Received: ${packet.toString().replace("\n", """\n""").replace("\r", "")}")
|
||||
|
||||
packetFactory?.run {
|
||||
when (this) {
|
||||
|
@ -180,32 +180,32 @@ class Oidb0x88d : ProtoBuf {
|
||||
@SerialId(12) val groupDefaultPage: Int? = null,
|
||||
@SerialId(13) val groupInfoSeq: Int? = null,
|
||||
@SerialId(14) val groupRoamingTime: Int? = null,
|
||||
@SerialId(15) val ingGroupName: ByteArray? = null,
|
||||
@SerialId(16) val ingGroupMemo: ByteArray? = null,
|
||||
@SerialId(17) val ingGroupFingerMemo: ByteArray? = null,
|
||||
@SerialId(18) val ingGroupClassText: ByteArray? = null,
|
||||
@SerialId(15) var groupName: String? = null,
|
||||
@SerialId(16) var groupMemo: String? = null,
|
||||
@SerialId(17) val ingGroupFingerMemo: String? = null,
|
||||
@SerialId(18) val ingGroupClassText: String? = null,
|
||||
@SerialId(19) val groupAllianceCode: List<Int>? = null,
|
||||
@SerialId(20) val groupExtraAdmNum: Int? = null,
|
||||
@SerialId(21) val groupUin: Long? = null,
|
||||
@SerialId(21) var groupUin: Long? = null,
|
||||
@SerialId(22) val groupCurMsgSeq: Int? = null,
|
||||
@SerialId(23) val groupLastMsgTime: Int? = null,
|
||||
@SerialId(24) val ingGroupQuestion: ByteArray? = null,
|
||||
@SerialId(25) val ingGroupAnswer: ByteArray? = null,
|
||||
@SerialId(24) val ingGroupQuestion: String? = null,
|
||||
@SerialId(25) val ingGroupAnswer: String? = null,
|
||||
@SerialId(26) val groupVisitorMaxNum: Int? = null,
|
||||
@SerialId(27) val groupVisitorCurNum: Int? = null,
|
||||
@SerialId(28) val levelNameSeq: Int? = null,
|
||||
@SerialId(29) val groupAdminMaxNum: Int? = null,
|
||||
@SerialId(30) val groupAioSkinTimestamp: Int? = null,
|
||||
@SerialId(31) val groupBoardSkinTimestamp: Int? = null,
|
||||
@SerialId(32) val ingGroupAioSkinUrl: ByteArray? = null,
|
||||
@SerialId(33) val ingGroupBoardSkinUrl: ByteArray? = null,
|
||||
@SerialId(32) val ingGroupAioSkinUrl: String? = null,
|
||||
@SerialId(33) val ingGroupBoardSkinUrl: String? = null,
|
||||
@SerialId(34) val groupCoverSkinTimestamp: Int? = null,
|
||||
@SerialId(35) val ingGroupCoverSkinUrl: ByteArray? = null,
|
||||
@SerialId(35) val ingGroupCoverSkinUrl: String? = null,
|
||||
@SerialId(36) val groupGrade: Int? = null,
|
||||
@SerialId(37) val activeMemberNum: Int? = null,
|
||||
@SerialId(38) val certificationType: Int? = null,
|
||||
@SerialId(39) val ingCertificationText: ByteArray? = null,
|
||||
@SerialId(40) val ingGroupRichFingerMemo: ByteArray? = null,
|
||||
@SerialId(39) val ingCertificationText: String? = null,
|
||||
@SerialId(40) val ingGroupRichFingerMemo: String? = null,
|
||||
@SerialId(41) val tagRecord: List<TagRecord>? = null,
|
||||
@SerialId(42) val groupGeoInfo: GroupGeoInfo? = null,
|
||||
@SerialId(43) val headPortraitSeq: Int? = null,
|
||||
@ -254,7 +254,7 @@ class Oidb0x88d : ProtoBuf {
|
||||
@SerialId(86) val isAllowConfGroupMemberNick: Int? = null,
|
||||
@SerialId(87) val isAllowConfGroupMemberAtAll: Int? = null,
|
||||
@SerialId(88) val isAllowConfGroupMemberModifyGroupName: Int? = null,
|
||||
@SerialId(89) val ingLongGroupName: ByteArray? = null,
|
||||
@SerialId(89) val longGroupName: String? = null,
|
||||
@SerialId(90) val cmduinJoinRealMsgSeq: Int? = null,
|
||||
@SerialId(91) val isGroupFreeze: Int? = null,
|
||||
@SerialId(92) val msgLimitFrequency: Int? = null,
|
||||
@ -265,7 +265,8 @@ class Oidb0x88d : ProtoBuf {
|
||||
@SerialId(97) val isAllowHlGuildBinary: Int? = null,
|
||||
@SerialId(98) val cmduinRingtoneId: Int? = null,
|
||||
@SerialId(99) val groupFlagext4: Int? = null,
|
||||
@SerialId(100) val groupFreezeReason: Int? = null
|
||||
@SerialId(100) val groupFreezeReason: Int? = null,
|
||||
@SerialId(101) var groupCode: Long? = null // mirai 添加
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
|
@ -135,7 +135,7 @@ internal object KnownPacketFactories {
|
||||
TroopManagement.EditSpecialTitle,
|
||||
TroopManagement.Mute,
|
||||
TroopManagement.GroupOperation,
|
||||
TroopManagement.GetGroupOperationInfo,
|
||||
TroopManagement.GetGroupInfo,
|
||||
TroopManagement.EditGroupNametag,
|
||||
TroopManagement.Kick,
|
||||
Heartbeat.Alive
|
||||
|
@ -14,6 +14,7 @@ import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.core.toByteArray
|
||||
import kotlinx.serialization.toUtf8Bytes
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
@ -23,12 +24,27 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.jce.ModifyGroupCardReq
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.stUinInfo
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.*
|
||||
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.OutgoingPacketFactory
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
|
||||
import net.mamoe.mirai.utils.daysToSeconds
|
||||
import net.mamoe.mirai.utils.io.encodeToString
|
||||
import net.mamoe.mirai.data.GroupInfo as MiraiGroupInfo
|
||||
|
||||
internal inline class GroupInfoImpl(
|
||||
internal val delegate: Oidb0x88d.GroupInfo
|
||||
) : MiraiGroupInfo, Packet {
|
||||
override val uin: Long get() = delegate.groupUin ?: error("cannot find groupUin")
|
||||
override val owner: Long get() = delegate.groupOwner ?: error("cannot find groupOwner")
|
||||
override val groupCode: Long get() = Group.calculateGroupCodeByGroupUin(uin)
|
||||
override val memo: String get() = delegate.groupMemo ?: error("cannot find groupMemo")
|
||||
override val name: String get() = delegate.groupName ?: delegate.longGroupName ?: error("cannot find groupName")
|
||||
override val allowMemberInvite get() = delegate.groupFlagExt?.and(0x000000c0) != 0
|
||||
override val allowAnonymousChat get() = delegate.groupFlagExt?.and(0x40000000) == 0
|
||||
override val autoApprove get() = delegate.groupFlagext3?.and(0x00100000) == 0
|
||||
override val confessTalk get() = delegate.groupFlagext3?.and(0x00002000) == 0
|
||||
override val muteAll: Boolean get() = delegate.shutupTimestamp != 0
|
||||
}
|
||||
|
||||
internal class TroopManagement {
|
||||
|
||||
@ -70,16 +86,7 @@ internal class TroopManagement {
|
||||
}
|
||||
|
||||
|
||||
internal object GetGroupOperationInfo : OutgoingPacketFactory<GetGroupOperationInfo.Response>("OidbSvc.0x88d_7") {
|
||||
class Response(
|
||||
val allowAnonymousChat: Boolean,
|
||||
val allowMemberInvite: Boolean,
|
||||
val autoApprove: Boolean,
|
||||
val confessTalk: Boolean
|
||||
) : Packet {
|
||||
override fun toString(): String = "Response(GroupInfo)"
|
||||
}
|
||||
|
||||
internal object GetGroupInfo : OutgoingPacketFactory<GroupInfoImpl>("OidbSvc.0x88d_7") {
|
||||
operator fun invoke(
|
||||
client: QQAndroidClient,
|
||||
groupCode: Long
|
||||
@ -109,8 +116,13 @@ internal class TroopManagement {
|
||||
cmduinUinFlag = 0,
|
||||
createSourceFlag = 0,
|
||||
noCodeFingerOpenFlag = 0,
|
||||
ingGroupQuestion = EMPTY_BYTE_ARRAY,
|
||||
ingGroupAnswer = EMPTY_BYTE_ARRAY
|
||||
ingGroupQuestion = "",
|
||||
ingGroupAnswer = "",
|
||||
groupName = "",
|
||||
longGroupName = "",
|
||||
groupMemo = "",
|
||||
groupUin = 0,
|
||||
groupOwner = 0
|
||||
),
|
||||
groupCode = groupCode
|
||||
)
|
||||
@ -121,14 +133,9 @@ internal class TroopManagement {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): GroupInfoImpl {
|
||||
with(this.readBytes().loadAs(OidbSso.OIDBSSOPkg.serializer()).bodybuffer.loadAs(Oidb0x88d.RspBody.serializer()).stzrspgroupinfo!![0].stgroupinfo!!) {
|
||||
return Response(
|
||||
allowMemberInvite = (this.groupFlagExt?.and(0x000000c0) != 0),
|
||||
allowAnonymousChat = (this.groupFlagExt?.and(0x40000000) == 0),
|
||||
autoApprove = (this.groupFlagext3?.and(0x00100000) == 0),
|
||||
confessTalk = (this.groupFlagext3?.and(0x00002000) == 0)
|
||||
)
|
||||
return GroupInfoImpl(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +133,15 @@ internal class MessageSvc {
|
||||
val messages = resp.uinPairMsgs.asSequence().filterNot { it.msg == null }.flatMap { it.msg!!.asSequence() }.mapNotNull {
|
||||
when (it.msgHead.msgType) {
|
||||
33 -> {
|
||||
if (it.msgHead.authUin == bot.uin) {
|
||||
val group = bot.getGroupByUinOrNull(it.msgHead.fromUin)
|
||||
if (group == null) {
|
||||
TODO("查询群信息, 添加群")
|
||||
}
|
||||
}
|
||||
|
||||
TODO("为 group 添加一个 fun Member() 来构造 member")
|
||||
// bot.getGroupByUin(it.msgHead.fromUin).members.delegate.addLast()
|
||||
println("GroupUin" + it.msgHead.fromUin + "新群员" + it.msgHead.authUin + " 出现了[" + it.msgHead.authNick + "] 添加刷新")
|
||||
null
|
||||
}
|
||||
|
@ -20,10 +20,10 @@ import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.message.GroupMessage
|
||||
import net.mamoe.mirai.qqandroid.GroupImpl
|
||||
import net.mamoe.mirai.qqandroid.MemberImpl
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.loadAs
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.message.toMessageChain
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgInfo
|
||||
@ -34,7 +34,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.OnlinePushTrans
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
|
||||
import net.mamoe.mirai.utils.cryptor.contentToString
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.discardExact
|
||||
import net.mamoe.mirai.utils.io.read
|
||||
import net.mamoe.mirai.utils.io.readString
|
||||
@ -96,6 +96,7 @@ internal class OnlinePush {
|
||||
|
||||
internal object PbPushTransMsg : IncomingPacketFactory<Packet>("OnlinePush.PbPushTransMsg", "OnlinePush.RespPush") {
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@ExperimentalUnsignedTypes
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet {
|
||||
val content = this.readProtoBuf(OnlinePushTrans.PbMsgInfo.serializer())
|
||||
@ -109,17 +110,16 @@ internal class OnlinePush {
|
||||
if (var4 != 0 && var4 != 1) {
|
||||
var5 = readUInt().toLong()
|
||||
}
|
||||
if (var5 == 0L && this.remaining == 1L) {//管理员变更
|
||||
val groupUin = content.fromUin
|
||||
|
||||
val member = bot.getGroupByUin(groupUin)[target] as MemberImpl
|
||||
val old = member.permission
|
||||
return if (this.readByte().toInt() == 1) {
|
||||
member.permission = MemberPermission.ADMINISTRATOR
|
||||
MemberPermissionChangeEvent(member, old, MemberPermission.ADMINISTRATOR)
|
||||
val group = bot.getGroupByUin(content.fromUin) as GroupImpl
|
||||
if (var5 == 0L && this.remaining == 1L) {//管理员变更
|
||||
val newPermission = if (this.readByte().toInt() == 1) MemberPermission.ADMINISTRATOR else MemberPermission.MEMBER
|
||||
|
||||
return if (target == bot.uin) {
|
||||
BotGroupPermissionChangeEvent(group, group.botPermission.also { group.botPermission = newPermission }, newPermission)
|
||||
} else {
|
||||
member.permission = MemberPermission.MEMBER
|
||||
MemberPermissionChangeEvent(member, old, MemberPermission.ADMINISTRATOR)
|
||||
val member = group[target] as MemberImpl
|
||||
MemberPermissionChangeEvent(member, member.permission.also { member.permission = newPermission }, newPermission)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,10 +129,12 @@ internal class OnlinePush {
|
||||
val target = readUInt().toLong()
|
||||
val groupUin = content.fromUin
|
||||
|
||||
bot.getGroupByUin(groupUin).let {
|
||||
val member = it[target] as MemberImpl
|
||||
bot.getGroupByUin(groupUin).let { group ->
|
||||
val member = group[target] as MemberImpl
|
||||
this.discardExact(1)
|
||||
return MemberLeaveEvent.Kick(member, it.members[readUInt().toLong()])
|
||||
return MemberLeaveEvent.Kick(member, group.members[readUInt().toLong()].also {
|
||||
group.members.delegate.remove(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -158,6 +160,7 @@ internal class OnlinePush {
|
||||
when {
|
||||
msgInfo.shMsgType.toInt() == 732 -> {
|
||||
val group = bot.getGroup(this.readUInt().toLong())
|
||||
group as GroupImpl
|
||||
|
||||
when (val internalType = this.readShort().toInt()) {
|
||||
3073 -> { // mute
|
||||
@ -169,9 +172,19 @@ internal class OnlinePush {
|
||||
|
||||
return if (target == 0L) {
|
||||
if (time == 0) {
|
||||
GroupMuteAllEvent(origin = true, new = false, operator = operator, group = group)
|
||||
GroupMuteAllEvent(
|
||||
origin = group.muteAll.also { group._muteAll = false },
|
||||
new = false,
|
||||
operator = operator,
|
||||
group = group
|
||||
)
|
||||
} else {
|
||||
GroupMuteAllEvent(origin = false, new = true, operator = operator, group = group)
|
||||
GroupMuteAllEvent(
|
||||
origin = group.muteAll.also { group._muteAll = true },
|
||||
new = true,
|
||||
operator = operator,
|
||||
group = group
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val member = group[target]
|
||||
@ -184,9 +197,10 @@ internal class OnlinePush {
|
||||
}
|
||||
3585 -> { // 匿名
|
||||
val operator = group[this.readUInt().toLong()]
|
||||
val switch = this.readInt() == 0
|
||||
return GroupAllowAnonymousChatEvent(
|
||||
origin = group.anonymousChat,
|
||||
new = this.readInt() == 0,
|
||||
origin = group.anonymousChat.also { group._anonymousChat = switch },
|
||||
new = switch,
|
||||
operator = operator,
|
||||
group = group
|
||||
)
|
||||
@ -196,23 +210,30 @@ internal class OnlinePush {
|
||||
val message = this.readString(this.readByte().toInt())
|
||||
println(dataBytes.toUHexString())
|
||||
|
||||
return if (dataBytes[0].toInt() != 59) {
|
||||
return GroupNameChangeEvent(origin = group.name, new = message, group = group)
|
||||
if (dataBytes[0].toInt() != 59) {
|
||||
return GroupNameChangeEvent(
|
||||
origin = group.name.also { group._name = message },
|
||||
new = message,
|
||||
group = group,
|
||||
isByBot = false
|
||||
)
|
||||
} else {
|
||||
//println(message + ":" + dataBytes.toUHexString())
|
||||
when (message) {
|
||||
"管理员已关闭群聊坦白说" -> {
|
||||
return GroupAllowConfessTalkEvent(
|
||||
origin = group.confessTalk,
|
||||
origin = group.confessTalk.also { group._confessTalk = false },
|
||||
new = false,
|
||||
group = group
|
||||
group = group,
|
||||
isByBot = false
|
||||
)
|
||||
}
|
||||
"管理员已开启群聊坦白说" -> {
|
||||
return GroupAllowConfessTalkEvent(
|
||||
origin = group.confessTalk,
|
||||
new = false,
|
||||
group = group
|
||||
origin = group.confessTalk.also { group._confessTalk = true },
|
||||
new = true,
|
||||
group = group,
|
||||
isByBot = false
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
@ -222,18 +243,19 @@ internal class OnlinePush {
|
||||
}
|
||||
}
|
||||
}
|
||||
4352 -> {
|
||||
println(msgInfo.contentToString())
|
||||
println(msgInfo.vMsg.toUHexString())
|
||||
}
|
||||
// 4352 -> {
|
||||
// println(msgInfo.contentToString())
|
||||
// println(msgInfo.vMsg.toUHexString())
|
||||
// }
|
||||
else -> {
|
||||
println("unknown group internal type $internalType , data: " + this.readBytes().toUHexString() + " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
msgInfo.shMsgType.toInt() == 528 -> {
|
||||
val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer())
|
||||
println(content.contentToString())
|
||||
println("unknown shtype ${msgInfo.shMsgType.toInt()}")
|
||||
// val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer())
|
||||
// println(content.contentToString())
|
||||
}
|
||||
else -> {
|
||||
println("unknown shtype ${msgInfo.shMsgType.toInt()}")
|
||||
|
@ -72,7 +72,7 @@ internal class FriendList {
|
||||
val members: List<StTroopMemberInfo>,
|
||||
val nextUin: Long
|
||||
) : Packet {
|
||||
override fun toString(): String = "Friendlist.GetTroopMemberList.Response"
|
||||
override fun toString(): String = "FriendList.GetTroopMemberList.Response"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -261,6 +261,12 @@ fun String.generateProtoBufDataClass(): GeneratedClass {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var name = _name
|
||||
when {
|
||||
|
||||
name.startsWith("string") -> {
|
||||
name = name.substringAfter("string").takeIf { it.isNotBlank() }?.adjustName() ?: "string"
|
||||
if (defaultValue == "EMPTY_BYTE_ARRAY")
|
||||
defaultValue = "\"\""
|
||||
}
|
||||
name.startsWith("str") -> {
|
||||
name = name.substringAfter("str").takeIf { it.isNotBlank() }?.adjustName() ?: "str"
|
||||
if (defaultValue == "EMPTY_BYTE_ARRAY")
|
||||
|
@ -19,13 +19,13 @@ import kotlinx.io.core.IoBuffer
|
||||
import kotlinx.io.core.use
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
import net.mamoe.mirai.data.FriendInfo
|
||||
import net.mamoe.mirai.data.GroupInfo
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.WeakRef
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.transferTo
|
||||
import net.mamoe.mirai.utils.toList
|
||||
|
||||
/**
|
||||
* 机器人对象. 一个机器人实例登录一个 QQ 账号.
|
||||
@ -65,6 +65,13 @@ abstract class Bot : CoroutineScope {
|
||||
*/
|
||||
abstract val uin: Long
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@MiraiExperimentalAPI("还未支持")
|
||||
val nick: String
|
||||
get() = TODO("bot 昵称获取")
|
||||
|
||||
/**
|
||||
* 日志记录器
|
||||
*/
|
||||
@ -116,7 +123,7 @@ abstract class Bot : CoroutineScope {
|
||||
* [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job.
|
||||
* 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭.
|
||||
*/
|
||||
abstract fun QQ(id: Long): QQ
|
||||
abstract fun QQ(friendInfo: FriendInfo): QQ
|
||||
|
||||
/**
|
||||
* 机器人加入的群列表.
|
||||
@ -131,6 +138,25 @@ abstract class Bot : CoroutineScope {
|
||||
?: throw NoSuchElementException("No such group $id for bot ${this.uin}")
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群列表. 返回值前 32 bits 为 uin, 后 32 bits 为 groupCode
|
||||
*/
|
||||
abstract suspend fun queryGroupList(): Sequence<Long>
|
||||
|
||||
/**
|
||||
* 查询群资料. 获得的仅为当前时刻的资料.
|
||||
* 请优先使用 [getGroup] 然后查看群资料.
|
||||
*/
|
||||
abstract suspend fun queryGroupInfo(id: Long): GroupInfo
|
||||
|
||||
/**
|
||||
* 查询群成员列表.
|
||||
* 请优先使用 [getGroup], [Group.members] 查看群成员.
|
||||
*
|
||||
* 这个函数很慢. 请不要频繁使用.
|
||||
*/
|
||||
abstract suspend fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence<MemberInfo>
|
||||
|
||||
// TODO 目前还不能构造群对象. 这将在以后支持
|
||||
|
||||
// endregion
|
||||
|
@ -12,8 +12,10 @@
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* 群. 在 QQ Android 中叫做 "Troop"
|
||||
@ -117,6 +119,14 @@ interface Group : Contact, CoroutineScope {
|
||||
*/
|
||||
suspend fun quit(): Boolean
|
||||
|
||||
/**
|
||||
* 构造一个 [Member].
|
||||
* 非特殊情况请不要使用这个函数. 优先使用 [get].
|
||||
*/
|
||||
@MiraiExperimentalAPI("dangerous")
|
||||
@Suppress("INAPPLICABLE_JVM_NAME")
|
||||
@JvmName("newMember")
|
||||
fun Member(memberInfo: MemberInfo): Member
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2020 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.data
|
||||
|
||||
interface FriendInfo {
|
||||
val uin: Long
|
||||
|
||||
val nick: String
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package net.mamoe.mirai.data
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
|
||||
/**
|
||||
* 群资料.
|
||||
*
|
||||
* 通过 [Bot.queryGroupInfo] 得到
|
||||
*/
|
||||
interface GroupInfo {
|
||||
/**
|
||||
* Uin
|
||||
*/
|
||||
val uin: Long
|
||||
|
||||
/**
|
||||
* 群号码
|
||||
*/ // 由 uin 计算得到
|
||||
val groupCode: Long
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
val name: String // 不一定能获取到
|
||||
|
||||
/**
|
||||
* 群主
|
||||
*/
|
||||
val owner: Long // 不一定能获取到
|
||||
|
||||
/**
|
||||
* 入群公告
|
||||
*/
|
||||
val memo: String // 不一定能获取到
|
||||
|
||||
/**
|
||||
* 允许群员邀请其他人加入群
|
||||
*/
|
||||
val allowMemberInvite: Boolean
|
||||
|
||||
/**
|
||||
* 允许匿名聊天
|
||||
*/
|
||||
val allowAnonymousChat: Boolean
|
||||
|
||||
/**
|
||||
* 自动审批加群请求
|
||||
*/
|
||||
val autoApprove: Boolean
|
||||
|
||||
/**
|
||||
* 坦白说开启状态
|
||||
*/
|
||||
val confessTalk: Boolean
|
||||
|
||||
/**
|
||||
* 全员禁言
|
||||
*/
|
||||
val muteAll: Boolean
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2020 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.data
|
||||
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
|
||||
interface MemberInfo : FriendInfo {
|
||||
val nameCard: String
|
||||
|
||||
val permission: MemberPermission
|
||||
|
||||
val specialTitle: String
|
||||
}
|
@ -74,6 +74,10 @@ interface Listener<in E : Event> : CompletableJob {
|
||||
* ```kotlin
|
||||
* bot.subscribe<Subscribe> { /* 一些处理 */ }
|
||||
* ```
|
||||
*
|
||||
* @see subscribeMessages 监听消息 DSL
|
||||
* @see subscribeGroupMessages 监听群消息
|
||||
* @see subscribeFriendMessages 监听好友消息
|
||||
*/
|
||||
inline fun <reified E : Event> CoroutineScope.subscribe(crossinline handler: suspend E.(E) -> ListeningStatus): Listener<E> =
|
||||
E::class.subscribeInternal(Handler { it.handler(it) })
|
||||
|
@ -13,6 +13,7 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.AbstractCancellableEvent
|
||||
import net.mamoe.mirai.event.BroadcastControllable
|
||||
import net.mamoe.mirai.event.CancellableEvent
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
@ -120,16 +121,19 @@ data class BotGroupPermissionChangeEvent(
|
||||
override val group: Group,
|
||||
val origin: MemberPermission,
|
||||
val new: MemberPermission
|
||||
) : BotPassiveEvent, GroupEvent
|
||||
) : BotPassiveEvent, GroupEvent, Packet
|
||||
|
||||
// region 群设置
|
||||
|
||||
/**
|
||||
* 群设置改变. 此事件广播前修改就已经完成.
|
||||
*/
|
||||
interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent {
|
||||
interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent, BroadcastControllable {
|
||||
val origin: T
|
||||
val new: T
|
||||
|
||||
override val shouldBroadcast: Boolean
|
||||
get() = origin != new
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,7 +142,8 @@ interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent {
|
||||
data class GroupNameChangeEvent(
|
||||
override val origin: String,
|
||||
override val new: String,
|
||||
override val group: Group
|
||||
override val group: Group,
|
||||
val isByBot: Boolean
|
||||
) : GroupSettingChangeEvent<String>, Packet
|
||||
|
||||
/**
|
||||
@ -187,7 +192,8 @@ data class GroupAllowAnonymousChatEvent(
|
||||
data class GroupAllowConfessTalkEvent(
|
||||
override val origin: Boolean,
|
||||
override val new: Boolean,
|
||||
override val group: Group
|
||||
override val group: Group,
|
||||
val isByBot: Boolean
|
||||
) : GroupSettingChangeEvent<Boolean>, Packet
|
||||
|
||||
/**
|
||||
@ -299,7 +305,7 @@ data class MemberPermissionChangeEvent(
|
||||
// region 禁言
|
||||
|
||||
/**
|
||||
* 群成员被禁言事件. 操作人和被禁言的成员都不可能是机器人本人
|
||||
* 群成员被禁言事件. 被禁言的成员都不可能是机器人本人
|
||||
*/
|
||||
data class MemberMuteEvent(
|
||||
override val member: Member,
|
||||
@ -311,7 +317,7 @@ data class MemberMuteEvent(
|
||||
) : GroupMemberEvent, Packet
|
||||
|
||||
/**
|
||||
* 群成员被取消禁言事件. 操作人和被禁言的成员都不可能是机器人本人
|
||||
* 群成员被取消禁言事件. 被禁言的成员都不可能是机器人本人
|
||||
*/
|
||||
data class MemberUnmuteEvent(
|
||||
override val member: Member,
|
||||
|
@ -12,7 +12,6 @@
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.groupCardOrNick
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
|
||||
|
||||
@ -21,7 +20,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
*/
|
||||
class At @MiraiInternalAPI constructor(val target: Long, val display: String) : Message {
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
constructor(member: Member) : this(member.id, "@${member.groupCardOrNick}")
|
||||
constructor(member: Member) : this(member.id, "@${member.nick}")
|
||||
|
||||
override fun toString(): String = display
|
||||
|
||||
|
@ -68,6 +68,20 @@ fun <E> LockFreeLinkedList<E>.asSequence(): Sequence<E> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建链表结构然后转为 [LockFreeLinkedList]
|
||||
*/
|
||||
fun <E> Iterable<E>.toLockFreeLinkedList(): LockFreeLinkedList<E> {
|
||||
return LockFreeLinkedList<E>().apply { addAll(this@toLockFreeLinkedList) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建链表结构然后转为 [LockFreeLinkedList]
|
||||
*/
|
||||
fun <E> Sequence<E>.toLockFreeLinkedList(): LockFreeLinkedList<E> {
|
||||
return LockFreeLinkedList<E>().apply { addAll(this@toLockFreeLinkedList) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of lock-free LinkedList.
|
||||
*
|
||||
@ -111,8 +125,10 @@ open class LockFreeLinkedList<E> {
|
||||
}
|
||||
|
||||
open fun addLast(element: E) {
|
||||
val node = element.asNode(tail)
|
||||
addLastNode(element.asNode(tail))
|
||||
}
|
||||
|
||||
private fun addLastNode(node: Node<E>) {
|
||||
while (true) {
|
||||
val tail = head.iterateBeforeFirst { it === tail } // find the last node.
|
||||
if (tail.nextNodeRef.compareAndSet(this.tail, node)) { // ensure the last node is the last node
|
||||
@ -121,6 +137,46 @@ open class LockFreeLinkedList<E> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 先把元素建立好链表, 再加入到 list.
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
open fun addAll(iterable: Iterable<E>) {
|
||||
var firstNode: Node<E>? = null
|
||||
|
||||
var currentNode: Node<E>? = null
|
||||
iterable.forEach {
|
||||
val nextNode = it.asNode(tail)
|
||||
if (firstNode == null) {
|
||||
firstNode = nextNode
|
||||
}
|
||||
currentNode?.nextNode = nextNode
|
||||
currentNode = nextNode
|
||||
}
|
||||
|
||||
firstNode?.let { addLastNode(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 先把元素建立好链表, 再加入到 list.
|
||||
*/
|
||||
@Suppress("DuplicatedCode")
|
||||
open fun addAll(iterable: Sequence<E>) {
|
||||
var firstNode: Node<E>? = null
|
||||
|
||||
var currentNode: Node<E>? = null
|
||||
iterable.forEach {
|
||||
val nextNode = it.asNode(tail)
|
||||
if (firstNode == null) {
|
||||
firstNode = nextNode
|
||||
}
|
||||
currentNode?.nextNode = nextNode
|
||||
currentNode = nextNode
|
||||
}
|
||||
|
||||
firstNode?.let { addLastNode(it) }
|
||||
}
|
||||
|
||||
open operator fun plusAssign(element: E) = this.addLast(element)
|
||||
|
||||
/**
|
||||
@ -243,8 +299,6 @@ open class LockFreeLinkedList<E> {
|
||||
}
|
||||
}
|
||||
|
||||
open fun addAll(elements: Collection<E>) = elements.forEach { addLast(it) }
|
||||
|
||||
@Suppress("unused")
|
||||
open fun clear() {
|
||||
val first = head.nextNode
|
||||
|
@ -17,7 +17,7 @@ dependencies {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-io:$coroutinesIoVersion")
|
||||
implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.62'
|
||||
implementation 'org.jsoup:jsoup:1.12.1'
|
||||
api 'org.jsoup:jsoup:1.12.1'
|
||||
}
|
||||
|
||||
run{
|
||||
|
Loading…
Reference in New Issue
Block a user