Updated events and KDocs

This commit is contained in:
Him188moe 2019-09-06 22:00:16 +08:00
parent 402e8fbb32
commit 305a7a6f73
36 changed files with 467 additions and 219 deletions

View File

@ -2,22 +2,25 @@ package net.mamoe.mirai;
import lombok.Getter;
import net.mamoe.mirai.event.MiraiEventManager;
import net.mamoe.mirai.event.events.server.ServerDisableEvent;
import net.mamoe.mirai.event.events.server.ServerEnableEvent;
import net.mamoe.mirai.event.events.server.ServerDisabledEvent;
import net.mamoe.mirai.event.events.server.ServerEnabledEvent;
import net.mamoe.mirai.network.packet.login.LoginState;
import net.mamoe.mirai.task.MiraiTaskManager;
import net.mamoe.mirai.utils.LoggerTextFormat;
import net.mamoe.mirai.utils.MiraiLogger;
import net.mamoe.mirai.utils.config.MiraiConfig;
import net.mamoe.mirai.utils.config.MiraiConfigSection;
import net.mamoe.mirai.utils.setting.MiraiSetting;
import net.mamoe.mirai.utils.setting.MiraiSettingListSection;
import net.mamoe.mirai.utils.setting.MiraiSettingMapSection;
import net.mamoe.mirai.utils.setting.MiraiSettings;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
/**
* @author NaturalHG
*/
public class MiraiServer {
private static MiraiServer instance;
@ -25,10 +28,8 @@ public class MiraiServer {
return instance;
}
//mirai version
private final static String MIRAI_VERSION = "1.0.0";
//qq version
private final static String QQ_VERSION = "4.9.0";
@ -46,30 +47,30 @@ public class MiraiServer {
@Getter
MiraiLogger logger;
MiraiSetting setting;
MiraiSettings settings;
MiraiConfig qqs;
protected MiraiServer() {
MiraiServer() {
instance = this;
this.onLoad();
this.onEnable();
this.onLoaded();
this.onEnabled();
}
private boolean enabled;
protected void shutdown() {
void shutdown() {
if (this.enabled) {
getLogger().info("About to shutdown Mirai");
this.getEventManager().broadcastEvent(new ServerDisableEvent());
this.eventManager.broadcastEventAsync(new ServerDisabledEvent());
getLogger().info("Data have been saved");
}
}
private void onLoad() {
private void onLoaded() {
this.parentFolder = new File(System.getProperty("user.dir"));
this.unix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS");
@ -86,7 +87,7 @@ public class MiraiServer {
if (!setting.exists()) {
this.initSetting(setting);
} else {
this.setting = new MiraiSetting(setting);
this.settings = new MiraiSettings(setting);
}
File qqs = new File(this.parentFolder + "/QQ.yml");
@ -114,27 +115,6 @@ public class MiraiServer {
});
*/
getLogger().info("ready to connect");
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
try {
Robot robot = new Robot(section);
robot.network.tryLogin$mirai_core((robot1, state) -> {
if (state == LoginState.SUCCEED) {
Robot.instances.add(robot);
} else {
robot.close();
}
return null;
});
} catch (Throwable e) {
e.printStackTrace();
getLogger().error("Could not load QQ robots config!");
System.exit(1);
}
});
}
private void initSetting(File setting) {
@ -147,21 +127,21 @@ public class MiraiServer {
} catch (IOException e) {
e.printStackTrace();
}
this.setting = new MiraiSetting(setting);
MiraiSettingMapSection network = this.setting.getMapSection("network");
this.settings = new MiraiSettings(setting);
MiraiSettingMapSection network = this.settings.getMapSection("network");
network.set("enable_proxy", "not supporting yet");
MiraiSettingListSection proxy = this.setting.getListSection("proxy");
MiraiSettingListSection proxy = this.settings.getListSection("proxy");
proxy.add("1.2.3.4:95");
proxy.add("1.2.3.4:100");
MiraiSettingMapSection worker = this.setting.getMapSection("worker");
MiraiSettingMapSection worker = this.settings.getMapSection("worker");
worker.set("core_task_pool_worker_amount", 5);
MiraiSettingMapSection plugin = this.setting.getMapSection("plugin");
MiraiSettingMapSection plugin = this.settings.getMapSection("plugin");
plugin.set("debug", false);
this.setting.save();
this.settings.save();
getLogger().info("initialized; changing can be made in setting file: " + setting.toString());
}
@ -187,11 +167,35 @@ public class MiraiServer {
getLogger().info("QQ account initialized; changing can be made in Config file: " + qqConfig.toString());
}
private void onEnable() {
this.eventManager.broadcastEvent(new ServerEnableEvent());
private void onEnabled() {
this.enabled = true;
this.eventManager.broadcastEventAsync(new ServerEnabledEvent());
getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai");
getLogger().info("Mirai Version=" + MiraiServer.MIRAI_VERSION + " QQ Version=" + MiraiServer.QQ_VERSION);
getLogger().info("Initializing [Robot]s");
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
getLogger().info("Initializing [Robot] " + section.getString("account"));
try {
Robot robot = new Robot(section);
var state = robot.network.tryLogin$mirai_core().get();
//robot.network.tryLogin$mirai_core().whenComplete((state, e) -> {
if (state == LoginState.SUCCEED) {
Robot.instances.add(robot);
getLogger().info(" Succeed");
} else {
getLogger().error(" Failed with error " + state);
robot.close();
}
// }).get();
} catch (Throwable e) {
e.printStackTrace();
getLogger().error("Could not load QQ robots config!");
System.exit(1);
}
});
}

View File

@ -13,6 +13,21 @@ import java.io.Closeable;
import java.util.*;
/**
* Mirai 的机器人. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人账号.
* <br>
* {@link Robot} 2 个模块组成.
* {@linkplain ContactSystem 联系人管理}: 可通过 {@link Robot#contacts} 访问
* {@linkplain RobotNetworkHandler 网络处理器}: 可通过 {@link Robot#network} 访问
* <br>
* 另外地, 若你需要得到机器人的 QQ 账号, 请访问 {@link Robot#account}
* 若你需要得到服务器上所有机器人列表, 请访问 {@link Robot#instances}
*
* @author Him188moe
* @author NatrualHG
* @see net.mamoe.mirai.contact.Contact
*
* <p>
* Robot that is the base of the whole program.
* It contains a {@link ContactSystem}, which manage contacts such as {@link QQ} and {@link Group}.
*/

View File

@ -5,8 +5,11 @@ import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.At
/**
* QQ 账号.
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Robot].
*
* A QQ instance helps you to receive message from or send message to.
* Notice that one QQ instance belong to one [Robot], that is, QQ instances from different [Robot] are NOT the same.
* Notice that, one QQ instance belong to one [Robot], that is, QQ instances from different [Robot] are NOT the same.
*
* @author Him188moe
*/
@ -21,6 +24,8 @@ class QQ(robot: Robot, number: Long) : Contact(robot, number) {
/**
* At(@) this account.
*
* @return an instance of [Message].
*/
fun at(): At {
return At(this)

View File

@ -0,0 +1,27 @@
package net.mamoe.mirai.event;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/**
* 实现这个接口的事件可以被异步执行或阻塞执行
*
* @author Him188moe
* @see AsyncEventKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现
*/
public interface AsyncEvent {
default CompletableFuture<? extends AsyncEvent> broadcastAsync() {
return MiraiEventManager.getInstance().broadcastEventAsync(this);
}
@SuppressWarnings("unchecked")
default <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(Consumer<E> callback) {
return MiraiEventManager.getInstance().broadcastEventAsync((E) this, callback);
}
@SuppressWarnings("unchecked")
default <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(Runnable callback) {
return MiraiEventManager.getInstance().broadcastEventAsync((E) this, callback);
}
}

View File

@ -0,0 +1,14 @@
@file:JvmName("AsyncEventKt")
package net.mamoe.mirai.event
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
fun <E : AsyncEvent> E.broadcastAsync(callback: Consumer<E>): CompletableFuture<E> {
return MiraiEventManager.getInstance().broadcastEventAsync(this, callback)
}
fun <E : AsyncEvent> E.broadcastAsync(callback: Runnable): CompletableFuture<out AsyncEvent> {
return MiraiEventManager.getInstance().broadcastEventAsync(this, callback)
}

View File

@ -1,4 +1,4 @@
package net.mamoe.mirai.event.events;
package net.mamoe.mirai.event;
/**
* @author NaturalHG

View File

@ -0,0 +1,35 @@
package net.mamoe.mirai.event;
import net.mamoe.mirai.utils.EventException;
/**
* @author NatrualHG
* @see AsyncEvent
*/
public abstract class MiraiEvent {
private boolean cancelled;
public boolean isCancelled() {
if (!(this instanceof Cancellable)) {
return false;
}
return this.cancelled;
}
public void cancel() {
cancel(true);
}
public void cancel(boolean value) {
if (!(this instanceof Cancellable)) {
throw new EventException("Event is not Cancellable");
}
this.cancelled = value;
}
public final MiraiEvent broadcast() {
MiraiEventManager.getInstance().broadcastEvent(this);
return this;
}
}

View File

@ -2,20 +2,21 @@ package net.mamoe.mirai.event;
import lombok.Getter;
import lombok.Setter;
import net.mamoe.mirai.event.events.Cancellable;
import net.mamoe.mirai.event.events.MiraiEvent;
import java.io.Closeable;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* @author NatrualHG
*/
public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
@Getter
Class<T> eventClass;
@Getter
private volatile Consumer<T> handler;
protected volatile Consumer<T> handler;
@Getter
private volatile int priority = 0;
@ -31,7 +32,7 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
* return true -> this hook need to be removed
*/
@Getter
private Predicate<T> valid;
protected Predicate<T> validChecker;
public MiraiEventHook(Class<T> eventClass) {
this(eventClass,null);
@ -58,26 +59,26 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
}
private MiraiEventHook<T> setValid(Predicate<T> valid) {
this.valid = valid;
private MiraiEventHook<T> setValidChecker(Predicate<T> validChecker) {
this.validChecker = validChecker;
return this;
}
public MiraiEventHook<T> setValidUntil(Predicate<T> valid) {
return this.setValid(valid);
return this.setValidChecker(valid);
}
public MiraiEventHook<T> setValidWhile(Predicate<T> valid) {
return this.setValid(valid.negate());
return this.setValidChecker(valid.negate());
}
@SuppressWarnings("unchecked")
public boolean accept(MiraiEvent event) {
if(!(event instanceof Cancellable && event.isCancelled() && this.isIgnoreCancelled())){
this.getHandler().accept((T) event);
this.getHandler().accept((T) event);
}
return this.valid == null || this.valid.test((T) event);
return this.validChecker == null || this.validChecker.test((T) event);
}
/**
@ -103,6 +104,6 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
@Override
public void close(){
this.handler = null;
this.valid = null;
this.validChecker = null;
}
}

View File

@ -0,0 +1,31 @@
package net.mamoe.mirai.event
import java.util.function.Consumer
import java.util.function.Predicate
/**
* @author Him188moe
*/
class MiraiEventHookKt<E : MiraiEvent>(eventClass: Class<E>) : MiraiEventHook<E>(eventClass) {
fun onEvent(handler: (E) -> Unit) {
this@MiraiEventHookKt.handler = Consumer(handler)
}
fun validChecker(predicate: (E) -> Boolean) {
this@MiraiEventHookKt.validChecker = Predicate(predicate)
}
}
/**
* Kotlin 风格回调
* 你的代码可以这样(并且 validChecker 是可选的):
*
* event.hook {
* onEvent {}
* validChecker {}
* }
*/
fun <E : MiraiEvent> E.hook(handler: MiraiEventHookKt<E>.() -> Unit): MiraiEventHookKt<E> {
return MiraiEventHookKt(this.javaClass).apply(handler)
}

View File

@ -0,0 +1,8 @@
@file:JvmName("MiraiEventKt")
package net.mamoe.mirai.event
fun <E : MiraiEvent> E.broadcast(): E {
MiraiEventManager.getInstance().broadcastEvent(this as MiraiEvent)
return this
}

View File

@ -1,21 +1,25 @@
package net.mamoe.mirai.event;
import net.mamoe.mirai.MiraiServer;
import net.mamoe.mirai.event.events.MiraiEvent;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 线程安全的事件管理器.
*
* @author NaturalHG
* @see MiraiEventManagerKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现
*/
public class MiraiEventManager {
MiraiEventManager() {
}
public static MiraiEventManager getInstance() {
return EventManager.INSTANCE;
return EventManager.INSTANCE;//实例来自 kotlin singleton
}
private final ReentrantReadWriteLock hooksLock = new ReentrantReadWriteLock();
@ -115,23 +119,40 @@ public class MiraiEventManager {
}
public void asyncBroadcastEvent(MiraiEvent event) {
this.asyncBroadcastEvent(event, a -> {
});
}
public <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(E event) {
Objects.requireNonNull(event);
if (!(event instanceof MiraiEvent)) {
throw new IllegalArgumentException("event must be instanceof MiraiEvent");
}
public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Consumer<D> callback) {
MiraiServer.getInstance().getTaskManager().ansycTask(() -> {
MiraiEventManager.this.broadcastEvent(event);
CompletableFuture<E> future = new CompletableFuture<>();
future.completeAsync(() -> {
MiraiEventManager.this.broadcastEvent((MiraiEvent) event);
return event;
}, callback);
});
return future;
}
public <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(E event, Consumer<E> callback) {
Objects.requireNonNull(event);
Objects.requireNonNull(callback);
if (!(event instanceof MiraiEvent)) {
throw new IllegalArgumentException("event must be instanceof MiraiEvent");
}
CompletableFuture<E> future = new CompletableFuture<>();
future.whenComplete((a, b) -> callback.accept(event));
future.completeAsync(() -> {
MiraiEventManager.this.broadcastEvent((MiraiEvent) event);
return event;
});
return future;
}
public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Runnable callback) {
asyncBroadcastEvent(event, t -> callback.run());
public <D extends AsyncEvent> CompletableFuture<D> broadcastEventAsync(D event, Runnable callback) {
return broadcastEventAsync(event, t -> callback.run());
}
}

View File

@ -1,33 +1,59 @@
@file:JvmName("MiraiEventManagerKt")
package net.mamoe.mirai.event
import net.mamoe.mirai.event.events.MiraiEvent
import kotlin.reflect.KClass
object EventManager : MiraiEventManager()
typealias MiraiEventManagerKt = EventManager
typealias EventMgr = EventManager
/**
* [MiraiEventManager] kotlin 简易化实现.
* 若要 hook 一个事件, 你可以:
* FriendMessageEvent::class.hookOnce {}
* FriendMessageEvent::class.hookAlways {}
*
* @author Him188moe
*/
object EventManager : MiraiEventManager()
/**
* 每次事件触发时都会调用 hook
*/
fun <C : Class<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook))
}
/**
* 当下一次事件触发时调用 hook
*/
fun <C : Class<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) {
MiraiEventManager.getInstance().hookOnce(MiraiEventHook<E>(this, hook))
}
/**
* 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook
*/
fun <C : Class<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHookSimple<E>(this, hook))
MiraiEventManager.getInstance().hookAlways(MiraiEventHookSimple(this, hook))
}
/**
* 每次事件触发时都会调用 hook
*/
fun <C : KClass<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
this.java.hookAlways(hook)
}
/**
* 当下一次事件触发时调用 hook
*/
fun <C : KClass<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) {
this.java.hookOnce(hook)
}
/**
* 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook
*/
fun <C : KClass<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) {
this.java.hookWhile(hook)
}

