diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt index 53d551898..5a39203e4 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberJoin.kt @@ -8,7 +8,7 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.MemberPermission -import net.mamoe.mirai.contact.internal.MemberImpl +import net.mamoe.mirai.contact.internal.Member import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.getGroup @@ -16,6 +16,7 @@ import net.mamoe.mirai.getQQ import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.discardExact +import kotlin.coroutines.coroutineContext /** * 成员加入前的事件. 群的成员列表中还没有这个人 @@ -74,7 +75,7 @@ internal object MemberJoinPacketHandler : KnownEventParserAndHandler() { + options.encoding = "UTF-8" +} \ No newline at end of file diff --git a/mirai-japt/mirai-japt.postfixTemplates b/mirai-japt/mirai-japt.postfixTemplates new file mode 100644 index 000000000..9df0de9fd --- /dev/null +++ b/mirai-japt/mirai-japt.postfixTemplates @@ -0,0 +1,4 @@ +# Contact + +.blocking : 阻塞式包装 + net.mamoe.mirai.contact.QQ [net.mamoe.mirai.japt.BlockingContacts] -> BlockingQQ. BlockingContacts.createBlocking($var$) diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContact.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContact.java new file mode 100644 index 000000000..dcc1585cb --- /dev/null +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContact.java @@ -0,0 +1,26 @@ +package net.mamoe.mirai.japt; + +import net.mamoe.mirai.Bot; +import net.mamoe.mirai.message.MessageChain; +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("unused") +public interface BlockingContact { + /** + * 这个联系人所属 [Bot] + */ + @NotNull + Bot getBot(); + + /** + * 可以是 QQ 号码或者群号码 [GroupId]. + */ + long getId(); + + /** + * 向这个对象发送消息. + *

+ * 速度太快会被服务器屏蔽(无响应). 在测试中不延迟地发送 6 条消息就会被屏蔽之后的数据包 1 秒左右. + */ + void sendMessage(@NotNull MessageChain messages); +} diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContacts.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContacts.java new file mode 100644 index 000000000..de7a3b9cf --- /dev/null +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingContacts.java @@ -0,0 +1,22 @@ +package net.mamoe.mirai.japt; + +import net.mamoe.mirai.contact.Group; +import net.mamoe.mirai.contact.Member; +import net.mamoe.mirai.contact.QQ; + +/** + * 构造阻塞式的联系人. + */ +public final class BlockingContacts { + public static BlockingQQ createBlocking(QQ qq) { + return new BlockingQQImpl(qq); + } + + public static BlockingGroup createBlocking(Group group) { + return new BlockingGroupImpl(group); + } + + public static BlockingMember createBlocking(Member member) { + return new BlockingMemberImpl(member); + } +} diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java new file mode 100644 index 000000000..37fdc62b1 --- /dev/null +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java @@ -0,0 +1,57 @@ +package net.mamoe.mirai.japt; + +import net.mamoe.mirai.network.protocol.tim.packet.action.GroupInfo; + +import java.util.Map; + +@SuppressWarnings("unused") +public interface BlockingGroup extends BlockingContact { + /** + * 内部 ID. 内部 ID 为 [GroupId] 的映射 + */ + Long getInternalId(); + + /** + * 群主 (同步事件更新) + * 进行 [updateGroupInfo] 时将会更新这个值. + */ + BlockingMember getOwner(); + + /** + * 群名称 (同步事件更新) + * 进行 [updateGroupInfo] 时将会更新这个值. + */ + String getName(); + + /** + * 入群公告, 没有时为空字符串. (同步事件更新) + * 进行 [updateGroupInfo] 时将会更新这个值. + */ + String getAnnouncement(); + + /** + * 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新 + *

+ * **注意**: 获得的列表仅为这一时刻的成员列表的镜像. 它将不会被更新 + */ + Map getMembers(); + + /** + * 获取群成员. 若此 ID 的成员不存在, 则会抛出 [kotlin.NoSuchElementException] + */ + BlockingMember getMember(long id); + + /** + * 更新群资料. 群资料会与服务器事件同步事件更新, 一般情况下不需要手动更新. + * + * @return 这一时刻的群资料 + */ + GroupInfo updateGroupInfo(); + + /** + * 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败 + */ + boolean quit(); + + String toFullString(); +} \ No newline at end of file diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingMember.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingMember.java new file mode 100644 index 000000000..bea594a00 --- /dev/null +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingMember.java @@ -0,0 +1,29 @@ +package net.mamoe.mirai.japt; + +import net.mamoe.mirai.contact.MemberPermission; + +@SuppressWarnings("unused") +public interface BlockingMember { + /** + * 所在的群 + */ + BlockingGroup getGroup(); + + /** + * 权限 + */ + MemberPermission getPermission(); + + /** + * 禁言 + * + * @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常. + * @return 若机器人无权限禁言这个群成员, 返回 `false` + */ + Boolean mute(int durationSeconds); + + /** + * 解除禁言 + */ + void unmute(); +} diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingQQ.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingQQ.java new file mode 100644 index 000000000..e01aa246c --- /dev/null +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingQQ.java @@ -0,0 +1,31 @@ +package net.mamoe.mirai.japt; + +import net.mamoe.mirai.contact.data.Profile; +import net.mamoe.mirai.network.protocol.tim.packet.action.FriendNameRemark; +import net.mamoe.mirai.network.protocol.tim.packet.action.PreviousNameList; +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings("unused") +public interface BlockingQQ extends BlockingContact { + /** + * 查询用户资料 + */ + @NotNull + Profile queryProfile(); + + /** + * 查询曾用名. + *

+ * 曾用名可能是: + * - 昵称 + * - 共同群内的群名片 + */ + @NotNull + PreviousNameList queryPreviousNameList(); + + /** + * 查询机器人账号给这个人设置的备注 + */ + @NotNull + FriendNameRemark queryRemark(); +} diff --git a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/BlockingContacts.kt b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/BlockingContacts.kt new file mode 100644 index 000000000..e583d33b5 --- /dev/null +++ b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/BlockingContacts.kt @@ -0,0 +1,11 @@ +@file:Suppress("NOTHING_TO_INLINE", "unused") + +package net.mamoe.mirai.japt + +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.Member +import net.mamoe.mirai.contact.QQ + +inline fun Group.blocking(): BlockingGroup = BlockingContacts.createBlocking(this) +inline fun QQ.blocking(): BlockingQQ = BlockingContacts.createBlocking(this) +inline fun Member.blocking(): BlockingMember = BlockingContacts.createBlocking(this) \ No newline at end of file diff --git a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/BlockingContactsImpl.kt b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/BlockingContactsImpl.kt new file mode 100644 index 000000000..550d5a807 --- /dev/null +++ b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/BlockingContactsImpl.kt @@ -0,0 +1,46 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE") + +package net.mamoe.mirai.japt + +import kotlinx.coroutines.runBlocking +import net.mamoe.mirai.Bot +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.Member +import net.mamoe.mirai.contact.MemberPermission +import net.mamoe.mirai.contact.QQ +import net.mamoe.mirai.contact.data.Profile +import net.mamoe.mirai.message.MessageChain +import net.mamoe.mirai.network.protocol.tim.packet.action.FriendNameRemark +import net.mamoe.mirai.network.protocol.tim.packet.action.GroupInfo +import net.mamoe.mirai.network.protocol.tim.packet.action.PreviousNameList + +internal class BlockingQQImpl(private val delegate: QQ) : BlockingQQ { + override fun getBot(): Bot = delegate.bot + override fun getId(): Long = delegate.id.toLong() + override fun sendMessage(messages: MessageChain) = runBlocking { delegate.sendMessage(messages) } + override fun queryProfile(): Profile = runBlocking { delegate.queryProfile() } + override fun queryPreviousNameList(): PreviousNameList = runBlocking { delegate.queryPreviousNameList() } + override fun queryRemark(): FriendNameRemark = runBlocking { delegate.queryRemark() } +} + +internal class BlockingGroupImpl(private val delegate: Group) : BlockingGroup { + override fun sendMessage(messages: MessageChain) = runBlocking { delegate.sendMessage(messages) } + override fun getOwner(): BlockingMember = delegate.owner.blocking() + override fun getName(): String = delegate.name + override fun getId(): Long = delegate.id.toLong() + override fun updateGroupInfo(): GroupInfo = runBlocking { delegate.updateGroupInfo() } + override fun toFullString(): String = delegate.toFullString() + override fun getMember(id: Long): BlockingMember = delegate.getMember(id.toUInt()).blocking() + override fun getBot(): Bot = delegate.bot + override fun getAnnouncement(): String = delegate.announcement + override fun getMembers(): MutableMap = delegate.members.mapKeys { it.key.toLong() }.mapValues { it.value.blocking() }.toMutableMap() + override fun getInternalId(): Long = delegate.internalId.value.toLong() + override fun quit(): Boolean = runBlocking { delegate.quit().isSuccess } +} + +internal class BlockingMemberImpl(private val delegate: Member) : BlockingMember { + override fun getGroup(): BlockingGroup = delegate.group.blocking() + override fun getPermission(): MemberPermission = delegate.permission + override fun mute(durationSeconds: Int): Boolean = runBlocking { delegate.mute(durationSeconds) } + override fun unmute() = runBlocking { delegate.unmute() } +} \ No newline at end of file diff --git a/mirai-japt/src/test/kotlin/BlockingTest.java b/mirai-japt/src/test/kotlin/BlockingTest.java new file mode 100644 index 000000000..385ab7c23 --- /dev/null +++ b/mirai-japt/src/test/kotlin/BlockingTest.java @@ -0,0 +1,14 @@ +public class BlockingTest { + + + public static void main(String[] args) { + //Bot bot = new Bot(new BotAccount(123456, ""), EmptyCoroutineContext.INSTANCE); + //if (bot.getNetwork().login() != LoginResult.) { + // throw IllegalStateException("Login failed") + //} + //bot.getContacts().getGroups(); + + //var qq = BlockingContacts.createBlocking(bot.getContacts().getQQ(123L)) + //println(createBlocking.queryRemark()) + } +} diff --git a/mirai-japt/src/test/kotlin/TestBlocking.kt b/mirai-japt/src/test/kotlin/TestBlocking.kt new file mode 100644 index 000000000..01393988a --- /dev/null +++ b/mirai-japt/src/test/kotlin/TestBlocking.kt @@ -0,0 +1,31 @@ +import net.mamoe.mirai.Bot +import net.mamoe.mirai.BotAccount +import net.mamoe.mirai.japt.BlockingContacts +import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult +import java.io.File + +@ExperimentalUnsignedTypes +private fun readTestAccount(): BotAccount? { + val file = File("testAccount.txt") + if (!file.exists() || !file.canRead()) { + return null + } + + val lines = file.readLines() + return try { + BotAccount(lines[0].toUInt(), lines[1]) + } catch (e: Exception) { + null + } +} + +@ExperimentalUnsignedTypes +suspend fun main() { + val bot = Bot(readTestAccount()!!) + if (bot.network.login() != LoginResult.SUCCESS) { + throw IllegalStateException("Login failed") + } + + val createBlocking = BlockingContacts.createBlocking(bot.contacts.getQQ(123L)) + println(createBlocking.queryRemark()) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index c73bf1627..8b6a36bf7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,6 +22,7 @@ rootProject.name = 'mirai' include(':mirai-core') +include(':mirai-japt') include(':mirai-console') //include(':mirai-api') include(':mirai-api-http')