mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-13 06:30:13 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
4a8f7255ec
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB |
40
README.md
40
README.md
@ -3,15 +3,21 @@
|
||||
一个以<b>TIM QQ协议</b>驱动的JAVA(+Kotlin) QQ机器人服务端核心
|
||||
我们坚持免费与开源
|
||||
|
||||
项目处于快速开发阶段, 现在已经可以接受群聊/好友消息.
|
||||
协议来自网络的易语言开源软件
|
||||
项目处于快速开发阶段, 现在已经可以接受和发送群聊/好友消息.
|
||||
协议来自网络上开源项目
|
||||
一切开发旨在学习, 请勿用于非法用途
|
||||
|
||||
### 我们会坚持开发, 但是,
|
||||

|
||||
<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
|
||||
...
|
||||
```
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package net.mamoe.mirai.event.events;
|
||||
|
||||
/**
|
||||
* @author NaturalHG
|
||||
*/
|
||||
public interface Cancellable {
|
||||
|
||||
|
||||
boolean isCancelled();
|
||||
|
||||
void cancel(boolean forceCancel);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
21
mirai-core/src/main/java/net/mamoe/mirai/message/FaceID.java
Normal file
21
mirai-core/src/main/java/net/mamoe/mirai/message/FaceID.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 -> {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
@ -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() {
|
||||
}
|
||||
}
|
@ -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() {
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ public final class MiraiTaskManager {
|
||||
|
||||
MiraiEventHook
|
||||
.onEvent(ServerDisableEvent.class)
|
||||
.setHandler(a -> this.pool.close())
|
||||
.handler(a -> this.pool.close())
|
||||
.mount();
|
||||
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.mamoe.mirai.util;
|
||||
package net.mamoe.mirai.utils;
|
||||
|
||||
/**
|
||||
* @author Him188moe
|
@ -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)
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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() }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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(){
|
||||
|
Loading…
Reference in New Issue
Block a user