Skip to content

Commit 25a4407

Browse files
committed
Analytics: applies changes for zabuzard 1st CR;
1 parent 071c1dc commit 25a4407

File tree

8 files changed

+84
-130
lines changed

8 files changed

+84
-130
lines changed

application/src/main/java/org/togetherjava/tjbot/Application.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.togetherjava.tjbot.db.Database;
1313
import org.togetherjava.tjbot.features.Features;
1414
import org.togetherjava.tjbot.features.SlashCommandAdapter;
15+
import org.togetherjava.tjbot.features.analytics.Metrics;
1516
import org.togetherjava.tjbot.features.system.BotCore;
1617
import org.togetherjava.tjbot.logging.LogMarkers;
1718
import org.togetherjava.tjbot.logging.discord.DiscordLogging;
@@ -82,13 +83,15 @@ public static void runBot(Config config) {
8283
}
8384
Database database = new Database("jdbc:sqlite:" + databasePath.toAbsolutePath());
8485

86+
Metrics metrics = new Metrics(database);
87+
8588
JDA jda = JDABuilder.createDefault(config.getToken())
8689
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.MESSAGE_CONTENT)
8790
.build();
8891

8992
jda.awaitReady();
9093

91-
BotCore core = new BotCore(jda, database, config);
94+
BotCore core = new BotCore(jda, database, config, metrics);
9295
CommandReloading.reloadCommands(jda, core);
9396
core.scheduleRoutines(jda);
9497

