Compare commits
294 Commits
b318b9f22b
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e17e55341 | |||
| 1f2bafa7f8 | |||
| eafa80f1d2 | |||
| 5ad1d9e891 | |||
| dcc4785edc | |||
| a1192db62b | |||
| 910b7a406c | |||
| eb953390e3 | |||
| 5d8fc55276 | |||
| 784ce11d6b | |||
| 799891ad6b | |||
| 0c575378f3 | |||
| 5015e82700 | |||
| 29f486566b | |||
| f80a49995b | |||
| bbd0299103 | |||
| fd2970fa59 | |||
| bb674240bb | |||
| 5a34e5994e | |||
| 14b54501fd | |||
| 1928cfe858 | |||
| 0b7880af88 | |||
| f1969c2043 | |||
| acad4bad8b | |||
| 4ce9acd428 | |||
| 2ab52bd713 | |||
| f38e34c1ce | |||
| f87854459f | |||
| 72c4ae2133 | |||
| 7f4ca6aa8e | |||
| c3793aa159 | |||
| 63fc1feaea | |||
| 71d646ff69 | |||
| 48d537d2db | |||
| 136447b9df | |||
| 372949a9e0 | |||
| 4f4549a81e | |||
| b0dd1c21b2 | |||
| d46368f0ce | |||
| 2032dc1d0e | |||
| 9c099230c9 | |||
| f2134cbdb9 | |||
| 97e846c3dc | |||
| 7f16a011b3 | |||
| b32ece3f88 | |||
| 681785ef0d | |||
| a72b6f690b | |||
| 645fb5f4a6 | |||
| a228067cce | |||
| 4f0eb7ce74 | |||
| a7e36299b6 | |||
| 1e7c43e360 | |||
| 2a4a80cc3f | |||
| a50e8c050b | |||
| f7c1b096bc | |||
| 884814064c | |||
| ce0938bc2c | |||
| aabfbd3020 | |||
| 25bc5a3ef2 | |||
| ab52f30eb2 | |||
| 2e0c2e4e14 | |||
| edf896efb0 | |||
| bd7355add9 | |||
| 6d210551af | |||
| abd3c02be6 | |||
| 50749f2108 | |||
| fc7b6d54d1 | |||
| f5d684c5a4 | |||
| 5ea12aa693 | |||
| 5c4f7e4252 | |||
| d9dcde7560 | |||
| 64762a9a4f | |||
| 4542985431 | |||
| 88d4b6461b | |||
| e8f1c85f08 | |||
| 1ed389c18b | |||
| 4320c9698a | |||
| 3a8044dda1 | |||
| 96ca58de12 | |||
| 5ed444ab92 | |||
| 19a5583594 | |||
| a39bcf68cb | |||
| eb08f0bcab | |||
| 87eadb88ef | |||
| 4f74541d1d | |||
| 86f370643e | |||
| 27133b8e3c | |||
| 54fee8a010 | |||
| 9e3289c616 | |||
| a5d647e6ba | |||
| 44add27d5e | |||
| fa17bf8ef6 | |||
| 90e576923d | |||
| ea31746442 | |||
| 3c522149c3 | |||
| ed1b25b403 | |||
| 04d93dd7a5 | |||
| f523e6cd92 | |||
| 35f52ec583 | |||
| 549f8bb48b | |||
| be9bd1a068 | |||
| 2a7be8868c | |||
| e51646ace4 | |||
| 4abd3d6179 | |||
| 0f2e2f876d | |||
| 06b28aac70 | |||
| 9f52e8747c | |||
| 829e19fac3 | |||
| ff323b9d8b | |||
| 5aa99ae4bf | |||
| 96465af441 | |||
| f4fc8811ef | |||
| 3abb48ba60 | |||
| d58cc08082 | |||
| 1675b62967 | |||
| cc29b63d78 | |||
| d816a1f1d9 | |||
| d788070eb8 | |||
| 627f6deb97 | |||
| 668375367a | |||
| 980cf5eef3 | |||
| ccf69a2903 | |||
| 6c6cdab9f4 | |||
| 05efe6c0d3 | |||
| 4f615378a6 | |||
| 118979bde4 | |||
| 009fec3be3 | |||
| 546637c188 | |||
| d315b3f38a | |||
| 94037b252f | |||
| d62d6bdfdd | |||
| 7f73d4fb23 | |||
| 383b09a53e | |||
| c9d69c512c | |||
| 8b1c1b4d04 | |||
| a28781b806 | |||
| 78bdadad06 | |||
| a5b9f9d993 | |||
| 64f0b611ca | |||
| f87d461b89 | |||
| 4f71b5f599 | |||
| 12e3c5fc2f | |||
| bec705ea6c | |||
| 3acab18ff2 | |||
| 99a92badc2 | |||
| f24c93acc0 | |||
| 3cc37b4669 | |||
| 5a74401f74 | |||
| db151a3f66 | |||
| c1e995225f | |||
| ae1313459d | |||
| ea29241d1d | |||
| ecff8ee31b | |||
| 9578fcdbc2 | |||
| 944b4cf905 | |||
| 21a5e07b3e | |||
| 32a5a46565 | |||
| 5dcfd7339e | |||
| 3bb006e0c6 | |||
| 0cc30ddf29 | |||
| b55a27fdfb | |||
| ee4c5155fa | |||
| d2abeb35fc | |||
| 546eb49144 | |||
| 6b97e0bb7e | |||
| 4df2429b09 | |||
| 7de23d8207 | |||
| d7aa5d75eb | |||
| b81a7e65d2 | |||
| 14d2505dac | |||
| 528940a9d1 | |||
| 4c653fc93c | |||
| 6bbaf3fe7e | |||
| 95b4f81235 | |||
| dee00e6814 | |||
| d6ef0da167 | |||
| fb752fb9a9 | |||
| 0d862da9ec | |||
| 57a5972d59 | |||
| 374f979ae3 | |||
| 53abab6bf3 | |||
| 5591b8abab | |||
| 5afb398299 | |||
| 0d8c3e2be3 | |||
| 818a25346b | |||
| 28286f5389 | |||
|
|
b174c581e9 | ||
| c843d2cd61 | |||
| 3bd09d2867 | |||
| c077b92f93 | |||
| ecff77867c | |||
| 5e4e79d505 | |||
| e1c22ac059 | |||
| 3676e9d5ad | |||
| 5acd900857 | |||
| d2f00c781e | |||
| 35b2c8fb42 | |||
| 420666eab1 | |||
| 52fe279f35 | |||
| 7ac72f9e38 | |||
| 00c46c1396 | |||
| 7f7ada9b9e | |||
| 47bd16fd13 | |||
| f5a8c2af48 | |||
| 50196bb8f9 | |||
| 26f1c880ea | |||
| 09c53c07a1 | |||
| 1c19f3c07f | |||
| d4c3afbddd | |||
| 96f298b654 | |||
| 48fdb32e15 | |||
| eb93362d16 | |||
| 9f147cee65 | |||
| b0d234a454 | |||
| c9082e84cc | |||
| 0be4389448 | |||
| c1059bb937 | |||
| 78f62b5f8d | |||
| 3a8a44adf0 | |||
| cfa7aef333 | |||
| 0e256e4cb5 | |||
| b0622f36aa | |||
| e451f59199 | |||
| f3cc9a2d75 | |||
| 8f5c29aa95 | |||
| df1e2426e3 | |||
| 91261f04e5 | |||
| bc0463dd38 | |||
| 161c91b45d | |||
| d09c59996b | |||
| 1c82d2b53b | |||
| 174b78704f | |||
| ba64c02049 | |||
| 264a94fe0d | |||
| 9e1888611a | |||
| b1b62bab9f | |||
| de34caa513 | |||
| 215e597a4d | |||
| 217022faca | |||
| 34c100acde | |||
| 496304c2c3 | |||
| e08fefbda3 | |||
| 6c4d362ca4 | |||
| 0e1a7d3459 | |||
| e2c84f62c3 | |||
| b20fb73371 | |||
| 61745c36d0 | |||
| 480b8b5eda | |||
| f9fe12a248 | |||
| 28d7ff18ba | |||
| 1644a4b07d | |||
| 68dceaff13 | |||
| 7dcdf9dbde | |||
| c4d81fb0e4 | |||
| 24a55e14fd | |||
| cce57b8108 | |||
| fc846fa901 | |||
| 4476dd2f7b | |||
| 2d7cadea02 | |||
| b4c80fe56a | |||
| 60ee5f2ae2 | |||
| 8ca70dac78 | |||
| d412801758 | |||
| 4ef42ffa9e | |||
| 0f54211ecd | |||
| d5664eb646 | |||
| 1421d52598 | |||
| 639c54bc52 | |||
| 28c0f1d750 | |||
| 3259a49ace | |||
| 4d888d68b9 | |||
| 7959044335 | |||
| fb3c08fc41 | |||
| 09ec600234 | |||
| c9ff329cbb | |||
| 8f4f341aab | |||
| a030821197 | |||
| e531eef1d6 | |||
| b033763704 | |||
| 982902fc6d | |||
| 00441f089f | |||
| 1f6f23e917 | |||
| 1e07ede83e | |||
| 1a8409994c | |||
| b0a1381589 | |||
| 9504921f27 | |||
| c9528848bc | |||
| 2d1f6699ba | |||
| 595e81e02c | |||
| 764ff23010 | |||
| 32ea099690 | |||
| 1410e4e8af | |||
| 81e621ec1a | |||
| c486630adb |
26
.drone.yml
26
.drone.yml
@@ -1,14 +1,14 @@
|
|||||||
kind: pipeline
|
kind: template
|
||||||
name: default
|
load: java-build-deploy.yaml
|
||||||
|
data:
|
||||||
trigger:
|
arch: arm64
|
||||||
branch:
|
os: linux
|
||||||
|
build_branches:
|
||||||
- main
|
- main
|
||||||
|
- develop
|
||||||
steps:
|
build_events:
|
||||||
- name: build
|
- push
|
||||||
image: maven:3-eclipse-temurin-16
|
- pull_request
|
||||||
commands:
|
sonar_project_key: HidekoBot
|
||||||
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
|
deploy_targets:
|
||||||
- mvn javadoc:javadoc
|
- production
|
||||||
- mvn test -B
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
target/
|
target/
|
||||||
.idea/
|
.idea/
|
||||||
|
scripts/
|
||||||
*.sqlite
|
*.sqlite
|
||||||
88
.pipelines/java-build-deploy.yaml
Normal file
88
.pipelines/java-build-deploy.yaml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
29
README.MD
29
README.MD
@@ -1,7 +1,16 @@
|
|||||||
# HidekoBot
|
# HidekoBot
|
||||||
|
[](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
|
||||||
|
[](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
|
||||||
|
[](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
|
||||||
|
[](https://drone.beatrice.wtf/bea/HidekoBot)
|
||||||
|
[](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
|
||||||
|
|
||||||
Hideko is a general-purpose Discord bot.
|
Hideko is a general-purpose Discord bot.
|
||||||
|
|
||||||
|
## Download
|
||||||
|
The latest stable version is always uploaded automatically to the [Maven repository](https://nexus.beatrice.wtf/#browse/browse:maven-releases:wtf%2Fbeatrice%2Fhidekobot%2FHidekoBot).
|
||||||
|
You can download the JAR directly by clicking [here](https://nexus.beatrice.wtf/service/rest/v1/search/assets/download?sort=version&repository=maven-releases&maven.groupId=wtf.beatrice.hidekobot&maven.artifactId=HidekoBot&maven.extension=jar).
|
||||||
|
|
||||||
## Startup
|
## Startup
|
||||||
Download a prebuilt JAR file or build it from source, then run it with:
|
Download a prebuilt JAR file or build it from source, then run it with:
|
||||||
```bash
|
```bash
|
||||||
@@ -12,16 +21,28 @@ make the bot change its behavior.
|
|||||||
|
|
||||||
Additionally available parameters are:
|
Additionally available parameters are:
|
||||||
- **verbose**: log every message that the bot receives, plus additional debugging messages. Very spammy and performance heavy.
|
- **verbose**: log every message that the bot receives, plus additional debugging messages. Very spammy and performance heavy.
|
||||||
- **refresh**: force refresh the bot's commands.
|
- **refresh**: force refresh the slash commands. This is useful in case there was a simple update to a command that did not drastically change it, so no changes are found at bootup (eg: fixing a typo in the command description).
|
||||||
|
|
||||||
*Note: Java 16 or later is required.*
|
*Note: Java 21 or later is required.*
|
||||||
|
|
||||||
## Initial setup
|
## Initial setup
|
||||||
|
|
||||||
Run the startup command once. The bot will generate a `config.yml` file in the directory you were when you ran it.
|
Run the startup command once. The bot will generate a `config.yml` file in your current directory (`$PWD` on GNU/Linux).
|
||||||
|
|
||||||
Edit the configuration file and set all values according to your needs.
|
Edit the configuration file and set all values according to your needs.
|
||||||
|
|
||||||
Save the file and start the bot again. If there are no issues, everything will load and it will print an
|
Save the file and start the bot again. If there are no issues, everything will load and it will print an
|
||||||
invite-link in your console. Click on the link to add your bot to any server with the correct permissions
|
invite-link in your console. Click on the link to add your bot to any server with the correct permissions
|
||||||
already set-up.
|
already set-up. The bot supports both slash commands and message commands, with prefix `hideko`. Most
|
||||||
|
commands support both systems, but some of them are limited in one way or another.
|
||||||
|
|
||||||
|
The bot currently supports SQLite as a database backend. A database file will be created after the first boot
|
||||||
|
in your current directory. Do not delete the database file to avoid corruption and unpredictable
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
# Development
|
||||||
|
## Versioning
|
||||||
|
This project uses the `x.y.z-releaseType` schema for releases.
|
||||||
|
Development builds are tagged as `x.y.z-SNAPSHOT` and sometimes pushed to the snapshots Maven repository.
|
||||||
|
Stable builds are tagged as `x.y.z` and always pushed to the releases Maven repository, by promoting the build on
|
||||||
|
[Drone](https://drone.beatrice.wtf/). Currently, promoting stable builds is a manual process.
|
||||||
|
|||||||
187
pom.xml
187
pom.xml
@@ -6,42 +6,129 @@
|
|||||||
|
|
||||||
<groupId>wtf.beatrice.hidekobot</groupId>
|
<groupId>wtf.beatrice.hidekobot</groupId>
|
||||||
<artifactId>HidekoBot</artifactId>
|
<artifactId>HidekoBot</artifactId>
|
||||||
<version>0.5.0</version>
|
<version>0.9.3-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>16</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>16</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>
|
||||||
|
<sonar.dependencyCheck.jsonReportPath>./target/dependency-check-report.json
|
||||||
|
</sonar.dependencyCheck.jsonReportPath>
|
||||||
|
<sonar.dependencyCheck.summarize>true</sonar.dependencyCheck.summarize>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- Basic JDA dependency for Discord API -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.dv8tion</groupId>
|
<groupId>net.dv8tion</groupId>
|
||||||
<artifactId>JDA</artifactId>
|
<artifactId>JDA</artifactId>
|
||||||
<version>5.0.0-alpha.22</version>
|
<version>5.5.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 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.4</version>
|
<version>2.0.17</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-simple</artifactId>
|
|
||||||
<version>2.0.4</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Dependency used for SQLite database connections-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.xerial</groupId>
|
<groupId>org.xerial</groupId>
|
||||||
<artifactId>sqlite-jdbc</artifactId>
|
<artifactId>sqlite-jdbc</artifactId>
|
||||||
<version>3.39.4.1</version>
|
<version>3.49.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Dependency used for YAML configuration files -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
<version>1.33</version>
|
<version>2.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JSoup is used to parse HTML into JSON objects for better handling in Java -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>1.19.1</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Various String manipulation utils -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-text</artifactId>
|
||||||
|
<version>1.13.1</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- JSON dependency used for better parsing of JSON files -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.json</groupId>
|
||||||
|
<artifactId>json</artifactId>
|
||||||
|
<version>20250517</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Start Random.org dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.jinahya</groupId>
|
||||||
|
<artifactId>random-org-json-rpc</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.13.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.18.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- End Random.org dependencies -->
|
||||||
|
|
||||||
|
<!-- Unit Tests Dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<version>5.13.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</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>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<!-- override dependencies to use newer versions -->
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.protobuf</groupId>
|
||||||
|
<artifactId>protobuf-java</artifactId>
|
||||||
|
<version>4.31.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<resources>
|
<resources>
|
||||||
@@ -54,34 +141,66 @@
|
|||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<executions>
|
<version>3.11.2</version>
|
||||||
<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>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>sonar-maven-plugin</artifactId>
|
||||||
<version>3.4.1</version>
|
<version>5.1.0.4751</version>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.owasp</groupId>
|
||||||
|
<artifactId>dependency-check-maven</artifactId>
|
||||||
|
<version>12.1.1</version>
|
||||||
|
<configuration>
|
||||||
|
<failBuildOnCVSS>8</failBuildOnCVSS>
|
||||||
|
<!--suppress UnresolvedMavenProperty -->
|
||||||
|
<nvdApiKey>${nvdApiKey}</nvdApiKey>
|
||||||
|
<knownExploitedUrl>
|
||||||
|
https://raw.githubusercontent.com/EugenMayer/cisa-known-exploited-mirror/main/known_exploited_vulnerabilities.json
|
||||||
|
</knownExploitedUrl>
|
||||||
|
<formats>
|
||||||
|
<format>html</format>
|
||||||
|
<format>json</format>
|
||||||
|
</formats>
|
||||||
|
<suppressionFiles>
|
||||||
|
<suppressionFile>./suppressions.xml</suppressionFile>
|
||||||
|
</suppressionFiles>
|
||||||
|
</configuration>
|
||||||
|
</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>
|
</plugin>
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>nexus-releases</id>
|
||||||
|
<url>https://nexus.beatrice.wtf/repository/maven-releases/</url>
|
||||||
|
</repository>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>nexus-snapshots</id>
|
||||||
|
<url>https://nexus.beatrice.wtf/repository/maven-snapshots/</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
3
renovate.json
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
||||||
20
settings.xml
Normal file
20
settings.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
|
||||||
|
|
||||||
|
<servers>
|
||||||
|
<server>
|
||||||
|
<id>nexus-snapshots</id>
|
||||||
|
<username>${maven.repo.username}</username>
|
||||||
|
<password>${maven.repo.password}</password>
|
||||||
|
</server>
|
||||||
|
<server>
|
||||||
|
<id>nexus-releases</id>
|
||||||
|
<username>${maven.repo.username}</username>
|
||||||
|
<password>${maven.repo.password}</password>
|
||||||
|
</server>
|
||||||
|
</servers>
|
||||||
|
|
||||||
|
</settings>
|
||||||
|
|
||||||
@@ -1,56 +1,87 @@
|
|||||||
package wtf.beatrice.hidekobot;
|
package wtf.beatrice.hidekobot;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
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.Logger;
|
import wtf.beatrice.hidekobot.util.Services;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
public class Cache
|
public class Cache
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private Cache()
|
||||||
|
{
|
||||||
|
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 botPrefix = "hideko";
|
private static final String BOT_PREFIX = "hideko";
|
||||||
private static final Logger logger = new Logger(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
|
||||||
|
// they get what they wanted.
|
||||||
|
// i didn't think this was worthy of a whole database table with a runnable checking for expiration,
|
||||||
|
// and it will get cleared after a few minutes anyway, so RAM caching is more than good enough.
|
||||||
|
private static final HashMap<String, Integer> loveCalculatorValues = new HashMap<>();
|
||||||
|
|
||||||
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 botMaintainerId = 979809420714332260L;
|
private static final long BOT_MAINTAINER_ID = 979809420714332260L;
|
||||||
private final static String expiryTimestampFormat = "yy/MM/dd HH:mm:ss";
|
private static final String EXPIRY_TIMESTAMP_FORMAT = "yy/MM/dd HH:mm:ss";
|
||||||
|
|
||||||
// note: discord sets interactions' expiry time to 15 minutes by default, so we can't go higher than that.
|
// note: discord sets interactions' expiry time to 15 minutes by default, so we can't go higher than that.
|
||||||
private final static long expiryTimeSeconds = 15L;
|
private static final long EXPIRY_TIME_SECONDS = 30L;
|
||||||
|
|
||||||
// used to count e.g. uptime
|
// used to count e.g. uptime
|
||||||
private static LocalDateTime startupTime = null;
|
private static LocalDateTime startupTime = null;
|
||||||
|
|
||||||
private final static String execPath = System.getProperty("user.dir");
|
// date of when the first bot commit was made (CEST time)
|
||||||
private static final String botName = "Hideko";
|
private static final LocalDateTime botBirthDate = LocalDateTime.of(2022, 8, 25, 21, 50);
|
||||||
|
|
||||||
|
// the scheduler that should always be used when running a scheduled task.
|
||||||
|
private static final ScheduledExecutorService taskScheduler = Executors.newSingleThreadScheduledExecutor(); // todo: try-with-resources
|
||||||
|
|
||||||
|
private static final String EXEC_PATH = System.getProperty("user.dir");
|
||||||
|
private static final String BOT_NAME = "Hideko";
|
||||||
|
|
||||||
private static SlashCommandListener slashCommandListener = null;
|
private static SlashCommandListener slashCommandListener = null;
|
||||||
private static SlashCommandCompletionListener slashCommandCompletionListener = null;
|
private static SlashCommandCompletionListener slashCommandCompletionListener = null;
|
||||||
private static MessageCommandListener messageCommandListener = null;
|
private static MessageCommandListener messageCommandListener = null;
|
||||||
|
|
||||||
private final static String defaultInviteLink =
|
private static final String DEFAULT_INVITE_LINK =
|
||||||
"https://discord.com/api/oauth2/authorize?client_id=%userid%&scope=bot+applications.commands&permissions=8";
|
"https://discord.com/api/oauth2/authorize?client_id=%userid%&scope=bot+applications.commands&permissions=8";
|
||||||
|
|
||||||
private static String botApplicationId = "";
|
private static String botApplicationId = "";
|
||||||
|
|
||||||
// discord api returns a broken image if you don't use specific sizes (powers of 2), so we limit it to these
|
// discord api returns a broken image if you don't use specific sizes (powers of 2), so we limit it to these
|
||||||
private static final int[] supportedAvatarResolutions = { 16, 32, 64, 128, 256, 512, 1024 };
|
private static final int[] supportedAvatarResolutions = {16, 32, 64, 128, 256, 512, 1024};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of all the Discord-supported avatar resolutions.
|
* Get an array of all the Discord-supported avatar resolutions.
|
||||||
@@ -58,14 +89,21 @@ public class Cache
|
|||||||
*
|
*
|
||||||
* @return array of supported resolutions.
|
* @return array of supported resolutions.
|
||||||
*/
|
*/
|
||||||
public static int[] getSupportedAvatarResolutions() { return supportedAvatarResolutions; }
|
public static int[] getSupportedAvatarResolutions()
|
||||||
|
{
|
||||||
|
return supportedAvatarResolutions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the bot has been started with the verbose argument.
|
* Checks if the bot has been started with the verbose argument.
|
||||||
*
|
*
|
||||||
* @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 boolean isVerbose() { return verbose; }
|
public static synchronized boolean isVerbose()
|
||||||
|
{
|
||||||
|
return verbose;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the bot's verbosity status at runtime.
|
* Set the bot's verbosity status at runtime.
|
||||||
@@ -73,24 +111,20 @@ public class Cache
|
|||||||
*
|
*
|
||||||
* @param v the verbosity boolean value
|
* @param v the verbosity boolean value
|
||||||
*/
|
*/
|
||||||
public static void setVerbose(boolean v)
|
public static synchronized void setVerbose(boolean v)
|
||||||
{
|
{
|
||||||
verbose = v;
|
verbose = v;
|
||||||
|
|
||||||
if(v)
|
if (verbosityLogger != null)
|
||||||
{
|
{
|
||||||
if(verbosityLogger == null)
|
HidekoBot.getAPI().removeEventListener(verbosityLogger);
|
||||||
{
|
verbosityLogger = null;
|
||||||
verbosityLogger = new MessageLogger();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (v)
|
||||||
|
{
|
||||||
|
verbosityLogger = new MessageLogger();
|
||||||
HidekoBot.getAPI().addEventListener(verbosityLogger);
|
HidekoBot.getAPI().addEventListener(verbosityLogger);
|
||||||
} else {
|
|
||||||
if(verbosityLogger != null)
|
|
||||||
{
|
|
||||||
HidekoBot.getAPI().removeEventListener(verbosityLogger);
|
|
||||||
verbosityLogger = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +133,8 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,17 +144,22 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the bot maintainer's profile id.
|
* Get the bot maintainer's profile id.
|
||||||
*
|
*
|
||||||
* @return a long of the account's id
|
* @return a long of the account's id
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static long getBotMaintainerId() { return botMaintainerId; }
|
public static long getBotMaintainerId()
|
||||||
|
{
|
||||||
|
return BOT_MAINTAINER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the bot's application id.
|
* Set the bot's application id.
|
||||||
@@ -136,34 +176,21 @@ 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() { return botApplicationId; }
|
public static String getBotApplicationId()
|
||||||
|
{
|
||||||
|
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 defaultInviteLink.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;
|
return DEFAULT_INVITE_LINK.replace("%userid%", botApplicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
@@ -179,25 +206,46 @@ public class Cache
|
|||||||
*
|
*
|
||||||
* @return the String of the DateTimeFormatter format.
|
* @return the String of the DateTimeFormatter format.
|
||||||
*/
|
*/
|
||||||
public static String getExpiryTimestampFormat(){ return expiryTimestampFormat; }
|
public static String getExpiryTimestampFormat()
|
||||||
|
{
|
||||||
|
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() { return expiryTimeSeconds; }
|
public static long getExpiryTimeSeconds()
|
||||||
|
{
|
||||||
|
return EXPIRY_TIME_SECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getBotName() { return botName; };
|
public static String getBotName()
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bot's source code URL.
|
||||||
|
*
|
||||||
|
* @return a String containing the base URL of the repository, including a <b>trailing slash</b>.
|
||||||
|
*/
|
||||||
|
public static String getRepositoryUrl()
|
||||||
|
{
|
||||||
|
String url = propertiesSource.getProperty("repo.base_url");
|
||||||
|
return url.endsWith("/") ? url : url + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,17 +253,20 @@ 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 (Exception e) {
|
} catch (RuntimeException | NoSuchFieldException | IllegalAccessException e)
|
||||||
logger.log("Unknown color: " + colorName);
|
{
|
||||||
|
LOGGER.error("Unknown color: {}", colorName);
|
||||||
}
|
}
|
||||||
return color == null ? defaultColor : color;
|
return color == null ? defaultColor : color;
|
||||||
}
|
}
|
||||||
@@ -223,21 +274,36 @@ 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() { return slashCommandListener; }
|
public static SlashCommandListener getSlashCommandListener()
|
||||||
|
{
|
||||||
|
return slashCommandListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void setSlashCommandCompletionListener(SlashCommandCompletionListener commandCompletionListener)
|
public static void setSlashCommandCompletionListener(SlashCommandCompletionListener commandCompletionListener)
|
||||||
{ slashCommandCompletionListener = commandCompletionListener; }
|
{
|
||||||
|
slashCommandCompletionListener = commandCompletionListener;
|
||||||
|
}
|
||||||
|
|
||||||
public static SlashCommandCompletionListener getSlashCommandCompletionListener() { return slashCommandCompletionListener; }
|
public static SlashCommandCompletionListener getSlashCommandCompletionListener()
|
||||||
|
{
|
||||||
|
return slashCommandCompletionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void setMessageCommandListener(MessageCommandListener commandListener)
|
public static void setMessageCommandListener(MessageCommandListener commandListener)
|
||||||
{ messageCommandListener = commandListener; }
|
{
|
||||||
|
messageCommandListener = commandListener;
|
||||||
|
}
|
||||||
|
|
||||||
public static MessageCommandListener getMessageCommandListener() { return messageCommandListener; }
|
public static MessageCommandListener getMessageCommandListener()
|
||||||
|
{
|
||||||
|
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.
|
||||||
@@ -245,7 +311,9 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,25 +321,81 @@ public class Cache
|
|||||||
*
|
*
|
||||||
* @return a LocalDateTime object of the startup instant.
|
* @return a LocalDateTime object of the startup instant.
|
||||||
*/
|
*/
|
||||||
public static LocalDateTime getStartupTime() { return startupTime; }
|
public static LocalDateTime getStartupTime()
|
||||||
|
{
|
||||||
|
return startupTime;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getFullHeartBeatLink() {
|
/**
|
||||||
|
* Get the time of when the bot was created.
|
||||||
|
*
|
||||||
|
* @return a LocalDateTime object of the first commit's instant.
|
||||||
|
*/
|
||||||
|
public static LocalDateTime getBotBirthDate()
|
||||||
|
{
|
||||||
|
return botBirthDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() { return execPath; }
|
public static String getExecPath()
|
||||||
|
{
|
||||||
|
return EXEC_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
/*private static ConfigurationSource getConfigurationSource()
|
/*private static ConfigurationSource getConfigurationSource()
|
||||||
{ return configurationSource; }*/
|
{ return configurationSource; }*/
|
||||||
|
|
||||||
|
public static String getRandomOrgApiKey()
|
||||||
|
{
|
||||||
|
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() { return botPrefix; }
|
public static String getBotPrefix()
|
||||||
|
{
|
||||||
|
return BOT_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void cacheLoveCalculatorValue(String userId1, String userId2, int value)
|
||||||
|
{
|
||||||
|
String merged = userId1 + "|" + userId2;
|
||||||
|
loveCalculatorValues.put(merged, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Integer getLoveCalculatorValue(String userId1, String userId2)
|
||||||
|
{
|
||||||
|
String merged1 = userId1 + "|" + userId2;
|
||||||
|
String merged2 = userId2 + "|" + userId1;
|
||||||
|
Integer value = null;
|
||||||
|
value = loveCalculatorValues.get(merged1);
|
||||||
|
if (value == null) value = loveCalculatorValues.get(merged2);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeLoveCalculatorValue(String userId1, String userId2)
|
||||||
|
{
|
||||||
|
loveCalculatorValues.remove(userId1 + "|" + userId2);
|
||||||
|
loveCalculatorValues.remove(userId2 + "|" + userId1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ScheduledExecutorService getTaskScheduler()
|
||||||
|
{
|
||||||
|
return taskScheduler;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,23 +3,28 @@ package wtf.beatrice.hidekobot;
|
|||||||
import net.dv8tion.jda.api.JDA;
|
import net.dv8tion.jda.api.JDA;
|
||||||
import net.dv8tion.jda.api.JDABuilder;
|
import net.dv8tion.jda.api.JDABuilder;
|
||||||
import net.dv8tion.jda.api.OnlineStatus;
|
import net.dv8tion.jda.api.OnlineStatus;
|
||||||
import net.dv8tion.jda.api.entities.Activity;
|
|
||||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||||
import sun.misc.Signal;
|
import org.slf4j.Logger;
|
||||||
import wtf.beatrice.hidekobot.commands.completer.AvatarCommandCompleter;
|
import org.slf4j.LoggerFactory;
|
||||||
import wtf.beatrice.hidekobot.commands.message.HelloCommand;
|
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.message.*;
|
||||||
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.ButtonInteractionListener;
|
import wtf.beatrice.hidekobot.listeners.*;
|
||||||
import wtf.beatrice.hidekobot.listeners.MessageCommandListener;
|
|
||||||
import wtf.beatrice.hidekobot.listeners.SlashCommandCompletionListener;
|
|
||||||
import wtf.beatrice.hidekobot.listeners.SlashCommandListener;
|
|
||||||
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.util.Logger;
|
import wtf.beatrice.hidekobot.runnables.RandomOrgSeedTask;
|
||||||
import wtf.beatrice.hidekobot.util.SlashCommandUtil;
|
import wtf.beatrice.hidekobot.runnables.StatusUpdateTask;
|
||||||
|
import wtf.beatrice.hidekobot.services.CommandService;
|
||||||
|
import wtf.beatrice.hidekobot.services.DatabaseService;
|
||||||
|
import wtf.beatrice.hidekobot.util.FormatUtil;
|
||||||
|
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;
|
||||||
@@ -30,39 +35,52 @@ 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;
|
||||||
|
|
||||||
private static final Logger logger = new Logger(HidekoBot.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(HidekoBot.class);
|
||||||
|
|
||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
{
|
{
|
||||||
|
|
||||||
// load configuration
|
// load configuration
|
||||||
logger.log("Loading configuration...");
|
LOGGER.info("Loading configuration...");
|
||||||
String configFilePath = Cache.getExecPath() + File.separator + "config.yml";
|
String configFilePath = Cache.getExecPath() + File.separator + "config.yml";
|
||||||
ConfigurationSource configurationSource = new ConfigurationSource(configFilePath);
|
ConfigurationSource configurationSource = new ConfigurationSource(configFilePath);
|
||||||
configurationSource.initConfig();
|
configurationSource.initConfig();
|
||||||
Cache.setConfigurationSource(configurationSource);
|
Cache.setConfigurationSource(configurationSource);
|
||||||
logger.log("Configuration loaded!");
|
LOGGER.info("Configuration loaded!");
|
||||||
|
|
||||||
// load properties
|
// load properties
|
||||||
logger.log("Loading properties...");
|
LOGGER.info("Loading properties...");
|
||||||
PropertiesSource propertiesSource = new PropertiesSource();
|
PropertiesSource propertiesSource = new PropertiesSource();
|
||||||
propertiesSource.load();
|
propertiesSource.load();
|
||||||
Cache.setPropertiesSourceInstance(propertiesSource);
|
Cache.setPropertiesSourceInstance(propertiesSource);
|
||||||
logger.log("Properties loaded!");
|
LOGGER.info("Properties loaded!");
|
||||||
|
|
||||||
// check loaded bot token
|
// check loaded bot token
|
||||||
String botToken = Cache.getBotToken();
|
String botToken = Cache.getBotToken();
|
||||||
if(botToken == null || botToken.isEmpty())
|
if (botToken == null || botToken.isEmpty())
|
||||||
{
|
{
|
||||||
logger.log("Invalid bot token!");
|
LOGGER.error("Invalid bot token!");
|
||||||
shutdown();
|
shutdown();
|
||||||
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.
|
||||||
@@ -76,9 +94,14 @@ public class HidekoBot
|
|||||||
);
|
);
|
||||||
|
|
||||||
jda = jdaBuilder.build().awaitReady();
|
jda = jdaBuilder.build().awaitReady();
|
||||||
|
} catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
LOGGER.error(e.getMessage()); // print the error message, omit the stack trace.
|
||||||
|
Thread.currentThread().interrupt(); // send interrupt to the thread.
|
||||||
|
shutdown(); // if we failed connecting and authenticating, then quit.
|
||||||
} catch (Exception e)
|
} catch (Exception e)
|
||||||
{
|
{
|
||||||
logger.log(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.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,105 +112,142 @@ public class HidekoBot
|
|||||||
// store if we have to force refresh commands despite no apparent changes.
|
// store if we have to force refresh commands despite no apparent changes.
|
||||||
boolean forceUpdateCommands = false;
|
boolean forceUpdateCommands = false;
|
||||||
|
|
||||||
// if there is more than 1 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 > 1) {
|
if (args.length > 0)
|
||||||
|
{
|
||||||
List<String> argsList = new ArrayList<>(Arrays.asList(args));
|
List<String> argsList = new ArrayList<>(Arrays.asList(args));
|
||||||
|
|
||||||
|
|
||||||
// NOTE: do not replace with enhanced for, since we might need
|
// NOTE: do not replace with enhanced for, since we might need
|
||||||
// to know what position we're at or do further elaboration of the string.
|
// to know what position we're at or do further elaboration of the string.
|
||||||
// we were using this for api key parsing in the past.
|
// we were using this for api key parsing in the past.
|
||||||
for(int i = 0; i < argsList.size(); i++)
|
for (int i = 0; i < argsList.size(); i++)
|
||||||
{
|
{
|
||||||
String arg = argsList.get(i);
|
String arg = argsList.get(i);
|
||||||
|
|
||||||
if(arg.equals("verbose")) Cache.setVerbose(true);
|
if (arg.equals("verbose")) Cache.setVerbose(true);
|
||||||
if(arg.equals("refresh")) forceUpdateCommands = true;
|
if (arg.equals("refresh")) forceUpdateCommands = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean enableRandomSeedUpdaterTask = false;
|
||||||
|
// initialize random.org object if API key is provided
|
||||||
|
{
|
||||||
|
if (RandomUtil.isRandomOrgKeyValid())
|
||||||
|
{
|
||||||
|
LOGGER.info("Enabling Random.org integration... This might take a while!");
|
||||||
|
RandomUtil.initRandomOrg();
|
||||||
|
enableRandomSeedUpdaterTask = true;
|
||||||
|
LOGGER.info("Random.org integration enabled!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// register slash commands and completers
|
// register slash commands and completers
|
||||||
SlashCommandListener slashCommandListener = new SlashCommandListener();
|
SlashCommandListener slashCommandListener = context.getBean(SlashCommandListener.class);
|
||||||
SlashCommandCompletionListener slashCommandCompletionListener = new SlashCommandCompletionListener();
|
SlashCommandCompletionListener slashCommandCompletionListener = context.getBean(SlashCommandCompletionListener.class);
|
||||||
AvatarCommand avatarCommand = new AvatarCommand();
|
MessageCommandListener messageCommandListener = context.getBean(MessageCommandListener.class);
|
||||||
AvatarCommandCompleter avatarCommandCompleter = new AvatarCommandCompleter(avatarCommand);
|
ButtonInteractionListener buttonInteractionListener = context.getBean(ButtonInteractionListener.class);
|
||||||
slashCommandListener.registerCommand(avatarCommand);
|
SelectMenuInteractionListener selectMenuInteractionListener = context.getBean(SelectMenuInteractionListener.class);
|
||||||
|
SlashAvatarCommand slashAvatarCommand = context.getBean(SlashAvatarCommand.class);
|
||||||
|
ProfileImageCommandCompleter avatarCommandCompleter = new ProfileImageCommandCompleter(slashAvatarCommand);
|
||||||
|
slashCommandListener.registerCommand(slashAvatarCommand);
|
||||||
slashCommandCompletionListener.registerCommandCompleter(avatarCommandCompleter);
|
slashCommandCompletionListener.registerCommandCompleter(avatarCommandCompleter);
|
||||||
slashCommandListener.registerCommand(new BotInfoCommand());
|
slashCommandListener.registerCommand(context.getBean(SlashBanCommand.class));
|
||||||
slashCommandListener.registerCommand(new ClearCommand());
|
SlashBannerCommand slashBannerCommand = context.getBean(SlashBannerCommand.class);
|
||||||
slashCommandListener.registerCommand(new CoinFlipCommand());
|
ProfileImageCommandCompleter bannerCommandCompleter = new ProfileImageCommandCompleter(slashBannerCommand);
|
||||||
slashCommandListener.registerCommand(new DieCommand());
|
slashCommandListener.registerCommand(slashBannerCommand);
|
||||||
slashCommandListener.registerCommand(new HelpCommand());
|
slashCommandCompletionListener.registerCommandCompleter(bannerCommandCompleter);
|
||||||
slashCommandListener.registerCommand(new InviteCommand());
|
slashCommandListener.registerCommand(context.getBean(SlashBotInfoCommand.class));
|
||||||
slashCommandListener.registerCommand(new PingCommand());
|
slashCommandListener.registerCommand(context.getBean(SlashClearCommand.class));
|
||||||
slashCommandListener.registerCommand(new SayCommand());
|
slashCommandListener.registerCommand(context.getBean(SlashCoinFlipCommand.class));
|
||||||
Cache.setSlashCommandListener(slashCommandListener);
|
slashCommandListener.registerCommand(context.getBean(SlashDiceRollCommand.class));
|
||||||
Cache.setSlashCommandCompletionListener(slashCommandCompletionListener);
|
slashCommandListener.registerCommand(new SlashDieCommand());
|
||||||
|
slashCommandListener.registerCommand(new SlashHelpCommand());
|
||||||
|
slashCommandListener.registerCommand(context.getBean(SlashInviteCommand.class));
|
||||||
|
slashCommandListener.registerCommand(context.getBean(SlashKickCommand.class));
|
||||||
|
slashCommandListener.registerCommand(context.getBean(SlashLoveCalculatorCommand.class));
|
||||||
|
slashCommandListener.registerCommand(context.getBean(SlashMagicBallCommand.class));
|
||||||
|
slashCommandListener.registerCommand(new SlashPingCommand());
|
||||||
|
slashCommandListener.registerCommand(context.getBean(SlashSayCommand.class));
|
||||||
|
slashCommandListener.registerCommand(context.getBean(SlashTimeoutCommand.class));
|
||||||
|
slashCommandListener.registerCommand(new SlashTriviaCommand());
|
||||||
|
slashCommandListener.registerCommand(new SlashUrbanDictionaryCommand());
|
||||||
|
|
||||||
// register message commands
|
// register message commands
|
||||||
MessageCommandListener messageCommandListener = new MessageCommandListener();
|
messageCommandListener.registerCommand(new MessageHelloCommand());
|
||||||
messageCommandListener.registerCommand(new HelloCommand());
|
messageCommandListener.registerCommand(context.getBean(MessageAliasCommand.class));
|
||||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.InviteCommand());
|
messageCommandListener.registerCommand(context.getBean(MessageAvatarCommand.class));
|
||||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BotInfoCommand());
|
messageCommandListener.registerCommand(context.getBean(MessageBanCommand.class));
|
||||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.CoinFlipCommand());
|
messageCommandListener.registerCommand(context.getBean(MessageBannerCommand.class));
|
||||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand());
|
messageCommandListener.registerCommand(context.getBean(MessageBotInfoCommand.class));
|
||||||
Cache.setMessageCommandListener(messageCommandListener);
|
messageCommandListener.registerCommand(context.getBean(MessageCoinFlipCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageClearCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageDiceRollCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageHelpCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageInviteCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageKickCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageLoveCalculatorCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageMagicBallCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageSayCommand.class));
|
||||||
|
messageCommandListener.registerCommand(context.getBean(MessageTimeoutCommand.class));
|
||||||
|
messageCommandListener.registerCommand(new MessageTriviaCommand());
|
||||||
|
messageCommandListener.registerCommand(new MessageUrbanDictionaryCommand());
|
||||||
|
|
||||||
// register listeners
|
// register listeners
|
||||||
|
Cache.setSlashCommandListener(slashCommandListener);
|
||||||
|
Cache.setSlashCommandCompletionListener(slashCommandCompletionListener);
|
||||||
|
Cache.setMessageCommandListener(messageCommandListener);
|
||||||
jda.addEventListener(messageCommandListener);
|
jda.addEventListener(messageCommandListener);
|
||||||
jda.addEventListener(slashCommandListener);
|
jda.addEventListener(slashCommandListener);
|
||||||
jda.addEventListener(slashCommandCompletionListener);
|
jda.addEventListener(slashCommandCompletionListener);
|
||||||
jda.addEventListener(new ButtonInteractionListener());
|
jda.addEventListener(buttonInteractionListener);
|
||||||
|
jda.addEventListener(selectMenuInteractionListener);
|
||||||
|
|
||||||
// update slash commands (delayed)
|
// update slash commands (delayed)
|
||||||
final boolean finalForceUpdateCommands = forceUpdateCommands;
|
final boolean finalForceUpdateCommands = forceUpdateCommands;
|
||||||
Executors.newSingleThreadScheduledExecutor().schedule(() -> // todo: try-with-resources
|
try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor())
|
||||||
SlashCommandUtil.updateSlashCommands(finalForceUpdateCommands), 1, TimeUnit.SECONDS);
|
{
|
||||||
|
executor.schedule(() -> commandService.updateSlashCommands(finalForceUpdateCommands),
|
||||||
|
1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
// set the bot's status
|
// set the bot's status
|
||||||
jda.getPresence().setStatus(OnlineStatus.ONLINE);
|
jda.getPresence().setStatus(OnlineStatus.ONLINE);
|
||||||
jda.getPresence().setActivity(Activity.playing("Hatsune Miku: Project DIVA"));
|
|
||||||
|
|
||||||
// connect to database
|
|
||||||
logger.log("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.log("Database connection initialized!");
|
|
||||||
Cache.setDatabaseSourceInstance(databaseSource);
|
|
||||||
|
|
||||||
// load data here...
|
|
||||||
|
|
||||||
logger.log("Database data loaded into memory!");
|
|
||||||
} else {
|
|
||||||
logger.log("Error initializing database connection!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// start scheduled runnables
|
// start scheduled runnables
|
||||||
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // todo: try-with-resources
|
ScheduledExecutorService scheduler = Cache.getTaskScheduler();
|
||||||
ExpiredMessageTask expiredMessageTask = new ExpiredMessageTask();
|
ExpiredMessageTask expiredMessageTask = new ExpiredMessageTask(services.databaseService(), services.commandService());
|
||||||
scheduler.scheduleAtFixedRate(expiredMessageTask, 5, 5, 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, 10, 30, TimeUnit.SECONDS); //every 30 seconds
|
scheduler.scheduleAtFixedRate(heartBeatTask, 10L, 30L, TimeUnit.SECONDS); //every 30 seconds
|
||||||
|
|
||||||
|
StatusUpdateTask statusUpdateTask = new StatusUpdateTask();
|
||||||
|
scheduler.scheduleAtFixedRate(statusUpdateTask, 0L, 60L * 5L, TimeUnit.SECONDS); // every 5 minutes
|
||||||
|
if (enableRandomSeedUpdaterTask)
|
||||||
|
{
|
||||||
|
RandomOrgSeedTask randomSeedTask = new RandomOrgSeedTask();
|
||||||
|
scheduler.scheduleAtFixedRate(randomSeedTask, 15L, 15L, TimeUnit.MINUTES); // every 15 minutes
|
||||||
|
}
|
||||||
|
|
||||||
// register shutdown interrupt signal listener for proper shutdown.
|
// register shutdown interrupt signal listener for proper shutdown.
|
||||||
Signal.handle(new Signal("INT"), signal -> shutdown());
|
Runtime.getRuntime().addShutdownHook(new Thread(HidekoBot::preShutdown));
|
||||||
|
|
||||||
// set startup time.
|
// set startup time.
|
||||||
Cache.setStartupTime(LocalDateTime.now());
|
Cache.setStartupTime(LocalDateTime.now());
|
||||||
|
|
||||||
// print the bot logo.
|
// print the bot logo.
|
||||||
logger.log("\n\n" + logger.getLogo() + "\nv" + Cache.getBotVersion() + " - bot is ready!\n", 2);
|
LOGGER.info("\n\n{}\nv{} - bot is ready!\n", FormatUtil.getLogo(), Cache.getBotVersion());
|
||||||
|
|
||||||
|
|
||||||
// log the invite-link to console so noob users can just click on it.
|
// log the invite-link to console so noob users can just click on it.
|
||||||
logger.log("Bot User ID: " + botUserId, 3);
|
LOGGER.info("Bot User ID: {}", botUserId);
|
||||||
logger.log("Invite Link: " + Cache.getInviteUrl(), 4);
|
LOGGER.info("Invite Link: {}", Cache.getInviteUrl());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JDA getAPI()
|
public static JDA getAPI()
|
||||||
{
|
{
|
||||||
return jda;
|
return jda;
|
||||||
@@ -195,9 +255,14 @@ public class HidekoBot
|
|||||||
|
|
||||||
public static void shutdown()
|
public static void shutdown()
|
||||||
{
|
{
|
||||||
logger.log("WARNING! Shutting down!");
|
preShutdown();
|
||||||
if(jda != null) jda.shutdown();
|
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void preShutdown()
|
||||||
|
{
|
||||||
|
LOGGER.warn("WARNING! Shutting down!");
|
||||||
|
if (jda != null) jda.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class Alias
|
||||||
|
{
|
||||||
|
|
||||||
|
public String generateNiceAliases(MessageCommand command)
|
||||||
|
{
|
||||||
|
LinkedList<String> aliases = command.getCommandLabels();
|
||||||
|
StringBuilder aliasesStringBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < aliases.size(); i++)
|
||||||
|
{
|
||||||
|
aliasesStringBuilder.append("`").append(aliases.get(i)).append("`");
|
||||||
|
|
||||||
|
if (i + 1 != aliases.size())
|
||||||
|
aliasesStringBuilder.append(", "); // separate with comma except on last iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliasesStringBuilder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,17 +2,21 @@ 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;
|
||||||
|
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
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
|
||||||
{
|
{
|
||||||
public static MessageEmbed generateEmbed(List<String> commandLabels)
|
|
||||||
|
public MessageEmbed generateEmbed(List<String> commandLabels)
|
||||||
{
|
{
|
||||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
embedBuilder.setColor(Cache.getBotColor());
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
@@ -20,7 +24,7 @@ public class BotInfo
|
|||||||
|
|
||||||
// thumbnail
|
// thumbnail
|
||||||
String botAvatarUrl = HidekoBot.getAPI().getSelfUser().getAvatarUrl();
|
String botAvatarUrl = HidekoBot.getAPI().getSelfUser().getAvatarUrl();
|
||||||
if(botAvatarUrl != null) embedBuilder.setThumbnail(botAvatarUrl);
|
if (botAvatarUrl != null) embedBuilder.setThumbnail(botAvatarUrl);
|
||||||
|
|
||||||
// help field
|
// help field
|
||||||
long ownerId = Cache.getBotOwnerId();
|
long ownerId = Cache.getBotOwnerId();
|
||||||
@@ -32,12 +36,12 @@ public class BotInfo
|
|||||||
|
|
||||||
// type-specific commands list field
|
// type-specific commands list field
|
||||||
StringBuilder commandsListBuilder = new StringBuilder();
|
StringBuilder commandsListBuilder = new StringBuilder();
|
||||||
commandsListBuilder.append(commandLabels.size()).append( " total - ");
|
commandsListBuilder.append(commandLabels.size()).append(" total - ");
|
||||||
for(int i = 0; i < commandLabels.size(); i++)
|
for (int i = 0; i < commandLabels.size(); i++)
|
||||||
{
|
{
|
||||||
commandsListBuilder.append("`").append(commandLabels.get(i)).append("`");
|
commandsListBuilder.append("`").append(commandLabels.get(i)).append("`");
|
||||||
|
|
||||||
if(i + 1 != commandLabels.size()) // don't add comma in last iteration
|
if (i + 1 != commandLabels.size()) // don't add comma in last iteration
|
||||||
{
|
{
|
||||||
commandsListBuilder.append(", ");
|
commandsListBuilder.append(", ");
|
||||||
}
|
}
|
||||||
@@ -48,25 +52,38 @@ public class BotInfo
|
|||||||
// keep track of how many total commands we have
|
// keep track of how many total commands we have
|
||||||
int commandsCount = 0;
|
int commandsCount = 0;
|
||||||
|
|
||||||
// message commands info fields
|
// message commands info field
|
||||||
StringBuilder messageCommandsInfoBuilder = new StringBuilder();
|
String messageCommandsInfo;
|
||||||
if(Cache.getMessageCommandListener() == null)
|
if (Cache.getMessageCommandListener() == null)
|
||||||
messageCommandsInfoBuilder.append("❌ disabled");
|
messageCommandsInfo = "❌ disabled";
|
||||||
else {
|
else
|
||||||
messageCommandsInfoBuilder.append("✅ available");
|
{
|
||||||
|
messageCommandsInfo = "✅ available";
|
||||||
commandsCount += Cache.getMessageCommandListener().getRegisteredCommands().size();
|
commandsCount += Cache.getMessageCommandListener().getRegisteredCommands().size();
|
||||||
}
|
}
|
||||||
embedBuilder.addField("Message commands", messageCommandsInfoBuilder.toString(), true);
|
embedBuilder.addField("Message commands", messageCommandsInfo, true);
|
||||||
|
|
||||||
// slash commands info fields
|
// slash commands info field
|
||||||
StringBuilder slashCommandsInfoBuilder = new StringBuilder();
|
String slashCommandsInfo;
|
||||||
if(Cache.getMessageCommandListener() == null)
|
if (Cache.getMessageCommandListener() == null)
|
||||||
slashCommandsInfoBuilder.append("❌ disabled");
|
slashCommandsInfo = "❌ disabled";
|
||||||
else {
|
else
|
||||||
slashCommandsInfoBuilder.append("✅ available");
|
{
|
||||||
|
slashCommandsInfo = "✅ available";
|
||||||
commandsCount += Cache.getSlashCommandListener().getRegisteredCommands().size();
|
commandsCount += Cache.getSlashCommandListener().getRegisteredCommands().size();
|
||||||
}
|
}
|
||||||
embedBuilder.addField("Slash commands", slashCommandsInfoBuilder.toString(), true);
|
embedBuilder.addField("Slash commands", slashCommandsInfo, true);
|
||||||
|
|
||||||
|
// random.org integration field
|
||||||
|
String randomOrgInfo;
|
||||||
|
if (RandomUtil.isRandomOrgKeyValid())
|
||||||
|
{
|
||||||
|
randomOrgInfo = "✅ connected";
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
randomOrgInfo = "❌ disabled";
|
||||||
|
}
|
||||||
|
embedBuilder.addField("Random.org", randomOrgInfo, true);
|
||||||
|
|
||||||
// commands count fields
|
// commands count fields
|
||||||
embedBuilder.addField("Total commands", "Loaded: `" + commandsCount + "`", true);
|
embedBuilder.addField("Total commands", "Loaded: `" + commandsCount + "`", true);
|
||||||
@@ -91,12 +108,18 @@ public class BotInfo
|
|||||||
embedBuilder.addField("Maintainer", developerMention, true);
|
embedBuilder.addField("Maintainer", developerMention, true);
|
||||||
|
|
||||||
// uptime field
|
// uptime field
|
||||||
embedBuilder.addField("Uptime", FormatUtil.getNiceUptime(), true);
|
embedBuilder.addField("Uptime", FormatUtil.getNiceTimeDiff(Cache.getStartupTime()), true);
|
||||||
|
|
||||||
// issue tracker field
|
// issue tracker field
|
||||||
|
|
||||||
|
String link = "[Issue tracker](" + Cache.getRepositoryUrl() + "issues)";
|
||||||
embedBuilder.addField("Support",
|
embedBuilder.addField("Support",
|
||||||
"[Issue tracker](https://git.beatrice.wtf/mind-overflow/HidekoBot/issues)",
|
link, true);
|
||||||
true); //todo: we should probably make this a final field in the config class
|
|
||||||
|
// bot birthday field
|
||||||
|
embedBuilder.addField("Bot age",
|
||||||
|
Cache.getBotName() + " was created " + FormatUtil.getNiceTimeDiff(Cache.getBotBirthDate()) + "ago!",
|
||||||
|
false);
|
||||||
|
|
||||||
return embedBuilder.build();
|
return embedBuilder.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,51 +7,59 @@ import net.dv8tion.jda.api.entities.channel.Channel;
|
|||||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
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.events.interaction.component.ButtonInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
|
||||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
import wtf.beatrice.hidekobot.Cache;
|
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()
|
||||||
public static String getLabel() {
|
{
|
||||||
return "clear";
|
return "clear";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDescription() {
|
public String getDescription()
|
||||||
|
{
|
||||||
return "Clear the current channel's chat.";
|
return "Clear the current channel's chat.";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Permission getPermission() {
|
public Permission getPermission()
|
||||||
|
{
|
||||||
return Permission.MESSAGE_MANAGE;
|
return Permission.MESSAGE_MANAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String checkDMs(Channel channel)
|
public 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 static String checkDeleteAmount(int toDeleteAmount)
|
public 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 static int delete(int toDeleteAmount, long startingMessageId, MessageChannel channel)
|
public int delete(int toDeleteAmount,
|
||||||
|
long startingMessageId,
|
||||||
|
MessageChannel channel)
|
||||||
{
|
{
|
||||||
// int to keep track of how many messages we actually deleted.
|
// int to keep track of how many messages we actually deleted.
|
||||||
int deleted = 0;
|
int deleted = 0;
|
||||||
|
|
||||||
int limit = 95; //discord limits this method to range 2-100. we set it to 95 to be safe.
|
int limit = 95; //discord limits this method to only 2<x<100 deletions per run.
|
||||||
|
// we set this slightly lower to be safe, and iterate as needed.
|
||||||
|
|
||||||
// increase the count by 1, because we technically aren't clearing the first ID ever
|
// increase the count by 1, because we technically aren't clearing the first ID ever
|
||||||
// which is actually the slash command's ID and not a message.
|
// which is actually the slash command's ID and not a message.
|
||||||
@@ -62,7 +70,7 @@ public class ClearChat
|
|||||||
|
|
||||||
//if there are some messages left, but less than <limit>, we need one more iterations.
|
//if there are some messages left, but less than <limit>, we need one more iterations.
|
||||||
int remainder = toDeleteAmount % limit;
|
int remainder = toDeleteAmount % limit;
|
||||||
if(remainder != 0) iterations++;
|
if (remainder != 0) iterations++;
|
||||||
|
|
||||||
// set the starting point.
|
// set the starting point.
|
||||||
long messageId = startingMessageId;
|
long messageId = startingMessageId;
|
||||||
@@ -71,70 +79,70 @@ public class ClearChat
|
|||||||
boolean outOfBounds = false;
|
boolean outOfBounds = false;
|
||||||
|
|
||||||
// do iterate.
|
// do iterate.
|
||||||
for(int iteration = 0; iteration < iterations; iteration++)
|
for (int iteration = 0; iteration < iterations; iteration++)
|
||||||
{
|
{
|
||||||
if(outOfBounds) break;
|
if (outOfBounds) break;
|
||||||
|
|
||||||
// set how many messages to delete for this iteration (usually <limit> unless there's a remainder)
|
// set how many messages to delete for this iteration (usually <limit> unless there's a remainder)
|
||||||
int iterationSize = limit;
|
int iterationSize = limit;
|
||||||
|
|
||||||
// if we are at the last iteration...
|
// if we are at the last iteration... check if we have <limit> or fewer messages to delete
|
||||||
if(iteration+1 == iterations)
|
if (iteration + 1 == iterations && remainder != 0)
|
||||||
{
|
{
|
||||||
// check if we have <limit> or fewer messages to delete
|
iterationSize = remainder;
|
||||||
if(remainder != 0) iterationSize = remainder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(iterationSize == 1)
|
if (iterationSize == 1)
|
||||||
{
|
{
|
||||||
// grab the message
|
// grab the message
|
||||||
Message toDelete = channel.retrieveMessageById(messageId).complete();
|
Message toDelete = channel.retrieveMessageById(messageId).complete();
|
||||||
//only delete one message
|
//only delete one message
|
||||||
if(toDelete != null) toDelete.delete().queue();
|
if (toDelete != null) toDelete.delete().queue();
|
||||||
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.
|
||||||
List<Message> messages = new ArrayList<>();
|
List<Message> messages = new ArrayList<>();
|
||||||
// (we are skipping first iteration since it would return an error, given that the id is the slash command and not a message)
|
// (we are skipping first iteration since it would return an error, given that the id is the slash command and not a message)
|
||||||
if(iteration!=0) messages.add(channel.retrieveMessageById(messageId).complete());
|
if (iteration != 0) messages.add(channel.retrieveMessageById(messageId).complete());
|
||||||
messages.addAll(action.complete().getRetrievedHistory());
|
messages.addAll(action.complete().getRetrievedHistory());
|
||||||
|
|
||||||
// check if we only have one or zero messages left (trying to delete more than possible)
|
// check if we only have one or zero messages left (trying to delete more than possible)
|
||||||
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.get(messages.size() - 1).getIdLong(), 1);
|
action = channel.getHistoryBefore(messages.getLast().getIdLong(), 1);
|
||||||
|
|
||||||
List<Message> previousMessage = action.complete().getRetrievedHistory();
|
List<Message> previousMessage = action.complete().getRetrievedHistory();
|
||||||
|
|
||||||
// if that message exists (we are not out of bounds)... store it
|
// if that message exists (we are not out of bounds)... store it
|
||||||
if(!previousMessage.isEmpty()) messageId = previousMessage.get(0).getIdLong();
|
if (!previousMessage.isEmpty()) messageId = previousMessage.getFirst().getIdLong();
|
||||||
else outOfBounds = true;
|
else outOfBounds = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// queue messages for deletion
|
// queue messages for deletion
|
||||||
if(messages.size() == 1)
|
if (messages.size() == 1)
|
||||||
{
|
{
|
||||||
messages.get(0).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
|
||||||
which are restricted by discord, and thus has to use
|
which are restricted by discord, and thus has to use
|
||||||
a less efficient way that triggers rate-limiting very quickly. */
|
a less efficient way that triggers rate-limiting very quickly. */
|
||||||
|
|
||||||
} catch (Exception e)
|
} catch (RuntimeException ignored)
|
||||||
{
|
{
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,46 +155,31 @@ public class ClearChat
|
|||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Button getDismissButton()
|
public Button getDismissButton()
|
||||||
{
|
{
|
||||||
return Button.primary("clear_dismiss", "Dismiss")
|
return Button.primary("generic_dismiss", "Dismiss")
|
||||||
.withEmoji(Emoji.fromUnicode("❌"));
|
.withEmoji(Emoji.fromUnicode("❌"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String parseAmount(int deleted)
|
public String parseAmount(int deleted)
|
||||||
{
|
{
|
||||||
if(deleted < 1)
|
|
||||||
|
if (deleted < 1)
|
||||||
{
|
{
|
||||||
return "\uD83D\uDE22 Couldn't clear any message!";
|
return "\uD83D\uDE22 Couldn't clear any message!";
|
||||||
} 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!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void respond(Object responseFlowObj, String content)
|
// cap the amount to avoid abuse.
|
||||||
|
public int getMaxAmount()
|
||||||
{
|
{
|
||||||
if(responseFlowObj instanceof InteractionHook) {
|
return 1000;
|
||||||
((InteractionHook) responseFlowObj).editOriginal(content).queue();
|
|
||||||
} else if (responseFlowObj instanceof Message) {
|
|
||||||
((Message) responseFlowObj).reply(content).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void dismissMessage(ButtonInteractionEvent event)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
|
|
||||||
{
|
|
||||||
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
event.getInteraction().getMessage().delete().queue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,64 +6,85 @@ 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()
|
||||||
|
{
|
||||||
public static Button getReflipButton() {
|
|
||||||
return Button.primary("coinflip_reflip", "Flip again")
|
return Button.primary("coinflip_reflip", "Flip again")
|
||||||
.withEmoji(Emoji.fromFormatted("\uD83E\uDE99"));
|
.withEmoji(Emoji.fromUnicode("\uD83E\uDE99"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String genRandom()
|
public String genRandom()
|
||||||
{
|
{
|
||||||
int rand = RandomUtil.getRandomNumber(0, 1);
|
int rand = RandomUtil.getRandomNumber(0, 1);
|
||||||
String msg;
|
String msg;
|
||||||
|
|
||||||
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 static void buttonReFlip(ButtonInteractionEvent event)
|
public void buttonReFlip(ButtonInteractionEvent event)
|
||||||
{
|
{
|
||||||
// check if the user interacting is the same one who ran the command
|
// Ack ASAP to avoid 3s timeout
|
||||||
if(!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
|
event.deferEdit().queue(hook -> {
|
||||||
{
|
// Permission check **after** ack
|
||||||
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
if (!Cache.getServices().databaseService().isUserTrackedFor(event.getUser().getId(), event.getMessageId()))
|
||||||
return;
|
{
|
||||||
}
|
hook.sendMessage("❌ You did not run this command!").setEphemeral(true).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set old message's button as disabled
|
// Disable all components on the original message
|
||||||
List<ActionRow> actionRows = event.getMessage().getActionRows();
|
List<ActionRow> oldRows = event.getMessage().getActionRows();
|
||||||
actionRows.set(0, actionRows.get(0).asDisabled());
|
List<ActionRow> disabledRows = new ArrayList<>(oldRows.size());
|
||||||
event.editComponents(actionRows).queue();
|
for (ActionRow row : oldRows)
|
||||||
|
{
|
||||||
|
disabledRows.add(row.asDisabled());
|
||||||
|
}
|
||||||
|
hook.editOriginalComponents(disabledRows).queue();
|
||||||
|
|
||||||
// perform coin flip
|
// Send a follow-up with a fresh button
|
||||||
event.getHook().sendMessage(genRandom())
|
hook.sendMessage(genRandom())
|
||||||
.addActionRow(getReflipButton())
|
.addActionRow(getReflipButton())
|
||||||
.queue((message) ->
|
.queue(msg -> trackAndRestrict(msg, event.getUser()), err -> {
|
||||||
{
|
});
|
||||||
// set the command as expiring and restrict it to the user who ran it
|
}, failure -> {
|
||||||
trackAndRestrict(message, event.getUser());
|
// Rare: if we couldn't ack, try best-effort fallbacks
|
||||||
}, (error) -> {});
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
event.getChannel().sendMessage(genRandom())
|
||||||
|
.addActionRow(getReflipButton())
|
||||||
|
.queue(msg -> trackAndRestrict(msg, event.getUser()), err -> {
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void trackAndRestrict(Message replyMessage, User user)
|
public void trackAndRestrict(Message replyMessage, User user)
|
||||||
{
|
{
|
||||||
String replyMessageId = replyMessage.getId();
|
Cache.getServices().databaseService().queueDisabling(replyMessage);
|
||||||
|
Cache.getServices().databaseService().trackRanCommandReply(replyMessage, user);
|
||||||
Cache.getDatabaseSource().queueDisabling(replyMessage);
|
|
||||||
Cache.getDatabaseSource().trackRanCommandReply(replyMessage, user);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
171
src/main/java/wtf/beatrice/hidekobot/commands/base/DiceRoll.java
Normal file
171
src/main/java/wtf/beatrice/hidekobot/commands/base/DiceRoll.java
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.Dice;
|
||||||
|
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DiceRoll
|
||||||
|
{
|
||||||
|
|
||||||
|
public MessageResponse buildResponse(User author, String[] args)
|
||||||
|
{
|
||||||
|
LinkedHashMap<Dice, Integer> dicesToRoll = new LinkedHashMap<>();
|
||||||
|
String diceRegex = "d\\d+";
|
||||||
|
String amountRegex = "\\d+";
|
||||||
|
|
||||||
|
Dice currentDice = null;
|
||||||
|
int currentAmount;
|
||||||
|
UUID lastPushedDice = null;
|
||||||
|
int totalRolls = 0;
|
||||||
|
|
||||||
|
for (String arg : args)
|
||||||
|
{
|
||||||
|
if (totalRolls > 200)
|
||||||
|
{
|
||||||
|
return new MessageResponse("Too many total rolls!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.matches(amountRegex))
|
||||||
|
{
|
||||||
|
currentAmount = Integer.parseInt(arg);
|
||||||
|
|
||||||
|
if (currentDice == null)
|
||||||
|
{
|
||||||
|
currentDice = new Dice(6);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
currentDice = new Dice(currentDice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentAmount > 100)
|
||||||
|
{
|
||||||
|
return new MessageResponse("Too many rolls (`" + currentAmount + "`)!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPushedDice = currentDice.getUUID();
|
||||||
|
dicesToRoll.put(currentDice, currentAmount);
|
||||||
|
totalRolls += currentAmount;
|
||||||
|
} else if (arg.matches(diceRegex))
|
||||||
|
{
|
||||||
|
int sides = Integer.parseInt(arg.substring(1));
|
||||||
|
|
||||||
|
if (sides > 10000)
|
||||||
|
{
|
||||||
|
return new MessageResponse("Too many sides (`" + sides + "`)!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 1)
|
||||||
|
{
|
||||||
|
dicesToRoll.put(new Dice(sides), 1);
|
||||||
|
totalRolls++;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (currentDice != null)
|
||||||
|
{
|
||||||
|
if (lastPushedDice == null || !lastPushedDice.equals(currentDice.getUUID()))
|
||||||
|
{
|
||||||
|
dicesToRoll.put(currentDice, 1);
|
||||||
|
lastPushedDice = currentDice.getUUID();
|
||||||
|
totalRolls++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDice = new Dice(sides);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastPushedDice == null)
|
||||||
|
{
|
||||||
|
if (currentDice != null)
|
||||||
|
{
|
||||||
|
dicesToRoll.put(currentDice, 1);
|
||||||
|
totalRolls++;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (!lastPushedDice.equals(currentDice.getUUID()))
|
||||||
|
{
|
||||||
|
dicesToRoll.put(new Dice(currentDice), 1);
|
||||||
|
totalRolls++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedList<Dice> rolledDices = new LinkedList<>();
|
||||||
|
|
||||||
|
// in case no dice was specified (or invalid), roll a standard 6-sided dice.
|
||||||
|
if (dicesToRoll.isEmpty())
|
||||||
|
{
|
||||||
|
Dice standardDice = new Dice(6);
|
||||||
|
dicesToRoll.put(standardDice, 1);
|
||||||
|
totalRolls = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<Dice, Integer> entry : dicesToRoll.entrySet())
|
||||||
|
{
|
||||||
|
Dice dice = entry.getKey();
|
||||||
|
Integer rollsToMake = entry.getValue();
|
||||||
|
|
||||||
|
for (int roll = 0; roll < rollsToMake; roll++)
|
||||||
|
{
|
||||||
|
dice.roll();
|
||||||
|
rolledDices.add(new Dice(dice));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl());
|
||||||
|
embedBuilder.setTitle("Dice Roll");
|
||||||
|
|
||||||
|
if (RandomUtil.isRandomOrgKeyValid())
|
||||||
|
embedBuilder.setFooter("Seed provided by random.org");
|
||||||
|
|
||||||
|
StringBuilder message = new StringBuilder();
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
int previousDiceSides = 0;
|
||||||
|
for (Dice dice : rolledDices)
|
||||||
|
{
|
||||||
|
int diceSize = dice.getSides();
|
||||||
|
|
||||||
|
if (previousDiceSides != diceSize)
|
||||||
|
{
|
||||||
|
message.append("\nd").append(diceSize).append(": ");
|
||||||
|
previousDiceSides = diceSize;
|
||||||
|
} else if (previousDiceSides != 0)
|
||||||
|
{
|
||||||
|
message.append(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
message.append("`").append(dice.getValue()).append("`");
|
||||||
|
|
||||||
|
total += dice.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
// discord doesn't allow embed fields to be longer than 1024 and errors out
|
||||||
|
if (message.length() > 1024)
|
||||||
|
{
|
||||||
|
return new MessageResponse("Too many rolls!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
embedBuilder.addField("\uD83C\uDFB2 Rolls", message.toString(), false);
|
||||||
|
|
||||||
|
String rolls = totalRolls == 1 ? "roll" : "rolls";
|
||||||
|
|
||||||
|
embedBuilder.addField("✨ Total", totalRolls + " " + rolls + ": " + total, false);
|
||||||
|
|
||||||
|
return new MessageResponse(null, embedBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,15 @@ 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 static MessageEmbed generateEmbed()
|
public MessageEmbed generateEmbed()
|
||||||
{
|
{
|
||||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
@@ -18,7 +20,7 @@ public class Invite
|
|||||||
{
|
{
|
||||||
embedBuilder.setColor(Cache.getBotColor());
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
String avatarUrl = HidekoBot.getAPI().getSelfUser().getAvatarUrl();
|
String avatarUrl = HidekoBot.getAPI().getSelfUser().getAvatarUrl();
|
||||||
if(avatarUrl != null) embedBuilder.setThumbnail(avatarUrl);
|
if (avatarUrl != null) embedBuilder.setThumbnail(avatarUrl);
|
||||||
embedBuilder.setTitle("Invite");
|
embedBuilder.setTitle("Invite");
|
||||||
embedBuilder.appendDescription("Click on the button below to invite " +
|
embedBuilder.appendDescription("Click on the button below to invite " +
|
||||||
Cache.getBotName() +
|
Cache.getBotName() +
|
||||||
@@ -28,7 +30,7 @@ public class Invite
|
|||||||
return embedBuilder.build();
|
return embedBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Button getInviteButton()
|
public Button getInviteButton()
|
||||||
{
|
{
|
||||||
String inviteUrl = Cache.getInviteUrl();
|
String inviteUrl = Cache.getInviteUrl();
|
||||||
return Button.link(inviteUrl, "Invite " + Cache.getBotName())
|
return Button.link(inviteUrl, "Invite " + Cache.getBotName())
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class LoveCalculator
|
||||||
|
{
|
||||||
|
|
||||||
|
public MessageEmbed buildEmbedAndCacheResult(User author, User user1, User user2)
|
||||||
|
{
|
||||||
|
String userId1 = user1.getId();
|
||||||
|
String userId2 = user2.getId();
|
||||||
|
|
||||||
|
Integer loveAmount = Cache.getLoveCalculatorValue(userId1, userId2);
|
||||||
|
if (loveAmount == null)
|
||||||
|
{
|
||||||
|
loveAmount = RandomUtil.getRandomNumber(0, 100);
|
||||||
|
Cache.cacheLoveCalculatorValue(userId1, userId2, loveAmount);
|
||||||
|
Cache.getTaskScheduler().schedule(() ->
|
||||||
|
Cache.removeLoveCalculatorValue(userId1, userId2), 10, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
String formattedAmount = loveAmount + "%";
|
||||||
|
if (loveAmount <= 30) formattedAmount += "... \uD83D\uDE22";
|
||||||
|
else if (loveAmount < 60) formattedAmount += "! \uD83E\uDDD0";
|
||||||
|
else if (loveAmount < 75) formattedAmount += "!!! \uD83E\uDD73";
|
||||||
|
else formattedAmount = "✨ " + formattedAmount + "!!! \uD83D\uDE0D\uD83D\uDCA5";
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl());
|
||||||
|
embedBuilder.setTitle("Love Calculator");
|
||||||
|
|
||||||
|
embedBuilder.addField("\uD83D\uDC65 People",
|
||||||
|
user1.getAsMention() + " & " + user2.getAsMention(),
|
||||||
|
false);
|
||||||
|
|
||||||
|
embedBuilder.addField("❤️\u200D\uD83D\uDD25 Match",
|
||||||
|
formattedAmount,
|
||||||
|
false);
|
||||||
|
|
||||||
|
return embedBuilder.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MagicBall
|
||||||
|
{
|
||||||
|
|
||||||
|
public LinkedList<String> getLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("8ball", "8b", "eightball", "magicball"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<String> answers = new ArrayList<>(
|
||||||
|
Arrays.asList("It is certain.",
|
||||||
|
"It is decidedly so.",
|
||||||
|
"Without a doubt.",
|
||||||
|
"Yes, definitely.",
|
||||||
|
"That would be a yes.",
|
||||||
|
"As I see it, yes.",
|
||||||
|
"Most likely.",
|
||||||
|
"Looks like it.",
|
||||||
|
"Yes.",
|
||||||
|
"Signs point to yes.",
|
||||||
|
"Reply hazy, try again.",
|
||||||
|
"Ask again later.",
|
||||||
|
"Better not tell you now.",
|
||||||
|
"Seems uncertain.",
|
||||||
|
"Concentrate and ask again.",
|
||||||
|
"Don't count on it.",
|
||||||
|
"My answer is no.",
|
||||||
|
"My sources say no.",
|
||||||
|
"Outlook not so good.",
|
||||||
|
"Very doubtful."));
|
||||||
|
|
||||||
|
public String getRandomAnswer()
|
||||||
|
{
|
||||||
|
int answerPos = RandomUtil.getRandomNumber(0, answers.size() - 1);
|
||||||
|
return answers.get(answerPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageEmbed generateEmbed(String question, User author)
|
||||||
|
{
|
||||||
|
// add a question mark at the end, if missing.
|
||||||
|
// this might not always apply but it's fun
|
||||||
|
if (!question.endsWith("?")) question += "?";
|
||||||
|
|
||||||
|
String answer = getRandomAnswer();
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl());
|
||||||
|
embedBuilder.setTitle("Magic Ball");
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.addField("❓ Question", question, false);
|
||||||
|
embedBuilder.addField("\uD83C\uDFB1 Answer", answer, false);
|
||||||
|
|
||||||
|
return embedBuilder.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.utils.ImageProxy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ProfileImage
|
||||||
|
{
|
||||||
|
|
||||||
|
public int parseResolution(int resolution)
|
||||||
|
{
|
||||||
|
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
|
||||||
|
|
||||||
|
// method to find closest value to accepted values
|
||||||
|
int distance = Math.abs(acceptedSizes[0] - resolution);
|
||||||
|
int idx = 0;
|
||||||
|
for (int c = 1; c < acceptedSizes.length; c++)
|
||||||
|
{
|
||||||
|
int cdistance = Math.abs(acceptedSizes[c] - resolution);
|
||||||
|
if (cdistance < distance)
|
||||||
|
{
|
||||||
|
idx = c;
|
||||||
|
distance = cdistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acceptedSizes[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageResponse buildResponse(int resolution, User user, ImageType imageType)
|
||||||
|
{
|
||||||
|
String imageTypeName = imageType.name().toLowerCase();
|
||||||
|
String resolutionString;
|
||||||
|
String imageLink = null;
|
||||||
|
|
||||||
|
User.Profile userProfile = user.retrieveProfile().complete();
|
||||||
|
ImageProxy bannerProxy = userProfile.getBanner();
|
||||||
|
|
||||||
|
if (imageType == ImageType.AVATAR)
|
||||||
|
{
|
||||||
|
resolutionString = resolution + " × " + resolution;
|
||||||
|
imageLink = user.getEffectiveAvatar().getUrl(resolution);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
int verticalRes = 361 * resolution / 1024;
|
||||||
|
resolutionString = resolution + " × " + verticalRes;
|
||||||
|
if (bannerProxy != null)
|
||||||
|
imageLink = bannerProxy.getUrl(resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle("Profile " + imageTypeName);
|
||||||
|
|
||||||
|
embedBuilder.addField("User", user.getAsMention(), false);
|
||||||
|
|
||||||
|
embedBuilder.addField("Current resolution", resolutionString, false);
|
||||||
|
|
||||||
|
// string builder to create a string that links to all available resolutions
|
||||||
|
StringBuilder links = new StringBuilder();
|
||||||
|
for (int pos = 0; pos < acceptedSizes.length; pos++)
|
||||||
|
{
|
||||||
|
int currSize = acceptedSizes[pos];
|
||||||
|
|
||||||
|
String currLink;
|
||||||
|
if (imageType == ImageType.AVATAR)
|
||||||
|
{
|
||||||
|
currLink = user.getEffectiveAvatar().getUrl(currSize);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (bannerProxy == null) break;
|
||||||
|
currLink = bannerProxy.getUrl(currSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
links.append("**[").append(currSize).append("px](").append(currLink).append(")**");
|
||||||
|
if (pos + 1 != acceptedSizes.length) // don't add a separator on the last iteration
|
||||||
|
{
|
||||||
|
links.append(" | ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
embedBuilder.addField("Available resolutions", links.toString(), false);
|
||||||
|
|
||||||
|
|
||||||
|
if (imageLink != null)
|
||||||
|
embedBuilder.setImage(imageLink);
|
||||||
|
|
||||||
|
|
||||||
|
if (imageLink == null)
|
||||||
|
{
|
||||||
|
String error = "I couldn't find " + user.getAsMention() + "'s " + imageTypeName + "!";
|
||||||
|
return new MessageResponse(error, null);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return new MessageResponse(null, embedBuilder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ImageType
|
||||||
|
{
|
||||||
|
AVATAR, BANNER;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/main/java/wtf/beatrice/hidekobot/commands/base/Say.java
Normal file
13
src/main/java/wtf/beatrice/hidekobot/commands/base/Say.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class Say
|
||||||
|
{
|
||||||
|
public Permission getPermission()
|
||||||
|
{
|
||||||
|
return Permission.MESSAGE_MANAGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
329
src/main/java/wtf/beatrice/hidekobot/commands/base/Trivia.java
Normal file
329
src/main/java/wtf/beatrice/hidekobot/commands/base/Trivia.java
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
|
||||||
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.comparators.TriviaCategoryComparator;
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
|
||||||
|
import wtf.beatrice.hidekobot.runnables.TriviaTask;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class Trivia
|
||||||
|
{
|
||||||
|
|
||||||
|
private Trivia()
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Utility class");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(Trivia.class);
|
||||||
|
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";
|
||||||
|
|
||||||
|
public static List<String> channelsRunningTrivia = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
// first string is the channelId, the list contain all users who responded there
|
||||||
|
public static ConcurrentHashMap<String, List<String>> channelAndWhoResponded = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 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 String getTriviaLink(int categoryId)
|
||||||
|
{
|
||||||
|
return TRIVIA_API_LINK + categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCategoriesLink()
|
||||||
|
{
|
||||||
|
return TRIVIA_API_CATEGORIES_LINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNoDMsError()
|
||||||
|
{
|
||||||
|
return "\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTriviaAlreadyRunningError()
|
||||||
|
{
|
||||||
|
// todo nicer looking
|
||||||
|
return "Trivia is already running here!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MessageResponse generateMainScreen()
|
||||||
|
{
|
||||||
|
JSONObject categoriesJson = Trivia.fetchJson(Trivia.getCategoriesLink());
|
||||||
|
if (categoriesJson == null)
|
||||||
|
return new MessageResponse("Error fetching trivia!", null); // todo nicer with emojis
|
||||||
|
List<TriviaCategory> categories = Trivia.parseCategories(categoriesJson);
|
||||||
|
if (categories.isEmpty())
|
||||||
|
return new MessageResponse("Error parsing trivia categories!", null); // todo nicer with emojis
|
||||||
|
|
||||||
|
categories.sort(new TriviaCategoryComparator());
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle("\uD83C\uDFB2 Trivia");
|
||||||
|
embedBuilder.addField("\uD83D\uDCD6 Begin here",
|
||||||
|
"Select a category from the dropdown menu to start a match!",
|
||||||
|
false);
|
||||||
|
embedBuilder.addField("❓ How to play",
|
||||||
|
"A new question gets posted every few seconds." +
|
||||||
|
"\nIf you get it right, you earn points!" +
|
||||||
|
"\nIf you choose a wrong answer, you lose points." +
|
||||||
|
"\nIf you are unsure, you can wait without answering and your score won't change!",
|
||||||
|
false);
|
||||||
|
|
||||||
|
StringSelectMenu.Builder menuBuilder = StringSelectMenu.create("trivia_categories");
|
||||||
|
|
||||||
|
for (TriviaCategory category : categories)
|
||||||
|
{
|
||||||
|
String name = category.categoryName();
|
||||||
|
int id = category.categoryId();
|
||||||
|
menuBuilder.addOption(name, String.valueOf(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MessageResponse(null, embedBuilder.build(), menuBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSONObject fetchJson(String link)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
URL url = new URL(link);
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||||
|
|
||||||
|
String currentChar;
|
||||||
|
StringBuilder jsonStrBuilder = new StringBuilder();
|
||||||
|
while ((currentChar = bufferedReader.readLine()) != null)
|
||||||
|
{
|
||||||
|
jsonStrBuilder.append(currentChar);
|
||||||
|
}
|
||||||
|
bufferedReader.close();
|
||||||
|
return new JSONObject(jsonStrBuilder.toString());
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
LOGGER.error("JSON Parsing Exception", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<TriviaQuestion> parseQuestions(JSONObject jsonObject)
|
||||||
|
{
|
||||||
|
List<TriviaQuestion> questions = new ArrayList<>();
|
||||||
|
|
||||||
|
JSONArray results = jsonObject.getJSONArray("results");
|
||||||
|
|
||||||
|
for (Object currentQuestionGeneric : results)
|
||||||
|
{
|
||||||
|
JSONObject questionJson = (JSONObject) currentQuestionGeneric;
|
||||||
|
String question = StringEscapeUtils.unescapeHtml4(questionJson.getString("question"));
|
||||||
|
String correctAnswer = StringEscapeUtils.unescapeHtml4(questionJson.getString("correct_answer"));
|
||||||
|
|
||||||
|
List<String> incorrectAnswersList = new ArrayList<>();
|
||||||
|
|
||||||
|
JSONArray incorrectAnswers = questionJson.getJSONArray("incorrect_answers");
|
||||||
|
for (Object incorrectAnswerGeneric : incorrectAnswers)
|
||||||
|
{
|
||||||
|
String incorrectAnswer = (String) incorrectAnswerGeneric;
|
||||||
|
incorrectAnswersList.add(StringEscapeUtils.unescapeHtml4(incorrectAnswer));
|
||||||
|
}
|
||||||
|
|
||||||
|
TriviaQuestion triviaQuestion = new TriviaQuestion(question, correctAnswer, incorrectAnswersList);
|
||||||
|
questions.add(triviaQuestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<TriviaCategory> parseCategories(JSONObject jsonObject)
|
||||||
|
{
|
||||||
|
List<TriviaCategory> categories = new ArrayList<>();
|
||||||
|
JSONArray categoriesArray = jsonObject.getJSONArray("trivia_categories");
|
||||||
|
for (Object categoryObject : categoriesArray)
|
||||||
|
{
|
||||||
|
JSONObject categoryJson = (JSONObject) categoryObject;
|
||||||
|
|
||||||
|
String name = categoryJson.getString("name");
|
||||||
|
int id = categoryJson.getInt("id");
|
||||||
|
|
||||||
|
categories.add(new TriviaCategory(name, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
String channelId = event.getChannel().getId();
|
||||||
|
|
||||||
|
if (trackResponse(user, event.getChannel()))
|
||||||
|
{
|
||||||
|
LinkedList<TriviaScore> scores = channelAndScores.get(channelId);
|
||||||
|
if (scores == null) scores = new LinkedList<>();
|
||||||
|
|
||||||
|
TriviaScore currentUserScore = null;
|
||||||
|
for (TriviaScore score : scores)
|
||||||
|
{
|
||||||
|
if (score.getUser().equals(user))
|
||||||
|
{
|
||||||
|
currentUserScore = score;
|
||||||
|
scores.remove(score);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUserScore == null)
|
||||||
|
{
|
||||||
|
currentUserScore = new TriviaScore(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (answerType.equals(AnswerType.CORRECT))
|
||||||
|
{
|
||||||
|
// Public message in channel
|
||||||
|
event.getChannel().sendMessage(user.getAsMention() + " got it right! \uD83E\uDD73 (**+3**)").queue();
|
||||||
|
currentUserScore.changeScore(3);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
event.getChannel().sendMessage("❌ " + user.getAsMention() + ", that's not the right answer! (**-1**)").queue();
|
||||||
|
currentUserScore.changeScore(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
scores.add(currentUserScore);
|
||||||
|
channelAndScores.put(channelId, scores);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// Show the warning **in the original ephemeral message**, then delete it after 5s.
|
||||||
|
hook.editOriginal("☹️ " + user.getAsMention() + ", you can't answer twice!").queue(v ->
|
||||||
|
hook.deleteOriginal().queueAfter(3, TimeUnit.SECONDS, null, __ -> {
|
||||||
|
})
|
||||||
|
);
|
||||||
|
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
|
||||||
|
hook.deleteOriginal().queue(null, __ -> {
|
||||||
|
});
|
||||||
|
}, __ -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized boolean trackResponse(User user, MessageChannel channel)
|
||||||
|
{
|
||||||
|
String userId = user.getId();
|
||||||
|
String channelId = channel.getId();
|
||||||
|
|
||||||
|
List<String> responders = channelAndWhoResponded.get(channelId);
|
||||||
|
|
||||||
|
if (responders == null)
|
||||||
|
{
|
||||||
|
responders = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responders.isEmpty() || !responders.contains(userId))
|
||||||
|
{
|
||||||
|
responders.add(userId);
|
||||||
|
channelAndWhoResponded.put(channelId, responders);
|
||||||
|
return true; // response was successfully tracked
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return false; // response wasn't tracked because there already was an entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if (!(Cache.getServices().databaseService().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
|
||||||
|
{
|
||||||
|
hook.sendMessage("❌ You did not run this command!").setEphemeral(true).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable buttons on the original message via service (uses separate REST calls)
|
||||||
|
Cache.getServices().commandService().disableExpired(event.getMessageId());
|
||||||
|
|
||||||
|
SelectOption pickedOption = event.getInteraction().getSelectedOptions().get(0);
|
||||||
|
String categoryName = pickedOption.getLabel();
|
||||||
|
String categoryIdString = pickedOption.getValue();
|
||||||
|
Integer categoryId = Integer.parseInt(categoryIdString);
|
||||||
|
|
||||||
|
TriviaCategory category = new TriviaCategory(categoryName, categoryId);
|
||||||
|
|
||||||
|
startTrivia(event, category);
|
||||||
|
|
||||||
|
// remove the ephemeral deferral to keep things clean
|
||||||
|
hook.deleteOriginal().queue(null, __ -> {
|
||||||
|
});
|
||||||
|
}, __ -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startTrivia(StringSelectInteractionEvent event, TriviaCategory category)
|
||||||
|
{
|
||||||
|
User author = event.getUser();
|
||||||
|
Message message = event.getMessage();
|
||||||
|
MessageChannel channel = message.getChannel();
|
||||||
|
|
||||||
|
if (Trivia.channelsRunningTrivia.contains(channel.getId()))
|
||||||
|
{
|
||||||
|
// Already running: inform ephemerally via hook (the interaction was deferred in the caller)
|
||||||
|
event.getHook().sendMessage(Trivia.getTriviaAlreadyRunningError())
|
||||||
|
.setEphemeral(true)
|
||||||
|
.queue(msg -> Cache.getTaskScheduler().schedule(() -> msg.delete().queue(), 10, TimeUnit.SECONDS));
|
||||||
|
return;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// Public info that a new session is starting
|
||||||
|
channel.sendMessage("Starting new Trivia session!").queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
TriviaTask triviaTask = new TriviaTask(author, channel, category,
|
||||||
|
Cache.getServices().databaseService(), Cache.getServices().commandService());
|
||||||
|
ScheduledFuture<?> future =
|
||||||
|
Cache.getTaskScheduler().scheduleAtFixedRate(triviaTask,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
|
triviaTask.setScheduledFuture(future);
|
||||||
|
|
||||||
|
Trivia.channelsRunningTrivia.add(channel.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AnswerType
|
||||||
|
{
|
||||||
|
CORRECT, WRONG
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,347 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
|
import org.apache.commons.text.WordUtils;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.services.DatabaseService;
|
||||||
|
import wtf.beatrice.hidekobot.util.SerializationUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UrbanDictionary
|
||||||
|
{
|
||||||
|
|
||||||
|
private UrbanDictionary()
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Utility class");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("urban", "urbandictionary", "ud"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getBaseUrl()
|
||||||
|
{
|
||||||
|
return "https://www.urbandictionary.com/define.php?term=";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Button getPreviousPageButton()
|
||||||
|
{
|
||||||
|
return Button.primary("urban_previouspage", "Back")
|
||||||
|
.withEmoji(Emoji.fromFormatted("⬅️"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Button getNextPageButton()
|
||||||
|
{
|
||||||
|
return Button.primary("urban_nextpage", "Next")
|
||||||
|
.withEmoji(Emoji.fromFormatted("➡️"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Button getDeleteButton()
|
||||||
|
{
|
||||||
|
return Button.danger("generic_dismiss", "Delete")
|
||||||
|
.withEmoji(Emoji.fromFormatted("\uD83D\uDDD1️"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNoArgsError()
|
||||||
|
{
|
||||||
|
return "\uD83D\uDE22 I need to know what to search for!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String sanitizeArgs(String term, boolean forUrl)
|
||||||
|
{
|
||||||
|
term = term.replaceAll("[^\\w\\s]", ""); // only keep letters, numbers and spaces
|
||||||
|
term = WordUtils.capitalizeFully(term); // Make Every Word Start With A Capital Letter
|
||||||
|
if (forUrl) term = term.replaceAll("\\s+", "+"); // replace all whitespaces with + for the url
|
||||||
|
if (term.length() > 64) term = term.substring(0, 64); // cut it to length to avoid abuse
|
||||||
|
return term;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateUrl(String term)
|
||||||
|
{
|
||||||
|
return getBaseUrl() + sanitizeArgs(term, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MessageEmbed buildEmbed(String term,
|
||||||
|
String url,
|
||||||
|
User author,
|
||||||
|
UrbanSearch search,
|
||||||
|
int page)
|
||||||
|
{
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle(term + ", on Urban Dictionary", url);
|
||||||
|
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl());
|
||||||
|
embedBuilder.addField("\uD83D\uDCD6 Definition", search.getPlaintextMeanings().get(page), false);
|
||||||
|
embedBuilder.addField("\uD83D\uDCAD Example", search.getPlaintextExamples().get(page), false);
|
||||||
|
embedBuilder.addField("\uD83D\uDCCC Submission",
|
||||||
|
"*Entry " + (page + 1) + " | Sent by " + search.getContributorsNames().get(page) +
|
||||||
|
" on" + search.getSubmissionDates().get(page) + "*",
|
||||||
|
false);
|
||||||
|
|
||||||
|
return embedBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getTermNotFoundError()
|
||||||
|
{
|
||||||
|
return "\uD83D\uDE22 I couldn't find that term!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void track(Message message, User user, UrbanSearch search, String sanitizedTerm)
|
||||||
|
{
|
||||||
|
Cache.getServices().databaseService().queueDisabling(message);
|
||||||
|
Cache.getServices().databaseService().trackRanCommandReply(message, user);
|
||||||
|
Cache.getServices().databaseService().trackUrban(search.getSerializedMeanings(),
|
||||||
|
search.getSerializedExamples(),
|
||||||
|
search.getSerializedContributors(),
|
||||||
|
search.getSerializedDates(),
|
||||||
|
message,
|
||||||
|
sanitizedTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void changePage(ButtonInteractionEvent event, ChangeType changeType)
|
||||||
|
{
|
||||||
|
event.deferEdit().queue();
|
||||||
|
String messageId = event.getMessageId();
|
||||||
|
DatabaseService database = Cache.getServices().databaseService();
|
||||||
|
|
||||||
|
// check if the user interacting is the same one who ran the command
|
||||||
|
if (!(database.isUserTrackedFor(event.getUser().getId(), messageId)))
|
||||||
|
{
|
||||||
|
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current page and calculate how many pages there are
|
||||||
|
int page = database.getUrbanPage(messageId);
|
||||||
|
|
||||||
|
String term = database.getUrbanTerm(messageId);
|
||||||
|
String url = generateUrl(term);
|
||||||
|
|
||||||
|
// get serialized parameters
|
||||||
|
String serializedMeanings = database.getUrbanMeanings(messageId);
|
||||||
|
String serializedExamples = database.getUrbanExamples(messageId);
|
||||||
|
String serializedContributors = database.getUrbanContributors(messageId);
|
||||||
|
String serializedDates = database.getUrbanDates(messageId);
|
||||||
|
|
||||||
|
// construct object by passing serialized parameters
|
||||||
|
UrbanSearch search = new UrbanSearch(serializedMeanings,
|
||||||
|
serializedExamples, serializedContributors, serializedDates);
|
||||||
|
|
||||||
|
// move to new page
|
||||||
|
if (changeType == ChangeType.NEXT)
|
||||||
|
page++;
|
||||||
|
else if (changeType == ChangeType.PREVIOUS)
|
||||||
|
page--;
|
||||||
|
|
||||||
|
term = UrbanDictionary.sanitizeArgs(term, false);
|
||||||
|
|
||||||
|
// generate embed with new results
|
||||||
|
MessageEmbed updatedEmbed = UrbanDictionary.buildEmbed(term, url, event.getUser(), search, page);
|
||||||
|
|
||||||
|
// get all attached components and check which ones need to be enabled or disabled
|
||||||
|
List<ItemComponent> components = new ArrayList<>();
|
||||||
|
|
||||||
|
if (page > 0)
|
||||||
|
{
|
||||||
|
components.add(UrbanDictionary.getPreviousPageButton().asEnabled());
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
components.add(UrbanDictionary.getPreviousPageButton().asDisabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page + 1 == search.getPages())
|
||||||
|
{
|
||||||
|
components.add(UrbanDictionary.getNextPageButton().asDisabled());
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
components.add(UrbanDictionary.getNextPageButton().asEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the components on the object
|
||||||
|
components.add(UrbanDictionary.getDeleteButton());
|
||||||
|
ActionRow currentRow = ActionRow.of(components);
|
||||||
|
|
||||||
|
// update the message
|
||||||
|
event.getHook().editOriginalEmbeds(updatedEmbed)
|
||||||
|
.setComponents(currentRow)
|
||||||
|
.queue();
|
||||||
|
database.setUrbanPage(messageId, page);
|
||||||
|
database.resetExpiryTimestamp(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UrbanSearch
|
||||||
|
{
|
||||||
|
final LinkedList<String> plaintextMeanings;
|
||||||
|
final LinkedList<String> plaintextExamples;
|
||||||
|
final LinkedList<String> contributorsNames;
|
||||||
|
final LinkedList<String> submissionDates;
|
||||||
|
|
||||||
|
final String serializedMeanings;
|
||||||
|
final String serializedExamples;
|
||||||
|
final String serializedContributors;
|
||||||
|
final String serializedDates;
|
||||||
|
|
||||||
|
final int pages;
|
||||||
|
|
||||||
|
public UrbanSearch(String serializedMeanings,
|
||||||
|
String serializedExamples,
|
||||||
|
String serializedContributors,
|
||||||
|
String serializedDates)
|
||||||
|
{
|
||||||
|
this.serializedMeanings = serializedMeanings;
|
||||||
|
this.serializedExamples = serializedExamples;
|
||||||
|
this.serializedContributors = serializedContributors;
|
||||||
|
this.serializedDates = serializedDates;
|
||||||
|
|
||||||
|
this.plaintextMeanings = SerializationUtil.deserializeBase64(serializedMeanings);
|
||||||
|
this.plaintextExamples = SerializationUtil.deserializeBase64(serializedExamples);
|
||||||
|
this.contributorsNames = SerializationUtil.deserializeBase64(serializedContributors);
|
||||||
|
this.submissionDates = SerializationUtil.deserializeBase64(serializedDates);
|
||||||
|
|
||||||
|
this.pages = submissionDates.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UrbanSearch(Elements definitions)
|
||||||
|
{
|
||||||
|
plaintextMeanings = new LinkedList<>();
|
||||||
|
plaintextExamples = new LinkedList<>();
|
||||||
|
contributorsNames = new LinkedList<>();
|
||||||
|
submissionDates = new LinkedList<>();
|
||||||
|
|
||||||
|
for (Element definition : definitions)
|
||||||
|
{
|
||||||
|
Elements meaningSingleton = definition.getElementsByClass("meaning");
|
||||||
|
if (meaningSingleton.isEmpty())
|
||||||
|
{
|
||||||
|
plaintextMeanings.add(" ");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Element meaning = meaningSingleton.get(0);
|
||||||
|
String text = meaning.html()
|
||||||
|
.replaceAll("<br\\s*?>", "\n") // keep newlines
|
||||||
|
.replaceAll("<.*?>", ""); // remove all other html tags
|
||||||
|
// this is used to fix eg. & being shown literally instead of being parsed
|
||||||
|
text = StringEscapeUtils.unescapeHtml4(text);
|
||||||
|
// discord only allows 1024 characters for embed fields
|
||||||
|
if (text.length() > 1024) text = text.substring(0, 1020) + "...";
|
||||||
|
plaintextMeanings.add(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Elements exampleSingleton = definition.getElementsByClass("example");
|
||||||
|
|
||||||
|
if (exampleSingleton.isEmpty())
|
||||||
|
{
|
||||||
|
plaintextExamples.add(" ");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Element example = exampleSingleton.get(0);
|
||||||
|
String text = example.html()
|
||||||
|
.replaceAll("<br\\s*?>", "\n") // keep newlines
|
||||||
|
.replaceAll("<.*?>", ""); // remove all other html tags
|
||||||
|
// this is used to fix eg. & being shown literally instead of being parsed
|
||||||
|
text = StringEscapeUtils.unescapeHtml4(text);
|
||||||
|
// discord only allows 1024 characters for embed fields
|
||||||
|
if (text.length() > 1024) text = text.substring(0, 1020) + "...";
|
||||||
|
plaintextExamples.add(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Elements contributorSingleton = definition.getElementsByClass("contributor");
|
||||||
|
if (contributorSingleton.isEmpty())
|
||||||
|
{
|
||||||
|
contributorsNames.add("Unknown");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Element contributor = contributorSingleton.get(0);
|
||||||
|
|
||||||
|
String htmlContributor = contributor.html();
|
||||||
|
String htmlContributorName = contributor.select("a").html();
|
||||||
|
String htmlSubmitDate = htmlContributor.substring(
|
||||||
|
htmlContributor.indexOf("</a>") + 4);
|
||||||
|
|
||||||
|
contributorsNames.add(htmlContributorName
|
||||||
|
.replaceAll("<.*?>", "")); // remove all html tags
|
||||||
|
|
||||||
|
submissionDates.add(htmlSubmitDate
|
||||||
|
.replaceAll("<.*?>", "")); // remove all html tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedMeanings = SerializationUtil.serializeBase64(plaintextMeanings);
|
||||||
|
serializedExamples = SerializationUtil.serializeBase64(plaintextExamples);
|
||||||
|
serializedContributors = SerializationUtil.serializeBase64(contributorsNames);
|
||||||
|
serializedDates = SerializationUtil.serializeBase64(submissionDates);
|
||||||
|
|
||||||
|
pages = submissionDates.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPlaintextMeanings()
|
||||||
|
{
|
||||||
|
return this.plaintextMeanings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPlaintextExamples()
|
||||||
|
{
|
||||||
|
return this.plaintextExamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getContributorsNames()
|
||||||
|
{
|
||||||
|
return this.contributorsNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSubmissionDates()
|
||||||
|
{
|
||||||
|
return this.submissionDates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSerializedMeanings()
|
||||||
|
{
|
||||||
|
return serializedMeanings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSerializedExamples()
|
||||||
|
{
|
||||||
|
return serializedExamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSerializedContributors()
|
||||||
|
{
|
||||||
|
return serializedContributors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSerializedDates()
|
||||||
|
{
|
||||||
|
return serializedDates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPages()
|
||||||
|
{
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ChangeType
|
||||||
|
{
|
||||||
|
NEXT,
|
||||||
|
PREVIOUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.base;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.IMentionable;
|
||||||
|
import net.dv8tion.jda.api.entities.Mentions;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.HidekoBot;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.util.FormatUtil;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserPunishment
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final Duration maxTimeoutDuration = Duration.of(28, ChronoUnit.DAYS);
|
||||||
|
private static final Duration minTimeoutDuration = Duration.of(30, ChronoUnit.SECONDS);
|
||||||
|
|
||||||
|
public void handle(SlashCommandInteractionEvent event, PunishmentType punishmentType)
|
||||||
|
{
|
||||||
|
// this might take a sec
|
||||||
|
event.deferReply().queue();
|
||||||
|
|
||||||
|
User targetUser = null;
|
||||||
|
|
||||||
|
OptionMapping targetUserArg = event.getOption("target");
|
||||||
|
if (targetUserArg != null)
|
||||||
|
{
|
||||||
|
targetUser = targetUserArg.getAsUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IMentionable> mentions = null;
|
||||||
|
if (targetUser != null) mentions = new ArrayList<>(Collections.singletonList(targetUser));
|
||||||
|
|
||||||
|
String reason = null;
|
||||||
|
OptionMapping reasonArg = event.getOption("reason");
|
||||||
|
if (reasonArg != null)
|
||||||
|
{
|
||||||
|
reason = reasonArg.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String timeDiff = null;
|
||||||
|
OptionMapping timeDiffArg = event.getOption("duration");
|
||||||
|
if (timeDiffArg != null)
|
||||||
|
{
|
||||||
|
timeDiff = timeDiffArg.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: the following code is not great, because we are making an array and then
|
||||||
|
// we are also recreating the string later in code. this is useless and a bit hacked on,
|
||||||
|
// but works for now. this happened because the function was NOT written with slash commands
|
||||||
|
// in mind, but with message commands, that send every word as a separate argument.
|
||||||
|
// we should probably rework the it so that it works better in both scenarios.
|
||||||
|
String[] reasonSplit = null;
|
||||||
|
// generate the arguments array by splitting the string
|
||||||
|
if (reason != null) reasonSplit = reason.split("\\s+");
|
||||||
|
//prepend timediff at index 0
|
||||||
|
if (timeDiff != null) reasonSplit = ArrayUtils.insert(0, reasonSplit, timeDiff);
|
||||||
|
// in message-commands, the first arg would contain the user mention. since we have no one mentioned here,
|
||||||
|
// because it's in its own argument, we just prepend an empty string. note that this makes relying on the
|
||||||
|
// first argument BAD, because it is no longer ensured that it contains the user mention.
|
||||||
|
if (timeDiff != null) reasonSplit = ArrayUtils.insert(0, reasonSplit, "");
|
||||||
|
|
||||||
|
MessageResponse response = getResponse(event.getUser(),
|
||||||
|
punishmentType,
|
||||||
|
event.getChannel(),
|
||||||
|
mentions,
|
||||||
|
reasonSplit);
|
||||||
|
|
||||||
|
if (response.embed() != null)
|
||||||
|
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||||
|
else if (response.content() != null)
|
||||||
|
event.getHook().editOriginal(response.content()).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(MessageReceivedEvent event, String[] args, PunishmentType punishmentType)
|
||||||
|
{
|
||||||
|
Mentions msgMentions = event.getMessage().getMentions();
|
||||||
|
List<IMentionable> mentions = msgMentions.getMentions();
|
||||||
|
|
||||||
|
MessageResponse response = getResponse(event.getAuthor(),
|
||||||
|
punishmentType,
|
||||||
|
event.getChannel(),
|
||||||
|
mentions,
|
||||||
|
args);
|
||||||
|
|
||||||
|
if (response.embed() != null)
|
||||||
|
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||||
|
else if (response.content() != null)
|
||||||
|
event.getMessage().reply(response.content()).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageResponse getResponse(User author,
|
||||||
|
PunishmentType punishmentType,
|
||||||
|
MessageChannelUnion channel,
|
||||||
|
List<IMentionable> mentions,
|
||||||
|
String[] args)
|
||||||
|
{
|
||||||
|
String punishmentTypeName = punishmentType.name().toLowerCase();
|
||||||
|
|
||||||
|
|
||||||
|
if (!(channel instanceof TextChannel))
|
||||||
|
{
|
||||||
|
// todo nicer looking with emojis
|
||||||
|
return new MessageResponse("Sorry! I can't " + punishmentTypeName + " people in DMs.", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mentions == null || mentions.isEmpty())
|
||||||
|
{
|
||||||
|
// todo nicer looking with emojis
|
||||||
|
return new MessageResponse("You have to tell me who to " + punishmentTypeName + "!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
String mentionedId = mentions.get(0).getId();
|
||||||
|
User mentioned = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mentioned = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
|
||||||
|
} catch (RuntimeException ignored)
|
||||||
|
{
|
||||||
|
// todo nicer looking with emojis
|
||||||
|
return new MessageResponse("I can't " + punishmentTypeName + " that user!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder reasonBuilder = new StringBuilder();
|
||||||
|
String reason = "";
|
||||||
|
|
||||||
|
// some commands require an additional parameter before the reason, so in that case, we should start at 2.
|
||||||
|
int startingPoint = punishmentType == PunishmentType.TIMEOUT ? 2 : 1;
|
||||||
|
|
||||||
|
if (args != null && args.length > startingPoint)
|
||||||
|
{
|
||||||
|
for (int i = startingPoint; i < args.length; i++)
|
||||||
|
{
|
||||||
|
String arg = args[i];
|
||||||
|
reasonBuilder.append(arg);
|
||||||
|
|
||||||
|
if (i + 1 != arg.length())
|
||||||
|
reasonBuilder.append(" "); // separate args with a space except on last iteration.
|
||||||
|
}
|
||||||
|
|
||||||
|
reason = reasonBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mentioned == null)
|
||||||
|
{
|
||||||
|
// todo nicer looking with emojis
|
||||||
|
return new MessageResponse("I can't " + punishmentTypeName + " that user!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Guild guild = ((TextChannel) channel).getGuild();
|
||||||
|
Duration duration = null;
|
||||||
|
|
||||||
|
AuditableRestAction<Void> punishmentAction = null;
|
||||||
|
boolean impossible = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (punishmentType)
|
||||||
|
{
|
||||||
|
case BAN -> punishmentAction = guild.ban(mentioned, 0, TimeUnit.SECONDS);
|
||||||
|
case KICK -> punishmentAction = guild.kick(mentioned);
|
||||||
|
case TIMEOUT ->
|
||||||
|
{
|
||||||
|
// 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];
|
||||||
|
duration = FormatUtil.parseDuration(durationStr);
|
||||||
|
|
||||||
|
boolean isDurationValid = true;
|
||||||
|
|
||||||
|
if (duration == null) isDurationValid = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (duration.compareTo(maxTimeoutDuration) > 0) isDurationValid = false;
|
||||||
|
if (minTimeoutDuration.compareTo(duration) > 0) isDurationValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDurationValid)
|
||||||
|
{
|
||||||
|
// todo nicer looking with emojis
|
||||||
|
return new MessageResponse("Sorry, but the specified duration is invalid!", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
punishmentAction = guild.timeoutFor(mentioned, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RuntimeException ignored)
|
||||||
|
{
|
||||||
|
impossible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (punishmentAction == null)
|
||||||
|
impossible = true;
|
||||||
|
|
||||||
|
if (impossible)
|
||||||
|
{
|
||||||
|
// todo nicer looking with emojis
|
||||||
|
return new MessageResponse("Sorry, I couldn't " + punishmentTypeName + " " + mentioned.getAsMention() + "!",
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reason.isEmpty() && !reasonBuilder.isEmpty())
|
||||||
|
punishmentAction.reason("[" + author.getName() + "] " + reason);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
punishmentAction.complete();
|
||||||
|
} catch (RuntimeException ignored)
|
||||||
|
{
|
||||||
|
// todo nicer looking with emojis
|
||||||
|
return new MessageResponse("Sorry, I couldn't " + punishmentTypeName + " " + mentioned.getAsMention() + "!",
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
|
embedBuilder.setAuthor(author.getName(), null, author.getAvatarUrl());
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle("User " + punishmentType.getPastTense());
|
||||||
|
|
||||||
|
embedBuilder.addField("\uD83D\uDC64 User", mentioned.getAsMention(), false);
|
||||||
|
embedBuilder.addField("✂️ By", author.getAsMention(), false);
|
||||||
|
if (duration != null)
|
||||||
|
embedBuilder.addField("⏱️ Duration", FormatUtil.getNiceDuration(duration), false);
|
||||||
|
|
||||||
|
if (reason.isEmpty())
|
||||||
|
reason = "*No reason specified*";
|
||||||
|
|
||||||
|
embedBuilder.addField("\uD83D\uDCD6 Reason", reason, false);
|
||||||
|
|
||||||
|
|
||||||
|
return new MessageResponse(null, embedBuilder.build());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PunishmentType
|
||||||
|
{
|
||||||
|
KICK("kicked"),
|
||||||
|
BAN("banned"),
|
||||||
|
TIMEOUT("timed out"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private final String pastTense;
|
||||||
|
|
||||||
|
PunishmentType(String pastTense)
|
||||||
|
{
|
||||||
|
this.pastTense = pastTense;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPastTense()
|
||||||
|
{
|
||||||
|
return pastTense;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,26 +10,28 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AvatarCommandCompleter extends SlashArgumentsCompleterImpl
|
public class ProfileImageCommandCompleter extends SlashArgumentsCompleterImpl
|
||||||
{
|
{
|
||||||
|
|
||||||
public AvatarCommandCompleter(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"))
|
||||||
{
|
{
|
||||||
|
|
||||||
List<Command.Choice> options = new ArrayList<>();
|
List<Command.Choice> options = new ArrayList<>();
|
||||||
|
|
||||||
for(int res : Cache.getSupportedAvatarResolutions())
|
for (int res : Cache.getSupportedAvatarResolutions())
|
||||||
{
|
{
|
||||||
String resString = String.valueOf(res);
|
String resString = String.valueOf(res);
|
||||||
String userInput = event.getFocusedOption().getValue();
|
String userInput = event.getFocusedOption().getValue();
|
||||||
|
|
||||||
if(resString.startsWith(userInput))
|
if (resString.startsWith(userInput))
|
||||||
options.add(new Command.Choice(resString, res));
|
options.add(new Command.Choice(resString, res));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package wtf.beatrice.hidekobot.commands.message;
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.Permission;
|
|
||||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import wtf.beatrice.hidekobot.Cache;
|
|
||||||
import wtf.beatrice.hidekobot.commands.base.BotInfo;
|
|
||||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BotInfoCommand implements MessageCommand
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LinkedList<String> getCommandLabels() {
|
|
||||||
return new LinkedList<>(Collections.singletonList("botinfo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public List<Permission> getPermissions() {
|
|
||||||
return null; // anyone can use it
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean passRawArgs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void runCommand(MessageReceivedEvent event, String label, String[] args) {
|
|
||||||
|
|
||||||
// get a list of message commands
|
|
||||||
LinkedList<MessageCommand> messageCommands = Cache.getMessageCommandListener().getRegisteredCommands();
|
|
||||||
LinkedList<String> commandNames = new LinkedList<>();
|
|
||||||
for (MessageCommand command : messageCommands) {
|
|
||||||
commandNames.add(command.getCommandLabels().get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// send the list
|
|
||||||
MessageEmbed embed = BotInfo.generateEmbed(commandNames);
|
|
||||||
event.getMessage().replyEmbeds(embed).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package wtf.beatrice.hidekobot.commands.message;
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.Permission;
|
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
|
||||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
|
||||||
import wtf.beatrice.hidekobot.Cache;
|
|
||||||
import wtf.beatrice.hidekobot.commands.base.ClearChat;
|
|
||||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ClearCommand implements MessageCommand
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LinkedList<String> getCommandLabels() {
|
|
||||||
return new LinkedList<>(Collections.singletonList(ClearChat.getLabel()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Permission> getPermissions() { return Collections.singletonList(ClearChat.getPermission()); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean passRawArgs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
|
||||||
{
|
|
||||||
String senderId = event.getMessage().getAuthor().getId();
|
|
||||||
|
|
||||||
// check if user is trying to run command in dms.
|
|
||||||
String error = ClearChat.checkDMs(event.getChannel());
|
|
||||||
if (error != null) {
|
|
||||||
event.getMessage().reply(error).queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the amount from the command args.
|
|
||||||
Integer toDeleteAmount;
|
|
||||||
if (args.length == 0) toDeleteAmount = 1;
|
|
||||||
else toDeleteAmount = Integer.parseInt(args[0]);
|
|
||||||
|
|
||||||
error = ClearChat.checkDeleteAmount(toDeleteAmount);
|
|
||||||
if (error != null) {
|
|
||||||
event.getMessage().reply(error).queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// answer by saying that the operation has begun.
|
|
||||||
String content = "\uD83D\uDEA7 Clearing...";
|
|
||||||
Message botMessage = event.getMessage().reply(content).complete();
|
|
||||||
|
|
||||||
int deleted = ClearChat.delete(toDeleteAmount,
|
|
||||||
event.getMessageIdLong(),
|
|
||||||
event.getChannel());
|
|
||||||
|
|
||||||
// get a nicely formatted message that logs the deletion of messages.
|
|
||||||
content = ClearChat.parseAmount(deleted);
|
|
||||||
|
|
||||||
// edit the message text and attach a button.
|
|
||||||
Button dismiss = ClearChat.getDismissButton();
|
|
||||||
// ^ todo: maybe the dismiss button should also delete the original message sent by the user?
|
|
||||||
// todo: but then, we need to differentiate between command type in the database, and store
|
|
||||||
// todo: that message's id too.
|
|
||||||
botMessage = botMessage.editMessage(content).setActionRow(dismiss).complete();
|
|
||||||
|
|
||||||
// add the message to database.
|
|
||||||
Cache.getDatabaseSource().queueDisabling(botMessage);
|
|
||||||
Cache.getDatabaseSource().trackRanCommandReply(botMessage, event.getAuthor());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package wtf.beatrice.hidekobot.commands.message;
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.Permission;
|
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import wtf.beatrice.hidekobot.commands.base.CoinFlip;
|
|
||||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CoinFlipCommand implements MessageCommand
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LinkedList<String> getCommandLabels() {
|
|
||||||
return new LinkedList<>(Arrays.asList("coinflip", "flip", "flipcoin"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public List<Permission> getPermissions() {
|
|
||||||
return null; // null because it can be used anywhere
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean passRawArgs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void runCommand(MessageReceivedEvent event, String label, String[] args) {
|
|
||||||
|
|
||||||
// perform coin flip
|
|
||||||
event.getMessage().reply(CoinFlip.genRandom())
|
|
||||||
.addActionRow(CoinFlip.getReflipButton())
|
|
||||||
.queue((message) ->
|
|
||||||
{
|
|
||||||
// set the command as expiring and restrict it to the user who ran it
|
|
||||||
CoinFlip.trackAndRestrict(message, event.getAuthor());
|
|
||||||
}, (error) -> {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package wtf.beatrice.hidekobot.commands.message;
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.Permission;
|
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
|
||||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class HelloCommand implements MessageCommand
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LinkedList<String> getCommandLabels() {
|
|
||||||
return new LinkedList<>(Arrays.asList("hi", "hello", "heya"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Permission> getPermissions() { return null; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean passRawArgs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
|
||||||
{
|
|
||||||
String senderId = event.getMessage().getAuthor().getId();
|
|
||||||
event.getMessage().reply("Hi, <@" + senderId + ">! :sparkles:").queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.commands.base.Alias;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageAliasCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
private final Alias alias;
|
||||||
|
|
||||||
|
public MessageAliasCommand(@Autowired Alias alias)
|
||||||
|
{
|
||||||
|
this.alias = alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("alias", "aliases"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; // anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.TOOLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "See other command aliases.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<command>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
if (args.length == 0)
|
||||||
|
{
|
||||||
|
event.getMessage().reply("\uD83D\uDE20 Hey, you have to specify a command!").queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String commandLabel = args[0].toLowerCase();
|
||||||
|
MessageCommand command = Cache.getMessageCommandListener().getRegisteredCommand(commandLabel);
|
||||||
|
if (command == null)
|
||||||
|
{
|
||||||
|
event.getMessage().reply("Unrecognized command: `" + commandLabel + "`!").queue(); // todo prettier
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String aliases = alias.generateNiceAliases(command);
|
||||||
|
aliases = "Aliases for **" + command.getCommandLabels().get(0) + "**: " + aliases;
|
||||||
|
|
||||||
|
event.getMessage()
|
||||||
|
.reply(aliases)
|
||||||
|
.queue();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Mentions;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.commands.base.ProfileImage;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageAvatarCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final ProfileImage profileImage;
|
||||||
|
|
||||||
|
public MessageAvatarCommand(@Autowired ProfileImage profileImage)
|
||||||
|
{
|
||||||
|
this.profileImage = profileImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("avatar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; // anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Get someone's avatar, or your own. You can additionally specify a resolution.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "[mentioned user] [resolution]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.TOOLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
User user;
|
||||||
|
int resolution = -1;
|
||||||
|
|
||||||
|
// we have no specific order for user and resolution, so let's try parsing any arg as resolution
|
||||||
|
// (mentions are handled differently by a specific method)
|
||||||
|
boolean resFound = false;
|
||||||
|
|
||||||
|
for (String arg : args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int givenRes = Integer.parseInt(arg);
|
||||||
|
resolution = profileImage.parseResolution(givenRes);
|
||||||
|
resFound = true;
|
||||||
|
break;
|
||||||
|
} catch (NumberFormatException ignored)
|
||||||
|
{
|
||||||
|
// ignored because we're running a check after this block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback in case we didn't find any specified resolution
|
||||||
|
if (!resFound) resolution = profileImage.parseResolution(512);
|
||||||
|
|
||||||
|
// check if someone is mentioned
|
||||||
|
Mentions mentions = event.getMessage().getMentions();
|
||||||
|
if (mentions.getMentions().isEmpty())
|
||||||
|
{
|
||||||
|
user = event.getAuthor();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
String mentionedId = mentions.getMentions().get(0).getId();
|
||||||
|
user = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case of issues, fallback to the sender
|
||||||
|
if (user == null) user = event.getAuthor();
|
||||||
|
|
||||||
|
// send a response
|
||||||
|
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
|
||||||
|
if (response.content() != null)
|
||||||
|
{
|
||||||
|
event.getMessage().reply(response.content()).queue();
|
||||||
|
} else if (response.embed() != null)
|
||||||
|
{
|
||||||
|
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageBanCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final UserPunishment userPunishment;
|
||||||
|
|
||||||
|
public MessageBanCommand(@Autowired UserPunishment userPunishment)
|
||||||
|
{
|
||||||
|
this.userPunishment = userPunishment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("ban"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return new ArrayList<Permission>(Collections.singletonList(Permission.BAN_MEMBERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.MODERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Ban the mentioned user.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<mentioned user> [reason]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
userPunishment.handle(event, args, UserPunishment.PunishmentType.BAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Mentions;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.commands.base.ProfileImage;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageBannerCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final ProfileImage profileImage;
|
||||||
|
|
||||||
|
public MessageBannerCommand(@Autowired ProfileImage profileImage)
|
||||||
|
{
|
||||||
|
this.profileImage = profileImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("banner"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; // anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Get someone's profile banner, or your own.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "[mentioned user] [resolution]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.TOOLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
User user;
|
||||||
|
int resolution = -1;
|
||||||
|
|
||||||
|
// we have no specific order for user and resolution, so let's try parsing any arg as resolution
|
||||||
|
// (mentions are handled differently by a specific method)
|
||||||
|
boolean resFound = false;
|
||||||
|
|
||||||
|
for (String arg : args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int givenRes = Integer.parseInt(arg);
|
||||||
|
resolution = profileImage.parseResolution(givenRes);
|
||||||
|
resFound = true;
|
||||||
|
break;
|
||||||
|
} catch (NumberFormatException ignored)
|
||||||
|
{
|
||||||
|
// ignored because we're running a check after this block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback in case we didn't find any specified resolution
|
||||||
|
if (!resFound) resolution = profileImage.parseResolution(512);
|
||||||
|
|
||||||
|
// check if someone is mentioned
|
||||||
|
Mentions mentions = event.getMessage().getMentions();
|
||||||
|
if (mentions.getMentions().isEmpty())
|
||||||
|
{
|
||||||
|
user = event.getAuthor();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
String mentionedId = mentions.getMentions().get(0).getId();
|
||||||
|
user = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case of issues, fallback to the sender
|
||||||
|
if (user == null) user = event.getAuthor();
|
||||||
|
|
||||||
|
// send a response
|
||||||
|
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER);
|
||||||
|
if (response.content() != null)
|
||||||
|
{
|
||||||
|
event.getMessage().reply(response.content()).queue();
|
||||||
|
} else if (response.embed() != null)
|
||||||
|
{
|
||||||
|
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.commands.base.BotInfo;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageBotInfoCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
private final BotInfo botInfo;
|
||||||
|
|
||||||
|
public MessageBotInfoCommand(@Autowired BotInfo botInfo)
|
||||||
|
{
|
||||||
|
this.botInfo = botInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("botinfo", "info"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; // anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Get general info about the bot.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.TOOLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
|
||||||
|
// get a list of message commands
|
||||||
|
LinkedList<MessageCommand> messageCommands = Cache.getMessageCommandListener().getRegisteredCommands();
|
||||||
|
LinkedList<String> commandNames = new LinkedList<>();
|
||||||
|
for (MessageCommand command : messageCommands)
|
||||||
|
{
|
||||||
|
commandNames.add(command.getCommandLabels().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the list
|
||||||
|
MessageEmbed embed = botInfo.generateEmbed(commandNames);
|
||||||
|
event.getMessage().replyEmbeds(embed).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.commands.base.ClearChat;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageClearCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
private final ClearChat clearChat;
|
||||||
|
|
||||||
|
public MessageClearCommand(@Autowired ClearChat clearChat)
|
||||||
|
{
|
||||||
|
this.clearChat = clearChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList(clearChat.getLabel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return Collections.singletonList(clearChat.getPermission());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.MODERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Clear the current channel's chat history.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "[amount]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
// check if user is trying to run command in dms.
|
||||||
|
String error = clearChat.checkDMs(event.getChannel());
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
event.getMessage().reply(error).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the amount from the command args.
|
||||||
|
Integer toDeleteAmount;
|
||||||
|
if (args.length == 0) toDeleteAmount = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
toDeleteAmount = Integer.parseInt(args[0]);
|
||||||
|
} catch (NumberFormatException e)
|
||||||
|
{
|
||||||
|
toDeleteAmount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cap the amount to avoid abuse.
|
||||||
|
if (toDeleteAmount > clearChat.getMaxAmount()) toDeleteAmount = 0;
|
||||||
|
|
||||||
|
error = clearChat.checkDeleteAmount(toDeleteAmount);
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
event.getMessage().reply(error).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// answer by saying that the operation has begun.
|
||||||
|
String content = "\uD83D\uDEA7 Clearing...";
|
||||||
|
Message botMessage = event.getMessage().reply(content).complete();
|
||||||
|
|
||||||
|
int deleted = clearChat.delete(toDeleteAmount,
|
||||||
|
event.getMessageIdLong(),
|
||||||
|
event.getChannel());
|
||||||
|
|
||||||
|
// get a nicely formatted message that logs the deletion of messages.
|
||||||
|
content = clearChat.parseAmount(deleted);
|
||||||
|
|
||||||
|
// edit the message text and attach a button.
|
||||||
|
Button dismiss = clearChat.getDismissButton();
|
||||||
|
Message finalMessage = event.getChannel().sendMessage(content).setActionRow(dismiss).complete();
|
||||||
|
|
||||||
|
// add the message to database.
|
||||||
|
Cache.getServices().databaseService().queueDisabling(finalMessage);
|
||||||
|
Cache.getServices().databaseService().trackRanCommandReply(finalMessage, event.getAuthor());
|
||||||
|
|
||||||
|
// delete the sender's message.
|
||||||
|
event.getMessage().delete().queue();
|
||||||
|
// delete the "clearing" info message.
|
||||||
|
botMessage.delete().queue();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageCoinFlipCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
private final CoinFlip coinFlip;
|
||||||
|
|
||||||
|
public MessageCoinFlipCommand(@Autowired CoinFlip coinFlip)
|
||||||
|
{
|
||||||
|
this.coinFlip = coinFlip;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("coinflip", "flip", "flipcoin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; // null because it can be used anywhere
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Flip a coin.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.FUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
|
||||||
|
// perform coin flip
|
||||||
|
event.getMessage().reply(coinFlip.genRandom())
|
||||||
|
.addActionRow(coinFlip.getReflipButton())
|
||||||
|
.queue((message) ->
|
||||||
|
{
|
||||||
|
// set the command as expiring and restrict it to the user who ran it
|
||||||
|
coinFlip.trackAndRestrict(message, event.getAuthor());
|
||||||
|
}, (error) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageDiceRollCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final DiceRoll diceRoll;
|
||||||
|
|
||||||
|
public MessageDiceRollCommand(@Autowired DiceRoll diceRoll)
|
||||||
|
{
|
||||||
|
this.diceRoll = diceRoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("diceroll", "droll", "roll"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; // anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return """
|
||||||
|
Roll dice. You can roll multiple dice at the same time.
|
||||||
|
Examples:
|
||||||
|
- `d8 10` to roll an 8-sided die 10 times.
|
||||||
|
- `d12 3 d5 10` to roll a 12-sided die 3 times, and then a 5-sided die 10 times.
|
||||||
|
- `30` to roll a standard 6-sided die 30 times.
|
||||||
|
- `d10` to roll a 10-sided die once.
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "[dice size] [rolls]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.FUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
|
||||||
|
MessageResponse response = diceRoll.buildResponse(event.getAuthor(), args);
|
||||||
|
|
||||||
|
if (response.content() != null)
|
||||||
|
{
|
||||||
|
event.getMessage().reply(response.content()).queue();
|
||||||
|
} else if (response.embed() != null)
|
||||||
|
{
|
||||||
|
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageHelloCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("hi", "hello", "heya"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Get pinged by the bot.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.FUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
String sender = event.getMessage().getAuthor().getAsMention();
|
||||||
|
event.getMessage().reply("Hi, " + sender + "! :sparkles:").queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.apache.commons.text.WordUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.commands.base.Alias;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageHelpCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
private final Alias alias;
|
||||||
|
|
||||||
|
public MessageHelpCommand(@Autowired Alias alias)
|
||||||
|
{
|
||||||
|
this.alias = alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("help"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Get general help on the bot. Specify a command if you want specific help about that command.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "[command]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.TOOLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
LinkedHashMap<CommandCategory, LinkedList<MessageCommand>> commandCategories = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
if (args.length == 0)
|
||||||
|
{
|
||||||
|
for (CommandCategory category : CommandCategory.values())
|
||||||
|
{
|
||||||
|
LinkedList<MessageCommand> commandsOfThisCategory = new LinkedList<>();
|
||||||
|
for (MessageCommand command : Cache.getMessageCommandListener().getRegisteredCommands())
|
||||||
|
{
|
||||||
|
if (command.getCategory().equals(category))
|
||||||
|
{
|
||||||
|
commandsOfThisCategory.add(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commandCategories.put(category, commandsOfThisCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle("Bot Help");
|
||||||
|
|
||||||
|
embedBuilder.addField("General Help",
|
||||||
|
"Type `" + Cache.getBotPrefix() + " help [command]` to get help on a specific command." +
|
||||||
|
"\nYou will find a list of commands organized in categories below.",
|
||||||
|
false);
|
||||||
|
|
||||||
|
for (Map.Entry<CommandCategory, LinkedList<MessageCommand>> entry : commandCategories.entrySet())
|
||||||
|
{
|
||||||
|
StringBuilder commandsList = new StringBuilder();
|
||||||
|
CommandCategory category = entry.getKey();
|
||||||
|
LinkedList<MessageCommand> commandsOfThisCategory = entry.getValue();
|
||||||
|
|
||||||
|
for (int pos = 0; pos < commandsOfThisCategory.size(); pos++)
|
||||||
|
{
|
||||||
|
MessageCommand command = commandsOfThisCategory.get(pos);
|
||||||
|
commandsList.append("`").append(command.getCommandLabels().get(0)).append("`");
|
||||||
|
|
||||||
|
if (pos + 1 != commandsOfThisCategory.size())
|
||||||
|
commandsList.append(", "); // separate with comma except on last run
|
||||||
|
}
|
||||||
|
|
||||||
|
String niceCategoryName = category.name().replace("_", " ");
|
||||||
|
niceCategoryName = WordUtils.capitalizeFully(niceCategoryName);
|
||||||
|
niceCategoryName = category.getEmoji() + " " + niceCategoryName;
|
||||||
|
|
||||||
|
embedBuilder.addField(niceCategoryName, commandsList.toString(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.getMessage().replyEmbeds(embedBuilder.build()).queue();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
|
||||||
|
String commandLabel = args[0].toLowerCase();
|
||||||
|
MessageCommand command = Cache.getMessageCommandListener().getRegisteredCommand(commandLabel);
|
||||||
|
if (command == null)
|
||||||
|
{
|
||||||
|
event.getMessage().reply("Unrecognized command: `" + commandLabel + "`!").queue(); // todo prettier
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
commandLabel = command.getCommandLabels().get(0);
|
||||||
|
String usage = "`" + Cache.getBotPrefix() + " " + commandLabel;
|
||||||
|
String internalUsage = command.getUsage();
|
||||||
|
if (internalUsage != null) usage += " " + internalUsage;
|
||||||
|
usage += "`";
|
||||||
|
|
||||||
|
String aliases = alias.generateNiceAliases(command);
|
||||||
|
|
||||||
|
List<Permission> permissions = command.getPermissions();
|
||||||
|
StringBuilder permissionsStringBuilder = new StringBuilder();
|
||||||
|
if (permissions == null)
|
||||||
|
{
|
||||||
|
permissionsStringBuilder = new StringBuilder("Available to everyone");
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < permissions.size(); i++)
|
||||||
|
{
|
||||||
|
Permission permission = permissions.get(i);
|
||||||
|
permissionsStringBuilder.append("**").append(permission.getName()).append("**");
|
||||||
|
|
||||||
|
if (i + 1 != permissions.size())
|
||||||
|
permissionsStringBuilder.append(", "); // separate with comma expect on last iteration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String title = command.getCategory().getEmoji() +
|
||||||
|
" \"" + WordUtils.capitalizeFully(commandLabel + "\" help");
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle(title);
|
||||||
|
|
||||||
|
embedBuilder.addField("Description", command.getDescription(), false);
|
||||||
|
embedBuilder.addField("Usage", usage, false);
|
||||||
|
embedBuilder.addField("Aliases", aliases, false);
|
||||||
|
embedBuilder.addField("Permissions", permissionsStringBuilder.toString(), false);
|
||||||
|
|
||||||
|
event.getMessage().replyEmbeds(embedBuilder.build()).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,43 +5,78 @@ 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.events.message.MessageReceivedEvent;
|
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.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.MessageCommand;
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class InviteCommand implements MessageCommand
|
@Component
|
||||||
|
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
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Get the bot's invite link.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.MODERATION;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
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())
|
||||||
{
|
{
|
||||||
event.getAuthor().openPrivateChannel().queue(privateChannel ->
|
event.getAuthor().openPrivateChannel().queue(privateChannel ->
|
||||||
{
|
{
|
||||||
@@ -49,10 +84,9 @@ public class InviteCommand implements MessageCommand
|
|||||||
.addActionRow(inviteButton)
|
.addActionRow(inviteButton)
|
||||||
.queue();
|
.queue();
|
||||||
event.getMessage().addReaction(Emoji.fromUnicode("✅")).queue();
|
event.getMessage().addReaction(Emoji.fromUnicode("✅")).queue();
|
||||||
}, (error) -> {
|
}, error -> event.getMessage().addReaction(Emoji.fromUnicode("❌")).queue());
|
||||||
event.getMessage().addReaction(Emoji.fromUnicode("❌")).queue();
|
} else
|
||||||
});
|
{
|
||||||
} else {
|
|
||||||
event.getMessage()
|
event.getMessage()
|
||||||
.replyEmbeds(inviteEmbed)
|
.replyEmbeds(inviteEmbed)
|
||||||
.addActionRow(inviteButton)
|
.addActionRow(inviteButton)
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageKickCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final UserPunishment userPunishment;
|
||||||
|
|
||||||
|
public MessageKickCommand(@Autowired UserPunishment userPunishment)
|
||||||
|
{
|
||||||
|
this.userPunishment = userPunishment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("kick"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return new ArrayList<Permission>(Collections.singletonList(Permission.KICK_MEMBERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.MODERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Kick the mentioned user from the guild.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<mentioned user> [reason]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
userPunishment.handle(event, args, UserPunishment.PunishmentType.KICK);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.IMentionable;
|
||||||
|
import net.dv8tion.jda.api.entities.Mentions;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.commands.base.LoveCalculator;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageLoveCalculatorCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final LoveCalculator loveCalculator;
|
||||||
|
|
||||||
|
public MessageLoveCalculatorCommand(@Autowired LoveCalculator loveCalculator)
|
||||||
|
{
|
||||||
|
this.loveCalculator = loveCalculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Arrays.asList("lovecalc", "lovecalculator", "lc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; //anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Calculate how much two people love each other. You can mention two people or just one.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<person 1> [person 2]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.FUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
|
||||||
|
Mentions mentionsObj = event.getMessage().getMentions();
|
||||||
|
List<IMentionable> mentions = mentionsObj.getMentions();
|
||||||
|
|
||||||
|
|
||||||
|
if (args.length == 0 || mentions.isEmpty())
|
||||||
|
{
|
||||||
|
event.getMessage()
|
||||||
|
.reply("\uD83D\uDE22 I need to know who to check! Please mention them.")
|
||||||
|
.queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
User user1, user2;
|
||||||
|
|
||||||
|
String mentionedUserId = mentions.get(0).getId();
|
||||||
|
user1 = HidekoBot.getAPI().retrieveUserById(mentionedUserId).complete();
|
||||||
|
|
||||||
|
if (mentions.size() == 1)
|
||||||
|
{
|
||||||
|
user2 = event.getAuthor();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
mentionedUserId = mentions.get(1).getId();
|
||||||
|
user2 = HidekoBot.getAPI().retrieveUserById(mentionedUserId).complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageEmbed embed = loveCalculator.buildEmbedAndCacheResult(event.getAuthor(), user1, user2);
|
||||||
|
event.getChannel().sendMessageEmbeds(embed).queue();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageMagicBallCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final MagicBall magicBall;
|
||||||
|
|
||||||
|
public MessageMagicBallCommand(@Autowired MagicBall magicBall)
|
||||||
|
{
|
||||||
|
this.magicBall = magicBall;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return magicBall.getLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; // anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Ask a question to the Magic Ball.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<question>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.FUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
if (args.length == 0)
|
||||||
|
{
|
||||||
|
event.getMessage().reply("You need to specify a question!").queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder questionBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < args.length; i++)
|
||||||
|
{
|
||||||
|
String arg = args[i];
|
||||||
|
questionBuilder.append(arg);
|
||||||
|
if (i + 1 != args.length) // don't add a separator on the last iteration
|
||||||
|
questionBuilder.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
String question = questionBuilder.toString();
|
||||||
|
|
||||||
|
|
||||||
|
event.getChannel().sendMessageEmbeds(magicBall.generateEmbed(question, event.getAuthor())).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageSayCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
private final Say say;
|
||||||
|
|
||||||
|
public MessageSayCommand(@Autowired Say say)
|
||||||
|
{
|
||||||
|
this.say = say;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("say"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return Collections.singletonList(say.getPermission());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Make the bot say something for you.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<text>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.TOOLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
|
||||||
|
String messageContent;
|
||||||
|
if (args.length != 0 && !args[0].isEmpty())
|
||||||
|
{
|
||||||
|
messageContent = args[0];
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
event.getMessage().reply("\uD83D\uDE20 Hey, you have to tell me what to say!")
|
||||||
|
.queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.getChannel().sendMessage(messageContent).queue();
|
||||||
|
|
||||||
|
if (event.getChannel() instanceof TextChannel)
|
||||||
|
{
|
||||||
|
event.getMessage().delete().queue(response -> {
|
||||||
|
// nothing to do with the response
|
||||||
|
}, error -> {
|
||||||
|
// ignore the error if we couldn't delete it, we were probably missing permissions
|
||||||
|
// without this block it would print a stack trace in console
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
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.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class MessageTimeoutCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
private final UserPunishment userPunishment;
|
||||||
|
|
||||||
|
public MessageTimeoutCommand(@Autowired UserPunishment userPunishment)
|
||||||
|
{
|
||||||
|
this.userPunishment = userPunishment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("timeout"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return new ArrayList<Permission>(Collections.singletonList(Permission.MODERATE_MEMBERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.MODERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Timeout the mentioned user.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<mentioned user> <duration> [reason]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
userPunishment.handle(event, args, UserPunishment.PunishmentType.TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class MessageTriviaCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return new LinkedList<>(Collections.singletonList("trivia"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.FUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Start a Trivia session and play with others!";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
MessageChannel channel = event.getChannel();
|
||||||
|
|
||||||
|
if (!(channel instanceof TextChannel))
|
||||||
|
{
|
||||||
|
channel.sendMessage(Trivia.getNoDMsError()).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Trivia.channelsRunningTrivia.contains(channel.getId()))
|
||||||
|
{
|
||||||
|
// todo: also what if the bot stops (database...?)
|
||||||
|
// todo: also what if the message is already deleted
|
||||||
|
Message err = event.getMessage().reply(Trivia.getTriviaAlreadyRunningError()).complete();
|
||||||
|
Cache.getTaskScheduler().schedule(() -> err.delete().queue(), 10, TimeUnit.SECONDS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageResponse response = Trivia.generateMainScreen();
|
||||||
|
|
||||||
|
Message recvMessage = event.getMessage();
|
||||||
|
MessageCreateAction responseAction = null;
|
||||||
|
if (response.content() != null) responseAction = recvMessage.reply(response.content());
|
||||||
|
else if (response.embed() != null) responseAction = recvMessage.replyEmbeds(response.embed());
|
||||||
|
|
||||||
|
if (responseAction != null)
|
||||||
|
{
|
||||||
|
if (response.components() != null) responseAction = responseAction.addActionRow(response.components());
|
||||||
|
|
||||||
|
responseAction.queue(message -> {
|
||||||
|
Cache.getServices().databaseService().trackRanCommandReply(message, event.getAuthor());
|
||||||
|
Cache.getServices().databaseService().queueDisabling(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.message;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MessageUrbanDictionaryCommand implements MessageCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkedList<String> getCommandLabels()
|
||||||
|
{
|
||||||
|
return UrbanDictionary.getCommandLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Permission> getPermissions()
|
||||||
|
{
|
||||||
|
return null; //anyone can use it
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean passRawArgs()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Look something up in the Urban Dictionary.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getUsage()
|
||||||
|
{
|
||||||
|
return "<query>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory()
|
||||||
|
{
|
||||||
|
return CommandCategory.FUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||||
|
{
|
||||||
|
if (args.length == 0)
|
||||||
|
{
|
||||||
|
event.getMessage().reply(UrbanDictionary.getNoArgsError()).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitize args by only keeping letters and numbers, and adding "+" instead of spaces for HTML parsing
|
||||||
|
StringBuilder termBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < args.length; i++)
|
||||||
|
{
|
||||||
|
String arg = args[i];
|
||||||
|
termBuilder.append(arg);
|
||||||
|
|
||||||
|
if (i + 1 != args.length) // add spaces between args, but not on the last run
|
||||||
|
termBuilder.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
String term = UrbanDictionary.sanitizeArgs(termBuilder.toString(), false);
|
||||||
|
String url = UrbanDictionary.generateUrl(term);
|
||||||
|
|
||||||
|
Document doc;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doc = Jsoup.connect(url).get();
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
event.getMessage().reply(UrbanDictionary.getTermNotFoundError()).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Elements definitions = doc.getElementsByClass("definition");
|
||||||
|
UrbanDictionary.UrbanSearch search = new UrbanDictionary.UrbanSearch(definitions);
|
||||||
|
MessageEmbed embed = UrbanDictionary.buildEmbed(term, url, event.getAuthor(), search, 0);
|
||||||
|
|
||||||
|
// disable next page if we only have one result
|
||||||
|
Button nextPageBtnLocal = UrbanDictionary.getNextPageButton();
|
||||||
|
if (search.getPages() == 1) nextPageBtnLocal = nextPageBtnLocal.asDisabled();
|
||||||
|
|
||||||
|
event.getChannel()
|
||||||
|
.sendMessageEmbeds(embed)
|
||||||
|
.addActionRow(UrbanDictionary.getPreviousPageButton().asDisabled(),
|
||||||
|
//disabled by default because we're on page 0
|
||||||
|
nextPageBtnLocal,
|
||||||
|
UrbanDictionary.getDeleteButton())
|
||||||
|
.queue(message -> UrbanDictionary.track(message, event.getAuthor(), search, term));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package wtf.beatrice.hidekobot.commands.slash;
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.EmbedBuilder;
|
|
||||||
import net.dv8tion.jda.api.entities.User;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
|
||||||
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.Commands;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import wtf.beatrice.hidekobot.Cache;
|
|
||||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
|
||||||
|
|
||||||
public class AvatarCommand extends SlashCommandImpl
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public CommandData getSlashCommandData() {
|
|
||||||
return Commands.slash("avatar", "Get someone's profile picture.")
|
|
||||||
.addOption(OptionType.USER, "user", "User you want to grab the avatar of.")
|
|
||||||
.addOption(OptionType.INTEGER, "size", "The size of the returned image.",
|
|
||||||
false,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
|
||||||
{
|
|
||||||
// defer reply because this might take a moment
|
|
||||||
event.deferReply().queue();
|
|
||||||
|
|
||||||
User user;
|
|
||||||
int resolution;
|
|
||||||
|
|
||||||
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
|
|
||||||
|
|
||||||
|
|
||||||
OptionMapping userArg = event.getOption("user");
|
|
||||||
if(userArg != null)
|
|
||||||
{
|
|
||||||
user = userArg.getAsUser();
|
|
||||||
} else {
|
|
||||||
user = event.getUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
OptionMapping sizeArg = event.getOption("size");
|
|
||||||
if(sizeArg != null)
|
|
||||||
{
|
|
||||||
resolution = sizeArg.getAsInt();
|
|
||||||
|
|
||||||
// method to find closest value to accepted values
|
|
||||||
int distance = Math.abs(acceptedSizes[0] - resolution);
|
|
||||||
int idx = 0;
|
|
||||||
for(int c = 1; c < acceptedSizes.length; c++){
|
|
||||||
int cdistance = Math.abs(acceptedSizes[c] - resolution);
|
|
||||||
if(cdistance < distance){
|
|
||||||
idx = c;
|
|
||||||
distance = cdistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolution = acceptedSizes[idx];
|
|
||||||
|
|
||||||
} else {
|
|
||||||
resolution = 512;
|
|
||||||
}
|
|
||||||
|
|
||||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
|
||||||
|
|
||||||
// embed processing
|
|
||||||
{
|
|
||||||
embedBuilder.setColor(Cache.getBotColor());
|
|
||||||
embedBuilder.setTitle("Profile picture");
|
|
||||||
|
|
||||||
embedBuilder.addField("User", "<@" + user.getId() + ">", false);
|
|
||||||
|
|
||||||
embedBuilder.addField("Current resolution", resolution + " × " + resolution, false);
|
|
||||||
|
|
||||||
// string builder to create a string that links to all available resolutions
|
|
||||||
StringBuilder links = new StringBuilder();
|
|
||||||
for(int pos = 0; pos < acceptedSizes.length; pos++)
|
|
||||||
{
|
|
||||||
int currSize = acceptedSizes[pos];
|
|
||||||
|
|
||||||
String currLink = user.getEffectiveAvatar().getUrl(currSize);
|
|
||||||
|
|
||||||
links.append("[").append(currSize).append("px](").append(currLink).append(")");
|
|
||||||
if(pos + 1 != acceptedSizes.length) // don't add a separator on the last iteration
|
|
||||||
{
|
|
||||||
links.append(" | ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
embedBuilder.addField("Available resolutions", links.toString(), false);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
embedBuilder.setImage(user.getEffectiveAvatar().getUrl(resolution));
|
|
||||||
}
|
|
||||||
|
|
||||||
event.getHook().editOriginalEmbeds(embedBuilder.build()).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
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.Commands;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.ProfileImage;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashAvatarCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
private final ProfileImage profileImage;
|
||||||
|
|
||||||
|
public SlashAvatarCommand(@NotNull ProfileImage profileImage)
|
||||||
|
{
|
||||||
|
this.profileImage = profileImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
return Commands.slash("avatar", "Get someone's profile picture.")
|
||||||
|
.addOption(OptionType.USER, "user", "User you want to grab the avatar of.")
|
||||||
|
.addOption(OptionType.INTEGER, "size", "The size of the returned image.",
|
||||||
|
false,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
// defer reply because this might take a moment
|
||||||
|
event.deferReply().queue();
|
||||||
|
|
||||||
|
User user;
|
||||||
|
int resolution;
|
||||||
|
|
||||||
|
OptionMapping userArg = event.getOption("user");
|
||||||
|
if (userArg != null)
|
||||||
|
{
|
||||||
|
user = userArg.getAsUser();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
user = event.getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionMapping sizeArg = event.getOption("size");
|
||||||
|
if (sizeArg != null)
|
||||||
|
{
|
||||||
|
resolution = profileImage.parseResolution(sizeArg.getAsInt());
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
resolution = profileImage.parseResolution(512);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
|
||||||
|
if (response.content() != null)
|
||||||
|
{
|
||||||
|
event.getHook().editOriginal(response.content()).queue();
|
||||||
|
} else if (response.embed() != null)
|
||||||
|
{
|
||||||
|
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||||
|
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.Commands;
|
||||||
|
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.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashBanCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
private final UserPunishment userPunishment;
|
||||||
|
|
||||||
|
public SlashBanCommand(@Autowired UserPunishment userPunishment)
|
||||||
|
{
|
||||||
|
this.userPunishment = userPunishment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash("ban", "Ban someone from the guild.")
|
||||||
|
.addOption(OptionType.MENTIONABLE, "target",
|
||||||
|
"The member user to ban.",
|
||||||
|
true,
|
||||||
|
false)
|
||||||
|
.addOption(OptionType.STRING, "reason",
|
||||||
|
"The reason for the punishment.",
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.BAN_MEMBERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
userPunishment.handle(event, UserPunishment.PunishmentType.BAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
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.Commands;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.ProfileImage;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashBannerCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
private final ProfileImage profileImage;
|
||||||
|
|
||||||
|
public SlashBannerCommand(@NotNull ProfileImage profileImage)
|
||||||
|
{
|
||||||
|
this.profileImage = profileImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
return Commands.slash("banner", "Get someone's profile banner.")
|
||||||
|
.addOption(OptionType.USER, "user", "User you want to grab the banner of.")
|
||||||
|
.addOption(OptionType.INTEGER, "size", "The size of the returned image.",
|
||||||
|
false,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
// defer reply because this might take a moment
|
||||||
|
event.deferReply().queue();
|
||||||
|
|
||||||
|
User user;
|
||||||
|
int resolution;
|
||||||
|
|
||||||
|
OptionMapping userArg = event.getOption("user");
|
||||||
|
if (userArg != null)
|
||||||
|
{
|
||||||
|
user = userArg.getAsUser();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
user = event.getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionMapping sizeArg = event.getOption("size");
|
||||||
|
if (sizeArg != null)
|
||||||
|
{
|
||||||
|
resolution = profileImage.parseResolution(sizeArg.getAsInt());
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
resolution = profileImage.parseResolution(512);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageResponse response = profileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER);
|
||||||
|
if (response.content() != null)
|
||||||
|
{
|
||||||
|
event.getHook().editOriginal(response.content()).queue();
|
||||||
|
} else if (response.embed() != null)
|
||||||
|
{
|
||||||
|
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ 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;
|
||||||
@@ -13,10 +15,20 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BotInfoCommand extends SlashCommandImpl
|
@Component
|
||||||
|
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,14 +41,14 @@ public class BotInfoCommand extends SlashCommandImpl
|
|||||||
// get a list of slash commands
|
// get a list of slash commands
|
||||||
List<SlashCommand> registeredCommands = Cache.getSlashCommandListener().getRegisteredCommands();
|
List<SlashCommand> registeredCommands = Cache.getSlashCommandListener().getRegisteredCommands();
|
||||||
LinkedList<String> registeredCommandNames = new LinkedList<>();
|
LinkedList<String> registeredCommandNames = new LinkedList<>();
|
||||||
for(SlashCommand command : registeredCommands)
|
for (SlashCommand command : registeredCommands)
|
||||||
{
|
{
|
||||||
// node: adding slash so people realize that this is specific about slash commands.
|
// node: adding slash so people realize that this is specific about slash commands.
|
||||||
registeredCommandNames.add("/" + command.getCommandName());
|
registeredCommandNames.add("/" + command.getCommandName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,19 +9,29 @@ 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;
|
||||||
|
|
||||||
public class ClearCommand extends SlashCommandImpl
|
@Component
|
||||||
|
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(),
|
{
|
||||||
ClearChat.getDescription())
|
return Commands.slash(clearChat.getLabel(),
|
||||||
|
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
|
||||||
@@ -31,8 +41,8 @@ public class ClearCommand 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();
|
||||||
return;
|
return;
|
||||||
@@ -44,8 +54,11 @@ public class ClearCommand extends SlashCommandImpl
|
|||||||
OptionMapping amountOption = event.getOption("amount");
|
OptionMapping amountOption = event.getOption("amount");
|
||||||
int toDeleteAmount = amountOption == null ? 1 : amountOption.getAsInt();
|
int toDeleteAmount = amountOption == null ? 1 : amountOption.getAsInt();
|
||||||
|
|
||||||
error = ClearChat.checkDeleteAmount(toDeleteAmount);
|
// cap the amount to avoid abuse.
|
||||||
if(error != null)
|
if (toDeleteAmount > clearChat.getMaxAmount()) toDeleteAmount = 0;
|
||||||
|
|
||||||
|
error = clearChat.checkDeleteAmount(toDeleteAmount);
|
||||||
|
if (error != null)
|
||||||
{
|
{
|
||||||
event.getHook().editOriginal(error).queue();
|
event.getHook().editOriginal(error).queue();
|
||||||
return;
|
return;
|
||||||
@@ -56,20 +69,20 @@ public class ClearCommand 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.getDatabaseSource().queueDisabling(botMessage);
|
Cache.getServices().databaseService().queueDisabling(botMessage);
|
||||||
Cache.getDatabaseSource().trackRanCommandReply(botMessage, event.getUser());
|
Cache.getServices().databaseService().trackRanCommandReply(botMessage, event.getUser());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,11 +4,20 @@ 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;
|
||||||
|
|
||||||
public class CoinFlipCommand extends SlashCommandImpl
|
@Component
|
||||||
|
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()
|
||||||
@@ -21,20 +30,19 @@ public class CoinFlipCommand 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) -> {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
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.Commands;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.DiceRoll;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashDiceRollCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
private final DiceRoll diceRoll;
|
||||||
|
|
||||||
|
public SlashDiceRollCommand(@NotNull DiceRoll diceRoll)
|
||||||
|
{
|
||||||
|
this.diceRoll = diceRoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash("diceroll", "Roll dice. You can roll multiple dice at the same time.")
|
||||||
|
.addOption(OptionType.STRING, "query",
|
||||||
|
"The dice to roll.",
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
event.deferReply().queue();
|
||||||
|
|
||||||
|
OptionMapping textOption = event.getOption("query");
|
||||||
|
String messageContent = "";
|
||||||
|
if (textOption != null)
|
||||||
|
{
|
||||||
|
messageContent = textOption.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] args = messageContent.split("\\s");
|
||||||
|
|
||||||
|
MessageResponse response = diceRoll.buildResponse(event.getUser(), args);
|
||||||
|
|
||||||
|
if (response.content() != null)
|
||||||
|
{
|
||||||
|
event.getHook().editOriginal(response.content()).queue();
|
||||||
|
} else if (response.embed() != null)
|
||||||
|
{
|
||||||
|
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,12 +10,14 @@ import wtf.beatrice.hidekobot.HidekoBot;
|
|||||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class DieCommand extends SlashCommandImpl
|
public class SlashDieCommand 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);
|
||||||
}
|
}
|
||||||
@@ -23,12 +25,16 @@ public class DieCommand extends SlashCommandImpl
|
|||||||
@Override
|
@Override
|
||||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
{
|
{
|
||||||
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();
|
||||||
Executors.newSingleThreadScheduledExecutor().schedule(HidekoBot::shutdown, 3, TimeUnit.SECONDS);
|
try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor())
|
||||||
|
{
|
||||||
|
executor.schedule(HidekoBot::shutdown, 3, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 HelpCommand extends SlashCommandImpl
|
public class SlashHelpCommand extends SlashCommandImpl
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public CommandData getSlashCommandData()
|
public CommandData getSlashCommandData()
|
||||||
@@ -10,11 +10,19 @@ 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;
|
||||||
|
|
||||||
public class InviteCommand extends SlashCommandImpl
|
@Component
|
||||||
|
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()
|
||||||
@@ -30,14 +38,14 @@ public class InviteCommand extends SlashCommandImpl
|
|||||||
ReplyCallbackAction replyCallbackAction = event.deferReply();
|
ReplyCallbackAction replyCallbackAction = event.deferReply();
|
||||||
|
|
||||||
// only make message permanent in DMs
|
// only make message permanent in DMs
|
||||||
if(event.getChannelType() != ChannelType.PRIVATE)
|
if (event.getChannelType() != ChannelType.PRIVATE)
|
||||||
{
|
{
|
||||||
replyCallbackAction = replyCallbackAction.setEphemeral(true);
|
replyCallbackAction = replyCallbackAction.setEphemeral(true);
|
||||||
}
|
}
|
||||||
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()
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||||
|
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.Commands;
|
||||||
|
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.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashKickCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
|
||||||
|
private final UserPunishment userPunishment;
|
||||||
|
|
||||||
|
public SlashKickCommand(@Autowired UserPunishment userPunishment)
|
||||||
|
{
|
||||||
|
this.userPunishment = userPunishment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash("kick", "Kick someone from the guild.")
|
||||||
|
.addOption(OptionType.MENTIONABLE, "target",
|
||||||
|
"The member user to kick.",
|
||||||
|
true,
|
||||||
|
false)
|
||||||
|
.addOption(OptionType.STRING, "reason",
|
||||||
|
"The reason for the punishment.",
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.KICK_MEMBERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
userPunishment.handle(event, UserPunishment.PunishmentType.KICK);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
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.Commands;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.LoveCalculator;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashLoveCalculatorCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
private final LoveCalculator loveCalculator;
|
||||||
|
|
||||||
|
public SlashLoveCalculatorCommand(@NotNull LoveCalculator loveCalculator)
|
||||||
|
{
|
||||||
|
this.loveCalculator = loveCalculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash("lovecalc",
|
||||||
|
"Calculate how much two people love each other.")
|
||||||
|
|
||||||
|
.addOption(OptionType.MENTIONABLE,
|
||||||
|
"first",
|
||||||
|
"The first person to account for",
|
||||||
|
true)
|
||||||
|
|
||||||
|
.addOption(OptionType.MENTIONABLE,
|
||||||
|
"second",
|
||||||
|
"The second person to account for",
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
User firstUser, secondUser;
|
||||||
|
|
||||||
|
OptionMapping firsUserArg = event.getOption("first");
|
||||||
|
if (firsUserArg != null)
|
||||||
|
{
|
||||||
|
firstUser = firsUserArg.getAsUser(); //todo null check?
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
event.reply("\uD83D\uDE22 I need to know who to check! Please mention them.")
|
||||||
|
.setEphemeral(true)
|
||||||
|
.queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionMapping secondUserArg = event.getOption("second");
|
||||||
|
if (secondUserArg != null)
|
||||||
|
{
|
||||||
|
secondUser = secondUserArg.getAsUser(); //todo null check?
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
secondUser = event.getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageEmbed embed = loveCalculator.buildEmbedAndCacheResult(event.getUser(), firstUser, secondUser);
|
||||||
|
event.replyEmbeds(embed).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
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.Commands;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.MagicBall;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashMagicBallCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
private final MagicBall magicBall;
|
||||||
|
|
||||||
|
public SlashMagicBallCommand(@NotNull MagicBall magicBall)
|
||||||
|
{
|
||||||
|
this.magicBall = magicBall;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash(magicBall.getLabels().get(0),
|
||||||
|
"Ask a question to the magic ball.")
|
||||||
|
.addOption(OptionType.STRING, "question",
|
||||||
|
"The question to ask.",
|
||||||
|
true,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
// get the asked question
|
||||||
|
OptionMapping textOption = event.getOption("question");
|
||||||
|
String question = "";
|
||||||
|
if (textOption != null)
|
||||||
|
{
|
||||||
|
question = textOption.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textOption == null || question.isEmpty())
|
||||||
|
{
|
||||||
|
event.reply("\uD83D\uDE20 Hey, you have to ask me a question!")
|
||||||
|
.setEphemeral(true)
|
||||||
|
.queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageEmbed response = magicBall.generateEmbed(question, event.getUser());
|
||||||
|
event.replyEmbeds(response).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 PingCommand extends SlashCommandImpl
|
public class SlashPingCommand extends SlashCommandImpl
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public CommandData getSlashCommandData()
|
public CommandData getSlashCommandData()
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package wtf.beatrice.hidekobot.commands.slash;
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
import net.dv8tion.jda.api.Permission;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||||
@@ -9,10 +8,20 @@ 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.objects.commands.SlashCommandImpl;
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
public class SayCommand extends SlashCommandImpl
|
@Component
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
@@ -22,7 +31,7 @@ public class SayCommand extends SlashCommandImpl
|
|||||||
"The message to send.",
|
"The message to send.",
|
||||||
true,
|
true,
|
||||||
false)
|
false)
|
||||||
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MESSAGE_MANAGE));
|
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(say.getPermission()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -33,12 +42,12 @@ public class SayCommand extends SlashCommandImpl
|
|||||||
// get the text to send
|
// get the text to send
|
||||||
OptionMapping textOption = event.getOption("text");
|
OptionMapping textOption = event.getOption("text");
|
||||||
String messageContent = "";
|
String messageContent = "";
|
||||||
if(textOption != null)
|
if (textOption != null)
|
||||||
{
|
{
|
||||||
messageContent = textOption.getAsString();
|
messageContent = textOption.getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(textOption == null || messageContent.isEmpty())
|
if (textOption == null || messageContent.isEmpty())
|
||||||
{
|
{
|
||||||
event.reply("\uD83D\uDE20 Hey, you have to tell me what to say!")
|
event.reply("\uD83D\uDE20 Hey, you have to tell me what to say!")
|
||||||
.setEphemeral(true)
|
.setEphemeral(true)
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||||
|
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.Commands;
|
||||||
|
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.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SlashTimeoutCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
|
||||||
|
private final UserPunishment userPunishment;
|
||||||
|
|
||||||
|
public SlashTimeoutCommand(@Autowired UserPunishment userPunishment)
|
||||||
|
{
|
||||||
|
this.userPunishment = userPunishment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash("timeout", "Timeout someone in the guild.")
|
||||||
|
.addOption(OptionType.MENTIONABLE, "target",
|
||||||
|
"The member user to time out.",
|
||||||
|
true,
|
||||||
|
false)
|
||||||
|
.addOption(OptionType.STRING, "duration",
|
||||||
|
"The duration of the timeout.",
|
||||||
|
true,
|
||||||
|
false)
|
||||||
|
.addOption(OptionType.STRING, "reason",
|
||||||
|
"The reason for the punishment.",
|
||||||
|
false,
|
||||||
|
false)
|
||||||
|
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MODERATE_MEMBERS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
userPunishment.handle(event, UserPunishment.PunishmentType.TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||||
|
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
public class SlashTriviaCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash("trivia",
|
||||||
|
"Start a Trivia session and play with others!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
MessageChannel channel = event.getChannel();
|
||||||
|
|
||||||
|
if (!(channel instanceof TextChannel))
|
||||||
|
{
|
||||||
|
event.reply(Trivia.getNoDMsError()).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Trivia.channelsRunningTrivia.contains(channel.getId()))
|
||||||
|
{
|
||||||
|
event.reply(Trivia.getTriviaAlreadyRunningError()).setEphemeral(true).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got here, this might take a bit
|
||||||
|
event.deferReply().queue();
|
||||||
|
MessageResponse response = Trivia.generateMainScreen();
|
||||||
|
|
||||||
|
event.getHook().editOriginalEmbeds(response.embed()).setActionRow(response.components()).queue(message ->
|
||||||
|
{
|
||||||
|
Cache.getServices().databaseService().trackRanCommandReply(message, event.getUser());
|
||||||
|
Cache.getServices().databaseService().queueDisabling(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
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.Commands;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class SlashUrbanDictionaryCommand extends SlashCommandImpl
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public CommandData getSlashCommandData()
|
||||||
|
{
|
||||||
|
|
||||||
|
return Commands.slash(UrbanDictionary.getCommandLabels().get(0),
|
||||||
|
"Look up a term on Urban Dictionary.")
|
||||||
|
.addOption(OptionType.STRING, "term", "The term to look up", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
|
{
|
||||||
|
event.deferReply().queue();
|
||||||
|
|
||||||
|
// get the term to look up
|
||||||
|
OptionMapping textOption = event.getOption("term");
|
||||||
|
String term = "";
|
||||||
|
if (textOption != null)
|
||||||
|
{
|
||||||
|
term = textOption.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textOption == null || term.isEmpty())
|
||||||
|
{
|
||||||
|
event.reply(UrbanDictionary.getNoArgsError())
|
||||||
|
.setEphemeral(true)
|
||||||
|
.queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String sanitizedTerm = UrbanDictionary.sanitizeArgs(term, false);
|
||||||
|
String url = UrbanDictionary.generateUrl(sanitizedTerm);
|
||||||
|
|
||||||
|
Document doc;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doc = Jsoup.connect(url).get();
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
event.reply(UrbanDictionary.getTermNotFoundError())
|
||||||
|
.setEphemeral(true)
|
||||||
|
.queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Elements definitions = doc.getElementsByClass("definition");
|
||||||
|
UrbanDictionary.UrbanSearch search = new UrbanDictionary.UrbanSearch(definitions);
|
||||||
|
MessageEmbed embed = UrbanDictionary.buildEmbed(sanitizedTerm, url, event.getUser(), search, 0);
|
||||||
|
|
||||||
|
// disable next page if we only have one result
|
||||||
|
Button nextPageBtnLocal = UrbanDictionary.getNextPageButton();
|
||||||
|
if (search.getPages() == 1) nextPageBtnLocal = nextPageBtnLocal.asDisabled();
|
||||||
|
|
||||||
|
ActionRow actionRow = ActionRow.of(UrbanDictionary.getPreviousPageButton().asDisabled(),
|
||||||
|
//disabled by default because we're on page 0
|
||||||
|
nextPageBtnLocal,
|
||||||
|
UrbanDictionary.getDeleteButton());
|
||||||
|
event.getHook().editOriginalEmbeds(embed).setComponents(actionRow).queue(message ->
|
||||||
|
UrbanDictionary.track(message, event.getUser(), search, sanitizedTerm));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,18 +7,27 @@ public enum ConfigurationEntry
|
|||||||
BOT_OWNER_ID("bot-owner-id", 100000000000000000L),
|
BOT_OWNER_ID("bot-owner-id", 100000000000000000L),
|
||||||
BOT_COLOR("bot-color", "PINK"),
|
BOT_COLOR("bot-color", "PINK"),
|
||||||
HEARTBEAT_LINK("heartbeat-link", "https://your-heartbeat-api.com/api/push/apikey?status=up&msg=OK&ping="),
|
HEARTBEAT_LINK("heartbeat-link", "https://your-heartbeat-api.com/api/push/apikey?status=up&msg=OK&ping="),
|
||||||
|
RANDOM_ORG_API_KEY("random-org-api-key", "00000000-0000-0000-0000-000000000000"),
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
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() { return path; }
|
public String getPath()
|
||||||
public Object getDefaultValue() { return defaultValue; }
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getDefaultValue()
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
package wtf.beatrice.hidekobot.datasources;
|
package wtf.beatrice.hidekobot.datasources;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
|
import org.yaml.snakeyaml.LoaderOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||||
import wtf.beatrice.hidekobot.HidekoBot;
|
import wtf.beatrice.hidekobot.HidekoBot;
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ConfigurationSource
|
public class ConfigurationSource
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
private final LinkedHashMap<String, Object> configurationEntries = new LinkedHashMap<>();
|
private final LinkedHashMap<String, Object> configurationEntries = new LinkedHashMap<>();
|
||||||
private final Logger logger;
|
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationSource.class);
|
||||||
private final String configFilePath;
|
private final String configFilePath;
|
||||||
|
|
||||||
public ConfigurationSource(String configFilePath)
|
public ConfigurationSource(String configFilePath)
|
||||||
{
|
{
|
||||||
this.configFilePath = configFilePath;
|
this.configFilePath = configFilePath;
|
||||||
logger = new Logger(getClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initConfig()
|
public void initConfig()
|
||||||
@@ -31,40 +32,52 @@ public class ConfigurationSource
|
|||||||
* we used to have a config.yml file in the "resources" folder, but that is no longer necessary.
|
* we used to have a config.yml file in the "resources" folder, but that is no longer necessary.
|
||||||
*/
|
*/
|
||||||
LinkedHashMap<String, Object> internalConfigContents = new LinkedHashMap<>(); // map holding all file entries
|
LinkedHashMap<String, Object> internalConfigContents = new LinkedHashMap<>(); // map holding all file entries
|
||||||
for(ConfigurationEntry entry : ConfigurationEntry.values())
|
for (ConfigurationEntry entry : ConfigurationEntry.values())
|
||||||
{
|
{
|
||||||
internalConfigContents.put(entry.getPath(), entry.getDefaultValue());
|
internalConfigContents.put(entry.getPath(), entry.getDefaultValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(internalConfigContents.isEmpty())
|
if (internalConfigContents.isEmpty())
|
||||||
{
|
{
|
||||||
logger.log("Error reading internal configuration!");
|
LOGGER.error("Error reading internal configuration!");
|
||||||
HidekoBot.shutdown();
|
HidekoBot.shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if config files exists in filesystem
|
// check if config files exists in filesystem
|
||||||
File fsConfigFile = new File(configFilePath);
|
File fsConfigFile = new File(configFilePath);
|
||||||
if(!fsConfigFile.exists())
|
if (!fsConfigFile.exists())
|
||||||
{
|
{
|
||||||
// try to create config file
|
// try to create config file
|
||||||
try { fsConfigFile.createNewFile(); }
|
try
|
||||||
catch (IOException e) {
|
{
|
||||||
logger.log("Error creating configuration file!");
|
if (!fsConfigFile.createNewFile())
|
||||||
logger.log(e.getMessage());
|
{
|
||||||
|
LOGGER.error("We tried creating a file that already exists!");
|
||||||
|
HidekoBot.shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Error creating configuration file!", e);
|
||||||
HidekoBot.shutdown();
|
HidekoBot.shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// load the YAML file from the filesystem
|
// load the YAML file from the filesystem
|
||||||
Yaml fsConfigYaml = new Yaml();
|
LoaderOptions options = new LoaderOptions();
|
||||||
|
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); }
|
{
|
||||||
catch (IOException e) { logger.log(e.getMessage()); }
|
fsConfigContents = fsConfigYaml.load(fsConfigStream);
|
||||||
|
} 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...
|
||||||
{
|
{
|
||||||
// "clean" them (this effectively forces a config file reset)
|
// "clean" them (this effectively forces a config file reset)
|
||||||
fsConfigContents = new LinkedHashMap<>();
|
fsConfigContents = new LinkedHashMap<>();
|
||||||
@@ -72,10 +85,10 @@ public class ConfigurationSource
|
|||||||
|
|
||||||
// check for missing keys
|
// check for missing keys
|
||||||
boolean missingKeys = false;
|
boolean missingKeys = false;
|
||||||
for(String key : internalConfigContents.keySet())
|
for (String key : internalConfigContents.keySet())
|
||||||
{
|
{
|
||||||
// if key is missing
|
// if key is missing
|
||||||
if(!fsConfigContents.containsKey(key))
|
if (!fsConfigContents.containsKey(key))
|
||||||
{
|
{
|
||||||
// quit and flag it, as we need to complete the file with the missing ones
|
// quit and flag it, as we need to complete the file with the missing ones
|
||||||
missingKeys = true;
|
missingKeys = true;
|
||||||
@@ -84,24 +97,29 @@ public class ConfigurationSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if keys are missing
|
// if keys are missing
|
||||||
if(missingKeys)
|
if (missingKeys)
|
||||||
{
|
{
|
||||||
// create a new mixed map that will take existing values from the non-missing keys
|
// create a new mixed map that will take existing values from the non-missing keys
|
||||||
// and fill everything else with the default values
|
// and fill everything else with the default values
|
||||||
LinkedHashMap<String, Object> filledEntries = new LinkedHashMap<>();
|
LinkedHashMap<String, Object> filledEntries = new LinkedHashMap<>();
|
||||||
for(String key : internalConfigContents.keySet())
|
|
||||||
|
for (Map.Entry<String, Object> entry : internalConfigContents.entrySet())
|
||||||
{
|
{
|
||||||
if(fsConfigContents.containsKey(key))
|
String key = entry.getKey();
|
||||||
|
|
||||||
|
if (fsConfigContents.containsKey(key))
|
||||||
{
|
{
|
||||||
// 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, internalConfigContents.get(key));
|
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);
|
||||||
|
|
||||||
@@ -114,15 +132,17 @@ 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.log(e.getMessage());
|
{
|
||||||
|
LOGGER.error(e.getMessage());
|
||||||
HidekoBot.shutdown();
|
HidekoBot.shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
@@ -133,6 +153,7 @@ 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());
|
||||||
|
|||||||
@@ -1,377 +0,0 @@
|
|||||||
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 wtf.beatrice.hidekobot.Cache;
|
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
|
||||||
|
|
||||||
import java.sql.*;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DatabaseSource
|
|
||||||
{
|
|
||||||
|
|
||||||
private final static String sqliteURL = "jdbc:sqlite:%path%";
|
|
||||||
private Connection dbConnection = null;
|
|
||||||
private final String dbPath;
|
|
||||||
private final Logger logger;
|
|
||||||
|
|
||||||
public DatabaseSource(String dbPath)
|
|
||||||
{
|
|
||||||
this.dbPath = dbPath;
|
|
||||||
this.logger = new Logger(getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean connect()
|
|
||||||
{
|
|
||||||
String url = sqliteURL.replace("%path%", dbPath);
|
|
||||||
|
|
||||||
if(!close()) return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
dbConnection = DriverManager.getConnection(url);
|
|
||||||
logger.log("Database connection established!");
|
|
||||||
return true;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean close()
|
|
||||||
{
|
|
||||||
if (dbConnection != null)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if(!dbConnection.isClosed())
|
|
||||||
{
|
|
||||||
dbConnection.close();
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
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 |
|
|
||||||
* --------------------------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
//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," + // channel the command was run in
|
|
||||||
"message_id TEXT NOT NULL," + // message id of the bot's response
|
|
||||||
"user_id TEXT NOT NULL, " + // user who ran the command
|
|
||||||
"channel_type TEXT NOT NULL" + // channel type (PRIVATE, FORUM, ...)
|
|
||||||
");");
|
|
||||||
|
|
||||||
for(String sql : newTables)
|
|
||||||
{
|
|
||||||
try (Statement stmt = dbConnection.createStatement()) {
|
|
||||||
// execute the statement
|
|
||||||
stmt.execute(sql);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
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) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package wtf.beatrice.hidekobot.datasources;
|
package wtf.beatrice.hidekobot.datasources;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import wtf.beatrice.hidekobot.HidekoBot;
|
import wtf.beatrice.hidekobot.HidekoBot;
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -12,7 +13,7 @@ public class PropertiesSource
|
|||||||
|
|
||||||
private Properties properties = null;
|
private Properties properties = null;
|
||||||
private final String fileName = "default.properties";
|
private final String fileName = "default.properties";
|
||||||
private final Logger logger = new Logger(getClass());
|
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesSource.class);
|
||||||
|
|
||||||
public void load()
|
public void load()
|
||||||
{
|
{
|
||||||
@@ -24,14 +25,16 @@ public class PropertiesSource
|
|||||||
{
|
{
|
||||||
properties.load(internalPropertiesStream);
|
properties.load(internalPropertiesStream);
|
||||||
|
|
||||||
}
|
} catch (IOException e)
|
||||||
catch (IOException e) {
|
{
|
||||||
logger.log(e.getMessage());
|
LOGGER.error(e.getMessage());
|
||||||
HidekoBot.shutdown();
|
HidekoBot.shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProperty(String property)
|
public String getProperty(String property)
|
||||||
{ return properties == null ? "" : properties.getProperty(property); }
|
{
|
||||||
|
return properties == null ? "" : properties.getProperty(property);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,23 +2,55 @@ package wtf.beatrice.hidekobot.listeners;
|
|||||||
|
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
import wtf.beatrice.hidekobot.commands.base.ClearChat;
|
import org.slf4j.Logger;
|
||||||
|
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.UrbanDictionary;
|
||||||
|
import wtf.beatrice.hidekobot.services.CommandService;
|
||||||
|
|
||||||
|
@Component
|
||||||
public class ButtonInteractionListener extends ListenerAdapter
|
public class ButtonInteractionListener extends ListenerAdapter
|
||||||
{
|
{
|
||||||
|
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
|
||||||
|
case "generic_dismiss" -> commandService.deleteUserLinkedMessage(event);
|
||||||
|
|
||||||
|
// urban dictionary navigation
|
||||||
|
case "urban_nextpage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.NEXT);
|
||||||
|
case "urban_previouspage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.PREVIOUS);
|
||||||
|
|
||||||
|
// trivia
|
||||||
|
case "trivia_correct" -> Trivia.handleAnswer(event, Trivia.AnswerType.CORRECT);
|
||||||
|
case "trivia_wrong_1", "trivia_wrong_2", "trivia_wrong_3" ->
|
||||||
|
Trivia.handleAnswer(event, Trivia.AnswerType.WRONG);
|
||||||
|
|
||||||
|
// error handling
|
||||||
|
default -> LOGGER.warn("Received unhandled {}", event.getClass().getSimpleName());
|
||||||
|
|
||||||
// clearchat command
|
|
||||||
case "clear_dismiss" -> ClearChat.dismissMessage(event);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,23 +7,27 @@ 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.objects.commands.CommandCategory;
|
||||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||||
import wtf.beatrice.hidekobot.objects.comparators.MessageCommandAliasesComparator;
|
import wtf.beatrice.hidekobot.objects.comparators.MessageCommandAliasesComparator;
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
|
@Component
|
||||||
public class MessageCommandListener extends ListenerAdapter
|
public class MessageCommandListener extends ListenerAdapter
|
||||||
{
|
{
|
||||||
|
|
||||||
// map storing command labels and command object alphabetically.
|
// map storing command labels and command object alphabetically.
|
||||||
private final TreeMap<LinkedList<String>, MessageCommand> registeredCommands =
|
private final TreeMap<LinkedList<String>, MessageCommand> registeredCommands =
|
||||||
new TreeMap<LinkedList<String>, MessageCommand>(new MessageCommandAliasesComparator());
|
new TreeMap<>(new MessageCommandAliasesComparator());
|
||||||
|
|
||||||
private final String commandRegex = "(?i)^(hideko|hde)\\b";
|
// map commands and their categories.
|
||||||
|
// this is not strictly needed but it's better to have it so we avoid looping every time we need to check the cat.
|
||||||
|
LinkedHashMap<CommandCategory, LinkedList<MessageCommand>> commandCategories = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private static final String COMMAND_REGEX = "(?i)^(hideko|hde)\\b";
|
||||||
// (?i) -> case insensitive flag
|
// (?i) -> case insensitive flag
|
||||||
// ^ -> start of string (not in middle of a sentence)
|
// ^ -> start of string (not in middle of a sentence)
|
||||||
// \b -> the word has to end here
|
// \b -> the word has to end here
|
||||||
@@ -36,12 +40,16 @@ public class MessageCommandListener extends ListenerAdapter
|
|||||||
|
|
||||||
public MessageCommand getRegisteredCommand(String label)
|
public MessageCommand getRegisteredCommand(String label)
|
||||||
{
|
{
|
||||||
for(LinkedList<String> aliases : registeredCommands.keySet())
|
for (Map.Entry<LinkedList<String>, MessageCommand> entry : registeredCommands.entrySet())
|
||||||
{
|
{
|
||||||
for(String currentAlias : aliases)
|
LinkedList<String> aliases = entry.getKey();
|
||||||
|
|
||||||
|
for (String currentAlias : aliases)
|
||||||
{
|
{
|
||||||
if(label.equals(currentAlias))
|
if (label.equals(currentAlias))
|
||||||
{ return registeredCommands.get(aliases); }
|
{
|
||||||
|
return entry.getValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,22 +57,25 @@ public class MessageCommandListener extends ListenerAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LinkedList<MessageCommand> getRegisteredCommands()
|
public LinkedList<MessageCommand> getRegisteredCommands()
|
||||||
{ return new LinkedList<>(registeredCommands.values()); }
|
{
|
||||||
|
return new LinkedList<>(registeredCommands.values());
|
||||||
|
}
|
||||||
private final Logger logger = new Logger(MessageCommandListener.class);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessageReceived(@NotNull MessageReceivedEvent event)
|
public void onMessageReceived(@NotNull MessageReceivedEvent event)
|
||||||
{
|
{
|
||||||
String eventMessage = event.getMessage().getContentDisplay();
|
// check if a bot is sending this message, and ignore it
|
||||||
|
if (event.getAuthor().isBot()) return;
|
||||||
|
|
||||||
|
// warning: we are getting the RAW value of the message content, not the DISPLAY value!
|
||||||
|
String eventMessage = event.getMessage().getContentRaw();
|
||||||
|
|
||||||
// check if the sent message matches the bot activation regex (prefix, name, ...)
|
// check if the sent message matches the bot activation regex (prefix, name, ...)
|
||||||
if(!eventMessage.toLowerCase().matches(commandRegex + ".*"))
|
if (!eventMessage.toLowerCase().matches("(?s)" + COMMAND_REGEX + ".*"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// generate args from the string
|
// generate args from the string
|
||||||
String argsString = eventMessage.replaceAll(commandRegex + "\\s*", "");
|
String argsString = eventMessage.replaceAll(COMMAND_REGEX + "\\s*", "");
|
||||||
|
|
||||||
|
|
||||||
// if no args were specified apart from the bot prefix
|
// if no args were specified apart from the bot prefix
|
||||||
@@ -72,9 +83,11 @@ public class MessageCommandListener extends ListenerAdapter
|
|||||||
// and that element is the whole string passed as a single argument, which would be empty in this case
|
// and that element is the whole string passed as a single argument, which would be empty in this case
|
||||||
// (or contain text in other cases like "string split ," if the passed text doesn't contain any comma ->
|
// (or contain text in other cases like "string split ," if the passed text doesn't contain any comma ->
|
||||||
// it will be the whole text as a single element.
|
// it will be the whole text as a single element.
|
||||||
if(argsString.isEmpty())
|
if (argsString.isEmpty())
|
||||||
{
|
{
|
||||||
event.getMessage().reply("Hello there! ✨").queue();
|
event.getMessage()
|
||||||
|
.reply("Hello there! ✨ Type `" + Cache.getBotPrefix() + " help` to get started!")
|
||||||
|
.queue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +98,13 @@ public class MessageCommandListener extends ListenerAdapter
|
|||||||
String commandLabel = argsRaw[0];
|
String commandLabel = argsRaw[0];
|
||||||
MessageCommand commandObject = getRegisteredCommand(commandLabel);
|
MessageCommand commandObject = getRegisteredCommand(commandLabel);
|
||||||
|
|
||||||
if(commandObject == null)
|
if (commandObject == null)
|
||||||
{
|
{
|
||||||
|
/* temporarily disabled because when people talk about the bot, it replies with this spammy message.
|
||||||
|
|
||||||
event.getMessage().reply("Unrecognized command: `" + commandLabel + "`!").queue(); // todo prettier
|
event.getMessage().reply("Unrecognized command: `" + commandLabel + "`!").queue(); // todo prettier
|
||||||
|
|
||||||
|
*/
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,36 +113,33 @@ public class MessageCommandListener extends ListenerAdapter
|
|||||||
|
|
||||||
// permissions check
|
// permissions check
|
||||||
List<Permission> requiredPermissions = commandObject.getPermissions();
|
List<Permission> requiredPermissions = commandObject.getPermissions();
|
||||||
if(requiredPermissions != null && !requiredPermissions.isEmpty())
|
if (requiredPermissions != null && !requiredPermissions.isEmpty())
|
||||||
{
|
{
|
||||||
if(channelType.isGuild()) //todo: what about forum post
|
if (channelType.isGuild()) //todo: what about forum post
|
||||||
{
|
{
|
||||||
Member member = event.getMember();
|
Member member = event.getMember();
|
||||||
GuildChannel channel = event.getGuildChannel(); //todo: what about forum post
|
GuildChannel channel = event.getGuildChannel(); //todo: what about forum post
|
||||||
if(member != null)
|
if (member != null && !member.hasPermission(channel, requiredPermissions))
|
||||||
{
|
{
|
||||||
if(!member.hasPermission(channel, requiredPermissions))
|
event.getMessage()
|
||||||
{
|
.reply("You do not have permissions to run this command!")
|
||||||
event.getMessage()
|
.queue(); // todo prettier
|
||||||
.reply("You do not have permissions to run this command!")
|
// todo: queue message deletion in 15 seconds or so
|
||||||
.queue(); // todo prettier
|
return;
|
||||||
// todo: queue message deletion in 15 seconds or so
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] commandArgs;
|
String[] commandArgs;
|
||||||
if(commandObject.passRawArgs())
|
if (commandObject.passRawArgs())
|
||||||
{
|
{
|
||||||
|
|
||||||
// remove first argument, which is the command label
|
// remove first argument, which is the command label
|
||||||
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);
|
||||||
|
|||||||
@@ -6,49 +6,51 @@ 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 net.dv8tion.jda.api.hooks.ListenerAdapter;
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
import org.slf4j.Logger;
|
||||||
|
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.
|
||||||
|
|
||||||
private final static String guildChannelFormat = "[%guild%] [#%channel%] %user%: %message%";
|
private static final String GUILD_MESSAGE_LOG_FORMAT = "[%guild%] [#%channel%] %user%: %message%";
|
||||||
private final static String dmFormat = "[DM] %user%: %message%";
|
private static final String DIRECT_MESSAGE_LOG_FORMAT = "[DM] %user%: %message%";
|
||||||
|
|
||||||
private final Logger logger = new Logger(MessageLogger.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(MessageLogger.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessageReceived(@NotNull MessageReceivedEvent event)
|
public void onMessageReceived(@NotNull MessageReceivedEvent event)
|
||||||
{
|
{
|
||||||
String toLog = "";
|
String toLog = "";
|
||||||
String userName = event.getAuthor().getAsTag();
|
String userName = event.getAuthor().getName();
|
||||||
String message = event.getMessage().getContentDisplay();
|
String message = event.getMessage().getContentDisplay();
|
||||||
|
|
||||||
if(event.getChannel() instanceof TextChannel)
|
if (event.getChannel() instanceof TextChannel channel)
|
||||||
{
|
{
|
||||||
String guildName = ((TextChannel) event.getChannel()).getGuild().getName();
|
String guildName = channel.getGuild().getName();
|
||||||
String channelName = event.getChannel().getName();
|
String channelName = event.getChannel().getName();
|
||||||
|
|
||||||
toLog = guildChannelFormat
|
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 = dmFormat;
|
toLog = DIRECT_MESSAGE_LOG_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
toLog = toLog
|
toLog = toLog
|
||||||
.replace("%user%", userName)
|
.replace("%user%", userName)
|
||||||
.replace("%message%", message);
|
.replace("%message%", message);
|
||||||
|
|
||||||
logger.log(toLog);
|
LOGGER.info(toLog);
|
||||||
|
|
||||||
if(!event.getMessage().getAttachments().isEmpty())
|
if (!event.getMessage().getAttachments().isEmpty())
|
||||||
{
|
{
|
||||||
for(Message.Attachment atch : event.getMessage().getAttachments())
|
for (Message.Attachment atch : event.getMessage().getAttachments())
|
||||||
{
|
{
|
||||||
logger.log(atch.getUrl());
|
LOGGER.info(atch.getUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package wtf.beatrice.hidekobot.listeners;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SelectMenuInteractionListener extends ListenerAdapter
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SelectMenuInteractionListener.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStringSelectInteraction(StringSelectInteractionEvent event)
|
||||||
|
{
|
||||||
|
switch (event.getComponentId().toLowerCase())
|
||||||
|
{
|
||||||
|
|
||||||
|
// trivia
|
||||||
|
case "trivia_categories" -> Trivia.handleMenuSelection(event);
|
||||||
|
|
||||||
|
// error handling
|
||||||
|
default -> LOGGER.warn("Received unhandled {}", event.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,13 @@ 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
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -21,16 +23,21 @@ 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)
|
||||||
{
|
{
|
||||||
String commandName = event.getName().toLowerCase();
|
String commandName = event.getName().toLowerCase();
|
||||||
SlashArgumentsCompleter completer = registeredCompleters.get(commandName);
|
SlashArgumentsCompleter completer = registeredCompleters.get(commandName);
|
||||||
if(completer == null) return;
|
if (completer == null) return;
|
||||||
|
|
||||||
// not running in a thread because nothing heavy should be done here...
|
// not running in a thread because nothing heavy should be done here...
|
||||||
completer.runCompletion(event);
|
completer.runCompletion(event);
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ 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
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -21,20 +23,24 @@ 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)
|
||||||
{
|
{
|
||||||
String commandName = event.getName().toLowerCase();
|
String commandName = event.getName().toLowerCase();
|
||||||
SlashCommand command = registeredCommands.get(commandName);
|
SlashCommand command = registeredCommands.get(commandName);
|
||||||
if(command == null) return;
|
if (command == null) return;
|
||||||
|
|
||||||
|
|
||||||
// finally run the command, in a new thread to avoid locking.
|
// finally run the command, in a new thread to avoid locking the main one.
|
||||||
new Thread(() -> command.runSlashCommand(event)).start();
|
new Thread(() -> command.runSlashCommand(event)).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record MessageResponse(@Nullable String content,
|
||||||
|
@Nullable MessageEmbed embed,
|
||||||
|
@Nullable ItemComponent... components)
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
MessageResponse response = (MessageResponse) o;
|
||||||
|
return Objects.equals(content, response.content) &&
|
||||||
|
Objects.equals(embed, response.embed) &&
|
||||||
|
Arrays.equals(components, response.components);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
int result = Objects.hash(content, embed);
|
||||||
|
result = 31 * result + Arrays.hashCode(components);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "MessageResponse{" +
|
||||||
|
"content=" + content +
|
||||||
|
", embed=" + embed +
|
||||||
|
", components=" + Arrays.toString(components) +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects.commands;
|
||||||
|
|
||||||
|
public enum CommandCategory
|
||||||
|
{
|
||||||
|
MODERATION("️\uD83D\uDC40"),
|
||||||
|
FUN("\uD83C\uDFB2"),
|
||||||
|
TOOLS("\uD83D\uDEE0"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private String emoji;
|
||||||
|
|
||||||
|
CommandCategory(String emoji)
|
||||||
|
{
|
||||||
|
this.emoji = emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmoji()
|
||||||
|
{
|
||||||
|
return emoji;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package wtf.beatrice.hidekobot.objects.commands;
|
|||||||
|
|
||||||
import net.dv8tion.jda.api.Permission;
|
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.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@@ -39,17 +40,40 @@ public interface MessageCommand
|
|||||||
*/
|
*/
|
||||||
boolean passRawArgs();
|
boolean passRawArgs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Say what category this command belongs to.
|
||||||
|
*
|
||||||
|
* @return the command category.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
CommandCategory getCategory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Say what this command does.
|
||||||
|
*
|
||||||
|
* @return a String explaining what this command does.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
String getDescription();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Say how people should use this command.
|
||||||
|
*
|
||||||
|
* @return a String explaining how to use the command, excluding the bot prefix and command name. Null if no parameter is needed
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
String getUsage();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the command logic by parsing the event and replying accordingly.
|
* Run the command logic by parsing the event and replying accordingly.
|
||||||
*
|
*
|
||||||
* @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, ...).
|
|
||||||
*/
|
*/
|
||||||
void runCommand(MessageReceivedEvent event, String label, String[] args);
|
void runCommand(MessageReceivedEvent event, String label, String[] args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -6,13 +6,16 @@ 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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ 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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,17 +8,20 @@ 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,15 @@ 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;
|
||||||
|
|
||||||
return linkedList.get(0).compareTo(t1.get(0));
|
return linkedList.get(0).compareTo(t1.get(0));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects.comparators;
|
||||||
|
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class gets two trivia categories, and compares them by their name.
|
||||||
|
*/
|
||||||
|
public class TriviaCategoryComparator implements Comparator<TriviaCategory>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(TriviaCategory o1, TriviaCategory o2)
|
||||||
|
{
|
||||||
|
return CharSequence.compare(o1.categoryName(), o2.categoryName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects.comparators;
|
||||||
|
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class gets two trivia scores, and compares their score.
|
||||||
|
*/
|
||||||
|
public class TriviaScoreComparator implements Comparator<TriviaScore>
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(TriviaScore o1, TriviaScore o2)
|
||||||
|
{
|
||||||
|
return Integer.compare(o2.getScore(), o1.getScore()); // inverted, because higher number should come first
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/main/java/wtf/beatrice/hidekobot/objects/fun/Dice.java
Normal file
45
src/main/java/wtf/beatrice/hidekobot/objects/fun/Dice.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects.fun;
|
||||||
|
|
||||||
|
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class Dice
|
||||||
|
{
|
||||||
|
private final int sides;
|
||||||
|
private int value = 0;
|
||||||
|
private final UUID uuid;
|
||||||
|
|
||||||
|
public Dice(int sides)
|
||||||
|
{
|
||||||
|
this.sides = sides;
|
||||||
|
this.uuid = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dice(Dice old)
|
||||||
|
{
|
||||||
|
this.sides = old.sides;
|
||||||
|
this.value = old.value;
|
||||||
|
this.uuid = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSides()
|
||||||
|
{
|
||||||
|
return sides;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void roll()
|
||||||
|
{
|
||||||
|
value = RandomUtil.getRandomNumber(1, sides);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getUUID()
|
||||||
|
{
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects.fun;
|
||||||
|
|
||||||
|
public record TriviaCategory(String categoryName, int categoryId)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects.fun;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record TriviaQuestion(String question, String correctAnswer,
|
||||||
|
List<String> wrongAnswers)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package wtf.beatrice.hidekobot.objects.fun;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
|
||||||
|
public class TriviaScore
|
||||||
|
{
|
||||||
|
|
||||||
|
private final User user;
|
||||||
|
private int score = 0;
|
||||||
|
|
||||||
|
public TriviaScore(User user)
|
||||||
|
{
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeScore(int add)
|
||||||
|
{
|
||||||
|
score += add;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScore()
|
||||||
|
{
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser()
|
||||||
|
{
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "[" + user.getName() + "," + score + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
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>
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
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>
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
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>
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,157 +1,66 @@
|
|||||||
package wtf.beatrice.hidekobot.runnables;
|
package wtf.beatrice.hidekobot.runnables;
|
||||||
|
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import org.slf4j.Logger;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
import org.slf4j.LoggerFactory;
|
||||||
import net.dv8tion.jda.api.entities.User;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|
||||||
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
|
||||||
import net.dv8tion.jda.api.requests.RestAction;
|
|
||||||
import wtf.beatrice.hidekobot.Cache;
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
import wtf.beatrice.hidekobot.HidekoBot;
|
import wtf.beatrice.hidekobot.services.CommandService;
|
||||||
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
import wtf.beatrice.hidekobot.services.DatabaseService;
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
|
||||||
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 final Logger logger;
|
private static final Logger LOGGER = LoggerFactory.getLogger(ExpiredMessageTask.class);
|
||||||
private DatabaseSource databaseSource;
|
|
||||||
|
|
||||||
|
|
||||||
public ExpiredMessageTask()
|
public ExpiredMessageTask(DatabaseService databaseService,
|
||||||
|
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();
|
|
||||||
logger = new Logger(getClass());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run()
|
||||||
|
{
|
||||||
|
|
||||||
databaseSource = Cache.getDatabaseSource();
|
List<String> expiringMessages = databaseService.getQueuedExpiringMessages();
|
||||||
if(databaseSource == null) return;
|
if (expiringMessages == null || expiringMessages.isEmpty()) return;
|
||||||
|
|
||||||
List<String> expiringMessages = Cache.getDatabaseSource().getQueuedExpiringMessages();
|
|
||||||
if(expiringMessages == null || expiringMessages.isEmpty()) return;
|
|
||||||
|
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
for(String messageId : expiringMessages)
|
for (String messageId : expiringMessages)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(Cache.isVerbose()) logger.log("expired check: " + messageId);
|
if (Cache.isVerbose()) LOGGER.info("expired check: {}", messageId);
|
||||||
|
|
||||||
String expiryTimestamp = databaseSource.getQueuedExpiringMessageExpiryDate(messageId);
|
String expiryTimestamp = databaseService.getQueuedExpiringMessageExpiryDate(messageId);
|
||||||
if(expiryTimestamp == null || expiryTimestamp.equals("")) // if missing timestamp
|
if (expiryTimestamp == null || expiryTimestamp.isEmpty()) // if missing timestamp
|
||||||
{
|
{
|
||||||
// count it as already expired
|
// count it as already expired
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
databaseService.untrackExpiredMessage(messageId);
|
||||||
// move on to next message
|
// move on to next message
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LocalDateTime expiryDate = LocalDateTime.parse(expiryTimestamp, formatter);
|
LocalDateTime expiryDate = LocalDateTime.parse(expiryTimestamp, formatter);
|
||||||
if(now.isAfter(expiryDate))
|
if (now.isAfter(expiryDate))
|
||||||
{
|
{
|
||||||
if(Cache.isVerbose()) logger.log("expired: " + messageId);
|
if (Cache.isVerbose()) LOGGER.info("expired: {}", messageId);
|
||||||
disableExpired(messageId);
|
commandService.disableExpired(messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disableExpired(String messageId)
|
|
||||||
{
|
|
||||||
String channelId = databaseSource.getQueuedExpiringMessageChannel(messageId);
|
|
||||||
|
|
||||||
ChannelType msgChannelType = databaseSource.getTrackedMessageChannelType(messageId);
|
|
||||||
|
|
||||||
MessageChannel textChannel = null;
|
|
||||||
|
|
||||||
|
|
||||||
// this should never happen, but only message channels are supported.
|
|
||||||
if(!msgChannelType.isMessage())
|
|
||||||
{
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is a DM
|
|
||||||
if(!(msgChannelType.isGuild()))
|
|
||||||
{
|
|
||||||
String userId = databaseSource.getTrackedReplyUserId(messageId);
|
|
||||||
User user = HidekoBot.getAPI().retrieveUserById(userId).complete();
|
|
||||||
if(user == null)
|
|
||||||
{
|
|
||||||
// if user is not found, consider it expired
|
|
||||||
// (deleted profile, or blocked the bot)
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
textChannel = user.openPrivateChannel().complete();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
String guildId = databaseSource.getQueuedExpiringMessageGuild(messageId);
|
|
||||||
Guild guild = HidekoBot.getAPI().getGuildById(guildId);
|
|
||||||
if(guild == null)
|
|
||||||
{
|
|
||||||
// if guild is not found, consider it expired
|
|
||||||
// (server was deleted or bot was kicked)
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
textChannel = guild.getTextChannelById(channelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(textChannel == null)
|
|
||||||
{
|
|
||||||
// if channel is not found, count it as expired
|
|
||||||
// (channel was deleted or bot permissions restricted)
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RestAction<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
|
|
||||||
|
|
||||||
|
|
||||||
if(Cache.isVerbose()) logger.log("cleaning up: " + messageId);
|
|
||||||
|
|
||||||
retrieveAction.queue(
|
|
||||||
|
|
||||||
message -> {
|
|
||||||
if(message == null)
|
|
||||||
{
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<LayoutComponent> components = message.getComponents();
|
|
||||||
List<LayoutComponent> newComponents = new ArrayList<>();
|
|
||||||
for (LayoutComponent component : components)
|
|
||||||
{
|
|
||||||
component = component.asDisabled();
|
|
||||||
newComponents.add(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
message.editMessageComponents(newComponents).queue();
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
},
|
|
||||||
|
|
||||||
(error) -> {
|
|
||||||
databaseSource.untrackExpiredMessage(messageId);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package wtf.beatrice.hidekobot.runnables;
|
package wtf.beatrice.hidekobot.runnables;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import wtf.beatrice.hidekobot.Cache;
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
import wtf.beatrice.hidekobot.util.Logger;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
@@ -9,22 +10,16 @@ import java.net.URL;
|
|||||||
|
|
||||||
public class HeartBeatTask implements Runnable
|
public class HeartBeatTask implements Runnable
|
||||||
{
|
{
|
||||||
private final Logger logger;
|
private static final Logger LOGGER = LoggerFactory.getLogger(HeartBeatTask.class);
|
||||||
|
|
||||||
public HeartBeatTask()
|
|
||||||
{
|
|
||||||
logger = new Logger(getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
@@ -34,18 +29,18 @@ public class HeartBeatTask implements Runnable
|
|||||||
connection.setReadTimeout(5000);
|
connection.setReadTimeout(5000);
|
||||||
|
|
||||||
int responseCode = connection.getResponseCode();
|
int responseCode = connection.getResponseCode();
|
||||||
if(200 <= responseCode && responseCode < 300)
|
if (200 <= responseCode && responseCode < 300)
|
||||||
{
|
{
|
||||||
// only log ok response codes when verbosity is enabled
|
// only log ok response codes when verbosity is enabled
|
||||||
if(Cache.isVerbose()) logger.log("Heartbeat response code: " + responseCode);
|
if (Cache.isVerbose()) LOGGER.info("Heartbeat response code: {}", responseCode);
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
logger.log("Heartbeat returned problematic response code: " + responseCode);
|
LOGGER.error("Heartbeat returned problematic response code: {}", responseCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e)
|
||||||
logger.log("Error while trying to push heartbeat: " + e.getMessage());
|
{
|
||||||
|
LOGGER.error("Error while trying to push heartbeat", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package wtf.beatrice.hidekobot.runnables;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This runnable pulls a random seed from random.org and used it to feed a SecureRandom,
|
||||||
|
* if the integration is enabled.
|
||||||
|
* <br/>
|
||||||
|
* This is necessary since we are not directly accessing random.org for each random number we
|
||||||
|
* need, and thus, only the seed is random - not the algorithm applied to it to compute the numbers.
|
||||||
|
*/
|
||||||
|
public class RandomOrgSeedTask implements Runnable
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(RandomOrgSeedTask.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
if (RandomUtil.isRandomOrgKeyValid())
|
||||||
|
{
|
||||||
|
if (Cache.isVerbose()) LOGGER.info("Updating Random seed from random.org...");
|
||||||
|
RandomUtil.initRandomOrg();
|
||||||
|
if (Cache.isVerbose()) LOGGER.info("Random.org seed updated!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package wtf.beatrice.hidekobot.runnables;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.Activity;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.HidekoBot;
|
||||||
|
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class StatusUpdateTask implements Runnable
|
||||||
|
{
|
||||||
|
|
||||||
|
List<String> statuses = Arrays.asList(
|
||||||
|
"Hatsune Miku: Project DIVA",
|
||||||
|
"Wii Sports",
|
||||||
|
"Excel",
|
||||||
|
"Mii Channel",
|
||||||
|
"Wii Speak",
|
||||||
|
"Minetest",
|
||||||
|
"Mario Kart Wii"
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
int randomPos = RandomUtil.getRandomNumber(0, statuses.size() - 1);
|
||||||
|
String status = statuses.get(randomPos) + " | " + Cache.getBotPrefix() + " help";
|
||||||
|
HidekoBot.getAPI().getPresence().setActivity(Activity.playing(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
216
src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java
Normal file
216
src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
package wtf.beatrice.hidekobot.runnables;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import wtf.beatrice.hidekobot.Cache;
|
||||||
|
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||||
|
import wtf.beatrice.hidekobot.objects.comparators.TriviaScoreComparator;
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
|
||||||
|
import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
|
||||||
|
import wtf.beatrice.hidekobot.services.CommandService;
|
||||||
|
import wtf.beatrice.hidekobot.services.DatabaseService;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
|
||||||
|
public class TriviaTask implements Runnable
|
||||||
|
{
|
||||||
|
|
||||||
|
private final DatabaseService databaseService;
|
||||||
|
private final CommandService commandService;
|
||||||
|
|
||||||
|
private final User author;
|
||||||
|
private final MessageChannel channel;
|
||||||
|
|
||||||
|
private Message previousMessage = null;
|
||||||
|
|
||||||
|
private final JSONObject triviaJson;
|
||||||
|
private final List<TriviaQuestion> questions;
|
||||||
|
private final TriviaCategory category;
|
||||||
|
|
||||||
|
ScheduledFuture<?> future = null;
|
||||||
|
|
||||||
|
private int iteration = 0;
|
||||||
|
|
||||||
|
public TriviaTask(User author,
|
||||||
|
MessageChannel channel,
|
||||||
|
TriviaCategory category,
|
||||||
|
DatabaseService databaseService,
|
||||||
|
CommandService commandService)
|
||||||
|
{
|
||||||
|
this.author = author;
|
||||||
|
this.channel = channel;
|
||||||
|
this.category = category;
|
||||||
|
this.databaseService = databaseService;
|
||||||
|
this.commandService = commandService;
|
||||||
|
|
||||||
|
triviaJson = Trivia.fetchJson(Trivia.getTriviaLink(category.categoryId()));
|
||||||
|
questions = Trivia.parseQuestions(triviaJson); //todo: null check, rate limiting...
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScheduledFuture(ScheduledFuture<?> future)
|
||||||
|
{
|
||||||
|
this.future = future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (previousMessage != null)
|
||||||
|
{
|
||||||
|
// todo: we shouldn't use this method, since it messes with the database... look at coin reflip
|
||||||
|
commandService.disableExpired(previousMessage.getId());
|
||||||
|
|
||||||
|
String previousCorrectAnswer = questions.get(iteration - 1).correctAnswer();
|
||||||
|
|
||||||
|
// we need this to be thread-locking to avoid getting out of sync with the rest of the trivia features
|
||||||
|
previousMessage.reply("The correct answer was: **" + previousCorrectAnswer + "**!").complete();
|
||||||
|
// todo: maybe also add who replied correctly as a list
|
||||||
|
|
||||||
|
// clean the list of people who answered, so they can answer again for the new question
|
||||||
|
Trivia.channelAndWhoResponded.put(previousMessage.getChannel().getId(), new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iteration >= questions.size())
|
||||||
|
{
|
||||||
|
|
||||||
|
String scoreboardText = "\uD83D\uDC23 Trivia session is over!";
|
||||||
|
|
||||||
|
List<String> winners = new ArrayList<>();
|
||||||
|
int topScore = 0;
|
||||||
|
StringBuilder othersBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
LinkedList<TriviaScore> triviaScores = Trivia.channelAndScores.get(channel.getId());
|
||||||
|
if (triviaScores == null) triviaScores = new LinkedList<>();
|
||||||
|
else triviaScores.sort(new TriviaScoreComparator());
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
Integer previousScore = null;
|
||||||
|
for (TriviaScore triviaScore : triviaScores)
|
||||||
|
{
|
||||||
|
if (pos > 10) break; // cap at top 10
|
||||||
|
|
||||||
|
String user = triviaScore.getUser().getAsMention();
|
||||||
|
int score = triviaScore.getScore();
|
||||||
|
if (previousScore == null)
|
||||||
|
{
|
||||||
|
previousScore = score;
|
||||||
|
topScore = score;
|
||||||
|
pos = 1;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (score != previousScore) pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == 1) winners.add(user);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
othersBuilder.append("\n").append(pos)
|
||||||
|
.append(" | ").append(user)
|
||||||
|
.append(": ").append(score).append(" points");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder winnersBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < winners.size(); i++)
|
||||||
|
{
|
||||||
|
String winner = winners.get(i);
|
||||||
|
winnersBuilder.append(winner);
|
||||||
|
if (i + 1 != winners.size())
|
||||||
|
{
|
||||||
|
winnersBuilder.append(", "); // separate with comma except on last run
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
winnersBuilder.append(": ").append(topScore).append(" points \uD83C\uDF89");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String winnersTitle = "\uD83D\uDCAB ";
|
||||||
|
winnersTitle += winners.size() == 1 ? "Winner" : "Winners";
|
||||||
|
|
||||||
|
String winnersString = winnersBuilder.toString();
|
||||||
|
String othersString = othersBuilder.toString();
|
||||||
|
|
||||||
|
EmbedBuilder scoreboardBuilder = new EmbedBuilder();
|
||||||
|
scoreboardBuilder.setColor(Cache.getBotColor());
|
||||||
|
scoreboardBuilder.setTitle("\uD83C\uDF1F Trivia Scoreboard");
|
||||||
|
if (!winnersString.isEmpty()) scoreboardBuilder.addField(winnersTitle, winnersString, false);
|
||||||
|
else scoreboardBuilder.addField("\uD83D\uDE22 Sad Trivia",
|
||||||
|
"No one played \uD83D\uDE2D", false);
|
||||||
|
if (!othersString.isEmpty()) scoreboardBuilder.addField("☁️ Others", othersString, false);
|
||||||
|
|
||||||
|
channel.sendMessage(scoreboardText).addEmbeds(scoreboardBuilder.build()).queue();
|
||||||
|
|
||||||
|
// remove all cached data
|
||||||
|
Trivia.channelsRunningTrivia.remove(channel.getId());
|
||||||
|
Trivia.channelAndWhoResponded.remove(channel.getId());
|
||||||
|
Trivia.channelAndScores.remove(channel.getId());
|
||||||
|
|
||||||
|
future.cancel(false);
|
||||||
|
// we didn't implement null checks on the future on purpose, because we need to know if we were unable
|
||||||
|
// to cancel it (and console errors should make it clear enough).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriviaQuestion currentTriviaQuestion = questions.get(iteration);
|
||||||
|
|
||||||
|
List<Button> answerButtons = new ArrayList<>();
|
||||||
|
|
||||||
|
Button correctAnswerButton = Button.primary("trivia_correct", currentTriviaQuestion.correctAnswer());
|
||||||
|
answerButtons.add(correctAnswerButton);
|
||||||
|
|
||||||
|
int i = 0; // we need to add a number because buttons can't have the same id
|
||||||
|
for (String wrongAnswer : currentTriviaQuestion.wrongAnswers())
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
Button wrongAnswerButton = Button.primary("trivia_wrong_" + i, wrongAnswer);
|
||||||
|
answerButtons.add(wrongAnswerButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.shuffle(answerButtons);
|
||||||
|
|
||||||
|
List<String> buttonEmojis = Arrays.asList("\uD83D\uDD34", "\uD83D\uDD35",
|
||||||
|
"\uD83D\uDFE2", "\uD83D\uDFE1", "\uD83D\uDFE4", "\uD83D\uDFE3", "\uD83D\uDFE0");
|
||||||
|
|
||||||
|
// add emojis to buttons
|
||||||
|
for (int emojiPos = 0; emojiPos < buttonEmojis.size(); emojiPos++)
|
||||||
|
{
|
||||||
|
if (emojiPos == answerButtons.size()) break;
|
||||||
|
|
||||||
|
String emoji = buttonEmojis.get(emojiPos);
|
||||||
|
Button button = answerButtons.get(emojiPos);
|
||||||
|
|
||||||
|
answerButtons.set(emojiPos, button.withEmoji(Emoji.fromUnicode(emoji)));
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||||
|
|
||||||
|
embedBuilder.setColor(Cache.getBotColor());
|
||||||
|
embedBuilder.setTitle("\uD83C\uDFB2 Trivia - " + category.categoryName() +
|
||||||
|
" (" + (iteration + 1) + "/" + questions.size() + ")");
|
||||||
|
|
||||||
|
embedBuilder.addField("❓ Question", currentTriviaQuestion.question(), false);
|
||||||
|
|
||||||
|
previousMessage = channel
|
||||||
|
.sendMessageEmbeds(embedBuilder.build())
|
||||||
|
.setActionRow(answerButtons)
|
||||||
|
.complete();
|
||||||
|
|
||||||
|
|
||||||
|
databaseService.trackRanCommandReply(previousMessage, author);
|
||||||
|
// 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: only stored in that table. this should be solved when we merge / fix the two main tables.
|
||||||
|
// todo: then, we can remove this instruction.
|
||||||
|
databaseService.queueDisabling(previousMessage);
|
||||||
|
|
||||||
|
iteration++;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
package wtf.beatrice.hidekobot.services;
|
||||||
|
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.Command;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||||
|
import net.dv8tion.jda.api.requests.RestAction;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
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.HidekoBot;
|
||||||
|
import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class CommandService
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(CommandService.class);
|
||||||
|
|
||||||
|
private final DatabaseService databaseService;
|
||||||
|
|
||||||
|
public CommandService(@Autowired DatabaseService databaseService)
|
||||||
|
{
|
||||||
|
this.databaseService = databaseService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to delete a message when a user clicks the "delete" button attached to that message.
|
||||||
|
* This will check in the database if that user ran the command originally.
|
||||||
|
*
|
||||||
|
* @param event the button interaction event.
|
||||||
|
*/
|
||||||
|
public void deleteUserLinkedMessage(ButtonInteractionEvent event)
|
||||||
|
{
|
||||||
|
// check if the user interacting is the same one who ran the command
|
||||||
|
if (!databaseService.isUserTrackedFor(event.getUser().getId(), event.getMessageId()))
|
||||||
|
{
|
||||||
|
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acknowledge immediately so the interaction token stays valid
|
||||||
|
event.deferEdit().queue(hook -> {
|
||||||
|
// Try deleting via the interaction webhook (works for original interaction responses)
|
||||||
|
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 */ }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to update slash commands registered on Discord's side.
|
||||||
|
* It runs automatically every time the bot starts, but only updates the commands in case differences
|
||||||
|
* are found, unless forced.
|
||||||
|
*
|
||||||
|
* @param force a boolean specifying if the update should be forced even if no differences were found.
|
||||||
|
*/
|
||||||
|
public void updateSlashCommands(boolean force)
|
||||||
|
{
|
||||||
|
|
||||||
|
// populate commands list from registered commands
|
||||||
|
List<CommandData> allCommands = new ArrayList<>();
|
||||||
|
for (SlashCommand cmd : Cache.getSlashCommandListener().getRegisteredCommands())
|
||||||
|
{
|
||||||
|
allCommands.add(cmd.getSlashCommandData());
|
||||||
|
}
|
||||||
|
|
||||||
|
JDA jdaInstance = HidekoBot.getAPI();
|
||||||
|
|
||||||
|
// get all the already registered commands
|
||||||
|
List<Command> registeredCommands = jdaInstance.retrieveCommands().complete();
|
||||||
|
|
||||||
|
boolean update = false;
|
||||||
|
|
||||||
|
if (force)
|
||||||
|
{
|
||||||
|
update = true;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
|
||||||
|
// for each command that we have already registered...
|
||||||
|
for (Command currRegCmd : registeredCommands)
|
||||||
|
{
|
||||||
|
boolean found = false;
|
||||||
|
|
||||||
|
// iterate through all "recognized" commands
|
||||||
|
for (CommandData cmdData : allCommands)
|
||||||
|
{
|
||||||
|
// if we find the same command...
|
||||||
|
if (cmdData.getName().equals(currRegCmd.getName()))
|
||||||
|
{
|
||||||
|
// quit the loop since we found it.
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no match was found, we need to send an updated command list because
|
||||||
|
// an old command was probably removed.
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
update = true;
|
||||||
|
|
||||||
|
// quit the loop since we only need to trigger this once.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if an update is not already queued...
|
||||||
|
if (!update)
|
||||||
|
{
|
||||||
|
// for each "recognized" valid command
|
||||||
|
for (CommandData currCmdData : allCommands)
|
||||||
|
{
|
||||||
|
boolean found = false;
|
||||||
|
|
||||||
|
// iterate through all already registered commands.
|
||||||
|
for (Command cmd : registeredCommands)
|
||||||
|
{
|
||||||
|
// if this command was already registered...
|
||||||
|
if (cmd.getName().equals(currCmdData.getName()))
|
||||||
|
{
|
||||||
|
// quit the loop since we found a match.
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no match was found, we need to send an updated command list because
|
||||||
|
// a new command was probably added.
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
update = true;
|
||||||
|
|
||||||
|
// quit the loop since we only need to trigger this once.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER.info("Found {} commands.", registeredCommands.size());
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
{
|
||||||
|
// send updated command list.
|
||||||
|
jdaInstance.updateCommands().addCommands(allCommands).queue();
|
||||||
|
LOGGER.info("Commands updated. New total: {}.", allCommands.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to disable all buttons from an expired message.
|
||||||
|
*
|
||||||
|
* @param messageId the message id to disable.
|
||||||
|
*/
|
||||||
|
public void disableExpired(String messageId)
|
||||||
|
{
|
||||||
|
String channelId = databaseService.getQueuedExpiringMessageChannel(messageId);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
ChannelType msgChannelType = databaseService.getTrackedMessageChannelType(messageId);
|
||||||
|
|
||||||
|
MessageChannel textChannel = null;
|
||||||
|
|
||||||
|
// this should never happen, but only message channels are supported.
|
||||||
|
if (!msgChannelType.isMessage())
|
||||||
|
{
|
||||||
|
databaseService.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a DM
|
||||||
|
if (!(msgChannelType.isGuild()))
|
||||||
|
{
|
||||||
|
String userId = databaseService.getTrackedReplyUserId(messageId);
|
||||||
|
User user = userId == null ? null : HidekoBot.getAPI().retrieveUserById(userId).complete();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
// if user is not found, consider it expired
|
||||||
|
// (deleted profile, or blocked the bot)
|
||||||
|
databaseService.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
textChannel = user.openPrivateChannel().complete();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
String guildId = databaseService.getQueuedExpiringMessageGuild(messageId);
|
||||||
|
Guild guild = guildId == null ? null : HidekoBot.getAPI().getGuildById(guildId);
|
||||||
|
if (guild == null)
|
||||||
|
{
|
||||||
|
// if guild is not found, consider it expired
|
||||||
|
// (server was deleted or bot was kicked)
|
||||||
|
databaseService.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
textChannel = guild.getTextChannelById(channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textChannel == null)
|
||||||
|
{
|
||||||
|
// if channel is not found, count it as expired
|
||||||
|
// (channel was deleted or bot permissions restricted)
|
||||||
|
databaseService.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RestAction<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
|
||||||
|
|
||||||
|
|
||||||
|
if (Cache.isVerbose()) LOGGER.info("cleaning up: {}", messageId);
|
||||||
|
|
||||||
|
retrieveAction.queue(
|
||||||
|
message -> {
|
||||||
|
if (message == null)
|
||||||
|
{
|
||||||
|
databaseService.untrackExpiredMessage(messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LayoutComponent> components = message.getComponents();
|
||||||
|
List<LayoutComponent> newComponents = new ArrayList<>();
|
||||||
|
for (LayoutComponent component : components)
|
||||||
|
{
|
||||||
|
component = component.asDisabled();
|
||||||
|
newComponents.add(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.editMessageComponents(newComponents).queue();
|
||||||
|
databaseService.untrackExpiredMessage(messageId);
|
||||||
|
},
|
||||||
|
|
||||||
|
error -> databaseService.untrackExpiredMessage(messageId));
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user