Commit b38c262d authored by Him188's avatar Him188

Multiplatform with gradle building

parent 58c6a407
......@@ -12,8 +12,6 @@
# Package Files #
*.war
*.jar
!/mirai-debug/lib/jpcap.jar
*.nar
*.ear
*.zip
......@@ -23,6 +21,8 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
target/
build/
.idea/
*.iml
......@@ -31,4 +31,7 @@ mirai.iml
.idea/*
/.idea/*
/test
\ No newline at end of file
/test
.gradle/
\ No newline at end of file
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
jcenter()
google()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
group = "net.mamoe"
version = "1.0"
repositories {
jcenter()
mavenCentral()
google()
maven { url "https://mirrors.huaweicloud.com/repository/maven/" }
maven { url "http://repo.maven.apache.org/maven2" }
}
apply from: rootProject.file('dependencies.gradle')
}
ext {
// dependencies
// kotlin
kotlinJvm = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
kotlinCommon = "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
// coroutine
coroutine_version = "1.3.0"
coroutine = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
coroutineCommon = "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutine_version"
coroutineNative = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$coroutine_version"
coroutineAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
coroutineJs = "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutine_version"
// reflect
reflect = "org.jetbrains.kotlin:kotlin-reflect:$coroutine_version"
}
#Thu Oct 03 14:28:43 CST 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
apply plugin: "kotlin"
apply plugin: "java"
dependencies {
implementation project(':mirai-core')
implementation project(':mirai-console')
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai</artifactId>
<version>1.0</version>
</parent>
<artifactId>mirai-api</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-core</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-console</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
</plugins>
</build>
</project>
\ No newline at end of file
package net.mamoe.mirai;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
public class Bot {
@Getter
private final long qq;
public Bot(long qq){
......
package net.mamoe.mirai;
import java.util.ArrayList;
import java.util.List;
/**
* MiraiAPI provides
* - the status of the Mirai-Core
* - the fundamental bot operations.
* - the plugin status.
* <p>
* It was designed for users, not developers,
* Web-based controller, UI controller or console is depending on Mirai-API
* <p>
* Mirai-API does NOT contains fancy objects, and this means there are less functions it can do compare with Mirai-Core
* <p>
* Again, for extending/developing Mirai, you should refer to Mirai-Core
* for only using , you should refer to Mirai-API
*/
public class MiraiAPI {
public static void startMirai(String[] args) {
MiraiMain.main(args);
}
public static void closeMirai() {
MiraiServer.INSTANCE.shutdown();
}
public static void restartMirai(String[] args) {
MiraiServer.INSTANCE.shutdown();
MiraiMain.main(args);
}
public static String getMiraiVersion() {
return MiraiServer.MIRAI_VERSION;
}
public static String getQQPortocolVersion() {
return MiraiServer.QQ_VERSION;
}
public static boolean isMiraiEnabled() {
// TODO: 2019/10/2
return false;
}
public static List<String> getEnabledPluginList() {
return new ArrayList<>();
}
public static List<Long> getEnabledBots() {
return new ArrayList<>();
}
public static Bot getBot(long qq) {
return new Bot(qq);
}
public static void addBot(long qq, String password) {
}
}
apply plugin: "kotlin"
apply plugin: "application"
apply plugin: "java"
dependencies {
compile project(':mirai-core')
compile rootProject.ext.kotlinCommon
compile rootProject.ext.kotlinJvm
compile rootProject.ext.reflect
compile rootProject.ext.coroutine
}
sourceCompatibility = "11"
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai</artifactId>
<version>1.0</version>
</parent>
<artifactId>mirai-console</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-core</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package net.mamoe.mirai;
import lombok.Getter;
/**
* @author NaturalHG
*/
public final class MiraiMain {
@Getter
private static MiraiServer server;
public static void main(String[] args) {
......
package net.mamoe.mirai
import kotlinx.coroutines.runBlocking
import lombok.Getter
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginState
import net.mamoe.mirai.task.MiraiTaskManager
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.config.MiraiConfig
import net.mamoe.mirai.utils.setting.MiraiSettings
......@@ -22,22 +20,11 @@ import java.util.concurrent.ExecutionException
* @author NaturalHG
*/
object MiraiServer {
const val MIRAI_VERSION = "1.0.0"
const val QQ_VERSION = "4.9.0"
@Getter //is running under UNIX
var isUnix: Boolean = false
private set
@Getter
var parentFolder: File = File(System.getProperty("user.dir"))
@Getter
var taskManager: MiraiTaskManager
internal set
@Getter
var logger: MiraiLogger
internal set
......@@ -52,9 +39,8 @@ object MiraiServer {
this.isUnix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS")
this.logger = MiraiLogger
this.taskManager = MiraiTaskManager.getInstance()
logger.info("About to run Mirai (" + MiraiServer.MIRAI_VERSION + ") under " + if (isUnix) "unix" else "windows")
logger.info("About to run Mirai (" + Mirai.VERSION + ") under " + if (isUnix) "unix" else "windows")
logger.info("Loading data under " + LoggerTextFormat.GREEN + this.parentFolder)
val setting = this.parentFolder + "/Mirai.ini"
......@@ -117,17 +103,17 @@ object MiraiServer {
this.settings = MiraiSettings(setting)
val network = this.settings.getMapSection("network")
network.set("enable_proxy", "not supporting yet")
network["enable_proxy"] = "not supporting yet"
val proxy = this.settings.getListSection("proxy")
proxy.add("1.2.3.4:95")
proxy.add("1.2.3.4:100")
val worker = this.settings.getMapSection("worker")
worker.set("core_task_pool_worker_amount", 5)
worker["core_task_pool_worker_amount"] = 5
val plugin = this.settings.getMapSection("plugin")
plugin.set("debug", false)
plugin["debug"] = false
this.settings.save()
logger.info("initialized; changing can be made in setting file: $setting")
......@@ -142,7 +128,7 @@ object MiraiServer {
private fun reload() {
this.enabled = true
MiraiLogger.info(LoggerTextFormat.GREEN.toString() + "Server enabled; Welcome to Mirai")
MiraiLogger.info("Mirai Version=" + MiraiServer.MIRAI_VERSION + " QQ Version=" + MiraiServer.QQ_VERSION)
MiraiLogger.info("Mirai Version=" + Mirai.VERSION)
MiraiLogger.info("Initializing [Bot]s")
......@@ -189,7 +175,7 @@ object MiraiServer {
val strings = it.split("----").dropLastWhile { it.isEmpty() }.toTypedArray()
val bot = Bot(BotAccount(strings[0].toLong(), strings[1]), Console())
if (runBlocking { bot.network.tryLogin(200).await() } === LoginState.SUCCESS) {
if (runBlocking { bot.network.tryLogin(200) } === LoginState.SUCCESS) {
bot.green("Login succeed")
return bot
}
......
apply plugin: "kotlin-multiplatform"
kotlin {
targets {
fromPreset(presets.jvm, "jvm")
}
jvm()
sourceSets {
commonMain {
dependencies {
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
implementation rootProject.ext.kotlinCommon
implementation rootProject.ext.reflect
implementation rootProject.ext.coroutine
implementation rootProject.ext.kotlinJvm
}
}
jvmMain {
dependencies {
implementation rootProject.ext.kotlinJvm
implementation rootProject.ext.reflect
implementation rootProject.ext.coroutine
implementation 'org.yaml:snakeyaml:1.18'
implementation 'org.jsoup:jsoup:1.12.1'
implementation 'org.ini4j:ini4j:0.5.2'
}
}
all {
languageSettings.enableLanguageFeature("InlineClasses")
}
}
}
compileKotlinJvm{
}
configurations {
compileClasspath
}
/*
dependencies {
compile 'com.google.protobuf:protobuf-java:3.5.0'
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M2'
compile 'io.netty:netty-all:4.1.38.Final'
compile 'net.java.dev.jna:jna:5.4.0'
compile 'org.apache.logging.log4j:log4j-core:2.12.1'
compile 'org.yaml:snakeyaml:1.18'
compile 'org.jetbrains.kotlin:kotlin-reflect:1.3.41'
compile 'org.jsoup:jsoup:1.12.1'
compile 'org.ini4j:ini4j:0.5.2'
}*/
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>mirai-core</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai</artifactId>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<!-- https://mvnrepository.com/artifact/jpcap/jpcap -->
<dependency>
<groupId>jpcap</groupId>
<artifactId>jpcap</artifactId>
<version>0.1.18-002</version>
<scope>system</scope>
<systemPath>../mirai-debug/lib/jpcap.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-XXLanguage:+InlineClasses</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package net.mamoe.mirai.utils
//todo
\ No newline at end of file
package net.mamoe.mirai
import kotlinx.coroutines.CompletableDeferred
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
......@@ -28,7 +27,7 @@ val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
//NetworkHandler
suspend fun Bot.sendPacket(packet: ClientPacket) = this.network.socket.sendPacket(packet)
fun Bot.login(touchingTimeoutMillis: Long = 200): CompletableDeferred<LoginState> = this.network.tryLogin()
suspend fun Bot.login(touchingTimeoutMillis: Long = 200): LoginState = this.network.tryLogin()
//BotAccount
......
package net.mamoe.mirai
//expect fun s(): String
/**
* @author Him188moe
*/
object Mirai {
const val VERSION: String = "1.0.0"
}
\ No newline at end of file
package net.mamoe.mirai.contact
import kotlinx.coroutines.CompletableDeferred
import net.mamoe.mirai.Bot
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.MessageChain
......@@ -24,7 +25,7 @@ abstract class Contact internal constructor(val bot: Bot, val number: Long) {
/**
* 上传图片
*/
fun uploadImage(session: LoginSession, image: UnsolvedImage): CompletableFuture<Unit> {
fun uploadImage(session: LoginSession, image: UnsolvedImage): CompletableDeferred<Unit> {
return image.upload(session, this)
}
......
package net.mamoe.mirai.message.defaults
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.mamoe.mirai.contact.Contact
......@@ -28,7 +29,7 @@ import javax.imageio.ImageIO
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> {
fun upload(session: LoginSession, contact: Contact): CompletableDeferred<Unit> {
return session.expectPacket<ServerTryGetImageIDResponsePacket> {
toSend { ClientTryGetImageIDPacket(session.bot.account.qqNumber, session.sessionKey, contact.number, image) }
......
......@@ -12,7 +12,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.Packet
import net.mamoe.mirai.network.protocol.tim.packet.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginState
import java.io.Closeable
/**
* Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务.
......@@ -32,7 +31,7 @@ import java.io.Closeable
*
* @author Him188moe
*/
interface BotNetworkHandler : Closeable {
interface BotNetworkHandler {
/**
* 网络层处理器. 用于编码/解码 [Packet], 发送/接受 [ByteArray]
*
......@@ -59,7 +58,7 @@ interface BotNetworkHandler : Closeable {
*
* @param touchingTimeoutMillis 连接每个服务器的 timeout
*/
fun tryLogin(touchingTimeoutMillis: Long = 200): CompletableDeferred<LoginState>
suspend fun tryLogin(touchingTimeoutMillis: Long = 200): LoginState
/**
* 添加一个临时包处理器
......@@ -68,5 +67,5 @@ interface BotNetworkHandler : Closeable {
*/
fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>)
override fun close()
fun close()
}
\ No newline at end of file
package net.mamoe.mirai.network
import kotlinx.coroutines.CompletableDeferred
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.protocol.tim.handler.DataPacketSocket
import net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.utils.getGTK
import java.util.concurrent.CompletableFuture
/**
* 登录会话. 当登录完成后, 客户端会拿到 sessionKey.
......@@ -58,10 +58,10 @@ class LoginSession(
* @return future. 可进行超时处理
*/
@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
inline fun <reified P : ServerPacket> expectPacket(handlerTemporary: TemporaryPacketHandler<P>.() -> Unit): CompletableDeferred<Unit> {
val deferred = CompletableDeferred<Unit>()
this.bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this).also(handlerTemporary))
return deferred
}
/**
......@@ -82,12 +82,12 @@ class LoginSession(
* @return future. 可进行超时处理
*/
@JvmSynthetic
inline fun <reified P : ServerPacket> expectPacket(toSend: ClientPacket, noinline handler: suspend (P) -> Unit): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
this.bot.network.addHandler(TemporaryPacketHandler(P::class, future, this).also {
inline fun <reified P : ServerPacket> expectPacket(toSend: ClientPacket, noinline handler: suspend (P) -> Unit): CompletableDeferred<Unit> {
val deferred = CompletableDeferred<Unit>()
this.bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this).also {
it.toSend(toSend)
it.onExpect(handler)
})
return future
return deferred
}
}
\ No newline at end of file
......@@ -13,17 +13,12 @@ import net.mamoe.mirai.network.NetworkScope
import net.mamoe.mirai.network.protocol.tim.handler.*
import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.*
import java.io.Closeable
import java.io.File
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import javax.imageio.ImageIO
/**
......@@ -53,28 +48,21 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
temporaryPacketHandlers.add(temporaryPacketHandler)
}
override fun tryLogin(touchingTimeoutMillis: Long): CompletableDeferred<LoginState> {
val ipQueue: LinkedList<String> = LinkedList(TIMProtocol.SERVER_IP)
val future = CompletableDeferred<LoginState>()
override suspend fun tryLogin(touchingTimeoutMillis: Long): LoginState {
return loginInternal(touchingTimeoutMillis, LinkedList(TIMProtocol.SERVER_IP))
}
fun login() {
this.socket.close()
val ip = ipQueue.poll()
if (ip == null) {
future.complete(LoginState.UNKNOWN)//所有服务器均返回 UNKNOWN
return
}
private suspend fun loginInternal(touchingTimeoutMillis: Long, ipQueue: LinkedList<String>): LoginState {
this.socket.close()
val ip = ipQueue.poll() ?: return LoginState.UNKNOWN//所有服务器均返回 UNKNOWN
this.socket.touch(ip, touchingTimeoutMillis).get().let { state ->
if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
login()//超时或未知, 重试连接下一个服务器
} else {
future.complete(state)
}
return this.socket.touch(ip, touchingTimeoutMillis).await().let { state ->
if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
loginInternal(touchingTimeoutMillis, ipQueue)//超时或未知, 重试连接下一个服务器
} else {
state
}
}
login()
return future
}
//private | internal
......@@ -97,7 +85,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
}
internal inner class BotSocket : Closeable, DataPacketSocket {
internal inner class BotSocket : DataPacketSocket {
override suspend fun distributePacket(packet: ServerPacket) {
try {
packet.decode()
......@@ -156,7 +144,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
restartSocket()
}
internal var loginFuture: CompletableFuture<LoginState>? = null
internal var loginResult: CompletableDeferred<LoginState>? = null
@Synchronized
private fun restartSocket() {
......@@ -186,23 +174,23 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
/**
* Start network and touch the server
*/
fun touch(serverAddress: String, timeoutMillis: Long): CompletableFuture<LoginState> {
fun touch(serverAddress: String, timeoutMillis: Long): CompletableDeferred<LoginState> {
bot.info("Connecting server: $serverAddress")
if (this@TIMBotNetworkHandler::login.isInitialized) {
login.close()
}
login = Login()
this.loginFuture = CompletableFuture()
this.loginResult = CompletableDeferred()
serverIP = serverAddress
bot.waitForPacket(ServerPacket::class, timeoutMillis) {
loginFuture!!.complete(LoginState.TIMEOUT)
loginResult!!.complete(LoginState.TIMEOUT)
}
runBlocking {
sendPacket(ClientTouchPacket(bot.account.qqNumber, serverIP))
}
return this.loginFuture!!
return this.loginResult!!
}
/**
......@@ -236,13 +224,14 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
override fun getOwner(): Bot = this@TIMBotNetworkHandler.bot
override fun close() {
this.socket?.close()
if (this.loginFuture != null) {
if (!this.loginFuture!!.isDone) {
this.loginFuture!!.cancel(true)
if (this.loginResult != null) {
if (!this.loginResult!!.isCompleted) {
this.loginResult!!.cancel(CancellationException("socket closed"))
}
this.loginFuture = null
this.loginResult = null
}
}
......@@ -254,7 +243,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
/**
* 处理登录过程
*/
inner class Login : Closeable {
inner class Login {
private lateinit var token00BA: ByteArray
private lateinit var token0825: ByteArray//56
private var loginTime: Int = 0
......@@ -269,7 +258,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
private var captchaSectionId: Int = 1
private var captchaCache: ByteArray? = byteArrayOf()//每次包只发一部分验证码来
private var heartbeatFuture: ScheduledFuture<*>? = null
private var heartbeatJob: Job? = null
suspend fun onPacketReceived(packet: ServerPacket) {
......@@ -289,7 +278,7 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
}
is ServerLoginResponseFailedPacket -> {
socket.loginFuture?.complete(packet.loginState)
socket.loginResult?.complete(packet.loginState)
bot.close()
return
}
......@@ -373,13 +362,13 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
is ServerSessionKeyResponsePacket -> {
sessionKey = packet.sessionKey
heartbeatFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
runBlocking {
socket.sendPacket(ClientHeartbeatPacket(bot.account.qqNumber, sessionKey))
}
}, 90000, 90000, TimeUnit.MILLISECONDS)
socket.loginFuture!!.complete(LoginState.SUCCESS)
heartbeatJob = NetworkScope.launch {
delay(90000)
socket.sendPacket(ClientHeartbeatPacket(bot.account.qqNumber, sessionKey))
}
socket.loginResult!!.complete(LoginState.SUCCESS)
login.changeOnlineStatus(ClientLoginStatus.ONLINE)
}
......@@ -420,12 +409,12 @@ internal class TIMBotNetworkHandler(private val bot: Bot) : BotNetworkHandler {
socket.sendPacket(ClientChangeOnlineStatusPacket(bot.account.qqNumber, sessionKey, status))
}
override fun close() {
fun close() {
this.captchaCache = null
this.heartbeatFuture?.cancel(true)
this.heartbeatJob?.cancel(CancellationException("handler closed"))
this.heartbeatFuture = null
this.heartbeatJob = null
}
}
}
......@@ -66,7 +66,7 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
session.sKey = packet.sKey
session.cookies = "uin=o" + session.bot.account.qqNumber + ";skey=" + session.sKey + ";"
sKeyRefresherFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sKeyRefresherFuture = MiraiThreadPool.instance.scheduleWithFixedDelay({
runBlocking {
session.socket.sendPacket(ClientSKeyRefreshmentRequestPacket(session.bot.account.qqNumber, session.sessionKey))
}
......
......@@ -6,7 +6,6 @@ import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import java.io.Closeable
/**
* 网络接口.
......@@ -15,7 +14,7 @@ import java.io.Closeable
*
* @author Him188moe
*/
interface DataPacketSocket : Closeable {
interface DataPacketSocket {
fun getOwner(): Bot
/**
......@@ -34,5 +33,5 @@ interface DataPacketSocket : Closeable {
fun isClosed(): Boolean
override fun close()
fun close()
}
\ No newline at end of file
package net.mamoe.mirai.network.protocol.tim.handler
import kotlinx.coroutines.CompletableDeferred
import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
......@@ -21,7 +22,7 @@ import kotlin.reflect.KClass
*/
open class TemporaryPacketHandler<P : ServerPacket>(
private val expectationClass: KClass<P>,
private val future: CompletableFuture<Unit>,
private val deferred: CompletableDeferred<Unit>,
private val fromSession: LoginSession
) {
private lateinit var toSend: ClientPacket
......@@ -53,7 +54,7 @@ open class TemporaryPacketHandler<P : ServerPacket>(
if (expectationClass.isInstance(packet) && session === this.fromSession) {
@Suppress("UNCHECKED_CAST")
expect(packet as P)
future.complete(Unit)
deferred.complete(Unit)
return true
}
return false
......
......@@ -2,7 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet
import lombok.Getter
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.PacketNameFormatter.adjustName
import net.mamoe.mirai.utils.*
......@@ -16,7 +15,6 @@ import java.security.MessageDigest
*/
abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
@Getter
val idHex: String
private var encoded: Boolean = false
......
......@@ -347,7 +347,7 @@ internal fun <P : ServerPacket> Bot.waitForPacket(packetClass: KClass<P>, timeou
}
MiraiThreadPool.getInstance().submit {
MiraiThreadPool.instance.submit {
val startingTime = System.currentTimeMillis()
while (!got) {
if (System.currentTimeMillis() - startingTime > timeoutMillis) {
......
package net.mamoe.mirai.task
import java.util.concurrent.Callable
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Consumer
import java.util.function.Predicate
/**
* @author NaturalHG
*/
/*
class MiraiTaskManager private constructor() {
private val pool: MiraiThreadPool
init {
this.pool = MiraiThreadPool()
}
/**
* 基础Future处理
*/
fun execute(runnable: Runnable) {
this.execute(runnable, MiraiTaskExceptionHandler.printing())
}
fun execute(runnable: Runnable, handler: MiraiTaskExceptionHandler) {
this.pool.execute {
try {
runnable.run()
} catch (e: Exception) {
handler.onHandle(e)
}
}
}
fun <D> submit(callable: Callable<D>): Future<D> {
return this.submit(callable, MiraiTaskExceptionHandler.printing())
}
fun <D> submit(callable: Callable<D>, handler: MiraiTaskExceptionHandler): Future<D> {
return this.pool.submit<D> {
try {
callable.call()
} catch (e: Throwable) {
handler.onHandle(e)
null
}
}
}
/**
* 异步任务
*/
fun <D> ansycTask(callable: Callable<D>, callback: Consumer<D>) {
this.ansycTask(callable, callback, MiraiTaskExceptionHandler.printing())
}
fun <D> ansycTask(callable: Callable<D>, callback: Consumer<D>, handler: MiraiTaskExceptionHandler) {
this.pool.execute {
try {
callback.accept(callable.call())
} catch (e: Throwable) {
handler.onHandle(e)
}
}
}
/**
* 定时任务
*/
fun repeatingTask(runnable: Runnable, intervalMillis: Long) {
this.repeatingTask(runnable, intervalMillis, MiraiTaskExceptionHandler.printing())
}
fun repeatingTask(runnable: Runnable, intervalMillis: Long, handler: MiraiTaskExceptionHandler) {
this.repeatingTask<Runnable>(runnable, intervalMillis, { a -> true }, handler)
}
fun repeatingTask(runnable: Runnable, intervalMillis: Long, times: Int) {
this.repeatingTask(runnable, intervalMillis, times, MiraiTaskExceptionHandler.printing())
}
fun repeatingTask(runnable: Runnable, intervalMillis: Long, times: Int, handler: MiraiTaskExceptionHandler) {
val integer = AtomicInteger(times - 1)
this.repeatingTask<Runnable>(
runnable, intervalMillis, { a -> integer.getAndDecrement() > 0 }, handler
)
}
fun <D : Runnable> repeatingTask(runnable: D, intervalMillis: Long, shouldContinue: Predicate<D>, handler: MiraiTaskExceptionHandler) {
Thread {
do {
this.pool.execute {
try {
runnable.run()
} catch (e: Exception) {
handler.onHandle(e)
}
}
try {
Thread.sleep(intervalMillis)
} catch (e: InterruptedException) {
e.printStackTrace()
}
} while (shouldContinue.test(runnable))
}.start()
}
fun deletingTask(runnable: Runnable, intervalMillis: Long) {
Thread {
try {
Thread.sleep(intervalMillis)
} catch (e: InterruptedException) {
e.printStackTrace()
}
this.pool.execute(runnable)
}.start()
}
companion object {
val instance = MiraiTaskManager()
}
}
*/
\ No newline at end of file
package net.mamoe.mirai.task
import net.mamoe.mirai.Mirai
import java.io.Closeable
import java.util.concurrent.ScheduledThreadPoolExecutor
/**
* @author NaturalHG
*/
class MiraiThreadPool internal constructor()/*super(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>()
);*/ : ScheduledThreadPoolExecutor(10), Closeable {
override fun close() {
this.shutdown()
if (!this.isShutdown) {
this.shutdownNow()
}
}
companion object {
val instance = MiraiThreadPool()
@JvmStatic
fun main(args: Array<String>) {
println(Mirai.VERSION)
}
}
}
package net.mamoe.mirai.utils
import java.awt.*
import java.awt.image.BufferedImage
import java.util.ArrayList
import java.util.concurrent.Callable
import kotlin.math.max
import kotlin.math.min
/**
* Convert IMAGE into Chars that could shows in terminal
*
* @author NaturalHG
*/
class CharImageConverter @JvmOverloads constructor(
/**
* width should depends on the width of the terminal
*/
private var image: BufferedImage?, private val width: Int, private val ignoreRate: Double = 0.95) : Callable<String> {
override fun call(): String {
/*
* resize Image
* */
val newHeight = (this.image!!.getHeight() * (width.toDouble() / this.image!!.getWidth())).toInt()
val tmp = image!!.getScaledInstance(width, newHeight, Image.SCALE_SMOOTH)
val dimg = BufferedImage(width, newHeight, BufferedImage.TYPE_INT_ARGB)
val g2d = dimg.createGraphics()
g2d.drawImage(tmp, 0, 0, null)
this.image = dimg
val background = gray(image!!.getRGB(0, 0))
val builder = StringBuilder()
val lines = ArrayList<StringBuilder>(this.image!!.getHeight())
var minXPos = this.width
var maxXPos = 0
for (y in 0 until image!!.getHeight()) {
val builderLine = StringBuilder()
for (x in 0 until image!!.getWidth()) {
val gray = gray(image!!.getRGB(x, y))
if (grayCompare(gray, background)) {
builderLine.append(" ")
} else {
builderLine.append("#")
if (x < minXPos) {
minXPos = x
}
if (x > maxXPos) {
maxXPos = x
}
}
}
if (builderLine.toString().isBlank()) {
continue
}
lines.add(builderLine)
}
for (line in lines) {
builder.append(line.substring(minXPos, maxXPos)).append("\n")
}
return builder.toString()
}
private fun gray(rgb: Int): Int {
val R = rgb and 0xff0000 shr 16
val G = rgb and 0x00ff00 shr 8
val B = rgb and 0x0000ff
return (R * 30 + G * 59 + B * 11 + 50) / 100
}
fun grayCompare(g1: Int, g2: Int): Boolean {
return min(g1, g2).toDouble() / max(g1, g2) >= ignoreRate
}
}
package net.mamoe.mirai.utils
import java.awt.image.BufferedImage
/**
* @author NaturalHG
*/
object CharImageUtil {
@JvmOverloads
fun createCharImg(image: BufferedImage, sizeWeight: Int = 100, sizeHeight: Int = 20): String {
return CharImageConverter(image, sizeWeight).call()
}
}
\ No newline at end of file
package net.mamoe.mirai.utils;
package net.mamoe.mirai.utils
/**
* QQ 在线状态
......@@ -6,17 +6,12 @@ package net.mamoe.mirai.utils;
* @author Him188moe
* @see net.mamoe.mirai.network.protocol.tim.packet.login.ClientChangeOnlineStatusPacket
*/
public enum ClientLoginStatus {
enum class ClientLoginStatus(
// TODO: 2019/8/31 add more ClientLoginStatus
val id: Int//1 ubyte
) {
/**
* 我在线上
*/
ONLINE(0x0A);
// TODO: 2019/8/31 add more ClientLoginStatus
public final int id;//1 ubyte
ClientLoginStatus(int id) {
this.id = id;
}
ONLINE(0x0A)
}
package net.mamoe.mirai.utils;
package net.mamoe.mirai.utils
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
/**
* @author NaturalHG
*/
public class ImageNetworkUtils {
public static boolean postImage(String uKeyHex, int fileSize, long botNumber, long groupCode, byte[] img) throws IOException {
object ImageNetworkUtils {
@Throws(IOException::class)
fun postImage(uKeyHex: String, fileSize: Int, botNumber: Long, groupCode: Long, img: ByteArray): Boolean {
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=” + 删全部空 (ukey) + “&filesize=” + 到文本 (fileSize) + “&range=0&uin=” + g_uin + “&groupcode=” + Group
String builder = "http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" +
val builder = "http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" +
"&ukey=" + uKeyHex.replace(" ", "") +
"&filezise=" + fileSize +
"&range=" + "0" +
"&uin=" + botNumber +
"&groupcode=" + groupCode;
HttpURLConnection conn = (HttpURLConnection) new URL(builder).openConnection();
conn.setRequestProperty("User-Agent", "QQClient");
conn.setRequestProperty("Content-Length", "" + fileSize);
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.getOutputStream().write(img);
"&groupcode=" + groupCode
val conn = URL(builder).openConnection() as HttpURLConnection
conn.setRequestProperty("User-Agent", "QQClient")
conn.setRequestProperty("Content-Length", "" + fileSize)
conn.requestMethod = "POST"
conn.doOutput = true
conn.outputStream.write(img)
conn.connect();
return conn.getResponseCode() == 200;
conn.connect()
return conn.responseCode == 200
}
}
package net.mamoe.mirai.utils
/**
* @author NaturalHG
*/
enum class LoggerTextFormat(private val format: String) {
RESET("\u001b[0m"),
BLUE("\u001b[0;34m"),
BLACK("\u001b[0;30m"),
DARK_GREY("\u001b[1;30m"),
LIGHT_BLUE("\u001b[1;34m"),
GREEN("\u001b[0;32m"),
LIGHT_GTEEN("\u001b[1;32m"),
CYAN("\u001b[0;36m"),
LIGHT_CYAN("\u001b[1;36m"),
RED("\u001b[0;31m"),
LIGHT_RED("\u001b[1;31m"),
PURPLE("\u001b[0;35m"),
LIGHT_PURPLE("\u001b[1;35m"),
BROWN("\u001b[0;33m"),
YELLOW("\u001b[1;33m"),
LIGHT_GRAY("\u001b[0;37m"),
WHITE("\u001b[1;37m");
override fun toString(): String {
//if(MiraiServer.getInstance().isUnix()){
return format
// }
// return "";
}
}
package net.mamoe.mirai.utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
......@@ -54,7 +51,6 @@ public class MiraiSynchronizedLinkedHashMap<K, V> extends AbstractMap<K, V> {
return sortedMap.get(key);
}
@Nullable
@Override
public V put(K key, V value) {
return sortedMap.put(key,value);
......@@ -66,7 +62,7 @@ public class MiraiSynchronizedLinkedHashMap<K, V> extends AbstractMap<K, V> {
}
@Override
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
public void putAll(Map<? extends K, ? extends V> m) {
sortedMap.putAll(m);
}
......@@ -75,19 +71,16 @@ public class MiraiSynchronizedLinkedHashMap<K, V> extends AbstractMap<K, V> {
sortedMap.clear();
}
@NotNull
@Override
public Set<K> keySet() {
return sortedMap.keySet();
}
@NotNull
@Override
public Collection<V> values() {
return sortedMap.values();
}
@NotNull
@Override
public Set<Entry<K, V>> entrySet() {
return sortedMap.entrySet();
......@@ -113,7 +106,6 @@ public class MiraiSynchronizedLinkedHashMap<K, V> extends AbstractMap<K, V> {
return this.sortedMap.replace(key,oldValue,newValue);
}
@Nullable
@Override
public V replace(K key, V value) {
return this.sortedMap.replace(key,value);
......@@ -144,7 +136,6 @@ public class MiraiSynchronizedLinkedHashMap<K, V> extends AbstractMap<K, V> {
return this.sortedMap.hashCode();
}
@Nullable
@Override
public V putIfAbsent(K key, V value) {
return this.sortedMap.putIfAbsent(key,value);
......
package net.mamoe.mirai.utils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.IntFunction;
......@@ -116,25 +114,21 @@ public class MiraiSynchronizedLinkedList<E> extends AbstractList<E> {
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);
......@@ -155,16 +149,14 @@ public class MiraiSynchronizedLinkedList<E> extends AbstractList<E> {
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) {
public <T> T[] toArray(T[] a) {
return this.syncList.toArray(a);
}
......@@ -174,22 +166,22 @@ public class MiraiSynchronizedLinkedList<E> extends AbstractList<E> {
}
@Override
public boolean containsAll(@NotNull Collection<?> c) {
public boolean containsAll(Collection<?> c) {
return this.syncList.containsAll(c);
}
@Override
public boolean addAll(@NotNull Collection<? extends E> c) {
public boolean addAll(Collection<? extends E> c) {
return this.syncList.addAll(c);
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
public boolean removeAll(Collection<?> c) {
return this.syncList.removeAll(c);
}
@Override
public boolean retainAll(@NotNull Collection<?> c) {
public boolean retainAll(Collection<?> c) {
return this.syncList.retainAll(c);
}
}
......@@ -15,6 +15,7 @@ import java.util.zip.CRC32
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import javax.imageio.ImageIO
import kotlin.jvm.JvmSynthetic
/**
......
package net.mamoe.mirai.utils.config
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.nio.charset.Charset
import java.util.*
/**
* YAML-TYPE CONFIG
* Thread SAFE
*
* @author NaturalHG
*/
class MiraiConfig(private val root: File) : MiraiConfigSection<Any>(parse(Objects.requireNonNull(root))) {
@Synchronized
fun save() {
val dumperOptions = DumperOptions()
dumperOptions.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
val yaml = Yaml(dumperOptions)
val content = yaml.dump(this.sortedMap)
try {
ByteArrayInputStream(content.toByteArray()).transferTo(FileOutputStream(this.root))
} catch (e: IOException) {
e.printStackTrace()
}
}
companion object {
@Suppress("UNCHECKED_CAST")
private fun parse(file: File): MutableMap<String, Any>? {
/*
if (!file.toURI().getPath().contains(MiraiServer.getInstance().getParentFolder().getPath())) {
file = new File(MiraiServer.getInstance().getParentFolder().getPath(), file.getName());
}*/
if (!file.exists()) {
try {
if (!file.createNewFile()) {
return linkedMapOf()
}
} catch (e: IOException) {
e.printStackTrace()
return linkedMapOf()
}
}
val dumperOptions = DumperOptions()
dumperOptions.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
val yaml = Yaml(dumperOptions)
return yaml.loadAs<LinkedHashMap<*, *>>(file.readLines(Charset.defaultCharset()).joinToString("\n"), LinkedHashMap::class.java) as MutableMap<String, Any>?
}
}
}
package net.mamoe.mirai.utils.config;
import net.mamoe.mirai.utils.MiraiSynchronizedLinkedHashMap;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
import java.util.Map;
......@@ -138,7 +137,6 @@ public class MiraiConfigSection<T> extends MiraiSynchronizedLinkedHashMap<String
return result.toString();
}
@Nullable
@Override
public T put(String key, T value) {
return super.put(key, value);
......
package net.mamoe.mirai.utils.setting
import org.ini4j.Profile
import java.io.IOException
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock
/**
* @author NaturalHG
*/
class MiraiSettingListSection : Vector<Any>(), MiraiSettingSection {
private val lock = ReentrantLock()
@Suppress("UNCHECKED_CAST")
fun <T> getAs(index: Int): T {
return super.get(index) as T
}
fun getInt(index: Int): Int {
return this.getAs(index)
}
fun getDouble(index: Int): Int {
return this.getAs(index)
}
fun getString(index: Int): Int {
return this.getAs(index)
}
fun getFloat(index: Int): Int {
return this.getAs(index)
}
@Synchronized
override fun saveAsSection(section: Profile.Section) {
section.clear()
val integer = AtomicInteger(0)
this.forEach { a -> section.put(integer.getAndAdd(1).toString(), a) }
}
@Throws(IOException::class)
override fun close() {
}
}
package net.mamoe.mirai.utils.setting
import org.ini4j.Profile
import java.io.IOException
import java.util.concurrent.ConcurrentHashMap
import java.util.stream.Collectors
import kotlin.streams.toList
/**
* @author NaturalHG
*/
class MiraiSettingMapSection : ConcurrentHashMap<String, Any>(), MiraiSettingSection {
operator fun <T> get(key: String?, defaultValue: T): T {
if (key == null || key.isEmpty()) {
return defaultValue
}
return if (super.containsKey(key)) {
super.get(key) as T
} else defaultValue
}
operator fun set(key: String, value: Any) {
this[key] = value
}
override fun remove(key: String) {
super.remove(key)
}
fun getInt(key: String): Int {
return this.getInt(key, 0)
}
fun getInt(key: String, defaultValue: Int): Int {
return Integer.parseInt(this[key, defaultValue].toString())
}
fun getDouble(key: String): Double {
return this.getDouble(key, 0.0)
}
fun getDouble(key: String, defaultValue: Double): Double {
return java.lang.Double.parseDouble(this[key, defaultValue].toString())
}
fun getFloat(key: String): Float {
return this.getFloat(key, 0f)
}
fun getFloat(key: String, defaultValue: Float): Float {
return java.lang.Float.parseFloat(this[key, defaultValue].toString())
}
fun getString(key: String): String {
return this.getString(key, "")
}
fun getString(key: String, defaultValue: String): String {
return this[key, defaultValue].toString()
}
fun getObject(key: String): Any? {
return this[key]
}
@Suppress("UNCHECKED_CAST")
fun <T> asList(): List<T> {
return this.values.stream().map { a -> a as T }.toList()
}
@Synchronized
override fun saveAsSection(section: Profile.Section) {
section.clear()
this.forEach{ key, value -> section.put(key, value) }
}
@Throws(IOException::class)
override fun close() {
}
}
package net.mamoe.mirai.utils.setting
import org.ini4j.Profile
import java.io.Closeable
/**
* @author NaturalHG
*/
interface MiraiSettingSection : Closeable {
fun saveAsSection(section: Profile.Section)
}
\ No newline at end of file
package net.mamoe.mirai.utils.setting
import org.ini4j.Config
import org.ini4j.Ini
import java.io.File
import java.io.IOException
import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* Thread-safe Mirai Config <br></br>
* Only supports `INI` format <br></br>
* Supports [Map] and [List]
*
* @author NaturalHG
*/
class MiraiSettings
/*
public MiraiSettings(MiraiPluginBase pluginBase, String filename) {
// TODO: 2019/9/6 每个插件独立文件夹存放
this(new File(filename));
}*/
(file: File) {
private val file: File
private var ini: Ini
private val cacheSection = ConcurrentHashMap<String, MiraiSettingSection>()
init {
var file = file
Objects.requireNonNull(file)
if (!file.name.contains(".")) {
file = File(file.path + ".ini")
}
this.file = file
if (!file.exists() && !file.createNewFile()) {
throw RuntimeException("cannot create config file $file")
}
val config = Config()
config.isMultiSection = true
ini = Ini()
ini.config = config
ini.load(this.file.toURI().toURL())
}
@Synchronized
fun setSection(key: String, section: MiraiSettingSection) {
cacheSection[key] = section
}
@Synchronized
fun getMapSection(key: String): MiraiSettingMapSection {
if (!cacheSection.containsKey(key)) {
val section = MiraiSettingMapSection()
if (ini.containsKey(key)) {
section.putAll(ini[key]!!)
}
cacheSection[key] = section
}
return cacheSection[key] as MiraiSettingMapSection
}
@Synchronized
fun getListSection(key: String): MiraiSettingListSection {
if (!cacheSection.containsKey(key)) {
val section = MiraiSettingListSection()
if (ini.containsKey(key)) {
section.addAll(ini[key]!!.values)
}
cacheSection[key] = section
}
return cacheSection[key] as MiraiSettingListSection
}
@Synchronized
fun save() {
cacheSection.forEach { (k, a) ->
if (!ini.containsKey(k)) {
ini.put(k, "", HashMap<Any, Any>())
}
a.saveAsSection(ini[k]!!)
}
this.clearCache()
try {
ini.store(file)
} catch (e: IOException) {
e.printStackTrace()
}
}
@Synchronized
fun clearCache() {
cacheSection.clear()
}
}
package net.mamoe.mirai
import java.io.File
/**
* @author Him188moe
*/
object Mirai {
val VERSION: String get() = internal.version
val WORKING_DIRECTORY: File get() = internal.workingDirectory
internal lateinit var internal: MiraiInternal
internal abstract class MiraiInternal {
abstract val workingDirectory: File
abstract val version: String
}
}
package net.mamoe.mirai.plugin;
import net.mamoe.mirai.Bot;
/**
* 插件基类.
* <p>
* 插件属于整个 Mirai, 而不是属于单个 {@link Bot}.
*
* @see net.mamoe.mirai.event.MiraiEventManager
* @see net.mamoe.mirai.event.MiraiEventManagerKt
* @author Him188moe
* @author NaturalHG
*/
public abstract class MiraiPluginBase {
}
package net.mamoe.mirai.task;
/**
* @author NaturalHG
*/
@FunctionalInterface
public interface MiraiTaskExceptionHandler {
void onHandle(Throwable e);
static MiraiTaskExceptionHandler printing() {
return Throwable::printStackTrace;
}
static MiraiTaskExceptionHandler ignoring() {
return a -> {
};
}
}
package net.mamoe.mirai.task;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* @author NaturalHG
*/
public final class MiraiTaskManager {
private static MiraiTaskManager instance = new MiraiTaskManager();
public static MiraiTaskManager getInstance() {
return MiraiTaskManager.instance;
}
private MiraiThreadPool pool;
private MiraiTaskManager() {
this.pool = new MiraiThreadPool();
}
/**
* 基础Future处理
*/
public void execute(Runnable runnable) {
this.execute(runnable, MiraiTaskExceptionHandler.printing());
}
public void execute(Runnable runnable, MiraiTaskExceptionHandler handler) {
this.pool.execute(() ->
{
try {
runnable.run();
} catch (Exception e) {
handler.onHandle(e);
}
});
}
public <D> Future<D> submit(Callable<D> callable) {
return this.submit(callable, MiraiTaskExceptionHandler.printing());
}
public <D> Future<D> submit(Callable<D> callable, MiraiTaskExceptionHandler handler) {
return this.pool.submit(() -> {
try {
return callable.call();
} catch (Throwable e) {
handler.onHandle(e);
return null;
}
});
}
/**
* 异步任务
*/
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback) {
this.ansycTask(callable, callback, MiraiTaskExceptionHandler.printing());
}
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback, MiraiTaskExceptionHandler handler) {
this.pool.execute(() -> {
try {
callback.accept(callable.call());
} catch (Throwable e) {
handler.onHandle(e);
}
});
}
/**
* 定时任务
*/
public void repeatingTask(Runnable runnable, long intervalMillis) {
this.repeatingTask(runnable, intervalMillis, MiraiTaskExceptionHandler.printing());
}
public void repeatingTask(Runnable runnable, long intervalMillis, MiraiTaskExceptionHandler handler) {
this.repeatingTask(runnable, intervalMillis, a -> true, handler);
}
public void repeatingTask(Runnable runnable, long intervalMillis, int times) {
this.repeatingTask(runnable, intervalMillis, times, MiraiTaskExceptionHandler.printing());
}
public void repeatingTask(Runnable runnable, long intervalMillis, int times, MiraiTaskExceptionHandler handler) {
AtomicInteger integer = new AtomicInteger(times - 1);
this.repeatingTask(
runnable, intervalMillis, a -> integer.getAndDecrement() > 0, handler
);
}
public <D extends Runnable> void repeatingTask(D runnable, long intervalMillis, Predicate<D> shouldContinue, MiraiTaskExceptionHandler handler) {
new Thread(() -> {
do {
this.pool.execute(() -> {
try {
runnable.run();
} catch (Exception e) {
handler.onHandle(e);
}
});
try {
Thread.sleep(intervalMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (shouldContinue.test(runnable));
}).start();
}
public void deletingTask(Runnable runnable, long intervalMillis) {
new Thread(() -> {
try {
Thread.sleep(intervalMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.pool.execute(runnable);
}).start();
}
}
package net.mamoe.mirai.task;
import java.io.Closeable;
import java.util.concurrent.ScheduledThreadPoolExecutor;
/**
* @author NaturalHG
*/
public final class MiraiThreadPool extends ScheduledThreadPoolExecutor implements Closeable {
private static MiraiThreadPool instance = new MiraiThreadPool();
public static MiraiThreadPool getInstance() {
return instance;
}
MiraiThreadPool() {
super(10);
/*super(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>()
);*/
}
@Override
public void close(){
this.shutdown();
if(!this.isShutdown()){
this.shutdownNow();
}
}
}
package net.mamoe.mirai.utils;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
/**
* Convert IMAGE into Chars that could shows in terminal
*
* @author NaturalHG
*/
public final class CharImageConverter implements Callable<String> {
/**
* width should depends on the width of the terminal
*/
private BufferedImage image;
private int width;
private double ignoreRate;
public CharImageConverter(BufferedImage image, int width) {
this(image, width, 0.95);
}
public CharImageConverter(BufferedImage image, int width, double ignoreRate) {
this.image = image;
this.width = width;
this.ignoreRate = ignoreRate;
}
@Override
public String call() {
/*
* resize Image
* */
int newHeight = (int) (this.image.getHeight() * (((double) width) / this.image.getWidth()));
Image tmp = image.getScaledInstance(width, newHeight, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(width, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
this.image = dimg;
int background = gray(image.getRGB(0, 0));
StringBuilder builder = new StringBuilder();
List<StringBuilder> lines = new ArrayList<>(this.image.getHeight());
int minXPos = this.width;
int maxXPos = 0;
for (int y = 0; y < image.getHeight(); y++) {
StringBuilder builderLine = new StringBuilder();
for (int x = 0; x < image.getWidth(); x++) {
int gray = gray(image.getRGB(x, y));
if (grayCompare(gray, background)) {
builderLine.append(" ");
} else {
builderLine.append("#");
if (x < minXPos) {
minXPos = x;
}
if (x > maxXPos) {
maxXPos = x;
}
}
}
if (builderLine.toString().isBlank()) {
continue;
}
lines.add(builderLine);
}
for (StringBuilder line : lines) {
builder.append(line.substring(minXPos, maxXPos)).append("\n");
}
return builder.toString();
}
private static int gray(int rgb) {
int R = (rgb & 0xff0000) >> 16;
int G = (rgb & 0x00ff00) >> 8;
int B = rgb & 0x0000ff;
return (R * 30 + G * 59 + B * 11 + 50) / 100;
}
public boolean grayCompare(int g1, int g2) {
return ((double) Math.min(g1, g2) / Math.max(g1, g2)) >= ignoreRate;
}
}
package net.mamoe.mirai.utils;
import java.awt.image.BufferedImage;
/**
* @author NaturalHG
*/
public final class CharImageUtil {
public static String createCharImg(BufferedImage image) {
return createCharImg(image, 100, 20);
}
public static String createCharImg(BufferedImage image, int sizeWeight, int sizeHeight) {
return new CharImageConverter(image,sizeWeight).call();
}
}
\ No newline at end of file
package net.mamoe.mirai.utils;
/**
* @author NaturalHG
*/
public enum LoggerTextFormat {
RESET("\33[0m"),
BLUE("\033[0;34m"),
BLACK("\033[0;30m"),
DARK_GREY("\033[1;30m"),
LIGHT_BLUE("\033[1;34m"),
GREEN("\033[0;32m"),
LIGHT_GTEEN("\033[1;32m"),
CYAN("\033[0;36m"),
LIGHT_CYAN("\033[1;36m"),
RED("\033[0;31m"),
LIGHT_RED("\033[1;31m"),
PURPLE("\033[0;35m"),
LIGHT_PURPLE("\033[1;35m"),
BROWN("\033[0;33m"),
YELLOW("\033[1;33m"),
LIGHT_GRAY("\033[0;37m"),
WHITE("\033[1;37m");
private String format;
LoggerTextFormat(String format) {
this.format = format;
}
@Override
public String toString() {
//if(MiraiServer.getInstance().isUnix()){
return format;
// }
// return "";
}
}
package net.mamoe.mirai.utils.config;
import kotlin.io.FilesKt;
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.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Objects;
/**
* YAML-TYPE CONFIG
* Thread SAFE
*
* @author NaturalHG
*/
public class MiraiConfig extends MiraiConfigSection<Object> {
private final File root;
public MiraiConfig(@NotNull File file) {
super(parse(Objects.requireNonNull(file)));
this.root = file;
}
public synchronized void save() {
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(dumperOptions);
String content = yaml.dump(this.sortedMap);
try {
new ByteArrayInputStream(content.getBytes()).transferTo(new FileOutputStream(this.root));
} catch (IOException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
private static LinkedHashMap<String,Object> parse(File file) {
/*
if (!file.toURI().getPath().contains(MiraiServer.getInstance().getParentFolder().getPath())) {
file = new File(MiraiServer.getInstance().getParentFolder().getPath(), file.getName());
}*/
if (!file.exists()) {
try {
if (!file.createNewFile()) {
return new LinkedHashMap<>();
}
} catch (IOException e) {
e.printStackTrace();
return new LinkedHashMap<>();
}
}
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(dumperOptions);
return yaml.loadAs(String.join("\n", FilesKt.readLines(file, Charset.defaultCharset())), LinkedHashMap.class);
}
}
package net.mamoe.mirai.utils.setting;
import org.ini4j.Profile;
import java.io.IOException;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author NaturalHG
*/
public class MiraiSettingListSection extends Vector<Object> implements MiraiSettingSection {
private Lock lock = new ReentrantLock();
@SuppressWarnings("unchecked")
public <T> T getAs(int index){
return (T)super.get(index);
}
public int getInt(int index){
return this.getAs(index);
}
public int getDouble(int index){
return this.getAs(index);
}
public int getString(int index){
return this.getAs(index);
}
public int getFloat(int index) {
return this.getAs(index);
}
@Override
public synchronized void saveAsSection(Profile.Section section) {
section.clear();
AtomicInteger integer = new AtomicInteger(0);
this.forEach(a -> {
section.put(String.valueOf(integer.getAndAdd(1)),a);
});
}
@Override
public void close() throws IOException {
}
}
package net.mamoe.mirai.utils.setting;
import org.ini4j.Profile;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* @author NaturalHG
*/
public class MiraiSettingMapSection extends ConcurrentHashMap<String, Object> implements MiraiSettingSection {
@SuppressWarnings("unchecked")
public <T> T get(String key, T defaultValue) {
if (key == null || key.isEmpty()){
return defaultValue;
}
if (super.containsKey(key)){
return (T) super.get(key);
}
return defaultValue;
}
@SuppressWarnings("unchecked")
public <T> T get(String key) {
if (key == null || key.isEmpty()) {
return null;
}
if (super.containsKey(key)) {
return (T) super.get(key);
}
return null;
}
public void set(String key, Object value){
this.put(key,value);
}
public void remove(String key){
super.remove(key);
}
public int getInt(String key) {
return this.getInt(key, 0);
}
public int getInt(String key, int defaultValue) {
return Integer.parseInt(String.valueOf(this.get(key, defaultValue)));
}
public double getDouble(String key) {
return this.getDouble(key, 0D);
}
public double getDouble(String key, double defaultValue) {
return Double.parseDouble(String.valueOf(this.get(key, defaultValue)));
}
public float getFloat(String key) {
return this.getFloat(key, 0F);
}
public float getFloat(String key, float defaultValue) {
return Float.parseFloat(String.valueOf(this.get(key, defaultValue)));
}
public String getString(String key) {
return this.getString(key, "");
}
public String getString(String key, String defaultValue) {
return String.valueOf(this.get(key, defaultValue));
}
public Object getObject(String key) {
return this.get(key);
}
@SuppressWarnings("unchecked")
public <T> List<T> asList(){
return this.values().stream().map(a -> (T)(a)).collect(Collectors.toList());
}
@Override
public synchronized void saveAsSection(Profile.Section section) {
section.clear();
this.forEach(section::put);
}
@Override
public void close() throws IOException {
}
}
package net.mamoe.mirai.utils.setting;
import org.ini4j.Profile;
import java.io.Closeable;
/**
* @author NaturalHG
*/
public interface MiraiSettingSection extends Closeable {
void saveAsSection(Profile.Section section);
}
package net.mamoe.mirai.utils.setting;
import net.mamoe.mirai.plugin.MiraiPluginBase;
import org.ini4j.Config;
import org.ini4j.Ini;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* Thread-safe Mirai Config <br>
* Only supports <code>INI</code> format <br>
* Supports {@link Map} and {@link List}
*
* @author NaturalHG
*/
public class MiraiSettings {
private File file;
private Ini ini;
private volatile Map<String, MiraiSettingSection> cacheSection = new ConcurrentHashMap<>();
public MiraiSettings(MiraiPluginBase pluginBase, String filename) {
// TODO: 2019/9/6 每个插件独立文件夹存放
this(new File(filename));
}
public MiraiSettings(@NotNull File file) {
Objects.requireNonNull(file);
if (!file.getName().contains(".")) {
file = new File(file.getPath() + ".ini");
}
this.file = file;
try {
if (!file.exists() && !file.createNewFile()) {
throw new RuntimeException("cannot create config file " + file);
}
Config config = new Config();
config.setMultiSection(true);
ini = new Ini();
ini.setConfig(config);
ini.load(this.file.toURI().toURL());
} catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void setSection(String key, MiraiSettingSection section) {
cacheSection.put(key, section);
}
public synchronized MiraiSettingMapSection getMapSection(String key) {
if (!cacheSection.containsKey(key)) {
MiraiSettingMapSection section = new MiraiSettingMapSection();
if (ini.containsKey(key)) {
section.putAll(ini.get(key));
}
cacheSection.put(key, section);
}
return (MiraiSettingMapSection) cacheSection.get(key);
}
public synchronized MiraiSettingListSection getListSection(String key) {
if (!cacheSection.containsKey(key)) {
MiraiSettingListSection section = new MiraiSettingListSection();
if (ini.containsKey(key)) {
section.addAll(ini.get(key).values());
}
cacheSection.put(key, section);
}
return (MiraiSettingListSection) cacheSection.get(key);
}
public synchronized void save() {
cacheSection.forEach((k, a) -> {
if (!ini.containsKey(k)) {
ini.put(k, "", new HashMap<>());
}
a.saveAsSection(ini.get(k));
});
this.clearCache();
try {
ini.store(file);
} catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void clearCache() {
cacheSection.clear();
}
}
apply plugin: "kotlin"
apply plugin: "java"
dependencies {
implementation project(':mirai-core')
compile 'com.google.protobuf:protobuf-java:3.5.0'
compile files('./lib/jpcap.jar')
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M2'
compile 'org.jetbrains.kotlin:kotlin-stdlib:1.3.50'
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>mirai-debug</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai</artifactId>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-core</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jpcap/jpcap -->
<dependency>
<groupId>jpcap</groupId>
<artifactId>jpcap</artifactId>
<version>0.1.18-002</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/jpcap.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<resources>
<resource>
<directory>/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-XXLanguage:+InlineClasses</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
import kotlin.ranges.IntRange;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import net.mamoe.mirai.network.protocol.tim.TIMProtocol;
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacketKt;
import net.mamoe.mirai.utils.UtilsKt;
......@@ -108,12 +106,14 @@ public class HexComparator {
}};
}
@ToString
@Getter
@AllArgsConstructor
private static class Match {
private IntRange range;
private String constName;
Match(IntRange range,String constName){
this.range = range;
this.constName = constName;
}
}
}
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:Suppress("EXPERIMENTAL_API_USAGE", "MemberVisibilityCanBePrivate")
import jpcap.JpcapCaptor
import jpcap.packet.IPPacket
......@@ -124,8 +124,8 @@ object Main {
@Volatile
private var debugStarted = false
private val qq: Int = 1994701021
private val password: String = "xiaoqqq"
private const val qq: Int = 1994701021
private const val password: String = "xiaoqqq"
lateinit var token0825: ByteArray//56
var loginTime: Int = 0
......
apply plugin: "kotlin"
apply plugin: "java"
dependencies {
implementation project(":mirai-core")
compile rootProject.ext.coroutine
compile 'org.jetbrains.kotlin:kotlin-stdlib:1.3.50'
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai-demos</artifactId>
<version>1.0</version>
</parent>
<artifactId>mirai-demo-1</artifactId>
<version>1.0</version>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -23,7 +23,7 @@ suspend fun main() {
password = "bb22222"
), Console())
bot.login().await().let {
bot.login().let {
check(it == LoginState.SUCCESS) { "Login failed: " + it.name }
}
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai</artifactId>
<version>1.0</version>
</parent>
<artifactId>mirai-demos</artifactId>
<version>1.0</version>
<modules>
<module>mirai-demo-1</module>
<module>mirai-demo-event</module>
</modules>
<dependencies>
<dependency>
<groupId>net.mamoe</groupId>
<artifactId>mirai-core</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>mirai-ui</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<parent>
<groupId>net.mamoe</groupId>
<artifactId>mirai</artifactId>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
</dependencies>
<build>
<resources>
<resource>
<directory>/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
</plugins>
</build>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.mamoe</groupId>
<name>Mirai</name>
<artifactId>mirai</artifactId>
<version>1.0</version>
<modules>
<module>mirai-core</module>
<module>mirai-ui</module>
<module>mirai-console</module>
<module>mirai-api</module>
<module>mirai-demos</module>
<module>mirai-debug</module>
</modules>
<repositories>
<!--
<repository>
<id>mamoe-repo</id>
<url>http://mamoe.net:8081/repository/public/</url>
</repository>
-->
<repository>
<id>huawei</id>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
</repository>
</repositories>
<packaging>pom</packaging>
<properties>
<kotlin.version>1.3.50</kotlin.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.intellij</groupId>
<artifactId>annotations</artifactId>
<version>12.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>compile</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<version>0.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.3.0-M2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.38.Final</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.18</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.3.41</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<defaultGoal>package</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>7.1</version>
</dependency>
</dependencies>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.6.0</version>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>shaded</shadedClassifierName>
</configuration>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.6.0</version>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
\ No newline at end of file
rootProject.name = 'mirai'
include(':mirai-core')
include(':mirai-console')
include(':mirai-api')
include(':mirai-demos:mirai-demo-1')
include(':mirai-demos')
include(':mirai-debug')
project(':mirai-demos:mirai-demo-1').projectDir = file('mirai-demos/mirai-demo-1')
def kotlinVersion = "1.3.50"
def coroutinesVersion = "1.3.0-M2"
def kotlinStandardLib = "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
def kotlinCoroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment