Commit cb320dd9 authored by Him188's avatar Him188

Inline messages, improved contacts

parent 1f9b003c
......@@ -176,7 +176,7 @@ object MiraiServer {
val bot = Bot(BotAccount(strings[0].toUInt(), strings[1]), MiraiLogger)
if (runBlocking { bot.login() } === LoginResult.SUCCESS) {
bot.logGreen("Login succeed")
bot.logger.logGreen("Login succeed")
return bot
}
}
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
package net.mamoe.mirai
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.Bot.ContactSystem
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.groupIdToNumber
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.utils.BotNetworkConfiguration
import net.mamoe.mirai.utils.ContactList
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.log
import kotlin.jvm.JvmOverloads
data class BotAccount(
val account: UInt,
val password: String//todo 不保存 password?
val id: UInt,
val password: String//todo 不保存 password?
)
/**
......@@ -51,8 +48,6 @@ data class BotAccount(
* @see net.mamoe.mirai.contact.Contact
*/
class Bot(val account: BotAccount, val logger: MiraiLogger) {
val id = nextId()
val contacts = ContactSystem()
var network: BotNetworkHandler<*> = TIMBotNetworkHandler(this)
......@@ -60,17 +55,20 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
init {
instances.add(this)
this.logger.identity = "Bot" + this.id + "(" + this.account.account + ")"
this.logger.identity = "Bot(" + this.account.id + ")"
}
override fun toString(): String = "Bot{id=$id,qq=${account.account}}"
override fun toString(): String = "Bot{qq=${account.id}}"
/**
* [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler.NetworkScope] 下的协程.
* [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程.
* 然后重新启动并尝试登录
*/
@JvmOverloads
suspend fun reinitializeNetworkHandler(configuration: BotNetworkConfiguration, cause: Throwable? = null): LoginResult {
suspend fun reinitializeNetworkHandler(
configuration: BotNetworkConfiguration,
cause: Throwable? = null
): LoginResult {
logger.logPurple("Reinitializing BotNetworkHandler")
try {
network.close(cause)
......@@ -93,23 +91,43 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
private val qqsLock = Mutex()
/**
* 通过群号码获取群对象.
* 注意: 在并发调用时, 这个方法并不是原子的.
* 获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个.
*
* 注: 这个方法是线程安全的
*/
fun getQQ(account: UInt): QQ = qqs.getOrPut(account) { QQ(this@Bot, account) }
suspend fun getQQ(account: UInt): QQ =
if (qqs.containsKey(account)) qqs[account]!!
else qqsLock.withLock {
qqs.getOrPut(account) { QQ(this@Bot, account) }
}
/**
* 通过群号码获取群对象.
* 注意: 在并发调用时, 这个方法并不是原子的.
* 获取缓存的群对象. 若没有对应的缓存, 则会创建一个.
*
* 注: 这个方法是线程安全的
*/
fun getGroupByNumber(groupNumber: UInt): Group = groups.getOrPut(groupNumber) { Group(this@Bot, groupNumber) }
suspend fun getGroup(internalId: GroupInternalId): Group = getGroup(internalId.toId())
fun getGroupById(groupId: UInt): Group {
return getGroupByNumber(Group.groupIdToNumber(groupId))
/**
* 获取缓存的群对象. 若没有对应的缓存, 则会创建一个.
*
* 注: 这个方法是线程安全的
*/
suspend fun getGroup(id: GroupId): Group = id.value.let {
if (groups.containsKey(it)) groups[it]!!
else groupsLock.withLock {
groups.getOrPut(it) { Group(this@Bot, it) }
}
}
}
suspend fun UInt.qq(): QQ = getQQ(this)
suspend fun Long.qq(): QQ = getQQ(this)
suspend fun UInt.group(): Group = getGroup(GroupId(this))
suspend fun GroupId.group(): Group = getGroup(this)
suspend fun GroupInternalId.group(): Group = getGroup(this)
suspend fun close() {
this.network.close()
this.contacts.groups.clear()
......@@ -118,8 +136,5 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
companion object {
val instances: MutableList<Bot> = mutableListOf()
private val id = atomic(0)
fun nextId(): Int = id.addAndGet(1)
}
}
\ No newline at end of file
......@@ -2,32 +2,24 @@
package net.mamoe.mirai
import kotlinx.io.core.readBytes
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.utils.BotNetworkConfiguration
import net.mamoe.mirai.utils.ContactList
import net.mamoe.mirai.utils.io.toUHexString
/**
/*
* The mirror of functions in inner classes of [Bot]
*/
//Contacts
fun Bot.getQQ(number: Long): QQ = this.contacts.getQQ(number.toUInt())
suspend fun Bot.getQQ(number: Long): QQ = this.contacts.getQQ(number.toUInt())
fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
suspend fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
fun Bot.getGroupByNumber(number: Long): Group = this.contacts.getGroupByNumber(number.toUInt())
fun Bot.getGroupByNumber(number: UInt): Group = this.contacts.getGroupByNumber(number)
fun Bot.getGroupById(number: UInt): Group = this.contacts.getGroupById(number)
suspend fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id)
suspend fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId)
val Bot.groups: ContactList<Group> get() = this.contacts.groups
val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
......@@ -39,26 +31,4 @@ suspend fun Bot.login(configuration: BotNetworkConfiguration.() -> Unit): LoginR
suspend fun Bot.login(): LoginResult = this.network.login(BotNetworkConfiguration.Default)
//BotAccount
val Bot.qqAccount: UInt get() = this.account.account
//logging
fun Bot.log(o: Any?) = logInfo(o)
fun Bot.println(o: Any?) = logInfo(o)
fun Bot.logInfo(o: Any?) = this.logger.logInfo(o)
fun Bot.logError(o: Any?) = this.logger.logError(o)
fun Bot.logPurple(o: Any?) = this.logger.logPurple(o)
fun Bot.logCyan(o: Any?) = this.logger.logCyan(o)
fun Bot.logGreen(o: Any?) = this.logger.logGreen(o)
fun Bot.logDebug(o: Any?) = this.logger.logDebug(o)
fun Bot.printPacketDebugging(packet: ServerPacket) {
logDebug("Packet=$packet")
logDebug("Packet size=" + packet.input.readBytes().size)
logDebug("Packet data=" + packet.input.readBytes().toUHexString())
}
\ No newline at end of file
val Bot.qqAccount: UInt get() = this.account.id
......@@ -7,13 +7,20 @@ import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.message.PlainText
import net.mamoe.mirai.message.toChain
import net.mamoe.mirai.network.protocol.tim.handler.EventPacketHandler
class ContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
/**
* 联系人平台基础. 包含所有平台通用的函数等.
* 联系人. 虽然叫做联系人, 但它直营
* 现支持的联系人只有 [QQ号][QQ] 和 [群][Group].
*
* @param bot 这个联系人所属 [Bot]
* @param id 可以是 QQ 号码或者群号码 [GroupId].
*
* @author Him188moe
*/
abstract class PlatformContactBase internal constructor(val bot: Bot, val number: UInt) {
sealed class Contact(val bot: Bot, val id: UInt) {
abstract suspend fun sendMessage(message: MessageChain)
......@@ -30,9 +37,101 @@ abstract class PlatformContactBase internal constructor(val bot: Bot, val number
abstract suspend fun sendXMLMessage(message: String)
}
/**
* 一般的用户可见的 ID.
* 在 TIM/QQ 客户端中所看到的的号码均是这个 ID
*
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
*/
inline class GroupId(val value: UInt)
/**
* 一些群 API 使用的 ID. 在使用时会特别注明
*
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
*/
inline class GroupInternalId(val value: UInt)
/**
* 群.
*
* Group ID 与 Group Number 并不是同一个值.
* - Group Number([Group.id]) 是通常使用的群号码.(在 QQ 客户端中可见)
* - Group ID([Group.internalId]) 是与调用 API 时使用的 id.(在 QQ 客户端中不可见)
* @author Him188moe
*/
class Group internal constructor(bot: Bot, id: UInt) : Contact(bot, id) {
val internalId = groupNumberToId(id)
val members: ContactList<QQ>
//todo members
get() = throw UnsupportedOperationException("Not yet supported")
override suspend fun sendMessage(message: MessageChain) {
bot.network[EventPacketHandler].sendGroupMessage(this, message)
}
override suspend fun sendXMLMessage(message: String) {
}
companion object
}
/**
* 所有的 [QQ], [Group] 都继承自这个类.
* 在不同平台可能有不同的实现.
* 如在 JVM, suspend 调用不便, [Contact] 中有简化调用的 `blocking`() 和 `async`
* QQ 账号.
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
*
* A QQ instance helps you to receive event from or sendPacket event to.
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
*
* @author Him188moe
*/
expect sealed class Contact(bot: Bot, number: UInt) : PlatformContactBase
\ No newline at end of file
class QQ internal constructor(bot: Bot, number: UInt) : Contact(bot, number) {
override suspend fun sendMessage(message: MessageChain) {
bot.network[EventPacketHandler].sendFriendMessage(this, message)
}
override suspend fun sendXMLMessage(message: String) {
TODO()
}
}
fun Group.Companion.groupNumberToId(number: UInt): UInt {//求你别出错
val left: Long = number.toString().let {
if (it.length < 6) {
return@groupNumberToId number
}
it.substring(0, it.length - 6).toLong()
}
val right: Long = number.toString().let {
it.substring(it.length - 6).toLong()
}
return when (left) {
in 1..10 -> {
((left + 202).toString() + right.toString()).toUInt()
}
in 11..19 -> {
((left + 469).toString() + right.toString()).toUInt()
}
in 20..66 -> {
((left + 208).toString() + right.toString()).toUInt()
}
in 67..156 -> {
((left + 1943).toString() + right.toString()).toUInt()
}
in 157..209 -> {
((left + 199).toString() + right.toString()).toUInt()
}
in 210..309 -> {
((left + 389).toString() + right.toString()).toUInt()
}
in 310..499 -> {
((left + 349).toString() + right.toString()).toUInt()
}
else -> number
}
}
......@@ -2,121 +2,100 @@
package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.utils.ContactList
/**
* 群.
*
* Group ID 与 Group Number 并不是同一个值.
* - Group Number([Group.number]) 是通常使用的群号码.(在 QQ 客户端中可见)
* - Group ID([Group.groupId]) 是与服务器通讯时使用的 id.(在 QQ 客户端中不可见)
*
* @author Him188moe
*/
expect class Group(bot: Bot, number: UInt) : Contact {
val groupId: UInt
val members: ContactList<QQ>
override suspend fun sendMessage(message: MessageChain)
override suspend fun sendXMLMessage(message: String)
companion object
}
fun Group.Companion.groupNumberToId(number: UInt): UInt {//求你别出错
val left: Long = number.toString().let {
fun GroupId.toInternalId(): GroupInternalId {//求你别出错
val left: Long = this.value.toString().let {
if (it.length < 6) {
return@groupNumberToId number
return GroupInternalId(this.value)
}
it.substring(0, it.length - 6).toLong()
}
val right: Long = number.toString().let {
val right: Long = this.value.toString().let {
it.substring(it.length - 6).toLong()
}
return when (left) {
in 1..10 -> {
((left + 202).toString() + right.toString()).toUInt()
}
in 11..19 -> {
((left + 469).toString() + right.toString()).toUInt()
}
in 20..66 -> {
((left + 208).toString() + right.toString()).toUInt()
}
in 67..156 -> {
((left + 1943).toString() + right.toString()).toUInt()
}
in 157..209 -> {
((left + 199).toString() + right.toString()).toUInt()
}
in 210..309 -> {
((left + 389).toString() + right.toString()).toUInt()
}
in 310..499 -> {
((left + 349).toString() + right.toString()).toUInt()
return GroupInternalId(
when (left) {
in 1..10 -> {
((left + 202).toString() + right.toString()).toUInt()
}
in 11..19 -> {
((left + 469).toString() + right.toString()).toUInt()
}
in 20..66 -> {
((left + 208).toString() + right.toString()).toUInt()
}
in 67..156 -> {
((left + 1943).toString() + right.toString()).toUInt()
}
in 157..209 -> {
((left + 199).toString() + right.toString()).toUInt()
}
in 210..309 -> {
((left + 389).toString() + right.toString()).toUInt()
}
in 310..499 -> {
((left + 349).toString() + right.toString()).toUInt()
}
else -> this.value
}
else -> number
}
)
}
fun Group.Companion.groupIdToNumber(id: UInt): UInt {//求你别出错
var left: UInt = id.toString().let {
fun GroupInternalId.toId(): GroupId {//求你别出错
var left: UInt = this.toString().let {
if (it.length < 6) {
return@groupIdToNumber id
return GroupId(this.value)
}
it.substring(0 until it.length - 6).toUInt()
}
return when (left.toInt()) {
return GroupId(when (left.toInt()) {
in 203..212 -> {
val right: UInt = id.toString().let {
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 202u).toString() + right.toString()).toUInt()
}
in 480..488 -> {
val right: UInt = id.toString().let {
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 469u).toString() + right.toString()).toUInt()
}
in 2100..2146 -> {
val right: UInt = id.toString().let {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 208u).toString() + right.toString()).toUInt()
}
in 2010..2099 -> {
val right: UInt = id.toString().let {
val right: UInt = this.toString().let {
it.substring(it.length - 6).toUInt()
}
((left - 1943u).toString() + right.toString()).toUInt()
}
in 2147..2199 -> {
val right: UInt = id.toString().let {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 199u).toString() + right.toString()).toUInt()
}
in 4100..4199 -> {
val right: UInt = id.toString().let {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 389u).toString() + right.toString()).toUInt()
}
in 3800..3989 -> {
val right: UInt = id.toString().let {
val right: UInt = this.toString().let {
it.substring(it.length - 7).toUInt()
}
left = left.toString().substring(0 until 3).toUInt()
((left - 349u).toString() + right.toString()).toUInt()
}
else -> id
}
else -> this.value
})
}
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.message.At
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageChain
/**
* QQ 账号.
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
*
* A QQ instance helps you to receive event from or sendPacket event to.
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
*
* @author Him188moe
*/
expect class QQ(bot: Bot, number: UInt) : Contact {
override suspend fun sendMessage(message: MessageChain)
override suspend fun sendXMLMessage(message: String)
}
/**
* At(@) this account.
*
* @return an instance of [Message].
*/
fun QQ.at(): At {
return At(this)
}
\ No newline at end of file
......@@ -6,7 +6,7 @@ package net.mamoe.mirai.message
* @author LamGC
*/
@Suppress("EnumEntryName", "unused", "SpellCheckingInspection")
enum class FaceID constructor(val id: UByte) {
enum class FaceID constructor(val value: UByte) {
unknown(0xffu),
// TODO: 2019/9/1 添加更多表情
jingya(0u),
......@@ -150,14 +150,14 @@ enum class FaceID constructor(val id: UByte) {
qingwa(170u);
override fun toString(): String {
return "$name($id)"
return "$name($value)"
}
companion object {
fun ofId(id: UByte): FaceID {
for (value in values()) {
if (value.id == id) {
if (value.value == id) {
return value
}
}
......
......@@ -81,7 +81,7 @@ interface Message {
// ==================================== PlainText ====================================
inline class PlainText(override val stringValue: String) : Message {
override operator fun contains(sub: String): Boolean = this.stringValue.contains(sub)
override operator fun contains(sub: String): Boolean = sub in stringValue
}
// ==================================== Image ====================================
......@@ -91,12 +91,11 @@ inline class PlainText(override val stringValue: String) : Message {
* 由接收消息时构建, 可直接发送
*
* @param id 这个图片的 [ImageId]
* @param filename 文件名. 这将决定图片的显示
*
* @see
*/
class Image(val id: ImageId, val filename: String = "") : Message {
override val stringValue: String = "[${id.value}]"
inline class Image(val id: ImageId) : Message {
override val stringValue: String get() = "[${id.value}]"
}
/**
......@@ -126,7 +125,7 @@ inline class At(val targetQQ: UInt) : Message {
* QQ 自带表情
*/
inline class Face(val id: FaceID) : Message {
override val stringValue: String get() = "[face${id.id}]"
override val stringValue: String get() = "[face${id.value}]"
}
// ==================================== MessageChain ====================================
......
......@@ -41,7 +41,7 @@ internal fun IoBuffer.parseMessageImage0x06(): Image {
val suffix = readString(filenameLength).substringAfter(".")
discardExact(8)//03 00 04 00 00 02 9C 04
val length = readShort()//=27
return Image(ImageId(readString(length)), "")//todo 文件名解析
return Image(ImageId(readString(length)))
//return Image("{${readString(length)}}.$suffix")
}
......@@ -60,8 +60,8 @@ fun main() {
}
internal fun IoBuffer.parseMessageImage0x03(): Image {
discardExact(1) //TODO 这里的文件名解析
return Image(ImageId(String(readLVByteArray())), "")
discardExact(1)
return Image(ImageId(String(readLVByteArray())))
}
internal fun ByteReadPacket.readMessage(): Message? {
......@@ -141,13 +141,13 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
writeShortLVPacket {
writeShort(1)
writeUByte(id.id)
writeUByte(id.value)
writeHex("0B 00 08 00 01 00 04 52 CC F5 D0 FF")
writeShort(2)
writeByte(0x14)//??
writeUByte((id.id + 65u).toUByte())
writeUByte((id.value + 65u).toUByte())
}
}
......
......@@ -64,5 +64,5 @@ suspend fun main() {
}
println("Filtering finished")
println(goodBotList.joinToString("\n") { it.account.account.toString() + " " + it.account.password })
println(goodBotList.joinToString("\n") { it.account.id.toString() + " " + it.account.password })
}
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