1 Commits

Author SHA1 Message Date
c42f646dc8 Update dependency org.apache.commons:commons-text to v1.13.0
Some checks failed
continuous-integration/drone/pr Build is failing
2025-03-23 06:25:00 +00:00
100 changed files with 1834 additions and 2376 deletions

View File

@@ -1,14 +1,77 @@
kind: template kind: pipeline
load: java-build-deploy.yaml type: docker
data: name: build
arch: arm64 platform:
os: linux os: linux
build_branches: arch: arm64
trigger:
branch:
- main - main
- develop event:
build_events:
- push - push
- pull_request - pull_request
sonar_project_key: HidekoBot
deploy_targets: steps:
# test if it compiles correctly
- name: build
image: maven:3-eclipse-temurin-21
commands:
- mvn verify --no-transfer-progress -DskipTests=true -Dmaven.javadoc.skip=true -B -V
# run unit tests
- name: test
image: maven:3-eclipse-temurin-21
commands:
- mvn test --no-transfer-progress -B -V
# check maven dependencies
- name: dependency-check
image: maven:3-eclipse-temurin-21
commands:
- mvn dependency-check:check --no-transfer-progress -B -V -DnvdApiKey=$NVD_API_KEY
environment:
NVD_API_KEY:
from_secret: nvd_api_key
# run code analysis
- name: code-analysis
image: maven:3-eclipse-temurin-21
commands:
- mvn sonar:sonar --no-transfer-progress -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.host.url=$SONAR_INSTANCE_URL -Dsonar.token=$SONAR_LOGIN_KEY -B -V
environment:
SONAR_PROJECT_KEY:
from_secret: sonar_project_key
SONAR_INSTANCE_URL:
from_secret: sonar_instance_url
SONAR_LOGIN_KEY:
from_secret: sonar_login_key
---
kind: pipeline
type: docker
name: deploy
platform:
os: linux
arch: arm64
trigger:
event:
- promote
target:
- production - production
steps:
# skip all previous steps because they were already ran in the "build" phase; we don't need to re-analyze the code.
# upload to maven repository
- name: maven-deploy
image: maven:3-eclipse-temurin-21
commands:
- mvn deploy --no-transfer-progress -DskipTests=true -Dmaven.javadoc.skip=true -B -V -gs settings.xml -Dmaven.repo.username=$MAVEN_REPO_USERNAME -Dmaven.repo.password=$MAVEN_REPO_PASSWORD
environment:
MAVEN_REPO_USERNAME:
from_secret: maven_repo_username
MAVEN_REPO_PASSWORD:
from_secret: maven_repo_password

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
target/ target/
.idea/ .idea/
scripts/
*.sqlite *.sqlite

View File

@@ -1,88 +0,0 @@
---
kind: pipeline
type: docker
name: build
platform:
os: {{ .input.os }}
arch: {{ .input.arch }}
trigger:
branch:
{{- range .input.build_branches }}
- {{ . }}
{{- end }}
event:
{{- range .input.build_events }}
- {{ . }}
{{- end }}
# Global project-specific environment variables
environment:
{{- range .input.envs }}
{{ .name }}: {{ .value }}
{{- end }}
steps:
# Test if it compiles correctly
- name: build
image: maven:3-eclipse-temurin-21
commands:
- mvn verify --no-transfer-progress -DskipTests=true -Dmaven.javadoc.skip=true -B -V
# Run unit tests
- name: test
image: maven:3-eclipse-temurin-21
commands:
- mvn test --no-transfer-progress -B -V
# Check maven dependencies
- name: dependency-check
image: owasp/dependency-check:latest
commands:
- dependency-check --scan /src --format ALL --out /src/target --nvdApiKey $NVD_API_KEY
environment:
NVD_API_KEY:
from_secret: nvd_api_key
# Run code analysis
- name: code-analysis
when:
event:
- push
image: maven:3-eclipse-temurin-21
commands:
- mvn sonar:sonar --no-transfer-progress -Dsonar.projectKey={{ .input.sonar_project_key }} -Dsonar.host.url=$SONAR_INSTANCE_URL -Dsonar.token=$SONAR_LOGIN_KEY -B -V
environment:
SONAR_INSTANCE_URL:
from_secret: sonar_instance_url
SONAR_LOGIN_KEY:
from_secret: sonar_login_key
---
kind: pipeline
type: kubernetes
name: deploy
trigger:
event:
- promote
target:
{{- range .input.deploy_targets }}
- {{ . }}
{{- end }}
# Global project-specific environment variables
environment:
{{- range .input.envs }}
{{ .name }}: {{ .value }}
{{- end }}
steps:
# Upload to Maven repository
- name: maven-deploy
image: maven:3-eclipse-temurin-21
commands:
- mvn deploy --no-transfer-progress -DskipTests=true -Dmaven.javadoc.skip=true -B -V -gs settings.xml -Dmaven.repo.username=$MAVEN_REPO_USERNAME -Dmaven.repo.password=$MAVEN_REPO_PASSWORD
environment:
MAVEN_REPO_USERNAME:
from_secret: maven_repo_username
MAVEN_REPO_PASSWORD:
from_secret: maven_repo_password

View File

