ggggggg
This commit is contained in:
commit
7e17389465
|
@ -0,0 +1,38 @@
|
||||||
|
# IntelliJ
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# Mac
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Maven
|
||||||
|
log/
|
||||||
|
/target/
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
|
||||||
|
# Java
|
||||||
|
*.jar
|
||||||
|
|
||||||
|
# JRebel
|
||||||
|
rebel.xml
|
||||||
|
|
||||||
|
# Keep libs
|
||||||
|
#!lib/*.jar
|
||||||
|
|
||||||
|
gradle
|
||||||
|
.gradle
|
||||||
|
**/build/
|
||||||
|
!src/**/build/
|
||||||
|
|
||||||
|
# Ignore Gradle GUI config
|
||||||
|
gradle-app.setting
|
||||||
|
|
||||||
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
|
!gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Cache of project
|
||||||
|
.gradletasknamecache
|
||||||
|
|
||||||
|
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||||
|
# gradle/wrapper/gradle-wrapper.properties
|
|
@ -0,0 +1 @@
|
||||||
|
# CoreXD-API
|
|
@ -0,0 +1 @@
|
||||||
|
["test"]
|
|
@ -0,0 +1,94 @@
|
||||||
|
plugins {
|
||||||
|
id 'idea'
|
||||||
|
id 'java'
|
||||||
|
id 'maven-publish'
|
||||||
|
id 'org.jetbrains.kotlin.jvm' version '1.6.0'
|
||||||
|
id 'com.github.johnrengelman.shadow' version '5.2.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'net.evilblock'
|
||||||
|
version '1.0'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
|
||||||
|
maven { url("https://m2.dv8tion.net/releases") }
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.6.0'
|
||||||
|
implementation 'commons-cli:commons-cli:1.4'
|
||||||
|
implementation 'net.evilblock.cubed:serializers:1.2'
|
||||||
|
implementation 'net.evilblock.cubed:store:1.2'
|
||||||
|
implementation 'com.minexd.rift:bukkit:1.2'
|
||||||
|
implementation 'com.google.guava:guava-io:r03'
|
||||||
|
implementation 'com.sparkjava:spark-kotlin:1.0.0-alpha'
|
||||||
|
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||||
|
implementation 'net.dv8tion:JDA:4.3.0_281'
|
||||||
|
implementation("com.neovisionaries:nv-websocket-client:2.14")
|
||||||
|
implementation('com.squareup.okhttp3:okhttp:4.9.3')
|
||||||
|
|
||||||
|
//Opus library support
|
||||||
|
implementation("club.minnced:opus-java:1.1.1")
|
||||||
|
|
||||||
|
//Collections Utility
|
||||||
|
implementation('org.apache.commons:commons-collections4:4.4')
|
||||||
|
|
||||||
|
//we use this only together with opus-java
|
||||||
|
// if that dependency is excluded it also doesn't need jna anymore
|
||||||
|
// since jna is a transitive runtime dependency of opus-java we don't include it explicitly as dependency
|
||||||
|
implementation('net.java.dev.jna:jna:5.9.0')
|
||||||
|
|
||||||
|
/* Internal dependencies */
|
||||||
|
|
||||||
|
//General Utility
|
||||||
|
implementation("net.sf.trove4j:trove4j:3.0.3")
|
||||||
|
implementation('com.fasterxml.jackson.core:jackson-databind:2.13.1')
|
||||||
|
|
||||||
|
implementation group: 'com.amazonaws', name: 'aws-java-sdk-ses', version: '1.11.983'
|
||||||
|
implementation 'org.projectlombok:lombok:1.18.18'
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
|
idea {
|
||||||
|
module {
|
||||||
|
downloadJavadoc = true
|
||||||
|
downloadSources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes(
|
||||||
|
'Main-Class': 'com.minexd.api.API'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
classifier = null
|
||||||
|
exclude '**/*.kotlin_metadata'
|
||||||
|
// exclude '**/*.kotlin_module'
|
||||||
|
exclude '**/*.kotlin_builtins'
|
||||||
|
archiveFileName = "nasa-api.jar"
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
shadow(MavenPublication) { publication ->
|
||||||
|
project.shadow.component(publication)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(Jar) {
|
||||||
|
def home = System.properties['user.home']
|
||||||
|
destinationDirectory = file("$home/Desktop/@Nasa/api/")
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"awsAccessKeyID":"",
|
||||||
|
"awsSecretAccessKey":"",
|
||||||
|
"redisURI":"",
|
||||||
|
"mongoURI":"mongodb://localhost:27017",
|
||||||
|
"mongoDb":"minexd_dev",
|
||||||
|
"discordMainToken":"",
|
||||||
|
"discordMainID":"",
|
||||||
|
"discordStaffToken":"",
|
||||||
|
"discordStaffID":"",
|
||||||
|
"serverName":"",
|
||||||
|
"rank-1":"",
|
||||||
|
"rank-2":"",
|
||||||
|
"rank-3":"",
|
||||||
|
"rank-4":"",
|
||||||
|
"rank-5":"",
|
||||||
|
"rank-6":""
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
kotlin.code.style=official
|
|
@ -0,0 +1,185 @@
|
||||||
|
#!/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=`expr $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"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,89 @@
|
||||||
|
@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 Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@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 execute
|
||||||
|
|
||||||
|
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 execute
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
: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 %*
|
||||||
|
|
||||||
|
: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
|
|
@ -0,0 +1,2 @@
|
||||||
|
rootProject.name = 'minexd-api'
|
||||||
|
|
|
@ -0,0 +1,464 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
import com.amazonaws.auth.AWSStaticCredentialsProvider
|
||||||
|
import com.amazonaws.auth.BasicAWSCredentials
|
||||||
|
import com.amazonaws.regions.Regions
|
||||||
|
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService
|
||||||
|
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder
|
||||||
|
import com.google.common.io.Files
|
||||||
|
import com.minexd.api.console.ConsoleInputScanner
|
||||||
|
import com.minexd.api.logging.APILogger
|
||||||
|
import com.minexd.api.logging.APILoggingFormatter
|
||||||
|
import com.minexd.api.module.audit.AuditAPI
|
||||||
|
import com.minexd.api.module.chattag.TagAPI
|
||||||
|
import com.minexd.api.module.chattag.TagHandler
|
||||||
|
import com.minexd.api.module.chattag.TagRepository
|
||||||
|
import com.minexd.api.module.clan.ClanAPI
|
||||||
|
import com.minexd.api.module.clan.ClanHandler
|
||||||
|
import com.minexd.api.module.clan.ClanRepository
|
||||||
|
import com.minexd.api.module.discord.main.DiscordHandler
|
||||||
|
import com.minexd.api.module.friend.FriendAPI
|
||||||
|
import com.minexd.api.module.friend.FriendHandler
|
||||||
|
import com.minexd.api.module.friend.FriendRepository
|
||||||
|
import com.minexd.api.module.leaderboard.LeaderboardsAPI
|
||||||
|
import com.minexd.api.module.leaderboard.LeaderboardsHandler
|
||||||
|
import com.minexd.api.module.messaging.MessagingAPI
|
||||||
|
import com.minexd.api.module.party.PartyAPI
|
||||||
|
import com.minexd.api.module.party.PartyHandler
|
||||||
|
import com.minexd.api.module.party.PartyRepository
|
||||||
|
import com.minexd.api.module.profile.ProfileAPI
|
||||||
|
import com.minexd.api.module.profile.ProfileHandler
|
||||||
|
import com.minexd.api.module.profile.ProfileRepository
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticAPI
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticsHandler
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticsRepository
|
||||||
|
import com.minexd.api.module.queue.service.QueuePollService
|
||||||
|
import com.minexd.api.module.rank.RankAPI
|
||||||
|
import com.minexd.api.module.rank.RankHandler
|
||||||
|
import com.minexd.api.module.rank.RankRepository
|
||||||
|
import com.minexd.api.module.register.RegisterAPI
|
||||||
|
import com.minexd.api.module.server.ServersAPI
|
||||||
|
import com.minexd.api.module.staff.StaffAPI
|
||||||
|
import com.minexd.api.module.staff.StaffHandler
|
||||||
|
import com.minexd.api.service.ServiceRegistry
|
||||||
|
import com.minexd.api.service.ServicesThread
|
||||||
|
import com.minexd.api.util.JsonTransformer
|
||||||
|
import com.minexd.rift.Rift
|
||||||
|
import com.mongodb.client.MongoDatabase
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import net.evilblock.cubed.serializers.impl.AbstractTypeSerializer
|
||||||
|
import net.evilblock.cubed.store.mongo.Mongo
|
||||||
|
import net.evilblock.cubed.store.redis.Redis
|
||||||
|
import net.evilblock.cubed.store.uuidcache.UUIDCache
|
||||||
|
import net.evilblock.cubed.store.uuidcache.impl.RedisUUIDCache
|
||||||
|
import net.evilblock.cubed.store.uuidcache.listener.UUIDUpdateMessageListeners
|
||||||
|
import net.evilblock.pidgin.Pidgin
|
||||||
|
import net.evilblock.pidgin.PidginOptions
|
||||||
|
import org.apache.commons.cli.DefaultParser
|
||||||
|
import org.apache.commons.cli.Option
|
||||||
|
import org.apache.commons.cli.Options
|
||||||
|
import spark.Request
|
||||||
|
import spark.Response
|
||||||
|
import spark.ResponseTransformer
|
||||||
|
import spark.Spark.*
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.logging.ConsoleHandler
|
||||||
|
import java.util.logging.Level
|
||||||
|
import java.util.logging.Logger
|
||||||
|
import kotlin.concurrent.timer
|
||||||
|
|
||||||
|
object API {
|
||||||
|
|
||||||
|
var debug: Boolean = false
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val portOption = Option.builder("p")
|
||||||
|
.required(false)
|
||||||
|
.hasArg(true)
|
||||||
|
.desc("Which port to listen on")
|
||||||
|
.longOpt("port")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val parser = DefaultParser()
|
||||||
|
|
||||||
|
val options = Options()
|
||||||
|
.addOption(portOption)
|
||||||
|
|
||||||
|
var port = 4567
|
||||||
|
|
||||||
|
try {
|
||||||
|
val commandLine = parser.parse(options, args)
|
||||||
|
|
||||||
|
if (commandLine.hasOption("p")) {
|
||||||
|
port = commandLine.getOptionValue("p").toInt()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
start(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GSON response transformer for returning OOP objects
|
||||||
|
*/
|
||||||
|
private val jsonTransformer: ResponseTransformer = JsonTransformer()
|
||||||
|
|
||||||
|
val logger: Logger = APILogger()
|
||||||
|
val directory: File = File(File(".").canonicalPath)
|
||||||
|
|
||||||
|
lateinit var config: APIConfig
|
||||||
|
|
||||||
|
val redis: Redis = Redis()
|
||||||
|
val mongo: Mongo = Mongo()
|
||||||
|
|
||||||
|
lateinit var sesClient: AmazonSimpleEmailService
|
||||||
|
|
||||||
|
lateinit var mongoDatabase: MongoDatabase
|
||||||
|
lateinit var uuidCache: UUIDCache
|
||||||
|
lateinit var pidgin: Pidgin
|
||||||
|
|
||||||
|
val commandServer: CommandServer = CommandServer()
|
||||||
|
|
||||||
|
var running: Boolean = false
|
||||||
|
var shutdown: Boolean = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
setupLogger()
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(Thread {
|
||||||
|
if (!shutdown) {
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start(port: Int) {
|
||||||
|
val configFile = File("config.json")
|
||||||
|
if (configFile.exists()) {
|
||||||
|
Files.newReader(configFile, Charsets.UTF_8).use { reader ->
|
||||||
|
config = Serializers.gson.fromJson(reader.readLine(), APIConfig::class.java)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config = APIConfig()
|
||||||
|
Files.write(Serializers.gson.toJson(config, APIConfig::class.java), configFile, Charsets.UTF_8)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Initializing API...")
|
||||||
|
|
||||||
|
Serializers.useGsonBuilderThenRebuild { builder ->
|
||||||
|
builder.registerTypeAdapter(SettingOption::class.java, AbstractTypeSerializer<SettingOption<*>>())
|
||||||
|
}
|
||||||
|
|
||||||
|
APIKeys.read()
|
||||||
|
|
||||||
|
sesClient = AmazonSimpleEmailServiceClientBuilder.standard()
|
||||||
|
.withRegion(Regions.US_EAST_2)
|
||||||
|
.withCredentials(
|
||||||
|
AWSStaticCredentialsProvider(
|
||||||
|
BasicAWSCredentials(
|
||||||
|
config.awsAccessKeyID,
|
||||||
|
config.awsSecretAccessKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
setupRedis()
|
||||||
|
setupMongoDB()
|
||||||
|
|
||||||
|
pidgin = Pidgin("CoreXD", redis.jedisPool!!, Serializers.gson, PidginOptions(async = true))
|
||||||
|
|
||||||
|
setupRepositories()
|
||||||
|
setupHandlers()
|
||||||
|
|
||||||
|
pidgin.registerListener(UUIDUpdateMessageListeners(uuidCache)) // register after repos & caches initialized
|
||||||
|
|
||||||
|
Rift(RiftImpl()).initialLoad()
|
||||||
|
ServiceRegistry.register(QueuePollService, 5L, 5L)
|
||||||
|
|
||||||
|
port(port)
|
||||||
|
logger.info("Configured listener to port $port")
|
||||||
|
|
||||||
|
setupRoutes()
|
||||||
|
setupExceptionHandling()
|
||||||
|
|
||||||
|
running = true
|
||||||
|
|
||||||
|
commandServer.start()
|
||||||
|
ServicesThread().start()
|
||||||
|
|
||||||
|
if (!config.discordMainToken.isEmpty()) {
|
||||||
|
DiscordHandler.init()
|
||||||
|
logger.info("Started Discord-Bot")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.discordStaffToken.isEmpty()) {
|
||||||
|
com.minexd.api.module.discord.staff.StaffHandler.init()
|
||||||
|
logger.info("Started Staff-Discord-Bot")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Now accepting requests and console input!")
|
||||||
|
ConsoleInputScanner().start() // scanner is blocking, so do this last
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
running = false
|
||||||
|
spark.Spark.stop()
|
||||||
|
redis.close()
|
||||||
|
mongo.close()
|
||||||
|
pidgin.close()
|
||||||
|
shutdown = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reload() {
|
||||||
|
RankHandler.load()
|
||||||
|
TagHandler.load()
|
||||||
|
StaffHandler.load()
|
||||||
|
ProfileHandler.load()
|
||||||
|
|
||||||
|
logger.info("Successfully reloaded the API server!")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupLogger() {
|
||||||
|
val handler = ConsoleHandler()
|
||||||
|
handler.level = Level.ALL
|
||||||
|
handler.formatter = APILoggingFormatter()
|
||||||
|
|
||||||
|
logger.addHandler(handler)
|
||||||
|
logger.useParentHandlers = false
|
||||||
|
logger.level = Level.ALL
|
||||||
|
logger.useParentHandlers = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRedis() {
|
||||||
|
logger.info("Connecting to Redis...")
|
||||||
|
redis.connect(config.redisURI)
|
||||||
|
logger.info("Connected to Redis!")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupMongoDB() {
|
||||||
|
logger.info("Connecting to MongoDB...")
|
||||||
|
|
||||||
|
mongo.connect(config.mongoURI)
|
||||||
|
mongoDatabase = mongo.client.getDatabase(config.mongoDb)
|
||||||
|
|
||||||
|
logger.info("Connected to MongoDB!")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRepositories() {
|
||||||
|
logger.info("Initializing repositories...")
|
||||||
|
|
||||||
|
RankRepository.initialize()
|
||||||
|
FriendRepository.initialize()
|
||||||
|
StatisticsRepository.initialize()
|
||||||
|
ProfileRepository.initialize()
|
||||||
|
PartyRepository.initialize()
|
||||||
|
ClanRepository.initialize()
|
||||||
|
TagRepository.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupHandlers() {
|
||||||
|
logger.info("Initializing caches...")
|
||||||
|
|
||||||
|
uuidCache = RedisUUIDCache(redis)
|
||||||
|
uuidCache.alwaysFetch = true
|
||||||
|
uuidCache.load()
|
||||||
|
|
||||||
|
ClanHandler.initialLoad()
|
||||||
|
PartyHandler.initialLoad()
|
||||||
|
RankHandler.load()
|
||||||
|
StaffHandler.load()
|
||||||
|
ProfileHandler.load()
|
||||||
|
LeaderboardsHandler.load()
|
||||||
|
FriendHandler.load()
|
||||||
|
StatisticsHandler.load()
|
||||||
|
TagHandler.load()
|
||||||
|
|
||||||
|
timer(initialDelay = TimeUnit.MINUTES.toMillis(2L), period = TimeUnit.MINUTES.toMillis(2L)) {
|
||||||
|
uuidCache.load()
|
||||||
|
|
||||||
|
RankHandler.load()
|
||||||
|
StaffHandler.load()
|
||||||
|
ProfileHandler.load()
|
||||||
|
LeaderboardsHandler.load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRoutes() {
|
||||||
|
logger.info("Building routes...")
|
||||||
|
|
||||||
|
// TODO: eventually change to annotation based dynamic routing, must be smart and build all routes
|
||||||
|
path("/api") {
|
||||||
|
before("/*") { request, response ->
|
||||||
|
handleRequest(request, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/servers") {
|
||||||
|
get("/player-count", ServersAPI.playerCount)
|
||||||
|
post("/lookup", ServersAPI.lookup)
|
||||||
|
post("/status", ServersAPI.serversStatus, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/ranks") {
|
||||||
|
get("/list", RankAPI.list, jsonTransformer)
|
||||||
|
get("/get/:id", RankAPI.get, jsonTransformer)
|
||||||
|
get("/get/:id/test", RankAPI.test, jsonTransformer)
|
||||||
|
post("/create/:id", RankAPI.create, jsonTransformer)
|
||||||
|
post("/delete/:id", RankAPI.delete, jsonTransformer)
|
||||||
|
post("/update/:id", RankAPI.update, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/tags") {
|
||||||
|
get("/list", TagAPI.list, jsonTransformer)
|
||||||
|
get("/get/:id", TagAPI.get, jsonTransformer)
|
||||||
|
post("/create/:id", TagAPI.create, jsonTransformer)
|
||||||
|
post("/update/:id", TagAPI.update, jsonTransformer)
|
||||||
|
post("/delete/:id", TagAPI.delete, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/audit") {
|
||||||
|
get("/grants", AuditAPI.getGrants, jsonTransformer)
|
||||||
|
get("/punishments", AuditAPI.getPunishments, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/staff") {
|
||||||
|
get("/list", StaffAPI.list, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/famous") {
|
||||||
|
get("/list",ProfileAPI.famousList, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/leaderboards", LeaderboardsAPI.byGame, jsonTransformer)
|
||||||
|
|
||||||
|
post("/register", RegisterAPI.register)
|
||||||
|
post("/register/complete", RegisterAPI.complete, jsonTransformer)
|
||||||
|
|
||||||
|
path("/profile") {
|
||||||
|
get("/:uuid", ProfileAPI.get, jsonTransformer)
|
||||||
|
|
||||||
|
path("/:uuid") {
|
||||||
|
get("/touch", ProfileAPI.touch, jsonTransformer)
|
||||||
|
post("/login", ProfileAPI.login, jsonTransformer)
|
||||||
|
post("/grant", ProfileAPI.grant)
|
||||||
|
post("/revokeGrant", ProfileAPI.revokeGrant)
|
||||||
|
post("/punish", ProfileAPI.punish)
|
||||||
|
post("/pardon", ProfileAPI.pardon)
|
||||||
|
post("/setTag", ProfileAPI.setTag)
|
||||||
|
|
||||||
|
path("/permissions") {
|
||||||
|
post("/add", ProfileAPI.addPermission)
|
||||||
|
post("/revoke", ProfileAPI.revokePermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/settings/update", ProfileAPI.updateSettings)
|
||||||
|
|
||||||
|
get("/presence", ProfileAPI.getPresence, jsonTransformer)
|
||||||
|
get("/suspensionInfo", ProfileAPI.getSuspensionInfo, jsonTransformer)
|
||||||
|
get("/sharedAccounts", ProfileAPI.getSharedAccounts, jsonTransformer)
|
||||||
|
|
||||||
|
post("/countVote", ProfileAPI.countVote)
|
||||||
|
post("/startSync", ProfileAPI.startSync)
|
||||||
|
post("/finishSync", ProfileAPI.finishSync)
|
||||||
|
post("/resetRegistration", RegisterAPI.reset)
|
||||||
|
post("/resetSync", ProfileAPI.resetSync)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/sync") {
|
||||||
|
get("/lookupCode", ProfileAPI.findBySyncCode, jsonTransformer)
|
||||||
|
get("/lookupAccount", ProfileAPI.findBySyncedAccount, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/friendships") {
|
||||||
|
get("/list", FriendAPI.listFriendships)
|
||||||
|
get("/incoming", FriendAPI.getIncoming)
|
||||||
|
get("/outgoing", FriendAPI.getOutgoing)
|
||||||
|
get("/show", FriendAPI.show)
|
||||||
|
post("/create", FriendAPI.create)
|
||||||
|
post("/destroy", FriendAPI.destroy)
|
||||||
|
post("/update", FriendAPI.update)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/stats") {
|
||||||
|
get("/:uuid", StatisticAPI.quickStats, jsonTransformer)
|
||||||
|
get("/:uuid/:game", StatisticAPI.gameStats, jsonTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/clans") {
|
||||||
|
get("/find/:search", ClanAPI.find, jsonTransformer)
|
||||||
|
post("/create", ClanAPI.create, jsonTransformer)
|
||||||
|
post("/disband", ClanAPI.disband, jsonTransformer)
|
||||||
|
post("/kick", ClanAPI.kick, jsonTransformer)
|
||||||
|
post("/invites/create", ClanAPI.createInvite)
|
||||||
|
post("/invites/destroy", ClanAPI.destroyInvite)
|
||||||
|
post("/invites/accept", ClanAPI.acceptInvite)
|
||||||
|
post("/update", ClanAPI.update)
|
||||||
|
post("/chat", ClanAPI.chat)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/party") {
|
||||||
|
get("/find", PartyAPI.find, jsonTransformer)
|
||||||
|
post("/create", PartyAPI.create, jsonTransformer)
|
||||||
|
post("/disband", PartyAPI.disband)
|
||||||
|
post("/update", PartyAPI.update)
|
||||||
|
post("/invites/create", PartyAPI.createInvite)
|
||||||
|
post("/invites/destroy", PartyAPI.destroyInvite)
|
||||||
|
post("/invites/accept", PartyAPI.acceptInvite)
|
||||||
|
post("/kick", PartyAPI.kick, jsonTransformer)
|
||||||
|
post("/leave", PartyAPI.leave, jsonTransformer)
|
||||||
|
post("/chat", PartyAPI.chat)
|
||||||
|
}
|
||||||
|
|
||||||
|
path("/messaging") {
|
||||||
|
post("/message", MessagingAPI.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleRequest(request: Request, response: Response) {
|
||||||
|
response.type("application/json")
|
||||||
|
|
||||||
|
val apiKey = request.headers("X-API-Key")
|
||||||
|
if (apiKey == null) {
|
||||||
|
halt(401, "Missing API key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: maybe switch to a database instead of flat-file lol
|
||||||
|
if (!APIKeys.keys.contains(apiKey)) {
|
||||||
|
println("Un-authorized API request: $apiKey")
|
||||||
|
halt(401, "Unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.contentType() != null && request.contentType() != "application/json" && request.contentType() != "application/json; charset=UTF-8") {
|
||||||
|
println("Malformed content-type")
|
||||||
|
halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupExceptionHandling() {
|
||||||
|
after("/*") { request, response ->
|
||||||
|
println(response.body())
|
||||||
|
logger.info(
|
||||||
|
response.status()
|
||||||
|
.toString() + " " + request.requestMethod() + " " + request.url() + "/" + request.servletPath()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
exception(java.lang.Exception::class.java) { exception, request, response ->
|
||||||
|
logger.info("ERROR 500 " + request.pathInfo())
|
||||||
|
exception.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
exception(Exception::class.java) { exception, request, response ->
|
||||||
|
logger.info("ERROR 500 " + request.pathInfo())
|
||||||
|
exception.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
class APIConfig {
|
||||||
|
|
||||||
|
val awsAccessKeyID = "AKIAY55EPDLCSRD7LV4X"
|
||||||
|
val awsSecretAccessKey = "KP5kHeve+k1VPTOa8nwZu3e032emq6QOoBB/Fkvc"
|
||||||
|
|
||||||
|
val redisURI: String = "redis://localhost:6379?db=0"
|
||||||
|
|
||||||
|
val mongoURI: String = "mongodb://localhost:27017"
|
||||||
|
val mongoDb: String = "minexd_dev"
|
||||||
|
|
||||||
|
val discordMainToken: String = ""
|
||||||
|
val discordStaffToken: String = ""
|
||||||
|
val discordMainID: String = ""
|
||||||
|
val discordStaffID: String = ""
|
||||||
|
val serverName: String = ""
|
||||||
|
|
||||||
|
val rank1: String = ""
|
||||||
|
val rank2: String = ""
|
||||||
|
val rank3: String = ""
|
||||||
|
val rank4: String = ""
|
||||||
|
val rank5: String = ""
|
||||||
|
val rank6: String = ""
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
import com.google.common.io.Files
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
object APIKeys {
|
||||||
|
|
||||||
|
val keys: MutableSet<String> = ConcurrentHashMap.newKeySet()
|
||||||
|
|
||||||
|
fun read() {
|
||||||
|
val file = File("api-keys.json")
|
||||||
|
if (!file.exists()) {
|
||||||
|
Files.write("[]".toByteArray(Charsets.UTF_8), file)
|
||||||
|
} else {
|
||||||
|
keys.addAll(Files.newReader(file, Charsets.UTF_8)
|
||||||
|
.use { reader -> Serializers.gson.fromJson(reader.readLine(), JsonArray::class.java) }
|
||||||
|
.map { it.asString })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
import com.minexd.api.module.profile.ProfileHandler
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
|
class CommandServer : Thread("Command-Server") {
|
||||||
|
|
||||||
|
val commandQueue: Queue<String> = ConcurrentLinkedQueue<String>()
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
while (API.running) {
|
||||||
|
try {
|
||||||
|
tickCommands()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(50L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tickCommands() {
|
||||||
|
if (commandQueue.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var command: String?
|
||||||
|
while (commandQueue.poll().also { command = it } != null) {
|
||||||
|
API.logger.info("[Console] $command")
|
||||||
|
|
||||||
|
when {
|
||||||
|
command.equals("help", ignoreCase = true) -> {
|
||||||
|
API.logger.info("/help")
|
||||||
|
API.logger.info("/reload")
|
||||||
|
API.logger.info("/stop")
|
||||||
|
API.logger.info("/clear-cache")
|
||||||
|
}
|
||||||
|
command.equals("stop", ignoreCase = true) -> {
|
||||||
|
API.stop()
|
||||||
|
}
|
||||||
|
command.equals("reload", ignoreCase = true) -> {
|
||||||
|
API.reload()
|
||||||
|
}
|
||||||
|
command.equals("cache info", ignoreCase = true) -> {
|
||||||
|
val profiles = ProfileHandler.getLoadedProfiles().size
|
||||||
|
API.logger.info("There are $profiles loaded profiles")
|
||||||
|
}
|
||||||
|
command.equals("cache clear", ignoreCase = true) -> {
|
||||||
|
val profiles = ProfileHandler.getLoadedProfiles().size
|
||||||
|
ProfileHandler.clearLoadedProfiles()
|
||||||
|
API.logger.info("Unloaded $profiles profiles")
|
||||||
|
}
|
||||||
|
command.equals("debug on", ignoreCase = true) -> {
|
||||||
|
API.debug = true
|
||||||
|
API.logger.info("Debug enabled")
|
||||||
|
}
|
||||||
|
command.equals("debug off", ignoreCase = true) -> {
|
||||||
|
API.debug = false
|
||||||
|
API.logger.info("Debug disabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
object Constants {
|
||||||
|
|
||||||
|
const val COLOR_CHAR = '§'
|
||||||
|
const val CONSOLE_NAME = "${COLOR_CHAR}4${COLOR_CHAR}lConsole"
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
class DynamicRoute() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
interface Repository {
|
||||||
|
|
||||||
|
fun initialize()
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.minexd.api
|
||||||
|
|
||||||
|
import com.minexd.rift.plugin.Plugin
|
||||||
|
import net.evilblock.cubed.store.redis.Redis
|
||||||
|
import java.io.File
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
class RiftImpl : Plugin {
|
||||||
|
|
||||||
|
override fun getDirectory(): File {
|
||||||
|
return API.directory
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLogger(): Logger {
|
||||||
|
return API.logger
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRedis(): Redis {
|
||||||
|
return API.redis
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasPresence(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getInstanceID(): String {
|
||||||
|
throw IllegalStateException("Not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.minexd.api.console
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ConsoleInputScanner {
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
val scanner = Scanner(System.`in`)
|
||||||
|
while (API.running) {
|
||||||
|
try {
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
val command = scanner.nextLine()
|
||||||
|
API.commandServer.commandQueue.add(command)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.sleep(50L)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Console Input Scanner finished")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.minexd.api.logging
|
||||||
|
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
class APILogger : Logger("API", null) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.minexd.api.logging
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
import java.util.logging.Formatter
|
||||||
|
import java.util.logging.LogRecord
|
||||||
|
|
||||||
|
class APILoggingFormatter : Formatter() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PREFIX = "[API]"
|
||||||
|
private val DT_FORMAT: SimpleDateFormat = SimpleDateFormat("MM/dd/yyyy HH:mm:ss")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun format(record: LogRecord): String {
|
||||||
|
return buildString {
|
||||||
|
append("[" + formatTime() + "]")
|
||||||
|
append(" ")
|
||||||
|
append("(" + record.level.name + ")")
|
||||||
|
append(": ")
|
||||||
|
append(PREFIX)
|
||||||
|
append(" ")
|
||||||
|
append(record.message)
|
||||||
|
append("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatTime(): String {
|
||||||
|
return DT_FORMAT.format(Date.from(Instant.now()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.minexd.api.module.audit
|
||||||
|
|
||||||
|
import com.minexd.api.module.profile.ProfileRepository
|
||||||
|
import spark.Route
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object AuditAPI {
|
||||||
|
|
||||||
|
val getGrants: Route = Route { request, response ->
|
||||||
|
val id = UUID.fromString(request.queryParams("id"))
|
||||||
|
return@Route ProfileRepository.findGrantsIssuedBy(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
val getPunishments: Route = Route { request, response ->
|
||||||
|
val id = UUID.fromString(request.queryParams("id"))
|
||||||
|
return@Route ProfileRepository.findPunishmentsIssuedBy(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.minexd.api.module.chattag
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Missionary (missionarymc@gmail.com)
|
||||||
|
* @since 7/20/2021
|
||||||
|
*
|
||||||
|
* Data class (POJO) that backs Tags, this is shared w/ CoreXD
|
||||||
|
*/
|
||||||
|
class Tag(var name: String) {
|
||||||
|
|
||||||
|
var content: String = "[SET_ME]"
|
||||||
|
var tagLocation: TagLocation = TagLocation.AFTER
|
||||||
|
|
||||||
|
internal fun handleUpdate(otherTag: Tag) {
|
||||||
|
this.name = otherTag.name
|
||||||
|
this.content = otherTag.content
|
||||||
|
this.tagLocation = otherTag.tagLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Tag
|
||||||
|
|
||||||
|
if (name != other.name) return false
|
||||||
|
if (content != other.content) return false
|
||||||
|
if (tagLocation != other.tagLocation) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = name.hashCode()
|
||||||
|
result = 31 * result + content.hashCode()
|
||||||
|
result = 31 * result + tagLocation.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
package com.minexd.api.module.chattag
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.rank.RankHandler
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
import spark.Route
|
||||||
|
import spark.kotlin.halt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Missionary (missionarymc@gmail.com)
|
||||||
|
* @since 7/20/2021
|
||||||
|
*/
|
||||||
|
object TagAPI {
|
||||||
|
|
||||||
|
// TODO: 7/20/2021 send pidgin update messages when updates occur so other servers can notice
|
||||||
|
|
||||||
|
val list = Route { request, response ->
|
||||||
|
return@Route TagHandler.getTags().sortedBy { it.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
val get = Route { request, response ->
|
||||||
|
return@Route RankHandler.getRankById(request.params(":id"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val create = Route { request, response ->
|
||||||
|
val name = request.params(":id")
|
||||||
|
|
||||||
|
if (TagHandler.getByName(name) != null) {
|
||||||
|
return@Route halt(400, "A \'$name\' tag already exists!")
|
||||||
|
}
|
||||||
|
|
||||||
|
val body = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
|
||||||
|
val tag = Tag(name)
|
||||||
|
|
||||||
|
if (body.has("content")) {
|
||||||
|
val content = body["content"].asString
|
||||||
|
tag.content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.has("tagLocation")) {
|
||||||
|
val tagLocationString = body["tagLocation"].asString
|
||||||
|
|
||||||
|
var tagLocation: TagLocation = TagLocation.AFTER
|
||||||
|
try {
|
||||||
|
tagLocation = TagLocation.valueOf(tagLocationString)
|
||||||
|
} catch (ex: IllegalArgumentException) {
|
||||||
|
return@Route halt(404, "Not a valid tag location")
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.tagLocation = tagLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
TagHandler.cache(tag)
|
||||||
|
TagRepository.save(tag)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "TagUpdate",
|
||||||
|
data = mapOf(
|
||||||
|
"TagID" to tag.name,
|
||||||
|
"Update" to TagUpdate.UPDATE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return@Route tag
|
||||||
|
}
|
||||||
|
|
||||||
|
val delete = Route { request, response ->
|
||||||
|
val tag = TagHandler.getByName(request.params(":id")) ?: return@Route halt(404)
|
||||||
|
|
||||||
|
TagHandler.forget(tag)
|
||||||
|
TagRepository.delete(tag)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "TagUpdate",
|
||||||
|
data = mapOf(
|
||||||
|
"TagID" to tag.name,
|
||||||
|
"Update" to TagUpdate.DELETE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return@Route tag
|
||||||
|
}
|
||||||
|
|
||||||
|
val update = Route { request, response ->
|
||||||
|
val tag = TagHandler.getByName(request.params(":id")) ?: return@Route halt(404)
|
||||||
|
|
||||||
|
var updated = false
|
||||||
|
|
||||||
|
val body = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
|
||||||
|
if (body.has("content")) {
|
||||||
|
val newBody = body["content"].asString
|
||||||
|
tag.content = newBody
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.has("tagLocation")) {
|
||||||
|
val newPossTagLoc = body["tagLocation"].asString
|
||||||
|
var tagLoc: TagLocation = TagLocation.AFTER
|
||||||
|
|
||||||
|
try {
|
||||||
|
tagLoc = TagLocation.valueOf(newPossTagLoc)
|
||||||
|
} catch (ex: IllegalArgumentException) {
|
||||||
|
return@Route halt(404, "Not a valid tag location")
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.tagLocation = tagLoc
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
TagHandler.cache(tag)
|
||||||
|
TagRepository.save(tag)
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "TagUpdate",
|
||||||
|
data = mapOf(
|
||||||
|
"TagID" to tag.name,
|
||||||
|
"Update" to TagUpdate.UPDATE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
response.status(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route tag
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.minexd.api.module.chattag
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Missionary (missionarymc@gmail.com)
|
||||||
|
* @since 7/20/2021
|
||||||
|
*/
|
||||||
|
object TagHandler {
|
||||||
|
|
||||||
|
private val tags = ConcurrentHashMap<String, Tag>()
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
val loadedTags = TagRepository.load()
|
||||||
|
for (tag in loadedTags) {
|
||||||
|
val existing = getByName(tag.name)
|
||||||
|
if (existing != null) {
|
||||||
|
existing.handleUpdate(tag)
|
||||||
|
} else {
|
||||||
|
cache(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cache(tag: Tag) {
|
||||||
|
tags[tag.name.lowercase()] = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getByName(name: String?): Tag? {
|
||||||
|
if (name.isNullOrEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
for (tag in tags.values) {
|
||||||
|
if (tag.name.equals(name, ignoreCase = true)) {
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTags(): Collection<Tag> {
|
||||||
|
return tags.values
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forget(tag: Tag) {
|
||||||
|
tags.remove(tag.name.lowercase())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.minexd.api.module.chattag
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Missionary (missionarymc@gmail.com)
|
||||||
|
* @since 7/20/2021
|
||||||
|
*/
|
||||||
|
enum class TagLocation {
|
||||||
|
BEFORE, // typically before the rank & display name (e.g., [TAG] [RANK_NAME] Username: Message)
|
||||||
|
AFTER // (e.g., [RANK_NAME] Username [TAG]: Message)
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.minexd.api.module.chattag
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.Repository
|
||||||
|
import com.mongodb.client.MongoCollection
|
||||||
|
import com.mongodb.client.model.Filters
|
||||||
|
import com.mongodb.client.model.ReplaceOptions
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import org.bson.Document
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Missionary (missionarymc@gmail.com)
|
||||||
|
* @since 7/20/2021
|
||||||
|
*/
|
||||||
|
object TagRepository : Repository {
|
||||||
|
|
||||||
|
private val TAG_TYPE: Type = object : TypeToken<Tag>() {}.type
|
||||||
|
|
||||||
|
private lateinit var tagsCollection: MongoCollection<Document>
|
||||||
|
|
||||||
|
override fun initialize() {
|
||||||
|
tagsCollection = API.mongoDatabase.getCollection("chattags")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun load(): Set<Tag> {
|
||||||
|
return mutableSetOf<Tag>().also { tags ->
|
||||||
|
val documents = tagsCollection.find()
|
||||||
|
for (document in documents) {
|
||||||
|
tags.add(deserialize(document))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findTagByName(name: String): Tag? {
|
||||||
|
return deserialize(tagsCollection.find(Filters.eq("name", name)).first() ?: return null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save(tag: Tag) {
|
||||||
|
tagsCollection.replaceOne(
|
||||||
|
Filters.eq("name", tag.name),
|
||||||
|
Document.parse(Serializers.gson.toJson(tag, TAG_TYPE)),
|
||||||
|
ReplaceOptions().upsert(true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun delete(tag: Tag) {
|
||||||
|
tagsCollection.deleteOne(Filters.eq("name", tag.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserialize(document: Document): Tag {
|
||||||
|
return Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), TAG_TYPE) as Tag
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.minexd.api.module.chattag
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Missionary (missionarymc@gmail.com)
|
||||||
|
* @since 7/20/2021
|
||||||
|
*/
|
||||||
|
enum class TagUpdate {
|
||||||
|
UPDATE,
|
||||||
|
DELETE
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.minexd.api.module.clan
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class Clan(
|
||||||
|
val id: UUID = UUID.randomUUID(),
|
||||||
|
var name: String,
|
||||||
|
var leader: UUID
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When this clan was created.
|
||||||
|
*/
|
||||||
|
val createdAt: Long = System.currentTimeMillis()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password to join without being invited.
|
||||||
|
*/
|
||||||
|
var password: String? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This clan's tag, which is a shorthand or abbreviation that represents this clan.
|
||||||
|
*/
|
||||||
|
var tag: String? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This clan's MOTD (message of the day).
|
||||||
|
*/
|
||||||
|
var motd: String = "This is the default description..."
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The members that form this clan.
|
||||||
|
*/
|
||||||
|
internal val members: MutableMap<UUID, ClanMember> = hashMapOf()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invitations sent to players to join this clan.
|
||||||
|
*/
|
||||||
|
internal val invites: MutableMap<UUID, ClanInvite> = hashMapOf()
|
||||||
|
|
||||||
|
fun isMember(player: UUID): Boolean {
|
||||||
|
return members.containsKey(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMember(player: UUID): ClanMember? {
|
||||||
|
return members[player]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasInvite(player: UUID): Boolean {
|
||||||
|
if (invites.containsKey(player)) {
|
||||||
|
val invite = invites[player]!!
|
||||||
|
if (System.currentTimeMillis() - invite.createdAt <= 86_400_000L) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
invites.remove(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInvite(player: UUID): ClanInvite? {
|
||||||
|
return invites[player]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addInvite(invite: ClanInvite) {
|
||||||
|
invites[invite.player] = invite
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeInvite(player: UUID) {
|
||||||
|
invites.remove(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val NAME_REGEX = Regex("^[-a-zA-Z0-9]+")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.minexd.api.module.clan
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.clan.result.CreateError
|
||||||
|
import com.minexd.api.module.party.PartyHandler
|
||||||
|
import spark.Route
|
||||||
|
import spark.kotlin.halt
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object ClanAPI {
|
||||||
|
|
||||||
|
val find: Route = Route { request, response ->
|
||||||
|
val search = request.params(":search")
|
||||||
|
|
||||||
|
try {
|
||||||
|
val uuid = UUID.fromString(search)
|
||||||
|
|
||||||
|
val clan = ClanHandler.getClanById(uuid) ?: ClanHandler.getClanByPlayer(uuid)
|
||||||
|
if (clan != null) {
|
||||||
|
return@Route clan
|
||||||
|
}
|
||||||
|
} catch (e: Exception) { }
|
||||||
|
|
||||||
|
var clan: Clan? = ClanHandler.getClanByName(search)
|
||||||
|
if (clan == null) {
|
||||||
|
val playerUUID = API.uuidCache.uuid(search)
|
||||||
|
if (playerUUID != null) {
|
||||||
|
clan = ClanHandler.getClanByPlayer(playerUUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route clan ?: halt(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
val create: Route = Route { request, response ->
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
if (PartyHandler.getPartyByPlayer(playerUUID) != null) {
|
||||||
|
return@Route halt(400, com.minexd.api.module.party.result.CreateError.ALREADY_IN_PARTY.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = request.body()
|
||||||
|
if (name.isEmpty() || name.length > 12 || !name.matches(Clan.NAME_REGEX)) {
|
||||||
|
return@Route halt(400, CreateError.NAME_INVALID.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClanHandler.getClanByName(name) != null) {
|
||||||
|
return@Route halt(400, CreateError.NAME_TAKEN.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClanHandler.getClanByPlayer(playerUUID) != null) {
|
||||||
|
return@Route halt(400, CreateError.ALREADY_IN_CLAN.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val clan = Clan(name = name, leader = playerUUID)
|
||||||
|
clan.members[playerUUID] = ClanMember(playerUUID, ClanMember.Role.LEADER)
|
||||||
|
|
||||||
|
ClanHandler.cache(clan)
|
||||||
|
ClanRepository.saveClan(clan)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route clan
|
||||||
|
}
|
||||||
|
|
||||||
|
val disband: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val kick: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val createInvite: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val destroyInvite: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val acceptInvite: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val update: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val chat: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.minexd.api.module.clan
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
object ClanHandler {
|
||||||
|
|
||||||
|
private val clanById: MutableMap<UUID, Clan> = ConcurrentHashMap()
|
||||||
|
private val clanByName: MutableMap<String, Clan> = ConcurrentHashMap()
|
||||||
|
private val clanByPlayer: MutableMap<UUID, Clan> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
fun initialLoad() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClanById(id: UUID): Clan? {
|
||||||
|
return clanById[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClanByName(name: String): Clan? {
|
||||||
|
return clanByName[name.toLowerCase()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getClanByPlayer(player: UUID): Clan? {
|
||||||
|
return clanByPlayer[player]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cache(clan: Clan) {
|
||||||
|
clanById[clan.id] = clan
|
||||||
|
clanByName[clan.name.toLowerCase()] = clan
|
||||||
|
|
||||||
|
for (member in clan.members.keys) {
|
||||||
|
clanByPlayer[member] = clan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forget(clan: Clan) {
|
||||||
|
clanById.remove(clan.id)
|
||||||
|
clanByName.remove(clan.name.toLowerCase())
|
||||||
|
|
||||||
|
for (member in clan.members.keys) {
|
||||||
|
clanByPlayer.remove(member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rename(clan: Clan, newName: String) {
|
||||||
|
clanByName.remove(clan.name.toLowerCase())
|
||||||
|
clan.name = newName
|
||||||
|
clanByName[clan.name.toLowerCase()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cache(player: UUID, clan: Clan) {
|
||||||
|
clanByPlayer[player] = clan
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forget(player: UUID) {
|
||||||
|
clanByPlayer.remove(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.minexd.api.module.clan
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class ClanInvite(val player: UUID, var invitedBy: UUID? = null, val createdAt: Long = System.currentTimeMillis())
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.minexd.api.module.clan
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class ClanMember(val uuid: UUID = UUID.randomUUID(), var role: Role) {
|
||||||
|
|
||||||
|
val createdAt: Long = System.currentTimeMillis()
|
||||||
|
|
||||||
|
constructor(uuid: UUID) : this(uuid, Role.MEMBER)
|
||||||
|
|
||||||
|
enum class Role(val displayName: String) {
|
||||||
|
LEADER("***"),
|
||||||
|
CAPTAIN("**"),
|
||||||
|
OFFICER("*"),
|
||||||
|
MEMBER("");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.minexd.api.module.clan
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.Repository
|
||||||
|
import com.mongodb.BasicDBObject
|
||||||
|
import com.mongodb.client.MongoCollection
|
||||||
|
import com.mongodb.client.model.Collation
|
||||||
|
import com.mongodb.client.model.CollationStrength
|
||||||
|
import com.mongodb.client.model.IndexOptions
|
||||||
|
import com.mongodb.client.model.ReplaceOptions
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import org.bson.Document
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object ClanRepository : Repository {
|
||||||
|
|
||||||
|
private val CLAN_TYPE = object : TypeToken<Clan>() {}.type
|
||||||
|
|
||||||
|
private lateinit var clansCollection: MongoCollection<Document>
|
||||||
|
|
||||||
|
override fun initialize() {
|
||||||
|
clansCollection = API.mongoDatabase.getCollection("clans")
|
||||||
|
clansCollection.createIndex(BasicDBObject("id", 1))
|
||||||
|
clansCollection.createIndex(BasicDBObject("name", 1), IndexOptions().collation(Collation.builder()
|
||||||
|
.locale("en")
|
||||||
|
.collationStrength(CollationStrength.SECONDARY)
|
||||||
|
.build())) // case insensitive index for name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findClan(id: UUID): Clan? {
|
||||||
|
val document = clansCollection.find(Document("id", id.toString())).first() ?: return null
|
||||||
|
return Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), CLAN_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findClan(name: String): Clan? {
|
||||||
|
val document = clansCollection.find(Document("name", name)).first() ?: return null
|
||||||
|
return Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), CLAN_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveClan(clan: Clan) {
|
||||||
|
val document = Document.parse(Serializers.gson.toJson(clan, CLAN_TYPE))
|
||||||
|
clansCollection.replaceOne(Document("id", clan.id.toString()), document, ReplaceOptions().upsert(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.minexd.api.module.clan.result
|
||||||
|
|
||||||
|
enum class CreateError {
|
||||||
|
|
||||||
|
NAME_INVALID,
|
||||||
|
NAME_TAKEN,
|
||||||
|
ALREADY_IN_CLAN,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.minexd.api.module.discord.main
|
||||||
|
|
||||||
|
import com.minexd.api.API.config
|
||||||
|
import com.minexd.api.module.discord.main.listener.JoinEventListener
|
||||||
|
import com.minexd.api.module.discord.main.listener.SyncChannelListener
|
||||||
|
import com.minexd.api.module.profile.Profile
|
||||||
|
import net.dv8tion.jda.api.JDA
|
||||||
|
import net.dv8tion.jda.api.JDABuilder
|
||||||
|
import net.dv8tion.jda.api.entities.Guild
|
||||||
|
import net.dv8tion.jda.api.entities.Member
|
||||||
|
import net.dv8tion.jda.api.requests.GatewayIntent
|
||||||
|
|
||||||
|
object DiscordHandler
|
||||||
|
{
|
||||||
|
lateinit var jda: JDA
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
jda = JDABuilder.createDefault(config.discordMainToken).enableIntents(
|
||||||
|
GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_VOICE_STATES).build()
|
||||||
|
|
||||||
|
jda.addEventListener(SyncChannelListener)
|
||||||
|
jda.addEventListener(JoinEventListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGuild() : Guild {
|
||||||
|
return jda.getGuildById(config.discordMainID)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMember(profile: Profile) : Member? {
|
||||||
|
return getGuild().getMemberById(profile.syncedAccount!!)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.minexd.api.module.discord.main.listener
|
||||||
|
|
||||||
|
import com.minexd.api.API.config
|
||||||
|
import com.minexd.api.APIConfig
|
||||||
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
|
|
||||||
|
object JoinEventListener : ListenerAdapter()
|
||||||
|
{
|
||||||
|
|
||||||
|
override fun onGuildMemberJoin(event: GuildMemberJoinEvent) {
|
||||||
|
val guild = event.guild
|
||||||
|
val channels = guild.getTextChannelsByName("welcome", true)
|
||||||
|
if (channels.isEmpty()) return
|
||||||
|
val member = event.member
|
||||||
|
val serverName = config.serverName
|
||||||
|
guild.addRoleToMember(member, guild.getRolesByName("Member", true)[0]).queue()
|
||||||
|
val welcomeChannel = channels[0]
|
||||||
|
val formattedString = "Welcome to the **${config.serverName} Discord**, " + member.asMention + "!"
|
||||||
|
welcomeChannel.sendMessage(formattedString).queue()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.minexd.api.module.discord.main.listener
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.API.config
|
||||||
|
import com.minexd.api.module.discord.main.DiscordHandler
|
||||||
|
import com.minexd.api.module.profile.ProfileRepository
|
||||||
|
import net.dv8tion.jda.api.entities.Member
|
||||||
|
import net.dv8tion.jda.api.entities.Role
|
||||||
|
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object SyncChannelListener : ListenerAdapter()
|
||||||
|
{
|
||||||
|
|
||||||
|
override fun onGuildMessageReceived(event: GuildMessageReceivedEvent) {
|
||||||
|
val channel = event.channel
|
||||||
|
|
||||||
|
if (!channel.name.equals("sync", ignoreCase = true)) return
|
||||||
|
if (!event.message.contentRaw.startsWith("!sync") && !event.message.author.isBot) {
|
||||||
|
event.message.delete().queueAfter(2, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val code = event.message.contentRaw.split(" ")[1]
|
||||||
|
|
||||||
|
val profile = ProfileRepository.findProfileBySyncCode(code)!!
|
||||||
|
|
||||||
|
if (profile.synced || profile.syncedAccount != null) {
|
||||||
|
event.channel.sendMessage("Your account is already synced").queue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.syncCode != code) {
|
||||||
|
event.channel.sendMessage("Invalid Code!").queue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() >= profile.syncCodeExpiry!!) {
|
||||||
|
event.channel.sendMessage("Expired Code!").queue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.syncCode = null
|
||||||
|
profile.syncCodeExpiry = null
|
||||||
|
profile.syncedAccount = event.author.id
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
var member: Member = event.member!!
|
||||||
|
var linkedRole: Role = event.guild.getRolesByName("linked", true)[0]
|
||||||
|
|
||||||
|
//LINKED ROLE
|
||||||
|
event.guild.addRoleToMember(member, linkedRole).queue()
|
||||||
|
|
||||||
|
if (!profile.getBestDisplayRank().default) {
|
||||||
|
if (profile.getBestDisplayRank().id.contains(config.rank1, ignoreCase = true)) {
|
||||||
|
var rank1: Role? = event.guild.getRolesByName(config.rank1, true)[0]
|
||||||
|
event.guild.addRoleToMember(member, rank1!!).queue()
|
||||||
|
} else if (profile.getBestDisplayRank().id.contains(config.rank2, ignoreCase = true)) {
|
||||||
|
var rank2: Role? = event.guild.getRolesByName(config.rank2, true)[0]
|
||||||
|
event.guild.addRoleToMember(member, rank2!!).queue()
|
||||||
|
} else if (profile.getBestDisplayRank().id.contains(config.rank3, ignoreCase = true)) {
|
||||||
|
var rank3: Role? = event.guild.getRolesByName(config.rank3, true)[0]
|
||||||
|
event.guild.addRoleToMember(member, rank3!!).queue()
|
||||||
|
} else if (profile.getBestDisplayRank().id.contains(config.rank4, true)) {
|
||||||
|
var rank4: Role? = event.guild.getRolesByName(config.rank4, true)[0]
|
||||||
|
event.guild.addRoleToMember(member, rank4!!).queue()
|
||||||
|
} else if (profile.getBestDisplayRank().id.contains(config.rank5, ignoreCase = true)) {
|
||||||
|
var rank5: Role? = event.guild.getRolesByName(config.rank5, true)[0]
|
||||||
|
event.guild.addRoleToMember(member, rank5!!).queue()
|
||||||
|
} else if (profile.getBestDisplayRank().id.contains(config.rank6, ignoreCase = true)) {
|
||||||
|
if (!config.rank6.isEmpty()) {
|
||||||
|
var rank6: Role? = event.guild.getRolesByName(config.rank6, true)[0]
|
||||||
|
event.guild.addRoleToMember(member, rank6!!).queue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "AccountSynced",
|
||||||
|
data = mapOf(
|
||||||
|
"PlayerUUID" to profile.uuid.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.minexd.api.module.discord.staff
|
||||||
|
|
||||||
|
import com.minexd.api.API.config
|
||||||
|
import com.minexd.api.module.discord.main.listener.SyncChannelListener
|
||||||
|
import com.minexd.api.module.discord.staff.listener.StaffJoinEventListener
|
||||||
|
import com.minexd.api.module.discord.staff.listener.StaffLeaveEventListener
|
||||||
|
import com.minexd.api.module.profile.Profile
|
||||||
|
import net.dv8tion.jda.api.JDA
|
||||||
|
import net.dv8tion.jda.api.JDABuilder
|
||||||
|
import net.dv8tion.jda.api.entities.Guild
|
||||||
|
import net.dv8tion.jda.api.entities.Member
|
||||||
|
import net.dv8tion.jda.api.requests.GatewayIntent
|
||||||
|
|
||||||
|
object StaffHandler
|
||||||
|
{
|
||||||
|
lateinit var jda: JDA
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
jda = JDABuilder.createDefault(config.discordStaffToken).enableIntents(
|
||||||
|
GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_VOICE_STATES).build()
|
||||||
|
|
||||||
|
jda.addEventListener(StaffJoinEventListener)
|
||||||
|
jda.addEventListener(StaffLeaveEventListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGuild() : Guild {
|
||||||
|
return jda.getGuildById(config.discordStaffID)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMember(profile: Profile) : Member? {
|
||||||
|
return getGuild().getMemberById(profile.syncedAccount!!)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.minexd.api.module.discord.staff.listener
|
||||||
|
|
||||||
|
import com.minexd.api.API.config
|
||||||
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
|
|
||||||
|
object StaffJoinEventListener : ListenerAdapter()
|
||||||
|
{
|
||||||
|
|
||||||
|
override fun onGuildMemberJoin(event: GuildMemberJoinEvent) {
|
||||||
|
val guild = event.guild
|
||||||
|
val channels = guild.getTextChannelsByName("welcome", true)
|
||||||
|
if (channels.isEmpty()) return
|
||||||
|
val member = event.member
|
||||||
|
val welcomeChannel = channels[0]
|
||||||
|
val formattedString = "Welcome to the **${config.serverName} Staff Team**, " + member.asMention + "!"
|
||||||
|
welcomeChannel.sendMessage(formattedString).queue()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.minexd.api.module.discord.staff.listener
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent
|
||||||
|
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter
|
||||||
|
|
||||||
|
object StaffLeaveEventListener : ListenerAdapter()
|
||||||
|
{
|
||||||
|
|
||||||
|
override fun onGuildMemberRemove(event: GuildMemberRemoveEvent) {
|
||||||
|
val guild = event.guild
|
||||||
|
val channels = guild.getTextChannelsByName("welcome", true)
|
||||||
|
if (channels.isEmpty()) return
|
||||||
|
val member = event.member
|
||||||
|
val welcomeChannel = channels[0]
|
||||||
|
val formattedString = "Goodbye, " + member!!.asMention + "!"
|
||||||
|
welcomeChannel.sendMessage(formattedString).queue()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,233 @@
|
||||||
|
package com.minexd.api.module.friend
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.friend.result.Friend
|
||||||
|
import com.minexd.api.module.friend.result.FriendRemoveResult
|
||||||
|
import com.minexd.api.module.friend.result.FriendRequestResult
|
||||||
|
import com.minexd.api.module.friend.structure.Friendship
|
||||||
|
import com.minexd.api.module.friend.structure.FriendshipType
|
||||||
|
import com.minexd.api.module.messaging.MessagingHandler
|
||||||
|
import com.minexd.api.module.profile.ProfileHandler
|
||||||
|
import com.minexd.api.module.profile.ProfileRepository
|
||||||
|
import com.minexd.api.module.profile.setting.impl.FriendRequestsSetting
|
||||||
|
import com.minexd.api.module.profile.setting.impl.PresenceVisibilitySetting
|
||||||
|
import com.minexd.api.presence.PlayerPresenceHandler
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
import spark.Route
|
||||||
|
import spark.kotlin.halt
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object FriendAPI {
|
||||||
|
|
||||||
|
val listFriendships = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
val friendships = FriendRepository.findFriendships(uuid)
|
||||||
|
.mapNotNull { friendship ->
|
||||||
|
val other = friendship.getOtherPlayer(uuid) ?: return@mapNotNull null
|
||||||
|
|
||||||
|
val friend = Friend(
|
||||||
|
other,
|
||||||
|
API.uuidCache.name(other),
|
||||||
|
friendship.type,
|
||||||
|
friendship.score,
|
||||||
|
friendship.player1,
|
||||||
|
friendship.createdAt,
|
||||||
|
friendship.favorited.contains(uuid)
|
||||||
|
)
|
||||||
|
|
||||||
|
val friendProfile = ProfileHandler.getOrFetchProfile(other)
|
||||||
|
|
||||||
|
val loadPresence = when (friendProfile.getSetting(PresenceVisibilitySetting).getValue()) {
|
||||||
|
PresenceVisibilitySetting.OptionValue.EVERYONE -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
PresenceVisibilitySetting.OptionValue.FRIENDS -> {
|
||||||
|
friendship.type == FriendshipType.FRIENDS
|
||||||
|
}
|
||||||
|
PresenceVisibilitySetting.OptionValue.NOBODY -> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadPresence) {
|
||||||
|
friend.presence = PlayerPresenceHandler.getOrFetchPresence(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route Serializers.gson.toJson(friendships.sortedWith(Friend.DEFAULT_SORT).reversed())
|
||||||
|
}
|
||||||
|
|
||||||
|
val getIncoming: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
return@Route FriendRepository.findIncomingFriendships(uuid)
|
||||||
|
.mapNotNull { friendship ->
|
||||||
|
val other = friendship.getOtherPlayer(uuid) ?: return@mapNotNull null
|
||||||
|
Friend(
|
||||||
|
other,
|
||||||
|
API.uuidCache.name(other),
|
||||||
|
friendship.type,
|
||||||
|
friendship.score,
|
||||||
|
friendship.player1,
|
||||||
|
friendship.createdAt,
|
||||||
|
friendship.favorited.contains(uuid)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val getOutgoing: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
return@Route FriendRepository.findOutgoingFriendships(uuid)
|
||||||
|
.mapNotNull { friendship ->
|
||||||
|
val other = friendship.getOtherPlayer(uuid) ?: return@mapNotNull null
|
||||||
|
Friend(
|
||||||
|
other,
|
||||||
|
API.uuidCache.name(other),
|
||||||
|
friendship.type,
|
||||||
|
friendship.score,
|
||||||
|
friendship.player1,
|
||||||
|
friendship.createdAt,
|
||||||
|
friendship.favorited.contains(uuid)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val show: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
val friendId = UUID.fromString(request.queryParams("friend_id"))
|
||||||
|
return@Route FriendRepository.findFriendship(uuid, friendId)
|
||||||
|
?: return@Route halt(400, "You are not friends with ${API.uuidCache.name(friendId)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val create: Route = Route { request, response ->
|
||||||
|
val senderUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
val friendUUID = UUID.fromString(request.queryParams("friend_id"))
|
||||||
|
|
||||||
|
if (senderUUID == friendUUID) {
|
||||||
|
return@Route halt(400, FriendRequestResult.CANNOT_REQUEST_SELF.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val friendship = FriendRepository.findFriendship(senderUUID, friendUUID)
|
||||||
|
if (friendship != null) {
|
||||||
|
if (friendship.type == FriendshipType.FRIENDS) {
|
||||||
|
return@Route halt(400, FriendRequestResult.ALREADY_FRIENDS.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (friendship.player1 == senderUUID) {
|
||||||
|
return@Route halt(400, FriendRequestResult.ALREADY_REQUESTED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FriendRepository.findFriends(senderUUID).size >= FriendHandler.MAX_FRIENDS_LIST_SIZE) {
|
||||||
|
return@Route halt(400, FriendRequestResult.FRIENDS_LIST_FULL.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FriendRepository.findFriends(friendUUID).size >= FriendHandler.MAX_FRIENDS_LIST_SIZE) {
|
||||||
|
return@Route halt(400, FriendRequestResult.TARGET_FRIENDS_LIST_FULL.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MessagingHandler.isIgnored(friendUUID, senderUUID)) {
|
||||||
|
return@Route halt(400, FriendRequestResult.CANNOT_REQUEST.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
friendship.type = FriendshipType.FRIENDS
|
||||||
|
friendship.createdAt = System.currentTimeMillis()
|
||||||
|
FriendRepository.saveFriendship(friendship)
|
||||||
|
|
||||||
|
val senderProfile = ProfileRepository.findOrCreateProfile(senderUUID)
|
||||||
|
val friendProfile = ProfileRepository.findOrCreateProfile(friendUUID)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "FriendAccept",
|
||||||
|
data = mapOf(
|
||||||
|
"Sender" to senderUUID.toString(),
|
||||||
|
"SenderName" to senderProfile.getColoredUsername(),
|
||||||
|
"Receiver" to friendProfile.uuid,
|
||||||
|
"ReceiverName" to friendProfile.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
|
||||||
|
return@Route Serializers.gson.toJsonTree(friendship).also { json ->
|
||||||
|
val obj = json.asJsonObject
|
||||||
|
obj.addProperty("action_taken", FriendRequestResult.REQUEST_ACCEPTED.name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val onlyAccept = request.queryParams("only_accept")?.toBoolean() ?: false
|
||||||
|
if (onlyAccept) {
|
||||||
|
return@Route halt(400, FriendRequestResult.NOT_REQUESTED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newFriendship = Friendship(player1 = senderUUID, player2 = friendUUID)
|
||||||
|
FriendRepository.saveFriendship(newFriendship)
|
||||||
|
|
||||||
|
val senderProfile = ProfileRepository.findOrCreateProfile(senderUUID)
|
||||||
|
val friendProfile = ProfileRepository.findOrCreateProfile(friendUUID)
|
||||||
|
|
||||||
|
if (friendProfile.getSetting(FriendRequestsSetting).getValue() == FriendRequestsSetting.OptionValue.DENY) {
|
||||||
|
return@Route halt(400, FriendRequestResult.CANNOT_REQUEST.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MessagingHandler.isIgnored(friendUUID, senderUUID)) {
|
||||||
|
return@Route halt(400, FriendRequestResult.CANNOT_REQUEST.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "FriendRequest",
|
||||||
|
data = mapOf(
|
||||||
|
"Sender" to senderUUID.toString(),
|
||||||
|
"SenderName" to senderProfile.getColoredUsername(),
|
||||||
|
"Receiver" to friendUUID,
|
||||||
|
"ReceiverName" to friendProfile.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
|
||||||
|
return@Route Serializers.gson.toJsonTree(newFriendship).also { json ->
|
||||||
|
val obj = json.asJsonObject
|
||||||
|
obj.addProperty("action_taken", FriendRequestResult.REQUEST_CREATED.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val destroy: Route = Route { request, response ->
|
||||||
|
val senderUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
val friendUUID = UUID.fromString(request.queryParams("friend_id"))
|
||||||
|
|
||||||
|
val friendship = FriendRepository.findFriendship(senderUUID, friendUUID)
|
||||||
|
?: return@Route halt(403, "You are not friends with ${API.uuidCache.name(friendUUID)}")
|
||||||
|
|
||||||
|
FriendRepository.deleteFriendship(friendship)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "FriendRemove",
|
||||||
|
data = mapOf(
|
||||||
|
"Sender" to senderUUID.toString(),
|
||||||
|
"Receiver" to friendUUID.toString()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
val result = if (friendship.type == FriendshipType.FRIENDS) {
|
||||||
|
FriendRemoveResult.FRIEND_REMOVED
|
||||||
|
} else {
|
||||||
|
if (senderUUID == friendship.player1) {
|
||||||
|
FriendRemoveResult.REQUEST_CANCELLED
|
||||||
|
} else {
|
||||||
|
FriendRemoveResult.REQUEST_REJECTED
|
||||||
|
}
|
||||||
|
}.toResponse()
|
||||||
|
|
||||||
|
return@Route Serializers.gson.toJsonTree(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
val update: Route = Route { request, response ->
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.minexd.api.module.friend
|
||||||
|
|
||||||
|
import com.minexd.api.module.friend.result.Friend
|
||||||
|
import com.minexd.api.service.ServiceRegistry
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object FriendHandler : Runnable {
|
||||||
|
|
||||||
|
const val MAX_FRIENDS_LIST_SIZE: Int = 500
|
||||||
|
|
||||||
|
private val cached: MutableMap<UUID, Pair<List<Friend>, Long>> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
fun cache(uuid: UUID, friends: List<Friend>) {
|
||||||
|
cached[uuid] = Pair(friends, System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1L))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
ServiceRegistry.register(this, 20L * 3L, 20L * 3L)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
val expired = cached.filter {
|
||||||
|
System.currentTimeMillis() >= it.value.second
|
||||||
|
}.keys
|
||||||
|
|
||||||
|
for (uuid in expired) {
|
||||||
|
cached.remove(uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package com.minexd.api.module.friend
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.Repository
|
||||||
|
import com.minexd.api.module.friend.structure.Friendship
|
||||||
|
import com.minexd.api.module.friend.structure.FriendshipType
|
||||||
|
import com.mongodb.BasicDBObject
|
||||||
|
import com.mongodb.client.MongoCollection
|
||||||
|
import com.mongodb.client.model.ReplaceOptions
|
||||||
|
import com.mongodb.client.model.UpdateOptions
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import org.bson.Document
|
||||||
|
import org.bson.types.BasicBSONList
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object FriendRepository : Repository {
|
||||||
|
|
||||||
|
private val RELATION_TYPE: Type = object : TypeToken<Friendship>() {}.type
|
||||||
|
|
||||||
|
private lateinit var friendshipsCol: MongoCollection<Document>
|
||||||
|
|
||||||
|
override fun initialize() {
|
||||||
|
friendshipsCol = API.mongoDatabase.getCollection("friends_friendships")
|
||||||
|
friendshipsCol.createIndex(BasicDBObject("friendshipId", 1))
|
||||||
|
friendshipsCol.createIndex(BasicDBObject("player1", 1))
|
||||||
|
friendshipsCol.createIndex(BasicDBObject("player2", 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to retrieve a [Friendship] from the given [id] (relation ID).
|
||||||
|
*/
|
||||||
|
fun findFriendship(id: UUID): Friendship? {
|
||||||
|
val document = friendshipsCol.find(Document("friendshipId", id.toString())).first() ?: return null
|
||||||
|
return Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), RELATION_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to retrieve a [Friendship] between the 2 given players, [player1] and [player2].
|
||||||
|
*/
|
||||||
|
fun findFriendship(player1: UUID, player2: UUID): Friendship? {
|
||||||
|
val query = Document("\$or", BasicBSONList().also { orList ->
|
||||||
|
orList.add(Document("\$and", BasicBSONList().also { andList ->
|
||||||
|
andList.add(Document("player1", player1.toString()))
|
||||||
|
andList.add(Document("player2", player2.toString()))
|
||||||
|
}))
|
||||||
|
|
||||||
|
orList.add(Document("\$and", BasicBSONList().also { andList ->
|
||||||
|
andList.add(Document("player1", player2.toString()))
|
||||||
|
andList.add(Document("player2", player1.toString()))
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
val document = friendshipsCol.find(query).first() ?: return null
|
||||||
|
return Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), RELATION_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of [Friendship]s for the given [player].
|
||||||
|
*/
|
||||||
|
fun findFriendships(player: UUID): List<Friendship> {
|
||||||
|
val query = Document("\$or", listOf(
|
||||||
|
Document("player1", player.toString()),
|
||||||
|
Document("player2", player.toString())
|
||||||
|
))
|
||||||
|
|
||||||
|
return arrayListOf<Friendship>().also { relations ->
|
||||||
|
val documents = friendshipsCol.find(query)
|
||||||
|
for (document in documents) {
|
||||||
|
relations.add(Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), RELATION_TYPE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of [Friendship]s for the given [player].
|
||||||
|
*/
|
||||||
|
fun findFriends(player: UUID): List<Friendship> {
|
||||||
|
val query = Document("\$and", BasicBSONList().also { andList ->
|
||||||
|
andList.add(Document("\$or", BasicBSONList().also { orList ->
|
||||||
|
orList.add(Document("player1", player.toString()))
|
||||||
|
orList.add(Document("player2", player.toString()))
|
||||||
|
}))
|
||||||
|
|
||||||
|
andList.add(Document("type", FriendshipType.FRIENDS.name))
|
||||||
|
})
|
||||||
|
|
||||||
|
return arrayListOf<Friendship>().also { relations ->
|
||||||
|
val documents = friendshipsCol.find(query)
|
||||||
|
for (document in documents) {
|
||||||
|
relations.add(Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), RELATION_TYPE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of incoming [Friendship]s for the given [player].
|
||||||
|
*/
|
||||||
|
fun findIncomingFriendships(player: UUID): List<Friendship> {
|
||||||
|
val query = Document("\$and", BasicBSONList().also { andList ->
|
||||||
|
andList.add(Document("player2", player.toString()))
|
||||||
|
andList.add(Document("type", FriendshipType.PENDING.name))
|
||||||
|
})
|
||||||
|
|
||||||
|
return arrayListOf<Friendship>().also { relations ->
|
||||||
|
val documents = friendshipsCol.find(query)
|
||||||
|
for (document in documents) {
|
||||||
|
relations.add(Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), RELATION_TYPE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of outgoing [Friendship]s for the given [player].
|
||||||
|
*/
|
||||||
|
fun findOutgoingFriendships(player: UUID): List<Friendship> {
|
||||||
|
val query = Document("\$and", BasicBSONList().also { andList ->
|
||||||
|
andList.add(Document("player1", player.toString()))
|
||||||
|
andList.add(Document("type", FriendshipType.PENDING.name))
|
||||||
|
})
|
||||||
|
|
||||||
|
return arrayListOf<Friendship>().also { relations ->
|
||||||
|
val documents = friendshipsCol.find(Document(query))
|
||||||
|
for (document in documents) {
|
||||||
|
relations.add(Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), RELATION_TYPE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the given [friendship].
|
||||||
|
*/
|
||||||
|
fun saveFriendship(friendship: Friendship) {
|
||||||
|
friendshipsCol.replaceOne(Document("friendshipId", friendship.friendshipId.toString()), Document.parse(Serializers.gson.toJson(friendship, RELATION_TYPE)), ReplaceOptions().upsert(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given [friendship].
|
||||||
|
*/
|
||||||
|
fun deleteFriendship(friendship: Friendship) {
|
||||||
|
friendshipsCol.deleteOne(Document("friendshipId", friendship.friendshipId.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the "Friend Score" for the given [friendship].
|
||||||
|
*/
|
||||||
|
fun modifyFriendScore(friendship: Friendship) {
|
||||||
|
friendshipsCol.updateOne(Document("friendshipId", friendship.friendshipId.toString()), Document("\$inc", Document("score", friendship.score)), UpdateOptions().upsert(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.minexd.api.module.friend.result
|
||||||
|
|
||||||
|
import com.minexd.api.module.friend.structure.FriendshipType
|
||||||
|
import com.minexd.api.presence.PlayerPresence
|
||||||
|
import com.minexd.api.util.NaturalOrderComparator
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a friend from a friendship.
|
||||||
|
*/
|
||||||
|
data class Friend(
|
||||||
|
val uuid: UUID,
|
||||||
|
var username: String,
|
||||||
|
var type: FriendshipType,
|
||||||
|
var score: Int,
|
||||||
|
val createdBy: UUID,
|
||||||
|
val createdAt: Long,
|
||||||
|
var favorited: Boolean
|
||||||
|
) {
|
||||||
|
|
||||||
|
var presence: PlayerPresence? = null
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return if (other is Friend) {
|
||||||
|
uuid == other.uuid
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return uuid.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
val DEFAULT_SORT: Comparator<Friend> = Comparator { o1: Friend, o2: Friend ->
|
||||||
|
if (o1.favorited && !o2.favorited) {
|
||||||
|
return@Comparator 1
|
||||||
|
} else if (!o1.favorited && o2.favorited) {
|
||||||
|
return@Comparator -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o1.type == FriendshipType.FRIENDS && o2.type == FriendshipType.FRIENDS) {
|
||||||
|
if (o1.presence != null && o2.presence != null) {
|
||||||
|
if (o1.presence!!.isOnline() && o2.presence!!.isOnline()) {
|
||||||
|
return@Comparator o1.score - o2.score
|
||||||
|
} else if (o1.presence!!.isOnline()) {
|
||||||
|
return@Comparator 1
|
||||||
|
} else if (o2.presence!!.isOnline()) {
|
||||||
|
return@Comparator -1
|
||||||
|
} else {
|
||||||
|
return@Comparator (o1.presence!!.heartbeat - o2.presence!!.heartbeat).toInt()
|
||||||
|
}
|
||||||
|
} else if (o1.presence != null && o2.presence == null) {
|
||||||
|
return@Comparator 1
|
||||||
|
} else if (o1.presence == null && o2.presence != null) {
|
||||||
|
return@Comparator -1
|
||||||
|
} else if ((o1.presence != null && o1.presence!!.isOnline()) && (o2.presence == null || !o2.presence!!.isOnline())) {
|
||||||
|
return@Comparator -1
|
||||||
|
} else if ((o1.presence == null || !o1.presence!!.isOnline()) && (o2.presence != null && o2.presence!!.isOnline())) {
|
||||||
|
return@Comparator 1
|
||||||
|
} else {
|
||||||
|
return@Comparator o1.score - o2.score
|
||||||
|
}
|
||||||
|
} else if (o1.type == FriendshipType.FRIENDS && o2.type != FriendshipType.FRIENDS) {
|
||||||
|
return@Comparator 1
|
||||||
|
} else if (o1.type != FriendshipType.FRIENDS && o2.type == FriendshipType.FRIENDS) {
|
||||||
|
return@Comparator -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Comparator (o1.createdAt - o2.createdAt).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val ALPHABETICAL_SORT: Comparator<Friend> = Comparator { o1, o2 ->
|
||||||
|
return@Comparator if (o1.presence != null && o2.presence != null) {
|
||||||
|
if (o1.presence?.isOnline() == true && o2.presence?.isOnline() == true) {
|
||||||
|
NaturalOrderComparator.compare(o1.username.toLowerCase(), o2.username.toLowerCase())
|
||||||
|
} else if (o1.presence?.isOnline() == true) {
|
||||||
|
1
|
||||||
|
} else if (o2.presence?.isOnline() == true) {
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
NaturalOrderComparator.compare(o1.username.toLowerCase(), o2.username.toLowerCase())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NaturalOrderComparator.compare(o1.username.toLowerCase(), o2.username.toLowerCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val LAST_ONLINE_SORT: Comparator<Friend> = Comparator { o1, o2 ->
|
||||||
|
return@Comparator if (o1.presence != null && o2.presence != null) {
|
||||||
|
if (o1.presence?.isOnline() == true && o2.presence?.isOnline() == true) {
|
||||||
|
NaturalOrderComparator.compare(o1.username, o2.username)
|
||||||
|
} else if (o1.presence?.isOnline() == true) {
|
||||||
|
1
|
||||||
|
} else if (o2.presence?.isOnline() == true) {
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
(o1.presence!!.heartbeat - o2.presence!!.heartbeat).toInt()
|
||||||
|
}
|
||||||
|
} else if (o1.presence != null) {
|
||||||
|
return@Comparator 1
|
||||||
|
} else if (o2.presence != null) {
|
||||||
|
return@Comparator -1
|
||||||
|
} else {
|
||||||
|
return@Comparator 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.minexd.api.module.friend.result
|
||||||
|
|
||||||
|
enum class FriendRemoveResult {
|
||||||
|
|
||||||
|
FRIEND_REMOVED,
|
||||||
|
REQUEST_CANCELLED,
|
||||||
|
REQUEST_REJECTED;
|
||||||
|
|
||||||
|
fun toResponse(): Response {
|
||||||
|
return Response(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Response(val result: FriendRemoveResult)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.minexd.api.module.friend.result
|
||||||
|
|
||||||
|
enum class FriendRequestResult {
|
||||||
|
|
||||||
|
ALREADY_FRIENDS,
|
||||||
|
ALREADY_REQUESTED,
|
||||||
|
REQUEST_CREATED,
|
||||||
|
REQUEST_ACCEPTED,
|
||||||
|
NOT_REQUESTED,
|
||||||
|
CANNOT_REQUEST_SELF,
|
||||||
|
CANNOT_REQUEST,
|
||||||
|
FRIENDS_LIST_FULL,
|
||||||
|
TARGET_FRIENDS_LIST_FULL,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.minexd.api.module.friend.structure
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model for a friendship between 2 players. Specially crafted
|
||||||
|
* to meet the needs of both OOP and efficient MongoDB queries,
|
||||||
|
* which is important because we use GSON reflection serializer
|
||||||
|
* to convert this object into a BSON document.
|
||||||
|
*
|
||||||
|
* The core assumption of this model is:
|
||||||
|
* player1 is ALWAYS the player to initiate the friendship ("friend request")
|
||||||
|
*/
|
||||||
|
data class Friendship(
|
||||||
|
val player1: UUID,
|
||||||
|
val player2: UUID
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unique ID for this friendship.
|
||||||
|
*/
|
||||||
|
val friendshipId: UUID = UUID.randomUUID()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When this friendship was created.
|
||||||
|
*/
|
||||||
|
var createdAt: Long = System.currentTimeMillis()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of friendship between the two participants.
|
||||||
|
*/
|
||||||
|
var type: FriendshipType = FriendshipType.PENDING
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This friendship's score, otherwise known as "Friend Score".
|
||||||
|
*/
|
||||||
|
var score: Int = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Who involved in this friendship has favorited this friendship on their "friend's list".
|
||||||
|
*/
|
||||||
|
val favorited: MutableList<UUID> = arrayListOf()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the given [source] is involved in this friendship, the counterpart player UUID is returned, otherwise null.
|
||||||
|
*/
|
||||||
|
fun getOtherPlayer(source: UUID): UUID? {
|
||||||
|
return when (source) {
|
||||||
|
player1 -> player2
|
||||||
|
player2 -> player1
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.minexd.api.module.friend.structure
|
||||||
|
|
||||||
|
enum class FriendshipType {
|
||||||
|
|
||||||
|
PENDING,
|
||||||
|
FRIENDS
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.minexd.api.module.leaderboard
|
||||||
|
|
||||||
|
abstract class Leaderboard(val id: String, val name: String) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal const val CACHED_ENTRIES_SIZE = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var entries: List<LeaderboardEntry<*>> = arrayListOf()
|
||||||
|
|
||||||
|
abstract fun fetchEntries(): List<LeaderboardEntry<*>>
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
val newEntries = fetchEntries()
|
||||||
|
var position = 1
|
||||||
|
|
||||||
|
for (entry in newEntries) {
|
||||||
|
entry.position = position++
|
||||||
|
}
|
||||||
|
|
||||||
|
this.entries = newEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Joel Evans
|
||||||
|
*
|
||||||
|
* Use and or redistribution of compiled JAR file and or source code is permitted only if given
|
||||||
|
* explicit permission from original author: Joel Evans
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.minexd.api.module.leaderboard
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class LeaderboardEntry<T>(
|
||||||
|
var position: Int,
|
||||||
|
val uuid: UUID,
|
||||||
|
val displayName: String,
|
||||||
|
val value: T
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.minexd.api.module.leaderboard
|
||||||
|
|
||||||
|
import com.minexd.api.module.server.util.GameType
|
||||||
|
import spark.Route
|
||||||
|
|
||||||
|
object LeaderboardsAPI {
|
||||||
|
|
||||||
|
val byGame: Route = Route { request, response ->
|
||||||
|
return@Route LeaderboardsHandler.getLeaderboardsByGame(GameType.valueOf(request.queryParams("game")))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.minexd.api.module.leaderboard
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.leaderboard.prison.*
|
||||||
|
import com.minexd.api.module.server.util.GameType
|
||||||
|
|
||||||
|
object LeaderboardsHandler {
|
||||||
|
|
||||||
|
private val leaderboards: Map<GameType, List<Leaderboard>> = mapOf(
|
||||||
|
GameType.PRISON to listOf(
|
||||||
|
PrisonBlocksMinedLeaderboard,
|
||||||
|
PrisonKillsLeaderboard,
|
||||||
|
PrisonPlayTimeLeaderboard,
|
||||||
|
PrisonRankLeaderboard,
|
||||||
|
PrisonTokensLeaderboard
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
for ((gameType, leaderboards) in leaderboards) {
|
||||||
|
API.logger.info("Updating $gameType leaderboards")
|
||||||
|
|
||||||
|
for (leaderboard in leaderboards) {
|
||||||
|
try {
|
||||||
|
leaderboard.refresh()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLeaderboardsByGame(gameType: GameType): List<Leaderboard> {
|
||||||
|
return leaderboards[gameType] ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Joel Evans
|
||||||
|
*
|
||||||
|
* Use and or redistribution of compiled JAR file and or source code is permitted only if given
|
||||||
|
* explicit permission from original author: Joel Evans
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.minexd.api.module.leaderboard.prison
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.leaderboard.Leaderboard
|
||||||
|
import com.minexd.api.module.leaderboard.LeaderboardEntry
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticsRepository
|
||||||
|
import com.minexd.api.module.staff.StaffHandler
|
||||||
|
import org.bson.Document
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PrisonBlocksMinedLeaderboard : Leaderboard("top-blocks-mined", "Top Blocks Mined") {
|
||||||
|
|
||||||
|
override fun fetchEntries(): List<LeaderboardEntry<*>> {
|
||||||
|
val entries = arrayListOf<LeaderboardEntry<Int>>()
|
||||||
|
|
||||||
|
for (document in StatisticsRepository.prisonProfilesCollection.find()) {
|
||||||
|
val uuid = UUID.fromString(document.getString("uuid"))
|
||||||
|
|
||||||
|
if (StaffHandler.staffMembers.contains(uuid)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayName = API.uuidCache.name(uuid)
|
||||||
|
val blocksMined = (document["statistics"] as Document).getInteger("blocksMined")
|
||||||
|
|
||||||
|
entries.add(LeaderboardEntry(0, uuid, displayName, blocksMined))
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries.sortedByDescending { it.value }.take(CACHED_ENTRIES_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Joel Evans
|
||||||
|
*
|
||||||
|
* Use and or redistribution of compiled JAR file and or source code is permitted only if given
|
||||||
|
* explicit permission from original author: Joel Evans
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.minexd.api.module.leaderboard.prison
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.leaderboard.Leaderboard
|
||||||
|
import com.minexd.api.module.leaderboard.LeaderboardEntry
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticsRepository
|
||||||
|
import com.minexd.api.module.staff.StaffHandler
|
||||||
|
import org.bson.Document
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PrisonKillsLeaderboard : Leaderboard("top-kills", "Top Kills") {
|
||||||
|
|
||||||
|
override fun fetchEntries(): List<LeaderboardEntry<*>> {
|
||||||
|
val entries = arrayListOf<LeaderboardEntry<Int>>()
|
||||||
|
|
||||||
|
for (document in StatisticsRepository.prisonProfilesCollection.find()) {
|
||||||
|
val uuid = UUID.fromString(document.getString("uuid"))
|
||||||
|
|
||||||
|
if (StaffHandler.staffMembers.contains(uuid)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayName = API.uuidCache.name(uuid)
|
||||||
|
|
||||||
|
val statistics = document["statistics"] as Document
|
||||||
|
val kills = statistics.getInteger("kills") ?: statistics.getInteger("currentPrestige")
|
||||||
|
|
||||||
|
entries.add(LeaderboardEntry(0, uuid, displayName, kills))
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries.sortedByDescending { it.value }.take(CACHED_ENTRIES_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Joel Evans
|
||||||
|
*
|
||||||
|
* Use and or redistribution of compiled JAR file and or source code is permitted only if given
|
||||||
|
* explicit permission from original author: Joel Evans
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.minexd.api.module.leaderboard.prison
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.leaderboard.Leaderboard
|
||||||
|
import com.minexd.api.module.leaderboard.LeaderboardEntry
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticsRepository
|
||||||
|
import com.minexd.api.module.staff.StaffHandler
|
||||||
|
import org.bson.Document
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PrisonPlayTimeLeaderboard : Leaderboard("top-play-time", "Top Play Time") {
|
||||||
|
|
||||||
|
override fun fetchEntries(): List<LeaderboardEntry<*>> {
|
||||||
|
val entries = arrayListOf<LeaderboardEntry<Long>>()
|
||||||
|
|
||||||
|
for (document in StatisticsRepository.prisonProfilesCollection.find()) {
|
||||||
|
val uuid = UUID.fromString(document.getString("uuid"))
|
||||||
|
|
||||||
|
if (StaffHandler.staffMembers.contains(uuid)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayName = API.uuidCache.name(uuid)
|
||||||
|
val statistics = document["statistics"] ?: continue
|
||||||
|
|
||||||
|
val playTime = (statistics as Document)["playTime"]
|
||||||
|
if (playTime is Int) {
|
||||||
|
entries.add(LeaderboardEntry(0, uuid, displayName, playTime.toLong()))
|
||||||
|
} else {
|
||||||
|
entries.add(LeaderboardEntry(0, uuid, displayName, playTime as Long))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries.sortedByDescending { it.value }.take(CACHED_ENTRIES_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Joel Evans
|
||||||
|
*
|
||||||
|
* Use and or redistribution of compiled JAR file and or source code is permitted only if given
|
||||||
|
* explicit permission from original author: Joel Evans
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.minexd.api.module.leaderboard.prison
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.leaderboard.Leaderboard
|
||||||
|
import com.minexd.api.module.leaderboard.LeaderboardEntry
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticsRepository
|
||||||
|
import com.minexd.api.module.staff.StaffHandler
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PrisonRankLeaderboard : Leaderboard("top-rank", "Top Rank") {
|
||||||
|
|
||||||
|
override fun fetchEntries(): List<LeaderboardEntry<*>> {
|
||||||
|
val entries = arrayListOf<LeaderboardEntry<Int>>()
|
||||||
|
|
||||||
|
for (document in StatisticsRepository.prisonProfilesCollection.find()) {
|
||||||
|
val uuid = UUID.fromString(document.getString("uuid"))
|
||||||
|
|
||||||
|
if (StaffHandler.staffMembers.contains(uuid)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayName = API.uuidCache.name(uuid)
|
||||||
|
val rank = document.getInteger("rank")
|
||||||
|
|
||||||
|
entries.add(LeaderboardEntry(0, uuid, displayName, rank))
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries.sortedByDescending { it.value }.take(CACHED_ENTRIES_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Joel Evans
|
||||||
|
*
|
||||||
|
* Use and or redistribution of compiled JAR file and or source code is permitted only if given
|
||||||
|
* explicit permission from original author: Joel Evans
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.minexd.api.module.leaderboard.prison
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.leaderboard.Leaderboard
|
||||||
|
import com.minexd.api.module.leaderboard.LeaderboardEntry
|
||||||
|
import com.minexd.api.module.profile.statistic.StatisticsRepository
|
||||||
|
import com.minexd.api.module.staff.StaffHandler
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PrisonTokensLeaderboard : Leaderboard("top-tokens", "Top Tokens") {
|
||||||
|
|
||||||
|
override fun fetchEntries(): List<LeaderboardEntry<*>> {
|
||||||
|
val entries = arrayListOf<LeaderboardEntry<Long>>()
|
||||||
|
|
||||||
|
for (document in StatisticsRepository.prisonProfilesCollection.find()) {
|
||||||
|
val uuid = UUID.fromString(document.getString("uuid"))
|
||||||
|
|
||||||
|
if (StaffHandler.staffMembers.contains(uuid)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayName = API.uuidCache.name(uuid)
|
||||||
|
|
||||||
|
val balance = document["tokenBalance"] ?: document["tokensBalance"]
|
||||||
|
if (balance is Int) {
|
||||||
|
entries.add(LeaderboardEntry(0, uuid, displayName, balance.toLong()))
|
||||||
|
} else {
|
||||||
|
entries.add(LeaderboardEntry(0, uuid, displayName, balance as Long))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries.sortedByDescending { it.value }.take(CACHED_ENTRIES_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.minexd.api.module.messaging
|
||||||
|
|
||||||
|
enum class MessageError {
|
||||||
|
|
||||||
|
SELF_MESSAGES_DISABLED,
|
||||||
|
PLAYER_OFFLINE,
|
||||||
|
PLAYER_IGNORED,
|
||||||
|
PLAYER_MESSAGES_FRIENDS_ONLY,
|
||||||
|
PLAYER_MESSAGES_DISABLED,
|
||||||
|
CANNOT_MESSAGE_PLAYER,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.minexd.api.module.messaging
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.friend.FriendRepository
|
||||||
|
import com.minexd.api.module.friend.structure.FriendshipType
|
||||||
|
import com.minexd.api.module.profile.ProfileHandler
|
||||||
|
import com.minexd.api.module.profile.setting.impl.PrivateMessagesSetting
|
||||||
|
import com.minexd.api.presence.PlayerPresenceHandler
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
import spark.Route
|
||||||
|
import spark.kotlin.halt
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object MessagingAPI {
|
||||||
|
|
||||||
|
val message: Route = Route { request, response ->
|
||||||
|
val senderUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
val friendUUID = UUID.fromString(request.queryParams("friend_id"))
|
||||||
|
|
||||||
|
val senderPresence = PlayerPresenceHandler.getOrFetchPresence(senderUUID)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
val friendPresence = PlayerPresenceHandler.getOrFetchPresence(friendUUID)
|
||||||
|
if (friendPresence == null || !friendPresence.isOnline()) {
|
||||||
|
return@Route halt(400, MessageError.PLAYER_OFFLINE.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MessagingHandler.isIgnored(senderUUID, friendUUID)) {
|
||||||
|
return@Route halt(400, MessageError.PLAYER_IGNORED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MessagingHandler.isIgnored(friendUUID, senderUUID)) {
|
||||||
|
return@Route halt(400, MessageError.CANNOT_MESSAGE_PLAYER.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val friendship = FriendRepository.findFriendship(senderUUID, friendUUID)
|
||||||
|
val senderProfile = ProfileHandler.getOrFetchProfile(senderUUID)
|
||||||
|
|
||||||
|
when (senderProfile.getSetting(PrivateMessagesSetting).getValue()) {
|
||||||
|
PrivateMessagesSetting.OptionValue.NOBODY -> {
|
||||||
|
return@Route halt(400, MessageError.SELF_MESSAGES_DISABLED.name)
|
||||||
|
}
|
||||||
|
PrivateMessagesSetting.OptionValue.FRIENDS -> {
|
||||||
|
if (friendship == null || friendship.type == FriendshipType.PENDING) {
|
||||||
|
return@Route halt(400, MessageError.SELF_MESSAGES_DISABLED.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> { }
|
||||||
|
}
|
||||||
|
|
||||||
|
val friendProfile = ProfileHandler.getOrFetchProfile(friendUUID) ?: return@Route halt(404)
|
||||||
|
when (friendProfile.getSetting(PrivateMessagesSetting).getValue()) {
|
||||||
|
PrivateMessagesSetting.OptionValue.NOBODY -> {
|
||||||
|
return@Route halt(400, MessageError.PLAYER_MESSAGES_DISABLED.name)
|
||||||
|
}
|
||||||
|
PrivateMessagesSetting.OptionValue.FRIENDS -> {
|
||||||
|
if (friendship == null || friendship.type != FriendshipType.FRIENDS) {
|
||||||
|
return@Route halt(400, MessageError.PLAYER_MESSAGES_FRIENDS_ONLY.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> { }
|
||||||
|
}
|
||||||
|
|
||||||
|
MessagingHandler.setLastMessaged(senderUUID, friendUUID)
|
||||||
|
MessagingHandler.setLastMessaged(friendUUID, senderUUID)
|
||||||
|
|
||||||
|
val rawMessage = request.body()
|
||||||
|
val message = rawMessage.substring(1, rawMessage.length - 1)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "PrivateMessage",
|
||||||
|
data = mapOf(
|
||||||
|
"SenderUUID" to senderUUID.toString(),
|
||||||
|
"SenderName" to senderProfile.getColoredUsername(),
|
||||||
|
"SenderServer" to senderPresence.server,
|
||||||
|
"ReceiverUUID" to friendUUID.toString(),
|
||||||
|
"ReceiverName" to friendProfile.getColoredUsername(),
|
||||||
|
"ReceiverServer" to friendPresence.server,
|
||||||
|
"Message" to message
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.minexd.api.module.messaging
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object MessagingHandler {
|
||||||
|
|
||||||
|
private const val REDIS_KEY = "Core:Messaging"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last messaged player for a player.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
*
|
||||||
|
* @return the last player's uuid that [player] has messaged
|
||||||
|
*/
|
||||||
|
fun getLastMessaged(player: UUID): UUID? {
|
||||||
|
return API.redis.runRedisCommand { redis ->
|
||||||
|
redis.hget("$REDIS_KEY:$player", "LastMessaged")
|
||||||
|
}?.run { UUID.fromString(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the last messaged player for a player.
|
||||||
|
*/
|
||||||
|
fun setLastMessaged(player: UUID, lastMessaged: UUID) {
|
||||||
|
API.redis.runRedisCommand { redis ->
|
||||||
|
redis.hset("$REDIS_KEY:$player", hashMapOf("LastMessaged" to "$lastMessaged"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a player's ignore list.
|
||||||
|
*/
|
||||||
|
fun getIgnoreList(player: UUID): Set<UUID> {
|
||||||
|
return API.redis.runRedisCommand { redis ->
|
||||||
|
if (redis.exists("$REDIS_KEY:IgnoreList:$player")) {
|
||||||
|
redis.smembers("$REDIS_KEY:IgnoreList:$player").map { UUID.fromString(it) }.toSet()
|
||||||
|
} else {
|
||||||
|
emptySet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether or not the [player] is ignored by the [target].
|
||||||
|
*/
|
||||||
|
fun isIgnored(player: UUID, target: UUID): Boolean {
|
||||||
|
return getIgnoreList(player).contains(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a target to a player's ignore list.
|
||||||
|
*/
|
||||||
|
fun addToIgnoreList(player: UUID, target: UUID) {
|
||||||
|
API.redis.runRedisCommand { redis ->
|
||||||
|
redis.sadd("$REDIS_KEY:IgnoreList:$player", target.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a target from a player's ignore list.
|
||||||
|
*/
|
||||||
|
fun removeFromIgnoreList(player: UUID, target: UUID) {
|
||||||
|
API.redis.runRedisCommand { redis ->
|
||||||
|
redis.srem("$REDIS_KEY:IgnoreList:$player", target.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears a player's ignore list.
|
||||||
|
*/
|
||||||
|
fun clearIgnoreList(player: UUID) {
|
||||||
|
API.redis.runRedisCommand { redis ->
|
||||||
|
redis.del("$REDIS_KEY:IgnoreList:$player")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.minexd.api.module.party
|
||||||
|
|
||||||
|
import com.minexd.api.module.party.structure.PartyInvite
|
||||||
|
import com.minexd.api.module.party.structure.PartyPrivacy
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class Party(
|
||||||
|
val id: UUID = UUID.randomUUID(),
|
||||||
|
var leader: UUID
|
||||||
|
) {
|
||||||
|
|
||||||
|
var privacy: PartyPrivacy = PartyPrivacy.EVERYONE
|
||||||
|
|
||||||
|
internal val members: MutableSet<UUID> = hashSetOf()
|
||||||
|
internal val invites: MutableMap<UUID, PartyInvite> = hashMapOf()
|
||||||
|
|
||||||
|
fun hasInvite(player: UUID): Boolean {
|
||||||
|
if (invites.containsKey(player)) {
|
||||||
|
val invite = invites[player]!!
|
||||||
|
if (System.currentTimeMillis() - invite.createdAt <= 86_400_000L) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
invites.remove(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInvite(player: UUID): PartyInvite? {
|
||||||
|
return invites[player]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addInvite(invite: PartyInvite) {
|
||||||
|
invites[invite.player] = invite
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeInvite(player: UUID) {
|
||||||
|
invites.remove(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,472 @@
|
||||||
|
package com.minexd.api.module.party
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.discord.main.DiscordHandler
|
||||||
|
import com.minexd.api.module.friend.FriendRepository
|
||||||
|
import com.minexd.api.module.friend.structure.FriendshipType
|
||||||
|
import com.minexd.api.module.messaging.MessagingHandler
|
||||||
|
import com.minexd.api.module.party.result.*
|
||||||
|
import com.minexd.api.module.party.structure.PartyInvite
|
||||||
|
import com.minexd.api.module.party.structure.PartyPrivacy
|
||||||
|
import com.minexd.api.module.profile.ProfileHandler
|
||||||
|
import com.minexd.api.module.profile.setting.impl.PartyRequestsSetting
|
||||||
|
import com.minexd.api.presence.PlayerPresenceHandler
|
||||||
|
import com.minexd.api.service.ServiceRegistry
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
import spark.Route
|
||||||
|
import spark.kotlin.halt
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PartyAPI {
|
||||||
|
|
||||||
|
private val MAP_TYPE = object : TypeToken<Map<String, Any?>>() {}.type
|
||||||
|
|
||||||
|
val find: Route = Route { request, response ->
|
||||||
|
when {
|
||||||
|
request.queryParams().contains("party_id") -> {
|
||||||
|
val uuid = UUID.fromString(request.queryParams("party_id"))
|
||||||
|
return@Route PartyHandler.getPartyById(uuid) ?: halt(404)
|
||||||
|
}
|
||||||
|
request.queryParams().contains("player_id") -> {
|
||||||
|
val uuid = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
return@Route PartyHandler.getPartyByPlayer(uuid) ?: halt(404)
|
||||||
|
}
|
||||||
|
request.queryParams().contains("search") -> {
|
||||||
|
val search = request.queryParams("search")
|
||||||
|
|
||||||
|
try {
|
||||||
|
val uuid = UUID.fromString(search)
|
||||||
|
|
||||||
|
val party = PartyHandler.getPartyById(uuid) ?: PartyHandler.getPartyByPlayer(uuid)
|
||||||
|
if (party != null) {
|
||||||
|
return@Route party
|
||||||
|
}
|
||||||
|
} catch (e: Exception) { }
|
||||||
|
|
||||||
|
val playerUUID = API.uuidCache.uuid(search)
|
||||||
|
if (playerUUID != null) {
|
||||||
|
return@Route PartyHandler.getPartyByPlayer(playerUUID) ?: halt(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route halt(404)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val create: Route = Route { request, response ->
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
if (PartyHandler.getPartyByPlayer(playerUUID) != null) {
|
||||||
|
return@Route halt(400, CreateError.ALREADY_IN_PARTY.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val party = Party(leader = playerUUID)
|
||||||
|
party.members.add(playerUUID)
|
||||||
|
|
||||||
|
PartyHandler.cache(party)
|
||||||
|
PartyRepository.saveParty(party)
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(playerUUID)
|
||||||
|
if (profile != null && (profile.synced || profile.syncedAccount != null)) {
|
||||||
|
DiscordHandler.getGuild().createVoiceChannel("party-${API.uuidCache.fetchUsername(party.leader)?.lowercase()}", DiscordHandler.getGuild().getCategoryById("930535751043653682")).queue {
|
||||||
|
val member = DiscordHandler.getMember(profile)
|
||||||
|
if (member != null) {
|
||||||
|
DiscordHandler.getGuild().moveVoiceMember(member, it).queue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route party
|
||||||
|
}
|
||||||
|
|
||||||
|
val disband: Route = Route { request, response ->
|
||||||
|
val partyUUID = UUID.fromString(request.queryParams("party_id"))
|
||||||
|
|
||||||
|
|
||||||
|
// check if party exists
|
||||||
|
val party = PartyHandler.getPartyById(partyUUID)
|
||||||
|
?: return@Route halt(404, DisbandError.NO_PARTY.name)
|
||||||
|
|
||||||
|
val playerUUID = if (request.queryParams().contains("player_id")) {
|
||||||
|
UUID.fromString(request.queryParams("player_id"))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerUUID != null) {
|
||||||
|
// check if player is the party leader
|
||||||
|
if (playerUUID != party.leader) {
|
||||||
|
return@Route halt(400, DisbandError.NOT_LEADER.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PartyHandler.forget(party)
|
||||||
|
PartyRepository.deleteParty(party)
|
||||||
|
|
||||||
|
ServiceRegistry.executeOnce(Runnable {
|
||||||
|
val data = mutableMapOf("PartyID" to party.id.toString())
|
||||||
|
|
||||||
|
if (playerUUID != null) {
|
||||||
|
val profile = ProfileHandler.getOrFetchProfile(playerUUID)!!
|
||||||
|
data["PlayerUUID"] = playerUUID.toString()
|
||||||
|
data["PlayerName"] = profile.getColoredUsername()
|
||||||
|
}
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyDisband",
|
||||||
|
data = mapOf("PartyID" to party.id.toString())
|
||||||
|
))
|
||||||
|
}, 1L)
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(playerUUID!!)
|
||||||
|
if (profile != null && (profile.synced || profile.syncedAccount != null)) {
|
||||||
|
DiscordHandler.getGuild().getVoiceChannelsByName("party-${API.uuidCache.fetchUsername(party.leader)}", true).firstOrNull()?.delete()?.queue()
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val update: Route = Route { request, response ->
|
||||||
|
val partyUUID = UUID.fromString(request.queryParams("party_id"))
|
||||||
|
|
||||||
|
val party = PartyHandler.getPartyById(partyUUID)
|
||||||
|
?: return@Route halt(404, UpdateError.NO_PARTY.name)
|
||||||
|
|
||||||
|
val body = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
|
||||||
|
var updated = false
|
||||||
|
|
||||||
|
if (body.has("leader")) {
|
||||||
|
party.leader = UUID.fromString(body["leader"].asString)
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.has("privacy")) {
|
||||||
|
party.privacy = PartyPrivacy.valueOf(body["privacy"].asString)
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
PartyRepository.saveParty(party)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyUpdate",
|
||||||
|
data = Serializers.gson.fromJson(body, MAP_TYPE)
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
return@Route halt(304, UpdateError.NO_CHANGES.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val createInvite: Route = Route { request, response ->
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
val targetUUID = UUID.fromString(request.queryParams("target_id"))
|
||||||
|
|
||||||
|
// cannot invite self
|
||||||
|
if (playerUUID == targetUUID) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if player has a party
|
||||||
|
val party = PartyHandler.getPartyByPlayer(playerUUID)
|
||||||
|
?: return@Route halt(404, CreateInviteError.NO_PARTY.name)
|
||||||
|
|
||||||
|
// check if player is the party leader
|
||||||
|
if (playerUUID != party.leader) {
|
||||||
|
return@Route halt(400, CreateInviteError.NOT_LEADER.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target is already a member
|
||||||
|
if (party.members.contains(targetUUID)) {
|
||||||
|
return@Route halt(400, CreateInviteError.TARGET_ALREADY_MEMBER.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target is already invited
|
||||||
|
if (party.hasInvite(targetUUID)) {
|
||||||
|
return@Route halt(400, CreateInviteError.TARGET_ALREADY_INVITED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if player is ignoring target
|
||||||
|
if (MessagingHandler.isIgnored(playerUUID, targetUUID)) {
|
||||||
|
return@Route halt(400, CreateInviteError.TARGET_IGNORED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target is ignoring player
|
||||||
|
if (MessagingHandler.isIgnored(targetUUID, playerUUID)) {
|
||||||
|
return@Route halt(400, CreateInviteError.CANNOT_INVITE.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val senderProfile = ProfileHandler.getOrFetchProfile(playerUUID)
|
||||||
|
|
||||||
|
// check if target is online
|
||||||
|
val targetPresence = PlayerPresenceHandler.getOrFetchPresence(targetUUID)
|
||||||
|
if (targetPresence == null || !targetPresence.isOnline()) {
|
||||||
|
return@Route halt(400, CreateInviteError.TARGET_OFFLINE.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target has profile
|
||||||
|
val targetProfile = ProfileHandler.getOrTryFetchProfile(targetUUID)
|
||||||
|
?: return@Route halt(400, CreateInviteError.TARGET_OFFLINE.name)
|
||||||
|
|
||||||
|
// check target privacy settings
|
||||||
|
when (targetProfile.getSetting(PartyRequestsSetting).getValue()) {
|
||||||
|
PartyRequestsSetting.OptionValue.FRIENDS -> {
|
||||||
|
val friendship = FriendRepository.findFriendship(playerUUID, targetUUID)
|
||||||
|
if (friendship == null || friendship.type != FriendshipType.FRIENDS) {
|
||||||
|
return@Route halt(400, CreateInviteError.CANNOT_INVITE.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PartyRequestsSetting.OptionValue.NOBODY -> {
|
||||||
|
return@Route halt(400, CreateInviteError.CANNOT_INVITE.name)
|
||||||
|
}
|
||||||
|
else -> { }
|
||||||
|
}
|
||||||
|
|
||||||
|
val invite = PartyInvite(player = targetUUID, invitedBy = playerUUID)
|
||||||
|
|
||||||
|
party.addInvite(invite)
|
||||||
|
PartyRepository.saveParty(party)
|
||||||
|
|
||||||
|
ServiceRegistry.executeOnce(Runnable {
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyInvite",
|
||||||
|
data = mapOf(
|
||||||
|
"PartyID" to party.id.toString(),
|
||||||
|
"TargetUUID" to targetUUID.toString(),
|
||||||
|
"TargetName" to targetProfile.getColoredUsername(),
|
||||||
|
"SenderUUID" to playerUUID.toString(),
|
||||||
|
"SenderName" to senderProfile.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}, 1L)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val destroyInvite: Route = Route { request, response ->
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
val targetUUID = UUID.fromString(request.queryParams("target_id"))
|
||||||
|
|
||||||
|
// cannot invite self
|
||||||
|
if (playerUUID == targetUUID) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if player has a party
|
||||||
|
val party = PartyHandler.getPartyByPlayer(playerUUID)
|
||||||
|
?: return@Route halt(404, DestroyInviteError.NO_PARTY.name)
|
||||||
|
|
||||||
|
// check if player is the party leader
|
||||||
|
if (playerUUID != party.leader) {
|
||||||
|
return@Route halt(400, DestroyInviteError.NOT_LEADER.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target is already a member
|
||||||
|
if (party.members.contains(targetUUID)) {
|
||||||
|
return@Route halt(400, DestroyInviteError.TARGET_ALREADY_MEMBER.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target is invited
|
||||||
|
if (!party.hasInvite(targetUUID)) {
|
||||||
|
return@Route halt(400, DestroyInviteError.TARGET_NOT_INVITED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val senderProfile = ProfileHandler.getOrFetchProfile(playerUUID)
|
||||||
|
|
||||||
|
// check if target has profile
|
||||||
|
val targetProfile = ProfileHandler.getOrTryFetchProfile(targetUUID)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
party.removeInvite(targetUUID)
|
||||||
|
PartyRepository.saveParty(party)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyInviteRevoke",
|
||||||
|
data = mapOf(
|
||||||
|
"PartyID" to party.id.toString(),
|
||||||
|
"TargetUUID" to targetUUID.toString(),
|
||||||
|
"TargetName" to targetProfile.getColoredUsername(),
|
||||||
|
"SenderUUID" to playerUUID.toString(),
|
||||||
|
"SenderName" to senderProfile.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val acceptInvite: Route = Route { request, response ->
|
||||||
|
val partyUUID = UUID.fromString(request.queryParams("party_id"))
|
||||||
|
val party = PartyHandler.getPartyById(partyUUID)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
// check if player is already in a party
|
||||||
|
if (PartyHandler.getPartyByPlayer(playerUUID) != null) {
|
||||||
|
return@Route halt(400, AcceptInviteError.ALREADY_IN_PARTY.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if player is invited
|
||||||
|
if (!party.hasInvite(playerUUID)) {
|
||||||
|
return@Route halt(400, AcceptInviteError.NO_INVITE.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if party is full
|
||||||
|
if (party.members.size >= PartyHandler.MAX_PARTY_SIZE) {
|
||||||
|
return@Route halt(400, AcceptInviteError.PARTY_FULL.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(playerUUID)
|
||||||
|
|
||||||
|
party.removeInvite(playerUUID)
|
||||||
|
party.members.add(playerUUID)
|
||||||
|
PartyHandler.cache(playerUUID, party)
|
||||||
|
PartyRepository.saveParty(party)
|
||||||
|
|
||||||
|
if (profile != null && (profile.synced || profile.syncedAccount != null)) {
|
||||||
|
val member = DiscordHandler.getMember(profile)
|
||||||
|
if (member != null) {
|
||||||
|
DiscordHandler.getGuild().moveVoiceMember(member, DiscordHandler.getGuild().getVoiceChannelsByName("party-${API.uuidCache.fetchUsername(party.leader)}", true).firstOrNull()).queue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyJoin",
|
||||||
|
data = mapOf(
|
||||||
|
"PartyID" to party.id.toString(),
|
||||||
|
"PlayerUUID" to playerUUID.toString(),
|
||||||
|
"PlayerName" to profile?.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val kick: Route = Route { request, response ->
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
val targetUUID = UUID.fromString(request.queryParams("target_id"))
|
||||||
|
|
||||||
|
// cannot kick self
|
||||||
|
if (playerUUID == targetUUID) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if player has party
|
||||||
|
val party = PartyHandler.getPartyByPlayer(playerUUID)
|
||||||
|
?: return@Route halt(404, KickError.NO_PARTY.name)
|
||||||
|
|
||||||
|
// check if player is the party leader
|
||||||
|
if (playerUUID != party.leader) {
|
||||||
|
return@Route halt(400, KickError.NOT_LEADER.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if target is a member
|
||||||
|
if (!party.members.contains(targetUUID)) {
|
||||||
|
return@Route KickError.TARGET_NOT_MEMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
val senderProfile = ProfileHandler.getOrFetchProfile(playerUUID)
|
||||||
|
|
||||||
|
// check if target has profile
|
||||||
|
val targetProfile = ProfileHandler.getOrTryFetchProfile(targetUUID)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
party.members.remove(targetUUID)
|
||||||
|
PartyHandler.forget(targetUUID)
|
||||||
|
PartyRepository.saveParty(party)
|
||||||
|
|
||||||
|
if (targetProfile.synced || targetProfile.syncedAccount != null) {
|
||||||
|
val member = DiscordHandler.getMember(targetProfile)
|
||||||
|
if (member != null) {
|
||||||
|
DiscordHandler.getGuild().kickVoiceMember(member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyKick",
|
||||||
|
data = mapOf(
|
||||||
|
"PartyID" to party.id.toString(),
|
||||||
|
"SenderUUID" to playerUUID.toString(),
|
||||||
|
"SenderName" to senderProfile.getColoredUsername(),
|
||||||
|
"TargetUUID" to targetUUID.toString(),
|
||||||
|
"TargetName" to targetProfile.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val leave: Route = Route { request, response ->
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
// check if player has party
|
||||||
|
val party = PartyHandler.getPartyByPlayer(playerUUID)
|
||||||
|
?: return@Route halt(404, LeaveError.NO_PARTY.name)
|
||||||
|
|
||||||
|
val senderProfile = ProfileHandler.getOrTryFetchProfile(playerUUID)
|
||||||
|
|
||||||
|
party.members.remove(playerUUID)
|
||||||
|
PartyHandler.forget(playerUUID)
|
||||||
|
PartyRepository.saveParty(party)
|
||||||
|
|
||||||
|
if (senderProfile != null && (senderProfile.synced || senderProfile.syncedAccount != null)) {
|
||||||
|
val member = DiscordHandler.getMember(senderProfile)
|
||||||
|
if (member != null) {
|
||||||
|
DiscordHandler.getGuild().kickVoiceMember(member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyLeave",
|
||||||
|
data = mapOf(
|
||||||
|
"PartyID" to party.id.toString(),
|
||||||
|
"PlayerUUID" to playerUUID.toString(),
|
||||||
|
"PlayerName" to senderProfile?.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val chat: Route = Route { request, response ->
|
||||||
|
val playerUUID = UUID.fromString(request.queryParams("player_id"))
|
||||||
|
|
||||||
|
val party = PartyHandler.getPartyByPlayer(playerUUID)
|
||||||
|
?: return@Route halt(404, ChatError.NO_PARTY.name)
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrFetchProfile(playerUUID)
|
||||||
|
|
||||||
|
val presence = PlayerPresenceHandler.getOrFetchPresence(playerUUID)
|
||||||
|
?: return@Route halt(404, "Bad request")
|
||||||
|
|
||||||
|
val rawMessage = request.body()
|
||||||
|
val message = rawMessage.substring(1, rawMessage.length - 1)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyChat",
|
||||||
|
data = mapOf(
|
||||||
|
"PartyID" to party.id.toString(),
|
||||||
|
"SenderUUID" to playerUUID.toString(),
|
||||||
|
"SenderName" to profile.getColoredUsername(),
|
||||||
|
"SenderServer" to presence.server,
|
||||||
|
"Message" to message
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.minexd.api.module.party
|
||||||
|
|
||||||
|
import com.minexd.api.module.party.service.PartyAutoDisbandService
|
||||||
|
import com.minexd.api.module.party.service.PartyInviteExpiryService
|
||||||
|
import com.minexd.api.service.ServiceRegistry
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object PartyHandler {
|
||||||
|
|
||||||
|
val parties: MutableSet<Party> = ConcurrentHashMap.newKeySet()
|
||||||
|
private val partyById: MutableMap<UUID, Party> = ConcurrentHashMap()
|
||||||
|
private val partyByPlayer: MutableMap<UUID, Party> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
const val MAX_PARTY_SIZE = 32
|
||||||
|
val INVITE_LIFE_TIME = TimeUnit.MINUTES.toMillis(2L)
|
||||||
|
|
||||||
|
fun initialLoad() {
|
||||||
|
for (party in PartyRepository.findParties()) {
|
||||||
|
cache(party)
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceRegistry.register(PartyAutoDisbandService, 20L, 20L)
|
||||||
|
ServiceRegistry.register(PartyInviteExpiryService, 20L, 20L)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPartyById(id: UUID): Party? {
|
||||||
|
return partyById[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPartyByPlayer(player: UUID): Party? {
|
||||||
|
return partyByPlayer[player]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cache(party: Party) {
|
||||||
|
parties.add(party)
|
||||||
|
partyById[party.id] = party
|
||||||
|
|
||||||
|
for (member in party.members) {
|
||||||
|
partyByPlayer[member] = party
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forget(party: Party) {
|
||||||
|
parties.remove(party)
|
||||||
|
partyById.remove(party.id)
|
||||||
|
|
||||||
|
for (member in party.members) {
|
||||||
|
partyByPlayer.remove(member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cache(player: UUID, party: Party) {
|
||||||
|
partyByPlayer[player] = party
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forget(player: UUID) {
|
||||||
|
partyByPlayer.remove(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.minexd.api.module.party
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.Repository
|
||||||
|
import com.mongodb.BasicDBObject
|
||||||
|
import com.mongodb.client.MongoCollection
|
||||||
|
import com.mongodb.client.model.Collation
|
||||||
|
import com.mongodb.client.model.CollationStrength
|
||||||
|
import com.mongodb.client.model.IndexOptions
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import org.bson.Document
|
||||||
|
|
||||||
|
object PartyRepository : Repository {
|
||||||
|
|
||||||
|
private val PARTY_TYPE = object : TypeToken<Party>() {}.type
|
||||||
|
|
||||||
|
private lateinit var partyCollection: MongoCollection<Document>
|
||||||
|
|
||||||
|
override fun initialize() {
|
||||||
|
partyCollection = API.mongoDatabase.getCollection("party")
|
||||||
|
partyCollection.createIndex(BasicDBObject("id", 1))
|
||||||
|
partyCollection.createIndex(BasicDBObject("owner", 1))
|
||||||
|
partyCollection.createIndex(BasicDBObject("name", 1), IndexOptions().collation(Collation.builder()
|
||||||
|
.locale("en")
|
||||||
|
.collationStrength(CollationStrength.SECONDARY)
|
||||||
|
.build())) // case insensitive index for name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findParties(): List<Party> {
|
||||||
|
return arrayListOf<Party>().also { list ->
|
||||||
|
for (document in partyCollection.find()) {
|
||||||
|
list.add(Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), PARTY_TYPE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveParty(party: Party) {
|
||||||
|
partyCollection.replaceOne(Document("id", party.id.toString()), Document.parse(Serializers.gson.toJson(party, PARTY_TYPE)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteParty(party: Party) {
|
||||||
|
partyCollection.deleteOne(Document("id", party.id.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class AcceptInviteError {
|
||||||
|
|
||||||
|
NO_INVITE,
|
||||||
|
ALREADY_IN_PARTY,
|
||||||
|
PARTY_FULL,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class ChatError {
|
||||||
|
|
||||||
|
NO_PARTY,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class CreateError {
|
||||||
|
|
||||||
|
ALREADY_IN_PARTY,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class CreateInviteError {
|
||||||
|
|
||||||
|
NO_PARTY,
|
||||||
|
NOT_LEADER,
|
||||||
|
TARGET_OFFLINE,
|
||||||
|
TARGET_IGNORED,
|
||||||
|
TARGET_ALREADY_INVITED,
|
||||||
|
TARGET_ALREADY_MEMBER,
|
||||||
|
CANNOT_INVITE,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class DestroyInviteError {
|
||||||
|
|
||||||
|
NO_PARTY,
|
||||||
|
NOT_LEADER,
|
||||||
|
TARGET_NOT_INVITED,
|
||||||
|
TARGET_ALREADY_MEMBER,
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class DisbandError {
|
||||||
|
|
||||||
|
NO_PARTY,
|
||||||
|
NOT_LEADER
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class KickError {
|
||||||
|
|
||||||
|
NO_PARTY,
|
||||||
|
NOT_LEADER,
|
||||||
|
TARGET_NOT_MEMBER
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class LeaveError {
|
||||||
|
|
||||||
|
NO_PARTY
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.minexd.api.module.party.result
|
||||||
|
|
||||||
|
enum class UpdateError {
|
||||||
|
|
||||||
|
NO_PARTY,
|
||||||
|
NO_CHANGES
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.minexd.api.module.party.service
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.party.Party
|
||||||
|
import com.minexd.api.module.party.PartyHandler
|
||||||
|
import com.minexd.api.module.party.PartyRepository
|
||||||
|
import com.minexd.api.presence.PlayerPresenceHandler
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
|
||||||
|
object PartyAutoDisbandService : Runnable {
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
val disband = arrayListOf<Party>()
|
||||||
|
|
||||||
|
for (party in PartyHandler.parties) {
|
||||||
|
val onlineMembers = party.members.filter { uuid ->
|
||||||
|
val presence = PlayerPresenceHandler.getOrFetchPresence(uuid)
|
||||||
|
presence != null && presence.isOnline()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onlineMembers.isEmpty()) {
|
||||||
|
disband.add(party)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (party in disband) {
|
||||||
|
PartyHandler.forget(party)
|
||||||
|
PartyRepository.deleteParty(party)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyDisband",
|
||||||
|
data = mapOf("PartyID" to party.id.toString())
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.minexd.api.module.party.service
|
||||||
|
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.party.PartyHandler
|
||||||
|
import com.minexd.api.module.profile.ProfileHandler
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PartyInviteExpiryService : Runnable {
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
for (party in PartyHandler.parties) {
|
||||||
|
if (party.invites.isNotEmpty()) {
|
||||||
|
val expired = arrayListOf<UUID>()
|
||||||
|
|
||||||
|
for (invite in party.invites.values) {
|
||||||
|
if (invite.isExpired()) {
|
||||||
|
expired.add(invite.player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expired.isNotEmpty()) {
|
||||||
|
for (invite in expired) {
|
||||||
|
party.removeInvite(invite)
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(invite) ?: continue
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(Message(
|
||||||
|
id = "PartyInviteExpire",
|
||||||
|
data = mapOf(
|
||||||
|
"PartyID" to party.id.toString(),
|
||||||
|
"PlayerUUID" to invite.toString(),
|
||||||
|
"PlayerName" to profile.getColoredUsername()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.minexd.api.module.party.structure
|
||||||
|
|
||||||
|
import com.minexd.api.module.party.PartyHandler
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class PartyInvite(
|
||||||
|
val player: UUID,
|
||||||
|
val invitedBy: UUID,
|
||||||
|
val createdAt: Long = System.currentTimeMillis()
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun isExpired(): Boolean {
|
||||||
|
return System.currentTimeMillis() - createdAt >= PartyHandler.INVITE_LIFE_TIME
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.minexd.api.module.party.structure
|
||||||
|
|
||||||
|
enum class PartyPrivacy {
|
||||||
|
|
||||||
|
EVERYONE,
|
||||||
|
FRIENDS,
|
||||||
|
INVITED,
|
||||||
|
NOBODY
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
package com.minexd.api.module.profile
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.module.profile.grant.Grant
|
||||||
|
import com.minexd.api.module.profile.punishment.Punishment
|
||||||
|
import com.minexd.api.module.profile.punishment.PunishmentType
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
import com.minexd.api.module.profile.setting.SettingsMapSerializer
|
||||||
|
import com.minexd.api.module.profile.vote.VoteRecord
|
||||||
|
import com.minexd.api.module.rank.Rank
|
||||||
|
import com.minexd.api.module.rank.RankHandler
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
class Profile(val uuid: UUID) {
|
||||||
|
|
||||||
|
@Transient var cacheExpiry: Long? = null
|
||||||
|
|
||||||
|
val firstSeen: Long = System.currentTimeMillis()
|
||||||
|
val identities: MutableSet<String> = hashSetOf()
|
||||||
|
|
||||||
|
val grants: ArrayList<Grant> = arrayListOf()
|
||||||
|
val permissions: ArrayList<String> = arrayListOf()
|
||||||
|
val punishments: MutableList<Punishment> = arrayListOf()
|
||||||
|
|
||||||
|
var votes: MutableList<VoteRecord> = arrayListOf()
|
||||||
|
var votePoints: Int = 0
|
||||||
|
|
||||||
|
var registered: Boolean = false
|
||||||
|
var registeredEmail: String? = null
|
||||||
|
var registrationCode: String? = null
|
||||||
|
var registrationCodeExpiry: Long? = null
|
||||||
|
|
||||||
|
var synced: Boolean = false
|
||||||
|
var syncedAccount: String? = null
|
||||||
|
var syncCode: String? = null
|
||||||
|
var syncCodeExpiry: Long? = null
|
||||||
|
val coins: Int = 0
|
||||||
|
|
||||||
|
var activeTag: String? = null
|
||||||
|
|
||||||
|
@JsonAdapter(SettingsMapSerializer::class)
|
||||||
|
val settings: ConcurrentHashMap<Setting<*>, SettingOption<*>> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
fun getUsername(): String {
|
||||||
|
return API.uuidCache.name(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getColoredUsername(): String {
|
||||||
|
return getBestDisplayRank().getColor() + getUsername()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCompoundedPermissions(): Set<String> {
|
||||||
|
val permissions = HashSet<String>()
|
||||||
|
permissions.addAll(this.permissions)
|
||||||
|
permissions.addAll(RankHandler.defaultRank.getCompoundedPermissions())
|
||||||
|
|
||||||
|
getGrantsByGroups(setOf("GLOBAL"))
|
||||||
|
.filter { grant -> grant.isActive() }
|
||||||
|
.sortedBy { grant -> grant.rank.displayOrder }
|
||||||
|
.forEach { grant -> grant.rank.let { permissions.addAll(it.getCompoundedPermissions()) } }
|
||||||
|
|
||||||
|
return permissions
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMappedCompoundedPermissions(): Map<Rank, Set<String>> {
|
||||||
|
val defaultRank = RankHandler.defaultRank
|
||||||
|
|
||||||
|
val map: HashMap<Rank, HashSet<String>> = hashMapOf()
|
||||||
|
defaultRank.getMappedCompoundedPermissions(map)
|
||||||
|
|
||||||
|
getGrantsByGroups(setOf("GLOBAL"))
|
||||||
|
.filter { grant -> grant.isActive() }
|
||||||
|
.sortedBy { grant -> grant.rank.displayOrder }
|
||||||
|
.forEach { grant -> grant.rank.getMappedCompoundedPermissions(map) }
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGrantById(id: UUID): Grant? {
|
||||||
|
for (grant in grants) {
|
||||||
|
if (grant.id == id) {
|
||||||
|
return grant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGrantByRank(rank: Rank, active: Boolean): Grant? {
|
||||||
|
for (grant in grants) {
|
||||||
|
if (grant.rank == rank && (!active || grant.isActive())) {
|
||||||
|
return grant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGrantsByGroups(groups: Set<String>): List<Grant> {
|
||||||
|
val grants = arrayListOf<Grant>()
|
||||||
|
for (grant in this.grants) {
|
||||||
|
for (group in groups) {
|
||||||
|
if (grant.rank.groups.contains(group)) {
|
||||||
|
grants.add(grant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grants
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBestDisplayRank(): Rank {
|
||||||
|
return getBestDisplayGrant()?.rank ?: RankHandler.defaultRank
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBestDisplayGrant(): Grant? {
|
||||||
|
return getGrantsByGroups(setOf("GLOBAL"))
|
||||||
|
.filter { grant -> grant.isActive() && !grant.rank.hidden }
|
||||||
|
.minByOrNull { grant -> grant.rank.displayOrder }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPunishmentById(id: UUID): Punishment? {
|
||||||
|
for (punishment in punishments) {
|
||||||
|
if (punishment.uuid == id) {
|
||||||
|
return punishment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getActivePunishment(type: PunishmentType): Punishment? {
|
||||||
|
for (punishment in punishments) {
|
||||||
|
if (punishment.punishmentType == type && punishment.isActive()) {
|
||||||
|
return punishment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the user's setting option for the given [setting].
|
||||||
|
*/
|
||||||
|
fun updateSetting(setting: Setting<*>, value: SettingOption<*>) {
|
||||||
|
settings[setting] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the user's setting option for the given [setting].
|
||||||
|
*/
|
||||||
|
fun <T> getSetting(setting: Setting<T>): SettingOption<T> {
|
||||||
|
if (!settings.containsKey(setting)) {
|
||||||
|
settings[setting] = setting.newDefaultOption()
|
||||||
|
}
|
||||||
|
return settings[setting]!! as SettingOption<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,513 @@
|
||||||
|
package com.minexd.api.module.profile
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.Constants
|
||||||
|
import com.minexd.api.module.chattag.TagHandler
|
||||||
|
import com.minexd.api.module.profile.grant.Grant
|
||||||
|
import com.minexd.api.module.profile.punishment.Punishment
|
||||||
|
import com.minexd.api.module.profile.punishment.PunishmentType
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingsRegistry
|
||||||
|
import com.minexd.api.module.profile.sync.SyncError
|
||||||
|
import com.minexd.api.module.profile.vote.VoteRecord
|
||||||
|
import com.minexd.api.module.rank.RankHandler
|
||||||
|
import com.minexd.api.presence.PlayerPresenceHandler
|
||||||
|
import com.minexd.api.util.Cryptography
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import net.evilblock.pidgin.message.Message
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils
|
||||||
|
import spark.Route
|
||||||
|
import spark.kotlin.halt
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object ProfileAPI {
|
||||||
|
|
||||||
|
val get: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
return@Route ProfileHandler.getOrTryFetchProfile(uuid) ?: halt(404, "That player does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
val touch: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
|
||||||
|
if (API.debug) {
|
||||||
|
API.logger.info(Serializers.gson.toJson(ProfileHandler.getOrFetchProfile(uuid), Profile::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route ProfileHandler.getOrFetchProfile(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
val login: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileRepository.findOrCreateProfile(uuid)
|
||||||
|
|
||||||
|
val body = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
if (!body.has("ipAddress")) {
|
||||||
|
halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
val ipAddress = body.get("ipAddress").asString
|
||||||
|
val isLocalAddress = ipAddress == "localhost" || ipAddress == "127.0.0.1" || ipAddress.startsWith("192.168")
|
||||||
|
if (!isLocalAddress) {
|
||||||
|
val identity = String(Cryptography.encrypt(ipAddress.toByteArray()))
|
||||||
|
if (!profile.identities.contains(identity)) {
|
||||||
|
profile.identities.add(identity)
|
||||||
|
ProfileRepository.saveIdentity(uuid, identity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (API.debug) {
|
||||||
|
API.logger.info(Serializers.gson.toJson(profile, Profile::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route profile
|
||||||
|
}
|
||||||
|
|
||||||
|
val grant: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileRepository.findOrCreateProfile(uuid)
|
||||||
|
|
||||||
|
val grant = Serializers.gson.fromJson(request.body(), Grant::class.java)
|
||||||
|
if (grant.rank == null) {
|
||||||
|
return@Route halt(400, "That rank does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.getGrantByRank(grant.rank, active = true) != null) {
|
||||||
|
return@Route halt(400, "Player already has the ${grant.rank.id} rank")
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.grants.add(grant)
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
API.logger.info("Broadcasting grant (${grant.rank.id}) for $uuid across network")
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ProfileGrant",
|
||||||
|
data = mapOf(
|
||||||
|
"UUID" to uuid.toString(),
|
||||||
|
"Grant" to grant.id.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val revokeGrant: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileRepository.findOrCreateProfile(uuid)
|
||||||
|
|
||||||
|
val body = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
|
||||||
|
val rank = RankHandler.getRankById(body["rank"].asString) ?: return@Route halt(404)
|
||||||
|
val grant = profile.getGrantByRank(rank, active = true) ?: return@Route halt(403, "Player does not have rank")
|
||||||
|
|
||||||
|
val removedBy = UUID.fromString(body["removedBy"].asString)
|
||||||
|
val removeReason = body["removeReason"].asString
|
||||||
|
|
||||||
|
grant.removed = true
|
||||||
|
grant.removedAt = System.currentTimeMillis()
|
||||||
|
grant.removedBy = removedBy
|
||||||
|
grant.removeReason = removeReason
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
API.logger.info("Broadcasting revoke grant (${grant.rank.id}) for $uuid across network")
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ProfileGrant",
|
||||||
|
data = mapOf(
|
||||||
|
"UUID" to uuid.toString(),
|
||||||
|
"Grant" to grant.id.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val punish: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileRepository.findOrCreateProfile(uuid)
|
||||||
|
|
||||||
|
val punishment = Serializers.gson.fromJson(request.body(), Punishment::class.java)
|
||||||
|
|
||||||
|
if (punishment.punishmentType != PunishmentType.WARN)
|
||||||
|
{
|
||||||
|
if (profile.getActivePunishment(punishment.punishmentType) != null)
|
||||||
|
{
|
||||||
|
return@Route halt(403, "Player is already ${punishment.punishmentType.context}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.punishments.add(punishment)
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
val executorProfile = punishment.issuedBy?.let { ProfileRepository.findOrCreateProfile(it) }
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ProfileUpdate",
|
||||||
|
data = mapOf("UUID" to uuid.toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ExecutePunishment",
|
||||||
|
data = mapOf(
|
||||||
|
"Target" to profile.uuid.toString(),
|
||||||
|
"TargetName" to profile.getColoredUsername(),
|
||||||
|
"Executor" to executorProfile?.uuid,
|
||||||
|
"ExecutorName" to (executorProfile?.getColoredUsername() ?: Constants.CONSOLE_NAME),
|
||||||
|
"Punishment" to punishment,
|
||||||
|
"SharedAccounts" to ProfileRepository.findSharedAccounts(profile),
|
||||||
|
"Silent" to true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val pardon: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileRepository.findOrCreateProfile(uuid)
|
||||||
|
|
||||||
|
val body = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
|
||||||
|
val punishmentType = PunishmentType.valueOf(body["punishmentType"].asString)
|
||||||
|
if (punishmentType == PunishmentType.WARN) {
|
||||||
|
return@Route halt(403, "Cannot pardon a warning")
|
||||||
|
}
|
||||||
|
|
||||||
|
val punishment = profile.getActivePunishment(punishmentType)
|
||||||
|
?: return@Route halt(403, "Player is not ${punishmentType.context}")
|
||||||
|
|
||||||
|
val pardonedBy = if (body["pardonedBy"].isJsonPrimitive) {
|
||||||
|
UUID.fromString(body["pardonedBy"].asString)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val pardonReason = body["pardonReason"].asString
|
||||||
|
|
||||||
|
punishment.pardoned = true
|
||||||
|
punishment.pardonedAt = System.currentTimeMillis()
|
||||||
|
punishment.pardonedBy = pardonedBy
|
||||||
|
punishment.pardonReason = pardonReason
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
val executorProfile = punishment.pardonedBy?.let { ProfileRepository.findOrCreateProfile(it) }
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ProfileUpdate",
|
||||||
|
data = mapOf("UUID" to uuid.toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ExecutePunishment",
|
||||||
|
data = mapOf(
|
||||||
|
"Target" to profile.uuid.toString(),
|
||||||
|
"TargetName" to profile.getColoredUsername(),
|
||||||
|
"Executor" to executorProfile?.uuid,
|
||||||
|
"ExecutorName" to (executorProfile?.getColoredUsername() ?: Constants.CONSOLE_NAME),
|
||||||
|
"Punishment" to punishment,
|
||||||
|
"Silent" to true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val addPermission: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileRepository.findOrCreateProfile(uuid)
|
||||||
|
|
||||||
|
val permission = request.body().replace("\"", "")
|
||||||
|
|
||||||
|
if (!profile.permissions.contains(permission)) {
|
||||||
|
profile.permissions.add(permission)
|
||||||
|
} else {
|
||||||
|
halt(403, "Player has already been granted that permission")
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ProfileUpdate",
|
||||||
|
data = mapOf("UUID" to uuid.toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val revokePermission: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileRepository.findOrCreateProfile(uuid)
|
||||||
|
|
||||||
|
val permission = request.body().replace("\"", "")
|
||||||
|
|
||||||
|
if (profile.permissions.contains(permission)) {
|
||||||
|
profile.permissions.remove(permission)
|
||||||
|
} else {
|
||||||
|
halt(403, "Player has not been granted that permission")
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "ProfileUpdate",
|
||||||
|
data = mapOf("UUID" to uuid.toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val updateSettings: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid) ?: return@Route halt(404)
|
||||||
|
|
||||||
|
val body = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
|
||||||
|
if (!body.has("setting")) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!body.has("value")) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
val setting: Setting<*> = SettingsRegistry.find(body["setting"].asString)
|
||||||
|
?: return@Route halt(400, "Bad request")
|
||||||
|
|
||||||
|
val value = setting.deserializeOption(body.get("value"))
|
||||||
|
if (!setting.getOptions().contains(value)) {
|
||||||
|
return@Route halt(400, "Option not accepted")
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.updateSetting(setting, value)
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val getPresence: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
return@Route PlayerPresenceHandler.getOrFetchPresence(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
val getSuspensionInfo: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid)
|
||||||
|
?: return@Route halt(404, "Player is not banned")
|
||||||
|
|
||||||
|
val activeBan = ProfileRepository.findActiveBan(profile)
|
||||||
|
?: return@Route halt(404, "Player is not banned")
|
||||||
|
|
||||||
|
return@Route mapOf(
|
||||||
|
"punishment" to activeBan.second,
|
||||||
|
"relation" to activeBan.first.uuid.toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val getSharedAccounts: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid) ?: return@Route emptyList<UUID>()
|
||||||
|
return@Route ProfileRepository.findSharedAccounts(profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
val countVote: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
val record = Serializers.gson.fromJson(request.body(), VoteRecord::class.java)
|
||||||
|
profile.votes.add(record)
|
||||||
|
profile.votePoints++
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val startSync: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
if (profile.synced || profile.syncedAccount != null) {
|
||||||
|
return@Route halt(400, SyncError.ALREADY_SYNCED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.syncCode != null && System.currentTimeMillis() < profile.syncCodeExpiry!!) {
|
||||||
|
return@Route halt(400, SyncError.CODE_ALREADY_SENT.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.syncCode = RandomStringUtils.random(8, 0, 0, true, true, null, Cryptography.secureRandom)
|
||||||
|
profile.syncCodeExpiry = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3L)
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
response.status(201)
|
||||||
|
response.body(profile.syncCode)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val finishSync: Route = Route { request, response ->
|
||||||
|
val data = Serializers.gson.fromJson(request.body(), JsonObject::class.java)
|
||||||
|
if (!data.has("code")) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.has("account")) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
if (profile.synced || profile.syncedAccount != null) {
|
||||||
|
return@Route halt(400, SyncError.ALREADY_SYNCED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.syncCode != data["code"].asString) {
|
||||||
|
return@Route halt(400, SyncError.CODE_INVALID.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() >= profile.syncCodeExpiry!!) {
|
||||||
|
return@Route halt(400, SyncError.CODE_EXPIRED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.syncCode = null
|
||||||
|
profile.syncCodeExpiry = null
|
||||||
|
profile.syncedAccount = data["account"].asString
|
||||||
|
profile.synced = true
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
API.pidgin.sendMessage(
|
||||||
|
Message(
|
||||||
|
id = "AccountSynced",
|
||||||
|
data = mapOf(
|
||||||
|
"PlayerUUID" to profile.uuid.toString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val resetSync: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
profile.synced = false
|
||||||
|
profile.syncedAccount = null
|
||||||
|
profile.syncCode = null
|
||||||
|
profile.syncCodeExpiry = null
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val findBySyncCode: Route = Route { request, response ->
|
||||||
|
if (!request.queryParams().contains("code")) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
val profile = ProfileRepository.findProfileBySyncCode(request.queryParams("code"))
|
||||||
|
println(profile != null)
|
||||||
|
|
||||||
|
return@Route profile ?: halt(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
val findBySyncedAccount: Route = Route { request, response ->
|
||||||
|
if (!request.queryParams().contains("id")) {
|
||||||
|
return@Route halt(400, "Bad request")
|
||||||
|
}
|
||||||
|
|
||||||
|
return@Route ProfileRepository.findProfileBySyncedAccount(request.queryParams("id")) ?: halt(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
val setTag: Route = Route { request, response ->
|
||||||
|
val uuid = UUID.fromString(request.params(":uuid"))
|
||||||
|
|
||||||
|
val profile = ProfileHandler.getOrTryFetchProfile(uuid)
|
||||||
|
?: return@Route halt(404)
|
||||||
|
|
||||||
|
// for SOME reason the requester in CoreXD is sending "" wrapped around the body?!
|
||||||
|
// see https://github.com/square/retrofit/issues/1210
|
||||||
|
// &&
|
||||||
|
// https://stackoverflow.com/questions/36904477/retrofit-2-multipart-post-request-sends-extra-quotes-to-php/36907435#36907435
|
||||||
|
val tag = request.body().replace("\"", "")
|
||||||
|
|
||||||
|
API.logger.info("setTag request $uuid, $tag")
|
||||||
|
API.logger.info(TagHandler.getTags().map { it.name }.toString())
|
||||||
|
|
||||||
|
if (tag.isBlank()) {
|
||||||
|
profile.activeTag = null
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val validTagObj = TagHandler.getByName(tag)
|
||||||
|
|
||||||
|
if (validTagObj == null) {
|
||||||
|
return@Route halt(400, "That tag does not exist.")
|
||||||
|
} else {
|
||||||
|
profile.activeTag = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileRepository.saveProfile(profile)
|
||||||
|
|
||||||
|
response.status(200)
|
||||||
|
return@Route response
|
||||||
|
}
|
||||||
|
|
||||||
|
val famousList = Route { request, response ->
|
||||||
|
return@Route JsonObject().also { json ->
|
||||||
|
for ((rank, players) in ProfileHandler.famous.entries) {
|
||||||
|
json.add(rank.id, JsonArray().also { playerArray ->
|
||||||
|
for (player in players) {
|
||||||
|
playerArray.add(Serializers.gson.toJsonTree(player))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.minexd.api.module.profile
|
||||||
|
|
||||||
|
import com.minexd.api.module.rank.Rank
|
||||||
|
import com.minexd.api.module.rank.RankRepository
|
||||||
|
import com.minexd.api.module.staff.Staff
|
||||||
|
import com.minexd.api.module.staff.StaffHandler
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object ProfileHandler {
|
||||||
|
|
||||||
|
private val profiles: MutableMap<UUID, Profile> = ConcurrentHashMap()
|
||||||
|
|
||||||
|
val famous: MutableMap<Rank,List<UUID>> = ConcurrentHashMap()
|
||||||
|
var famousMembers: MutableSet<UUID> = ConcurrentHashMap.newKeySet()
|
||||||
|
|
||||||
|
fun load() {
|
||||||
|
for (rank in RankRepository.findRanks().filter{it.isFamous()}) {
|
||||||
|
this.famous[rank] = ProfileRepository.findProfilesByRank(rank).map{it.uuid}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newMembersSet = ConcurrentHashMap.newKeySet<UUID>()
|
||||||
|
for (famousList in this.famous.values) {
|
||||||
|
for (famous in famousList) {
|
||||||
|
newMembersSet.add(famous)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.famousMembers = newMembersSet
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLoadedProfiles(): Collection<Profile> {
|
||||||
|
return profiles.values
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearLoadedProfiles() {
|
||||||
|
profiles.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isProfileLoaded(uuid: UUID): Boolean {
|
||||||
|
return profiles.containsKey(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getProfile(uuid: UUID): Profile {
|
||||||
|
return profiles[uuid]!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOrTryFetchProfile(uuid: UUID): Profile? {
|
||||||
|
return if (isProfileLoaded(uuid)) {
|
||||||
|
getProfile(uuid)
|
||||||
|
} else {
|
||||||
|
return ProfileRepository.findProfileById(uuid)?.also { profile ->
|
||||||
|
profile.cacheExpiry = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1L)
|
||||||
|
cacheProfile(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOrFetchProfile(uuid: UUID): Profile {
|
||||||
|
return if (isProfileLoaded(uuid)) {
|
||||||
|
getProfile(uuid)
|
||||||
|
} else {
|
||||||
|
return ProfileRepository.findOrCreateProfile(uuid).also { profile ->
|
||||||
|
profile.cacheExpiry = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1L)
|
||||||
|
cacheProfile(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheProfile(profile: Profile) {
|
||||||
|
profiles[profile.uuid] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forgetProfile(profile: Profile) {
|
||||||
|
profiles.remove(profile.uuid)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
package com.minexd.api.module.profile
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import com.minexd.api.API
|
||||||
|
import com.minexd.api.Repository
|
||||||
|
import com.minexd.api.module.profile.grant.GrantQueryResult
|
||||||
|
import com.minexd.api.module.profile.punishment.Punishment
|
||||||
|
import com.minexd.api.module.profile.punishment.PunishmentQueryResult
|
||||||
|
import com.minexd.api.module.profile.punishment.PunishmentType
|
||||||
|
import com.minexd.api.module.rank.Rank
|
||||||
|
import com.mongodb.BasicDBObject
|
||||||
|
import com.mongodb.client.MongoCollection
|
||||||
|
import com.mongodb.client.model.*
|
||||||
|
import net.evilblock.cubed.serializers.Serializers
|
||||||
|
import org.bson.Document
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object ProfileRepository : Repository {
|
||||||
|
|
||||||
|
private val PROFILE_TYPE = object : TypeToken<Profile>() {}.type
|
||||||
|
|
||||||
|
private lateinit var profilesCol: MongoCollection<Document>
|
||||||
|
|
||||||
|
override fun initialize() {
|
||||||
|
profilesCol = API.mongoDatabase.getCollection("profiles")
|
||||||
|
profilesCol.createIndex(BasicDBObject("uuid", 1))
|
||||||
|
profilesCol.createIndex(BasicDBObject("registeredEmail", 1), IndexOptions().collation(
|
||||||
|
Collation.builder()
|
||||||
|
.locale("en")
|
||||||
|
.collationStrength(CollationStrength.SECONDARY)
|
||||||
|
.build()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserializeDocument(document: Document): Profile {
|
||||||
|
return (Serializers.gson.fromJson(document.toJson(Serializers.writerSettings), PROFILE_TYPE) as Profile).also { profile ->
|
||||||
|
if (profile.votes == null) profile.votes = arrayListOf()
|
||||||
|
profile.grants.removeIf {
|
||||||
|
it.rank == null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findProfileById(player: UUID): Profile? {
|
||||||
|
return deserializeDocument(profilesCol.find(Document("uuid", player.toString())).first() ?: return null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findProfileByEmail(email: String): Profile? {
|
||||||
|
val query = Document().also { query ->
|
||||||
|
query.append("registered", true)
|
||||||
|
query.append("registeredEmail", email)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deserializeDocument(profilesCol.find(query).first() ?: return null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findProfileByRegistrationCode(code: String): Profile? {
|
||||||
|
return deserializeDocument(profilesCol.find(Document("registrationCode", code)).first() ?: return null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findProfileBySyncCode(code: String): Profile? {
|
||||||
|
return deserializeDocument(profilesCol.find(Document("syncCode", code)).first() ?: return null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findProfileBySyncedAccount(code: String): Profile? {
|
||||||
|
return deserializeDocument(profilesCol.find(Document("syncedAccount", code)).first() ?: return null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findOrCreateProfile(player: UUID): Profile {
|
||||||
|
return ProfileHandler.getOrTryFetchProfile(player) ?: Profile(player).also { newProfile ->
|
||||||
|
saveProfile(newProfile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveProfile(profile: Profile) {
|
||||||
|
profilesCol.replaceOne(Document("uuid", profile.uuid.toString()), Document.parse(Serializers.gson.toJson(profile, PROFILE_TYPE)), ReplaceOptions().upsert(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveIdentity(uuid: UUID, identity: String) {
|
||||||
|
profilesCol.updateOne(Document("uuid", uuid.toString()), Document("\$push", Document("identities", identity)), UpdateOptions().upsert(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findSharedAccounts(profile: Profile): List<UUID> {
|
||||||
|
val identities = profile.identities
|
||||||
|
if (identities.isEmpty()) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrayListOf<UUID>().also { results ->
|
||||||
|
for (matchingDocument in profilesCol.find(Document("identities", identities))) {
|
||||||
|
val uuid = UUID.fromString(matchingDocument.getString("uuid"))
|
||||||
|
if (uuid != null && !results.contains(uuid) && uuid != profile.uuid) {
|
||||||
|
results.add(uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findActiveBan(profile: Profile): Pair<Profile, Punishment>? {
|
||||||
|
profile.getActivePunishment(PunishmentType.BLACKLIST)?.also { blacklist ->
|
||||||
|
return Pair(profile, blacklist)
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.getActivePunishment(PunishmentType.BAN)?.also { ban ->
|
||||||
|
return Pair(profile, ban)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sharedAccounts = findSharedAccounts(profile)
|
||||||
|
if (sharedAccounts.isNotEmpty()) {
|
||||||
|
for (altId in sharedAccounts) {
|
||||||
|
val altProfile = findOrCreateProfile(altId)
|
||||||
|
|
||||||
|
altProfile.getActivePunishment(PunishmentType.BLACKLIST)?.also { blacklist ->
|
||||||
|
return Pair(altProfile, blacklist)
|
||||||
|
}
|
||||||
|
|
||||||
|
altProfile.getActivePunishment(PunishmentType.BAN)?.also { ban ->
|
||||||
|
return Pair(altProfile, ban)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findGrantsIssuedBy(uuid: UUID): List<GrantQueryResult> {
|
||||||
|
return arrayListOf<GrantQueryResult>().also { results ->
|
||||||
|
val query = Document("grants", Document("\$elemMatch", Document("issuedBy", uuid.toString())))
|
||||||
|
for (matchingDocument in profilesCol.find(query)) {
|
||||||
|
val profile = deserializeDocument(matchingDocument)
|
||||||
|
for (grant in profile.grants) {
|
||||||
|
if (grant.issuedBy == uuid) {
|
||||||
|
results.add(GrantQueryResult(profile, grant))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findPunishmentsIssuedBy(uuid: UUID): List<PunishmentQueryResult> {
|
||||||
|
return arrayListOf<PunishmentQueryResult>().also { results ->
|
||||||
|
val query = Document("punishments", Document("\$elemMatch", Document("issuedBy", uuid.toString())))
|
||||||
|
for (matchingDocument in profilesCol.find(query)) {
|
||||||
|
val profile = deserializeDocument(matchingDocument)
|
||||||
|
for (punishment in profile.punishments) {
|
||||||
|
if (punishment.issuedBy == uuid) {
|
||||||
|
results.add(PunishmentQueryResult(profile, punishment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findProfilesByRank(rank: Rank): List<Profile> {
|
||||||
|
return arrayListOf<Profile>().also { results ->
|
||||||
|
val query = Document("grants", Document("\$elemMatch", Document("rank", rank.id).append("removed", false)))
|
||||||
|
for (matchingDocument in profilesCol.find(query)) {
|
||||||
|
val profile = deserializeDocument(matchingDocument)
|
||||||
|
val bestGrant = profile.getBestDisplayGrant() ?: continue
|
||||||
|
if (bestGrant.rank == rank) {
|
||||||
|
results.add(profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.minexd.api.module.profile.grant
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter
|
||||||
|
import com.minexd.api.module.rank.Rank
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
open class Grant(
|
||||||
|
@JsonAdapter(Rank.RankReferenceSerializer::class)
|
||||||
|
var rank: Rank,
|
||||||
|
var issuedBy: UUID? = null,
|
||||||
|
val reason: String,
|
||||||
|
var expiresAt: Long? = null
|
||||||
|
) {
|
||||||
|
|
||||||
|
val id: UUID = UUID.randomUUID()
|
||||||
|
val issuedAt: Long = System.currentTimeMillis()
|
||||||
|
|
||||||
|
var removed: Boolean = false
|
||||||
|
var removedBy: UUID? = null
|
||||||
|
var removedAt: Long? = null
|
||||||
|
var removeReason: String? = null
|
||||||
|
|
||||||
|
fun isActive(): Boolean {
|
||||||
|
return removedAt == null && (expiresAt == null || System.currentTimeMillis() < expiresAt!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.minexd.api.module.profile.grant
|
||||||
|
|
||||||
|
import com.minexd.api.module.profile.Profile
|
||||||
|
|
||||||
|
class GrantQueryResult(val profile: Profile, val grant: Grant)
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.minexd.api.module.profile.punishment
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class Punishment(val uuid: UUID = UUID.randomUUID(), val punishmentType: PunishmentType) {
|
||||||
|
|
||||||
|
var reason: String = ""
|
||||||
|
var issuedBy: UUID? = null
|
||||||
|
val issuedAt: Long = System.currentTimeMillis()
|
||||||
|
var expiresAt: Long? = null
|
||||||
|
|
||||||
|
var pardoned: Boolean = false
|
||||||
|
var pardonReason: String? = null
|
||||||
|
var pardonedBy: UUID? = null
|
||||||
|
var pardonedAt: Long? = null
|
||||||
|
|
||||||
|
fun isActive(): Boolean {
|
||||||
|
return !pardoned && (expiresAt == null || System.currentTimeMillis() < expiresAt!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPermanent(): Boolean {
|
||||||
|
return expiresAt == null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDuration(): Long {
|
||||||
|
return expiresAt!! - issuedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRemainingTime(): Long {
|
||||||
|
return expiresAt!! - System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.minexd.api.module.profile.punishment
|
||||||
|
|
||||||
|
import com.minexd.api.module.profile.Profile
|
||||||
|
|
||||||
|
data class PunishmentQueryResult(val profile: Profile, val punishment: Punishment)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.minexd.api.module.profile.punishment
|
||||||
|
|
||||||
|
enum class PunishmentType(val context: String) {
|
||||||
|
|
||||||
|
BLACKLIST("blacklisted"),
|
||||||
|
BAN("banned"),
|
||||||
|
MUTE("muted"),
|
||||||
|
WARN("warned")
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.minexd.api.module.profile.setting
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
|
||||||
|
abstract class Setting<T> {
|
||||||
|
|
||||||
|
val cached: SettingOption<T> = newDefaultOption()
|
||||||
|
|
||||||
|
abstract fun newDefaultOption(): SettingOption<T>
|
||||||
|
|
||||||
|
abstract fun getOptions(): List<SettingOption<T>>
|
||||||
|
|
||||||
|
abstract fun deserializeOption(value: JsonElement): SettingOption<T>
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020. Joel Evans
|
||||||
|
*
|
||||||
|
* Use and or redistribution of compiled JAR file and or source code is permitted only if given
|
||||||
|
* explicit permission from original author: Joel Evans
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.minexd.api.module.profile.setting
|
||||||
|
|
||||||
|
import net.evilblock.cubed.serializers.impl.AbstractTypeSerializable
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
interface SettingOption<T> : AbstractTypeSerializable {
|
||||||
|
|
||||||
|
fun getValue(): T
|
||||||
|
|
||||||
|
override fun getAbstractType(): Type {
|
||||||
|
return this::class.java
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.minexd.api.module.profile.setting
|
||||||
|
|
||||||
|
import com.google.gson.*
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
class SettingsMapSerializer : JsonSerializer<ConcurrentHashMap<Setting<*>, SettingOption<*>>>, JsonDeserializer<ConcurrentHashMap<Setting<*>, SettingOption<*>>> {
|
||||||
|
|
||||||
|
override fun serialize(map: ConcurrentHashMap<Setting<*>, SettingOption<*>>, type: Type, context: JsonSerializationContext): JsonElement {
|
||||||
|
return JsonObject().also { json ->
|
||||||
|
for ((key, value) in map) {
|
||||||
|
json.add(key.javaClass.simpleName, context.serialize(value.getValue()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(json: JsonElement, type: Type, context: JsonDeserializationContext): ConcurrentHashMap<Setting<*>, SettingOption<*>> {
|
||||||
|
return ConcurrentHashMap<Setting<*>, SettingOption<*>>().also { map ->
|
||||||
|
for ((key, value) in json.asJsonObject.entrySet()) {
|
||||||
|
try {
|
||||||
|
val setting = SettingsRegistry.find(key) ?: continue
|
||||||
|
val settingValue = setting.deserializeOption(value)
|
||||||
|
map[setting] = settingValue
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.minexd.api.module.profile.setting
|
||||||
|
|
||||||
|
import com.minexd.api.module.profile.setting.impl.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
object SettingsRegistry {
|
||||||
|
|
||||||
|
private val registered: ConcurrentHashMap<String, Setting<*>> = ConcurrentHashMap<String, Setting<*>>().also { map ->
|
||||||
|
listOf(
|
||||||
|
FriendRequestsSetting,
|
||||||
|
FriendsStreamSetting,
|
||||||
|
LobbyPlayerVisibilitySetting,
|
||||||
|
MessagingSoundsSetting,
|
||||||
|
PartyRequestsSetting,
|
||||||
|
PresenceVisibilitySetting,
|
||||||
|
PrivateMessagesSetting,
|
||||||
|
StaffReportsSetting,
|
||||||
|
StaffVanishSetting
|
||||||
|
).forEach { map[it.javaClass.simpleName] = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun find(className: String): Setting<*>? {
|
||||||
|
return registered[className]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object FriendRequestsSetting : Setting<FriendRequestsSetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): Option {
|
||||||
|
return Option(OptionValue.ALLOW)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
ALLOW,
|
||||||
|
DENY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object FriendsStreamSetting : Setting<FriendsStreamSetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): Option {
|
||||||
|
return Option(OptionValue.SHOW)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
SHOW,
|
||||||
|
HIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object LobbyPlayerVisibilitySetting : Setting<LobbyPlayerVisibilitySetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): Option {
|
||||||
|
return Option(OptionValue.EVERYONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
EVERYONE,
|
||||||
|
FRIENDS,
|
||||||
|
NOBODY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object MessagingSoundsSetting : Setting<MessagingSoundsSetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): Option {
|
||||||
|
return Option(OptionValue.PLAY_SOUND)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
PLAY_SOUND,
|
||||||
|
NO_SOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object PartyRequestsSetting : Setting<PartyRequestsSetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.EVERYONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
EVERYONE,
|
||||||
|
FRIENDS,
|
||||||
|
NOBODY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object PresenceVisibilitySetting : Setting<PresenceVisibilitySetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): Option {
|
||||||
|
return Option(OptionValue.EVERYONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
EVERYONE,
|
||||||
|
FRIENDS,
|
||||||
|
NOBODY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object PrivateMessagesSetting : Setting<PrivateMessagesSetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): Option {
|
||||||
|
return Option(OptionValue.EVERYONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
EVERYONE,
|
||||||
|
FRIENDS,
|
||||||
|
NOBODY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object StaffReportsSetting : Setting<StaffReportsSetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.SHOW)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<SettingOption<OptionValue>> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
SHOW,
|
||||||
|
HIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.minexd.api.module.profile.setting.impl
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.minexd.api.module.profile.setting.Setting
|
||||||
|
import com.minexd.api.module.profile.setting.SettingOption
|
||||||
|
|
||||||
|
object StaffVanishSetting : Setting<StaffVanishSetting.OptionValue>() {
|
||||||
|
|
||||||
|
override fun newDefaultOption(): Option {
|
||||||
|
return Option(OptionValue.VISIBLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptions(): List<Option> {
|
||||||
|
return OptionValue.values().map { Option(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserializeOption(value: JsonElement): SettingOption<OptionValue> {
|
||||||
|
return Option(OptionValue.valueOf(value.asString))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Option(val option: OptionValue) : SettingOption<OptionValue> {
|
||||||
|
override fun getValue(): OptionValue {
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is Option && other.getValue() == getValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return option.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class OptionValue {
|
||||||
|
VISIBLE,
|
||||||
|
HIDDEN
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.minexd.api.module.profile.social
|
||||||
|
|
||||||
|
enum class SocialMediaType {
|
||||||
|
|
||||||
|
YOUTUBE,
|
||||||
|
TWITCH,
|
||||||
|
TWITTER,
|
||||||
|
INSTAGRAM,
|
||||||
|
DISCORD,
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue