diff --git a/.classpath b/.classpath deleted file mode 100644 index 400804d..0000000 --- a/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml new file mode 100644 index 0000000..08b114e --- /dev/null +++ b/.github/workflows/github-actions.yml @@ -0,0 +1,22 @@ +name: Build and deploy +on: [push] +jobs: + Build-and-deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + - name: Build with Gradle + run: ./gradlew build + - name: Deploy jar to server + uses: garygrossgarten/github-action-scp@release + with: + local: PickupBot.jar + remote: /home/pug/PickupDiscord/PickupBot.jar + host: ${{ secrets.HOST }} + username: ${{ secrets.SSH_USER }} + password: ${{ secrets.SSH_PASSWORD }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8d9372e..5613d62 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # Log file *.log +*.lck # BlueJ files *.ctxt @@ -21,3 +22,18 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* /bin/ + +# config file +*.json +*.env + +# Gradle +.gradle/ +gradle/ + +#IntelliJ +.idea/ +build/ + +#Database +*.db diff --git a/.project b/.project deleted file mode 100644 index 171c23f..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - PickupBotDiscord - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index d17b672..0000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,12 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.7 diff --git a/CHANGELOG.txt b/CHANGELOG.txt deleted file mode 100644 index ec4b0c1..0000000 --- a/CHANGELOG.txt +++ /dev/null @@ -1,42 +0,0 @@ -[20180606] -- allow admin cmds from admin channel -- added "warning" for first noshow -- added multiqueue option (todo: help msgs) - -[20180603] -- fixed player recognized as banned -- added accuracy to timeToString -- fixed inability to add while info is sent -- used player status (online, idle, ...) as indicator for AFK check -- allow players to change vote -- added map to normal aftermath msg -- created special aftermath msg for abandoned matches - -[20180528] -- fixed several bugs regarding ban system (fml parsing :c) - -[20180523] -- improved ban message -- added !baninfo cmd - -[20180520] -- added ban system (untested) -- keep gameconfig when re-enabling gametype -- threading match start - -[20180511] -- show most voted map in bold -- display [0/10] PLAYER removed. -- ability to group !status msg for more than one gametype -- reduce of spam on !map for more than one gametype -- use avg elo when creating a new player -- added afk check - -[20180428] -- added win percentage to !elo -- added !live -- added !match - - -SOON: -!map \ No newline at end of file diff --git a/README.md b/README.md index 2d36d82..3a556ba 100644 --- a/README.md +++ b/README.md @@ -1 +1,73 @@ -# PickupDiscord \ No newline at end of file +[![urtpickup](https://i.imgur.com/f9DaZDT.png)](https://discord.gg/An8hxdM) + +# UrT-Pickup +[![Discord](https://img.shields.io/discord/117622053061787657)](https://discord.gg/An8hxdM) + + +## About +This is a network of Urban Terror servers associating players through [Discord](https://discord.gg/An8hxdM) thanks to a simple java bot. + +From this discord server you can: +- play competitive matchmaking in any team-based game type, +- challenge other users in individual modes such as GunGame, LMS or KnockOut matches :resurgence:, +- improve your skills with the SkeetShoot and AimTraining modes both with record tracking :resurgence:, +- request a game server for a few hours for your personal use. + + +## Commands + +### User Commands +- !add +- !remove +- !maps displays the map list for each gametype. +- !map +- !status to get information on the queues. +- !help +- !surrender to abandon your match. +- !live sends info on the live matches. +- !pick <1/2> +- !votes to get the current votes. +- !register +- !country See:` +- !elo +- !stats +- !top10 displays the top 10 players +- !topcountries ordered by average ELO +- !topwin: players with the best win ratio +- !topkdr: players with the best KDR +- !match +- !last + + +### Admin Commands + +- !lock to prevent commands from PUBLIC channel. +- !unlock +- !reset +- !reboot +- !getdata +- !enablemap +- !disablemap +- !rcon +- !forceadd +- !enablegametype +- !disablegametype +- !showgameconfig +- !ban (duration=1y1M1w1d1h1m1s) +- !unban +- !baninfo +- !showservers +- !addserver +- !enableserver +- !disableserver +- !updateserver +- !showmatches displays the queues AND live matches +- !unregister +- !country +- !addchannel <#name> +- !removechannel <#name> +- !addrole <@role> +- !removerole <@role> + + +[![love](https://forthebadge.com/images/badges/built-with-love.svg)](https://forthebadge.com) [![java](https://forthebadge.com/images/badges/made-with-java.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/powered-by-black-magic.svg)](https://forthebadge.com) diff --git a/TODO.txt b/TODO.txt index b6a0303..5900198 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,4 +1,14 @@ TODO: +URGENT: +/// +- !ringer function +- !game ID -> match details. +\\\ + +- gtv (needs !enablegtv/!disablegtv option) +- !pro +- Enhanced 2v2 with mate !2v2 @gost0r to play your 2v2 with him. + ( ) rework match abort/cancel/end behaviour to be more consistent ( ) !showmatch starttime @@ -84,13 +94,13 @@ approach: Remaining issue: e.g.: TeamSize=4, 1x 3p, 2x 2p : solution: 2 2 vs 3 1 = algo: 3 vs 2 1 => break up 3rd team -REPORT SYSTEM: -!report auth leave/noshow/insult/troll -to defend yourself: !excuse -if 3 games a report -msg to admins to review case !review -!accept !decline -suspended for 1d, 3d, 1w, 2w, 1m, 3m, perm -show bans and time !banlist -ability to ban ppl as admin !permban ? if temp bans wished: !ban <1d> ex.? -end bans: !pardon +//REPORT SYSTEM: +//!report auth leave/noshow/insult/troll +//to defend yourself: !excuse +//if 3 games a report +//msg to admins to review case !review +//!accept !decline +//suspended for 1d, 3d, 1w, 2w, 1m, 3m, perm +//show bans and time !banlist +//ability to ban ppl as admin !permban ? if temp bans wished: !ban <1d> ex.? +//end bans: !pardon diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..9596b1a --- /dev/null +++ b/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'java' +} + +group 'org.example' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +sourceSets { + main { + java { + srcDirs = ['src'] + } + } +} + +dependencies { + implementation 'io.sentry:sentry:1.7.23' + implementation 'commons-io:commons-io:2.6' + implementation 'org.apache.commons:commons-lang3:3.10' + implementation 'javax.websocket:javax.websocket-api:1.1' + implementation 'org.json:json:20190722' + implementation 'org.xerial:sqlite-jdbc:3.8.11.2' + implementation 'org.glassfish.tyrus.bundles:tyrus-standalone-client:1.9' + implementation 'org.slf4j:slf4j-simple:1.7.9' + implementation 'io.github.cdimascio:dotenv-java:2.2.0' +} + +jar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + manifest { + attributes 'Main-Class' : 'de.gost0r.pickupbot.PickupBotDiscordMain' + } + destinationDirectory.set(file("$rootDir/")) + archiveName 'PickupBot.jar' + + from { + configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} diff --git a/cmds.txt b/cmds.txt deleted file mode 100644 index 6705d3f..0000000 --- a/cmds.txt +++ /dev/null @@ -1,33 +0,0 @@ - -CMD PARMS LEVEL EXPLANATION -!add authed - -!remove - authed -!maps - authed -!map added -!status - - -!help - - -!lock admin -!unlock admin -!reset admin avi in channel -!getdata admin -!enablemap admin -!disablemap admin -!rcon admin - -!enablegametype admin -!disablegametype admin - -!register - -!elo authed -!top5 authed - -!showservers admin -!addserver admin -!enableserver admin -!disableserver admin -!updateserver admin - - -!showroles -!showadminroles \ No newline at end of file diff --git a/config.json b/config.json deleted file mode 100644 index 29a3444..0000000 --- a/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "token": "TOKEN" -} \ No newline at end of file diff --git a/configs/1V1.cfg b/configs/1V1.cfg new file mode 100644 index 0000000..c6e9d0c --- /dev/null +++ b/configs/1V1.cfg @@ -0,0 +1,24 @@ +set sv_dlURL "maps.pugbot.net" +set sv_floodprotect "8" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_sayprefix "^4MissTS^3: " +set sv_timeout "60" +sets " Discord" "www.discord.io/urbanterror" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 618 2797" + +set fraglimit "15" +set timelimit "0" +set g_allowVote "31" +set g_friendlyFire "2" +set g_gametype "4" +set g_gear "" +set g_inactivity "40" +set g_inactivityAction "0" +set g_matchmode "1" +set g_maxrounds "0" +set g_roundtime "1" +set g_strattime "0" +set g_swaproles "0" \ No newline at end of file diff --git a/configs/2V2.cfg b/configs/2V2.cfg new file mode 100644 index 0000000..c6e9d0c --- /dev/null +++ b/configs/2V2.cfg @@ -0,0 +1,24 @@ +set sv_dlURL "maps.pugbot.net" +set sv_floodprotect "8" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_sayprefix "^4MissTS^3: " +set sv_timeout "60" +sets " Discord" "www.discord.io/urbanterror" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 618 2797" + +set fraglimit "15" +set timelimit "0" +set g_allowVote "31" +set g_friendlyFire "2" +set g_gametype "4" +set g_gear "" +set g_inactivity "40" +set g_inactivityAction "0" +set g_matchmode "1" +set g_maxrounds "0" +set g_roundtime "1" +set g_strattime "0" +set g_swaproles "0" \ No newline at end of file diff --git a/configs/AIM.cfg b/configs/AIM.cfg new file mode 100644 index 0000000..b81a0cb --- /dev/null +++ b/configs/AIM.cfg @@ -0,0 +1,36 @@ +set sv_dlURL "maps.pugbot.net" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_timeout "90" +sets " Discord" "www.discord.io/urbanterror" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 618 2797" +set bot_enable "1" +map ut4_aimtraining_b1 + +set fraglimit "0" +set timelimit "5" +set g_allowVote "31" +set g_gametype "3" +set g_gear "" +set g_matchmode "1" +set g_respawnProtection "0" +set g_respawndelay "0" +set g_forcerespawn "1" +set g_waveRespawns "0" +set g_swaproles "0" +set g_autobalance "0" +set bot_enable "1" +set g_nextmap "ut4_aimtraining_b1" + +addbot del4 1 Red 10 adn`Holycrap +addbot del4 1 Red 10 pwnz.Tarquas +addbot del3 1 Red 10 MQCD|Biddle +addbot del3 1 Red 10 MQCD|Asloon +addbot del2 1 Red 10 hg`.Gost0r +addbot del2 1 Red 10 hg`.Hashbolla +addbot del1 1 Red 50 GlaD-Solitary +addbot del1 1 Red 50 GlaD-slackin +addbot del1 1 Red 90 strayA#Hypperz +addbot del1 1 Red 90 strayA#Delirium \ No newline at end of file diff --git a/configs/BM.cfg b/configs/BM.cfg new file mode 100644 index 0000000..a70dec7 --- /dev/null +++ b/configs/BM.cfg @@ -0,0 +1,28 @@ +set sv_dlURL "maps.pugbot.net" +set sv_floodprotect "8" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_sayprefix "^4MissBM^3: " +set sv_timeout "60" +sets " Discord" "www.discord.io/urbanterror" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 2797" + +set fraglimit "11" +set timelimit "0" +set g_allowVote "12" +set g_friendlyFire "2" +set g_gametype "8" +set g_gear "" +set g_inactivity "40" +set g_inactivityAction "0" +set g_matchmode "1" +set g_maxrounds "10" +set g_roundtime "1.5" +set g_strattime "3" +set g_suddendeath "0" +set g_swaproles "1" +set g_timeouts "0" +seta g_bombdefusetime "7" +seta g_bombexplodetime "35" \ No newline at end of file diff --git a/configs/CTF.cfg b/configs/CTF.cfg new file mode 100644 index 0000000..69cb772 --- /dev/null +++ b/configs/CTF.cfg @@ -0,0 +1,31 @@ +set sv_dlURL "maps.pugbot.net" +set sv_floodprotect "8" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_sayprefix "^4MissCTF^3: " +set sv_timeout "60" +sets " Discord" "www.discord.gg/An8hxdM" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 618 2797" + +set capturelimit "0" +set fraglimit "0" +set timelimit "10" +set g_allowVote "8" +set g_friendlyFire "2" +set g_gametype "7" +set g_gear "KNZiQ" +set g_inactivity "40" +set g_inactivityAction "0" +set g_matchmode "1" +set g_respawnProtection "2" +set g_strattime "3" +set g_suddendeath "0" +set g_swaproles "1" +set g_timeouts "0" +set g_flagReturnTime "30" +set g_hotpotato "1" +set g_waveRespawns "1" +set g_redWave "15" +set g_blueWave "15" \ No newline at end of file diff --git a/configs/DIV1.cfg b/configs/DIV1.cfg new file mode 100644 index 0000000..b278ba2 --- /dev/null +++ b/configs/DIV1.cfg @@ -0,0 +1,26 @@ +set sv_dlURL "maps.pugbot.net" +set sv_floodprotect "8" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_sayprefix "^4MissTS^3: " +set sv_timeout "60" +sets " Discord" "www.discord.io/urbanterror" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 2797" + +set fraglimit "0" +set timelimit "20" +set g_allowVote "13" +set g_friendlyFire "2" +set g_gametype "4" +set g_gear "KQ" +set g_inactivity "40" +set g_inactivityAction "0" +set g_matchmode "1" +set g_maxrounds "0" +set g_roundtime "1.5" +set g_strattime "3" +set g_suddendeath "1" +set g_swaproles "0" +set g_timeouts "0" \ No newline at end of file diff --git a/configs/SKEET.cfg b/configs/SKEET.cfg new file mode 100644 index 0000000..c0b9e12 --- /dev/null +++ b/configs/SKEET.cfg @@ -0,0 +1,47 @@ +set sv_dlURL "maps.pugbot.net" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_timeout "90" +sets " Discord" "www.discord.io/urbanterror" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 618 2797" +set bot_enable "1" +map ut4_skeetshoot_pug + +set fraglimit "0" +set timelimit "5" +set g_allowVote "31" +set g_gametype "3" +set g_gear "" +set g_matchmode "1" +set g_respawnProtection "0" +set g_respawndelay "0" +set g_forcerespawn "1" +set g_waveRespawns "0" +set g_swaproles "0" +set g_autobalance "0" +set bot_enable "1" +set g_nextmap "ut4_skeetshoot_pug" + +addbot dre 5 Red 10 hg`.Gost0r +addbot staple 5 Red 10 MQCD|Biddle +addbot scarab 5 Red 10 adn`Holycrap +addbot drop 3 Red 50 GlaD-slackin +addbot windex 3 Red 50 pir8|xesya +addbot twist 3 Red 50 [b00bs]The-Spiki +addbot rock 5 Red 50 ]Vz[rYuuJiN +addbot raven 3 Red 50 6th|Clear +addbot oscar 3 Red 50 GT-Rick +addbot nails 5 Red 50 =jF=Tidus +addbot mutt 3 Red 20 )O,O(Liquid +addbot lollipop 3 Red 10 strayA#Hypperz +addbot drop 3 Red 89 hg`.Hashbolla +addbot dre 3 Red 89 puff**b0ciam +addbot buzz 3 Red 89 pwnz.Tarquas +addbot bleach 3 Red 89 |it|Nvb +addbot adrastos 3 Red 89 sC`SailorMoon +addbot oscar 5 Red 89 glory.LaSer +addbot boa 3 Red 89 [Gore]Fenix +addbot chicken 3 Red 89 MQCD|Asloon +addbot python 3 Red 89 GlaD-Solitary \ No newline at end of file diff --git a/configs/TS.cfg b/configs/TS.cfg new file mode 100644 index 0000000..d669137 --- /dev/null +++ b/configs/TS.cfg @@ -0,0 +1,26 @@ +set sv_dlURL "maps.pugbot.net" +set sv_floodprotect "8" +set sv_hostname "Pickup @ WWW.DISCORD.ME/URT" +set sv_pure "0" +set sv_sayprefix "^4MissTS^3: " +set sv_timeout "60" +sets " Discord" "www.discord.io/urbanterror" +set auth_enable "1" +set auth_notoriety "0" +set auth_owners "13 2797" + +set fraglimit "0" +set timelimit "20" +set g_allowVote "8" +set g_friendlyFire "2" +set g_gametype "4" +set g_gear "KQ" +set g_inactivity "40" +set g_inactivityAction "0" +set g_matchmode "1" +set g_maxrounds "0" +set g_roundtime "1.5" +set g_strattime "3" +set g_suddendeath "1" +set g_swaproles "0" +set g_timeouts "0" \ No newline at end of file diff --git a/country-and-continent-codes-list.json b/country-and-continent-codes-list.json new file mode 100644 index 0000000..7174a69 --- /dev/null +++ b/country-and-continent-codes-list.json @@ -0,0 +1 @@ +[{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Afghanistan, Islamic Republic of", "Country_Number": 4, "Three_Letter_Country_Code": "AFG", "Two_Letter_Country_Code": "AF"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Albania, Republic of", "Country_Number": 8, "Three_Letter_Country_Code": "ALB", "Two_Letter_Country_Code": "AL"},{"Continent_Code": "AN", "Continent_Name": "Antarctica", "Country_Name": "Antarctica (the territory South of 60 deg S)", "Country_Number": 10, "Three_Letter_Country_Code": "ATA", "Two_Letter_Country_Code": "AQ"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Algeria, People's Democratic Republic of", "Country_Number": 12, "Three_Letter_Country_Code": "DZA", "Two_Letter_Country_Code": "DZ"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "American Samoa", "Country_Number": 16, "Three_Letter_Country_Code": "ASM", "Two_Letter_Country_Code": "AS"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Andorra, Principality of", "Country_Number": 20, "Three_Letter_Country_Code": "AND", "Two_Letter_Country_Code": "AD"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Angola, Republic of", "Country_Number": 24, "Three_Letter_Country_Code": "AGO", "Two_Letter_Country_Code": "AO"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Antigua and Barbuda", "Country_Number": 28, "Three_Letter_Country_Code": "ATG", "Two_Letter_Country_Code": "AG"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Azerbaijan, Republic of", "Country_Number": 31, "Three_Letter_Country_Code": "AZE", "Two_Letter_Country_Code": "AZ"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Azerbaijan, Republic of", "Country_Number": 31, "Three_Letter_Country_Code": "AZE", "Two_Letter_Country_Code": "AZ"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Argentina, Argentine Republic", "Country_Number": 32, "Three_Letter_Country_Code": "ARG", "Two_Letter_Country_Code": "AR"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Australia, Commonwealth of", "Country_Number": 36, "Three_Letter_Country_Code": "AUS", "Two_Letter_Country_Code": "AU"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Austria, Republic of", "Country_Number": 40, "Three_Letter_Country_Code": "AUT", "Two_Letter_Country_Code": "AT"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Bahamas, Commonwealth of the", "Country_Number": 44, "Three_Letter_Country_Code": "BHS", "Two_Letter_Country_Code": "BS"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Bahrain, Kingdom of", "Country_Number": 48, "Three_Letter_Country_Code": "BHR", "Two_Letter_Country_Code": "BH"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Bangladesh, People's Republic of", "Country_Number": 50, "Three_Letter_Country_Code": "BGD", "Two_Letter_Country_Code": "BD"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Armenia, Republic of", "Country_Number": 51, "Three_Letter_Country_Code": "ARM", "Two_Letter_Country_Code": "AM"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Armenia, Republic of", "Country_Number": 51, "Three_Letter_Country_Code": "ARM", "Two_Letter_Country_Code": "AM"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Barbados", "Country_Number": 52, "Three_Letter_Country_Code": "BRB", "Two_Letter_Country_Code": "BB"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Belgium, Kingdom of", "Country_Number": 56, "Three_Letter_Country_Code": "BEL", "Two_Letter_Country_Code": "BE"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Bermuda", "Country_Number": 60, "Three_Letter_Country_Code": "BMU", "Two_Letter_Country_Code": "BM"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Bhutan, Kingdom of", "Country_Number": 64, "Three_Letter_Country_Code": "BTN", "Two_Letter_Country_Code": "BT"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Bolivia, Republic of", "Country_Number": 68, "Three_Letter_Country_Code": "BOL", "Two_Letter_Country_Code": "BO"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Bosnia and Herzegovina", "Country_Number": 70, "Three_Letter_Country_Code": "BIH", "Two_Letter_Country_Code": "BA"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Botswana, Republic of", "Country_Number": 72, "Three_Letter_Country_Code": "BWA", "Two_Letter_Country_Code": "BW"},{"Continent_Code": "AN", "Continent_Name": "Antarctica", "Country_Name": "Bouvet Island (Bouvetoya)", "Country_Number": 74, "Three_Letter_Country_Code": "BVT", "Two_Letter_Country_Code": "BV"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Brazil, Federative Republic of", "Country_Number": 76, "Three_Letter_Country_Code": "BRA", "Two_Letter_Country_Code": "BR"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Belize", "Country_Number": 84, "Three_Letter_Country_Code": "BLZ", "Two_Letter_Country_Code": "BZ"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "British Indian Ocean Territory (Chagos Archipelago)", "Country_Number": 86, "Three_Letter_Country_Code": "IOT", "Two_Letter_Country_Code": "IO"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Solomon Islands", "Country_Number": 90, "Three_Letter_Country_Code": "SLB", "Two_Letter_Country_Code": "SB"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "British Virgin Islands", "Country_Number": 92, "Three_Letter_Country_Code": "VGB", "Two_Letter_Country_Code": "VG"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Brunei Darussalam", "Country_Number": 96, "Three_Letter_Country_Code": "BRN", "Two_Letter_Country_Code": "BN"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Bulgaria, Republic of", "Country_Number": 100, "Three_Letter_Country_Code": "BGR", "Two_Letter_Country_Code": "BG"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Myanmar, Union of", "Country_Number": 104, "Three_Letter_Country_Code": "MMR", "Two_Letter_Country_Code": "MM"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Burundi, Republic of", "Country_Number": 108, "Three_Letter_Country_Code": "BDI", "Two_Letter_Country_Code": "BI"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Belarus, Republic of", "Country_Number": 112, "Three_Letter_Country_Code": "BLR", "Two_Letter_Country_Code": "BY"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Cambodia, Kingdom of", "Country_Number": 116, "Three_Letter_Country_Code": "KHM", "Two_Letter_Country_Code": "KH"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Cameroon, Republic of", "Country_Number": 120, "Three_Letter_Country_Code": "CMR", "Two_Letter_Country_Code": "CM"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Canada", "Country_Number": 124, "Three_Letter_Country_Code": "CAN", "Two_Letter_Country_Code": "CA"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Cape Verde, Republic of", "Country_Number": 132, "Three_Letter_Country_Code": "CPV", "Two_Letter_Country_Code": "CV"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Cayman Islands", "Country_Number": 136, "Three_Letter_Country_Code": "CYM", "Two_Letter_Country_Code": "KY"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Central African Republic", "Country_Number": 140, "Three_Letter_Country_Code": "CAF", "Two_Letter_Country_Code": "CF"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Sri Lanka, Democratic Socialist Republic of", "Country_Number": 144, "Three_Letter_Country_Code": "LKA", "Two_Letter_Country_Code": "LK"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Chad, Republic of", "Country_Number": 148, "Three_Letter_Country_Code": "TCD", "Two_Letter_Country_Code": "TD"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Chile, Republic of", "Country_Number": 152, "Three_Letter_Country_Code": "CHL", "Two_Letter_Country_Code": "CL"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "China, People's Republic of", "Country_Number": 156, "Three_Letter_Country_Code": "CHN", "Two_Letter_Country_Code": "CN"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Taiwan", "Country_Number": 158, "Three_Letter_Country_Code": "TWN", "Two_Letter_Country_Code": "TW"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Christmas Island", "Country_Number": 162, "Three_Letter_Country_Code": "CXR", "Two_Letter_Country_Code": "CX"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Cocos (Keeling) Islands", "Country_Number": 166, "Three_Letter_Country_Code": "CCK", "Two_Letter_Country_Code": "CC"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Colombia, Republic of", "Country_Number": 170, "Three_Letter_Country_Code": "COL", "Two_Letter_Country_Code": "CO"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Comoros, Union of the", "Country_Number": 174, "Three_Letter_Country_Code": "COM", "Two_Letter_Country_Code": "KM"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Mayotte", "Country_Number": 175, "Three_Letter_Country_Code": "MYT", "Two_Letter_Country_Code": "YT"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Congo, Republic of the", "Country_Number": 178, "Three_Letter_Country_Code": "COG", "Two_Letter_Country_Code": "CG"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Congo, Democratic Republic of the", "Country_Number": 180, "Three_Letter_Country_Code": "COD", "Two_Letter_Country_Code": "CD"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Cook Islands", "Country_Number": 184, "Three_Letter_Country_Code": "COK", "Two_Letter_Country_Code": "CK"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Costa Rica, Republic of", "Country_Number": 188, "Three_Letter_Country_Code": "CRI", "Two_Letter_Country_Code": "CR"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Croatia, Republic of", "Country_Number": 191, "Three_Letter_Country_Code": "HRV", "Two_Letter_Country_Code": "HR"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Cuba, Republic of", "Country_Number": 192, "Three_Letter_Country_Code": "CUB", "Two_Letter_Country_Code": "CU"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Cyprus, Republic of", "Country_Number": 196, "Three_Letter_Country_Code": "CYP", "Two_Letter_Country_Code": "CY"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Cyprus, Republic of", "Country_Number": 196, "Three_Letter_Country_Code": "CYP", "Two_Letter_Country_Code": "CY"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Czech Republic", "Country_Number": 203, "Three_Letter_Country_Code": "CZE", "Two_Letter_Country_Code": "CZ"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Benin, Republic of", "Country_Number": 204, "Three_Letter_Country_Code": "BEN", "Two_Letter_Country_Code": "BJ"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Denmark, Kingdom of", "Country_Number": 208, "Three_Letter_Country_Code": "DNK", "Two_Letter_Country_Code": "DK"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Dominica, Commonwealth of", "Country_Number": 212, "Three_Letter_Country_Code": "DMA", "Two_Letter_Country_Code": "DM"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Dominican Republic", "Country_Number": 214, "Three_Letter_Country_Code": "DOM", "Two_Letter_Country_Code": "DO"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Ecuador, Republic of", "Country_Number": 218, "Three_Letter_Country_Code": "ECU", "Two_Letter_Country_Code": "EC"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "El Salvador, Republic of", "Country_Number": 222, "Three_Letter_Country_Code": "SLV", "Two_Letter_Country_Code": "SV"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Equatorial Guinea, Republic of", "Country_Number": 226, "Three_Letter_Country_Code": "GNQ", "Two_Letter_Country_Code": "GQ"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Ethiopia, Federal Democratic Republic of", "Country_Number": 231, "Three_Letter_Country_Code": "ETH", "Two_Letter_Country_Code": "ET"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Eritrea, State of", "Country_Number": 232, "Three_Letter_Country_Code": "ERI", "Two_Letter_Country_Code": "ER"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Estonia, Republic of", "Country_Number": 233, "Three_Letter_Country_Code": "EST", "Two_Letter_Country_Code": "EE"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Faroe Islands", "Country_Number": 234, "Three_Letter_Country_Code": "FRO", "Two_Letter_Country_Code": "FO"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Falkland Islands (Malvinas)", "Country_Number": 238, "Three_Letter_Country_Code": "FLK", "Two_Letter_Country_Code": "FK"},{"Continent_Code": "AN", "Continent_Name": "Antarctica", "Country_Name": "South Georgia and the South Sandwich Islands", "Country_Number": 239, "Three_Letter_Country_Code": "SGS", "Two_Letter_Country_Code": "GS"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Fiji, Republic of the Fiji Islands", "Country_Number": 242, "Three_Letter_Country_Code": "FJI", "Two_Letter_Country_Code": "FJ"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Finland, Republic of", "Country_Number": 246, "Three_Letter_Country_Code": "FIN", "Two_Letter_Country_Code": "FI"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "\u00c5land Islands", "Country_Number": 248, "Three_Letter_Country_Code": "ALA", "Two_Letter_Country_Code": "AX"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "France, French Republic", "Country_Number": 250, "Three_Letter_Country_Code": "FRA", "Two_Letter_Country_Code": "FR"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "French Guiana", "Country_Number": 254, "Three_Letter_Country_Code": "GUF", "Two_Letter_Country_Code": "GF"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "French Polynesia", "Country_Number": 258, "Three_Letter_Country_Code": "PYF", "Two_Letter_Country_Code": "PF"},{"Continent_Code": "AN", "Continent_Name": "Antarctica", "Country_Name": "French Southern Territories", "Country_Number": 260, "Three_Letter_Country_Code": "ATF", "Two_Letter_Country_Code": "TF"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Djibouti, Republic of", "Country_Number": 262, "Three_Letter_Country_Code": "DJI", "Two_Letter_Country_Code": "DJ"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Gabon, Gabonese Republic", "Country_Number": 266, "Three_Letter_Country_Code": "GAB", "Two_Letter_Country_Code": "GA"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Georgia", "Country_Number": 268, "Three_Letter_Country_Code": "GEO", "Two_Letter_Country_Code": "GE"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Georgia", "Country_Number": 268, "Three_Letter_Country_Code": "GEO", "Two_Letter_Country_Code": "GE"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Gambia, Republic of the", "Country_Number": 270, "Three_Letter_Country_Code": "GMB", "Two_Letter_Country_Code": "GM"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Palestinian Territory, Occupied", "Country_Number": 275, "Three_Letter_Country_Code": "PSE", "Two_Letter_Country_Code": "PS"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Germany, Federal Republic of", "Country_Number": 276, "Three_Letter_Country_Code": "DEU", "Two_Letter_Country_Code": "DE"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Ghana, Republic of", "Country_Number": 288, "Three_Letter_Country_Code": "GHA", "Two_Letter_Country_Code": "GH"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Gibraltar", "Country_Number": 292, "Three_Letter_Country_Code": "GIB", "Two_Letter_Country_Code": "GI"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Kiribati, Republic of", "Country_Number": 296, "Three_Letter_Country_Code": "KIR", "Two_Letter_Country_Code": "KI"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Greece, Hellenic Republic", "Country_Number": 300, "Three_Letter_Country_Code": "GRC", "Two_Letter_Country_Code": "GR"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Greenland", "Country_Number": 304, "Three_Letter_Country_Code": "GRL", "Two_Letter_Country_Code": "GL"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Grenada", "Country_Number": 308, "Three_Letter_Country_Code": "GRD", "Two_Letter_Country_Code": "GD"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Guadeloupe", "Country_Number": 312, "Three_Letter_Country_Code": "GLP", "Two_Letter_Country_Code": "GP"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Guam", "Country_Number": 316, "Three_Letter_Country_Code": "GUM", "Two_Letter_Country_Code": "GU"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Guatemala, Republic of", "Country_Number": 320, "Three_Letter_Country_Code": "GTM", "Two_Letter_Country_Code": "GT"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Guinea, Republic of", "Country_Number": 324, "Three_Letter_Country_Code": "GIN", "Two_Letter_Country_Code": "GN"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Guyana, Co-operative Republic of", "Country_Number": 328, "Three_Letter_Country_Code": "GUY", "Two_Letter_Country_Code": "GY"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Haiti, Republic of", "Country_Number": 332, "Three_Letter_Country_Code": "HTI", "Two_Letter_Country_Code": "HT"},{"Continent_Code": "AN", "Continent_Name": "Antarctica", "Country_Name": "Heard Island and McDonald Islands", "Country_Number": 334, "Three_Letter_Country_Code": "HMD", "Two_Letter_Country_Code": "HM"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Holy See (Vatican City State)", "Country_Number": 336, "Three_Letter_Country_Code": "VAT", "Two_Letter_Country_Code": "VA"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Honduras, Republic of", "Country_Number": 340, "Three_Letter_Country_Code": "HND", "Two_Letter_Country_Code": "HN"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Hong Kong, Special Administrative Region of China", "Country_Number": 344, "Three_Letter_Country_Code": "HKG", "Two_Letter_Country_Code": "HK"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Hungary, Republic of", "Country_Number": 348, "Three_Letter_Country_Code": "HUN", "Two_Letter_Country_Code": "HU"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Iceland, Republic of", "Country_Number": 352, "Three_Letter_Country_Code": "ISL", "Two_Letter_Country_Code": "IS"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "India, Republic of", "Country_Number": 356, "Three_Letter_Country_Code": "IND", "Two_Letter_Country_Code": "IN"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Indonesia, Republic of", "Country_Number": 360, "Three_Letter_Country_Code": "IDN", "Two_Letter_Country_Code": "ID"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Iran, Islamic Republic of", "Country_Number": 364, "Three_Letter_Country_Code": "IRN", "Two_Letter_Country_Code": "IR"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Iraq, Republic of", "Country_Number": 368, "Three_Letter_Country_Code": "IRQ", "Two_Letter_Country_Code": "IQ"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Ireland", "Country_Number": 372, "Three_Letter_Country_Code": "IRL", "Two_Letter_Country_Code": "IE"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Israel, State of", "Country_Number": 376, "Three_Letter_Country_Code": "ISR", "Two_Letter_Country_Code": "IL"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Italy, Italian Republic", "Country_Number": 380, "Three_Letter_Country_Code": "ITA", "Two_Letter_Country_Code": "IT"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Cote d'Ivoire, Republic of", "Country_Number": 384, "Three_Letter_Country_Code": "CIV", "Two_Letter_Country_Code": "CI"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Jamaica", "Country_Number": 388, "Three_Letter_Country_Code": "JAM", "Two_Letter_Country_Code": "JM"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Japan", "Country_Number": 392, "Three_Letter_Country_Code": "JPN", "Two_Letter_Country_Code": "JP"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Kazakhstan, Republic of", "Country_Number": 398, "Three_Letter_Country_Code": "KAZ", "Two_Letter_Country_Code": "KZ"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Kazakhstan, Republic of", "Country_Number": 398, "Three_Letter_Country_Code": "KAZ", "Two_Letter_Country_Code": "KZ"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Jordan, Hashemite Kingdom of", "Country_Number": 400, "Three_Letter_Country_Code": "JOR", "Two_Letter_Country_Code": "JO"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Kenya, Republic of", "Country_Number": 404, "Three_Letter_Country_Code": "KEN", "Two_Letter_Country_Code": "KE"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Korea, Democratic People's Republic of", "Country_Number": 408, "Three_Letter_Country_Code": "PRK", "Two_Letter_Country_Code": "KP"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Korea, Republic of", "Country_Number": 410, "Three_Letter_Country_Code": "KOR", "Two_Letter_Country_Code": "KR"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Kuwait, State of", "Country_Number": 414, "Three_Letter_Country_Code": "KWT", "Two_Letter_Country_Code": "KW"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Kyrgyz Republic", "Country_Number": 417, "Three_Letter_Country_Code": "KGZ", "Two_Letter_Country_Code": "KG"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Lao People's Democratic Republic", "Country_Number": 418, "Three_Letter_Country_Code": "LAO", "Two_Letter_Country_Code": "LA"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Lebanon, Lebanese Republic", "Country_Number": 422, "Three_Letter_Country_Code": "LBN", "Two_Letter_Country_Code": "LB"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Lesotho, Kingdom of", "Country_Number": 426, "Three_Letter_Country_Code": "LSO", "Two_Letter_Country_Code": "LS"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Latvia, Republic of", "Country_Number": 428, "Three_Letter_Country_Code": "LVA", "Two_Letter_Country_Code": "LV"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Liberia, Republic of", "Country_Number": 430, "Three_Letter_Country_Code": "LBR", "Two_Letter_Country_Code": "LR"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Libyan Arab Jamahiriya", "Country_Number": 434, "Three_Letter_Country_Code": "LBY", "Two_Letter_Country_Code": "LY"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Liechtenstein, Principality of", "Country_Number": 438, "Three_Letter_Country_Code": "LIE", "Two_Letter_Country_Code": "LI"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Lithuania, Republic of", "Country_Number": 440, "Three_Letter_Country_Code": "LTU", "Two_Letter_Country_Code": "LT"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Luxembourg, Grand Duchy of", "Country_Number": 442, "Three_Letter_Country_Code": "LUX", "Two_Letter_Country_Code": "LU"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Macao, Special Administrative Region of China", "Country_Number": 446, "Three_Letter_Country_Code": "MAC", "Two_Letter_Country_Code": "MO"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Madagascar, Republic of", "Country_Number": 450, "Three_Letter_Country_Code": "MDG", "Two_Letter_Country_Code": "MG"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Malawi, Republic of", "Country_Number": 454, "Three_Letter_Country_Code": "MWI", "Two_Letter_Country_Code": "MW"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Malaysia", "Country_Number": 458, "Three_Letter_Country_Code": "MYS", "Two_Letter_Country_Code": "MY"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Maldives, Republic of", "Country_Number": 462, "Three_Letter_Country_Code": "MDV", "Two_Letter_Country_Code": "MV"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Mali, Republic of", "Country_Number": 466, "Three_Letter_Country_Code": "MLI", "Two_Letter_Country_Code": "ML"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Malta, Republic of", "Country_Number": 470, "Three_Letter_Country_Code": "MLT", "Two_Letter_Country_Code": "MT"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Martinique", "Country_Number": 474, "Three_Letter_Country_Code": "MTQ", "Two_Letter_Country_Code": "MQ"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Mauritania, Islamic Republic of", "Country_Number": 478, "Three_Letter_Country_Code": "MRT", "Two_Letter_Country_Code": "MR"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Mauritius, Republic of", "Country_Number": 480, "Three_Letter_Country_Code": "MUS", "Two_Letter_Country_Code": "MU"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Mexico, United Mexican States", "Country_Number": 484, "Three_Letter_Country_Code": "MEX", "Two_Letter_Country_Code": "MX"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Monaco, Principality of", "Country_Number": 492, "Three_Letter_Country_Code": "MCO", "Two_Letter_Country_Code": "MC"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Mongolia", "Country_Number": 496, "Three_Letter_Country_Code": "MNG", "Two_Letter_Country_Code": "MN"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Moldova, Republic of", "Country_Number": 498, "Three_Letter_Country_Code": "MDA", "Two_Letter_Country_Code": "MD"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Montenegro, Republic of", "Country_Number": 499, "Three_Letter_Country_Code": "MNE", "Two_Letter_Country_Code": "ME"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Montserrat", "Country_Number": 500, "Three_Letter_Country_Code": "MSR", "Two_Letter_Country_Code": "MS"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Morocco, Kingdom of", "Country_Number": 504, "Three_Letter_Country_Code": "MAR", "Two_Letter_Country_Code": "MA"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Mozambique, Republic of", "Country_Number": 508, "Three_Letter_Country_Code": "MOZ", "Two_Letter_Country_Code": "MZ"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Oman, Sultanate of", "Country_Number": 512, "Three_Letter_Country_Code": "OMN", "Two_Letter_Country_Code": "OM"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Namibia, Republic of", "Country_Number": 516, "Three_Letter_Country_Code": "NAM", "Two_Letter_Country_Code": "NA"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Nauru, Republic of", "Country_Number": 520, "Three_Letter_Country_Code": "NRU", "Two_Letter_Country_Code": "NR"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Nepal, State of", "Country_Number": 524, "Three_Letter_Country_Code": "NPL", "Two_Letter_Country_Code": "NP"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Netherlands, Kingdom of the", "Country_Number": 528, "Three_Letter_Country_Code": "NLD", "Two_Letter_Country_Code": "NL"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Netherlands Antilles", "Country_Number": 530, "Three_Letter_Country_Code": "ANT", "Two_Letter_Country_Code": "AN"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Cura\u00e7ao", "Country_Number": 531, "Three_Letter_Country_Code": "CUW", "Two_Letter_Country_Code": "CW"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Aruba", "Country_Number": 533, "Three_Letter_Country_Code": "ABW", "Two_Letter_Country_Code": "AW"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Sint Maarten (Netherlands)", "Country_Number": 534, "Three_Letter_Country_Code": "SXM", "Two_Letter_Country_Code": "SX"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Bonaire, Sint Eustatius and Saba", "Country_Number": 535, "Three_Letter_Country_Code": "BES", "Two_Letter_Country_Code": "BQ"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "New Caledonia", "Country_Number": 540, "Three_Letter_Country_Code": "NCL", "Two_Letter_Country_Code": "NC"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Vanuatu, Republic of", "Country_Number": 548, "Three_Letter_Country_Code": "VUT", "Two_Letter_Country_Code": "VU"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "New Zealand", "Country_Number": 554, "Three_Letter_Country_Code": "NZL", "Two_Letter_Country_Code": "NZ"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Nicaragua, Republic of", "Country_Number": 558, "Three_Letter_Country_Code": "NIC", "Two_Letter_Country_Code": "NI"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Niger, Republic of", "Country_Number": 562, "Three_Letter_Country_Code": "NER", "Two_Letter_Country_Code": "NE"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Nigeria, Federal Republic of", "Country_Number": 566, "Three_Letter_Country_Code": "NGA", "Two_Letter_Country_Code": "NG"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Niue", "Country_Number": 570, "Three_Letter_Country_Code": "NIU", "Two_Letter_Country_Code": "NU"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Norfolk Island", "Country_Number": 574, "Three_Letter_Country_Code": "NFK", "Two_Letter_Country_Code": "NF"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Norway, Kingdom of", "Country_Number": 578, "Three_Letter_Country_Code": "NOR", "Two_Letter_Country_Code": "NO"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Northern Mariana Islands, Commonwealth of the", "Country_Number": 580, "Three_Letter_Country_Code": "MNP", "Two_Letter_Country_Code": "MP"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "United States Minor Outlying Islands", "Country_Number": 581, "Three_Letter_Country_Code": "UMI", "Two_Letter_Country_Code": "UM"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "United States Minor Outlying Islands", "Country_Number": 581, "Three_Letter_Country_Code": "UMI", "Two_Letter_Country_Code": "UM"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Micronesia, Federated States of", "Country_Number": 583, "Three_Letter_Country_Code": "FSM", "Two_Letter_Country_Code": "FM"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Marshall Islands, Republic of the", "Country_Number": 584, "Three_Letter_Country_Code": "MHL", "Two_Letter_Country_Code": "MH"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Palau, Republic of", "Country_Number": 585, "Three_Letter_Country_Code": "PLW", "Two_Letter_Country_Code": "PW"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Pakistan, Islamic Republic of", "Country_Number": 586, "Three_Letter_Country_Code": "PAK", "Two_Letter_Country_Code": "PK"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Panama, Republic of", "Country_Number": 591, "Three_Letter_Country_Code": "PAN", "Two_Letter_Country_Code": "PA"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Papua New Guinea, Independent State of", "Country_Number": 598, "Three_Letter_Country_Code": "PNG", "Two_Letter_Country_Code": "PG"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Paraguay, Republic of", "Country_Number": 600, "Three_Letter_Country_Code": "PRY", "Two_Letter_Country_Code": "PY"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Peru, Republic of", "Country_Number": 604, "Three_Letter_Country_Code": "PER", "Two_Letter_Country_Code": "PE"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Philippines, Republic of the", "Country_Number": 608, "Three_Letter_Country_Code": "PHL", "Two_Letter_Country_Code": "PH"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Pitcairn Islands", "Country_Number": 612, "Three_Letter_Country_Code": "PCN", "Two_Letter_Country_Code": "PN"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Poland, Republic of", "Country_Number": 616, "Three_Letter_Country_Code": "POL", "Two_Letter_Country_Code": "PL"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Portugal, Portuguese Republic", "Country_Number": 620, "Three_Letter_Country_Code": "PRT", "Two_Letter_Country_Code": "PT"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Guinea-Bissau, Republic of", "Country_Number": 624, "Three_Letter_Country_Code": "GNB", "Two_Letter_Country_Code": "GW"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Timor-Leste, Democratic Republic of", "Country_Number": 626, "Three_Letter_Country_Code": "TLS", "Two_Letter_Country_Code": "TL"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Puerto Rico, Commonwealth of", "Country_Number": 630, "Three_Letter_Country_Code": "PRI", "Two_Letter_Country_Code": "PR"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Qatar, State of", "Country_Number": 634, "Three_Letter_Country_Code": "QAT", "Two_Letter_Country_Code": "QA"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Reunion", "Country_Number": 638, "Three_Letter_Country_Code": "REU", "Two_Letter_Country_Code": "RE"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Romania", "Country_Number": 642, "Three_Letter_Country_Code": "ROU", "Two_Letter_Country_Code": "RO"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Russian Federation", "Country_Number": 643, "Three_Letter_Country_Code": "RUS", "Two_Letter_Country_Code": "RU"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Russian Federation", "Country_Number": 643, "Three_Letter_Country_Code": "RUS", "Two_Letter_Country_Code": "RU"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Rwanda, Republic of", "Country_Number": 646, "Three_Letter_Country_Code": "RWA", "Two_Letter_Country_Code": "RW"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Saint Barthelemy", "Country_Number": 652, "Three_Letter_Country_Code": "BLM", "Two_Letter_Country_Code": "BL"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Saint Helena", "Country_Number": 654, "Three_Letter_Country_Code": "SHN", "Two_Letter_Country_Code": "SH"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Saint Kitts and Nevis, Federation of", "Country_Number": 659, "Three_Letter_Country_Code": "KNA", "Two_Letter_Country_Code": "KN"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Anguilla", "Country_Number": 660, "Three_Letter_Country_Code": "AIA", "Two_Letter_Country_Code": "AI"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Saint Lucia", "Country_Number": 662, "Three_Letter_Country_Code": "LCA", "Two_Letter_Country_Code": "LC"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Saint Martin", "Country_Number": 663, "Three_Letter_Country_Code": "MAF", "Two_Letter_Country_Code": "MF"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Saint Pierre and Miquelon", "Country_Number": 666, "Three_Letter_Country_Code": "SPM", "Two_Letter_Country_Code": "PM"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Saint Vincent and the Grenadines", "Country_Number": 670, "Three_Letter_Country_Code": "VCT", "Two_Letter_Country_Code": "VC"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "San Marino, Republic of", "Country_Number": 674, "Three_Letter_Country_Code": "SMR", "Two_Letter_Country_Code": "SM"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Sao Tome and Principe, Democratic Republic of", "Country_Number": 678, "Three_Letter_Country_Code": "STP", "Two_Letter_Country_Code": "ST"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Saudi Arabia, Kingdom of", "Country_Number": 682, "Three_Letter_Country_Code": "SAU", "Two_Letter_Country_Code": "SA"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Senegal, Republic of", "Country_Number": 686, "Three_Letter_Country_Code": "SEN", "Two_Letter_Country_Code": "SN"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Serbia, Republic of", "Country_Number": 688, "Three_Letter_Country_Code": "SRB", "Two_Letter_Country_Code": "RS"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Seychelles, Republic of", "Country_Number": 690, "Three_Letter_Country_Code": "SYC", "Two_Letter_Country_Code": "SC"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Sierra Leone, Republic of", "Country_Number": 694, "Three_Letter_Country_Code": "SLE", "Two_Letter_Country_Code": "SL"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Singapore, Republic of", "Country_Number": 702, "Three_Letter_Country_Code": "SGP", "Two_Letter_Country_Code": "SG"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Slovakia (Slovak Republic)", "Country_Number": 703, "Three_Letter_Country_Code": "SVK", "Two_Letter_Country_Code": "SK"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Vietnam, Socialist Republic of", "Country_Number": 704, "Three_Letter_Country_Code": "VNM", "Two_Letter_Country_Code": "VN"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Slovenia, Republic of", "Country_Number": 705, "Three_Letter_Country_Code": "SVN", "Two_Letter_Country_Code": "SI"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Somalia, Somali Republic", "Country_Number": 706, "Three_Letter_Country_Code": "SOM", "Two_Letter_Country_Code": "SO"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "South Africa, Republic of", "Country_Number": 710, "Three_Letter_Country_Code": "ZAF", "Two_Letter_Country_Code": "ZA"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Zimbabwe, Republic of", "Country_Number": 716, "Three_Letter_Country_Code": "ZWE", "Two_Letter_Country_Code": "ZW"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Spain, Kingdom of", "Country_Number": 724, "Three_Letter_Country_Code": "ESP", "Two_Letter_Country_Code": "ES"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "South Sudan", "Country_Number": 728, "Three_Letter_Country_Code": "SSD", "Two_Letter_Country_Code": "SS"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Western Sahara", "Country_Number": 732, "Three_Letter_Country_Code": "ESH", "Two_Letter_Country_Code": "EH"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Sudan, Republic of", "Country_Number": 736, "Three_Letter_Country_Code": "SDN", "Two_Letter_Country_Code": "SD"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Suriname, Republic of", "Country_Number": 740, "Three_Letter_Country_Code": "SUR", "Two_Letter_Country_Code": "SR"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Svalbard & Jan Mayen Islands", "Country_Number": 744, "Three_Letter_Country_Code": "SJM", "Two_Letter_Country_Code": "SJ"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Swaziland, Kingdom of", "Country_Number": 748, "Three_Letter_Country_Code": "SWZ", "Two_Letter_Country_Code": "SZ"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Sweden, Kingdom of", "Country_Number": 752, "Three_Letter_Country_Code": "SWE", "Two_Letter_Country_Code": "SE"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Switzerland, Swiss Confederation", "Country_Number": 756, "Three_Letter_Country_Code": "CHE", "Two_Letter_Country_Code": "CH"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Syrian Arab Republic", "Country_Number": 760, "Three_Letter_Country_Code": "SYR", "Two_Letter_Country_Code": "SY"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Tajikistan, Republic of", "Country_Number": 762, "Three_Letter_Country_Code": "TJK", "Two_Letter_Country_Code": "TJ"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Thailand, Kingdom of", "Country_Number": 764, "Three_Letter_Country_Code": "THA", "Two_Letter_Country_Code": "TH"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Togo, Togolese Republic", "Country_Number": 768, "Three_Letter_Country_Code": "TGO", "Two_Letter_Country_Code": "TG"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Tokelau", "Country_Number": 772, "Three_Letter_Country_Code": "TKL", "Two_Letter_Country_Code": "TK"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Tonga, Kingdom of", "Country_Number": 776, "Three_Letter_Country_Code": "TON", "Two_Letter_Country_Code": "TO"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Trinidad and Tobago, Republic of", "Country_Number": 780, "Three_Letter_Country_Code": "TTO", "Two_Letter_Country_Code": "TT"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "United Arab Emirates", "Country_Number": 784, "Three_Letter_Country_Code": "ARE", "Two_Letter_Country_Code": "AE"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Tunisia, Tunisian Republic", "Country_Number": 788, "Three_Letter_Country_Code": "TUN", "Two_Letter_Country_Code": "TN"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Turkey, Republic of", "Country_Number": 792, "Three_Letter_Country_Code": "TUR", "Two_Letter_Country_Code": "TR"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Turkey, Republic of", "Country_Number": 792, "Three_Letter_Country_Code": "TUR", "Two_Letter_Country_Code": "TR"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Turkmenistan", "Country_Number": 795, "Three_Letter_Country_Code": "TKM", "Two_Letter_Country_Code": "TM"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Turks and Caicos Islands", "Country_Number": 796, "Three_Letter_Country_Code": "TCA", "Two_Letter_Country_Code": "TC"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Tuvalu", "Country_Number": 798, "Three_Letter_Country_Code": "TUV", "Two_Letter_Country_Code": "TV"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Uganda, Republic of", "Country_Number": 800, "Three_Letter_Country_Code": "UGA", "Two_Letter_Country_Code": "UG"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Ukraine", "Country_Number": 804, "Three_Letter_Country_Code": "UKR", "Two_Letter_Country_Code": "UA"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Macedonia, The Former Yugoslav Republic of", "Country_Number": 807, "Three_Letter_Country_Code": "MKD", "Two_Letter_Country_Code": "MK"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Egypt, Arab Republic of", "Country_Number": 818, "Three_Letter_Country_Code": "EGY", "Two_Letter_Country_Code": "EG"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "United Kingdom of Great Britain & Northern Ireland", "Country_Number": 826, "Three_Letter_Country_Code": "GBR", "Two_Letter_Country_Code": "GB"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Guernsey, Bailiwick of", "Country_Number": 831, "Three_Letter_Country_Code": "GGY", "Two_Letter_Country_Code": "GG"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Jersey, Bailiwick of", "Country_Number": 832, "Three_Letter_Country_Code": "JEY", "Two_Letter_Country_Code": "JE"},{"Continent_Code": "EU", "Continent_Name": "Europe", "Country_Name": "Isle of Man", "Country_Number": 833, "Three_Letter_Country_Code": "IMN", "Two_Letter_Country_Code": "IM"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Tanzania, United Republic of", "Country_Number": 834, "Three_Letter_Country_Code": "TZA", "Two_Letter_Country_Code": "TZ"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "United States of America", "Country_Number": 840, "Three_Letter_Country_Code": "USA", "Two_Letter_Country_Code": "US"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "United States Virgin Islands", "Country_Number": 850, "Three_Letter_Country_Code": "VIR", "Two_Letter_Country_Code": "VI"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Burkina Faso", "Country_Number": 854, "Three_Letter_Country_Code": "BFA", "Two_Letter_Country_Code": "BF"},{"Continent_Code": "SA", "Continent_Name": "South America", "Country_Name": "Uruguay, Eastern Republic of", "Country_Number": 858, "Three_Letter_Country_Code": "URY", "Two_Letter_Country_Code": "UY"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Uzbekistan, Republic of", "Country_Number": 860, "Three_Letter_Country_Code": "UZB", "Two_Letter_Country_Code": "UZ"},{"Continent_Code": "NA", "Continent_Name": "North America", "Country_Name": "Venezuela, Bolivarian Republic of", "Country_Number": 862, "Three_Letter_Country_Code": "VEN", "Two_Letter_Country_Code": "VE"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Wallis and Futuna", "Country_Number": 876, "Three_Letter_Country_Code": "WLF", "Two_Letter_Country_Code": "WF"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Samoa, Independent State of", "Country_Number": 882, "Three_Letter_Country_Code": "WSM", "Two_Letter_Country_Code": "WS"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Yemen", "Country_Number": 887, "Three_Letter_Country_Code": "YEM", "Two_Letter_Country_Code": "YE"},{"Continent_Code": "AF", "Continent_Name": "Africa", "Country_Name": "Zambia, Republic of", "Country_Number": 894, "Three_Letter_Country_Code": "ZMB", "Two_Letter_Country_Code": "ZM"},{"Continent_Code": "OC", "Continent_Name": "Oceania", "Country_Name": "Disputed Territory", "Country_Number": null, "Three_Letter_Country_Code": null, "Two_Letter_Country_Code": "XX"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Iraq-Saudi Arabia Neutral Zone", "Country_Number": null, "Three_Letter_Country_Code": null, "Two_Letter_Country_Code": "XE"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "United Nations Neutral Zone", "Country_Number": null, "Three_Letter_Country_Code": null, "Two_Letter_Country_Code": "XD"},{"Continent_Code": "AS", "Continent_Name": "Asia", "Country_Name": "Spratly Islands", "Country_Number": null, "Three_Letter_Country_Code": null, "Two_Letter_Country_Code": "XS"}] \ No newline at end of file diff --git a/country_continents.xlsx b/country_continents.xlsx new file mode 100644 index 0000000..2f00844 Binary files /dev/null and b/country_continents.xlsx differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..69a9715 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..744e882 --- /dev/null +++ b/gradlew @@ -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 + ;; + MSYS* | 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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -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 diff --git a/sentry.properties b/sentry.properties new file mode 100644 index 0000000..88c78c6 --- /dev/null +++ b/sentry.properties @@ -0,0 +1 @@ +stacktrace.app.packages=de.gost0r.pickupbot \ No newline at end of file diff --git a/src/de/gost0r/pickupbot/PickupBotDiscordMain.java b/src/de/gost0r/pickupbot/PickupBotDiscordMain.java index ad84dbb..664dca2 100644 --- a/src/de/gost0r/pickupbot/PickupBotDiscordMain.java +++ b/src/de/gost0r/pickupbot/PickupBotDiscordMain.java @@ -1,12 +1,6 @@ package de.gost0r.pickupbot; -import java.io.FileNotFoundException; import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Locale; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; @@ -14,139 +8,72 @@ import java.util.logging.Logger; import java.util.logging.SimpleFormatter; +import de.gost0r.pickupbot.ftwgl.FtwglAPI; +import io.github.cdimascio.dotenv.Dotenv; import org.json.JSONException; -import org.json.JSONObject; import de.gost0r.pickupbot.discord.DiscordBot; +import de.gost0r.pickupbot.pickup.Country; import de.gost0r.pickupbot.pickup.PickupBot; +import io.sentry.Sentry; + public class PickupBotDiscordMain { private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private static String env; + private static Dotenv dotenv; public static void main(String[] args) { Locale.setDefault(new Locale("en", "EN")); try { - - setupLogger(); + dotenv = Dotenv.configure().filename(".prod.env").load(); + env = "prod"; + if (args.length > 0 && args[0].equals("dev")){ + dotenv = Dotenv.configure().filename(".dev.env").load(); + env = "dev"; + } - String config = new String(Files.readAllBytes(Paths.get("config.json"))); - JSONObject cfg = new JSONObject(config); - DiscordBot.setToken(cfg.getString("token")); + setupLogger(); + Country.initCountryCodes(); + + DiscordBot.setToken(dotenv.get("DISCORD_TOKEN")); + DiscordBot.setApplicationId(dotenv.get("DISCORD_APPLICATION_ID")); + FtwglAPI.setupCredentials(dotenv.get("FTW_URL"), dotenv.get("FTW_KEY")); PickupBot bot = new PickupBot(); - bot.init(); - - // TEST: make admin chan to pub chan. -// DiscordChannel targetChannel = DiscordChannel.findChannel("143233743107129344"); -// if (!bot.logic.getChannelByType(PickupChannelType.PUBLIC).contains(targetChannel)) -// { -// bot.logic.addChannel(PickupChannelType.PUBLIC, targetChannel); -// } -// targetChannel = DiscordChannel.findChannel("402541587164561419"); -// if (bot.logic.getChannelByType(PickupChannelType.PUBLIC).contains(targetChannel)) -// { -// bot.logic.removeChannel(PickupChannelType.PUBLIC, targetChannel); -// } - + bot.init(env); + + while (true) { Thread.sleep(5000); } - } catch (InterruptedException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (FileNotFoundException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (SecurityException e) { + + } catch (IOException | JSONException | SecurityException | InterruptedException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } - -// eloTest(); - -// serverTest(); } public static void setupLogger() throws SecurityException, IOException { - System.setProperty("java.util.logging.SimpleFormatter.format", "%1$tF %1$tT %4$s - %2$s(): %5$s%6$s%n"); - Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - logger.setLevel(Level.ALL); - - logger.setUseParentHandlers(false); - ConsoleHandler handler = new ConsoleHandler(); - handler.setLevel(Level.ALL); - logger.addHandler(handler); - - FileHandler logfile = new FileHandler("bot.log"); - logfile.setFormatter(new SimpleFormatter()); - logfile.setLevel(Level.ALL); - logger.addHandler(logfile); - - LOGGER.severe("Bot started."); - } - - public static void eloTest() { - int eloSelf = 1000; - int eloOpp = 1000; - - double w = 1; // 1 win, 0.5 draw, 0 loss - - double tSelf = Math.pow(10d, eloSelf/400d); - double tOpp = Math.pow(10d, eloOpp/400d); - double e = tSelf / (tSelf + tOpp); - - double resultSelf = 32d * (w - e); - int elochange = (int) Math.floor(resultSelf); - System.out.println(elochange); - } - - public static void serverTest() { - - try { - DatagramChannel channel = DatagramChannel.open(); - channel.configureBlocking(true); - channel.connect(new InetSocketAddress("sd.biddle.cf", 27960)); - - String rconpassword = "HereWeGo"; - String rcon = "xxxxrcon " + rconpassword + " players"; - - byte[] sendBuffer = rcon.getBytes(); - - sendBuffer[0] = (byte) 0xff; - sendBuffer[1] = (byte) 0xff; - sendBuffer[2] = (byte) 0xff; - sendBuffer[3] = (byte) 0xff; - - ByteBuffer buf = ByteBuffer.allocate(2048); - buf.clear(); - buf.put(sendBuffer); - buf.flip(); - //int bytesWritten = channel.write(buf); - - buf = ByteBuffer.allocate(2000); - buf.clear(); - - int bytesRead = 0; - while ((bytesRead = channel.read(buf)) > 0) - { - System.out.println(bytesRead); - String newString = new String(buf.array()); - - buf = ByteBuffer.allocate(2000); - buf.clear(); - - System.out.println(newString); - } - System.out.println("exit"); - - - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } + System.setProperty("java.util.logging.SimpleFormatter.format", "%1$tF %1$tT %4$s - %2$s(): %5$s%6$s%n"); + Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + logger.setLevel(Level.WARNING); + logger.setUseParentHandlers(false); + ConsoleHandler handler = new ConsoleHandler(); + handler.setLevel(Level.WARNING); + logger.addHandler(handler); + + FileHandler logfile = new FileHandler("bot.log"); + logfile.setFormatter(new SimpleFormatter()); + logfile.setLevel(Level.WARNING); + logger.addHandler(logfile); + + Sentry.init(dotenv.get("SENTRY_DSN") + "?environment=" + env); + + LOGGER.severe("Bot started."); + Sentry.capture("Bot started"); + } } diff --git a/src/de/gost0r/pickupbot/discord/DiscordApplicationCommand.java b/src/de/gost0r/pickupbot/discord/DiscordApplicationCommand.java new file mode 100644 index 0000000..9192643 --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordApplicationCommand.java @@ -0,0 +1,51 @@ +package de.gost0r.pickupbot.discord; + +import de.gost0r.pickupbot.discord.api.DiscordAPI; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class DiscordApplicationCommand { + public int type; + public String name; + public String description; + public String default_member_permissions; + public ArrayList options; + + public DiscordApplicationCommand(String name, String description){ + this.type = 1; + this.name = name; + this.description = description; + this.options = new ArrayList(); + } + + public JSONObject getJSON(){ + JSONObject jsonObj = new JSONObject(); + jsonObj.put("type", type); + jsonObj.put("name", name); + jsonObj.put("description", description); + + if (default_member_permissions != null){ + jsonObj.put("default_member_permissions", default_member_permissions); + } + + if (options.size() > 0){ + List optionList = new ArrayList(); + for (DiscordCommandOption option : options){ + optionList.add(option.getJSON()); + } + jsonObj.put("options", optionList); + } + return jsonObj; + } + + public void addOption(DiscordCommandOption option){ + options.add(option); + } + + public void create(){ + DiscordAPI.createApplicationCommand(this); + } + +} diff --git a/src/de/gost0r/pickupbot/discord/DiscordBot.java b/src/de/gost0r/pickupbot/discord/DiscordBot.java index 75290da..15c6709 100644 --- a/src/de/gost0r/pickupbot/discord/DiscordBot.java +++ b/src/de/gost0r/pickupbot/discord/DiscordBot.java @@ -2,9 +2,13 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.List; +import java.util.function.LongFunction; import java.util.logging.Level; import java.util.logging.Logger; +import io.sentry.Sentry; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -17,26 +21,29 @@ public class DiscordBot { private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); private static String token = ""; - private static DiscordGuild guild; + private static String application_id = ""; + private static List guilds; protected DiscordUser self = null; private DiscordGateway gateway = null; private WsClientEndPoint endpoint = null; + private String env; public DiscordBot() { } - public void init() { + public void init(String env) { reconnect(); self = DiscordUser.getUser("@me"); - guild = DiscordGuild.getGuild("117622053061787657"); + guilds = DiscordAPI.getBotGuilds(); + this.env = env; } public void reconnect() { try { if (endpoint == null) { - endpoint = new WsClientEndPoint(this, new URI("wss://gateway.discord.gg/?encoding=json&v=6")); + endpoint = new WsClientEndPoint(this, new URI("wss://gateway.discord.gg/?encoding=json&v=9")); } else { endpoint.reconnect(); } @@ -48,11 +55,12 @@ public void reconnect() { endpoint.addMessageHandler(gateway); } catch (URISyntaxException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } catch (Exception e) { // we can trigger a handshake exception for endpoint. if that's the case, simply try again. // TODO: need to check that this is really working LOGGER.log(Level.SEVERE, "Exception: ", e); - init(); + init(env); } } @@ -67,6 +75,41 @@ public void handleEvent(DiscordGatewayEvent event, JSONObject obj) { obj.getString("content")); recvMessage(msg); break; + case INTERACTION_CREATE: + DiscordMessage message = null; + if (obj.has("message")){ + message = new DiscordMessage(obj.getJSONObject("message").getString("id"), + DiscordUser.getUser(obj.getJSONObject("message").getJSONObject("author")), + DiscordChannel.findChannel(obj.getJSONObject("message").getString("channel_id")), + obj.getJSONObject("message").getString("content")); + } + + if (obj.has("member")){ + user = DiscordUser.getUser(obj.getJSONObject("member").getJSONObject("user")); + } + else{ + user = DiscordUser.getUser(obj.getJSONObject("user")); + } + JSONArray values = null; + if (obj.getJSONObject("data").has("values")){ + values = obj.getJSONObject("data").getJSONArray("values"); + } + DiscordInteraction interaction = new DiscordInteraction(obj.getString("id"), + obj.getString("token"), + obj.getJSONObject("data"), + user, + message, + values); + if (obj.getJSONObject("data").has("custom_id")){ + recvInteraction(interaction); + } + else if (obj.getJSONObject("data").has("name")){ + if (obj.getJSONObject("data").has("options")){ + interaction.getOptions(obj.getJSONObject("data").optJSONArray("options")); + } + recvApplicationCommand(interaction); + } + break; case GUILD_MEMBER_UPDATE: user = DiscordUser.findUser(obj.getJSONObject("user").getString("id")); if (user != null) { @@ -86,6 +129,7 @@ public void handleEvent(DiscordGatewayEvent event, JSONObject obj) { } } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } tick(); } @@ -98,13 +142,38 @@ protected void recvMessage(DiscordMessage msg) { } + protected void recvInteraction(DiscordInteraction interaction) { + + } + + protected void recvApplicationCommand(DiscordInteraction interaction) { + + } + public void sendMsg(DiscordChannel channel, String msg) { DiscordAPI.sendMessage(channel, msg); } + public void sendMsg(DiscordChannel channel, String msg, DiscordEmbed embed) { + DiscordAPI.sendMessage(channel, msg, embed); + } + + public void sendMsg(DiscordUser user, String msg) { sendMsg(user.getDMChannel(), msg); } + + public void sendMsg(DiscordUser user, String msg, DiscordEmbed embed) { + sendMsg(user.getDMChannel(), msg, embed); + } + + public DiscordMessage sendMsgToEdit(DiscordChannel channel, String msg, DiscordEmbed embed, List components) { + return DiscordAPI.sendMessageToEdit(channel, msg, embed, components); + } + + public DiscordChannel createThread(DiscordChannel channel, String name) { + return DiscordAPI.createThread(channel, name); + } public static String getToken() { return token; @@ -114,12 +183,16 @@ public static void setToken(String token) { DiscordBot.token = token; } - public static DiscordGuild getGuild() { - return guild; + public static String getApplicationId() { + return application_id; + } + + public static void setApplicationId(String application_id) { + DiscordBot.application_id = application_id; } - public static void setGuild(DiscordGuild guild) { - DiscordBot.guild = guild; + public static List getGuilds() { + return guilds; } public DiscordUser parseMention(String string) { @@ -127,4 +200,12 @@ public DiscordUser parseMention(String string) { return DiscordUser.getUser(string); } + public boolean addUserRole(DiscordUser user, DiscordRole role){ + return DiscordAPI.addUserRole(user, role); + } + + public boolean removeUserRole(DiscordUser user, DiscordRole role){ + return DiscordAPI.removeUserRole(user, role); + } + } diff --git a/src/de/gost0r/pickupbot/discord/DiscordButton.java b/src/de/gost0r/pickupbot/discord/DiscordButton.java new file mode 100644 index 0000000..3cdb53b --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordButton.java @@ -0,0 +1,44 @@ +package de.gost0r.pickupbot.discord; + +import org.json.JSONObject; + +public class DiscordButton extends DiscordComponent { + + public DiscordButtonStyle style; + public String label; + public JSONObject emoji; + public String url; + + public DiscordButton(DiscordButtonStyle style) { + if (style == DiscordButtonStyle.NONE) { + style = DiscordButtonStyle.BLURPLE; + } + this.style = style; + disabled = false; + type = 2; + } + + public JSONObject getJSON() { + JSONObject buttonJSON = new JSONObject(); + buttonJSON.put("type", type); + buttonJSON.put("style", style.ordinal()); + + if (disabled) { + buttonJSON.put("disabled", disabled); + } + if (label != null) { + buttonJSON.put("label", label); + } + if (emoji != null) { + buttonJSON.put("emoji", emoji); + } + if (custom_id != null) { + buttonJSON.put("custom_id", custom_id); + } + if (url != null) { + buttonJSON.put("url", url); + } + + return buttonJSON; + } +} \ No newline at end of file diff --git a/src/de/gost0r/pickupbot/discord/DiscordButtonStyle.java b/src/de/gost0r/pickupbot/discord/DiscordButtonStyle.java new file mode 100644 index 0000000..ac7daee --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordButtonStyle.java @@ -0,0 +1,10 @@ +package de.gost0r.pickupbot.discord; + +public enum DiscordButtonStyle { + NONE, + BLURPLE, + GREY, + GREEN, + RED, + URL +} \ No newline at end of file diff --git a/src/de/gost0r/pickupbot/discord/DiscordChannel.java b/src/de/gost0r/pickupbot/discord/DiscordChannel.java index 453628c..ea1a271 100644 --- a/src/de/gost0r/pickupbot/discord/DiscordChannel.java +++ b/src/de/gost0r/pickupbot/discord/DiscordChannel.java @@ -5,6 +5,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import io.sentry.Sentry; import org.json.JSONException; import org.json.JSONObject; @@ -17,15 +18,22 @@ public class DiscordChannel { public DiscordChannelType type; public String name; public String topic; + public String parent_id; + public String guild_id; + public boolean isThread; public DiscordChannel(JSONObject channel) { try { this.id = channel.getString("id"); - this.type = DiscordChannelType.values()[channel.getInt("type")]; - this.name = channel.has("name") ? channel.getString("name") : null; - this.topic = channel.has("topic") ? channel.getString("topic") : null; + this.type = channel.getInt("type") == 11 ? DiscordChannelType.THREAD_CHANNEL : DiscordChannelType.values()[channel.getInt("type")]; + this.name = channel.isNull("name") ? null : channel.getString("name") ; + this.topic = channel.isNull("topic") ? null : channel.getString("topic"); + this.parent_id = channel.isNull("parent_id") ? null : channel.getString("parent_id"); + this.guild_id = channel.isNull("guild_id") ? null : channel.getString("guild_id"); + this.isThread = !channel.isNull("thread_metadata"); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -57,7 +65,7 @@ public static DiscordChannel findChannel(String channelID) { public boolean equals(Object o) { if (o instanceof DiscordChannel) { DiscordChannel chan = (DiscordChannel) o; - return chan.id == this.id; + return chan.id.equals(this.id); } return false; } @@ -67,4 +75,14 @@ public boolean equals(Object o) { public String toString() { return id; } + + public void archive() { + if (isThread) { + DiscordAPI.archiveThread(this); + } + } + + public void delete(){ + DiscordAPI.deleteChannel(this); + } } diff --git a/src/de/gost0r/pickupbot/discord/DiscordChannelType.java b/src/de/gost0r/pickupbot/discord/DiscordChannelType.java index 0849b76..700c047 100644 --- a/src/de/gost0r/pickupbot/discord/DiscordChannelType.java +++ b/src/de/gost0r/pickupbot/discord/DiscordChannelType.java @@ -5,5 +5,6 @@ public enum DiscordChannelType { DM, GUILD_VOICE, GROUP_DM, - GUILD_CATEGORY + GUILD_CATEGORY, + THREAD_CHANNEL } diff --git a/src/de/gost0r/pickupbot/discord/DiscordCommandOption.java b/src/de/gost0r/pickupbot/discord/DiscordCommandOption.java new file mode 100644 index 0000000..18c03c8 --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordCommandOption.java @@ -0,0 +1,48 @@ +package de.gost0r.pickupbot.discord; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class DiscordCommandOption { + public DiscordCommandOptionType type; + public String name; + public String description; + public boolean required; + public ArrayList choices; + + public DiscordCommandOption(DiscordCommandOptionType type, String name, String description){ + this.type = type; + this.name = name; + this.description = description; + this.required = true; + this.choices = new ArrayList(); + } + + public void addChoice(DiscordCommandOptionChoice choice){ + choices.add(choice); + } + + public JSONObject getJSON(){ + JSONObject jsonObj = new JSONObject(); + jsonObj.put("type", type.ordinal()); + jsonObj.put("name", name); + jsonObj.put("description", description); + + if (required){ + jsonObj.put("required", required); + } + + if (choices.size() > 0){ + List choiceList = new ArrayList(); + for (DiscordCommandOptionChoice choice : choices){ + choiceList.add(choice.getJSON()); + } + jsonObj.put("choices", choiceList); + } + + return jsonObj; + } + +} diff --git a/src/de/gost0r/pickupbot/discord/DiscordCommandOptionChoice.java b/src/de/gost0r/pickupbot/discord/DiscordCommandOptionChoice.java new file mode 100644 index 0000000..393506c --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordCommandOptionChoice.java @@ -0,0 +1,21 @@ +package de.gost0r.pickupbot.discord; + +import org.json.JSONObject; + +public class DiscordCommandOptionChoice { + public String name; + public String value; + + public DiscordCommandOptionChoice(String name, String value){ + this.name= name; + this.value = value; + } + + public JSONObject getJSON(){ + JSONObject jsonObj = new JSONObject(); + jsonObj.put("name", name); + jsonObj.put("value", value); + return jsonObj; + } +} + diff --git a/src/de/gost0r/pickupbot/discord/DiscordCommandOptionType.java b/src/de/gost0r/pickupbot/discord/DiscordCommandOptionType.java new file mode 100644 index 0000000..9806506 --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordCommandOptionType.java @@ -0,0 +1,16 @@ +package de.gost0r.pickupbot.discord; + +public enum DiscordCommandOptionType { + NONE, + SUB_COMMAND, + SUB_COMMAND_GROUP, + STRING, + INTEGER, + BOOLEAN, + USER, + CHANNEL, + ROLE, + MENTIONABLE, + NUMBER, + ATTACHMENT +} diff --git a/src/de/gost0r/pickupbot/discord/DiscordComponent.java b/src/de/gost0r/pickupbot/discord/DiscordComponent.java new file mode 100644 index 0000000..168b811 --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordComponent.java @@ -0,0 +1,16 @@ +package de.gost0r.pickupbot.discord; + +import org.json.JSONObject; + +public class DiscordComponent { + public int type; + public String custom_id; + public boolean disabled; + + public JSONObject getJSON() { + return null; + } +} + + + diff --git a/src/de/gost0r/pickupbot/discord/DiscordEmbed.java b/src/de/gost0r/pickupbot/discord/DiscordEmbed.java new file mode 100644 index 0000000..74601d8 --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordEmbed.java @@ -0,0 +1,63 @@ +package de.gost0r.pickupbot.discord; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONObject; + +public class DiscordEmbed { + + public String title; + public String description; + public String thumbnail; + public int color; + public List fields; + public String footer; + public String footer_icon; + public String timestamp; + + public DiscordEmbed() { + fields = new ArrayList(); + } + + public void addField(String name, String value, boolean inline) { + JSONObject field = new JSONObject(); + field.put("name", name); + field.put("value", value); + field.put("inline", inline); + + fields.add(field); + } + + public JSONObject getJSON() { + JSONObject embedJSON = new JSONObject(); + + if (title != null) { + embedJSON.put("title", title); + } + if (description != null) { + embedJSON.put("description", description); + } + if (thumbnail != null) { + embedJSON.put("thumbnail", new JSONObject().put("url", thumbnail)); + } + if (color != 0) { + embedJSON.put("color", color); + } + if (fields != null) { + embedJSON.put("fields", fields); + } + if (footer != null){ + JSONObject footerJSON = new JSONObject().put("text", footer); + if (footer_icon != null){ + footerJSON.put("icon_url", footer_icon); + } + embedJSON.put("footer", footerJSON); + } + if (timestamp != null) { + embedJSON.put("timestamp", timestamp); + } + + return embedJSON; + } +} \ No newline at end of file diff --git a/src/de/gost0r/pickupbot/discord/DiscordInteraction.java b/src/de/gost0r/pickupbot/discord/DiscordInteraction.java new file mode 100644 index 0000000..f8a689e --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordInteraction.java @@ -0,0 +1,61 @@ +package de.gost0r.pickupbot.discord; + +import de.gost0r.pickupbot.discord.api.DiscordAPI; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.lang.reflect.Array; +import java.util.ArrayList; + +public class DiscordInteraction { + public String id; + public String name; // Application commands + public String token; + public DiscordUser user; + public String custom_id; + public DiscordMessage message; + public ArrayList values; + public ArrayList options; + + public DiscordInteraction(String id, String token, JSONObject data, DiscordUser user, DiscordMessage message, JSONArray values) { + this.id = id; + this.token = token; + this.user = user; + if (data.has("custom_id")){ + this.custom_id = data.getString("custom_id"); + } + if (data.has("name")){ + this.name = data.getString("name"); + } + this.message = message; + + ArrayList strValues = new ArrayList(); + if (values != null){ + for (int i = 0; i < values.length(); i++){ + strValues.add(values.getString(i)); + } + } + this.values = strValues; + this.options = new ArrayList(); + } + + public void respond(String content) { + DiscordAPI.interactionRespond(id, token, content, null, null); + } + + public void respond(String content, DiscordEmbed embed) { + DiscordAPI.interactionRespond(id, token, content, embed, null); + } + + public void respond(String content, DiscordEmbed embed, ArrayList components) { + DiscordAPI.interactionRespond(id, token, content, embed, components); + } + + public void getOptions(JSONArray options){ + for (int i = 0 ; i < options.length() ; i++){ + JSONObject choiceObj = options.getJSONObject(i); + DiscordCommandOptionChoice choice = new DiscordCommandOptionChoice(choiceObj.getString("name"), String.valueOf(choiceObj.get("value"))); + this.options.add(choice); + } + } +} \ No newline at end of file diff --git a/src/de/gost0r/pickupbot/discord/DiscordMessage.java b/src/de/gost0r/pickupbot/discord/DiscordMessage.java index d812da9..eb04274 100644 --- a/src/de/gost0r/pickupbot/discord/DiscordMessage.java +++ b/src/de/gost0r/pickupbot/discord/DiscordMessage.java @@ -1,5 +1,7 @@ package de.gost0r.pickupbot.discord; +import de.gost0r.pickupbot.discord.api.DiscordAPI; + public class DiscordMessage { public String id; @@ -13,4 +15,13 @@ public DiscordMessage(String id, DiscordUser user, DiscordChannel channel, Strin this.channel = channel; this.content = content; } + + public void edit(String content, DiscordEmbed embed) { + DiscordAPI.editMessage(this, content, embed); + this.content = content; + } + + public void delete() { + DiscordAPI.deleteMessage(channel, id); + } } diff --git a/src/de/gost0r/pickupbot/discord/DiscordSelectMenu.java b/src/de/gost0r/pickupbot/discord/DiscordSelectMenu.java new file mode 100644 index 0000000..6692b96 --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordSelectMenu.java @@ -0,0 +1,41 @@ +package de.gost0r.pickupbot.discord; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class DiscordSelectMenu extends DiscordComponent { + public ArrayList options; + public String placeholder; + + public DiscordSelectMenu(ArrayList options) { + this.options = options; + disabled = false; + type = 3; + } + + public JSONObject getJSON() { + JSONObject selectMenuJSON = new JSONObject(); + selectMenuJSON.put("type", type); + selectMenuJSON.put("custom_id", custom_id); + + if (options != null){ + List optionList = new ArrayList(); + for (DiscordSelectOption option : options){ + optionList.add(option.getJSON()); + } + selectMenuJSON.put("options", optionList); + } + + if (disabled) { + selectMenuJSON.put("disabled", disabled); + } + + if (placeholder != null) { + selectMenuJSON.put("placeholder", placeholder); + } + + return selectMenuJSON; + } +} diff --git a/src/de/gost0r/pickupbot/discord/DiscordSelectOption.java b/src/de/gost0r/pickupbot/discord/DiscordSelectOption.java new file mode 100644 index 0000000..4cc24f3 --- /dev/null +++ b/src/de/gost0r/pickupbot/discord/DiscordSelectOption.java @@ -0,0 +1,35 @@ +package de.gost0r.pickupbot.discord; + +import org.json.JSONObject; + +public class DiscordSelectOption { + public String label; + public String value; + public String description; + public JSONObject emoji; + public boolean isDefault; + + public DiscordSelectOption(String label, String value){ + this.label = label; + this.value = value; + } + + + public JSONObject getJSON() { + JSONObject selectOptionJSON = new JSONObject(); + selectOptionJSON.put("label", label); + selectOptionJSON.put("value", value); + + if (description != null){ + selectOptionJSON.put("description", description); + } + if (emoji != null) { + selectOptionJSON.put("emoji", emoji); + } + if (isDefault) { + selectOptionJSON.put("default", isDefault); + } + + return selectOptionJSON; + } +} diff --git a/src/de/gost0r/pickupbot/discord/DiscordUser.java b/src/de/gost0r/pickupbot/discord/DiscordUser.java index fef1b81..35740d8 100644 --- a/src/de/gost0r/pickupbot/discord/DiscordUser.java +++ b/src/de/gost0r/pickupbot/discord/DiscordUser.java @@ -7,11 +7,13 @@ import java.util.logging.Level; import java.util.logging.Logger; +import io.sentry.Sentry; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import de.gost0r.pickupbot.discord.api.DiscordAPI; +import de.gost0r.pickupbot.pickup.PickupBot; public class DiscordUser { private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); @@ -40,9 +42,10 @@ public DiscordUser(JSONObject user) { this.id = user.getString("id"); this.username = user.getString("username"); this.discriminator = user.getString("discriminator"); - this.avatar = user.getString("avatar"); + this.avatar = user.isNull("avatar") ? null : user.getString("avatar"); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -65,26 +68,54 @@ public void setRoles(DiscordGuild guild, JSONArray array) { roles.put(guild, roleList); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception for " + array.toString() + ": ", e); + Sentry.capture(e); } } - public List getRoles(DiscordGuild guild) { - if (roles.containsKey(guild)) { - return roles.get(guild); - } else { + public List getRoles(List guilds) { + List list = new ArrayList(); + for (DiscordGuild guild : guilds) { + if (roles.containsKey(guild)){ + list.addAll(roles.get(guild)); + continue; + } + JSONArray ar = DiscordAPI.requestUserGuildRoles(guild.id, this.id); - List list = new ArrayList(); + if (ar == null){ // If the user is not a member of this guild + continue; + } + for (int i = 0; i < ar.length(); ++i) { try { DiscordRole role = DiscordRole.getRole(ar.getString(i)); list.add(role); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } roles.put(guild, list); - return list; } + return list; + } + + public boolean hasRole(DiscordGuild guild, DiscordRole role){ + JSONArray ar = DiscordAPI.requestUserGuildRoles(guild.id, this.id); + if (ar == null){ // If the user is not a member of this guild + return false; + } + + for (int i = 0; i < ar.length(); ++i) { + try { + if (role.id.equals(ar.getString(i))){ + return true; + } + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + return false; } public DiscordChannel getDMChannel() { @@ -95,10 +126,9 @@ public DiscordChannel getDMChannel() { this.channel = channel; } return channel; - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (NullPointerException e) { + } catch (JSONException | NullPointerException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } @@ -115,11 +145,12 @@ public static DiscordUser getUser(JSONObject obj) { return newUser; } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } public static DiscordUser getUser(String id) { - if (id.matches("[0-9]+")) { + if (id.matches("[0-9]+") && id.length() > 10) { if (userList.containsKey(id)) { return userList.get(id); } @@ -142,6 +173,32 @@ public static DiscordUser findUser(String id) { return null; } + public boolean hasAdminRights() { + List roleList = this.getRoles(DiscordBot.getGuilds()); + List adminList = PickupBot.logic.getAdminList(); + for (DiscordRole s : roleList) { + for (DiscordRole r : adminList) { + if (s.equals(r)) { + return true; + } + } + } + return false; + } + + public boolean hasSuperAdminRights() { + List roleList = this.getRoles(DiscordBot.getGuilds()); + List adminList = PickupBot.logic.getSuperAdminList(); + for (DiscordRole s : roleList) { + for (DiscordRole r : adminList) { + if (s.equals(r)) { + return true; + } + } + } + return false; + } + @Override public boolean equals(Object o) { if (o instanceof DiscordUser) { @@ -150,5 +207,9 @@ public boolean equals(Object o) { } return false; } + + public String getAvatarUrl() { + return "https://cdn.discordapp.com/avatars/" + id + "/" + avatar + ".png"; + } } diff --git a/src/de/gost0r/pickupbot/discord/api/DiscordAPI.java b/src/de/gost0r/pickupbot/discord/api/DiscordAPI.java index 55a0123..96935f9 100644 --- a/src/de/gost0r/pickupbot/discord/api/DiscordAPI.java +++ b/src/de/gost0r/pickupbot/discord/api/DiscordAPI.java @@ -1,44 +1,126 @@ package de.gost0r.pickupbot.discord.api; -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; +import java.io.*; import java.net.HttpURLConnection; -import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.net.http.*; +import de.gost0r.pickupbot.discord.*; +import io.sentry.Sentry; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import de.gost0r.pickupbot.discord.DiscordBot; -import de.gost0r.pickupbot.discord.DiscordChannel; - public class DiscordAPI { private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); public static final String api_url = "https://discordapp.com/api/"; - public static final String api_version = "v6"; + public static final String api_version = "v9"; public static boolean sendMessage(DiscordChannel channel, String msg) { try { String reply = sendPostRequest("/channels/"+ channel.id + "/messages", new JSONObject().put("content", msg)); + + if (reply == null) { + return false; + } + JSONObject obj = new JSONObject(reply); - return obj != null && !obj.has("code"); + return !obj.has("code"); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return false; } + public static boolean sendMessage(DiscordChannel channel, String msg, DiscordEmbed embed) { + try { + List embedList = new ArrayList(); + embedList.add(embed.getJSON()); + String reply = sendPostRequest("/channels/"+ channel.id + "/messages", new JSONObject().put("content", msg).put("embeds", embedList)); + + if (reply == null) { + return false; + } + + JSONObject obj = new JSONObject(reply); + return !obj.has("code"); + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return false; + } + + public static DiscordMessage sendMessageToEdit(DiscordChannel channel, String msg, DiscordEmbed embed, List components) { + try { + List embedList = new ArrayList(); + if (embed != null) { + embedList.add(embed.getJSON()); + } + + List> componentListGlob = new ArrayList>(); + List componentList = new ArrayList(); + if(components != null) { + for (DiscordComponent component : components) { + componentList.add(component.getJSON()); + + if (componentList.size() == 5) { + componentListGlob.add(new ArrayList(componentList)); + componentList.clear(); + } + } + + if (!componentList.isEmpty()) { + componentListGlob.add(componentList); + } + } + +// if (msg.equals("")){ +// msg= null; +// } + JSONObject content = new JSONObject().put("content", msg); + + if (!embedList.isEmpty()) { + content.put("embeds", embedList); + } + if(!componentListGlob.isEmpty()) { + List componentObjList = new ArrayList(); + for (List componentListElem : componentListGlob) { + JSONObject componentObj = new JSONObject(); + componentObj.put("type", 1); + componentObj.put("components", componentListElem); + componentObjList.add(componentObj); + } + content.put("components", componentObjList); + } + + String reply = sendPostRequest("/channels/"+ channel.id + "/messages", content); + + if (reply == null) { + return null; + } + + JSONObject obj = new JSONObject(reply); + + return new DiscordMessage(obj.getString("id"), null, channel, msg); + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + public static boolean deleteMessage(DiscordChannel channel, String msgid) { try { String reply = sendDeleteRequest("/channels/"+ channel.id + "/messages/" + msgid); - LOGGER.info(reply); // TODO: Remove JSONObject obj = null; if (reply != null) { if (reply.isEmpty()) { @@ -50,10 +132,116 @@ public static boolean deleteMessage(DiscordChannel channel, String msgid) { return obj != null && !obj.has("code"); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return false; + } + + public static boolean deleteChannel(DiscordChannel channel) { + try { + String reply = sendDeleteRequest("/channels/"+ channel.id); + JSONObject obj = null; + if (reply != null) { + if (reply.isEmpty()) { + return true; + } + obj = new JSONObject(reply); + } + return obj != null && !obj.has("code"); + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return false; + } + + public static boolean editMessage(DiscordMessage msg, String content, DiscordEmbed embed) { + try { + List embedList = new ArrayList(); + embedList.add(embed.getJSON()); + String reply = sendPatchRequest("/channels/"+ msg.channel.id + "/messages/" + msg.id, new JSONObject().put("content", content).put("embeds", embedList)); + return reply != null; + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return false; + } + + public static DiscordChannel createThread(DiscordChannel channel, String name) { + try { + String reply = sendPostRequest("/channels/"+ channel.id + "/threads", new JSONObject().put("name", name).put("type", 11).put("auto_archive_duration", 60)); + + if (reply == null) { + return null; + } + + JSONObject obj = new JSONObject(reply); + return new DiscordChannel(obj); + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + + public static boolean archiveThread(DiscordChannel channel) { + try { + String reply = sendPatchRequest("/channels/"+ channel.id, new JSONObject().put("archived", true)); + //JSONObject obj = new JSONObject(reply); + return true; + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return false; } + public static void interactionRespond(String interaction_id, String interaction_token, String content, DiscordEmbed embed, ArrayList components) { + if (content == null && embed == null && components == null) { + sendPostRequest("/interactions/"+ interaction_id + "/" + interaction_token + "/callback", new JSONObject().put("type", 6)); + return; + } + + JSONObject data = new JSONObject(); + data.put("flags", 64); + if (content != null){ + data.put("content", content); + } + if (embed != null){ + List embedList = new ArrayList(); + embedList.add(embed.getJSON()); + data.put("embeds", embedList); + } + + List> componentListGlob = new ArrayList>(); + List componentList = new ArrayList(); + if(components != null) { + for (DiscordComponent component : components) { + componentList.add(component.getJSON()); + + if (componentList.size() == 5) { + componentListGlob.add(new ArrayList(componentList)); + componentList.clear(); + } + } + + if (!componentList.isEmpty()) { + componentListGlob.add(componentList); + } + + List componentObjList = new ArrayList(); + for (List componentListElem : componentListGlob) { + JSONObject componentObj = new JSONObject(); + componentObj.put("type", 1); + componentObj.put("components", componentListElem); + componentObjList.add(componentObj); + } + data.put("components", componentObjList); + } + sendPostRequest("/interactions/"+ interaction_id + "/" + interaction_token + "/callback", new JSONObject().put("type", 4).put("data", data)); + } + private static synchronized String sendPostRequest(String request, JSONObject content) { // Thread.dumpStack(); try { @@ -74,16 +262,20 @@ private static synchronized String sendPostRequest(String request, JSONObject co wr.write(postData); } - if (c.getResponseCode() != 200) { - LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + if (c.getResponseCode() != 200 && c.getResponseCode() != 201 && c.getResponseCode() != 204) { if (c.getResponseCode() == 429 || c.getResponseCode() == 502) { try { Thread.sleep(1000); return sendPostRequest(request, content); } catch (InterruptedException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + } return null; } @@ -93,10 +285,9 @@ private static synchronized String sendPostRequest(String request, JSONObject co while ((output = br.readLine()) != null) { try { fullmsg += output; - } catch (ClassCastException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (NullPointerException e) { + } catch (ClassCastException | NullPointerException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } c.disconnect(); @@ -104,15 +295,61 @@ private static synchronized String sendPostRequest(String request, JSONObject co LOGGER.fine("API call complete for " + request + ": " + fullmsg); return fullmsg; - } catch (MalformedURLException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } - private static synchronized String sendGetRequest(String request) { + private static synchronized String sendPatchRequest(String request, JSONObject content) { +// Thread.dumpStack(); + try { + + URL url = new URL(api_url + api_version + request); + HttpURLConnection c = (HttpURLConnection) url.openConnection(); + + HttpClient httpClient = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_2) + .build(); + + HttpRequest patchRequest = HttpRequest.newBuilder() + .uri(URI.create(api_url + api_version + request)) + .method("PATCH", HttpRequest.BodyPublishers.ofString(content.toString())) + .header("User-Agent", "Bot") + .header("Authorization", "Bot " + DiscordBot.getToken()) + .header("Content-Type", "application/json") + .build(); + + HttpResponse response = httpClient.send(patchRequest,HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != 200 && response.statusCode() != 201 && response.statusCode() != 204) { + if (response.statusCode() == 429 || response.statusCode() == 502) { + try { + Thread.sleep(1000); + return sendPatchRequest(request, content); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + } + } + + return response.toString(); + + } catch (InterruptedException | IOException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + + private static synchronized String sendGetRequest(String request, boolean canBeNull) { // Thread.dumpStack(); try { URL url = new URL(api_url + api_version + request); @@ -121,17 +358,24 @@ private static synchronized String sendGetRequest(String request) { c.setRequestProperty("User-Agent", "Bot"); c.setRequestProperty("Authorization", "Bot " + DiscordBot.getToken()); - if (c.getResponseCode() != 200) { - LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + if (c.getResponseCode() != 200 && c.getResponseCode() != 201 && c.getResponseCode() != 204) { if (c.getResponseCode() == 429 || c.getResponseCode() == 502) { try { Thread.sleep(1000); - return sendGetRequest(request); + return sendGetRequest(request, canBeNull); } catch (InterruptedException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } + if (c.getResponseCode() == 404 && canBeNull){ + return null; + } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + } } BufferedReader br = new BufferedReader(new InputStreamReader((c.getInputStream()))); @@ -140,10 +384,9 @@ private static synchronized String sendGetRequest(String request) { while ((output = br.readLine()) != null) { try { fullmsg += output; - } catch (ClassCastException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (NullPointerException e) { + } catch (ClassCastException | NullPointerException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } c.disconnect(); @@ -151,10 +394,9 @@ private static synchronized String sendGetRequest(String request) { LOGGER.fine("API call complete for " + request + ": " + fullmsg); return fullmsg; - } catch (MalformedURLException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } @@ -168,18 +410,22 @@ private static synchronized String sendDeleteRequest(String request) { c.setRequestMethod("DELETE"); c.setRequestProperty("User-Agent", "Bot"); c.setRequestProperty("Authorization", "Bot " + DiscordBot.getToken()); - - if (c.getResponseCode() != 200) { - LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + + if (c.getResponseCode() != 200 && c.getResponseCode() != 201 && c.getResponseCode() != 204) { if (c.getResponseCode() == 429 || c.getResponseCode() == 502) { try { Thread.sleep(1000); - return sendGetRequest(request); + return sendDeleteRequest(request); } catch (InterruptedException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + } } BufferedReader br = new BufferedReader(new InputStreamReader((c.getInputStream()))); @@ -188,10 +434,9 @@ private static synchronized String sendDeleteRequest(String request) { while ((output = br.readLine()) != null) { try { fullmsg += output; - } catch (ClassCastException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (NullPointerException e) { + } catch (ClassCastException | NullPointerException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } c.disconnect(); @@ -199,10 +444,69 @@ private static synchronized String sendDeleteRequest(String request) { LOGGER.fine("API call complete for " + request + ": " + fullmsg); return fullmsg; - } catch (MalformedURLException e) { + } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + + private static synchronized String sendPutRequest(String request, JSONObject content) { +// Thread.dumpStack(); + try { + byte[] postData = content.toString().getBytes( StandardCharsets.UTF_8 ); + int postDataLength = postData.length; + + URL url = new URL(api_url + api_version + request); + HttpURLConnection c = (HttpURLConnection) url.openConnection(); + c.setRequestMethod("PUT"); + c.setRequestProperty("Authorization", "Bot " + DiscordBot.getToken()); + c.setRequestProperty("charset", "utf-8"); + c.setRequestProperty("Content-Type", "application/json"); + c.setRequestProperty("User-Agent", "Bot"); + c.setDoOutput(true); + c.setUseCaches(false); + c.setRequestProperty("Content-Length", Integer.toString(postDataLength)); + try (DataOutputStream wr = new DataOutputStream( c.getOutputStream())) { + wr.write(postData); + } + + if (c.getResponseCode() != 200 && c.getResponseCode() != 201 && c.getResponseCode() != 204) { + if (c.getResponseCode() == 429 || c.getResponseCode() == 502) { + try { + Thread.sleep(1000); + return sendPostRequest(request, content); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + } + return null; + } + + BufferedReader br = new BufferedReader(new InputStreamReader((c.getInputStream()))); + String fullmsg = ""; + String output = ""; + while ((output = br.readLine()) != null) { + try { + fullmsg += output; + } catch (ClassCastException | NullPointerException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + c.disconnect(); + + LOGGER.fine("API call complete for " + request + ": " + fullmsg); + return fullmsg; + } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } @@ -215,56 +519,118 @@ public static JSONObject createDM(String userid) { return obj; } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } public static JSONArray requestDM() { - String reply = sendGetRequest("/users/@me/channels"); + String reply = sendGetRequest("/users/@me/channels", false); if (reply != null && !reply.isEmpty()) { try { return new JSONArray(reply); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } return null; } public static JSONObject requestUser(String userID) { - String reply = sendGetRequest("/users/" + userID); + String reply = sendGetRequest("/users/" + userID, false); if (reply != null && !reply.isEmpty()) { try { return new JSONObject(reply); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } return null; } public static JSONObject requestChannel(String channelID) { - String reply = sendGetRequest("/channels/" + channelID); + String reply = sendGetRequest("/channels/" + channelID, false); if (reply != null && !reply.isEmpty()) { try { return new JSONObject(reply); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } return null; } public static JSONArray requestUserGuildRoles(String guild, String userID) { - String reply = sendGetRequest("/guilds/" + guild + "/members/" + userID); + String reply = sendGetRequest("/guilds/" + guild + "/members/" + userID, true); if (reply != null && !reply.isEmpty()) { try { return new JSONObject(reply).getJSONArray("roles"); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture("Unable to send msg, session is closed."); } } return null; } + public static List getBotGuilds() { + List guilds = new ArrayList(); + String reply = sendGetRequest("/users/@me/guilds", false); + if (reply != null && !reply.isEmpty()) { + try { + JSONArray guildArray = new JSONArray(reply); + for (int i = 0 ; i < guildArray.length() ; i++){ + guilds.add(DiscordGuild.getGuild(guildArray.getJSONObject(i).getString("id"))); + } + return guilds; + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture("Unable to send msg, session is closed."); + } + } + return null; + } + + public static boolean addUserRole(DiscordUser user, DiscordRole role) { + try { + // TODO IMPLEMENT THIS FOR OTHER SERVERS + String reply = sendPutRequest("/guilds/117622053061787657/members/" + user.id + "/roles/" + role.id, new JSONObject()); + + return reply != null; + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return false; + } + + public static boolean removeUserRole(DiscordUser user, DiscordRole role) { + try { + // TODO IMPLEMENT THIS FOR OTHER SERVERS + // 404 can happen when trying to remove a role from a user that is only in the other pickup server + String reply = sendDeleteRequest("/guilds/117622053061787657/members/" + user.id + "/roles/" + role.id); + + return reply != null; + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return false; + } + + public static boolean createApplicationCommand(DiscordApplicationCommand app){ + try { + String reply = sendPostRequest("/applications/" + DiscordBot.getApplicationId() + "/commands", app.getJSON()); + + return reply != null; + } catch (JSONException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return false; + } + } diff --git a/src/de/gost0r/pickupbot/discord/web/DiscordGateway.java b/src/de/gost0r/pickupbot/discord/web/DiscordGateway.java index 86532ba..5b3294c 100644 --- a/src/de/gost0r/pickupbot/discord/web/DiscordGateway.java +++ b/src/de/gost0r/pickupbot/discord/web/DiscordGateway.java @@ -4,6 +4,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import io.sentry.Sentry; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -47,7 +48,18 @@ public void handleMessage(String message) { // Parse to packet int op = jsonObj.getInt("op"); - JSONObject d = jsonObj.isNull("d") ? null : new JSONObject(jsonObj.getString("d")); + + JSONObject d; + + if (jsonObj.isNull("d")) + { + d = null; + } + else + { + d = jsonObj.getJSONObject("d"); + } + int s = jsonObj.isNull("s") ? -1 : jsonObj.getInt("s") ; String t = jsonObj.isNull("t") ? null : jsonObj.getString("t"); DiscordPacket incPak = new DiscordPacket(DiscordGatewayOpcode.values()[op], d, s, t); @@ -60,6 +72,7 @@ public void handleMessage(String message) { } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception for '" + message + "': ", e); + Sentry.capture(e); } } @@ -83,6 +96,7 @@ private void handlePacketHello(DiscordPacket p) { heartbeatTimer.scheduleAtFixedRate(heartbeatTask, interval/2, interval); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } // sending identify @@ -108,12 +122,15 @@ private void handlePacketHello(DiscordPacket p) { presence.put("afk", false); msg.put("presence", presence); + msg.put("intents", 32365); + DiscordPacket outP = new DiscordPacket(DiscordGatewayOpcode.Identify, msg, -1, "__null__"); sendMessage(outP.toSend()); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } diff --git a/src/de/gost0r/pickupbot/discord/web/DiscordGatewayEvent.java b/src/de/gost0r/pickupbot/discord/web/DiscordGatewayEvent.java index 104a7d0..62af5d9 100644 --- a/src/de/gost0r/pickupbot/discord/web/DiscordGatewayEvent.java +++ b/src/de/gost0r/pickupbot/discord/web/DiscordGatewayEvent.java @@ -25,6 +25,9 @@ public enum DiscordGatewayEvent { GUILD_ROLE_CREATE, GUILD_ROLE_UPDATE, GUILD_ROLE_DELETE, + GUILD_JOIN_REQUEST_DELETE, + GUILD_JOIN_REQUEST_UPDATE, + GUILD_AUDIT_LOG_ENTRY_CREATE, MESSAGE_CREATE, MESSAGE_UPDATE, @@ -42,5 +45,15 @@ public enum DiscordGatewayEvent { VOICE_STATE_UPDATE, VOICE_SERVER_UPDATE, - WEBHOOKS_UPDATE + WEBHOOKS_UPDATE, + + THREAD_CREATE, + THREAD_MEMBER_UPDATE, + THREAD_UPDATE, + THREAD_DELETE, + + INTERACTION_CREATE, + + INVITE_CREATE, + INVITE_DELETE } diff --git a/src/de/gost0r/pickupbot/discord/web/DiscordPacket.java b/src/de/gost0r/pickupbot/discord/web/DiscordPacket.java index 70b5d7e..ff52752 100644 --- a/src/de/gost0r/pickupbot/discord/web/DiscordPacket.java +++ b/src/de/gost0r/pickupbot/discord/web/DiscordPacket.java @@ -3,6 +3,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import io.sentry.Sentry; import org.json.JSONException; import org.json.JSONObject; @@ -37,6 +38,7 @@ public String toSend() { return msg.toString(); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } diff --git a/src/de/gost0r/pickupbot/discord/web/HeartbeatTask.java b/src/de/gost0r/pickupbot/discord/web/HeartbeatTask.java index 5fdde34..610fcc5 100644 --- a/src/de/gost0r/pickupbot/discord/web/HeartbeatTask.java +++ b/src/de/gost0r/pickupbot/discord/web/HeartbeatTask.java @@ -4,6 +4,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import io.sentry.Sentry; import org.json.JSONException; import org.json.JSONObject; @@ -28,6 +29,7 @@ private void sendHeatbeat() { dg.sendMessage(msg.toString()); } catch (JSONException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } diff --git a/src/de/gost0r/pickupbot/discord/web/WsClientEndPoint.java b/src/de/gost0r/pickupbot/discord/web/WsClientEndPoint.java index d3024f8..c12eede 100644 --- a/src/de/gost0r/pickupbot/discord/web/WsClientEndPoint.java +++ b/src/de/gost0r/pickupbot/discord/web/WsClientEndPoint.java @@ -16,6 +16,7 @@ import javax.websocket.WebSocketContainer; import de.gost0r.pickupbot.discord.DiscordBot; +import io.sentry.Sentry; @ClientEndpoint public class WsClientEndPoint { @@ -38,10 +39,9 @@ private void connect() { try { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); container.connectToServer(this, endpointURI); - } catch (DeploymentException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (IOException e) { + } catch (DeploymentException | IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -53,18 +53,21 @@ public void reconnect() { connect(); } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @OnOpen public void onOpen(Session userSession) { LOGGER.warning("WebSocket opened. "); + Sentry.capture("WebSocket opened. "); this.userSession = userSession; } @OnClose public void onClose(Session userSession, CloseReason reason) { LOGGER.warning("WebSocket closed. " + reason.toString()); + Sentry.capture("WebSocket closed. " + reason.toString()); this.userSession = null; bot.reconnect(); } @@ -85,6 +88,7 @@ public void sendMessage(String message) { this.userSession.getAsyncRemote().sendText(message); } else { LOGGER.warning("Unable to send msg, session is closed."); + Sentry.capture("Unable to send msg, session is closed."); } } diff --git a/src/de/gost0r/pickupbot/ftwgl/FtwglAPI.java b/src/de/gost0r/pickupbot/ftwgl/FtwglAPI.java new file mode 100644 index 0000000..147a414 --- /dev/null +++ b/src/de/gost0r/pickupbot/ftwgl/FtwglAPI.java @@ -0,0 +1,301 @@ +package de.gost0r.pickupbot.ftwgl; + +import de.gost0r.pickupbot.discord.DiscordUser; +import de.gost0r.pickupbot.pickup.Config; +import de.gost0r.pickupbot.pickup.Country; +import de.gost0r.pickupbot.pickup.Player; +import de.gost0r.pickupbot.pickup.Region; +import de.gost0r.pickupbot.pickup.server.Server; +import io.sentry.Sentry; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class FtwglAPI { + private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + private static String api_url; + private static String api_key; + private static final int port = 27960; + + public static void setupCredentials(String url, String key){ + FtwglAPI.api_url = url; + FtwglAPI.api_key = key; + } + + public static String launchAC(Player player, String ip, String password){ + JSONObject requestObj = new JSONObject() + .put("discord_id", Long.parseLong(player.getDiscordUser().id)) + .put("address", ip) + .put("password", password); + + String response = sendPostRequest("/launch/pug", requestObj); + + if (response == null){ + return Config.ftw_notconnected; + } + else return Config.ftw_success; + } + + public static boolean checkIfPingStored(Player p){ + String request = "/ping/" + p.getDiscordUser().id; + int response = sendHeadRequest(request); + return response == 200; + } + + public static String requestPingUrl(Player player){ + JSONObject requestObj = new JSONObject() + .put("discord_id", Long.parseLong(player.getDiscordUser().id)) + .put("username", player.getDiscordUser().username) + .put("urt_auth", player.getUrtauth()); + + String response = sendPostRequest("/ping", requestObj); + JSONObject obj = new JSONObject(response); + + if (!obj.has("url")){ + return Config.ftw_error; + } + return obj.getString("url"); + } + + public static Server spawnDynamicServer(List playerList){ + JSONObject requestObj = new JSONObject(); + List discordIdList = new ArrayList(); + for (Player player : playerList){ + discordIdList.add(Long.parseLong(player.getDiscordUser().id)); + } + requestObj.put("discord_ids", discordIdList); + String response = sendPostRequest("/rent/pug", requestObj); + JSONObject obj = new JSONObject(response); + + if (!obj.has("server") || !obj.getJSONObject("server").has("config")){ + // spawnDynamicServer(playerList); + LOGGER.warning("CAN'T SPAWN: " + response); + return null; + } + + LOGGER.warning(obj.toString()); + + JSONObject serverObj = obj.getJSONObject("server").getJSONObject("config"); + JSONObject serverLocationObj = obj.getJSONObject("server_location"); + JSONObject playerPingsObj = obj.getJSONObject("pings"); + + String country = serverLocationObj.getString("country"); + Region region = Region.valueOf(Country.getContinent(country)); + + Map playerPing = new HashMap(); + for (String discordId : playerPingsObj.keySet()){ + DiscordUser user = DiscordUser.getUser(discordId); + Player player = Player.get(user); + if (player != null){ + playerPing.put(player, playerPingsObj.getInt(discordId)); + } + } + + Server server = new Server(obj.getJSONObject("server").getInt("id"), null, port, serverObj.getString("rcon"), serverObj.getString("password"), true, region); + server.country = country; + server.city = serverLocationObj.getString("name"); + server.playerPing = playerPing; + return server; + } + + public static Server getSpawnedServerIp(Server server){ + String response = sendGetRequest("/rent/" + String.valueOf(server.id), true); + + if (response == null){ + return null; + } + LOGGER.warning(response); + + JSONObject obj = new JSONObject(response); + JSONObject serverObj = obj.getJSONObject("config"); + if (!serverObj.has("ip")){ + try { + Thread.sleep(1000); + return getSpawnedServerIp(server); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + server.IP = serverObj.getString("ip"); + if (!server.isOnline()){ + try { + Thread.sleep(1000); + return getSpawnedServerIp(server); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + + return server; + } + + private static synchronized String sendPostRequest(String request, JSONObject content) { + try { + byte[] postData = content.toString().getBytes( StandardCharsets.UTF_8 ); + int postDataLength = postData.length; + + URL url = new URL(api_url + request); + HttpURLConnection c = (HttpURLConnection) url.openConnection(); + c.setRequestMethod("POST"); + c.setRequestProperty("Authorization", api_key); + c.setRequestProperty("charset", "utf-8"); + c.setRequestProperty("Content-Type", "application/json"); + c.setRequestProperty("User-Agent", "Bot"); + c.setDoOutput(true); + c.setUseCaches(false); + c.setRequestProperty("Content-Length", Integer.toString(postDataLength)); + try (DataOutputStream wr = new DataOutputStream( c.getOutputStream())) { + wr.write(postData); + } + + if (c.getResponseCode() != 200 && c.getResponseCode() != 201 && c.getResponseCode() != 204 ) { + if (c.getResponseCode() == 429 || c.getResponseCode() == 502) { + try { + Thread.sleep(1000); + return sendPostRequest(request, content); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString() + " - Loadout: " + content.toString()); + } + return null; + } + + BufferedReader br = new BufferedReader(new InputStreamReader((c.getInputStream()))); + String fullmsg = ""; + String output = ""; + while ((output = br.readLine()) != null) { + try { + fullmsg += output; + } catch (ClassCastException | NullPointerException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + c.disconnect(); + + LOGGER.fine("API call complete for " + request + ": " + fullmsg); + return fullmsg; + + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + + private static synchronized String sendGetRequest(String request, boolean canBeNull) { +// Thread.dumpStack(); + try { + URL url = new URL(api_url + request); + HttpURLConnection c = (HttpURLConnection) url.openConnection(); + c.setRequestMethod("GET"); + c.setRequestProperty("User-Agent", "Bot"); + c.setRequestProperty("Authorization", api_key); + + if (c.getResponseCode() != 200 && c.getResponseCode() != 201 && c.getResponseCode() != 204) { + if (c.getResponseCode() == 429 || c.getResponseCode() == 502) { + try { + Thread.sleep(1000); + return sendGetRequest(request, canBeNull); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + if (c.getResponseCode() == 404 && canBeNull){ + return null; + } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + } + } + + BufferedReader br = new BufferedReader(new InputStreamReader((c.getInputStream()))); + String fullmsg = ""; + String output = ""; + while ((output = br.readLine()) != null) { + try { + fullmsg += output; + } catch (ClassCastException | NullPointerException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + c.disconnect(); + + LOGGER.fine("API call complete for " + request + ": " + fullmsg); + return fullmsg; + + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return null; + } + + private static synchronized int sendHeadRequest(String request) { + try { + URL url = new URL(api_url + request); + HttpURLConnection c = (HttpURLConnection) url.openConnection(); + c.setRequestMethod("HEAD"); + c.setRequestProperty("User-Agent", "Bot"); + c.setRequestProperty("Authorization", api_key); + + if (c.getResponseCode() != 200 && c.getResponseCode() != 201 && c.getResponseCode() != 204) { + if (c.getResponseCode() == 429 || c.getResponseCode() == 502) { + try { + Thread.sleep(1000); + return sendHeadRequest(request); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return -1; + } + if (c.getResponseCode() == 404){ + return c.getResponseCode(); + } + else{ + LOGGER.warning("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + Sentry.capture("API call failed: (" + c.getResponseCode() + ") " + c.getResponseMessage() + " for " + url.toString()); + } + } + + c.disconnect(); + LOGGER.fine("API call complete for " + request + ": " + String.valueOf(c.getResponseCode())); + return c.getResponseCode(); + + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return -1; + } + + public static boolean hasLauncherOn(Player p){ + String response = sendGetRequest("/connected/launcher/" + p.getDiscordUser().id, true); + return response != null; + } +} diff --git a/src/de/gost0r/pickupbot/pickup/Bet.java b/src/de/gost0r/pickupbot/pickup/Bet.java new file mode 100644 index 0000000..bf37fad --- /dev/null +++ b/src/de/gost0r/pickupbot/pickup/Bet.java @@ -0,0 +1,98 @@ +package de.gost0r.pickupbot.pickup; + +import org.json.JSONObject; + +public class Bet { + public int matchid; + public Player player; + public String color; + public long amount; + public float odds; + public boolean open; + public boolean won; + + public static PickupLogic logic; + public static JSONObject bronzeEmoji = new JSONObject().put("name", "pugcoin_bronze").put("id", "1081604558381400064"); + public static JSONObject silverEmoji = new JSONObject().put("name", "pugcoin_silver").put("id", "1081604664568578128"); + public static JSONObject goldEmoji = new JSONObject().put("name", "pugcoin_gold").put("id", "1081604760249053296"); + public static JSONObject amberEmoji = new JSONObject().put("name", "pugcoin_amber").put("id", "1081605085450219623"); + public static JSONObject rubyEmoji = new JSONObject().put("name", "pugcoin_ruby").put("id", "1081605151598583848"); + public static JSONObject pearlEmoji = new JSONObject().put("name", "pugcoin_pearl").put("id", "1081605198071480451"); + public static JSONObject amethystEmoji = new JSONObject().put("name", "pugcoin_amethyst").put("id", "1081605266535108739"); + public static JSONObject diamondEmoji = new JSONObject().put("name", "pugcoin_diamond").put("id", "1081605316262772826"); + public static JSONObject smaragdEmoji = new JSONObject().put("name", "pugcoin_smaragd").put("id", "1081605371367534672"); + public static JSONObject prismaEmoji = new JSONObject().put("name", "pugcoin_prisma").put("id", "1081605422764527768"); + + public Bet(int matchid, Player p, String color, long amount, float odds){ + this.matchid = matchid; + this.player = p; + this.color = color; + this.amount = amount; + this.odds = odds; + this.open = true; + } + + public void place(Match match){ + boolean allIn = amount == player.getCoins(); + player.spendCoins(amount); + JSONObject emoji = getCoinEmoji(amount); + String msg = Config.bets_place; + msg = msg.replace(".player.", player.getDiscordUser().getMentionString()); + msg = msg.replace(".amount.", String.format("%,d", amount)); + msg = msg.replace(".emojiname.", emoji.getString("name")); + msg = msg.replace(".emojiid.", emoji.getString("id")); + + if (allIn){ + msg +=" **ALL IN!!**"; + logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), msg); + } + logic.bot.sendMsg(match.threadChannels, msg); + } + + public void enterResult(boolean result){ + won = result; + open = false; + + if (won){ + int wonAmount = Math.round(amount * odds); + player.addCoins(wonAmount); + } + player.saveWallet(); + logic.db.createBet(this); + } + + public void refund(Match match){ + player.addCoins(amount); + open = false; + String msg = Config.bets_refund; + JSONObject emoji = getCoinEmoji(amount); + msg = msg.replace(".player.", player.getDiscordUser().getMentionString()); + msg = msg.replace(".amount.", String.format("%,d", amount)); + msg = msg.replace(".emojiname.", emoji.getString("name")); + msg = msg.replace(".emojiid.", emoji.getString("id")); + logic.bot.sendMsg(match.threadChannels, msg); + } + + public static JSONObject getCoinEmoji(long amount){ + if (amount < 500){ + return bronzeEmoji; + } else if (amount < 1000){ + return silverEmoji; + } else if (amount < 10000){ + return goldEmoji; + } else if (amount < 25000){ + return amberEmoji; + } else if (amount < 50000){ + return rubyEmoji; + } else if (amount < 100000){ + return pearlEmoji; + } else if (amount < 250000){ + return amethystEmoji; + } else if (amount < 500000){ + return diamondEmoji; + } else if (amount < 1000000){ + return smaragdEmoji; + } + return prismaEmoji; + } +} diff --git a/src/de/gost0r/pickupbot/pickup/Config.java b/src/de/gost0r/pickupbot/pickup/Config.java index 303f983..2aff0db 100644 --- a/src/de/gost0r/pickupbot/pickup/Config.java +++ b/src/de/gost0r/pickupbot/pickup/Config.java @@ -2,6 +2,14 @@ public class Config { public static final String CMD_ADD = "!add"; + public static final String CMD_BM = "!bm"; + public static final String CMD_CTF = "!ctf"; + public static final String CMD_TS = "!ts"; + public static final String CMD_1v1 = "!1v1"; + public static final String CMD_2v2 = "!2v2"; + public static final String CMD_DIV1 = "!div1"; + public static final String CMD_SKEET = "!skeet"; + public static final String CMD_AIM = "!aim"; public static final String CMD_REMOVE = "!remove"; public static final String CMD_MAPS = "!maps"; public static final String CMD_MAP = "!map"; @@ -9,7 +17,19 @@ public class Config { public static final String CMD_HELP = "!help"; public static final String CMD_SURRENDER = "!surrender"; public static final String CMD_LIVE = "!live"; - + public static final String CMD_VOTES = "!votes"; + public static final String CMD_TEAM = "!team"; + public static final String CMD_LEAVETEAM = "!leaveteam"; + public static final String CMD_SCRIM = "!scrim"; + public static final String CMD_REMOVETEAM = "!removeteam"; + public static final String CMD_TEAMS = "!teams"; + public static final String CMD_PING = "!ping"; + public static final String CMD_ADDVOTE = "!addvote"; + public static final String CMD_BANMAP = "!banmap"; + public static final String CMD_WALLET = "!wallet"; + public static final String CMD_DONATE = "!donate"; + public static final String CMD_BETHISTORY = "!bethistory"; + public static final String CMD_LOCK = "!lock"; public static final String CMD_UNLOCK = "!unlock"; public static final String CMD_RESET = "!reset"; @@ -17,26 +37,39 @@ public class Config { public static final String CMD_ENABLEMAP = "!enablemap"; public static final String CMD_DISABLEMAP = "!disablemap"; public static final String CMD_RCON = "!rcon"; + public static final String CMD_FORCEADD = "!forceadd"; + public static final String CMD_REBOOT = "!reboot"; public static final String CMD_ENABLEGAMETYPE = "!enablegametype"; public static final String CMD_DISABLEGAMETYPE = "!disablegametype"; - public static final String CMD_ADDGAMECONFIG = "!addgameconfig"; - public static final String CMD_REMOVEGAMECONFIG = "!delgameconfig"; + public static final String CMD_ENABLEDYNSERVER = "!enabledynservers"; + public static final String CMD_DISABLEDYNSERVER = "!disabledynservers"; + + //public static final String CMD_ADDGAMECONFIG = "!addgameconfig"; + //public static final String CMD_REMOVEGAMECONFIG = "!delgameconfig"; public static final String CMD_LISTGAMECONFIG = "!showgameconfig"; - + public static final String CMD_REGISTER = "!register"; + public static final String CMD_COUNTRY = "!country"; public static final String CMD_GETELO = "!elo"; - public static final String CMD_TOP5 = "!top5"; + public static final String CMD_GETSTATS = "!stats"; + public static final String CMD_TOP_PLAYERS = "!top10"; + public static final String CMD_TOP_COUNTRIES = "!topcountries"; + public static final String CMD_TOP_WDL = "!topwin"; + public static final String CMD_TOP_KDR = "!topkdr"; + public static final String CMD_TOP = "!top"; + public static final String CMD_TOP_RICH = "!toprich"; + public static final String CMD_MATCH = "!match"; - + public static final String CMD_LAST = "!last"; + //public static final String CMD_REPORT = "!report"; //public static final String CMD_EXCUSE = "!excuse"; //public static final String CMD_REPORTLIST = "!reportlist"; - + public static final String CMD_ADDBAN = "!ban"; - //public static final String CMD_REMOVEBAN = "!unban"; - + public static final String CMD_REMOVEBAN = "!unban"; public static final String CMD_BANINFO = "!baninfo"; public static final String CMD_SHOWSERVERS = "!showservers"; @@ -46,154 +79,248 @@ public class Config { public static final String CMD_UPDATESERVER = "!updateserver"; public static final String CMD_SHOWMATCHES = "!showmatches"; - + public static final String CMD_UNREGISTER = "!unregister"; + public static final String CMD_ENFORCEAC = "!enforceac"; public static final String CMD_ADDCHANNEL = "!addchannel"; public static final String CMD_REMOVECHANNEL = "!removechannel"; - public static final String CMD_ADDROLE = "!addrole"; public static final String CMD_REMOVEROLE = "!removerole"; - public static final String PUB_LIST = "" + CMD_ADD + " " + CMD_REMOVE + " " + CMD_MAPS + " " + CMD_LIVE + " " + CMD_MATCH + " " - + CMD_MAP + " " + CMD_STATUS + " " + CMD_HELP + " " + CMD_REGISTER + " " + CMD_GETELO + " " + CMD_TOP5 + " " + CMD_SURRENDER - + " " + CMD_BANINFO; + public static final String CMD_RESETELO = "!resetelo"; + + public static final String PUB_LIST = "" + CMD_ADD + " " + CMD_REMOVE + " " + CMD_MAPS + " " + CMD_MAP + " " + CMD_MATCH + " " + + CMD_LAST + " " + CMD_LIVE + " " + CMD_STATUS + " " + CMD_HELP + " " + CMD_REGISTER + " " + CMD_GETELO + " " + CMD_TOP_PLAYERS + + " " + CMD_TOP_COUNTRIES + " " + CMD_TOP_KDR + " " + CMD_TOP_WDL + " " + CMD_COUNTRY + " " + CMD_SURRENDER + " " + CMD_BANINFO + + " " + CMD_VOTES + " " + CMD_LAST + " " + CMD_TEAM + " " + CMD_LEAVETEAM + " " + CMD_SCRIM + " " + CMD_REMOVETEAM + " " + CMD_TEAMS; - public static final String ADMIN_LIST = "" + CMD_LOCK + " " + CMD_UNLOCK + " " + CMD_RESET + " " + CMD_GETDATA + " " - + CMD_ENABLEMAP + " " + CMD_DISABLEMAP + " " + CMD_RCON + " " + CMD_SHOWSERVERS + " " + CMD_ENABLEGAMETYPE + " " + CMD_DISABLEGAMETYPE - + " " + CMD_ADDSERVER + " " + CMD_ENABLESERVER + " " + CMD_DISABLESERVER + " " + CMD_UPDATESERVER + " " + CMD_ADDBAN + " " + CMD_SHOWMATCHES - + " " + CMD_UNREGISTER + " " + CMD_ADDGAMECONFIG + " " + CMD_REMOVEGAMECONFIG + " " + CMD_LISTGAMECONFIG; - + public static final String ADMIN_LIST = "" + CMD_LOCK + " " + CMD_UNLOCK + " " + CMD_RESET + " " + CMD_GETDATA + " " + CMD_ENABLEMAP + + " " + CMD_DISABLEMAP + " " + CMD_RCON + " " + CMD_SHOWSERVERS + " " + CMD_ENABLEGAMETYPE + " " + CMD_DISABLEGAMETYPE + " " + + CMD_ADDSERVER + " " + CMD_ENABLESERVER + " " + CMD_DISABLESERVER + " " + CMD_UPDATESERVER + " " + CMD_ADDBAN + " " + CMD_REMOVEBAN + + " " + CMD_SHOWMATCHES + " " + CMD_UNREGISTER + " " + CMD_ADDROLE + " " + CMD_REMOVEROLE + " " + CMD_ADDCHANNEL + " " + CMD_REMOVECHANNEL + + " " + CMD_FORCEADD + " " + CMD_REBOOT; + //------------------------------------------------------------------------------------// - - public static final String USE_CMD_ADD = "!add "; + + public static final String USE_CMD_ADD = "!"; public static final String USE_CMD_REMOVE = "!remove "; - public static final String USE_CMD_MAPS = "!maps"; + public static final String USE_CMD_BM = "!bm "; + public static final String USE_CMD_CTF = "!ctf "; + public static final String USE_CMD_TS = "!ts "; + public static final String USE_CMD_1v1 = "!1v1 "; + public static final String USE_CMD_2v2 = "!2v2 "; + public static final String USE_CMD_DIV1 = "!div1 "; + public static final String USE_CMD_MAPS = "!maps displays the map list for each gametype."; public static final String USE_CMD_MAP = "!map "; - public static final String USE_CMD_STATUS = "!status"; + public static final String USE_CMD_ADDVOTE = "!addvote "; + public static final String USE_CMD_BANMAP = "!banmap "; + public static final String USE_CMD_STATUS = "Type !status to get information on the queues."; public static final String USE_CMD_HELP = "!help "; - public static final String USE_CMD_SURRENDER = "!surrender"; - public static final String USE_CMD_LIVE = "!live"; - - public static final String USE_CMD_LOCK = "!lock"; + public static final String USE_CMD_SURRENDER = "Type !surrender to abandon your match."; + public static final String USE_CMD_LIVE = "!live sends info on the live matches."; + public static final String USE_CMD_VOTES = "Type !votes to get the current votes."; + + public static final String USE_CMD_LOCK = "!lock to prevent joining the queues."; public static final String USE_CMD_UNLOCK = "!unlock"; - public static final String USE_CMD_RESET = "!reset "; - public static final String USE_CMD_GETDATA = "!getdata "; + public static final String USE_CMD_RESET = "!reset "; + public static final String USE_CMD_GETDATA = "!getdata"; public static final String USE_CMD_ENABLEMAP = "!enablemap "; public static final String USE_CMD_DISABLEMAP = "!disablemap "; public static final String USE_CMD_RCON = "!rcon "; + public static final String USE_CMD_FORCEADD = "!forceadd "; + public static final String USE_CMD_REBOOT = "Use !reboot to restart the BOT. This will reset queues and current matches."; public static final String USE_CMD_ENABLEGAMETYPE = "!enablegametype "; public static final String USE_CMD_DISABLEGAMETYPE = "!disablegametype "; - - public static final String USE_CMD_ADDGAMECONFIG = "!addgameconfig "; - public static final String USE_CMD_REMOVEGAMECONFIG = "!delgameconfig "; + + //public static final String USE_CMD_ADDGAMECONFIG = "!addgameconfig "; + //public static final String USE_CMD_REMOVEGAMECONFIG = "!delgameconfig "; public static final String USE_CMD_LISTGAMECONFIG = "!showgameconfig "; - + public static final String USE_CMD_REGISTER = "!register "; - public static final String USE_CMD_GETELO = "!elo "; - public static final String USE_CMD_TOP5 = "!top5"; + public static final String USE_CMD_COUNTRY = "!country See:` "; + public static final String USE_CMD_CHANGE_COUNTRY = "!country See:` "; + public static final String USE_CMD_GETELO = "!elo "; + public static final String USE_CMD_GETSTATS = "!stats "; + public static final String USE_CMD_TOP10 = "!top10 displays the top 10 players"; + public static final String USE_CMD_TOP_COUNTRIES = "!topcountries ordered by average ELO"; + public static final String USE_CMD_TOP_WDL = "!topwin : players with the best win ratio for a specific gamemode"; + public static final String USE_CMD_TOP_KDR = "!topkdr : players with the best KDR for a specific gamemode"; + public static final String USE_CMD_MATCH = "!match "; - + public static final String USE_CMD_LAST = "!last "; + //public static final String USE_CMD_REPORT = "!report "; //public static final String USE_CMD_EXCUSE = "!excuse "; //public static final String USE_CMD_REPORTLIST = "!reportlist"; - + public static final String USE_CMD_ADDBAN = "!ban (duration=1y1M1w1d1h1m1s)"; - //public static final String USE_CMD_REMOVEBAN = "!unban "; - - public static final String USE_CMD_BANINFO = "!baninfo "; + public static final String USE_CMD_REMOVEBAN = "!unban "; + public static final String USE_CMD_BANINFO = "!baninfo "; public static final String USE_CMD_SHOWSERVERS = "!showservers"; - public static final String USE_CMD_ADDSERVER = "!addserver "; - + public static final String USE_CMD_ADDSERVER = "!addserver "; public static final String USE_CMD_ENABLESERVER = "!enableserver "; public static final String USE_CMD_DISABLESERVER = "!disableserver "; public static final String USE_CMD_UPDATESERVER = "!updateserver "; - - public static final String USE_CMD_SHOWMATCHES = "!showmatches"; - + + public static final String USE_CMD_SHOWMATCHES = "!showmatches displays the queues AND live matches"; + public static final String USE_CMD_UNREGISTER = "!unregister "; + public static final String USE_CMD_ENFORCEAC = "!enforceac "; + + public static final String USE_CMD_ADDCHANNEL = "!addchannel <#name> "; + public static final String USE_CMD_REMOVECHANNEL = "!removechannel <#name> "; + public static final String USE_CMD_ADDROLE = "!addrole <@role> "; + public static final String USE_CMD_REMOVEROLE = "!removerole <@role> "; + + public static final String USE_CMD_SCRIM = "!scrim "; + public static final String USE_CMD_REMOVETEAM = "!removeteam "; + public static final String USE_CMD_TEAM = "!team <@user1> <@user2> <...>"; + public static final String USE_CMD_LEAVETEAM = "!leaveteam removes you from your current team"; + public static final String USE_CMD_TEAMS = "!teams lists the active teams"; + + public static final String USE_CMD_DONATE = "!donate "; + + //------------------------------------------------------------------------------------// + public static final String INT_PICK = "pick"; + public static final String INT_LAUNCHAC = "launchac"; + public static final String INT_TEAMINVITE = "teaminvite"; + public static final String INT_TEAMREMOVE = "teamremove"; + public static final String INT_SEASONSTATS = "seasonstats"; + public static final String INT_SEASONLIST = "seasonlist"; + public static final String INT_SEASONSELECTED = "seasonselected"; + public static final String INT_LASTMATCHPLAYER = "lastgameplayer"; + public static final String INT_SHOWBET = "showbet"; + public static final String INT_BET = "bet"; + public static final String INT_BUY = "buy"; + public static final String INT_BUY_BOOST = "eloboost"; + public static final String INT_BUY_SHOWVOTEOPTIONS = "showvoteoptions"; + public static final String INT_BUY_ADDVOTES = "additionalvote"; + public static final String INT_BUY_MAPBAN = "banmap"; + //------------------------------------------------------------------------------------// - public static final String pkup_lock = "This game is currently locked"; - public static final String pkup_map = "You voted for .map.."; + public static final String APP_BET = "bet"; + public static final String APP_BUY = "buy"; + + //------------------------------------------------------------------------------------// + + public static final String BTN_LAUNCHAC = "Connect to server"; + + //------------------------------------------------------------------------------------// + public static final String pkup_lock = "Pickup games are now locked. :lock:"; + public static final String pkup_launcheroff = "Please turn on your FTW launcher before adding. See <#1067227082372952084> for more details."; + public static final String pkup_map = "You voted for .map. (``.count.``)."; public static final String pkup_map_list = "**.gametype.**: .maplist."; // public static final String pkup_signup = "You can sign up again!"; public static final String pkup_pw = "[ /connect .server. ; password .password. ]"; - public static final String pkup_status_noone = "**.gametype.**: Nobody has signed up. Type `" + USE_CMD_ADD + "` to play."; + public static final String pkup_status_noone = "**.gametype.**: Nobody signed up. Type `" + USE_CMD_ADD + "` to play."; public static final String pkup_status_signup = "**.gametype.**: Sign up: [.playernumber./.maxplayer.] .playerlist."; - public static final String pkup_status_server = "**.gametype.**: Awaiting available server."; + public static final String pkup_status_server = "**.gametype.**: The match is about to start, while captains are picking players, you can continue to vote for a map with ``!map ``. \nCurrent votes: .votes."; // public static final String pkup_status_players = "**.gametype.**: Players [.playernumber./10]: .playerlist."; -// public static final String pkup_started = "**.gametype.**: Game has already started. .status. - .time. minutes in."; +// public static final String pkup_started = "**.gametype.**: Match has already started. .status. - .time. minutes in."; - public static final String pkup_reset_all = "*All matches have been reset.*"; - public static final String pkup_reset_cur = "*The current matches have been reset.*"; - public static final String pkup_reset_type = "*.gametype. has been reset.*"; - public static final String pkup_reset_id = "*The match .id. has been reset.*"; - - public static final String pkup_match_print_live = "**[** Pickup Game #.gamenumber. **][** Live **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; - public static final String pkup_match_print_signup = "**[** Pickup Game #--- **][** Signup **][** Gametype: .gametype. **][** .playernumber./.maxplayer. **][** Players: .playerlist. **]**"; - public static final String pkup_match_print_server = "**[** Pickup Game #--- **][** AwaitingServer **][** Gametype: .gametype. **][** Players: .playerlist. **]**"; - public static final String pkup_match_print_done = "**[** Pickup Game #.gamenumber. **][** Done **][** Score: .score. **][** Gametype: .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; - public static final String pkup_match_print_abort = "**[** Pickup Game #.gamenumber. **][** Abort **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; - public static final String pkup_match_print_abandon = "**[** Pickup Game #.gamenumber. **][** Abandon **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; - public static final String pkup_match_print_sur = "**[** Pickup Game #.gamenumber. **][** Surrender **][** Score: .score. **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; - - public static final String pkup_match_print_info = "**.gametype.**: #.gamenumber. **][** .map. **][** .ingame. **][** `.redteam.` **][** `.blueteam.`"; - - public static final String pkup_go_admin = "[ Pickup Game #.gamenumber. ][ Password: .password. ][ .map. ][ ELO red: .elored. ELO blue: .eloblue. ]"; - public static final String pkup_go_player = "UrTPickup starts now! Connect to the server, choose positions and ready up! [ /connect .server. ; password .password. ]"; - public static final String pkup_go_pub_head = "**.gametype.**: UrTPickup #.gamenumber. (avg ELO: .elo.) is about to start!"; - public static final String pkup_go_pub_team = "**.gametype.**: .team. team: .playerlist."; - public static final String pkup_go_pub_map = "**.gametype.**: Map: .map."; - public static final String pkup_go_pub_calm = "**.gametype.**: You will receive the connection info via DM."; - public static final String pkup_go_pub_sent = "**.gametype.**: All connection info has been sent. Enjoy the match!"; + public static final String pkup_reset_all = "*All matches and queues have been reset.*"; + public static final String pkup_reset_cur = "*All queues have been reset.*"; + public static final String pkup_reset_type = "*.gametype. queue has been reset.*"; + public static final String pkup_reset_id = "*Match #.id. has been reset.*"; + + public static final String pkup_match_print_live = "**[** Match #.gamenumber. **][** Live **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; + public static final String pkup_match_print_signup = "**[** Match #--- **][** Signup **][** Gametype: .gametype. **][** .playernumber./.maxplayer. **][** Players: .playerlist. **]**"; + public static final String pkup_match_print_server = "**[** Match #--- **][** AwaitingServer **][** Gametype: .gametype. **][** Players: .playerlist. **]**"; + public static final String pkup_match_print_done = "**[** Match #.gamenumber. **][** Done **][** Score: .score. **][** Gametype: .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; + public static final String pkup_match_print_abort = "**[** Match #.gamenumber. **][** Abort **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; + public static final String pkup_match_print_abandon = "**[** Match #.gamenumber. **][** Abandon **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; + public static final String pkup_match_print_sur = "**[** Match #.gamenumber. **][** Surrender **][** Score: .score. **][** .gametype. **][** .map. **][** ELO red: .elored. ELO blue: .eloblue. **][** Players: .playerlist. **]**"; + + public static final String pkup_match_print_info = "**.gametype. #.gamenumber.**: **[**.map.**] [**.ingame.**] [**.redteam.**]** VS **[**.blueteam.**]**"; + + public static final String pkup_go_player = "**Pickup starts now! Connect, choose positions and ready up!**\n ```/connect .server. ; password .password. ```\n :bulb: The **AC** will soon be **mandatory** for all players, forcing you to press the **Connect to server** button to join games.\n\n This enhancement is made to ensure matches remain fair and fun. Thank you for your continued involvement and activity in Urban Terror. Happy fragging!\n\n Check out: "; + public static final String pkup_go_player_ac = "Pickup starts now! Click the button below to join the server, choose positions and ready up!."; + public static final String pkup_go_captains = "Pickup is about to start and you are **captain**! Please pick players in the recently created discord thread."; + public static final String pkup_go_pub_head = "**.gametype.: Match #.gamenumber.** .region. (avg ELO: .elo.)"; + public static final String pkup_go_pub_team = "**.team.** .playerlist."; + public static final String pkup_go_pub_map = "Map: .map."; + public static final String pkup_go_pub_calm = "**GTV**: connect gtv.b00bs-clan.com:709; password SevenAndJehar"; // temporarily hard coded + public static final String pkup_go_pub_calm_notavi = "GTV: not available"; + public static final String pkup_go_pub_sent = "**.gametype.**: Server info has been sent. If you didn't get a DM try **!lostpass**."; + public static final String pkup_go_pub_threadtitle = "Match .ID."; + public static final String pkup_go_pub_captains = "The captains are .captain1. (**red**) and .captain2. (**blue**). Player stats:"; + public static final String pkup_go_pub_pick = ".captain. pick a player:"; + public static final String pkup_go_pub_pickjoin = ".pick. joins the **.color.** team."; + public static final String pkup_go_pub_servspawn = "**Spawning Server:** .flag. ``.city.`` (fairest server to all players)\n:hourglass: The ip will be send to all players when the server is spawned. ``Estimated wait time: 2min``"; + public static final String pkup_go_pub_requestserver= "**Requesting Server:** .flag. ``.region.``"; + public static final String pkup_go_pub_noserv = "The server rental system is temporarilly unavailable, please try again later."; public static final String pkup_aftermath_head = "**.gametype.**: Aftermath #.gamenumber. (.map.):"; - public static final String pkup_aftermath_result = ".team. team .result. (.score.) -"; + public static final String pkup_aftermath_result = ".team. .result. (.score.) -"; public static final String pkup_aftermath_player = ".player. (.elochange.)"; public static final String pkup_aftermath_rank = ".player. was ranked .updown. to **.rank.**"; public static final String pkup_aftermath_abandon_1 = "Match was abandoned due to **.reason.**."; public static final String pkup_aftermath_abandon_2 = ".players. .be. punished accordingly."; - public static final String pkup_config_list = "Gameconfig for .gametype.\n.configlist."; - - public static final String pkup_getelo = "#.position.\t **.rank.**\t **.urtauth.**\t .elo.\t .wdl.%"; - public static final String pkup_top5_header = "**Top5:**"; - - public static final String pkup_surrender_cast = "You have voted to surrender. **.num.** more teammate.s. needed."; - public static final String pkup_surrender_time = "You cannot surrender this early. You still have to wait for .time.."; - - public static final String is_banned = ".user. (.urtauth.) is suspended for .reason. and cannot participate for .time."; - public static final String not_banned = "No active bans found for .user. (.urtauth.)"; + public static final String pkup_config_list = "Gameconfig for .gametype.\n.configlist."; + + public static final String pkup_getelo = "#.position.\t **.rank.**\t .country. **.urtauth.**\t .elo.\t .wdl.%\t .kdr."; + public static final String pkup_getelo_country = "#.position.\t .country.\t .elo."; + public static final String pkup_top5_header = "**Top countries:**"; + + public static final String pkup_surrender_cast = "You voted to surrender. **.num.** more teammate.s. needed."; + public static final String pkup_surrender_time = "You cannot surrender this early. Please wait .time.."; + + public static final String is_banned = ".user. (.urtauth.) is suspended .time. for .reason."; + public static final String is_unbanned = ".user. (.urtauth.) is unbanned."; + public static final String is_notbanned = ".urtauth. is not banned (yet)."; + public static final String not_banned = "No active bans found for .urtauth."; + public static final String ban_history = "**__Ban history:__** (Past 2 months)"; + public static final String ban_history_item = " .duration. .reason."; public static final String map_not_found = "Map not found."; public static final String map_not_unique = "Mapstring not unique."; public static final String map_cannot_vote = "You cannot vote right now."; - public static final String map_specify_gametype = "You need to specify a gametype."; + public static final String map_specify_gametype = "Please use: **!map **."; + public static final String map_played_last_game = "This map was played last game, please vote for a different map."; + public static final String map_already_banned = "This map is already banned (Expires )."; + public static final String map_banned = "This map is currently banned (Expires )."; + public static final String no_additonal_vote = "You currently don't have any additional vote to spend. Buy some by sending ``/buy``."; + public static final String used_additonal_vote = ".player. used their bonus and added ``.vote.`` votes to .map. (``.count.``)"; + public static final String no_map_ban = "You currently don't have any map bans to spend. Buy some by sending ``/buy``."; + public static final String used_map_ban = ".player. used their bonus and banned the map .map. until "; public static final String player_not_found = "Player not found."; public static final String user_not_registered = "You're not registered. Please use `" + USE_CMD_REGISTER + "`"; + public static final String other_user_not_registered= "The user .user. is not registered."; + public static final String country_added = "Your country has been set."; - public static final String auth_taken_urtauth = "This urtauth is already registered."; + public static final String auth_taken_urtauth = "This **urtauth** is already registered."; public static final String auth_taken_user = "You have already registered an account."; - public static final String auth_invalid = "Your urtauth seems to be invalid."; - public static final String auth_success = "Your auth has been successfully linked to your account."; + public static final String auth_invalid = "Your **urtauth** seems to be invalid."; + public static final String auth_success = "Your **urtauth** has been linked to your account."; + public static final String auth_success_admin = "The user .user. registered using the auth ``.urtauth.``."; public static final String auth_sent_key = "You have to register your auth name and not your auth key!!!"; + public static final String ac_enforced = ":warning: **From now on the anticheat (AC) will be mandatory for you to play pickup games.**\n\n If the AC is new to you, check out the channel below for information on how to set it up: https://discord.com/channels/117622053061787657/1067227082372952084/1067227082372952084 If you run into any kind of issue, don't hesitate to ask for support in **#help** or by DM to ``@solitary#5004``\n\nYou will now need to press the green **Connect to server** button in the **#pickup** channel to join."; + public static final String player_already_added = "You are already added to a pickup game."; public static final String player_already_removed = "You are not added to any pickup game."; public static final String player_cannot_add = "You cannot add right now."; public static final String player_cannot_remove = "You cannot remove."; - public static final String player_not_in_match = "You are currently not in a match."; + public static final String player_not_in_match = "You are not added to any queue."; public static final String player_already_match = "You are already in a match."; + public static final String player_not_admin = "You must be an admin to use this command."; + public static final String player_notdiv1 = "Hi <:puma:849287183474884628>, you need to be in either the Top ``.minrank.`` **ELO** (#``.rank.``) *OR* Top ``.minkdrrank.`` **KDR** (#``.kdrrank.``) *OR* Top ``.minwinrank.`` **Win Rate** (#``.winrank.``) to add to the **div1** queue. Keep practicing on Sexy CTF."; - public static final String player_already_surrender = "You have already surrendered."; - - public static final String afk_reminder = "[*AFK CHECK*] .user. will be removed in 5 minutes."; + public static final String player_not_captain = "You can't pick a player. You are not captain or it is not your turn to pick."; + + public static final String player_already_surrender = "You already surrendered."; + + public static final String afk_reminder = "**[AFK]** .user. will be removed in 3 minutes. Write something in the channel to stay in queue."; + public static final String pick_reminder = "**[CAPTAIN PICK]** .user. you have 1 min to pick a player in the game thread channel."; + public static final String pick_reset = "**[CAPTAIN PICK]** The match **.matchid.** has been reset. .user. did not pick a player in time and was punished accordingly."; // public static final String report_wrong_arg = "Your report reason is invalid, check !reportlist to check the possible reasons."; // public static final String report_invalid_urtauth = "No player could be found with this urtauth."; @@ -205,20 +332,88 @@ public class Config { public static final String wrong_argument_amount = "Wrong amount of arguments: `.cmd.`"; public static final String help_prefix = "How to use the command: `.cmd.`"; - public static final String help_cmd_avi = "These commands are available (use `" + USE_CMD_HELP + "` for more info):\n`.cmds.`"; - - public static final String help_unknown = "I do not know that command."; + public static final String help_cmd_avi = "Here is the command list (use `" + USE_CMD_HELP + "` for more info):\n`.cmds.`"; + + public static final String help_unknown = "I do not know that command :("; - public static final String lock_enable = "*Game is LOCKED.*"; + public static final String lock_enable = "*Game is LOCKED.* :lock:"; public static final String lock_disable = "*Game is now unlocked.*"; - public static final String pkup_match_unavi = "Match is not available right now."; + public static final String pkup_match_unavi = "Match is not available right now."; public static final String pkup_match_invalid_gt = "No match for that gametype is available right now."; - public static final String no_gt_found = "Unable to find a matching gametype."; - - public static final String banreason_not_found = "Banreason not found in .banreasons."; - public static final String banduration_invalid = "Ban duration invalid."; + public static final String no_gt_found = "Unable to find a matching gametype. Try `!add ctf`"; + public static final String no_gt_team_found = "Unable to find a matching gametype. Try `!scrim ctf`"; + + public static final String banreason_not_found = "Use one of the following ban reasons: .banreasons."; + public static final String banduration_invalid = "Invalid ban duration. Try 1m,1h,1d,1w,1M."; + + public static final String admin_cmd_successful = ":white_check_mark: Successful: "; + public static final String admin_cmd_unsuccessful = ":x: Unsuccessful: "; + public static final String wait_testing_server = "Testing server list. This can take a while..."; + public static final String admin_enforce_ac_on = ":white_check_mark: The anticheat is now enforced for the player ``.urtauth.``"; + public static final String admin_enforce_ac_off = ":negative_squared_cross_mark: The anticheat is **no longer** enforced for the player ``.urtauth.``"; - public static final String admin_cmd_successful = "Successful: "; - public static final String admin_cmd_unsuccessful = "Unsuccessful: "; + public static final String bot_online = "The bot is back online!"; + + public static final String elo_reset = "The elo has been reset! Don't forget to reboot the bot and remove all rank roles manually."; + + public static final String ftw_playernotinmatch = "Player not in match"; + public static final String ftw_matchnotfound = "Match not found"; + public static final String ftw_success = "Game launched"; + public static final String ftw_notconnected = "User not connected to the FTW launcher"; + public static final String ftw_error = "Unknown error"; + public static final String ftw_error_noping = "Check your dms and click on the link to register your ping in the different server locations."; + public static final String ftw_dm_noping = "Please click here to register your ping in the different server locations: .url."; + + public static final String team_involved_other = "You are already involved in an active team. Leave it by sending ``!leaveteam``."; + public static final String team_already_involved = "``.auth.`` is already in a team."; + public static final String team_is_full = "Your team already has 5 players."; + public static final String team_already_in = "The player ``.auth.`` is already in your team."; + public static final String team_already_invited = "The player ``.auth.`` has already been invited to your team."; + public static final String team_invited = ".invited. You are invited to join .captain.'s team."; + public static final String team_accepted = ".player. is now a member of this team."; + public static final String team_declined = ".player. declined the invitation to join this team."; + public static final String team_canceled = ".player.'s invitation to join your team has been canceled."; + public static final String team_removed = ".player. has been removed from this team."; + public static final String team_leave_captain = "Your team has been dissolved."; + public static final String team_leave = "You left ``.captain.``'s team."; + public static final String team_noteam = "You are currently not involved in any team."; + public static final String team_noteam_captain = "You are not captain of any team."; + public static final String team_error_invite = "Only the player ``.player.`` can answer."; + public static final String team_error_active = "This team is no longer active."; + public static final String team_error_remove = "Only the team captain or the player in question can press this button."; + public static final String team_error_teamsize = "You need to be exactly ``.teamsize.`` in your team to queue for this gamemode."; + public static final String team_error_wrong_gt = "You can't add as a team for that gamemode."; + public static final String team_added = "**.gamemode.**: Team sign up: .team."; + public static final String team_removed_queue = "Team removed from all queues: .team."; + public static final String team_cant_soloqueue = "You can't queue by yourself as you are currently in a team. ``!leaveteam`` to solo queue."; + public static final String team_print_info = "Your current team: .team."; + public static final String team_print_all = "__List of all active teams:__"; + public static final String team_print_noteam = "No team is currently active."; + public static final String team_only_mentions = "Please specify players using: ``!team @user``."; + public static final String team_no_scrim = "**SCRIM**: No team signed up. Type ``!scrim `` to play."; + + public static final String bets_notaccepting = "This match is not accepting bets anymore."; + public static final String bets_howmuch = "How much would you like to bet for team ``.team.``? \nTo bet a custom amount, use ``/bet .matchid. .team. ``\n*Current balance:* <:.emojiname.:.emojiid.> ``.balance.``"; + public static final String bets_insufficient = "Insufficient funds."; + public static final String bets_won = ".player. won their bet: <:.emojiname.:.emojiid.> ``+.amount.``"; + public static final String bets_refund = ".player.'s bet was canceled and was refunded <:.emojiname.:.emojiid.> ``.amount.``"; + public static final String bets_otherteam = "You can't bet against your team, have some faith!"; + public static final String bets_nomoney = "You can't bet, you are broke :'("; + public static final String bets_above_limit = "You must **ALL IN** if you want to place a bet higher than 1M coins."; + public static final String bets_place = ".player. bets <:.emojiname.:.emojiid.> ``.amount.`` on this game."; + + public static final String buy_show = "What perk would you like to purchase? \n*Current balance:* <:.emojiname.:.emojiid.> ``.balance.``"; + public static final String buy_boostactive = "You already have an active elo boost. (Expires )"; + public static final String buy_boostactivated = ".player. purchased an elo boost for <:.emojiname.:.emojiid.> ``.price.`` (Expires )"; + public static final String buy_showvoteoptions = "How many map votes would you like to purchase?"; + public static final String buy_voteoptionsalready = "You already have a set of additional votes (currently ``.vote.`` votes). Use them to purchase another set."; + public static final String buy_addvotesactivated = ".player. purchased ``.vote.`` additional votes for <:.emojiname.:.emojiid.> ``.price.``"; + public static final String buy_mapbanactivated = ".player. purchased a map ban for <:.emojiname.:.emojiid.> ``.price.``"; + public static final String buy_addvote_purchased = "You can spend your additional votes by calling ``!addvote ``"; + public static final String buy_banmap_purchased = "You can now ban a map for 2h by calling ``!banmap ``"; + public static final String buy_show_wallet = "Current balance of **.player.**: <:.emojiname.:.emojiid.> ``.balance.``"; + public static final String donate_incorrect_amount = "Incorrect amount of coin to donate."; + public static final String donate_processed = ".player. donated <:.emojiname.:.emojiid.> ``.amount.`` to .otherplayer."; + public static final String donate_above_limit = "You can't donate more than ``10,000`` coins."; } diff --git a/src/de/gost0r/pickupbot/pickup/Country.java b/src/de/gost0r/pickupbot/pickup/Country.java new file mode 100644 index 0000000..c620cf3 --- /dev/null +++ b/src/de/gost0r/pickupbot/pickup/Country.java @@ -0,0 +1,55 @@ +package de.gost0r.pickupbot.pickup; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Paths; +import java.util.HashMap; + +import org.apache.commons.io.IOUtils; +import org.json.JSONArray; + +public class Country { + + private static HashMap CountryToContinentMap = new HashMap(); + + public static void initCountryCodes() throws IOException { + String fileName = Paths.get("country-and-continent-codes-list.json").toString(); + InputStream is = new FileInputStream(fileName); + String jsonTxt = IOUtils.toString(is, "UTF-8"); + + JSONArray arr = new JSONArray(jsonTxt); + + for(int i = 0; i < arr.length(); i++) { + String continent = arr.getJSONObject(i).get("Continent_Code").toString(); + String country = arr.getJSONObject(i).get("Two_Letter_Country_Code").toString(); + + CountryToContinentMap.put(country, continent); + } + } + + public static String getContinent(String country) { + return CountryToContinentMap.get(country); + } + + public static Boolean isValid(String country) { + Object o = CountryToContinentMap.get(country); + + return o != null; + } + + public static String getCountryFlag(String country) { + String msg; + + if( country.equalsIgnoreCase("NOT_DEFINED")) { + msg = ""; + } + else { + msg = ":flag_" + country.toLowerCase() + ":"; + } + + return msg; + } + +} diff --git a/src/de/gost0r/pickupbot/pickup/CountryRank.java b/src/de/gost0r/pickupbot/pickup/CountryRank.java new file mode 100644 index 0000000..8fc7652 --- /dev/null +++ b/src/de/gost0r/pickupbot/pickup/CountryRank.java @@ -0,0 +1,12 @@ +package de.gost0r.pickupbot.pickup; + +public class CountryRank { + public String country; + public Float elo; + + public CountryRank(String country, Float elo) { + this.country = country; + this.elo = elo; + } + +} diff --git a/src/de/gost0r/pickupbot/pickup/Database.java b/src/de/gost0r/pickupbot/pickup/Database.java index f35b983..7e0a3a5 100644 --- a/src/de/gost0r/pickupbot/pickup/Database.java +++ b/src/de/gost0r/pickupbot/pickup/Database.java @@ -8,6 +8,7 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -19,24 +20,29 @@ import de.gost0r.pickupbot.pickup.PlayerBan.BanReason; import de.gost0r.pickupbot.pickup.server.Server; import de.gost0r.pickupbot.pickup.stats.WinDrawLoss; +import io.sentry.Sentry; public class Database { private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); private Connection c = null; - private PickupLogic logic; - + private final PickupLogic logic; + private Map preparedStmtCache; + + public Database(PickupLogic logic) { + preparedStmtCache = new HashMap<>(); this.logic = logic; initConnection(); } private void initConnection() { try { - c = DriverManager.getConnection("jdbc:sqlite:pickup.db"); + c = DriverManager.getConnection("jdbc:sqlite:" + logic.bot.env + ".pickup.db"); initTable(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -45,9 +51,19 @@ public void disconnect() { c.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } + public PreparedStatement getPreparedStatement(String sql) throws SQLException { + PreparedStatement stmt = preparedStmtCache.get(sql); + if (stmt == null) { + stmt = c.prepareStatement(sql); + preparedStmtCache.put(sql, stmt); + } + return stmt; + } + private void initTable() { try { Statement stmt = c.createStatement(); @@ -56,23 +72,24 @@ private void initTable() { + "elo INTEGER DEFAULT 1000," + "elochange INTEGER DEFAULT 0," + "active TEXT," + + "country TEXT," + + "enforce_ac TEXT DEFAULT 'true'," + + "coins INTEGER DEFAULT 1000," + + "eloboost INTEGER DEFAULT 0," + + "mapvote INTEGER DEFAULT 0," + + "mapban INTEGER DEFAULT 0," + "PRIMARY KEY (userid, urtauth) )"; stmt.executeUpdate(sql); sql = "CREATE TABLE IF NOT EXISTS gametype ( gametype TEXT PRIMARY KEY," - + "config TEXT," + "teamsize INTEGER, " + "active TEXT )"; stmt.executeUpdate(sql); - sql = "CREATE TABLE IF NOT EXISTS gameconfig ( gametype TEXT," - + "config TEXT," - + "PRIMARY KEY (gametype, config) )"; - stmt.executeUpdate(sql); - sql = "CREATE TABLE IF NOT EXISTS map ( map TEXT," + "gametype TEXT," + "active TEXT," + + "banned_until INTEGER DEFAULT 0," + "FOREIGN KEY (gametype) REFERENCES gametype(gametype)," + "PRIMARY KEY (map, gametype) )"; stmt.executeUpdate(sql); @@ -84,6 +101,7 @@ private void initTable() { + "start INTEGER," + "end INTEGER," + "pardon TEXT," + + "forgiven BOOLEAN," + "FOREIGN KEY (player_userid, player_urtauth) REFERENCES player(userid, urtauth) )"; stmt.executeUpdate(sql); @@ -149,7 +167,8 @@ private void initTable() { + "port INTEGER," + "rcon TEXT," + "password TEXT," - + "active TEXT)"; + + "active TEXT," + + "region TEXT)"; stmt.executeUpdate(sql); sql = "CREATE TABLE IF NOT EXISTS roles (role TEXT," @@ -161,10 +180,29 @@ private void initTable() { + "type TEXT," + "PRIMARY KEY (channel, type) )"; stmt.executeUpdate(sql); + + sql = "CREATE TABLE IF NOT EXISTS season (number INTEGER," + + "startdate INTEGER," + + "enddate INTEGER," + + "PRIMARY KEY (number) )"; + stmt.executeUpdate(sql); + + sql = "CREATE TABLE IF NOT EXISTS bets (ID INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_userid TEXT," + + "player_urtauth TEXT," + + "matchid INTEGER," + + "team INTEGER," // red = 0 blue = 1 + + "won TEXT," + + "amount INTEGER," + + "odds FLOAT," + + "FOREIGN KEY (matchid) REFERENCES match(ID), " + + "FOREIGN KEY (player_userid, player_urtauth) REFERENCES player(userid, urtauth) )"; + stmt.executeUpdate(sql); stmt.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -174,37 +212,39 @@ public void createPlayer(Player player) { try { // check whether user exists String sql = "SELECT * FROM player WHERE userid=? AND urtauth=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, player.getDiscordUser().id); pstmt.setString(2, player.getUrtauth()); ResultSet rs = pstmt.executeQuery(); if (!rs.next()) { - sql = "INSERT INTO player (userid, urtauth, elo, elochange, active) VALUES (?, ?, ?, ?, ?)"; - pstmt = c.prepareStatement(sql); + sql = "INSERT INTO player (userid, urtauth, elo, elochange, active, country) VALUES (?, ?, ?, ?, ?, ?)"; + pstmt = getPreparedStatement(sql); pstmt.setString(1, player.getDiscordUser().id); pstmt.setString(2, player.getUrtauth()); pstmt.setInt(3, player.getElo()); pstmt.setInt(4, player.getEloChange()); pstmt.setString(5, String.valueOf(true)); + pstmt.setString(6, player.getCountry()); pstmt.executeUpdate(); } else { sql = "UPDATE player SET active=? WHERE userid=? AND urtauth=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, String.valueOf(true)); pstmt.setString(2, player.getDiscordUser().id); pstmt.setString(3, player.getUrtauth()); pstmt.executeUpdate(); } - pstmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } public void createBan(PlayerBan ban) { try { - String sql = "INSERT INTO banlist (player_userid, player_urtauth, start, end, reason, pardon) VALUES (?, ?, ?, ?, ?, 'null')"; - PreparedStatement pstmt = c.prepareStatement(sql); + String sql = "INSERT INTO banlist (player_userid, player_urtauth, start, end, reason, pardon, forgiven) VALUES (?, ?, ?, ?, ?, 'null', 0)"; + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, ban.player.getDiscordUser().id); pstmt.setString(2, ban.player.getUrtauth()); pstmt.setLong(3, ban.startTime); @@ -213,48 +253,64 @@ public void createBan(PlayerBan ban) { pstmt.executeUpdate(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + + public void forgiveBan(Player player) { + try { + String sql = "UPDATE banlist SET forgiven = 1 WHERE player_urtauth = ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, player.getUrtauth()); + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } public void createServer(Server server) { try { - String sql = "INSERT INTO server (ip, port, rcon, password, active) VALUES (?, ?, ?, ?, ?)"; - PreparedStatement pstmt = c.prepareStatement(sql); + String sql = "INSERT INTO server (ip, port, rcon, password, active, region) VALUES (?, ?, ?, ?, ?, ?)"; + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, server.IP); pstmt.setInt(2, server.port); pstmt.setString(3, server.rconpassword); pstmt.setString(4, server.password); pstmt.setString(5, String.valueOf(server.active)); + pstmt.setString(6, server.region.toString()); pstmt.executeUpdate(); - pstmt.close(); Statement stmt = c.createStatement(); sql = "SELECT ID FROM server ORDER BY ID DESC"; ResultSet rs = stmt.executeQuery(sql); rs.next(); server.id = rs.getInt("ID"); + stmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } public void createMap(GameMap map, Gametype gametype) { try { String sql = "INSERT INTO map (map, gametype, active) VALUES (?, ?, ?)"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql);; pstmt.setString(1, map.name); pstmt.setString(2, gametype.getName()); pstmt.setString(3, String.valueOf(map.isActiveForGametype(gametype))); pstmt.executeUpdate(); - pstmt.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } public int createMatch(Match match) { try { String sql = "INSERT INTO match (state, gametype, server, starttime, map, elo_red, elo_blue) VALUES (?, ?, ?, ?, ?, ?, ?)"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql);; pstmt.setString(1, match.getMatchState().name()); pstmt.setString(2, match.getGametype().getName()); pstmt.setInt(3, match.getServer().id); @@ -280,7 +336,7 @@ public int createMatch(Match match) { score[i] = rs.getInt("ID"); } sql = "INSERT INTO player_in_match (matchid, player_userid, player_urtauth, team) VALUES (?, ?, ?, ?)"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, mid); pstmt.setString(2, player.getDiscordUser().id); pstmt.setString(3, player.getUrtauth()); @@ -291,18 +347,53 @@ public int createMatch(Match match) { rs.next(); int pidmid = rs.getInt("ID"); sql = "INSERT INTO stats (pim, ip, score_1, score_2, status) VALUES (?, null, ?, ?, ?)"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, pidmid); pstmt.setInt(2, score[0]); pstmt.setInt(3, score[1]); pstmt.setString(4, match.getStats(player).getStatus().name()); pstmt.executeUpdate(); } - pstmt.close(); stmt.close(); + rs.close(); return mid; } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return -1; + } + + public int getLastMatchID() { + try { + Statement stmt = c.createStatement(); + String sql = "SELECT ID FROM match ORDER BY ID DESC"; + ResultSet rs = stmt.executeQuery(sql); + rs.next(); + int id = rs.getInt("id"); + stmt.close(); + rs.close(); + return id; + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return -1; + } + + public int getNumberOfGames(Player player) { + try { + String sql = "SELECT COUNT(player_urtauth) as count FROM player_in_match WHERE player_urtauth = ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, player.getUrtauth()); + ResultSet rs = pstmt.executeQuery(); + rs.next(); + int count = rs.getInt("count"); + rs.close(); + return count; + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return -1; } @@ -323,13 +414,16 @@ public Map> loadRoles() { map.put(type, new ArrayList()); } DiscordRole role = DiscordRole.getRole(rs.getString("role")); + assert role != null; LOGGER.config("loadRoles(): " + role.id + " type=" + type.name()); map.get(type).add(role); } stmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return map; } @@ -347,23 +441,26 @@ public Map> loadChannels() { map.put(type, new ArrayList()); } DiscordChannel channel = DiscordChannel.findChannel(rs.getString("channel")); + assert channel != null; map.get(type).add(channel); LOGGER.config("loadChannels(): " + channel.id + " name=" + channel.name + " type=" + type.name()); } stmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return map; } public List loadServers() { - List serverList = new ArrayList(); + List serverList = new ArrayList(); try { Statement stmt = c.createStatement(); - String sql = "SELECT id, ip, port, rcon, password, active FROM server"; + String sql = "SELECT id, ip, port, rcon, password, active, region FROM server"; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { int id = rs.getInt("id"); @@ -371,13 +468,17 @@ public List loadServers() { int port = rs.getInt("port"); String rcon = rs.getString("rcon"); String password = rs.getString("password"); - boolean active = Boolean.valueOf(rs.getString("active")); + boolean active = Boolean.parseBoolean(rs.getString("active")); + String str_region = rs.getString("region"); - Server server = new Server(id, ip, port, rcon, password, active); + Server server = new Server(id, ip, port, rcon, password, active, Region.valueOf(str_region)); serverList.add(server); } + stmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return serverList; } @@ -391,39 +492,26 @@ public List loadGametypes() { String sql = "SELECT gametype, teamsize, active FROM gametype"; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { - Gametype gametype = new Gametype(rs.getString("gametype"), rs.getInt("teamsize"), Boolean.valueOf(rs.getString("active"))); + Gametype gametype = new Gametype(rs.getString("gametype"), rs.getInt("teamsize"), Boolean.parseBoolean(rs.getString("active"))); LOGGER.config(gametype.getName() + " active=" + gametype.getActive()); - loadGameConfig(gametype); gametypeList.add(gametype); } + stmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return gametypeList; } - - - public void loadGameConfig(Gametype gametype) { - try { - String sql = "SELECT config FROM gameconfig WHERE gametype=?"; - PreparedStatement pstmt = c.prepareStatement(sql); - pstmt.setString(1, gametype.getName()); - ResultSet rs = pstmt.executeQuery(); - while (rs.next()) { - gametype.addConfig(rs.getString("config")); - } - LOGGER.config(gametype.getName() + " count=" + gametype.getConfig().size()); - } catch (SQLException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); - } - } + public List loadMaps() { List maplist = new ArrayList(); try { Statement stmt = c.createStatement(); - String sql = "SELECT map, gametype, active FROM map"; + String sql = "SELECT map, gametype, active, banned_until FROM map"; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { GameMap map = null; @@ -435,13 +523,23 @@ public List loadMaps() { } if (map == null) { map = new GameMap(rs.getString("map")); + map.bannedUntil = rs.getLong("banned_until"); maplist.add(map); } - map.setGametype(logic.getGametypeByString(rs.getString("gametype")), Boolean.valueOf(rs.getString("active"))); + map.setGametype(logic.getGametypeByString(rs.getString("gametype")), Boolean.parseBoolean(rs.getString("active"))); + if (rs.getString("gametype").equalsIgnoreCase("TS")){ + map.setGametype(logic.getGametypeByString("SCRIM TS"), Boolean.parseBoolean(rs.getString("active"))); + } + if (rs.getString("gametype").equalsIgnoreCase("CTF")){ + map.setGametype(logic.getGametypeByString("SCRIM CTF"), Boolean.parseBoolean(rs.getString("active"))); + } LOGGER.config(map.name + " " + rs.getString("gametype") + "="+ map.isActiveForGametype(logic.getGametypeByString(rs.getString("gametype")))); } + stmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return maplist; } @@ -451,7 +549,7 @@ public List loadOngoingMatches() { try { ResultSet rs; String sql = "SELECT ID FROM match WHERE state=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, MatchState.Live.name()); rs = pstmt.executeQuery(); while(rs.next()) { @@ -460,8 +558,10 @@ public List loadOngoingMatches() { matchList.add(m); } } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return matchList; } @@ -471,7 +571,7 @@ public Match loadMatch(int id) { try { ResultSet rs, rs1, rs2, rs3; String sql = "SELECT starttime, map, gametype, score_red, score_blue, elo_red, elo_blue, state, server FROM match WHERE ID=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setInt(1, id); rs = pstmt.executeQuery(); if (rs.next()) { @@ -483,7 +583,7 @@ public Match loadMatch(int id) { // getting players in match sql = "SELECT ID, player_userid, player_urtauth, team FROM player_in_match WHERE matchid=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, id); rs1 = pstmt.executeQuery(); while (rs1.next()) { @@ -494,7 +594,7 @@ public Match loadMatch(int id) { // getting stats sql = "SELECT ip, score_1, score_2, status FROM stats WHERE pim=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, pidmid); rs2 = pstmt.executeQuery(); rs2.next(); @@ -508,7 +608,7 @@ public Match loadMatch(int id) { // getting score for(int i = 0; i < 2; ++i) { sql = "SELECT kills, deaths, assists, caps, returns, fckills, stopcaps, protflag FROM score WHERE ID=? ORDER BY ID DESC"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, scoreid[i]); rs3 = pstmt.executeQuery(); rs3.next(); @@ -543,13 +643,51 @@ public Match loadMatch(int id) { state, gametype, server, - stats); - match.setLogic(logic); + stats, + logic); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return match; + } + + public Match loadLastMatch() { + Match match = null; + try { + String sql = "SELECT * FROM match ORDER BY ID DESC LIMIT 1"; + PreparedStatement pstmt = getPreparedStatement(sql); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + match = loadMatch(rs.getInt("ID")); + } + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return match; + + } + + public Match loadLastMatchPlayer(Player p) { + Match match = null; + try { + String sql = "SELECT matchid FROM player_in_match WHERE player_urtauth = ? ORDER BY ID DESC LIMIT 1;"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, p.getUrtauth()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + match = loadMatch(rs.getInt("matchid")); } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return match; + } public Player loadPlayer(String urtauth) { @@ -564,8 +702,8 @@ public Player loadPlayer(DiscordUser user) { public Player loadPlayer(DiscordUser user, String urtauth, boolean onlyActive) { Player player = null; try { - String sql = "SELECT userid, urtauth, elo, elochange, active FROM player WHERE userid LIKE ? AND urtauth LIKE ? AND active LIKE ?"; - PreparedStatement pstmt = c.prepareStatement(sql); + String sql = "SELECT * FROM player WHERE userid LIKE ? AND urtauth LIKE ? AND active LIKE ?"; + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, user == null ? "%" : user.id); pstmt.setString(2, urtauth == null ? "%" : urtauth); pstmt.setString(3, onlyActive ? String.valueOf(true) : "%"); @@ -574,10 +712,16 @@ public Player loadPlayer(DiscordUser user, String urtauth, boolean onlyActive) { player = new Player(DiscordUser.getUser(rs.getString("userid")), rs.getString("urtauth")); player.setElo(rs.getInt("elo")); player.setEloChange(rs.getInt("elochange")); - player.setActive(Boolean.valueOf(rs.getString("active"))); + player.setActive(Boolean.parseBoolean(rs.getString("active"))); + player.setEnforceAC(Boolean.parseBoolean(rs.getString("enforce_ac"))); + player.setCountry(rs.getString("country")); + player.setCoins(rs.getLong("coins")); + player.setEloBoost(rs.getLong("eloboost")); + player.setAdditionalMapVotes(rs.getInt("mapvote")); + player.setMapBans(rs.getInt("mapban")); - sql = "SELECT start, end, reason, pardon FROM banlist WHERE player_userid=? AND player_urtauth=?"; - PreparedStatement banstmt = c.prepareStatement(sql); + sql = "SELECT start, end, reason, pardon, forgiven FROM banlist WHERE player_userid=? AND player_urtauth=?"; + PreparedStatement banstmt = getPreparedStatement(sql); banstmt.setString(1, player.getDiscordUser().id); banstmt.setString(2, player.getUrtauth()); ResultSet banSet = banstmt.executeQuery(); @@ -588,31 +732,50 @@ public Player loadPlayer(DiscordUser user, String urtauth, boolean onlyActive) { ban.endTime = banSet.getLong("end"); ban.reason = BanReason.valueOf(banSet.getString("reason")); ban.pardon = banSet.getString("pardon").matches("^[0-9]*$") ? DiscordUser.getUser(banSet.getString("pardon")) : null; + ban.forgiven = banSet.getBoolean("forgiven"); player.addBan(ban); } + player.setRank(getRankForPlayer(player)); + player.stats = getPlayerStats(player, logic.currentSeason); + banSet.close(); } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return player; } - + public void updatePlayerCountry(Player player, String country) { + try { + String sql = "UPDATE player SET country=? WHERE userid=?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, country); + pstmt.setString(2, player.getDiscordUser().id); + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + // UPDATE SERVER public void updateServer(Server server) { try { - String sql = "UPDATE server SET ip=?, port=?, rcon=?, password=?, active=? WHERE id=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + String sql = "UPDATE server SET ip=?, port=?, rcon=?, password=?, active=?, region=? WHERE id=?"; + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, server.IP); pstmt.setInt(2, server.port); pstmt.setString(3, server.rconpassword); pstmt.setString(4, server.password); pstmt.setString(5, String.valueOf(server.active)); - pstmt.setInt(6, server.id); + pstmt.setString(6, server.region.toString()); + pstmt.setInt(7, server.id); pstmt.executeUpdate(); - pstmt.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -621,7 +784,7 @@ public void updateServer(Server server) { public void updateMap(GameMap map, Gametype gametype) { try { String sql = "SELECT * FROM map WHERE map=? AND gametype=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, map.name); pstmt.setString(2, gametype.getName()); ResultSet rs = pstmt.executeQuery(); @@ -630,60 +793,63 @@ public void updateMap(GameMap map, Gametype gametype) { return; } sql = "UPDATE map SET active=? WHERE map=? AND gametype=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, String.valueOf(map.isActiveForGametype(gametype))); pstmt.setString(2, map.name); pstmt.setString(3, gametype.getName()); pstmt.executeUpdate(); - pstmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } public void updateChannel(DiscordChannel channel, PickupChannelType type) { try { String sql = "SELECT * FROM channels WHERE channel=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, channel.id); ResultSet rs = pstmt.executeQuery(); if (!rs.next()) { sql = "INSERT INTO channels (channel) VALUES (?)"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, channel.id); pstmt.executeUpdate(); } sql = "UPDATE channels SET type=? WHERE channel=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, type.name()); pstmt.setString(2, channel.id); pstmt.executeUpdate(); - pstmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } public void updateRole(DiscordRole role, PickupRoleType type) { try { String sql = "SELECT * FROM roles WHERE role=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, role.id); ResultSet rs = pstmt.executeQuery(); if (!rs.next()) { sql = "INSERT INTO roles (role) VALUES (?)"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, role.id); pstmt.executeUpdate(); } sql = "UPDATE roles SET type=? WHERE role=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, type.name()); pstmt.setString(2, role.id); pstmt.executeUpdate(); - pstmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -694,7 +860,7 @@ public void saveMatch(Match match) { try { ResultSet rs; String sql = "UPDATE match SET state=?, score_red=?, score_blue=? WHERE id=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, match.getMatchState().name()); pstmt.setInt(2, match.getScoreRed()); pstmt.setInt(3, match.getScoreBlue()); @@ -704,7 +870,7 @@ public void saveMatch(Match match) { for (Player player : match.getPlayerList()) { // get ids sql = "SELECT ID FROM player_in_match WHERE matchid=? AND player_userid=? AND player_urtauth=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, match.getID()); pstmt.setString(2, player.getDiscordUser().id); pstmt.setString(3, player.getUrtauth()); @@ -713,7 +879,7 @@ public void saveMatch(Match match) { int pim = rs.getInt("ID"); sql = "SELECT score_1, score_2 FROM stats WHERE pim=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, pim); rs = pstmt.executeQuery(); rs.next(); @@ -721,7 +887,7 @@ public void saveMatch(Match match) { // update ip & status (leaver etc) sql = "UPDATE stats SET ip=?, status=? WHERE pim=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, match.getStats(player).getIP()); pstmt.setString(2, match.getStats(player).getStatus().name()); pstmt.setInt(3, pim); @@ -729,7 +895,7 @@ public void saveMatch(Match match) { // update playerscore sql = "UPDATE score SET kills=?, deaths=?, assists=?, caps=?, returns=?, fckills=?, stopcaps=?, protflag=? WHERE ID=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); for (int i=0; i < 2; ++i) { pstmt.setInt(1, match.getStats(player).score[i].score); pstmt.setInt(2, match.getStats(player).score[i].deaths); @@ -745,69 +911,71 @@ public void saveMatch(Match match) { // update elo change sql = "UPDATE player SET elo=?, elochange=? WHERE userid=? AND urtauth=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, player.getElo()); pstmt.setInt(2, player.getEloChange()); pstmt.setString(3, player.getDiscordUser().id); pstmt.setString(4, player.getUrtauth()); - pstmt.executeUpdate(); + pstmt.executeUpdate(); + rs.close(); } } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } - // need to check whether this is newly created or not public void updateGametype(Gametype gt) { try { String sql = "SELECT gametype FROM gametype WHERE gametype=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, gt.getName()); ResultSet rs = pstmt.executeQuery(); if (!rs.next()) { sql = "INSERT INTO gametype (gametype) VALUES (?)"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setString(1, gt.getName()); pstmt.executeUpdate(); } sql = "UPDATE gametype SET teamsize=?, active=? WHERE gametype=?"; - pstmt = c.prepareStatement(sql); + pstmt = getPreparedStatement(sql); pstmt.setInt(1, gt.getTeamSize()); pstmt.setString(2, String.valueOf(gt.getActive())); pstmt.setString(3, gt.getName()); pstmt.executeUpdate(); - sql = "DELETE FROM gameconfig WHERE gametype=?"; - pstmt = c.prepareStatement(sql); - pstmt.setString(1, gt.getName()); - pstmt.executeUpdate(); - for (String config : gt.getConfig()) { - sql = "INSERT INTO gameconfig (gametype, config) VALUES (?, ?)"; - pstmt = c.prepareStatement(sql); - pstmt.setString(1, gt.getName()); - pstmt.setString(2, config); - pstmt.executeUpdate(); - } - pstmt.close(); + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } - - - public void removePlayer(Player player) { try { String sql = "UPDATE player SET active=? WHERE userid=? AND urtauth=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, String.valueOf(false)); pstmt.setString(2, player.getDiscordUser().id); pstmt.setString(3, player.getUrtauth()); pstmt.executeUpdate(); - pstmt.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + + public void enforcePlayerAC(Player player) { + try { + String sql = "UPDATE player SET enforce_ac=? WHERE userid=? AND urtauth=?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, String.valueOf(player.getEnforceAC())); + pstmt.setString(2, player.getDiscordUser().id); + pstmt.setString(3, player.getUrtauth()); + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -816,7 +984,7 @@ public List getTopPlayers(int number) { List list = new ArrayList(); try { String sql = "SELECT urtauth FROM player WHERE active=? ORDER BY elo DESC LIMIT ?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, String.valueOf(true)); pstmt.setInt(2, number); ResultSet rs = pstmt.executeQuery(); @@ -824,8 +992,32 @@ public List getTopPlayers(int number) { Player p = Player.get(rs.getString("urtauth")); list.add(p); } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + return list; + } + + public ArrayList getTopCountries(int number) { + ArrayList list = new ArrayList(); + try { + String sql = "SELECT AVG(elo) as Average_Elo, country FROM player WHERE active=? GROUP BY country ORDER BY Average_Elo DESC LIMIT ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, String.valueOf(true)); + pstmt.setInt(2, number); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + if(!rs.getString("country").equalsIgnoreCase("NOT_DEFINED")) + { + list.add(new CountryRank(rs.getString("country"), rs.getFloat("Average_Elo"))); + } + } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return list; } @@ -834,7 +1026,7 @@ public int getRankForPlayer(Player player) { int rank = -1; try { String sql = "SELECT (SELECT COUNT(*) FROM player b WHERE a.elo < b.elo AND active=?) AS rank FROM player a WHERE userid=? AND urtauth=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, String.valueOf(true)); pstmt.setString(2, player.getDiscordUser().id); pstmt.setString(3, player.getUrtauth()); @@ -842,13 +1034,15 @@ public int getRankForPlayer(Player player) { if (rs.next()) { rank = rs.getInt("rank") + 1; } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return rank; } - public WinDrawLoss getWDLForPlayer(Player player) { + public WinDrawLoss getWDLForPlayer(Player player, Gametype gt, Season season) { WinDrawLoss wdl = new WinDrawLoss(); try { String sql = "SELECT SUM(CASE WHEN stat.myscore > stat.oppscore THEN 1 ELSE 0 END) AS win, " @@ -861,33 +1055,162 @@ public WinDrawLoss getWDLForPlayer(Player player) { + "FROM 'player_in_match' AS pim " + "JOIN 'match' AS m ON m.id = pim.matchid " + "JOIN 'player' AS p ON pim.player_urtauth=p.urtauth AND pim.player_userid=p.userid " - + "WHERE (m.state = 'Done' OR m.state = 'Surrender') " + + "WHERE (m.state = 'Done' OR m.state = 'Surrender' OR m.state = 'Mercy') AND m.gametype=? AND m.starttime > ? AND m.starttime < ?" + "AND p.urtauth=? AND p.userid=?) AS stat "; - PreparedStatement pstmt = c.prepareStatement(sql); - pstmt.setString(1, player.getUrtauth()); - pstmt.setString(2, player.getDiscordUser().id); + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, gt.getName()); + pstmt.setLong(2, season.startdate); + pstmt.setLong(3, season.enddate); + pstmt.setString(4, player.getUrtauth()); + pstmt.setString(5, player.getDiscordUser().id); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { wdl.win = rs.getInt("win"); wdl.draw = rs.getInt("draw"); wdl.loss = rs.getInt("loss"); } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); } return wdl; } + public int getWDLRankForPlayer(Player player, Gametype gt, Season season) { + int rank = -1; + try { + int limit = 20; + if (season.number == 0){ + limit = 100; + } + if (gt.getName().equals("CTF")){ + limit = 10; + } + String sql = "WITH tablewdl (urtauth, matchcount, winrate) AS (SELECT urtauth, COUNT(urtauth) as matchcount, (CAST(SUM(CASE WHEN stat.myscore > stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)+ CAST(SUM(CASE WHEN stat.myscore = stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)/2)/(CAST(SUM(CASE WHEN stat.myscore > stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)+ CAST(SUM(CASE WHEN stat.myscore = stat.oppscore THEN 1 ELSE 0 END) AS FLOAT) + CAST(SUM(CASE WHEN stat.myscore < stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)) as winrate FROM (SELECT pim.player_urtauth AS urtauth, (CASE WHEN pim.team = 'red' THEN m.score_red ELSE m.score_blue END) AS myscore, (CASE WHEN pim.team = 'blue' THEN m.score_red ELSE m.score_blue END) AS oppscore FROM 'player_in_match' AS pim JOIN 'match' AS m ON m.id = pim.matchid JOIN 'player' AS p ON pim.player_urtauth=p.urtauth AND pim.player_userid=p.userid AND p.active='true' WHERE (m.state = 'Done' OR m.state = 'Surrender' OR m.state = 'Mercy') AND m.starttime > ? AND m.starttime < ? AND m.gametype = ?) AS stat GROUP BY urtauth HAVING COUNT(urtauth) > ? ORDER BY winrate DESC) SELECT ( SELECT COUNT(*) + 1 FROM tablewdl WHERE winrate > t.winrate) as rowIndex FROM tablewdl t WHERE urtauth = ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setLong(1, season.startdate); + pstmt.setLong(2, season.enddate); + pstmt.setString(3, gt.getName()); + pstmt.setInt(4, limit); + pstmt.setString(5, player.getUrtauth()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + rank = rs.getInt("rowIndex"); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return rank; + } + + public int getKDRRankForPlayer(Player player, Gametype gt, Season season) { + int rank = -1; + try { + int limit = 20; + if (season.number == 0){ + limit = 100; + } + + String rating_query = "(CAST(SUM(kills) AS FLOAT) + CAST(SUM(assists) AS FLOAT)/2) / CAST(SUM(deaths) AS FLOAT)"; + if (gt.getName().equals("CTF")){ + limit = 10; + rating_query = "CAST (SUM(score.kills) AS FLOAT) / (COUNT(player_in_match.player_urtauth)/2 ) / 50"; + } + String sql = "WITH tablekdr (auth, matchcount, kdr) AS (SELECT player.urtauth AS auth, COUNT(player_in_match.player_urtauth)/2 as matchcount, " + rating_query + " AS kdr FROM (score INNER JOIN stats ON stats.score_1 = score.ID OR stats.score_2 = score.ID INNER JOIN player_in_match ON player_in_match.ID = stats.pim INNER JOIN player ON player_in_match.player_userid = player.userid INNER JOIN match ON player_in_match.matchid = match.id) WHERE player.active = 'true' AND (match.state = 'Done' OR match.state = 'Surrender' OR match.state = 'Mercy') AND match.gametype=? AND match.starttime > ? AND match.starttime < ? GROUP BY player_in_match.player_urtauth HAVING matchcount > ? ORDER BY kdr DESC) SELECT ( SELECT COUNT(*) + 1 FROM tablekdr WHERE kdr > t.kdr) as rowIndex FROM tablekdr t WHERE auth = ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, gt.getName()); + pstmt.setLong(2, season.startdate); + pstmt.setLong(3, season.enddate); + pstmt.setInt(4, limit); + pstmt.setString(5, player.getUrtauth()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + rank = rs.getInt("rowIndex"); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return rank; + } + + public Map getTopWDL(int number, Gametype gt, Season season) { + Map topwdl = new LinkedHashMap(); + try { + int limit = 20; + if (season.number == 0){ + limit = 100; + } + if (gt.getName().equals("CTF")){ + limit = 10; + } + String sql = "SELECT urtauth, COUNT(urtauth) as matchcount, SUM(CASE WHEN stat.myscore > stat.oppscore THEN 1 ELSE 0 END) as win, SUM(CASE WHEN stat.myscore = stat.oppscore THEN 1 ELSE 0 END) as draw, SUM(CASE WHEN stat.myscore < stat.oppscore THEN 1 ELSE 0 END) loss , (CAST(SUM(CASE WHEN stat.myscore > stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)+ CAST(SUM(CASE WHEN stat.myscore = stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)/2)/(CAST(SUM(CASE WHEN stat.myscore > stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)+ CAST(SUM(CASE WHEN stat.myscore = stat.oppscore THEN 1 ELSE 0 END) AS FLOAT) + CAST(SUM(CASE WHEN stat.myscore < stat.oppscore THEN 1 ELSE 0 END) AS FLOAT)) as winrate FROM (SELECT pim.player_urtauth AS urtauth, (CASE WHEN pim.team = 'red' THEN m.score_red ELSE m.score_blue END) AS myscore, (CASE WHEN pim.team = 'blue' THEN m.score_red ELSE m.score_blue END) AS oppscore FROM 'player_in_match' AS pim JOIN 'match' AS m ON m.id = pim.matchid JOIN 'player' AS p ON pim.player_urtauth=p.urtauth AND pim.player_userid=p.userid AND p.active='true' WHERE (m.state = 'Done' OR m.state = 'Surrender' OR m.state = 'Mercy') AND m.gametype = ? AND m.starttime > ? AND m.starttime < ?) AS stat GROUP BY urtauth HAVING COUNT(urtauth) > ? ORDER BY winrate DESC LIMIT ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, gt.getName()); + pstmt.setLong(2, season.startdate); + pstmt.setLong(3, season.enddate); + pstmt.setLong(4, limit); + pstmt.setInt(5, number); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + Player p = Player.get(rs.getString("urtauth")); + topwdl.put(p, rs.getFloat("winrate")); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return topwdl; + } + + public Map getTopKDR(int number, Gametype gt, Season season) { + Map topkdr = new LinkedHashMap(); + try { + int limit = 20; + if (season.number == 0){ + limit = 100; + } + + String rating_query = "(CAST(SUM(kills) AS FLOAT) + CAST(SUM(assists) AS FLOAT)/2) / CAST(SUM(deaths) AS FLOAT)"; + if (gt.getName().equals("CTF")){ + limit = 10; + rating_query = "CAST (SUM(score.kills) AS FLOAT) / (COUNT(player_in_match.player_urtauth)/2 ) / 50"; + } else if (gt.getName().equals("SKEET") || gt.getName().equals("AIM")){ + limit = 0; + rating_query = "MAX(kills)"; + } + String sql = "SELECT player.urtauth AS auth, COUNT(player_in_match.player_urtauth)/2 as matchcount, " + rating_query + " AS kdr FROM score INNER JOIN stats ON stats.score_1 = score.ID OR stats.score_2 = score.ID INNER JOIN player_in_match ON player_in_match.ID = stats.pim INNER JOIN player ON player_in_match.player_userid = player.userid INNER JOIN match ON match.id = player_in_match.matchid WHERE player.active = \"true\" AND (match.state = 'Done' OR match.state = 'Surrender' OR match.state = 'Mercy') AND match.gametype = ? AND match.starttime > ? AND match.starttime < ? GROUP BY player_in_match.player_urtauth HAVING matchcount > ? ORDER BY kdr DESC LIMIT ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, gt.getName()); + pstmt.setLong(2, season.startdate); + pstmt.setLong(3, season.enddate); + pstmt.setLong(4, limit); + pstmt.setInt(5, number); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + Player p = Player.get(rs.getString("auth")); + LOGGER.info(p.getUrtauth()); + topkdr.put(p, rs.getFloat("kdr")); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return topkdr; + } + public int getAvgElo() { int elo = -1; try { String sql = "SELECT AVG(elo) AS avg_elo FROM player WHERE active=?"; - PreparedStatement pstmt = c.prepareStatement(sql); + PreparedStatement pstmt = getPreparedStatement(sql); pstmt.setString(1, String.valueOf(true)); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { elo = rs.getInt("avg_elo"); } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); } @@ -912,9 +1235,219 @@ public void resetStats() { stmt.executeUpdate(sql); sql = "DELETE FROM player WHERE active='false'"; stmt.executeUpdate(sql); + stmt.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + } + + public PlayerStats getPlayerStats(Player player, Season season) { + PlayerStats stats = new PlayerStats(); + + stats.kdrRank = getKDRRankForPlayer(player, logic.getGametypeByString("TS"), season); + stats.ctfRank = getKDRRankForPlayer(player, logic.getGametypeByString("CTF"), season); + + stats.wdlRank = getWDLRankForPlayer(player, logic.getGametypeByString("TS"), season); + stats.ctfWdlRank = getWDLRankForPlayer(player, logic.getGametypeByString("CTF"), season); + + stats.ts_wdl = getWDLForPlayer(player, logic.getGametypeByString("TS"), season); + stats.ctf_wdl = getWDLForPlayer(player, logic.getGametypeByString("CTF"), season); + + try { + // TODO: maybe move this somewhere + String sql = "SELECT SUM(kills) as sumkills, SUM(deaths) as sumdeaths, SUM(assists) as sumassists FROM score INNER JOIN stats ON stats.score_1 = score.ID OR stats.score_2 = score.ID INNER JOIN player_in_match ON player_in_match.ID = stats.pim INNER JOIN match ON match.id = player_in_match.matchid WHERE match.gametype=\"TS\" AND (match.state = 'Done' OR match.state = 'Surrender' OR match.state = 'Mercy') AND player_userid=? AND player_urtauth=? AND match.starttime > ? AND match.starttime < ?;"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, player.getDiscordUser().id); + pstmt.setString(2, player.getUrtauth()); + pstmt.setLong(3, season.startdate); + pstmt.setLong(4, season.enddate); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + float kdr = ((float) rs.getInt("sumkills") + (float) rs.getInt("sumassists") / 2) / (float) rs.getInt("sumdeaths"); + player.setKdr(kdr); + stats.kdr = kdr; + stats.kills = rs.getInt("sumkills"); + stats.assists = rs.getInt("sumassists"); + stats.deaths = rs.getInt("sumdeaths"); + } + + // CTF + sql = "SELECT COUNT(player_in_match.player_urtauth)/2 as matchcount, CAST (SUM(score.kills) AS FLOAT) / (COUNT(player_in_match.player_urtauth)/2 ) / 50 as ctfrating, SUM(caps) as sumcaps, SUM(returns) as sumreturns, SUM(fckills) as sumfckills, SUM(stopcaps) as sumstopcaps, SUM(protflag) as sumprotflag, player_in_match.player_urtauth as auth, match.id as matchid FROM score INNER JOIN stats ON (score.id = stats.score_1 OR score.id = stats.score_2) INNER JOIN player_in_match ON player_in_match.id = stats.pim INNER JOIN match ON player_in_match.matchid = match.id WHERE match.gametype=\"CTF\" AND (match.state = 'Done' OR match.state = 'Surrender' OR match.state = 'Mercy') AND auth=? AND match.starttime > ? AND match.starttime < ?;"; + pstmt = getPreparedStatement(sql); + pstmt.setString(1, player.getUrtauth()); + pstmt.setLong(2, season.startdate); + pstmt.setLong(3, season.enddate); + rs = pstmt.executeQuery(); + if (rs.next()) { + stats.ctf_rating = rs.getFloat("ctfrating"); + stats.caps = rs.getInt("sumcaps"); + stats.returns = rs.getInt("sumreturns"); + stats.fckills = rs.getInt("sumfckills"); + stats.stopcaps = rs.getInt("sumstopcaps"); + stats.protflag = rs.getInt("sumprotflag"); + } + rs.close(); } catch (SQLException e) { LOGGER.log(Level.WARNING, "Exception: ", e); } + + return stats; } + public void resetElo() { + try { + // TODO: maybe move this somewhere + String sql = "UPDATE player SET elo = 500 WHERE elo < 1200;"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.executeUpdate(); + + sql = "UPDATE player SET elo = 750 WHERE elo > 1200 AND elo < 1400;"; + pstmt = getPreparedStatement(sql); + pstmt.executeUpdate(); + + sql = "UPDATE player SET elo = 1000 WHERE elo > 1400;"; + pstmt = getPreparedStatement(sql); + pstmt.executeUpdate(); + + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + } + + public Season getCurrentSeason(){ + try { + String sql = "SELECT number, startdate, enddate FROM season ORDER BY number DESC LIMIT 1;"; + PreparedStatement pstmt = getPreparedStatement(sql); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + int number = rs.getInt("number"); + long startdate = rs.getLong("startdate"); + long enddate = rs.getLong("enddate"); + return new Season(number, startdate, enddate); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return null; + } + + public Season getSeason(int number){ + try { + String sql = "SELECT number, startdate, enddate FROM season WHERE number = ?;"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, String.valueOf(number)); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + long startdate = rs.getLong("startdate"); + long enddate = rs.getLong("enddate"); + return new Season(number, startdate, enddate); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return null; + } + + public void updatePlayerCoins(Player player){ + try{ + String sql = "UPDATE player SET coins=? WHERE userid=? AND urtauth=?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setLong(1, player.getCoins()); + pstmt.setString(2, player.getDiscordUser().id); + pstmt.setString(3, player.getUrtauth()); + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + } + + public void updatePlayerBoost(Player player){ + try{ + String sql = "UPDATE player SET eloboost=?, mapvote=?, mapban=? WHERE userid=? AND urtauth=?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setLong(1, player.getEloBoost()); + pstmt.setInt(2, player.getAdditionalMapVotes()); + pstmt.setInt(3, player.getMapBans()); + pstmt.setString(4, player.getDiscordUser().id); + pstmt.setString(5, player.getUrtauth()); + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + } + + public void createBet(Bet bet) { + try { + String sql = "INSERT INTO bets (player_userid, player_urtauth, matchid, team, won, amount, odds) VALUES (?, ?, ?, ?, ?, ?, ?)"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, bet.player.getDiscordUser().id); + pstmt.setString(2, bet.player.getUrtauth()); + pstmt.setInt(3, bet.matchid); + pstmt.setInt(4, bet.color.equals("red") ? 0 : 1); + pstmt.setString(5, String.valueOf(bet.won)); + pstmt.setLong(6, bet.amount); + pstmt.setFloat(7, bet.odds); + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + + public Map getTopRich(int number) { + Map toprich = new LinkedHashMap(); + try { + String sql = "SELECT urtauth, coins FROM player INNER JOIN bets ON (player.urtauth = bets.player_urtauth ) GROUP BY urtauth ORDER BY coins DESC LIMIT ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setInt(1, number); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + Player p = Player.get(rs.getString("urtauth")); + LOGGER.info(p.getUrtauth()); + toprich.put(p, rs.getLong("coins")); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return toprich; + } + + public void updateMapBan(GameMap map){ + try { + String sql = "UPDATE map set banned_until = ? WHERE map = ?"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setLong(1, map.bannedUntil); + pstmt.setString(2, map.name); + pstmt.executeUpdate(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + + } + + public ArrayList getBetHistory(Player p){ + ArrayList betList = new ArrayList(); + try { + String sql = "SELECT * from bets WHERE bets.player_urtauth = ? ORDER BY bets.ID DESC LIMIT 10;"; + PreparedStatement pstmt = getPreparedStatement(sql); + pstmt.setString(1, p.getUrtauth()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + int matchid = rs.getInt("matchid"); + String color = rs.getInt("team") == 0 ? "red" : "blue"; + int amount = rs.getInt("amount"); + float odds = rs.getFloat("odds"); + Bet bet = new Bet(matchid, p, color, amount, odds); + bet.won = Boolean.parseBoolean(rs.getString("won")); + betList.add(bet); + } + rs.close(); + } catch (SQLException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + } + return betList; + } } diff --git a/src/de/gost0r/pickupbot/pickup/GameMap.java b/src/de/gost0r/pickupbot/pickup/GameMap.java index 485ea5f..5dcdf62 100644 --- a/src/de/gost0r/pickupbot/pickup/GameMap.java +++ b/src/de/gost0r/pickupbot/pickup/GameMap.java @@ -6,10 +6,12 @@ public class GameMap { public String name; + public long bannedUntil; public Map gametypeList = new HashMap(); public GameMap(String name) { this.name = name; + this.bannedUntil = 0; } public void setGametype(Gametype gametype, boolean active) { diff --git a/src/de/gost0r/pickupbot/pickup/Gametype.java b/src/de/gost0r/pickupbot/pickup/Gametype.java index 9cd9343..42e2fb0 100644 --- a/src/de/gost0r/pickupbot/pickup/Gametype.java +++ b/src/de/gost0r/pickupbot/pickup/Gametype.java @@ -1,10 +1,22 @@ package de.gost0r.pickupbot.pickup; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; public class Gametype { + private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final static File CONFIGS_DIRECTORY = new File("configs/"); + private String name; private boolean active; @@ -18,6 +30,7 @@ public Gametype(String name, int teamSize, boolean active) { this.setTeamSize(teamSize); config = new ArrayList(); + this.loadGameConfig(); } public String getName() { @@ -62,8 +75,64 @@ public void removeConfig(String configString) { public boolean equals(Object o) { if (o instanceof Gametype) { Gametype gt = (Gametype) o; - return gt.name == this.name; + return gt.name.equals(this.name); } return false; } + + public void loadGameConfig() { + // Read file gametype.cfg + File[] contents = CONFIGS_DIRECTORY.listFiles(); + boolean config_found = false; + + try { + for ( File f : contents ) { + String configName = this.getName(); + if (configName.startsWith("SCRIM")){ + configName = configName.split(" ")[1]; + } + if ( f.getName().contentEquals(configName + ".cfg") ) { + + BufferedReader br = new BufferedReader(new FileReader(CONFIGS_DIRECTORY.getPath() + "/" + f.getName())); + + String line = br.readLine(); + while(line != null) { + // Avoid commentary and empty lines + if(line.isEmpty() || line.charAt(0) == '/' && line.charAt(1) == '/') { + if(!line.contains("rconpassword")) { + line = br.readLine(); + } + continue; + } + + String ParameterWithoutCommentary = StringUtils.substringBefore(line, "//"); + ParameterWithoutCommentary = StringUtils.normalizeSpace(ParameterWithoutCommentary); + + this.addConfig(ParameterWithoutCommentary); + + line = br.readLine(); + } + + br.close(); + + config_found = true; + break; + } + } + } catch (FileNotFoundException e) { + LOGGER.log(Level.SEVERE, "Error while openning gametype config file : " + this.getName(), e); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error while reading gametype config file : " + this.getName(), e); + } + + if (!config_found) { + LOGGER.log(Level.SEVERE, "Configuration file not found for gametype : " + this.getName()); + } + + } + + public boolean isTeamGamemode(){ + return this.getName().equalsIgnoreCase("SCRIM TS") || this.getName().equalsIgnoreCase("SCRIM CTF") || this.getName().equalsIgnoreCase("2v2"); + } + } diff --git a/src/de/gost0r/pickupbot/pickup/Match.java b/src/de/gost0r/pickupbot/pickup/Match.java index 61e5174..68d58ba 100644 --- a/src/de/gost0r/pickupbot/pickup/Match.java +++ b/src/de/gost0r/pickupbot/pickup/Match.java @@ -1,48 +1,82 @@ package de.gost0r.pickupbot.pickup; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; +import de.gost0r.pickupbot.ftwgl.FtwglAPI; +import io.sentry.Sentry; +import org.json.JSONObject; + +import de.gost0r.pickupbot.discord.DiscordButton; +import de.gost0r.pickupbot.discord.DiscordButtonStyle; +import de.gost0r.pickupbot.discord.DiscordChannel; +import de.gost0r.pickupbot.discord.DiscordComponent; +import de.gost0r.pickupbot.discord.DiscordEmbed; +import de.gost0r.pickupbot.discord.DiscordMessage; import de.gost0r.pickupbot.pickup.MatchStats.Status; import de.gost0r.pickupbot.pickup.server.Server; import de.gost0r.pickupbot.pickup.server.ServerMonitor.ServerState; public class Match implements Runnable { - private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - + private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private Gametype gametype; private MatchState state; private int id; private Map> teamList; private Map mapVotes; - private Map playerStats = new HashMap(); + private Map playerStats; + private List sortedPlayers; + private List squadList; // Premade teams + public List threadChannels; + public List liveScoreMsgs; + private Server server; + private Server gtvServer; private GameMap map; private int[] elo = new int[2]; private int[] score = new int[2]; + private float[] odds = new float[2]; + private Player[] captains = new Player[2]; + private int captainTurn; + private int pickRound; + private int[] pickSequence; + private int[] surrender; - + private long startTime; - + private long timeLastPick; + private boolean pickReminderSent; + private List pickMessages = new ArrayList(); + private PickupLogic logic; - + + public ArrayList bets; + public int payWin = 50; + public int payLose = 25; + private Match() { + playerStats = new HashMap(); teamList = new HashMap>(); teamList.put("red", new ArrayList()); teamList.put("blue", new ArrayList()); mapVotes = new HashMap(); + sortedPlayers = new ArrayList(); + captainTurn = 1; + pickRound = 0; + pickSequence = new int[] {1, 0, 1, 0, 1, 0, 1, 0};//{1, 0, 0, 1, 0, 1, 1, 0}; + threadChannels = new ArrayList(); + liveScoreMsgs = new ArrayList(); + squadList = new ArrayList(); + bets = new ArrayList(); } - + public Match(PickupLogic logic, Gametype gametype, List maplist) { this(); this.logic = logic; @@ -58,7 +92,7 @@ public Match(PickupLogic logic, Gametype gametype, List maplist) { public Match(int id, long startTime, GameMap map, int[] score, int[] elo, Map> teamList, MatchState state, Gametype gametype, Server server, - Map playerStats) { + Map playerStats, PickupLogic logic) { this(); this.id = id; this.startTime = startTime; @@ -70,6 +104,12 @@ public Match(int id, long startTime, GameMap map, int[] score, int[] elo, this.gametype = gametype; this.server = server; this.playerStats = playerStats; + this.logic = logic; + + if (server == null){ + abort(); + return; + } if (!isOver()) { server.startMonitoring(this); @@ -85,29 +125,33 @@ public void reset() { resetAwaitingServer(); } else if (state == MatchState.Live) { resetLive(); - } else if (isOver()) { - // do nothing } + refundBets(); } - + private void resetSignup() { // reset mapvote for(Player p : playerStats.keySet()) { p.resetVotes(); } - for (GameMap m : mapVotes.keySet()) { - mapVotes.put(m, 0); + mapVotes.replaceAll((m, v) -> 0); + + if(!threadChannels.isEmpty()){ + for (DiscordChannel threadChannel : threadChannels){ + threadChannel.delete(); + } + threadChannels = new ArrayList(); } playerStats.clear(); } - + private void resetAwaitingServer() { logic.cancelRequestServer(this); resetSignup(); state = MatchState.Signup; } - + private void resetLive() { abort(); } @@ -123,7 +167,7 @@ public void removePlayer(Player player, boolean shouldSpam) { if ((state == MatchState.Signup || state == MatchState.AwaitingServer) && isInMatch(player)) { GameMap map = player.getVotedMap(gametype); if (map != null) { - mapVotes.put(map, mapVotes.get(map).intValue() - 1); + mapVotes.put(map, mapVotes.get(map) - 1); player.voteMap(gametype, null); } playerStats.remove(player); @@ -132,25 +176,50 @@ public void removePlayer(Player player, boolean shouldSpam) { } } - public void voteMap(Player player, GameMap map) { - if ((state == MatchState.Signup || state == MatchState.AwaitingServer) && isInMatch(player)) { + public void addSquad(Team squad) { + if (state == MatchState.Signup) { + squadList.add(squad); + } + } + + public void removeSquad(Team squad) { + if (state == MatchState.Signup) { + squadList.remove(squad); + } + } + + public void voteMap(Player player, GameMap map, int number, boolean bonus) { + if ((state == MatchState.Signup || state == MatchState.AwaitingServer) && (isInMatch(player) || sortedPlayers.contains(player))) { GameMap oldMap = player.getVotedMap(gametype); if (oldMap != null) { - mapVotes.put(oldMap, mapVotes.get(oldMap).intValue() - 1); - player.voteMap(gametype, null); + if (!bonus){ + mapVotes.put(oldMap, mapVotes.get(oldMap) - 1); + player.voteMap(gametype, null); + } } - mapVotes.put(map, mapVotes.get(map).intValue() + 1); + mapVotes.put(map, mapVotes.get(map) + number); player.voteMap(gametype, map); String msg = Config.pkup_map; msg = msg.replace(".map.", map.name); + msg = msg.replace(".count.", String.valueOf(mapVotes.get(map))); + if (bonus){ + player.setAdditionalMapVotes(0); + msg = Config.used_additonal_vote; + msg = msg.replace(".player.", player.getDiscordUser().getMentionString()); + msg = msg.replace(".vote.", String.valueOf(number)); + msg = msg.replace(".map.", map.name); + msg = msg.replace(".count.", String.valueOf(mapVotes.get(map))); + logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), msg); + return; + } logic.bot.sendNotice(player.getDiscordUser(), msg); } else { logic.bot.sendNotice(player.getDiscordUser(), Config.map_cannot_vote); } } - + public void voteSurrender(Player player) { - long timeUntilSurrender = (startTime + 600000L) - System.currentTimeMillis(); // 10min in milliseconds + long timeUntilSurrender = (startTime + 180000L) - System.currentTimeMillis(); // 3min in milliseconds if (timeUntilSurrender < 0) { if (!player.hasVotedSurrender()) { player.voteSurrender(); @@ -177,7 +246,7 @@ public void voteSurrender(Player player) { logic.bot.sendNotice(player.getDiscordUser(), msg); } } - + public void checkSurrender() { for (int i = 0; i < 2; ++i) { if (surrender[i] <= 0) { @@ -186,27 +255,32 @@ public void checkSurrender() { server.getServerMonitor().surrender(i); } catch (Exception e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } cleanUp(); + logic.db.saveMatch(this); sendAftermath(); logic.matchRemove(this); - logic.db.saveMatch(this); } } } - + public void checkReadyState(Player player) { - if (playerStats.keySet().size() == gametype.getTeamSize() * 2) { + if (playerStats.keySet().size() == gametype.getTeamSize() * 2 + || (gametype.getTeamSize() == 0 && playerStats.keySet().size() == 1)) { state = MatchState.AwaitingServer; - logic.cmdStatus(this, null, true); + logic.cmdStatus(this, player, true); + + // Compute region majority logic.requestServer(this); } else { logic.cmdStatus(this, player, true); } } - + public void checkServerState() { - if (state == MatchState.AwaitingServer && playerStats.keySet().size() != gametype.getTeamSize() * 2) { + if (state == MatchState.AwaitingServer && (playerStats.keySet().size() != gametype.getTeamSize() * 2 + || (gametype.getTeamSize() == 0 && playerStats.keySet().size() != 1))) { state = MatchState.Signup; logic.cancelRequestServer(this); } @@ -216,22 +290,66 @@ public void abort() { state = MatchState.Abort; cleanUp(); logic.db.saveMatch(this); + +// for (DiscordChannel threadChannel : threadChannels){ +// threadChannel.archive(); +// } + + if (gtvServer != null) { + gtvServer.free(); + gtvServer.sendRcon("gtv_disconnect 1"); + } + refundBets(); } public void abandon(Status status, List involvedPlayers) { state = MatchState.Abandon; cleanUp(); + logic.db.saveMatch(this); + +// for (DiscordChannel threadChannel : threadChannels){ +// threadChannel.archive(); +// } + + if (gtvServer != null) { + gtvServer.free(); + gtvServer.sendRcon("gtv_disconnect 1"); + } + sendAftermath(status, involvedPlayers); logic.matchRemove(this); - logic.db.saveMatch(this); + refundBets(); } public void end() { state = MatchState.Done; + if (server.getServerMonitor().noMercyIssued){ + state = MatchState.Mercy; + payWin = 75; + } cleanUp(); + + logic.db.saveMatch(this); + // Update player stats + for (Player p : playerStats.keySet()){ + p.stats = logic.db.getPlayerStats(p, logic.currentSeason); + p.setRank(logic.db.getRankForPlayer(p)); + } + +// for (DiscordChannel threadChannel : threadChannels){ +// threadChannel.archive(); +// } + + if (gtvServer != null) { + gtvServer.free(); + gtvServer.sendRcon("gtv_disconnect 1"); + } + sendAftermath(); logic.matchRemove(this); - logic.db.saveMatch(this); + if (gametype.getTeamSize() > 0){ + payPlayers(); + } } public void cancelStart() { @@ -240,44 +358,55 @@ public void cancelStart() { logic.matchStarted(this); logic.matchRemove(this); } - + private void cleanUp() { for(Player p : playerStats.keySet()) { p.resetVotes(); } - server.free(); + if (server != null){ + server.free(); + } - logic.matchEnded(this); + logic.matchEnded(); } - + private void sendAftermath() { - String fullmsg = Config.pkup_aftermath_head; - fullmsg = fullmsg.replace(".gametype.", gametype.getName()); - fullmsg = fullmsg.replace(".gamenumber.", String.valueOf(id)); - fullmsg = fullmsg.replace(".map.", map.name); + StringBuilder fullmsg = new StringBuilder(Config.pkup_aftermath_head); + fullmsg = new StringBuilder(fullmsg.toString().replace(".gamenumber.", String.valueOf(id)).replace(".gametype.", gametype.getName()).replace(".map.", map.name)); for (int i = 0; i < 2; ++i) { - int team = i; - int opp = (team + 1) % 2; - String teamname = (team == 0) ? "Red" : "Blue"; - String msg = Config.pkup_aftermath_result; - msg = msg.replace(".team.", teamname); - if (score[team] > score[opp]) { - msg = msg.replace(".result.", "won"); - } else if (score[team] < score[opp]) { - msg = msg.replace(".result.", "lost"); + int opp = (i + 1) % 2; + String teamname = (i == 0) ? "Red" : "Blue"; + StringBuilder msg = new StringBuilder(Config.pkup_aftermath_result); + msg = new StringBuilder(msg.toString().replace(".team.", teamname)); + if (score[i] > score[opp]) { + msg = new StringBuilder(msg.toString().replace(".result.", "won")); + } else if (score[i] < score[opp]) { + msg = new StringBuilder(msg.toString().replace(".result.", "lost")); } else { - msg = msg.replace(".result.", "draw"); - } - msg = msg.replace(".score.", score[team] + "-" + score[opp]); + msg = new StringBuilder(msg.toString().replace(".result.", "draw")); + } + msg = new StringBuilder(msg.toString().replace(".score.", score[i] + "-" + score[opp])); for (Player p : teamList.get(teamname.toLowerCase())) { String msgelo = Config.pkup_aftermath_player; msgelo = msgelo.replace(".player.", p.getDiscordUser().getMentionString()); - String elochange = ((p.getEloChange() >= 0) ? "+" : "") + String.valueOf(p.getEloChange()); + String elochange = ((p.getEloChange() >= 0) ? "+" : "") + p.getEloChange(); + if (p.hasBoostActive()){ + elochange = "**" + ((p.getEloChange() > 0) ? "+" : "") + p.getEloChange() + "** ``BOOST``"; + } msgelo = msgelo.replace(".elochange.", elochange); - msg += " " + msgelo; + msg.append(" ").append(msgelo); } - fullmsg += "\n" + msg; + if (gametype.getTeamSize() > 0){ + if (score[i] > score[opp]) { + msg.append(" Team reward: <:pugcoin_bronze:1081604558381400064> ``" + payWin + "``"); + } + else{ + msg.append(" Team reward: <:pugcoin_bronze:1081604558381400064> ``" + payLose + "``"); + } + } + + fullmsg.append("\n").append(msg); } for (Player player : getPlayerList()) { if (player.didChangeRank()) { @@ -285,13 +414,13 @@ private void sendAftermath() { msg = msg.replace(".player.", player.getDiscordUser().getMentionString()); msg = msg.replace(".updown.", player.getEloChange() > 0 ? "up" : "down"); msg = msg.replace(".rank.", player.getRank().getEmoji()); - fullmsg += "\n" + msg; + fullmsg.append("\n").append(msg); } } - logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), fullmsg); + logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), fullmsg.toString(), getMatchEmbed(false)); } - + private void sendAftermath(Status status, List involvedPlayers) { String fullmsg = Config.pkup_aftermath_head; fullmsg = fullmsg.replace(".gametype.", gametype.getName()); @@ -301,62 +430,290 @@ private void sendAftermath(Status status, List involvedPlayers) { String msg = Config.pkup_aftermath_abandon_1; msg = msg.replace(".reason.", status.name()); fullmsg += "\n" + msg; - + if (!involvedPlayers.isEmpty()) { // only if we have people who left - String playerlist = ""; + StringBuilder playerlist = new StringBuilder(); for (Player player : involvedPlayers) { - if (!playerlist.isEmpty()) { - playerlist += " "; + if (playerlist.length() > 0) { + playerlist.append(" "); } - playerlist += player.getUrtauth(); + playerlist.append(player.getUrtauth()); } String be = involvedPlayers.size() == 1 ? "was" : "were"; - + msg = Config.pkup_aftermath_abandon_2; - msg = msg.replace(".players.", playerlist); + msg = msg.replace(".players.", playerlist.toString()); msg = msg.replace(".be.", be); fullmsg += "\n" + msg; } - + logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), fullmsg); } - + public void launch(Server server) { if (!isOver() && state != MatchState.Live) { - + // Need to set temp id here to cancel games during draft + id = logic.db.getLastMatchID() + 1; this.server = server; server.take(); - - state = MatchState.Live; - + server.matchid = id; + for (Player player : getPlayerList()) { for (Match m : logic.playerInMatch(player)) { - if (m == this) continue; + if (m == this || (gametype.getTeamSize() <= 2 && m.getGametype().getTeamSize() > 2)){ + continue; + } + if (gametype.getTeamSize() > 2 && m.getGametype().getTeamSize() <= 2 && m.state == MatchState.Live){ + m.reset(); + continue; + } m.removePlayer(player, false); } } - + + String threadTitle = Config.pkup_go_pub_threadtitle; + threadTitle = threadTitle.replace(".ID.", String.valueOf(logic.db.getLastMatchID() + 1)); + + for (DiscordChannel publicChannel : logic.getChannelByType(PickupChannelType.PUBLIC)){ + threadChannels.add(logic.bot.createThread(publicChannel, threadTitle)); + } + logic.matchStarted(this); + timeLastPick = System.currentTimeMillis(); + sortPlayers(); + + } + } + + public void sortPlayers() { + // Sort players by elo + List playerList = new ArrayList(playerStats.keySet()); + + if (squadList.size() > 0){ + Team squad = squadList.get(0); + for (Player player : playerList){ + if (squad.isInTeam(player)){ + teamList.get("red").add(player); + } + else { + teamList.get("blue").add(player); + } + } + sortedPlayers.clear(); + checkTeams(); + return; + } + + sortedPlayers.add(playerList.get(0)); + for (Player player : playerList) { + for (Player sortedPlayer : sortedPlayers) { + if (player.getCaptainScore(gametype) >= sortedPlayer.getCaptainScore(gametype) && !player.equals(sortedPlayer)) { + sortedPlayers.add(sortedPlayers.indexOf(sortedPlayer), player); + break; + } + } + if (!sortedPlayers.contains(player)) { + sortedPlayers.add(player); + } + } + if (gametype.getTeamSize() != 0) { + captains[0] = sortedPlayers.get(0); + teamList.get("red").add(captains[0]); + + captains[1] = sortedPlayers.get(1); + teamList.get("blue").add(captains[1]); + } else{ + captains[0] = sortedPlayers.get(0); + teamList.get("blue").add(captains[0]); + } + + if (sortedPlayers.size() > 2){ + String captainAnnouncement = Config.pkup_go_pub_captains; + captainAnnouncement = captainAnnouncement.replace(".captain1.", captains[0].getDiscordUser().getMentionString()); + captainAnnouncement = captainAnnouncement.replace(".captain2.", captains[1].getDiscordUser().getMentionString()); + // List players stats + for (Player p : sortedPlayers){ + captainAnnouncement += "\n" + logic.cmdGetElo(p, gametype); + if (logic.getDynamicServers()){ + captainAnnouncement += " " + String.valueOf("\t " + server.playerPing.get(p)) + "ms"; + } + } + if (logic.getDynamicServers()){ + captainAnnouncement +="\n**Server:** " + Country.getCountryFlag(server.country) + "``" + server.city + "``"; + } + else{ + captainAnnouncement +="\n**Server:** " + server.getRegionFlag(false, false) + "``" + server.region.name() + "``"; + } + logic.bot.sendMsg(threadChannels, captainAnnouncement); + + String captainDm = Config.pkup_go_captains; + logic.bot.sendMsg(captains[0].getDiscordUser(), captainDm); + logic.bot.sendMsg(captains[1].getDiscordUser(), captainDm); + } + + sortedPlayers.remove(0); + if (gametype.getTeamSize() != 0){ + sortedPlayers.remove(0); + } + + checkTeams(); + /* + + if (true) { // WIP + // 5: i=0,1 => red: (0, 9) (2, 7) -> blue: (1, 8) (3, 6) if cond: (4, 5) + // 4: i=0,1 => red: (0, 7) (2, 5) -> blue: (1, 7) (3, 4) if cond: none + // 3: i=0 => red: (0, 5) -> blue: (1, 4) if cond: (2, 3) + // 2: i=0 => red: (0, 3) -> blue: (1, 2) if cond: none + // 1: i=- => red: none -> blue: none if cond: (0, 1) + for (int i = 0; i < Math.floor(gametype.getTeamSize() / 2); ++i) { + teamList.get("red").add(sortPlayers.get(i*2)); + teamList.get("red").add(sortPlayers.get(((gametype.getTeamSize()*2)-1)-i*2)); + + teamList.get("blue").add(sortPlayers.get(i*2+1)); + teamList.get("blue").add(sortPlayers.get(((gametype.getTeamSize()*2)-1)-i*2-1)); + } + + if ((gametype.getTeamSize() % 2) == 1) { + int better = gametype.getTeamSize() - 1; + int worse = gametype.getTeamSize(); + + // compute avg elo up till now + elo = new int[] {0, 0}; + for (Player p : teamList.get("red")) elo[0] += p.getElo(); + for (Player p : teamList.get("blue")) elo[1] += p.getElo(); + elo[0] /= Math.max(1, gametype.getTeamSize() - 1); + elo[1] /= Math.max(1, gametype.getTeamSize() - 1); + + if (elo[0] > elo[1]) { + teamList.get("red").add(sortPlayers.get(worse)); + teamList.get("blue").add(sortPlayers.get(better)); + } else { + teamList.get("red").add(sortPlayers.get(better)); + teamList.get("blue").add(sortPlayers.get(worse)); + } + } - logic.cmdStatus(); + logic.matchStarted(this); // do important changes that affect possibly other matches/servers/playerlists outside the thread! new Thread(this).start(); } + */ } + public void checkTeams() { + if (sortedPlayers.isEmpty() && state == MatchState.AwaitingServer) { + state = MatchState.Live; + new Thread(this).start(); // do important changes that affect possibly other matches/servers/playerlists outside the thread! + } + else { + List buttons = new ArrayList(); + int choiceNumber = 10; + if (sortedPlayers.size() < choiceNumber) { + choiceNumber = sortedPlayers.size(); + } + for (int i = 0; i < choiceNumber; i++) { + DiscordButton button = new DiscordButton(DiscordButtonStyle.BLURPLE); + button.custom_id = Config.INT_PICK + "_" + i; + button.label = sortedPlayers.get(i).getUrtauth() + " (" + sortedPlayers.get(i).getElo() + ")"; + button.emoji = sortedPlayers.get(i).getRank().getEmojiJSON(); + buttons.add(button); + } + + // Include in the choices players that played less than 10 games to allow for new player skill uncertainty + if (choiceNumber < sortedPlayers.size()) { + for (int i = choiceNumber; i < sortedPlayers.size(); i++) { + int matchPlayed = logic.db.getNumberOfGames(sortedPlayers.get(i)); + if (matchPlayed < 30) { + DiscordButton button = new DiscordButton(DiscordButtonStyle.GREY); + button.custom_id = Config.INT_PICK + "_" + i; + button.label = sortedPlayers.get(i).getUrtauth(); + button.emoji = new JSONObject().put("name", "\u2753"); + buttons.add(button); + } + } + } + + String pickPromptMsg = Config.pkup_go_pub_pick; + pickPromptMsg = pickPromptMsg.replace(".captain.", captains[captainTurn].getDiscordUser().getMentionString()); + pickMessages = logic.bot.sendMsgToEdit(threadChannels, pickPromptMsg, null, buttons); + } + } + + public boolean isCaptainTurn(Player player) { + if (captains[captainTurn] == null){ + return false; + } + return captains[captainTurn].getUrtauth().equals(player.getUrtauth()); + } + + public Player getCaptainsTurn() { + return captains[captainTurn]; + } + + public void pick(Player captain, int pick) { + String pickMsg = Config.pkup_go_pub_pickjoin; + pickMsg = pickMsg.replace(".pick.", sortedPlayers.get(pick).getDiscordUser().getMentionString()); + + if (captain.getUrtauth().equals(captains[0].getUrtauth())) { + teamList.get("red").add(sortedPlayers.get(pick)); + pickMsg = pickMsg.replace(".color.", "red"); + } + else { + teamList.get("blue").add(sortedPlayers.get(pick)); + pickMsg = pickMsg.replace(".color.", "blue"); + } + + sortedPlayers.remove(pick); + logic.bot.sendMsg(threadChannels, pickMsg); + + if (!sortedPlayers.isEmpty()){ + pickRound++; + captainTurn = pickSequence[pickRound]; + } + + timeLastPick = System.currentTimeMillis(); + + for (DiscordMessage pickMessage : pickMessages){ + pickMessage.delete(); + } + pickMessages.clear(); + + + if (sortedPlayers.size() == 1) { + pick(captains[captainTurn], 0); + return; + } + checkTeams(); + } + + public long getTimeLastPick() { + return timeLastPick; + } + + public boolean getPickReminderSent() { + return pickReminderSent; + } + + public void setPickReminderSent(boolean reminderSent) { + pickReminderSent = reminderSent; + } + // DONT CALL THIS OUTSIDE OF launch() !!! - public void run() { + public void run() { startTime = System.currentTimeMillis(); - + gtvServer = logic.setupGTV(); Random rand = new Random(); - int password = rand.nextInt((999999-100000) + 1) + 100000; - server.password = String.valueOf(password); - LOGGER.info("Password: " + server.password); + + if (!logic.getDynamicServers() && gametype.getTeamSize() > 0){ + int password = rand.nextInt((999999-100000) + 1) + 100000; + server.password = String.valueOf(password); + LOGGER.info("Password: " + server.password); + } // Get most voted map List mapList = getMostMapVotes(); @@ -368,151 +725,167 @@ public void run() { this.map = mapList.size() == 1 ? mapList.get(0) : mapList.get(rand.nextInt(mapList.size()-1)); LOGGER.info("Map: " + this.map.name); - // Sort players by elo - List playerList = new ArrayList(); - for (Player p : playerStats.keySet()) { - playerList.add(p); - } - List sortPlayers = new ArrayList(); - sortPlayers.add(playerList.get(0)); - for (Player player : playerList) { - for (Player sortplayer : sortPlayers) { - if (player.equals(sortplayer)) continue; - else if (player.getElo() >= sortplayer.getElo()) { - sortPlayers.add(sortPlayers.indexOf(sortplayer), player); - break; - } - } - if (!sortPlayers.contains(player)) { - sortPlayers.add(player); - } - } - - // 5: i=0,1 => red: (0, 9) (2, 7) -> blue: (1, 8) (3, 6) if cond: (4, 5) - // 4: i=0,1 => red: (0, 7) (2, 5) -> blue: (1, 7) (3, 4) if cond: none - // 3: i=0 => red: (0, 5) -> blue: (1, 4) if cond: (2, 3) - // 2: i=0 => red: (0, 3) -> blue: (1, 2) if cond: none - // 1: i=- => red: none -> blue: none if cond: (0, 1) - for (int i = 0; i < Math.floor(gametype.getTeamSize() / 2); ++i) { - teamList.get("red").add(sortPlayers.get(i*2)); - teamList.get("red").add(sortPlayers.get(((gametype.getTeamSize()*2)-1)-i*2)); - - teamList.get("blue").add(sortPlayers.get(i*2+1)); - teamList.get("blue").add(sortPlayers.get(((gametype.getTeamSize()*2)-1)-i*2-1)); - } - - if ((gametype.getTeamSize() % 2) == 1) { - int better = gametype.getTeamSize() - 1; - int worse = gametype.getTeamSize(); - - // compute avg elo up till now - elo = new int[] {0, 0}; + // avg elo + elo = new int[] {0, 0}; + if (gametype.getTeamSize() != 0){ for (Player p : teamList.get("red")) elo[0] += p.getElo(); for (Player p : teamList.get("blue")) elo[1] += p.getElo(); - elo[0] /= Math.max(1, gametype.getTeamSize() - 1); - elo[1] /= Math.max(1, gametype.getTeamSize() - 1); - - if (elo[0] > elo[1]) { - teamList.get("red").add(sortPlayers.get(worse)); - teamList.get("blue").add(sortPlayers.get(better)); - } else { - teamList.get("red").add(sortPlayers.get(better)); - teamList.get("blue").add(sortPlayers.get(worse)); - } + elo[0] /= gametype.getTeamSize(); + elo[1] /= gametype.getTeamSize(); } - - // avg elo - elo = new int[] {0, 0}; - for (Player p : teamList.get("red")) elo[0] += p.getElo(); - for (Player p : teamList.get("blue")) elo[1] += p.getElo(); - elo[0] /= gametype.getTeamSize(); - elo[1] /= gametype.getTeamSize(); LOGGER.info("Team Red: " + elo[0] + " " + Arrays.toString(teamList.get("red").toArray())); LOGGER.info("Team Blue: " + elo[1] + " " + Arrays.toString(teamList.get("blue").toArray())); - + id = logic.db.createMatch(this); - + server.matchid = id; + // MESSAGE HYPE - - -// String msg = Config.pkup_go_admin.replace(".elored.", String.valueOf(elo[0])); -// msg = msg.replace(".eloblue.", String.valueOf(elo[1])); -// msg = msg.replace(".gamenumber.", String.valueOf(id)); -// msg = msg.replace(".password.", server.password); -// msg = msg.replace(".map.", this.map.name); -// logic.bot.sendMsg(logic.bot.adminchan, msg); - - String fullmsg = ""; - + String msg = Config.pkup_go_pub_head; - msg = msg.replace(".elo.", String.valueOf((elo[0] + elo[1])/2)); msg = msg.replace(".gamenumber.", String.valueOf(id)); msg = msg.replace(".gametype.", gametype.getName()); - fullmsg = msg; - + msg = msg.replace(".elo.", String.valueOf((elo[0] + elo[1])/2)); + if (logic.getDynamicServers() || gametype.getTeamSize() == 0){ + msg = msg.replace(".region.", Country.getCountryFlag(server.country) + " ``" + server.city + "``"); + } else if (server.region == Region.NAE || server.region == Region.NAW) { + msg = msg.replace(".region.", ":flag_us:"); + } else if (server.region == Region.EU) { + msg = msg.replace(".region.", ":flag_eu:"); + } else if (server.region == Region.OC) { + msg = msg.replace(".region.", ":flag_au:"); + } else if (server.region == Region.SA) { + msg = msg.replace(".region.", ":flag_br:"); + } else { + msg = msg.replace(".region.", server.region.name()); + } + StringBuilder fullmsg = new StringBuilder(msg); + msg = Config.pkup_map_list; msg = msg.replace(".gametype.", gametype.getName()); if (getMostMapVotes().size() == mapVotes.keySet().size()) { - msg = msg.replace(".maplist.", "NO VOTES - RANDOM!"); + msg = msg.replace(".maplist.", "NO VOTES - RANDOM"); } else { msg = msg.replace(".maplist.", getMapVotes(true)); } - fullmsg += "\n" + msg; - + fullmsg.append("\n").append(msg); + String[] teamname = {"Red", "Blue"}; + if (gametype.getTeamSize() == 0){ + teamname = new String[]{"Blue"}; + } for (String team : teamname) { - String playernames = ""; + StringBuilder playernames = new StringBuilder(); for (Player p : teamList.get(team.toLowerCase())) { - if (!playernames.equals("")) { - playernames += " "; + if (!playernames.toString().equals("")) { + playernames.append(" "); } - playernames += p.getDiscordUser().getMentionString(); + playernames.append(p.getDiscordUser().getMentionString()); } msg = Config.pkup_go_pub_team; - msg = msg.replace(".team.", team); + msg = msg.replace(".team.", team.equals("Red") ? "<:rush_red:510982162263179275> Red: " : "<:rush_blue:510067909628788736> Blue:"); msg = msg.replace(".gametype.", gametype.getName()); - msg = msg.replace(".playerlist.", playernames); - fullmsg += "\n" + msg; + msg = msg.replace(".playerlist.", playernames.toString()); + fullmsg.append("\n").append(msg); } - + msg = Config.pkup_go_pub_map; msg = msg.replace(".map.", this.map.name); msg = msg.replace(".gametype.", gametype.getName()); - fullmsg += "\n" + msg; + fullmsg.append("\n").append(msg); + msg = Config.pkup_go_pub_calm; - msg = msg.replace(".gametype.", gametype.getName()); - fullmsg += "\n" + msg; - - logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), fullmsg); + if (gtvServer == null) { + msg = Config.pkup_go_pub_calm_notavi; + } + //msg = msg.replace(".elo.", String.valueOf((elo[0] + elo[1])/2)); + fullmsg.append("\n").append(msg); + + ArrayList buttons = null; + if (gametype.getTeamSize() > 1){ + computeOdds(); + + buttons = new ArrayList(); + JSONObject emojiRed = new JSONObject(); + emojiRed.put("name", "helmet_red"); + emojiRed.put("id", "900477396237549620"); + + JSONObject emojiBlue = new JSONObject(); + emojiBlue.put("name", "helmet_blue"); + emojiBlue.put("id", "900477396573110282"); + + DiscordButton buttonBetRed = new DiscordButton(DiscordButtonStyle.GREY); + buttonBetRed.emoji = emojiRed; + buttonBetRed.label = "Bet red (" + String.format("%.02f", odds[0]) + ")"; + buttonBetRed.custom_id = "showbet_red_" + id; + + DiscordButton buttonBetBlue = new DiscordButton(DiscordButtonStyle.GREY); + buttonBetBlue.emoji = emojiBlue; + buttonBetBlue.label = "Bet blue (" + String.format("%.02f", odds[1]) + ")"; + buttonBetBlue.custom_id = "showbet_blue_" + id; + + buttons.add(buttonBetRed); + buttons.add(buttonBetBlue); + } + + logic.bot.sendMsgToEdit(logic.getChannelByType(PickupChannelType.PUBLIC), fullmsg.toString(), null, buttons); + + if (logic.getDynamicServers() || gametype.getTeamSize() == 0){ + FtwglAPI.getSpawnedServerIp(server); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + } + + buttons = new ArrayList(); + DiscordButton button = new DiscordButton(DiscordButtonStyle.GREEN); + button.custom_id = Config.INT_LAUNCHAC + "_" + String.valueOf(id) + "_" + server.getAddress() + "_" + server.password; + button.label = Config.BTN_LAUNCHAC; + buttons.add(button); msg = Config.pkup_go_player; msg = msg.replace(".server.", server.getAddress()); msg = msg.replace(".password.", server.password); for (String team : teamList.keySet()) { for (Player player : teamList.get(team)) { + if (player.getEnforceAC()){ + logic.bot.sendMsgToEdit(player.getDiscordUser().getDMChannel(), Config.pkup_go_player_ac, null, buttons); + continue; + } String msg_t = msg.replace(".team.", team.toUpperCase()); logic.bot.sendMsg(player.getDiscordUser(), msg_t); } } - + msg = Config.pkup_go_pub_sent; msg = msg.replace(".gametype.", gametype.getName()); - logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), msg); + logic.bot.sendMsgToEdit(logic.getChannelByType(PickupChannelType.PUBLIC), msg, null, buttons); // set server data + server.sendRcon("kick allbots"); + server.sendRcon("g_password " + server.password); for (String s : this.gametype.getConfig()) { server.sendRcon(s); } - server.sendRcon("g_password " + server.password); server.sendRcon("map " + this.map.name); server.sendRcon("g_warmup 10"); - server.startMonitoring(this);; + //logic.setLastMapPlayed(gametype, map); + + if (gtvServer != null) { + gtvServer.sendRcon("gtv_connect " + server.getAddress() + " " + server.password); + } + + server.startMonitoring(this); + + liveScoreMsgs = logic.bot.sendMsgToEdit(threadChannels, "", getMatchEmbed(false), null); } - + List getMostMapVotes() { List mapList = new ArrayList(); int currentVotes = -1; @@ -527,45 +900,55 @@ List getMostMapVotes() { mapList.clear(); mapList.add(map); currentVotes = mapVotes.get(map); - } else if (mapVotes.get(map) == currentVotes) { + } else if (mapVotes.get(map) == currentVotes && !map.equals(logic.getLastMapPlayed(gametype)) && map.bannedUntil < System.currentTimeMillis()) { mapList.add(map); } } return mapList; } - + public String getMapVotes(boolean skipNull) { List mostMapVotes = getMostMapVotes(); - String msg = "None"; + StringBuilder msg = new StringBuilder("None"); for (GameMap map : mapVotes.keySet()) { - if (skipNull && mapVotes.get(map) == 0) continue; - if (msg.equals("None")) { - msg = ""; + if (skipNull && mapVotes.get(map) == 0 && !logic.getLastMapPlayed(gametype).name.equals(map.name) && map.bannedUntil < System.currentTimeMillis()) continue; + if (msg.toString().equals("None")) { + msg = new StringBuilder(); } else { - msg += " - "; + msg.append(" - "); } - String mapString = map.name + ": " + String.valueOf(mapVotes.get(map)); - if (mostMapVotes.size() < mapVotes.keySet().size() && mostMapVotes.contains(map)) { - mapString = "**" + mapString + "**"; + LOGGER.info(logic.getLastMapPlayed(gametype).name + " " + map.name); + if (logic.getLastMapPlayed(gametype).name.equals(map.name)) { + String mapString = "~~" + map.name + "~~"; + msg.append(mapString); + } + else if (map.bannedUntil >= System.currentTimeMillis()){ + String mapString = "~~" + map.name + "~~ (Expires )"; + msg.append(mapString); + } else { + String mapString = map.name + ": " + mapVotes.get(map); + if (mostMapVotes.size() < mapVotes.keySet().size() && mostMapVotes.contains(map)) { + mapString = "**" + mapString + "**"; + } + msg.append(mapString); } - msg += mapString; } - return msg; + return msg.toString(); } - + public boolean isOver() { - return state == MatchState.Done || state == MatchState.Abort || state == MatchState.Abandon || state == MatchState.Surrender; + return state == MatchState.Done || state == MatchState.Abort || state == MatchState.Abandon || state == MatchState.Surrender || state == MatchState.Mercy; } - + public MatchState getMatchState() { return state; } - + public Gametype getGametype() { return gametype; } - + public boolean isInMatch(Player player) { return playerStats.containsKey(player); } @@ -578,10 +961,6 @@ public int getID() { return id; } - public void setID(int id) { - this.id = id; - } - public void setLogic(PickupLogic logic) { this.logic = logic; } @@ -601,7 +980,7 @@ public long getStartTime() { public GameMap getMap() { return map; } - + public int[] getElo() { return elo; } @@ -613,25 +992,19 @@ public int getEloRed() { public int getEloBlue() { return elo[1]; } - + public void setScore(int[] score) { this.score = score; } - public List getTeamRed() { - return teamList.get("red"); - } + public List getTeamRed() { return teamList.get("red"); } public List getTeamBlue() { return teamList.get("blue"); } public List getPlayerList() { - List list = new ArrayList(); - for (Player p : playerStats.keySet()) { - list.add(p); - } - return list; + return new ArrayList(playerStats.keySet()); } public String getTeam(Player player) { @@ -646,7 +1019,7 @@ public String getTeam(Player player) { public int getScoreRed() { return score[0]; } - + public int getScoreBlue() { return score[1]; } @@ -658,10 +1031,12 @@ public MatchStats getStats(Player player) { public int getPlayerCount() { return playerStats.keySet().size(); } - + public String getIngameInfo() { String info; - if (state == MatchState.Live) { + if (state == MatchState.AwaitingServer || server.getServerMonitor() == null) { + info = "Captains's pick"; + } else if (state == MatchState.Live) { ServerState serverState = server.getServerMonitor().getState(); info = serverState.name(); if (serverState == ServerState.LIVE) { @@ -675,39 +1050,165 @@ public String getIngameInfo() { } return info; } - + public String getMatchInfo() { - String redplayers = "None"; + StringBuilder redplayers = new StringBuilder("None"); for (Player p : teamList.get("red")) { - if (redplayers.equals("None")) { - redplayers = p.getUrtauth(); + if (redplayers.toString().equals("None")) { + redplayers = new StringBuilder(p.getUrtauth()); } else { - redplayers += " " + p.getUrtauth(); + redplayers.append(" ").append(p.getUrtauth()); } } - String blueplayers = "None"; + StringBuilder blueplayers = new StringBuilder("None"); for (Player p : teamList.get("blue")) { - if (blueplayers.equals("None")) { - blueplayers = p.getUrtauth(); + if (blueplayers.toString().equals("None")) { + blueplayers = new StringBuilder(p.getUrtauth()); } else { - blueplayers += " " + p.getUrtauth(); + blueplayers.append(" ").append(p.getUrtauth()); } } String msg = Config.pkup_match_print_info; - msg = msg.replace(".gamenumber.", String.valueOf(id)); + msg = msg.replace(".gamenumber.", id == 0 ? String.valueOf(logic.db.getLastMatchID() + 1) : String.valueOf(id)); msg = msg.replace(".gametype.", gametype.getName()); - msg = msg.replace(".map.", map != null ? map.name : "null"); - msg = msg.replace(".redteam.", redplayers); - msg = msg.replace(".blueteam.", blueplayers); - - + msg = msg.replace(".map.", map != null ? map.name : "ut4_?"); + msg = msg.replace(".redteam.", redplayers.toString()); + msg = msg.replace(".blueteam.", blueplayers.toString()); msg = msg.replace(".ingame.", getIngameInfo()); - + return msg; } + public DiscordEmbed getMatchEmbed(boolean forceNoDynamic) { + ServerState serverState = null; + if (server != null && server.isTaken()) { + serverState = server.getServerMonitor().getState(); + } + + DiscordEmbed embed = new DiscordEmbed(); + + String region_flag = ":globe_with_meridians:"; + if (server != null) { + region_flag = server.getRegionFlag(logic.getDynamicServers() || gametype.getTeamSize() == 0, forceNoDynamic); + } + + if (serverState == ServerState.LIVE && state == MatchState.Live && server != null) { + embed.title = region_flag + " Match #" + id + " (" + server.getServerMonitor().getGameTime() + ")"; + } + else { + embed.title = region_flag + " Match #" + id ; + } + + embed.color = 7056881; + embed.description = map != null ? "**" + gametype.getName() + "** - [" + map.name + "](https://maps.pugbot.net/q3ut4/" + map.name + ".pk3)" : "null"; + + StringBuilder red_team_player_embed = new StringBuilder(); + StringBuilder red_team_score_embed = new StringBuilder(); + StringBuilder red_team_ping_embed = new StringBuilder(); + StringBuilder blue_team_player_embed = new StringBuilder(); + StringBuilder blue_team_score_embed = new StringBuilder(); + StringBuilder blue_team_ping_embed = new StringBuilder(); + + // Order teams scores by score + List> entries = new ArrayList>(playerStats.entrySet()); + entries.sort((a, b) -> Integer.compare( + b.getValue().score[0].score + b.getValue().score[1].score, + a.getValue().score[0].score + a.getValue().score[1].score)); + + for (Map.Entry entry : entries) { + String country; + if( entry.getKey().getCountry().equalsIgnoreCase("NOT_DEFINED")) { + country = "<:puma:849287183474884628>"; + } + else { + country = ":flag_" + entry.getKey().getCountry().toLowerCase() + ":"; + } + String player_row = country + " \u200b \u200b " + entry.getKey().getUrtauth() + "\n"; + int score = entry.getValue().score[0].score + entry.getValue().score[1].score; + int deaths = entry.getValue().score[0].deaths + entry.getValue().score[1].deaths; + int assists = entry.getValue().score[0].assists + entry.getValue().score[1].assists; + String score_row = score + "/" + deaths + "/" + assists + "\n"; + + String ping_row = ""; + if ((logic.getDynamicServers() || gametype.getTeamSize() == 0) && !forceNoDynamic){ + ping_row = String.valueOf(server.playerPing.get(entry.getKey())) + "\n"; + } + if (gametype.getTeamSize() != 0 && teamList.get("red").contains(entry.getKey())) { + red_team_player_embed.append(player_row); + red_team_score_embed.append(score_row); + if ((logic.getDynamicServers() || gametype.getTeamSize() == 0) && !forceNoDynamic){ + red_team_ping_embed.append(ping_row); + } + } + else if (teamList.get("blue").contains(entry.getKey())) { + blue_team_player_embed.append(player_row); + blue_team_score_embed.append(score_row); + if ((logic.getDynamicServers() || gametype.getTeamSize() == 0) && !forceNoDynamic){ + blue_team_ping_embed.append(ping_row); + } + } + } + if (gametype.getTeamSize() != 0) { + + embed.addField("<:rush_red:510982162263179275> \u200b \u200b " + getScoreRed() + "\n \u200b", red_team_player_embed.toString(), true); + embed.addField("K/D/A" + "\n \u200b", red_team_score_embed.toString(), true); + if (logic.getDynamicServers() || gametype.getTeamSize() == 0) { + embed.addField("Ping (ms)" + "\n \u200b", red_team_ping_embed.toString(), true); + } + embed.addField("\u200b", "\u200b", false); + } + + embed.addField("<:rush_blue:510067909628788736> \u200b \u200b " + getScoreBlue() + "\n \u200b", blue_team_player_embed.toString(), true); + embed.addField("K/D/A" + "\n \u200b", blue_team_score_embed.toString(), true); + if (logic.getDynamicServers() || gametype.getTeamSize() == 0){ + embed.addField("Ping (ms)" + "\n \u200b", blue_team_ping_embed.toString(), true); + } + + Date startDate = new Date(startTime); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + embed.timestamp = df.format(startDate); + embed.footer = state.name(); + + return embed; + } + + public Region getPreferredServerRegion() { + float euPlayers = 0.0f; + float ocPlayers = 0.0f; + float saPlayers = 0.0f; + + for(Player p : playerStats.keySet()) { + if (p.getRegion() == Region.EU){ + euPlayers++; + } + else if (p.getRegion() == Region.OC){ + ocPlayers++; + } + else if (p.getRegion() == Region.SA){ + saPlayers++; + } + } + float regionScore = euPlayers - gametype.getTeamSize() * ocPlayers; + + if (ocPlayers > gametype.getTeamSize() * 2 * 0.7){ + return Region.OC; + } + else if (saPlayers > gametype.getTeamSize() * 2 * 0.7){ + return Region.SA; + } + else if (euPlayers > gametype.getTeamSize() * 2 * 0.7 || (euPlayers > gametype.getTeamSize() * 2 * 0.6 && (saPlayers + ocPlayers) < 2)){ + return Region.EU; + } + else if (regionScore < 0){ + return Region.NAW; + } + else { + return Region.NAE; + } + } + @Override public String toString() { String msg = ""; @@ -716,18 +1217,19 @@ public String toString() { case AwaitingServer: msg = Config.pkup_match_print_server; break; case Live: msg = Config.pkup_match_print_live; break; case Done: msg = Config.pkup_match_print_done; break; + case Mercy: msg = Config.pkup_match_print_done; break; case Abort: msg = Config.pkup_match_print_abort; break; case Abandon: msg = Config.pkup_match_print_abandon; break; case Surrender: msg = Config.pkup_match_print_sur; break; default: break; } - String playernames = "None"; + StringBuilder playernames = new StringBuilder("None"); for (Player p : playerStats.keySet()) { - if (playernames.equals("None")) { - playernames = p.getDiscordUser().getMentionString(); + if (playernames.toString().equals("None")) { + playernames = new StringBuilder(p.getDiscordUser().getMentionString()); } else { - playernames += " " + p.getDiscordUser().getMentionString(); + playernames.append(" ").append(p.getDiscordUser().getMentionString()); } } @@ -737,9 +1239,126 @@ public String toString() { msg = msg.replace(".elored.", String.valueOf(elo[0])); msg = msg.replace(".eloblue.", String.valueOf(elo[1])); msg = msg.replace(".playernumber.", String.valueOf(getPlayerCount())); - msg = msg.replace(".maxplayer.", String.valueOf(gametype.getTeamSize() * 2)); - msg = msg.replace(".playerlist.", playernames); - msg = msg.replace(".score.", String.valueOf(score[0]) + " " + String.valueOf(score[1])); + int maxplayer = gametype.getTeamSize() == 0 ? 1 : gametype.getTeamSize() * 2; + msg = msg.replace(".maxplayer.", String.valueOf(maxplayer)); + msg = msg.replace(".playerlist.", playernames.toString()); + msg = msg.replace(".score.", score[0] + " " + score[1]); return msg; } + + public void updateScoreEmbed() { + if (state == MatchState.Live) { + score = server.getServerMonitor().getScoreArray(); + } + for (DiscordMessage liveScoreMsg : liveScoreMsgs) { + liveScoreMsg.edit(null, getMatchEmbed(false)); + } + } + + public Server getGtvServer() { + return gtvServer; + } + + public boolean hasSquads(){ + return squadList.size() > 0; + } + + public void payPlayers(){ + + String winningTeam = ""; + int redPay = payLose; + int bluePay = payLose; + if (score[0] > score[1]){ + winningTeam = "red"; + redPay = payWin; + } + else if (score[1] > score[0]){ + winningTeam = "blue"; + bluePay = payWin; + } + + // If game result was corrupted and created a draw + if (winningTeam.equals("")){ + refundBets(); + return; + } + + for (Player redP : teamList.get("red")){ + redP.addCoins(redPay); + redP.saveWallet(); + } + for (Player blueP : teamList.get("blue")){ + blueP.addCoins(bluePay); + blueP.saveWallet(); + } + + String betMsg = ""; + for (Bet bet : bets){ + bet.enterResult(bet.color.equals(winningTeam)); + if (bet.won){ + int wonAmount = Math.round(bet.amount * bet.odds); + JSONObject emoji = Bet.getCoinEmoji(wonAmount); + String msg = Config.bets_won; + msg = msg.replace(".player.", bet.player.getDiscordUser().getMentionString()); + msg = msg.replace(".amount.", String.format("%,d", wonAmount)); + msg = msg.replace(".emojiname.", emoji.getString("name")); + msg = msg.replace(".emojiid.", emoji.getString("id")); + betMsg = betMsg + msg + '\n'; + } + } + if (!betMsg.equals("")){ + logic.bot.sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), betMsg); + } + bets.clear(); + } + + public boolean acceptBets(){ + if (gametype.getTeamSize() <= 1){ + return false; + } + if (state != MatchState.Live && state != MatchState.AwaitingServer){ + return false; + } + return server == null + || server.getServerMonitor() == null + || server.getServerMonitor().getState() == ServerState.WELCOME + || server.getServerMonitor().getState() == ServerState.WARMUP; + } + + private void computeOdds(){ + float scoreRed = 0.0f; + for (Player redP : teamList.get("red")){ + scoreRed += redP.getCaptainScore(gametype); + } + scoreRed /= gametype.getTeamSize(); + + float scoreBlue = 0.0f; + for (Player blueP : teamList.get("blue")){ + scoreBlue += blueP.getCaptainScore(gametype); + } + scoreBlue /= gametype.getTeamSize(); + + float scoreAvg = (scoreBlue + scoreRed) / 2.0f; + + odds[0] = (float) Math.pow(10.0f, - (scoreRed - scoreBlue) / scoreAvg) + 1.0f; + odds[1] = (float) Math.pow(10.0f, - (scoreBlue - scoreRed) / scoreAvg) + 1.0f; + } + + public float getOdds(int team){ + return odds[team]; + } + + public void refundBets(){ + for (Bet bet : bets){ + bet.refund(this); + bet.player.saveWallet(); + } + bets.clear(); + } + + public void banMap(GameMap map){ + if (getMapList().contains(map)){ + mapVotes.put(map, 0); + } + } } diff --git a/src/de/gost0r/pickupbot/pickup/MatchState.java b/src/de/gost0r/pickupbot/pickup/MatchState.java index 26f88fb..59cfc63 100644 --- a/src/de/gost0r/pickupbot/pickup/MatchState.java +++ b/src/de/gost0r/pickupbot/pickup/MatchState.java @@ -7,5 +7,6 @@ public enum MatchState { Done, Abort, Abandon, - Surrender + Surrender, + Mercy } diff --git a/src/de/gost0r/pickupbot/pickup/MatchStats.java b/src/de/gost0r/pickupbot/pickup/MatchStats.java index 6e0e8f6..119ab85 100644 --- a/src/de/gost0r/pickupbot/pickup/MatchStats.java +++ b/src/de/gost0r/pickupbot/pickup/MatchStats.java @@ -1,25 +1,25 @@ package de.gost0r.pickupbot.pickup; public class MatchStats { - + public enum Status { PLAYING, LEFT, NOSHOW, RAGEQUIT } - + private String ip; private Status status = Status.NOSHOW; - + public Score[] score = new Score[2]; - + public MatchStats() { score = new Score[2]; score [0] = new Score(); score [1] = new Score(); } - + public MatchStats(Score score1, Score score2, String ip, Status status) { this(); score[0] = score1; @@ -27,15 +27,15 @@ public MatchStats(Score score1, Score score2, String ip, Status status) { this.ip = ip; this.status = status; } - + public void updateStatus(Status status) { this.status = status; } - + public Status getStatus() { return status; } - + public void updateIP(String ip) { this.ip = ip; } @@ -43,5 +43,5 @@ public void updateIP(String ip) { public String getIP() { return ip; } - + } diff --git a/src/de/gost0r/pickupbot/pickup/PickupBot.java b/src/de/gost0r/pickupbot/pickup/PickupBot.java index a86a944..1ceb0ba 100644 --- a/src/de/gost0r/pickupbot/pickup/PickupBot.java +++ b/src/de/gost0r/pickupbot/pickup/PickupBot.java @@ -1,76 +1,65 @@ package de.gost0r.pickupbot.pickup; +import java.io.IOException; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -import de.gost0r.pickupbot.discord.DiscordBot; -import de.gost0r.pickupbot.discord.DiscordChannel; -import de.gost0r.pickupbot.discord.DiscordChannelType; -import de.gost0r.pickupbot.discord.DiscordMessage; -import de.gost0r.pickupbot.discord.DiscordRole; -import de.gost0r.pickupbot.discord.DiscordUser; +import de.gost0r.pickupbot.discord.*; import de.gost0r.pickupbot.pickup.PlayerBan.BanReason; public class PickupBot extends DiscordBot { - private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); private DiscordChannel latestMessageChannel; - - public PickupLogic logic; - + + public static PickupLogic logic; + public String env; + @Override - public void init() { - super.init(); - -// pubchan = DiscordChannel.findChannel("143233743107129344"); // pickup_dev -// pubchan = DiscordChannel.findChannel("402541587164561419"); // urtpickup - + public void init(String env) { + super.init(env); + this.env = env; + logic = new PickupLogic(this); -// logic.cmdEnableGametype("TEST", "1"); - - // TEST -// Player p = Player.get("v3nd3tta"); -// Gametype gt = logic.getGametypeByString("test"); -// List list = new ArrayList(); -// list.add(gt); -// logic.cmdAddPlayer(p, list); + createApplicationCommands(); + sendMsg(logic.getChannelByType(PickupChannelType.PUBLIC), Config.bot_online); } - + @Override protected void tick() { super.tick(); - + if (logic != null) { logic.afkCheck(); } - - } @Override protected void recvMessage(DiscordMessage msg) { LOGGER.info("RECV #" + ((msg.channel == null || msg.channel.name == null) ? "null" : msg.channel.name) + " " + msg.user.username + ": " + msg.content); - + this.latestMessageChannel = msg.channel; - - if (msg.user.equals(self)) { -// System.out.println("Msg from self, ignore."); + + if (msg.user.equals(self) || logic == null) { return; } - + String[] data = msg.content.split(" "); if (isChannel(PickupChannelType.PUBLIC, msg.channel)) { Player p = Player.get(msg.user); - - // AFK CHECK CODE + if (p != null) { p.afkCheck(); + p.setLastPublicChannel(msg.channel); } - + // Execute code according to cmd switch (data[0].toLowerCase()) { @@ -88,7 +77,7 @@ protected void recvMessage(DiscordMessage msg) { } } if (gametypes.size() > 0) { - logic.cmdAddPlayer(p, gametypes); + logic.cmdAddPlayer(p, gametypes, false); } else { sendNotice(msg.user, Config.no_gt_found); } @@ -97,11 +86,139 @@ protected void recvMessage(DiscordMessage msg) { } else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ADD)); break; - + + case Config.CMD_TS: + if (p != null) + { + Gametype gt = logic.getGametypeByString("TS"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + + if (data.length > 1) { + logic.cmdMapVote(p, gt, data[1], 1); + } + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_CTF: + if (p != null) + { + Gametype gt = logic.getGametypeByString("CTF"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + + if (data.length > 1) { + logic.cmdMapVote(p, gt, data[1], 1); + } + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_BM: + if (p != null) + { + Gametype gt = logic.getGametypeByString("BM"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + + if (data.length > 1) { + logic.cmdMapVote(p, gt, data[1], 1); + } + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_1v1: + if (p != null) + { + Gametype gt = logic.getGametypeByString("1v1"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + + if (data.length > 1) { + logic.cmdMapVote(p, gt, data[1], 1); + } + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_2v2: + if (p != null) + { + Gametype gt = logic.getGametypeByString("2v2"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + + if (data.length > 1) { + logic.cmdMapVote(p, gt, data[1], 1); + } + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_DIV1: + if (p != null) + { + Gametype gt = logic.getGametypeByString("div1"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + + if (data.length > 1) { + logic.cmdMapVote(p, gt, data[1], 1); + } + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_SKEET: + if (p != null) + { + Gametype gt = logic.getGametypeByString("SKEET"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_AIM: + if (p != null) + { + Gametype gt = logic.getGametypeByString("aim"); + if (gt != null) { + logic.cmdAddPlayer(p, gt, false); + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_REMOVE: Player player = p; int startindex = 1; - if (hasAdminRights(msg.user) && data.length > 1) + if (msg.user.hasAdminRights() && data.length > 1) { DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); if (u != null) @@ -133,31 +250,113 @@ protected void recvMessage(DiscordMessage msg) { } } } - logic.cmdRemovePlayer(player, gametypes); + if (player != null){ + logic.cmdRemovePlayer(player, gametypes); + player.setLastPublicChannel(p.getLastPublicChannel()); + } + + break; + + case Config.CMD_FORCEADD: + if (!msg.user.hasAdminRights()) + { + sendNotice(msg.user, Config.player_not_admin); + return; + } + if (data.length >= 3) + { + for (int i = 2; i < data.length; i ++) { + if (data[i].trim().length() == 0) { + continue; + } + DiscordUser u = DiscordUser.getUser(data[i].replaceAll("[^\\d.]", "")); + Player playerToAdd = null; + if (u != null) + { + playerToAdd = Player.get(u); + } + else { + playerToAdd = Player.get(data[i]); + } + if (playerToAdd != null) + { + playerToAdd.setLastPublicChannel(p.getLastPublicChannel()); + gametypes = new ArrayList(); + String[] modes = Arrays.copyOfRange(data, 1, data.length); + for (String mode : modes) { + Gametype gt = logic.getGametypeByString(mode); + if (gt != null) { + gametypes.add(gt); + } + } + if (gametypes.size() > 0) { + for (Team activeTeam : logic.getActiveTeams()){ + if (activeTeam.isInTeam(playerToAdd)){ + logic.cmdAddTeam(playerToAdd, gametypes.get(0), true); + return; + } + } + logic.cmdAddPlayer(playerToAdd, gametypes, true); + } else { + sendNotice(msg.user, Config.no_gt_found); + } + } + else sendNotice(msg.user, Config.other_user_not_registered.replace(".user.", data[i])); + } + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_FORCEADD)); break; - + case Config.CMD_MAPS: case Config.CMD_MAP: if (p != null) { if (data.length == 1) { - logic.cmdGetMaps(); + logic.cmdGetMaps(p, true); } else if (data.length == 2) { - logic.cmdMapVote(p, null, data[1]); + logic.cmdMapVote(p, null, data[1], 1); } else if (data.length == 3) { Gametype gt = logic.getGametypeByString(data[1]); - logic.cmdMapVote(p, gt, data[2]); + logic.cmdMapVote(p, gt, data[2], 1); } else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_MAP)); } else sendNotice(msg.user, Config.user_not_registered); break; - + case Config.CMD_ADDVOTE: + if (p != null) + { + if (data.length == 2) + { + logic.cmdMapVote(p, null, data[1], 0); + } + else if (data.length == 3) + { + Gametype gt = logic.getGametypeByString(data[1]); + logic.cmdMapVote(p, gt, data[2], 0); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ADDVOTE)); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_BANMAP: + if (p != null) + { + if (data.length == 2) + { + logic.cmdUseMapBan(p, data[1]); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_BANMAP)); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_STATUS: if (data.length == 1) { @@ -165,7 +364,15 @@ else if (data.length == 3) } else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_STATUS)); break; - + + case Config.CMD_VOTES: + if (data.length == 1) + { + logic.cmdGetMaps(p, false); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_VOTES)); + break; + case Config.CMD_SURRENDER: if (data.length == 1) { @@ -176,9 +383,9 @@ else if (data.length == 3) } else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_SURRENDER)); break; - + case Config.CMD_RESET: - if (hasAdminRights(msg.user)) + if (msg.user.hasAdminRights()) { if (data.length == 1) { @@ -191,9 +398,9 @@ else if (data.length == 2) else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_RESET)); } break; - + case Config.CMD_LOCK: - if (hasAdminRights(msg.user)) + if (msg.user.hasAdminRights()) { if (data.length == 1) { @@ -202,9 +409,9 @@ else if (data.length == 2) else super.sendMsg(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_LOCK)); } break; - + case Config.CMD_UNLOCK: - if (hasAdminRights(msg.user)) + if (msg.user.hasAdminRights()) { if (data.length == 1) { @@ -213,17 +420,17 @@ else if (data.length == 2) else super.sendMsg(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_UNLOCK)); } break; - + case Config.CMD_GETELO: if (p != null) { if (data.length == 1) { - logic.cmdGetElo(p); + logic.cmdGetStats(p); } else if (data.length == 2) { - Player pOther = null; + Player pOther; DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); if (u != null) { @@ -236,7 +443,7 @@ else if (data.length == 2) if (pOther != null) { - logic.cmdGetElo(pOther); + logic.cmdGetStats(pOther); } else sendNotice(msg.user, Config.player_not_found); } @@ -244,19 +451,98 @@ else if (data.length == 2) } else sendNotice(msg.user, Config.user_not_registered); break; - - case Config.CMD_TOP5: + + case Config.CMD_GETSTATS: + if (p != null) + { + if (data.length == 1) + { + logic.cmdGetStats(p); + } + else if (data.length == 2) + { + Player pOther; + DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); + if (u != null) + { + pOther = Player.get(u); + } + else + { + pOther = Player.get(data[1].toLowerCase()); + } + if (pOther != null) + { + logic.cmdGetStats(pOther); + } + else sendNotice(msg.user, Config.player_not_found); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_GETSTATS)); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_TOP_PLAYERS: + if (p != null) + { + if (data.length == 1) + { + logic.cmdTopElo(10); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_TOP10)); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_TOP_COUNTRIES: if (p != null) { if (data.length == 1) { - logic.cmdTopElo(5); + logic.cmdTopCountries(5); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_TOP10)); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_TOP_WDL: + if (p != null) + { + if (data.length == 2) + { + Gametype gt = logic.getGametypeByString(data[1]); + if (gt != null) { + logic.cmdTopWDL(10, gt); + } + else { + sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_TOP_WDL)); + } } - else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_TOP5)); + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_TOP_WDL)); } else sendNotice(msg.user, Config.user_not_registered); break; + case Config.CMD_TOP: + case Config.CMD_TOP_KDR: + if (p != null) + { + if (data.length == 2) + { + Gametype gt = logic.getGametypeByString(data[1]); + if (gt != null) { + logic.cmdTopKDR(10, gt); + } + else { + sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_TOP_KDR)); + } + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_TOP_KDR)); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_REGISTER: if (data.length == 2) { @@ -264,19 +550,27 @@ else if (data.length == 2) } else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_REGISTER)); break; - + + case Config.CMD_COUNTRY: + if (data.length == 2) + { + logic.cmdSetPlayerCountry(msg.user, data[1].toUpperCase()); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_COUNTRY)); + break; + case Config.CMD_LIVE: if (p != null) { if (data.length == 1) { - logic.cmdLive(); + logic.cmdLive(msg.channel); } else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_LIVE)); } else sendNotice(msg.user, Config.user_not_registered); break; - + case Config.CMD_MATCH: if (p != null) { @@ -288,17 +582,17 @@ else if (data.length == 2) } else sendNotice(msg.user, Config.user_not_registered); break; - - case Config.CMD_BANINFO: + + case Config.CMD_LAST: if (p != null) { if (data.length == 1) { - sendMsg(msg.channel, logic.printBanInfo(p)); + logic.cmdDisplayLastMatch(); } else if (data.length == 2) { - Player pOther = null; + Player pOther; DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); if (u != null) { @@ -311,122 +605,330 @@ else if (data.length == 2) if (pOther != null) { - sendMsg(msg.channel, logic.printBanInfo(pOther)); + logic.cmdDisplayLastMatchPlayer(pOther); } else sendNotice(msg.user, Config.player_not_found); } - else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_BANINFO)); + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_LAST)); } else sendNotice(msg.user, Config.user_not_registered); break; - } - } - // use admin channel or DM for super admins - if (isChannel(PickupChannelType.ADMIN, msg.channel) - || msg.channel.type == DiscordChannelType.DM && hasSuperAdminRights(msg.user)) - { - if (hasAdminRights(msg.user)) - { - // Execute code according to cmd - switch (data[0].toLowerCase()) - { - case Config.CMD_GETDATA: - if (data.length == 2) + case Config.CMD_BANINFO: + if (p != null) + { + if (data.length == 1) { - logic.cmdGetData(msg.user, data[1]); + sendMsg(msg.channel, logic.printBanInfo(p)); } - else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_GETDATA)); - break; - - case Config.CMD_ENABLEMAP: - if (data.length == 3) + else if (data.length == 2) { - if (logic.cmdEnableMap(data[1], data[2])) + Player pOther; + DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); + if (u != null) { - super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); + pOther = Player.get(u); } - else super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); - } - else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENABLEMAP)); - break; - - case Config.CMD_DISABLEMAP: - if (data.length == 3) - { - if (logic.cmdDisableMap(data[1], data[2])) + else { - super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); + pOther = Player.get(data[1].toLowerCase()); } - else super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); - } - else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_DISABLEMAP)); - break; - - case Config.CMD_ENABLEGAMETYPE: - if (data.length == 3) - { - if (logic.cmdEnableGametype(data[1], data[2])) + + if (pOther != null) { - super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); + sendMsg(msg.channel, logic.printBanInfo(pOther)); + } + else sendNotice(msg.user, Config.player_not_found); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_BANINFO)); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + + case Config.CMD_TEAM: + if (p != null) { + if (data.length >= 2) { + List invitedPlayers = new ArrayList(); + boolean noMentions = false; + for (int i = 1; i < data.length; i++) { + if (data[i].trim().length() == 0) { + continue; + } + DiscordUser u = DiscordUser.getUser(data[i].replaceAll("[^\\d.]", "")); + Player playerToInvite = null; + if (u != null) { + playerToInvite = Player.get(u); + } + else { + noMentions = true; + continue; + } + if (playerToInvite != null) { + invitedPlayers.add(playerToInvite); + } + else{ + logic.bot.sendNotice(p.getDiscordUser(), Config.other_user_not_registered.replace(".user.", u.getMentionString())); + } + } + if (noMentions){ + logic.bot.sendNotice(p.getDiscordUser(), Config.team_only_mentions); + } + if (!invitedPlayers.isEmpty()){ + logic.invitePlayersToTeam(p, invitedPlayers); + } + } + else{ + logic.cmdPrintTeam(p); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_LEAVETEAM: + if (p != null){ + logic.leaveTeam(p); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_SCRIM: + if (p != null){ + if (data.length == 2) { + Gametype gt; + if (data[1].equalsIgnoreCase("2V2")){ + gt = logic.getGametypeByString("2V2"); + } + else gt = logic.getGametypeByString( "SCRIM " + data[1]); + + if (gt == null || !gt.isTeamGamemode()) { + sendNotice(msg.user, Config.team_error_wrong_gt); + return; + } + + logic.cmdAddTeam(p, gt, false); + } + else { + sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_SCRIM)); + } + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_REMOVETEAM: + if (p != null){ + logic.cmdRemoveTeam(p, true); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_TEAMS: + if (p != null){ + logic.cmdPrintTeams(p); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_PING: + if (p != null){ + logic.cmdGetPingURL(p); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_TOP_RICH: + if (p != null){ + logic.cmdTopRich(10); + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_WALLET: + if (p != null){ + if (data.length == 1){ + logic.cmdWallet(p); + } + + else if (data.length == 2) + { + Player pOther; + DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); + if (u != null) + { + pOther = Player.get(u); + } + else + { + pOther = Player.get(data[1].toLowerCase()); + } + + if (pOther != null) + { + logic.cmdWallet(pOther); + } + else sendNotice(msg.user, Config.player_not_found); + } + + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_DONATE: + if (p != null){ + if (data.length == 3){ + + Player pOther; + DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); + if (u != null) + { + pOther = Player.get(u); + } + else + { + pOther = Player.get(data[1].toLowerCase()); + } + + if (pOther != null) + { + int amount; + try { + amount = Integer.parseInt(data[2]); + pOther.setLastPublicChannel(msg.channel); + logic.cmdDonate(p, pOther, amount); + } catch (NumberFormatException nfe) { + sendNotice(p.getDiscordUser(), Config.donate_incorrect_amount); + } + } + else sendNotice(msg.user, Config.player_not_found); + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_DONATE)); + + } + else sendNotice(msg.user, Config.user_not_registered); + break; + case Config.CMD_BETHISTORY: + if (p != null){ + if (data.length == 1){ + logic.cmdBetHistory(p); + } + + else if (data.length == 2) + { + Player pOther; + DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); + if (u != null) + { + pOther = Player.get(u); + } + else + { + pOther = Player.get(data[1].toLowerCase()); + } + + if (pOther != null) + { + logic.cmdBetHistory(pOther); + } + else sendNotice(msg.user, Config.player_not_found); + } + + } + else sendNotice(msg.user, Config.user_not_registered); + break; + } + } + + if (msg.channel.isThread) + { + Player p = Player.get(msg.user); + + // AFK CHECK CODE + if (p != null) { + p.afkCheck(); + } + } + + // use admin channel or DM for super admins + if (isChannel(PickupChannelType.ADMIN, msg.channel) + || msg.channel.type == DiscordChannelType.DM && msg.user.hasSuperAdminRights()) + { + if (msg.user.hasAdminRights()) + { + // Execute code according to cmd + switch (data[0].toLowerCase()) + { + case Config.CMD_REBOOT: + try { + super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); + logic.restartApplication(); + } catch (URISyntaxException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + break; + + case Config.CMD_GETDATA: + if (data.length == 1) + { + logic.cmdGetData(msg.channel); + } + else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_GETDATA)); + break; + + case Config.CMD_ENABLEMAP: + if (data.length == 3) + { + if (logic.cmdEnableMap(data[1], data[2])) + { + super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); } else super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); } - else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENABLEGAMETYPE)); + else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENABLEMAP)); break; - - case Config.CMD_DISABLEGAMETYPE: - if (data.length == 2) + + case Config.CMD_DISABLEMAP: + if (data.length == 3) { - if (logic.cmdDisableGametype(data[1])) + if (logic.cmdDisableMap(data[1], data[2])) { super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); } else super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); } - else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENABLEGAMETYPE)); + else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_DISABLEMAP)); break; - - case Config.CMD_ADDGAMECONFIG: - if (data.length >= 3) + + case Config.CMD_ENABLEGAMETYPE: + if (data.length == 3) { - if (logic.cmdAddGameConfig(data[1], msg.content.substring(Config.CMD_ADDGAMECONFIG.length() + data[1].length() + 2))) + if (logic.cmdEnableGametype(data[1], data[2])) { super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); } else super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); } - else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ADDGAMECONFIG)); + else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENABLEGAMETYPE)); break; - - case Config.CMD_REMOVEGAMECONFIG: - if (data.length >= 3) + + case Config.CMD_DISABLEGAMETYPE: + if (data.length == 2) { - if (logic.cmdRemoveGameConfig(data[1], msg.content.substring(Config.CMD_REMOVEGAMECONFIG.length() + data[1].length() + 2))) + if (logic.cmdDisableGametype(data[1])) { super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); } else super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); } - else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_REMOVEGAMECONFIG)); + else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENABLEGAMETYPE)); break; - + case Config.CMD_LISTGAMECONFIG: if (data.length == 2) { - if (logic.cmdListGameConfig(msg.channel, data[1])) + if (!logic.cmdListGameConfig(msg.channel, data[1])) { - // if successful it will print the info + super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); } - else super.sendMsg(msg.channel, Config.admin_cmd_unsuccessful + msg.content); } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_LISTGAMECONFIG)); break; - + case Config.CMD_ADDSERVER: - if (data.length == 3) + if (data.length == 4) { - if (logic.cmdAddServer(data[1], data[2])) + if (logic.cmdAddServer(data[1], data[2], data[3])) { super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); } @@ -434,7 +936,7 @@ else if (data.length == 2) } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ADDSERVER)); break; - + case Config.CMD_ENABLESERVER: if (data.length == 2) { @@ -446,7 +948,7 @@ else if (data.length == 2) } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENABLESERVER)); break; - + case Config.CMD_DISABLESERVER: if (data.length == 2) { @@ -458,7 +960,7 @@ else if (data.length == 2) } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_DISABLESERVER)); break; - + case Config.CMD_UPDATESERVER: if (data.length == 3) { @@ -470,15 +972,16 @@ else if (data.length == 2) } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_UPDATESERVER)); break; - + case Config.CMD_SHOWSERVERS: if (data.length == 1) { + super.sendMsg(msg.channel, Config.wait_testing_server); logic.cmdServerList(msg.channel); } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_SHOWSERVERS)); break; - + case Config.CMD_RCON: if (data.length > 2) { @@ -490,7 +993,7 @@ else if (data.length == 2) } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_RCON)); break; - + case Config.CMD_SHOWMATCHES: if (data.length == 1) { @@ -498,7 +1001,7 @@ else if (data.length == 2) } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_SHOWMATCHES)); break; - + case Config.CMD_UNREGISTER: if (data.length == 2) { @@ -515,11 +1018,28 @@ else if (data.length == 2) } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_UNREGISTER)); break; - + + case Config.CMD_ENFORCEAC: + if (data.length == 2) + { + Player player = Player.get(data[1].toLowerCase()); + if (player != null) + { + if (logic.cmdEnforcePlayerAC(player)) + { + super.sendMsg(msg.channel, Config.admin_enforce_ac_on.replace(".urtauth.", player.getUrtauth())); + } + else super.sendMsg(msg.channel, Config.admin_enforce_ac_off.replace(".urtauth.", player.getUrtauth())); + } + else sendMsg(msg.channel, Config.player_not_found); + } + else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ENFORCEAC)); + break; + case Config.CMD_ADDBAN: if (data.length == 4) { - Player p = null; + Player p; DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); if (u != null) { @@ -552,309 +1072,626 @@ else if (data.length == 2) else sendMsg(msg.channel, Config.player_not_found); } else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ADDBAN)); - break; + break; + + case Config.CMD_REMOVEBAN: + if (data.length == 2) + { + Player p; + DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); + if (u != null) + { + p = Player.get(u); + } + else + { + p = Player.get(data[1].toLowerCase()); + } + + if (p != null) + { + logic.UnbanPlayer(p); + } + else sendMsg(msg.channel, Config.player_not_found); + } + else super.sendMsg(msg.channel, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_ADDBAN)); + break; + + case Config.CMD_COUNTRY: + if (data.length == 3) + { + Player p; + DiscordUser u = DiscordUser.getUser(data[1].replaceAll("[^\\d.]", "")); + if (u != null) + { + p = Player.get(u); + } + else + { + p = Player.get(data[1].toLowerCase()); + } + + if (p != null) + { + logic.cmdChangePlayerCountry(p, data[2].toUpperCase()); + } + else sendMsg(msg.channel, Config.player_not_found); + + } + else sendNotice(msg.user, Config.wrong_argument_amount.replace(".cmd.", Config.USE_CMD_CHANGE_COUNTRY)); + break; + + case Config.CMD_RESETELO: + logic.cmdResetElo(); + sendNotice(msg.user, Config.elo_reset); + break; + + case Config.CMD_ENABLEDYNSERVER: + logic.cmdEnableDynamicServer(); + super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); + break; + + case Config.CMD_DISABLEDYNSERVER: + logic.cmdDisableDynamicServer(); + super.sendMsg(msg.channel, Config.admin_cmd_successful + msg.content); + break; } } } - + if (isChannel(PickupChannelType.PUBLIC, msg.channel) || isChannel(PickupChannelType.ADMIN, msg.channel) || msg.channel.type == DiscordChannelType.DM) { - if (data[0].toLowerCase().equals(Config.CMD_HELP)) + switch (data[0].toLowerCase()) { + case Config.CMD_HELP: + if (data.length == 2) { + String cmd = (!data[1].startsWith("!") ? "!" : "") + data[1]; + switch (cmd) { + case Config.CMD_ADD: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADD)); + break; + case Config.CMD_REMOVE: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVE)); + break; + case Config.CMD_MAPS: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_MAPS)); + break; + case Config.CMD_MAP: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_MAP)); + break; + case Config.CMD_STATUS: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_STATUS)); + break; + case Config.CMD_HELP: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_HELP)); + break; + case Config.CMD_LOCK: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LOCK)); + break; + case Config.CMD_UNLOCK: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_UNLOCK)); + break; + case Config.CMD_RESET: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_RESET)); + break; + case Config.CMD_GETDATA: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_GETDATA)); + break; + case Config.CMD_ENABLEMAP: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ENABLEMAP)); + break; + case Config.CMD_DISABLEMAP: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_DISABLEMAP)); + break; + case Config.CMD_RCON: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_RCON)); + break; + case Config.CMD_REGISTER: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REGISTER)); + break; + case Config.CMD_GETELO: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_GETELO)); + break; + case Config.CMD_GETSTATS: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_GETSTATS)); + break; + case Config.CMD_TOP_PLAYERS: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TOP10)); + break; + case Config.CMD_TOP_COUNTRIES: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TOP_COUNTRIES)); + break; + case Config.CMD_MATCH: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_MATCH)); + break; + case Config.CMD_SURRENDER: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_SURRENDER)); + break; + case Config.CMD_LIVE: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LIVE)); + break; + case Config.CMD_ADDBAN: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADDBAN)); + break; + case Config.CMD_REMOVEBAN: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVEBAN)); + break; + case Config.CMD_BANINFO: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_BANINFO)); + break; + case Config.CMD_SHOWSERVERS: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_SHOWSERVERS)); + break; + case Config.CMD_ADDSERVER: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADDSERVER)); + break; + case Config.CMD_ENABLESERVER: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ENABLESERVER)); + break; + case Config.CMD_DISABLESERVER: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_DISABLESERVER)); + break; + case Config.CMD_UPDATESERVER: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_UPDATESERVER)); + break; + case Config.CMD_ENABLEGAMETYPE: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ENABLEGAMETYPE)); + break; + case Config.CMD_DISABLEGAMETYPE: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_DISABLEGAMETYPE)); + break; + case Config.CMD_ADDCHANNEL: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADDCHANNEL)); + break; + case Config.CMD_REMOVECHANNEL: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVECHANNEL)); + break; + case Config.CMD_ADDROLE: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADDROLE)); + break; + case Config.CMD_REMOVEROLE: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVEROLE)); + break; + case Config.CMD_SHOWMATCHES: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_SHOWMATCHES)); + break; + case Config.CMD_LISTGAMECONFIG: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LISTGAMECONFIG)); + break; + case Config.CMD_COUNTRY: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_COUNTRY)); + break; + case Config.CMD_BM: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_BM)); + break; + case Config.CMD_CTF: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_CTF)); + break; + case Config.CMD_TS: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TS)); + break; + case Config.CMD_DIV1: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_DIV1)); + break; + case Config.CMD_VOTES: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_VOTES)); + break; + case Config.CMD_TOP_WDL: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TOP_WDL)); + break; + case Config.CMD_TOP_KDR: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TOP_KDR)); + break; + case Config.CMD_LAST: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LAST)); + break; + case Config.CMD_SCRIM: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_SCRIM)); + break; + case Config.CMD_REMOVETEAM: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVETEAM)); + break; + case Config.CMD_TEAM: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TEAM)); + break; + case Config.CMD_LEAVETEAM: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LEAVETEAM)); + break; + case Config.CMD_TEAMS: + super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TEAMS)); + break; + + default: + super.sendMsg(msg.channel, Config.help_unknown); + break; + } + } else { + if (isChannel(PickupChannelType.PUBLIC, msg.channel)) { + super.sendMsg(msg.channel, Config.help_cmd_avi.replace(".cmds.", Config.PUB_LIST)); + } + if (msg.user.hasAdminRights() && (isChannel(PickupChannelType.ADMIN, msg.channel) || msg.channel.type == DiscordChannelType.DM)) { + super.sendMsg(msg.channel, Config.help_cmd_avi.replace(".cmds.", Config.ADMIN_LIST)); + } + } + break; + + case Config.CMD_ADDCHANNEL: + if (msg.user.hasSuperAdminRights()) { + if (data.length == 3) { + DiscordChannel targetChannel = DiscordChannel.findChannel(data[1].replaceAll("[^\\d.]", "")); + if (targetChannel != null) { + try { + PickupChannelType type = PickupChannelType.valueOf(data[2].toUpperCase()); + if (!logic.getChannelByType(type).contains(targetChannel)) { + if (logic.addChannel(type, targetChannel)) { + sendNotice(msg.user, "successfully added the channel"); + } else sendNotice(msg.user, "unsuccessfully added the channel"); + } + } catch (IllegalArgumentException e) { + sendNotice(msg.user, "unknown channel type"); + } + } else sendNotice(msg.user, "invalid channel"); + } else sendNotice(msg.user, "invalid options"); + } else sendNotice(msg.user, "You need SuperAdmin rights to use this."); + break; + + case Config.CMD_REMOVECHANNEL: + if (msg.user.hasSuperAdminRights()) { + if (data.length == 3) { + DiscordChannel targetChannel = DiscordChannel.findChannel(data[1].replaceAll("[^\\d.]", "")); + if (targetChannel != null) { + try { + PickupChannelType type = PickupChannelType.valueOf(data[2].toUpperCase()); + if (logic.getChannelByType(type).contains(targetChannel)) { + if (logic.removeChannel(type, targetChannel)) { + sendNotice(msg.user, "successfully removed the channel."); + } else sendNotice(msg.user, "unsuccessfully removed the channel."); + } + + } catch (IllegalArgumentException e) { + sendNotice(msg.user, "unknown role type"); + } + } else sendNotice(msg.user, "invalid channel"); + } else sendNotice(msg.user, "invalid options"); + } else sendNotice(msg.user, "You need SuperAdmin rights to use this."); + break; + + case Config.CMD_ADDROLE: + if (msg.user.hasSuperAdminRights()) { + if (data.length == 3) { + DiscordRole targetRole = DiscordRole.getRole(data[1].replaceAll("[^\\d.]", "")); + if (targetRole != null) { + try { + PickupRoleType type = PickupRoleType.valueOf(data[2].toUpperCase()); + if (!logic.getRoleByType(type).contains(targetRole)) { + if (logic.addRole(type, targetRole)) { + sendNotice(msg.user, "successfully added the role."); + } else sendNotice(msg.user, "unsuccessfully removed the role."); + } + + } catch (IllegalArgumentException e) { + sendNotice(msg.user, "unknown role type"); + } + } else sendNotice(msg.user, "invalid channel"); + } else sendNotice(msg.user, "invalid options"); + } else sendNotice(msg.user, "You need SuperAdmin rights to use this."); + break; + + case Config.CMD_REMOVEROLE: + if (msg.user.hasSuperAdminRights()) { + if (data.length == 3) { + DiscordRole targetRole = DiscordRole.getRole(data[1].replaceAll("[^\\d.]", "")); + if (targetRole != null) { + try { + PickupRoleType type = PickupRoleType.valueOf(data[2].toUpperCase()); + if (logic.getRoleByType(type).contains(targetRole)) { + if (logic.removeRole(type, targetRole)) { + sendNotice(msg.user, "successfully removed the role."); + } else sendNotice(msg.user, "unsuccessfully removed the role."); + } + } catch (IllegalArgumentException e) { + sendNotice(msg.user, "unknown role type"); + } + } else sendNotice(msg.user, "invalid role"); + } else sendNotice(msg.user, "invalid options"); + } else sendNotice(msg.user, "You need SuperAdmin rights to use this."); + break; + } + } + + // Any channel + else + { + if (data[0].equalsIgnoreCase("!showroles")) { + DiscordUser u = msg.user; if (data.length == 2) { - String cmd = (!data[1].startsWith("!") ? "!" : "") + data[1]; - switch (cmd) - { - case Config.CMD_ADD: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADD)); break; - case Config.CMD_REMOVE: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVE)); break; - case Config.CMD_MAPS: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_MAPS)); break; - case Config.CMD_MAP: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_MAP)); break; - case Config.CMD_STATUS: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_STATUS)); break; - case Config.CMD_HELP: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_HELP)); break; - case Config.CMD_LOCK: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LOCK)); break; - case Config.CMD_UNLOCK: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_UNLOCK)); break; - case Config.CMD_RESET: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_RESET)); break; - case Config.CMD_GETDATA: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_GETDATA)); break; - case Config.CMD_ENABLEMAP: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ENABLEMAP)); break; - case Config.CMD_DISABLEMAP: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_DISABLEMAP)); break; - case Config.CMD_RCON: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_RCON)); break; - case Config.CMD_REGISTER: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REGISTER)); break; - case Config.CMD_GETELO: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_GETELO)); break; - case Config.CMD_TOP5: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_TOP5)); break; - case Config.CMD_MATCH: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_MATCH)); break; - case Config.CMD_SURRENDER: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_SURRENDER)); break; - case Config.CMD_LIVE: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LIVE)); break; - //case Config.CMD_REPORT: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REPORT)); break; - //case Config.CMD_EXCUSE: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_EXCUSE)); break; - //case Config.CMD_REPORTLIST: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REPORTLIST)); break; - case Config.CMD_ADDBAN: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADDBAN)); break; - //case Config.CMD_REMOVEBAN: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVEBAN)); break; - case Config.CMD_BANINFO: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_BANINFO)); break; - case Config.CMD_SHOWSERVERS: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_SHOWSERVERS)); break; - case Config.CMD_ADDSERVER: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADDSERVER)); break; - case Config.CMD_ENABLESERVER: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ENABLESERVER)); break; - case Config.CMD_DISABLESERVER: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_DISABLESERVER)); break; - case Config.CMD_UPDATESERVER: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_UPDATESERVER)); break; - case Config.CMD_ENABLEGAMETYPE: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ENABLEGAMETYPE)); break; - case Config.CMD_DISABLEGAMETYPE: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_DISABLEGAMETYPE)); break; - case Config.CMD_SHOWMATCHES: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_SHOWMATCHES)); break; - case Config.CMD_ADDGAMECONFIG: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_ADDGAMECONFIG)); break; - case Config.CMD_REMOVEGAMECONFIG: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_REMOVEGAMECONFIG)); break; - case Config.CMD_LISTGAMECONFIG: super.sendMsg(msg.channel, Config.help_prefix.replace(".cmd.", Config.USE_CMD_LISTGAMECONFIG)); break; - default: super.sendMsg(msg.channel, Config.help_unknown); break; - } - } - else - { - if (isChannel(PickupChannelType.PUBLIC, msg.channel)) - { - super.sendMsg(msg.channel, Config.help_cmd_avi.replace(".cmds.", Config.PUB_LIST)); - } - if (hasAdminRights(msg.user) && (isChannel(PickupChannelType.ADMIN, msg.channel) || msg.channel.type == DiscordChannelType.DM)) + DiscordUser testUser = super.parseMention(data[1]); + if (testUser != null) { - super.sendMsg(msg.channel, Config.help_cmd_avi.replace(".cmds.", Config.ADMIN_LIST)); + u = testUser; } } - } - else if (data[0].toLowerCase().equals(Config.CMD_ADDCHANNEL)) - { - if (hasAdminRights(msg.user)) + List list = u.getRoles(DiscordBot.getGuilds()); + StringBuilder message = new StringBuilder(); + for (DiscordRole role : list) { - if (data.length == 3) - { - DiscordChannel targetChannel = DiscordChannel.findChannel(data[1].replaceAll("[^\\d.]", "")); - if (targetChannel != null) - { - try { - PickupChannelType type = PickupChannelType.valueOf(data[2].toUpperCase()); - if (!logic.getChannelByType(type).contains(targetChannel)) - { - if (logic.addChannel(type, targetChannel)) - { - sendNotice(msg.user, "successfully added the channel"); - } - else sendNotice(msg.user, "unsuccessfully added the channel"); - } - } - catch (IllegalArgumentException e) - { - sendNotice(msg.user, "unknown channel type"); - } - } - else sendNotice(msg.user, "invalid channel"); - } - else sendNotice(msg.user, "invalid options"); + message.append(role.getMentionString()).append(" "); } + sendNotice(u, message.toString()); } - else if (data[0].toLowerCase().equals(Config.CMD_REMOVECHANNEL)) + + if (data[0].equalsIgnoreCase("!showknownroles")) { - if (hasAdminRights(msg.user)) - { - if (data.length == 3) - { - DiscordChannel targetChannel = DiscordChannel.findChannel(data[1].replaceAll("[^\\d.]", "")); - if (targetChannel != null) - { - try - { - PickupChannelType type = PickupChannelType.valueOf(data[2].toUpperCase()); - if (logic.getChannelByType(type).contains(targetChannel)) - { - if (logic.removeChannel(type, targetChannel)) - { - sendNotice(msg.user, "successfully removed the channel."); - } - else sendNotice(msg.user, "unsuccessfully removed the channel."); - } + StringBuilder message = new StringBuilder("Roles: "); + for (PickupRoleType type : logic.getRoleTypes()) { + message.append("\n**").append(type.name()).append("**:"); - } - catch (IllegalArgumentException e) - { - sendNotice(msg.user, "unknown role type"); - } - } - else sendNotice(msg.user, "invalid channel"); + for (DiscordRole role : logic.getRoleByType(type)) + { + message.append(" ").append(role.getMentionString()).append(" "); } - else sendNotice(msg.user, "invalid options"); } + sendMsg(msg.channel, message.toString()); } - else if (data[0].toLowerCase().equals(Config.CMD_ADDROLE)) + + if (data[0].equalsIgnoreCase("!showknownchannels")) { - if (hasSuperAdminRights(msg.user)) - { - if (data.length == 3) - { - DiscordRole targetRole = DiscordRole.getRole(data[1].replaceAll("[^\\d.]", "")); - if (targetRole != null) - { - try - { - PickupRoleType type = PickupRoleType.valueOf(data[2].toUpperCase()); - if (!logic.getRoleByType(type).contains(targetRole)) - { - if (logic.addRole(type, targetRole)) - { - sendNotice(msg.user, "successfully added the role."); - } - else sendNotice(msg.user, "unsuccessfully removed the role."); - } + StringBuilder message = new StringBuilder("Channels: "); + for (PickupChannelType type : logic.getChannelTypes()) { + message.append("\n**").append(type.name()).append("**:"); - } - catch (IllegalArgumentException e) - { - sendNotice(msg.user, "unknown role type"); - } - } - else sendNotice(msg.user, "invalid channel"); + for (DiscordChannel channel : logic.getChannelByType(type)) + { + message.append(" ").append(channel.getMentionString()).append(" "); } - else sendNotice(msg.user, "invalid options"); } + sendMsg(msg.channel, message.toString()); } - else if (data[0].toLowerCase().equals(Config.CMD_REMOVEROLE)) - { - if (hasSuperAdminRights(msg.user)) - { - if (data.length == 3) - { - DiscordRole targetRole = DiscordRole.getRole(data[1].replaceAll("[^\\d.]", "")); - if (targetRole != null) - { - try - { - PickupRoleType type = PickupRoleType.valueOf(data[2].toUpperCase()); - if (logic.getRoleByType(type).contains(targetRole)) - { - if (logic.removeRole(type, targetRole)) - { - sendNotice(msg.user, "successfully removed the role."); - } - else sendNotice(msg.user, "unsuccessfully removed the role."); - } - } - catch (IllegalArgumentException e) - { - sendNotice(msg.user, "unknown role type"); - } + + if (data[0].equalsIgnoreCase("!godrole")) { + if (logic.getRoleByType(PickupRoleType.SUPERADMIN).size() == 0) { + if (data.length == 2) { + DiscordRole role = DiscordRole.getRole(data[1].replaceAll("[^\\d.]", "")); + if (role != null) { + logic.addRole(PickupRoleType.SUPERADMIN, role); + sendNotice(msg.user, "*" + role.getMentionString() + " set as SUPERADMIN role*"); } - else sendNotice(msg.user, "invalid role"); } - else sendNotice(msg.user, "invalid options"); } + else sendNotice(msg.user, "A DiscordRole is already set as SUPERADMIN, check the DB."); } - } - - if (data[0].toLowerCase().equals("!showroles")) - { - DiscordUser u = msg.user; - if (data.length == 2) - { - DiscordUser testUser = super.parseMention(data[1]); - if (testUser != null) - { - u = testUser; - } - } - List list = u.getRoles(DiscordBot.getGuild()); - String message = ""; - for (DiscordRole role : list) - { - message += role.getMentionString() + " "; - } - sendNotice(u, message); + } + + @Override + protected void recvInteraction(DiscordInteraction interaction) { + LOGGER.info("RECV #" + ((interaction.message.channel == null || interaction.message.channel.name == null) ? "null" : interaction.message.channel.name) + " " + interaction.user.username + ": " + interaction.custom_id); + + Player p = Player.get(interaction.user); + if (p == null) { + interaction.respond(Config.user_not_registered); + return; } - - if (data[0].toLowerCase().equals("!showknownroles")) - { - String message = "Roles: "; - for (PickupRoleType type : logic.getRoleTypes()) { - message += "\n**" + type.name() + "**:"; - for (DiscordRole role : logic.getRoleByType(type)) - { - message += " " + role.getMentionString() + " "; - } - } - sendMsg(msg.channel, message); + if (interaction.message != null && interaction.message.channel != null && isChannel(PickupChannelType.PUBLIC, interaction.message.channel)){ + p.setLastPublicChannel(interaction.message.channel); } - - if (data[0].toLowerCase().equals("!showknownchannels")) + + String[] data = interaction.custom_id.split("_"); + + switch (data[0].toLowerCase()) { - String message = "Channels: "; - for (PickupChannelType type : logic.getChannelTypes()) { - message += "\n**" + type.name() + "**:"; + case Config.INT_PICK: + logic.cmdPick(interaction, p, Integer.parseInt(data[1])); + break; - for (DiscordChannel channel : logic.getChannelByType(type)) - { - message += " " + channel.getMentionString() + " "; - } - } - sendMsg(msg.channel, message); + case Config.INT_LAUNCHAC: + logic.cmdLaunchAC(interaction, p, Integer.parseInt(data[1]), data[2], data[3]); + break; + + case Config.INT_TEAMINVITE: + logic.answerTeamInvite(interaction, p, Integer.parseInt(data[1]), Player.get(data[2]), Player.get(data[3])); + break; + + case Config.INT_TEAMREMOVE: + logic.removeTeamMember(interaction, p, Player.get(data[1]), Player.get(data[2])); + break; + + case Config.INT_SEASONSTATS: + logic.showSeasonStats(interaction, Player.get(data[1]), Integer.parseInt(data[2])); + break; + + case Config.INT_SEASONLIST: + logic.showSeasonList(interaction, Player.get(data[1])); + break; + + case Config.INT_LASTMATCHPLAYER: + logic.showLastMatchPlayer(interaction, Player.get(data[1])); + break; + + case Config.INT_SEASONSELECTED: + logic.showSeasonStats(interaction, Player.get(data[1]), Integer.parseInt(interaction.values.get(0))); + break; + + case Config.INT_SHOWBET: + logic.showBets(interaction, Integer.parseInt(data[2]), data[1], p); + break; + + case Config.INT_BET: + logic.bet(interaction, Integer.parseInt(data[1]), data[2], Integer.parseInt(data[3]), p); + break; + +// case Config.INT_BUY: +// switch(data[1]){ +// case Config.INT_BUY_BOOST: +// logic.buyBoost(interaction, p); +// break; +// case Config.INT_BUY_SHOWVOTEOPTIONS: +// logic.showAdditionalVoteOptions(interaction, p); +// break; +// case Config.INT_BUY_ADDVOTES: +// logic.buyAdditionalVotes(interaction, p, Integer.parseInt(data[2])); +// break; +// case Config.INT_BUY_MAPBAN: +// logic.buyBanMap(interaction, p); +// break; +// } +// break; } - - if (data[0].toLowerCase().equals("!godrole")) { - if (logic.getRoleByType(PickupRoleType.SUPERADMIN).size() == 0) { - if (data.length == 2) { - DiscordRole role = DiscordRole.getRole(data[1].replaceAll("[^\\d.]", "")); - if (role != null) { - logic.addRole(PickupRoleType.SUPERADMIN, role); - sendNotice(msg.user, "*" + role.getMentionString() + " set as SUPERADMIN role*"); - } - } - } + } + + @Override + protected void recvApplicationCommand(DiscordInteraction interaction) { + LOGGER.info("RECV #" + interaction.name + " " + interaction.user.username); + + Player p = Player.get(interaction.user); + if (p == null) { + interaction.respond(Config.user_not_registered); + return; } - - if (data[0].toLowerCase().equals("!banme")) { - //Player p = Player.get(msg.user); - //logic.banPlayer(p, BanReason.NOSHOW); + + switch (interaction.name) + { + case Config.APP_BET: + logic.bet(interaction, Integer.parseInt(interaction.options.get(0).value), interaction.options.get(1).value, Integer.parseInt(interaction.options.get(2).value), p); + break; + +// case Config.APP_BUY: +// logic.showBuys(interaction, p); +// break; } } - + public boolean isChannel(PickupChannelType type, DiscordChannel channel) { return logic.getChannelByType(type).contains(channel); } - - public boolean hasAdminRights(DiscordUser user) { - List roleList = user.getRoles(DiscordBot.getGuild()); - List adminList = logic.getAdminList(); - for (DiscordRole s : roleList) { - for (DiscordRole r : adminList) { - if (s.equals(r)) { - return true; + + + + public void sendNotice(DiscordUser user, String msg) { + sendMsg(getLatestMessageChannel(), user.getMentionString() + " " + msg); + } + + public void sendMsg(List channelList, String msg) { + List mentionedPlayers = new ArrayList(); + Matcher m = Pattern.compile("<@(.*?)>").matcher(msg); + while (m.find()) { + DiscordUser dsUser = DiscordUser.getUser(m.group(1)); + Player playerMentioned = Player.get(dsUser); + if (dsUser != null){ + mentionedPlayers.add(playerMentioned); + } + } + + for (DiscordChannel channel : channelList) { + String msgCopy = String.valueOf(msg); + for (Player p : mentionedPlayers){ + if ((p.getLastPublicChannel() != null && !channel.isThread && !channel.id.equals(p.getLastPublicChannel().id)) || + ((p.getLastPublicChannel() != null && channel.isThread && channel.parent_id != null && !channel.parent_id.equals(p.getLastPublicChannel().id))) + ){ + msgCopy = msgCopy.replace( + "<@" + p.getDiscordUser().id + ">", + "**" + p.getDiscordUser().username + "**" + ); } } + sendMsg(channel, msgCopy); } - return false; } -public boolean hasSuperAdminRights(DiscordUser user) { - List roleList = user.getRoles(DiscordBot.getGuild()); - List adminList = logic.getSuperAdminList(); - for (DiscordRole s : roleList) { - for (DiscordRole r : adminList) { - if (s.equals(r)) { - return true; + public void sendMsg(List channelList, String msg, DiscordEmbed embed) { + if (msg == null){ + msg = ""; + } + + List mentionedPlayers = new ArrayList(); + Matcher m = Pattern.compile("<@(.*?)>").matcher(msg); + while (m.find()) { + DiscordUser dsUser = DiscordUser.getUser(m.group(1)); + Player playerMentioned = Player.get(dsUser); + if (dsUser != null){ + mentionedPlayers.add(playerMentioned); } } + + for (DiscordChannel channel : channelList) { + String msgCopy = String.valueOf(msg); + for (Player p : mentionedPlayers){ + if ((p.getLastPublicChannel() != null && !channel.isThread && !channel.id.equals(p.getLastPublicChannel().id)) || + ((p.getLastPublicChannel() != null && channel.isThread && channel.parent_id != null && !channel.parent_id.equals(p.getLastPublicChannel().id))) + ){ + msgCopy = msgCopy.replace( + "<@" + p.getDiscordUser().id + ">", + "**" + p.getDiscordUser().username + "**" + ); + } + } + sendMsg(channel, msgCopy, embed); + } } - return false; -} + public List sendMsgToEdit(List channelList, String msg, DiscordEmbed embed, List components) { + if (msg == null){ + msg = ""; + } - public void sendNotice(DiscordUser user, String msg) { - sendMsg(getLatestMessageChannel(), user.getMentionString() + " " + msg); - } + List mentionedPlayers = new ArrayList(); + List sentMessages = new ArrayList(); + Matcher m = Pattern.compile("<@(.*?)>").matcher(msg); + while (m.find()) { + DiscordUser dsUser = DiscordUser.getUser(m.group(1)); + Player playerMentioned = Player.get(dsUser); + if (dsUser != null){ + mentionedPlayers.add(playerMentioned); + } + } - public void sendMsg(List channelList, String msg) { for (DiscordChannel channel : channelList) { - sendMsg(channel, msg); + String msgCopy = String.valueOf(msg); + for (Player p : mentionedPlayers){ + if ((p.getLastPublicChannel() != null && !channel.isThread && !channel.id.equals(p.getLastPublicChannel().id)) || + ((p.getLastPublicChannel() != null && channel.isThread && channel.parent_id != null && !channel.parent_id.equals(p.getLastPublicChannel().id))) + ){ + msgCopy = msgCopy.replace( + "<@" + p.getDiscordUser().id + ">", + "**" + p.getDiscordUser().username + "**" + ); + } + } + sentMessages.add(sendMsgToEdit(channel, msgCopy, embed, components)); + } + + return sentMessages; + } + + public List createThread(List channelList, String name) { + List threadChannels = new ArrayList(); + for (DiscordChannel channel : channelList) { + threadChannels.add(createThread(channel, name)); } + return threadChannels; } public DiscordChannel getLatestMessageChannel() { return latestMessageChannel; } -} \ No newline at end of file + + public void createApplicationCommands(){ + DiscordApplicationCommand appBet = new DiscordApplicationCommand("bet", "Place a bet for a game"); + DiscordCommandOption option1 = new DiscordCommandOption(DiscordCommandOptionType.INTEGER, "matchid", "The game number."); + appBet.addOption(option1); + DiscordCommandOption option2 = new DiscordCommandOption(DiscordCommandOptionType.STRING, "team", "The color of the team you want to bet on."); + option2.addChoice(new DiscordCommandOptionChoice("red", "red")); + option2.addChoice(new DiscordCommandOptionChoice("blue", "blue")); + appBet.addOption(option2); + DiscordCommandOption option3 = new DiscordCommandOption(DiscordCommandOptionType.INTEGER, "amount", "The amount of coins you want to bet"); + appBet.addOption(option3); + appBet.create(); + + DiscordApplicationCommand appBuy = new DiscordApplicationCommand("buy", "Buy a perk with your coins."); + appBuy.create(); + } +} diff --git a/src/de/gost0r/pickupbot/pickup/PickupLogic.java b/src/de/gost0r/pickupbot/pickup/PickupLogic.java index 9601b0e..a493642 100644 --- a/src/de/gost0r/pickupbot/pickup/PickupLogic.java +++ b/src/de/gost0r/pickupbot/pickup/PickupLogic.java @@ -1,9 +1,10 @@ package de.gost0r.pickupbot.pickup; +import java.io.*; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -12,105 +13,166 @@ import java.util.logging.Level; import java.util.logging.Logger; -import de.gost0r.pickupbot.discord.DiscordChannel; -import de.gost0r.pickupbot.discord.DiscordRole; -import de.gost0r.pickupbot.discord.DiscordUser; -import de.gost0r.pickupbot.discord.DiscordUserStatus; +import de.gost0r.pickupbot.PickupBotDiscordMain; +import de.gost0r.pickupbot.discord.*; import de.gost0r.pickupbot.discord.api.DiscordAPI; +import de.gost0r.pickupbot.ftwgl.FtwglAPI; import de.gost0r.pickupbot.pickup.PlayerBan.BanReason; import de.gost0r.pickupbot.pickup.server.Server; +import io.sentry.Sentry; +import org.json.JSONObject; public class PickupLogic { - private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - + private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + public PickupBot bot; public Database db; - + private List serverList; + private List gtvServerList; private List mapList; private Map> roles; private Map> channels; private List ongoingMatches; // ongoing matches (live) + private List activeTeams; private Queue awaitingServer; private Map curMatch; + private Map teamsQueued; private boolean locked; + private boolean dynamicServers; private Map banDuration; + private Map lastMapPlayed; + + public Season currentSeason; public PickupLogic(PickupBot bot) { this.bot = bot; db = new Database(this); Player.db = db; - // handle db stuff - -// db.resetStats(); + Player.logic = this; + Bet.logic = this; + + currentSeason = db.getCurrentSeason(); serverList = db.loadServers(); roles = db.loadRoles(); channels = db.loadChannels(); - + + awaitingServer = new LinkedList(); curMatch = new HashMap(); + teamsQueued = new HashMap(); + lastMapPlayed = new HashMap(); for (Gametype gt : db.loadGametypes()) { if (gt.getActive()) { curMatch.put(gt, null); + lastMapPlayed.put(getGametypeByString(gt.getName()), new GameMap("null")); } } mapList = db.loadMaps(); // needs current gamemode list ongoingMatches = db.loadOngoingMatches(); // need maps, servers and gamemodes - + activeTeams = new ArrayList(); + createCurrentMatches(); banDuration = new HashMap(); - banDuration.put(BanReason.NOSHOW, new String[] {"10m", "2h", "12h", "1d", "3d", "1w", "2w", "1M", "3M", "1y"}); - banDuration.put(BanReason.RAGEQUIT, new String[] {"12h", "1d", "3d", "1w", "2w", "1M", "3M", "1y"}); + banDuration.put(BanReason.NOSHOW, new String[] {"10m", "30m", "1h", "2h", "6h", "12h", "1d", "2d", "3d", "1w", "1w", "1w", "1M"}); + banDuration.put(BanReason.RAGEQUIT, new String[] {"6h", "12h", "1d", "2d", "3d", "1w", "1w", "1w", "1w", "1w", "1w", "1w", "1M"}); + - awaitingServer = new LinkedList(); + //Server testGTV = new Server(0, "gtv.b00bs-clan.com", 709, "arkon4bmn", "SevenAndJehar", true, null); + gtvServerList = new ArrayList(); + //gtvServerList.add(testGTV); + } + + public void cmdAddPlayer(Player player, List modes, boolean forced) { + for (Gametype gt : modes) { + cmdAddPlayer(player, gt, forced); + } } - - public void cmdAddPlayer(Player player, List modes) { - if (locked) { + public void cmdAddPlayer(Player player, Gametype gt, boolean forced) { + if ((dynamicServers || gt.getTeamSize() == 0) && !FtwglAPI.checkIfPingStored(player)){ + cmdGetPingURL(player); + return; + } + if (player.getEnforceAC() && !FtwglAPI.hasLauncherOn(player)){ + bot.sendNotice(player.getDiscordUser(), Config.pkup_launcheroff); + return; + } + + if (locked && !forced) { bot.sendNotice(player.getDiscordUser(), Config.pkup_lock); return; } - if (player.isBanned()) { + if (player.isBanned() && !forced) { bot.sendMsg(bot.getLatestMessageChannel(), printBanInfo(player)); return; } - if (playerInActiveMatch(player) != null) { + if (playerInActiveMatch(player) != null && playerInActiveMatch(player).getGametype().getTeamSize() > 2) { bot.sendNotice(player.getDiscordUser(), Config.player_already_match); return; } + for (Team activeTeam: activeTeams){ + if (activeTeam.isInTeam(player)){ + bot.sendNotice(player.getDiscordUser(), Config.team_cant_soloqueue); + return; + } + } - String defmsg = "Unable to sign up for:"; - String msg = defmsg; - for (Gametype gt : modes) { - if (gt != null && curMatch.keySet().contains(gt)) { - Match m = curMatch.get(gt); - if (m.getMatchState() != MatchState.Signup || m.isInMatch(player) || playerInActiveMatch(player) != null) { - msg += " " + gt.getName(); - } else { - m.addPlayer(player); - } + if (gt.getName().equalsIgnoreCase("div1")){ + int eloRank = player.getEloRank(); + int minEloRank = 40; + int kdrRank = player.stats.kdrRank; + int minKdrRank = 20; + int winRank = player.stats.wdlRank; + int minWinRank = 20; + if (eloRank > minEloRank + && (kdrRank > minKdrRank || kdrRank == -1) + && (winRank > minWinRank || winRank == -1)) { + String errmsg = Config.player_notdiv1; + errmsg = errmsg.replace(".minrank.", String.valueOf(minEloRank)); + errmsg = errmsg.replace(".rank.", String.valueOf(eloRank)); + errmsg = errmsg.replace(".minkdrrank.", String.valueOf(minKdrRank)); + errmsg = errmsg.replace(".kdrrank.", String.valueOf(kdrRank)); + errmsg = errmsg.replace(".minwinrank.", String.valueOf(minWinRank)); + errmsg = errmsg.replace(".winrank.", String.valueOf(winRank)); + bot.sendNotice(player.getDiscordUser(), errmsg); + return; + } + } + + if (forced) { + player.setLastMessage(System.currentTimeMillis()); + } + + String defmsg = "You are already queued for:"; + StringBuilder msg = new StringBuilder(defmsg); + + if (curMatch.containsKey(gt)) { + Match m = curMatch.get(gt); + if (m.getMatchState() != MatchState.Signup || m.isInMatch(player) || playerInActiveMatch(player) != null) { + msg.append(" ").append(gt.getName()); + } else { + m.addPlayer(player); } } - if (!msg.equals(defmsg)) { - bot.sendNotice(player.getDiscordUser(), msg); + if (!msg.toString().equals(defmsg)) { + bot.sendNotice(player.getDiscordUser(), msg.toString()); } + + checkTeams(); } - + public void cmdRemovePlayer(Player player, List modes) { - if (locked) { - bot.sendNotice(player.getDiscordUser(), Config.pkup_lock); - return; - } + if (playerInActiveMatch(player) != null) { bot.sendNotice(player.getDiscordUser(), Config.player_already_match); return; @@ -121,28 +183,41 @@ public void cmdRemovePlayer(Player player, List modes) { for (Match match : curMatch.values()) { match.removePlayer(player, true); } + cmdRemoveTeam(player, false); + checkTeams(); return; } for (Gametype gt : modes) { - if (gt != null && curMatch.keySet().contains(gt)) { + if (gt != null && curMatch.containsKey(gt)) { curMatch.get(gt).removePlayer(player, true); // conditions checked within function } } + cmdRemoveTeam(player, false); + checkTeams(); } - - public boolean cmdLock() { + + public void cmdPick(DiscordInteraction interaction, Player player, int pick) { + for (Match match : ongoingMatches) { + if (!match.hasSquads() && match.isCaptainTurn(player)) { + interaction.respond(null); + match.pick(player, pick); + return; + } + } + interaction.respond(Config.player_not_captain); + } + + public void cmdLock() { locked = true; bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.lock_enable); - return true; } - - public boolean cmdUnlock() { + + public void cmdUnlock() { locked = false; bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.lock_disable); - return true; } - + public void cmdRegisterPlayer(DiscordUser user, String urtauth, String msgid) { // check whether the user and the urtauth aren't taken if (Player.get(user) == null) { @@ -153,6 +228,11 @@ public void cmdRegisterPlayer(DiscordUser user, String urtauth, String msgid) { p.setElo(db.getAvgElo()); db.createPlayer(p); bot.sendNotice(user, Config.auth_success); + bot.sendMsg(user.getDMChannel(), Config.ac_enforced); + String admin_msg = Config.auth_success_admin; + admin_msg = admin_msg.replace(".user.", user.getMentionString()); + admin_msg = admin_msg.replace(".urtauth.", urtauth); + bot.sendMsg(getChannelByType(PickupChannelType.ADMIN), admin_msg); } else { DiscordAPI.deleteMessage(bot.getLatestMessageChannel(), msgid); bot.sendNotice(user, Config.auth_sent_key); @@ -167,7 +247,7 @@ public void cmdRegisterPlayer(DiscordUser user, String urtauth, String msgid) { bot.sendNotice(user, Config.auth_taken_user); } } - + public boolean cmdUnregisterPlayer(Player player) { List matches = playerInMatch(player); for (Match m : matches) { @@ -178,59 +258,490 @@ public boolean cmdUnregisterPlayer(Player player) { return true; } + public boolean cmdEnforcePlayerAC(Player player) { + boolean oldEnforceAC = player.getEnforceAC(); + player.setEnforceAC(!oldEnforceAC); + db.enforcePlayerAC(player); + return !oldEnforceAC; + } + + public void cmdSetPlayerCountry(DiscordUser user, String str_country) { + + // check if user is already registered + if (Player.get(user) != null) { + Player p = db.loadPlayer(user); + + if(p.getCountry().equalsIgnoreCase("NOT_DEFINED") || user.hasAdminRights()) { + + if(Country.isValid(str_country)) + { + db.updatePlayerCountry(p, str_country); + Player.get(user).setCountry(str_country); + bot.sendNotice(user, Config.country_added); + } + else + { + bot.sendNotice(user, "Unknown county code. Check: "); + } + } + else { + // Region has been set by user, need an admin + bot.sendNotice(user, "Your country is already set. Corresponding region: " + p.getRegion().toString()); + } + } + else { + // send register first notice + bot.sendNotice(user, Config.user_not_registered); + } + + } + + public void cmdChangePlayerCountry(Player p, String str_country) { + + if(Country.isValid(str_country)) + { + db.updatePlayerCountry(p, str_country); + p.setCountry(str_country); + bot.sendMsg(getChannelByType(PickupChannelType.ADMIN), "Country code updated to " + str_country); + } + else + { + bot.sendMsg(getChannelByType(PickupChannelType.ADMIN), "Unknown county code. Check: "); + } + } + public void cmdTopElo(int number) { - String msg = Config.pkup_top5_header; + StringBuilder embed_rank = new StringBuilder(); + StringBuilder embed_player = new StringBuilder(); + StringBuilder embed_elo = new StringBuilder(); + DiscordEmbed embed = new DiscordEmbed(); + embed.title = "Top 10 players"; + embed.color = 7056881; + int rank = 1; List players = db.getTopPlayers(number); if (players.isEmpty()) { - bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg + " None"); + bot.sendMsg(bot.getLatestMessageChannel(), "None"); } else { for (Player p : players) { - msg += "\n" + cmdGetElo(p, false); + String country; + if( p.getCountry().equalsIgnoreCase("NOT_DEFINED")) { + country = "<:puma:849287183474884628>"; + } + else { + country = ":flag_" + p.getCountry().toLowerCase() + ":"; + } + embed_rank.append("**").append(rank).append("**\n"); + embed_player.append(country).append(" \u200b \u200b ").append(p.getUrtauth()).append('\n'); + embed_elo.append(p.getRank().getEmoji()).append(" \u200b \u200b ").append(p.getElo()).append("\n"); + rank++; } - bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + embed.addField("\u200b", embed_rank.toString(), true); + embed.addField("Player", embed_player.toString(), true); + embed.addField("Elo", embed_elo.toString(), true); + + bot.sendMsg(bot.getLatestMessageChannel(), null, embed); + } + } + + public void cmdTopWDL(int number, Gametype gt) { + StringBuilder embed_rank = new StringBuilder(); + StringBuilder embed_player = new StringBuilder(); + StringBuilder embed_wdl = new StringBuilder(); + DiscordEmbed embed = new DiscordEmbed(); + embed.title = "Top 10 win rate " + gt.getName(); + embed.description = "``Season " + currentSeason.number + "``"; + embed.color = 7056881; + + + Map topwdl = db.getTopWDL(number, gt, currentSeason); + if (topwdl.isEmpty()) { + bot.sendMsg(bot.getLatestMessageChannel(), "None"); + } else { + int rank = 1; + for (Map.Entry entry : topwdl.entrySet()) { + String country; + if( entry.getKey().getCountry().equalsIgnoreCase("NOT_DEFINED")) { + country = ":puma:"; + } + else { + country = ":flag_" + entry.getKey().getCountry().toLowerCase() + ":"; + } + embed_rank.append("**").append(rank).append("**\n"); + embed_player.append(country).append(" \u200b \u200b ").append(entry.getKey().getUrtauth()).append('\n'); + embed_wdl.append(Math.round(entry.getValue() * 100d)).append(" %\n"); + rank++; + } + embed.addField("\u200b", embed_rank.toString(), true); + embed.addField("Player", embed_player.toString(), true); + embed.addField("Win rate", embed_wdl.toString(), true); + + bot.sendMsg(bot.getLatestMessageChannel(), null, embed); + } + } + + public void cmdTopKDR(int number, Gametype gt) { + StringBuilder embed_rank = new StringBuilder(); + StringBuilder embed_player = new StringBuilder(); + StringBuilder embed_wdl = new StringBuilder(); + DiscordEmbed embed = new DiscordEmbed(); + embed.title = "Top 10 kill death ratio " + gt.getName(); + embed.description = "``Season " + currentSeason.number + "``"; + embed.color = 7056881; + + Map topkdr = db.getTopKDR(number, gt, currentSeason); + if (topkdr.isEmpty()) { + bot.sendMsg(bot.getLatestMessageChannel(), "None"); + } else { + int rank = 1; + for (Map.Entry entry : topkdr.entrySet()) { + String country; + if( entry.getKey().getCountry().equalsIgnoreCase("NOT_DEFINED")) { + country = "<:puma:849287183474884628>"; + } + else { + country = ":flag_" + entry.getKey().getCountry().toLowerCase() + ":"; + } + embed_rank.append("**").append(rank).append("**\n"); + embed_player.append(country).append(" \u200b \u200b ").append(entry.getKey().getUrtauth()).append('\n'); + embed_wdl.append(String.format("%.02f", entry.getValue())).append("\n"); + rank++; + } + embed.addField("\u200b", embed_rank.toString(), true); + embed.addField("Player", embed_player.toString(), true); + embed.addField("KDR", embed_wdl.toString(), true); + + bot.sendMsg(bot.getLatestMessageChannel(), null, embed); } } - public String cmdGetElo(Player p) { - return cmdGetElo(p, true); + public void cmdTopRich(int number) { + StringBuilder embed_rank = new StringBuilder(); + StringBuilder embed_player = new StringBuilder(); + StringBuilder embed_rich = new StringBuilder(); + DiscordEmbed embed = new DiscordEmbed(); + embed.title = "Top 10 richest players"; + embed.color = 7056881; + + Map topRich = db.getTopRich(number); + if (topRich.isEmpty()) { + bot.sendMsg(bot.getLatestMessageChannel(), "None"); + } else { + int rank = 1; + for (Map.Entry entry : topRich.entrySet()) { + String country; + if( entry.getKey().getCountry().equalsIgnoreCase("NOT_DEFINED")) { + country = "<:puma:849287183474884628>"; + } + else { + country = ":flag_" + entry.getKey().getCountry().toLowerCase() + ":"; + } + embed_rank.append("**").append(rank).append("**\n"); + embed_player.append(country).append(" \u200b \u200b ").append(entry.getKey().getUrtauth()).append('\n'); + JSONObject emoji = Bet.getCoinEmoji(entry.getValue()); + embed_rich.append("<:" + emoji.getString("name") + ":" + emoji.getString("id") + "> " + String.format("%,d", entry.getValue())).append("\n"); + rank++; + } + embed.addField("\u200b", embed_rank.toString(), true); + embed.addField("Player", embed_player.toString(), true); + embed.addField("Balance", embed_rich.toString(), true); + + bot.sendMsg(bot.getLatestMessageChannel(), null, embed); + } } - public String cmdGetElo(Player p, boolean sendMsg) { + public String cmdGetElo(Player p, Gametype gt) { if (p == null) { return ""; } String msg = Config.pkup_getelo; msg = msg.replace(".urtauth.", p.getUrtauth()); msg = msg.replace(".elo.", String.valueOf(p.getElo())); - msg = msg.replace(".wdl.", String.valueOf(Math.round(db.getWDLForPlayer(p).calcWinRatio() * 100d))); - msg = msg.replace(".position.", String.valueOf(db.getRankForPlayer(p))); + if (gt.getName().equals("CTF")){ + msg = msg.replace(".wdl.", String.format("%.02f", p.stats.ctf_wdl.calcWinRatio() * 100d)); + msg = msg.replace(".kdr.", String.format("%.02f", p.stats.ctf_rating)); + } + else { + msg = msg.replace(".wdl.", String.format("%.02f", p.stats.ts_wdl.calcWinRatio() * 100d)); + msg = msg.replace(".kdr.", String.format("%.02f", p.stats.kdr)); + } + + msg = msg.replace(".position.", String.valueOf(p.getEloRank())); msg = msg.replace(".rank.", p.getRank().getEmoji()); - if (sendMsg) { - bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + + + if( p.getCountry().equalsIgnoreCase("NOT_DEFINED")) { + msg = msg.replace(".country.", "<:puma:849287183474884628>"); } + else { + msg = msg.replace(".country.", ":flag_" + p.getCountry().toLowerCase() + ":"); + } + return msg; } - - public void cmdGetMaps() { - String msg = "None"; + + public void cmdGetStats(Player p) { + if (p == null) { + return; + } + + DiscordEmbed statsEmbed = getStatsEmbed(p); + + List buttons = new ArrayList(); + DiscordButton buttonSeason = new DiscordButton(DiscordButtonStyle.GREY); + buttonSeason.custom_id = Config.INT_SEASONLIST + "_" + p.getUrtauth(); + buttonSeason.label = "Select season"; + buttons.add(buttonSeason); + DiscordButton buttonAlltime = new DiscordButton(DiscordButtonStyle.BLURPLE); + buttonAlltime.custom_id = Config.INT_SEASONSTATS + "_" + p.getUrtauth() + "_0"; + buttonAlltime.label = "All-time stats"; + buttons.add(buttonAlltime); + DiscordButton buttomLastGame = new DiscordButton(DiscordButtonStyle.GREY); + buttomLastGame.custom_id = Config.INT_LASTMATCHPLAYER + "_" + p.getUrtauth(); + buttomLastGame.label = "Last game"; + buttons.add(buttomLastGame); + + bot.sendMsgToEdit(bot.getLatestMessageChannel(), null, statsEmbed, buttons); + } + + public void showSeasonList(DiscordInteraction interaction, Player p){ + if (p == null) { + return; + } + ArrayList options = new ArrayList(); + for (int i = 1 ; i <= currentSeason.number ; i++){ + DiscordSelectOption option = new DiscordSelectOption("Season " + i, String.valueOf(i)); + options.add(option); + } + + ArrayList components = new ArrayList(); + DiscordSelectMenu seasonMenu = new DiscordSelectMenu(options); + seasonMenu.custom_id = Config.INT_SEASONSELECTED + "_" + p.getUrtauth(); + components.add(seasonMenu); + interaction.respond(null, null, components); + } + + public void showSeasonStats(DiscordInteraction interaction, Player p, int seasonnumber){ + if (p == null) { + return; + } + + Season season; + if (seasonnumber == 0){ + season = Season.AllTimeSeason(); + } + else{ + season = db.getSeason(seasonnumber); + } + + DiscordEmbed statsEmbed = getDetailedStatsEmbed(p, season); + interaction.respond(null, statsEmbed); + } + + public DiscordEmbed getStatsEmbed(Player p){ + PlayerStats stats = db.getPlayerStats(p, currentSeason); + String country = "<:puma:849287183474884628>"; + if(!p.getCountry().equalsIgnoreCase("NOT_DEFINED")) { + country = ":flag_" + p.getCountry().toLowerCase() + ":"; + } + + DiscordEmbed statsEmbed = new DiscordEmbed(); + statsEmbed.color = 7056881; + statsEmbed.title = country + " \u200b \u200b " + p.getUrtauth(); + statsEmbed.thumbnail = p.getDiscordUser().getAvatarUrl(); + + String boostActive = ""; + if (p.hasBoostActive()){ + boostActive = "\n**ELO BOOST** (Expires )"; + } + statsEmbed.description = p.getRank().getEmoji() + " \u200b \u200b **" + p.getElo() + "** #" + p.getEloRank() + boostActive + "\n\n``Season " + currentSeason.number + "``"; + + statsEmbed.footer_icon = "https://cdn.discordapp.com/emojis/" + Bet.getCoinEmoji(p.getCoins()).getString("id"); + statsEmbed.footer = String.valueOf(p.getCoins()); + + if (stats.ts_wdl.getTotal() < 5){ + statsEmbed.addField("\u200b", "**TS**: ``" + stats.ts_wdl.getTotal() + "/5`` placement games", false); + } + else{ + statsEmbed.addField("\u200b", "TS <:lr:401457276478554112>", false); + statsEmbed.addField("Played", String.valueOf(stats.ts_wdl.getTotal()), true); + if (stats.kdrRank == -1) { + statsEmbed.addField("KDR", String.format("%.02f", stats.kdr), true); + + } else { + statsEmbed.addField("KDR", String.format("%.02f", stats.kdr) + " (#" + stats.kdrRank + ")", true); + } + if (p.stats.wdlRank == -1) { + statsEmbed.addField("Win %", Math.round(stats.ts_wdl.calcWinRatio() * 100d) + "%", true); + + } else { + statsEmbed.addField("Win %", Math.round(stats.ts_wdl.calcWinRatio() * 100d) + "% (#" + stats.wdlRank + ")", true); + } + } + + if (stats.ctf_wdl.getTotal() < 5) { + statsEmbed.addField("\u200b", "**CTF**: ``" + stats.ctf_wdl.getTotal() + "/5`` placement games", false); + } + else{ + statsEmbed.addField("\u200b", "CTF <:red_flag:400778174415503371>", false); + statsEmbed.addField("Played", String.valueOf(stats.ctf_wdl.getTotal()), true); + if (stats.ctfRank == -1) { + statsEmbed.addField("Rating", String.format("%.02f", stats.ctf_rating), true); + + } else { + statsEmbed.addField("Rating", String.format("%.02f", stats.ctf_rating) + " (#" + stats.ctfRank + ")", true); + } + + if (stats.ctfWdlRank == -1) { + statsEmbed.addField("Win %", Math.round(stats.ctf_wdl.calcWinRatio() * 100d) + "%", true); + + } else { + statsEmbed.addField("Win %", Math.round(stats.ctf_wdl.calcWinRatio() * 100d) + "% (#" + stats.ctfWdlRank + ")", true); + } + } + + return statsEmbed; + } + + public DiscordEmbed getDetailedStatsEmbed(Player p, Season season){ + PlayerStats stats = db.getPlayerStats(p, season); + String country = "<:puma:849287183474884628>"; + if(!p.getCountry().equalsIgnoreCase("NOT_DEFINED")) { + country = ":flag_" + p.getCountry().toLowerCase() + ":"; + } + + DiscordEmbed statsEmbed = new DiscordEmbed(); + statsEmbed.color = 7056881; + statsEmbed.title = country + " \u200b \u200b " + p.getUrtauth(); + statsEmbed.thumbnail = p.getDiscordUser().getAvatarUrl(); + statsEmbed.description = "Season " + season.number + " (from to )"; + if (season.startdate == 0){ + statsEmbed.description = "Season " + season.number + " (until )"; + } + if (season.number == 0){ + statsEmbed.description = "All time stats"; + } + if (stats.ts_wdl.getTotal() == 0){ + statsEmbed.addField("\u200b", "*No TS games this season*", false); + } + else{ + statsEmbed.addField("\u200b", "``Team Survivor``", false); + statsEmbed.addField("Kills / Assists", stats.kills + "/" + stats.assists, true); + statsEmbed.addField("Deaths", String.valueOf(stats.deaths), true); + if (stats.kdrRank == -1) { + statsEmbed.addField("KDR", String.format("%.02f", stats.kdr), true); + + } else { + statsEmbed.addField("KDR", String.format("%.02f", stats.kdr) + " (#" + stats.kdrRank + ")", true); + } + + statsEmbed.addField("Wins", String.valueOf(stats.ts_wdl.win), true); + statsEmbed.addField("Defeats", String.valueOf(stats.ts_wdl.loss), true); + if (stats.wdlRank == -1) { + statsEmbed.addField("Win rate", Math.round(stats.ts_wdl.calcWinRatio() * 100d) + "%", true); + + } else { + statsEmbed.addField("Win rate", Math.round(stats.ts_wdl.calcWinRatio() * 100d) + "% (#" + stats.wdlRank + ")", true); + } + } + if (stats.ctf_wdl.getTotal() == 0){ + statsEmbed.addField("\u200b", "*No CTF games this season*", false); + } + else { + statsEmbed.addField("\u200b", "``Capture the Flag``", false); + statsEmbed.addField("Caps", String.valueOf(stats.caps), true); + statsEmbed.addField("Returns", String.valueOf(stats.returns), true); + if (stats.ctfRank == -1) { + statsEmbed.addField("Rating", String.format("%.02f", stats.ctf_rating), true); + + } else { + statsEmbed.addField("Rating", String.format("%.02f", stats.ctf_rating) + " (#" + stats.ctfRank + ")", true); + } + statsEmbed.addField("FC kills", String.valueOf(stats.fckills), true); + statsEmbed.addField("Stopped caps", String.valueOf(stats.stopcaps), true); + statsEmbed.addField("Protected flags", String.valueOf(stats.protflag), true); + statsEmbed.addField("Wins", String.valueOf(stats.ctf_wdl.win), true); + statsEmbed.addField("Defeats", String.valueOf(stats.ctf_wdl.loss), true); + if (stats.ctfWdlRank == -1) { + statsEmbed.addField("Win rate", Math.round(stats.ctf_wdl.calcWinRatio() * 100d) + "%", true); + + } else { + statsEmbed.addField("Win rate", Math.round(stats.ctf_wdl.calcWinRatio() * 100d) + "% (#" + stats.ctfWdlRank + ")", true); + } + } + + return statsEmbed; + } + + public void cmdTopCountries(int number) { + StringBuilder msg = new StringBuilder(Config.pkup_top5_header); + + ArrayList countries = db.getTopCountries(number); + if (countries.isEmpty()) { + bot.sendMsg(bot.getLatestMessageChannel(), msg + " None"); + } else { + + for(int i = 0; i < countries.size(); i++) { + String ranking = Config.pkup_getelo_country; + + ranking = ranking.replace(".position.", Integer.toString(i + 1)); + ranking = ranking.replace(".country.", Country.getCountryFlag(countries.get(i).country)); + ranking = ranking.replace(".elo.", countries.get(i).elo.toString()); + + msg.append("\n").append(ranking); + } + + bot.sendMsg(bot.getLatestMessageChannel(), msg.toString()); + } + } + + public void cmdGetMaps(Player player, boolean showZeroVote) { + Match activeMatch = playerInActiveMatch(player); + if (activeMatch != null){ + String msg = Config.pkup_map_list; + msg = msg.replace(".gametype.", activeMatch.getGametype().getName()); + msg = msg.replace(".maplist.", activeMatch.getMapVotes(true)); + bot.sendMsg(bot.getLatestMessageChannel(), msg); + return; + } + StringBuilder msg = new StringBuilder("None"); for (Gametype gametype : curMatch.keySet()) { - if (msg.equals("None")) { - msg = ""; + if (gametype.getName().startsWith("SCRIM") && curMatch.get(gametype).getPlayerCount() == 0){ + continue; + } + if (msg.toString().equals("None")) { + msg = new StringBuilder(); } else { - msg += "\n"; + msg.append("\n"); } String mapString = Config.pkup_map_list; mapString = mapString.replace(".gametype.", gametype.getName()); - mapString = mapString.replace(".maplist.", curMatch.get(gametype).getMapVotes(false)); - msg += mapString; + mapString = mapString.replace(".maplist.", curMatch.get(gametype).getMapVotes(!showZeroVote)); + msg.append(mapString); } - bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + bot.sendMsg(bot.getLatestMessageChannel(), msg.toString()); } - - public void cmdMapVote(Player player, Gametype gametype, String mapname) { - if (gametype == null) { + public void cmdMapVote(Player player, Gametype gametype, String mapname, int number) { + // Use boost if the player has it + boolean bonus = false; + if (number == 0){ + if (player.getAdditionalMapVotes() == 0){ + bot.sendNotice(player.getDiscordUser(), Config.no_additonal_vote); + return; + } + number = player.getAdditionalMapVotes(); + bonus = true; + } + Match activeMatch = playerInActiveMatch(player); + Match m = null; + + if (activeMatch != null) { + m = activeMatch; + gametype = m.getGametype(); + } + + else if (gametype == null) { List matches = playerInMatch(player); if (matches.size() == 1) { gametype = matches.get(0).getGametype(); @@ -242,8 +753,11 @@ public void cmdMapVote(Player player, Gametype gametype, String mapname) { return; } } - if (curMatch.containsKey(gametype)) { - Match m = curMatch.get(gametype); + if (curMatch.containsKey(gametype) || m != null) { + if (m == null) { + m = curMatch.get(gametype); + } + if (m.getMatchState() == MatchState.Signup || m.getMatchState() == MatchState.AwaitingServer) { int counter = 0; GameMap map = null; @@ -257,30 +771,46 @@ public void cmdMapVote(Player player, Gametype gametype, String mapname) { bot.sendNotice(player.getDiscordUser(), Config.map_not_unique); } else if (counter == 0) { bot.sendNotice(player.getDiscordUser(), Config.map_not_found); + } else if (lastMapPlayed.get(gametype).name.equals(map.name)) { + bot.sendNotice(player.getDiscordUser(), Config.map_played_last_game); + } else if (map.bannedUntil >= System.currentTimeMillis()) { + bot.sendNotice(player.getDiscordUser(), Config.map_banned.replace(".remaining.", String.valueOf(map.bannedUntil / 1000))); } else { - m.voteMap(player, map); // handles sending a msg itself + m.voteMap(player, map, number, bonus); // handles sending a msg itself } } } } - + public void cmdStatus() { if (curMatch.isEmpty()) { - bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.pkup_match_unavi); + bot.sendMsg(bot.getLatestMessageChannel(), Config.pkup_match_unavi); return; } - String msg = "None"; + StringBuilder msg = new StringBuilder("None"); + boolean scrimEmpty = true; for (Match m : curMatch.values()) { - if (msg.equals("None")) { - msg = ""; + if (m.getGametype().getName().startsWith("SCRIM")) { + if (m.getPlayerCount() > 0){ + scrimEmpty = false; + } + else{ + continue; + } + } + if (msg.toString().equals("None")) { + msg = new StringBuilder(); } else { - msg += "\n"; + msg.append("\n"); } - msg += cmdStatus(m, null, false); + msg.append(cmdStatus(m, null, false)); } - bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + if (scrimEmpty){ + msg.append("\n" + Config.team_no_scrim); + } + bot.sendMsg(bot.getLatestMessageChannel(), msg.toString()); } - + public String cmdStatus(Match match, Player player, boolean shouldSend) { String msg = ""; int playerCount = match.getPlayerCount(); @@ -288,38 +818,49 @@ public String cmdStatus(Match match, Player player, boolean shouldSend) { msg = Config.pkup_status_noone; msg = msg.replace(".gametype.", match.getGametype().getName().toUpperCase()); msg = msg.replace("", match.getGametype().getName().toLowerCase()); - } else if (match.getMatchState() == MatchState.Signup){ + } else if (match.getMatchState() == MatchState.Signup || match.getMatchState() == MatchState.AwaitingServer){ msg = Config.pkup_status_signup; msg = msg.replace(".gametype.", match.getGametype().getName().toUpperCase()); msg = msg.replace(".playernumber.", String.valueOf(playerCount)); - msg = msg.replace(".maxplayer.", String.valueOf(match.getGametype().getTeamSize() * 2)); + int maxplayer = match.getGametype().getTeamSize() == 0 ? 1 : match.getGametype().getTeamSize() * 2; + msg = msg.replace(".maxplayer.", String.valueOf(maxplayer)); - String playernames = "None"; + StringBuilder playernames = new StringBuilder("None"); if (player == null) { for (Player p : match.getPlayerList()) { - if (playernames.equals("None")) { - playernames = p.getUrtauth(); + if (playernames.toString().equals("None")) { + playernames = new StringBuilder(p.getUrtauth()); } else { - playernames += " " + p.getUrtauth(); + playernames.append(" ").append(p.getUrtauth()); + } + } + for (Team teamQueued : teamsQueued.keySet()){ + if (teamsQueued.get(teamQueued).equals(match.getGametype())){ + playernames.append("\n__Awaiting team__: "); + playernames.append(teamQueued.getTeamStringNoMention()); } } } else { - playernames = player.getDiscordUser().getMentionString(); - playernames += (match.isInMatch(player)) ? " added." : " removed."; + playernames = new StringBuilder(player.getDiscordUser().getMentionString()); + playernames.append((match.isInMatch(player)) ? " added." : " removed."); } - msg = msg.replace(".playerlist.", playernames); + msg = msg.replace(".playerlist.", playernames.toString()); - } else if (match.getMatchState() == MatchState.AwaitingServer){ + } + if (shouldSend) { + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + } + if (match.getMatchState() == MatchState.AwaitingServer && shouldSend && match.getGametype().getTeamSize() > 1) { msg = Config.pkup_status_server; msg = msg.replace(".gametype.", match.getGametype().getName().toUpperCase()); - } - if (shouldSend) { + msg = msg.replace(".votes.", match.getMapVotes(true)); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); } return msg; } - + public void cmdSurrender(Player player) { Match match = playerInActiveMatch(player); if (match != null && match.getMatchState() == MatchState.Live) { @@ -328,16 +869,14 @@ public void cmdSurrender(Player player) { else bot.sendNotice(player.getDiscordUser(), Config.player_not_in_match); } - public boolean cmdReset(String cmd) { - return cmdReset(cmd, null); + public void cmdReset(String cmd) { + cmdReset(cmd, null); } - public boolean cmdReset(String cmd, String mode) { + public void cmdReset(String cmd, String mode) { if (cmd.equals("all")) { - Iterator iter = ongoingMatches.iterator(); List toRemove = new ArrayList(); - while (iter.hasNext()) { - Match match = iter.next(); + for (Match match : ongoingMatches) { match.reset(); toRemove.add(match); } @@ -347,7 +886,6 @@ public boolean cmdReset(String cmd, String mode) { createCurrentMatches(); } bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.pkup_reset_all); - return true; } else if (cmd.equals("cur")) { Gametype gt = getGametypeByString(mode); if (gt != null) { @@ -360,7 +898,6 @@ public boolean cmdReset(String cmd, String mode) { createCurrentMatches(); } bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.pkup_reset_cur); - return true; } else { Gametype gt = getGametypeByString(cmd); if (gt != null) { @@ -369,46 +906,36 @@ public boolean cmdReset(String cmd, String mode) { bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.pkup_reset_type.replace(".gametype.", gt.getName())); } else { try { - int idx = Integer.valueOf(cmd); - Iterator iter = ongoingMatches.iterator(); - while (iter.hasNext()) { - Match match = iter.next(); + int idx = Integer.parseInt(cmd); + for (Match match : ongoingMatches) { if (match.getID() == idx) { match.reset(); ongoingMatches.remove(match); bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.pkup_reset_id.replace(".id.", cmd)); - return true; } } - } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } } - return false; } - - public boolean cmdGetData(DiscordUser user, String id) { - String msg = "Match not found."; - try { - int i_id = Integer.valueOf(id); - for (Match match : ongoingMatches) { - if (match.getID() == i_id) { - msg = Config.pkup_pw; - msg = msg.replace(".server.", match.getServer().getAddress()); - msg = msg.replace(".password.", match.getServer().password); - bot.sendMsg(user, msg); - return true; - } - } - } catch (NumberFormatException e) { - LOGGER.log(Level.WARNING, "Exception: ", e); + + public void cmdGetData(DiscordChannel channel) { + if (ongoingMatches.isEmpty()){ + String msg = "No active matches."; + bot.sendMsg(channel, msg); + } + + for (Match match : ongoingMatches) { + String msg = "**" + match.getGametype().getName() + " Match #" + String.valueOf(match.getID()) + "** " + Config.pkup_pw + " RCON: " + match.getServer().rconpassword + " Server #" + match.getServer().id; + msg = msg.replace(".server.", match.getServer().getAddress()); + msg = msg.replace(".password.", match.getServer().password); + bot.sendMsg(channel, msg); } - bot.sendMsg(user, msg); - return false; } - + public boolean cmdEnableMap(String mapname, String gametype) { Gametype gt = getGametypeByString(gametype); if (gt == null) return false; @@ -431,7 +958,7 @@ public boolean cmdEnableMap(String mapname, String gametype) { } return true; } - + public boolean cmdDisableMap(String mapname, String gametype) { Gametype gt = getGametypeByString(gametype); if (gt == null) return false; @@ -445,14 +972,13 @@ public boolean cmdDisableMap(String mapname, String gametype) { } return false; } - + public boolean cmdEnableGametype(String gametype, String teamSize) { try { - int i_teamSize = Integer.valueOf(teamSize); + int i_teamSize = Integer.parseInt(teamSize); Gametype gt = getGametypeByString(gametype); if (gt == null) { gt = new Gametype(gametype.toUpperCase(), i_teamSize, true); - db.loadGameConfig(gt); } gt.setTeamSize(i_teamSize); gt.setActive(true); @@ -466,10 +992,11 @@ public boolean cmdEnableGametype(String gametype, String teamSize) { return true; } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); return false; } } - + public boolean cmdDisableGametype(String gametype) { Gametype gt = getGametypeByString(gametype); if (gt == null) return false; @@ -480,17 +1007,16 @@ public boolean cmdDisableGametype(String gametype) { curMatch.remove(gt); return true; } - + public boolean cmdAddGameConfig(String gametype, String command) { Gametype gt = getGametypeByString(gametype); if (gt == null) return false; gt.addConfig(command); - - db.updateGametype(gt); + return true; } - + public boolean cmdRemoveGameConfig(String gametype, String command) { Gametype gt = getGametypeByString(gametype); if (gt == null) return false; @@ -500,56 +1026,59 @@ public boolean cmdRemoveGameConfig(String gametype, String command) { db.updateGametype(gt); return true; } - + public boolean cmdListGameConfig(DiscordChannel channel, String gametype) { Gametype gt = getGametypeByString(gametype); if (gt == null) return false; - String configlist = ""; + StringBuilder configlist = new StringBuilder(); for (String config : gt.getConfig()) { - if (!configlist.isEmpty()) { - configlist += "\n"; + if (configlist.length() > 0) { + configlist.append("\n"); } - configlist += config; + configlist.append(config); } String msg = Config.pkup_config_list; msg = msg.replace(".gametype.", gt.getName()); - msg = msg.replace(".configlist.", configlist); + msg = msg.replace(".configlist.", configlist.toString()); bot.sendMsg(channel, msg); return true; -// return !configlist.isEmpty(); // we sent the info anyways, so its fine } - - public boolean cmdAddServer(String serveraddr, String rcon) { + + public boolean cmdAddServer(String serveraddr, String rcon, String str_region) { try { String ip = serveraddr; int port = 27960; if (serveraddr.contains(":")) { String[] servers = serveraddr.split(":"); ip = servers[0]; - port = Integer.valueOf(servers[1]); + port = Integer.parseInt(servers[1]); } for (Server s : serverList) { if (s.IP.equals(ip) && s.port == port) { return false; } } - Server server = new Server(-1, ip, port, rcon, "???", true); + + Region region = Region.valueOf(str_region); + Server server = new Server(-1, ip, port, rcon, "???", true, region); + db.createServer(server); serverList.add(server); checkServer(); return true; - } catch (NumberFormatException e) { + } catch (IllegalArgumentException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); return false; } } public boolean cmdServerActivation(String id, boolean active) { try { - int idx = Integer.valueOf(id); + int idx = Integer.parseInt(id); for (Server server : serverList) { if (server.id == idx && !server.isTaken() && server.active != active) { server.active = active; @@ -560,13 +1089,14 @@ public boolean cmdServerActivation(String id, boolean active) { } } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return false; } public boolean cmdServerChangeRcon(String id, String rcon) { try { - int idx = Integer.valueOf(id); + int idx = Integer.parseInt(id); for (Server server : serverList) { if (server.id == idx) { server.rconpassword = rcon; @@ -576,13 +1106,14 @@ public boolean cmdServerChangeRcon(String id, String rcon) { } } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return false; } public boolean cmdServerSendRcon(String id, String rconString) { try { - int idx = Integer.valueOf(id); + int idx = Integer.parseInt(id); for (Server server : serverList) { if (server.id == idx) { server.sendRcon(rconString); @@ -591,87 +1122,163 @@ public boolean cmdServerSendRcon(String id, String rconString) { } } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return false; } - - public boolean cmdServerList(DiscordChannel channel) { - String msg = "None"; + + public void cmdServerList(DiscordChannel channel) { + StringBuilder msg = new StringBuilder("None"); for (Server server : serverList) { - if (msg.equals("None")) { - msg = server.toString(); + if (msg.toString().equals("None")) { + msg = new StringBuilder(server.toString()); } else { - msg += "\n" + server.toString(); + msg.append("\n").append(server.toString()); } } - bot.sendMsg(channel, msg); - return true; + bot.sendMsg(channel, msg.toString()); } - - public boolean cmdMatchList(DiscordChannel channel) { - String msg = "None"; + + public void cmdMatchList(DiscordChannel channel) { + StringBuilder msg = new StringBuilder("None"); for (Match match : curMatch.values()) { - if (msg.equals("None")) { - msg = match.toString(); + if (msg.toString().equals("None")) { + msg = new StringBuilder(match.toString()); } else { - msg += "\n" + match.toString(); + msg.append("\n").append(match.toString()); } } for (Match match : ongoingMatches) { - if (msg.equals("None")) { - msg = match.toString(); + if (msg.toString().equals("None")) { + msg = new StringBuilder(match.toString()); } else { - msg += "\n" + match.toString(); + msg.append("\n").append(match.toString()); } } - bot.sendMsg(channel, msg); - return true; + bot.sendMsg(channel, msg.toString()); } - - public boolean cmdLive() { + + public void cmdLive(DiscordChannel channel) { String msg = "No live matches found."; + DiscordEmbed scoreBoardLinkEmbed = new DiscordEmbed(); for (Match match : ongoingMatches) { - if (msg.equals("No live matches found.")) { - msg = match.getMatchInfo(); - } else { - msg += "\n" + match.getMatchInfo(); + msg = match.getMatchInfo(); + if (match.getMatchState() == MatchState.AwaitingServer || match.getServer().getServerMonitor() == null || match.liveScoreMsgs.isEmpty()) { + bot.sendMsg(bot.getLatestMessageChannel(), msg); + } + else { + String scoreBoardLink = ""; + for (DiscordMessage liveScoreMsg : match.liveScoreMsgs){ + if(liveScoreMsg.channel.parent_id.equals(channel.id)){ + scoreBoardLink = "[Live scoreboard](https://discord.com/channels/" + liveScoreMsg.channel.guild_id + "/" + liveScoreMsg.channel.id + "/" + liveScoreMsg.id + ")"; + break; + } + } + StringBuilder embedDescription = new StringBuilder(scoreBoardLink); + + scoreBoardLinkEmbed.color = 7056881; + + if (match.getGtvServer() != null) { + embedDescription.append("\n").append(Config.pkup_go_pub_calm); + } + + scoreBoardLinkEmbed.description = embedDescription.toString(); + bot.sendMsg(bot.getLatestMessageChannel(), msg, scoreBoardLinkEmbed); } } - bot.sendMsg(bot.getLatestMessageChannel(), msg); - return true; + + if (msg.equals("No live matches found.")) { + bot.sendMsg(bot.getLatestMessageChannel(), msg); + } } - - public boolean cmdDisplayMatch(String matchid) { + + public void cmdDisplayMatch(String matchid) { try { - int idx = Integer.valueOf(matchid); + int idx = Integer.parseInt(matchid); for (Match match : ongoingMatches) { if (match.getID() == idx) { bot.sendMsg(bot.getLatestMessageChannel(), match.getMatchInfo()); - return true; + return; } } Match match = db.loadMatch(idx); // TODO: cache? if (match != null) { - bot.sendMsg(bot.getLatestMessageChannel(), match.getMatchInfo()); - return true; + bot.sendMsg(bot.getLatestMessageChannel(), null, match.getMatchEmbed(true)); + return; + } + + } catch (NumberFormatException e) { + bot.sendMsg(bot.getLatestMessageChannel(), "Match not found."); + } + bot.sendMsg(bot.getLatestMessageChannel(), "Match not found."); + } + + public void cmdDisplayLastMatch() { + if (!ongoingMatches.isEmpty()){ + bot.sendMsg(bot.getLatestMessageChannel(), "Can't display the match when a game is active."); + return; + } + try { + Match match = db.loadLastMatch(); + if (match != null) { + bot.sendMsg(bot.getLatestMessageChannel(), null, match.getMatchEmbed(true)); + return; } } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } bot.sendMsg(bot.getLatestMessageChannel(), "Match not found."); - return false; } - + + public void cmdDisplayLastMatchPlayer(Player p) { + if (!ongoingMatches.isEmpty()){ + bot.sendMsg(bot.getLatestMessageChannel(), "Can't display the match when a game is active."); + return; + } + try { + Match match = db.loadLastMatchPlayer(p); + if (match != null) { + bot.sendMsg(bot.getLatestMessageChannel(), null, match.getMatchEmbed(true)); + return; + } + + } catch (NumberFormatException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + bot.sendMsg(bot.getLatestMessageChannel(), "Match not found."); + } + + public void showLastMatchPlayer(DiscordInteraction interaction, Player p) { + try { + Match match = db.loadLastMatchPlayer(p); + if (match != null) { + interaction.respond(null, match.getMatchEmbed(true)); + return; + } + + } catch (NumberFormatException e) { + LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); + } + interaction.respond("Match not found."); + } + + public void cmdResetElo(){ + db.resetElo(); + } + // Matchcreation - + private void createCurrentMatches() { for (Gametype gametype : curMatch.keySet()) { createMatch(gametype); } } - + private void createMatch(Gametype gametype) { List gametypeMapList = new ArrayList(); for (GameMap map : mapList) { @@ -690,10 +1297,11 @@ public void requestServer(Match match) { checkServer(); } } - + public void cancelRequestServer(Match match) { - if (awaitingServer.contains(match)) { - awaitingServer.remove(match); + awaitingServer.remove(match); + if (match.getServer() != null && match.getServer().isTaken()){ + match.getServer().free(); } } @@ -702,31 +1310,86 @@ public void matchStarted(Match match) { ongoingMatches.add(match); } - public void matchEnded(Match match) { + public void matchEnded() { // matchRemove(match); // dont remove as this can be called while in a loop checkServer(); } public void matchRemove(Match match) { - if (ongoingMatches.contains(match)) { - ongoingMatches.remove(match); - } + ongoingMatches.remove(match); } private void checkServer() { - for (Server server : serverList) { - if (server.active && !server.isTaken() && !awaitingServer.isEmpty()) { - Match m = awaitingServer.poll(); - if (m != null && m.getMatchState() == MatchState.AwaitingServer) { - m.launch(server); + if(!awaitingServer.isEmpty()) + { + Match m = awaitingServer.poll(); + + if(m != null) + { + Server bs; + if (dynamicServers || m.getGametype().getTeamSize() == 0){ + bs = FtwglAPI.spawnDynamicServer(m.getPlayerList()); + if (bs != null) { + String spawnMsg = Config.pkup_go_pub_servspawn; + spawnMsg = spawnMsg.replace(".flag.", Country.getCountryFlag(bs.country)); + spawnMsg = spawnMsg.replace(".city.", bs.city); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), spawnMsg); + } + else { + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.pkup_go_pub_noserv); + m.reset(); + } + } + else{ + bs = getBestServer(m.getPreferredServerRegion()); + if (bs != null){ + String spawnMsg = Config.pkup_go_pub_requestserver; + spawnMsg = spawnMsg.replace(".flag.", bs.getRegionFlag(false, false)); + spawnMsg = spawnMsg.replace(".region.", bs.region.name()); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), spawnMsg); + } + } + + if(bs != null && m.getMatchState() == MatchState.AwaitingServer) + { + m.launch(bs); + } + else + { + for (Server server : serverList) { // Use NAE server by default when the best region is not avi + if (server.active && !server.isTaken() && server.isOnline() && m.getMatchState() == MatchState.AwaitingServer && server.region == Region.NAE) { + m.launch(server); + return; + } + } + for (Server server : serverList) { // If no NAE servers are avi take the first avi + if (server.active && !server.isTaken() && server.isOnline() && m.getMatchState() == MatchState.AwaitingServer) { + m.launch(server); + return; + } + } } } } } - - + + private Server getBestServer(Region r) + { + Server bestServer = null; + + for (Server server : serverList) { + if (server.region == r && server.active && !server.isTaken() && server.isOnline()) { + bestServer = server; + break; + } + } + + return bestServer; + } + + // ROLES & CHANNEL - + public boolean addRole(PickupRoleType type, DiscordRole role) { if (!roles.containsKey(type)) { roles.put(type, new ArrayList()); @@ -738,7 +1401,7 @@ public boolean addRole(PickupRoleType type, DiscordRole role) { } return false; } - + public boolean removeRole(PickupRoleType type, DiscordRole role) { if (roles.containsKey(type)) { roles.get(type).remove(role); @@ -747,7 +1410,7 @@ public boolean removeRole(PickupRoleType type, DiscordRole role) { } return false; } - + public boolean addChannel(PickupChannelType type, DiscordChannel channel) { if (!channels.containsKey(type)) { channels.put(type, new ArrayList()); @@ -759,7 +1422,7 @@ public boolean addChannel(PickupChannelType type, DiscordChannel channel) { } return false; } - + public boolean removeChannel(PickupChannelType type, DiscordChannel channel) { if (channels.containsKey(type)) { channels.get(type).remove(channel); @@ -770,7 +1433,7 @@ public boolean removeChannel(PickupChannelType type, DiscordChannel channel) { } // AFK CHECK - + public void afkCheck() { Set playerList = new HashSet(); for (Match m : curMatch.values()) { @@ -778,27 +1441,54 @@ public void afkCheck() { } for (Player p : playerList) { - long latestAFKmsg = p.getDiscordUser().statusChangeTime > p.getLastMessage() ? p.getDiscordUser().statusChangeTime : p.getLastMessage(); - long afkKickTime = latestAFKmsg + 30 * 60 * 1000; - long afkReminderTime = latestAFKmsg + 25 * 60 * 1000; - if (afkKickTime < System.currentTimeMillis() && isPlayerStatusAFK(p)) { + if (playerInActiveMatch(p) != null) { + continue; + } + + long latestAFKmsg = p.getLastMessage(); + long afkKickTime = latestAFKmsg + 20 * 60 * 1000; + long afkReminderTime = latestAFKmsg + 17 * 60 * 1000; + + if (afkKickTime < System.currentTimeMillis()) { LOGGER.info("AFK: REMOVE - " + p.getUrtauth() + ": " + afkKickTime + " > " + System.currentTimeMillis()); cmdRemovePlayer(p, null); - } else if (afkReminderTime < System.currentTimeMillis() && !p.getAfkReminderSent() && isPlayerStatusAFK(p)) { + } else if (afkReminderTime < System.currentTimeMillis() && !p.getAfkReminderSent()) { p.setAfkReminderSent(true); LOGGER.info("AFK: REMINDER - " + p.getUrtauth() + ": " + afkKickTime + " > " + System.currentTimeMillis()); String msg = Config.afk_reminder; msg = msg.replace(".user.", p.getDiscordUser().getMentionString()); bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); - + } + } + + for (Match m :ongoingMatches) { + if (m.getMatchState() != MatchState.AwaitingServer) { + continue; + } + + long lastPickTime = m.getTimeLastPick(); + long pickKickTime = lastPickTime + 3 * 60 * 1000; + long pickReminderTime = lastPickTime + 2 * 60 * 1000; + + if (pickKickTime < System.currentTimeMillis()) { + String msg = Config.pick_reset; + msg = msg.replace(".matchid.", String.valueOf(db.getLastMatchID() + 1)); + msg = msg.replace(".user.", m.getCaptainsTurn().getDiscordUser().getMentionString()); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + autoBanPlayer(m.getCaptainsTurn(), BanReason.NOSHOW); + m.reset(); + ongoingMatches.remove(m); + } else if (pickReminderTime < System.currentTimeMillis() && !m.getPickReminderSent()) { + m.setPickReminderSent(true); + LOGGER.info("PICK: REMINDER - " + m.getCaptainsTurn().getUrtauth() + ": " + pickKickTime + " > " + System.currentTimeMillis()); + String msg = Config.pick_reminder; + msg = msg.replace(".user.", m.getCaptainsTurn().getDiscordUser().getMentionString()); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); } } } - - private boolean isPlayerStatusAFK(Player player) { - return player.getDiscordUser().status != DiscordUserStatus.online; - } - + + public void autoBanPlayer(Player player, BanReason reason) { String[] durationString = banDuration.get(reason); PlayerBan latestBan = player.getLatestBan(); @@ -806,7 +1496,7 @@ public void autoBanPlayer(Player player, BanReason reason) { int strength = 0; if (latestBan != null) { long latestBanDuration = latestBan.endTime - latestBan.startTime; - latestBanDuration = Math.max(latestBanDuration * 2, parseDurationFromString("1w")); + latestBanDuration = Math.max(latestBanDuration * 2, parseDurationFromString("1M")); strength = player.getPlayerBanCountSince(System.currentTimeMillis() - latestBanDuration); strength = Math.min(strength, durationString.length - 1); } @@ -814,11 +1504,11 @@ public void autoBanPlayer(Player player, BanReason reason) { banPlayer(player, reason, duration); } - + public void banPlayer(Player player, BanReason reason, long duration) { // add reminaing bantime to the new ban - long endTime = -1L; + long endTime; if (player.isBanned()) { endTime = player.getLatestBan().endTime + duration; } else { @@ -840,38 +1530,89 @@ public void banPlayer(Player player, BanReason reason, long duration) { match.removePlayer(player, true); } } - + + public void UnbanPlayer(Player player) { + + if (player.isBanned()) { + + player.forgiveBan(); + + bot.sendMsg(getChannelByType(PickupChannelType.ADMIN), printUnbanInfo(player)); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), printUnbanInfo(player)); + } else { + // Player is not banned + bot.sendMsg(getChannelByType(PickupChannelType.ADMIN), printPlayerNotBannedInfo(player)); + } + } + + public String printBanInfo(Player player) { PlayerBan ban = player.getLatestBan(); - - if (ban == null || ban.endTime <= System.currentTimeMillis()) { - String msg = Config.not_banned; + + String msg = Config.not_banned; + if (ban == null || ban.endTime <= System.currentTimeMillis() || ban.forgiven) { + msg = msg.replace(".user.", player.getDiscordUser().getMentionString()); + msg = msg.replace(".urtauth.", player.getUrtauth()); + } + else{ + String time = parseStringFromDuration(ban.endTime - System.currentTimeMillis()); + + msg = Config.is_banned; msg = msg.replace(".user.", player.getDiscordUser().getMentionString()); msg = msg.replace(".urtauth.", player.getUrtauth()); + msg = msg.replace(".reason.", ban.reason.name()); + msg = msg.replace(".time.", time); + } + + ArrayList past_bans = player.getPlayerBanListSince(System.currentTimeMillis() - parseDurationFromString("2M")); + if (past_bans.size() == 0){ return msg; } - - String time = parseStringFromDuration(ban.endTime - System.currentTimeMillis()); - - String msg = Config.is_banned; + + msg = msg + "\n\n"; + msg = msg + Config.ban_history; + String ban_item; + for (PlayerBan past_ban : past_bans){ + ban_item = '\n' + Config.ban_history_item; + + ban_item = ban_item.replace(".date.", String.valueOf(past_ban.startTime / 1000)); + ban_item = ban_item.replace(".duration.", parseStringFromDuration(past_ban.endTime - past_ban.startTime)); + ban_item = ban_item.replace(".reason.", past_ban.reason.name()); + + if (past_ban.forgiven){ + ban_item = ban_item + " *(forgiven)*"; + } + msg = msg + ban_item; + } + + return msg; + } + + public String printUnbanInfo(Player player) { + String msg = Config.is_unbanned; + msg = msg.replace(".user.", player.getDiscordUser().getMentionString()); + msg = msg.replace(".urtauth.", player.getUrtauth()); + return msg; + } + + public String printPlayerNotBannedInfo(Player player) { + String msg = Config.is_notbanned; msg = msg.replace(".user.", player.getDiscordUser().getMentionString()); msg = msg.replace(".urtauth.", player.getUrtauth()); - msg = msg.replace(".reason.", ban.reason.name()); - msg = msg.replace(".time.", time); return msg; } // HELPER - + public static long parseDurationFromString(String string) { long total = 0; - String curDuration = ""; + StringBuilder curDuration = new StringBuilder(); for (int i = 0; i < string.length(); ++i) { if (Character.isDigit(string.charAt(i))) { - curDuration += String.valueOf(string.charAt(i)); + curDuration.append(string.charAt(i)); } else { - long duration = Long.valueOf(curDuration); + long duration = Long.parseLong(curDuration.toString()); switch (string.charAt(i)) { case 'y': duration *= 12; case 'M': duration *= 4; @@ -882,12 +1623,12 @@ public static long parseDurationFromString(String string) { case 's': duration *= 1000; } total += duration; - curDuration = ""; + curDuration = new StringBuilder(); } } return total; } - + public static String parseStringFromDuration(long duration) { String string = ""; @@ -903,12 +1644,12 @@ public static String parseStringFromDuration(long duration) { long year = month * 12; long curAmount; - if ((curAmount = duration / year) > 0 && acc > 0) { + if ((curAmount = duration / year) > 0) { string += curAmount + "y"; duration = duration % year; --acc; } - if ((curAmount = duration / month) > 0 && acc > 0) { + if ((curAmount = duration / month) > 0) { string += curAmount + "M"; duration = duration % month; --acc; @@ -935,11 +1676,9 @@ public static String parseStringFromDuration(long duration) { } if ((curAmount = duration / second) > 0 && acc > 0) { string += curAmount + "s"; - duration = duration % second; --acc; } - return string; } @@ -951,7 +1690,7 @@ public Gametype getGametypeByString(String mode) { } return null; } - + public List playerInMatch(Player player) { List matchlist = new ArrayList(); for (Gametype gt : curMatch.keySet()) { @@ -962,7 +1701,7 @@ public List playerInMatch(Player player) { } return matchlist; } - + public Match playerInActiveMatch(Player player) { for (Match m : ongoingMatches) { if (m.isInMatch(player)) { @@ -971,7 +1710,7 @@ public Match playerInActiveMatch(Player player) { } return null; } - + public Match playerInMatch(Gametype gametype, Player player) { if (curMatch.containsKey(gametype)) { if (curMatch.get(gametype).isInMatch(player)) { @@ -980,10 +1719,6 @@ public Match playerInMatch(Gametype gametype, Player player) { } return null; } - - public boolean isLocked() { - return locked; - } public List getAdminList() { List list = new ArrayList(); @@ -995,7 +1730,7 @@ public List getAdminList() { } return list; } - + public List getSuperAdminList() { List list = new ArrayList(); if (roles.containsKey(PickupRoleType.SUPERADMIN)) { @@ -1010,7 +1745,7 @@ public List getRoleByType(PickupRoleType type) { } return new ArrayList(); } - + public List getChannelByType(PickupChannelType type) { if (channels.containsKey(type)) { return channels.get(type); @@ -1044,4 +1779,809 @@ public GameMap getMapByName(String name) { return null; } + public void setLastMapPlayed(Gametype gt, GameMap map) { + lastMapPlayed.remove(gt); + lastMapPlayed.put(gt, map); + } + + public void removeLastMapPlayed(Gametype gt){ + lastMapPlayed.remove(gt); + } + + public GameMap getLastMapPlayed(Gametype gt) { + return lastMapPlayed.get(gt); + } + + public Server setupGTV() { + for (Server gtv : gtvServerList) { + if (!gtv.isTaken()) { + gtv.take(); + return gtv; + } + } + return null; + } + + public void restartApplication() throws URISyntaxException, IOException + { + final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + final File currentJar = new File(PickupBotDiscordMain.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + + /* is it a jar file? */ + if(!currentJar.getName().endsWith(".jar")) + return; + + /* Build command: java -jar application.jar */ + final ArrayList command = new ArrayList(); + command.add(javaBin); + command.add("-jar"); + command.add(currentJar.getPath()); + + final ProcessBuilder builder = new ProcessBuilder(command); + builder.start(); + System.exit(0); + } + + public void cmdLaunchAC(DiscordInteraction interaction, Player player, int matchId, String ip, String password){ + for (Match match : ongoingMatches) { + if (match.getID() == matchId) { + for (Player playerInMatch : match.getPlayerList()){ + if (playerInMatch.equals(player)){ + String response = FtwglAPI.launchAC(player, ip, password); + interaction.respond(response); + return; + } + } + interaction.respond(Config.ftw_playernotinmatch); + return; + } + } + + interaction.respond(Config.ftw_matchnotfound); + } + + public void invitePlayersToTeam(Player captain, List invitedPlayers) { + Team team = null; + for (Team activeTeam : activeTeams){ + if (activeTeam.getCaptain().equals(captain)){ + team = activeTeam; + break; + } + else if (activeTeam.isInTeam(captain)){ + bot.sendNotice(captain.getDiscordUser(), Config.team_involved_other); + return; + } + } + if (team == null){ + team = new Team(this, captain); + activeTeams.add(team); + } + + for (Player invitedPlayer : invitedPlayers){ + if (team.isFull()){ + bot.sendNotice(captain.getDiscordUser(), Config.team_is_full); + return; + } + if (team.isInTeam(invitedPlayer)){ + bot.sendNotice(captain.getDiscordUser(), Config.team_already_in.replace(".auth.", invitedPlayer.getUrtauth())); + continue; + } + if (team.isInvitedToTeam(invitedPlayer)){ + bot.sendNotice(captain.getDiscordUser(), Config.team_already_invited.replace(".auth.", invitedPlayer.getUrtauth())); + continue; + } + boolean alreadyInvolved = false; + for (Team activeTeam : activeTeams) { + if (activeTeam.isInTeam(invitedPlayer)) { + alreadyInvolved = true; + break; + } + } + if (alreadyInvolved){ + bot.sendNotice(captain.getDiscordUser(), Config.team_already_involved.replace(".auth.", invitedPlayer.getUrtauth())); + continue; + } + team.invitePlayer(invitedPlayer); + } + cmdRemovePlayer(captain, null); + } + + public void leaveTeam(Player player){ + for (Team activeTeam : activeTeams){ + if (activeTeam.getCaptain().equals(player)){ + activeTeams.remove(activeTeam); + activeTeam.archive(); + bot.sendNotice(player.getDiscordUser(), Config.team_leave_captain); + + cmdRemovePlayer(player, null); + return; + } + else if (activeTeam.isInTeam(player)){ + activeTeam.removePlayer(player); + bot.sendNotice(player.getDiscordUser(), Config.team_leave.replace(".captain.", activeTeam.getCaptain().getUrtauth())); + cmdRemovePlayer(player, null); + return; + } + } + bot.sendNotice(player.getDiscordUser(), Config.team_noteam); + } + + public void answerTeamInvite(DiscordInteraction interaction, Player player, int answer, Player captain, Player invitedPlayer){ + Team team = null; + for (Team activeTeam : activeTeams) { + if (activeTeam.getCaptain().equals(captain)) { + team = activeTeam; + } + } + if (team == null){ + interaction.respond(Config.team_error_active); + interaction.message.delete(); + return; + } + + if (player.equals(captain) && answer == 2){ + team.cancelInvitation(invitedPlayer); + interaction.respond(null); + interaction.message.delete(); + return; + } + if (!player.equals(invitedPlayer)){ + interaction.respond(Config.team_error_invite.replace(".player.", invitedPlayer.getUrtauth())); + return; + } + + if (answer == 1){ + if (team.isFull()){ + interaction.respond(Config.team_is_full); + return; + } + team.acceptInvitation(invitedPlayer); + + } + else{ + team.declineInvitation(invitedPlayer); + } + interaction.respond(null); + interaction.message.delete(); + } + + public void removeTeamMember(DiscordInteraction interaction, Player player, Player captain, Player playerToRemove){ + if (!player.equals(playerToRemove) && !player.equals(captain)){ + interaction.respond(Config.team_error_remove); + return; + } + + Team team = null; + for (Team activeTeam : activeTeams) { + if (activeTeam.getCaptain().equals(captain)) { + team = activeTeam; + } + } + if (team == null){ + interaction.respond(Config.team_error_active); + return; + } + + team.removePlayer(playerToRemove); + interaction.respond(null); + interaction.message.delete(); + cmdRemovePlayer(playerToRemove, null); + } + + public void cmdAddTeam(Player player, Gametype gt, boolean forced){ + Team team = null; + for (Team activeTeam : activeTeams){ + if (activeTeam.getCaptain().equals(player) || (forced && activeTeam.isInTeam(player))){ + team = activeTeam; + break; + } + } + if (team == null){ + bot.sendNotice(player.getDiscordUser(), Config.team_noteam_captain); + return; + } + +// if (gt.getTeamSize() < 2){ +// bot.sendNotice(player.getDiscordUser(), Config.team_error_wrong_gt); +// return; +// } + + if (team.getPlayers().size() != gt.getTeamSize()){ + bot.sendNotice(player.getDiscordUser(), Config.team_error_teamsize.replace(".teamsize.", String.valueOf(gt.getTeamSize()))); + return; + } + + Match m = curMatch.get(gt); + + for (Player teamPlayer : team.getPlayers()){ + if (teamPlayer.isBanned()) { + bot.sendMsg(bot.getLatestMessageChannel(), printBanInfo(teamPlayer)); + return; + } + if (playerInActiveMatch(teamPlayer) != null) { + bot.sendNotice(teamPlayer.getDiscordUser(), Config.player_already_match); + return; + } + if (curMatch.containsKey(gt)) { + if (m.getMatchState() != MatchState.Signup || m.isInMatch(teamPlayer) || playerInActiveMatch(teamPlayer) != null) { + m.removePlayer(teamPlayer, false); + } + } + } + + teamsQueued.put(team, gt); + + String addedMsg = Config.team_added; + addedMsg = addedMsg.replace(".gamemode.", gt.getName()); + addedMsg = addedMsg.replace(".team.", team.getTeamString()); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), addedMsg); + checkTeams(); + } + + public void checkTeams(){ + for (Team queuedTeam : teamsQueued.keySet()) { + Gametype gt = teamsQueued.get(queuedTeam); + Match m = curMatch.get(gt); + if (curMatch.containsKey(gt)) { + if (m.getPlayerCount() <= gt.getTeamSize()) { + m.addSquad(queuedTeam); + teamsQueued.remove(queuedTeam); + for (Player teamPlayer : queuedTeam.getPlayers()) { + m.addPlayer(teamPlayer); + teamPlayer.afkCheck(); + } + } + } + } + } + + public void cmdRemoveTeam(Player player, boolean shouldSpam){ + Team team = null; + for (Team activeTeam : activeTeams){ + if (activeTeam.isInTeam(player)){ + team = activeTeam; + break; + } + } + if (team == null){ + if (shouldSpam){ + bot.sendNotice(player.getDiscordUser(), Config.team_noteam); + } + return; + } + + if (playerInActiveMatch(player) != null) { + if (shouldSpam) { + bot.sendNotice(player.getDiscordUser(), Config.player_already_match); + } + return; + } + + teamsQueued.remove(team); + + for (Match match : curMatch.values()) { + for (Player teamPlayer : team.getPlayers()){ + match.removePlayer(teamPlayer, shouldSpam); + } + match.removeSquad(team); + } + + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), Config.team_removed_queue.replace(".team.", team.getTeamString())); + } + + public void cmdPrintTeam(Player player){ + Team team = null; + for (Team activeTeam : activeTeams){ + if (activeTeam.isInTeam(player)){ + team = activeTeam; + break; + } + } + if (team == null){ + bot.sendNotice(player.getDiscordUser(), Config.team_noteam); + return; + } + + bot.sendNotice(player.getDiscordUser(), Config.team_print_info.replace(".team.", team.getTeamStringNoMention())); + } + + public void cmdPrintTeams(Player player){ + String msg = Config.team_print_all; + for (Team activeTeam : activeTeams){ + msg = msg + "\n" + activeTeam.getTeamStringNoMention(); + } + if (msg.equals(Config.team_print_all)){ + bot.sendMsg(player.getLastPublicChannel(), Config.team_print_noteam); + return; + } + + bot.sendMsg(player.getLastPublicChannel(), msg); + } + + public void cmdEnableDynamicServer(){ + dynamicServers = true; + } + + public void cmdDisableDynamicServer(){ + dynamicServers = false; + } + + public boolean getDynamicServers(){ + return dynamicServers; + } + + public void cmdGetPingURL(Player player) { + bot.sendNotice(player.getDiscordUser(), Config.ftw_error_noping); + bot.sendMsg(player.getDiscordUser(), Config.ftw_dm_noping.replace(".url.", FtwglAPI.requestPingUrl(player))); + } + + public void showBets(DiscordInteraction interaction, int matchId, String color, Player p){ + Match match = null; + for (Match m : ongoingMatches){ + if (m.getID() == matchId){ + match = m; + } + } + if (match == null || !match.acceptBets()){ + interaction.respond(Config.bets_notaccepting); + return; + } + + String otherTeam = color.equals("red") ? "blue" : "red"; + if (match.isInMatch(p) && match.getTeam(p).equals(otherTeam)){ + interaction.respond(Config.bets_otherteam); + return; + } + + if (p.getCoins() <= 0){ + interaction.respond(Config.bets_nomoney); + return; + } + + ArrayList buttons = new ArrayList(); + + if (p.getCoins() > 10){ + JSONObject coinEmoji = Bet.getCoinEmoji(10); + DiscordButton button10 = new DiscordButton(DiscordButtonStyle.GREY); + button10.label = "10"; + button10.custom_id = "bet_" + matchId + "_" + color + "_" + 10; + button10.emoji = coinEmoji; + buttons.add(button10); + } + + if (p.getCoins() > 100){ + JSONObject coinEmoji = Bet.getCoinEmoji(100); + DiscordButton button100 = new DiscordButton(DiscordButtonStyle.GREY); + button100.label = "100"; + button100.custom_id = "bet_" + matchId + "_" + color + "_" + 100; + button100.emoji = coinEmoji; + buttons.add(button100); + } + + + if (p.getCoins() > 1000){ + JSONObject coinEmoji = Bet.getCoinEmoji(1000); + DiscordButton button1000 = new DiscordButton(DiscordButtonStyle.GREY); + button1000.label = String.format("%,d", 1000); + button1000.custom_id = "bet_" + matchId + "_" + color + "_" + 1000; + button1000.emoji = coinEmoji; + buttons.add(button1000); + } + + if (p.getCoins() > 10000){ + JSONObject coinEmoji = Bet.getCoinEmoji(10000); + DiscordButton button10000 = new DiscordButton(DiscordButtonStyle.GREY); + button10000.label = String.format("%,d", 10000); + button10000.custom_id = "bet_" + matchId + "_" + color + "_" + 10000; + button10000.emoji = coinEmoji; + buttons.add(button10000); + } + + if (p.getCoins() > 100000){ + JSONObject coinEmoji = Bet.getCoinEmoji(100000); + DiscordButton button100000 = new DiscordButton(DiscordButtonStyle.GREY); + button100000.label = String.format("%,d", 100000); + button100000.custom_id = "bet_" + matchId + "_" + color + "_" + 100000; + button100000.emoji = coinEmoji; + buttons.add(button100000); + } + + if (p.getCoins() > 1000000){ + JSONObject coinEmoji = Bet.getCoinEmoji(1000000); + DiscordButton button1000000 = new DiscordButton(DiscordButtonStyle.GREY); + button1000000.label = String.format("%,d", 1000000); + button1000000.custom_id = "bet_" + matchId + "_" + color + "_" + 1000000; + button1000000.emoji = coinEmoji; + buttons.add(button1000000); + } + + JSONObject coinEmoji = Bet.getCoinEmoji(p.getCoins()); + DiscordButton buttonallin = new DiscordButton(DiscordButtonStyle.RED); + buttonallin.label = String.format("%,d", p.getCoins()) + " (ALL IN)"; + buttonallin.custom_id = "bet_" + matchId + "_" + color + "_-1"; + buttonallin.emoji = coinEmoji; + buttons.add(buttonallin); + + String msg = Config.bets_howmuch; + msg = msg.replace(".team.", color); + msg = msg.replace(".balance.", String.valueOf(p.getCoins())); + msg = msg.replace(".matchid.", String.valueOf(matchId)); + msg = msg.replace(".emojiname.", coinEmoji.getString("name")); + msg = msg.replace(".emojiid.", coinEmoji.getString("id")); + interaction.respond(msg, null, buttons); + } + + public void bet(DiscordInteraction interaction, int matchId, String color, long amount, Player p){ + boolean allIn = false; + Match match = null; + for (Match m : ongoingMatches){ + if (m.getID() == matchId){ + match = m; + } + } + if (match == null || !match.acceptBets()){ + interaction.respond(Config.bets_notaccepting); + return; + } + + if (amount > p.getCoins()){ + interaction.respond(Config.bets_insufficient); + return; + } + + if (p.getCoins() <= 0){ + interaction.respond(Config.bets_nomoney); + return; + } + + if (amount > 1000000){ + interaction.respond(Config.bets_above_limit); + return; + } + + // All in + if (amount == -1){ + amount = p.getCoins(); + allIn = true; + } + + String otherTeam = color.equals("red") ? "blue" : "red"; + if (match.isInMatch(p) && match.getTeam(p).equals(otherTeam)){ + interaction.respond(Config.bets_otherteam); + return; + } + float odds = color.equals("red") ? match.getOdds(0) : match.getOdds(1); + Bet bet = new Bet(match.getID(), p, color, amount, odds); + for (Bet matchBet : match.bets){ + if (matchBet.player.equals(p) && color.equals(matchBet.color)){ + if (!allIn && amount + matchBet.amount > 1000000){ + interaction.respond(Config.bets_above_limit); + return; + } + matchBet.amount += amount; + bet.place(match); + return; + } + } + match.bets.add(bet); + bet.place(match); + + interaction.respond(null); + } + + public void showBuys(DiscordInteraction interaction, Player p){ + ArrayList buttons = new ArrayList(); + + int price; + JSONObject emoji; + DiscordButton button; + + // Elo boost + price = 1000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " Elo boost (2h)"; + button.custom_id = "buy_eloboost"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + // Elo boost + price = 1000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " Additional map votes"; + button.custom_id = "buy_showvoteoptions"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + // Elo boost + price = 10000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " Ban a map (2h)"; + button.custom_id = "buy_banmap"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + JSONObject coinEmoji = Bet.getCoinEmoji(p.getCoins()); + String msg = Config.buy_show; + msg = msg.replace(".balance.", String.format("%,d", p.getCoins())); + msg = msg.replace(".emojiname.", coinEmoji.getString("name")); + msg = msg.replace(".emojiid.", coinEmoji.getString("id")); + interaction.respond(msg, null, buttons); + } + + public void buyBoost(DiscordInteraction interaction, Player p){ + int price = 1000; + JSONObject emoji = Bet.getCoinEmoji(price); + if (p.getCoins() < price){ + interaction.respond(Config.bets_insufficient); + return; + } + + if (p.hasBoostActive()){ + interaction.respond(Config.buy_boostactive.replace(".remaining.", String.valueOf(p.getEloBoost() / 1000))); + return; + } + + p.setEloBoost((long) (System.currentTimeMillis() + 7.2e6)); // 2h + p.spendCoins(price); + p.saveWallet(); + interaction.respond(null); + + String msg = Config.buy_boostactivated; + msg = msg.replace(".player.", p.getDiscordUser().getMentionString()); + msg = msg.replace(".price.", String.valueOf(price)); + msg = msg.replace(".emojiname.", emoji.getString("name")); + msg = msg.replace(".emojiid.", emoji.getString("id")); + msg = msg.replace(".remaining.", String.valueOf(p.getEloBoost() / 1000)); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + } + + public void showAdditionalVoteOptions(DiscordInteraction interaction, Player p){ + if (p.getAdditionalMapVotes() > 0){ + interaction.respond(Config.buy_voteoptionsalready.replace(".vote.", String.valueOf(p.getAdditionalMapVotes()))); + return; + } + ArrayList buttons = new ArrayList(); + + int price; + JSONObject emoji; + DiscordButton button; + + price = 1000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " 1 vote"; + button.custom_id = "buy_additionalvote_1"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + price = 2000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " 2 votes"; + button.custom_id = "buy_additionalvote_2"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + price = 4000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " 3 votes"; + button.custom_id = "buy_additionalvote_3"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + price = 8000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " 4 votes"; + button.custom_id = "buy_additionalvote_4"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + price = 16000; + emoji = Bet.getCoinEmoji(price); + button = new DiscordButton(DiscordButtonStyle.GREY); + button.label = price + " 5 votes"; + button.custom_id = "buy_additionalvote_5"; + button.emoji = emoji; + button.disabled = p.getCoins() < price; + buttons.add(button); + + + JSONObject coinEmoji = Bet.getCoinEmoji(p.getCoins()); + String msg = Config.buy_showvoteoptions; + interaction.respond(msg, null, buttons); + } + + public void buyAdditionalVotes(DiscordInteraction interaction, Player p, int number){ + int price = 1000; + if (number == 2){ + price = 2000; + } else if (number == 3){ + price = 4000; + } else if (number == 4){ + price = 8000; + }else if (number == 5){ + price = 16000; + } + if (p.getAdditionalMapVotes() > 0){ + interaction.respond(Config.buy_voteoptionsalready.replace(".vote.", String.valueOf(p.getAdditionalMapVotes()))); + return; + } + + JSONObject emoji = Bet.getCoinEmoji(price); + if (p.getCoins() < price){ + interaction.respond(Config.bets_insufficient); + return; + } + + p.setAdditionalMapVotes(number); + p.spendCoins(price); + p.saveWallet(); + interaction.respond(Config.buy_addvote_purchased); + + String msg = Config.buy_addvotesactivated; + msg = msg.replace(".player.", p.getDiscordUser().getMentionString()); + msg = msg.replace(".price.", String.valueOf(price)); + msg = msg.replace(".emojiname.", emoji.getString("name")); + msg = msg.replace(".emojiid.", emoji.getString("id")); + msg = msg.replace(".vote.", String.valueOf(p.getAdditionalMapVotes())); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + } + + public void buyBanMap(DiscordInteraction interaction, Player p){ + int price = 10000; + + JSONObject emoji = Bet.getCoinEmoji(price); + if (p.getCoins() < price){ + interaction.respond(Config.bets_insufficient); + return; + } + + p.setMapBans(p.getMapBans() + 1); + p.spendCoins(price); + p.saveWallet(); + interaction.respond(Config.buy_banmap_purchased); + + String msg = Config.buy_mapbanactivated; + msg = msg.replace(".player.", p.getDiscordUser().getMentionString()); + msg = msg.replace(".price.", String.valueOf(price)); + msg = msg.replace(".emojiname.", emoji.getString("name")); + msg = msg.replace(".emojiid.", emoji.getString("id")); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + } + + public void cmdUseMapBan(Player p, String mapname){ + if (p.getMapBans() == 0){ + bot.sendNotice(p.getDiscordUser(), Config.no_map_ban); + return; + } + + int counter = 0; + GameMap map = null; + for (GameMap xmap : mapList) { + if (xmap.name.toLowerCase().contains(mapname.toLowerCase())) { + counter++; + map = xmap; + } + } + if (counter > 1) { + bot.sendNotice(p.getDiscordUser(), Config.map_not_unique); + return; + } else if (counter == 0) { + bot.sendNotice(p.getDiscordUser(), Config.map_not_found); + return; + } else if (map.bannedUntil >= System.currentTimeMillis()) { + bot.sendNotice(p.getDiscordUser(), Config.map_already_banned.replace(".remaining.", String.valueOf(map.bannedUntil / 1000))); + return; + } + + p.setMapBans(p.getMapBans() - 1); + map.bannedUntil = (long) (System.currentTimeMillis() + 7.2e6); + db.updateMapBan(map); + + for (Gametype gt : curMatch.keySet()){ + curMatch.get(gt).banMap(map); + } + + String msg = Config.used_map_ban; + msg = msg.replace(".player.", p.getDiscordUser().getMentionString()); + msg = msg.replace(".map.", map.name); + msg = msg.replace(".time.", String.valueOf(map.bannedUntil / 1000)); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + } + + public void cmdWallet(Player p) { + JSONObject coinEmoji = Bet.getCoinEmoji(p.getCoins()); + String msg = Config.buy_show_wallet; + msg = msg.replace(".player.", p.getDiscordUser().username); + msg = msg.replace(".balance.", String.format("%,d", p.getCoins())); + msg = msg.replace(".emojiname.", coinEmoji.getString("name")); + msg = msg.replace(".emojiid.", coinEmoji.getString("id")); + bot.sendMsg(bot.getLatestMessageChannel(), msg); + } + + public void cmdDonate(Player p, Player destP, int amount) { + if (amount <= 0){ + bot.sendNotice(p.getDiscordUser(), Config.donate_incorrect_amount); + return; + } else if(amount > 10000){ + bot.sendNotice(p.getDiscordUser(), Config.donate_above_limit); + return; + } else if(amount > p.getCoins()){ + bot.sendNotice(p.getDiscordUser(), Config.bets_insufficient); + return; + } + + p.spendCoins(amount); + p.saveWallet(); + destP.addCoins(amount); + destP.saveWallet(); + + JSONObject coinEmoji = Bet.getCoinEmoji(amount); + String msg = Config.donate_processed; + msg = msg.replace(".player.", p.getDiscordUser().getMentionString()); + msg = msg.replace(".otherplayer.", destP.getDiscordUser().getMentionString()); + msg = msg.replace(".amount.", String.format("%,d", amount)); + msg = msg.replace(".emojiname.", coinEmoji.getString("name")); + msg = msg.replace(".emojiid.", coinEmoji.getString("id")); + bot.sendMsg(getChannelByType(PickupChannelType.PUBLIC), msg); + } + + public void cmdBetHistory(Player p) { + ArrayList betList = db.getBetHistory(p); + + DiscordEmbed embed = new DiscordEmbed(); + embed.title = "Last 10 bets of " + p.getDiscordUser().username; + embed.color = 7056881; + if (betList.size() == 0){ + embed.description = "No bets yet"; + bot.sendMsg(bot.getLatestMessageChannel(), null, embed); + return; + } + + String matchIdField = ""; + String amountField = ""; + String resultField = ""; + + boolean firstBet = true; + for (Bet bet : betList){ + String linebreak = "\n"; + if (firstBet){ + linebreak = ""; + firstBet = false; + } + matchIdField += linebreak + bet.matchid; + + JSONObject amountEmoji = Bet.getCoinEmoji(bet.amount); + amountField += linebreak + "<:" + amountEmoji.get("name") + ":" + amountEmoji.get("id") + "> " + String.format("%,d", bet.amount); + + if (bet.won){ + JSONObject wonEmoji = Bet.getCoinEmoji(Math.round(bet.amount * bet.odds)); + resultField += linebreak + "Won <:" + wonEmoji.get("name") + ":" + wonEmoji.get("id") + "> " + String.format("%,d", Math.round(bet.amount * bet.odds)); + } + else { + resultField += linebreak + "Lost"; + } + } + embed.addField("Match id", matchIdField, true); + embed.addField("Amount", amountField, true); + embed.addField("Result", resultField, true); + + bot.sendMsg(bot.getLatestMessageChannel(), null, embed); + } + + public List getActiveTeams(){ + return activeTeams; + } } diff --git a/src/de/gost0r/pickupbot/pickup/Player.java b/src/de/gost0r/pickupbot/pickup/Player.java index 0b7b791..bc8ea5a 100644 --- a/src/de/gost0r/pickupbot/pickup/Player.java +++ b/src/de/gost0r/pickupbot/pickup/Player.java @@ -5,11 +5,17 @@ import java.util.List; import java.util.Map; +import de.gost0r.pickupbot.discord.DiscordChannel; +import de.gost0r.pickupbot.discord.DiscordGuild; import de.gost0r.pickupbot.discord.DiscordUser; +import de.gost0r.pickupbot.pickup.stats.WinDrawLoss; + +import javax.swing.*; public class Player { public static Database db; + public static PickupLogic logic; private DiscordUser user; private String urtauth; @@ -17,30 +23,44 @@ public class Player { private Map votedMap = new HashMap(); private int elo = 1000; private int eloChange = 0; + private int elorank = 0; + + private float kdr = 0.0f; + + public PlayerStats stats; private List bans = new ArrayList(); private boolean active = true; + private boolean enforceAC = false; private boolean surrender = false; private long lastMessage = -1L; private boolean afkReminderSent = false; + private DiscordChannel lastPublicChannel; + private String country = "NOT_DEFINED"; + + private long coins; + private long eloBoost; + private int additionalMapVotes; + private int mapBans; + public Player(DiscordUser user, String urtauth) { this.user = user; this.setUrtauth(urtauth); playerList.add(this); } - + public void voteMap(Gametype gametype, GameMap map) { votedMap.put(gametype, map); } - + public void voteSurrender() { surrender = true; } - + public void resetVotes() { for (Gametype gt : votedMap.keySet()) { votedMap.put(gt, null); @@ -54,31 +74,35 @@ public void addElo(int elochange) { // db update done by servermonitor } - + public void afkCheck() { lastMessage = System.currentTimeMillis(); afkReminderSent = false; } - + public long getLastMessage() { return lastMessage; } + public void setLastMessage(long lastMessage) { + this.lastMessage = lastMessage; + } + public boolean getAfkReminderSent() { return afkReminderSent; } - + public void setAfkReminderSent(boolean value) { afkReminderSent = value; } - + public GameMap getVotedMap(Gametype gametype) { if (votedMap.containsKey(gametype)) { return votedMap.get(gametype); } return null; } - + public boolean hasVotedSurrender() { return surrender; } @@ -92,12 +116,20 @@ public int getElo() { } public void setElo(int elo) { - if (elo < 0) { + if (elo <= 0) { this.elo = 1000; } else { this.elo = elo; } } + + public float getKdr() { + return kdr; + } + + public void setKdr(float kdr) { + this.kdr = kdr; + } public int getEloChange() { return eloChange; @@ -106,7 +138,7 @@ public int getEloChange() { public void setEloChange(int eloChange) { this.eloChange = eloChange; } - + public String getUrtauth() { return urtauth; } @@ -114,7 +146,7 @@ public String getUrtauth() { public void setUrtauth(String urtauth) { this.urtauth = urtauth; } - + public boolean getActive() { return active; } @@ -122,11 +154,19 @@ public boolean getActive() { public void setActive(boolean active) { this.active = active; } - + public void addBan(PlayerBan ban) { bans.add(ban); } - + + public void forgiveBan() { + for (PlayerBan ban : bans) { + ban.forgiven = true; + } + + db.forgiveBan(this); + } + public PlayerBan getLatestBan() { PlayerBan current = null; for (PlayerBan ban : bans) { @@ -134,13 +174,13 @@ public PlayerBan getLatestBan() { current = ban; continue; } - if (ban.startTime > current.startTime) { + if ((ban.startTime > current.startTime) && !ban.forgiven) { current = ban; } } return current; } - + public int getPlayerBanCountSince(long time) { int i = 0; for (PlayerBan ban : bans) { @@ -151,38 +191,64 @@ public int getPlayerBanCountSince(long time) { return i; } + public ArrayList getPlayerBanListSince(long time) { + ArrayList banList= new ArrayList(); + for (PlayerBan ban : bans) { + if (ban.startTime >= time) { + banList.add(ban); + } + } + return banList; + } + public boolean isBanned() { for (PlayerBan ban : bans) { - if (ban.endTime > System.currentTimeMillis()) { + if (!ban.forgiven && ban.endTime > System.currentTimeMillis()) { return true; } } return false; } - + public PlayerRank getRank() { return getRank(elo); } - + private PlayerRank getRank(int elo) { - if (elo > 1600) { + if (elo >= 1600) { return PlayerRank.DIAMOND; - } else if (elo > 1350) { + } else if (elo >= 1400) { return PlayerRank.PLATINUM; - } else if (elo > 1150) { + } else if (elo >= 1200) { return PlayerRank.GOLD; - } else if (elo > 1000) { + } else if (elo >= 1000) { return PlayerRank.SILVER; - } else if (elo > 850) { + } else if (elo >= 800) { return PlayerRank.BRONZE; } else { return PlayerRank.WOOD; } } - + public boolean didChangeRank() { PlayerRank currentRank = getRank(elo); PlayerRank previousRank = getRank(elo-eloChange); + + // Update roles + // TODO make it work for different servers + if (currentRank != previousRank){ + logic.bot.removeUserRole(getDiscordUser(), previousRank.getRole()); + } + if (getDiscordUser().hasRole(new DiscordGuild("117622053061787657"), PlayerRank.LEET.getRole()) && elorank > 5){ + logic.bot.removeUserRole(getDiscordUser(), PlayerRank.LEET.getRole()); + } + if (!getDiscordUser().hasRole(new DiscordGuild("117622053061787657"), PlayerRank.LEET.getRole()) && elorank <= 5){ + logic.bot.addUserRole(getDiscordUser(), PlayerRank.LEET.getRole()); + } + if (!getDiscordUser().hasRole(new DiscordGuild("117622053061787657"), currentRank.getRole())){ + logic.bot.addUserRole(getDiscordUser(), currentRank.getRole()); + } + return currentRank != previousRank; } @@ -222,15 +288,99 @@ public boolean equals(Object o) { } return false; } - + @Override public String toString() { return this.urtauth; } + public Region getRegion() { + if(this.country.equalsIgnoreCase("NOT_DEFINED")) { + return Region.WORLD; + } + else { + String continent = Country.getContinent(this.country); + + return Region.valueOf(continent); + } + } + + public String getCountry() { + return this.country; + } + + public void setCountry(String country) { + this.country = country; + } + public static void remove(Player player) { if (playerList.contains(player)) { playerList.remove(player); } } + + public DiscordChannel getLastPublicChannel() { return lastPublicChannel; } + + public void setLastPublicChannel(DiscordChannel channel){ + lastPublicChannel = channel; + } + + public boolean getEnforceAC() { return this.enforceAC; } + + public void setEnforceAC(boolean enforceAC) { this.enforceAC = enforceAC; } + + public float getCaptainScore(Gametype gt){ + WinDrawLoss wdl = stats.ts_wdl; + float kdr = stats.kdr; + if (gt.getName().equals("CTF")){ + wdl = stats.ctf_wdl; + kdr = stats.ctf_rating; + } + if (wdl.getTotal() < 5){ + return (float) elo; + } + return (float) (elo + (kdr * 500 + wdl.calcWinRatio() * 500.0) / 4); + } + + public void setRank(int rank){ + this.elorank = rank; + } + public int getEloRank(){ + return db.getRankForPlayer(this); + } + + public long getCoins() {return coins;} + public void setCoins(long coins) {this.coins = coins ;} + + public void addCoins(long amount) { + coins += amount ; + } + public void spendCoins(long amount) { + coins -= amount ; + } + public void saveWallet(){ + db.updatePlayerCoins(this); + } + + public long getEloBoost() {return eloBoost;} + public void setEloBoost(long eloBoost) { + this.eloBoost = eloBoost ; + db.updatePlayerBoost(this); + } + + public boolean hasBoostActive(){ + return eloBoost >= System.currentTimeMillis(); + } + + public int getAdditionalMapVotes() {return additionalMapVotes;} + public void setAdditionalMapVotes(int mapVotes) { + this.additionalMapVotes = mapVotes ; + db.updatePlayerBoost(this); + } + + public int getMapBans() {return mapBans;} + public void setMapBans(int mapBans) { + this.mapBans = mapBans ; + db.updatePlayerBoost(this); + } } diff --git a/src/de/gost0r/pickupbot/pickup/PlayerBan.java b/src/de/gost0r/pickupbot/pickup/PlayerBan.java index 5645d3d..73c6e09 100644 --- a/src/de/gost0r/pickupbot/pickup/PlayerBan.java +++ b/src/de/gost0r/pickupbot/pickup/PlayerBan.java @@ -22,5 +22,7 @@ public enum BanReason { public BanReason reason; public DiscordUser pardon = null; + + public Boolean forgiven = false; } diff --git a/src/de/gost0r/pickupbot/pickup/PlayerRank.java b/src/de/gost0r/pickupbot/pickup/PlayerRank.java index 38c813a..579ca9f 100644 --- a/src/de/gost0r/pickupbot/pickup/PlayerRank.java +++ b/src/de/gost0r/pickupbot/pickup/PlayerRank.java @@ -1,19 +1,37 @@ package de.gost0r.pickupbot.pickup; +import de.gost0r.pickupbot.discord.DiscordRole; +import org.json.JSONObject; + public enum PlayerRank { - DIAMOND("<:pickup_diamond:415516710708445185>"), - PLATINUM("<:pickup_platinium:415517181674258432>"), - GOLD("<:pickup_gold:415517181783179264>"), - SILVER("<:pickup_silver:415517181481189387>"), - BRONZE("<:pickup_bronze:415517181489709058>"), - WOOD("<:pickup_wood:415517181137387520>"); + LEET("<:pickup_diamond:415516710708445185>", "525240137538469904"), + DIAMOND("<:pickup_diamond:415516710708445185>", "933030919198085121"), + PLATINUM("<:pickup_platinium:415517181674258432>", "934854051911327815"), + GOLD("<:pickup_gold:415517181783179264>", "934856926020374528"), + SILVER("<:pickup_silver:415517181481189387>", "934856641810153522"), + BRONZE("<:pickup_bronze:415517181489709058>", "934856813743071295"), + WOOD("<:pickup_wood:415517181137387520>", "934858171409895495"); - PlayerRank(String emoji) { + PlayerRank(String emoji, String roleId) { this.emoji = emoji; + this.roleId = roleId; } - protected String emoji; + private String emoji; + private String roleId; public String getEmoji() { return emoji; } + + public DiscordRole getRole() { return DiscordRole.getRole(roleId); } + + public JSONObject getEmojiJSON() { + JSONObject emojiJSON = new JSONObject(); + String name = emoji.split(":")[1]; + String id = emoji.split(":")[2].replaceAll(">", ""); + emojiJSON.put("name", name); + emojiJSON.put("id", id); + + return emojiJSON; + } } diff --git a/src/de/gost0r/pickupbot/pickup/PlayerStats.java b/src/de/gost0r/pickupbot/pickup/PlayerStats.java new file mode 100644 index 0000000..2bec77d --- /dev/null +++ b/src/de/gost0r/pickupbot/pickup/PlayerStats.java @@ -0,0 +1,25 @@ +package de.gost0r.pickupbot.pickup; + +import de.gost0r.pickupbot.pickup.stats.WinDrawLoss; + +public class PlayerStats { + // TS + public int kills = 0; + public int deaths = 0; + public int assists = 0; + public float kdr = 0.0f; + public int kdrRank = 0; + public WinDrawLoss ts_wdl = new WinDrawLoss(); + public int wdlRank = 0; + + // CTF + public WinDrawLoss ctf_wdl = new WinDrawLoss(); + public int ctfWdlRank = 0; + public float ctf_rating = 0.f; + public int ctfRank = 0; + public int caps = 0; + public int returns = 0; + public int fckills = 0; + public int stopcaps = 0; + public int protflag = 0; +} \ No newline at end of file diff --git a/src/de/gost0r/pickupbot/pickup/Region.java b/src/de/gost0r/pickupbot/pickup/Region.java new file mode 100644 index 0000000..33c55d9 --- /dev/null +++ b/src/de/gost0r/pickupbot/pickup/Region.java @@ -0,0 +1,14 @@ +package de.gost0r.pickupbot.pickup; + +public enum Region { + AF, // AFRICA + AN, // ANTARCTICA + AS, // ASIA + EU, // EUROPE + NA, // NORTH AMERICA + OC, // OCEANIA ( AUSTRALIA and ISLANDS ) + SA, // SOUTH AMERICA + WORLD, + NAE, // NA EAST + NAW // NA WEST +} diff --git a/src/de/gost0r/pickupbot/pickup/Season.java b/src/de/gost0r/pickupbot/pickup/Season.java new file mode 100644 index 0000000..0a74639 --- /dev/null +++ b/src/de/gost0r/pickupbot/pickup/Season.java @@ -0,0 +1,17 @@ +package de.gost0r.pickupbot.pickup; + +public class Season { + public int number; + public long startdate; + public long enddate; + + public Season(int number, long startdate, long enddate){ + this.number = number; + this.startdate = startdate; + this.enddate = enddate; + } + + public static Season AllTimeSeason(){ + return new Season(0, 0, System.currentTimeMillis()); + } +} diff --git a/src/de/gost0r/pickupbot/pickup/Team.java b/src/de/gost0r/pickupbot/pickup/Team.java new file mode 100644 index 0000000..7cdb52c --- /dev/null +++ b/src/de/gost0r/pickupbot/pickup/Team.java @@ -0,0 +1,136 @@ +package de.gost0r.pickupbot.pickup; + +import de.gost0r.pickupbot.discord.DiscordButton; +import de.gost0r.pickupbot.discord.DiscordButtonStyle; +import de.gost0r.pickupbot.discord.DiscordChannel; +import de.gost0r.pickupbot.discord.DiscordComponent; + +import java.util.ArrayList; +import java.util.List; + +public class Team { + private PickupLogic logic; + + private int id; + private Player captain; + private List players; + private List invitedPlayers; + private final int maxPlayers = 5; + private DiscordChannel threadChannel; + + + + public Team(PickupLogic logic, Player captain){ + this.logic = logic; + + this.captain = captain; + + players = new ArrayList(); + invitedPlayers = new ArrayList(); + + threadChannel = this.logic.bot.createThread(captain.getLastPublicChannel(), captain.getUrtauth() + "'s team"); + + addPlayer(captain); + } + + public List getPlayers(){ return players; } + + public List getInvitedPlayers(){ return invitedPlayers; } + + public Player getCaptain(){ return captain; } + + public DiscordChannel getThreadChannel() { return threadChannel; } + + public boolean isInTeam(Player player) { return players.contains(player); } + public boolean isInvitedToTeam(Player player) { return invitedPlayers.contains(player); } + + public void setCaptain(Player newCaptain){ + if (!players.contains(newCaptain)){ + addPlayer(newCaptain); + } + captain = newCaptain; + } + + public void addPlayer(Player player){ players.add(player); } + + public void removePlayer(Player player){ + players.remove(player); + logic.bot.sendMsg(threadChannel, Config.team_removed.replace(".player.", player.getDiscordUser().getMentionString())); + } + + public void invitePlayer(Player player){ + invitedPlayers.add(player); + + List buttons = new ArrayList(); + DiscordButton button_accept = new DiscordButton(DiscordButtonStyle.GREEN); + button_accept.custom_id = "teaminvite_1_" + captain.getUrtauth() + "_" + player.getUrtauth(); + button_accept.label = "Accept"; + buttons.add(button_accept); + DiscordButton button_decline = new DiscordButton(DiscordButtonStyle.RED); + button_decline.custom_id = "teaminvite_0_" + captain.getUrtauth() + "_" + player.getUrtauth(); + button_decline.label = "Decline"; + buttons.add(button_decline); + DiscordButton button_cancel = new DiscordButton(DiscordButtonStyle.GREY); + button_cancel.custom_id = "teaminvite_2_" + captain.getUrtauth() + "_" + player.getUrtauth(); + button_cancel.label = "Cancel"; + buttons.add(button_cancel); + + String invite_message = Config.team_invited; + invite_message = invite_message.replace(".invited.", player.getDiscordUser().getMentionString()); + invite_message = invite_message.replace(".captain.", captain.getDiscordUser().getMentionString()); + logic.bot.sendMsgToEdit(threadChannel, invite_message, null, buttons); + } + + public void acceptInvitation(Player player){ + invitedPlayers.remove(player); + addPlayer(player); + + List buttons = new ArrayList(); + DiscordButton button_remove = new DiscordButton(DiscordButtonStyle.RED); + button_remove.custom_id = "teamremove_" + captain.getUrtauth() + "_" + player.getUrtauth(); + button_remove.label = "Remove"; + buttons.add(button_remove); + + String accept_message = Config.team_accepted; + accept_message = accept_message.replace(".player.", player.getDiscordUser().getMentionString()); + logic.bot.sendMsgToEdit(threadChannel, accept_message, null, buttons); + } + + public void declineInvitation(Player player){ + invitedPlayers.remove(player); + logic.bot.sendMsg(threadChannel, Config.team_declined.replace(".player.", player.getDiscordUser().getMentionString())); + } + + public void cancelInvitation(Player player){ + invitedPlayers.remove(player); + logic.bot.sendMsg(threadChannel, Config.team_canceled.replace(".player.", player.getDiscordUser().getMentionString())); + } + + public boolean isFull(){ + return players.size() == maxPlayers; + } + + public void archive(){ + threadChannel.archive(); + } + + public String getTeamString(){ + String str = captain.getDiscordUser().getMentionString() + " (captain)"; + for (Player player : players){ + if (!player.equals(captain)){ + str = str + " " + player.getDiscordUser().getMentionString(); + } + } + return str; + } + + public String getTeamStringNoMention(){ + String str = captain.getUrtauth() + " (captain)"; + for (Player player : players){ + if (!player.equals(captain)){ + str = str + " " + player.getUrtauth(); + } + } + return str; + } +} diff --git a/src/de/gost0r/pickupbot/pickup/server/CTF_Stats.java b/src/de/gost0r/pickupbot/pickup/server/CTF_Stats.java index 8aabf4f..171096a 100644 --- a/src/de/gost0r/pickupbot/pickup/server/CTF_Stats.java +++ b/src/de/gost0r/pickupbot/pickup/server/CTF_Stats.java @@ -2,14 +2,26 @@ public class CTF_Stats { - public String score = "0"; - public String deaths = "0"; - public String assists = "0"; + public int score = 0; + public int deaths = 0; + public int assists = 0; - public String caps = "0"; - public String returns = "0"; - public String fc_kills = "0"; - public String stop_caps = "0"; - public String protect_flag = "0"; + public int caps = 0; + public int returns = 0; + public int fc_kills = 0; + public int stop_caps = 0; + public int protect_flag = 0; + + public void add(CTF_Stats inStats){ + this.score += inStats.score; + this.deaths += inStats.deaths; + this.assists += inStats.assists; + + this.caps += inStats.caps; + this.returns += inStats.returns; + this.fc_kills += inStats.fc_kills; + this.stop_caps += inStats.stop_caps; + this.protect_flag += inStats.protect_flag; + } } diff --git a/src/de/gost0r/pickupbot/pickup/server/RconPlayersParsed.java b/src/de/gost0r/pickupbot/pickup/server/RconPlayersParsed.java index 0e2de13..5ad479f 100644 --- a/src/de/gost0r/pickupbot/pickup/server/RconPlayersParsed.java +++ b/src/de/gost0r/pickupbot/pickup/server/RconPlayersParsed.java @@ -16,6 +16,6 @@ public class RconPlayersParsed { public String roundtime; public String half; - public List players = new ArrayList<>(); + public List players = new ArrayList(); } diff --git a/src/de/gost0r/pickupbot/pickup/server/Server.java b/src/de/gost0r/pickupbot/pickup/server/Server.java index 1419b23..084b743 100644 --- a/src/de/gost0r/pickupbot/pickup/server/Server.java +++ b/src/de/gost0r/pickupbot/pickup/server/Server.java @@ -7,39 +7,54 @@ import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import de.gost0r.pickupbot.pickup.Country; import de.gost0r.pickupbot.pickup.Match; +import de.gost0r.pickupbot.pickup.Player; +import de.gost0r.pickupbot.pickup.Region; +import io.sentry.Sentry; public class Server { - private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); - - public int id; - public String IP; - public int port; - public String rconpassword; - public String password; - public boolean active; - + private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + + public int id; + + public String IP; + public int port; + public String rconpassword; + public String password; + public boolean active; + public Region region; + public String country; + public String city; + public Map playerPing; + private boolean taken = false; - + private DatagramSocket socket; private ServerMonitor monitor; private Thread monitorThread; - public Server(int id, String ip, int port, String rconpassword, String password, boolean active) { + public int matchid; + + public Server(int id, String ip, int port, String rconpassword, String password, boolean active, Region region) { this.id = id; this.IP = ip; this.port = port; this.rconpassword = rconpassword; this.password = password; this.active = active; + this.region = region; connect(); monitor = null; + playerPing = new HashMap(); } public void connect() { @@ -48,6 +63,7 @@ public void connect() { this.socket.setSoTimeout(1000); } catch (SocketException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -59,10 +75,10 @@ public synchronized String sendRcon(String rconString) { connect(); } String rcon = "xxxxrcon " + rconpassword + " " + rconString; - + byte[] recvBuffer = new byte[2048]; byte[] sendBuffer = rcon.getBytes(); - + sendBuffer[0] = (byte) 0xff; sendBuffer[1] = (byte) 0xff; sendBuffer[2] = (byte) 0xff; @@ -75,34 +91,62 @@ public synchronized String sendRcon(String rconString) { this.socket.send(sendPacket); String string = ""; - while (true) { - try { - this.socket.receive(recvPacket); - String newString = new String(recvPacket.getData()); - - newString = newString.substring(4); // remove the goddamn first 4 chars - - string += newString; - - recvBuffer = new byte[2048]; // empty buffer - recvPacket = new DatagramPacket(recvBuffer, recvBuffer.length); - } catch (SocketTimeoutException e) { - break; - } - } - - string = string.replace("" + (char) 0, ""); - - Thread.sleep(100); - return string; + while (true) { + try { + this.socket.receive(recvPacket); + String newString = new String(recvPacket.getData()); + + newString = newString.substring(4); // remove the goddamn first 4 chars + + string += newString; + + recvBuffer = new byte[2048]; // empty buffer + recvPacket = new DatagramPacket(recvBuffer, recvBuffer.length); + } catch (SocketTimeoutException e) { + break; + } + } + + string = string.replace("" + (char) 0, ""); + + // Thread.sleep(100); + return string; } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); - } catch (InterruptedException e) { + Sentry.capture(e); + } + return null; + } + + + public synchronized String pushRcon(String rconString) { + try { + if (this.socket.isClosed()) { + LOGGER.severe("SOCKET IS CLOSED"); + connect(); + } + String rcon = "xxxxrcon " + rconpassword + " " + rconString; + + byte[] sendBuffer = rcon.getBytes(); + + sendBuffer[0] = (byte) 0xff; + sendBuffer[1] = (byte) 0xff; + sendBuffer[2] = (byte) 0xff; + sendBuffer[3] = (byte) 0xff; + + LOGGER.fine(rcon); + + DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, getInetIP(), port); + this.socket.send(sendPacket); + + } catch (IOException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } - return null; + return null; } + public void startMonitoring(Match match) { if (this.monitor == null) { this.monitor = new ServerMonitor(this, match); @@ -136,6 +180,7 @@ public InetAddress getInetIP() { return InetAddress.getByName(IP); } catch (UnknownHostException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } return null; } @@ -146,11 +191,81 @@ public boolean isTaken() { @Override public String toString() { - return "#" + id + " " + IP + ":" + port + " - active: " + active; + String isActive = this.active ? "" : "(inactive)"; + String isTaken = this.isTaken() ? "used for Match#" + matchid : ""; + return "#" + id + " " + IP + ":" + port + " " + region + " " + isReachable() + " " + isActive + isTaken ; + } + + public boolean isOnline(){ + try { + InetAddress.getByName(IP).isReachable(1000); + } catch (UnknownHostException e) { + return false; + } catch (IOException e) { + return false; + } + + String rconStatusAck = sendRcon("status"); // TODO: Change to rcon players + return rconStatusAck.contains("score ping name"); + } + + public String isReachable() { + String status = ":red_circle: (Host Timeout)"; + + try { + InetAddress.getByName(IP).isReachable(1000); + } catch (UnknownHostException e) { + return status; + } catch (IOException e) { + return status; + } + + String rconStatusAck = sendRcon("status"); // TODO: Change to rcon players + + if(rconStatusAck.contains("score ping name")) + { + // rcon is correct and server is up + status = ":green_circle:"; + } + else if (rconStatusAck.contains("Bad rconpassword")) + { + // server is up but rcon is wrong + status = ":orange_circle: (bad rcon)"; + } + else if (rconStatusAck.contains("No rconpassword set on the server.")) + { + // server is up but rcon not defined in server CVARs + status = ":orange_circle: (no rcon set on server)"; + } + else + { + // server is down + status = ":red_circle: (server down)"; + } + + return status; } public String getAddress() { return IP + ":" + port; } + public String getRegionFlag(boolean dynServer, boolean forceNoDynamic){ + if (region == null) { + return ""; + } else if (dynServer && !forceNoDynamic){ + return Country.getCountryFlag(country) + " " + city + " - "; + } else if (region == Region.NAE || region == Region.NAW) { + return ":flag_us:"; + } else if (region == Region.OC) { + return ":flag_au:"; + } else if (region == Region.SA) { + return ":flag_br:"; + } else if (region == Region.EU) { + return ":flag_eu:"; + } else { + return region.name(); + } + } + } diff --git a/src/de/gost0r/pickupbot/pickup/server/ServerMonitor.java b/src/de/gost0r/pickupbot/pickup/server/ServerMonitor.java index 30ed9d2..4f2d29c 100644 --- a/src/de/gost0r/pickupbot/pickup/server/ServerMonitor.java +++ b/src/de/gost0r/pickupbot/pickup/server/ServerMonitor.java @@ -1,13 +1,10 @@ package de.gost0r.pickupbot.pickup.server; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import de.gost0r.pickupbot.pickup.Match; -import de.gost0r.pickupbot.pickup.MatchState; import de.gost0r.pickupbot.pickup.MatchStats; import de.gost0r.pickupbot.pickup.MatchStats.Status; import de.gost0r.pickupbot.pickup.PickupChannelType; @@ -15,6 +12,7 @@ import de.gost0r.pickupbot.pickup.PlayerBan.BanReason; import de.gost0r.pickupbot.pickup.Score; import de.gost0r.pickupbot.pickup.server.ServerPlayer.ServerPlayerState; +import io.sentry.Sentry; public class ServerMonitor implements Runnable { private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); @@ -35,6 +33,7 @@ public static enum ServerState { private List players; + private Map backupStats; private List leavers; private String gameTime; private ServerState state; @@ -47,6 +46,8 @@ public static enum ServerState { private long lastServerMessage = 0L; private long lastDiscordMessage = System.currentTimeMillis(); + public boolean noMercyIssued; + private RconPlayersParsed prevRPP = new RconPlayersParsed(); public ServerMonitor(Server server, Match match) { @@ -61,13 +62,16 @@ public ServerMonitor(Server server, Match match) { hasPaused = false; isPauseDetected = false; + noMercyIssued = false; + players = new ArrayList(); + backupStats = new HashMap(); leavers = new ArrayList(); } @Override public void run() { - LOGGER.info("run() started"); + LOGGER.info("run() started on " + this.server.IP + ":" + this.server.port); try { while (!stopped) { observe(); @@ -75,6 +79,7 @@ public void run() { } } catch (Exception e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); match.getLogic().bot.sendMsg(match.getLogic().getChannelByType(PickupChannelType.ADMIN), "ServerMonitor " + e.toString()); } LOGGER.info("run() ended"); @@ -95,27 +100,20 @@ private void observe() throws Exception { updatePlayers(rpp); evaluateState(rpp); - forceplayers(); - - if (state == ServerState.WELCOME) - { - - } - else if (state == ServerState.WARMUP) - { - // Do nothing - } - else if (state == ServerState.LIVE) - { - - } - else if (state == ServerState.SCORE) - { - - } checkNoshow(); checkRagequit(); + + } + private void checkNoMercy(RconPlayersParsed rpp) { + if (Math.abs(rpp.scores[0] - rpp.scores[1]) >= 10 && !noMercyIssued && match.getGametype().getTeamSize() > 2){ + server.sendRcon("timelimit 1"); + server.sendRcon("bigtext \"Mercy rule! Ending game in 1min\""); + server.sendRcon("say \"^1[MERCY RULE] ^3Ending game in 1min\""); + noMercyIssued = true; + LOGGER.info("Mercy rule, game will end in 1min"); + sendDiscordMsg("**[MERCY RULE]** The match #" + String.valueOf(match.getID()) + " is a massacre and will end in 1min."); + } } private void checkNoshow() throws Exception { @@ -132,7 +130,7 @@ private void checkNoshow() throws Exception { } if (!playerlist.isEmpty()) { - long timeleft = (match.getStartTime() + 300000L) - System.currentTimeMillis(); // 5min + long timeleft = (match.getStartTime() + 180000L) - System.currentTimeMillis(); // 3min if (timeleft > 0) { String time = getTimeString(timeleft); String sendString = "(" + time + ") Waiting for: ^1" + playerlist; @@ -142,8 +140,9 @@ private void checkNoshow() throws Exception { sendDiscordMsg(sendDiscordString); if (state == ServerState.WARMUP || state == ServerState.LIVE) { server.sendRcon("restart"); // restart map + server.sendRcon("startserverdemo all"); } - } else if (timeleft < -300000L) { // if noshow timer ran out twice + } else if (timeleft < -180000L) { // if noshow timer ran out twice // we're way over time to accurately log a noshow, therefore simply abandon abandonMatch(MatchStats.Status.NOSHOW, new ArrayList()); } else { // if the noshow time ran out @@ -183,10 +182,11 @@ private void checkRagequit() throws Exception { boolean shouldPause = false; long timeleft = 0; if (state == ServerState.WELCOME) { - timeleft = (earliestLeaver + 300000L) - System.currentTimeMillis(); // 5min + timeleft = (earliestLeaver + 180000L) - System.currentTimeMillis(); // 3min } else if (state == ServerState.WARMUP) { - timeleft = (earliestLeaver + 300000L) - System.currentTimeMillis(); // 5min + timeleft = (earliestLeaver + 180000L) - System.currentTimeMillis(); // 3min server.sendRcon("restart"); // restart map + server.sendRcon("startserverdemo all"); } else if (state == ServerState.LIVE) { if (getRemainingSeconds() < 90 && isLastHalf()) { LOGGER.warning(getRemainingSeconds() + "s remaining, don't report."); @@ -209,10 +209,11 @@ private void checkRagequit() throws Exception { return; // ignore leavers in the score screen } if (timeleft > 0) { - // pause if someone left + // pause if someone leaves if (!hasPaused && shouldPause) { if (!isPauseDetected) { server.sendRcon("pause"); + server.sendRcon("startserverdemo all"); } hasPaused = true; } @@ -234,6 +235,7 @@ private void checkRagequit() throws Exception { if (hasPaused && isPauseDetected) { if (state == ServerState.LIVE) { server.sendRcon("pause"); + server.sendRcon("startserverdemo all"); } hasPaused = false; } @@ -257,13 +259,13 @@ private void setAllPlayersStatus(List playerList, Status status) { } } - private void saveStats(int[] scorex) throws Exception { + private void saveStats(int[] scorex, RconPlayersParsed rpp) throws Exception { int half = firstHalf ? 0 : 1; score[half] = scorex; // reset matchstats to previous - for (ServerPlayer sp : prevRPP.players) { + for (ServerPlayer sp : rpp.players) { for (ServerPlayer player : players) { if (sp.equals(player)) { player.copy(sp); @@ -275,18 +277,21 @@ private void saveStats(int[] scorex) throws Exception { // save playerscores for (ServerPlayer player : players) { try { - if (player.player != null && match.isInMatch(player.player)) { - match.getStats(player.player).score[half].score = Integer.valueOf(player.ctfstats.score); - match.getStats(player.player).score[half].deaths = Integer.valueOf(player.ctfstats.deaths); - match.getStats(player.player).score[half].assists = Integer.valueOf(player.ctfstats.assists); - match.getStats(player.player).score[half].caps = Integer.valueOf(player.ctfstats.caps); - match.getStats(player.player).score[half].returns = Integer.valueOf(player.ctfstats.returns); - match.getStats(player.player).score[half].fc_kills = Integer.valueOf(player.ctfstats.fc_kills); - match.getStats(player.player).score[half].stop_caps = Integer.valueOf(player.ctfstats.stop_caps); - match.getStats(player.player).score[half].protect_flag = Integer.valueOf(player.ctfstats.protect_flag); + if (player.player != null && match.isInMatch(player.player) && rpp.players.contains(player)) { + // player.ctfstats.add(backupStats.get(player.auth)); + CTF_Stats backupstats = backupStats.get(player.auth); + match.getStats(player.player).score[half].score = player.ctfstats.score + backupstats.score; + match.getStats(player.player).score[half].deaths = player.ctfstats.deaths + backupstats.deaths; + match.getStats(player.player).score[half].assists = player.ctfstats.assists + backupstats.assists; + match.getStats(player.player).score[half].caps = player.ctfstats.caps + backupstats.caps; + match.getStats(player.player).score[half].returns = player.ctfstats.returns + backupstats.returns; + match.getStats(player.player).score[half].fc_kills = player.ctfstats.fc_kills + backupstats.fc_kills; + match.getStats(player.player).score[half].stop_caps = player.ctfstats.stop_caps + backupstats.stop_caps; + match.getStats(player.player).score[half].protect_flag = player.ctfstats.protect_flag + backupstats.protect_flag; } } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Exception: ", e); + Sentry.capture(e); } } @@ -301,7 +306,7 @@ private void forceplayers() throws Exception { sp.player = player; match.getStats(player).updateStatus(MatchStats.Status.PLAYING); match.getStats(player).updateIP(sp.ip); - } else if (player != null && match.getLogic().bot.hasAdminRights(player.getDiscordUser())) { + } else if (player != null && player.getDiscordUser().hasAdminRights()) { // PLAYER IS AN ADMIN, DONT FORCE/KICK HIM continue; } else { // if player not authed, auth not registered or not playing in this match -> kick @@ -341,7 +346,12 @@ else if (!sp.team.equalsIgnoreCase(oppTeam) && !firstHalf) // we should have swi private void evaluateState(RconPlayersParsed rpp) throws Exception { if (state == ServerState.WELCOME) { - if (rpp.matchready[0] && rpp.matchready[1] && rpp.warmupphase) + if (match.getGametype().getTeamSize() == 0 && (rpp.matchready[0] || rpp.matchready[1]) && !rpp.warmupphase){ + server.sendRcon("forceready"); + state = ServerState.WARMUP; + LOGGER.info("SWITCHED WELCOME -> WARMUP"); + } + else if (rpp.matchready[0] && rpp.matchready[1] && rpp.warmupphase) { state = ServerState.WARMUP; LOGGER.info("SWITCHED WELCOME -> WARMUP"); @@ -349,6 +359,9 @@ private void evaluateState(RconPlayersParsed rpp) throws Exception { else if (rpp.matchready[0] && rpp.matchready[1] && !rpp.warmupphase) { state = ServerState.LIVE; + if (match.getGametype().getTeamSize() > 2){ + match.getLogic().setLastMapPlayed(match.getGametype(), match.getMap()); + } LOGGER.info("SWITCHED WELCOME -> LIVE"); } } @@ -357,6 +370,13 @@ else if (state == ServerState.WARMUP) if (rpp.matchready[0] && rpp.matchready[1] && !rpp.warmupphase) { state = ServerState.LIVE; + if (match.getGametype().getTeamSize() > 2){ + match.getLogic().setLastMapPlayed(match.getGametype(), match.getMap()); + } + backupStats.clear(); + for (ServerPlayer p : players){ + backupStats.put(p.auth, new CTF_Stats()); + } LOGGER.info("SWITCHED WARMUP -> LIVE"); } else if (!rpp.matchready[0] || !rpp.matchready[1]) @@ -367,11 +387,19 @@ else if (!rpp.matchready[0] || !rpp.matchready[1]) } else if (state == ServerState.LIVE) { - if (rpp.gametime != null && rpp.gametime.equals("00:00:00")) + // TODO: Refactor this, hard-coded for 1v1 and 2v2 + if ((rpp.gametime != null && rpp.gametime.equals("00:00:00") + && match.getGametype().getTeamSize() > 2) + || (match.getGametype().getTeamSize() <= 2 && (rpp.scores[0] >= 15 || rpp.scores[1] >= 15) + && rpp.gametime.equals("00:00:00"))) { state = ServerState.SCORE; LOGGER.info("SWITCHED LIVE -> SCORE"); } + checkNoMercy(rpp); + //backUpScores(rpp); + saveStats(rpp.scores, rpp); + match.updateScoreEmbed(); } else if (state == ServerState.SCORE) { @@ -411,7 +439,7 @@ private int getPlayerCount(String team) throws Exception { private void handleScoreTransition() throws Exception { swapRoles = getSwapRoles(); - saveStats(prevRPP.scores); + saveStats(prevRPP.scores, prevRPP); if (!swapRoles || (swapRoles && !firstHalf)) { endGame(); } else { @@ -425,7 +453,7 @@ private void updatePlayers(RconPlayersParsed rpp) throws Exception { for (ServerPlayer player : rpp.players) { - if (player.state == ServerPlayerState.Connecting) continue; // ignore connecting players + if (player.state == ServerPlayerState.Connecting || player.name.equals("GTV-b00bs")) continue; // ignore connecting players if (player.auth.equals("---")) { requestAuth(player); @@ -458,6 +486,9 @@ private void updatePlayers(RconPlayersParsed rpp) throws Exception { if (player.state != ServerPlayerState.Disconnected) { player.state = ServerPlayerState.Disconnected; player.timeDisconnect = System.currentTimeMillis(); +// CTF_Stats backup_stats = backupStats.get(player.auth); +// backup_stats.add(player.ctfstats); +// backupStats.put(player.auth, backup_stats); LOGGER.info("Player " + player.name + " (" + player.auth + ") disconnected."); } } @@ -503,7 +534,7 @@ private RconPlayersParsed parseRconPlayers() throws Exception { // hax to avoid empty if (stripped.length == 1) { - LOGGER.warning("Corrupted RPP (too short), taking prevRPP instead"); + LOGGER.info("Corrupted RPP (too short), taking prevRPP instead"); return prevRPP; } @@ -566,11 +597,11 @@ else if (line.startsWith("Half")) if (splitted[0].equals("CTF:") && awaitsStats) { // ctfstats - ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).caps = splitted[1].split(":")[1]; - ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).returns = splitted[2].split(":")[1]; - ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).fc_kills = splitted[3].split(":")[1]; - ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).stop_caps = splitted[4].split(":")[1]; - ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).protect_flag = splitted[5].split(":")[1]; + ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).caps = Integer.parseInt(splitted[1].split(":")[1]); + ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).returns = Integer.parseInt(splitted[2].split(":")[1]); + ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).fc_kills = Integer.parseInt(splitted[3].split(":")[1]); + ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).stop_caps = Integer.parseInt(splitted[4].split(":")[1]); + ((CTF_Stats) rpp.players.get(rpp.players.size()-1).ctfstats).protect_flag = Integer.parseInt(splitted[5].split(":")[1]); awaitsStats = false; } else if (splitted[0].equals("BOMB:") && awaitsStats) @@ -592,9 +623,9 @@ else if (rpp.players.size() < rpp.playercount) sp.id = splitted[0].split(":")[0]; sp.name = splitted[0].split(":").length > 1 ? splitted[0].split(":")[1] : "unknown"; sp.team = splitted[1].split(":")[1]; - sp.ctfstats.score = splitted[2].split(":")[1]; - sp.ctfstats.deaths = splitted[3].split(":")[1]; - sp.ctfstats.assists = splitted[4].split(":")[1]; + sp.ctfstats.score = Integer.parseInt(splitted[2].split(":")[1]); + sp.ctfstats.deaths = Integer.parseInt(splitted[3].split(":")[1]); + sp.ctfstats.assists = Integer.parseInt(splitted[4].split(":")[1]); sp.ping = splitted[5].split(":")[1]; sp.auth = splitted[6].split(":")[1]; sp.ip = splitted[7].split(":")[1]; @@ -612,8 +643,8 @@ else if (rpp.players.size() < rpp.playercount) } } // hax to avoid empty - if (rpp.map == null || rpp.playercount != rpp.players.size()) { - LOGGER.warning("Corrupted RPP, taking prevRPP instead"); + if (rpp.map == null || (rpp.playercount != rpp.players.size() && match.getGametype().getTeamSize() != 0)) { + LOGGER.info("Corrupted RPP, taking prevRPP instead"); return prevRPP; } return rpp; @@ -639,6 +670,11 @@ private void calcStats() throws Exception { } public void calcElo(Player player, int[] score) throws Exception { + if (match.getGametype().getTeamSize() <= 2 || match.getGametype().getName().equalsIgnoreCase("SCRIM TS") || match.getGametype().getName().equalsIgnoreCase("SCRIM CTF")){ + player.addElo(0); + return; + } + int team = match.getTeam(player).equalsIgnoreCase("red") ? 0 : 1; int opp = (team + 1) % 2; @@ -660,7 +696,7 @@ public void calcElo(Player player, int[] score) throws Exception { Score[] playerStats = match.getStats(player).score; int performance = 0; for (Score stats : playerStats) { - performance += stats.score + stats.assists; + performance += 1.5 * stats.score + 0.75 * stats.assists; } float performanceRating = 1; if (elochange > 0) { @@ -670,7 +706,10 @@ public void calcElo(Player player, int[] score) throws Exception { } performanceRating = Math.min(1.75f, Math.max(0.25f, performanceRating)); elochange = (int) Math.floor(result * performanceRating); - + + if (player.hasBoostActive()){ + elochange = Math.max(0, elochange) * 2; + } int newelo = player.getElo() + elochange; LOGGER.info("ELO player: " + player.getUrtauth() + " old ELO: " + player.getElo() + " new ELO: " + newelo + " (" + (!String.valueOf(elochange).startsWith("-") ? "+" : "") + elochange + ")"); @@ -680,7 +719,7 @@ public void calcElo(Player player, int[] score) throws Exception { public void surrender(int teamid) throws Exception { // save stats if (state == ServerState.LIVE || state == ServerState.SCORE) { - saveStats(new int[] {0, 0}); // score don't matter as we override them. don't matter + saveStats(new int[] {0, 0}, prevRPP); // score don't matter as we override them. don't matter } int[] scorex = new int[2]; @@ -698,21 +737,23 @@ public void surrender(int teamid) throws Exception { private void abandonMatch(Status status, List involvedPlayers) throws Exception { // save stats if (state == ServerState.LIVE || state == ServerState.SCORE) { - saveStats(new int[] {0, 0}); // score don't matter as we override them. don't matter + saveStats(new int[] {0, 0}, prevRPP); // score don't matter as we override them. don't matter } for (Player player : match.getPlayerList()) { player.setEloChange(0); } - - for (Player player : involvedPlayers) { - BanReason reason = BanReason.NOSHOW; - if (match.getStats(player).getStatus() == Status.NOSHOW) { - reason = BanReason.NOSHOW; - } else if (match.getStats(player).getStatus() == Status.RAGEQUIT) { - reason = BanReason.RAGEQUIT; + + if (match.getGametype().getTeamSize() > 2){ + for (Player player : involvedPlayers) { + BanReason reason = BanReason.NOSHOW; + if (match.getStats(player).getStatus() == Status.NOSHOW) { + reason = BanReason.NOSHOW; + } else if (match.getStats(player).getStatus() == Status.RAGEQUIT) { + reason = BanReason.RAGEQUIT; + } + match.getLogic().autoBanPlayer(player, reason); } - match.getLogic().autoBanPlayer(player, reason); } String reason = status == Status.NOSHOW ? "NOSHOW" : status == Status.RAGEQUIT ? "RAGEQUIT" : "UNKNOWN"; @@ -724,6 +765,9 @@ private void abandonMatch(Status status, List involvedPlayers) throws Ex LOGGER.info(sendString); stop(); + if (match.getLogic().getLastMapPlayed(match.getGametype()).equals(match.getMap())){ + match.getLogic().removeLastMapPlayed(match.getGametype()); + } match.abandon(status, involvedPlayers); } @@ -749,6 +793,24 @@ public String getScore() { return String.valueOf(total[0]) + "-" + String.valueOf(total[1]); } + public int[] getScoreArray() { + + int[] total = new int[] { 0, 0 }; + if (!firstHalf) { + total[0] = score[0][0]; + total[1] = score[0][1]; + } + + if (state == ServerState.LIVE || state == ServerState.SCORE) { + int team1 = firstHalf ? 0 : 1; + int team2 = firstHalf ? 1 : 0; + total[0] += prevRPP.scores[team1]; + total[1] += prevRPP.scores[team2]; + } + + return total; + } + private String getTimeString(long time) throws Exception { time /= 1000L; // time in s @@ -789,4 +851,24 @@ private void sendDiscordMsg(String text) { public ServerState getState() { return state; } + + private void backUpScores(RconPlayersParsed rpp){ + for (ServerPlayer p : rpp.players){ + ServerPlayer oldP = null; + for (ServerPlayer prevP : prevRPP.players){ + if (p.auth.equals(prevP.auth)){ + oldP = prevP; + break; + } + } + if (oldP != null){ + // + 4 in case of tks in between ticks + if (p.ctfstats.score + 4 < oldP.ctfstats.score || p.ctfstats.assists < oldP.ctfstats.assists || p.ctfstats.deaths < oldP.ctfstats.deaths){ + CTF_Stats newStats = backupStats.get(p.auth); + newStats.add(oldP.ctfstats); + backupStats.put(oldP.auth, newStats); + } + } + } + } } diff --git a/src/de/gost0r/pickupbot/pickup/stats/WinDrawLoss.java b/src/de/gost0r/pickupbot/pickup/stats/WinDrawLoss.java index 940be3d..3ad748f 100644 --- a/src/de/gost0r/pickupbot/pickup/stats/WinDrawLoss.java +++ b/src/de/gost0r/pickupbot/pickup/stats/WinDrawLoss.java @@ -17,4 +17,8 @@ public double calcWinRatio() { return (d_win + d_draw * 0.5d) / (d_total); } + public int getTotal(){ + return win + draw + loss; + } + }