Merge remote-tracking branch 'origin/master'

This commit is contained in:
liujiahua123123 2019-09-01 18:50:37 +08:00
commit 4a8f7255ec
48 changed files with 739 additions and 394 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -3,15 +3,21 @@
一个以<b>TIM QQ协议</b>驱动的JAVA(+Kotlin) QQ机器人服务端核心
我们坚持免费与开源
项目处于快速开发阶段, 现在已经可以接受群聊/好友消息.
协议来自网络的易语言开源软件
项目处于快速开发阶段, 现在已经可以接受和发送群聊/好友消息.
协议来自网络上开源项目
一切开发旨在学习, 请勿用于非法用途
### 我们会坚持开发, 但是,
![LV7_YX10AQ5TW@E~308_0JN.png](https://i.loli.net/2019/08/24/oQIzhaLvyJOeW1f.png)
<br>
A JAVA(+Kotlin) powered open-source project under GPL license<br>
It use protocols from <i>TIM QQ</i>, that is, it won't be affected by the close of <i>Smart QQ</i><br>
The project is all for <b>learning proposes</b> and still in <b>developing stage</b><br>
### 代码结构
Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持), 与插件相关性强(或任何其他在二次开发中容易接触的部分)均使用 Java 完成.
Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持).
与插件相关性强(或其他在二次开发中容易接触的部分)均使用 Java 完成,
同时也会针对kotlin提供优化的方法调用. 例如对'+'操作符的重载: `String+BufferedImage+QQ.At+Face+URL+String+File` 将会被自动处理为String消息.
### TODO
- [x] 事件(Event)模块
@ -19,25 +25,23 @@ Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持), 与
- [x] Network - Touch
- [X] Network - Login
- [X] Network - Session
- [X] Network - Message Receive
- [X] Network - Message Send
- [ ] Network - Verification Code (Low priority)
- [X] Network - Message Receiving
- [X] Network - Message Sending
- [ ] Network - Events **(Working on)**
<br>
A JAVA(+Kotlin) powered open-sources project under GPL license<br>
It use protocols from <i>TIM QQ</i>, meaning it won't be affect by the close of <i>smart QQ</i><br>
The project is all for <b>learning proposes</b> and still in <b>developing stage</b><br>
- [ ] Robot - Friend/group list
- [ ] Message Section **(Working on)**
- [ ] Contact
<br>
## 使用方法
### 要求
- Java 11 或更高
- Kotlin
- Kotlin 1.3 或更高
### 插件开发
``` php
to be continue
to be continued
...
```
@ -45,10 +49,10 @@ The project is all for <b>learning proposes</b> and still in <b>developing stage
## Usage
### Requirements
- Java 11 or higher
- Kotlin
### Plugin Developments
- Kotlin 1.3 or higher
### Plugin Development
``` php
to be continue
to be continued
...
```

View File

@ -1,7 +1,10 @@
package net.mamoe.mirai.contact
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.PlainText
/**
* A contact is a QQ account or a QQ Group.
* A contact is a [QQ] or a [Group] for one particular [Robot] instance only.
*
* @author Him188moe
*/
@ -10,10 +13,14 @@ abstract class Contact(val number: Int) {
/**
* Async
*/
abstract fun sendMessage(message: String)
abstract fun sendMessage(message: Message)
fun sendMessage(message: String) {
this.sendMessage(PlainText(message))
}
/**
* Async
*/
abstract fun sendObjectMessage(message: String)
abstract fun sendXMLMessage(message: String)
}

View File