application/src/main/java/org/togetherjava/tjbot/features/Features.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.togetherjava.tjbot.config.FeatureBlacklist;
77
import org.togetherjava.tjbot.config.FeatureBlacklistConfig;
88
import org.togetherjava.tjbot.db.Database;
9+
import org.togetherjava.tjbot.features.analytics.Metrics;
910
import org.togetherjava.tjbot.features.basic.MemberCountDisplayRoutine;
1011
import org.togetherjava.tjbot.features.basic.PingCommand;
1112
import org.togetherjava.tjbot.features.basic.QuoteBoardForwarder;
@@ -91,7 +92,7 @@
9192
* it with the system.
9293
* <p>
9394
* To add a new slash command, extend the commands returned by
94-
* {@link #createFeatures(JDA, Database, Config)}.
95+
* {@link #createFeatures(JDA, Database, Config, Metrics)}.
9596
*/
9697
public class Features {
9798
private Features() {
@@ -107,9 +108,12 @@ private Features() {
107108
* @param jda the JDA instance commands will be registered at
108109
* @param database the database of the application, which features can use to persist data
109110
* @param config the configuration features should use
111+
* @param metrics the metrics service for tracking analytics
110112
* @return a collection of all features
111113
*/
112-
public static Collection<Feature> createFeatures(JDA jda, Database database, Config config) {
114+
@SuppressWarnings("unused")
115+
public static Collection<Feature> createFeatures(JDA jda, Database database, Config config,
116+
Metrics metrics) {
113117
FeatureBlacklistConfig blacklistConfig = config.getFeatureBlacklistConfig();
114118
JShellEval jshellEval = new JShellEval(config.getJshell(), config.getGitHubApiKey());
115119

application/src/main/java/org/togetherjava/tjbot/features/analytics/AnalyticsService.java

Lines changed: 0 additions & 88 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.togetherjava.tjbot.features.analytics;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
import org.togetherjava.tjbot.db.Database;
7+
import org.togetherjava.tjbot.db.generated.tables.Analytics;
8+
9+
import java.time.Instant;
10+
import java.util.concurrent.ExecutorService;
11+
import java.util.concurrent.Executors;
12+
13+
/**
14+
* Service for tracking and recording events for analytics purposes.
15+
*/
16+
public final class Metrics {
17+
private static final Logger logger = LoggerFactory.getLogger(Metrics.class);
18+
19+
private final Database database;
20+
21+
private final ExecutorService service = Executors.newSingleThreadExecutor();
22+
23+
/**
24+
* Creates a new instance.
25+
*
26+
* @param database the database to use for storing and retrieving analytics data
27+
*/
28+
public Metrics(Database database) {
29+
this.database = database;
30+
}
31+
32+
/**
33+
* Track an event execution.
34+
*
35+
* @param event the event to save
36+
*/
37+
public void count(String event) {
38+
logger.debug("Counting new record for event: {}", event);
39+
Instant moment = Instant.now();
40+
service.submit(() -> persist(event, moment));
41+
42+
logger.debug("Event {} new record saved successfully", event);
43+
}
44+
45+
/**
46+
*
47+
* @param event the event to save
48+
* @param moment the moment when the event is dispatched
49+
*/
50+
private void persist(String event, Instant moment) {
51+
logger.debug("Persisting event: {}, at {}", event, moment);
52+
database.write(context -> context.newRecord(Analytics.ANALYTICS)
53+
.setEvent(event)
54+
.setHappenedAt(moment)
55+
.insert());
56+
logger.debug("Event {} persisted successfully", event);
57+
}
58+
59+
}

application/src/main/java/org/togetherjava/tjbot/features/analytics/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Analytics system for collecting and persisting bot activity metrics.
33
* <p>
44
* This package provides services and components that record events for later analysis and reporting
5-
* across multiple feature areas, not limited to commands.
5+
* across multiple feature areas.
66
*/
77
@MethodsReturnNonnullByDefault
88
@ParametersAreNonnullByDefault

application/src/main/java/org/togetherjava/tjbot/features/basic/PingCommand.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.togetherjava.tjbot.features.basic;
22

3-
import net.dv8tion.jda.api.entities.Guild;
43
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
54

65
import org.togetherjava.tjbot.features.CommandVisibility;
@@ -12,7 +11,6 @@
1211
* The implemented command is {@code /ping}, upon which the bot will respond with {@code Pong!}.
1312
*/
1413
public final class PingCommand extends SlashCommandAdapter {
15-
1614
/**
1715
* Creates an instance of the ping pong command.
1816
*/
@@ -27,12 +25,6 @@ public PingCommand() {
2725
*/
2826
@Override
2927
public void onSlashCommand(SlashCommandInteractionEvent event) {
30-
Guild guild = event.getGuild();
31-
if (guild == null) {
32-
event.reply("This command can only be used in a server!").setEphemeral(true).queue();
33-
return;
34-
}
35-
3628
event.reply("Pong!").queue();
3729
}
3830
}

application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import org.togetherjava.tjbot.features.UserInteractionType;
4242
import org.togetherjava.tjbot.features.UserInteractor;
4343
import org.togetherjava.tjbot.features.VoiceReceiver;
44-
import org.togetherjava.tjbot.features.analytics.AnalyticsService;
44+
import org.togetherjava.tjbot.features.analytics.Metrics;
4545
import org.togetherjava.tjbot.features.componentids.ComponentId;
4646
import org.togetherjava.tjbot.features.componentids.ComponentIdParser;
4747
import org.togetherjava.tjbot.features.componentids.ComponentIdStore;
@@ -85,7 +85,7 @@ public final class BotCore extends ListenerAdapter implements CommandProvider {
8585
private final ComponentIdStore componentIdStore;
8686
private final Map<Pattern, MessageReceiver> channelNameToMessageReceiver = new HashMap<>();
8787
private final Map<Pattern, VoiceReceiver> channelNameToVoiceReceiver = new HashMap<>();
88-
private final AnalyticsService analyticsService;
88+
private final Metrics metrics;
8989

9090
/**
9191
* Creates a new command system which uses the given database to allow commands to persist data.
@@ -95,12 +95,11 @@ public final class BotCore extends ListenerAdapter implements CommandProvider {
9595
* @param jda the JDA instance that this command system will be used with
9696
* @param database the database that commands may use to persist data
9797
* @param config the configuration to use for this system
98+
* @param metrics the metrics service for tracking analytics
9899
*/
99-
public BotCore(JDA jda, Database database, Config config) {
100-
Collection<Feature> features = Features.createFeatures(jda, database, config);
101-
102-
// Initialize analytics service
103-
analyticsService = new AnalyticsService(database);
100+
public BotCore(JDA jda, Database database, Config config, Metrics metrics) {
101+
this.metrics = metrics;
102+
Collection<Feature> features = Features.createFeatures(jda, database, config, metrics);
104103

105104
// Message receivers
106105
features.stream()
@@ -383,22 +382,14 @@ public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
383382
logger.debug("Received slash command '{}' (#{}) on guild '{}'", name, event.getId(),
384383
event.getGuild());
385384
COMMAND_SERVICE.execute(() -> {
386-
try {
387-
requireUserInteractor(UserInteractionType.SLASH_COMMAND.getPrefixedName(name),
388-
SlashCommand.class)
389-
.onSlashCommand(event);
390385

386+
SlashCommand interactor = requireUserInteractor(
387+
UserInteractionType.SLASH_COMMAND.getPrefixedName(name), SlashCommand.class);
391388

392-
analyticsService.recordCommandSuccess(event.getChannel().getIdLong(), name,
393-
event.getUser().getIdLong());
394-
} catch (Exception ex) {
389+
metrics.count("slash-" + name);
395390

396-
analyticsService.recordCommandFailure(event.getChannel().getIdLong(), name,
397-
event.getUser().getIdLong(),
398-
ex.getMessage() != null ? ex.getMessage() : ex.getClass().getSimpleName());
391+
interactor.onSlashCommand(event);
399392

400-
throw ex;
401-
}
402393
});
403394
}
404395

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
CREATE TABLE command_usage
1+
CREATE TABLE analytics
22
(
3-
id INTEGER PRIMARY KEY AUTOINCREMENT,
4-
channel_id BIGINT NOT NULL,
5-
command_name TEXT NOT NULL,
6-
user_id BIGINT NOT NULL,
7-
executed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
8-
success BOOLEAN NOT NULL DEFAULT TRUE,
9-
error_message TEXT
3+
id INTEGER PRIMARY KEY AUTOINCREMENT,
4+
event TEXT NOT NULL,
5+
happened_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
106
);
11-
12-
CREATE INDEX idx_command_usage_channel ON command_usage(channel_id);
13-
CREATE INDEX idx_command_usage_command_name ON command_usage(command_name);

0 commit comments

Comments
 (0)