View File

@ -1,53 +0,0 @@
package net.mamoe.mirai.event.events;
import net.mamoe.mirai.event.MiraiEventManager;
import net.mamoe.mirai.utils.EventException;
import java.util.function.Consumer;
public abstract class MiraiEvent {
private boolean cancelled;
public boolean isCancelled() {
if (!(this instanceof Cancellable)) {
return false;
}
return this.cancelled;
}
public void cancel() {
cancel(true);
}
public void cancel(boolean value) {
if (!(this instanceof Cancellable)) {
throw new EventException("Event is not Cancellable");
}
this.cancelled = value;
}
protected String eventName;
public String getEventName() {
if (this.eventName == null) {
return this.getClass().getSimpleName();
}
return this.eventName;
}
public final MiraiEvent broadcast() {
MiraiEventManager.getInstance().broadcastEvent(this);
return this;
}
@SuppressWarnings("unchecked")
public final <D extends MiraiEvent> void asyncBroadcast(Consumer<D> callback) {
MiraiEventManager.getInstance().asyncBroadcastEvent((D) this, callback);
}
@SuppressWarnings("unchecked")
public final <D extends MiraiEvent> void asyncBroadcast(Runnable callback) {
MiraiEventManager.getInstance().asyncBroadcastEvent((D) this, callback);
}
}