@@ -1,9 +1,9 @@
# HidekoBot # HidekoBot
[![Reliability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=reliability_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot) [![Reliability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot_AYWyYHsvX-1Ma0D4pJ59&metric=reliability_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot_AYWyYHsvX-1Ma0D4pJ59)
[![Maintainability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=sqale_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot) [![Maintainability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot_AYWyYHsvX-1Ma0D4pJ59&metric=sqale_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot_AYWyYHsvX-1Ma0D4pJ59)
[![Security Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=security_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot) [![Security Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot_AYWyYHsvX-1Ma0D4pJ59&metric=security_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot_AYWyYHsvX-1Ma0D4pJ59)
[![Build Status](https://drone.beatrice.wtf/api/badges/bea/HidekoBot/status.svg)](https://drone.beatrice.wtf/bea/HidekoBot) [![Build Status](https://drone.beatrice.wtf/api/badges/bea/HidekoBot/status.svg)](https://drone.beatrice.wtf/bea/HidekoBot)
[![Lines of Code](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=ncloc&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot) [![Lines of Code](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot_AYWyYHsvX-1Ma0D4pJ59&metric=ncloc&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot_AYWyYHsvX-1Ma0D4pJ59)
Hideko is a general-purpose Discord bot. Hideko is a general-purpose Discord bot.

107
pom.xml
View File

@@ -6,16 +6,14 @@
<groupId>wtf.beatrice.hidekobot</groupId> <groupId>wtf.beatrice.hidekobot</groupId>
<artifactId>HidekoBot</artifactId> <artifactId>HidekoBot</artifactId>
<version>0.9.3-SNAPSHOT</version> <version>0.6.3-SNAPSHOT</version>
<properties> <properties>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<sonar.dependencyCheck.htmlReportPath>./target/dependency-check-report.html <sonar.dependencyCheck.htmlReportPath>./target/dependency-check-report.html</sonar.dependencyCheck.htmlReportPath>
</sonar.dependencyCheck.htmlReportPath> <sonar.dependencyCheck.jsonReportPath>./target/dependency-check-report.json</sonar.dependencyCheck.jsonReportPath>
<sonar.dependencyCheck.jsonReportPath>./target/dependency-check-report.json
</sonar.dependencyCheck.jsonReportPath>
<sonar.dependencyCheck.summarize>true</sonar.dependencyCheck.summarize> <sonar.dependencyCheck.summarize>true</sonar.dependencyCheck.summarize>
</properties> </properties>
@@ -24,13 +22,18 @@
<dependency> <dependency>
<groupId>net.dv8tion</groupId> <groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId> <artifactId>JDA</artifactId>
<version>5.5.1</version> <version>5.3.0</version>
</dependency> </dependency>
<!-- JDA depends on SLF4J for logging --> <!-- JDA depends on SLF4J for logging -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
<version>2.0.17</version> <version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.6</version>
</dependency> </dependency>
<!-- Dependency used for SQLite database connections--> <!-- Dependency used for SQLite database connections-->
@@ -44,26 +47,26 @@
<dependency> <dependency>
<groupId>org.yaml</groupId> <groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId> <artifactId>snakeyaml</artifactId>
<version>2.4</version> <version>2.0</version>
</dependency> </dependency>
<!-- JSoup is used to parse HTML into JSON objects for better handling in Java --> <!-- JSoup is used to parse HTML into JSON objects for better handling in Java -->
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<version>1.19.1</version> <version>1.15.3</version>
</dependency> </dependency>
<!-- Various String manipulation utils --> <!-- Various String manipulation utils -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId> <artifactId>commons-text</artifactId>
<version>1.13.1</version> <version>1.13.0</version>
</dependency> </dependency>
<!-- JSON dependency used for better parsing of JSON files --> <!-- JSON dependency used for better parsing of JSON files -->
<dependency> <dependency>
<groupId>org.json</groupId> <groupId>org.json</groupId>
<artifactId>json</artifactId> <artifactId>json</artifactId>
<version>20250517</version> <version>20231013</version>
</dependency> </dependency>
<!-- Start Random.org dependencies --> <!-- Start Random.org dependencies -->
@@ -75,12 +78,12 @@
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.13.1</version> <version>2.10.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-codec</groupId> <groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId> <artifactId>commons-codec</artifactId>
<version>1.18.0</version> <version>1.15</version>
</dependency> </dependency>
<!-- End Random.org dependencies --> <!-- End Random.org dependencies -->
@@ -88,34 +91,9 @@
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.13.0</version> <version>5.9.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.4.5</version>
</dependency>
<!-- Hibernate core + SQLite dialects -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.6.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-community-dialects</artifactId>
<version>6.6.1.Final</version>
</dependency>
<!-- Jakarta Persistence API (usually provided via Hibernate, but include explicitly for compile-time types) -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies> </dependencies>
<!-- override dependencies to use newer versions --> <!-- override dependencies to use newer versions -->
@@ -124,7 +102,7 @@
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
<version>4.31.1</version> <version>4.30.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@@ -139,29 +117,49 @@
</resources> </resources>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>wtf.beatrice.hidekobot.HidekoBot</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.2</version> <version>3.4.1</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.sonarsource.scanner.maven</groupId> <groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId> <artifactId>sonar-maven-plugin</artifactId>
<version>5.1.0.4751</version> <version>3.9.1.2184</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId> <artifactId>dependency-check-maven</artifactId>
<version>12.1.1</version> <version>12.1.0</version>
<configuration> <configuration>
<failBuildOnCVSS>8</failBuildOnCVSS> <failBuildOnCVSS>8</failBuildOnCVSS>
<!--suppress UnresolvedMavenProperty --> <!--suppress UnresolvedMavenProperty -->
<nvdApiKey>${nvdApiKey}</nvdApiKey> <nvdApiKey>${nvdApiKey}</nvdApiKey>
<knownExploitedUrl>
https://raw.githubusercontent.com/EugenMayer/cisa-known-exploited-mirror/main/known_exploited_vulnerabilities.json
</knownExploitedUrl>
<formats> <formats>
<format>html</format> <format>html</format>
<format>json</format> <format>json</format>
@@ -172,23 +170,6 @@
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.4.5</version>
<configuration>
<!-- Replace the main artifact (no classifier) -->
<classifier></classifier>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@@ -5,12 +5,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.datasources.ConfigurationEntry; import wtf.beatrice.hidekobot.datasources.ConfigurationEntry;
import wtf.beatrice.hidekobot.datasources.ConfigurationSource; import wtf.beatrice.hidekobot.datasources.ConfigurationSource;
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
import wtf.beatrice.hidekobot.datasources.PropertiesSource; import wtf.beatrice.hidekobot.datasources.PropertiesSource;
import wtf.beatrice.hidekobot.listeners.MessageCommandListener; import wtf.beatrice.hidekobot.listeners.MessageCommandListener;
import wtf.beatrice.hidekobot.listeners.MessageLogger; import wtf.beatrice.hidekobot.listeners.MessageLogger;
import wtf.beatrice.hidekobot.listeners.SlashCommandCompletionListener; import wtf.beatrice.hidekobot.listeners.SlashCommandCompletionListener;
import wtf.beatrice.hidekobot.listeners.SlashCommandListener; import wtf.beatrice.hidekobot.listeners.SlashCommandListener;
import wtf.beatrice.hidekobot.util.Services;
import java.awt.*; import java.awt.*;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -22,27 +22,15 @@ import java.util.concurrent.ScheduledExecutorService;
public class Cache public class Cache
{ {
private Cache() private Cache() {
{
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
// todo: make this compatible with the message listener's regex // todo: make this compatible with the message listener's regex
private static final String BOT_PREFIX = "hideko"; private static final String BOT_PREFIX = "hideko";
private static final Logger LOGGER = LoggerFactory.getLogger(Cache.class); private static final Logger LOGGER = LoggerFactory.getLogger(Cache.class);
private static Services SERVICES;
public static void setServices(Services services)
{
SERVICES = services;
}
public static Services getServices()
{
return SERVICES;
}
// map to store results of "love calculator", to avoid people re-running the same command until // map to store results of "love calculator", to avoid people re-running the same command until
// they get what they wanted. // they get what they wanted.
// i didn't think this was worthy of a whole database table with a runnable checking for expiration, // i didn't think this was worthy of a whole database table with a runnable checking for expiration,
@@ -51,6 +39,7 @@ public class Cache
private static PropertiesSource propertiesSource = null; private static PropertiesSource propertiesSource = null;
private static ConfigurationSource configurationSource = null; private static ConfigurationSource configurationSource = null;
private static DatabaseSource databaseSource = null;
private static boolean verbose = false; private static boolean verbose = false;
private static MessageLogger verbosityLogger = null; private static MessageLogger verbosityLogger = null;
private static final long BOT_MAINTAINER_ID = 979809420714332260L; private static final long BOT_MAINTAINER_ID = 979809420714332260L;
@@ -89,10 +78,7 @@ public class Cache
* *
* @return array of supported resolutions. * @return array of supported resolutions.
*/ */
public static int[] getSupportedAvatarResolutions() public static int[] getSupportedAvatarResolutions() { return supportedAvatarResolutions; }
{
return supportedAvatarResolutions;
}
/** /**
@@ -100,10 +86,7 @@ public class Cache
* *
* @return a boolean which is true if the bot is in verbose-mode * @return a boolean which is true if the bot is in verbose-mode
*/ */
public static synchronized boolean isVerbose() public static synchronized boolean isVerbose() { return verbose; }
{
return verbose;
}
/** /**
* Set the bot's verbosity status at runtime. * Set the bot's verbosity status at runtime.
@@ -133,8 +116,7 @@ public class Cache
* *
* @return a long of the account's id * @return a long of the account's id
*/ */
public static long getBotOwnerId() public static long getBotOwnerId() {
{
return configurationSource == null ? 0L : (Long) configurationSource.getConfigValue(ConfigurationEntry.BOT_OWNER_ID); return configurationSource == null ? 0L : (Long) configurationSource.getConfigValue(ConfigurationEntry.BOT_OWNER_ID);
} }
@@ -144,8 +126,7 @@ public class Cache
* *
* @return a String of the bot's token. * @return a String of the bot's token.
*/ */
public static String getBotToken() public static String getBotToken() {
{
return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.BOT_TOKEN); return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.BOT_TOKEN);
} }
@@ -156,10 +137,7 @@ public class Cache
* @return a long of the account's id * @return a long of the account's id
*/ */
public static long getBotMaintainerId() public static long getBotMaintainerId() { return BOT_MAINTAINER_ID; }
{
return BOT_MAINTAINER_ID;
}
/** /**
* Set the bot's application id. * Set the bot's application id.
@@ -176,21 +154,34 @@ public class Cache
* *
* @return a string of the bot's application id * @return a string of the bot's application id
*/ */
public static String getBotApplicationId() public static String getBotApplicationId() { return botApplicationId; }
{
return botApplicationId;
}
/** /**
* Function to generate an invite link for the bot * Function to generate an invite link for the bot
* *
* @return a string containing the invite link * @return a string containing the invite link
*/ */
public static String getInviteUrl() public static String getInviteUrl() {
{
return DEFAULT_INVITE_LINK.replace("%userid%", botApplicationId); return DEFAULT_INVITE_LINK.replace("%userid%", botApplicationId);
} }
/**
* Set the already fully-initialized DatabaseSource instance, ready to be accessed and used.
*
* @param databaseSourceInstance the fully-initialized DatabaseSource instance.
*/
public static void setDatabaseSourceInstance(DatabaseSource databaseSourceInstance)
{
databaseSource = databaseSourceInstance;
}
/**
* Get the fully-initialized DatabaseSource instance, ready to be used.
*
* @return the DatabaseSource instance.
*/
public static @Nullable DatabaseSource getDatabaseSource() { return databaseSource; }
/** /**
* Set the properties source instance loaded from the JAR archive. * Set the properties source instance loaded from the JAR archive.
* *
@@ -206,35 +197,25 @@ public class Cache
* *
* @return the String of the DateTimeFormatter format. * @return the String of the DateTimeFormatter format.
*/ */
public static String getExpiryTimestampFormat() public static String getExpiryTimestampFormat(){ return EXPIRY_TIMESTAMP_FORMAT; }
{
return EXPIRY_TIMESTAMP_FORMAT;
}
/** /**
* Get the amount of seconds after which a message expires. * Get the amount of seconds after which a message expires.
* *
* @return long value of the expiry seconds. * @return long value of the expiry seconds.
*/ */
public static long getExpiryTimeSeconds() public static long getExpiryTimeSeconds() { return EXPIRY_TIME_SECONDS; }
{
return EXPIRY_TIME_SECONDS;
}
public static String getBotName() public static String getBotName() { return BOT_NAME; }
{
return BOT_NAME;
}
/** /**
* Get the bot's version. * Get the bot's version.
* *
* @return a String of the bot version. * @return a String of the bot version.
*/ */
public static String getBotVersion() public static String getBotVersion() {
{ return propertiesSource.getProperty("bot.version");
return propertiesSource.getProperty("bot.version").toLowerCase();
} }
/** /**
@@ -242,8 +223,7 @@ public class Cache
* *
* @return a String containing the base URL of the repository, including a <b>trailing slash</b>. * @return a String containing the base URL of the repository, including a <b>trailing slash</b>.
*/ */
public static String getRepositoryUrl() public static String getRepositoryUrl() {
{
String url = propertiesSource.getProperty("repo.base_url"); String url = propertiesSource.getProperty("repo.base_url");
return url.endsWith("/") ? url : url + "/"; return url.endsWith("/") ? url : url + "/";
} }
@@ -253,19 +233,16 @@ public class Cache
* *
* @return the Color object. * @return the Color object.
*/ */
public static Color getBotColor() public static Color getBotColor() {
{
Color defaultColor = Color.PINK; Color defaultColor = Color.PINK;
if(configurationSource == null) return defaultColor; if(configurationSource == null) return defaultColor;
String colorName = (String) configurationSource.getConfigValue(ConfigurationEntry.BOT_COLOR); String colorName = (String) configurationSource.getConfigValue(ConfigurationEntry.BOT_COLOR);
Color color = null; Color color = null;
try try {
{
Field field = Color.class.getField(colorName); Field field = Color.class.getField(colorName);
color = (Color)field.get(null); color = (Color)field.get(null);
} catch (RuntimeException | NoSuchFieldException | IllegalAccessException e) } catch (RuntimeException | NoSuchFieldException | IllegalAccessException e) {
{
LOGGER.error("Unknown color: {}", colorName); LOGGER.error("Unknown color: {}", colorName);
} }
return color == null ? defaultColor : color; return color == null ? defaultColor : color;
@@ -274,36 +251,21 @@ public class Cache
//todo javadocs //todo javadocs
public static void setSlashCommandListener(SlashCommandListener commandListener) public static void setSlashCommandListener(SlashCommandListener commandListener)
{ { slashCommandListener = commandListener; }
slashCommandListener = commandListener;
}
public static SlashCommandListener getSlashCommandListener() public static SlashCommandListener getSlashCommandListener() { return slashCommandListener; }
{
return slashCommandListener;
}
public static void setSlashCommandCompletionListener(SlashCommandCompletionListener commandCompletionListener) public static void setSlashCommandCompletionListener(SlashCommandCompletionListener commandCompletionListener)
{ { slashCommandCompletionListener = commandCompletionListener; }
slashCommandCompletionListener = commandCompletionListener;
}
public static SlashCommandCompletionListener getSlashCommandCompletionListener() public static SlashCommandCompletionListener getSlashCommandCompletionListener() { return slashCommandCompletionListener; }
{
return slashCommandCompletionListener;
}
public static void setMessageCommandListener(MessageCommandListener commandListener) public static void setMessageCommandListener(MessageCommandListener commandListener)
{ { messageCommandListener = commandListener; }
messageCommandListener = commandListener;
}
public static MessageCommandListener getMessageCommandListener() public static MessageCommandListener getMessageCommandListener() { return messageCommandListener; }
{
return messageCommandListener;
}
/** /**
* Set the bot's startup time. Generally only used at boot time. * Set the bot's startup time. Generally only used at boot time.
@@ -311,9 +273,7 @@ public class Cache
* @param time a LocalDateTime of the startup moment. * @param time a LocalDateTime of the startup moment.
*/ */
public static void setStartupTime(LocalDateTime time) public static void setStartupTime(LocalDateTime time)
{ { startupTime = time; }
startupTime = time;
}
/** /**
@@ -321,54 +281,37 @@ public class Cache
* *
* @return a LocalDateTime object of the startup instant. * @return a LocalDateTime object of the startup instant.
*/ */
public static LocalDateTime getStartupTime() public static LocalDateTime getStartupTime() { return startupTime; }
{
return startupTime;
}
/** /**
* Get the time of when the bot was created. * Get the time of when the bot was created.
* *
* @return a LocalDateTime object of the first commit's instant. * @return a LocalDateTime object of the first commit's instant.
*/ */
public static LocalDateTime getBotBirthDate() public static LocalDateTime getBotBirthDate() { return botBirthDate; }
{
return botBirthDate;
}
public static String getFullHeartBeatLink() public static String getFullHeartBeatLink() {
{
return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.HEARTBEAT_LINK); return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.HEARTBEAT_LINK);
} }
//todo javadocs //todo javadocs
public static String getExecPath() public static String getExecPath() { return EXEC_PATH; }
{
return EXEC_PATH;
}
/*private static ConfigurationSource getConfigurationSource() /*private static ConfigurationSource getConfigurationSource()
{ return configurationSource; }*/ { return configurationSource; }*/
public static String getRandomOrgApiKey() public static String getRandomOrgApiKey() {
{
return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.RANDOM_ORG_API_KEY); return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.RANDOM_ORG_API_KEY);
} }
public static void setConfigurationSource(ConfigurationSource configurationSource) public static void setConfigurationSource(ConfigurationSource configurationSource)
{ { Cache.configurationSource = configurationSource; }
Cache.configurationSource = configurationSource;
}
/** /**
* Get the bot's prefix * Get the bot's prefix
* *
* @return a String of the bot's prefix. * @return a String of the bot's prefix.
*/ */
public static String getBotPrefix() public static String getBotPrefix() { return BOT_PREFIX; }
{
return BOT_PREFIX;
}
public static void cacheLoveCalculatorValue(String userId1, String userId2, int value) public static void cacheLoveCalculatorValue(String userId1, String userId2, int value)
{ {
@@ -393,8 +336,7 @@ public class Cache
loveCalculatorValues.remove(userId2 + "|" + userId1); loveCalculatorValues.remove(userId2 + "|" + userId1);
} }
public static ScheduledExecutorService getTaskScheduler() public static ScheduledExecutorService getTaskScheduler() {
{
return taskScheduler; return taskScheduler;
} }

View File

@@ -6,25 +6,20 @@ import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.requests.GatewayIntent;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.context.ConfigurableApplicationContext;
import wtf.beatrice.hidekobot.commands.completer.ProfileImageCommandCompleter; import wtf.beatrice.hidekobot.commands.completer.ProfileImageCommandCompleter;
import wtf.beatrice.hidekobot.commands.message.*; import wtf.beatrice.hidekobot.commands.message.HelloCommand;
import wtf.beatrice.hidekobot.commands.slash.*; import wtf.beatrice.hidekobot.commands.slash.*;
import wtf.beatrice.hidekobot.datasources.ConfigurationSource; import wtf.beatrice.hidekobot.datasources.ConfigurationSource;
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
import wtf.beatrice.hidekobot.datasources.PropertiesSource; import wtf.beatrice.hidekobot.datasources.PropertiesSource;
import wtf.beatrice.hidekobot.listeners.*; import wtf.beatrice.hidekobot.listeners.*;
import wtf.beatrice.hidekobot.runnables.ExpiredMessageTask; import wtf.beatrice.hidekobot.runnables.ExpiredMessageTask;
import wtf.beatrice.hidekobot.runnables.HeartBeatTask; import wtf.beatrice.hidekobot.runnables.HeartBeatTask;
import wtf.beatrice.hidekobot.runnables.RandomOrgSeedTask; import wtf.beatrice.hidekobot.runnables.RandomOrgSeedTask;
import wtf.beatrice.hidekobot.runnables.StatusUpdateTask; import wtf.beatrice.hidekobot.runnables.StatusUpdateTask;
import wtf.beatrice.hidekobot.services.CommandService; import wtf.beatrice.hidekobot.util.CommandUtil;
import wtf.beatrice.hidekobot.services.DatabaseService;
import wtf.beatrice.hidekobot.util.FormatUtil; import wtf.beatrice.hidekobot.util.FormatUtil;
import wtf.beatrice.hidekobot.util.RandomUtil; import wtf.beatrice.hidekobot.util.RandomUtil;
import wtf.beatrice.hidekobot.util.Services;
import java.io.File; import java.io.File;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -35,7 +30,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@SpringBootApplication
public class HidekoBot public class HidekoBot
{ {
private static JDA jda; private static JDA jda;
@@ -69,18 +63,6 @@ public class HidekoBot
return; return;
} }
ApplicationHome home = new ApplicationHome(HidekoBot.class);
System.setProperty("APP_HOME", home.getDir().getAbsolutePath());
ConfigurableApplicationContext context = SpringApplication.run(HidekoBot.class, args);
CommandService commandService = context.getBean(CommandService.class);
DatabaseService databaseService = context.getBean(DatabaseService.class);
Services services = new wtf.beatrice.hidekobot.util.Services(
commandService,
databaseService
);
Cache.setServices(services);
try try
{ {
// try to create the bot object and authenticate it with discord. // try to create the bot object and authenticate it with discord.
@@ -94,12 +76,12 @@ public class HidekoBot
); );
jda = jdaBuilder.build().awaitReady(); jda = jdaBuilder.build().awaitReady();
} catch (InterruptedException e) } catch (InterruptedException e) {
{
LOGGER.error(e.getMessage()); // print the error message, omit the stack trace. LOGGER.error(e.getMessage()); // print the error message, omit the stack trace.
Thread.currentThread().interrupt(); // send interrupt to the thread. Thread.currentThread().interrupt(); // send interrupt to the thread.
shutdown(); // if we failed connecting and authenticating, then quit. shutdown(); // if we failed connecting and authenticating, then quit.
} catch (Exception e) }
catch (Exception e)
{ {
LOGGER.error(e.getMessage()); // print the error message, omit the stack trace. LOGGER.error(e.getMessage()); // print the error message, omit the stack trace.
shutdown(); // if we failed connecting and authenticating, then quit. shutdown(); // if we failed connecting and authenticating, then quit.
@@ -114,8 +96,7 @@ public class HidekoBot
// if there is at least one arg, then iterate through them because we have additional things to do. // if there is at least one arg, then iterate through them because we have additional things to do.
// we are doing this at the end because we might need the API to be already initialized for some things. // we are doing this at the end because we might need the API to be already initialized for some things.
if (args.length > 0) if(args.length > 0) {
{
List<String> argsList = new ArrayList<>(Arrays.asList(args)); List<String> argsList = new ArrayList<>(Arrays.asList(args));
@@ -132,6 +113,7 @@ public class HidekoBot
} }
boolean enableRandomSeedUpdaterTask = false; boolean enableRandomSeedUpdaterTask = false;
// initialize random.org object if API key is provided // initialize random.org object if API key is provided
{ {
@@ -145,55 +127,53 @@ public class HidekoBot
} }
// register slash commands and completers // register slash commands and completers
SlashCommandListener slashCommandListener = context.getBean(SlashCommandListener.class); SlashCommandListener slashCommandListener = new SlashCommandListener();
SlashCommandCompletionListener slashCommandCompletionListener = context.getBean(SlashCommandCompletionListener.class); SlashCommandCompletionListener slashCommandCompletionListener = new SlashCommandCompletionListener();
MessageCommandListener messageCommandListener = context.getBean(MessageCommandListener.class); AvatarCommand avatarCommand = new AvatarCommand();
ButtonInteractionListener buttonInteractionListener = context.getBean(ButtonInteractionListener.class); ProfileImageCommandCompleter avatarCommandCompleter = new ProfileImageCommandCompleter(avatarCommand);
SelectMenuInteractionListener selectMenuInteractionListener = context.getBean(SelectMenuInteractionListener.class); slashCommandListener.registerCommand(avatarCommand);
SlashAvatarCommand slashAvatarCommand = context.getBean(SlashAvatarCommand.class);
ProfileImageCommandCompleter avatarCommandCompleter = new ProfileImageCommandCompleter(slashAvatarCommand);
slashCommandListener.registerCommand(slashAvatarCommand);
slashCommandCompletionListener.registerCommandCompleter(avatarCommandCompleter); slashCommandCompletionListener.registerCommandCompleter(avatarCommandCompleter);
slashCommandListener.registerCommand(context.getBean(SlashBanCommand.class)); slashCommandListener.registerCommand(new BanCommand());
SlashBannerCommand slashBannerCommand = context.getBean(SlashBannerCommand.class); BannerCommand bannerCommand = new BannerCommand();
ProfileImageCommandCompleter bannerCommandCompleter = new ProfileImageCommandCompleter(slashBannerCommand); ProfileImageCommandCompleter bannerCommandCompleter = new ProfileImageCommandCompleter(bannerCommand);
slashCommandListener.registerCommand(slashBannerCommand); slashCommandListener.registerCommand(bannerCommand);
slashCommandCompletionListener.registerCommandCompleter(bannerCommandCompleter); slashCommandCompletionListener.registerCommandCompleter(bannerCommandCompleter);
slashCommandListener.registerCommand(context.getBean(SlashBotInfoCommand.class)); slashCommandListener.registerCommand(new BotInfoCommand());
slashCommandListener.registerCommand(context.getBean(SlashClearCommand.class)); slashCommandListener.registerCommand(new ClearCommand());
slashCommandListener.registerCommand(context.getBean(SlashCoinFlipCommand.class)); slashCommandListener.registerCommand(new CoinFlipCommand());
slashCommandListener.registerCommand(context.getBean(SlashDiceRollCommand.class)); slashCommandListener.registerCommand(new DiceRollCommand());
slashCommandListener.registerCommand(new SlashDieCommand()); slashCommandListener.registerCommand(new DieCommand());
slashCommandListener.registerCommand(new SlashHelpCommand()); slashCommandListener.registerCommand(new HelpCommand());
slashCommandListener.registerCommand(context.getBean(SlashInviteCommand.class)); slashCommandListener.registerCommand(new InviteCommand());
slashCommandListener.registerCommand(context.getBean(SlashKickCommand.class)); slashCommandListener.registerCommand(new KickCommand());
slashCommandListener.registerCommand(context.getBean(SlashLoveCalculatorCommand.class)); slashCommandListener.registerCommand(new LoveCalculatorCommand());
slashCommandListener.registerCommand(context.getBean(SlashMagicBallCommand.class)); slashCommandListener.registerCommand(new MagicBallCommand());
slashCommandListener.registerCommand(new SlashPingCommand()); slashCommandListener.registerCommand(new PingCommand());
slashCommandListener.registerCommand(context.getBean(SlashSayCommand.class)); slashCommandListener.registerCommand(new SayCommand());
slashCommandListener.registerCommand(context.getBean(SlashTimeoutCommand.class)); slashCommandListener.registerCommand(new TimeoutCommand());
slashCommandListener.registerCommand(new SlashTriviaCommand()); slashCommandListener.registerCommand(new TriviaCommand());
slashCommandListener.registerCommand(new SlashUrbanDictionaryCommand()); slashCommandListener.registerCommand(new UrbanDictionaryCommand());
// register message commands // register message commands
messageCommandListener.registerCommand(new MessageHelloCommand()); MessageCommandListener messageCommandListener = new MessageCommandListener();
messageCommandListener.registerCommand(context.getBean(MessageAliasCommand.class)); messageCommandListener.registerCommand(new HelloCommand());
messageCommandListener.registerCommand(context.getBean(MessageAvatarCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AliasCommand());
messageCommandListener.registerCommand(context.getBean(MessageBanCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AvatarCommand());
messageCommandListener.registerCommand(context.getBean(MessageBannerCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BanCommand());
messageCommandListener.registerCommand(context.getBean(MessageBotInfoCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BannerCommand());
messageCommandListener.registerCommand(context.getBean(MessageCoinFlipCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BotInfoCommand());
messageCommandListener.registerCommand(context.getBean(MessageClearCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.CoinFlipCommand());
messageCommandListener.registerCommand(context.getBean(MessageDiceRollCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand());
messageCommandListener.registerCommand(context.getBean(MessageHelpCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.DiceRollCommand());
messageCommandListener.registerCommand(context.getBean(MessageInviteCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.HelpCommand());
messageCommandListener.registerCommand(context.getBean(MessageKickCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.InviteCommand());
messageCommandListener.registerCommand(context.getBean(MessageLoveCalculatorCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.KickCommand());
messageCommandListener.registerCommand(context.getBean(MessageMagicBallCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.LoveCalculatorCommand());
messageCommandListener.registerCommand(context.getBean(MessageSayCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.MagicBallCommand());
messageCommandListener.registerCommand(context.getBean(MessageTimeoutCommand.class)); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.SayCommand());
messageCommandListener.registerCommand(new MessageTriviaCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.TimeoutCommand());
messageCommandListener.registerCommand(new MessageUrbanDictionaryCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.TriviaCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.UrbanDictionaryCommand());
// register listeners // register listeners
Cache.setSlashCommandListener(slashCommandListener); Cache.setSlashCommandListener(slashCommandListener);
@@ -202,28 +182,41 @@ public class HidekoBot
jda.addEventListener(messageCommandListener); jda.addEventListener(messageCommandListener);
jda.addEventListener(slashCommandListener); jda.addEventListener(slashCommandListener);
jda.addEventListener(slashCommandCompletionListener); jda.addEventListener(slashCommandCompletionListener);
jda.addEventListener(buttonInteractionListener); jda.addEventListener(new ButtonInteractionListener());
jda.addEventListener(selectMenuInteractionListener); jda.addEventListener(new SelectMenuInteractionListener());
// update slash commands (delayed) // update slash commands (delayed)
final boolean finalForceUpdateCommands = forceUpdateCommands; final boolean finalForceUpdateCommands = forceUpdateCommands;
try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor()) try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor()) {
{ executor.schedule(() -> CommandUtil.updateSlashCommands(finalForceUpdateCommands),
executor.schedule(() -> commandService.updateSlashCommands(finalForceUpdateCommands),
1, TimeUnit.SECONDS); 1, TimeUnit.SECONDS);
} }
// set the bot's status // set the bot's status
jda.getPresence().setStatus(OnlineStatus.ONLINE); jda.getPresence().setStatus(OnlineStatus.ONLINE);
// connect to database
LOGGER.info("Connecting to database...");
String dbFilePath = Cache.getExecPath() + File.separator + "db.sqlite"; // in current directory
DatabaseSource databaseSource = new DatabaseSource(dbFilePath);
if(databaseSource.connect() && databaseSource.initDb())
{
LOGGER.info("Database connection initialized!");
Cache.setDatabaseSourceInstance(databaseSource);
// load data here...
LOGGER.info("Database data loaded into memory!");
} else {
LOGGER.error("Error initializing database connection!");
}
// start scheduled runnables // start scheduled runnables
ScheduledExecutorService scheduler = Cache.getTaskScheduler(); ScheduledExecutorService scheduler = Cache.getTaskScheduler();
ExpiredMessageTask expiredMessageTask = new ExpiredMessageTask(services.databaseService(), services.commandService()); ExpiredMessageTask expiredMessageTask = new ExpiredMessageTask();
scheduler.scheduleAtFixedRate(expiredMessageTask, 5L, 5L, TimeUnit.SECONDS); //every 5 seconds scheduler.scheduleAtFixedRate(expiredMessageTask, 5L, 5L, TimeUnit.SECONDS); //every 5 seconds
HeartBeatTask heartBeatTask = new HeartBeatTask(); HeartBeatTask heartBeatTask = new HeartBeatTask();
scheduler.scheduleAtFixedRate(heartBeatTask, 10L, 30L, TimeUnit.SECONDS); //every 30 seconds scheduler.scheduleAtFixedRate(heartBeatTask, 10L, 30L, TimeUnit.SECONDS); //every 30 seconds
StatusUpdateTask statusUpdateTask = new StatusUpdateTask(); StatusUpdateTask statusUpdateTask = new StatusUpdateTask();
scheduler.scheduleAtFixedRate(statusUpdateTask, 0L, 60L * 5L, TimeUnit.SECONDS); // every 5 minutes scheduler.scheduleAtFixedRate(statusUpdateTask, 0L, 60L * 5L, TimeUnit.SECONDS); // every 5 minutes
if(enableRandomSeedUpdaterTask) if(enableRandomSeedUpdaterTask)
@@ -247,7 +240,6 @@ public class HidekoBot
LOGGER.info("Invite Link: {}", Cache.getInviteUrl()); LOGGER.info("Invite Link: {}", Cache.getInviteUrl());
} }
public static JDA getAPI() public static JDA getAPI()
{ {
return jda; return jda;

View File

@@ -1,15 +1,16 @@
package wtf.beatrice.hidekobot.commands.base; package wtf.beatrice.hidekobot.commands.base;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import java.util.LinkedList; import java.util.LinkedList;
@Component
public class Alias public class Alias
{ {
private Alias() {
throw new IllegalStateException("Utility class");
}
public String generateNiceAliases(MessageCommand command) public static String generateNiceAliases(MessageCommand command)
{ {
LinkedList<String> aliases = command.getCommandLabels(); LinkedList<String> aliases = command.getCommandLabels();
StringBuilder aliasesStringBuilder = new StringBuilder(); StringBuilder aliasesStringBuilder = new StringBuilder();

View File

@@ -2,7 +2,6 @@ package wtf.beatrice.hidekobot.commands.base;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.util.FormatUtil; import wtf.beatrice.hidekobot.util.FormatUtil;
@@ -12,11 +11,13 @@ import java.lang.management.ManagementFactory;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.List; import java.util.List;
@Component
public class BotInfo public class BotInfo
{ {
private BotInfo() {
throw new IllegalStateException("Utility class");
}
public MessageEmbed generateEmbed(List<String> commandLabels) public static MessageEmbed generateEmbed(List<String> commandLabels)
{ {
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setColor(Cache.getBotColor()); embedBuilder.setColor(Cache.getBotColor());
@@ -56,8 +57,7 @@ public class BotInfo
String messageCommandsInfo; String messageCommandsInfo;
if(Cache.getMessageCommandListener() == null) if(Cache.getMessageCommandListener() == null)
messageCommandsInfo = "❌ disabled"; messageCommandsInfo = "❌ disabled";
else else {
{
messageCommandsInfo = "✅ available"; messageCommandsInfo = "✅ available";
commandsCount += Cache.getMessageCommandListener().getRegisteredCommands().size(); commandsCount += Cache.getMessageCommandListener().getRegisteredCommands().size();
} }
@@ -67,8 +67,7 @@ public class BotInfo
String slashCommandsInfo; String slashCommandsInfo;
if(Cache.getMessageCommandListener() == null) if(Cache.getMessageCommandListener() == null)
slashCommandsInfo = "❌ disabled"; slashCommandsInfo = "❌ disabled";
else else {
{
slashCommandsInfo = "✅ available"; slashCommandsInfo = "✅ available";
commandsCount += Cache.getSlashCommandListener().getRegisteredCommands().size(); commandsCount += Cache.getSlashCommandListener().getRegisteredCommands().size();
} }
@@ -79,8 +78,7 @@ public class BotInfo
if(RandomUtil.isRandomOrgKeyValid()) if(RandomUtil.isRandomOrgKeyValid())
{ {
randomOrgInfo = "✅ connected"; randomOrgInfo = "✅ connected";
} else } else {
{
randomOrgInfo = "❌ disabled"; randomOrgInfo = "❌ disabled";
} }
embedBuilder.addField("Random.org", randomOrgInfo, true); embedBuilder.addField("Random.org", randomOrgInfo, true);

View File

@@ -8,50 +8,45 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import org.springframework.stereotype.Component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@Component
public class ClearChat public class ClearChat
{ {
public String getLabel() private ClearChat() {
{ throw new IllegalStateException("Utility class");
}
public static String getLabel() {
return "clear"; return "clear";
} }
public String getDescription() public static String getDescription() {
{
return "Clear the current channel's chat."; return "Clear the current channel's chat.";
} }
public Permission getPermission() public static Permission getPermission() {
{
return Permission.MESSAGE_MANAGE; return Permission.MESSAGE_MANAGE;
} }
public String checkDMs(Channel channel) public static String checkDMs(Channel channel)
{ {
if(!(channel instanceof TextChannel)) if(!(channel instanceof TextChannel))
{ { return "\uD83D\uDE22 Sorry! I can't delete messages here."; }
return "\uD83D\uDE22 Sorry! I can't delete messages here.";
}
return null; return null;
} }
public String checkDeleteAmount(int toDeleteAmount) public static String checkDeleteAmount(int toDeleteAmount)
{ {
if(toDeleteAmount <= 0) if(toDeleteAmount <= 0)
{ { return "\uD83D\uDE22 Sorry, I can't delete that amount of messages!"; }
return "\uD83D\uDE22 Sorry, I can't delete that amount of messages!";
}
return null; return null;
} }
public int delete(int toDeleteAmount, public static int delete(int toDeleteAmount,
long startingMessageId, long startingMessageId,
MessageChannel channel) MessageChannel channel)
{ {
@@ -87,8 +82,7 @@ public class ClearChat
int iterationSize = limit; int iterationSize = limit;
// if we are at the last iteration... check if we have <limit> or fewer messages to delete // if we are at the last iteration... check if we have <limit> or fewer messages to delete
if (iteration + 1 == iterations && remainder != 0) if(iteration+1 == iterations && remainder != 0) {
{
iterationSize = remainder; iterationSize = remainder;
} }
@@ -101,8 +95,7 @@ public class ClearChat
else outOfBounds = true; else outOfBounds = true;
// increase deleted counter by 1 // increase deleted counter by 1
deleted++; deleted++;
} else } else {
{
// get the last <iterationSize - 1> messages. // get the last <iterationSize - 1> messages.
MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1); MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1);
// note: first one is the most recent, last one is the oldest message. // note: first one is the most recent, last one is the oldest message.
@@ -115,8 +108,7 @@ public class ClearChat
if(messages.size() <= 1) if(messages.size() <= 1)
{ {
outOfBounds = true; outOfBounds = true;
} else } else {
{
// before deleting, we need to grab the <previous to the oldest> message's id for next iteration. // before deleting, we need to grab the <previous to the oldest> message's id for next iteration.
action = channel.getHistoryBefore(messages.getLast().getIdLong(), 1); action = channel.getHistoryBefore(messages.getLast().getIdLong(), 1);
@@ -131,10 +123,10 @@ public class ClearChat
if(messages.size() == 1) if(messages.size() == 1)
{ {
messages.getFirst().delete().queue(); messages.getFirst().delete().queue();
} else if (!messages.isEmpty()) }
{ else if(!messages.isEmpty())
try
{ {
try {
((TextChannel) channel).deleteMessages(messages).complete(); ((TextChannel) channel).deleteMessages(messages).complete();
/* alternatively, we could use purgeMessages, which is smarter... /* alternatively, we could use purgeMessages, which is smarter...
however, it also tries to delete messages older than 2 weeks however, it also tries to delete messages older than 2 weeks
@@ -155,13 +147,13 @@ public class ClearChat
return deleted; return deleted;
} }
public Button getDismissButton() public static Button getDismissButton()
{ {
return Button.primary("generic_dismiss", "Dismiss") return Button.primary("generic_dismiss", "Dismiss")
.withEmoji(Emoji.fromUnicode("")); .withEmoji(Emoji.fromUnicode(""));
} }
public String parseAmount(int deleted) public static String parseAmount(int deleted)
{ {
if(deleted < 1) if(deleted < 1)
@@ -170,16 +162,12 @@ public class ClearChat
} else if(deleted == 1) } else if(deleted == 1)
{ {
return "✂ Cleared 1 message!"; return "✂ Cleared 1 message!";
} else } else {
{
return "✂ Cleared " + deleted + " messages!"; return "✂ Cleared " + deleted + " messages!";
} }
} }
// cap the amount to avoid abuse. // cap the amount to avoid abuse.
public int getMaxAmount() public static int getMaxAmount() { return 1000; }
{
return 1000;
}
} }

View File

@@ -6,23 +6,24 @@ import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.util.RandomUtil; import wtf.beatrice.hidekobot.util.RandomUtil;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Component
public class CoinFlip public class CoinFlip
{ {
public Button getReflipButton()
{ private CoinFlip() {
throw new IllegalStateException("Utility class");
}
public static Button getReflipButton() {
return Button.primary("coinflip_reflip", "Flip again") return Button.primary("coinflip_reflip", "Flip again")
.withEmoji(Emoji.fromUnicode("\uD83E\uDE99")); .withEmoji(Emoji.fromUnicode("\uD83E\uDE99"));
} }
public String genRandom() public static String genRandom()
{ {
int rand = RandomUtil.getRandomNumber(0, 1); int rand = RandomUtil.getRandomNumber(0, 1);
String msg; String msg;
@@ -30,61 +31,40 @@ public class CoinFlip
if(rand == 1) if(rand == 1)
{ {
msg = ":coin: It's **Heads**!"; msg = ":coin: It's **Heads**!";
} else } else {
{
msg = "It's **Tails**! :coin:"; msg = "It's **Tails**! :coin:";
} }
return msg; return msg;
} }
public void buttonReFlip(ButtonInteractionEvent event) public static void buttonReFlip(ButtonInteractionEvent event)
{ {
// Ack ASAP to avoid 3s timeout // check if the user interacting is the same one who ran the command
event.deferEdit().queue(hook -> { if(!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
// Permission check **after** ack
if (!Cache.getServices().databaseService().isUserTrackedFor(event.getUser().getId(), event.getMessageId()))
{ {
hook.sendMessage("❌ You did not run this command!").setEphemeral(true).queue(); event.reply("❌ You did not run this command!").setEphemeral(true).queue();
return; return;
} }
// Disable all components on the original message // set old message's button as disabled
List<ActionRow> oldRows = event.getMessage().getActionRows(); List<ActionRow> actionRows = event.getMessage().getActionRows();
List<ActionRow> disabledRows = new ArrayList<>(oldRows.size()); actionRows.set(0, actionRows.get(0).asDisabled());
for (ActionRow row : oldRows) event.editComponents(actionRows).queue();
{
disabledRows.add(row.asDisabled());
}
hook.editOriginalComponents(disabledRows).queue();
// Send a follow-up with a fresh button // perform coin flip
hook.sendMessage(genRandom()) event.getHook().sendMessage(genRandom())
.addActionRow(getReflipButton()) .addActionRow(getReflipButton())
.queue(msg -> trackAndRestrict(msg, event.getUser()), err -> { .queue((message) ->
});
}, failure -> {
// Rare: if we couldn't ack, try best-effort fallbacks
try
{
List<ActionRow> oldRows = event.getMessage().getActionRows();
List<ActionRow> disabledRows = new ArrayList<>(oldRows.size());
for (ActionRow row : oldRows) disabledRows.add(row.asDisabled());
event.getMessage().editMessageComponents(disabledRows).queue();
} catch (Exception ignored)
{ {
// set the command as expiring and restrict it to the user who ran it
trackAndRestrict(message, event.getUser());
}, (error) -> {});
} }
event.getChannel().sendMessage(genRandom()) public static void trackAndRestrict(Message replyMessage, User user)
.addActionRow(getReflipButton())
.queue(msg -> trackAndRestrict(msg, event.getUser()), err -> {
});
});
}
public void trackAndRestrict(Message replyMessage, User user)
{ {
Cache.getServices().databaseService().queueDisabling(replyMessage); Cache.getDatabaseSource().queueDisabling(replyMessage);
Cache.getServices().databaseService().trackRanCommandReply(replyMessage, user); Cache.getDatabaseSource().trackRanCommandReply(replyMessage, user);
} }
} }

View File

@@ -2,7 +2,6 @@ package wtf.beatrice.hidekobot.commands.base;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.fun.Dice; import wtf.beatrice.hidekobot.objects.fun.Dice;
@@ -13,11 +12,14 @@ import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Component
public class DiceRoll public class DiceRoll
{ {
public MessageResponse buildResponse(User author, String[] args) private DiceRoll() {
throw new IllegalStateException("Utility class");
}
public static MessageResponse buildResponse(User author, String[] args)
{ {
LinkedHashMap<Dice, Integer> dicesToRoll = new LinkedHashMap<>(); LinkedHashMap<Dice, Integer> dicesToRoll = new LinkedHashMap<>();
String diceRegex = "d\\d+"; String diceRegex = "d\\d+";
@@ -42,8 +44,7 @@ public class DiceRoll
if(currentDice == null) if(currentDice == null)
{ {
currentDice = new Dice(6); currentDice = new Dice(6);
} else } else {
{
currentDice = new Dice(currentDice); currentDice = new Dice(currentDice);
} }
@@ -55,7 +56,8 @@ public class DiceRoll
lastPushedDice = currentDice.getUUID(); lastPushedDice = currentDice.getUUID();
dicesToRoll.put(currentDice, currentAmount); dicesToRoll.put(currentDice, currentAmount);
totalRolls += currentAmount; totalRolls += currentAmount;
} else if (arg.matches(diceRegex)) }
else if(arg.matches(diceRegex))
{ {
int sides = Integer.parseInt(arg.substring(1)); int sides = Integer.parseInt(arg.substring(1));
@@ -126,7 +128,7 @@ public class DiceRoll
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setColor(Cache.getBotColor()); embedBuilder.setColor(Cache.getBotColor());
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl()); embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
embedBuilder.setTitle("Dice Roll"); embedBuilder.setTitle("Dice Roll");
if(RandomUtil.isRandomOrgKeyValid()) if(RandomUtil.isRandomOrgKeyValid())
@@ -136,16 +138,13 @@ public class DiceRoll
int total = 0; int total = 0;
int previousDiceSides = 0; int previousDiceSides = 0;
for (Dice dice : rolledDices) for (Dice dice : rolledDices) {
{
int diceSize = dice.getSides(); int diceSize = dice.getSides();
if (previousDiceSides != diceSize) if (previousDiceSides != diceSize) {
{
message.append("\nd").append(diceSize).append(": "); message.append("\nd").append(diceSize).append(": ");
previousDiceSides = diceSize; previousDiceSides = diceSize;
} else if (previousDiceSides != 0) } else if (previousDiceSides != 0) {
{
message.append(", "); message.append(", ");
} }

View File

@@ -4,15 +4,17 @@ import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.HidekoBot;
@Component
public class Invite public class Invite
{ {
public MessageEmbed generateEmbed() private Invite() {
throw new IllegalStateException("Utility class");
}
public static MessageEmbed generateEmbed()
{ {
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
@@ -30,7 +32,7 @@ public class Invite
return embedBuilder.build(); return embedBuilder.build();
} }
public Button getInviteButton() public static Button getInviteButton()
{ {
String inviteUrl = Cache.getInviteUrl(); String inviteUrl = Cache.getInviteUrl();
return Button.link(inviteUrl, "Invite " + Cache.getBotName()) return Button.link(inviteUrl, "Invite " + Cache.getBotName())

View File

@@ -3,17 +3,19 @@ package wtf.beatrice.hidekobot.commands.base;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.util.RandomUtil; import wtf.beatrice.hidekobot.util.RandomUtil;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Component
public class LoveCalculator public class LoveCalculator
{ {
public MessageEmbed buildEmbedAndCacheResult(User author, User user1, User user2) private LoveCalculator() {
throw new IllegalStateException("Utility class");
}
public static MessageEmbed buildEmbedAndCacheResult(User author, User user1, User user2)
{ {
String userId1 = user1.getId(); String userId1 = user1.getId();
String userId2 = user2.getId(); String userId2 = user2.getId();
@@ -35,7 +37,7 @@ public class LoveCalculator
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setColor(Cache.getBotColor()); embedBuilder.setColor(Cache.getBotColor());
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl()); embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
embedBuilder.setTitle("Love Calculator"); embedBuilder.setTitle("Love Calculator");
embedBuilder.addField("\uD83D\uDC65 People", embedBuilder.addField("\uD83D\uDC65 People",

View File

@@ -3,7 +3,6 @@ package wtf.beatrice.hidekobot.commands.base;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.util.RandomUtil; import wtf.beatrice.hidekobot.util.RandomUtil;
@@ -12,16 +11,19 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component
public class MagicBall public class MagicBall
{ {
public LinkedList<String> getLabels() private MagicBall() {
throw new IllegalStateException("Utility class");
}
public static LinkedList<String> getLabels()
{ {
return new LinkedList<>(Arrays.asList("8ball", "8b", "eightball", "magicball")); return new LinkedList<>(Arrays.asList("8ball", "8b", "eightball", "magicball"));
} }
private final List<String> answers = new ArrayList<>( private static final List<String> answers = new ArrayList<>(
Arrays.asList("It is certain.", Arrays.asList("It is certain.",
"It is decidedly so.", "It is decidedly so.",
"Without a doubt.", "Without a doubt.",
@@ -43,13 +45,13 @@ public class MagicBall
"Outlook not so good.", "Outlook not so good.",
"Very doubtful.")); "Very doubtful."));
public String getRandomAnswer() public static String getRandomAnswer()
{ {
int answerPos = RandomUtil.getRandomNumber(0, answers.size() - 1); int answerPos = RandomUtil.getRandomNumber(0, answers.size() - 1);
return answers.get(answerPos); return answers.get(answerPos);
} }
public MessageEmbed generateEmbed(String question, User author) public static MessageEmbed generateEmbed(String question, User author)
{ {
// add a question mark at the end, if missing. // add a question mark at the end, if missing.
// this might not always apply but it's fun // this might not always apply but it's fun
@@ -58,7 +60,7 @@ public class MagicBall
String answer = getRandomAnswer(); String answer = getRandomAnswer();
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl()); embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
embedBuilder.setTitle("Magic Ball"); embedBuilder.setTitle("Magic Ball");
embedBuilder.setColor(Cache.getBotColor()); embedBuilder.setColor(Cache.getBotColor());
embedBuilder.addField("❓ Question", question, false); embedBuilder.addField("❓ Question", question, false);

View File

@@ -3,26 +3,26 @@ package wtf.beatrice.hidekobot.commands.base;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.utils.ImageProxy; import net.dv8tion.jda.api.utils.ImageProxy;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
@Component
public class ProfileImage public class ProfileImage
{ {
public int parseResolution(int resolution) private ProfileImage() {
throw new IllegalStateException("Utility class");
}
public static int parseResolution(int resolution)
{ {
int[] acceptedSizes = Cache.getSupportedAvatarResolutions(); int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
// method to find closest value to accepted values // method to find closest value to accepted values
int distance = Math.abs(acceptedSizes[0] - resolution); int distance = Math.abs(acceptedSizes[0] - resolution);
int idx = 0; int idx = 0;
for (int c = 1; c < acceptedSizes.length; c++) for(int c = 1; c < acceptedSizes.length; c++){
{
int cdistance = Math.abs(acceptedSizes[c] - resolution); int cdistance = Math.abs(acceptedSizes[c] - resolution);
if (cdistance < distance) if(cdistance < distance){
{
idx = c; idx = c;
distance = cdistance; distance = cdistance;
} }
@@ -31,7 +31,7 @@ public class ProfileImage
return acceptedSizes[idx]; return acceptedSizes[idx];
} }
public MessageResponse buildResponse(int resolution, User user, ImageType imageType) public static MessageResponse buildResponse(int resolution, User user, ImageType imageType)
{ {
String imageTypeName = imageType.name().toLowerCase(); String imageTypeName = imageType.name().toLowerCase();
String resolutionString; String resolutionString;
@@ -44,8 +44,7 @@ public class ProfileImage
{ {
resolutionString = resolution + " × " + resolution; resolutionString = resolution + " × " + resolution;
imageLink = user.getEffectiveAvatar().getUrl(resolution); imageLink = user.getEffectiveAvatar().getUrl(resolution);
} else } else {
{
int verticalRes = 361 * resolution / 1024; int verticalRes = 361 * resolution / 1024;
resolutionString = resolution + " × " + verticalRes; resolutionString = resolution + " × " + verticalRes;
if(bannerProxy != null) if(bannerProxy != null)
@@ -73,8 +72,7 @@ public class ProfileImage
if(imageType == ImageType.AVATAR) if(imageType == ImageType.AVATAR)
{ {
currLink = user.getEffectiveAvatar().getUrl(currSize); currLink = user.getEffectiveAvatar().getUrl(currSize);
} else } else {
{
if(bannerProxy == null) break; if(bannerProxy == null) break;
currLink = bannerProxy.getUrl(currSize); currLink = bannerProxy.getUrl(currSize);
} }
@@ -89,16 +87,15 @@ public class ProfileImage
embedBuilder.addField("Available resolutions", links.toString(), false); embedBuilder.addField("Available resolutions", links.toString(), false);
if(imageLink != null) if(imageLink != null)
embedBuilder.setImage(imageLink); embedBuilder.setImage(imageLink);
if (imageLink == null) if(imageLink == null) {
{
String error = "I couldn't find " + user.getAsMention() + "'s " + imageTypeName + "!"; String error = "I couldn't find " + user.getAsMention() + "'s " + imageTypeName + "!";
return new MessageResponse(error, null); return new MessageResponse(error, null);
} else } else {
{
return new MessageResponse(null, embedBuilder.build()); return new MessageResponse(null, embedBuilder.build());
} }
} }

View File

@@ -1,13 +1,15 @@
package wtf.beatrice.hidekobot.commands.base; package wtf.beatrice.hidekobot.commands.base;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import org.springframework.stereotype.Component;
@Component
public class Say public class Say
{ {
public Permission getPermission()
{ private Say() {
throw new IllegalStateException("Utility class");
}
public static Permission getPermission() {
return Permission.MESSAGE_MANAGE; return Permission.MESSAGE_MANAGE;
} }
} }

View File

@@ -19,6 +19,7 @@ import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion; import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
import wtf.beatrice.hidekobot.objects.fun.TriviaScore; import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
import wtf.beatrice.hidekobot.runnables.TriviaTask; import wtf.beatrice.hidekobot.runnables.TriviaTask;
import wtf.beatrice.hidekobot.util.CommandUtil;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
@@ -26,18 +27,16 @@ import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class Trivia public class Trivia
{ {
private Trivia() private Trivia() {
{
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
@@ -45,31 +44,22 @@ public class Trivia
private static final String TRIVIA_API_LINK = "https://opentdb.com/api.php?amount=10&type=multiple&category="; private static final String TRIVIA_API_LINK = "https://opentdb.com/api.php?amount=10&type=multiple&category=";
private static final String TRIVIA_API_CATEGORIES_LINK = "https://opentdb.com/api_category.php"; private static final String TRIVIA_API_CATEGORIES_LINK = "https://opentdb.com/api_category.php";
public static List<String> channelsRunningTrivia = Collections.synchronizedList(new ArrayList<>()); public static List<String> channelsRunningTrivia = new ArrayList<>();
// first string is the channelId, the list contain all users who responded there // first string is the channelId, the list contain all users who responded there
public static ConcurrentHashMap<String, List<String>> channelAndWhoResponded = new ConcurrentHashMap<>(); public static HashMap<String, List<String>> channelAndWhoResponded = new HashMap<>();
// first string is the channelId, the list contain all score records for that channel // first string is the channelId, the list contain all score records for that channel
public static ConcurrentHashMap<String, LinkedList<TriviaScore>> channelAndScores = new ConcurrentHashMap<>(); public static HashMap<String, LinkedList<TriviaScore>> channelAndScores = new HashMap<>();
public static String getTriviaLink(int categoryId) public static String getTriviaLink(int categoryId) {return TRIVIA_API_LINK + categoryId; }
{ public static String getCategoriesLink() {return TRIVIA_API_CATEGORIES_LINK; }
return TRIVIA_API_LINK + categoryId;
}
public static String getCategoriesLink() public static String getNoDMsError() {
{
return TRIVIA_API_CATEGORIES_LINK;
}
public static String getNoDMsError()
{
return "\uD83D\uDE22 Sorry! Trivia doesn't work in DMs."; return "\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.";
} }
public static String getTriviaAlreadyRunningError() public static String getTriviaAlreadyRunningError() {
{
// todo nicer looking // todo nicer looking
return "Trivia is already running here!"; return "Trivia is already running here!";
} }
@@ -112,8 +102,7 @@ public class Trivia
public static JSONObject fetchJson(String link) public static JSONObject fetchJson(String link)
{ {
try try {
{
URL url = new URL(link); URL url = new URL(link);
URLConnection connection = url.openConnection(); URLConnection connection = url.openConnection();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
@@ -126,8 +115,7 @@ public class Trivia
} }
bufferedReader.close(); bufferedReader.close();
return new JSONObject(jsonStrBuilder.toString()); return new JSONObject(jsonStrBuilder.toString());
} catch (IOException e) } catch (IOException e) {
{
LOGGER.error("JSON Parsing Exception", e); LOGGER.error("JSON Parsing Exception", e);
} }
@@ -181,8 +169,6 @@ public class Trivia
public static void handleAnswer(ButtonInteractionEvent event, AnswerType answerType) public static void handleAnswer(ButtonInteractionEvent event, AnswerType answerType)
{ {
// Ack immediately with an ephemeral deferral to avoid 3s timeout
event.deferReply(true).queue(hook -> {
User user = event.getUser(); User user = event.getUser();
String channelId = event.getChannel().getId(); String channelId = event.getChannel().getId();
@@ -190,7 +176,6 @@ public class Trivia
{ {
LinkedList<TriviaScore> scores = channelAndScores.get(channelId); LinkedList<TriviaScore> scores = channelAndScores.get(channelId);
if(scores == null) scores = new LinkedList<>(); if(scores == null) scores = new LinkedList<>();
TriviaScore currentUserScore = null; TriviaScore currentUserScore = null;
for(TriviaScore score : scores) for(TriviaScore score : scores)
{ {
@@ -209,35 +194,26 @@ public class Trivia
if(answerType.equals(AnswerType.CORRECT)) if(answerType.equals(AnswerType.CORRECT))
{ {
// Public message in channel
event.getChannel().sendMessage(user.getAsMention() + " got it right! \uD83E\uDD73 (**+3**)").queue(); event.reply(user.getAsMention() + " got it right! \uD83E\uDD73 (**+3**)").queue();
currentUserScore.changeScore(3); currentUserScore.changeScore(3);
} else
{ } else {
event.getChannel().sendMessage("" + user.getAsMention() + ", that's not the right answer! (**-1**)").queue(); event.reply("" + user.getAsMention() + ", that's not the right answer! (**-1**)").queue();
currentUserScore.changeScore(-1); currentUserScore.changeScore(-1);
} }
scores.add(currentUserScore); scores.add(currentUserScore);
channelAndScores.put(channelId, scores); channelAndScores.put(channelId, scores);
} else } else {
{ event.reply("☹️ " + user.getAsMention() + ", you can't answer twice!")
// Show the warning **in the original ephemeral message**, then delete it after 5s. .queue(interaction ->
hook.editOriginal("☹️ " + user.getAsMention() + ", you can't answer twice!").queue(v -> Cache.getTaskScheduler().schedule(() ->
hook.deleteOriginal().queueAfter(3, TimeUnit.SECONDS, null, __ -> { interaction.deleteOriginal().queue(), 3, TimeUnit.SECONDS));
}) }
);
return; // don't run the generic cleanup below; we want the message visible for ~5s
} }
// Clean up the ephemeral deferral (no visible ephemeral message left) for the normal path private static boolean trackResponse(User user, MessageChannel channel)
hook.deleteOriginal().queue(null, __ -> {
});
}, __ -> {
});
}
private static synchronized boolean trackResponse(User user, MessageChannel channel)
{ {
String userId = user.getId(); String userId = user.getId();
String channelId = channel.getId(); String channelId = channel.getId();
@@ -254,25 +230,22 @@ public class Trivia
responders.add(userId); responders.add(userId);
channelAndWhoResponded.put(channelId, responders); channelAndWhoResponded.put(channelId, responders);
return true; // response was successfully tracked return true; // response was successfully tracked
} else } else {
{
return false; // response wasn't tracked because there already was an entry return false; // response wasn't tracked because there already was an entry
} }
} }
public static void handleMenuSelection(StringSelectInteractionEvent event) public static void handleMenuSelection(StringSelectInteractionEvent event)
{ {
// Ack immediately (ephemeral) so we can safely do DB/work
event.deferReply(true).queue(hook -> {
// check if the user interacting is the same one who ran the command // check if the user interacting is the same one who ran the command
if (!(Cache.getServices().databaseService().isUserTrackedFor(event.getUser().getId(), event.getMessageId()))) if(!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
{ {
hook.sendMessage("❌ You did not run this command!").setEphemeral(true).queue(); event.reply("❌ You did not run this command!").setEphemeral(true).queue();
return; return;
} }
// Disable buttons on the original message via service (uses separate REST calls) // todo: we shouldn't use this method, since it messes with the database... look at coin reflip
Cache.getServices().commandService().disableExpired(event.getMessageId()); CommandUtil.disableExpired(event.getMessageId());
SelectOption pickedOption = event.getInteraction().getSelectedOptions().get(0); SelectOption pickedOption = event.getInteraction().getSelectedOptions().get(0);
String categoryName = pickedOption.getLabel(); String categoryName = pickedOption.getLabel();
@@ -282,12 +255,6 @@ public class Trivia
TriviaCategory category = new TriviaCategory(categoryName, categoryId); TriviaCategory category = new TriviaCategory(categoryName, categoryId);
startTrivia(event, category); startTrivia(event, category);
// remove the ephemeral deferral to keep things clean
hook.deleteOriginal().queue(null, __ -> {
});
}, __ -> {
});
} }
public static void startTrivia(StringSelectInteractionEvent event, TriviaCategory category) public static void startTrivia(StringSelectInteractionEvent event, TriviaCategory category)
@@ -298,19 +265,19 @@ public class Trivia
if(Trivia.channelsRunningTrivia.contains(channel.getId())) if(Trivia.channelsRunningTrivia.contains(channel.getId()))
{ {
// Already running: inform ephemerally via hook (the interaction was deferred in the caller) // todo nicer looking
event.getHook().sendMessage(Trivia.getTriviaAlreadyRunningError()) // todo: also what if the bot stops (database...?)
.setEphemeral(true) // todo: also what if the message is already deleted
.queue(msg -> Cache.getTaskScheduler().schedule(() -> msg.delete().queue(), 10, TimeUnit.SECONDS)); Message err = event.reply("Trivia is already running here!").complete().retrieveOriginal().complete();
Cache.getTaskScheduler().schedule(() -> err.delete().queue(), 10, TimeUnit.SECONDS);
return; return;
} else } else {
{ // todo nicer looking
// Public info that a new session is starting event.reply("Starting new Trivia session!").queue();
channel.sendMessage("Starting new Trivia session!").queue();
} }
TriviaTask triviaTask = new TriviaTask(author, channel, category,
Cache.getServices().databaseService(), Cache.getServices().commandService()); TriviaTask triviaTask = new TriviaTask(author, channel, category);
ScheduledFuture<?> future = ScheduledFuture<?> future =
Cache.getTaskScheduler().scheduleAtFixedRate(triviaTask, Cache.getTaskScheduler().scheduleAtFixedRate(triviaTask,
0, 0,
@@ -321,8 +288,7 @@ public class Trivia
Trivia.channelsRunningTrivia.add(channel.getId()); Trivia.channelsRunningTrivia.add(channel.getId());
} }
public enum AnswerType public enum AnswerType {
{
CORRECT, WRONG CORRECT, WRONG
} }

View File

@@ -14,7 +14,7 @@ import org.apache.commons.text.WordUtils;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.services.DatabaseService; import wtf.beatrice.hidekobot.datasources.DatabaseSource;
import wtf.beatrice.hidekobot.util.SerializationUtil; import wtf.beatrice.hidekobot.util.SerializationUtil;
import java.util.ArrayList; import java.util.ArrayList;
@@ -25,19 +25,15 @@ import java.util.List;
public class UrbanDictionary public class UrbanDictionary
{ {
private UrbanDictionary() private UrbanDictionary() {
{
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
public static LinkedList<String> getCommandLabels() public static LinkedList<String> getCommandLabels()
{ { return new LinkedList<>(Arrays.asList("urban", "urbandictionary", "ud")); }
return new LinkedList<>(Arrays.asList("urban", "urbandictionary", "ud"));
}
public static String getBaseUrl() public static String getBaseUrl() {
{
return "https://www.urbandictionary.com/define.php?term="; return "https://www.urbandictionary.com/define.php?term=";
} }
@@ -59,8 +55,7 @@ public class UrbanDictionary
.withEmoji(Emoji.fromFormatted("\uD83D\uDDD1")); .withEmoji(Emoji.fromFormatted("\uD83D\uDDD1"));
} }
public static String getNoArgsError() public static String getNoArgsError() {
{
return "\uD83D\uDE22 I need to know what to search for!"; return "\uD83D\uDE22 I need to know what to search for!";
} }
@@ -88,7 +83,7 @@ public class UrbanDictionary
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setColor(Cache.getBotColor()); embedBuilder.setColor(Cache.getBotColor());
embedBuilder.setTitle(term + ", on Urban Dictionary", url); embedBuilder.setTitle(term + ", on Urban Dictionary", url);
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl()); embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
embedBuilder.addField("\uD83D\uDCD6 Definition", search.getPlaintextMeanings().get(page), false); embedBuilder.addField("\uD83D\uDCD6 Definition", search.getPlaintextMeanings().get(page), false);
embedBuilder.addField("\uD83D\uDCAD Example", search.getPlaintextExamples().get(page), false); embedBuilder.addField("\uD83D\uDCAD Example", search.getPlaintextExamples().get(page), false);
embedBuilder.addField("\uD83D\uDCCC Submission", embedBuilder.addField("\uD83D\uDCCC Submission",
@@ -107,9 +102,9 @@ public class UrbanDictionary
public static void track(Message message, User user, UrbanSearch search, String sanitizedTerm) public static void track(Message message, User user, UrbanSearch search, String sanitizedTerm)
{ {
Cache.getServices().databaseService().queueDisabling(message); Cache.getDatabaseSource().queueDisabling(message);
Cache.getServices().databaseService().trackRanCommandReply(message, user); Cache.getDatabaseSource().trackRanCommandReply(message, user);
Cache.getServices().databaseService().trackUrban(search.getSerializedMeanings(), Cache.getDatabaseSource().trackUrban(search.getSerializedMeanings(),
search.getSerializedExamples(), search.getSerializedExamples(),
search.getSerializedContributors(), search.getSerializedContributors(),
search.getSerializedDates(), search.getSerializedDates(),
@@ -119,19 +114,17 @@ public class UrbanDictionary
public static void changePage(ButtonInteractionEvent event, ChangeType changeType) public static void changePage(ButtonInteractionEvent event, ChangeType changeType)
{ {
event.deferEdit().queue();
String messageId = event.getMessageId(); String messageId = event.getMessageId();
DatabaseService database = Cache.getServices().databaseService(); DatabaseSource database = Cache.getDatabaseSource();
// check if the user interacting is the same one who ran the command // check if the user interacting is the same one who ran the command
if (!(database.isUserTrackedFor(event.getUser().getId(), messageId))) if (!(database.isUserTrackedFor(event.getUser().getId(), messageId))) {
{
event.reply("❌ You did not run this command!").setEphemeral(true).queue(); event.reply("❌ You did not run this command!").setEphemeral(true).queue();
return; return;
} }
// get current page and calculate how many pages there are // get current page and calculate how many pages there are
int page = database.getUrbanPage(messageId); int page = Cache.getDatabaseSource().getUrbanPage(messageId);
String term = database.getUrbanTerm(messageId); String term = database.getUrbanTerm(messageId);
String url = generateUrl(term); String url = generateUrl(term);
@@ -163,16 +156,14 @@ public class UrbanDictionary
if(page > 0) if(page > 0)
{ {
components.add(UrbanDictionary.getPreviousPageButton().asEnabled()); components.add(UrbanDictionary.getPreviousPageButton().asEnabled());
} else } else {
{
components.add(UrbanDictionary.getPreviousPageButton().asDisabled()); components.add(UrbanDictionary.getPreviousPageButton().asDisabled());
} }
if(page + 1 == search.getPages()) if(page + 1 == search.getPages())
{ {
components.add(UrbanDictionary.getNextPageButton().asDisabled()); components.add(UrbanDictionary.getNextPageButton().asDisabled());
} else } else {
{
components.add(UrbanDictionary.getNextPageButton().asEnabled()); components.add(UrbanDictionary.getNextPageButton().asEnabled());
} }
@@ -181,9 +172,7 @@ public class UrbanDictionary
ActionRow currentRow = ActionRow.of(components); ActionRow currentRow = ActionRow.of(components);
// update the message // update the message
event.getHook().editOriginalEmbeds(updatedEmbed) event.editComponents(currentRow).setEmbeds(updatedEmbed).queue();
.setComponents(currentRow)
.queue();
database.setUrbanPage(messageId, page); database.setUrbanPage(messageId, page);
database.resetExpiryTimestamp(messageId); database.resetExpiryTimestamp(messageId);
} }
@@ -293,48 +282,39 @@ public class UrbanDictionary
pages = submissionDates.size(); pages = submissionDates.size();
} }
public List<String> getPlaintextMeanings() public List<String> getPlaintextMeanings() {
{
return this.plaintextMeanings; return this.plaintextMeanings;
} }
public List<String> getPlaintextExamples() public List<String> getPlaintextExamples() {
{
return this.plaintextExamples; return this.plaintextExamples;
} }
public List<String> getContributorsNames() public List<String> getContributorsNames() {
{
return this.contributorsNames; return this.contributorsNames;
} }
public List<String> getSubmissionDates() public List<String> getSubmissionDates() {
{
return this.submissionDates; return this.submissionDates;
} }
public String getSerializedMeanings() public String getSerializedMeanings() {
{
return serializedMeanings; return serializedMeanings;
} }
public String getSerializedExamples() public String getSerializedExamples() {
{
return serializedExamples; return serializedExamples;
} }
public String getSerializedContributors() public String getSerializedContributors() {
{
return serializedContributors; return serializedContributors;
} }
public String getSerializedDates() public String getSerializedDates() {
{
return serializedDates; return serializedDates;
} }
public int getPages() public int getPages() {
{
return pages; return pages;
} }
} }

View File

@@ -12,7 +12,6 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
@@ -25,14 +24,17 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Component
public class UserPunishment public class UserPunishment
{ {
private UserPunishment() {
throw new IllegalStateException("Utility class");
}
private static final Duration maxTimeoutDuration = Duration.of(28, ChronoUnit.DAYS); private static final Duration maxTimeoutDuration = Duration.of(28, ChronoUnit.DAYS);
private static final Duration minTimeoutDuration = Duration.of(30, ChronoUnit.SECONDS); private static final Duration minTimeoutDuration = Duration.of(30, ChronoUnit.SECONDS);
public void handle(SlashCommandInteractionEvent event, PunishmentType punishmentType) public static void handle(SlashCommandInteractionEvent event, PunishmentType punishmentType)
{ {
// this might take a sec // this might take a sec
event.deferReply().queue(); event.deferReply().queue();
@@ -89,7 +91,7 @@ public class UserPunishment
event.getHook().editOriginal(response.content()).queue(); event.getHook().editOriginal(response.content()).queue();
} }
public void handle(MessageReceivedEvent event, String[] args, PunishmentType punishmentType) public static void handle(MessageReceivedEvent event, String[] args, PunishmentType punishmentType)
{ {
Mentions msgMentions = event.getMessage().getMentions(); Mentions msgMentions = event.getMessage().getMentions();
List<IMentionable> mentions = msgMentions.getMentions(); List<IMentionable> mentions = msgMentions.getMentions();
@@ -106,7 +108,7 @@ public class UserPunishment
event.getMessage().reply(response.content()).queue(); event.getMessage().reply(response.content()).queue();
} }
public MessageResponse getResponse(User author, public static MessageResponse getResponse(User author,
PunishmentType punishmentType, PunishmentType punishmentType,
MessageChannelUnion channel, MessageChannelUnion channel,
List<IMentionable> mentions, List<IMentionable> mentions,
@@ -130,8 +132,7 @@ public class UserPunishment
String mentionedId = mentions.get(0).getId(); String mentionedId = mentions.get(0).getId();
User mentioned = null; User mentioned = null;
try try {
{
mentioned = HidekoBot.getAPI().retrieveUserById(mentionedId).complete(); mentioned = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
} catch (RuntimeException ignored) } catch (RuntimeException ignored)
{ {
@@ -171,22 +172,16 @@ public class UserPunishment
AuditableRestAction<Void> punishmentAction = null; AuditableRestAction<Void> punishmentAction = null;
boolean impossible = false; boolean impossible = false;
try try {
{ switch (punishmentType) {
switch (punishmentType)
{
case BAN -> punishmentAction = guild.ban(mentioned, 0, TimeUnit.SECONDS); case BAN -> punishmentAction = guild.ban(mentioned, 0, TimeUnit.SECONDS);
case KICK -> punishmentAction = guild.kick(mentioned); case KICK -> punishmentAction = guild.kick(mentioned);
case TIMEOUT -> case TIMEOUT -> {
if(args != null)
{ {
// Ensure a duration argument is provided at index 1 (after mention/user)
if (args == null || args.length <= 1)
{
return new MessageResponse("Please specify a punishment duration!", null);
}
String durationStr = args[1]; String durationStr = args[1];
duration = FormatUtil.parseDuration(durationStr); duration = FormatUtil.parseDuration(durationStr);
}
boolean isDurationValid = true; boolean isDurationValid = true;
@@ -197,7 +192,7 @@ public class UserPunishment
if(minTimeoutDuration.compareTo(duration) > 0) isDurationValid = false; if(minTimeoutDuration.compareTo(duration) > 0) isDurationValid = false;
} }
if (!isDurationValid) if(duration == null || !isDurationValid)
{ {
// todo nicer looking with emojis // todo nicer looking with emojis
return new MessageResponse("Sorry, but the specified duration is invalid!", null); return new MessageResponse("Sorry, but the specified duration is invalid!", null);
@@ -206,8 +201,7 @@ public class UserPunishment
punishmentAction = guild.timeoutFor(mentioned, duration); punishmentAction = guild.timeoutFor(mentioned, duration);
} }
} }
} catch (RuntimeException ignored) } catch (RuntimeException ignored) {
{
impossible = true; impossible = true;
} }
@@ -222,10 +216,9 @@ public class UserPunishment
} }
if(!reason.isEmpty() && !reasonBuilder.isEmpty()) if(!reason.isEmpty() && !reasonBuilder.isEmpty())
punishmentAction.reason("[" + author.getName() + "] " + reason); punishmentAction.reason("[" + author.getAsTag() + "] " + reason);
try try {
{
punishmentAction.complete(); punishmentAction.complete();
} catch (RuntimeException ignored) } catch (RuntimeException ignored)
{ {
@@ -236,7 +229,7 @@ public class UserPunishment
EmbedBuilder embedBuilder = new EmbedBuilder(); EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl()); embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
embedBuilder.setColor(Cache.getBotColor()); embedBuilder.setColor(Cache.getBotColor());
embedBuilder.setTitle("User " + punishmentType.getPastTense()); embedBuilder.setTitle("User " + punishmentType.getPastTense());
@@ -255,8 +248,7 @@ public class UserPunishment
} }
public enum PunishmentType public enum PunishmentType {
{
KICK("kicked"), KICK("kicked"),
BAN("banned"), BAN("banned"),
TIMEOUT("timed out"), TIMEOUT("timed out"),

View File

@@ -13,14 +13,12 @@ import java.util.List;
public class ProfileImageCommandCompleter extends SlashArgumentsCompleterImpl public class ProfileImageCommandCompleter extends SlashArgumentsCompleterImpl
{ {
public ProfileImageCommandCompleter(SlashCommand parentCommand) public ProfileImageCommandCompleter(SlashCommand parentCommand) {
{
super(parentCommand); super(parentCommand);
} }
@Override @Override
public void runCompletion(@NotNull CommandAutoCompleteInteractionEvent event) public void runCompletion(@NotNull CommandAutoCompleteInteractionEvent event) {
{
if(event.getFocusedOption().getName().equals("size")) if(event.getFocusedOption().getName().equals("size"))
{ {

View File

@@ -4,8 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.commands.base.Alias; import wtf.beatrice.hidekobot.commands.base.Alias;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
@@ -15,54 +13,40 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class AliasCommand implements MessageCommand
public class MessageAliasCommand implements MessageCommand
{ {
private final Alias alias;
public MessageAliasCommand(@Autowired Alias alias)
{
this.alias = alias;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Arrays.asList("alias", "aliases")); return new LinkedList<>(Arrays.asList("alias", "aliases"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; // anyone can use it return null; // anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.TOOLS; return CommandCategory.TOOLS;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "See other command aliases."; return "See other command aliases.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<command>"; return "<command>";
} }
@@ -83,7 +67,7 @@ public class MessageAliasCommand implements MessageCommand
return; return;
} }
String aliases = alias.generateNiceAliases(command); String aliases = Alias.generateNiceAliases(command);
aliases = "Aliases for **" + command.getCommandLabels().get(0) + "**: " + aliases; aliases = "Aliases for **" + command.getCommandLabels().get(0) + "**: " + aliases;
event.getMessage() event.getMessage()

View File

@@ -6,8 +6,6 @@ import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.commands.base.ProfileImage; import wtf.beatrice.hidekobot.commands.base.ProfileImage;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
@@ -18,53 +16,40 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class AvatarCommand implements MessageCommand
public class MessageAvatarCommand implements MessageCommand
{ {
private final ProfileImage profileImage;
public MessageAvatarCommand(@Autowired ProfileImage profileImage)
{
this.profileImage = profileImage;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("avatar")); return new LinkedList<>(Collections.singletonList("avatar"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; // anyone can use it return null; // anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Get someone's avatar, or your own. You can additionally specify a resolution."; return "Get someone's avatar, or your own. You can additionally specify a resolution.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "[mentioned user] [resolution]"; return "[mentioned user] [resolution]";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.TOOLS; return CommandCategory.TOOLS;
} }
@@ -78,22 +63,19 @@ public class MessageAvatarCommand implements MessageCommand
// (mentions are handled differently by a specific method) // (mentions are handled differently by a specific method)
boolean resFound = false; boolean resFound = false;
for (String arg : args) for (String arg : args) {
{ try {
try
{
int givenRes = Integer.parseInt(arg); int givenRes = Integer.parseInt(arg);
resolution = profileImage.parseResolution(givenRes); resolution = ProfileImage.parseResolution(givenRes);
resFound = true; resFound = true;
break; break;
} catch (NumberFormatException ignored) } catch (NumberFormatException ignored) {
{
// ignored because we're running a check after this block // ignored because we're running a check after this block
} }
} }
// fallback in case we didn't find any specified resolution // fallback in case we didn't find any specified resolution
if (!resFound) resolution = profileImage.parseResolution(512); if(!resFound) resolution = ProfileImage.parseResolution(512);
// check if someone is mentioned // check if someone is mentioned
Mentions mentions = event.getMessage().getMentions(); Mentions mentions = event.getMessage().getMentions();
@@ -110,7 +92,7 @@ public class MessageAvatarCommand implements MessageCommand
if(user == null) user = event.getAuthor(); if(user == null) user = event.getAuthor();
// send a response // send a response
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR); MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
if(response.content() != null) if(response.content() != null)
{ {
event.getMessage().reply(response.content()).queue(); event.getMessage().reply(response.content()).queue();

View File

@@ -4,8 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.UserPunishment; import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -15,59 +13,46 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class BanCommand implements MessageCommand
public class MessageBanCommand implements MessageCommand
{ {
private final UserPunishment userPunishment;
public MessageBanCommand(@Autowired UserPunishment userPunishment)
{
this.userPunishment = userPunishment;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("ban")); return new LinkedList<>(Collections.singletonList("ban"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return new ArrayList<Permission>(Collections.singletonList(Permission.BAN_MEMBERS)); return new ArrayList<Permission>(Collections.singletonList(Permission.BAN_MEMBERS));
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.MODERATION; return CommandCategory.MODERATION;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Ban the mentioned user."; return "Ban the mentioned user.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<mentioned user> [reason]"; return "<mentioned user> [reason]";
} }
@Override @Override
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args)
{ {
userPunishment.handle(event, args, UserPunishment.PunishmentType.BAN); UserPunishment.handle(event, args, UserPunishment.PunishmentType.BAN);
} }
} }

View File

@@ -6,8 +6,6 @@ import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.commands.base.ProfileImage; import wtf.beatrice.hidekobot.commands.base.ProfileImage;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
@@ -18,53 +16,40 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class BannerCommand implements MessageCommand
public class MessageBannerCommand implements MessageCommand
{ {
private final ProfileImage profileImage;
public MessageBannerCommand(@Autowired ProfileImage profileImage)
{
this.profileImage = profileImage;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("banner")); return new LinkedList<>(Collections.singletonList("banner"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; // anyone can use it return null; // anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Get someone's profile banner, or your own."; return "Get someone's profile banner, or your own.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "[mentioned user] [resolution]"; return "[mentioned user] [resolution]";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.TOOLS; return CommandCategory.TOOLS;
} }
@@ -78,22 +63,19 @@ public class MessageBannerCommand implements MessageCommand
// (mentions are handled differently by a specific method) // (mentions are handled differently by a specific method)
boolean resFound = false; boolean resFound = false;
for (String arg : args) for (String arg : args) {
{ try {
try
{
int givenRes = Integer.parseInt(arg); int givenRes = Integer.parseInt(arg);
resolution = profileImage.parseResolution(givenRes); resolution = ProfileImage.parseResolution(givenRes);
resFound = true; resFound = true;
break; break;
} catch (NumberFormatException ignored) } catch (NumberFormatException ignored) {
{
// ignored because we're running a check after this block // ignored because we're running a check after this block
} }
} }
// fallback in case we didn't find any specified resolution // fallback in case we didn't find any specified resolution
if (!resFound) resolution = profileImage.parseResolution(512); if(!resFound) resolution = ProfileImage.parseResolution(512);
// check if someone is mentioned // check if someone is mentioned
Mentions mentions = event.getMessage().getMentions(); Mentions mentions = event.getMessage().getMentions();
@@ -110,7 +92,7 @@ public class MessageBannerCommand implements MessageCommand
if(user == null) user = event.getAuthor(); if(user == null) user = event.getAuthor();
// send a response // send a response
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER); MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER);
if(response.content() != null) if(response.content() != null)
{ {
event.getMessage().reply(response.content()).queue(); event.getMessage().reply(response.content()).queue();

View File

@@ -5,8 +5,6 @@ import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.commands.base.BotInfo; import wtf.beatrice.hidekobot.commands.base.BotInfo;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
@@ -16,71 +14,55 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class BotInfoCommand implements MessageCommand
public class MessageBotInfoCommand implements MessageCommand
{ {
private final BotInfo botInfo;
public MessageBotInfoCommand(@Autowired BotInfo botInfo)
{
this.botInfo = botInfo;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Arrays.asList("botinfo", "info")); return new LinkedList<>(Arrays.asList("botinfo", "info"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; // anyone can use it return null; // anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Get general info about the bot."; return "Get general info about the bot.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return null; return null;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.TOOLS; return CommandCategory.TOOLS;
} }
@Override @Override
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args) {
{
// get a list of message commands // get a list of message commands
LinkedList<MessageCommand> messageCommands = Cache.getMessageCommandListener().getRegisteredCommands(); LinkedList<MessageCommand> messageCommands = Cache.getMessageCommandListener().getRegisteredCommands();
LinkedList<String> commandNames = new LinkedList<>(); LinkedList<String> commandNames = new LinkedList<>();
for (MessageCommand command : messageCommands) for (MessageCommand command : messageCommands) {
{
commandNames.add(command.getCommandLabels().get(0)); commandNames.add(command.getCommandLabels().get(0));
} }
// send the list // send the list
MessageEmbed embed = botInfo.generateEmbed(commandNames); MessageEmbed embed = BotInfo.generateEmbed(commandNames);
event.getMessage().replyEmbeds(embed).queue(); event.getMessage().replyEmbeds(embed).queue();
} }
} }

View File

@@ -6,8 +6,6 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.commands.base.ClearChat; import wtf.beatrice.hidekobot.commands.base.ClearChat;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
@@ -17,53 +15,37 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class ClearCommand implements MessageCommand
public class MessageClearCommand implements MessageCommand
{ {
private final ClearChat clearChat; @Override
public LinkedList<String> getCommandLabels() {
public MessageClearCommand(@Autowired ClearChat clearChat) return new LinkedList<>(Collections.singletonList(ClearChat.getLabel()));
{
this.clearChat = clearChat;
} }
@Override @Override
public LinkedList<String> getCommandLabels() public List<Permission> getPermissions() { return Collections.singletonList(ClearChat.getPermission()); }
{
return new LinkedList<>(Collections.singletonList(clearChat.getLabel()));
}
@Override @Override
public List<Permission> getPermissions() public boolean passRawArgs() {
{
return Collections.singletonList(clearChat.getPermission());
}
@Override
public boolean passRawArgs()
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.MODERATION; return CommandCategory.MODERATION;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Clear the current channel's chat history."; return "Clear the current channel's chat history.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "[amount]"; return "[amount]";
} }
@@ -71,9 +53,8 @@ public class MessageClearCommand implements MessageCommand
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args)
{ {
// check if user is trying to run command in dms. // check if user is trying to run command in dms.
String error = clearChat.checkDMs(event.getChannel()); String error = ClearChat.checkDMs(event.getChannel());
if (error != null) if (error != null) {
{
event.getMessage().reply(error).queue(); event.getMessage().reply(error).queue();
return; return;
} }
@@ -83,8 +64,7 @@ public class MessageClearCommand implements MessageCommand
if (args.length == 0) toDeleteAmount = 1; if (args.length == 0) toDeleteAmount = 1;
else else
{ {
try try {
{
toDeleteAmount = Integer.parseInt(args[0]); toDeleteAmount = Integer.parseInt(args[0]);
} catch (NumberFormatException e) } catch (NumberFormatException e)
{ {
@@ -93,11 +73,10 @@ public class MessageClearCommand implements MessageCommand
} }
// cap the amount to avoid abuse. // cap the amount to avoid abuse.
if (toDeleteAmount > clearChat.getMaxAmount()) toDeleteAmount = 0; if(toDeleteAmount > ClearChat.getMaxAmount()) toDeleteAmount = 0;
error = clearChat.checkDeleteAmount(toDeleteAmount); error = ClearChat.checkDeleteAmount(toDeleteAmount);
if (error != null) if (error != null) {
{
event.getMessage().reply(error).queue(); event.getMessage().reply(error).queue();
return; return;
} }
@@ -106,20 +85,20 @@ public class MessageClearCommand implements MessageCommand
String content = "\uD83D\uDEA7 Clearing..."; String content = "\uD83D\uDEA7 Clearing...";
Message botMessage = event.getMessage().reply(content).complete(); Message botMessage = event.getMessage().reply(content).complete();
int deleted = clearChat.delete(toDeleteAmount, int deleted = ClearChat.delete(toDeleteAmount,
event.getMessageIdLong(), event.getMessageIdLong(),
event.getChannel()); event.getChannel());
// get a nicely formatted message that logs the deletion of messages. // get a nicely formatted message that logs the deletion of messages.
content = clearChat.parseAmount(deleted); content = ClearChat.parseAmount(deleted);
// edit the message text and attach a button. // edit the message text and attach a button.
Button dismiss = clearChat.getDismissButton(); Button dismiss = ClearChat.getDismissButton();
Message finalMessage = event.getChannel().sendMessage(content).setActionRow(dismiss).complete(); Message finalMessage = event.getChannel().sendMessage(content).setActionRow(dismiss).complete();
// add the message to database. // add the message to database.
Cache.getServices().databaseService().queueDisabling(finalMessage); Cache.getDatabaseSource().queueDisabling(finalMessage);
Cache.getServices().databaseService().trackRanCommandReply(finalMessage, event.getAuthor()); Cache.getDatabaseSource().trackRanCommandReply(finalMessage, event.getAuthor());
// delete the sender's message. // delete the sender's message.
event.getMessage().delete().queue(); event.getMessage().delete().queue();

View File

@@ -4,8 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.CoinFlip; import wtf.beatrice.hidekobot.commands.base.CoinFlip;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -14,69 +12,53 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class CoinFlipCommand implements MessageCommand
public class MessageCoinFlipCommand implements MessageCommand
{ {
private final CoinFlip coinFlip;
public MessageCoinFlipCommand(@Autowired CoinFlip coinFlip)
{
this.coinFlip = coinFlip;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Arrays.asList("coinflip", "flip", "flipcoin")); return new LinkedList<>(Arrays.asList("coinflip", "flip", "flipcoin"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; // null because it can be used anywhere return null; // null because it can be used anywhere
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Flip a coin."; return "Flip a coin.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return null; return null;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.FUN; return CommandCategory.FUN;
} }
@Override @Override
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args) {
{
// perform coin flip // perform coin flip
event.getMessage().reply(coinFlip.genRandom()) event.getMessage().reply(CoinFlip.genRandom())
.addActionRow(coinFlip.getReflipButton()) .addActionRow(CoinFlip.getReflipButton())
.queue((message) -> .queue((message) ->
{ {
// set the command as expiring and restrict it to the user who ran it // set the command as expiring and restrict it to the user who ran it
coinFlip.trackAndRestrict(message, event.getAuthor()); CoinFlip.trackAndRestrict(message, event.getAuthor());
}, (error) -> { }, (error) -> {});
});
} }
} }

View File

@@ -4,8 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.DiceRoll; import wtf.beatrice.hidekobot.commands.base.DiceRoll;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
@@ -15,32 +13,21 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class DiceRollCommand implements MessageCommand
public class MessageDiceRollCommand implements MessageCommand
{ {
private final DiceRoll diceRoll;
public MessageDiceRollCommand(@Autowired DiceRoll diceRoll)
{
this.diceRoll = diceRoll;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Arrays.asList("diceroll", "droll", "roll")); return new LinkedList<>(Arrays.asList("diceroll", "droll", "roll"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; // anyone can use it return null; // anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@@ -60,15 +47,13 @@ public class MessageDiceRollCommand implements MessageCommand
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "[dice size] [rolls]"; return "[dice size] [rolls]";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.FUN; return CommandCategory.FUN;
} }
@@ -76,7 +61,7 @@ public class MessageDiceRollCommand implements MessageCommand
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args)
{ {
MessageResponse response = diceRoll.buildResponse(event.getAuthor(), args); MessageResponse response = DiceRoll.buildResponse(event.getAuthor(), args);
if(response.content() != null) if(response.content() != null)
{ {

View File

@@ -4,7 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -12,46 +11,37 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class HelloCommand implements MessageCommand
public class MessageHelloCommand implements MessageCommand
{ {
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Arrays.asList("hi", "hello", "heya")); return new LinkedList<>(Arrays.asList("hi", "hello", "heya"));
} }
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() { return null; }
{
return null;
}
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Get pinged by the bot."; return "Get pinged by the bot.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return null; return null;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.FUN; return CommandCategory.FUN;
} }

View File

@@ -6,8 +6,6 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.apache.commons.text.WordUtils; import org.apache.commons.text.WordUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.commands.base.Alias; import wtf.beatrice.hidekobot.commands.base.Alias;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
@@ -15,54 +13,39 @@ import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import java.util.*; import java.util.*;
@Component public class HelpCommand implements MessageCommand
public class MessageHelpCommand implements MessageCommand
{ {
private final Alias alias;
public MessageHelpCommand(@Autowired Alias alias)
{
this.alias = alias;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("help")); return new LinkedList<>(Collections.singletonList("help"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() { return null; }
{
return null;
}
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Get general help on the bot. Specify a command if you want specific help about that command."; return "Get general help on the bot. Specify a command if you want specific help about that command.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "[command]"; return "[command]";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.TOOLS; return CommandCategory.TOOLS;
} }
@@ -119,8 +102,7 @@ public class MessageHelpCommand implements MessageCommand
} }
event.getMessage().replyEmbeds(embedBuilder.build()).queue(); event.getMessage().replyEmbeds(embedBuilder.build()).queue();
} else } else {
{
String commandLabel = args[0].toLowerCase(); String commandLabel = args[0].toLowerCase();
MessageCommand command = Cache.getMessageCommandListener().getRegisteredCommand(commandLabel); MessageCommand command = Cache.getMessageCommandListener().getRegisteredCommand(commandLabel);
@@ -136,15 +118,14 @@ public class MessageHelpCommand implements MessageCommand
if(internalUsage != null) usage += " " + internalUsage; if(internalUsage != null) usage += " " + internalUsage;
usage += "`"; usage += "`";
String aliases = alias.generateNiceAliases(command); String aliases = Alias.generateNiceAliases(command);
List<Permission> permissions = command.getPermissions(); List<Permission> permissions = command.getPermissions();
StringBuilder permissionsStringBuilder = new StringBuilder(); StringBuilder permissionsStringBuilder = new StringBuilder();
if(permissions == null) if(permissions == null)
{ {
permissionsStringBuilder = new StringBuilder("Available to everyone"); permissionsStringBuilder = new StringBuilder("Available to everyone");
} else } else {
{
for(int i = 0; i < permissions.size(); i++) for(int i = 0; i < permissions.size(); i++)
{ {
Permission permission = permissions.get(i); Permission permission = permissions.get(i);

View File

@@ -7,8 +7,6 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.Invite; import wtf.beatrice.hidekobot.commands.base.Invite;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -17,53 +15,40 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class InviteCommand implements MessageCommand
public class MessageInviteCommand implements MessageCommand
{ {
private final Invite invite;
public MessageInviteCommand(@Autowired Invite invite)
{
this.invite = invite;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("invite")); return new LinkedList<>(Collections.singletonList("invite"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; return null;
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Get the bot's invite link."; return "Get the bot's invite link.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return null; return null;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.MODERATION; return CommandCategory.MODERATION;
} }
@@ -72,8 +57,8 @@ public class MessageInviteCommand implements MessageCommand
{ {
MessageEmbed inviteEmbed = invite.generateEmbed(); MessageEmbed inviteEmbed = Invite.generateEmbed();
Button inviteButton = invite.getInviteButton(); Button inviteButton = Invite.getInviteButton();
// if this is a guild, don't spam the invite in public but DM it // if this is a guild, don't spam the invite in public but DM it
if(event.getChannelType().isGuild()) if(event.getChannelType().isGuild())
@@ -85,8 +70,7 @@ public class MessageInviteCommand implements MessageCommand
.queue(); .queue();
event.getMessage().addReaction(Emoji.fromUnicode("")).queue(); event.getMessage().addReaction(Emoji.fromUnicode("")).queue();
}, error -> event.getMessage().addReaction(Emoji.fromUnicode("")).queue()); }, error -> event.getMessage().addReaction(Emoji.fromUnicode("")).queue());
} else } else {
{
event.getMessage() event.getMessage()
.replyEmbeds(inviteEmbed) .replyEmbeds(inviteEmbed)
.addActionRow(inviteButton) .addActionRow(inviteButton)

View File

@@ -4,8 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.UserPunishment; import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -15,59 +13,46 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class KickCommand implements MessageCommand
public class MessageKickCommand implements MessageCommand
{ {
private final UserPunishment userPunishment;
public MessageKickCommand(@Autowired UserPunishment userPunishment)
{
this.userPunishment = userPunishment;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("kick")); return new LinkedList<>(Collections.singletonList("kick"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return new ArrayList<Permission>(Collections.singletonList(Permission.KICK_MEMBERS)); return new ArrayList<Permission>(Collections.singletonList(Permission.KICK_MEMBERS));
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.MODERATION; return CommandCategory.MODERATION;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Kick the mentioned user from the guild."; return "Kick the mentioned user from the guild.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<mentioned user> [reason]"; return "<mentioned user> [reason]";
} }
@Override @Override
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args)
{ {
userPunishment.handle(event, args, UserPunishment.PunishmentType.KICK); UserPunishment.handle(event, args, UserPunishment.PunishmentType.KICK);
} }
} }

View File

@@ -8,8 +8,6 @@ import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.commands.base.LoveCalculator; import wtf.beatrice.hidekobot.commands.base.LoveCalculator;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
@@ -19,15 +17,8 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class LoveCalculatorCommand implements MessageCommand
public class MessageLoveCalculatorCommand implements MessageCommand
{ {
private final LoveCalculator loveCalculator;
public MessageLoveCalculatorCommand(@Autowired LoveCalculator loveCalculator)
{
this.loveCalculator = loveCalculator;
}
@Override @Override
@@ -38,35 +29,30 @@ public class MessageLoveCalculatorCommand implements MessageCommand
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; //anyone can use it return null; //anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Calculate how much two people love each other. You can mention two people or just one."; return "Calculate how much two people love each other. You can mention two people or just one.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<person 1> [person 2]"; return "<person 1> [person 2]";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.FUN; return CommandCategory.FUN;
} }
@@ -94,13 +80,12 @@ public class MessageLoveCalculatorCommand implements MessageCommand
if(mentions.size() == 1) if(mentions.size() == 1)
{ {
user2 = event.getAuthor(); user2 = event.getAuthor();
} else } else {
{
mentionedUserId = mentions.get(1).getId(); mentionedUserId = mentions.get(1).getId();
user2 = HidekoBot.getAPI().retrieveUserById(mentionedUserId).complete(); user2 = HidekoBot.getAPI().retrieveUserById(mentionedUserId).complete();
} }
MessageEmbed embed = loveCalculator.buildEmbedAndCacheResult(event.getAuthor(), user1, user2); MessageEmbed embed = LoveCalculator.buildEmbedAndCacheResult(event.getAuthor(), user1, user2);
event.getChannel().sendMessageEmbeds(embed).queue(); event.getChannel().sendMessageEmbeds(embed).queue();
} }

View File

@@ -4,8 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.MagicBall; import wtf.beatrice.hidekobot.commands.base.MagicBall;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -13,53 +11,40 @@ import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class MagicBallCommand implements MessageCommand
public class MessageMagicBallCommand implements MessageCommand
{ {
private final MagicBall magicBall;
public MessageMagicBallCommand(@Autowired MagicBall magicBall)
{
this.magicBall = magicBall;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{ return MagicBall.getLabels();
return magicBall.getLabels();
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; // anyone can use it return null; // anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Ask a question to the Magic Ball."; return "Ask a question to the Magic Ball.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<question>"; return "<question>";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.FUN; return CommandCategory.FUN;
} }
@@ -84,6 +69,6 @@ public class MessageMagicBallCommand implements MessageCommand
String question = questionBuilder.toString(); String question = questionBuilder.toString();
event.getChannel().sendMessageEmbeds(magicBall.generateEmbed(question, event.getAuthor())).queue(); event.getChannel().sendMessageEmbeds(MagicBall.generateEmbed(question, event.getAuthor())).queue();
} }
} }

View File

@@ -5,8 +5,6 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.Say; import wtf.beatrice.hidekobot.commands.base.Say;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -15,54 +13,39 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class SayCommand implements MessageCommand
public class MessageSayCommand implements MessageCommand
{ {
private final Say say;
public MessageSayCommand(@Autowired Say say)
{
this.say = say;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("say")); return new LinkedList<>(Collections.singletonList("say"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() { return Collections.singletonList(Say.getPermission()); }
{
return Collections.singletonList(say.getPermission());
}
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return true; return true;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Make the bot say something for you."; return "Make the bot say something for you.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<text>"; return "<text>";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.TOOLS; return CommandCategory.TOOLS;
} }

View File

@@ -4,8 +4,6 @@ import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.UserPunishment; import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -15,59 +13,46 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class TimeoutCommand implements MessageCommand
public class MessageTimeoutCommand implements MessageCommand
{ {
private final UserPunishment userPunishment;
public MessageTimeoutCommand(@Autowired UserPunishment userPunishment)
{
this.userPunishment = userPunishment;
}
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("timeout")); return new LinkedList<>(Collections.singletonList("timeout"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return new ArrayList<Permission>(Collections.singletonList(Permission.MODERATE_MEMBERS)); return new ArrayList<Permission>(Collections.singletonList(Permission.MODERATE_MEMBERS));
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.MODERATION; return CommandCategory.MODERATION;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Timeout the mentioned user."; return "Timeout the mentioned user.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<mentioned user> <duration> [reason]"; return "<mentioned user> <duration> [reason]";
} }
@Override @Override
public void runCommand(MessageReceivedEvent event, String label, String[] args) public void runCommand(MessageReceivedEvent event, String label, String[] args)
{ {
userPunishment.handle(event, args, UserPunishment.PunishmentType.TIMEOUT); UserPunishment.handle(event, args, UserPunishment.PunishmentType.TIMEOUT);
} }
} }

View File

@@ -19,46 +19,40 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class MessageTriviaCommand implements MessageCommand public class TriviaCommand implements MessageCommand
{ {
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return new LinkedList<>(Collections.singletonList("trivia")); return new LinkedList<>(Collections.singletonList("trivia"));
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; return null;
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.FUN; return CommandCategory.FUN;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Start a Trivia session and play with others!"; return "Start a Trivia session and play with others!";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return null; return null;
} }
@@ -89,13 +83,12 @@ public class MessageTriviaCommand implements MessageCommand
if(response.content() != null) responseAction = recvMessage.reply(response.content()); if(response.content() != null) responseAction = recvMessage.reply(response.content());
else if(response.embed() != null) responseAction = recvMessage.replyEmbeds(response.embed()); else if(response.embed() != null) responseAction = recvMessage.replyEmbeds(response.embed());
if (responseAction != null) if(responseAction != null) {
{
if(response.components() != null) responseAction = responseAction.addActionRow(response.components()); if(response.components() != null) responseAction = responseAction.addActionRow(response.components());
responseAction.queue(message -> { responseAction.queue(message -> {
Cache.getServices().databaseService().trackRanCommandReply(message, event.getAuthor()); Cache.getDatabaseSource().trackRanCommandReply(message, event.getAuthor());
Cache.getServices().databaseService().queueDisabling(message); Cache.getDatabaseSource().queueDisabling(message);
}); });
} }

View File

@@ -17,46 +17,40 @@ import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class MessageUrbanDictionaryCommand implements MessageCommand public class UrbanDictionaryCommand implements MessageCommand
{ {
@Override @Override
public LinkedList<String> getCommandLabels() public LinkedList<String> getCommandLabels() {
{
return UrbanDictionary.getCommandLabels(); return UrbanDictionary.getCommandLabels();
} }
@Nullable @Nullable
@Override @Override
public List<Permission> getPermissions() public List<Permission> getPermissions() {
{
return null; //anyone can use it return null; //anyone can use it
} }
@Override @Override
public boolean passRawArgs() public boolean passRawArgs() {
{
return false; return false;
} }
@NotNull @NotNull
@Override @Override
public String getDescription() public String getDescription() {
{
return "Look something up in the Urban Dictionary."; return "Look something up in the Urban Dictionary.";
} }
@Nullable @Nullable
@Override @Override
public String getUsage() public String getUsage() {
{
return "<query>"; return "<query>";
} }
@NotNull @NotNull
@Override @Override
public CommandCategory getCategory() public CommandCategory getCategory() {
{
return CommandCategory.FUN; return CommandCategory.FUN;
} }
@@ -72,8 +66,7 @@ public class MessageUrbanDictionaryCommand implements MessageCommand
// sanitize args by only keeping letters and numbers, and adding "+" instead of spaces for HTML parsing // sanitize args by only keeping letters and numbers, and adding "+" instead of spaces for HTML parsing
StringBuilder termBuilder = new StringBuilder(); StringBuilder termBuilder = new StringBuilder();
for (int i = 0; i < args.length; i++) for (int i = 0; i < args.length; i++) {
{
String arg = args[i]; String arg = args[i];
termBuilder.append(arg); termBuilder.append(arg);
@@ -86,11 +79,9 @@ public class MessageUrbanDictionaryCommand implements MessageCommand
Document doc; Document doc;
try try {
{
doc = Jsoup.connect(url).get(); doc = Jsoup.connect(url).get();
} catch (IOException e) } catch (IOException e) {
{
event.getMessage().reply(UrbanDictionary.getTermNotFoundError()).queue(); event.getMessage().reply(UrbanDictionary.getTermNotFoundError()).queue();
return; return;
} }

View File

@@ -7,24 +7,14 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.ProfileImage; import wtf.beatrice.hidekobot.commands.base.ProfileImage;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class AvatarCommand extends SlashCommandImpl
public class SlashAvatarCommand extends SlashCommandImpl
{ {
private final ProfileImage profileImage;
public SlashAvatarCommand(@NotNull ProfileImage profileImage)
{
this.profileImage = profileImage;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData() {
{
return Commands.slash("avatar", "Get someone's profile picture.") return Commands.slash("avatar", "Get someone's profile picture.")
.addOption(OptionType.USER, "user", "User you want to grab the avatar of.") .addOption(OptionType.USER, "user", "User you want to grab the avatar of.")
.addOption(OptionType.INTEGER, "size", "The size of the returned image.", .addOption(OptionType.INTEGER, "size", "The size of the returned image.",
@@ -45,21 +35,19 @@ public class SlashAvatarCommand extends SlashCommandImpl
if(userArg != null) if(userArg != null)
{ {
user = userArg.getAsUser(); user = userArg.getAsUser();
} else } else {
{
user = event.getUser(); user = event.getUser();
} }
OptionMapping sizeArg = event.getOption("size"); OptionMapping sizeArg = event.getOption("size");
if(sizeArg != null) if(sizeArg != null)
{ {
resolution = profileImage.parseResolution(sizeArg.getAsInt()); resolution = ProfileImage.parseResolution(sizeArg.getAsInt());
} else } else {
{ resolution = ProfileImage.parseResolution(512);
resolution = profileImage.parseResolution(512);
} }
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR); MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
if(response.content() != null) if(response.content() != null)
{ {
event.getHook().editOriginal(response.content()).queue(); event.getHook().editOriginal(response.content()).queue();

View File

@@ -7,21 +7,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.UserPunishment; import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class BanCommand extends SlashCommandImpl
public class SlashBanCommand extends SlashCommandImpl
{ {
private final UserPunishment userPunishment;
public SlashBanCommand(@Autowired UserPunishment userPunishment)
{
this.userPunishment = userPunishment;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
{ {
@@ -41,6 +31,6 @@ public class SlashBanCommand extends SlashCommandImpl
@Override @Override
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
{ {
userPunishment.handle(event, UserPunishment.PunishmentType.BAN); UserPunishment.handle(event, UserPunishment.PunishmentType.BAN);
} }
} }

View File

@@ -7,24 +7,14 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.ProfileImage; import wtf.beatrice.hidekobot.commands.base.ProfileImage;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class BannerCommand extends SlashCommandImpl
public class SlashBannerCommand extends SlashCommandImpl
{ {
private final ProfileImage profileImage;
public SlashBannerCommand(@NotNull ProfileImage profileImage)
{
this.profileImage = profileImage;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData() {
{
return Commands.slash("banner", "Get someone's profile banner.") return Commands.slash("banner", "Get someone's profile banner.")
.addOption(OptionType.USER, "user", "User you want to grab the banner of.") .addOption(OptionType.USER, "user", "User you want to grab the banner of.")
.addOption(OptionType.INTEGER, "size", "The size of the returned image.", .addOption(OptionType.INTEGER, "size", "The size of the returned image.",
@@ -45,21 +35,19 @@ public class SlashBannerCommand extends SlashCommandImpl
if(userArg != null) if(userArg != null)
{ {
user = userArg.getAsUser(); user = userArg.getAsUser();
} else } else {
{
user = event.getUser(); user = event.getUser();
} }
OptionMapping sizeArg = event.getOption("size"); OptionMapping sizeArg = event.getOption("size");
if(sizeArg != null) if(sizeArg != null)
{ {
resolution = profileImage.parseResolution(sizeArg.getAsInt()); resolution = ProfileImage.parseResolution(sizeArg.getAsInt());
} else } else {
{ resolution = ProfileImage.parseResolution(512);
resolution = profileImage.parseResolution(512);
} }
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER); MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER);
if(response.content() != null) if(response.content() != null)
{ {
event.getHook().editOriginal(response.content()).queue(); event.getHook().editOriginal(response.content()).queue();

View File

@@ -5,8 +5,6 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEve
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.commands.base.BotInfo; import wtf.beatrice.hidekobot.commands.base.BotInfo;
import wtf.beatrice.hidekobot.objects.commands.SlashCommand; import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
@@ -15,20 +13,10 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@Component public class BotInfoCommand extends SlashCommandImpl
public class SlashBotInfoCommand extends SlashCommandImpl
{ {
private final BotInfo botInfo;
public SlashBotInfoCommand(@Autowired BotInfo botInfo)
{
this.botInfo = botInfo;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData() {
{
return Commands.slash("botinfo", "Get info about the bot."); return Commands.slash("botinfo", "Get info about the bot.");
} }
@@ -48,7 +36,7 @@ public class SlashBotInfoCommand extends SlashCommandImpl
} }
// send the list // send the list
MessageEmbed embed = botInfo.generateEmbed(registeredCommandNames); MessageEmbed embed = BotInfo.generateEmbed(registeredCommandNames);
event.getHook().editOriginalEmbeds(embed).queue(); event.getHook().editOriginalEmbeds(embed).queue();
} }
} }

View File

@@ -9,29 +9,19 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.commands.base.ClearChat; import wtf.beatrice.hidekobot.commands.base.ClearChat;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class ClearCommand extends SlashCommandImpl
public class SlashClearCommand extends SlashCommandImpl
{ {
private final ClearChat clearChat;
public SlashClearCommand(@Autowired ClearChat clearChat)
{
this.clearChat = clearChat;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData() {
{ return Commands.slash(ClearChat.getLabel(),
return Commands.slash(clearChat.getLabel(), ClearChat.getDescription())
clearChat.getDescription())
.addOption(OptionType.INTEGER, "amount", "The amount of messages to delete.") .addOption(OptionType.INTEGER, "amount", "The amount of messages to delete.")
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(clearChat.getPermission())); .setDefaultPermissions(DefaultMemberPermissions.enabledFor(ClearChat.getPermission()));
} }
@Override @Override
@@ -41,7 +31,7 @@ public class SlashClearCommand extends SlashCommandImpl
event.deferReply().queue(); event.deferReply().queue();
// check if user is trying to run command in dms. // check if user is trying to run command in dms.
String error = clearChat.checkDMs(event.getChannel()); String error = ClearChat.checkDMs(event.getChannel());
if(error != null) if(error != null)
{ {
event.getHook().editOriginal(error).queue(); event.getHook().editOriginal(error).queue();
@@ -55,9 +45,9 @@ public class SlashClearCommand extends SlashCommandImpl
int toDeleteAmount = amountOption == null ? 1 : amountOption.getAsInt(); int toDeleteAmount = amountOption == null ? 1 : amountOption.getAsInt();
// cap the amount to avoid abuse. // cap the amount to avoid abuse.
if (toDeleteAmount > clearChat.getMaxAmount()) toDeleteAmount = 0; if(toDeleteAmount > ClearChat.getMaxAmount()) toDeleteAmount = 0;
error = clearChat.checkDeleteAmount(toDeleteAmount); error = ClearChat.checkDeleteAmount(toDeleteAmount);
if(error != null) if(error != null)
{ {
event.getHook().editOriginal(error).queue(); event.getHook().editOriginal(error).queue();
@@ -69,20 +59,20 @@ public class SlashClearCommand extends SlashCommandImpl
Message botMessage = event.getHook().editOriginal(content).complete(); Message botMessage = event.getHook().editOriginal(content).complete();
// actually delete the messages. // actually delete the messages.
int deleted = clearChat.delete(toDeleteAmount, int deleted = ClearChat.delete(toDeleteAmount,
event.getInteraction().getIdLong(), event.getInteraction().getIdLong(),
event.getChannel()); event.getChannel());
// get a nicely formatted message that logs the deletion of messages. // get a nicely formatted message that logs the deletion of messages.
content = clearChat.parseAmount(deleted); content = ClearChat.parseAmount(deleted);
// edit the message text and attach a button. // edit the message text and attach a button.
Button dismiss = clearChat.getDismissButton(); Button dismiss = ClearChat.getDismissButton();
botMessage = botMessage.editMessage(content).setActionRow(dismiss).complete(); botMessage = botMessage.editMessage(content).setActionRow(dismiss).complete();
// add the message to database. // add the message to database.
Cache.getServices().databaseService().queueDisabling(botMessage); Cache.getDatabaseSource().queueDisabling(botMessage);
Cache.getServices().databaseService().trackRanCommandReply(botMessage, event.getUser()); Cache.getDatabaseSource().trackRanCommandReply(botMessage, event.getUser());
} }
} }

View File

@@ -4,20 +4,11 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEve
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.CoinFlip; import wtf.beatrice.hidekobot.commands.base.CoinFlip;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class CoinFlipCommand extends SlashCommandImpl
public class SlashCoinFlipCommand extends SlashCommandImpl
{ {
private final CoinFlip coinFlip;
public SlashCoinFlipCommand(@Autowired CoinFlip coinFlip)
{
this.coinFlip = coinFlip;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
@@ -30,19 +21,20 @@ public class SlashCoinFlipCommand extends SlashCommandImpl
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
{ {
// perform coin flip // perform coin flip
event.reply(coinFlip.genRandom()) event.reply(CoinFlip.genRandom())
.addActionRow(coinFlip.getReflipButton()) .addActionRow(CoinFlip.getReflipButton())
.queue((interaction) -> .queue((interaction) ->
{ {
// set the command as expiring and restrict it to the user who ran it // set the command as expiring and restrict it to the user who ran it
interaction.retrieveOriginal().queue((message) -> interaction.retrieveOriginal().queue((message) ->
{ {
coinFlip.trackAndRestrict(message, event.getUser()); CoinFlip.trackAndRestrict(message, event.getUser());
}, (error) -> { }, (error) -> {});
}); }, (error) -> {});
}, (error) -> {
});
} }
} }

View File

@@ -6,21 +6,12 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.DiceRoll; import wtf.beatrice.hidekobot.commands.base.DiceRoll;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class DiceRollCommand extends SlashCommandImpl
public class SlashDiceRollCommand extends SlashCommandImpl
{ {
private final DiceRoll diceRoll;
public SlashDiceRollCommand(@NotNull DiceRoll diceRoll)
{
this.diceRoll = diceRoll;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
{ {
@@ -46,7 +37,7 @@ public class SlashDiceRollCommand extends SlashCommandImpl
String[] args = messageContent.split("\\s"); String[] args = messageContent.split("\\s");
MessageResponse response = diceRoll.buildResponse(event.getUser(), args); MessageResponse response = DiceRoll.buildResponse(event.getUser(), args);
if(response.content() != null) if(response.content() != null)
{ {

View File

@@ -13,11 +13,10 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class SlashDieCommand extends SlashCommandImpl public class DieCommand extends SlashCommandImpl
{ {
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData() {
{
return Commands.slash("die", "Stop the bot's process.") return Commands.slash("die", "Stop the bot's process.")
.setDefaultPermissions(DefaultMemberPermissions.DISABLED); .setDefaultPermissions(DefaultMemberPermissions.DISABLED);
} }
@@ -28,11 +27,9 @@ public class SlashDieCommand extends SlashCommandImpl
if(Cache.getBotOwnerId() != event.getUser().getIdLong()) if(Cache.getBotOwnerId() != event.getUser().getIdLong())
{ {
event.reply("Sorry, only the bot owner can run this command!").setEphemeral(true).queue(); event.reply("Sorry, only the bot owner can run this command!").setEphemeral(true).queue();
} else } else {
{
event.reply("Going to sleep! Cya ✨").queue(); event.reply("Going to sleep! Cya ✨").queue();
try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor()) try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor()) {
{
executor.schedule(HidekoBot::shutdown, 3, TimeUnit.SECONDS); executor.schedule(HidekoBot::shutdown, 3, TimeUnit.SECONDS);
} }
} }

View File

@@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class SlashHelpCommand extends SlashCommandImpl public class HelpCommand extends SlashCommandImpl
{ {
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()

View File

@@ -10,19 +10,11 @@ import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.requests.restaction.WebhookMessageEditAction; import net.dv8tion.jda.api.requests.restaction.WebhookMessageEditAction;
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.Invite; import wtf.beatrice.hidekobot.commands.base.Invite;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class InviteCommand extends SlashCommandImpl
public class SlashInviteCommand extends SlashCommandImpl
{ {
private final Invite invite;
public SlashInviteCommand(@NotNull Invite invite)
{
this.invite = invite;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
@@ -44,8 +36,8 @@ public class SlashInviteCommand extends SlashCommandImpl
} }
replyCallbackAction.queue(); replyCallbackAction.queue();
MessageEmbed inviteEmbed = invite.generateEmbed(); MessageEmbed inviteEmbed = Invite.generateEmbed();
Button inviteButton = invite.getInviteButton(); Button inviteButton = Invite.getInviteButton();
WebhookMessageEditAction<Message> reply = WebhookMessageEditAction<Message> reply =
event.getHook() event.getHook()

View File

@@ -7,22 +7,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.UserPunishment; import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class KickCommand extends SlashCommandImpl
public class SlashKickCommand extends SlashCommandImpl
{ {
private final UserPunishment userPunishment;
public SlashKickCommand(@Autowired UserPunishment userPunishment)
{
this.userPunishment = userPunishment;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
{ {
@@ -42,6 +31,6 @@ public class SlashKickCommand extends SlashCommandImpl
@Override @Override
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
{ {
userPunishment.handle(event, UserPunishment.PunishmentType.KICK); UserPunishment.handle(event, UserPunishment.PunishmentType.KICK);
} }
} }

View File

@@ -8,20 +8,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.LoveCalculator; import wtf.beatrice.hidekobot.commands.base.LoveCalculator;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class LoveCalculatorCommand extends SlashCommandImpl
public class SlashLoveCalculatorCommand extends SlashCommandImpl
{ {
private final LoveCalculator loveCalculator;
public SlashLoveCalculatorCommand(@NotNull LoveCalculator loveCalculator)
{
this.loveCalculator = loveCalculator;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
{ {
@@ -49,8 +40,7 @@ public class SlashLoveCalculatorCommand extends SlashCommandImpl
if(firsUserArg != null) if(firsUserArg != null)
{ {
firstUser = firsUserArg.getAsUser(); //todo null check? firstUser = firsUserArg.getAsUser(); //todo null check?
} else } else {
{
event.reply("\uD83D\uDE22 I need to know who to check! Please mention them.") event.reply("\uD83D\uDE22 I need to know who to check! Please mention them.")
.setEphemeral(true) .setEphemeral(true)
.queue(); .queue();
@@ -61,12 +51,11 @@ public class SlashLoveCalculatorCommand extends SlashCommandImpl
if(secondUserArg != null) if(secondUserArg != null)
{ {
secondUser = secondUserArg.getAsUser(); //todo null check? secondUser = secondUserArg.getAsUser(); //todo null check?
} else } else {
{
secondUser = event.getUser(); secondUser = event.getUser();
} }
MessageEmbed embed = loveCalculator.buildEmbedAndCacheResult(event.getUser(), firstUser, secondUser); MessageEmbed embed = LoveCalculator.buildEmbedAndCacheResult(event.getUser(), firstUser, secondUser);
event.replyEmbeds(embed).queue(); event.replyEmbeds(embed).queue();
} }
} }

View File

@@ -7,25 +7,16 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.MagicBall; import wtf.beatrice.hidekobot.commands.base.MagicBall;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class MagicBallCommand extends SlashCommandImpl
public class SlashMagicBallCommand extends SlashCommandImpl
{ {
private final MagicBall magicBall;
public SlashMagicBallCommand(@NotNull MagicBall magicBall)
{
this.magicBall = magicBall;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
{ {
return Commands.slash(magicBall.getLabels().get(0), return Commands.slash(MagicBall.getLabels().get(0),
"Ask a question to the magic ball.") "Ask a question to the magic ball.")
.addOption(OptionType.STRING, "question", .addOption(OptionType.STRING, "question",
"The question to ask.", "The question to ask.",
@@ -52,7 +43,7 @@ public class SlashMagicBallCommand extends SlashCommandImpl
return; return;
} }
MessageEmbed response = magicBall.generateEmbed(question, event.getUser()); MessageEmbed response = MagicBall.generateEmbed(question, event.getUser());
event.replyEmbeds(response).queue(); event.replyEmbeds(response).queue();
} }
} }

View File

@@ -6,7 +6,7 @@ import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class SlashPingCommand extends SlashCommandImpl public class PingCommand extends SlashCommandImpl
{ {
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()

View File

@@ -8,20 +8,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.Say; import wtf.beatrice.hidekobot.commands.base.Say;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class SayCommand extends SlashCommandImpl
public class SlashSayCommand extends SlashCommandImpl
{ {
private final Say say;
public SlashSayCommand(@NotNull Say say)
{
this.say = say;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
{ {
@@ -31,7 +22,7 @@ public class SlashSayCommand extends SlashCommandImpl
"The message to send.", "The message to send.",
true, true,
false) false)
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(say.getPermission())); .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Say.getPermission()));
} }
@Override @Override

View File

@@ -7,22 +7,11 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.UserPunishment; import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
@Component public class TimeoutCommand extends SlashCommandImpl
public class SlashTimeoutCommand extends SlashCommandImpl
{ {
private final UserPunishment userPunishment;
public SlashTimeoutCommand(@Autowired UserPunishment userPunishment)
{
this.userPunishment = userPunishment;
}
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
{ {
@@ -46,6 +35,6 @@ public class SlashTimeoutCommand extends SlashCommandImpl
@Override @Override
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
{ {
userPunishment.handle(event, UserPunishment.PunishmentType.TIMEOUT); UserPunishment.handle(event, UserPunishment.PunishmentType.TIMEOUT);
} }
} }

View File

@@ -11,7 +11,7 @@ import wtf.beatrice.hidekobot.commands.base.Trivia;
import wtf.beatrice.hidekobot.objects.MessageResponse; import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class SlashTriviaCommand extends SlashCommandImpl public class TriviaCommand extends SlashCommandImpl
{ {
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
@@ -44,8 +44,8 @@ public class SlashTriviaCommand extends SlashCommandImpl
event.getHook().editOriginalEmbeds(response.embed()).setActionRow(response.components()).queue(message -> event.getHook().editOriginalEmbeds(response.embed()).setActionRow(response.components()).queue(message ->
{ {
Cache.getServices().databaseService().trackRanCommandReply(message, event.getUser()); Cache.getDatabaseSource().trackRanCommandReply(message, event.getUser());
Cache.getServices().databaseService().queueDisabling(message); Cache.getDatabaseSource().queueDisabling(message);
}); });
} }
} }

View File

@@ -17,7 +17,7 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
import java.io.IOException; import java.io.IOException;
public class SlashUrbanDictionaryCommand extends SlashCommandImpl public class UrbanDictionaryCommand extends SlashCommandImpl
{ {
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData()
@@ -54,11 +54,9 @@ public class SlashUrbanDictionaryCommand extends SlashCommandImpl
Document doc; Document doc;
try try {
{
doc = Jsoup.connect(url).get(); doc = Jsoup.connect(url).get();
} catch (IOException e) } catch (IOException e) {
{
event.reply(UrbanDictionary.getTermNotFoundError()) event.reply(UrbanDictionary.getTermNotFoundError())
.setEphemeral(true) .setEphemeral(true)
.queue(); .queue();

View File

@@ -14,20 +14,12 @@ public enum ConfigurationEntry
private String path; private String path;
private Object defaultValue; private Object defaultValue;
ConfigurationEntry(String path, Object defaultValue) ConfigurationEntry(String path, Object defaultValue)
{ {
this.path = path; this.path = path;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
public String getPath() public String getPath() { return path; }
{ public Object getDefaultValue() { return defaultValue; }
return path;
}
public Object getDefaultValue()
{
return defaultValue;
}
} }

View File

@@ -49,16 +49,15 @@ public class ConfigurationSource
if(!fsConfigFile.exists()) if(!fsConfigFile.exists())
{ {
// try to create config file // try to create config file
try try {
{
if(!fsConfigFile.createNewFile()) if(!fsConfigFile.createNewFile())
{ {
LOGGER.error("We tried creating a file that already exists!"); LOGGER.error("We tried creating a file that already exists!");
HidekoBot.shutdown(); HidekoBot.shutdown();
return; return;
} }
} catch (IOException e) }
{ catch (IOException e) {
LOGGER.error("Error creating configuration file!", e); LOGGER.error("Error creating configuration file!", e);
HidekoBot.shutdown(); HidekoBot.shutdown();
return; return;
@@ -69,12 +68,8 @@ public class ConfigurationSource
Yaml fsConfigYaml = new Yaml(new SafeConstructor(options)); Yaml fsConfigYaml = new Yaml(new SafeConstructor(options));
LinkedHashMap<String, Object> fsConfigContents = null; // map holding all file entries LinkedHashMap<String, Object> fsConfigContents = null; // map holding all file entries
try (InputStream fsConfigStream = new FileInputStream(fsConfigFile)) try (InputStream fsConfigStream = new FileInputStream(fsConfigFile))
{ { fsConfigContents = fsConfigYaml.load(fsConfigStream); }
fsConfigContents = fsConfigYaml.load(fsConfigStream); catch (IOException e) { LOGGER.error(e.getMessage()); }
} catch (IOException e)
{
LOGGER.error(e.getMessage());
}
if(fsConfigContents == null) // if file contents are empty or corrupted... if(fsConfigContents == null) // if file contents are empty or corrupted...
@@ -111,15 +106,13 @@ public class ConfigurationSource
{ {
// if the key already exists, copy the original value // if the key already exists, copy the original value
filledEntries.put(key, fsConfigContents.get(key)); filledEntries.put(key, fsConfigContents.get(key));
} else } else {
{
// else, copy the value from the example config file // else, copy the value from the example config file
filledEntries.put(key, entry.getValue()); filledEntries.put(key, entry.getValue());
} }
} }
try try {
{
// new writer to actually write the contents to the file // new writer to actually write the contents to the file
PrintWriter missingKeysWriter = new PrintWriter(fsConfigFile); PrintWriter missingKeysWriter = new PrintWriter(fsConfigFile);
@@ -132,8 +125,7 @@ public class ConfigurationSource
// create the yaml object and dump the values to filesystem // create the yaml object and dump the values to filesystem
Yaml yaml = new Yaml(dumperOptions); Yaml yaml = new Yaml(dumperOptions);
yaml.dump(filledEntries, missingKeysWriter); yaml.dump(filledEntries, missingKeysWriter);
} catch (FileNotFoundException e) } catch (FileNotFoundException e) {
{
LOGGER.error(e.getMessage()); LOGGER.error(e.getMessage());
HidekoBot.shutdown(); HidekoBot.shutdown();
return; return;
@@ -141,8 +133,7 @@ public class ConfigurationSource
// finally, dump all entries to cache. // finally, dump all entries to cache.
loadConfig(filledEntries); loadConfig(filledEntries);
} else } else {
{
// if no key is missing, just cache all entries and values from filesystem. // if no key is missing, just cache all entries and values from filesystem.
loadConfig(fsConfigContents); loadConfig(fsConfigContents);
} }
@@ -153,7 +144,6 @@ public class ConfigurationSource
{ {
this.configurationEntries.putAll(configurationEntries); this.configurationEntries.putAll(configurationEntries);
} }
public Object getConfigValue(ConfigurationEntry key) public Object getConfigValue(ConfigurationEntry key)
{ {
return configurationEntries.get(key.getPath()); return configurationEntries.get(key.getPath());

View File

@@ -0,0 +1,592 @@
package wtf.beatrice.hidekobot.datasources;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.Cache;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class DatabaseSource {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DatabaseSource.class);
private static final String JDBC_URL = "jdbc:sqlite:%path%";
private Connection dbConnection = null;
private final String dbPath;
public DatabaseSource(String dbPath) {
this.dbPath = dbPath;
}
private void logException(SQLException e) {
LOGGER.error("Database Exception", e);
}
public boolean connect() {
String url = JDBC_URL.replace("%path%", dbPath);
if (!close()) return false;
try {
dbConnection = DriverManager.getConnection(url);
LOGGER.info("Database connection established!");
return true;
} catch (SQLException e) {
logException(e);
return false;
}
}
public boolean close() {
if (dbConnection != null) {
try {
if (!dbConnection.isClosed()) {
dbConnection.close();
}
} catch (SQLException e) {
logException(e);
return false;
}
dbConnection = null;
}
return true;
}
/*
* DB STRUCTURE
* TABLE 1: pending_disabled_messages
* ----------------------------------------------------------------------------------
* | guild_id | channel_id | message_id | expiry_timestamp |
* ----------------------------------------------------------------------------------
* |39402849302 | 39402849302 | 39402849302 | 2022-11-20 22:45:53:300 |
* ---------------------------------------------------------------------------------
*
*
* TABLE 2: command_runners
* --------------------------------------------------------------------------------------------
* | guild_id | channel_id | message_id | user_id | channel_type |
* --------------------------------------------------------------------------------------------
* | 39402849302 | 39402849302 | 39402849302 | 39402849302 | PRIVATE |
* --------------------------------------------------------------------------------------------
*
* TABLE 3: urban_dictionary
* -----------------------------------------------------------------------------------------------------
* | message_id | page | meanings | examples | contributors | dates | term |
* -----------------------------------------------------------------------------------------------------
* | 39402849302 | 0 | base64 | base64 | base64 | base64 | miku |
* -----------------------------------------------------------------------------------------------------
*/
//todo: javadocs
public boolean initDb() {
List<String> newTables = new ArrayList<>();
newTables.add("""
CREATE TABLE IF NOT EXISTS pending_disabled_messages (
guild_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message_id TEXT NOT NULL,
expiry_timestamp TEXT NOT NULL);
""");
newTables.add("""
CREATE TABLE IF NOT EXISTS command_runners (
guild_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message_id TEXT NOT NULL,
user_id TEXT NOT NULL,
channel_type TEXT NOT NULL);
""");
newTables.add("""
CREATE TABLE IF NOT EXISTS urban_dictionary (
message_id TEXT NOT NULL,
page INTEGER NOT NULL,
meanings TEXT NOT NULL,
examples TEXT NOT NULL,
contributors TEXT NOT NULL,
dates TEXT NOT NULL,
term TEXT NOT NULL
);
""");
for (String sql : newTables) {
try (Statement stmt = dbConnection.createStatement()) {
// execute the statement
stmt.execute(sql);
} catch (SQLException e) {
logException(e);
return false;
}
}
return true;
}
public boolean trackRanCommandReply(Message message, User user) {
String userId = user.getId();
String guildId;
ChannelType channelType = message.getChannelType();
if (!(channelType.isGuild())) {
guildId = userId;
} else {
guildId = message.getGuild().getId();
}
String channelId = message.getChannel().getId();
String messageId = message.getId();
String query = """
INSERT INTO command_runners
(guild_id, channel_id, message_id, user_id, channel_type) VALUES
(?, ?, ?, ?, ?);
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, guildId);
preparedStatement.setString(2, channelId);
preparedStatement.setString(3, messageId);
preparedStatement.setString(4, userId);
preparedStatement.setString(5, channelType.name());
preparedStatement.executeUpdate();
return true;
} catch (SQLException e) {
logException(e);
}
return false;
}
public boolean isUserTrackedFor(String userId, String messageId) {
String trackedUserId = getTrackedReplyUserId(messageId);
if (trackedUserId == null) return false;
return userId.equals(trackedUserId);
}
public ChannelType getTrackedMessageChannelType(String messageId) {
String query = """
SELECT channel_type
FROM command_runners
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
String channelTypeName = resultSet.getString("channel_type");
return ChannelType.valueOf(channelTypeName);
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public String getTrackedReplyUserId(String messageId) {
String query = """
SELECT user_id
FROM command_runners
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("user_id");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public boolean queueDisabling(Message message) {
String messageId = message.getId();
String channelId = message.getChannel().getId();
String guildId;
ChannelType channelType = message.getChannelType();
if (!(channelType.isGuild())) {
guildId = "PRIVATE";
} else {
guildId = message.getGuild().getId();
}
LocalDateTime expiryTime = LocalDateTime.now().plusSeconds(Cache.getExpiryTimeSeconds());
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Cache.getExpiryTimestampFormat());
String expiryTimeFormatted = dateTimeFormatter.format(expiryTime);
String query = """
INSERT INTO pending_disabled_messages
(guild_id, channel_id, message_id, expiry_timestamp) VALUES
(?, ?, ?, ?);
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, guildId);
preparedStatement.setString(2, channelId);
preparedStatement.setString(3, messageId);
preparedStatement.setString(4, expiryTimeFormatted);
preparedStatement.executeUpdate();
return true;
} catch (SQLException e) {
logException(e);
}
return false;
}
public List<String> getQueuedExpiringMessages() {
List<String> messages = new ArrayList<>();
String query = """
SELECT message_id
FROM pending_disabled_messages;
""";
try (Statement statement = dbConnection.createStatement()) {
ResultSet resultSet = statement.executeQuery(query);
if (resultSet.isClosed()) return messages;
while (resultSet.next()) {
messages.add(resultSet.getString("message_id"));
}
} catch (SQLException e) {
logException(e);
}
return messages;
}
public boolean untrackExpiredMessage(String messageId) {
String query = "DELETE FROM pending_disabled_messages WHERE message_id = ?;";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
preparedStatement.execute();
} catch (SQLException e) {
logException(e);
return false;
}
query = "DELETE FROM command_runners WHERE message_id = ?;";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
preparedStatement.execute();
} catch (SQLException e) {
logException(e);
return false;
}
query = "DELETE FROM urban_dictionary WHERE message_id = ?;";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
preparedStatement.execute();
} catch (SQLException e) {
logException(e);
return false;
}
return true;
}
public String getQueuedExpiringMessageExpiryDate(String messageId) {
String query = """
SELECT expiry_timestamp
FROM pending_disabled_messages
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("expiry_timestamp");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public String getQueuedExpiringMessageChannel(String messageId) {
String query = """
SELECT channel_id
FROM pending_disabled_messages
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("channel_id");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public String getQueuedExpiringMessageGuild(String messageId) {
String query = """
SELECT guild_id
FROM pending_disabled_messages
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("guild_id");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public boolean trackUrban(String meanings, String examples,
String contributors, String dates,
Message message, String term) {
String query = """
INSERT INTO urban_dictionary
(message_id, page, meanings, examples, contributors, dates, term) VALUES
(?, ?, ?, ?, ?, ?, ?);
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, message.getId());
preparedStatement.setInt(2, 0);
preparedStatement.setString(3, meanings);
preparedStatement.setString(4, examples);
preparedStatement.setString(5, contributors);
preparedStatement.setString(6, dates);
preparedStatement.setString(7, term);
preparedStatement.executeUpdate();
return true;
} catch (SQLException e) {
logException(e);
}
return false;
}
public int getUrbanPage(String messageId) {
String query = """
SELECT page
FROM urban_dictionary
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return 0;
while (resultSet.next()) {
return resultSet.getInt("page");
}
} catch (SQLException e) {
logException(e);
}
return 0;
}
public String getUrbanMeanings(String messageId) {
String query = """
SELECT meanings
FROM urban_dictionary
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("meanings");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public String getUrbanExamples(String messageId) {
String query = """
SELECT examples
FROM urban_dictionary
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("examples");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public String getUrbanContributors(String messageId) {
String query = """
SELECT contributors
FROM urban_dictionary
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("contributors");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public String getUrbanDates(String messageId) {
String query = """
SELECT dates
FROM urban_dictionary
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("dates");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public String getUrbanTerm(String messageId) {
String query = """
SELECT term
FROM urban_dictionary
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, messageId);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.isClosed()) return null;
while (resultSet.next()) {
return resultSet.getString("term");
}
} catch (SQLException e) {
logException(e);
}
return null;
}
public boolean setUrbanPage(String messageId, int page) {
String query = """
UPDATE urban_dictionary
SET page = ?
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setInt(1, page);
preparedStatement.setString(2, messageId);
preparedStatement.executeUpdate();
return true;
} catch (SQLException e) {
logException(e);
}
return false;
}
public boolean resetExpiryTimestamp(String messageId) {
LocalDateTime expiryTime = LocalDateTime.now().plusSeconds(Cache.getExpiryTimeSeconds());
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Cache.getExpiryTimestampFormat());
String expiryTimeFormatted = dateTimeFormatter.format(expiryTime);
String query = """
UPDATE pending_disabled_messages
SET expiry_timestamp = ?
WHERE message_id = ?;
""";
try (PreparedStatement preparedStatement = dbConnection.prepareStatement(query)) {
preparedStatement.setString(1, expiryTimeFormatted);
preparedStatement.setString(2, messageId);
preparedStatement.executeUpdate();
return true;
} catch (SQLException e) {
logException(e);
}
return false;
}
}

View File

@@ -25,8 +25,8 @@ public class PropertiesSource
{ {
properties.load(internalPropertiesStream); properties.load(internalPropertiesStream);
} catch (IOException e) }
{ catch (IOException e) {
LOGGER.error(e.getMessage()); LOGGER.error(e.getMessage());
HidekoBot.shutdown(); HidekoBot.shutdown();
return; return;
@@ -34,7 +34,5 @@ public class PropertiesSource
} }
public String getProperty(String property) public String getProperty(String property)
{ { return properties == null ? "" : properties.getProperty(property); }
return properties == null ? "" : properties.getProperty(property);
}
} }

View File

@@ -1,77 +0,0 @@
package wtf.beatrice.hidekobot.entities;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "command_runners")
public class CommandRunner
{
@Id
@Column(name = "message_id", nullable = false)
private String messageId;
@Column(name = "guild_id", nullable = false)
private String guildId;
@Column(name = "channel_id", nullable = false)
private String channelId;
@Column(name = "user_id", nullable = false)
private String userId;
@Column(name = "channel_type", nullable = false)
private String channelType; // store JDA enum name
public String getMessageId()
{
return messageId;
}
public void setMessageId(String messageId)
{
this.messageId = messageId;
}
public String getGuildId()
{
return guildId;
}
public void setGuildId(String guildId)
{
this.guildId = guildId;
}
public String getChannelId()
{
return channelId;
}
public void setChannelId(String channelId)
{
this.channelId = channelId;
}
public String getUserId()
{
return userId;
}
public void setUserId(String userId)
{
this.userId = userId;
}
public String getChannelType()
{
return channelType;
}
public void setChannelType(String channelType)
{
this.channelType = channelType;
}
}

View File

@@ -1,64 +0,0 @@
package wtf.beatrice.hidekobot.entities;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "pending_disabled_messages")
public class PendingDisabledMessage
{
@Id
@Column(name = "message_id", nullable = false)
private String messageId;
@Column(name = "guild_id", nullable = false)
private String guildId;
@Column(name = "channel_id", nullable = false)
private String channelId;
@Column(name = "expiry_timestamp", nullable = false)
private String expiryTimestamp; // keep as String to match your format for now
public String getMessageId()
{
return messageId;
}
public void setMessageId(String messageId)
{
this.messageId = messageId;
}
public String getGuildId()
{
return guildId;
}
public void setGuildId(String guildId)
{
this.guildId = guildId;
}
public String getChannelId()
{
return channelId;
}
public void setChannelId(String channelId)
{
this.channelId = channelId;
}
public String getExpiryTimestamp()
{
return expiryTimestamp;
}
public void setExpiryTimestamp(String expiryTimestamp)
{
this.expiryTimestamp = expiryTimestamp;
}
}

View File

@@ -1,103 +0,0 @@
package wtf.beatrice.hidekobot.entities;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "urban_dictionary")
public class UrbanDictionaryEntry
{
@Id
@Column(name = "message_id", nullable = false)
private String messageId;
@Column(name = "page", nullable = false)
private Integer page;
@Column(name = "meanings", nullable = false, columnDefinition = "TEXT")
private String meanings;
@Column(name = "examples", nullable = false, columnDefinition = "TEXT")
private String examples;
@Column(name = "contributors", nullable = false, columnDefinition = "TEXT")
private String contributors;
@Column(name = "dates", nullable = false, columnDefinition = "TEXT")
private String dates;
@Column(name = "term", nullable = false)
private String term;
public String getMessageId()
{
return messageId;
}
public void setMessageId(String messageId)
{
this.messageId = messageId;
}
public Integer getPage()
{
return page;
}
public void setPage(Integer page)
{
this.page = page;
}
public String getMeanings()
{
return meanings;
}
public void setMeanings(String meanings)
{
this.meanings = meanings;
}
public String getExamples()
{
return examples;
}
public void setExamples(String examples)
{
this.examples = examples;
}
public String getContributors()
{
return contributors;
}
public void setContributors(String contributors)
{
this.contributors = contributors;
}
public String getDates()
{
return dates;
}
public void setDates(String dates)
{
this.dates = dates;
}
public String getTerm()
{
return term;
}
public void setTerm(String term)
{
this.term = term;
}
}

View File

@@ -4,40 +4,27 @@ import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.CoinFlip; import wtf.beatrice.hidekobot.commands.base.CoinFlip;
import wtf.beatrice.hidekobot.commands.base.Trivia; import wtf.beatrice.hidekobot.commands.base.Trivia;
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary; import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
import wtf.beatrice.hidekobot.services.CommandService; import wtf.beatrice.hidekobot.util.CommandUtil;
@Component
public class ButtonInteractionListener extends ListenerAdapter public class ButtonInteractionListener extends ListenerAdapter
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(ButtonInteractionListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(ButtonInteractionListener.class);
private final CommandService commandService;
private final CoinFlip coinFlip;
public ButtonInteractionListener(@Autowired CommandService commandService,
@Autowired CoinFlip coinFlip)
{
this.commandService = commandService;
this.coinFlip = coinFlip;
}
@Override @Override
public void onButtonInteraction(ButtonInteractionEvent event) public void onButtonInteraction(ButtonInteractionEvent event)
{ {
switch (event.getComponentId().toLowerCase()) switch (event.getComponentId().toLowerCase()) {
{
// coinflip // coinflip
case "coinflip_reflip" -> coinFlip.buttonReFlip(event); case "coinflip_reflip" -> CoinFlip.buttonReFlip(event);
// generic dismiss button // generic dismiss button
case "generic_dismiss" -> commandService.deleteUserLinkedMessage(event); case "generic_dismiss" -> CommandUtil.delete(event);
// urban dictionary navigation // urban dictionary navigation
case "urban_nextpage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.NEXT); case "urban_nextpage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.NEXT);

View File

@@ -7,7 +7,6 @@ import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory; import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand; import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -15,7 +14,6 @@ import wtf.beatrice.hidekobot.objects.comparators.MessageCommandAliasesComparato
import java.util.*; import java.util.*;
@Component
public class MessageCommandListener extends ListenerAdapter public class MessageCommandListener extends ListenerAdapter
{ {
@@ -47,9 +45,7 @@ public class MessageCommandListener extends ListenerAdapter
for(String currentAlias : aliases) for(String currentAlias : aliases)
{ {
if(label.equals(currentAlias)) if(label.equals(currentAlias))
{ { return entry.getValue(); }
return entry.getValue();
}
} }
} }
@@ -57,9 +53,7 @@ public class MessageCommandListener extends ListenerAdapter
} }
public LinkedList<MessageCommand> getRegisteredCommands() public LinkedList<MessageCommand> getRegisteredCommands()
{ { return new LinkedList<>(registeredCommands.values()); }
return new LinkedList<>(registeredCommands.values());
}
@Override @Override
public void onMessageReceived(@NotNull MessageReceivedEvent event) public void onMessageReceived(@NotNull MessageReceivedEvent event)
@@ -139,7 +133,8 @@ public class MessageCommandListener extends ListenerAdapter
argsString = argsString.replaceAll("^[\\S]+\\s*", ""); argsString = argsString.replaceAll("^[\\S]+\\s*", "");
// pass all other arguments as a single argument as the first array element // pass all other arguments as a single argument as the first array element
commandArgs = new String[]{argsString}; commandArgs = new String[]{argsString};
} else }
else
{ {
// copy all split arguments to the array, except from the command label // copy all split arguments to the array, except from the command label
commandArgs = Arrays.copyOfRange(argsRaw, 1, argsRaw.length); commandArgs = Arrays.copyOfRange(argsRaw, 1, argsRaw.length);

View File

@@ -8,9 +8,7 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class MessageLogger extends ListenerAdapter public class MessageLogger extends ListenerAdapter
{ {
// this class only gets loaded as a listener if verbosity is set to true on startup. // this class only gets loaded as a listener if verbosity is set to true on startup.
@@ -24,7 +22,7 @@ public class MessageLogger extends ListenerAdapter
public void onMessageReceived(@NotNull MessageReceivedEvent event) public void onMessageReceived(@NotNull MessageReceivedEvent event)
{ {
String toLog = ""; String toLog = "";
String userName = event.getAuthor().getName(); String userName = event.getAuthor().getAsTag();
String message = event.getMessage().getContentDisplay(); String message = event.getMessage().getContentDisplay();
if(event.getChannel() instanceof TextChannel channel) if(event.getChannel() instanceof TextChannel channel)
@@ -35,7 +33,8 @@ public class MessageLogger extends ListenerAdapter
toLog = GUILD_MESSAGE_LOG_FORMAT toLog = GUILD_MESSAGE_LOG_FORMAT
.replace("%guild%", guildName) .replace("%guild%", guildName)
.replace("%channel%", channelName); .replace("%channel%", channelName);
} else if (event.getChannel() instanceof PrivateChannel) }
else if(event.getChannel() instanceof PrivateChannel)
{ {
toLog = DIRECT_MESSAGE_LOG_FORMAT; toLog = DIRECT_MESSAGE_LOG_FORMAT;
} }

View File

@@ -4,10 +4,8 @@ import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionE
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.commands.base.Trivia; import wtf.beatrice.hidekobot.commands.base.Trivia;
@Component
public class SelectMenuInteractionListener extends ListenerAdapter public class SelectMenuInteractionListener extends ListenerAdapter
{ {
@@ -16,8 +14,7 @@ public class SelectMenuInteractionListener extends ListenerAdapter
@Override @Override
public void onStringSelectInteraction(StringSelectInteractionEvent event) public void onStringSelectInteraction(StringSelectInteractionEvent event)
{ {
switch (event.getComponentId().toLowerCase()) switch (event.getComponentId().toLowerCase()) {
{
// trivia // trivia
case "trivia_categories" -> Trivia.handleMenuSelection(event); case "trivia_categories" -> Trivia.handleMenuSelection(event);

View File

@@ -2,13 +2,11 @@ package wtf.beatrice.hidekobot.listeners;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.objects.commands.SlashArgumentsCompleter; import wtf.beatrice.hidekobot.objects.commands.SlashArgumentsCompleter;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.TreeMap; import java.util.TreeMap;
@Component
public class SlashCommandCompletionListener extends ListenerAdapter public class SlashCommandCompletionListener extends ListenerAdapter
{ {
@@ -23,15 +21,10 @@ public class SlashCommandCompletionListener extends ListenerAdapter
} }
public SlashArgumentsCompleter getRegisteredCompleter(String label) public SlashArgumentsCompleter getRegisteredCompleter(String label)
{ { return registeredCompleters.get(label); }
return registeredCompleters.get(label);
}
public LinkedList<SlashArgumentsCompleter> getRegisteredCompleters() public LinkedList<SlashArgumentsCompleter> getRegisteredCompleters()
{ { return new LinkedList<>(registeredCompleters.values()); }
return new LinkedList<>(registeredCompleters.values());
}
@Override @Override
public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent event) public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent event)
{ {

View File

@@ -3,13 +3,11 @@ package wtf.beatrice.hidekobot.listeners;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.objects.commands.SlashCommand; import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.TreeMap; import java.util.TreeMap;
@Component
public class SlashCommandListener extends ListenerAdapter public class SlashCommandListener extends ListenerAdapter
{ {
@@ -23,14 +21,10 @@ public class SlashCommandListener extends ListenerAdapter
} }
public SlashCommand getRegisteredCommand(String label) public SlashCommand getRegisteredCommand(String label)
{ { return registeredCommands.get(label); }
return registeredCommands.get(label);
}
public LinkedList<SlashCommand> getRegisteredCommands() public LinkedList<SlashCommand> getRegisteredCommands()
{ { return new LinkedList<>(registeredCommands.values()); }
return new LinkedList<>(registeredCommands.values());
}
@Override @Override
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event)

View File

@@ -9,12 +9,10 @@ import java.util.Objects;
public record MessageResponse(@Nullable String content, public record MessageResponse(@Nullable String content,
@Nullable MessageEmbed embed, @Nullable MessageEmbed embed,
@Nullable ItemComponent... components) @Nullable ItemComponent... components) {
{
@Override @Override
public boolean equals(Object o) public boolean equals(Object o) {
{
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
MessageResponse response = (MessageResponse) o; MessageResponse response = (MessageResponse) o;
@@ -24,16 +22,14 @@ public record MessageResponse(@Nullable String content,
} }
@Override @Override
public int hashCode() public int hashCode() {
{
int result = Objects.hash(content, embed); int result = Objects.hash(content, embed);
result = 31 * result + Arrays.hashCode(components); result = 31 * result + Arrays.hashCode(components);
return result; return result;
} }
@Override @Override
public String toString() public String toString() {
{
return "MessageResponse{" + return "MessageResponse{" +
"content=" + content + "content=" + content +
", embed=" + embed + ", embed=" + embed +

View File

@@ -9,14 +9,10 @@ public enum CommandCategory
; ;
private String emoji; private String emoji;
CommandCategory(String emoji) CommandCategory(String emoji)
{ {
this.emoji = emoji; this.emoji = emoji;
} }
public String getEmoji() public String getEmoji() { return emoji; }
{
return emoji;
}
} }

View File

@@ -70,7 +70,9 @@ public interface MessageCommand
* *
* @param event the received message event. It should not be used for parsing message contents data as * @param event the received message event. It should not be used for parsing message contents data as
* the arguments already account for it in a better way. * the arguments already account for it in a better way.
*
* @param label the command label that was used, taken from all available command aliases. * @param label the command label that was used, taken from all available command aliases.
*
* @param args a pre-formatted list of arguments, excluding the bot prefix and the command name. * @param args a pre-formatted list of arguments, excluding the bot prefix and the command name.
* This is useful because command logic won't have to change in case the bot prefix is changed, * This is useful because command logic won't have to change in case the bot prefix is changed,
* removed, or we switch to another method of triggering commands (ping, trigger words, ...). * removed, or we switch to another method of triggering commands (ping, trigger words, ...).

View File

@@ -12,7 +12,6 @@ public interface SlashArgumentsCompleter
* @return the command object. * @return the command object.
*/ */
SlashCommand getCommand(); SlashCommand getCommand();
/** /**
* Run the argument-completion logic by parsing the event and replying accordingly. * Run the argument-completion logic by parsing the event and replying accordingly.
* *

View File

@@ -6,16 +6,13 @@ import org.jetbrains.annotations.NotNull;
public class SlashArgumentsCompleterImpl implements SlashArgumentsCompleter public class SlashArgumentsCompleterImpl implements SlashArgumentsCompleter
{ {
private final SlashCommand parentCommand; private final SlashCommand parentCommand;
public SlashArgumentsCompleterImpl(SlashCommand parentCommand) public SlashArgumentsCompleterImpl(SlashCommand parentCommand)
{ {
this.parentCommand = parentCommand; this.parentCommand = parentCommand;
} }
public SlashCommand getCommand() public SlashCommand getCommand()
{ { return parentCommand; }
return parentCommand;
}
public void runCompletion(@NotNull CommandAutoCompleteInteractionEvent event) public void runCompletion(@NotNull CommandAutoCompleteInteractionEvent event)
{ {

View File

@@ -21,7 +21,6 @@ public interface SlashCommand
* @return the command data object. * @return the command data object.
*/ */
CommandData getSlashCommandData(); CommandData getSlashCommandData();
/** /**
* Run the command logic by parsing the event and replying accordingly. * Run the command logic by parsing the event and replying accordingly.
* *

View File

@@ -8,20 +8,17 @@ public class SlashCommandImpl implements SlashCommand
{ {
@Override @Override
public String getCommandName() public String getCommandName() {
{
return getSlashCommandData().getName(); return getSlashCommandData().getName();
} }
@Override @Override
public CommandData getSlashCommandData() public CommandData getSlashCommandData() {
{
return null; return null;
} }
@Override @Override
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) {
{
event.reply("Base command implementation").queue(); event.reply("Base command implementation").queue();
} }
} }

View File

@@ -6,12 +6,10 @@ import java.util.LinkedList;
/** /**
* This class gets two linked lists, and compares their first value alphabetically. * This class gets two linked lists, and compares their first value alphabetically.
*/ */
public class MessageCommandAliasesComparator implements Comparator<LinkedList<String>> public class MessageCommandAliasesComparator implements Comparator<LinkedList<String>> {
{
@Override @Override
public int compare(LinkedList<String> linkedList, LinkedList<String> t1) public int compare(LinkedList<String> linkedList, LinkedList<String> t1) {
{
if(linkedList.isEmpty()) return 0; if(linkedList.isEmpty()) return 0;
if(t1.isEmpty()) return 0; if(t1.isEmpty()) return 0;

View File

@@ -7,12 +7,10 @@ import java.util.Comparator;
/** /**
* This class gets two trivia categories, and compares them by their name. * This class gets two trivia categories, and compares them by their name.
*/ */
public class TriviaCategoryComparator implements Comparator<TriviaCategory> public class TriviaCategoryComparator implements Comparator<TriviaCategory> {
{
@Override @Override
public int compare(TriviaCategory o1, TriviaCategory o2) public int compare(TriviaCategory o1, TriviaCategory o2) {
{
return CharSequence.compare(o1.categoryName(), o2.categoryName()); return CharSequence.compare(o1.categoryName(), o2.categoryName());
} }
} }

View File

@@ -7,12 +7,10 @@ import java.util.Comparator;
/** /**
* This class gets two trivia scores, and compares their score. * This class gets two trivia scores, and compares their score.
*/ */
public class TriviaScoreComparator implements Comparator<TriviaScore> public class TriviaScoreComparator implements Comparator<TriviaScore> {
{
@Override @Override
public int compare(TriviaScore o1, TriviaScore o2) public int compare(TriviaScore o1, TriviaScore o2) {
{
return Integer.compare(o2.getScore(), o1.getScore()); // inverted, because higher number should come first return Integer.compare(o2.getScore(), o1.getScore()); // inverted, because higher number should come first
} }
} }

View File

@@ -1,6 +1,5 @@
package wtf.beatrice.hidekobot.objects.fun; package wtf.beatrice.hidekobot.objects.fun;
public record TriviaCategory(String categoryName, int categoryId) public record TriviaCategory(String categoryName, int categoryId) {
{
} }

View File

@@ -3,7 +3,6 @@ package wtf.beatrice.hidekobot.objects.fun;
import java.util.List; import java.util.List;
public record TriviaQuestion(String question, String correctAnswer, public record TriviaQuestion(String question, String correctAnswer,
List<String> wrongAnswers) List<String> wrongAnswers) {
{
} }

View File

@@ -18,20 +18,14 @@ public class TriviaScore
score += add; score += add;
} }
public int getScore() public int getScore() { return score; }
{
return score;
}
public User getUser() public User getUser() { return user; }
{
return user;
}
@Override @Override
public String toString() public String toString()
{ {
return "[" + user.getName() + "," + score + "]"; return "[" + user.getAsTag() + "," + score + "]";
} }
} }

View File

@@ -1,8 +0,0 @@
package wtf.beatrice.hidekobot.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import wtf.beatrice.hidekobot.entities.CommandRunner;
public interface CommandRunnerRepository extends JpaRepository<CommandRunner, String>
{
}

View File

@@ -1,8 +0,0 @@
package wtf.beatrice.hidekobot.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import wtf.beatrice.hidekobot.entities.PendingDisabledMessage;
public interface PendingDisabledMessageRepository extends JpaRepository<PendingDisabledMessage, String>
{
}

View File

@@ -1,8 +0,0 @@
package wtf.beatrice.hidekobot.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import wtf.beatrice.hidekobot.entities.UrbanDictionaryEntry;
public interface UrbanDictionaryRepository extends JpaRepository<UrbanDictionaryEntry, String>
{
}

View File

@@ -3,38 +3,35 @@ package wtf.beatrice.hidekobot.runnables;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.services.CommandService; import wtf.beatrice.hidekobot.datasources.DatabaseSource;
import wtf.beatrice.hidekobot.services.DatabaseService; import wtf.beatrice.hidekobot.util.CommandUtil;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
public class ExpiredMessageTask implements Runnable public class ExpiredMessageTask implements Runnable {
{
private final DatabaseService databaseService;
private final CommandService commandService;
private final DateTimeFormatter formatter; private final DateTimeFormatter formatter;
private static final Logger LOGGER = LoggerFactory.getLogger(ExpiredMessageTask.class); private static final Logger LOGGER = LoggerFactory.getLogger(ExpiredMessageTask.class);
private DatabaseSource databaseSource;
public ExpiredMessageTask(DatabaseService databaseService, public ExpiredMessageTask()
CommandService commandService)
{ {
this.databaseService = databaseService;
this.commandService = commandService;
String format = Cache.getExpiryTimestampFormat(); String format = Cache.getExpiryTimestampFormat();
formatter = DateTimeFormatter.ofPattern(format); formatter = DateTimeFormatter.ofPattern(format);
databaseSource = Cache.getDatabaseSource();
} }
@Override @Override
public void run() public void run() {
{
List<String> expiringMessages = databaseService.getQueuedExpiringMessages(); databaseSource = Cache.getDatabaseSource();
if(databaseSource == null) return;
List<String> expiringMessages = Cache.getDatabaseSource().getQueuedExpiringMessages();
if(expiringMessages == null || expiringMessages.isEmpty()) return; if(expiringMessages == null || expiringMessages.isEmpty()) return;
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
@@ -44,11 +41,11 @@ public class ExpiredMessageTask implements Runnable
if(Cache.isVerbose()) LOGGER.info("expired check: {}", messageId); if(Cache.isVerbose()) LOGGER.info("expired check: {}", messageId);
String expiryTimestamp = databaseService.getQueuedExpiringMessageExpiryDate(messageId); String expiryTimestamp = databaseSource.getQueuedExpiringMessageExpiryDate(messageId);
if(expiryTimestamp == null || expiryTimestamp.isEmpty()) // if missing timestamp if(expiryTimestamp == null || expiryTimestamp.isEmpty()) // if missing timestamp
{ {
// count it as already expired // count it as already expired
databaseService.untrackExpiredMessage(messageId); databaseSource.untrackExpiredMessage(messageId);
// move on to next message // move on to next message
continue; continue;
} }
@@ -58,7 +55,7 @@ public class ExpiredMessageTask implements Runnable
if(now.isAfter(expiryDate)) if(now.isAfter(expiryDate))
{ {
if(Cache.isVerbose()) LOGGER.info("expired: {}", messageId); if(Cache.isVerbose()) LOGGER.info("expired: {}", messageId);
commandService.disableExpired(messageId); CommandUtil.disableExpired(messageId);
} }
} }

View File

@@ -18,8 +18,7 @@ public class HeartBeatTask implements Runnable
String urlString = Cache.getFullHeartBeatLink(); String urlString = Cache.getFullHeartBeatLink();
if(urlString == null || urlString.isEmpty()) return; if(urlString == null || urlString.isEmpty()) return;
try try {
{
URL heartbeatUrl = new URL(urlString); URL heartbeatUrl = new URL(urlString);
@@ -33,13 +32,13 @@ public class HeartBeatTask implements Runnable
{ {
// only log ok response codes when verbosity is enabled // only log ok response codes when verbosity is enabled
if(Cache.isVerbose()) LOGGER.info("Heartbeat response code: {}", responseCode); if(Cache.isVerbose()) LOGGER.info("Heartbeat response code: {}", responseCode);
} else }
else
{ {
LOGGER.error("Heartbeat returned problematic response code: {}", responseCode); LOGGER.error("Heartbeat returned problematic response code: {}", responseCode);
} }
} catch (IOException e) } catch (IOException e) {
{
LOGGER.error("Error while trying to push heartbeat", e); LOGGER.error("Error while trying to push heartbeat", e);
} }

View File

@@ -22,8 +22,7 @@ public class StatusUpdateTask implements Runnable
); );
@Override @Override
public void run() public void run() {
{
int randomPos = RandomUtil.getRandomNumber(0, statuses.size() - 1); int randomPos = RandomUtil.getRandomNumber(0, statuses.size() - 1);
String status = statuses.get(randomPos) + " | " + Cache.getBotPrefix() + " help"; String status = statuses.get(randomPos) + " | " + Cache.getBotPrefix() + " help";
HidekoBot.getAPI().getPresence().setActivity(Activity.playing(status)); HidekoBot.getAPI().getPresence().setActivity(Activity.playing(status));

View File

@@ -13,18 +13,13 @@ import wtf.beatrice.hidekobot.objects.comparators.TriviaScoreComparator;
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory; import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion; import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
import wtf.beatrice.hidekobot.objects.fun.TriviaScore; import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
import wtf.beatrice.hidekobot.services.CommandService; import wtf.beatrice.hidekobot.util.CommandUtil;
import wtf.beatrice.hidekobot.services.DatabaseService;
import java.util.*; import java.util.*;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
public class TriviaTask implements Runnable public class TriviaTask implements Runnable
{ {
private final DatabaseService databaseService;
private final CommandService commandService;
private final User author; private final User author;
private final MessageChannel channel; private final MessageChannel channel;
@@ -38,17 +33,11 @@ public class TriviaTask implements Runnable
private int iteration = 0; private int iteration = 0;
public TriviaTask(User author, public TriviaTask(User author, MessageChannel channel, TriviaCategory category)
MessageChannel channel,
TriviaCategory category,
DatabaseService databaseService,
CommandService commandService)
{ {
this.author = author; this.author = author;
this.channel = channel; this.channel = channel;
this.category = category; this.category = category;
this.databaseService = databaseService;
this.commandService = commandService;
triviaJson = Trivia.fetchJson(Trivia.getTriviaLink(category.categoryId())); triviaJson = Trivia.fetchJson(Trivia.getTriviaLink(category.categoryId()));
questions = Trivia.parseQuestions(triviaJson); //todo: null check, rate limiting... questions = Trivia.parseQuestions(triviaJson); //todo: null check, rate limiting...
@@ -66,7 +55,7 @@ public class TriviaTask implements Runnable
if(previousMessage != null) if(previousMessage != null)
{ {
// todo: we shouldn't use this method, since it messes with the database... look at coin reflip // todo: we shouldn't use this method, since it messes with the database... look at coin reflip
commandService.disableExpired(previousMessage.getId()); CommandUtil.disableExpired(previousMessage.getId());
String previousCorrectAnswer = questions.get(iteration-1).correctAnswer(); String previousCorrectAnswer = questions.get(iteration-1).correctAnswer();
@@ -104,14 +93,12 @@ public class TriviaTask implements Runnable
previousScore = score; previousScore = score;
topScore = score; topScore = score;
pos = 1; pos = 1;
} else } else {
{
if(score != previousScore) pos++; if(score != previousScore) pos++;
} }
if(pos == 1) winners.add(user); if(pos == 1) winners.add(user);
else else {
{
othersBuilder.append("\n").append(pos) othersBuilder.append("\n").append(pos)
.append(" | ").append(user) .append(" | ").append(user)
.append(": ").append(score).append(" points"); .append(": ").append(score).append(" points");
@@ -126,8 +113,7 @@ public class TriviaTask implements Runnable
if(i + 1 != winners.size()) if(i + 1 != winners.size())
{ {
winnersBuilder.append(", "); // separate with comma except on last run winnersBuilder.append(", "); // separate with comma except on last run
} else } else {
{
winnersBuilder.append(": ").append(topScore).append(" points \uD83C\uDF89"); winnersBuilder.append(": ").append(topScore).append(" points \uD83C\uDF89");
} }
} }
@@ -204,12 +190,12 @@ public class TriviaTask implements Runnable
.complete(); .complete();
databaseService.trackRanCommandReply(previousMessage, author); Cache.getDatabaseSource().trackRanCommandReply(previousMessage, author);
// todo: ^ we should get rid of this tracking, since we don't need to know who started the trivia. // todo: ^ we should get rid of this tracking, since we don't need to know who started the trivia.
// todo: however, for now, that's the only way to avoid a thread-locking scenario as some data is // todo: however, for now, that's the only way to avoid a thread-locking scenario as some data is
// todo: only stored in that table. this should be solved when we merge / fix the two main tables. // todo: only stored in that table. this should be solved when we merge / fix the two main tables.
// todo: then, we can remove this instruction. // todo: then, we can remove this instruction.
databaseService.queueDisabling(previousMessage); Cache.getDatabaseSource().queueDisabling(previousMessage);
iteration++; iteration++;
} }

View File

@@ -1,183 +0,0 @@
package wtf.beatrice.hidekobot.services;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.entities.CommandRunner;
import wtf.beatrice.hidekobot.entities.PendingDisabledMessage;
import wtf.beatrice.hidekobot.entities.UrbanDictionaryEntry;
import wtf.beatrice.hidekobot.repositories.CommandRunnerRepository;
import wtf.beatrice.hidekobot.repositories.PendingDisabledMessageRepository;
import wtf.beatrice.hidekobot.repositories.UrbanDictionaryRepository;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Service
@Transactional
public class DatabaseService
{
private final PendingDisabledMessageRepository pendingRepo;
private final CommandRunnerRepository runnerRepo;
private final UrbanDictionaryRepository urbanRepo;
public DatabaseService(PendingDisabledMessageRepository p, CommandRunnerRepository c, UrbanDictionaryRepository u)
{
this.pendingRepo = p;
this.runnerRepo = c;
this.urbanRepo = u;
}
// trackRanCommandReply
public void trackRanCommandReply(Message message, User user)
{
String userId = user.getId();
String guildId = message.getChannelType().isGuild() ? message.getGuild().getId() : userId;
CommandRunner row = new CommandRunner();
row.setMessageId(message.getId());
row.setGuildId(guildId);
row.setChannelId(message.getChannel().getId());
row.setUserId(userId);
row.setChannelType(message.getChannelType().name());
runnerRepo.save(row);
}
public boolean isUserTrackedFor(String userId, String messageId)
{
return runnerRepo.findById(messageId)
.map(r -> userId.equals(r.getUserId()))
.orElse(false);
}
public ChannelType getTrackedMessageChannelType(String messageId)
{
return runnerRepo.findById(messageId)
.map(r -> ChannelType.valueOf(r.getChannelType()))
.orElse(null);
}
public String getTrackedReplyUserId(String messageId)
{
return runnerRepo.findById(messageId)
.map(CommandRunner::getUserId)
.orElse(null);
}
public void queueDisabling(Message message)
{
String guildId = message.getChannelType().isGuild() ? message.getGuild().getId() : "PRIVATE";
LocalDateTime expiry = LocalDateTime.now().plusSeconds(Cache.getExpiryTimeSeconds());
String formatted = DateTimeFormatter.ofPattern(Cache.getExpiryTimestampFormat()).format(expiry);
PendingDisabledMessage row = new PendingDisabledMessage();
row.setMessageId(message.getId());
row.setChannelId(message.getChannel().getId());
row.setGuildId(guildId);
row.setExpiryTimestamp(formatted);
pendingRepo.save(row);
}
public List<String> getQueuedExpiringMessages()
{
return pendingRepo.findAll()
.stream()
.map(PendingDisabledMessage::getMessageId)
.toList();
}
public void untrackExpiredMessage(String messageId)
{
pendingRepo.deleteById(messageId);
runnerRepo.deleteById(messageId);
urbanRepo.deleteById(messageId);
}
public String getQueuedExpiringMessageExpiryDate(String messageId)
{
return pendingRepo.findById(messageId).map(PendingDisabledMessage::getExpiryTimestamp).orElse(null);
}
public String getQueuedExpiringMessageChannel(String messageId)
{
return pendingRepo.findById(messageId).map(PendingDisabledMessage::getChannelId).orElse(null);
}
public String getQueuedExpiringMessageGuild(String messageId)
{
return pendingRepo.findById(messageId).map(PendingDisabledMessage::getGuildId).orElse(null);
}
public void trackUrban(String meanings, String examples, String contributors, String dates, Message message, String term)
{
UrbanDictionaryEntry e = new UrbanDictionaryEntry();
e.setMessageId(message.getId());
e.setPage(0);
e.setMeanings(meanings);
e.setExamples(examples);
e.setContributors(contributors);
e.setDates(dates);
e.setTerm(term);
urbanRepo.save(e);
}
public int getUrbanPage(String messageId)
{
return urbanRepo.findById(messageId)
.map(UrbanDictionaryEntry::getPage)
.orElse(0);
}
public String getUrbanMeanings(String messageId)
{
return urbanRepo.findById(messageId).map(UrbanDictionaryEntry::getMeanings).orElse(null);
}
public String getUrbanExamples(String messageId)
{
return urbanRepo.findById(messageId).map(UrbanDictionaryEntry::getExamples).orElse(null);
}
public String getUrbanContributors(String messageId)
{
return urbanRepo.findById(messageId).map(UrbanDictionaryEntry::getContributors).orElse(null);
}
public String getUrbanDates(String messageId)
{
return urbanRepo.findById(messageId).map(UrbanDictionaryEntry::getDates).orElse(null);
}
public String getUrbanTerm(String messageId)
{
return urbanRepo.findById(messageId).map(UrbanDictionaryEntry::getTerm).orElse(null);
}
public void setUrbanPage(String messageId, int page)
{
urbanRepo.findById(messageId).ifPresent(e -> {
e.setPage(page);
urbanRepo.save(e);
});
}
public void resetExpiryTimestamp(String messageId)
{
pendingRepo.findById(messageId).ifPresent(row -> {
String formatted = DateTimeFormatter
.ofPattern(Cache.getExpiryTimestampFormat())
.format(LocalDateTime.now().plusSeconds(Cache.getExpiryTimeSeconds()));
row.setExpiryTimestamp(formatted);
pendingRepo.save(row);
});
}
}

View File

@@ -1,4 +1,4 @@
package wtf.beatrice.hidekobot.services; package wtf.beatrice.hidekobot.util;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
@@ -14,26 +14,21 @@ import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.RestAction;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot; import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
import wtf.beatrice.hidekobot.objects.commands.SlashCommand; import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@Component public class CommandUtil
public class CommandService
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(CommandService.class); private static final Logger LOGGER = LoggerFactory.getLogger(CommandUtil.class);
private final DatabaseService databaseService; private CommandUtil() {
throw new IllegalStateException("Utility class");
public CommandService(@Autowired DatabaseService databaseService)
{
this.databaseService = databaseService;
} }
/** /**
@@ -42,37 +37,17 @@ public class CommandService
* *
* @param event the button interaction event. * @param event the button interaction event.
*/ */
public void deleteUserLinkedMessage(ButtonInteractionEvent event) public static void delete(ButtonInteractionEvent event)
{ {
// check if the user interacting is the same one who ran the command // check if the user interacting is the same one who ran the command
if (!databaseService.isUserTrackedFor(event.getUser().getId(), event.getMessageId())) if (!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId()))) {
{
event.reply("❌ You did not run this command!").setEphemeral(true).queue(); event.reply("❌ You did not run this command!").setEphemeral(true).queue();
return; return;
} }
// Acknowledge immediately so the interaction token stays valid // delete the message
event.deferEdit().queue(hook -> { event.getInteraction().getMessage().delete().queue();
// Try deleting via the interaction webhook (works for original interaction responses) // no need to manually untrack it from database, it will be purged on the next planned check.
hook.deleteOriginal().queue(
success -> { /* optional: databaseService.untrackExpiredMessage(event.getMessageId()); */ },
failure -> {
// Fallback to channel delete (works even if webhook token expired)
event.getChannel().deleteMessageById(event.getMessageId()).queue(
null,
__ -> { /* ignore if already deleted */ }
);
}
);
},
failure -> {
// If we failed to acknowledge (interaction already expired), try channel delete anyway
event.getChannel().deleteMessageById(event.getMessageId()).queue(
null,
__ -> { /* ignore if already deleted */ }
);
}
);
} }
@@ -83,15 +58,13 @@ public class CommandService
* *
* @param force a boolean specifying if the update should be forced even if no differences were found. * @param force a boolean specifying if the update should be forced even if no differences were found.
*/ */
public void updateSlashCommands(boolean force) public static void updateSlashCommands(boolean force)
{ {
// populate commands list from registered commands // populate commands list from registered commands
List<CommandData> allCommands = new ArrayList<>(); List<CommandData> allCommands = new ArrayList<>();
for(SlashCommand cmd : Cache.getSlashCommandListener().getRegisteredCommands()) for(SlashCommand cmd : Cache.getSlashCommandListener().getRegisteredCommands())
{ { allCommands.add(cmd.getSlashCommandData()); }
allCommands.add(cmd.getSlashCommandData());
}
JDA jdaInstance = HidekoBot.getAPI(); JDA jdaInstance = HidekoBot.getAPI();
@@ -183,46 +156,49 @@ public class CommandService
* *
* @param messageId the message id to disable. * @param messageId the message id to disable.
*/ */
public void disableExpired(String messageId) public static void disableExpired(String messageId)
{ {
String channelId = databaseService.getQueuedExpiringMessageChannel(messageId); DatabaseSource databaseSource = Cache.getDatabaseSource();
String channelId = databaseSource.getQueuedExpiringMessageChannel(messageId);
// todo: warning, the following method + related if check are thread-locking. // todo: warning, the following method + related if check are thread-locking.
// todo: we should probably merge the two tables somehow, since they have redundant information. // todo: we should probably merge the two tables somehow, since they have redundant information.
ChannelType msgChannelType = databaseService.getTrackedMessageChannelType(messageId); ChannelType msgChannelType = databaseSource.getTrackedMessageChannelType(messageId);
MessageChannel textChannel = null; MessageChannel textChannel = null;
// this should never happen, but only message channels are supported. // this should never happen, but only message channels are supported.
if(!msgChannelType.isMessage()) if(!msgChannelType.isMessage())
{ {
databaseService.untrackExpiredMessage(messageId); databaseSource.untrackExpiredMessage(messageId);
return; return;
} }
// if this is a DM // if this is a DM
if(!(msgChannelType.isGuild())) if(!(msgChannelType.isGuild()))
{ {
String userId = databaseService.getTrackedReplyUserId(messageId); String userId = databaseSource.getTrackedReplyUserId(messageId);
User user = userId == null ? null : HidekoBot.getAPI().retrieveUserById(userId).complete(); User user = userId == null ? null : HidekoBot.getAPI().retrieveUserById(userId).complete();
if(user == null) if(user == null)
{ {
// if user is not found, consider it expired // if user is not found, consider it expired
// (deleted profile, or blocked the bot) // (deleted profile, or blocked the bot)
databaseService.untrackExpiredMessage(messageId); databaseSource.untrackExpiredMessage(messageId);
return; return;
} }
textChannel = user.openPrivateChannel().complete(); textChannel = user.openPrivateChannel().complete();
} else }
else
{ {
String guildId = databaseService.getQueuedExpiringMessageGuild(messageId); String guildId = databaseSource.getQueuedExpiringMessageGuild(messageId);
Guild guild = guildId == null ? null : HidekoBot.getAPI().getGuildById(guildId); Guild guild = guildId == null ? null : HidekoBot.getAPI().getGuildById(guildId);
if(guild == null) if(guild == null)
{ {
// if guild is not found, consider it expired // if guild is not found, consider it expired
// (server was deleted or bot was kicked) // (server was deleted or bot was kicked)
databaseService.untrackExpiredMessage(messageId); databaseSource.untrackExpiredMessage(messageId);
return; return;
} }
textChannel = guild.getTextChannelById(channelId); textChannel = guild.getTextChannelById(channelId);
@@ -232,7 +208,7 @@ public class CommandService
{ {
// if channel is not found, count it as expired // if channel is not found, count it as expired
// (channel was deleted or bot permissions restricted) // (channel was deleted or bot permissions restricted)
databaseService.untrackExpiredMessage(messageId); databaseSource.untrackExpiredMessage(messageId);
return; return;
} }
@@ -245,7 +221,7 @@ public class CommandService
message -> { message -> {
if(message == null) if(message == null)
{ {
databaseService.untrackExpiredMessage(messageId); databaseSource.untrackExpiredMessage(messageId);
return; return;
} }
@@ -258,9 +234,9 @@ public class CommandService
} }
message.editMessageComponents(newComponents).queue(); message.editMessageComponents(newComponents).queue();
databaseService.untrackExpiredMessage(messageId); databaseSource.untrackExpiredMessage(messageId);
}, },
error -> databaseService.untrackExpiredMessage(messageId)); error -> databaseSource.untrackExpiredMessage(messageId));
} }
} }

View File

@@ -12,8 +12,7 @@ import java.util.Arrays;
public class FormatUtil public class FormatUtil
{ {
private FormatUtil() private FormatUtil() {
{
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
@@ -61,25 +60,21 @@ public class FormatUtil
* *
* @return the formatted String * @return the formatted String
*/ */
public static String getNiceDuration(Duration duration) public static String getNiceDuration(Duration duration) {
{
long days = duration.toDays(); long days = duration.toDays();
long hours = duration.toHoursPart(); long hours = duration.toHoursPart();
long minutes = duration.toMinutesPart(); long minutes = duration.toMinutesPart();
long seconds = duration.toSecondsPart(); long seconds = duration.toSecondsPart();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (days > 0) if (days > 0) {
{
sb.append(days).append("d "); sb.append(days).append("d ");
sb.append(hours).append("h "); sb.append(hours).append("h ");
sb.append(minutes).append("m "); sb.append(minutes).append("m ");
} else if (hours > 0) } else if (hours > 0) {
{
sb.append(hours).append("h "); sb.append(hours).append("h ");
sb.append(minutes).append("m "); sb.append(minutes).append("m ");
} else if (minutes > 0) } else if (minutes > 0) {
{
sb.append(minutes).append("m "); sb.append(minutes).append("m ");
} }
sb.append(seconds).append("s"); sb.append(seconds).append("s");

View File

@@ -0,0 +1,70 @@
package wtf.beatrice.hidekobot.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Deprecated(since = "0.5.16", forRemoval = true)
public class Logger<T>
{
// objects that we need to have for a properly formatted message
private final String className;
private final String format = "[%date% %time%] [%class%] %message%";
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
// when initializing a new logger, save variables in that instance
public Logger(Class<T> logClass)
{
className = logClass.getSimpleName();
}
/**
* Logs a message to console, following a specific format.
*
* @param message the message to log
*/
public void log(String message)
{
LocalDateTime now = LocalDateTime.now();
String currentDate = dateFormatter.format(now);
String currentTime = timeFormatter.format(now);
logRaw(format
.replace("%date%", currentDate)
.replace("%time%", currentTime)
.replace("%class%", className)
.replace("%message%", message));
}
/**
* Logs a message to console, after delaying it.
*
* @param message the message to log
* @param delay the time to wait before logging, in seconds
*/
public void log(String message, int delay)
{
// create a new scheduled executor with an anonymous runnable...
//... after waiting <delay> seconds.
try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor()) {
executor.schedule(() -> log(message), delay, TimeUnit.SECONDS);
}
}
/**
* Prints a message to console without any formatting.
*
* @param message the message to log
*/
public void logRaw(String message)
{
System.out.println(message);
}
}

View File

@@ -10,8 +10,7 @@ import java.util.Random;
public class RandomUtil public class RandomUtil
{ {
private RandomUtil() private RandomUtil() {
{
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
@@ -48,8 +47,7 @@ public class RandomUtil
} }
public static Random getRandom() public static Random getRandom() {
{
return randomInstance; return randomInstance;
} }

View File

@@ -10,38 +10,33 @@ import java.util.List;
public class SerializationUtil public class SerializationUtil
{ {
private SerializationUtil() private SerializationUtil() {
{
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
public static <T> String serializeBase64(List<T> dataList) public static <T> String serializeBase64(List<T> dataList) {
{
try (ByteArrayOutputStream bo = new ByteArrayOutputStream(); try (ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream so = new ObjectOutputStream(bo)) ObjectOutputStream so = new ObjectOutputStream(bo)) {
{
so.writeObject(dataList); so.writeObject(dataList);
so.flush(); so.flush();
return Base64.getEncoder().encodeToString(bo.toByteArray()); return Base64.getEncoder().encodeToString(bo.toByteArray());
} catch (IOException e) }
{ catch (IOException e) {
throw new SerializationException("Error during serialization", e); throw new SerializationException("Error during serialization", e);
} }
} }
public static <T> LinkedList<T> deserializeBase64(String dataStr) public static <T> LinkedList<T> deserializeBase64(String dataStr) {
{
byte[] b = Base64.getDecoder().decode(dataStr); byte[] b = Base64.getDecoder().decode(dataStr);
ByteArrayInputStream bi = new ByteArrayInputStream(b); ByteArrayInputStream bi = new ByteArrayInputStream(b);
ObjectInputStream si; ObjectInputStream si;
try try {
{
si = new ObjectInputStream(bi); si = new ObjectInputStream(bi);
return LinkedList.class.cast(si.readObject()); return LinkedList.class.cast(si.readObject());
} catch (IOException | ClassNotFoundException e) }
{ catch (IOException | ClassNotFoundException e) {
throw new SerializationException("Error during deserialization", e); throw new SerializationException("Error during deserialization", e);
} }
} }

View File

@@ -1,8 +0,0 @@
package wtf.beatrice.hidekobot.util;
import wtf.beatrice.hidekobot.services.CommandService;
import wtf.beatrice.hidekobot.services.DatabaseService;
public record Services(CommandService commandService, DatabaseService databaseService)
{
}

View File

@@ -1,8 +0,0 @@
spring.datasource.url=jdbc:sqlite:${APP_HOME}/db.sqlite
spring.datasource.driver-class-name=org.sqlite.JDBC
# let Hibernate create/update tables for you during the migration
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
# optional logging while migrating
#spring.jpa.show-sql=true
#spring.jpa.properties.hibernate.format_sql=true

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<!--
<suppress> <suppress>
<notes><![CDATA[ <notes><![CDATA[
file name: snakeyaml-1.33.jar file name: snakeyaml-1.33.jar
@@ -8,5 +7,25 @@
<packageUrl regex="true">^pkg:maven/org\.yaml/snakeyaml@.*$</packageUrl> <packageUrl regex="true">^pkg:maven/org\.yaml/snakeyaml@.*$</packageUrl>
<cve>CVE-2021-4235</cve> <cve>CVE-2021-4235</cve>
</suppress> </suppress>
--> <suppress>
<notes><![CDATA[
file name: snakeyaml-1.33.jar
]]></notes>
<packageUrl regex="true">^pkg:maven/org\.yaml/snakeyaml@.*$</packageUrl>
<cve>CVE-2022-3064</cve>
</suppress>
<suppress>
<notes><![CDATA[
file name: snakeyaml-1.33.jar
]]></notes>
<packageUrl regex="true">^pkg:maven/org\.yaml/snakeyaml@.*$</packageUrl>
<vulnerabilityName>CVE-2022-1471</vulnerabilityName>
</suppress>
<suppress>
<notes><![CDATA[
file name: json-20220924.jar
]]></notes>
<packageUrl regex="true">^pkg:maven/org\.json/json@.*$</packageUrl>
<vulnerabilityName>CVE-2022-45688</vulnerabilityName>
</suppress>
</suppressions> </suppressions>