Skip to content

Commit dd16c93

Browse files
Merge pull request #349 from Java-Discord/dynxsty/experience_routes
2 parents b03783c + a13f7ea commit dd16c93

File tree

12 files changed

+259
-44
lines changed

12 files changed

+259
-44
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ public class PingCommand extends SlashCommand {
5656

5757
For more information on how this works, visit the [DIH4JDA Wiki!](https://github.com/DynxstyGIT/DIH4JDA/wiki)
5858

59+
# API Documentation
60+
61+
#### `GET` `guilds/{guild_id}/metrics`
62+
- Responds with guild-specific metrics, such as the member- and (approximate) online count.
63+
64+
#### `GET` `guilds/{guild_id}/users/{user_id}`
65+
- Responds with the specified users' profile which includes some basic info, such as the users' name and avatar url, but also their recent warns, their current help experience and more!
66+
67+
#### `GET` `guilds/{guild_id}/leaderboard/qotw?page=1`
68+
- A paginated endpoint which responds with an ordered list of users, based on their QOTW points.
69+
70+
#### `GET` `guilds/{guild_id}/leaderboard/experience?page=1`
71+
- A paginated endpoint which responds with an ordered list of users, based on their help channel experience.
72+
73+
You can try out the API yourself on `api.javadiscord.net`!
74+
5975
# Credits
6076

6177
Inspiration we took from other communities:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package net.javadiscord.javabot.api.routes.data;
2+
3+
import lombok.Data;
4+
5+
/**
6+
* Abstract class which contains basic user values.
7+
*/
8+
@Data
9+
public abstract class UserData {
10+
private long userId;
11+
private String userName;
12+
private String discriminator;
13+
private String effectiveAvatarUrl;
14+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package net.javadiscord.javabot.api.routes.leaderboard.help_experience;
2+
3+
import com.github.benmanes.caffeine.cache.Caffeine;
4+
import net.dv8tion.jda.api.JDA;
5+
import net.dv8tion.jda.api.entities.Guild;
6+
import net.javadiscord.javabot.Bot;
7+
import net.javadiscord.javabot.api.exception.InvalidEntityIdException;
8+
import net.javadiscord.javabot.api.routes.CaffeineCache;
9+
import net.javadiscord.javabot.api.routes.leaderboard.help_experience.model.ExperienceUserData;
10+
import net.javadiscord.javabot.systems.help.HelpExperienceService;
11+
import net.javadiscord.javabot.util.Pair;
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.http.HttpStatus;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PathVariable;
17+
import org.springframework.web.bind.annotation.RequestParam;
18+
import org.springframework.web.bind.annotation.RestController;
19+
20+
import java.util.List;
21+
import java.util.concurrent.TimeUnit;
22+
23+
/**
24+
* Handles all GET-Requests on the guilds/{guild_id}/leaderboard/experience/ route.
25+
*/
26+
@RestController
27+
public class HelpExperienceLeaderboardController extends CaffeineCache<Pair<Long, Integer>, List<ExperienceUserData>> {
28+
private static final int PAGE_AMOUNT = 8;
29+
private final JDA jda;
30+
31+
/**
32+
* The constructor of this class which initializes the {@link Caffeine} cache.
33+
*
34+
* @param jda The {@link JDA} instance to use.
35+
*/
36+
@Autowired
37+
public HelpExperienceLeaderboardController(final JDA jda) {
38+
super(Caffeine.newBuilder()
39+
.expireAfterWrite(10, TimeUnit.MINUTES)
40+
.build()
41+
);
42+
this.jda = jda;
43+
}
44+
45+
/**
46+
* Serves the specified amount of users. Sorted by the
47+
* amount of help experience.
48+
*
49+
* @param guildId The guilds' id.
50+
* @param page The page to get. Defaults to 1.
51+
* @return The {@link ResponseEntity}.
52+
*/
53+
@GetMapping("guilds/{guild_id}/leaderboard/experience")
54+
public ResponseEntity<List<ExperienceUserData>> getHelpExperienceLeaderboard(
55+
@PathVariable("guild_id") long guildId,
56+
@RequestParam(value = "page", defaultValue = "1") int page
57+
) {
58+
Guild guild = jda.getGuildById(guildId);
59+
if (guild == null) {
60+
throw new InvalidEntityIdException(Guild.class, "You've provided an invalid guild id!");
61+
}
62+
HelpExperienceService service = new HelpExperienceService(Bot.getDataSource());
63+
List<ExperienceUserData> members = getCache().getIfPresent(new Pair<>(guild.getIdLong(), page));
64+
if (members == null || members.isEmpty()) {
65+
members = service.getTopAccounts(PAGE_AMOUNT, page).stream()
66+
.map(p -> ExperienceUserData.of(p, jda.retrieveUserById(p.getUserId()).complete()))
67+
.toList();
68+
getCache().put(new Pair<>(guild.getIdLong(), page), members);
69+
}
70+
return new ResponseEntity<>(members, HttpStatus.OK);
71+
}
72+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package net.javadiscord.javabot.api.routes.leaderboard.help_experience.model;
2+
3+
import lombok.Data;
4+
import lombok.EqualsAndHashCode;
5+
import net.dv8tion.jda.api.entities.User;
6+
import net.javadiscord.javabot.api.routes.data.UserData;
7+
import net.javadiscord.javabot.systems.help.model.HelpAccount;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
/**
12+
* API-Data class which contains all necessary information about a single users'
13+
* Help-Account.
14+
*/
15+
@Data
16+
@EqualsAndHashCode(callSuper = false)
17+
public class ExperienceUserData extends UserData {
18+
private HelpAccount account;
19+
20+
/**
21+
* Creates a new {@link ExperienceUserData} instance.
22+
*
23+
* @param account The {@link HelpAccount} to use.
24+
* @param user A nullable {@link User}.
25+
* @return The {@link ExperienceUserData}.
26+
*/
27+
public static @NotNull ExperienceUserData of(@NotNull HelpAccount account, @Nullable User user) {
28+
ExperienceUserData data = new ExperienceUserData();
29+
data.setUserId(account.getUserId());
30+
if (user != null) {
31+
data.setUserName(user.getName());
32+
data.setDiscriminator(user.getDiscriminator());
33+
data.setEffectiveAvatarUrl(user.getEffectiveAvatarUrl());
34+
}
35+
data.setAccount(account);
36+
return data;
37+
}
38+
}

src/main/java/net/javadiscord/javabot/api/routes/qotw_leaderboard/QOTWLeaderboardController.java renamed to src/main/java/net/javadiscord/javabot/api/routes/leaderboard/qotw/QOTWLeaderboardController.java

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
package net.javadiscord.javabot.api.routes.qotw_leaderboard;
1+
package net.javadiscord.javabot.api.routes.leaderboard.qotw;
22

33
import com.github.benmanes.caffeine.cache.Caffeine;
44
import net.dv8tion.jda.api.JDA;
55
import net.dv8tion.jda.api.entities.Guild;
66
import net.javadiscord.javabot.Bot;
77
import net.javadiscord.javabot.api.exception.InvalidEntityIdException;
88
import net.javadiscord.javabot.api.routes.CaffeineCache;
9-
import net.javadiscord.javabot.api.routes.qotw_leaderboard.model.QOTWMemberData;
9+
import net.javadiscord.javabot.api.routes.leaderboard.qotw.model.QOTWUserData;
1010
import net.javadiscord.javabot.systems.qotw.QOTWPointsService;
11+
import net.javadiscord.javabot.util.Pair;
1112
import org.springframework.beans.factory.annotation.Autowired;
1213
import org.springframework.http.HttpStatus;
1314
import org.springframework.http.ResponseEntity;
@@ -20,10 +21,11 @@
2021
import java.util.concurrent.TimeUnit;
2122

2223
/**
23-
* Handles all GET-Requests on the {guild_id}/qotw/leaderboard route.
24+
* Handles all GET-Requests on the guilds/{guild_id}/leaderboard/qotw/ route.
2425
*/
2526
@RestController
26-
public class QOTWLeaderboardController extends CaffeineCache<Long, List<QOTWMemberData>> {
27+
public class QOTWLeaderboardController extends CaffeineCache<Pair<Long, Integer>, List<QOTWUserData>> {
28+
private static final int PAGE_AMOUNT = 8;
2729
private final JDA jda;
2830

2931
/**
@@ -45,34 +47,26 @@ public QOTWLeaderboardController(final JDA jda) {
4547
* amount of qotw-points.
4648
*
4749
* @param guildId The guilds' id.
48-
* @param amount The amount of users to return. Defaults to 3.
50+
* @param page The page to get. Defaults to 1.
4951
* @return The {@link ResponseEntity}.
5052
*/
51-
@GetMapping("guilds/{guild_id}/qotw/leaderboard")
52-
public ResponseEntity<List<QOTWMemberData>> getQOTWLeaderboard(
53+
@GetMapping("guilds/{guild_id}/leaderboard/qotw")
54+
public ResponseEntity<List<QOTWUserData>> getQOTWLeaderboard(
5355
@PathVariable("guild_id") long guildId,
54-
@RequestParam(value = "amount", defaultValue = "3") int amount
56+
@RequestParam(value = "page", defaultValue = "1") int page
5557
) {
5658
Guild guild = jda.getGuildById(guildId);
5759
if (guild == null) {
5860
throw new InvalidEntityIdException(Guild.class, "You've provided an invalid guild id!");
5961
}
6062
QOTWPointsService service = new QOTWPointsService(Bot.getDataSource());
61-
List<QOTWMemberData> members = getCache().getIfPresent(guild.getIdLong());
63+
List<QOTWUserData> members = getCache().getIfPresent(new Pair<>(guild.getIdLong(), page));
6264
if (members == null || members.isEmpty()) {
63-
members = service.getTopMembers(amount, guild).stream()
64-
.map(p -> {
65-
QOTWMemberData data = new QOTWMemberData();
66-
data.setUserId(p.second().getIdLong());
67-
data.setUserName(p.second().getUser().getName());
68-
data.setDiscriminator(p.second().getUser().getDiscriminator());
69-
data.setEffectiveAvatarUrl(p.second().getEffectiveAvatarUrl());
70-
data.setAccount(p.first());
71-
return data;
72-
})
65+
members = service.getTopAccounts(PAGE_AMOUNT, page).stream()
66+
.map(p -> QOTWUserData.of(p, jda.retrieveUserById(p.getUserId()).complete()))
7367
.toList();
74-
getCache().put(guild.getIdLong(), members);
68+
getCache().put(new Pair<>(guild.getIdLong(), page), members);
7569
}
76-
return new ResponseEntity<>(members.stream().limit(amount).toList(), HttpStatus.OK);
70+
return new ResponseEntity<>(members, HttpStatus.OK);
7771
}
7872
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package net.javadiscord.javabot.api.routes.leaderboard.qotw.model;
2+
3+
import lombok.Data;
4+
import lombok.EqualsAndHashCode;
5+
import net.dv8tion.jda.api.entities.User;
6+
import net.javadiscord.javabot.api.routes.data.UserData;
7+
import net.javadiscord.javabot.systems.qotw.model.QOTWAccount;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
/**
12+
* API-Data class which contains all necessary information about a single users'
13+
* QOTW-Account.
14+
*/
15+
@Data
16+
@EqualsAndHashCode(callSuper = false)
17+
public class QOTWUserData extends UserData {
18+
private QOTWAccount account;
19+
20+
/**
21+
* Creates a new {@link QOTWUserData} instance.
22+
*
23+
* @param account The {@link QOTWAccount} to use.
24+
* @param user A nullable {@link User}.
25+
* @return The {@link QOTWUserData}.
26+
*/
27+
public static @NotNull QOTWUserData of(@NotNull QOTWAccount account, @Nullable User user) {
28+
QOTWUserData data = new QOTWUserData();
29+
data.setUserId(account.getUserId());
30+
if (user != null) {
31+
data.setUserName(user.getName());
32+
data.setDiscriminator(user.getDiscriminator());
33+
data.setEffectiveAvatarUrl(user.getEffectiveAvatarUrl());
34+
}
35+
data.setAccount(account);
36+
return data;
37+
}
38+
}

src/main/java/net/javadiscord/javabot/api/routes/metrics/MetricsController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.util.concurrent.TimeUnit;
2020

2121
/**
22-
* Handles all GET-Requests on the {guild_id}/metrics route.
22+
* Handles all GET-Requests on the guilds/{guild_id}/metrics/ route.
2323
*/
2424
@Slf4j
2525
@RestController

src/main/java/net/javadiscord/javabot/api/routes/qotw_leaderboard/model/QOTWMemberData.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/main/java/net/javadiscord/javabot/api/routes/user_profile/UserProfileController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import java.util.concurrent.TimeUnit;
3535

3636
/**
37-
* Handles all GET-Requests on the {guild_id}/{user_id} route.
37+
* Handles all GET-Requests on the guilds/{guild_id}/users/{user_id}/ route.
3838
*/
3939
@RestController
4040
public class UserProfileController extends CaffeineCache<Pair<Long, Long>, UserProfileData> {

src/main/java/net/javadiscord/javabot/systems/help/HelpExperienceService.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
import net.javadiscord.javabot.systems.help.model.HelpAccount;
1212
import net.javadiscord.javabot.systems.help.model.HelpTransaction;
1313
import net.javadiscord.javabot.systems.help.model.HelpTransactionMessage;
14+
import net.javadiscord.javabot.util.ExceptionLogger;
1415
import net.javadiscord.javabot.util.Pair;
16+
import org.jetbrains.annotations.NotNull;
1517

1618
import java.sql.Connection;
1719
import java.sql.SQLException;
20+
import java.util.ArrayList;
1821
import java.util.List;
1922
import java.util.Optional;
2023

@@ -37,21 +40,40 @@ public HelpAccount getOrCreateAccount(long userId) throws SQLException {
3740
HelpAccount account;
3841
try (Connection con = this.dataSource.getConnection()) {
3942
con.setAutoCommit(false);
40-
HelpAccountRepository accountRepository = new HelpAccountRepository(con);
41-
Optional<HelpAccount> optional = accountRepository.getByUserId(userId);
43+
HelpAccountRepository repo = new HelpAccountRepository(con);
44+
Optional<HelpAccount> optional = repo.getByUserId(userId);
4245
if (optional.isPresent()) {
4346
account = optional.get();
4447
} else {
4548
account = new HelpAccount();
4649
account.setUserId(userId);
4750
account.setExperience(0);
48-
accountRepository.insert(account);
51+
repo.insert(account);
4952
}
5053
con.commit();
5154
return account;
5255
}
5356
}
5457

58+
/**
59+
* Returns the specified amount of {@link HelpAccount}s, sorted by their experience.
60+
*
61+
* @param amount The amount to retrieve.
62+
* @param page The page to get.
63+
* @return A {@link List} of {@link HelpAccount}s.
64+
*/
65+
public List<HelpAccount> getTopAccounts(int amount, int page) {
66+
List<HelpAccount> accounts = new ArrayList<>(amount);
67+
try (Connection con = dataSource.getConnection()) {
68+
con.setReadOnly(true);
69+
HelpAccountRepository repo = new HelpAccountRepository(con);
70+
return repo.getAccounts(page, amount);
71+
} catch (SQLException e) {
72+
ExceptionLogger.capture(e, getClass().getSimpleName());
73+
return accounts;
74+
}
75+
}
76+
5577
/**
5678
* Gets all recent help transactions from a user.
5779
*
@@ -102,7 +124,7 @@ public HelpTransaction performTransaction(long recipient, double value, HelpTran
102124
}
103125
}
104126

105-
private void checkExperienceRoles(Guild guild, HelpAccount account) {
127+
private void checkExperienceRoles(@NotNull Guild guild, @NotNull HelpAccount account) {
106128
guild.retrieveMemberById(account.getUserId()).queue(member ->
107129
Bot.getConfig().get(guild).getHelpConfig().getExperienceRoles().forEach((key, value) -> {
108130
Pair<Role, Double> role = account.getCurrentExperienceGoal(guild);

0 commit comments

Comments
 (0)