Commit 4a8f7255 authored by liujiahua123123's avatar liujiahua123123

Merge remote-tracking branch 'origin/master'

parents c2dea1d7 c8fd31f4
...@@ -3,15 +3,21 @@ ...@@ -3,15 +3,21 @@
一个以<b>TIM QQ协议</b>驱动的JAVA(+Kotlin) QQ机器人服务端核心 一个以<b>TIM QQ协议</b>驱动的JAVA(+Kotlin) QQ机器人服务端核心
我们坚持免费与开源 我们坚持免费与开源
项目处于快速开发阶段, 现在已经可以接受群聊/好友消息. 项目处于快速开发阶段, 现在已经可以接受和发送群聊/好友消息.
协议来自网络的易语言开源软件 协议来自网络上开源项目
一切开发旨在学习, 请勿用于非法用途 一切开发旨在学习, 请勿用于非法用途
### 我们会坚持开发, 但是, <br>
![LV7_YX10AQ5TW@E~308_0JN.png](https://i.loli.net/2019/08/24/oQIzhaLvyJOeW1f.png)
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 ### TODO
- [x] 事件(Event)模块 - [x] 事件(Event)模块
...@@ -19,25 +25,23 @@ Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持), 与 ...@@ -19,25 +25,23 @@ Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持), 与
- [x] Network - Touch - [x] Network - Touch
- [X] Network - Login - [X] Network - Login
- [X] Network - Session - [X] Network - Session
- [X] Network - Message Receive - [ ] Network - Verification Code (Low priority)
- [X] Network - Message Send - [X] Network - Message Receiving
- [X] Network - Message Sending
- [ ] Network - Events **(Working on)** - [ ] Network - Events **(Working on)**
- [ ] Robot - Friend/group list
<br> - [ ] Message Section **(Working on)**
- [ ] Contact
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>
<br> <br>
## 使用方法 ## 使用方法
### 要求 ### 要求
- Java 11 或更高 - Java 11 或更高
- Kotlin - Kotlin 1.3 或更高
### 插件开发 ### 插件开发
``` php ``` 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 ...@@ -45,10 +49,10 @@ The project is all for <b>learning proposes</b> and still in <b>developing stage
## Usage ## Usage
### Requirements ### Requirements
- Java 11 or higher - Java 11 or higher
- Kotlin - Kotlin 1.3 or higher
### Plugin Developments ### Plugin Development
``` php ``` php
to be continue to be continued
... ...
``` ```
......
package net.mamoe.mirai.contact 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 * @author Him188moe
*/ */
...@@ -10,10 +13,14 @@ abstract class Contact(val number: Int) { ...@@ -10,10 +13,14 @@ abstract class Contact(val number: Int) {
/** /**
* Async * Async
*/ */
abstract fun sendMessage(message: String) abstract fun sendMessage(message: Message)
fun sendMessage(message: String) {
this.sendMessage(PlainText(message))
}
/** /**
* Async * Async
*/ */
abstract fun sendObjectMessage(message: String) abstract fun sendXMLMessage(message: String)
} }
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.message.Message
class Group(number: Int) : Contact(number) { class Group(number: Int) : Contact(number) {
val groupId = groupNumberToId(number)
init { init {
Instances.groups.add(this) 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
}
}
} }
} }
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.At
/** /**
* @author Him188moe * @author Him188moe
*/ */
...@@ -8,19 +11,19 @@ class QQ(number: Int) : Contact(number) { ...@@ -8,19 +11,19 @@ class QQ(number: Int) : Contact(number) {
Instances.qqs.add(this) 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. * At(@) this account.
*/ */
fun at(): String { fun at(): At {
return "[@$number]" return At(this)
} }
......
...@@ -39,20 +39,20 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable { ...@@ -39,20 +39,20 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
public MiraiEventHook(Class<T> eventClass, Consumer<T> handler){ public MiraiEventHook(Class<T> eventClass, Consumer<T> handler){
this.eventClass = eventClass; 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; this.handler = handler;
return this; return this;
} }
public MiraiEventHook<T> setPriority(int priority){ public MiraiEventHook<T> priority(int priority) {
this.priority = priority; this.priority = priority;
return this; return this;
} }
public MiraiEventHook<T> setIgnoreCancelled(boolean ignoreCancelled){ public MiraiEventHook<T> ignoreCancelled(boolean ignoreCancelled) {
this.ignoreCancelled = ignoreCancelled; this.ignoreCancelled = ignoreCancelled;
return this; return this;
} }
......
...@@ -4,52 +4,60 @@ import net.mamoe.mirai.MiraiServer; ...@@ -4,52 +4,60 @@ import net.mamoe.mirai.MiraiServer;
import net.mamoe.mirai.event.events.MiraiEvent; import net.mamoe.mirai.event.events.MiraiEvent;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class MiraiEventManager { public class MiraiEventManager {
private MiraiEventManager(){ private MiraiEventManager() {
} }
private static MiraiEventManager instance; private static MiraiEventManager instance = new MiraiEventManager();
public static MiraiEventManager getInstance(){ public static MiraiEventManager getInstance() {
if(MiraiEventManager.instance == null){
MiraiEventManager.instance = new MiraiEventManager();
}
return MiraiEventManager.instance; 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<>(); 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)); 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)); this.mountHook(hook.setValidWhile(toKeep));
} }
public <D extends MiraiEvent> void hookOnce(MiraiEventHook<D> hook){ public <D extends MiraiEvent> void hookAlways(MiraiEventHook<D> hook) {
this.hookUntil(hook,(a) -> true); this.hookUntil(hook, (a) -> false);
}
public <D extends MiraiEvent> void hookOnce(MiraiEventHook<D> hook) {
this.hookUntil(hook, (a) -> true);
} }
public <D extends MiraiEvent> void registerHook(MiraiEventHook<D> hook){ public <D extends MiraiEvent> void registerHook(MiraiEventHook<D> hook) {
this.mountHook(hook); this.mountHook(hook);
} }
private <D extends MiraiEvent> void mountHook(MiraiEventHook<D> hook){ private <D extends MiraiEvent> void mountHook(MiraiEventHook<D> hook) {
if(!hook.isMount()) { if (!hook.isMount()) {
hook.setMount(true); hook.setMount(true);
hooksLock.lock(); hooksLock.writeLock().lock();
hooks.putIfAbsent(hook.getEventClass(), new ArrayList<>()); try {
hooks.get(hook.getEventClass()).add(hook); if (!hooks.containsKey(hook.getEventClass())) {
hooksLock.unlock(); 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 { ...@@ -57,53 +65,55 @@ public class MiraiEventManager {
* 不推荐onEvent * 不推荐onEvent
* 由于不能保证Hook的原子性 非线程安全 * 由于不能保证Hook的原子性 非线程安全
* 不能保证下一个 D event发生时handler就位 * 不能保证下一个 D event发生时handler就位
*
* @author NaturalHG Aug27 * @author NaturalHG Aug27
* use {@link MiraiEventHook::onEvent()} to replace * use {@link MiraiEventHook::onEvent()} to replace
*/ */
@Deprecated @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); MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.registerHook(hook); this.registerHook(hook);
return hook; return hook;
} }
@Deprecated @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); MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.hookOnce(hook); this.hookOnce(hook);
return hook; return hook;
} }
@Deprecated @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); MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.hookUntil(hook,toRemove); this.hookUntil(hook, toRemove);
return hook; return hook;
} }
@Deprecated @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); MiraiEventHook<D> hook = new MiraiEventHook<>(event);
this.hookWhile(hook,toKeep); this.hookWhile(hook, toKeep);
return hook; return hook;
} }
public void broadcastEvent(MiraiEvent event) {
hooksLock.readLock().lock();
public void broadcastEvent(MiraiEvent event){ try {
hooksLock.lock(); if (hooks.containsKey(event.getClass())) {
if(hooks.containsKey(event.getClass())){ hooks.put(event.getClass(),
hooks.put(event.getClass(), hooks.get(event.getClass())
hooks.get(event.getClass()) .stream()
.stream() .sorted(Comparator.comparingInt(MiraiEventHook::getPriority))
.sorted(Comparator.comparingInt(MiraiEventHook::getPriority)) .filter(a -> !a.accept(event))
.filter(a -> !a.accept(event)) .collect(Collectors.toList())
.collect(Collectors.toList()) );
); }
} finally {
hooksLock.readLock().unlock();
} }
hooksLock.unlock();
} }
...@@ -116,10 +126,14 @@ public class MiraiEventManager { ...@@ -116,10 +126,14 @@ public class MiraiEventManager {
MiraiServer.getInstance().getTaskManager().ansycTask(() -> { MiraiServer.getInstance().getTaskManager().ansycTask(() -> {
MiraiEventManager.this.broadcastEvent(event); MiraiEventManager.this.broadcastEvent(event);
return event; return event;
},callback); }, callback);
} }
public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Runnable callback) {
asyncBroadcastEvent(event, t -> callback.run());
}
} }
......
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)
}
}
\ No newline at end of file
package net.mamoe.mirai.event.events; package net.mamoe.mirai.event.events;
/**
* @author NaturalHG
*/
public interface Cancellable { public interface Cancellable {
boolean isCancelled(); boolean isCancelled();
void cancel(boolean forceCancel); void cancel(boolean forceCancel);
......
package net.mamoe.mirai.event.events; package net.mamoe.mirai.event.events;
import net.mamoe.mirai.event.MiraiEventManager;
import net.mamoe.mirai.utils.EventException; import net.mamoe.mirai.utils.EventException;
import java.util.function.Consumer;
public abstract class MiraiEvent { public abstract class MiraiEvent {
private boolean cancelled; private boolean cancelled;
...@@ -25,12 +28,26 @@ public abstract class MiraiEvent { ...@@ -25,12 +28,26 @@ public abstract class MiraiEvent {
} }
protected String eventName; protected String eventName;
public String getEventName() { public String getEventName() {
if(this.eventName == null){ if (this.eventName == null) {
return this.getClass().getSimpleName(); return this.getClass().getSimpleName();
} }
return this.eventName; 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);
}
} }
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;
}
}
package net.mamoe.mirai.message; 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;
}
} }
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)
\ No newline at end of file
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;
}
}
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();
}
}
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;
}
}
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;
}
}
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;
}
}
...@@ -2,15 +2,15 @@ package net.mamoe.mirai.network ...@@ -2,15 +2,15 @@ package net.mamoe.mirai.network
import net.mamoe.mirai.MiraiServer import net.mamoe.mirai.MiraiServer
import net.mamoe.mirai.Robot import net.mamoe.mirai.Robot
import net.mamoe.mirai.event.MiraiEventManager
import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.login.* 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.ServerVerificationCodePacket
import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted
import net.mamoe.mirai.task.MiraiThreadPool import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.util.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.MiraiLogger
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.net.DatagramPacket import java.net.DatagramPacket
...@@ -196,7 +196,7 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor ...@@ -196,7 +196,7 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor
MiraiThreadPool.getInstance().scheduleWithFixedDelay({ MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey)) sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS) }, 90000, 90000, TimeUnit.MILLISECONDS)
MiraiEventManager.getInstance().asyncBroadcastEvent(RobotLoginSucceedEvent(robot)) RobotLoginSucceedEvent(robot).broadcast()
this.tlv0105 = packet.tlv0105 this.tlv0105 = packet.tlv0105
sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE)) sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE))
} }
...@@ -229,11 +229,19 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor ...@@ -229,11 +229,19 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor
is ServerFriendMessageEventPacket -> { is ServerFriendMessageEventPacket -> {
println(packet.toString())
if (packet.message == "牛逼") {
sendPacket(ClientSendFriendMessagePacket(this.number, packet.qq, this.sessionKey, "牛逼!!"))
}
//friend message //friend message
} }
is ServerGroupMessageEventPacket -> { is ServerGroupMessageEventPacket -> {
//group message //group message
if (packet.message == "牛逼") {
sendPacket(ClientSendGroupMessagePacket(packet.group, this.number, this.sessionKey, "牛逼!"))
}
} }
is UnknownServerEventPacket -> { is UnknownServerEventPacket -> {
......
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);
}
}
}
\ No newline at end of file
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream import java.io.DataInputStream
/** /**
......
...@@ -2,7 +2,8 @@ package net.mamoe.mirai.network.packet ...@@ -2,7 +2,8 @@ package net.mamoe.mirai.network.packet
import lombok.Getter import lombok.Getter
import net.mamoe.mirai.network.Protocol 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.DataOutputStream
import java.io.IOException import java.io.IOException
import java.net.InetAddress import java.net.InetAddress
...@@ -72,6 +73,10 @@ fun DataOutputStream.writeIP(ip: String) { ...@@ -72,6 +73,10 @@ fun DataOutputStream.writeIP(ip: String) {
} }
} }
@Throws(IOException::class)
fun DataOutputStream.writeTime() {
this.writeInt(System.currentTimeMillis().toInt())
}
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@Throws(IOException::class) @Throws(IOException::class)
...@@ -201,9 +206,9 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf( ...@@ -201,9 +206,9 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf(
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun Int.toHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator); 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 @ExperimentalUnsignedTypes
@Throws(IOException::class) @Throws(IOException::class)
......
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.util.toUHexString import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
...@@ -11,7 +11,7 @@ import java.io.DataInputStream ...@@ -11,7 +11,7 @@ import java.io.DataInputStream
*/ */
@PacketId("")//随后写入 @PacketId("")//随后写入
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
open class ClientMessageResponsePacket( class ClientMessageResponsePacket(
private val qq: Int, private val qq: Int,
private val packetIdFromServer: ByteArray, private val packetIdFromServer: ByteArray,
private val sessionKey: ByteArray, private val sessionKey: ByteArray,
......
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream import java.io.DataInputStream
......
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.util.toUHexString import net.mamoe.mirai.utils.toUHexString
import net.mamoe.mirai.utils.MiraiLogger
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.DataInputStream import java.io.DataInputStream
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
...@@ -33,7 +32,7 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr ...@@ -33,7 +32,7 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr
lateinit var message: String lateinit var message: String
override fun decode() { 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 }//todo test
} }
...@@ -47,7 +46,12 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, ...@@ -47,7 +46,12 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
NORMAL, NORMAL,
XML, XML,
AT, AT,
IMAGE, FACE,//qq自带表情 [face107.gif]
PLAIN_TEXT, //纯文本
IMAGE, //自定义图片 {F50C5235-F958-6DF7-4EFA-397736E125A4}.gif
ANONYMOUS,//匿名用户发出的消息
OTHER, OTHER,
} }
...@@ -56,17 +60,26 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, ...@@ -56,17 +60,26 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
group = this.input.goto(51).readInt() group = this.input.goto(51).readInt()
qq = this.input.goto(56).readInt() qq = this.input.goto(56).readInt()
val fontLength = this.input.goto(108).readShort() 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()) { messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) {
19 -> MessageType.NORMAL 19 -> MessageType.NORMAL
14 -> MessageType.XML 14 -> MessageType.XML
2 -> MessageType.IMAGE
6 -> MessageType.AT 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) { when (messageType) {
MessageType.NORMAL -> { MessageType.NORMAL -> {
val gzippedMessage = this.input.goto(110 + fontLength + 16).readNBytes(this.input.goto(110 + fontLength + 3).readShort().toInt() - 11) 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, ...@@ -84,7 +97,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
} }
} }
MessageType.IMAGE -> { MessageType.FACE -> {
val faceId = this.input.goto(110 + fontLength + 8).readByte() val faceId = this.input.goto(110 + fontLength + 8).readByte()
message = "[face${faceId}.gif]" message = "[face${faceId}.gif]"
} }
...@@ -123,9 +136,6 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, ...@@ -123,9 +136,6 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
}*/ }*/
} }
} }
MiraiLogger info this.toString()
} }
} }
......
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.packet.login.* 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.network.packet.verification.ServerVerificationCodePacketEncrypted
import net.mamoe.mirai.util.getAllDeclaredFields import net.mamoe.mirai.utils.getAllDeclaredFields
import net.mamoe.mirai.util.hexToBytes import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.util.toUHexString import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
/** /**
...@@ -78,6 +80,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { ...@@ -78,6 +80,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"00 81" -> UnknownServerPacket(stream) "00 81" -> UnknownServerPacket(stream)
"00 CD" -> ServerSendFriendMessageResponsePacket(stream)
"00 02" -> ServerSendGroupMessageResponsePacket(stream)
else -> throw IllegalArgumentException(idHex) else -> throw IllegalArgumentException(idHex)
} }
} }
......
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.ByteArrayDataOutputStream import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.util.getRandomKey import net.mamoe.mirai.utils.getRandomKey
import net.mamoe.mirai.util.lazyEncode import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream import java.io.DataInputStream
import java.net.InetAddress import java.net.InetAddress
...@@ -68,7 +68,7 @@ class ClientSessionRequestPacket( ...@@ -68,7 +68,7 @@ class ClientSessionRequestPacket(
* *
* @author Him188moe * @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 sessionKey: ByteArray
lateinit var tlv0105: ByteArray lateinit var tlv0105: ByteArray
......
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.* import net.mamoe.mirai.utils.*
import java.io.DataInputStream import java.io.DataInputStream
import java.io.IOException import java.io.IOException
......
...@@ -2,7 +2,11 @@ package net.mamoe.mirai.network.packet.login ...@@ -2,7 +2,11 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.* 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 import java.io.DataOutputStream
/** /**
......
...@@ -2,7 +2,7 @@ package net.mamoe.mirai.network.packet.login ...@@ -2,7 +2,7 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.util.ClientLoginStatus import net.mamoe.mirai.utils.ClientLoginStatus
/** /**
* @author Him188moe * @author Him188moe
......
...@@ -2,8 +2,8 @@ package net.mamoe.mirai.network.packet.login ...@@ -2,8 +2,8 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.util.ByteArrayDataOutputStream import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.utils.TEACryptor
/** /**
* @author Him188moe * @author Him188moe
......
...@@ -5,10 +5,10 @@ import net.mamoe.mirai.network.packet.ServerPacket ...@@ -5,10 +5,10 @@ import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.goto import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.network.packet.readNBytes import net.mamoe.mirai.network.packet.readNBytes
import net.mamoe.mirai.network.packet.readVarString import net.mamoe.mirai.network.packet.readVarString
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.util.TestedSuccessfully import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.util.hexToBytes import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.util.toUHexString import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
/** /**
......
...@@ -4,10 +4,10 @@ import net.mamoe.mirai.network.packet.PacketId ...@@ -4,10 +4,10 @@ import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.ServerPacket import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.util.TEACryptor
import net.mamoe.mirai.util.TestedSuccessfully import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.util.hexToUBytes import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.util.toUHexString import net.mamoe.mirai.utils.hexToUBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
/** /**
......
...@@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.login ...@@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.packet.ServerPacket import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream import java.io.DataInputStream
/** /**
......
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() {
}
}
\ No newline at end of file
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() {
}
}
\ No newline at end of file
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() {
}
}
\ No newline at end of file
...@@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.verification ...@@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.verification
import net.mamoe.mirai.network.packet.ServerPacket import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream import java.io.DataInputStream
/** /**
......
...@@ -25,7 +25,7 @@ public final class MiraiTaskManager { ...@@ -25,7 +25,7 @@ public final class MiraiTaskManager {
MiraiEventHook MiraiEventHook
.onEvent(ServerDisableEvent.class) .onEvent(ServerDisableEvent.class)
.setHandler(a -> this.pool.close()) .handler(a -> this.pool.close())
.mount(); .mount();
} }
......
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")
}
\ No newline at end of file
package net.mamoe.mirai.util; package net.mamoe.mirai.utils;
/** /**
* @author Him188moe * @author Him188moe
......
...@@ -38,4 +38,8 @@ object MiraiLogger { ...@@ -38,4 +38,8 @@ object MiraiLogger {
} }
fun log(any: Any?) = MiraiLogger.info(any) fun Any.logInfo() = MiraiLogger.info(this)
\ No newline at end of file
fun Any.logDebug() = MiraiLogger.debug(this)
fun Any.logError() = MiraiLogger.error(this)
\ No newline at end of file
package net.mamoe.mirai.util; package net.mamoe.mirai.utils;
import net.mamoe.mirai.network.Protocol; import net.mamoe.mirai.network.Protocol;
...@@ -239,13 +239,10 @@ public class TEACryptor { ...@@ -239,13 +239,10 @@ public class TEACryptor {
} }
public byte[] encrypt(byte[] plaintext) { public byte[] encrypt(byte[] plaintext) {
DebugLoggerKt.encryptionDebugLogging(plaintext);
//System.out.println("TEA加密, 原文=" + Utils.INSTANCE.toHexString(plaintext, ""));
return encrypt(plaintext, 0, plaintext.length); return encrypt(plaintext, 0, plaintext.length);
} }
public byte[] decrypt(byte[] ciphertext) { public byte[] decrypt(byte[] ciphertext) {
DebugLoggerKt.decryptionDebugLogging(ciphertext);
return decrypt(ciphertext, 0, ciphertext.length); return decrypt(ciphertext, 0, ciphertext.length);
} }
} }
\ No newline at end of file
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();
}
}
}
package net.mamoe.mirai.util package net.mamoe.mirai.utils
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
...@@ -8,30 +8,15 @@ import java.lang.reflect.Field ...@@ -8,30 +8,15 @@ import java.lang.reflect.Field
import java.util.* import java.util.*
import java.util.zip.CRC32 import java.util.zip.CRC32
/** fun ByteArray.toHexString(): String = toHexString(" ")
* @author Him188moe fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(separator) {
*/ var ret = it.toString(16).toUpperCase()
object Utils { if (ret.length == 1) {
fun toHexString(byteArray: ByteArray, separator: String = " "): String = byteArray.joinToString(separator) { ret = "0$ret"
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
} }
return@joinToString ret
} }
fun ByteArray.toHexString(): String = toHexString(" ")
fun ByteArray.toHexString(separator: String = " "): String = Utils.toHexString(this, separator)
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator) fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator)
...@@ -39,7 +24,13 @@ fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray( ...@@ -39,7 +24,13 @@ fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray(
fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString() fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString()
@ExperimentalUnsignedTypes @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 @ExperimentalUnsignedTypes
fun UByteArray.toUHexString(): String = this.toUHexString(" ") fun UByteArray.toUHexString(): String = this.toUHexString(" ")
...@@ -94,10 +85,6 @@ fun getGTK(sKey: String): Int { ...@@ -94,10 +85,6 @@ fun getGTK(sKey: String): Int {
return value return value
} }
fun main() {
println(getGTK("ABCDEFGEFC"))
}
fun getCrc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() } fun getCrc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
......
package net.mamoe.mirai.utils.config; package net.mamoe.mirai.utils.config;
import kotlin.io.FilesKt;
import net.mamoe.mirai.MiraiServer; import net.mamoe.mirai.MiraiServer;
import net.mamoe.mirai.utils.Utils;
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;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Objects;
/** /**
* YAML-TYPE CONFIG * YAML-TYPE CONFIG
...@@ -35,7 +39,7 @@ public class MiraiConfig extends MiraiConfigSection<Object> { ...@@ -35,7 +39,7 @@ public class MiraiConfig extends MiraiConfigSection<Object> {
Yaml yaml = new Yaml(dumperOptions); Yaml yaml = new Yaml(dumperOptions);
String content = yaml.dump(this.sortedMap); String content = yaml.dump(this.sortedMap);
try { try {
Utils.writeFile(this.root, content); new ByteArrayInputStream(content.getBytes()).transferTo(new FileOutputStream(this.root));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
...@@ -59,12 +63,7 @@ public class MiraiConfig extends MiraiConfigSection<Object> { ...@@ -59,12 +63,7 @@ public class MiraiConfig extends MiraiConfigSection<Object> {
DumperOptions dumperOptions = new DumperOptions(); DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(dumperOptions); Yaml yaml = new Yaml(dumperOptions);
try { return yaml.loadAs(String.join("\n", FilesKt.readLines(file, Charset.defaultCharset())), LinkedHashMap.class);
return yaml.loadAs(Utils.readFile(file), LinkedHashMap.class);
} catch (IOException e) {
e.printStackTrace();
}
return new LinkedHashMap<>();
} }
......
import net.mamoe.mirai.network.packet.login.ClientPasswordSubmissionPacket import net.mamoe.mirai.network.packet.login.ClientPasswordSubmissionPacket
import net.mamoe.mirai.util.toUHexString import net.mamoe.mirai.utils.toUHexString
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun main(){ fun main(){
......
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