Commit 240183d5 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

parents df114f69 3b0ee9ad
...@@ -11,9 +11,7 @@ package net.mamoe.mirai.qqandroid ...@@ -11,9 +11,7 @@ package net.mamoe.mirai.qqandroid
import kotlinx.coroutines.launch 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.*
import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.data.Profile
import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.* import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
...@@ -24,6 +22,7 @@ import net.mamoe.mirai.message.data.MessageChain ...@@ -24,6 +22,7 @@ import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.NotOnlineImageFromFile import net.mamoe.mirai.message.data.NotOnlineImageFromFile
import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
import net.mamoe.mirai.qqandroid.network.highway.postImage 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.data.proto.Cmd0x352
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
...@@ -33,6 +32,7 @@ import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString ...@@ -33,6 +32,7 @@ import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo
internal abstract class ContactImpl : Contact { internal abstract class ContactImpl : Contact {
override fun hashCode(): Int { override fun hashCode(): Int {
...@@ -49,10 +49,22 @@ internal abstract class ContactImpl : Contact { ...@@ -49,10 +49,22 @@ internal abstract class ContactImpl : Contact {
} }
} }
internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: CoroutineContext, override val id: Long) : ContactImpl(), QQ { internal inline class FriendInfoImpl(
override val bot: QQAndroidBot by bot.unsafeWeakRef() 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) { override suspend fun sendMessage(message: MessageChain) {
val event = FriendMessageSendEvent(this, message).broadcast() val event = FriendMessageSendEvent(this, message).broadcast()
...@@ -135,14 +147,17 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin ...@@ -135,14 +147,17 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
image.input.close() image.input.close()
} }
@MiraiExperimentalAPI
override suspend fun queryProfile(): Profile { override suspend fun queryProfile(): Profile {
TODO("not implemented") TODO("not implemented")
} }
@MiraiExperimentalAPI
override suspend fun queryPreviousNameList(): PreviousNameList { override suspend fun queryPreviousNameList(): PreviousNameList {
TODO("not implemented") TODO("not implemented")
} }
@MiraiExperimentalAPI
override suspend fun queryRemark(): FriendNameRemark { override suspend fun queryRemark(): FriendNameRemark {
TODO("not implemented") TODO("not implemented")
} }
...@@ -156,24 +171,27 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin ...@@ -156,24 +171,27 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
} }
@Suppress("MemberVisibilityCanBePrivate")
internal class MemberImpl( internal class MemberImpl(
qq: QQImpl, qq: QQImpl,
var _groupCard: String,
var _specialTitle: String,
group: GroupImpl, group: GroupImpl,
override val coroutineContext: CoroutineContext, override val coroutineContext: CoroutineContext,
override var permission: MemberPermission memberInfo: MemberInfo
) : ContactImpl(), Member, QQ by qq { ) : ContactImpl(), Member, QQ by qq {
override val group: GroupImpl by group.unsafeWeakRef() override val group: GroupImpl by group.unsafeWeakRef()
val qq: QQImpl by qq.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 override var nameCard: String
get() = _groupCard get() = _nameCard
set(newValue) { set(newValue) {
group.checkBotPermissionOperator() group.checkBotPermissionOperator()
if (_groupCard != newValue) { if (_nameCard != newValue) {
val oldValue = _groupCard val oldValue = _nameCard
_groupCard = newValue _nameCard = newValue
launch { launch {
bot.network.run { bot.network.run {
TroopManagement.EditGroupNametag( TroopManagement.EditGroupNametag(
...@@ -223,7 +241,8 @@ internal class MemberImpl( ...@@ -223,7 +241,8 @@ internal class MemberImpl(
).sendAndExpect<TroopManagement.Mute.Response>() ).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 return true
} }
...@@ -241,7 +260,8 @@ internal class MemberImpl( ...@@ -241,7 +260,8 @@ internal class MemberImpl(
).sendAndExpect<TroopManagement.Mute.Response>() ).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 return true
} }
...@@ -269,25 +289,60 @@ internal class MemberImpl( ...@@ -269,25 +289,60 @@ internal class MemberImpl(
override fun hashCode(): Int = super.hashCode() 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 * 对GroupImpl
* 中name/announcement的更改会直接向服务器异步汇报 * 中name/announcement的更改会直接向服务器异步汇报
*/ */
@Suppress("PropertyName")
@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, groupInfo: GroupInfo,
var _name: String, members: Sequence<MemberInfo>
var _announcement: String,
var _allowMemberInvite: Boolean,
var _confessTalk: Boolean,
var _muteAll: Boolean,
var _autoApprove: Boolean,
var _anonymousChat: Boolean,
override val members: ContactList<Member>
) : ContactImpl(), Group { ) : 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 override var name: String
get() = _name get() = _name
...@@ -304,7 +359,7 @@ internal class GroupImpl( ...@@ -304,7 +359,7 @@ internal class GroupImpl(
newName = newValue newName = newValue
).sendWithoutExpect() ).sendWithoutExpect()
} }
GroupNameChangeEvent(oldValue, newValue, this@GroupImpl, null).broadcast() GroupNameChangeEvent(oldValue, newValue, this@GroupImpl, true).broadcast()
} }
} }
} }
...@@ -377,7 +432,7 @@ internal class GroupImpl( ...@@ -377,7 +432,7 @@ internal class GroupImpl(
switch = newValue switch = newValue
).sendWithoutExpect() ).sendWithoutExpect()
} }
GroupAllowConfessTalkEvent(oldValue, newValue, this@GroupImpl, null).broadcast() GroupAllowConfessTalkEvent(oldValue, newValue, this@GroupImpl, true).broadcast()
} }
} }
} }
...@@ -403,16 +458,21 @@ internal class GroupImpl( ...@@ -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 { override suspend fun quit(): Boolean {
check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" } check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
TODO("not implemented") 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 { override operator fun get(id: Long): Member {
return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("member $id not found in group $uin") return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("member $id not found in group $uin")
...@@ -426,8 +486,6 @@ internal class GroupImpl( ...@@ -426,8 +486,6 @@ internal class GroupImpl(
return members.delegate.filteringGetOrNull { it.id == id } return members.delegate.filteringGetOrNull { it.id == id }
} }
override val bot: QQAndroidBot by bot.unsafeWeakRef()
override suspend fun sendMessage(message: MessageChain) { override suspend fun sendMessage(message: MessageChain) {
val event = GroupMessageSendEvent(this, message).broadcast() val event = GroupMessageSendEvent(this, message).broadcast()
if (event.isCancelled) { if (event.isCancelled) {
......
...@@ -17,11 +17,18 @@ import net.mamoe.mirai.contact.Group ...@@ -17,11 +17,18 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.filteringGetOrNull import net.mamoe.mirai.contact.filteringGetOrNull
import net.mamoe.mirai.data.AddFriendResult 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.event.events.BotEvent
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.qqandroid.network.QQAndroidClient 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 net.mamoe.mirai.utils.*
import kotlin.collections.asSequence
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
...@@ -31,22 +38,32 @@ internal expect class QQAndroidBot constructor( ...@@ -31,22 +38,32 @@ internal expect class QQAndroidBot constructor(
configuration: BotConfiguration configuration: BotConfiguration
) : QQAndroidBotBase ) : QQAndroidBotBase
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal abstract class QQAndroidBotBase constructor( internal abstract class QQAndroidBotBase constructor(
context: Context, context: Context,
account: BotAccount, account: BotAccount,
configuration: BotConfiguration configuration: BotConfiguration
) : BotImpl<QQAndroidBotNetworkHandler>(account, configuration) { ) : BotImpl<QQAndroidBotNetworkHandler>(account, configuration) {
val client: QQAndroidClient = 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 internal var firstLoginSucceed: Boolean = false
override val uin: Long get() = client.uin override val uin: Long get() = client.uin
override val qqs: ContactList<QQ> = ContactList(LockFreeLinkedList()) 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 { override fun QQ(friendInfo: FriendInfo): QQ {
return QQImpl(this as QQAndroidBot, coroutineContext, id) return QQImpl(this as QQAndroidBot, coroutineContext, friendInfo.uin, friendInfo)
} }
override fun createNetworkHandler(coroutineContext: CoroutineContext): QQAndroidBotNetworkHandler { override fun createNetworkHandler(coroutineContext: CoroutineContext): QQAndroidBotNetworkHandler {
...@@ -60,6 +77,45 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -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}") 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 { override fun onEvent(event: BotEvent): Boolean {
return firstLoginSucceed return firstLoginSucceed
} }
......
...@@ -18,22 +18,19 @@ import kotlinx.io.core.ByteReadPacket ...@@ -18,22 +18,19 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.core.use 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.MultiPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.* import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.qqandroid.FriendInfoImpl
import net.mamoe.mirai.qqandroid.GroupImpl import net.mamoe.mirai.qqandroid.GroupImpl
import net.mamoe.mirai.qqandroid.MemberImpl
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.QQImpl import net.mamoe.mirai.qqandroid.QQImpl
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.GroupInfoImpl
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.Heartbeat import net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat
...@@ -148,7 +145,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -148,7 +145,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
totalFriendCount = data.totalFriendCount totalFriendCount = data.totalFriendCount
data.friendList.forEach { data.friendList.forEach {
// atomic add // 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++ currentFriendCount++
} }
} }
...@@ -171,32 +168,33 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -171,32 +168,33 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2) .sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
troopListData.groups.forEach { troopNum -> 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 { launch {
try { 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) { } catch (e: Exception) {
bot.logger.error("群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试") bot.logger.error("群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试")
bot.logger.error(e) bot.logger.error(e)
...@@ -242,47 +240,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -242,47 +240,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
return lastException 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]. 超时后将清空缓存, 以免阻碍后续包的处理 * 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理
*/ */
...@@ -360,7 +317,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -360,7 +317,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (packet is CancellableEvent && packet.isCancelled) return 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 { packetFactory?.run {
when (this) { when (this) {
......
...@@ -180,32 +180,32 @@ class Oidb0x88d : ProtoBuf { ...@@ -180,32 +180,32 @@ class Oidb0x88d : ProtoBuf {
@SerialId(12) val groupDefaultPage: Int? = null, @SerialId(12) val groupDefaultPage: Int? = null,
@SerialId(13) val groupInfoSeq: Int? = null, @SerialId(13) val groupInfoSeq: Int? = null,
@SerialId(14) val groupRoamingTime: Int? = null, @SerialId(14) val groupRoamingTime: Int? = null,
@SerialId(15) val ingGroupName: ByteArray? = null, @SerialId(15) var groupName: String? = null,
@SerialId(16) val ingGroupMemo: ByteArray? = null, @SerialId(16) var groupMemo: String? = null,
@SerialId(17) val ingGroupFingerMemo: ByteArray? = null, @SerialId(17) val ingGroupFingerMemo: String? = null,
@SerialId(18) val ingGroupClassText: ByteArray? = null, @SerialId(18) val ingGroupClassText: String? = null,
@SerialId(19) val groupAllianceCode: List<Int>? = null, @SerialId(19) val groupAllianceCode: List<Int>? = null,
@SerialId(20) val groupExtraAdmNum: 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(22) val groupCurMsgSeq: Int? = null,
@SerialId(23) val groupLastMsgTime: Int? = null, @SerialId(23) val groupLastMsgTime: Int? = null,
@SerialId(24) val ingGroupQuestion: ByteArray? = null, @SerialId(24) val ingGroupQuestion: String? = null,
@SerialId(25) val ingGroupAnswer: ByteArray? = null, @SerialId(25) val ingGroupAnswer: String? = null,
@SerialId(26) val groupVisitorMaxNum: Int? = null, @SerialId(26) val groupVisitorMaxNum: Int? = null,
@SerialId(27) val groupVisitorCurNum: Int? = null, @SerialId(27) val groupVisitorCurNum: Int? = null,
@SerialId(28) val levelNameSeq: Int? = null, @SerialId(28) val levelNameSeq: Int? = null,
@SerialId(29) val groupAdminMaxNum: Int? = null, @SerialId(29) val groupAdminMaxNum: Int? = null,
@SerialId(30) val groupAioSkinTimestamp: Int? = null, @SerialId(30) val groupAioSkinTimestamp: Int? = null,
@SerialId(31) val groupBoardSkinTimestamp: Int? = null, @SerialId(31) val groupBoardSkinTimestamp: Int? = null,
@SerialId(32) val ingGroupAioSkinUrl: ByteArray? = null, @SerialId(32) val ingGroupAioSkinUrl: String? = null,
@SerialId(33) val ingGroupBoardSkinUrl: ByteArray? = null, @SerialId(33) val ingGroupBoardSkinUrl: String? = null,
@SerialId(34) val groupCoverSkinTimestamp: Int? = 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(36) val groupGrade: Int? = null,
@SerialId(37) val activeMemberNum: Int? = null, @SerialId(37) val activeMemberNum: Int? = null,
@SerialId(38) val certificationType: Int? = null, @SerialId(38) val certificationType: Int? = null,
@SerialId(39) val ingCertificationText: ByteArray? = null, @SerialId(39) val ingCertificationText: String? = null,
@SerialId(40) val ingGroupRichFingerMemo: ByteArray? = null, @SerialId(40) val ingGroupRichFingerMemo: String? = null,
@SerialId(41) val tagRecord: List<TagRecord>? = null, @SerialId(41) val tagRecord: List<TagRecord>? = null,
@SerialId(42) val groupGeoInfo: GroupGeoInfo? = null, @SerialId(42) val groupGeoInfo: GroupGeoInfo? = null,
@SerialId(43) val headPortraitSeq: Int? = null, @SerialId(43) val headPortraitSeq: Int? = null,
...@@ -254,7 +254,7 @@ class Oidb0x88d : ProtoBuf { ...@@ -254,7 +254,7 @@ class Oidb0x88d : ProtoBuf {
@SerialId(86) val isAllowConfGroupMemberNick: Int? = null, @SerialId(86) val isAllowConfGroupMemberNick: Int? = null,
@SerialId(87) val isAllowConfGroupMemberAtAll: Int? = null, @SerialId(87) val isAllowConfGroupMemberAtAll: Int? = null,
@SerialId(88) val isAllowConfGroupMemberModifyGroupName: 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(90) val cmduinJoinRealMsgSeq: Int? = null,
@SerialId(91) val isGroupFreeze: Int? = null, @SerialId(91) val isGroupFreeze: Int? = null,
@SerialId(92) val msgLimitFrequency: Int? = null, @SerialId(92) val msgLimitFrequency: Int? = null,
...@@ -265,7 +265,8 @@ class Oidb0x88d : ProtoBuf { ...@@ -265,7 +265,8 @@ class Oidb0x88d : ProtoBuf {
@SerialId(97) val isAllowHlGuildBinary: Int? = null, @SerialId(97) val isAllowHlGuildBinary: Int? = null,
@SerialId(98) val cmduinRingtoneId: Int? = null, @SerialId(98) val cmduinRingtoneId: Int? = null,
@SerialId(99) val groupFlagext4: 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 ) : ProtoBuf
@Serializable @Serializable
......
...@@ -135,7 +135,7 @@ internal object KnownPacketFactories { ...@@ -135,7 +135,7 @@ internal object KnownPacketFactories {
TroopManagement.EditSpecialTitle, TroopManagement.EditSpecialTitle,
TroopManagement.Mute, TroopManagement.Mute,
TroopManagement.GroupOperation, TroopManagement.GroupOperation,
TroopManagement.GetGroupOperationInfo, TroopManagement.GetGroupInfo,
TroopManagement.EditGroupNametag, TroopManagement.EditGroupNametag,
TroopManagement.Kick, TroopManagement.Kick,
Heartbeat.Alive Heartbeat.Alive
......
...@@ -14,6 +14,7 @@ import kotlinx.io.core.buildPacket ...@@ -14,6 +14,7 @@ import kotlinx.io.core.buildPacket
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import kotlinx.io.core.toByteArray import kotlinx.io.core.toByteArray
import kotlinx.serialization.toUtf8Bytes import kotlinx.serialization.toUtf8Bytes
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
...@@ -23,12 +24,27 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.jce.ModifyGroupCardReq ...@@ -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.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.stUinInfo 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.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.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory 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.utils.daysToSeconds import net.mamoe.mirai.utils.daysToSeconds
import net.mamoe.mirai.utils.io.encodeToString 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 { internal class TroopManagement {
...@@ -70,16 +86,7 @@ internal class TroopManagement { ...@@ -70,16 +86,7 @@ internal class TroopManagement {
} }
internal object GetGroupOperationInfo : OutgoingPacketFactory<GetGroupOperationInfo.Response>("OidbSvc.0x88d_7") { internal object GetGroupInfo : OutgoingPacketFactory<GroupInfoImpl>("OidbSvc.0x88d_7") {
class Response(
val allowAnonymousChat: Boolean,
val allowMemberInvite: Boolean,
val autoApprove: Boolean,
val confessTalk: Boolean
) : Packet {
override fun toString(): String = "Response(GroupInfo)"
}
operator fun invoke( operator fun invoke(
client: QQAndroidClient, client: QQAndroidClient,
groupCode: Long groupCode: Long
...@@ -109,8 +116,13 @@ internal class TroopManagement { ...@@ -109,8 +116,13 @@ internal class TroopManagement {
cmduinUinFlag = 0, cmduinUinFlag = 0,
createSourceFlag = 0, createSourceFlag = 0,
noCodeFingerOpenFlag = 0, noCodeFingerOpenFlag = 0,
ingGroupQuestion = EMPTY_BYTE_ARRAY, ingGroupQuestion = "",
ingGroupAnswer = EMPTY_BYTE_ARRAY ingGroupAnswer = "",
groupName = "",
longGroupName = "",
groupMemo = "",
groupUin = 0,
groupOwner = 0
), ),
groupCode = groupCode groupCode = groupCode
) )
...@@ -121,14 +133,9 @@ internal class TroopManagement { ...@@ -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!!) { with(this.readBytes().loadAs(OidbSso.OIDBSSOPkg.serializer()).bodybuffer.loadAs(Oidb0x88d.RspBody.serializer()).stzrspgroupinfo!![0].stgroupinfo!!) {
return Response( return GroupInfoImpl(this)
allowMemberInvite = (this.groupFlagExt?.and(0x000000c0) != 0),
allowAnonymousChat = (this.groupFlagExt?.and(0x40000000) == 0),
autoApprove = (this.groupFlagext3?.and(0x00100000) == 0),
confessTalk = (this.groupFlagext3?.and(0x00002000) == 0)
)
} }
} }
} }
......
...@@ -133,6 +133,15 @@ internal class MessageSvc { ...@@ -133,6 +133,15 @@ internal class MessageSvc {
val messages = resp.uinPairMsgs.asSequence().filterNot { it.msg == null }.flatMap { it.msg!!.asSequence() }.mapNotNull { val messages = resp.uinPairMsgs.asSequence().filterNot { it.msg == null }.flatMap { it.msg!!.asSequence() }.mapNotNull {
when (it.msgHead.msgType) { when (it.msgHead.msgType) {
33 -> { 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 + "] 添加刷新") println("GroupUin" + it.msgHead.fromUin + "新群员" + it.msgHead.authUin + " 出现了[" + it.msgHead.authNick + "] 添加刷新")
null null
} }
......
...@@ -72,7 +72,7 @@ internal class FriendList { ...@@ -72,7 +72,7 @@ internal class FriendList {
val members: List<StTroopMemberInfo>, val members: List<StTroopMemberInfo>,
val nextUin: Long val nextUin: Long
) : Packet { ) : Packet {
override fun toString(): String = "Friendlist.GetTroopMemberList.Response" override fun toString(): String = "FriendList.GetTroopMemberList.Response"
} }
} }
......
...@@ -261,6 +261,12 @@ fun String.generateProtoBufDataClass(): GeneratedClass { ...@@ -261,6 +261,12 @@ fun String.generateProtoBufDataClass(): GeneratedClass {
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
var name = _name var name = _name
when { when {
name.startsWith("string") -> {
name = name.substringAfter("string").takeIf { it.isNotBlank() }?.adjustName() ?: "string"
if (defaultValue == "EMPTY_BYTE_ARRAY")
defaultValue = "\"\""
}
name.startsWith("str") -> { name.startsWith("str") -> {
name = name.substringAfter("str").takeIf { it.isNotBlank() }?.adjustName() ?: "str" name = name.substringAfter("str").takeIf { it.isNotBlank() }?.adjustName() ?: "str"
if (defaultValue == "EMPTY_BYTE_ARRAY") if (defaultValue == "EMPTY_BYTE_ARRAY")
......
...@@ -19,13 +19,13 @@ import kotlinx.io.core.IoBuffer ...@@ -19,13 +19,13 @@ import kotlinx.io.core.IoBuffer
import kotlinx.io.core.use import kotlinx.io.core.use
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult 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.message.data.Image
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.WeakRef
import net.mamoe.mirai.utils.io.transferTo import net.mamoe.mirai.utils.io.transferTo
import net.mamoe.mirai.utils.toList
/** /**
* 机器人对象. 一个机器人实例登录一个 QQ 账号. * 机器人对象. 一个机器人实例登录一个 QQ 账号.
...@@ -65,6 +65,13 @@ abstract class Bot : CoroutineScope { ...@@ -65,6 +65,13 @@ abstract class Bot : CoroutineScope {
*/ */
abstract val uin: Long abstract val uin: Long
/**
* 昵称
*/
@MiraiExperimentalAPI("还未支持")
val nick: String
get() = TODO("bot 昵称获取")
/** /**
* 日志记录器 * 日志记录器
*/ */
...@@ -116,7 +123,7 @@ abstract class Bot : CoroutineScope { ...@@ -116,7 +123,7 @@ abstract class Bot : CoroutineScope {
* [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job. * [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job.
* 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭. * 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭.
*/ */
abstract fun QQ(id: Long): QQ abstract fun QQ(friendInfo: FriendInfo): QQ
/** /**
* 机器人加入的群列表. * 机器人加入的群列表.
...@@ -131,6 +138,25 @@ abstract class Bot : CoroutineScope { ...@@ -131,6 +138,25 @@ abstract class Bot : CoroutineScope {
?: throw NoSuchElementException("No such group $id for bot ${this.uin}") ?: 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 目前还不能构造群对象. 这将在以后支持 // TODO 目前还不能构造群对象. 这将在以后支持
// endregion // endregion
......
...@@ -12,8 +12,10 @@ ...@@ -12,8 +12,10 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.events.* import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.jvm.JvmName
/** /**
* 群. 在 QQ Android 中叫做 "Troop" * 群. 在 QQ Android 中叫做 "Troop"
...@@ -117,6 +119,14 @@ interface Group : Contact, CoroutineScope { ...@@ -117,6 +119,14 @@ interface Group : Contact, CoroutineScope {
*/ */
suspend fun quit(): Boolean suspend fun quit(): Boolean
/**
* 构造一个 [Member].
* 非特殊情况请不要使用这个函数. 优先使用 [get].
*/
@MiraiExperimentalAPI("dangerous")
@Suppress("INAPPLICABLE_JVM_NAME")
@JvmName("newMember")
fun Member(memberInfo: MemberInfo): Member
companion object { companion object {
......
/*
* 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
}
\ No newline at end of file
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
}
\ No newline at end of file
/*
* 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
}
\ No newline at end of file
...@@ -74,6 +74,10 @@ interface Listener<in E : Event> : CompletableJob { ...@@ -74,6 +74,10 @@ interface Listener<in E : Event> : CompletableJob {
* ```kotlin * ```kotlin
* bot.subscribe<Subscribe> { /* 一些处理 */ } * 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> = inline fun <reified E : Event> CoroutineScope.subscribe(crossinline handler: suspend E.(E) -> ListeningStatus): Listener<E> =
E::class.subscribeInternal(Handler { it.handler(it) }) E::class.subscribeInternal(Handler { it.handler(it) })
......
...@@ -13,6 +13,7 @@ import net.mamoe.mirai.Bot ...@@ -13,6 +13,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.AbstractCancellableEvent import net.mamoe.mirai.event.AbstractCancellableEvent
import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.event.CancellableEvent import net.mamoe.mirai.event.CancellableEvent
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
...@@ -120,16 +121,19 @@ data class BotGroupPermissionChangeEvent( ...@@ -120,16 +121,19 @@ data class BotGroupPermissionChangeEvent(
override val group: Group, override val group: Group,
val origin: MemberPermission, val origin: MemberPermission,
val new: MemberPermission val new: MemberPermission
) : BotPassiveEvent, GroupEvent ) : BotPassiveEvent, GroupEvent, Packet
// region 群设置 // region 群设置
/** /**
* 群设置改变. 此事件广播前修改就已经完成. * 群设置改变. 此事件广播前修改就已经完成.
*/ */
interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent { interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent, BroadcastControllable {
val origin: T val origin: T
val new: T val new: T
override val shouldBroadcast: Boolean
get() = origin != new
} }
/** /**
...@@ -138,7 +142,8 @@ interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent { ...@@ -138,7 +142,8 @@ interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent {
data class GroupNameChangeEvent( data class GroupNameChangeEvent(
override val origin: String, override val origin: String,
override val new: String, override val new: String,
override val group: Group override val group: Group,
val isByBot: Boolean
) : GroupSettingChangeEvent<String>, Packet ) : GroupSettingChangeEvent<String>, Packet
/** /**
...@@ -187,7 +192,8 @@ data class GroupAllowAnonymousChatEvent( ...@@ -187,7 +192,8 @@ data class GroupAllowAnonymousChatEvent(
data class GroupAllowConfessTalkEvent( data class GroupAllowConfessTalkEvent(
override val origin: Boolean, override val origin: Boolean,
override val new: Boolean, override val new: Boolean,
override val group: Group override val group: Group,
val isByBot: Boolean
) : GroupSettingChangeEvent<Boolean>, Packet ) : GroupSettingChangeEvent<Boolean>, Packet
/** /**
...@@ -299,7 +305,7 @@ data class MemberPermissionChangeEvent( ...@@ -299,7 +305,7 @@ data class MemberPermissionChangeEvent(
// region 禁言 // region 禁言
/** /**
* 群成员被禁言事件. 操作人和被禁言的成员都不可能是机器人本人 * 群成员被禁言事件. 被禁言的成员都不可能是机器人本人
*/ */
data class MemberMuteEvent( data class MemberMuteEvent(
override val member: Member, override val member: Member,
...@@ -311,7 +317,7 @@ data class MemberMuteEvent( ...@@ -311,7 +317,7 @@ data class MemberMuteEvent(
) : GroupMemberEvent, Packet ) : GroupMemberEvent, Packet
/** /**
* 群成员被取消禁言事件. 操作人和被禁言的成员都不可能是机器人本人 * 群成员被取消禁言事件. 被禁言的成员都不可能是机器人本人
*/ */
data class MemberUnmuteEvent( data class MemberUnmuteEvent(
override val member: Member, override val member: Member,
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
package net.mamoe.mirai.message.data package net.mamoe.mirai.message.data
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.groupCardOrNick
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
...@@ -21,7 +20,7 @@ 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 { class At @MiraiInternalAPI constructor(val target: Long, val display: String) : Message {
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
constructor(member: Member) : this(member.id, "@${member.groupCardOrNick}") constructor(member: Member) : this(member.id, "@${member.nick}")
override fun toString(): String = display override fun toString(): String = display
......
...@@ -68,6 +68,20 @@ fun <E> LockFreeLinkedList<E>.asSequence(): Sequence<E> { ...@@ -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. * Implementation of lock-free LinkedList.
* *
...@@ -111,8 +125,10 @@ open class LockFreeLinkedList<E> { ...@@ -111,8 +125,10 @@ open class LockFreeLinkedList<E> {
} }
open fun addLast(element: E) { open fun addLast(element: E) {
val node = element.asNode(tail) addLastNode(element.asNode(tail))
}
private fun addLastNode(node: Node<E>) {
while (true) { while (true) {
val tail = head.iterateBeforeFirst { it === tail } // find the last node. 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 if (tail.nextNodeRef.compareAndSet(this.tail, node)) { // ensure the last node is the last node
...@@ -121,6 +137,46 @@ open class LockFreeLinkedList<E> { ...@@ -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) open operator fun plusAssign(element: E) = this.addLast(element)
/** /**
...@@ -243,8 +299,6 @@ open class LockFreeLinkedList<E> { ...@@ -243,8 +299,6 @@ open class LockFreeLinkedList<E> {
} }
} }
open fun addAll(elements: Collection<E>) = elements.forEach { addLast(it) }
@Suppress("unused") @Suppress("unused")
open fun clear() { open fun clear() {
val first = head.nextNode val first = head.nextNode
......
...@@ -17,7 +17,7 @@ dependencies { ...@@ -17,7 +17,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-io:$coroutinesIoVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-io:$coroutinesIoVersion")
implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.62' 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{ run{
......
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