@ -1,15 +1,117 @@
package net.mamoe.mirai.contact
import net.mamoe.mirai.message.Message
class Group(number: Int) : Contact(number) {
val groupId = groupNumberToId(number)
init {
Instances.groups.add(this)
}
override fun sendMessage(message: String) {
override fun sendMessage(message: Message) {
}
override fun sendObjectMessage(message: String) {
override fun sendXMLMessage(message: String) {
}
companion object {
fun groupNumberToId(number: Int): Int {
val left: Int = number.toString().let {
if (it.length < 6) {
return@groupNumberToId number
}
it.substring(0, it.length - 6).toInt()
}
val right: Int = number.toString().let {
it.substring(it.length - 6).toInt()
}
return when (left) {
in 1..10 -> {
((left + 202).toString() + right.toString()).toInt()
}
in 11..19 -> {
((left + 469).toString() + right.toString()).toInt()
}
in 20..66 -> {
((left + 208).toString() + right.toString()).toInt()
}
in 67..156 -> {
((left + 1943).toString() + right.toString()).toInt()
}
in 157..209 -> {
((left + 199).toString() + right.toString()).toInt()
}
in 210..309 -> {
((left + 389).toString() + right.toString()).toInt()
}
in 310..499 -> {
((left + 349).toString() + right.toString()).toInt()
}
else -> number
}
}
fun groupIdToNumber(id: Int): Int {
var left: Int = id.toString().let {
if (it.length < 6) {
return@groupIdToNumber id
}
it.substring(0 until it.length - 6).toInt()
}
return when (left) {
in 203..212 -> {
val right: Int = id.toString().let {
it.substring(it.length - 6).toInt()
}
((left - 202).toString() + right.toString()).toInt()
}
in 480..488 -> {
val right: Int = id.toString().let {
it.substring(it.length - 6).toInt()
}
((left - 469).toString() + right.toString()).toInt()
}
in 2100..2146 -> {
val right: Int = id.toString().let {
it.substring(it.length - 7).toInt()
}
left = left.toString().substring(0 until 3).toInt()
((left - 208).toString() + right.toString()).toInt()
}
in 2010..2099 -> {
val right: Int = id.toString().let {
it.substring(it.length - 6).toInt()
}
((left - 1943).toString() + right.toString()).toInt()
}
in 2147..2199 -> {
val right: Int = id.toString().let {
it.substring(it.length - 7).toInt()
}
left = left.toString().substring(0 until 3).toInt()
((left - 199).toString() + right.toString()).toInt()
}
in 4100..4199 -> {
val right: Int = id.toString().let {
it.substring(it.length - 7).toInt()
}
left = left.toString().substring(0 until 3).toInt()
((left - 389).toString() + right.toString()).toInt()
}
in 3800..3989 -> {
val right: Int = id.toString().let {
it.substring(it.length - 7).toInt()
}
left = left.toString().substring(0 until 3).toInt()
((left - 349).toString() + right.toString()).toInt()
}
else -> id
}
}
}
}

View File

@ -1,5 +1,8 @@
package net.mamoe.mirai.contact
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.At
/**
* @author Him188moe
*/
@ -8,19 +11,19 @@ class QQ(number: Int) : Contact(number) {
Instances.qqs.add(this)
}
override fun sendMessage(message: String) {
override fun sendMessage(message: Message) {
}
override fun sendObjectMessage(message: String) {
override fun sendXMLMessage(message: String) {
}
/**
* At(@) this account.
*/
fun at(): String {
return "[@$number]"
fun at(): At {
return At(this)
}

View File

@ -39,20 +39,20 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
public MiraiEventHook(Class<T> eventClass, Consumer<T> handler){
this.eventClass = eventClass;
this.setHandler(handler);
this.handler(handler);
}
public MiraiEventHook<T> setHandler(Consumer<T> handler){
public MiraiEventHook<T> handler(Consumer<T> handler) {
this.handler = handler;
return this;
}
public MiraiEventHook<T> setPriority(int priority){
public MiraiEventHook<T> priority(int priority) {
this.priority = priority;
return this;
}
public MiraiEventHook<T> setIgnoreCancelled(boolean ignoreCancelled){
public MiraiEventHook<T> ignoreCancelled(boolean ignoreCancelled) {
this.ignoreCancelled = ignoreCancelled;
return this;
}

View File

@ -4,52 +4,60 @@ import net.mamoe.mirai.MiraiServer;
import net.mamoe.mirai.event.events.MiraiEvent;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class MiraiEventManager {
private MiraiEventManager(){
private MiraiEventManager() {
}
private static MiraiEventManager instance;
private static MiraiEventManager instance = new MiraiEventManager();
public static MiraiEventManager getInstance(){
if(MiraiEventManager.instance == null){
MiraiEventManager.instance = new MiraiEventManager();
}
public static MiraiEventManager getInstance() {
return MiraiEventManager.instance;
}
Lock hooksLock = new ReentrantLock();
private final ReentrantReadWriteLock hooksLock = new ReentrantReadWriteLock();
private Map<Class<? extends MiraiEvent>, List<MiraiEventHook<? extends MiraiEvent>>> hooks = new HashMap<>();
public <D extends MiraiEvent> void hookUntil(MiraiEventHook<D> hook, Predicate<D> toRemove){
public <D extends MiraiEvent> void hookUntil(MiraiEventHook<D> hook, Predicate<D> toRemove) {
this.mountHook(hook.setValidUntil(toRemove));
}
public <D extends MiraiEvent> void hookWhile(MiraiEventHook<D> hook, Predicate<D> toKeep){
public <D extends MiraiEvent> void hookWhile(MiraiEventHook<D> hook, Predicate<D> toKeep) {
this.mountHook(hook.setValidWhile(toKeep));
}
public <D extends MiraiEvent> void hookOnce(MiraiEventHook<D> hook){
this.hookUntil(hook,(a) -> true);
public <D extends MiraiEvent> void hookAlways(MiraiEventHook<D> hook) {
this.hookUntil(hook, (a) -> false);
}
public <D extends MiraiEvent> void registerHook(MiraiEventHook<D> hook){
this.mountHook(hook);
public <D extends MiraiEvent> void hookOnce(MiraiEventHook<D> hook) {
this.hookUntil(hook, (a) -> true);
}
private <D extends MiraiEvent> void mountHook(MiraiEventHook<D> hook){
if(!hook.isMount()) {
public <D extends MiraiEvent> void registerHook(MiraiEventHook<D> hook) {
this.mountHook(hook);
}
private <D extends MiraiEvent> void mountHook(MiraiEventHook<D> hook) {
if (!hook.isMount()) {
hook.setMount(true);
hooksLock.lock();
hooks.putIfAbsent(hook.getEventClass(), new ArrayList<>());
hooks.get(hook.getEventClass()).add(hook);
hooksLock.unlock();
hooksLock.writeLock().lock();
try {
if (!hooks.containsKey(hook.getEventClass())) {
hooks.put(hook.getEventClass(), new LinkedList<>() {{
add(hook);
}});
} else {
hooks.get(hook.getEventClass()).add(hook);
}
} finally {
hooksLock.writeLock().unlock();
}
}
}
@ -57,53 +65,55 @@ public class MiraiEventManager {
* 不推荐onEvent
* 由于不能保证Hook的原子性 非线程安全
* 不能保证下一个 D event发生时handler就位
*
* @author NaturalHG Aug27
* use {@link MiraiEventHook::onEvent()} to replace
*/
@Deprecated
public <D extends MiraiEvent> MiraiEventHook<D> onEvent(Class<D> event){
public <D extends MiraiEvent> MiraiEventHook<D> onEvent(Class<D> event) {
MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.registerHook(hook);
return hook;
}
@Deprecated
public <D extends MiraiEvent> MiraiEventHook<D> onEventOnce(Class<D> event){
public <D extends MiraiEvent> MiraiEventHook<D> onEventOnce(Class<D> event) {
MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.hookOnce(hook);
return hook;
}
@Deprecated
public <D extends MiraiEvent> MiraiEventHook<D> onEventUntil(Class<D> event, Predicate<D> toRemove){
public <D extends MiraiEvent> MiraiEventHook<D> onEventUntil(Class<D> event, Predicate<D> toRemove) {
MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.hookUntil(hook,toRemove);
this.hookUntil(hook, toRemove);
return hook;
}
@Deprecated
public <D extends MiraiEvent> MiraiEventHook<D> onEventWhile(Class<D> event, Predicate<D> toKeep){
public <D extends MiraiEvent> MiraiEventHook<D> onEventWhile(Class<D> event, Predicate<D> toKeep) {
MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.hookWhile(hook,toKeep);
this.hookWhile(hook, toKeep);
return hook;
}
public void broadcastEvent(MiraiEvent event){
hooksLock.lock();
if(hooks.containsKey(event.getClass())){
hooks.put(event.getClass(),
hooks.get(event.getClass())
.stream()
.sorted(Comparator.comparingInt(MiraiEventHook::getPriority))
.filter(a -> !a.accept(event))
.collect(Collectors.toList())
);
public void broadcastEvent(MiraiEvent event) {
hooksLock.readLock().lock();
try {
if (hooks.containsKey(event.getClass())) {
hooks.put(event.getClass(),
hooks.get(event.getClass())
.stream()
.sorted(Comparator.comparingInt(MiraiEventHook::getPriority))
.filter(a -> !a.accept(event))
.collect(Collectors.toList())
);
}
} finally {
hooksLock.readLock().unlock();
}
hooksLock.unlock();
}
@ -116,10 +126,14 @@ public class MiraiEventManager {
MiraiServer.getInstance().getTaskManager().ansycTask(() -> {
MiraiEventManager.this.broadcastEvent(event);
return event;
},callback);
}, callback);
}
public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Runnable callback) {
asyncBroadcastEvent(event, t -> callback.run());
}
}

View File

@ -0,0 +1,38 @@
package net.mamoe.mirai.event
import net.mamoe.mirai.event.events.MiraiEvent
import kotlin.reflect.KClass
fun <C : Class<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook))
}
fun <C : Class<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) {
MiraiEventManager.getInstance().hookOnce(MiraiEventHook<E>(this, hook))
}
fun <C : Class<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHookSimple<E>(this, hook))
}
fun <C : KClass<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
this.java.hookAlways(hook)
}
fun <C : KClass<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) {
this.java.hookOnce(hook)
}
fun <C : KClass<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) {
this.java.hookWhile(hook)
}
private class MiraiEventHookSimple<E : MiraiEvent>(clazz: Class<E>, val hook: (E) -> Boolean) : MiraiEventHook<E>(clazz) {
override fun accept(event: MiraiEvent?): Boolean {
@Suppress("UNCHECKED_CAST")
return hook.invoke(event as E)
}
}

View File

@ -1,8 +1,9 @@
package net.mamoe.mirai.event.events;
/**
* @author NaturalHG
*/
public interface Cancellable {
boolean isCancelled();
void cancel(boolean forceCancel);

View File

@ -1,7 +1,10 @@
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;
@ -25,12 +28,26 @@ public abstract class MiraiEvent {
}
protected String eventName;
public String getEventName() {
if(this.eventName == null){
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,21 @@
package net.mamoe.mirai.message;
/**
* @author Him188moe
*/
public enum FaceID {
// TODO: 2019/9/1
;
private final int id;
FaceID(int id) {
this.id = id;
}
public int getId() {
return id;
}
}

View File

@ -1,4 +1,72 @@
package net.mamoe.mirai.message;
public class Message {
import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.message.defaults.At;
import net.mamoe.mirai.message.defaults.MessageChain;
import net.mamoe.mirai.message.defaults.PlainText;
import org.jetbrains.annotations.NotNull;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Objects;
/**
* @author Him188moe
*/
public abstract class Message {
@Override
public abstract String toString();
/**
* 把这个消息连接到另一个消息的头部. 相当于字符串相加
* <p>
* Connects this Message to the head of another Message.
* That is, another message becomes the tail of this message.
* This method does similar to {@link String#concat(String)}
* <p>
* E.g.:
* PlainText a = new PlainText("Hello ");
* PlainText b = new PlainText("world");
* PlainText c = a.concat(b);
* <p>
* the text of c is "Hello world"
*
* @param tail tail
* @return message connected
*/
public Message concat(@NotNull Message tail) {
return new MessageChain(this, Objects.requireNonNull(tail));
}
public Message concat(String tail) {
return concat(new PlainText(tail));
}
public Message withImage(String imageId) {
// TODO: 2019/9/1
return this;
}
public Message withImage(BufferedImage image) {
// TODO: 2019/9/1
return this;
}
public Message withImage(File image) {
// TODO: 2019/9/1
return this;
}
public Message withAt(@NotNull QQ target) {
this.concat(target.at());
return this;
}
public Message withAt(int target) {
this.concat(new At(target));
return this;
}
}

View File

@ -0,0 +1,15 @@
package net.mamoe.mirai.message
import net.mamoe.mirai.message.defaults.PlainText
/**
* 实现使用 '+' 操作符连接 [Message] [Message]
*/
infix operator fun Message.plus(another: Message): Message = this.concat(another)
/**
* 实现使用 '+' 操作符连接 [Message] [String]
*/
infix operator fun Message.plus(another: String): Message = this.concat(another)
infix fun String.concat(another: Message): Message = PlainText(this).concat(another)

View File

@ -0,0 +1,31 @@
package net.mamoe.mirai.message.defaults;
import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.message.Message;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* @author Him188moe
*/
public final class At extends Message {
private final int target;
public At(@NotNull QQ target) {
this(Objects.requireNonNull(target).getNumber());
}
public At(int target) {
this.target = target;
}
public int getTarget() {
return target;
}
@Override
public String toString() {
return null;
}
}

View File

@ -0,0 +1,25 @@
package net.mamoe.mirai.message.defaults;
import net.mamoe.mirai.message.FaceID;
import net.mamoe.mirai.message.Message;
/**
* @author Him188moe
*/
public final class Face extends Message {
private final FaceID id;
public Face(FaceID id) {
this.id = id;
}
public FaceID getId() {
return id;
}
@Override
public String toString() {
// TODO: 2019/9/1
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,43 @@
package net.mamoe.mirai.message.defaults;
import net.mamoe.mirai.message.Message;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
/**
* @author Him188moe
*/
public final class Image extends Message {
public Image(InputStream inputStream) {
}
public Image(BufferedImage image) {
}
public Image(File imageFile) throws FileNotFoundException {
this(new FileInputStream(imageFile));
}
public Image(URL url) throws IOException {
this(ImageIO.read(url));
}
/**
* {xxxxx}.jpg
*
* @param imageID
*/
public Image(String imageID) {
}
@Override
public String toString() {
return null;
}
}

View File

@ -0,0 +1,34 @@
package net.mamoe.mirai.message.defaults;
import net.mamoe.mirai.message.Message;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedList;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author Him188moe
*/
public final class MessageChain extends Message {
private LinkedList<Message> list = new LinkedList<>();
public MessageChain(@NotNull Message head, @NotNull Message tail) {
Objects.requireNonNull(head);
Objects.requireNonNull(tail);
list.add(head);
list.add(tail);
}
@Override
public synchronized String toString() {
return this.list.stream().map(Message::toString).collect(Collectors.joining(""));
}
@Override
public synchronized Message concat(@NotNull Message tail) {
this.list.add(tail);
return this;
}
}

View File

@ -0,0 +1,19 @@
package net.mamoe.mirai.message.defaults;
import net.mamoe.mirai.message.Message;
/**
* @author Him188moe
*/
public final class PlainText extends Message {
private final String text;
public PlainText(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}

View File

@ -2,15 +2,15 @@ package net.mamoe.mirai.network
import net.mamoe.mirai.MiraiServer
import net.mamoe.mirai.Robot
import net.mamoe.mirai.event.MiraiEventManager
import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.network.packet.message.ClientSendFriendMessagePacket
import net.mamoe.mirai.network.packet.message.ClientSendGroupMessagePacket
import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacket
import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.util.*
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.*
import java.io.ByteArrayInputStream
import java.io.FileOutputStream
import java.net.DatagramPacket
@ -196,7 +196,7 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor
MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS)
MiraiEventManager.getInstance().asyncBroadcastEvent(RobotLoginSucceedEvent(robot))
RobotLoginSucceedEvent(robot).broadcast()
this.tlv0105 = packet.tlv0105
sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE))
}
@ -229,11 +229,19 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor
is ServerFriendMessageEventPacket -> {
println(packet.toString())
if (packet.message == "牛逼") {
sendPacket(ClientSendFriendMessagePacket(this.number, packet.qq, this.sessionKey, "牛逼!!"))
}
//friend message
}
is ServerGroupMessageEventPacket -> {
//group message
if (packet.message == "牛逼") {
sendPacket(ClientSendGroupMessagePacket(packet.group, this.number, this.sessionKey, "牛逼!"))
}
}
is UnknownServerEventPacket -> {

View File

@ -1,106 +0,0 @@
package net.mamoe.mirai.network.connection;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.mamoe.mirai.utils.MiraiLogger;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* UDP Client
* Try to keep long-alive UDP connection in order to improve performance
*
*/
public class MiraiUDPClient {
private LocalUDPListener listener;
public MiraiUDPClient(InetAddress address, short serverPort, short localPort){
this.listener = new LocalUDPListener(new LocalUDPSocketProvider(
address,localPort,serverPort,null
));
this.listener.startup();
}
}
@AllArgsConstructor
class LocalUDPSocketProvider
{
private InetAddress address;
private short localPort;
private short serverPort;
@Getter
private DatagramSocket socket = null;
public void initSocket()
{
try
{
this.socket = new DatagramSocket(this.localPort);
this.socket.connect(this.address, this.serverPort);
this.socket.setReuseAddress(true);
}
catch (Exception e)
{
MiraiLogger.INSTANCE.catching(e);
}
}
}
class LocalUDPListener
{
private Thread thread = null;
protected LocalUDPSocketProvider provider;
public LocalUDPListener(LocalUDPSocketProvider provider){
this.provider = provider;
if(this.provider.getSocket() == null){
this.provider.initSocket();
}
}
public void startup()
{
this.thread = new Thread(() -> {
try
{
LocalUDPListener.this.listener();
}
catch (Exception e)
{
MiraiLogger.INSTANCE.catching(e);
}
});
this.thread.start();
}
private void listener() throws Exception
{
while (true)
{
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
DatagramSocket localUDPSocket = this.provider.getSocket();
if ((localUDPSocket == null) || (localUDPSocket.isClosed()))
continue;
localUDPSocket.receive(packet);
//todo use CALLBACK
String pFromServer = new String(packet.getData(), 0 , packet.getLength(), "UTF-8");
System.out.println("【NOTE】>>>>>> 收到服务端的消息:"+pFromServer);
}
}
}

View File

@ -1,7 +1,7 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream
/**

View File

@ -2,7 +2,8 @@ package net.mamoe.mirai.network.packet
import lombok.Getter
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.*
import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.utils.*
import java.io.DataOutputStream
import java.io.IOException
import java.net.InetAddress
@ -72,6 +73,10 @@ fun DataOutputStream.writeIP(ip: String) {
}
}
@Throws(IOException::class)
fun DataOutputStream.writeTime() {
this.writeInt(System.currentTimeMillis().toInt())
}
@ExperimentalUnsignedTypes
@Throws(IOException::class)
@ -201,9 +206,9 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf(
@ExperimentalUnsignedTypes
fun Int.toHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator);
private fun md5(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray())
internal fun md5(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray())
private fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
internal fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
@ExperimentalUnsignedTypes
@Throws(IOException::class)

View File

@ -1,8 +1,8 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.util.toUHexString
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
@ -11,7 +11,7 @@ import java.io.DataInputStream
*/
@PacketId("")//随后写入
@ExperimentalUnsignedTypes
open class ClientMessageResponsePacket(
class ClientMessageResponsePacket(
private val qq: Int,
private val packetIdFromServer: ByteArray,
private val sessionKey: ByteArray,

View File

@ -1,7 +1,7 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream

View File

@ -1,7 +1,6 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.util.toUHexString
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.toUHexString
import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.util.zip.GZIPInputStream
@ -33,7 +32,7 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr
lateinit var message: String
override fun decode() {
message = String(this.input.goto(64).readNBytes(this.input.goto(60).readShort().toInt()))
message = String(this.input.goto(65).readNBytes(this.input.goto(60).readShort().toInt()))
}//todo test
}
@ -47,7 +46,12 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
NORMAL,
XML,
AT,
IMAGE,
FACE,//qq自带表情 [face107.gif]
PLAIN_TEXT, //纯文本
IMAGE, //自定义图片 {F50C5235-F958-6DF7-4EFA-397736E125A4}.gif
ANONYMOUS,//匿名用户发出的消息
OTHER,
}
@ -56,17 +60,26 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
group = this.input.goto(51).readInt()
qq = this.input.goto(56).readInt()
val fontLength = this.input.goto(108).readShort()
println(this.input.goto(110 + fontLength).readNBytes(2).toUHexString())
//println(this.input.goto(110 + fontLength).readNBytes(2).toUHexString())//always 00 00
messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) {
19 -> MessageType.NORMAL
14 -> MessageType.XML
2 -> MessageType.IMAGE
6 -> MessageType.AT
else -> MessageType.OTHER
1 -> MessageType.PLAIN_TEXT
2 -> MessageType.FACE
3 -> MessageType.IMAGE
25 -> MessageType.ANONYMOUS
else -> {
println("id=$id")
MessageType.OTHER
}
}
when (messageType) {
MessageType.NORMAL -> {
val gzippedMessage = this.input.goto(110 + fontLength + 16).readNBytes(this.input.goto(110 + fontLength + 3).readShort().toInt() - 11)
@ -84,7 +97,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
}
}
MessageType.IMAGE -> {
MessageType.FACE -> {
val faceId = this.input.goto(110 + fontLength + 8).readByte()
message = "[face${faceId}.gif]"
}
@ -123,9 +136,6 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
}*/
}
}
MiraiLogger info this.toString()
}
}

View File

@ -1,10 +1,12 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.network.packet.message.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.message.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted
import net.mamoe.mirai.util.getAllDeclaredFields
import net.mamoe.mirai.util.hexToBytes
import net.mamoe.mirai.util.toUHexString
import net.mamoe.mirai.utils.getAllDeclaredFields
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
/**
@ -78,6 +80,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"00 81" -> UnknownServerPacket(stream)
"00 CD" -> ServerSendFriendMessageResponsePacket(stream)
"00 02" -> ServerSendGroupMessageResponsePacket(stream)
else -> throw IllegalArgumentException(idHex)
}
}

View File

@ -1,10 +1,10 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.ByteArrayDataOutputStream
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.util.getRandomKey
import net.mamoe.mirai.util.lazyEncode
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.getRandomKey
import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream
import java.net.InetAddress
@ -68,7 +68,7 @@ class ClientSessionRequestPacket(
*
* @author Him188moe
*/
class ServerSessionKeyResponsePacket(inputStream: DataInputStream, val dataLength: Int) : ServerPacket(inputStream) {
class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val dataLength: Int) : ServerPacket(inputStream) {
lateinit var sessionKey: ByteArray
lateinit var tlv0105: ByteArray

View File

@ -1,7 +1,7 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.*
import net.mamoe.mirai.utils.*
import java.io.DataInputStream
import java.io.IOException

View File

@ -2,7 +2,11 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.util.*
import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataOutputStream
/**

View File

@ -2,7 +2,7 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.util.ClientLoginStatus
import net.mamoe.mirai.utils.ClientLoginStatus
/**
* @author Him188moe

View File

@ -2,8 +2,8 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.util.ByteArrayDataOutputStream
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEACryptor
/**
* @author Him188moe

View File

@ -5,10 +5,10 @@ import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.network.packet.readNBytes
import net.mamoe.mirai.network.packet.readVarString
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.util.hexToBytes
import net.mamoe.mirai.util.toUHexString
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
/**

View File

@ -4,10 +4,10 @@ import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.util.hexToUBytes
import net.mamoe.mirai.util.toUHexString
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.hexToUBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
/**

View File

@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream
/**

View File

@ -1,17 +0,0 @@
package net.mamoe.mirai.network.packet.message
import net.mamoe.mirai.network.packet.ClientPacket
/**
* @author Him188moe
*/
@ExperimentalUnsignedTypes
class ClientFriendMessagePacket(
val qq: Int,
val sessionKey: ByteArray,
val message: String
) : ClientPacket() {
override fun encode() {
}
}

View File

@ -0,0 +1,64 @@
package net.mamoe.mirai.network.packet.message
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream
/**
* @author Him188moe
*/
@PacketId("00 CD")
@ExperimentalUnsignedTypes
class ClientSendFriendMessagePacket(
val robotQQ: Int,
val targetQQ: Int,
val sessionKey: ByteArray,
val message: String
) : ClientPacket() {
override fun encode() {
this.writeRandom(2)//part of packet id
this.writeQQ(robotQQ)
this.writeHex(Protocol._fixVer)
this.encryptAndWrite(sessionKey) {
it.writeQQ(robotQQ)
it.writeQQ(targetQQ)
it.writeHex("00 00 00 08 00 01 00 04 00 00 00 00")
it.writeHex("37 0F")
it.writeQQ(robotQQ)
it.writeQQ(targetQQ)
it.write(md5(lazyEncode { md5Key -> md5Key.writeQQ(targetQQ); md5Key.write(sessionKey) }))
it.writeHex("00 0B")
it.writeRandom(2)
it.writeTime()
it.writeHex("00 00 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00")
it.writeTime()
it.writeRandom(4)
it.writeHex("00 00 00 00 09 00 86 00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91")
it.writeZero(2)
if ("[face" in message
|| ".gif]" in message
|| ".jpg]" in message
|| ".png]" in message
) {
TODO("复合消息构建")
} else {
//Plain text
val bytes = message.toByteArray()
it.writeByte(0x01)
it.writeShort(bytes.size + 3)
it.writeByte(0x01)
it.writeShort(bytes.size)
it.write(bytes)
}//todo check
}
}
}
@PacketId("00 CD")
class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input) {
override fun decode() {
}
}

View File

@ -0,0 +1,50 @@
package net.mamoe.mirai.network.packet.message
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import java.io.DataInputStream
/**
* @author Him188moe
*/
@PacketId("00 02")
@ExperimentalUnsignedTypes
class ClientSendGroupMessagePacket(
private val groupId: Int,//不是 number
private val qq: Int,
private val sessionKey: ByteArray,
private val message: String
) : ClientPacket() {
override fun encode() {
this.writeRandom(2)//part of packet id
this.writeQQ(qq)
this.writeHex(Protocol._fixVer)
this.encryptAndWrite(sessionKey) {
it.writeHex("00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
it.writeTime()
it.writeRandom(4)
it.writeHex("00 00 00 00 09 00 86 00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91")
it.writeZero(2)
//messages
val bytes = message.toByteArray()
it.writeByte(0x2A)
it.writeInt(groupId)
it.writeShort(19 + bytes.size)
it.writeByte(0x01)
it.writeByte(0x01)
it.writeShort(bytes.size + 3)
it.writeByte(0x01)
it.writeShort(bytes.size)
it.write(bytes)
}
}
}
@PacketId("00 02")
class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input) {
override fun decode() {
}
}

View File

@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.verification
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream
/**

View File

@ -25,7 +25,7 @@ public final class MiraiTaskManager {
MiraiEventHook
.onEvent(ServerDisableEvent.class)
.setHandler(a -> this.pool.close())
.handler(a -> this.pool.close())
.mount();
}

View File

@ -1,34 +0,0 @@
package net.mamoe.mirai.util
import net.mamoe.mirai.network.packet.ClientPacket
import net.mamoe.mirai.network.packet.ServerPacket
/**
* @author Him188moe
*/
object DebugLogger {
val buff = StringBuilder()
}
fun ByteArray.encryptionDebugLogging() {
DebugLogger.buff.append("TEA encrypt: " + this.toUHexString()).append("\n")
}
fun ByteArray.packetSentDebugLogging() {
DebugLogger.buff.append("packet sent: " + this.toUHexString()).append("\n")
}
fun ByteArray.decryptionDebugLogging() {
DebugLogger.buff.append("TEA decrypted: " + this.toUHexString()).append("\n")
}
fun ServerPacket.logging() {
DebugLogger.buff.append(this.toString()).append("\n")
}
@ExperimentalUnsignedTypes
fun ClientPacket.logging() {
DebugLogger.buff.append(this.toString()).append("\n")
}

View File

@ -1,4 +1,4 @@
package net.mamoe.mirai.util;
package net.mamoe.mirai.utils;
/**
* @author Him188moe

View File

@ -38,4 +38,8 @@ object MiraiLogger {
}
fun log(any: Any?) = MiraiLogger.info(any)
fun Any.logInfo() = MiraiLogger.info(this)
fun Any.logDebug() = MiraiLogger.debug(this)
fun Any.logError() = MiraiLogger.error(this)

View File

@ -1,4 +1,4 @@
package net.mamoe.mirai.util;
package net.mamoe.mirai.utils;
import net.mamoe.mirai.network.Protocol;
@ -239,13 +239,10 @@ public class TEACryptor {
}
public byte[] encrypt(byte[] plaintext) {
DebugLoggerKt.encryptionDebugLogging(plaintext);
//System.out.println("TEA加密, 原文=" + Utils.INSTANCE.toHexString(plaintext, ""));
return encrypt(plaintext, 0, plaintext.length);
}
public byte[] decrypt(byte[] ciphertext) {
DebugLoggerKt.decryptionDebugLogging(ciphertext);
return decrypt(ciphertext, 0, ciphertext.length);
}
}

View File

@ -1,73 +0,0 @@
package net.mamoe.mirai.utils;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class Utils {
/**
* File supporting from Nukkit
* */
public static void writeFile(String fileName, String content) throws IOException {
writeFile(fileName, new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
}
public static void writeFile(String fileName, InputStream content) throws IOException {
writeFile(new File(fileName), content);
}
public static void writeFile(File file, String content) throws IOException {
writeFile(file, new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
}
public static void writeFile(File file, InputStream content) throws IOException {
if (content == null) {
throw new IllegalArgumentException("content must not be null");
}
if (!file.exists()) {
file.createNewFile();
}
try (FileOutputStream stream = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int length;
while ((length = content.read(buffer)) != -1) {
stream.write(buffer, 0, length);
}
}
content.close();
}
public static String readFile(File file) throws IOException {
if (!file.exists() || file.isDirectory()) {
throw new FileNotFoundException();
}
return readFile(new FileInputStream(file));
}
public static String readFile(String filename) throws IOException {
File file = new File(filename);
if (!file.exists() || file.isDirectory()) {
throw new FileNotFoundException();
}
return readFile(new FileInputStream(file));
}
public static String readFile(InputStream inputStream) throws IOException {
return readFile(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
}
private static String readFile(Reader reader) throws IOException {
try (BufferedReader br = new BufferedReader(reader)) {
String temp;
StringBuilder stringBuilder = new StringBuilder();
temp = br.readLine();
while (temp != null) {
if (stringBuilder.length() != 0) {
stringBuilder.append("\n");
}
stringBuilder.append(temp);
temp = br.readLine();
}
return stringBuilder.toString();
}
}
}

View File

@ -1,4 +1,4 @@
package net.mamoe.mirai.util
package net.mamoe.mirai.utils
import net.mamoe.mirai.network.Protocol
import java.io.ByteArrayOutputStream
@ -8,30 +8,15 @@ import java.lang.reflect.Field
import java.util.*
import java.util.zip.CRC32
/**
* @author Him188moe
*/
object Utils {
fun toHexString(byteArray: ByteArray, separator: String = " "): String = byteArray.joinToString(separator) {
var ret = it.toString(16).toUpperCase()
if (ret.length == 1) {
ret = "0$ret"
}
return@joinToString ret
}
@ExperimentalUnsignedTypes
fun toHexString(byteArray: UByteArray, separator: String = " "): String = byteArray.joinToString(separator) {
var ret = it.toString(16).toUpperCase()
if (ret.length == 1) {
ret = "0$ret"
}
return@joinToString ret
fun ByteArray.toHexString(): String = toHexString(" ")
fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(separator) {
var ret = it.toString(16).toUpperCase()
if (ret.length == 1) {
ret = "0$ret"
}
return@joinToString ret
}
fun ByteArray.toHexString(): String = toHexString(" ")
fun ByteArray.toHexString(separator: String = " "): String = Utils.toHexString(this, separator)
@ExperimentalUnsignedTypes
fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator)
@ -39,7 +24,13 @@ fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray(
fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString()
@ExperimentalUnsignedTypes
fun UByteArray.toUHexString(separator: String = " "): String = Utils.toHexString(this, separator)
fun UByteArray.toUHexString(separator: String = " "): String = this.joinToString(separator) {
var ret = it.toString(16).toUpperCase()
if (ret.length == 1) {
ret = "0$ret"
}
return@joinToString ret
}
@ExperimentalUnsignedTypes
fun UByteArray.toUHexString(): String = this.toUHexString(" ")
@ -94,10 +85,6 @@ fun getGTK(sKey: String): Int {
return value
}
fun main() {
println(getGTK("ABCDEFGEFC"))
}
fun getCrc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }

View File

@ -1,14 +1,18 @@
package net.mamoe.mirai.utils.config;
import kotlin.io.FilesKt;
import net.mamoe.mirai.MiraiServer;
import net.mamoe.mirai.utils.Utils;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Objects;
/**
* YAML-TYPE CONFIG
@ -35,7 +39,7 @@ public class MiraiConfig extends MiraiConfigSection<Object> {
Yaml yaml = new Yaml(dumperOptions);
String content = yaml.dump(this.sortedMap);
try {
Utils.writeFile(this.root, content);
new ByteArrayInputStream(content.getBytes()).transferTo(new FileOutputStream(this.root));
} catch (IOException e) {
e.printStackTrace();
}
@ -59,12 +63,7 @@ public class MiraiConfig extends MiraiConfigSection<Object> {
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(dumperOptions);
try {
return yaml.loadAs(Utils.readFile(file), LinkedHashMap.class);
} catch (IOException e) {
e.printStackTrace();
}
return new LinkedHashMap<>();
return yaml.loadAs(String.join("\n", FilesKt.readLines(file, Charset.defaultCharset())), LinkedHashMap.class);
}

View File

@ -1,5 +1,5 @@
import net.mamoe.mirai.network.packet.login.ClientPasswordSubmissionPacket
import net.mamoe.mirai.util.toUHexString
import net.mamoe.mirai.utils.toUHexString
@ExperimentalUnsignedTypes
fun main(){