It works!
This commit is contained in:
parent
f452a9b3de
commit
91a24dc7e1
@ -1,2 +1,6 @@
|
||||
# Tether
|
||||
An API designed to provide real-time access to a user's Discord data.
|
||||
An API designed to provide real-time access to a user's Discord data.
|
||||
|
||||
## TODO
|
||||
- [ ] Caching
|
||||
- [ ] User account for extra account? (about me, connections, etc)
|
19
pom.xml
19
pom.xml
@ -3,6 +3,12 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.2</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- Project Details -->
|
||||
<groupId>me.braydon</groupId>
|
||||
@ -13,6 +19,8 @@
|
||||
<!-- Properties -->
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@ -59,6 +67,17 @@
|
||||
<version>1.18.34</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dv8tion</groupId>
|
||||
<artifactId>JDA</artifactId>
|
||||
<version>5.1.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>club.minnced</groupId>
|
||||
<artifactId>opus-java</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
|
@ -1,10 +1,36 @@
|
||||
package me.braydon.tether;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
public final class Tether {
|
||||
public static void main(String[] args) {
|
||||
@SpringBootApplication
|
||||
@Log4j2(topic = "Tether")
|
||||
public class Tether {
|
||||
@SneakyThrows
|
||||
public static void main(@NonNull String[] args) {
|
||||
// Load the application.yml configuration file
|
||||
File config = new File("application.yml");
|
||||
if (!config.exists()) { // Saving the default config if it doesn't exist locally
|
||||
Files.copy(Objects.requireNonNull(Tether.class.getResourceAsStream("/application.yml")), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
log.info("Saved the default configuration to '{}', please re-launch the application",
|
||||
config.getAbsolutePath()
|
||||
);
|
||||
return;
|
||||
}
|
||||
log.info("Found configuration at '{}'", config.getAbsolutePath());
|
||||
|
||||
// Start the app
|
||||
SpringApplication.run(Tether.class, args);
|
||||
}
|
||||
}
|
19
src/main/java/me/braydon/tether/common/EnvironmentUtils.java
Normal file
19
src/main/java/me/braydon/tether/common/EnvironmentUtils.java
Normal file
@ -0,0 +1,19 @@
|
||||
package me.braydon.tether.common;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* @author Braydon
|
||||
*/
|
||||
@UtilityClass
|
||||
public final class EnvironmentUtils {
|
||||
/**
|
||||
* Is the app running in a production environment?
|
||||
*/
|
||||
@Getter private static final boolean production;
|
||||
static {
|
||||
String appEnv = System.getenv("APP_ENV");
|
||||
production = appEnv != null && (appEnv.equals("production"));
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package me.braydon.tether.controller;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.tether.common.EnvironmentUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.info.BuildProperties;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The root controller for this app.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public final class AppController {
|
||||
/**
|
||||
* The build properties for this app, null if not available.
|
||||
*/
|
||||
private final BuildProperties buildProperties;
|
||||
|
||||
@Autowired
|
||||
public AppController(BuildProperties buildProperties) {
|
||||
this.buildProperties = buildProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* A GET endpoint to get info about this app.
|
||||
*
|
||||
* @return the info response
|
||||
*/
|
||||
@GetMapping @ResponseBody @NonNull
|
||||
public ResponseEntity<Map<String, String>> getAppInfo() {
|
||||
return ResponseEntity.ok(Map.of(
|
||||
"app", buildProperties == null ? "N/A" : buildProperties.getName(),
|
||||
"version", buildProperties == null ? "N/A" : buildProperties.getVersion(),
|
||||
"environment", EnvironmentUtils.isProduction() ? "production" : "staging"
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package me.braydon.tether.controller;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.braydon.tether.exception.impl.BadRequestException;
|
||||
import me.braydon.tether.exception.impl.ResourceNotFoundException;
|
||||
import me.braydon.tether.exception.impl.ServiceUnavailableException;
|
||||
import me.braydon.tether.model.response.DiscordUserResponse;
|
||||
import me.braydon.tether.service.DiscordService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* This controller is responsible for
|
||||
* handling Discord user related requests.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public final class UserController {
|
||||
@NonNull private final DiscordService discordService;
|
||||
|
||||
@Autowired
|
||||
public UserController(@NonNull DiscordService discordService) {
|
||||
this.discordService = discordService;
|
||||
}
|
||||
|
||||
/**
|
||||
* A GET endpoint to get info about a
|
||||
* Discord user by their snowflake.
|
||||
*
|
||||
* @param snowflake the user snowflake
|
||||
* @return the retrieved user
|
||||
*/
|
||||
@GetMapping("/{snowflake}") @ResponseBody @NonNull
|
||||
public ResponseEntity<DiscordUserResponse> getUserBySnowflake(@PathVariable @NonNull String snowflake)
|
||||
throws BadRequestException, ServiceUnavailableException, ResourceNotFoundException
|
||||
{
|
||||
return ResponseEntity.ok(discordService.getUserBySnowflake(snowflake));
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package me.braydon.tether.exception;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.NonNull;
|
||||
import me.braydon.tether.model.ErrorResponse;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
|
||||
import org.springframework.boot.web.error.ErrorAttributeOptions;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The route to handle errors for this app.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = "/error", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public final class ExceptionController extends AbstractErrorController {
|
||||
public ExceptionController(@NonNull ErrorAttributes errorAttributes) {
|
||||
super(errorAttributes);
|
||||
}
|
||||
|
||||
@RequestMapping @ResponseBody @NonNull
|
||||
public ResponseEntity<ErrorResponse> onError(@NonNull HttpServletRequest request) {
|
||||
Map<String, Object> error = getErrorAttributes(request, ErrorAttributeOptions.of(
|
||||
ErrorAttributeOptions.Include.MESSAGE
|
||||
));
|
||||
HttpStatus status = getStatus(request); // The status code
|
||||
return new ResponseEntity<>(new ErrorResponse(status, (String) error.get("message")), status);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package me.braydon.tether.exception.impl;
|
||||
|
||||
import lombok.NonNull;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* This exception is raised
|
||||
* when a bad request is made.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public final class BadRequestException extends RuntimeException {
|
||||
public BadRequestException(@NonNull String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package me.braydon.tether.exception.impl;
|
||||
|
||||
import lombok.NonNull;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* This exception is raised
|
||||
* when a resource is not found.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public final class ResourceNotFoundException extends RuntimeException {
|
||||
public ResourceNotFoundException(@NonNull String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package me.braydon.tether.exception.impl;
|
||||
|
||||
import lombok.NonNull;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* This exception is raised when a
|
||||
* service is unavailable. Such as
|
||||
* when trying to interact with Discord
|
||||
* and the bot is not connected.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
|
||||
public final class ServiceUnavailableException extends RuntimeException {
|
||||
public ServiceUnavailableException(@NonNull String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
180
src/main/java/me/braydon/tether/model/DiscordUser.java
Normal file
180
src/main/java/me/braydon/tether/model/DiscordUser.java
Normal file
@ -0,0 +1,180 @@
|
||||
package me.braydon.tether.model;
|
||||
|
||||
import lombok.*;
|
||||
import net.dv8tion.jda.api.OnlineStatus;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A model of a Discord user.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
|
||||
public final class DiscordUser {
|
||||
/**
|
||||
* The unique snowflake of this user.
|
||||
*/
|
||||
@EqualsAndHashCode.Include private final long snowflake;
|
||||
|
||||
/**
|
||||
* The username of this user.
|
||||
*/
|
||||
@NonNull private final String username;
|
||||
|
||||
/**
|
||||
* The display name of this user, if any.
|
||||
*/
|
||||
private final String displayName;
|
||||
|
||||
/**
|
||||
* The flags of this user.
|
||||
*/
|
||||
@NonNull private final UserFlags flags;
|
||||
|
||||
/**
|
||||
* The avatar of this user.
|
||||
*/
|
||||
@NonNull private final Avatar avatar;
|
||||
|
||||
/**
|
||||
* The banner of this user, if any.
|
||||
*/
|
||||
private final Banner banner;
|
||||
|
||||
/**
|
||||
* The accent color of this user.
|
||||
*/
|
||||
@NonNull private final String accentColor;
|
||||
|
||||
/**
|
||||
* The online status of this user, if known.
|
||||
*/
|
||||
private final OnlineStatus onlineStatus;
|
||||
|
||||
/**
|
||||
* The clients this user is active on, if known.
|
||||
*/
|
||||
private final EnumSet<ClientType> activeClients;
|
||||
|
||||
/**
|
||||
* The activities of this user, if known.
|
||||
*/
|
||||
private final List<Activity> activities;
|
||||
|
||||
/**
|
||||
* The Spotify activity of this user, if known.
|
||||
*/
|
||||
private final SpotifyActivity spotify;
|
||||
|
||||
/**
|
||||
* Is this user a bot?
|
||||
*/
|
||||
private final boolean bot;
|
||||
|
||||
/**
|
||||
* The user creation date.
|
||||
*/
|
||||
@NonNull private final OffsetDateTime createdAt;
|
||||
|
||||
/**
|
||||
* Builds a Discord user from the
|
||||
* raw entities returned from Discord.
|
||||
*
|
||||
* @param user the raw user entity
|
||||
* @param profile the raw profile entity
|
||||
* @param member the raw member entity, if any
|
||||
* @return the built user
|
||||
*/
|
||||
@NonNull
|
||||
public static DiscordUser buildFromEntity(@NonNull User user, @NonNull User.Profile profile, Member member) {
|
||||
Avatar avatar = new Avatar(user.getAvatarId() == null ? user.getDefaultAvatarId() : user.getAvatarId(), user.getEffectiveAvatarUrl());
|
||||
Banner banner = profile.getBannerId() == null || profile.getBannerUrl() == null ? null : new Banner(profile.getBannerId(), profile.getBannerUrl());
|
||||
String accentColor = String.format("#%06X", (0xFFFFFF & profile.getAccentColorRaw()));
|
||||
|
||||
OnlineStatus onlineStatus = member == null ? null : member.getOnlineStatus();
|
||||
EnumSet<ClientType> activeClients = member == null ? null : member.getActiveClients();
|
||||
List<Activity> activities = member == null ? null : member.getActivities();
|
||||
SpotifyActivity spotify = null;
|
||||
if (activities != null) {
|
||||
for (Activity activity : activities) {
|
||||
if (!activity.getName().equals("Spotify") || !activity.isRich()) {
|
||||
continue;
|
||||
}
|
||||
spotify = SpotifyActivity.fromActivity(Objects.requireNonNull(activity.asRichPresence()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new DiscordUser(
|
||||
user.getIdLong(), user.getName(), user.getGlobalName(), new UserFlags(user.getFlags(), user.getFlagsRaw()),
|
||||
avatar, banner, accentColor, onlineStatus, activeClients, activities, spotify, user.isBot(), user.getTimeCreated()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A user's flags.
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public static class UserFlags {
|
||||
private final EnumSet<User.UserFlag> list;
|
||||
private final int raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* A user's avatar.
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public static class Avatar {
|
||||
@NonNull private final String id;
|
||||
@NonNull private final String url;
|
||||
}
|
||||
|
||||
/**
|
||||
* A user's banner.
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public static class Banner {
|
||||
@NonNull private final String id;
|
||||
@NonNull private final String url;
|
||||
}
|
||||
|
||||
/**
|
||||
* A user's Spotify activity data.
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public static class SpotifyActivity {
|
||||
@NonNull private final String song;
|
||||
@NonNull private final String artist;
|
||||
@NonNull private final String trackProgress;
|
||||
@NonNull private final String trackLength;
|
||||
private final long started;
|
||||
private final long ends;
|
||||
|
||||
/**
|
||||
* Build a Spotify activity from the raw Discord data.
|
||||
*
|
||||
* @param richPresence the raw Discord data
|
||||
* @return the built Spotify activity
|
||||
*/
|
||||
@NonNull
|
||||
public static SpotifyActivity fromActivity(@NonNull RichPresence richPresence) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("m:ss");
|
||||
long started = Objects.requireNonNull(richPresence.getTimestamps()).getStart();
|
||||
long ends = richPresence.getTimestamps().getEnd();
|
||||
|
||||
long trackLength = ends - started;
|
||||
long trackProgress = Math.min(System.currentTimeMillis() - started, trackLength);
|
||||
|
||||
return new SpotifyActivity(
|
||||
Objects.requireNonNull(richPresence.getDetails()), Objects.requireNonNull(richPresence.getState()).replace(";", ","),
|
||||
dateFormat.format(trackProgress), dateFormat.format(trackLength),
|
||||
started, ends
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
43
src/main/java/me/braydon/tether/model/ErrorResponse.java
Normal file
43
src/main/java/me/braydon/tether/model/ErrorResponse.java
Normal file
@ -0,0 +1,43 @@
|
||||
package me.braydon.tether.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* A basic response model.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Getter @ToString
|
||||
public class ErrorResponse {
|
||||
/**
|
||||
* The status code of this error.
|
||||
*/
|
||||
@NonNull private final HttpStatus status;
|
||||
|
||||
/**
|
||||
* The HTTP code of this error.
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* The message of this error.
|
||||
*/
|
||||
@NonNull private final String message;
|
||||
|
||||
/**
|
||||
* The timestamp this error occurred.
|
||||
*/
|
||||
@NonNull private final Date timestamp;
|
||||
|
||||
public ErrorResponse(@NonNull HttpStatus status, @NonNull String message) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
this.code = status.value();
|
||||
timestamp = new Date();
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package me.braydon.tether.model.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.braydon.tether.model.DiscordUser;
|
||||
|
||||
/**
|
||||
* A response for a successful Discord user request.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter
|
||||
public final class DiscordUserResponse {
|
||||
/**
|
||||
* The user that was retrieved.
|
||||
*/
|
||||
@NonNull private final DiscordUser user;
|
||||
|
||||
/**
|
||||
* The unix timestamp of when this
|
||||
* user was cached, -1 if fresh.
|
||||
*/
|
||||
private final long cached;
|
||||
}
|
104
src/main/java/me/braydon/tether/service/DiscordService.java
Normal file
104
src/main/java/me/braydon/tether/service/DiscordService.java
Normal file
@ -0,0 +1,104 @@
|
||||
package me.braydon.tether.service;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import me.braydon.tether.exception.impl.BadRequestException;
|
||||
import me.braydon.tether.exception.impl.ResourceNotFoundException;
|
||||
import me.braydon.tether.exception.impl.ServiceUnavailableException;
|
||||
import me.braydon.tether.model.DiscordUser;
|
||||
import me.braydon.tether.model.response.DiscordUserResponse;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDABuilder;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* This service is responsible for
|
||||
* interacting with the Discord API.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@Service
|
||||
@Log4j2(topic = "Discord")
|
||||
public final class DiscordService {
|
||||
@Value("${discord.bot-token}")
|
||||
private String botToken;
|
||||
|
||||
/**
|
||||
* The current instance of the Discord bot.
|
||||
*/
|
||||
private JDA jda;
|
||||
|
||||
@PostConstruct
|
||||
public void onInitialize() {
|
||||
connectBot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Discord user by their snowflake.
|
||||
*
|
||||
* @param rawSnowflake the user snowflake
|
||||
* @return the user response
|
||||
* @throws ServiceUnavailableException if the bot is not connected
|
||||
* @throws ResourceNotFoundException if the user is not found
|
||||
*/
|
||||
@NonNull
|
||||
public DiscordUserResponse getUserBySnowflake(@NonNull String rawSnowflake) throws BadRequestException, ServiceUnavailableException, ResourceNotFoundException {
|
||||
if (jda == null || (jda.getStatus() != JDA.Status.CONNECTED)) { // Ensure bot is connected
|
||||
throw new ServiceUnavailableException("Not connected to Discord.");
|
||||
}
|
||||
long snowflake;
|
||||
try {
|
||||
snowflake = Long.parseLong(rawSnowflake);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new BadRequestException("Not a valid snowflake");
|
||||
}
|
||||
try {
|
||||
// First try to locate the user in a guild
|
||||
Member member = null;
|
||||
for (Guild guild : jda.getGuilds()) {
|
||||
if ((member = guild.getMemberById(snowflake)) != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
User user = jda.retrieveUserById(snowflake).complete();
|
||||
User.Profile profile = user.retrieveProfile().complete();
|
||||
return new DiscordUserResponse(DiscordUser.buildFromEntity(user, profile, member), -1L);
|
||||
} catch (ErrorResponseException ex) {
|
||||
// Failed to lookup the user, handle appropriately
|
||||
if (ex.getErrorCode() == 10013) {
|
||||
throw new ResourceNotFoundException("User not found.");
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects the bot to the Discord API.
|
||||
*/
|
||||
@SneakyThrows
|
||||
private void connectBot() {
|
||||
log.info("Connecting bot...");
|
||||
jda = JDABuilder.createDefault(botToken, GatewayIntent.GUILD_PRESENCES, GatewayIntent.GUILD_MEMBERS)
|
||||
.enableCache(CacheFlag.getPrivileged())
|
||||
.setActivity(Activity.watching("you"))
|
||||
.build();
|
||||
jda.getRestPing().queue(ping -> {
|
||||
log.info("The latency to Discord is {}ms", ping);
|
||||
});
|
||||
jda.awaitReady();
|
||||
log.info("Bot connected! Logged in as {}, invite me using {}",
|
||||
jda.getSelfUser().getAsTag(),
|
||||
"https://discord.com/oauth2/authorize?client_id=" + jda.getSelfUser().getId()
|
||||
);
|
||||
}
|
||||
}
|
19
src/main/resources/application.yml
Normal file
19
src/main/resources/application.yml
Normal file
@ -0,0 +1,19 @@
|
||||
# Server Configuration
|
||||
server:
|
||||
address: 0.0.0.0
|
||||
port: 7500
|
||||
|
||||
# Log Configuration
|
||||
logging:
|
||||
file:
|
||||
path: "./logs"
|
||||
|
||||
# Discord Configuration
|
||||
discord:
|
||||
bot-token: "CHANGE_ME"
|
||||
|
||||
# Spring Configuration
|
||||
spring:
|
||||
# Ignore
|
||||
banner:
|
||||
location: "classpath:banner.txt"
|
10
src/main/resources/banner.txt
Normal file
10
src/main/resources/banner.txt
Normal file
@ -0,0 +1,10 @@
|
||||
_______ _ _
|
||||
|__ __| | | | |
|
||||
| | ___| |_| |__ ___ _ __
|
||||
| |/ _ \ __| '_ \ / _ \ '__|
|
||||
| | __/ |_| | | | __/ |
|
||||
|_|\___|\__|_| |_|\___|_|
|
||||
|
||||
| API Version - v${application.version}
|
||||
| Spring Version - ${spring-boot.formatted-version}
|
||||
_______________________________________
|
Loading…
x
Reference in New Issue
Block a user