1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-24 20:43:33 +08:00

Multiplatform with gradle building

This commit is contained in:
Him188 2019-10-03 19:07:03 +08:00
parent 58c6a40795
commit b38c262df0
130 changed files with 1213 additions and 1671 deletions
.gitignorebuild.gradledependencies.gradle
gradle/wrapper
gradlewgradlew.bat
mirai-api
build.gradlepom.xml
src/main/java/net/mamoe/mirai
mirai-console
mirai-core
build.gradlepom.xml
src
commonMain/kotlin/net.mamoe.mirai/utils
jvmMain/kotlin/net/mamoe/mirai
Bot.ktBotHelper.ktMirai.kt
contact
event
message
network
plugin
task
utils

9
.gitignore vendored
View File

@ -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
/test
.gradle/

28
build.gradle Normal file
View File

@ -0,0 +1,28 @@
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')
}

18
dependencies.gradle Normal file
View File

@ -0,0 +1,18 @@
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"
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#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

188
gradlew vendored Normal file
View File

@ -0,0 +1,188 @@
#!/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" "$@"

100
gradlew.bat vendored Normal file
View File

@ -0,0 +1,100 @@
@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

7
mirai-api/build.gradle Normal file
View File

@ -0,0 +1,7 @@
apply plugin: "kotlin"
apply plugin: "java"
dependencies {
implementation project(':mirai-core')
implementation project(':mirai-console')
}

View File

@ -1,51 +0,0 @@
<?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>

View File

@ -1,13 +1,10 @@
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){

View File

@ -1,64 +0,0 @@
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) {
}
}

View File

@ -0,0 +1,18 @@
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"
}

View File

@ -1,99 +0,0 @@
<?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>

View File

@ -1,13 +1,10 @@
package net.mamoe.mirai;
import lombok.Getter;
/**
* @author NaturalHG
*/
public final class MiraiMain {
@Getter
private static MiraiServer server;
public static void main(String[] args) {

View File

@ -1,9 +1,7 @@
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
}

58
mirai-core/build.gradle Normal file
View File

@ -0,0 +1,58 @@
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'
}*/

View File

@ -1,118 +0,0 @@
<?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>

View File

@ -0,0 +1,3 @@
package net.mamoe.mirai.utils
//todo

View File

@ -1,6 +1,5 @@
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

View File

@ -0,0 +1,9 @@
package net.mamoe.mirai
//expect fun s(): String
/**
* @author Him188moe
*/
object Mirai {
const val VERSION: String = "1.0.0"
}

View File

@ -1,5 +1,6 @@
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)
}

View File

@ -1,5 +1,6 @@
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) }

View File

@ -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()
}

View File

@ -1,12 +1,12 @@
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
}
}

View 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
}
}
}

View File

@ -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))
}

View File

@ -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()
}

View File

@ -1,5 +1,6 @@
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

View File

@ -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

View File

@ -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) {

View File

@ -0,0 +1,135 @@
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()
}
}
*/

View File

@ -0,0 +1,35 @@
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)
}
}
}

View File

@ -0,0 +1,79 @@
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
}
}

View File

@ -0,0 +1,15 @@
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()
}
}

View File

@ -0,0 +1,17 @@
package net.mamoe.mirai.utils
/**
* QQ 在线状态
*
* @author Him188moe
* @see net.mamoe.mirai.network.protocol.tim.packet.login.ClientChangeOnlineStatusPacket
*/
enum class ClientLoginStatus(
// TODO: 2019/8/31 add more ClientLoginStatus
val id: Int//1 ubyte
) {
/**
* 我在线上
*/
ONLINE(0x0A)
}

View File

@ -0,0 +1,31 @@
package net.mamoe.mirai.utils
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
/**
* @author NaturalHG
*/
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
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
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.responseCode == 200
}
}

View File

@ -0,0 +1,32 @@
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 "";
}
}

View File

@ -1,8 +1,5 @@
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);

View File

@ -1,7 +1,5 @@
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);
}
}

View File

@ -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
/**

View File

@ -0,0 +1,58 @@
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>?
}
}
}

View File

@ -1,7 +1,6 @@
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);

Some files were not shown because too many files have changed in this diff Show More