mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-23 18:30:13 +08:00
send image
This commit is contained in:
parent
37fa8055b5
commit
ff0acc87e3
@ -17,6 +17,19 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.pcap4j</groupId>
|
||||||
|
<artifactId>pcap4j-core</artifactId>
|
||||||
|
<version>1.8.2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.pcap4j</groupId>
|
||||||
|
<artifactId>pcap4j-packetfactory-static</artifactId>
|
||||||
|
<version>1.8.2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.mamoe</groupId>
|
<groupId>net.mamoe</groupId>
|
||||||
<artifactId>mirai-api</artifactId>
|
<artifactId>mirai-api</artifactId>
|
||||||
|
@ -3,6 +3,7 @@ package net.mamoe.mirai;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.mamoe.mirai.contact.Group;
|
import net.mamoe.mirai.contact.Group;
|
||||||
import net.mamoe.mirai.contact.QQ;
|
import net.mamoe.mirai.contact.QQ;
|
||||||
|
import net.mamoe.mirai.network.BotNetworkHandler;
|
||||||
import net.mamoe.mirai.network.BotNetworkHandlerImpl;
|
import net.mamoe.mirai.network.BotNetworkHandlerImpl;
|
||||||
import net.mamoe.mirai.utils.BotAccount;
|
import net.mamoe.mirai.utils.BotAccount;
|
||||||
import net.mamoe.mirai.utils.ContactList;
|
import net.mamoe.mirai.utils.ContactList;
|
||||||
@ -53,7 +54,7 @@ public final class Bot implements Closeable {
|
|||||||
|
|
||||||
public final ContactSystem contacts = new ContactSystem();
|
public final ContactSystem contacts = new ContactSystem();
|
||||||
|
|
||||||
public final BotNetworkHandlerImpl network;
|
public final BotNetworkHandler network;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -212,22 +212,29 @@ public final class MiraiServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String qqList = "3150499752----1234567890\n" +
|
String qqList =
|
||||||
"3119292829----987654321\n" +
|
"2573990098----qq123456789\n" +
|
||||||
"2399148773----12345678910\n" +
|
"3303923865----q123456789\n" +
|
||||||
"3145561616----987654321\n" +
|
"3349933294----q123456789\n" +
|
||||||
"2374150554----12345678910\n" +
|
"3303708824----q123456789\n" +
|
||||||
"2375307394----12345678910\n" +
|
"3227036647----q123456789\n" +
|
||||||
"2401645747----12345678910\n" +
|
"3451394431----q123456789\n" +
|
||||||
"1515419818----1234567890\n" +
|
"3533243484----q123456789\n" +
|
||||||
"3107367848----987654321\n";
|
"3364512686----q123456789\n" +
|
||||||
|
"3137567463----q123456789\n" +
|
||||||
|
"3414786399----q123456789\n" +
|
||||||
|
"3347405939----q123456789\n" +
|
||||||
|
"3544089622----q123456789\n" +
|
||||||
|
"3108512993----q123456789\n" +
|
||||||
|
"2985563549----q123456789\n" +
|
||||||
|
"3463531892----q123456789\n";
|
||||||
|
|
||||||
private Bot getAvailableBot() throws ExecutionException, InterruptedException {
|
private Bot getAvailableBot() throws ExecutionException, InterruptedException {
|
||||||
for (String it : qqList.split("\n")) {
|
for (String it : qqList.split("\n")) {
|
||||||
var strings = it.split("----");
|
var strings = it.split("----");
|
||||||
var bot = new Bot(new BotAccount(Long.parseLong(strings[0]), strings[1]), List.of());
|
var bot = new Bot(new BotAccount(Long.parseLong(strings[0]), strings[1]), List.of());
|
||||||
|
|
||||||
if (bot.network.tryLogin$mirai_core().get() == LoginState.SUCCESS) {
|
if (bot.network.tryLogin(200).get() == LoginState.SUCCESS) {
|
||||||
MiraiLoggerKt.success(bot, "Login succeed");
|
MiraiLoggerKt.success(bot, "Login succeed");
|
||||||
return bot;
|
return bot;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,9 @@ import net.mamoe.mirai.Bot
|
|||||||
import net.mamoe.mirai.message.Message
|
import net.mamoe.mirai.message.Message
|
||||||
import net.mamoe.mirai.message.defaults.MessageChain
|
import net.mamoe.mirai.message.defaults.MessageChain
|
||||||
import net.mamoe.mirai.message.defaults.PlainText
|
import net.mamoe.mirai.message.defaults.PlainText
|
||||||
|
import net.mamoe.mirai.message.defaults.UnsolvedImage
|
||||||
|
import net.mamoe.mirai.network.LoginSession
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 联系人.
|
* 联系人.
|
||||||
@ -20,6 +23,13 @@ abstract class Contact internal constructor(val bot: Bot, val number: Long) {
|
|||||||
*/
|
*/
|
||||||
abstract fun sendMessage(message: MessageChain)
|
abstract fun sendMessage(message: MessageChain)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传图片
|
||||||
|
*/
|
||||||
|
fun uploadImage(session: LoginSession, image: UnsolvedImage): CompletableFuture<Unit> {
|
||||||
|
return image.upload(session, this)
|
||||||
|
}
|
||||||
|
|
||||||
fun sendMessage(message: Message) {
|
fun sendMessage(message: Message) {
|
||||||
if (message is MessageChain) {
|
if (message is MessageChain) {
|
||||||
return sendMessage(message)
|
return sendMessage(message)
|
||||||
|
@ -12,7 +12,7 @@ import net.mamoe.mirai.message.defaults.MessageChain
|
|||||||
* Java 获取 qq 号: `qq.getNumber()`
|
* Java 获取 qq 号: `qq.getNumber()`
|
||||||
* Java 获取所属 bot: `qq.getBot()`
|
* Java 获取所属 bot: `qq.getBot()`
|
||||||
*
|
*
|
||||||
* A QQ instance helps you to receive message from or send message to.
|
* A QQ instance helps you to receive message from or sendPacket message to.
|
||||||
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
|
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
|
||||||
*
|
*
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
|
@ -4,6 +4,8 @@ package net.mamoe.mirai.message;
|
|||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
public enum FaceID {
|
public enum FaceID {
|
||||||
|
unknown(0xff),
|
||||||
|
|
||||||
Face_jingya(0),
|
Face_jingya(0),
|
||||||
Face_piezui(1),
|
Face_piezui(1),
|
||||||
Face_se(2),
|
Face_se(2),
|
||||||
|
@ -2,10 +2,7 @@ package net.mamoe.mirai.message
|
|||||||
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
import net.mamoe.mirai.contact.Contact
|
||||||
import net.mamoe.mirai.contact.QQ
|
import net.mamoe.mirai.contact.QQ
|
||||||
import net.mamoe.mirai.message.defaults.At
|
import net.mamoe.mirai.message.defaults.*
|
||||||
import net.mamoe.mirai.message.defaults.Image
|
|
||||||
import net.mamoe.mirai.message.defaults.MessageChain
|
|
||||||
import net.mamoe.mirai.message.defaults.PlainText
|
|
||||||
import java.awt.image.BufferedImage
|
import java.awt.image.BufferedImage
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -106,41 +103,22 @@ abstract class Message {
|
|||||||
* @param tail tail
|
* @param tail tail
|
||||||
* @return message connected
|
* @return message connected
|
||||||
*/
|
*/
|
||||||
open fun concat(tail: Message): Message {
|
open fun concat(tail: Message): MessageChain {
|
||||||
return MessageChain(this, Objects.requireNonNull(tail))
|
return MessageChain(this, Objects.requireNonNull(tail))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun concat(tail: String): Message {
|
fun concat(tail: String): MessageChain {
|
||||||
return concat(PlainText(tail))
|
return concat(PlainText(tail))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun withImage(imageId: String): Message {
|
infix fun withImage(imageId: String): MessageChain = this + Image(imageId)
|
||||||
|
fun withImage(filename: String, image: BufferedImage): MessageChain = this + UnsolvedImage(filename, image)
|
||||||
|
infix fun withImage(imageFile: File): MessageChain = this + UnsolvedImage(imageFile)
|
||||||
|
|
||||||
// TODO: 2019/9/1
|
infix fun withAt(target: QQ): MessageChain = this + target.at()
|
||||||
return this
|
infix fun withAt(target: Long): MessageChain = this + At(target)
|
||||||
}
|
|
||||||
|
|
||||||
fun withImage(image: BufferedImage): Message {
|
|
||||||
// TODO: 2019/9/1
|
|
||||||
return this
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withImage(image: File): Message {
|
|
||||||
// TODO: 2019/9/1
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withAt(target: QQ): Message {
|
|
||||||
this.concat(target.at())
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withAt(target: Int): Message {
|
|
||||||
this.concat(At(target.toLong()))
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun toChain(): MessageChain {
|
open fun toChain(): MessageChain {
|
||||||
return MessageChain(this)
|
return MessageChain(this)
|
||||||
@ -152,22 +130,22 @@ abstract class Message {
|
|||||||
/**
|
/**
|
||||||
* 实现使用 '+' 操作符连接 [Message] 与 [Message]
|
* 实现使用 '+' 操作符连接 [Message] 与 [Message]
|
||||||
*/
|
*/
|
||||||
infix operator fun plus(another: Message): Message = this.concat(another)
|
infix operator fun plus(another: Message): MessageChain = this.concat(another)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实现使用 '+' 操作符连接 [Message] 与 [String]
|
* 实现使用 '+' 操作符连接 [Message] 与 [String]
|
||||||
*/
|
*/
|
||||||
infix operator fun plus(another: String): Message = this.concat(another)
|
infix operator fun plus(another: String): MessageChain = this.concat(another)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实现使用 '+' 操作符连接 [Message] 与 [Number]
|
* 实现使用 '+' 操作符连接 [Message] 与 [Number]
|
||||||
*/
|
*/
|
||||||
infix operator fun plus(another: Number): Message = this.concat(another.toString())
|
infix operator fun plus(another: Number): MessageChain = this.concat(another.toString())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接 [String] 与 [Message]
|
* 连接 [String] 与 [Message]
|
||||||
*/
|
*/
|
||||||
fun String.concat(another: Message): Message = PlainText(this).concat(another)
|
fun String.concat(another: Message): MessageChain = PlainText(this).concat(another)
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return javaClass.hashCode()
|
return javaClass.hashCode()
|
||||||
|
@ -3,26 +3,38 @@ package net.mamoe.mirai.message.defaults
|
|||||||
import net.mamoe.mirai.message.FaceID
|
import net.mamoe.mirai.message.FaceID
|
||||||
import net.mamoe.mirai.message.Message
|
import net.mamoe.mirai.message.Message
|
||||||
import net.mamoe.mirai.message.MessageId
|
import net.mamoe.mirai.message.MessageId
|
||||||
|
import net.mamoe.mirai.network.packet.writeHex
|
||||||
|
import net.mamoe.mirai.network.packet.writeLVByteArray
|
||||||
|
import net.mamoe.mirai.utils.lazyEncode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QQ 自带表情
|
* QQ 自带表情
|
||||||
*
|
*
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
class Face(val id: FaceID?) : Message() {
|
class Face(val id: FaceID) : Message() {
|
||||||
override val type: Int = MessageId.FACE
|
override val type: Int = MessageId.FACE
|
||||||
|
|
||||||
override fun toStringImpl(): String {
|
override fun toStringImpl(): String {
|
||||||
return if (id == null) {
|
return String.format("[face%d]", id.id)
|
||||||
"[face?]"
|
|
||||||
|
|
||||||
} else String.format("[face%d]", id.id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toByteArray(): ByteArray {
|
override fun toByteArray(): ByteArray = lazyEncode { section ->
|
||||||
TODO()
|
section.writeByte(this.type)
|
||||||
|
|
||||||
|
section.writeLVByteArray(lazyEncode { child ->
|
||||||
|
child.writeShort(1)
|
||||||
|
child.writeByte(this.id.id)
|
||||||
|
|
||||||
|
child.writeHex("0B 00 08 00 01 00 04 52 CC F5 D0 FF")
|
||||||
|
|
||||||
|
child.writeShort(2)
|
||||||
|
child.writeByte(0x14)//??
|
||||||
|
child.writeByte(this.id.id + 65)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun valueEquals(another: Message): Boolean {
|
override fun valueEquals(another: Message): Boolean {
|
||||||
if (another !is Face) {
|
if (another !is Face) {
|
||||||
return false
|
return false
|
||||||
|
@ -2,66 +2,49 @@ package net.mamoe.mirai.message.defaults
|
|||||||
|
|
||||||
import net.mamoe.mirai.message.Message
|
import net.mamoe.mirai.message.Message
|
||||||
import net.mamoe.mirai.message.MessageId
|
import net.mamoe.mirai.message.MessageId
|
||||||
import net.mamoe.mirai.network.packet.cutTail
|
import net.mamoe.mirai.network.packet.writeHex
|
||||||
import net.mamoe.mirai.network.packet.md5
|
import net.mamoe.mirai.network.packet.writeLVByteArray
|
||||||
import java.awt.image.BufferedImage
|
import net.mamoe.mirai.network.packet.writeLVString
|
||||||
import java.io.*
|
import net.mamoe.mirai.utils.lazyEncode
|
||||||
import java.net.URL
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 图片消息.
|
||||||
|
* 由接收消息时构建, 可直接发送
|
||||||
|
*
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
class Image : Message {
|
open class Image internal constructor(val imageID: String) : Message() {
|
||||||
override val type: Int = MessageId.IMAGE
|
override val type: Int = MessageId.IMAGE
|
||||||
|
|
||||||
private var imageID: String? = null
|
|
||||||
|
|
||||||
constructor(inputStream: InputStream) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(image: BufferedImage) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(FileNotFoundException::class)
|
|
||||||
constructor(imageFile: File) : this(FileInputStream(imageFile)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
constructor(url: URL) : this(ImageIO.read(url)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {xxxxx}.jpg
|
|
||||||
*
|
|
||||||
* @param imageID
|
|
||||||
*/
|
|
||||||
constructor(imageID: String) {
|
|
||||||
this.imageID = imageID
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toStringImpl(): String {
|
override fun toStringImpl(): String {
|
||||||
return imageID!!
|
return imageID
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toByteArray(): ByteArray {
|
override fun toByteArray(): ByteArray = lazyEncode { section ->
|
||||||
TODO()
|
section.writeByte(0x03)//todo 可能是 0x03?
|
||||||
|
|
||||||
|
section.writeLVByteArray(lazyEncode { child ->
|
||||||
|
child.writeByte(0x02)
|
||||||
|
child.writeLVString(this.imageID)
|
||||||
|
child.writeHex("04 00 " +
|
||||||
|
"04 9B 53 B0 08 " +
|
||||||
|
"05 00 " +
|
||||||
|
"04 D9 8A 5A 70 " +
|
||||||
|
"06 00 " +
|
||||||
|
"04 00 00 00 50 " +
|
||||||
|
"07 00 " +
|
||||||
|
"01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 11 00 00 00 15 00 04 00 00 02 BC 16 00 04 00 00 02 BC 18 00 04 00 00 7D 5E FF 00 5C 15 36 20 39 32 6B 41 31 43 39 62 35 33 62 30 30 38 64 39 38 61 35 61 37 30 20")
|
||||||
|
child.writeHex("20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20")
|
||||||
|
child.writeBytes(this.imageID)
|
||||||
|
child.writeByte(0x41)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun valueEquals(another: Message): Boolean {
|
override fun valueEquals(another: Message): Boolean {
|
||||||
if (another !is Image) {
|
if (another is Image) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
return this.imageID == another.imageID
|
return this.imageID == another.imageID
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
return false
|
||||||
fun getImageID(filename: String): ByteArray = md5(filename).cutTail(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
|
||||||
println(0xB0)
|
|
||||||
}
|
|
@ -11,7 +11,7 @@ import java.util.stream.Stream
|
|||||||
class MessageChain : Message {
|
class MessageChain : Message {
|
||||||
override val type: Int = MessageId.CHAIN
|
override val type: Int = MessageId.CHAIN
|
||||||
|
|
||||||
internal val list = LinkedList<Message>()
|
val list: MutableList<Message> = Collections.synchronizedList(LinkedList<Message>())
|
||||||
|
|
||||||
constructor(head: Message, tail: Message) {
|
constructor(head: Message, tail: Message) {
|
||||||
Objects.requireNonNull(head)
|
Objects.requireNonNull(head)
|
||||||
@ -26,17 +26,16 @@ class MessageChain : Message {
|
|||||||
list.add(message)
|
list.add(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor()
|
constructor(messages: Collection<Message>) {
|
||||||
|
list.addAll(messages)
|
||||||
fun toList(): List<Message> {
|
|
||||||
return list.toList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
|
||||||
fun size(): Int {
|
fun size(): Int {
|
||||||
return list.size
|
return list.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun containsType(@MagicConstant(valuesFromClass = MessageId::class) type: Int): Boolean {
|
fun containsType(@MagicConstant(valuesFromClass = MessageId::class) type: Int): Boolean {
|
||||||
for (message in list) {
|
for (message in list) {
|
||||||
if (message.type == type) {
|
if (message.type == type) {
|
||||||
@ -47,21 +46,18 @@ class MessageChain : Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stream(): Stream<Message> {
|
fun stream(): Stream<Message> {
|
||||||
return ArrayList(list).stream()
|
return list.stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
override fun toStringImpl(): String {
|
override fun toStringImpl(): String {
|
||||||
return this.list.stream().map { it.toString() }.collect(Collectors.joining(""))
|
return this.list.stream().map { it.toString() }.collect(Collectors.joining(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
override fun toObjectString(): String {
|
override fun toObjectString(): String {
|
||||||
return String.format("MessageChain(%s)", this.list.stream().map { it.toObjectString() }.collect(Collectors.joining(", ")))
|
return String.format("MessageChain(%s)", this.list.stream().map { it.toObjectString() }.collect(Collectors.joining(", ")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
override fun concat(tail: Message): MessageChain {
|
||||||
override fun concat(tail: Message): Message {
|
|
||||||
this.list.add(tail)
|
this.list.add(tail)
|
||||||
clearToStringCache()
|
clearToStringCache()
|
||||||
return this
|
return this
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package net.mamoe.mirai.message.defaults
|
||||||
|
|
||||||
|
import net.mamoe.mirai.contact.Contact
|
||||||
|
import net.mamoe.mirai.network.LoginSession
|
||||||
|
import net.mamoe.mirai.network.packet.image.ClientTryGetImageIDPacket
|
||||||
|
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageFailedPacket
|
||||||
|
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket
|
||||||
|
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageSuccessPacket
|
||||||
|
import net.mamoe.mirai.network.packet.md5
|
||||||
|
import net.mamoe.mirai.utils.ImageNetworkUtils
|
||||||
|
import net.mamoe.mirai.utils.toByteArray
|
||||||
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不确定是否存在于服务器的 [Image].
|
||||||
|
* 必须在发送之前 [UnsolvedImage.upload] 或 [Contact.uploadImage], 否则会发送失败.
|
||||||
|
x *
|
||||||
|
* @author Him188moe
|
||||||
|
*/
|
||||||
|
class UnsolvedImage(filename: String, val image: BufferedImage) : Image(getImageId(filename)) {
|
||||||
|
constructor(imageFile: File) : this(imageFile.name, ImageIO.read(imageFile))
|
||||||
|
constructor(url: URL) : this(File(url.file))
|
||||||
|
|
||||||
|
fun upload(session: LoginSession, contact: Contact): CompletableFuture<Unit> {
|
||||||
|
return session.expectPacket<ServerTryUploadGroupImageResponsePacket> {
|
||||||
|
toSend { ClientTryGetImageIDPacket(session.bot.account.qqNumber, session.sessionKey, session.bot.account.qqNumber, image) }
|
||||||
|
|
||||||
|
expect {
|
||||||
|
when (it) {
|
||||||
|
is ServerTryUploadGroupImageFailedPacket -> {
|
||||||
|
//已经存在于服务器了
|
||||||
|
}
|
||||||
|
|
||||||
|
is ServerTryUploadGroupImageSuccessPacket -> {
|
||||||
|
val data = image.toByteArray()
|
||||||
|
if (!ImageNetworkUtils.postImage(it.uKey.toUHexString(), data.size, session.bot.account.qqNumber, contact.number, data)) {
|
||||||
|
throw RuntimeException("cannot upload image")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getImageId(filename: String): String {
|
||||||
|
val md5 = md5(filename)
|
||||||
|
|
||||||
|
return "{" + md5.copyOfRange(0, 4).toUHexString("") + "-"
|
||||||
|
.plus(md5.copyOfRange(4, 6).toUHexString("")) + "-"
|
||||||
|
.plus(md5.copyOfRange(6, 8).toUHexString("")) + "-"
|
||||||
|
.plus(md5.copyOfRange(8, 12).toUHexString("")) + "-"
|
||||||
|
.plus(md5.copyOfRange(12, 16).toUHexString("")) + "}." + if (filename.endsWith(".jpeg")) "jpg" else filename.substringAfter(".", "jpg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,14 @@ package net.mamoe.mirai.network
|
|||||||
|
|
||||||
import net.mamoe.mirai.network.BotNetworkHandlerImpl.BotSocket
|
import net.mamoe.mirai.network.BotNetworkHandlerImpl.BotSocket
|
||||||
import net.mamoe.mirai.network.BotNetworkHandlerImpl.Login
|
import net.mamoe.mirai.network.BotNetworkHandlerImpl.Login
|
||||||
import net.mamoe.mirai.network.handler.ActionPacketHandler
|
import net.mamoe.mirai.network.handler.*
|
||||||
import net.mamoe.mirai.network.handler.DataPacketSocket
|
|
||||||
import net.mamoe.mirai.network.handler.MessagePacketHandler
|
|
||||||
import net.mamoe.mirai.network.handler.PacketHandler
|
|
||||||
import net.mamoe.mirai.network.packet.ClientPacket
|
import net.mamoe.mirai.network.packet.ClientPacket
|
||||||
import net.mamoe.mirai.network.packet.Packet
|
import net.mamoe.mirai.network.packet.Packet
|
||||||
import net.mamoe.mirai.network.packet.ServerEventPacket
|
import net.mamoe.mirai.network.packet.ServerEventPacket
|
||||||
import net.mamoe.mirai.network.packet.ServerPacket
|
import net.mamoe.mirai.network.packet.ServerPacket
|
||||||
|
import net.mamoe.mirai.network.packet.login.LoginState
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务.
|
* Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务.
|
||||||
@ -51,4 +50,15 @@ interface BotNetworkHandler : Closeable {
|
|||||||
* java 调用方式: `botNetWorkHandler.getAction()`
|
* java 调用方式: `botNetWorkHandler.getAction()`
|
||||||
*/
|
*/
|
||||||
var action: ActionPacketHandler
|
var action: ActionPacketHandler
|
||||||
|
|
||||||
|
fun tryLogin(touchingTimeoutMillis: Long = 200): CompletableFuture<LoginState>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个临时包处理器
|
||||||
|
*
|
||||||
|
* @see [TemporaryPacketHandler]
|
||||||
|
*/
|
||||||
|
fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>)
|
||||||
|
|
||||||
|
override fun close()
|
||||||
}
|
}
|
@ -6,7 +6,6 @@ import net.mamoe.mirai.event.events.bot.BotLoginSucceedEvent
|
|||||||
import net.mamoe.mirai.event.events.network.BeforePacketSendEvent
|
import net.mamoe.mirai.event.events.network.BeforePacketSendEvent
|
||||||
import net.mamoe.mirai.event.events.network.PacketSentEvent
|
import net.mamoe.mirai.event.events.network.PacketSentEvent
|
||||||
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
|
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
|
||||||
import net.mamoe.mirai.event.hookWhile
|
|
||||||
import net.mamoe.mirai.network.handler.*
|
import net.mamoe.mirai.network.handler.*
|
||||||
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.*
|
||||||
@ -21,7 +20,6 @@ import java.util.concurrent.CompletableFuture
|
|||||||
import java.util.concurrent.ScheduledFuture
|
import java.util.concurrent.ScheduledFuture
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [BotNetworkHandler] 的内部实现, 该类不会有帮助理解的注解, 请查看 [BotNetworkHandler] 以获取帮助
|
* [BotNetworkHandler] 的内部实现, 该类不会有帮助理解的注解, 请查看 [BotNetworkHandler] 以获取帮助
|
||||||
@ -40,15 +38,20 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
|
|||||||
|
|
||||||
val packetHandlers: PacketHandlerList = PacketHandlerList()
|
val packetHandlers: PacketHandlerList = PacketHandlerList()
|
||||||
|
|
||||||
|
internal val temporaryPacketHandlers = Collections.synchronizedList(mutableListOf<TemporaryPacketHandler<*>>())
|
||||||
|
|
||||||
|
override fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>) {
|
||||||
|
temporaryPacketHandler.send(action.session)
|
||||||
|
temporaryPacketHandlers.add(temporaryPacketHandler)
|
||||||
|
}
|
||||||
|
|
||||||
//private | internal
|
//private | internal
|
||||||
/**
|
/**
|
||||||
* 尝试登录. 多次重复登录
|
* 尝试登录
|
||||||
*
|
*
|
||||||
* @param touchingTimeoutMillis 连接每个服务器的 timeout
|
* @param touchingTimeoutMillis 连接每个服务器的 timeout
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
override fun tryLogin(touchingTimeoutMillis: Long): CompletableFuture<LoginState> {
|
||||||
internal fun tryLogin(touchingTimeoutMillis: Long = 200): CompletableFuture<LoginState> {
|
|
||||||
val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
|
val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
|
||||||
val future = CompletableFuture<LoginState>()
|
val future = CompletableFuture<LoginState>()
|
||||||
|
|
||||||
@ -101,6 +104,10 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
temporaryPacketHandlers.removeIf {
|
||||||
|
it.onPacketReceived(action.session, packet)
|
||||||
|
}
|
||||||
|
|
||||||
//For debug
|
//For debug
|
||||||
kotlin.run {
|
kotlin.run {
|
||||||
if (!packet.javaClass.name.endsWith("Encrypted") && !packet.javaClass.name.endsWith("Raw")) {
|
if (!packet.javaClass.name.endsWith("Encrypted") && !packet.javaClass.name.endsWith("Raw")) {
|
||||||
@ -108,7 +115,7 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (packet is ServerEventPacket) {
|
if (packet is ServerEventPacket) {
|
||||||
sendPacket(ClientMessageResponsePacket(bot.account.qqNumber, packet.packetId, sessionKey, packet.eventIdentity))
|
sendPacket(ClientEventResponsePacket(bot.account.qqNumber, packet.packetId, sessionKey, packet.eventIdentity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +168,7 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
|
|||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start network and touch the server
|
* Start network and touch the server
|
||||||
*/
|
*/
|
||||||
@ -173,7 +181,7 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
|
|||||||
this.loginFuture = CompletableFuture()
|
this.loginFuture = CompletableFuture()
|
||||||
|
|
||||||
serverIP = serverAddress
|
serverIP = serverAddress
|
||||||
waitForPacket(ServerPacket::class, timeoutMillis) {
|
bot.waitForPacket(ServerPacket::class, timeoutMillis) {
|
||||||
loginFuture!!.complete(LoginState.TIMEOUT)
|
loginFuture!!.complete(LoginState.TIMEOUT)
|
||||||
}
|
}
|
||||||
sendPacket(ClientTouchPacket(bot.account.qqNumber, serverIP))
|
sendPacket(ClientTouchPacket(bot.account.qqNumber, serverIP))
|
||||||
@ -209,30 +217,7 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
override fun getOwner(): Bot = this@BotNetworkHandlerImpl.bot
|
||||||
internal fun <P : ServerPacket> waitForPacket(packetClass: KClass<P>, timeoutMillis: Long, timeout: () -> Unit) {
|
|
||||||
var got = false
|
|
||||||
ServerPacketReceivedEvent::class.hookWhile {
|
|
||||||
if (packetClass.isInstance(it.packet) && it.bot === bot) {
|
|
||||||
got = true
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MiraiThreadPool.getInstance().submit {
|
|
||||||
val startingTime = System.currentTimeMillis()
|
|
||||||
while (!got) {
|
|
||||||
if (System.currentTimeMillis() - startingTime > timeoutMillis) {
|
|
||||||
timeout.invoke()
|
|
||||||
return@submit
|
|
||||||
}
|
|
||||||
Thread.sleep(10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
this.socket?.close()
|
this.socket?.close()
|
||||||
|
@ -2,7 +2,11 @@ package net.mamoe.mirai.network
|
|||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.network.handler.DataPacketSocket
|
import net.mamoe.mirai.network.handler.DataPacketSocket
|
||||||
|
import net.mamoe.mirai.network.handler.TemporaryPacketHandler
|
||||||
|
import net.mamoe.mirai.network.packet.ClientPacket
|
||||||
|
import net.mamoe.mirai.network.packet.ServerPacket
|
||||||
import net.mamoe.mirai.utils.getGTK
|
import net.mamoe.mirai.utils.getGTK
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录会话. 当登录完成后, 客户端会拿到 sessionKey.
|
* 登录会话. 当登录完成后, 客户端会拿到 sessionKey.
|
||||||
@ -15,11 +19,80 @@ class LoginSession(
|
|||||||
val sessionKey: ByteArray,
|
val sessionKey: ByteArray,
|
||||||
val socket: DataPacketSocket
|
val socket: DataPacketSocket
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web api 使用
|
||||||
|
*/
|
||||||
lateinit var cookies: String
|
lateinit var cookies: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web api 使用
|
||||||
|
*/
|
||||||
var sKey: String = ""
|
var sKey: String = ""
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
gtk = getGTK(value)
|
gtk = getGTK(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web api 使用
|
||||||
|
*/
|
||||||
var gtk: Int = 0
|
var gtk: Int = 0
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket]. 仅 Kotlin 使用
|
||||||
|
* 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时.
|
||||||
|
*
|
||||||
|
* 实现方法:
|
||||||
|
* ```kotlin
|
||||||
|
* session.expectPacket<ServerPacketXXX> {
|
||||||
|
* toSend { ClientPacketXXX(...) }
|
||||||
|
* expect {//it: ServerPacketXXX
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param P 期待的包
|
||||||
|
* @param handlerTemporary 处理器.
|
||||||
|
* @return future. 可进行超时处理
|
||||||
|
*
|
||||||
|
* Kotlin DSL: 仅 Kotlin 使用.
|
||||||
|
*/
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun <reified P : ServerPacket> expectPacket(handlerTemporary: TemporaryPacketHandler<P>.() -> Unit): CompletableFuture<Unit> {
|
||||||
|
val future = CompletableFuture<Unit>()
|
||||||
|
this.bot.network.addHandler(TemporaryPacketHandler(P::class, future, this).also(handlerTemporary))
|
||||||
|
return future
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
|
||||||
|
* 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时.
|
||||||
|
* 由于包名可能过长, 可使用 `DataPacketSocket.expectPacket(PacketProcessor)` 替代.
|
||||||
|
*
|
||||||
|
* 实现方法:
|
||||||
|
* ```kotlin
|
||||||
|
* session.expectPacket<ServerPacketXXX>(ClientPacketXXX(...)) {//it: ServerPacketXXX
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param P 期待的包
|
||||||
|
* @param toSend 将要发送的包
|
||||||
|
* @param handler 处理期待的包
|
||||||
|
* @return future. 可进行超时处理
|
||||||
|
*
|
||||||
|
* Kotlin DSL: 仅 Kotlin 使用.
|
||||||
|
*/
|
||||||
|
@JvmSynthetic
|
||||||
|
inline fun <reified P : ServerPacket> expectPacket(toSend: ClientPacket, noinline handler: (P) -> Unit): CompletableFuture<Unit> {
|
||||||
|
val future = CompletableFuture<Unit>()
|
||||||
|
this.bot.network.addHandler(TemporaryPacketHandler(P::class, future, this).also {
|
||||||
|
it.toSend { toSend }
|
||||||
|
it.expect(handler)
|
||||||
|
})
|
||||||
|
return future
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,9 +1,14 @@
|
|||||||
package net.mamoe.mirai.network.handler
|
package net.mamoe.mirai.network.handler
|
||||||
|
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
|
||||||
import net.mamoe.mirai.network.BotNetworkHandlerImpl
|
import net.mamoe.mirai.network.BotNetworkHandlerImpl
|
||||||
|
import net.mamoe.mirai.network.LoginSession
|
||||||
import net.mamoe.mirai.network.packet.ClientPacket
|
import net.mamoe.mirai.network.packet.ClientPacket
|
||||||
import net.mamoe.mirai.network.packet.ServerPacket
|
import net.mamoe.mirai.network.packet.ServerPacket
|
||||||
|
import net.mamoe.mirai.task.MiraiThreadPool
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网络接口.
|
* 网络接口.
|
||||||
@ -13,12 +18,35 @@ import java.io.Closeable
|
|||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
interface DataPacketSocket : Closeable {
|
interface DataPacketSocket : Closeable {
|
||||||
|
fun getOwner(): Bot
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分发数据包给 [PacketHandler]
|
||||||
|
*/
|
||||||
fun distributePacket(packet: ServerPacket)
|
fun distributePacket(packet: ServerPacket)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送一个数据包(非异步).
|
||||||
|
*
|
||||||
|
* 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
|
||||||
|
*
|
||||||
|
* @see [LoginSession.expectPacket] kotlin DSL
|
||||||
|
*/
|
||||||
fun sendPacket(packet: ClientPacket)
|
fun sendPacket(packet: ClientPacket)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送一个数据包(异步).
|
||||||
|
*
|
||||||
|
* 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
|
||||||
|
*
|
||||||
|
* @see [LoginSession.expectPacket] kotlin DSL
|
||||||
|
*/
|
||||||
|
fun sendPacketAsync(packet: ClientPacket): Future<*> {
|
||||||
|
return MiraiThreadPool.getInstance().submit {
|
||||||
|
sendPacket(packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun isClosed(): Boolean
|
fun isClosed(): Boolean
|
||||||
|
|
||||||
override fun close()
|
override fun close()
|
||||||
|
@ -4,13 +4,16 @@ import net.mamoe.mirai.contact.Group
|
|||||||
import net.mamoe.mirai.contact.QQ
|
import net.mamoe.mirai.contact.QQ
|
||||||
import net.mamoe.mirai.event.events.qq.FriendMessageEvent
|
import net.mamoe.mirai.event.events.qq.FriendMessageEvent
|
||||||
import net.mamoe.mirai.event.hookWhile
|
import net.mamoe.mirai.event.hookWhile
|
||||||
import net.mamoe.mirai.message.Message
|
|
||||||
import net.mamoe.mirai.message.defaults.MessageChain
|
import net.mamoe.mirai.message.defaults.MessageChain
|
||||||
|
import net.mamoe.mirai.message.defaults.PlainText
|
||||||
|
import net.mamoe.mirai.message.defaults.UnsolvedImage
|
||||||
import net.mamoe.mirai.network.LoginSession
|
import net.mamoe.mirai.network.LoginSession
|
||||||
import net.mamoe.mirai.network.packet.*
|
import net.mamoe.mirai.network.packet.*
|
||||||
import net.mamoe.mirai.network.packet.action.ClientSendFriendMessagePacket
|
import net.mamoe.mirai.network.packet.action.ClientSendFriendMessagePacket
|
||||||
|
import net.mamoe.mirai.network.packet.action.ClientSendGroupMessagePacket
|
||||||
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
|
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
|
||||||
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
|
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理消息事件, 承担消息发送任务.
|
* 处理消息事件, 承担消息发送任务.
|
||||||
@ -27,10 +30,18 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
|
|||||||
if (session.socket.isClosed()) {
|
if (session.socket.isClosed()) {
|
||||||
return@hookWhile false
|
return@hookWhile false
|
||||||
}
|
}
|
||||||
if (it.message() valueEquals "你好") {
|
when {
|
||||||
it.qq.sendMessage("你好!")
|
it.message() valueEquals "你好" -> it.qq.sendMessage("你好!")
|
||||||
} else if (it.message().toString().startsWith("复读")) {
|
it.message().toString().startsWith("复读") -> it.qq.sendMessage(it.message())
|
||||||
it.qq.sendMessage(it.message())
|
it.message().toString().startsWith("发群") -> {
|
||||||
|
it.message().list.toMutableList().let { messages ->
|
||||||
|
messages.removeAt(0)
|
||||||
|
sendGroupMessage(Group(session.bot, 580266363), MessageChain(messages))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.message() valueEquals "发图片" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
|
||||||
|
image.upload(session, it.qq).get()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return@hookWhile true
|
return@hookWhile true
|
||||||
@ -71,11 +82,10 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sendFriendMessage(qq: QQ, message: MessageChain) {
|
fun sendFriendMessage(qq: QQ, message: MessageChain) {
|
||||||
session.socket.sendPacket(ClientSendFriendMessagePacket(session.bot.account.qqNumber, qq.number, session.sessionKey, message))
|
session.socket.sendPacketAsync(ClientSendFriendMessagePacket(session.bot.account.qqNumber, qq.number, session.sessionKey, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendGroupMessage(group: Group, message: Message) {
|
fun sendGroupMessage(group: Group, message: MessageChain) {
|
||||||
TODO()
|
session.socket.sendPacket(ClientSendGroupMessagePacket(group.groupId, session.bot.account.qqNumber, session.sessionKey, message))
|
||||||
//sendPacket(ClientSendGroupMessagePacket(group.groupId, bot.account.qqNumber, sessionKey, message))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,9 +2,8 @@ package net.mamoe.mirai.network.handler
|
|||||||
|
|
||||||
import net.mamoe.mirai.network.LoginSession
|
import net.mamoe.mirai.network.LoginSession
|
||||||
import net.mamoe.mirai.network.packet.ServerPacket
|
import net.mamoe.mirai.network.packet.ServerPacket
|
||||||
|
import net.mamoe.mirai.utils.MiraiSynchronizedLinkedList
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.util.*
|
|
||||||
import kotlin.NoSuchElementException
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据包(接受/发送)处理器
|
* 数据包(接受/发送)处理器
|
||||||
@ -28,7 +27,7 @@ fun PacketHandler.asNode(): PacketHandlerNode<PacketHandler> {
|
|||||||
return PacketHandlerNode(this.javaClass, this)
|
return PacketHandlerNode(this.javaClass, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class PacketHandlerList : LinkedList<PacketHandlerNode<*>>() {
|
class PacketHandlerList : MiraiSynchronizedLinkedList<PacketHandlerNode<*>>() {
|
||||||
|
|
||||||
fun <T : PacketHandler> get(clazz: Class<T>): T {
|
fun <T : PacketHandler> get(clazz: Class<T>): T {
|
||||||
this.forEach {
|
this.forEach {
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package net.mamoe.mirai.network.handler
|
||||||
|
|
||||||
|
import net.mamoe.mirai.network.LoginSession
|
||||||
|
import net.mamoe.mirai.network.packet.ClientPacket
|
||||||
|
import net.mamoe.mirai.network.packet.ServerPacket
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 临时数据包处理器
|
||||||
|
*
|
||||||
|
* @see LoginSession.expectPacket
|
||||||
|
*/
|
||||||
|
open class TemporaryPacketHandler<P : ServerPacket>(
|
||||||
|
private val expectationClass: KClass<P>,
|
||||||
|
private val future: CompletableFuture<Unit>,
|
||||||
|
private val fromSession: LoginSession
|
||||||
|
) {
|
||||||
|
private lateinit var toSend: ClientPacket
|
||||||
|
|
||||||
|
private lateinit var expect: (P) -> Unit
|
||||||
|
|
||||||
|
|
||||||
|
lateinit var session: LoginSession//无需覆盖
|
||||||
|
|
||||||
|
fun toSend(packet: () -> ClientPacket) {
|
||||||
|
this.toSend = packet()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun expect(handler: (P) -> Unit) {
|
||||||
|
this.expect = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
fun send(session: LoginSession) {
|
||||||
|
this.session = session
|
||||||
|
session.socket.sendPacket(toSend)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPacketReceived(session: LoginSession, packet: ServerPacket): Boolean {
|
||||||
|
if (expectationClass.isInstance(packet) && session === this.fromSession) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
expect(packet as P)
|
||||||
|
future.complete(Unit)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -217,7 +217,6 @@ fun DataOutputStream.writeQQ(qq: Long) {
|
|||||||
this.write(qq.toUInt().toByteArray())
|
this.write(qq.toUInt().toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun DataOutputStream.writeGroup(groupIdOrGroupNumber: Long) {
|
fun DataOutputStream.writeGroup(groupIdOrGroupNumber: Long) {
|
||||||
this.write(groupIdOrGroupNumber.toUInt().toByteArray())
|
this.write(groupIdOrGroupNumber.toUInt().toByteArray())
|
||||||
@ -231,3 +230,7 @@ fun DataOutputStream.writeLVByteArray(byteArray: ByteArray) {
|
|||||||
fun DataOutputStream.writeLVString(str: String) {
|
fun DataOutputStream.writeLVString(str: String) {
|
||||||
this.writeLVByteArray(str.toByteArray())
|
this.writeLVByteArray(str.toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun DataOutputStream.writeLVHex(hex: String) {
|
||||||
|
this.writeLVByteArray(hex.hexToBytes())
|
||||||
|
}
|
@ -105,6 +105,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
|
|||||||
|
|
||||||
|
|
||||||
override fun decode() {
|
override fun decode() {
|
||||||
|
println(this.input.goto(0).readAllBytes().toUHexString())
|
||||||
groupNumber = this.input.goto(51).readInt().toLong()
|
groupNumber = this.input.goto(51).readInt().toLong()
|
||||||
qq = this.input.goto(56).readLong()
|
qq = this.input.goto(56).readLong()
|
||||||
val fontLength = this.input.goto(108).readShort()
|
val fontLength = this.input.goto(108).readShort()
|
||||||
@ -225,16 +226,16 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
|
|||||||
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
|
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
|
||||||
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D
|
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D
|
||||||
|
|
||||||
val id1 = FaceID.ofId(readVarNumber().toInt())//可能这个是id, 也可能下面那个
|
val id1 = FaceID.ofId(readLVNumber().toInt())//可能这个是id, 也可能下面那个
|
||||||
this.skip(this.readByte().toLong())
|
this.skip(this.readByte().toLong())
|
||||||
this.readVarNumber()//某id?
|
this.readLVNumber()//某id?
|
||||||
return Face(id1)
|
return Face(id1)
|
||||||
}
|
}
|
||||||
0x06 -> {
|
0x06 -> {
|
||||||
this.skip(sectionLength - 37 - 1)
|
this.skip(sectionLength - 37 - 1)
|
||||||
val imageId = String(this.readNBytes(36))
|
val imageId = String(this.readNBytes(36))
|
||||||
this.skip(1)//0x41
|
this.skip(1)//0x41
|
||||||
return Image(imageId)
|
return Image("{$imageId}.jpg")//todo 如何确定文件后缀??
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@ -294,8 +295,7 @@ B1 89 BE 09 8F 00 1A E5 00 0B 03 A2 09 90 BB 7A 1F 40 00 A6 00 00 00 20 00 05 00
|
|||||||
* 告知服务器已经收到数据
|
* 告知服务器已经收到数据
|
||||||
*/
|
*/
|
||||||
@PacketId("")//随后写入
|
@PacketId("")//随后写入
|
||||||
|
class ClientEventResponsePacket(
|
||||||
class ClientMessageResponsePacket(
|
|
||||||
private val qq: Long,
|
private val qq: Long,
|
||||||
private val packetIdFromServer: ByteArray,//4bytes
|
private val packetIdFromServer: ByteArray,//4bytes
|
||||||
private val sessionKey: ByteArray,
|
private val sessionKey: ByteArray,
|
||||||
|
@ -4,14 +4,20 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.network.packet
|
package net.mamoe.mirai.network.packet
|
||||||
|
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
|
||||||
|
import net.mamoe.mirai.event.hookWhile
|
||||||
import net.mamoe.mirai.network.packet.PacketNameFormatter.adjustName
|
import net.mamoe.mirai.network.packet.PacketNameFormatter.adjustName
|
||||||
import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
|
import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
|
||||||
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
|
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
|
||||||
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
|
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
|
||||||
|
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket
|
||||||
import net.mamoe.mirai.network.packet.login.*
|
import net.mamoe.mirai.network.packet.login.*
|
||||||
|
import net.mamoe.mirai.task.MiraiThreadPool
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.EOFException
|
import java.io.EOFException
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
@ -76,7 +82,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
|
|||||||
|
|
||||||
println(bytes.size)
|
println(bytes.size)
|
||||||
return ServerLoginResponseFailedPacket(when (bytes.size) {
|
return ServerLoginResponseFailedPacket(when (bytes.size) {
|
||||||
63, 319, 135, 351 -> LoginState.WRONG_PASSWORD//这四个其中一个也是被冻结
|
63, 319, 135, 351 -> LoginState.WRONG_PASSWORD//这四个其中一个是被冻结
|
||||||
//135 -> LoginState.RETYPE_PASSWORD
|
//135 -> LoginState.RETYPE_PASSWORD
|
||||||
279 -> LoginState.BLOCKED
|
279 -> LoginState.BLOCKED
|
||||||
263 -> LoginState.UNKNOWN_QQ_NUMBER
|
263 -> LoginState.UNKNOWN_QQ_NUMBER
|
||||||
@ -115,6 +121,8 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
|
|||||||
|
|
||||||
"00 A7" -> ServerCanAddFriendResponsePacket(stream)
|
"00 A7" -> ServerCanAddFriendResponsePacket(stream)
|
||||||
|
|
||||||
|
"03 88" -> ServerTryUploadGroupImageResponsePacket.Encrypted(stream)
|
||||||
|
|
||||||
else -> throw IllegalArgumentException(idHex)
|
else -> throw IllegalArgumentException(idHex)
|
||||||
}
|
}
|
||||||
}.apply { this.idHex = idHex }
|
}.apply { this.idHex = idHex }
|
||||||
@ -223,7 +231,7 @@ fun <N : Number> DataInputStream.readNBytes(length: N): ByteArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun DataInputStream.readVarNumber(): Number {
|
fun DataInputStream.readLVNumber(): Number {
|
||||||
return when (this.readShort().toInt()) {
|
return when (this.readShort().toInt()) {
|
||||||
1 -> this.readByte()
|
1 -> this.readByte()
|
||||||
2 -> this.readShort()
|
2 -> this.readShort()
|
||||||
@ -288,6 +296,32 @@ fun DataInputStream.gotoWhere(matcher: ByteArray): DataInputStream {
|
|||||||
} while (true)
|
} while (true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
internal fun <P : ServerPacket> Bot.waitForPacket(packetClass: KClass<P>, timeoutMillis: Long = Long.MAX_VALUE, timeout: () -> Unit = {}) {
|
||||||
|
var got = false
|
||||||
|
ServerPacketReceivedEvent::class.hookWhile {
|
||||||
|
if (packetClass.isInstance(it.packet) && it.bot === this) {
|
||||||
|
got = true
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MiraiThreadPool.getInstance().submit {
|
||||||
|
val startingTime = System.currentTimeMillis()
|
||||||
|
while (!got) {
|
||||||
|
if (System.currentTimeMillis() - startingTime > timeoutMillis) {
|
||||||
|
timeout.invoke()
|
||||||
|
return@submit
|
||||||
|
}
|
||||||
|
Thread.sleep(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@Throws(EOFException::class)
|
@Throws(EOFException::class)
|
||||||
fun DataInputStream.gotoWhere(matcher: ByteArray) {
|
fun DataInputStream.gotoWhere(matcher: ByteArray) {
|
||||||
|
@ -17,7 +17,7 @@ import java.io.IOException
|
|||||||
*
|
*
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||||
@PacketId("08 25 31 01")
|
@PacketId("08 25 31 01")
|
||||||
class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||||
var serverIP: String? = null
|
var serverIP: String? = null
|
||||||
@ -33,7 +33,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
|
|||||||
|
|
||||||
|
|
||||||
override fun decode() {
|
override fun decode() {
|
||||||
when (val id = input.readByte().toInt()) {
|
when (val id = input.readByte().toUByte().toInt()) {
|
||||||
0xFE -> {
|
0xFE -> {
|
||||||
input.skip(94)
|
input.skip(94)
|
||||||
serverIP = input.readIP()
|
serverIP = input.readIP()
|
||||||
|
@ -19,6 +19,7 @@ class ClientSendFriendMessagePacket(
|
|||||||
) : ClientPacket() {
|
) : ClientPacket() {
|
||||||
override fun encode() {
|
override fun encode() {
|
||||||
this.writeRandom(2)//part of packet id
|
this.writeRandom(2)//part of packet id
|
||||||
|
|
||||||
this.writeQQ(botQQ)
|
this.writeQQ(botQQ)
|
||||||
this.writeHex(Protocol.fixVer2)
|
this.writeHex(Protocol.fixVer2)
|
||||||
|
|
||||||
@ -40,7 +41,6 @@ class ClientSendFriendMessagePacket(
|
|||||||
it.writeHex(Protocol.friendMessageConst1)//... 85 E9 BB 91
|
it.writeHex(Protocol.friendMessageConst1)//... 85 E9 BB 91
|
||||||
it.writeZero(2)
|
it.writeZero(2)
|
||||||
|
|
||||||
|
|
||||||
it.write(message.toByteArray())
|
it.write(message.toByteArray())
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.mamoe.mirai.network.packet.action
|
package net.mamoe.mirai.network.packet.action
|
||||||
|
|
||||||
|
import net.mamoe.mirai.message.defaults.MessageChain
|
||||||
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.utils.toUHexString
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
@ -9,12 +10,11 @@ import java.io.DataInputStream
|
|||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
@PacketId("00 02")
|
@PacketId("00 02")
|
||||||
|
|
||||||
class ClientSendGroupMessagePacket(
|
class ClientSendGroupMessagePacket(
|
||||||
private val groupId: Long,//不是 number
|
private val groupId: Long,//不是 number
|
||||||
private val botQQ: Long,
|
private val botQQ: Long,
|
||||||
private val sessionKey: ByteArray,
|
private val sessionKey: ByteArray,
|
||||||
private val message: String
|
private val message: MessageChain
|
||||||
) : ClientPacket() {
|
) : ClientPacket() {
|
||||||
override fun encode() {
|
override fun encode() {
|
||||||
this.writeRandom(2)//part of packet id
|
this.writeRandom(2)//part of packet id
|
||||||
@ -25,20 +25,24 @@ class ClientSendGroupMessagePacket(
|
|||||||
val bytes = message.toByteArray()
|
val bytes = message.toByteArray()
|
||||||
it.writeByte(0x2A)
|
it.writeByte(0x2A)
|
||||||
it.writeGroup(groupId)
|
it.writeGroup(groupId)
|
||||||
it.writeShort(56 + bytes.size)
|
|
||||||
|
|
||||||
it.writeHex("00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
|
it.writeShort(50 + bytes.size)
|
||||||
|
it.writeHex("00 01 01")
|
||||||
|
it.writeHex("00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
|
||||||
|
|
||||||
it.writeTime()
|
it.writeTime()
|
||||||
it.writeRandom(4)
|
it.writeRandom(4)
|
||||||
it.writeHex("Protocol.messageConst1")
|
it.writeHex("00 00 00 00 09 00 86")
|
||||||
|
it.writeHex(Protocol.friendMessageConst1)
|
||||||
it.writeZero(2)
|
it.writeZero(2)
|
||||||
|
|
||||||
//messages
|
//messages
|
||||||
it.writeByte(0x01)
|
it.write(bytes)
|
||||||
|
/*it.writeByte(0x01)
|
||||||
it.writeShort(bytes.size + 3)
|
it.writeShort(bytes.size + 3)
|
||||||
it.writeByte(0x01)
|
it.writeByte(0x01)
|
||||||
it.writeShort(bytes.size)
|
it.writeShort(bytes.size)
|
||||||
it.write(bytes)
|
it.write(bytes)*/
|
||||||
|
|
||||||
println(it.toByteArray().toUHexString())
|
println(it.toByteArray().toUHexString())
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,16 @@ import java.io.DataInputStream
|
|||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
@PacketId("03 88")
|
@PacketId("03 88")
|
||||||
|
class ClientTryGetImageIDPacket(
|
||||||
class ClientTryGetGroupImageIDPacket(
|
private val botNumber: Long,
|
||||||
private val bot: Long,
|
|
||||||
private val sessionKey: ByteArray,
|
private val sessionKey: ByteArray,
|
||||||
private val group: Long,
|
private val groupNumberOrQQNumber: Long,//todo 为什么还要有qq number呢? bot不就是了么
|
||||||
private val image: BufferedImage
|
private val image: BufferedImage
|
||||||
) : ClientPacket() {
|
) : ClientPacket() {
|
||||||
override fun encode() {
|
override fun encode() {
|
||||||
this.writeRandom(2)
|
this.writeRandom(2)
|
||||||
|
|
||||||
this.writeQQ(bot)
|
this.writeQQ(botNumber)
|
||||||
this.writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
|
this.writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
|
||||||
|
|
||||||
val byteArray = image.toByteArray()
|
val byteArray = image.toByteArray()
|
||||||
@ -46,10 +45,10 @@ class ClientTryGetGroupImageIDPacket(
|
|||||||
it.writeHex("5A")
|
it.writeHex("5A")
|
||||||
|
|
||||||
it.writeHex("08")
|
it.writeHex("08")
|
||||||
it.writeUVarInt(group)
|
it.writeUVarInt(groupNumberOrQQNumber)
|
||||||
|
|
||||||
it.writeHex("10")
|
it.writeHex("10")
|
||||||
it.writeUVarInt(bot)
|
it.writeUVarInt(botNumber)
|
||||||
|
|
||||||
it.writeHex("18 00")
|
it.writeHex("18 00")
|
||||||
|
|
||||||
@ -77,7 +76,7 @@ class ClientTryGetGroupImageIDPacket(
|
|||||||
|
|
||||||
it.writeHex("6A")
|
it.writeHex("6A")
|
||||||
it.writeHex("05")
|
it.writeHex("05")
|
||||||
it.writeHex("32 36 36 35 36 05")//6?
|
it.writeHex("32 36 36 35 36")
|
||||||
|
|
||||||
it.writeHex("70 00")
|
it.writeHex("70 00")
|
||||||
|
|
||||||
@ -95,7 +94,9 @@ abstract class ServerTryUploadGroupImageResponsePacket(input: DataInputStream) :
|
|||||||
class Encrypted(input: DataInputStream) : ServerPacket(input) {
|
class Encrypted(input: DataInputStream) : ServerPacket(input) {
|
||||||
fun decrypt(sessionKey: ByteArray): ServerTryUploadGroupImageResponsePacket {
|
fun decrypt(sessionKey: ByteArray): ServerTryUploadGroupImageResponsePacket {
|
||||||
val data = this.decryptAsByteArray(sessionKey)
|
val data = this.decryptAsByteArray(sessionKey)
|
||||||
if (data.size == 239) {
|
println(data.size)
|
||||||
|
println(data.size)
|
||||||
|
if (data.size == 209) {
|
||||||
return ServerTryUploadGroupImageSuccessPacket(data.dataInputStream()).setId(this.idHex)
|
return ServerTryUploadGroupImageSuccessPacket(data.dataInputStream()).setId(this.idHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package net.mamoe.mirai.utils;
|
package net.mamoe.mirai.utils;
|
||||||
|
|
||||||
import net.mamoe.mirai.contact.Contact;
|
import net.mamoe.mirai.contact.Contact;
|
||||||
import net.mamoe.mirai.utils.config.MiraiSynchronizedLinkedListMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
public class ContactList<C extends Contact> extends MiraiSynchronizedLinkedListMap<Long, C> {
|
public class ContactList<C extends Contact> extends MiraiSynchronizedLinkedHashMap<Long, C> {
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,31 @@
|
|||||||
package net.mamoe.mirai.utils;
|
package net.mamoe.mirai.utils;
|
||||||
|
|
||||||
import org.apache.commons.httpclient.util.HttpURLConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author NaturalHG
|
* @author NaturalHG
|
||||||
*/
|
*/
|
||||||
public class ImageNetworkUtils {
|
public class ImageNetworkUtils {
|
||||||
public static void postImage(String uKeyHex, int fileSize, String qqNumber, String groupCode, byte[] img) throws IOException {
|
public static boolean postImage(String uKeyHex, int fileSize, long qqNumber, long groupCode, byte[] img) throws IOException {
|
||||||
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=” + 删全部空 (ukey) + “&filesize=” + 到文本 (fileSize) + “&range=0&uin=” + g_uin + “&groupcode=” + Group
|
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=” + 删全部空 (ukey) + “&filesize=” + 到文本 (fileSize) + “&range=0&uin=” + g_uin + “&groupcode=” + Group
|
||||||
StringBuilder builder = new StringBuilder("http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc");
|
|
||||||
builder.append("&ukey=")
|
|
||||||
.append(uKeyHex.trim())
|
|
||||||
.append("&filezise=").append(fileSize)
|
|
||||||
.append("&range=").append("0")
|
|
||||||
.append("&uin=").append(qqNumber)
|
|
||||||
.append("&groupcode=").append(groupCode);
|
|
||||||
|
|
||||||
HttpURLConnection conn = (HttpURLConnection) new URL(builder.toString()).openConnection();
|
String builder = "http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" + "&ukey=" +
|
||||||
|
uKeyHex.replace(" ", "") +
|
||||||
|
"&filezise=" + fileSize +
|
||||||
|
"&range=" + "0" +
|
||||||
|
"&uin=" + qqNumber +
|
||||||
|
"&groupcode=" + groupCode;
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) new URL(builder).openConnection();
|
||||||
conn.setRequestProperty("User-agent", "QQClient");
|
conn.setRequestProperty("User-agent", "QQClient");
|
||||||
conn.setRequestProperty("Content-length", "" + fileSize);
|
conn.setRequestProperty("Content-length", "" + fileSize);
|
||||||
conn.setRequestMethod("POST");
|
conn.setRequestMethod("POST");
|
||||||
|
conn.setDoOutput(true);
|
||||||
conn.getOutputStream().write(img);
|
conn.getOutputStream().write(img);
|
||||||
|
|
||||||
conn.connect();
|
conn.connect();
|
||||||
System.out.println(conn.getResponseCode());
|
return conn.getResponseCode() == 200;
|
||||||
System.out.println(conn.getResponseMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package net.mamoe.mirai.utils.config;
|
package net.mamoe.mirai.utils;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -16,15 +16,15 @@ import java.util.function.Function;
|
|||||||
*
|
*
|
||||||
* @author NaturalHG
|
* @author NaturalHG
|
||||||
*/
|
*/
|
||||||
public class MiraiSynchronizedLinkedListMap<K,V> extends AbstractMap<K,V> {
|
public class MiraiSynchronizedLinkedHashMap<K, V> extends AbstractMap<K, V> {
|
||||||
|
|
||||||
public MiraiSynchronizedLinkedListMap(){
|
public MiraiSynchronizedLinkedHashMap() {
|
||||||
this.sortedMap = Collections.synchronizedMap(new LinkedHashMap<>());
|
this.sortedMap = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Map<K, V> sortedMap;
|
protected final Map<K, V> sortedMap;
|
||||||
|
|
||||||
public MiraiSynchronizedLinkedListMap(LinkedHashMap<K,V> map){
|
public MiraiSynchronizedLinkedHashMap(LinkedHashMap<K, V> map) {
|
||||||
this.sortedMap = Collections.synchronizedMap(map);
|
this.sortedMap = Collections.synchronizedMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,13 +155,13 @@ public class MiraiSynchronizedLinkedListMap<K,V> extends AbstractMap<K,V> {
|
|||||||
return this.sortedMap.merge(key,value,remappingFunction);
|
return this.sortedMap.merge(key,value,remappingFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(MiraiSynchronizedLinkedListMap o) {
|
public boolean equals(MiraiSynchronizedLinkedHashMap o) {
|
||||||
return this.sortedMap.equals(o.sortedMap);
|
return this.sortedMap.equals(o.sortedMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return o instanceof MiraiSynchronizedLinkedListMap?this.equals((MiraiSynchronizedLinkedListMap)o):super.equals(o);
|
return o instanceof MiraiSynchronizedLinkedHashMap ? this.equals((MiraiSynchronizedLinkedHashMap) o) : super.equals(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,195 @@
|
|||||||
|
package net.mamoe.mirai.utils;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.IntFunction;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Him188moe
|
||||||
|
*/
|
||||||
|
public class MiraiSynchronizedLinkedList<E> extends AbstractList<E> {
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
protected final List<E> syncList;
|
||||||
|
|
||||||
|
public MiraiSynchronizedLinkedList() {
|
||||||
|
this.syncList = Collections.synchronizedList(new LinkedList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MiraiSynchronizedLinkedList(Collection<E> collection) {
|
||||||
|
this.syncList = Collections.synchronizedList(new LinkedList<>(collection));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E get(int index) {
|
||||||
|
return this.syncList.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(Consumer<? super E> action) {
|
||||||
|
this.syncList.forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<E> spliterator() {
|
||||||
|
return this.syncList.spliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<E> stream() {
|
||||||
|
return this.syncList.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<E> parallelStream() {
|
||||||
|
return this.syncList.parallelStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return this.syncList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SuspiciousToArrayCall")
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(IntFunction<T[]> generator) {
|
||||||
|
return this.syncList.toArray(generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeIf(Predicate<? super E> filter) {
|
||||||
|
return this.syncList.removeIf(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replaceAll(UnaryOperator<E> operator) {
|
||||||
|
this.syncList.replaceAll(operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sort(Comparator<? super E> c) {
|
||||||
|
this.syncList.sort(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E e) {
|
||||||
|
return this.syncList.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E set(int index, E element) {
|
||||||
|
return this.syncList.set(index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, E element) {
|
||||||
|
this.syncList.add(index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E remove(int index) {
|
||||||
|
return this.syncList.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
return this.syncList.indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
return this.syncList.lastIndexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
this.syncList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int index, Collection<? extends E> c) {
|
||||||
|
return this.syncList.addAll(index, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return this.syncList.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ListIterator<E> listIterator() {
|
||||||
|
return this.syncList.listIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ListIterator<E> listIterator(int index) {
|
||||||
|
return this.syncList.listIterator(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public List<E> subList(int fromIndex, int toIndex) {
|
||||||
|
return this.syncList.subList(fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.syncList.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.syncList.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return this.syncList.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return this.syncList.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SuspiciousToArrayCall")
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(@NotNull T[] a) {
|
||||||
|
return this.syncList.toArray(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
return this.syncList.remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(@NotNull Collection<?> c) {
|
||||||
|
return this.syncList.containsAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(@NotNull Collection<? extends E> c) {
|
||||||
|
return this.syncList.addAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(@NotNull Collection<?> c) {
|
||||||
|
return this.syncList.removeAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(@NotNull Collection<?> c) {
|
||||||
|
return this.syncList.retainAll(c);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package net.mamoe.mirai.utils.config;
|
package net.mamoe.mirai.utils.config;
|
||||||
|
|
||||||
|
import net.mamoe.mirai.utils.MiraiSynchronizedLinkedHashMap;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -10,7 +11,7 @@ import java.util.function.Supplier;
|
|||||||
/**
|
/**
|
||||||
* @author NaturalHG
|
* @author NaturalHG
|
||||||
*/
|
*/
|
||||||
public class MiraiConfigSection<T> extends MiraiSynchronizedLinkedListMap<String, T> {
|
public class MiraiConfigSection<T> extends MiraiSynchronizedLinkedHashMap<String, T> {
|
||||||
|
|
||||||
public MiraiConfigSection(){
|
public MiraiConfigSection(){
|
||||||
super();
|
super();
|
||||||
|
Loading…
Reference in New Issue
Block a user