diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/artifacts/VkRconBot_jar.xml b/.idea/artifacts/VkRconBot_jar.xml
new file mode 100644
index 0000000..c6c4ecf
--- /dev/null
+++ b/.idea/artifacts/VkRconBot_jar.xml
@@ -0,0 +1,13 @@
+
+
+ $PROJECT_DIR$/out/artifacts/VkRconBot_jar
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b4bdd59
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..942f3a2
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..df543e3
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..4d27ef0
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..0e65cea
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..82dbec8
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
deleted file mode 100644
index bf82ff0..0000000
Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
deleted file mode 100644
index dc3affc..0000000
--- a/.mvn/wrapper/maven-wrapper.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
-wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index c5f3f6b..0000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "java.configuration.updateBuildConfiguration": "interactive"
-}
\ No newline at end of file
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
index 4394b07..8e4eadf 100644
--- a/dependency-reduced-pom.xml
+++ b/dependency-reduced-pom.xml
@@ -1,12 +1,36 @@
4.0.0
- theoni.vkbot
- vkbot
- VkBot
- 1.2
+ com.mefrreex.vkbot
+ vkrconbot
+ VkRconBot
+ 1.4
+ src/main/kotlin
+ src/test/kotlin
+ ${project.name}-${project.version}
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ 1.7.21
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+
maven-shade-plugin
2.1
@@ -19,27 +43,86 @@
- theoni.vkbot.Bot
+ com.mefrreex.vkbot.BootstrapKt
+
+ maven-surefire-plugin
+ 2.22.2
+
+
+ maven-failsafe-plugin
+ 2.22.2
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.6.0
+
+ com.mefrreex.vkbot.BootstrapKt
+
+
+
+ maven-compiler-plugin
+
+
+ 17
+
+
+
+
+ mavenCentral
+ https://repo1.maven.org/maven2/
+
+
- org.projectlombok
- lombok
- 1.18.20
- provided
+ org.jetbrains.kotlin
+ kotlin-test-junit5
+ 1.7.21
+ test
+
+
+ kotlin-test
+ org.jetbrains.kotlin
+
+
+ junit-jupiter-api
+ org.junit.jupiter
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.8.2
+ test
+
+
+ junit-platform-engine
+ org.junit.platform
+
+
+ apiguardian-api
+ org.apiguardian
+
+
+ junit-jupiter-api
+ org.junit.jupiter
+
+
- 17
- 17
+ official
UTF-8
+ 17
diff --git a/mvnw b/mvnw
deleted file mode 100644
index 095fed6..0000000
--- a/mvnw
+++ /dev/null
@@ -1,287 +0,0 @@
-#!/bin/sh
-# ----------------------------------------------------------------------------
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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
-#
-# http://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.
-# ----------------------------------------------------------------------------
-
-# ----------------------------------------------------------------------------
-# Apache Maven Wrapper startup batch script, version 3.1.1
-#
-# Required ENV vars:
-# ------------------
-# JAVA_HOME - location of a JDK home dir
-#
-# Optional ENV vars
-# -----------------
-# MAVEN_OPTS - parameters passed to the Java VM when running Maven
-# e.g. to debug Maven itself, use
-# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
-# ----------------------------------------------------------------------------
-
-if [ -z "$MAVEN_SKIP_RC" ] ; then
-
- if [ -f /usr/local/etc/mavenrc ] ; then
- . /usr/local/etc/mavenrc
- fi
-
- if [ -f /etc/mavenrc ] ; then
- . /etc/mavenrc
- fi
-
- if [ -f "$HOME/.mavenrc" ] ; then
- . "$HOME/.mavenrc"
- fi
-
-fi
-
-# OS specific support. $var _must_ be set to either true or false.
-cygwin=false;
-darwin=false;
-mingw=false
-case "`uname`" in
- CYGWIN*) cygwin=true ;;
- MINGW*) mingw=true;;
- Darwin*) darwin=true
- # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
- # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
- if [ -z "$JAVA_HOME" ]; then
- if [ -x "/usr/libexec/java_home" ]; then
- JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME
- else
- JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
- fi
- fi
- ;;
-esac
-
-if [ -z "$JAVA_HOME" ] ; then
- if [ -r /etc/gentoo-release ] ; then
- JAVA_HOME=`java-config --jre-home`
- fi
-fi
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
-fi
-
-# For Mingw, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-fi
-
-if [ -z "$JAVA_HOME" ]; then
- javaExecutable="`which javac`"
- if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
- # readlink(1) is not available as standard on Solaris 10.
- readLink=`which readlink`
- if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
- if $darwin ; then
- javaHome="`dirname \"$javaExecutable\"`"
- javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
- else
- javaExecutable="`readlink -f \"$javaExecutable\"`"
- fi
- javaHome="`dirname \"$javaExecutable\"`"
- javaHome=`expr "$javaHome" : '\(.*\)/bin'`
- JAVA_HOME="$javaHome"
- export JAVA_HOME
- fi
- fi
-fi
-
-if [ -z "$JAVACMD" ] ; then
- 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
- else
- JAVACMD="`\\unset -f command; \\command -v java`"
- fi
-fi
-
-if [ ! -x "$JAVACMD" ] ; then
- echo "Error: JAVA_HOME is not defined correctly." >&2
- echo " We cannot execute $JAVACMD" >&2
- exit 1
-fi
-
-if [ -z "$JAVA_HOME" ] ; then
- echo "Warning: JAVA_HOME environment variable is not set."
-fi
-
-# traverses directory structure from process work directory to filesystem root
-# first directory with .mvn subdirectory is considered project base directory
-find_maven_basedir() {
- if [ -z "$1" ]
- then
- echo "Path not specified to find_maven_basedir"
- return 1
- fi
-
- basedir="$1"
- wdir="$1"
- while [ "$wdir" != '/' ] ; do
- if [ -d "$wdir"/.mvn ] ; then
- basedir=$wdir
- break
- fi
- # workaround for JBEAP-8937 (on Solaris 10/Sparc)
- if [ -d "${wdir}" ]; then
- wdir=`cd "$wdir/.."; pwd`
- fi
- # end of workaround
- done
- printf '%s' "$(cd "$basedir"; pwd)"
-}
-
-# concatenates all lines of a file
-concat_lines() {
- if [ -f "$1" ]; then
- echo "$(tr -s '\n' ' ' < "$1")"
- fi
-}
-
-BASE_DIR=$(find_maven_basedir "$(dirname $0)")
-if [ -z "$BASE_DIR" ]; then
- exit 1;
-fi
-
-MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
-if [ "$MVNW_VERBOSE" = true ]; then
- echo $MAVEN_PROJECTBASEDIR
-fi
-
-##########################################################################################
-# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
-# This allows using the maven wrapper in projects that prohibit checking in binary data.
-##########################################################################################
-if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Found .mvn/wrapper/maven-wrapper.jar"
- fi
-else
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
- fi
- if [ -n "$MVNW_REPOURL" ]; then
- wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
- else
- wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
- fi
- while IFS="=" read key value; do
- case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;;
- esac
- done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Downloading from: $wrapperUrl"
- fi
- wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
- if $cygwin; then
- wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
- fi
-
- if command -v wget > /dev/null; then
- QUIET="--quiet"
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Found wget ... using wget"
- QUIET=""
- fi
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- wget $QUIET "$wrapperUrl" -O "$wrapperJarPath"
- else
- wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath"
- fi
- [ $? -eq 0 ] || rm -f "$wrapperJarPath"
- elif command -v curl > /dev/null; then
- QUIET="--silent"
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Found curl ... using curl"
- QUIET=""
- fi
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L
- else
- curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L
- fi
- [ $? -eq 0 ] || rm -f "$wrapperJarPath"
- else
- if [ "$MVNW_VERBOSE" = true ]; then
- echo "Falling back to using Java to download"
- fi
- javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
- javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class"
- # For Cygwin, switch paths to Windows format before running javac
- if $cygwin; then
- javaSource=`cygpath --path --windows "$javaSource"`
- javaClass=`cygpath --path --windows "$javaClass"`
- fi
- if [ -e "$javaSource" ]; then
- if [ ! -e "$javaClass" ]; then
- if [ "$MVNW_VERBOSE" = true ]; then
- echo " - Compiling MavenWrapperDownloader.java ..."
- fi
- # Compiling the Java class
- ("$JAVA_HOME/bin/javac" "$javaSource")
- fi
- if [ -e "$javaClass" ]; then
- # Running the downloader
- if [ "$MVNW_VERBOSE" = true ]; then
- echo " - Running MavenWrapperDownloader.java ..."
- fi
- ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
- fi
- fi
- fi
-fi
-##########################################################################################
-# End of extension
-##########################################################################################
-
-MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin; then
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
- [ -n "$MAVEN_PROJECTBASEDIR" ] &&
- MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
-fi
-
-# Provide a "standardized" way to retrieve the CLI args that will
-# work with both Windows and non-Windows executions.
-MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
-export MAVEN_CMD_LINE_ARGS
-
-WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-exec "$JAVACMD" \
- $MAVEN_OPTS \
- $MAVEN_DEBUG_OPTS \
- -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
- "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
- ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
deleted file mode 100644
index cba1f04..0000000
--- a/mvnw.cmd
+++ /dev/null
@@ -1,187 +0,0 @@
-@REM ----------------------------------------------------------------------------
-@REM Licensed to the Apache Software Foundation (ASF) under one
-@REM or more contributor license agreements. See the NOTICE file
-@REM distributed with this work for additional information
-@REM regarding copyright ownership. The ASF licenses this file
-@REM to you under the Apache License, Version 2.0 (the
-@REM "License"); you may not use this file except in compliance
-@REM with the License. You may obtain a copy of the License at
-@REM
-@REM http://www.apache.org/licenses/LICENSE-2.0
-@REM
-@REM Unless required by applicable law or agreed to in writing,
-@REM software distributed under the License is distributed on an
-@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-@REM KIND, either express or implied. See the License for the
-@REM specific language governing permissions and limitations
-@REM under the License.
-@REM ----------------------------------------------------------------------------
-
-@REM ----------------------------------------------------------------------------
-@REM Apache Maven Wrapper startup batch script, version 3.1.1
-@REM
-@REM Required ENV vars:
-@REM JAVA_HOME - location of a JDK home dir
-@REM
-@REM Optional ENV vars
-@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
-@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
-@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
-@REM e.g. to debug Maven itself, use
-@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
-@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
-@REM ----------------------------------------------------------------------------
-
-@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
-@echo off
-@REM set title of command window
-title %0
-@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
-@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
-
-@REM set %HOME% to equivalent of $HOME
-if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
-
-@REM Execute a user defined script before this one
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
-@REM check for pre script, once with legacy .bat ending and once with .cmd ending
-if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
-if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
-:skipRcPre
-
-@setlocal
-
-set ERROR_CODE=0
-
-@REM To isolate internal variables from possible post scripts, we use another setlocal
-@setlocal
-
-@REM ==== START VALIDATION ====
-if not "%JAVA_HOME%" == "" goto OkJHome
-
-echo.
-echo Error: JAVA_HOME not found in your environment. >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-:OkJHome
-if exist "%JAVA_HOME%\bin\java.exe" goto init
-
-echo.
-echo Error: JAVA_HOME is set to an invalid directory. >&2
-echo JAVA_HOME = "%JAVA_HOME%" >&2
-echo Please set the JAVA_HOME variable in your environment to match the >&2
-echo location of your Java installation. >&2
-echo.
-goto error
-
-@REM ==== END VALIDATION ====
-
-:init
-
-@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
-@REM Fallback to current working directory if not found.
-
-set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
-IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
-
-set EXEC_DIR=%CD%
-set WDIR=%EXEC_DIR%
-:findBaseDir
-IF EXIST "%WDIR%"\.mvn goto baseDirFound
-cd ..
-IF "%WDIR%"=="%CD%" goto baseDirNotFound
-set WDIR=%CD%
-goto findBaseDir
-
-:baseDirFound
-set MAVEN_PROJECTBASEDIR=%WDIR%
-cd "%EXEC_DIR%"
-goto endDetectBaseDir
-
-:baseDirNotFound
-set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
-cd "%EXEC_DIR%"
-
-:endDetectBaseDir
-
-IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
-
-@setlocal EnableExtensions EnableDelayedExpansion
-for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
-@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
-
-:endReadAdditionalConfig
-
-SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
-set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
-set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-
-set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
-
-FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
- IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
-)
-
-@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
-@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
-if exist %WRAPPER_JAR% (
- if "%MVNW_VERBOSE%" == "true" (
- echo Found %WRAPPER_JAR%
- )
-) else (
- if not "%MVNW_REPOURL%" == "" (
- SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
- )
- if "%MVNW_VERBOSE%" == "true" (
- echo Couldn't find %WRAPPER_JAR%, downloading it ...
- echo Downloading from: %WRAPPER_URL%
- )
-
- powershell -Command "&{"^
- "$webclient = new-object System.Net.WebClient;"^
- "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
- "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
- "}"^
- "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
- "}"
- if "%MVNW_VERBOSE%" == "true" (
- echo Finished downloading %WRAPPER_JAR%
- )
-)
-@REM End of extension
-
-@REM Provide a "standardized" way to retrieve the CLI args that will
-@REM work with both Windows and non-Windows executions.
-set MAVEN_CMD_LINE_ARGS=%*
-
-%MAVEN_JAVA_EXE% ^
- %JVM_CONFIG_MAVEN_PROPS% ^
- %MAVEN_OPTS% ^
- %MAVEN_DEBUG_OPTS% ^
- -classpath %WRAPPER_JAR% ^
- "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
- %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
-if ERRORLEVEL 1 goto error
-goto end
-
-:error
-set ERROR_CODE=1
-
-:end
-@endlocal & set ERROR_CODE=%ERROR_CODE%
-
-if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
-@REM check for post script, once with legacy .bat ending and once with .cmd ending
-if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
-if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
-:skipRcPost
-
-@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
-if "%MAVEN_BATCH_PAUSE%"=="on" pause
-
-if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
-
-cmd /C exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
index 198cda0..470bcf9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,63 +1,131 @@
-
-
- 4.0.0
-
- theoni.vkbot
- vkbot
- 1.3
- jar
-
- VkBot
-
-
- UTF-8
- 17
- 17
-
-
-
-
- org.projectlombok
- lombok
- 1.18.20
- provided
-
-
- com.vk.api
- sdk
- 1.0.14
-
-
- org.yaml
- snakeyaml
- 1.21
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 2.1
-
-
- package
-
- shade
-
-
-
-
- theoni.vkbot.Bot
-
-
-
-
-
-
-
-
+
+
+ 4.0.0
+
+ vkrconbot
+ com.mefrreex.vkbot
+ 1.4
+ jar
+
+ VkRconBot
+
+
+ UTF-8
+ official
+ 17
+
+
+
+
+ mavenCentral
+ https://repo1.maven.org/maven2/
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-test-junit5
+ 1.7.21
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.8.2
+ test
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ 1.7.21
+
+
+ com.vk.api
+ sdk
+ 1.0.14
+
+
+ org.yaml
+ snakeyaml
+ 1.21
+
+
+
+
+ ${project.name}-${project.version}
+ src/main/kotlin
+ src/test/kotlin
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ 1.7.21
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.1
+
+
+ package
+
+ shade
+
+
+
+
+ com.mefrreex.vkbot.BootstrapKt
+
+
+
+
+
+
+
+ maven-surefire-plugin
+ 2.22.2
+
+
+ maven-failsafe-plugin
+ 2.22.2
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.6.0
+
+ com.mefrreex.vkbot.BootstrapKt
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 17
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/theoni/vkbot/Bot.java b/src/main/java/theoni/vkbot/Bot.java
deleted file mode 100644
index 76e8800..0000000
--- a/src/main/java/theoni/vkbot/Bot.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package theoni.vkbot;
-
-import theoni.vkbot.managers.ConfigManager;
-
-public class Bot {
-
- public static void main(String[] args) throws InterruptedException {
-
- ConfigManager.saveResource("config.yml");
- ConfigManager.saveResource("messages.yml");
-
- new BotHandler().startBot();
- System.out.println(Messages.BOT_STARTED.getText());
- }
-}
diff --git a/src/main/java/theoni/vkbot/BotHandler.java b/src/main/java/theoni/vkbot/BotHandler.java
deleted file mode 100644
index 38cc238..0000000
--- a/src/main/java/theoni/vkbot/BotHandler.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package theoni.vkbot;
-
-import com.vk.api.sdk.client.actors.GroupActor;
-import com.vk.api.sdk.exceptions.ApiException;
-import com.vk.api.sdk.exceptions.ClientException;
-import com.vk.api.sdk.objects.messages.*;
-import com.vk.api.sdk.queries.messages.MessagesGetLongPollHistoryQuery;
-
-import theoni.vkbot.managers.BotManager;
-import theoni.vkbot.managers.ConfigManager;
-import theoni.vkbot.managers.RconManager;
-import theoni.vkbot.utils.Keyboards;
-
-import java.io.File;
-import java.util.List;
-
-public class BotHandler {
-
- private ConfigManager config;
- private String commandPrefix;
-
- public BotHandler() {
- this.config = new ConfigManager(new File("config.yml"));
- this.commandPrefix = config.getString("command-prefix");
- }
-
- public void startBot() {
-
- Thread thread = new Thread(new Runnable() {
-
- public void run() {
-
- GroupActor actor = new GroupActor(config.getInt("groupId"), config.getString("token"));
- BotManager manager = new BotManager(actor);
-
- Integer ts = manager.getTs();
-
- while (true){
- try {
-
- MessagesGetLongPollHistoryQuery historyQuery = manager.getHistoryQuery(ts);
- List messages = historyQuery.execute().getMessages().getItems();
-
- if (!messages.isEmpty()){
- messages.forEach(message -> {
-
- String text = message.getText().toLowerCase();
-
- if (text.equals("Начать")){
- manager.sendMessage(Messages.START.getText(), message, Keyboards.rconKeyboard());
- }
-
- else if (text.equals("/getid")) {
- manager.sendMessage(String.format(Messages.USER_ID.getText(), message.getFromId()), message);
- }
-
- else if (text.equals("rcon") || text.equals("/rcon")){
- if (config.getList("allowed-users").contains(message.getFromId())) {
- if (config.getList("fast-commands").size() != 0) {
- manager.sendMessage(Messages.RCON_WITH_COMMANDS.getText(), message, Keyboards.commandsKeyboard());
- } else {
- manager.sendMessage(Messages.RCON.getText(), message, Keyboards.commandsKeyboard());
- }
- } else {
- manager.sendMessage(Messages.USER_NOT_WHITELISTED.getText(), message);
- }
- }
-
- else if (text.length() > 1) {
- if (text.substring(0, 1).equals(config.getString("command-prefix")) || config.getString("command-prefix").equals("")) {
-
- if (!config.getList("allowed-users").contains(message.getFromId())) {
- manager.sendMessage(Messages.USER_NOT_WHITELISTED.getText(), message);
- return;
- }
-
- if (config.getList("blocked-commands").contains(text.replace("/", "").replace(commandPrefix, ""))) {
- manager.sendMessage(String.format(Messages.COMMAND_BLOCKED.getText(), text), message);
- return;
- }
-
- String response = RconManager.command(text.replace("/", "").replace(commandPrefix, ""));
- if (response.equals("Connection error")) {
- response = Messages.FAILED_TO_CONNECT.getText();
- } else if (response.equals("Authentication error")) {
- response = Messages.FAILED_TO_AUTHENTICATE.getText();
- } else if (response.equals("")) {
- response = Messages.RESPONSE_NULL.getText();
- } else {
- response = String.format(Messages.COMMAND_SENDED.getText(), response.replaceAll("§", ""));
- }
-
- manager.sendMessage(response, message);
- }
- }
- });
- }
-
- ts = manager.getTs();
- Thread.sleep(500);
-
- } catch (InterruptedException | ClientException | ApiException e) {
- e.printStackTrace();
- }
- }
- }
- });
- thread.start();
- }
-}
diff --git a/src/main/java/theoni/vkbot/Messages.java b/src/main/java/theoni/vkbot/Messages.java
deleted file mode 100644
index 27f0354..0000000
--- a/src/main/java/theoni/vkbot/Messages.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package theoni.vkbot;
-
-import java.io.File;
-
-import lombok.Getter;
-import theoni.vkbot.managers.ConfigManager;
-
-public enum Messages {
-
- BOT_STARTED("Бот успешно запущен."),
- START(),
- USER_ID(),
- RCON(),
- RCON_WITH_COMMANDS(),
- USER_NOT_WHITELISTED(),
- COMMAND_BLOCKED(),
- FAILED_TO_CONNECT(),
- FAILED_TO_AUTHENTICATE(),
- COMMAND_SENDED(),
- RESPONSE_NULL();
-
- @Getter private String text;
-
- Messages(String text) {
- this.text = text;
- }
-
- Messages() {
- this.text = new ConfigManager(new File("messages.yml")).getString(name());
- }
-
-}
diff --git a/src/main/java/theoni/vkbot/managers/BotManager.java b/src/main/java/theoni/vkbot/managers/BotManager.java
deleted file mode 100644
index 7dd1695..0000000
--- a/src/main/java/theoni/vkbot/managers/BotManager.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package theoni.vkbot.managers;
-
-import java.util.Random;
-import com.vk.api.sdk.client.VkApiClient;
-import com.vk.api.sdk.client.actors.GroupActor;
-import com.vk.api.sdk.exceptions.ApiException;
-import com.vk.api.sdk.exceptions.ClientException;
-import com.vk.api.sdk.httpclient.HttpTransportClient;
-import com.vk.api.sdk.objects.messages.*;
-import com.vk.api.sdk.queries.messages.MessagesGetLongPollHistoryQuery;
-
-public class BotManager {
-
- private VkApiClient vk;
- private GroupActor actor;
- private Random random;
-
- public BotManager(GroupActor actor) {
- this.vk = new VkApiClient(new HttpTransportClient());
- this.actor = actor;
- this.random = new Random();
- }
-
- public void sendMessage(String text, Message message, Keyboard keyboard) {
- try {
- int length = text.length();
- int max = 4000;
- for (int i = 0; i < length; i += max) {
- int endIndex = Math.min(i + max, length);
- vk.messages().send(actor).message(text.substring(i, endIndex)).peerId(message.getPeerId()).randomId(random.nextInt(10000)).keyboard(keyboard).execute();
- }
- } catch (ApiException | ClientException e) {
- e.printStackTrace();
- }
- }
-
- public void sendMessage(String text, Message message) {
- try {
- int length = text.length();
- int max = 4000;
- for (int i = 0; i < length; i += max) {
- int endIndex = Math.min(i + max, length);
- vk.messages().send(actor).message(text.substring(i, endIndex)).peerId(message.getPeerId()).randomId(random.nextInt(10000)).execute();
- }
- } catch (ApiException | ClientException e) {
- e.printStackTrace();
- }
- }
-
- public void sendMessage(String text, Integer id, Keyboard keyboard) {
- try {
- int length = text.length();
- int max = 4000;
- for (int i = 0; i < length; i += max) {
- int endIndex = Math.min(i + max, length);
- vk.messages().send(actor).message(text.substring(i, endIndex)).peerId(id).randomId(random.nextInt(10000)).keyboard(keyboard).execute();
- }
- } catch (ApiException | ClientException e) {
- e.printStackTrace();
- }
- }
-
- public void sendMessage(String text, Integer id) {
- try {
- int length = text.length();
- int max = 4000;
- for (int i = 0; i < length; i += max) {
- int endIndex = Math.min(i + max, length);
- vk.messages().send(actor).message(text.substring(i, endIndex)).peerId(id).randomId(random.nextInt(10000)).execute();
- }
- } catch (ApiException | ClientException e) {
- e.printStackTrace();
- }
- }
-
- public Integer getTs() {
- try {
- Integer ts = vk.messages().getLongPollServer(actor).execute().getTs();
- return ts;
- } catch (ApiException | ClientException e) {
- e.printStackTrace();
- }
- return 0;
- }
-
- public MessagesGetLongPollHistoryQuery getHistoryQuery(Integer ts) {
- MessagesGetLongPollHistoryQuery historyQuery = vk.messages().getLongPollHistory(actor).ts(ts);
- return historyQuery;
- }
-
-}
diff --git a/src/main/java/theoni/vkbot/managers/ConfigManager.java b/src/main/java/theoni/vkbot/managers/ConfigManager.java
deleted file mode 100644
index 60e9bf6..0000000
--- a/src/main/java/theoni/vkbot/managers/ConfigManager.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package theoni.vkbot.managers;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Map;
-
-import org.yaml.snakeyaml.Yaml;
-
-import theoni.vkbot.Bot;
-
-public class ConfigManager {
-
- private Yaml yaml;
- private Map config;
-
- public ConfigManager(File configFile) {
- try {
- this.yaml = new Yaml();
- this.config = yaml.load(new FileInputStream(configFile));
- } catch(FileNotFoundException e) {}
- }
-
- public String getString(String key) {
- return (String) getValue(key);
- }
-
- public int getInt(String key) {
- return (int) getValue(key);
- }
-
- public boolean getBoolean(String key) {
- return (boolean) getValue(key);
- }
-
- public List> getList(String key) {
- return (List>) getValue(key);
- }
-
- private Object getValue(String key) {
- String[] keys = key.split("\\.");
- Map value = config;
- for (int i = 0; i < keys.length - 1; i++) {
- value = (Map) value.get(keys[i]);
- }
- return value.get(keys[keys.length - 1]);
- }
-
- public static void saveResource(String target) {
- Path currentPath = Paths.get("").toAbsolutePath();
- String configPath = "/" + target;
- File file = new File(currentPath.toString(), target);
-
- if (!file.exists()) {
- try {
-
- InputStream inputStream = Bot.class.getResourceAsStream(configPath);
- OutputStream outputStream = new FileOutputStream(file);
-
- byte[] buffer = new byte[1024];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytesRead);
- }
-
- inputStream.close();
- outputStream.close();
-
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/theoni/vkbot/managers/RconManager.java b/src/main/java/theoni/vkbot/managers/RconManager.java
deleted file mode 100644
index 47f32ec..0000000
--- a/src/main/java/theoni/vkbot/managers/RconManager.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package theoni.vkbot.managers;
-
-import java.io.File;
-import java.io.IOException;
-import net.kronos.rkon.core.Rcon;
-import net.kronos.rkon.core.ex.AuthenticationException;
-
-public class RconManager {
-
- public static String command(String name) {
- ConfigManager config = new ConfigManager(new File("config.yml"));
- try {
- Rcon rcon = new Rcon(config.getString("rcon.host"), config.getInt("rcon.port"), config.getString("rcon.password").getBytes());
- String result = rcon.command(name.getBytes("UTF-8"));
- return result;
- } catch (IOException e) {
- return "Connection error";
- } catch (AuthenticationException e) {
- return "Authentication error";
- }
- }
-
-}
diff --git a/src/main/java/theoni/vkbot/utils/Keyboards.java b/src/main/java/theoni/vkbot/utils/Keyboards.java
deleted file mode 100644
index f2ce036..0000000
--- a/src/main/java/theoni/vkbot/utils/Keyboards.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package theoni.vkbot.utils;
-
-import com.vk.api.sdk.objects.messages.Keyboard;
-import com.vk.api.sdk.objects.messages.KeyboardButton;
-import com.vk.api.sdk.objects.messages.KeyboardButtonAction;
-import com.vk.api.sdk.objects.messages.KeyboardButtonColor;
-import com.vk.api.sdk.objects.messages.TemplateActionTypeNames;
-
-import theoni.vkbot.managers.ConfigManager;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class Keyboards {
-
- public static Keyboard rconKeyboard() {
- Keyboard keyboard = new Keyboard();
-
- keyboard.setButtons(Arrays.asList(
- Arrays.asList(
- new KeyboardButton()
- .setColor(KeyboardButtonColor.POSITIVE)
- .setAction(new KeyboardButtonAction()
- .setLabel("Rcon")
- .setType(TemplateActionTypeNames.TEXT))
- )
- ));
- keyboard.setInline(true);
-
- return keyboard;
- }
-
- public static Keyboard commandsKeyboard() {
- Keyboard keyboard = new Keyboard();
- ConfigManager config = new ConfigManager(new File("config.yml"));
-
- List list = new ArrayList<>();
- for (Object command : config.getList("fast-commands")) {
- list.add(
- new KeyboardButton()
- .setColor(KeyboardButtonColor.POSITIVE)
- .setAction(new KeyboardButtonAction()
- .setLabel((config.getString("command-prefix").equals("") ? "/" : config.getString("command-prefix")) + command)
- .setType(TemplateActionTypeNames.TEXT))
- );
- }
-
- keyboard.setButtons(Arrays.asList(list));
- keyboard.setInline(true);
-
- return keyboard;
- }
-}
diff --git a/src/main/kotlin/com/mefrreex/vkbot/Bootstrap.kt b/src/main/kotlin/com/mefrreex/vkbot/Bootstrap.kt
new file mode 100644
index 0000000..73a0c21
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/Bootstrap.kt
@@ -0,0 +1,32 @@
+package com.mefrreex.vkbot
+
+import com.mefrreex.vkbot.config.ConfigManager
+import com.mefrreex.vkbot.logger.Logger
+import java.io.File
+
+val logger = Logger.getInstance()
+val configManager = ConfigManager()
+
+var server: Server? = null
+
+class Bootstrap {
+
+}
+
+fun main() {
+ logger.info("Starting the bot...")
+ val bootstrap = Bootstrap();
+
+ val resources = listOf(File("config.yml"), File("allow_list.yml"), File("messages.yml"))
+ if (!resources.all { it.exists() }) {
+ logger.info("Saving resources.")
+ configManager.saveResource("allow_list.yml")
+ configManager.saveResource("messages.yml")
+ configManager.saveResource("config.yml")
+ }
+
+ server = Server(bootstrap)
+ server!!.start()
+
+ logger.info("Bot is started!")
+}
diff --git a/src/main/kotlin/com/mefrreex/vkbot/Server.kt b/src/main/kotlin/com/mefrreex/vkbot/Server.kt
new file mode 100644
index 0000000..e75667a
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/Server.kt
@@ -0,0 +1,45 @@
+package com.mefrreex.vkbot
+
+import com.mefrreex.vkbot.config.Config
+import com.mefrreex.vkbot.config.defaults.Settings
+import com.mefrreex.vkbot.handler.EventHandler
+import com.vk.api.sdk.client.VkApiClient
+import com.vk.api.sdk.client.actors.GroupActor
+import com.vk.api.sdk.httpclient.HttpTransportClient
+
+class Server(val bootstrap: Bootstrap) {
+
+ private val config = Settings.getConfig()
+ private val groupId = Settings.GROUP_ID.int()
+ private val accessToken = Settings.ACCESS_TOKEN.string()
+
+ fun start() {
+
+ val httpClient: HttpTransportClient = HttpTransportClient.getInstance()
+ val vk = VkApiClient(httpClient)
+
+ val groupActor = GroupActor(groupId, accessToken)
+
+ vk.groupsLongPoll().setLongPollSettings(groupActor, groupId)
+ .enabled(true)
+ .messageNew(true)
+ .execute()
+
+ val handler = EventHandler(vk, groupActor, 1, this)
+ handler.run();
+
+ }
+
+ fun getConfig(): Config {
+ return config
+ }
+
+ fun getGroupId(): Int {
+ return groupId
+ }
+
+ fun getAccessToken(): String {
+ return accessToken
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/config/Config.kt b/src/main/kotlin/com/mefrreex/vkbot/config/Config.kt
new file mode 100644
index 0000000..c499056
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/config/Config.kt
@@ -0,0 +1,95 @@
+package com.mefrreex.vkbot.config
+
+import org.yaml.snakeyaml.DumperOptions
+import org.yaml.snakeyaml.Yaml
+import java.io.*
+import java.nio.charset.StandardCharsets
+
+
+class Config(private val file: File) {
+
+ constructor(fileName: String) : this(File(fileName))
+
+ private var yaml: Yaml? = null
+ private var config: MutableMap? = null
+
+ init {
+ if (!file.exists()) {
+ file.createNewFile()
+ }
+ val dumperOptions = DumperOptions();
+ dumperOptions.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK;
+ this.yaml = Yaml(dumperOptions)
+ this.config = yaml!!.load(FileInputStream(file)) as MutableMap?
+ }
+
+ fun getString(key: String): String? {
+ return getValue(key) as String?
+ }
+
+ fun getInt(key: String): Int {
+ return getValue(key) as Int
+ }
+
+ fun getBoolean(key: String): Boolean {
+ return getValue(key) as Boolean
+ }
+
+ fun getList(key: String): List<*>? {
+ return getValue(key) as List<*>?
+ }
+
+ fun getAll(): Map? {
+ return config
+ }
+
+ fun getValue(key: String): Any? {
+ val keys = key.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ var value = config as Map?
+ for (i in 0 until keys.size - 1) {
+ value = value!![keys[i]] as Map?
+ }
+ return value!![keys[keys.size - 1]]
+ }
+
+ fun set(key: String, value: Any) {
+ val keys = key.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ var current = config
+ for (i in 0 until keys.size - 1) {
+ val next = current!![keys[i]] as MutableMap?
+ if (next == null) {
+ val newMap = mutableMapOf()
+ current[keys[i]] = newMap
+ current = newMap
+ } else {
+ current = next
+ }
+ }
+ current!![keys[keys.size - 1]] = value
+ }
+
+ fun save() {
+ val content = yaml!!.dump(config)
+ val lines = mutableListOf()
+
+ // Считываем старое содержимое файла
+ if (file.exists()) {
+ file.forEachLine { line ->
+ lines.add(line)
+ }
+ }
+
+ // Обновляем содержимое файла
+ val writer = file.bufferedWriter()
+ for (line in lines) {
+ if (line.trim().startsWith("#")) {
+ writer.write(line)
+ writer.newLine()
+ }
+ }
+ writer.write(content)
+ writer.flush()
+ writer.close()
+ }
+
+}
diff --git a/src/main/kotlin/com/mefrreex/vkbot/config/ConfigManager.kt b/src/main/kotlin/com/mefrreex/vkbot/config/ConfigManager.kt
new file mode 100644
index 0000000..8ba298e
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/config/ConfigManager.kt
@@ -0,0 +1,33 @@
+package com.mefrreex.vkbot.config
+
+import java.nio.file.Paths.*
+import java.io.*
+
+
+class ConfigManager {
+
+ fun saveResource(target: String) {
+
+ val currentPath = get("").toAbsolutePath()
+ val configPath = "/$target"
+ val file = File(currentPath.toString(), target)
+
+ if (!file.exists()) {
+ try {
+ val inputStream: InputStream = javaClass.getResourceAsStream(configPath)
+ val outputStream: OutputStream = FileOutputStream(file)
+ val buffer = ByteArray(1024)
+ var bytesRead: Int
+ while (inputStream.read(buffer).also { bytesRead = it } != -1) {
+ outputStream.write(buffer, 0, bytesRead)
+ }
+ inputStream.close()
+ outputStream.close()
+
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/config/defaults/AllowList.kt b/src/main/kotlin/com/mefrreex/vkbot/config/defaults/AllowList.kt
new file mode 100644
index 0000000..6d65140
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/config/defaults/AllowList.kt
@@ -0,0 +1,26 @@
+package com.mefrreex.vkbot.config.defaults
+
+import com.mefrreex.vkbot.config.Config
+
+enum class AllowList(private val key: String) {
+
+ ALLOWED_USERS("allowed-users");
+
+ companion object {
+
+ private val CONFIG = Config("allow_list.yml")
+
+ fun getConfig(): Config {
+ return CONFIG
+ }
+ }
+
+ private fun getValue(key: String): Any? {
+ return CONFIG.getList(key)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ fun get(): List {
+ return this.getValue(key) as List
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/config/defaults/Messages.kt b/src/main/kotlin/com/mefrreex/vkbot/config/defaults/Messages.kt
new file mode 100644
index 0000000..9db696f
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/config/defaults/Messages.kt
@@ -0,0 +1,34 @@
+package com.mefrreex.vkbot.config.defaults
+
+import com.mefrreex.vkbot.config.Config
+
+enum class Messages(private val key: String) {
+
+ USER_ID("user_id"),
+ USER_NOT_WHITELISTED("user_not_whitelisted"),
+
+ START("start"),
+
+ RCON("rcon"),
+ RCON_WITH_COMMANDS("rcon_with_commands"),
+
+ COMMAND_BLOCKED("command_blocked"),
+ FAILED_TO_CONNECT("failed_to_connect"),
+ FAILED_TO_AUTHENTICATE("failed_to_authenticate"),
+
+ COMMAND_SENDED("command_sended"),
+ RESPONSE_NULL("response_null");
+
+ companion object {
+
+ private val CONFIG = Config("messages.yml")
+
+ fun getConfig(): Config {
+ return CONFIG
+ }
+ }
+
+ fun get(): String {
+ return CONFIG.getString(key)!!
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/config/defaults/Settings.kt b/src/main/kotlin/com/mefrreex/vkbot/config/defaults/Settings.kt
new file mode 100644
index 0000000..4fcf515
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/config/defaults/Settings.kt
@@ -0,0 +1,46 @@
+package com.mefrreex.vkbot.config.defaults
+
+import com.mefrreex.vkbot.config.Config
+
+enum class Settings(private val key: String) {
+
+ GROUP_ID("vk.groupId"),
+ ACCESS_TOKEN("vk.accessToken"),
+
+ RCON_HOST("rcon.host"),
+ RCON_PORT("rcon.port"),
+ RCON_PASSWORD("rcon.password"),
+
+ COMMAND_PREFIX("commands.prefix"),
+ FAST_COMMANDS("commands.fast-commands"),
+ BLOCKED_COMMANDS("commands.blocked-commands");
+
+ companion object {
+
+ private val CONFIG = Config("config.yml")
+
+ fun getConfig(): Config {
+ return CONFIG
+ }
+ }
+
+ private fun getValue(key: String): Any? {
+ return CONFIG.getValue(key)
+ }
+
+ fun string(): String {
+ return this.getValue(key) as String
+ }
+
+ fun int(): Int {
+ return this.getValue(key) as Int
+ }
+
+ fun list(): List<*> {
+ return this.getValue(key) as List<*>
+ }
+
+ fun get(): Any? {
+ return this.getValue(key)
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/handler/EventHandler.kt b/src/main/kotlin/com/mefrreex/vkbot/handler/EventHandler.kt
new file mode 100644
index 0000000..f86de34
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/handler/EventHandler.kt
@@ -0,0 +1,144 @@
+package com.mefrreex.vkbot.handler
+
+import com.mefrreex.vkbot.Server
+import com.mefrreex.vkbot.config.defaults.AllowList
+import com.mefrreex.vkbot.config.defaults.Messages
+import com.mefrreex.vkbot.config.defaults.Settings
+import com.mefrreex.vkbot.logger.Logger
+import com.mefrreex.vkbot.objects.MessageFix
+import com.mefrreex.vkbot.rcon.Rcon
+import com.mefrreex.vkbot.utils.Keyboards
+import com.mefrreex.vkbot.utils.TextFormat
+import com.vk.api.sdk.client.VkApiClient
+import com.vk.api.sdk.client.actors.GroupActor
+import com.vk.api.sdk.events.Events
+import com.vk.api.sdk.events.longpoll.GroupLongPollApi
+import com.vk.api.sdk.objects.callback.messages.CallbackMessage
+import com.vk.api.sdk.objects.messages.Message
+import net.kronos.rkon.core.ex.AuthenticationException
+import java.io.IOException
+import kotlin.random.Random
+
+class EventHandler(
+
+ private val client: VkApiClient,
+ private val actor: GroupActor,
+ private val waitTime: Int,
+ private val server: Server
+
+) : GroupLongPollApi(client, actor, waitTime) {
+
+ val logger = Logger.getInstance()
+
+ fun messageNewFix(groupId: Int, message: Message) {
+
+ val text = message.text.lowercase()
+
+ when {
+
+ text == "начать" || text == "start" || text == "rcon" -> {
+ if (!AllowList.ALLOWED_USERS.get().contains(message.fromId)) {
+ client.messages().send(actor)
+ .message(Messages.USER_NOT_WHITELISTED.get())
+ .keyboard(Keyboards.commandsKeyboard())
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ return
+ }
+ client.messages().send(actor)
+ .message(Messages.START.get())
+ .keyboard(Keyboards.commandsKeyboard())
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ }
+
+ text.startsWith(Settings.COMMAND_PREFIX.string()) -> {
+
+ val command = text.substring(Settings.COMMAND_PREFIX.string().length, text.length)
+ val commandName = command.split("\\s+".toRegex())[0].removePrefix("/")
+
+ when(command) {
+
+ "getid" -> {
+ client.messages().send(actor)
+ .message(Messages.USER_ID.get().format(message.fromId))
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ }
+
+ else -> {
+
+ if (!AllowList.ALLOWED_USERS.get().contains(message.fromId)) {
+ return
+ }
+
+ if (Settings.BLOCKED_COMMANDS.list().contains(commandName)) {
+ client.messages().send(actor)
+ .message(Messages.COMMAND_BLOCKED.get().format(command))
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ logger.warn("User ${message.fromId} tried to use the blocked command ${TextFormat.RED}`$command`${TextFormat.RESET}.")
+ return
+ }
+
+ try {
+ Rcon.command(command, response = {
+ if (it.length < 4000) {
+ client.messages().send(actor)
+ .message(
+ if (it != "") {
+ Messages.COMMAND_SENDED.get().format(it)
+ } else {
+ Messages.RESPONSE_NULL.get()
+ }
+ )
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ } else {
+ client.messages().send(actor)
+ .message("The command's response is too long.")
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ }
+ logger.info("Used ${TextFormat.YELLOW}`$command`${TextFormat.RESET} command by user ${message.fromId}")
+ })
+
+ } catch (e: IOException) {
+ client.messages().send(actor)
+ .message(Messages.FAILED_TO_CONNECT.get())
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ logger.error("Unhandled exception when trying to connect to RCON:", e)
+
+ } catch (e: AuthenticationException) {
+ client.messages().send(actor)
+ .message(Messages.FAILED_TO_AUTHENTICATE.get())
+ .peerId(message.peerId)
+ .randomId(Random.nextInt(10000))
+ .execute()
+ logger.error("Unhandled exception on RCON authentication attempt:", e)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override fun parse(message: CallbackMessage): String? {
+ if (message.type == Events.MESSAGE_NEW) {
+ gson.fromJson(message.getObject(), MessageFix::class.java).message?.let {
+ this.messageNewFix(message.groupId, it)
+ }
+ return "OK"
+ }
+ return super.parse(message)
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/logger/Logger.kt b/src/main/kotlin/com/mefrreex/vkbot/logger/Logger.kt
new file mode 100644
index 0000000..f07ef07
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/logger/Logger.kt
@@ -0,0 +1,50 @@
+package com.mefrreex.vkbot.logger
+
+import com.mefrreex.vkbot.utils.TextFormat
+import java.text.SimpleDateFormat
+import java.util.*
+
+
+class Logger {
+
+ private val DATE_FORMAT = SimpleDateFormat("HH:mm:ss.SSS")
+
+ fun info(message: String) {
+ log(TextFormat.BLUE + "INFO" + TextFormat.RESET, message)
+ }
+
+ fun warn(message: String) {
+ log(TextFormat.RED + "WARN" + TextFormat.RESET, message)
+ }
+
+ fun error(message: String) {
+ log(TextFormat.RED + "ERROR" + TextFormat.RESET, message)
+ }
+
+ fun error(message: String, throwable: Throwable) {
+ log(TextFormat.RED + "ERROR" + TextFormat.RESET, message, throwable)
+ }
+
+ fun debug(message: String) {
+ log(TextFormat.BLUE + "DEBUG" + TextFormat.RESET, message)
+ }
+
+ private fun log(level: String, message: String) {
+ log(level, message, null)
+ }
+
+ private fun log(level: String, message: String, throwable: Throwable?) {
+ val date = DATE_FORMAT.format(Date())
+ println("${TextFormat.CYAN}$date ${TextFormat.WHITE}[$level] $message")
+ throwable?.printStackTrace()
+ }
+
+ companion object {
+
+ private val instance = Logger()
+
+ fun getInstance(): Logger {
+ return instance
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/objects/MessageFix.kt b/src/main/kotlin/com/mefrreex/vkbot/objects/MessageFix.kt
new file mode 100644
index 0000000..cffce71
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/objects/MessageFix.kt
@@ -0,0 +1,11 @@
+package com.mefrreex.vkbot.objects
+
+import com.google.gson.annotations.SerializedName
+import com.vk.api.sdk.objects.messages.Message
+
+class MessageFix {
+
+ @SerializedName("message")
+ val message: Message? = null
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/rcon/Rcon.kt b/src/main/kotlin/com/mefrreex/vkbot/rcon/Rcon.kt
new file mode 100644
index 0000000..a0c98a2
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/rcon/Rcon.kt
@@ -0,0 +1,24 @@
+package com.mefrreex.vkbot.rcon
+
+import com.mefrreex.vkbot.config.defaults.Settings
+import net.kronos.rkon.core.ex.AuthenticationException
+import net.kronos.rkon.core.Rcon as RconManager
+import kotlin.jvm.Throws
+import java.io.IOException
+import java.util.function.Consumer
+
+class Rcon {
+
+ companion object {
+
+ fun command(name: String, response: Consumer) {
+ response.accept(command(name));
+ }
+
+ @Throws(IOException::class, AuthenticationException::class)
+ fun command(name: String): String {
+ val rcon = RconManager(Settings.RCON_HOST.string(), Settings.RCON_PORT.int(), Settings.RCON_PASSWORD.string().toByteArray())
+ return rcon.command(name.toByteArray(charset("UTF-8")));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/utils/Keyboards.kt b/src/main/kotlin/com/mefrreex/vkbot/utils/Keyboards.kt
new file mode 100644
index 0000000..3dc2ee0
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/utils/Keyboards.kt
@@ -0,0 +1,29 @@
+package com.mefrreex.vkbot.utils
+
+import com.mefrreex.vkbot.config.defaults.Settings
+import com.vk.api.sdk.objects.messages.*
+import java.util.*
+
+object Keyboards {
+
+ @Suppress("UNCHECKED_CAST")
+ fun commandsKeyboard(): Keyboard {
+ val keyboard = Keyboard()
+
+ val list: MutableList = ArrayList()
+ for (command in Settings.FAST_COMMANDS.list() as List) {
+ list.add(
+ KeyboardButton()
+ .setColor(KeyboardButtonColor.POSITIVE)
+ .setAction(
+ KeyboardButtonAction()
+ .setLabel(Settings.COMMAND_PREFIX.string() + command)
+ .setType(TemplateActionTypeNames.TEXT)
+ )
+ )
+ }
+ keyboard.buttons = listOf>(list)
+ keyboard.inline = true
+ return keyboard
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/mefrreex/vkbot/utils/TextFormat.kt b/src/main/kotlin/com/mefrreex/vkbot/utils/TextFormat.kt
new file mode 100644
index 0000000..6ec71c6
--- /dev/null
+++ b/src/main/kotlin/com/mefrreex/vkbot/utils/TextFormat.kt
@@ -0,0 +1,27 @@
+package com.mefrreex.vkbot.utils
+
+object TextFormat {
+
+ val BLACK = "\u001b[0;30m"
+ val RED = "\u001b[0;31m"
+ val GREEN = "\u001b[0;92m"
+ val YELLOW = "\u001b[0;33m"
+ val BLUE = "\u001b[0;34m"
+ val MAGENTA = "\u001b[0;35m"
+ val CYAN = "\u001b[0;36m"
+ val WHITE = "\u001b[0;37m"
+
+ val BLACK_BACKGROUND = "\u001b[40m"
+ val RED_BACKGROUND = "\u001b[41m"
+ val GREEN_BACKGROUND = "\u001b[42m"
+ val YELLOW_BACKGROUND = "\u001b[43m"
+ val BLUE_BACKGROUND = "\u001b[44m"
+ val MAGENTA_BACKGROUND = "\u001b[45m"
+ val CYAN_BACKGROUND = "\u001b[46m"
+ val WHITE_BACKGROUND = "\u001b[47m"
+
+ val RESET = "\u001b[0m"
+ val BOLD = "\u001b[1m"
+ val UNDERLINE = "\u001b[4m"
+ val REVERSED = "\u001b[7m"
+}
\ No newline at end of file
diff --git a/src/main/java/net/kronos/rkon/core/Rcon.java b/src/main/kotlin/net/kronos/rkon/core/Rcon.java
similarity index 95%
rename from src/main/java/net/kronos/rkon/core/Rcon.java
rename to src/main/kotlin/net/kronos/rkon/core/Rcon.java
index b150156..3d32ea5 100644
--- a/src/main/java/net/kronos/rkon/core/Rcon.java
+++ b/src/main/kotlin/net/kronos/rkon/core/Rcon.java
@@ -1,133 +1,133 @@
-package net.kronos.rkon.core;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.nio.charset.Charset;
-import java.util.Random;
-
-import net.kronos.rkon.core.ex.AuthenticationException;
-
-public class Rcon {
-
- private final Object sync = new Object();
- private final Random rand = new Random();
-
- private int requestId;
- private Socket socket;
-
- private Charset charset;
-
- /**
- * Create, connect and authenticate a new Rcon object
- *
- * @param host Rcon server address
- * @param port Rcon server port
- * @param password Rcon server password
- *
- * @throws IOException
- * @throws AuthenticationException
- */
- public Rcon(String host, int port, byte[] password) throws IOException, AuthenticationException {
- // Default charset is utf8
- this.charset = Charset.forName("UTF-8");
-
- // Connect to host
- this.connect(host, port, password);
- }
-
- /**
- * Connect to a rcon server
- *
- * @param host Rcon server address
- * @param port Rcon server port
- * @param password Rcon server password
- *
- * @throws IOException
- * @throws AuthenticationException
- */
- public void connect(String host, int port, byte[] password) throws IOException, AuthenticationException {
- if(host == null || host.trim().isEmpty()) {
- throw new IllegalArgumentException("Host can't be null or empty");
- }
-
- if(port < 1 || port > 65535) {
- throw new IllegalArgumentException("Port is out of range");
- }
-
- // Connect to the rcon server
- synchronized(sync) {
- // New random request id
- this.requestId = rand.nextInt();
-
- // We can't reuse a socket, so we need a new one
- this.socket = new Socket(host, port);
- }
-
- // Send the auth packet
- RconPacket res = this.send(RconPacket.SERVERDATA_AUTH, password);
-
- // Auth failed
- if(res.getRequestId() == -1) {
- throw new AuthenticationException("Password rejected by server");
- }
- }
-
- /**
- * Disconnect from the current server
- *
- * @throws IOException
- */
- public void disconnect() throws IOException {
- synchronized(sync) {
- this.socket.close();
- }
- }
-
- /**
- * Send a command to the server
- *
- * @param payload The command to send
- * @return The payload of the response
- *
- * @throws IOException
- */
- public String command(String payload) throws IOException {
- if(payload == null || payload.trim().isEmpty()) {
- throw new IllegalArgumentException("Payload can't be null or empty");
- }
-
- RconPacket response = this.send(RconPacket.SERVERDATA_EXECCOMMAND, payload.getBytes());
-
- return new String(response.getPayload(), this.getCharset());
- }
-
- public String command(byte[] payload) throws IOException {
-
- RconPacket response = this.send(RconPacket.SERVERDATA_EXECCOMMAND, payload);
-
- return new String(response.getPayload(), this.getCharset());
- }
-
- private RconPacket send(int type, byte[] payload) throws IOException {
- synchronized(sync) {
- return RconPacket.send(this, type, payload);
- }
- }
-
- public int getRequestId() {
- return requestId;
- }
-
- public Socket getSocket() {
- return socket;
- }
-
- public Charset getCharset() {
- return charset;
- }
-
- public void setCharset(Charset charset) {
- this.charset = charset;
- }
-
-}
+package net.kronos.rkon.core;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.nio.charset.Charset;
+import java.util.Random;
+
+import net.kronos.rkon.core.ex.AuthenticationException;
+
+public class Rcon {
+
+ private final Object sync = new Object();
+ private final Random rand = new Random();
+
+ private int requestId;
+ private Socket socket;
+
+ private Charset charset;
+
+ /**
+ * Create, connect and authenticate a new Rcon object
+ *
+ * @param host Rcon server address
+ * @param port Rcon server port
+ * @param password Rcon server password
+ *
+ * @throws IOException
+ * @throws AuthenticationException
+ */
+ public Rcon(String host, int port, byte[] password) throws IOException, AuthenticationException {
+ // Default charset is utf8
+ this.charset = Charset.forName("UTF-8");
+
+ // Connect to host
+ this.connect(host, port, password);
+ }
+
+ /**
+ * Connect to a rcon server
+ *
+ * @param host Rcon server address
+ * @param port Rcon server port
+ * @param password Rcon server password
+ *
+ * @throws IOException
+ * @throws AuthenticationException
+ */
+ public void connect(String host, int port, byte[] password) throws IOException, AuthenticationException {
+ if(host == null || host.trim().isEmpty()) {
+ throw new IllegalArgumentException("Host can't be null or empty");
+ }
+
+ if(port < 1 || port > 65535) {
+ throw new IllegalArgumentException("Port is out of range");
+ }
+
+ // Connect to the rcon server
+ synchronized(sync) {
+ // New random request id
+ this.requestId = rand.nextInt();
+
+ // We can't reuse a socket, so we need a new one
+ this.socket = new Socket(host, port);
+ }
+
+ // Send the auth packet
+ RconPacket res = this.send(RconPacket.SERVERDATA_AUTH, password);
+
+ // Auth failed
+ if(res.getRequestId() == -1) {
+ throw new AuthenticationException("Password rejected by server");
+ }
+ }
+
+ /**
+ * Disconnect from the current server
+ *
+ * @throws IOException
+ */
+ public void disconnect() throws IOException {
+ synchronized(sync) {
+ this.socket.close();
+ }
+ }
+
+ /**
+ * Send a command to the server
+ *
+ * @param payload The command to send
+ * @return The payload of the response
+ *
+ * @throws IOException
+ */
+ public String command(String payload) throws IOException {
+ if(payload == null || payload.trim().isEmpty()) {
+ throw new IllegalArgumentException("Payload can't be null or empty");
+ }
+
+ RconPacket response = this.send(RconPacket.SERVERDATA_EXECCOMMAND, payload.getBytes());
+
+ return new String(response.getPayload(), this.getCharset());
+ }
+
+ public String command(byte[] payload) throws IOException {
+
+ RconPacket response = this.send(RconPacket.SERVERDATA_EXECCOMMAND, payload);
+
+ return new String(response.getPayload(), this.getCharset());
+ }
+
+ private RconPacket send(int type, byte[] payload) throws IOException {
+ synchronized(sync) {
+ return RconPacket.send(this, type, payload);
+ }
+ }
+
+ public int getRequestId() {
+ return requestId;
+ }
+
+ public Socket getSocket() {
+ return socket;
+ }
+
+ public Charset getCharset() {
+ return charset;
+ }
+
+ public void setCharset(Charset charset) {
+ this.charset = charset;
+ }
+
+}
diff --git a/src/main/java/net/kronos/rkon/core/RconPacket.java b/src/main/kotlin/net/kronos/rkon/core/RconPacket.java
similarity index 96%
rename from src/main/java/net/kronos/rkon/core/RconPacket.java
rename to src/main/kotlin/net/kronos/rkon/core/RconPacket.java
index fdd0e87..b2deec2 100644
--- a/src/main/java/net/kronos/rkon/core/RconPacket.java
+++ b/src/main/kotlin/net/kronos/rkon/core/RconPacket.java
@@ -1,152 +1,152 @@
-package net.kronos.rkon.core;
-
-import java.io.DataInputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.SocketException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-import net.kronos.rkon.core.ex.MalformedPacketException;
-
-public class RconPacket {
-
- public static final int SERVERDATA_EXECCOMMAND = 2;
- public static final int SERVERDATA_AUTH = 3;
-
- private int requestId;
- private int type;
- private byte[] payload;
-
- private RconPacket(int requestId, int type, byte[] payload) {
- this.requestId = requestId;
- this.type = type;
- this.payload = payload;
- }
-
- public int getRequestId() {
- return requestId;
- }
-
- public int getType() {
- return type;
- }
-
- public byte[] getPayload() {
- return payload;
- }
-
- /**
- * Send a Rcon packet and fetch the response
- *
- * @param rcon Rcon instance
- * @param type The packet type
- * @param payload The payload (password, command, etc.)
- * @return A RconPacket object containing the response
- *
- * @throws IOException
- * @throws MalformedPacketException
- */
- protected static RconPacket send(Rcon rcon, int type, byte[] payload) throws IOException {
- try {
- RconPacket.write(rcon.getSocket().getOutputStream(), rcon.getRequestId(), type, payload);
- }
- catch(SocketException se) {
- // Close the socket if something happens
- rcon.getSocket().close();
-
- // Rethrow the exception
- throw se;
- }
-
- return RconPacket.read(rcon.getSocket().getInputStream());
- }
-
- /**
- * Write a rcon packet on an outputstream
- *
- * @param out The OutputStream to write on
- * @param requestId The request id
- * @param type The packet type
- * @param payload The payload
- *
- * @throws IOException
- */
- private static void write(OutputStream out, int requestId, int type, byte[] payload) throws IOException {
- int bodyLength = RconPacket.getBodyLength(payload.length);
- int packetLength = RconPacket.getPacketLength(bodyLength);
-
- ByteBuffer buffer = ByteBuffer.allocate(packetLength);
- buffer.order(ByteOrder.LITTLE_ENDIAN);
-
- buffer.putInt(bodyLength);
- buffer.putInt(requestId);
- buffer.putInt(type);
- buffer.put(payload);
-
- // Null bytes terminators
- buffer.put((byte)0);
- buffer.put((byte)0);
-
- // Woosh!
- out.write(buffer.array());
- out.flush();
- }
-
- /**
- * Read an incoming rcon packet
- *
- * @param in The InputStream to read on
- * @return The read RconPacket
- *
- * @throws IOException
- * @throws MalformedPacketException
- */
- private static RconPacket read(InputStream in) throws IOException {
- // Header is 3 4-bytes ints
- byte[] header = new byte[4 * 3];
-
- // Read the 3 ints
- in.read(header);
-
- try {
- // Use a bytebuffer in little endian to read the first 3 ints
- ByteBuffer buffer = ByteBuffer.wrap(header);
- buffer.order(ByteOrder.LITTLE_ENDIAN);
-
- int length = buffer.getInt();
- int requestId = buffer.getInt();
- int type = buffer.getInt();
-
- // Payload size can be computed now that we have its length
- byte[] payload = new byte[length - 4 - 4 - 2];
-
- DataInputStream dis = new DataInputStream(in);
-
- // Read the full payload
- dis.readFully(payload);
-
- // Read the null bytes
- dis.read(new byte[2]);
-
- return new RconPacket(requestId, type, payload);
- }
- catch(BufferUnderflowException | EOFException e) {
- throw new MalformedPacketException("Cannot read the whole packet");
- }
- }
-
- private static int getPacketLength(int bodyLength) {
- // 4 bytes for length + x bytes for body length
- return 4 + bodyLength;
- }
-
- private static int getBodyLength(int payloadLength) {
- // 4 bytes for requestId, 4 bytes for type, x bytes for payload, 2 bytes for two null bytes
- return 4 + 4 + payloadLength + 2;
- }
-
-}
+package net.kronos.rkon.core;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import net.kronos.rkon.core.ex.MalformedPacketException;
+
+public class RconPacket {
+
+ public static final int SERVERDATA_EXECCOMMAND = 2;
+ public static final int SERVERDATA_AUTH = 3;
+
+ private int requestId;
+ private int type;
+ private byte[] payload;
+
+ private RconPacket(int requestId, int type, byte[] payload) {
+ this.requestId = requestId;
+ this.type = type;
+ this.payload = payload;
+ }
+
+ public int getRequestId() {
+ return requestId;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ /**
+ * Send a Rcon packet and fetch the response
+ *
+ * @param rcon Rcon instance
+ * @param type The packet type
+ * @param payload The payload (password, command, etc.)
+ * @return A RconPacket object containing the response
+ *
+ * @throws IOException
+ * @throws MalformedPacketException
+ */
+ protected static RconPacket send(Rcon rcon, int type, byte[] payload) throws IOException {
+ try {
+ RconPacket.write(rcon.getSocket().getOutputStream(), rcon.getRequestId(), type, payload);
+ }
+ catch(SocketException se) {
+ // Close the socket if something happens
+ rcon.getSocket().close();
+
+ // Rethrow the exception
+ throw se;
+ }
+
+ return RconPacket.read(rcon.getSocket().getInputStream());
+ }
+
+ /**
+ * Write a rcon packet on an outputstream
+ *
+ * @param out The OutputStream to write on
+ * @param requestId The request id
+ * @param type The packet type
+ * @param payload The payload
+ *
+ * @throws IOException
+ */
+ private static void write(OutputStream out, int requestId, int type, byte[] payload) throws IOException {
+ int bodyLength = RconPacket.getBodyLength(payload.length);
+ int packetLength = RconPacket.getPacketLength(bodyLength);
+
+ ByteBuffer buffer = ByteBuffer.allocate(packetLength);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ buffer.putInt(bodyLength);
+ buffer.putInt(requestId);
+ buffer.putInt(type);
+ buffer.put(payload);
+
+ // Null bytes terminators
+ buffer.put((byte)0);
+ buffer.put((byte)0);
+
+ // Woosh!
+ out.write(buffer.array());
+ out.flush();
+ }
+
+ /**
+ * Read an incoming rcon packet
+ *
+ * @param in The InputStream to read on
+ * @return The read RconPacket
+ *
+ * @throws IOException
+ * @throws MalformedPacketException
+ */
+ private static RconPacket read(InputStream in) throws IOException {
+ // Header is 3 4-bytes ints
+ byte[] header = new byte[4 * 3];
+
+ // Read the 3 ints
+ in.read(header);
+
+ try {
+ // Use a bytebuffer in little endian to read the first 3 ints
+ ByteBuffer buffer = ByteBuffer.wrap(header);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ int length = buffer.getInt();
+ int requestId = buffer.getInt();
+ int type = buffer.getInt();
+
+ // Payload size can be computed now that we have its length
+ byte[] payload = new byte[length - 4 - 4 - 2];
+
+ DataInputStream dis = new DataInputStream(in);
+
+ // Read the full payload
+ dis.readFully(payload);
+
+ // Read the null bytes
+ dis.read(new byte[2]);
+
+ return new RconPacket(requestId, type, payload);
+ }
+ catch(BufferUnderflowException | EOFException e) {
+ throw new MalformedPacketException("Cannot read the whole packet");
+ }
+ }
+
+ private static int getPacketLength(int bodyLength) {
+ // 4 bytes for length + x bytes for body length
+ return 4 + bodyLength;
+ }
+
+ private static int getBodyLength(int payloadLength) {
+ // 4 bytes for requestId, 4 bytes for type, x bytes for payload, 2 bytes for two null bytes
+ return 4 + 4 + payloadLength + 2;
+ }
+
+}
diff --git a/src/main/java/net/kronos/rkon/core/ex/AuthenticationException.java b/src/main/kotlin/net/kronos/rkon/core/ex/AuthenticationException.java
similarity index 94%
rename from src/main/java/net/kronos/rkon/core/ex/AuthenticationException.java
rename to src/main/kotlin/net/kronos/rkon/core/ex/AuthenticationException.java
index 9b31de8..45ef113 100644
--- a/src/main/java/net/kronos/rkon/core/ex/AuthenticationException.java
+++ b/src/main/kotlin/net/kronos/rkon/core/ex/AuthenticationException.java
@@ -1,9 +1,9 @@
-package net.kronos.rkon.core.ex;
-
-public class AuthenticationException extends Exception {
-
- public AuthenticationException(String message) {
- super(message);
- }
-
-}
+package net.kronos.rkon.core.ex;
+
+public class AuthenticationException extends Exception {
+
+ public AuthenticationException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/net/kronos/rkon/core/ex/MalformedPacketException.java b/src/main/kotlin/net/kronos/rkon/core/ex/MalformedPacketException.java
similarity index 94%
rename from src/main/java/net/kronos/rkon/core/ex/MalformedPacketException.java
rename to src/main/kotlin/net/kronos/rkon/core/ex/MalformedPacketException.java
index 8ec8c13..ab2254b 100644
--- a/src/main/java/net/kronos/rkon/core/ex/MalformedPacketException.java
+++ b/src/main/kotlin/net/kronos/rkon/core/ex/MalformedPacketException.java
@@ -1,11 +1,11 @@
-package net.kronos.rkon.core.ex;
-
-import java.io.IOException;
-
-public class MalformedPacketException extends IOException {
-
- public MalformedPacketException(String message) {
- super(message);
- }
-
-}
+package net.kronos.rkon.core.ex;
+
+import java.io.IOException;
+
+public class MalformedPacketException extends IOException {
+
+ public MalformedPacketException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..fe490c3
--- /dev/null
+++ b/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.mefrreex.vkbot.BootstrapKt
+
diff --git a/src/main/resources/allow_list.yml b/src/main/resources/allow_list.yml
new file mode 100644
index 0000000..5b76d5f
--- /dev/null
+++ b/src/main/resources/allow_list.yml
@@ -0,0 +1,4 @@
+# Список разрешенных пользователей
+# (id пользователей которые могут использовать RCON)
+allowed-users:
+ - 1 # Цифровые ID
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index d477506..400a7cb 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,24 +1,20 @@
-groupId: 123 # ID группы вк
-token: "" # Токен группы ВК
-
-# Символ перед командой для отправки комманды. Например "/".
-# Оставьте пустым если не требуется.
-command-prefix: "/"
+vk:
+ groupId: 123 # Id группы ВК
+ accessToken: "token" # Токен группы ВК
rcon:
host: "localhost" # Адрес RCON
port: 19132 # Порт RCON
- password: "5YzNmYjUyM" # Пароль RCON
+ password: "password" # Пароль RCON
-allowed-users:
- - 1 # Цифровые ID
+commands:
+ # Символ перед командой для отправки комманды. Например '/'.
+ # Оставьте пустым если не требуется.
+ prefix: '/'
-# Быстрые команды, будут отображатся при вводе Rcon
-fast-commands:
- - "ver"
- - "status"
- - "stop"
+ # Быстрые команды
+ # (Отображаются при вводе "Начать", "Rcon")
+ fast-commands: ["ver:", "status", "stop"]
-# Заблокированные команды
-blocked-commands:
- - "stop"
+ # Заблокированные команды
+ blocked-commands: ["stop"]
diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml
index b1446bf..f93df06 100644
--- a/src/main/resources/messages.yml
+++ b/src/main/resources/messages.yml
@@ -1,10 +1,15 @@
-USER_ID: "Ваш ID: %s"
-START: "Введите команду или используйте кнопку ниже."
-RCON: "Введите команду для отправки на сервер."
-RCON_WITH_COMMANDS: "Введите команду для отправки на сервер.\n\nБыстрые команды:"
-USER_NOT_WHITELISTED: "Вас нет в списке пользователей, которые могут использовать RCON."
-COMMAND_BLOCKED: "Данная команда заблокирована."
-FAILED_TO_CONNECT: "Не удалось подключится к серверу. Возможно он оффлайн."
-FAILED_TO_AUTHENTICATE: "Не удалось пройти аутентификацию RCON."
-COMMAND_SENDED: "Команда отправлена.\nОтвет сервера: %s"
-RESPONSE_NULL: "Команда отправлена. Сервер не вернул ответа."
\ No newline at end of file
+user_id: "Ваш ID: %s"
+
+start: "Введите команду или используйте кнопку ниже."
+
+rcon: "Введите команду для отправки на сервер."
+rcon_with_commands: "Введите команду для отправки на сервер.\n\nБыстрые команды:"
+
+user_not_whitelisted: "Вас нет в списке пользователей, которые могут использовать RCON."
+
+command_blocked: "Данная команда заблокирована."
+failed_to_connect: "Не удалось подключится к серверу. Возможно он оффлайн."
+failed_to_authenticate: "Не удалось пройти аутентификацию RCON."
+
+command_sended: "Команда отправлена.\nОтвет сервера: %s"
+response_null: "Команда отправлена. Сервер не вернул ответа."
\ No newline at end of file