Commit 38e3f133 authored by liujiahua123123's avatar liujiahua123123

Merge remote-tracking branch 'origin/master'

parents 615de9ef 312fb437
# Mirai # Mirai
[![HitCount](http://hits.dwyl.io/him188/mamoe/mirai.svg)](http://hits.dwyl.io/him188/mamoe/mirai)
一个以<b>TIM QQ协议(非web)</b>驱动的JAVA(+Kotlin) QQ机器人服务端核心 一个以<b>TIM QQ协议(非web)</b>驱动的QQ机器人服务端核心
采用服务端-插件模式运行,同时提供独立的协议层库 采用服务端-插件模式运行,同时提供独立的协议层库
**我们承诺项目的所有模块均开源** **我们承诺项目的所有模块均开源**
...@@ -17,23 +18,23 @@ ...@@ -17,23 +18,23 @@
1. Clone 1. Clone
2. Import as Maven project 2. Import as Maven project
3. Run [MiraiMain](mirai-core/src/main/java/net/mamoe/mirai/MiraiMain.java#L7) 3. Run demo[Demo 1 Main](mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt#L16)
### 事件 Hook ### 事件 Hook
#### Java: #### Java:
``` ```
MiraiEventHook.onEvent(FriendMessageEvent.class) MiraiEventHook.onEvent(FriendMessageEvent.class)
.handler(a -> { .handler(a -> {
if(a.getMessageString().equals("你好")) if(a.message.eq("你好"))
a.getQQ().sendMessage("你好!"); a.getSender().sendMessage("你好!");
}) })
.mountAlways(); .mountAlways();
``` ```
#### Kotlin: #### Kotlin:
``` ```
FriendMessageEvent::class.hookAlways{ FriendMessageEvent::class.hookAlways{
if(it.message() valueEquals "你好") if(it.message eq "你好")
it.qq.sendMessage("你好!") it.reply("你好!")
} }
``` ```
![AYWVE86P](.github/A%7DYWVE860U%28%25YQD%24R1GB1%5BP.png) ![AYWVE86P](.github/A%7DYWVE860U%28%25YQD%24R1GB1%5BP.png)
...@@ -44,7 +45,7 @@ FriendMessageEvent::class.hookAlways{ ...@@ -44,7 +45,7 @@ FriendMessageEvent::class.hookAlways{
![](.github/68f8fec9.png) ![](.github/68f8fec9.png)
发送图片已经完成,但我们还在开发上传图片至服务器。 发送图片已经完成,但我们还在开发上传图片至服务器。
现在你可以通过发送一张图片给机器人账号,再让机器人账号发送这张图片。你可以查看 [Image](src/main/java/net/mamoe/mirai/message/Image.kt) 现在你可以通过发送一张图片给机器人账号,再让机器人账号发送这张图片。你可以查看 [Image.kt](mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Image.kt#L20-L93)
## 语言使用说明 ## 语言使用说明
我们使用 Kotlin,但我们也会保留对 Java 和 Java开发者的支持。 我们使用 Kotlin,但我们也会保留对 Java 和 Java开发者的支持。
......
...@@ -22,6 +22,14 @@ ...@@ -22,6 +22,14 @@
<version>1.0</version> <version>1.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-console</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<dependency> <dependency>
<groupId>net.mamoe</groupId> <groupId>net.mamoe</groupId>
<artifactId>mirai-api</artifactId> <artifactId>mirai-core</artifactId>
<version>1.0</version> <version>1.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
......
package net.mamoe.mirai; package net.mamoe.mirai;
import java.util.Scanner;
public class MiraiConsole { public class MiraiConsole {
public static void main(String[] args){ public static void main(String[] args){
System.out.println(">>> starting Mirai"); System.out.println(">>> starting Mirai");
MiraiAPI.startMirai(args); // MiraiAPI.startMirai(args);
} }
public static void processCommand(String command){ public static void processCommand(String command){
......
...@@ -11,15 +11,12 @@ import net.mamoe.mirai.utils.LoggerTextFormat; ...@@ -11,15 +11,12 @@ import net.mamoe.mirai.utils.LoggerTextFormat;
import net.mamoe.mirai.utils.MiraiLogger; import net.mamoe.mirai.utils.MiraiLogger;
import net.mamoe.mirai.utils.MiraiLoggerKt; import net.mamoe.mirai.utils.MiraiLoggerKt;
import net.mamoe.mirai.utils.config.MiraiConfig; import net.mamoe.mirai.utils.config.MiraiConfig;
import net.mamoe.mirai.utils.config.MiraiConfigSection;
import net.mamoe.mirai.utils.setting.MiraiSettingListSection; import net.mamoe.mirai.utils.setting.MiraiSettingListSection;
import net.mamoe.mirai.utils.setting.MiraiSettingMapSection; import net.mamoe.mirai.utils.setting.MiraiSettingMapSection;
import net.mamoe.mirai.utils.setting.MiraiSettings; import net.mamoe.mirai.utils.setting.MiraiSettings;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
/** /**
...@@ -91,6 +88,7 @@ public final class MiraiServer { ...@@ -91,6 +88,7 @@ public final class MiraiServer {
File setting = new File(this.parentFolder + "/Mirai.ini"); File setting = new File(this.parentFolder + "/Mirai.ini");
getLogger().info("Selecting setting from " + LoggerTextFormat.GREEN + setting); getLogger().info("Selecting setting from " + LoggerTextFormat.GREEN + setting);
/*
if (!setting.exists()) { if (!setting.exists()) {
this.initSetting(setting); this.initSetting(setting);
} else { } else {
...@@ -106,7 +104,7 @@ public final class MiraiServer { ...@@ -106,7 +104,7 @@ public final class MiraiServer {
} }
if (this.qqs.isEmpty()) { if (this.qqs.isEmpty()) {
this.initQQConfig(qqs); this.initQQConfig(qqs);
} }*/
/* /*
MiraiSettingMapSection qqs = this.setting.getMapSection("qq"); MiraiSettingMapSection qqs = this.setting.getMapSection("qq");
...@@ -198,12 +196,12 @@ public final class MiraiServer { ...@@ -198,12 +196,12 @@ public final class MiraiServer {
String qqList = String qqList =
"3034551466----zxcvbnm\n"; "1683921395----bb22222\n";
private Bot getAvailableBot() throws ExecutionException, InterruptedException { private Bot getAvailableBot() throws ExecutionException, InterruptedException {
for (String it : qqList.split("\n")) { for (String it : qqList.split("\n")) {
var strings = it.split("----"); var strings = it.split("----");
var bot = new Bot(new BotAccount(Long.parseLong(strings[0]), strings[1]), List.of()); var bot = new Bot(new BotAccount(Long.parseLong(strings[0]), strings[1]));
if (bot.network.tryLogin(200).get() == LoginState.SUCCESS) { if (bot.network.tryLogin(200).get() == LoginState.SUCCESS) {
MiraiLoggerKt.success(bot, "Login succeed"); MiraiLoggerKt.success(bot, "Login succeed");
......
package net.mamoe.mirai; package net.mamoe.mirai;
import lombok.Getter;
import net.mamoe.mirai.contact.Group; import net.mamoe.mirai.contact.Group;
import net.mamoe.mirai.contact.QQ; import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.network.BotNetworkHandler; import net.mamoe.mirai.network.BotNetworkHandler;
import net.mamoe.mirai.network.BotNetworkHandlerImpl; import net.mamoe.mirai.network.BotNetworkHandlerImpl;
import net.mamoe.mirai.utils.BotAccount; import net.mamoe.mirai.utils.BotAccount;
import net.mamoe.mirai.utils.ContactList; import net.mamoe.mirai.utils.ContactList;
import net.mamoe.mirai.utils.config.MiraiConfigSection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.Closeable; import java.io.Closeable;
import java.util.*; import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
...@@ -58,7 +59,7 @@ public final class Bot implements Closeable { ...@@ -58,7 +59,7 @@ public final class Bot implements Closeable {
@Override @Override
public String toString() { public String toString() {
return String.format("Bot{id=%d,qq=%d}", id, this.account.qqNumber); return String.format("Bot{id=%d,qq=%d}", id, this.account.getQqNumber());
} }
/** /**
...@@ -93,32 +94,9 @@ public final class Bot implements Closeable { ...@@ -93,32 +94,9 @@ public final class Bot implements Closeable {
} }
} }
public Bot(@NotNull BotAccount account) {
/**
* Ref list
*/
@Getter
private final List<String> owners;
public boolean isOwnBy(String ownerName) {
return owners.contains(ownerName);
}
public Bot(MiraiConfigSection<Object> data) throws Throwable {
this(
new BotAccount(
data.getLongOrThrow("account", () -> new IllegalArgumentException("account")),
data.getStringOrThrow("password", () -> new IllegalArgumentException("password"))
),
data.getAsOrDefault("owners", ArrayList::new)
);
}
public Bot(@NotNull BotAccount account, @NotNull List<String> owners) {
Objects.requireNonNull(account); Objects.requireNonNull(account);
Objects.requireNonNull(owners);
this.account = account; this.account = account;
this.owners = Collections.unmodifiableList(owners);
this.network = new BotNetworkHandlerImpl(this); this.network = new BotNetworkHandlerImpl(this);
} }
......
package net.mamoe.mirai
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.network.packet.ClientPacket
/**
* The mirror of functions in inner classes of [Bot]
*
* @author Him188moe
*/
//Contacts
fun Bot.getQQ(number: Long): QQ = this.contacts.getQQ(number)
fun Bot.getGroupByNumber(number: Long): Group = this.contacts.getGroupByNumber(number)
fun Bot.getGroupById(number: Long): Group = this.contacts.getGroupById(number)
//NetworkHandler
suspend fun Bot.sendPacket(packet: ClientPacket) {
this.network.socket.sendPacket(packet)
}
//BotAccount
fun Bot.getBotQQ(): Long = this.account.qqNumber
\ No newline at end of file
...@@ -18,10 +18,7 @@ import java.util.concurrent.CompletableFuture ...@@ -18,10 +18,7 @@ import java.util.concurrent.CompletableFuture
* @author Him188moe * @author Him188moe
*/ */
abstract class Contact internal constructor(val bot: Bot, val number: Long) { abstract class Contact internal constructor(val bot: Bot, val number: Long) {
/** abstract suspend fun sendMessage(message: MessageChain)
* Async
*/
abstract fun sendMessage(message: MessageChain)
/** /**
* 上传图片 * 上传图片
...@@ -30,19 +27,23 @@ abstract class Contact internal constructor(val bot: Bot, val number: Long) { ...@@ -30,19 +27,23 @@ abstract class Contact internal constructor(val bot: Bot, val number: Long) {
return image.upload(session, this) return image.upload(session, this)
} }
fun sendMessage(message: Message) { suspend fun sendMessage(message: Message) {
if (message is MessageChain) { if (message is MessageChain) {
return sendMessage(message) return sendMessage(message)
} }
return sendMessage(message.toChain()) return sendMessage(message.toChain())
} }
fun sendMessage(message: String) { suspend fun sendMessage(message: String) {
this.sendMessage(PlainText(message)) this.sendMessage(PlainText(message))
} }
suspend fun sendMessage(message: List<Message>) {
this.sendMessage(MessageChain(message))
}
/** /**
* Async * Async
*/ */
abstract fun sendXMLMessage(message: String) abstract suspend fun sendXMLMessage(message: String)
} }
...@@ -25,11 +25,11 @@ class Group(bot: Bot, number: Long) : Contact(bot, number), Closeable { ...@@ -25,11 +25,11 @@ class Group(bot: Bot, number: Long) : Contact(bot, number), Closeable {
val groupId = groupNumberToId(number) val groupId = groupNumberToId(number)
val members = ContactList<QQ>() val members = ContactList<QQ>()
override fun sendMessage(message: MessageChain) { override suspend fun sendMessage(message: MessageChain) {
bot.network.message.sendGroupMessage(this, message) bot.network.message.sendGroupMessage(this, message)
} }
override fun sendXMLMessage(message: String) { override suspend fun sendXMLMessage(message: String) {
} }
......
...@@ -18,11 +18,11 @@ import net.mamoe.mirai.message.defaults.MessageChain ...@@ -18,11 +18,11 @@ import net.mamoe.mirai.message.defaults.MessageChain
* @author Him188moe * @author Him188moe
*/ */
class QQ(bot: Bot, number: Long) : Contact(bot, number) { class QQ(bot: Bot, number: Long) : Contact(bot, number) {
override fun sendMessage(message: MessageChain) { override suspend fun sendMessage(message: MessageChain) {
bot.network.message.sendFriendMessage(this, message) bot.network.message.sendFriendMessage(this, message)
} }
override fun sendXMLMessage(message: String) { override suspend fun sendXMLMessage(message: String) {
} }
......
package net.mamoe.mirai.event package net.mamoe.mirai.event
import kotlinx.coroutines.runBlocking
import java.util.function.Consumer import java.util.function.Consumer
import java.util.function.Predicate import java.util.function.Predicate
...@@ -7,12 +8,20 @@ import java.util.function.Predicate ...@@ -7,12 +8,20 @@ import java.util.function.Predicate
* @author Him188moe * @author Him188moe
*/ */
class MiraiEventHookKt<E : MiraiEvent>(eventClass: Class<E>) : MiraiEventHook<E>(eventClass) { class MiraiEventHookKt<E : MiraiEvent>(eventClass: Class<E>) : MiraiEventHook<E>(eventClass) {
fun onEvent(handler: (E) -> Unit) { fun onEvent(handler: suspend (E) -> Unit) {
this@MiraiEventHookKt.handler = Consumer(handler) this@MiraiEventHookKt.handler = Consumer {
runBlocking {
handler(it)
}
}
} }
fun validChecker(predicate: (E) -> Boolean) { fun validChecker(predicate: suspend (E) -> Boolean) {//todo 把 mirai event 变为 suspend, 而不是在这里 run blocking
this@MiraiEventHookKt.validChecker = Predicate(predicate) this@MiraiEventHookKt.validChecker = Predicate {
runBlocking {
predicate(it)
}
}
} }
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
package net.mamoe.mirai.event package net.mamoe.mirai.event
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.Bot
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
...@@ -18,50 +20,63 @@ object EventManager : MiraiEventManager() ...@@ -18,50 +20,63 @@ object EventManager : MiraiEventManager()
/** /**
* 每次事件触发时都会调用 hook * 每次事件触发时都会调用 hook
*/ */
fun <C : Class<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) { fun <C : Class<E>, E : MiraiEvent> C.hookAlways(bot: Bot? = null, hook: suspend (E) -> Unit) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook)) MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this) {
runBlocking {
hook(it)
}
})
} }
/** /**
* 当下一次事件触发时调用 hook * 当下一次事件触发时调用 hook
*/ */
fun <C : Class<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) { fun <C : Class<E>, E : MiraiEvent> C.hookOnce(bot: Bot? = null, hook: suspend (E) -> Unit) {
MiraiEventManager.getInstance().hookOnce(MiraiEventHook<E>(this, hook)) //todo bot 限制 实现. 或重写 hook
MiraiEventManager.getInstance().hookOnce(MiraiEventHook<E>(this) {
runBlocking {
hook(it)
}
})
} }
/** /**
* 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook * 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook
*/ */
fun <C : Class<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) { fun <C : Class<E>, E : MiraiEvent> C.hookWhile(bot: Bot? = null, hook: suspend (E) -> Boolean) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHookSimple(this, hook)) MiraiEventManager.getInstance().hookAlways(MiraiEventHookSimple(this, hook))
} }
/** /**
* 每次事件触发时都会调用 hook * 每次事件触发时都会调用 hook
*
* @param bot 指定仅限于某个 [Bot] 的事件.
*/ */
fun <C : KClass<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) { fun <C : KClass<E>, E : MiraiEvent> C.hookAlways(bot: Bot? = null, hook: suspend (E) -> Unit) {
this.java.hookAlways(hook) this.java.hookAlways(bot, hook)
} }
/** /**
* 当下一次事件触发时调用 hook * 当下一次事件触发时调用 hook
*/ */
fun <C : KClass<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) { fun <C : KClass<E>, E : MiraiEvent> C.hookOnce(bot: Bot? = null, hook: suspend (E) -> Unit) {
this.java.hookOnce(hook) this.java.hookOnce(bot, hook)
} }
/** /**
* 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook * 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook
*/ */
fun <C : KClass<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) { fun <C : KClass<E>, E : MiraiEvent> C.hookWhile(bot: Bot? = null, hook: suspend (E) -> Boolean) {
this.java.hookWhile(hook) this.java.hookWhile(bot, hook)
} }
private class MiraiEventHookSimple<E : MiraiEvent>(clazz: Class<E>, val hook: (E) -> Boolean) : MiraiEventHook<E>(clazz) { private class MiraiEventHookSimple<E : MiraiEvent>(clazz: Class<E>, val hook: suspend (E) -> Boolean) : MiraiEventHook<E>(clazz) {
override fun accept(event: MiraiEvent?): Boolean { override fun accept(event: MiraiEvent?): Boolean {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return !hook.invoke(event as E) return runBlocking {
return@runBlocking !hook.invoke(event as E)
}
} }
} }
\ No newline at end of file
package net.mamoe.mirai.event.events.group;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.contact.Group;
import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.message.defaults.MessageChain;
import org.jetbrains.annotations.NotNull;
/**
* @author Him188moe
*/
public final class GroupMessageEvent extends GroupEvent {
public final QQ sender;
public final MessageChain chain;
public final String message;
public GroupMessageEvent(@NotNull Bot bot, @NotNull Group group, @NotNull QQ sender, @NotNull MessageChain chain) {
super(bot, group);
this.sender = sender;
this.chain = chain;
this.message = chain.toString();
}
@NotNull
public MessageChain getChain() {
return chain;
}
@NotNull
public String getMessage() {
return message;
}
@NotNull
public QQ getSender() {
return sender;
}
}
package net.mamoe.mirai.event.events.group
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.MessageChain
/**
* @author Him188moe
*/
class GroupMessageEvent(bot: Bot, group: Group, val sender: QQ, val message: MessageChain) : GroupEvent(bot, group) {
@JvmSynthetic
suspend inline fun reply(message: Message) = group.sendMessage(message)
@JvmSynthetic
suspend inline fun reply(message: String) = group.sendMessage(message)
@JvmSynthetic
suspend inline fun reply(message: List<Message>) = group.sendMessage(message)
@JvmSynthetic
suspend inline fun reply(message: MessageChain) = group.sendMessage(message)
}
...@@ -2,12 +2,12 @@ package net.mamoe.mirai.event.events.network; ...@@ -2,12 +2,12 @@ package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.Bot; import net.mamoe.mirai.Bot;
import net.mamoe.mirai.event.Cancellable; import net.mamoe.mirai.event.Cancellable;
import net.mamoe.mirai.network.packet.ServerCatchaPacket;
import net.mamoe.mirai.network.packet.ServerPacket; import net.mamoe.mirai.network.packet.ServerPacket;
import net.mamoe.mirai.network.packet.ServerVerificationCodePacket;
/** /**
* 服务器接到某数据包时触发这个事件. * 服务器接到某数据包时触发这个事件.
* 注意, 当接收到数据包的加密包(如 {@link ServerVerificationCodePacket.Encrypted})也会触发这个事件, 随后才会 * 注意, 当接收到数据包的加密包(如 {@link ServerCatchaPacket.Encrypted})也会触发这个事件, 随后才会
* *
* @author Him188moe * @author Him188moe
*/ */
......
package net.mamoe.mirai.event.events.qq; package net.mamoe.mirai.event.events.qq;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.message.defaults.MessageChain;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/** /**
* @author Him188moe * @author Him188moe
*/ */
public final class FriendMessageEvent extends FriendEvent {
public final MessageChain message;
public FriendMessageEvent(@NotNull Bot bot, @NotNull QQ sender, @NotNull MessageChain message) {
super(bot, sender);
this.message = Objects.requireNonNull(message);
}
@NotNull
public MessageChain message() {
return message;
}
}
package net.mamoe.mirai.event.events.qq
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.MessageChain
/**
* @author Him188moe
*/
class FriendMessageEvent(bot: Bot, sender: QQ, val message: MessageChain) : FriendEvent(bot, sender) {
@JvmSynthetic
suspend inline fun reply(message: Message) = sender.sendMessage(message)
@JvmSynthetic
suspend inline fun reply(message: String) = sender.sendMessage(message)
@JvmSynthetic
suspend inline fun reply(message: List<Message>) = sender.sendMessage(message)
@JvmSynthetic
suspend inline fun reply(message: MessageChain) = sender.sendMessage(message)
}
...@@ -15,8 +15,8 @@ import java.util.* ...@@ -15,8 +15,8 @@ import java.util.*
* #### 在 Kotlin 使用 [Message] * #### 在 Kotlin 使用 [Message]
* 这与使用 [String] 的使用非常类似. * 这与使用 [String] 的使用非常类似.
* *
* 比较 [Message] 与 [String] (使用 infix [Message.valueEquals]): * 比较 [Message] 与 [String] (使用 infix [Message.eq]):
* `if(message valueEquals "你好") qq.sendMessage(message)` * `if(message eq "你好") qq.sendMessage(message)`
* *
* 连接 [Message] 与 [Message], [String], [BufferedImage] (使用 operator [Message.plus]): * 连接 [Message] 与 [Message], [String], [BufferedImage] (使用 operator [Message.plus]):
* ``` * ```
...@@ -28,7 +28,7 @@ import java.util.* ...@@ -28,7 +28,7 @@ import java.util.*
* @see Contact.sendMessage * @see Contact.sendMessage
*/ */
abstract class Message { abstract class Message {
internal abstract val type: Int internal abstract val type: MessageKey
private var toStringCache: String? = null private var toStringCache: String? = null
private val cacheLock = object : Any() {} private val cacheLock = object : Any() {}
...@@ -76,12 +76,17 @@ abstract class Message { ...@@ -76,12 +76,17 @@ abstract class Message {
* - [PlainText] 比较 [PlainText.text] * - [PlainText] 比较 [PlainText.text]
* - [Image] 比较 [Image.imageId] * - [Image] 比较 [Image.imageId]
*/ */
abstract infix fun valueEquals(another: Message): Boolean abstract infix fun eq(another: Message): Boolean
/** /**
* 将这个消息的 [toString] 与 [another] 比较 * 将这个消息的 [toString] 与 [another] 比较
*/ */
infix fun valueEquals(another: String): Boolean = this.toString() == another infix fun eq(another: String): Boolean = this.toString() == another
/**
* 判断 [sub] 是否存在于本消息中
*/
abstract operator fun contains(sub: String): Boolean
/** /**
* 把这个消息连接到另一个消息的头部. 相当于字符串相加 * 把这个消息连接到另一个消息的头部. 相当于字符串相加
...@@ -104,6 +109,12 @@ abstract class Message { ...@@ -104,6 +109,12 @@ abstract class Message {
* @return message connected * @return message connected
*/ */
open fun concat(tail: Message): MessageChain { open fun concat(tail: Message): MessageChain {
if (tail is MessageChain) {
return MessageChain(this).let {
tail.list.forEach { child -> it.concat(child) }
it
}
}
return MessageChain(this, Objects.requireNonNull(tail)) return MessageChain(this, Objects.requireNonNull(tail))
} }
......
package net.mamoe.mirai.message
/**
* @author Him188moe
*/
open class MessageKey(
val intValue: Int
)
\ No newline at end of file
...@@ -2,7 +2,7 @@ package net.mamoe.mirai.message.defaults ...@@ -2,7 +2,7 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageKey
/** /**
* At 一个人 * At 一个人
...@@ -10,7 +10,9 @@ import net.mamoe.mirai.message.MessageId ...@@ -10,7 +10,9 @@ import net.mamoe.mirai.message.MessageId
* @author Him188moe * @author Him188moe
*/ */
class At(val target: Long) : Message() { class At(val target: Long) : Message() {
override val type: Int = MessageId.AT companion object Key : MessageKey(0x06)
override val type: MessageKey = Key
constructor(target: QQ) : this(target.number) constructor(target: QQ) : this(target.number)
...@@ -20,7 +22,9 @@ class At(val target: Long) : Message() { ...@@ -20,7 +22,9 @@ class At(val target: Long) : Message() {
TODO() TODO()
} }
override fun valueEquals(another: Message): Boolean { override operator fun contains(sub: String): Boolean = false
override fun eq(another: Message): Boolean {
if (another !is At) { if (another !is At) {
return false return false
} }
......
...@@ -2,7 +2,7 @@ package net.mamoe.mirai.message.defaults ...@@ -2,7 +2,7 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.FaceID import net.mamoe.mirai.message.FaceID
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageKey
import net.mamoe.mirai.network.packet.readLVNumber import net.mamoe.mirai.network.packet.readLVNumber
import net.mamoe.mirai.network.packet.writeHex import net.mamoe.mirai.network.packet.writeHex
import net.mamoe.mirai.network.packet.writeLVByteArray import net.mamoe.mirai.network.packet.writeLVByteArray
...@@ -15,14 +15,16 @@ import net.mamoe.mirai.utils.lazyEncode ...@@ -15,14 +15,16 @@ import net.mamoe.mirai.utils.lazyEncode
* @author Him188moe * @author Him188moe
*/ */
class Face(val id: FaceID) : Message() { class Face(val id: FaceID) : Message() {
override val type: Int = MessageId.FACE companion object Key : MessageKey(0x02)
override val type: MessageKey = Key
override fun toStringImpl(): String { override fun toStringImpl(): String {
return String.format("[face%d]", id.id) return String.format("[face%d]", id.id)
} }
override fun toByteArray(): ByteArray = lazyEncode { section -> override fun toByteArray(): ByteArray = lazyEncode { section ->
section.writeByte(this.type) section.writeByte(this.type.intValue)
section.writeLVByteArray(lazyEncode { child -> section.writeLVByteArray(lazyEncode { child ->
child.writeShort(1) child.writeShort(1)
...@@ -37,14 +39,16 @@ class Face(val id: FaceID) : Message() { ...@@ -37,14 +39,16 @@ class Face(val id: FaceID) : Message() {
} }
override fun valueEquals(another: Message): Boolean { override fun eq(another: Message): Boolean {
if (another !is Face) { if (another !is Face) {
return false return false
} }
return this.id == another.id return this.id == another.id
} }
companion object { override operator fun contains(sub: String): Boolean = false
internal object PacketHelper {
fun ofByteArray(data: ByteArray): Face = lazyDecode(data) { fun ofByteArray(data: ByteArray): Face = lazyDecode(data) {
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0 //00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D //00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D
......
...@@ -2,6 +2,7 @@ package net.mamoe.mirai.message.defaults ...@@ -2,6 +2,7 @@ package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageId
import net.mamoe.mirai.message.MessageKey
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.lazyDecode import net.mamoe.mirai.utils.lazyDecode
import net.mamoe.mirai.utils.lazyEncode import net.mamoe.mirai.utils.lazyEncode
...@@ -16,8 +17,10 @@ import net.mamoe.mirai.utils.toUHexString ...@@ -16,8 +17,10 @@ import net.mamoe.mirai.utils.toUHexString
* *
* @author Him188moe * @author Him188moe
*/ */
open class Image internal constructor(val imageId: String) : Message() { open class Image(val imageId: String) : Message() {
override val type: Int = MessageId.IMAGE companion object Key : MessageKey(0x03)
override val type: MessageKey = Key
override fun toStringImpl(): String { override fun toStringImpl(): String {
return imageId return imageId
...@@ -43,7 +46,7 @@ open class Image internal constructor(val imageId: String) : Message() { ...@@ -43,7 +46,7 @@ open class Image internal constructor(val imageId: String) : Message() {
}) })
} }
override fun valueEquals(another: Message): Boolean { override fun eq(another: Message): Boolean {
if (another is Image) { if (another is Image) {
return this.imageId == another.imageId return this.imageId == another.imageId
} }
...@@ -51,7 +54,9 @@ open class Image internal constructor(val imageId: String) : Message() { ...@@ -51,7 +54,9 @@ open class Image internal constructor(val imageId: String) : Message() {
return false return false
} }
companion object { override operator fun contains(sub: String): Boolean = false //No string can be contained in a image
internal object PacketHelper {
@JvmStatic @JvmStatic
fun ofByteArray0x06(data: ByteArray): Image = lazyDecode(data) { fun ofByteArray0x06(data: ByteArray): Image = lazyDecode(data) {
it.skip(1) it.skip(1)
......
package net.mamoe.mirai.message.defaults package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageKey
import net.mamoe.mirai.utils.lazyEncode import net.mamoe.mirai.utils.lazyEncode
import org.intellij.lang.annotations.MagicConstant
import java.util.* import java.util.*
import java.util.stream.Collectors import java.util.stream.Collectors
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.reflect.KClass
class MessageChain : Message { class MessageChain : Message {
override val type: Int = MessageId.CHAIN companion object Key : MessageKey(0xff)//only used to compare
override val type: MessageKey = Key
/**
* Elements will not be instances of [MessageChain]
*/
val list: MutableList<Message> = Collections.synchronizedList(LinkedList<Message>()) val list: MutableList<Message> = Collections.synchronizedList(LinkedList<Message>())
constructor(head: Message, tail: Message) { constructor(head: Message, tail: Message) {
...@@ -32,18 +37,19 @@ class MessageChain : Message { ...@@ -32,18 +37,19 @@ class MessageChain : Message {
constructor() constructor()
/**
* 获取第一个这个类型的消息
*/
operator fun get(type: MessageKey): Message? = list.firstOrNull { it.type == type }
fun size(): Int { fun size(): Int {
return list.size return list.size
} }
fun containsType(@MagicConstant(valuesFromClass = MessageId::class) type: Int): Boolean { fun containsType(clazz: KClass<out Message>): Boolean = list.any { clazz.isInstance(it) }
for (message in list) { fun containsType(clazz: Class<out Message>): Boolean = list.any { clazz.isInstance(it) }
if (message.type == type) { operator fun contains(sub: KClass<out Message>): Boolean = containsType(sub)
return true operator fun contains(sub: Class<out Message>): Boolean = containsType(sub)
}
}
return false
}
fun stream(): Stream<Message> { fun stream(): Stream<Message> {
return list.stream() return list.stream()
...@@ -58,6 +64,10 @@ class MessageChain : Message { ...@@ -58,6 +64,10 @@ class MessageChain : Message {
} }
override fun concat(tail: Message): MessageChain { override fun concat(tail: Message): MessageChain {
if (tail is MessageChain) {
tail.list.forEach { child -> this.concat(child) }
return this
}
this.list.add(tail) this.list.add(tail)
clearToStringCache() clearToStringCache()
return this return this
...@@ -73,10 +83,16 @@ class MessageChain : Message { ...@@ -73,10 +83,16 @@ class MessageChain : Message {
} }
} }
override fun valueEquals(another: Message): Boolean { override fun eq(another: Message): Boolean {
if (another !is MessageChain) { if (another !is MessageChain) {
return false return false
} }
return this.list == another.list return this.list == another.list
} }
override operator fun contains(sub: String): Boolean = list.any { it.contains(sub) }
operator fun component1(): Message = this.list[0]
operator fun component2(): Message = this.list[1]
operator fun component3(): Message = this.list[2]
} }
package net.mamoe.mirai.message.defaults package net.mamoe.mirai.message.defaults
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageId import net.mamoe.mirai.message.MessageKey
import net.mamoe.mirai.network.packet.readLVString import net.mamoe.mirai.network.packet.readLVString
import net.mamoe.mirai.network.packet.writeLVByteArray import net.mamoe.mirai.network.packet.writeLVByteArray
import net.mamoe.mirai.network.packet.writeLVString import net.mamoe.mirai.network.packet.writeLVString
...@@ -12,14 +12,16 @@ import net.mamoe.mirai.utils.lazyEncode ...@@ -12,14 +12,16 @@ import net.mamoe.mirai.utils.lazyEncode
* @author Him188moe * @author Him188moe
*/ */
class PlainText(private val text: String) : Message() { class PlainText(private val text: String) : Message() {
override val type: Int = MessageId.TEXT companion object Key : MessageKey(0x01)
override val type: MessageKey = Key
override fun toStringImpl(): String { override fun toStringImpl(): String {
return text return text
} }
override fun toByteArray(): ByteArray = lazyEncode { section -> override fun toByteArray(): ByteArray = lazyEncode { section ->
section.writeByte(this.type) section.writeByte(this.type.intValue)
section.writeLVByteArray(lazyEncode { child -> section.writeLVByteArray(lazyEncode { child ->
child.writeByte(0x01) child.writeByte(0x01)
...@@ -27,14 +29,16 @@ class PlainText(private val text: String) : Message() { ...@@ -27,14 +29,16 @@ class PlainText(private val text: String) : Message() {
}) })
} }
override fun valueEquals(another: Message): Boolean { override fun eq(another: Message): Boolean {
if (another !is PlainText) { if (another !is PlainText) {
return false return false
} }
return this.text == another.text return this.text == another.text
} }
companion object { override operator fun contains(sub: String): Boolean = this.toString().contains(sub)
internal object PacketHelper {
fun ofByteArray(data: ByteArray): PlainText = lazyDecode(data) { fun ofByteArray(data: ByteArray): PlainText = lazyDecode(data) {
it.skip(1) it.skip(1)
PlainText(it.readLVString()) PlainText(it.readLVString())
......
...@@ -19,13 +19,13 @@ import javax.imageio.ImageIO ...@@ -19,13 +19,13 @@ import javax.imageio.ImageIO
/** /**
* 不确定是否存在于服务器的 [Image]. * 不确定是否存在于服务器的 [Image].
* 必须在发送之前 [UnsolvedImage.upload] 或 [Contact.uploadImage], 否则会发送失败. * 必须在发送之前 [UnsolvedImage.upload] 或 [Contact.uploadImage], 否则会发送失败.
x * *
* @suppress todo 重新设计
* @author Him188moe * @author Him188moe
*/ */
class UnsolvedImage(filename: String, val image: BufferedImage) : Image(getImageId(filename)) { class UnsolvedImage(filename: String, val image: BufferedImage) : Image(getImageId(filename)) {
constructor(imageFile: File) : this(imageFile.name, ImageIO.read(imageFile)) constructor(imageFile: File) : this(imageFile.name, ImageIO.read(imageFile))
constructor(url: URL) : this(File(url.file)) constructor(url: URL) : this(File(url.file))
fun upload(session: LoginSession, contact: Contact): CompletableFuture<Unit> { fun upload(session: LoginSession, contact: Contact): CompletableFuture<Unit> {
return session.expectPacket<ServerTryGetImageIDResponsePacket> { return session.expectPacket<ServerTryGetImageIDResponsePacket> {
toSend { ClientTryGetImageIDPacket(session.bot.account.qqNumber, session.sessionKey, contact.number, image) } toSend { ClientTryGetImageIDPacket(session.bot.account.qqNumber, session.sessionKey, contact.number, image) }
......
package net.mamoe.mirai.network
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
object NetworkScope : CoroutineScope by CoroutineScope(Dispatchers.Default)
\ No newline at end of file
package net.mamoe.mirai.network.handler package net.mamoe.mirai.network.handler
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.network.LoginSession import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.action.AddFriendResult import net.mamoe.mirai.network.packet.action.AddFriendResult
...@@ -32,7 +33,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { ...@@ -32,7 +33,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
private var sKeyRefresherFuture: ScheduledFuture<*>? = null private var sKeyRefresherFuture: ScheduledFuture<*>? = null
override fun onPacketReceived(packet: ServerPacket) { override suspend fun onPacketReceived(packet: ServerPacket) {
when (packet) { when (packet) {
is ServerCanAddFriendResponsePacket -> { is ServerCanAddFriendResponsePacket -> {
this.uploadImageSessions.forEach { this.uploadImageSessions.forEach {
...@@ -60,7 +61,9 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { ...@@ -60,7 +61,9 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
session.cookies = "uin=o" + session.bot.account.qqNumber + ";skey=" + session.sKey + ";" session.cookies = "uin=o" + session.bot.account.qqNumber + ";skey=" + session.sKey + ";"
sKeyRefresherFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({ sKeyRefresherFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
session.socket.sendPacket(ClientSKeyRefreshmentRequestPacket(session.bot.account.qqNumber, session.sessionKey)) runBlocking {
session.socket.sendPacket(ClientSKeyRefreshmentRequestPacket(session.bot.account.qqNumber, session.sessionKey))
}
}, 1800000, 1800000, TimeUnit.MILLISECONDS) }, 1800000, 1800000, TimeUnit.MILLISECONDS)
session.gtk = getGTK(session.sKey) session.gtk = getGTK(session.sKey)
...@@ -75,13 +78,13 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { ...@@ -75,13 +78,13 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
} }
fun addFriend(qqNumber: Long, message: Supplier<String>) { suspend fun addFriend(qqNumber: Long, message: Supplier<String>) {
addFriend(qqNumber, lazy { message.get() }) addFriend(qqNumber, lazy { message.get() })
} }
@JvmSynthetic @JvmSynthetic
fun addFriend(qqNumber: Long, message: Lazy<String> = lazyOf("")): CompletableFuture<AddFriendResult> { suspend fun addFriend(qqNumber: Long, message: Lazy<String> = lazyOf("")): CompletableFuture<AddFriendResult> {
val future = CompletableFuture<AddFriendResult>() val future = CompletableFuture<AddFriendResult>()
val session = AddFriendSession(qqNumber, future, message) val session = AddFriendSession(qqNumber, future, message)
// uploadImageSessions.add(session) // uploadImageSessions.add(session)
...@@ -90,12 +93,12 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { ...@@ -90,12 +93,12 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
} }
fun requestSKey() { suspend fun requestSKey() {
session.socket.sendPacket(ClientSKeyRequestPacket(session.bot.account.qqNumber, session.sessionKey)) session.socket.sendPacket(ClientSKeyRequestPacket(session.bot.account.qqNumber, session.sessionKey))
} }
fun requestAccountInfo() { suspend fun requestAccountInfo() {
session.socket.sendPacket(ClientAccountInfoRequestPacket(session.bot.account.qqNumber, session.sessionKey)) session.socket.sendPacket(ClientAccountInfoRequestPacket(session.bot.account.qqNumber, session.sessionKey))
} }
...@@ -165,7 +168,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { ...@@ -165,7 +168,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
lateinit var id: ByteArray lateinit var id: ByteArray
fun onPacketReceived(packet: ServerPacket) { suspend fun onPacketReceived(packet: ServerPacket) {
if (!::id.isInitialized) { if (!::id.isInitialized) {
return return
} }
...@@ -202,7 +205,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { ...@@ -202,7 +205,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
} }
fun sendAddRequest() { suspend fun sendAddRequest() {
session.socket.sendPacket(ClientCanAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey).also { this.id = it.packetIdLast }) session.socket.sendPacket(ClientCanAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey).also { this.id = it.packetIdLast })
} }
......
...@@ -6,9 +6,7 @@ import net.mamoe.mirai.network.BotNetworkHandlerImpl ...@@ -6,9 +6,7 @@ import net.mamoe.mirai.network.BotNetworkHandlerImpl
import net.mamoe.mirai.network.LoginSession import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.ClientPacket import net.mamoe.mirai.network.packet.ClientPacket
import net.mamoe.mirai.network.packet.ServerPacket import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.task.MiraiThreadPool
import java.io.Closeable import java.io.Closeable
import java.util.concurrent.Future
/** /**
* 网络接口. * 网络接口.
...@@ -23,7 +21,7 @@ interface DataPacketSocket : Closeable { ...@@ -23,7 +21,7 @@ interface DataPacketSocket : Closeable {
/** /**
* 分发数据包给 [PacketHandler] * 分发数据包给 [PacketHandler]
*/ */
fun distributePacket(packet: ServerPacket) suspend fun distributePacket(packet: ServerPacket)
/** /**
* 发送一个数据包(非异步). * 发送一个数据包(非异步).
...@@ -32,20 +30,7 @@ interface DataPacketSocket : Closeable { ...@@ -32,20 +30,7 @@ interface DataPacketSocket : Closeable {
* *
* @see [LoginSession.expectPacket] kotlin DSL * @see [LoginSession.expectPacket] kotlin DSL
*/ */
fun sendPacket(packet: ClientPacket) suspend fun sendPacket(packet: ClientPacket)
/**
* 发送一个数据包(异步).
*
* 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
*
* @see [LoginSession.expectPacket] kotlin DSL
*/
fun sendPacketAsync(packet: ClientPacket): Future<*> {
return MiraiThreadPool.getInstance().submit {
sendPacket(packet)
}
}
fun isClosed(): Boolean fun isClosed(): Boolean
......
...@@ -4,10 +4,7 @@ import net.mamoe.mirai.contact.Group ...@@ -4,10 +4,7 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.events.group.GroupMessageEvent import net.mamoe.mirai.event.events.group.GroupMessageEvent
import net.mamoe.mirai.event.events.qq.FriendMessageEvent import net.mamoe.mirai.event.events.qq.FriendMessageEvent
import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.message.defaults.Image
import net.mamoe.mirai.message.defaults.MessageChain import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.message.defaults.PlainText
import net.mamoe.mirai.network.LoginSession import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.ServerFriendMessageEventPacket import net.mamoe.mirai.network.packet.ServerFriendMessageEventPacket
import net.mamoe.mirai.network.packet.ServerGroupMessageEventPacket import net.mamoe.mirai.network.packet.ServerGroupMessageEventPacket
...@@ -27,49 +24,7 @@ import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacke ...@@ -27,49 +24,7 @@ import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacke
class MessagePacketHandler(session: LoginSession) : PacketHandler(session) { class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
internal var ignoreMessage: Boolean = true internal var ignoreMessage: Boolean = true
init { override suspend fun onPacketReceived(packet: ServerPacket) {
//todo for test
FriendMessageEvent::class.hookWhile {
if (session.socket.isClosed()) {
return@hookWhile false
}
when {
it.message valueEquals "你好" -> it.sender.sendMessage("你好!")
it.message.toString().startsWith("复读") -> it.sender.sendMessage(it.message())
it.message.toString().startsWith("发群") -> {
it.message().list.toMutableList().let { messages ->
messages.removeAt(0)
sendGroupMessage(Group(session.bot, 580266363), MessageChain(messages))
}
}
/*it.message valueEquals "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, Group(session.bot, 580266363)).get()
})*/
it.message valueEquals "发图片群2" -> sendGroupMessage(Group(session.bot, 580266363), Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg").toChain())
/* it.message valueEquals "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.sender).get()
})*/
it.message valueEquals "发图片2" -> sendFriendMessage(it.sender, PlainText("test") + Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"))
}
return@hookWhile true
}
GroupMessageEvent::class.hookWhile {
if (session.socket.isClosed()) {
return@hookWhile false
}
when {
it.message.contains("复读") -> it.group.sendMessage(it.chain)
}
return@hookWhile true
}
}
override fun onPacketReceived(packet: ServerPacket) {
when (packet) { when (packet) {
is ServerGroupUploadFileEventPacket -> { is ServerGroupUploadFileEventPacket -> {
//todo //todo
...@@ -99,11 +54,11 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) { ...@@ -99,11 +54,11 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
} }
} }
fun sendFriendMessage(qq: QQ, message: MessageChain) { suspend fun sendFriendMessage(qq: QQ, message: MessageChain) {
session.socket.sendPacketAsync(ClientSendFriendMessagePacket(session.bot.account.qqNumber, qq.number, session.sessionKey, message)) session.socket.sendPacket(ClientSendFriendMessagePacket(session.bot.account.qqNumber, qq.number, session.sessionKey, message))
} }
fun sendGroupMessage(group: Group, message: MessageChain) { suspend fun sendGroupMessage(group: Group, message: MessageChain) {
session.socket.sendPacket(ClientSendGroupMessagePacket(session.bot.account.qqNumber, group.groupId, session.sessionKey, message)) session.socket.sendPacket(ClientSendGroupMessagePacket(session.bot.account.qqNumber, group.groupId, session.sessionKey, message))
} }
} }
\ No newline at end of file
...@@ -11,7 +11,7 @@ import java.io.Closeable ...@@ -11,7 +11,7 @@ import java.io.Closeable
abstract class PacketHandler( abstract class PacketHandler(
val session: LoginSession val session: LoginSession
) : Closeable { ) : Closeable {
abstract fun onPacketReceived(packet: ServerPacket) abstract suspend fun onPacketReceived(packet: ServerPacket)
override fun close() { override fun close() {
......
...@@ -32,7 +32,7 @@ open class TemporaryPacketHandler<P : ServerPacket>( ...@@ -32,7 +32,7 @@ open class TemporaryPacketHandler<P : ServerPacket>(
this.expect = handler this.expect = handler
} }
fun send(session: LoginSession) { suspend fun send(session: LoginSession) {
this.session = session this.session = session
session.socket.sendPacket(toSend) session.socket.sendPacket(toSend)
} }
......
...@@ -144,12 +144,6 @@ fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, lo ...@@ -144,12 +144,6 @@ fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, lo
} }
} }
fun main() {
println(lazyEncode {
it.writeTLV0006(1040400290, "asdHim188moe", System.currentTimeMillis().toInt(), "123.123.123.123", getRandomByteArray(56))
}.size)
}
@Tested @Tested
fun DataOutputStream.writeCRC32() = writeCRC32(getRandomByteArray(16)) fun DataOutputStream.writeCRC32() = writeCRC32(getRandomByteArray(16))
......
...@@ -263,10 +263,10 @@ private fun DataInputStream.readSection(): Message? { ...@@ -263,10 +263,10 @@ private fun DataInputStream.readSection(): Message? {
val sectionLength = this.readShort().toLong()//sectionLength: short val sectionLength = this.readShort().toLong()//sectionLength: short
val sectionData = this.readNBytes(sectionLength) val sectionData = this.readNBytes(sectionLength)
return when (messageType) { return when (messageType) {
0x01 -> PlainText.ofByteArray(sectionData) 0x01 -> PlainText.PacketHelper.ofByteArray(sectionData)
0x02 -> Face.ofByteArray(sectionData) 0x02 -> Face.PacketHelper.ofByteArray(sectionData)
0x03 -> Image.ofByteArray0x03(sectionData) 0x03 -> Image.PacketHelper.ofByteArray0x03(sectionData)
0x06 -> Image.ofByteArray0x06(sectionData) 0x06 -> Image.PacketHelper.ofByteArray0x06(sectionData)
0x19 -> {//长文本 0x19 -> {//长文本
......
...@@ -111,7 +111,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { ...@@ -111,7 +111,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"00 58" -> ServerHeartbeatResponsePacket(stream) "00 58" -> ServerHeartbeatResponsePacket(stream)
"00 BA" -> ServerVerificationCodePacket.Encrypted(stream, idHex) "00 BA" -> ServerCatchaPacket.Encrypted(stream, idHex)
"00 CE", "00 17" -> ServerEventPacket.Raw.Encrypted(stream, idHex.hexToBytes()) "00 CE", "00 17" -> ServerEventPacket.Raw.Encrypted(stream, idHex.hexToBytes())
......
...@@ -126,7 +126,7 @@ class ClientVerificationCodeRefreshPacket( ...@@ -126,7 +126,7 @@ class ClientVerificationCodeRefreshPacket(
* 验证码输入错误, 同时也会给一部分验证码 * 验证码输入错误, 同时也会给一部分验证码
*/ */
@PacketId("00 BA 32") @PacketId("00 BA 32")
class ServerVerificationCodeWrongPacket(input: DataInputStream, dataSize: Int, packetId: ByteArray) : ServerVerificationCodeTransmissionPacket(input, dataSize, packetId) class ServerCaptchaWrongPacket(input: DataInputStream, dataSize: Int, packetId: ByteArray) : ServerCaptchaTransmissionPacket(input, dataSize, packetId)
/** /**
* 服务器发送验证码图片文件一部分过来 * 服务器发送验证码图片文件一部分过来
...@@ -134,7 +134,7 @@ class ServerVerificationCodeWrongPacket(input: DataInputStream, dataSize: Int, p ...@@ -134,7 +134,7 @@ class ServerVerificationCodeWrongPacket(input: DataInputStream, dataSize: Int, p
* @author Him188moe * @author Him188moe
*/ */
@PacketId("00 BA 31") @PacketId("00 BA 31")
open class ServerVerificationCodeTransmissionPacket(input: DataInputStream, private val dataSize: Int, private val packetId: ByteArray) : ServerVerificationCodePacket(input) { open class ServerCaptchaTransmissionPacket(input: DataInputStream, private val dataSize: Int, private val packetId: ByteArray) : ServerCatchaPacket(input) {
lateinit var captchaSectionN: ByteArray lateinit var captchaSectionN: ByteArray
lateinit var verificationToken: ByteArray//56bytes lateinit var verificationToken: ByteArray//56bytes
...@@ -177,7 +177,7 @@ fun main() { ...@@ -177,7 +177,7 @@ fun main() {
* @author Him188moe * @author Him188moe
*/ */
@PacketId("00 BA 32") @PacketId("00 BA 32")
class ServerVerificationCodeCorrectPacket(input: DataInputStream) : ServerVerificationCodePacket(input) { class ServerCaptchaCorrectPacket(input: DataInputStream) : ServerCatchaPacket(input) {
lateinit var token00BA: ByteArray//56 bytes lateinit var token00BA: ByteArray//56 bytes
...@@ -187,24 +187,24 @@ class ServerVerificationCodeCorrectPacket(input: DataInputStream) : ServerVerifi ...@@ -187,24 +187,24 @@ class ServerVerificationCodeCorrectPacket(input: DataInputStream) : ServerVerifi
} }
} }
abstract class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input) { abstract class ServerCatchaPacket(input: DataInputStream) : ServerPacket(input) {
@PacketId("00 BA") @PacketId("00 BA")
class Encrypted(input: DataInputStream, private val id: String) : ServerPacket(input) { class Encrypted(input: DataInputStream, private val id: String) : ServerPacket(input) {
fun decrypt(): ServerVerificationCodePacket { fun decrypt(): ServerCatchaPacket {
this.input goto 14 this.input goto 14
val data = TEA.decrypt(this.input.readAllBytes().cutTail(1), Protocol.key00BA.hexToBytes()) val data = TEA.decrypt(this.input.readAllBytes().cutTail(1), Protocol.key00BA.hexToBytes())
if (id.startsWith("00 BA 32")) { if (id.startsWith("00 BA 32")) {
return when (data.size) { return when (data.size) {
66, 66,
95 -> ServerVerificationCodeCorrectPacket(data.dataInputStream()) 95 -> ServerCaptchaCorrectPacket(data.dataInputStream())
//66 -> ServerVerificationCodeUnknownPacket(data.dataInputStream()) //66 -> ServerVerificationCodeUnknownPacket(data.dataInputStream())
else -> return ServerVerificationCodeWrongPacket(data.dataInputStream(), data.size, this.input.readNBytesAt(3, 4)) else -> return ServerCaptchaWrongPacket(data.dataInputStream(), data.size, this.input.readNBytesAt(3, 4))
}.setId(this.idHex) }.setId(this.idHex)
} }
return ServerVerificationCodeTransmissionPacket(data.dataInputStream(), data.size, this.input.readNBytesAt(3, 4)).setId(this.idHex) return ServerCaptchaTransmissionPacket(data.dataInputStream(), data.size, this.input.readNBytesAt(3, 4)).setId(this.idHex)
} }
override fun getFixedId(): String = this.getFixedId(id) override fun getFixedId(): String = this.getFixedId(id)
......
package net.mamoe.mirai.utils;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class BotAccount {
public final long qqNumber;
private final String password;
}
package net.mamoe.mirai.utils
/**
* @author Him188moe
*/
data class BotAccount(
val qqNumber: Long,
val password: String
)
\ No newline at end of file
package net.mamoe.mirai.utils.config; package net.mamoe.mirai.utils.config;
import kotlin.io.FilesKt; import kotlin.io.FilesKt;
import net.mamoe.mirai.MiraiServer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
...@@ -24,10 +23,6 @@ public class MiraiConfig extends MiraiConfigSection<Object> { ...@@ -24,10 +23,6 @@ public class MiraiConfig extends MiraiConfigSection<Object> {
private final File root; private final File root;
public MiraiConfig(@NotNull String configName) {
this(new File(MiraiServer.getInstance().getParentFolder(), Objects.requireNonNull(configName)));
}
public MiraiConfig(@NotNull File file) { public MiraiConfig(@NotNull File file) {
super(parse(Objects.requireNonNull(file))); super(parse(Objects.requireNonNull(file)));
this.root = file; this.root = file;
...@@ -47,9 +42,10 @@ public class MiraiConfig extends MiraiConfigSection<Object> { ...@@ -47,9 +42,10 @@ public class MiraiConfig extends MiraiConfigSection<Object> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static LinkedHashMap<String,Object> parse(File file) { private static LinkedHashMap<String,Object> parse(File file) {
/*
if (!file.toURI().getPath().contains(MiraiServer.getInstance().getParentFolder().getPath())) { if (!file.toURI().getPath().contains(MiraiServer.getInstance().getParentFolder().getPath())) {
file = new File(MiraiServer.getInstance().getParentFolder().getPath(), file.getName()); file = new File(MiraiServer.getInstance().getParentFolder().getPath(), file.getName());
} }*/
if (!file.exists()) { if (!file.exists()) {
try { try {
if (!file.createNewFile()) { if (!file.createNewFile()) {
......
...@@ -74,7 +74,7 @@ fun main() { ...@@ -74,7 +74,7 @@ fun main() {
return@let password.substring(0, password.length - 1) return@let password.substring(0, password.length - 1)
} }
return@let password return@let password
}), listOf()) }))
bot.network.tryLogin().whenComplete { state, _ -> bot.network.tryLogin().whenComplete { state, _ ->
if (!(state == LoginState.BLOCKED || state == LoginState.DEVICE_LOCK || state == LoginState.WRONG_PASSWORD)) { if (!(state == LoginState.BLOCKED || state == LoginState.DEVICE_LOCK || state == LoginState.WRONG_PASSWORD)) {
......
import net.mamoe.mirai.MiraiServer;
import net.mamoe.mirai.utils.config.MiraiConfig;
public class ConfigTest {
public static void main(String[] args){
MiraiConfig config = new MiraiConfig("QQ.yml");
}
}
...@@ -40,7 +40,7 @@ object Main { ...@@ -40,7 +40,7 @@ object Main {
println("-------------------------------------------") println("-------------------------------------------")
exitProcess(0)*/ exitProcess(0)*/
var jpcap: JpcapCaptor? = null val jpcap: JpcapCaptor?
val caplen = 4096 val caplen = 4096
val promiscCheck = true val promiscCheck = true
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai-demos</artifactId>
<version>1.0</version>
</parent>
<artifactId>mirai-demo-1</artifactId>
<version>1.0</version>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package demo1
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.event.events.group.GroupMessageEvent
import net.mamoe.mirai.event.events.qq.FriendMessageEvent
import net.mamoe.mirai.event.hookAlways
import net.mamoe.mirai.message.defaults.Image
import net.mamoe.mirai.message.defaults.PlainText
import net.mamoe.mirai.network.packet.login.LoginState
import net.mamoe.mirai.utils.BotAccount
/**
* @author Him188moe
*/
fun main() {
val bot = Bot(BotAccount(
qqNumber = 1683921395,
password = "bb22222"
))
bot.network.tryLogin().get().let {
check(it == LoginState.SUCCESS) { "Login failed: " + it.name }
}
//监听事件:
FriendMessageEvent::class.hookAlways {
//获取第一个纯文本消息
val firstText = it.message[PlainText]
//获取第一个图片
val firstImage = it.message[Image]
when {
it.message eq "你好" -> it.reply("你好!")
"复读" in it.message -> it.sender.sendMessage(it.message)
"发群" in it.message -> {
it.message.list.toMutableList().let { messages ->
messages.removeAt(0)
Group(bot, 580266363).sendMessage(messages)
}
}
/*it.message eq "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, Group(session.bot, 580266363)).get()
})*/
it.message eq "发图片群2" -> Group(bot, 580266363).sendMessage(Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"))
/* it.message eq "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.sender).get()
})*/
it.message eq "发图片2" -> it.reply(PlainText("test") + Image("{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"))
}
}
GroupMessageEvent::class.hookAlways {
when {
it.message.contains("复读") -> it.reply(it.message)
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai</artifactId>
<version>1.0</version>
</parent>
<artifactId>mirai-demos</artifactId>
<version>1.0</version>
<modules>
<module>mirai-demo-1</module>
</modules>
<dependencies>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-core</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
<module>mirai-ui</module> <module>mirai-ui</module>
<module>mirai-console</module> <module>mirai-console</module>
<module>mirai-api</module> <module>mirai-api</module>
<module>mirai-demos</module>
</modules> </modules>
<repositories> <repositories>
......
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