Skip to content

Commit 5fb1f5c

Browse files
authored
Merge pull request #6 from janesth/csbot-features
merge a million new things into master
2 parents d4a495e + c19ad70 commit 5fb1f5c

33 files changed

Lines changed: 1690 additions & 259 deletions

README.md

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,49 @@
11
# CounterStrikeBot
22

3+
[![GitHub release](https://img.shields.io/github/v/release/janesth/CounterStrikeBot)](https://github.com/janesth/CounterStrikeBot)
4+
35
The purpose of this bot is to inform users of a Discord server in a specific channel about played matches.
4-
Continuing from its original purpose, it also allows a specific user group to execute "rcon changelevel <map_name>" to change the level on a to be specified retake server.
6+
Continuing from its original purpose, it also allows a specific user group to execute "rcon changelevel <map_name>" to change the level on a retake server.
57

68
## Configuration
79

8-
All of these properties are defined in "config.properties".
9-
These properties are relevant to connect to Discord and to be aware of the permitted user group:
10-
- `discord.apiToken` - https://discord.com/developers/applications to receive your individual bot token
11-
- `discord.allowedRoleId` - the ID of a user group to use the command
10+
All of these properties are defined in "config.properties". Mandatory properties are **bold**.
11+
12+
These properties are relevant to connect to Discord and to limit this bot's functionalities by various factors:
13+
- **`discord.apiToken` - https://discord.com/developers/applications to receive your individual bot token**
14+
- `discord.allowedRoleId` - the ID of a user group to use commands
15+
- `discord.thisIsMyHome` - the ID of the server's home base
1216

1317
These properties are currently relevant for the CS2 stats feature.
14-
- `steam.api` - Steam Web API key
15-
- `` -
16-
- `` -
17-
- `` -
18+
- **`steam.api` - Steam Web API key**
1819

19-
The following properties are relevant to connect to the retake server and what maps are allowed to be played:
20+
The following properties were relevant to connect to the retake server and what maps are allowed to be played:
2021
- `server.ip` - the IP address of the csgo server
2122
- `server.port` - the port of the csgo server
2223
- `server.password` - the RCON password of the csgo server (to define in `server.cfg`)
2324
- `csgo.maps` - a comma seperated list of allowed maps to switch to (like `de_dust,de_tuscan,...`)
2425
- `server.delay` - a delay (in seconds) to stop users from spamming a map change.
2526

26-
The next properties were relevant for a scratched commendation system. They can be empty as of now:
27+
The next properties were relevant for a scratched commendation system:
2728
- `server.ftp.ip` - the IP address to access the server using FTP
2829
- `server.ftp.port` - the FTP port
2930
- `server.ftp.user` - the FTP user (access has to be granted outside of this application)
3031
- `server.ftp.password` - the FTP password
32+
33+
The last property is relevant regarding the bot's wow feature:
34+
- **`db.url` - the URL of a database for the bot to store all submitted wow clips**
35+
36+
## Run the bot
37+
38+
TO BE DEFINED
39+
40+
## F.A.Q.
41+
42+
### Can I run the bot myself? Do I have to invite the public instance of the bot to my server??
43+
You can invite the bot to your server or run it yourself by changing the properties in config.properties and following the tutorial above the F.A.Q. section.
44+
45+
### Why doesn't ``/map`` work?
46+
SourceMod is not compatible with Counter Strike 2.
47+
48+
### Can I fork this and make it better?
49+
Yes.

pom.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@
4848
<artifactId>mysql-connector-java</artifactId>
4949
<version>8.0.33</version>
5050
</dependency>
51+
<dependency>
52+
<groupId>com.sparkjava</groupId>
53+
<artifactId>spark-core</artifactId>
54+
<version>2.9.3</version>
55+
</dependency>
56+
<dependency>
57+
<groupId>com.google.guava</groupId>
58+
<artifactId>guava</artifactId>
59+
<version>33.2.1-jre</version>
60+
</dependency>
61+
<dependency>
62+
<groupId>org.junit.jupiter</groupId>
63+
<artifactId>junit-jupiter-engine</artifactId>
64+
<version>5.10.0</version>
65+
<scope>test</scope>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.mockito</groupId>
69+
<artifactId>mockito-core</artifactId>
70+
<version>5.12.0</version>
71+
</dependency>
72+
5173

5274

5375
</dependencies>

src/main/java/StartUp.java

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,59 @@
22
import net.dv8tion.jda.api.JDA;
33
import net.dv8tion.jda.api.JDABuilder;
44
import net.dv8tion.jda.api.entities.Activity;
5-
import net.dv8tion.jda.api.events.GenericEvent;
6-
import net.dv8tion.jda.api.events.session.ReadyEvent;
7-
import net.dv8tion.jda.api.hooks.EventListener;
85
import net.dv8tion.jda.api.interactions.commands.Command;
96
import net.dv8tion.jda.api.interactions.commands.OptionType;
107
import net.dv8tion.jda.api.interactions.commands.build.Commands;
8+
import net.dv8tion.jda.api.requests.GatewayIntent;
9+
import net.dv8tion.jda.api.utils.ChunkingFilter;
10+
import net.dv8tion.jda.api.utils.MemberCachePolicy;
11+
import services.DataService;
12+
import services.FaceitMatchService;
1113

1214
import java.io.IOException;
1315
import java.io.InputStream;
16+
import java.sql.SQLException;
1417
import java.time.LocalDateTime;
1518
import java.time.format.DateTimeFormatter;
1619
import java.util.Locale;
1720
import java.util.Properties;
1821
import java.util.ResourceBundle;
1922

20-
public class StartUp implements EventListener {
23+
import static spark.Spark.get;
24+
import static spark.Spark.port;
2125

22-
private final DateTimeFormatter START_TIME = DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss");
26+
public class StartUp {
27+
28+
private static final DateTimeFormatter START_TIME = DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss");
2329

2430
public static void main(String[] args) {
2531
try {
32+
33+
String startMessage = LocalDateTime.now().format(START_TIME) + " - Started application.";
34+
System.out.println(startMessage);
35+
2636
InputStream inputStream = StartUp.class.getClassLoader().getResourceAsStream("config.properties");
2737
Properties properties = new Properties();
2838
properties.load(inputStream);
2939

3040
//figure out a way to get discord guild locale if possible
3141
//FYI: Locale.getDefault() returns locale of OS
3242
ResourceBundle resourceBundle = ResourceBundle.getBundle("localization", new Locale("en"));
43+
DataService dataService = null;
44+
try {
45+
dataService = new DataService(properties);
46+
} catch (SQLException ex) {
47+
System.out.println("SQL Exception thrown: " + ex.getMessage());
48+
}
3349

50+
FaceitMatchService faceitMatchService = new FaceitMatchService(properties, dataService);
3451

3552
JDA jda = JDABuilder.createDefault(properties.getProperty("discord.apiToken"))
36-
.addEventListeners(new StartUp())
37-
.addEventListeners(new CounterStrikeBotListener(properties))
53+
.addEventListeners(new CounterStrikeBotListener(properties, dataService))
54+
.setChunkingFilter(ChunkingFilter.ALL)
55+
.setMemberCachePolicy(MemberCachePolicy.ALL)
56+
.enableIntents(GatewayIntent.GUILD_MEMBERS)
57+
.enableIntents(GatewayIntent.DIRECT_MESSAGES)
3858
.build();
3959

4060
jda.getPresence().setActivity(Activity.playing("YOINC.ch"));
@@ -43,20 +63,26 @@ public static void main(String[] args) {
4363
Commands.slash("stats", resourceBundle.getString("command.stats.description")).addOption(OptionType.STRING,"player",resourceBundle.getString("command.stats.value.description"), true),
4464
Commands.slash("compare", resourceBundle.getString("command.compare.description")).addOption(OptionType.STRING, "playerone", resourceBundle.getString("command.compare.valueone.description"), true).addOption(OptionType.STRING, "playertwo", resourceBundle.getString("command.compare.valuetwo.description"), true),
4565
Commands.slash("wow", resourceBundle.getString("command.wow.description")).addOption(OptionType.STRING, "url", resourceBundle.getString("command.wow.value.description"), true),
46-
Commands.context(Command.Type.USER, "wow")).queue();
66+
Commands.slash("teams", resourceBundle.getString("command.teams.description")).addOption(OptionType.NUMBER, "amountofteams", resourceBundle.getString("command.teams.value.description"), false),
67+
Commands.slash("status", resourceBundle.getString("command.status.description")),
68+
Commands.context(Command.Type.USER, "wow"),
69+
Commands.context(Command.Type.USER, "retake stats")).queue();
70+
4771
jda.awaitReady();
72+
73+
port(50429);
74+
get("/faceit-match-started", (request, response) -> {
75+
faceitMatchService.handleFaceitMatchStartEvent(request, jda.getGuilds());
76+
return response;
77+
});
78+
get("/faceit-match-ended", (request, response) -> {
79+
faceitMatchService.handleFaceitMatchEndEvent(request, jda.getGuilds());
80+
return response;
81+
});
4882
} catch (InterruptedException ex) {
49-
System.out.println("Nice. Something interrupted the connection.");
83+
System.out.println("InterruptedException thrown: " + ex.getMessage());
5084
} catch (IOException ex) {
51-
System.out.println("Nice. Problems with that property.");
52-
}
53-
}
54-
55-
@Override
56-
public void onEvent(GenericEvent genericEvent) {
57-
if (genericEvent instanceof ReadyEvent) {
58-
String startMessage = LocalDateTime.now().format(START_TIME) + " - Started application.";
59-
System.out.println(startMessage);
85+
System.out.println("IOException thrown: " + ex.getMessage());
6086
}
6187
}
6288
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package http;
2+
3+
import com.google.gson.*;
4+
import model.faceit.FaceitMatch;
5+
import model.steam.ResponseData;
6+
7+
import java.io.IOException;
8+
import java.net.URI;
9+
import java.net.http.HttpClient;
10+
import java.net.http.HttpRequest;
11+
import java.net.http.HttpResponse;
12+
import java.util.Properties;
13+
14+
public class ConnectionBuilder {
15+
16+
Properties properties;
17+
18+
public ConnectionBuilder(Properties properties) {
19+
this.properties = properties;
20+
}
21+
22+
public ResponseData fetchSteamUserStats(String steamID) throws InterruptedException, IOException {
23+
24+
HttpClient client = HttpClient.newHttpClient();
25+
26+
HttpRequest request;
27+
ResponseData responseData;
28+
29+
request = HttpRequest.newBuilder()
30+
.uri(URI.create("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" + properties.get("steam.api") + "&steamids=" + steamID))
31+
.build();
32+
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
33+
34+
responseData = new Gson().fromJson(response.body(), ResponseData.class);
35+
36+
request = HttpRequest.newBuilder()
37+
.uri(URI.create("https://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?key=" + properties.get("steam.api") + "&appid=730&steamid=" + steamID))
38+
.build();
39+
response = client.send(request, HttpResponse.BodyHandlers.ofString());
40+
responseData.setPlayerstats(new Gson().fromJson(response.body(), ResponseData.class).getPlayerstats());
41+
42+
return responseData;
43+
}
44+
45+
public FaceitMatch fetchFaceitMatchDetails(String userId) throws IOException, InterruptedException {
46+
HttpClient client = HttpClient.newHttpClient();
47+
48+
String matchId = fetchFaceitMatchId(client, userId);
49+
50+
HttpRequest request;
51+
FaceitMatch responseData;
52+
53+
request = HttpRequest.newBuilder()
54+
.uri(URI.create("https://open.faceit.com/data/v4/matches/" + matchId))
55+
.header("Authorization", "Bearer " + properties.getProperty("faceit.api"))
56+
.build();
57+
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
58+
59+
responseData = new Gson().fromJson(response.body(), FaceitMatch.class);
60+
61+
return responseData;
62+
}
63+
64+
private String fetchFaceitMatchId(HttpClient client, String userId) throws IOException, InterruptedException {
65+
HttpRequest request;
66+
String id = null;
67+
68+
request = HttpRequest.newBuilder()
69+
.uri(URI.create("https://www.faceit.com/api/match/v1/matches/groupByState?userId=" + userId))
70+
.build();
71+
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
72+
73+
JsonElement jsonElement = JsonParser.parseString(response.body());
74+
JsonObject jsonObject = jsonElement.getAsJsonObject();
75+
76+
JsonObject payload = jsonObject.getAsJsonObject("payload");
77+
JsonArray ongoingArray = payload.getAsJsonArray("ONGOING");
78+
JsonArray readyArray = payload.getAsJsonArray("READY");
79+
if (ongoingArray != null && !ongoingArray.isEmpty()) {
80+
JsonObject firstOngoingItem = ongoingArray.get(0).getAsJsonObject();
81+
id = firstOngoingItem.get("id").getAsString();
82+
} else if (readyArray != null && !readyArray.isEmpty()) {
83+
JsonObject firstReadyItem = readyArray.get(0).getAsJsonObject();
84+
id = firstReadyItem.get("id").getAsString();
85+
}
86+
return id;
87+
}
88+
}
Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
package listeners;
22

3-
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent;
3+
import net.dv8tion.jda.api.JDA;
44
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
55
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
6+
import net.dv8tion.jda.api.events.session.ReadyEvent;
67
import net.dv8tion.jda.api.hooks.ListenerAdapter;
7-
import services.CsFunService;
8-
import services.CsStatsService;
9-
import services.RetakeService;
8+
import services.*;
109

1110
import java.util.Properties;
11+
import java.util.concurrent.CompletableFuture;
1212

1313
public class CounterStrikeBotListener extends ListenerAdapter {
1414

1515
private CsStatsService csStatsService;
1616
private RetakeService retakeService;
1717
private CsFunService csFunService;
18+
private DiscordService discordService;
1819

19-
public CounterStrikeBotListener(Properties properties) {
20-
csStatsService = new CsStatsService(properties);
21-
csFunService = new CsFunService(properties);
22-
retakeService = new RetakeService(properties);
20+
public CounterStrikeBotListener(Properties properties, DataService dataService) {
21+
csStatsService = new CsStatsService(properties, dataService);
22+
csFunService = new CsFunService(properties, dataService);
23+
retakeService = new RetakeService(properties, dataService);
24+
discordService = new DiscordService(properties, dataService, retakeService);
2325
}
2426

2527
@Override
2628
public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
2729

28-
String locale = getUserLocale(event);
30+
String locale = discordService.getUserLocale(event);
2931

3032
if ("stats".equals(event.getName())) {
3133
event.deferReply().queue();
@@ -46,6 +48,16 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
4648
event.deferReply().queue();
4749
event.getHook().sendMessage(csFunService.handleAddWowEvent(event, locale)).queue();
4850
}
51+
52+
if("teams".equals(event.getName())) {
53+
event.deferReply().queue();
54+
event.getHook().sendMessageEmbeds(csFunService.handleSetTeamsEvent(event, locale).build()).queue();
55+
}
56+
57+
if("status".equals(event.getName())) {
58+
event.deferReply().queue();
59+
event.getHook().sendMessageEmbeds(retakeService.handleStatusEvent(event, locale).build()).queue();
60+
}
4961
}
5062

5163
@Override
@@ -58,36 +70,21 @@ public void onUserContextInteraction(UserContextInteractionEvent event) {
5870
language of the enacting user. as a "neutral" locale the guild could be used
5971
(event.getGuild().getLocale()) but it could result in the same problem.
6072
*/
61-
String locale = getUserLocale(event);
73+
String locale = discordService.getUserLocale(event);
6274

6375
if("wow".equals(event.getName())) {
6476
event.deferReply().queue();
6577
event.getHook().sendMessage(csFunService.handleWowEvent(event, locale)).queue();
6678
}
67-
68-
}
69-
70-
private String getUserLocale(GenericCommandInteractionEvent event) {
71-
String locale = "en";
72-
if(event.getInteraction().getUserLocale().getLocale().equals("de")) {
73-
locale = "de";
79+
if("retake stats".equals(event.getName())){
80+
event.deferReply().queue();
81+
event.getHook().sendMessageEmbeds(retakeService.handleStatsEvent(event, locale).build()).queue();
7482
}
75-
return locale;
76-
}
77-
78-
public CsStatsService getCsStatsService() {
79-
return csStatsService;
80-
}
81-
82-
public void setCsStatsService(CsStatsService csStatsService) {
83-
this.csStatsService = csStatsService;
84-
}
85-
86-
public RetakeService getRetakeService() {
87-
return retakeService;
8883
}
8984

90-
public void setRetakeService(RetakeService retakeService) {
91-
this.retakeService = retakeService;
85+
@Override
86+
public void onReady(ReadyEvent event){
87+
JDA jda = event.getJDA();
88+
CompletableFuture.runAsync( () -> discordService.scheduleAllTasks(jda));
9289
}
9390
}

0 commit comments

Comments
 (0)