View File

@ -0,0 +1,14 @@
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.Cancellable;
import net.mamoe.mirai.network.packet.ClientPacket;
import org.jetbrains.annotations.NotNull;
/**
* @author Him188moe
*/
public final class BeforePacketSendEvent extends ClientPacketEvent implements Cancellable {
public BeforePacketSendEvent(@NotNull ClientPacket packet) {
super(packet);
}
}

View File

@ -0,0 +1,18 @@
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.network.packet.ClientPacket;
import org.jetbrains.annotations.NotNull;
/**
* @author Him188moe
*/
public abstract class ClientPacketEvent extends PacketEvent {
public ClientPacketEvent(@NotNull ClientPacket packet) {
super(packet);
}
@Override
public ClientPacket getPacket() {
return (ClientPacket) super.getPacket();
}
}

View File

@ -1,7 +1,10 @@
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.events.MiraiEvent;
import net.mamoe.mirai.event.MiraiEvent;
import net.mamoe.mirai.network.packet.Packet;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* @author Him188moe
@ -9,8 +12,8 @@ import net.mamoe.mirai.network.packet.Packet;
public abstract class PacketEvent extends MiraiEvent {
private final Packet packet;
public PacketEvent(Packet packet) {
this.packet = packet;
public PacketEvent(@NotNull Packet packet) {
this.packet = Objects.requireNonNull(packet);
}
public Packet getPacket() {

View File

@ -1,7 +0,0 @@
package net.mamoe.mirai.event.events.network;
/**
* @author Him188moe
*/
public class PacketReceivedEvent {
}

View File

@ -1,12 +1,16 @@
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.events.Cancellable;
import net.mamoe.mirai.event.Cancellable;
import net.mamoe.mirai.network.packet.ServerPacket;
import net.mamoe.mirai.network.packet.ServerVerificationCodePacket;
/**
* 服务器接到某数据包时触发这个事件.
* 注意, 当接收到数据包的加密包( {@link ServerVerificationCodePacket.Encrypted})也会触发这个事件, 随后才会
*
* @author Him188moe
*/
public class ServerPacketReceivedEvent extends ServerPacketEvent implements Cancellable {
public final class ServerPacketReceivedEvent extends ServerPacketEvent implements Cancellable {
public ServerPacketReceivedEvent(ServerPacket packet) {
super(packet);
}

View File

@ -1,7 +1,7 @@
package net.mamoe.mirai.event.events.robot;
import net.mamoe.mirai.Robot;
import net.mamoe.mirai.event.events.MiraiEvent;
import net.mamoe.mirai.event.MiraiEvent;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;

View File

@ -1,7 +1,7 @@
package net.mamoe.mirai.event.events.robot
import net.mamoe.mirai.Robot
import net.mamoe.mirai.event.events.MiraiEvent
import net.mamoe.mirai.event.MiraiEvent
/**
* @author Him188moe
@ -16,4 +16,3 @@ class RobotMessageReceivedEvent(val robot: Robot, val type: Type, val message: S
GROUP
}
}

View File

@ -7,5 +7,4 @@ public final class RobotLoginSucceedEvent extends RobotEvent {
public RobotLoginSucceedEvent(Robot robot) {
super(robot);
}
}

View File

@ -1,7 +0,0 @@
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.events.MiraiEvent;
public final class ServerDisableEvent extends MiraiEvent {
}

View File

@ -0,0 +1,8 @@
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.AsyncEvent;
import net.mamoe.mirai.event.MiraiEvent;
public final class ServerDisabledEvent extends MiraiEvent implements AsyncEvent {
}

View File

@ -1,8 +0,0 @@
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.events.MiraiEvent;
public final class ServerEnableEvent extends MiraiEvent {
}

View File

@ -0,0 +1,9 @@
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.AsyncEvent;
import net.mamoe.mirai.event.MiraiEvent;
public final class ServerEnabledEvent extends MiraiEvent implements AsyncEvent {
}

View File

@ -1,7 +1,9 @@
package net.mamoe.mirai.message;
import net.mamoe.mirai.contact.Contact;
import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.message.defaults.At;
import net.mamoe.mirai.message.defaults.Image;
import net.mamoe.mirai.message.defaults.MessageChain;
import net.mamoe.mirai.message.defaults.PlainText;
import org.jetbrains.annotations.NotNull;
@ -11,7 +13,12 @@ import java.io.File;
import java.util.Objects;
/**
* 可发送的或从服务器接收的消息.
* 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 {@linkplain PlainText 纯文本}, {@linkplain Image 图片} .
*
* @author Him188moe
* @see Contact#sendMessage(Message) 发送这个消息
* @see MessageKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现
*/
public abstract class Message {
@Override
@ -38,7 +45,7 @@ public abstract class Message {
return new MessageChain(this, Objects.requireNonNull(tail));
}
public Message concat(String tail) {
public final Message concat(String tail) {
return concat(new PlainText(tail));
}

View File

@ -1,3 +1,5 @@
@file:JvmName("MessageKt")
package net.mamoe.mirai.message
import net.mamoe.mirai.message.defaults.PlainText
@ -12,4 +14,7 @@ infix operator fun Message.plus(another: Message): Message = this.concat(another
*/
infix operator fun Message.plus(another: String): Message = this.concat(another)
/**
* 连接 [String] [Message]
*/
infix fun String.concat(another: Message): Message = PlainText(this).concat(another)

View File

@ -7,6 +7,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* At 一个人的消息.
*
* @author Him188moe
*/
public final class At extends Message {

View File

@ -1,3 +1,5 @@
@file:JvmMultifileClass
@file:JvmName("RobotNetworkHandler")
package net.mamoe.mirai.network
import net.mamoe.mirai.Robot
@ -8,6 +10,7 @@ import net.mamoe.mirai.event.events.qq.FriendMessageEvent
import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent
import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.network.RobotNetworkHandler.*
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
@ -19,17 +22,33 @@ import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import kotlin.reflect.KClass
/**
* Mirai 的网络处理器, 它处理所有数据包([Packet])的发送和接收.
* [RobotNetworkHandler] 是全程异步和线程安全的.
*
* [RobotNetworkHandler] 2 个模块构成:
* - [SocketHandler]: 处理数据包底层的发送([ByteArray])
* - [PacketHandler]: 制作 [Packet] 并传递给 [SocketHandler] 继续处理; 分析来自服务器的数据包并处理
*
* 其中, [PacketHandler] 4 个子模块构成:
* - [DebugHandler] 输出 [Packet.toString]
* - [LoginHandler] 处理 touch/login/verification code 相关
* - [MessageHandler] 处理消息相关(群消息/好友消息)([ServerEventPacket])
* - [ActionHandler] 处理动作相关(踢人/加入群/好友列表等)
*
* A RobotNetworkHandler is used to connect with Tencent servers.
*
* @author Him188moe
*/
@Suppress("EXPERIMENTAL_API_USAGE")//to simplify code
internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
class RobotNetworkHandler(private val robot: Robot) : Closeable {
private val socketHandler: SocketHandler = SocketHandler()
val debugHandler = DebugHandler()
@ -44,9 +63,6 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
ActionHandler::class to actionHandler
)
private var closed: Boolean = false
/**
* Not async
*/
@ -65,30 +81,49 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
//private | internal
internal fun tryLogin(): CompletableFuture<LoginState> = this.tryLogin(500, TimeUnit.MILLISECONDS)
/**
* 仅当 [LoginState] [LoginState.UNKNOWN] 且非 [LoginState.TIMEOUT] 才会调用 [loginHook].
* 如果要输入验证码, 那么会以参数 [LoginState.VERIFICATION_CODE] 调用 [loginHandler], 登录完成后再以 [LoginState.SUCCEED] 调用 [loginHandler]
*
* @param connectingTimeout 连接每个服务器的 timeout
*/
internal fun tryLogin(loginHook: (Robot.(LoginState) -> Unit)? = null) {
internal fun tryLogin(connectingTimeout: Long, unit: TimeUnit = TimeUnit.MILLISECONDS): CompletableFuture<LoginState> {
val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
fun login(): Boolean {
val future = CompletableFuture<LoginState>()
fun login() {
val ip = ipQueue.poll()
return if (ip != null) {
this@RobotNetworkHandler.socketHandler.touch(ip) { state ->
if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
login()
} else {
if (loginHook != null) {
robot.loginHook(state)
if (ip != null) {
// val future = this@RobotNetworkHandler.socketHandler.touch(ip)
this@RobotNetworkHandler.socketHandler.touch(ip).runCatching {
this@runCatching.get(connectingTimeout, unit).let { state ->
if (state == LoginState.UNKNOWN) {
login()
} else {
future.complete(state)
}
}
}.onFailure {
when (it) {
is TimeoutException -> login()
else -> throw it
}
}
true
} else false
} else {
future.complete(LoginState.UNKNOWN)//所有服务器均返回 UNKNOWN
}
}
login()
return future
}
/**
* 分配收到的数据包
*/
@ExperimentalUnsignedTypes
internal fun distributePacket(packet: ServerPacket) {
packet.decode()
@ -112,14 +147,7 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
restartSocket()
}
private var loginHook: ((LoginState) -> Unit)? = null
internal var loginState: LoginState? = null
set(value) {
field = value
if (value != null) {
loginHook?.invoke(value)
}
}
internal var loginFuture: CompletableFuture<LoginState>? = null
private fun restartSocket() {
socket?.close()
@ -151,17 +179,14 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
/**
* Start network and touch the server
*/
internal fun touch(serverAddress: String, loginHook: ((LoginState) -> Unit)? = null) {
internal fun touch(serverAddress: String): CompletableFuture<LoginState> {
MiraiLogger.info("Connecting server: $serverAddress")
this.loginFuture = CompletableFuture()
socketHandler.serverIP = serverAddress
if (loginHook != null) {
this.loginHook = loginHook
}
sendPacket(ClientTouchPacket(robot.account.qqNumber, socketHandler.serverIP))
waitForPacket(ServerTouchResponsePacket::class, 100) {
MiraiLogger.error(" Timeout")
loginHook?.invoke(LoginState.TIMEOUT)
}
return this.loginFuture!!
}
/**
@ -209,8 +234,12 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
override fun close() {
this.socket?.close()
this.loginState = null
this.loginHook = null
if (this.loginFuture != null) {
if (!this.loginFuture!!.isDone) {
this.loginFuture!!.cancel(true)
}
this.loginFuture = null
}
}
}
@ -286,8 +315,7 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
}
is ServerLoginResponseFailedPacket -> {
socketHandler.loginState = packet.loginState
MiraiLogger error "Login failed: " + packet.loginState.toString()
socketHandler.loginFuture!!.complete(packet.loginState)
return
}
@ -309,8 +337,6 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
}
is ServerVerificationCodeTransmissionPacket -> {
socketHandler.loginState = LoginState.VERIFICATION_CODE
this.verificationCodeSequence++
this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN
@ -391,7 +417,7 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
}
is ServerLoginSuccessPacket -> {
socketHandler.loginState = LoginState.SUCCEED
socketHandler.loginFuture!!.complete(LoginState.SUCCEED)
sendPacket(ClientSKeyRequestPacket(robot.account.qqNumber, sessionKey))
}

View File

@ -46,8 +46,8 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
}
return ServerLoginResponseFailedPacket(when (bytes.size) {
319 -> LoginState.WRONG_PASSWORD
135 -> LoginState.RETYPE_PASSWORD
319, 135 -> LoginState.WRONG_PASSWORD
//135 -> LoginState.RETYPE_PASSWORD
279 -> LoginState.BLOCKED
263 -> LoginState.UNKNOWN_QQ_NUMBER
551, 487 -> LoginState.DEVICE_LOCK

View File

@ -4,7 +4,6 @@ import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.*
import java.io.DataInputStream
/**
* 客户端请求验证码图片数据的第几部分
*/

View File

@ -4,20 +4,43 @@ package net.mamoe.mirai.network.packet.login
* @author Him188moe
*/
enum class LoginState {
/**
* 登录成功
*/
SUCCEED,
/**
* 密码错误
*/
WRONG_PASSWORD,
// UNKNOWN,//? 要再次发送某数据包
RETYPE_PASSWORD,//similar to [WRONG_PASSWORD]
BLOCKED,//你的帐号存在被盗风险,已进入保护模式
UNKNOWN_QQ_NUMBER,//你输入的帐号不存在
DEVICE_LOCK,//设备锁
TAKEN_BACK,//被回收
/**
* 被冻结
*/
BLOCKED,
VERIFICATION_CODE,//需要验证码
/**
* QQ 号码输入有误
*/
UNKNOWN_QQ_NUMBER,
/**
* 账号开启了设备锁. 暂不支持设备锁登录
*/
DEVICE_LOCK,
/**
* 账号被回收
*/
TAKEN_BACK,
/**
* 需要验证码登录
*/
VERIFICATION_CODE,
/**
* 未知. 更换服务器或等几分钟再登录可能解决.
*/
UNKNOWN,
TIMEOUT,
}

View File

@ -1,4 +1,14 @@
package net.mamoe.mirai.plugin;
public class MiraiPluginBase {
import net.mamoe.mirai.Robot;
/**
* 插件基类.
* <p>
* 插件属于整个 Mirai, 而不是属于单个 {@link Robot}.
*
* @see net.mamoe.mirai.event.MiraiEventManager
* @see net.mamoe.mirai.event.MiraiEventManagerKt
*/
public abstract class MiraiPluginBase {
}

View File

@ -2,7 +2,7 @@ package net.mamoe.mirai.task;
import net.mamoe.mirai.event.MiraiEventHook;
import net.mamoe.mirai.event.events.server.ServerDisableEvent;
import net.mamoe.mirai.event.events.server.ServerDisabledEvent;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
@ -24,7 +24,7 @@ public final class MiraiTaskManager {
this.pool = new MiraiThreadPool();
MiraiEventHook
.onEvent(ServerDisableEvent.class)
.onEvent(ServerDisabledEvent.class)
.handler(a -> this.pool.close())
.mount();

View File

@ -1,5 +1,6 @@
package net.mamoe.mirai.utils.setting;
import net.mamoe.mirai.plugin.MiraiPluginBase;
import org.ini4j.Config;
import org.ini4j.Ini;
@ -15,7 +16,7 @@ import java.util.concurrent.ConcurrentHashMap;
* Only supports <code>INI</code> format <br>
* Supports {@link Map} and {@link List}
*/
public class MiraiSetting {
public class MiraiSettings {
private File file;
@ -23,14 +24,21 @@ public class MiraiSetting {
private volatile Map<String, MiraiSettingSection> cacheSection = new ConcurrentHashMap<>();
public MiraiSetting(File file){
public MiraiSettings(MiraiPluginBase pluginBase, String filename) {
// TODO: 2019/9/6 每个插件独立文件夹存放
this(new File(filename));
}
public MiraiSettings(File file) {
if(!file.getName().contains(".")){
file = new File(file.getParent() + file.getName() + ".ini");
file = new File(file.getPath() + ".ini");
}
this.file = file;
try {
if(file.exists()){
file.createNewFile();
if (!file.createNewFile()) {
throw new RuntimeException("cannot create config file " + file);
}
}
Config config = new Config();
config.setMultiSection(true);

View File

@ -107,14 +107,16 @@ fun main() {
qqList.split("\n").forEach {
GlobalScope.launch {
val strings = it.split("----")
Robot(RobotAccount(strings[0].toLong(), strings[1].let { password ->
val robot = Robot(RobotAccount(strings[0].toLong(), strings[1].let { password ->
if (password.endsWith(".")) {
return@let password.substring(0, password.length - 1)
}
return@let password
}), listOf()).network.tryLogin { state ->
}), listOf())
robot.network.tryLogin().whenComplete { state, _ ->
if (!(state == LoginState.BLOCKED || state == LoginState.DEVICE_LOCK || state == LoginState.WRONG_PASSWORD)) {
goodRobotList.add(this)
goodRobotList.add(robot)
}
}
}