107 Commits

Author SHA1 Message Date
Bea
57a43703f2 Update instructions order when registering listeners
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 05:35:54 +01:00
Bea
de356a0163 Update a small comment 2022-12-28 05:35:49 +01:00
Bea
5440df2bb6 Update 'README.MD' 2022-12-28 05:35:42 +01:00
Bea
5710a5d7fe Update 'README.MD' 2022-12-28 05:35:29 +01:00
f0c55f7bf8 Bump version to 0.5.16
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-26 04:11:01 +01:00
791d314da4 Implement ban, kick, timeout slash commands
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-26 04:10:17 +01:00
81d3aebb7f Make trivia command defer reply
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-26 03:51:43 +01:00
ffb5ef7181 Rework time parsing utils
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-26 03:34:26 +01:00
51e11e8445 Implement basic timeout command
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-26 03:34:11 +01:00
00c61968b8 Implement basic ban command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-26 02:40:25 +01:00
e2fda4c7cd Implement basic kick command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-26 00:22:32 +01:00
11d4b7fa56 Add bot age info
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-25 02:12:39 +01:00
f7ef27066d Bump version to 0.5.15
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-25 01:49:43 +01:00
beae316bb3 Implement profile banner grabber command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-25 01:48:31 +01:00
b02892d60b Fix minetest's name in random statuses
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-24 15:20:39 +01:00
8b2197b4f6 Fix missing JSON dependency
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-24 14:48:17 +01:00
e65ec54fd7 Mitigate potential RCE from SnakeYaml (CVE-2022-1471)
Some checks failed
continuous-integration/drone/push Build is failing
This vulnerability is very unlikely to ever happen, since the only way to modify the YAML file is to edit it yourself, and it would be useless for a bot owner to RCE their own bot. No other person can edit the configuration file remotely (eg. with bot commands), so realistically, this could not happen.
2022-12-24 14:42:01 +01:00
cd1a50a6d1 Make trivia support slash commands too
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 23:31:12 +01:00
ad8078809b Handle trivia edge cases without hanging
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 20:27:56 +01:00
5488ec567e Bump version to 0.5.14
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 17:59:40 +01:00
42cb72fd3d Implement trivia welcome screen with category picker
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 17:59:25 +01:00
71904f4243 Bump version to 0.5.13
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 16:40:41 +01:00
69bd1a5652 Make trivia have a functional scoreboard
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 16:40:17 +01:00
d188eae1e2 Make trivia functional
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 14:24:07 +01:00
336f8364c7 Add emojis to trivia buttons
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 04:37:32 +01:00
acb2ee21c2 Make trivia announce correct answer
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 04:26:36 +01:00
c02cc8c7df Raise trivia timeout to 15s
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 04:11:05 +01:00
8d0d181ad9 Make trivia loop through all questions
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 04:09:27 +01:00
7dce206a01 Disable trivia in dms
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 03:50:23 +01:00
702ed65a12 Raise interaction expiration time to 30s
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 03:43:55 +01:00
53fd3dc81d Make trivia functional for a single question
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 03:40:13 +01:00
c3354b9976 Only fetch multiple-answer trivia for now
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 02:54:07 +01:00
ae1101e93f Fix build errors
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 02:52:17 +01:00
3f67777659 Remove unneeded methods from MessageResponse
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-21 02:51:23 +01:00
c63bafc88e Start implementing trivia command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-21 02:50:22 +01:00
6c857e2f9a Remove double space on urban footer
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 23:37:37 +01:00
6c33581dc4 Add emojis to urban dictionary
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 23:30:40 +01:00
931d6efaef Convert message response to immutable record
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 23:07:01 +01:00
e3e8b469ba Bump version to 0.5.12
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 23:03:34 +01:00
54022221a0 Make dice roll support slash commands too
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 23:03:21 +01:00
58c4412f75 Add a MessageResponse class for mixed-type content
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-20 23:03:07 +01:00
0bd63e76bd Rename method
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 22:49:20 +01:00
d49fe3ee15 Cache love calculator results in RAM
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 22:48:29 +01:00
5e48652587 Make love calculator also support slash commands
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 22:32:40 +01:00
6ffe10e4c8 Optimize imports 2022-12-20 22:24:34 +01:00
0f54fe856e Increase randomness by updating the random's seed every minute
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 22:15:52 +01:00
5a7f884703 Bump version to 0.5.11 2022-12-20 22:08:58 +01:00
e5c5993fb2 Make random statuses update automatically
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 22:08:05 +01:00
d331c48ced Fix invite command being categorized as fun
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 18:04:37 +01:00
84ff5a752e Improve help command title
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 18:03:02 +01:00
4f408fb5f9 Make permissions bold instead of code-wrapped
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 17:58:14 +01:00
1384259187 Bump version to 0.5.10
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 17:55:43 +01:00
82698ec5fe Fix nothing being rolles if no arg was specified
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 17:55:30 +01:00
0762068465 Make help command use descriptions and usages
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 17:51:28 +01:00
cc9aee3441 Fix spacing 2022-12-20 17:23:52 +01:00
86c7c30d8f Improve default responses 2022-12-20 17:23:20 +01:00
19e3cde7e6 Bump version to 0.5.9
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 17:18:39 +01:00
72f9bb4eb5 Make command category not null
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 17:18:24 +01:00
407ca279f5 Implement alias command 2022-12-20 17:18:09 +01:00
9eefa4b958 Implement alias command 2022-12-20 17:17:13 +01:00
9278b485d9 Add help command and command categories
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 17:06:18 +01:00
5a2205e567 Remove deprecated and unused private method
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 15:15:03 +01:00
3212ceb03c Throw exception in case of serialization issue
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 15:13:21 +01:00
a80b2cc5a9 Make serialization util class
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 15:11:44 +01:00
d087de1d01 Use enum instead of boolean for page switching
All checks were successful
continuous-integration/drone/push Build is passing
This is useless but looks better
2022-12-20 15:09:35 +01:00
99fc980e00 Remove duplication
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 15:04:39 +01:00
02627ab732 Add javadoc comment
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 14:53:58 +01:00
dead16f338 Merge two classes
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 14:51:29 +01:00
2f51f9d40c Optimize imports
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 14:50:09 +01:00
5f5fc8d3a8 Remove duplicated method
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 14:49:44 +01:00
4c98182da7 Fix small emoji translation issue
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 03:44:00 +01:00
9e57a3a426 Optimize imports
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 03:17:32 +01:00
2bf08c27b7 Make urban command support slash too
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 02:17:27 +01:00
b085efeccb Allow sender to delete their own urban command results
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 00:59:26 +01:00
d8604c7ae5 Fix urban dictionary term not getting parsed correctly for url
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 00:52:31 +01:00
e83d7de7f5 Bump version to 0.5.8
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 00:35:08 +01:00
dfa25e54f3 Make urban command support multiple entries
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 00:34:49 +01:00
d1dc71dde9 Improve urban dictionary parsing
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 22:53:25 +01:00
6fcd3b4cdf Improve urban dictionary parsing
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 22:45:02 +01:00
deb7d83e64 Keep newlines in urban dictionary parser
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 21:58:57 +01:00
010a25fd66 Bump version to 0.5.7
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 21:43:07 +01:00
7c2530c88b Implement urban dictionary lookup command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 21:42:55 +01:00
20665f4862 Change a magic ball response
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 21:03:20 +01:00
ecfa3cded8 Make it send a message instead of responding to 8ball
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 21:00:25 +01:00
90e0c4ddf9 Bump version to 0.5.6
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 20:39:16 +01:00
fd9fe4ead6 Add basic love calculator message command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 20:38:32 +01:00
6cdd44da29 Bump version to 0.5.5
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 18:37:38 +01:00
3dd30a3a89 Make magicball support slash commands too
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 18:37:17 +01:00
5c8bad2b02 Improve magic ball answers
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 18:28:21 +01:00
b23bc30fc0 Implement magic ball message command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 18:24:29 +01:00
018e24034f Bump version to 0.5.4
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 17:32:18 +01:00
Lorenzo Dellacà
1a19a9ea06 Improve diceroll looks, implement limits to avoid abuse
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 17:31:28 +01:00
Lorenzo Dellacà
495f164552 Ignore bots interacting with hideko
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 16:54:15 +01:00
Lorenzo Dellacà
fd100649a7 Remove unneeded todo 2022-12-19 16:48:20 +01:00
Lorenzo Dellacà
b3990ff04f Make clear command also delete the sender's message
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 16:47:49 +01:00
Lorenzo Dellacà
f5238ced89 Bump version to 0.5.3
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 01:41:04 +01:00
Lorenzo Dellacà
f0ee565185 Implement basic functional diceroll command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 01:36:43 +01:00
Lorenzo Dellacà
a21d179308 Fix command label being passed as arg in case of no args
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:22:51 +01:00
Lorenzo Dellacà
36ad728bbc Fallback to 0 instead of 1
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:14:30 +01:00
Lorenzo Dellacà
1a6fe6465c Fix console error when int parsing fails in clear message
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:13:14 +01:00
Lorenzo Dellacà
f0004dc555 Re-register accidentally removed invite command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:10:15 +01:00
Lorenzo Dellacà
8ddf0ab80d Bump JDA version to more stable beta
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:07:02 +01:00
Lorenzo Dellacà
660e18d1f4 Bump version to 0.5.2
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:05:49 +01:00
Lorenzo Dellacà
db943f7e05 Fix messages with newlines not being handled for commands
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:05:36 +01:00
Lorenzo Dellacà
cb49bda84a Make say support both slash and message commands 2022-12-19 00:05:13 +01:00
Lorenzo Dellacà
b318b9f22b Bump version to 0.5.1
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-18 23:49:00 +01:00
Lorenzo Dellacà
1447f8c177 Make avatar support both slash and message commands
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-18 23:47:54 +01:00
91 changed files with 1057 additions and 1971 deletions

View File

@@ -1,14 +1,14 @@
kind: template
load: java-build-deploy.yaml
data:
arch: arm64
os: linux
build_branches:
kind: pipeline
name: default
trigger:
branch:
- main
- develop
build_events:
- push
- pull_request
sonar_project_key: HidekoBot
deploy_targets:
- production
steps:
- name: build
image: maven:3-eclipse-temurin-16
commands:
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
- mvn javadoc:javadoc
- mvn test -B

1
.gitignore vendored
View File

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

View File

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

View File

@@ -1,16 +1,7 @@
# HidekoBot
[![Reliability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=reliability_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
[![Maintainability Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=sqale_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
[![Security Rating](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=security_rating&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
[![Build Status](https://drone.beatrice.wtf/api/badges/bea/HidekoBot/status.svg)](https://drone.beatrice.wtf/bea/HidekoBot)
[![Lines of Code](https://sonar.beatrice.wtf/api/project_badges/measure?project=HidekoBot&metric=ncloc&token=0a63c149148555d6d2ee40665af1afae8f67cc3f)](https://sonar.beatrice.wtf/dashboard?id=HidekoBot)
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
Download a prebuilt JAR file or build it from source, then run it with:
```bash
@@ -23,11 +14,11 @@ Additionally available parameters are:
- **verbose**: log every message that the bot receives, plus additional debugging messages. Very spammy and performance heavy.
- **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 21 or later is required.*
*Note: Java 16 or later is required.*
## Initial setup
Run the startup command once. The bot will generate a `config.yml` file in your current directory (`$PWD` on GNU/Linux).
Run the startup command once. The bot will generate a `config.yml` file in the directory you were when you ran it.
Edit the configuration file and set all values according to your needs.
@@ -37,12 +28,5 @@ already set-up. The bot supports both slash commands and message commands, with
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
in the same directory that you ran it. 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.

109
pom.xml
View File

@@ -6,107 +6,58 @@
<groupId>wtf.beatrice.hidekobot</groupId>
<artifactId>HidekoBot</artifactId>
<version>0.6.3-SNAPSHOT</version>
<version>0.5.16</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<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>
<dependencies>
<!-- Basic JDA dependency for Discord API -->
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>5.6.1</version>
<version>5.0.0-beta.2</version>
</dependency>
<!-- JDA depends on SLF4J for logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.17</version>
<version>2.0.4</version>
</dependency>
<!-- Dependency used for SQLite database connections-->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.51.0.0</version>
<version>3.39.4.1</version>
</dependency>
<!-- Dependency used for YAML configuration files -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.5</version>
<version>1.33</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.21.2</version>
<version>1.15.3</version>
</dependency>
<!-- Various String manipulation utils -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.14.0</version>
<version>1.10.0</version>
</dependency>
<!-- JSON dependency used for better parsing of JSON files -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20250517</version>
<version>20220924</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.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.20.0</version>
</dependency>
<!-- End Random.org dependencies -->
<!-- Unit Tests Dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>6.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- override dependencies to use newer versions -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.33.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<resources>
@@ -143,46 +94,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
</plugin>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>5.3.0.6276</version>
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>12.1.8</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>
<version>3.4.1</version>
</plugin>
</plugins>
</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>

View File

@@ -1,3 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

View File

@@ -1,20 +0,0 @@
<?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>

View File

@@ -1,8 +1,6 @@
package wtf.beatrice.hidekobot;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.datasources.ConfigurationEntry;
import wtf.beatrice.hidekobot.datasources.ConfigurationSource;
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
@@ -11,26 +9,27 @@ import wtf.beatrice.hidekobot.listeners.MessageCommandListener;
import wtf.beatrice.hidekobot.listeners.MessageLogger;
import wtf.beatrice.hidekobot.listeners.SlashCommandCompletionListener;
import wtf.beatrice.hidekobot.listeners.SlashCommandListener;
import wtf.beatrice.hidekobot.util.Logger;
import java.awt.*;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
public class Cache
{
private Cache()
{
throw new IllegalStateException("Utility class");
}
// todo: make this compatible with the message listener's regex
private static final String BOT_PREFIX = "hideko";
private static final Logger LOGGER = LoggerFactory.getLogger(Cache.class);
private static final String botPrefix = "hideko";
private static final Logger logger = new Logger(Cache.class);
// the Random instance that we should always use when looking for an RNG based thing.
// the seed is updated periodically.
private static final Random randomInstance = new Random();
// map to store results of "love calculator", to avoid people re-running the same command until
// they get what they wanted.
@@ -43,11 +42,11 @@ public class Cache
private static DatabaseSource databaseSource = null;
private static boolean verbose = false;
private static MessageLogger verbosityLogger = null;
private static final long BOT_MAINTAINER_ID = 979809420714332260L;
private static final String EXPIRY_TIMESTAMP_FORMAT = "yy/MM/dd HH:mm:ss";
private static final long botMaintainerId = 979809420714332260L;
private final static String expiryTimestampFormat = "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.
private static final long EXPIRY_TIME_SECONDS = 30L;
private final static long expiryTimeSeconds = 30L;
// used to count e.g. uptime
private static LocalDateTime startupTime = null;
@@ -56,16 +55,16 @@ public class Cache
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 final static 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 final static String execPath = System.getProperty("user.dir");
private static final String botName = "Hideko";
private static SlashCommandListener slashCommandListener = null;
private static SlashCommandCompletionListener slashCommandCompletionListener = null;
private static MessageCommandListener messageCommandListener = null;
private static final String DEFAULT_INVITE_LINK =
private final static String defaultInviteLink =
"https://discord.com/api/oauth2/authorize?client_id=%userid%&scope=bot+applications.commands&permissions=8";
private static String botApplicationId = "";
@@ -79,21 +78,22 @@ public class Cache
*
* @return array of supported resolutions.
*/
public static int[] getSupportedAvatarResolutions()
{
return supportedAvatarResolutions;
public static int[] getSupportedAvatarResolutions() { return supportedAvatarResolutions; }
public static Random getRandom() {
return randomInstance;
}
public static void setRandomSeed(long seed) {
randomInstance.setSeed(seed);
}
/**
* Checks if the bot has been started with the verbose argument.
*
* @return a boolean which is true if the bot is in verbose-mode
*/
public static synchronized boolean isVerbose()
{
return verbose;
}
public static boolean isVerbose() { return verbose; }
/**
* Set the bot's verbosity status at runtime.
@@ -101,20 +101,24 @@ public class Cache
*
* @param v the verbosity boolean value
*/
public static synchronized void setVerbose(boolean v)
public static void setVerbose(boolean v)
{
verbose = v;
if(v)
{
if(verbosityLogger == null)
{
verbosityLogger = new MessageLogger();
}
HidekoBot.getAPI().addEventListener(verbosityLogger);
} else {
if(verbosityLogger != null)
{
HidekoBot.getAPI().removeEventListener(verbosityLogger);
verbosityLogger = null;
}
if (v)
{
verbosityLogger = new MessageLogger();
HidekoBot.getAPI().addEventListener(verbosityLogger);
}
}
@@ -123,8 +127,7 @@ public class Cache
*
* @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);
}
@@ -134,22 +137,17 @@ public class Cache
*
* @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);
}
/**
* Get the bot maintainer's profile id.
*
* @return a long of the account's id
*/
public static long getBotMaintainerId()
{
return BOT_MAINTAINER_ID;
}
public static long getBotMaintainerId() { return botMaintainerId; }
/**
* Set the bot's application id.
@@ -166,19 +164,15 @@ public class Cache
*
* @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
*
* @return a string containing the invite link
*/
public static String getInviteUrl()
{
return DEFAULT_INVITE_LINK.replace("%userid%", botApplicationId);
public static String getInviteUrl() {
return defaultInviteLink.replace("%userid%", botApplicationId);
}
/**
@@ -196,10 +190,7 @@ public class Cache
*
* @return the DatabaseSource instance.
*/
public static @Nullable DatabaseSource getDatabaseSource()
{
return databaseSource;
}
public static @Nullable DatabaseSource getDatabaseSource() { return databaseSource; }
/**
* Set the properties source instance loaded from the JAR archive.
@@ -216,67 +207,43 @@ public class Cache
*
* @return the String of the DateTimeFormatter format.
*/
public static String getExpiryTimestampFormat()
{
return EXPIRY_TIMESTAMP_FORMAT;
}
public static String getExpiryTimestampFormat(){ return expiryTimestampFormat; }
/**
* Get the amount of seconds after which a message expires.
*
* @return long value of the expiry seconds.
*/
public static long getExpiryTimeSeconds()
{
return EXPIRY_TIME_SECONDS;
}
public static long getExpiryTimeSeconds() { return expiryTimeSeconds; }
public static String getBotName()
{
return BOT_NAME;
}
public static String getBotName() { return botName; };
/**
* Get the bot's version.
*
* @return a String of the bot version.
*/
public static String getBotVersion()
{
public static String getBotVersion() {
return propertiesSource.getProperty("bot.version");
}
/**
* 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 + "/";
}
/**
* Get the bot's global color.
*
* @return the Color object.
*/
public static Color getBotColor()
{
public static Color getBotColor() {
Color defaultColor = Color.PINK;
if(configurationSource == null) return defaultColor;
String colorName = (String) configurationSource.getConfigValue(ConfigurationEntry.BOT_COLOR);
Color color = null;
try
{
try {
Field field = Color.class.getField(colorName);
color = (Color)field.get(null);
} catch (RuntimeException | NoSuchFieldException | IllegalAccessException e)
{
LOGGER.error("Unknown color: {}", colorName);
} catch (RuntimeException | NoSuchFieldException | IllegalAccessException e) {
logger.log("Unknown color: " + colorName);
}
return color == null ? defaultColor : color;
}
@@ -284,36 +251,21 @@ public class Cache
//todo javadocs
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)
{
slashCommandCompletionListener = commandCompletionListener;
}
{ slashCommandCompletionListener = commandCompletionListener; }
public static SlashCommandCompletionListener getSlashCommandCompletionListener()
{
return slashCommandCompletionListener;
}
public static SlashCommandCompletionListener getSlashCommandCompletionListener() { return slashCommandCompletionListener; }
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.
@@ -321,9 +273,7 @@ public class Cache
* @param time a LocalDateTime of the startup moment.
*/
public static void setStartupTime(LocalDateTime time)
{
startupTime = time;
}
{ startupTime = time; }
/**
@@ -331,54 +281,33 @@ public class Cache
*
* @return a LocalDateTime object of the startup instant.
*/
public static LocalDateTime getStartupTime()
{
return startupTime;
}
public static LocalDateTime getStartupTime() { return startupTime; }
/**
* 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 LocalDateTime getBotBirthDate() { return botBirthDate; }
public static String getFullHeartBeatLink()
{
public static String getFullHeartBeatLink() {
return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.HEARTBEAT_LINK);
}
//todo javadocs
public static String getExecPath()
{
return EXEC_PATH;
}
public static String getExecPath() { return execPath; }
/*private static ConfigurationSource getConfigurationSource()
{ return configurationSource; }*/
public static String getRandomOrgApiKey()
{
return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.RANDOM_ORG_API_KEY);
}
public static void setConfigurationSource(ConfigurationSource configurationSource)
{
Cache.configurationSource = configurationSource;
}
{ Cache.configurationSource = configurationSource; }
/**
* Get the bot's prefix
*
* @return a String of the bot's prefix.
*/
public static String getBotPrefix()
{
return BOT_PREFIX;
}
public static String getBotPrefix() { return botPrefix; }
public static void cacheLoveCalculatorValue(String userId1, String userId2, int value)
{
@@ -403,8 +332,7 @@ public class Cache
loveCalculatorValues.remove(userId2 + "|" + userId1);
}
public static ScheduledExecutorService getTaskScheduler()
{
public static ScheduledExecutorService getTaskScheduler() {
return taskScheduler;
}

View File

@@ -4,8 +4,7 @@ import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.requests.GatewayIntent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Signal;
import wtf.beatrice.hidekobot.commands.completer.ProfileImageCommandCompleter;
import wtf.beatrice.hidekobot.commands.message.HelloCommand;
import wtf.beatrice.hidekobot.commands.slash.*;
@@ -15,11 +14,10 @@ import wtf.beatrice.hidekobot.datasources.PropertiesSource;
import wtf.beatrice.hidekobot.listeners.*;
import wtf.beatrice.hidekobot.runnables.ExpiredMessageTask;
import wtf.beatrice.hidekobot.runnables.HeartBeatTask;
import wtf.beatrice.hidekobot.runnables.RandomOrgSeedTask;
import wtf.beatrice.hidekobot.runnables.RandomSeedTask;
import wtf.beatrice.hidekobot.runnables.StatusUpdateTask;
import wtf.beatrice.hidekobot.util.CommandUtil;
import wtf.beatrice.hidekobot.util.FormatUtil;
import wtf.beatrice.hidekobot.util.RandomUtil;
import wtf.beatrice.hidekobot.util.Logger;
import java.io.File;
import java.time.LocalDateTime;
@@ -34,31 +32,31 @@ public class HidekoBot
{
private static JDA jda;
private static final Logger LOGGER = LoggerFactory.getLogger(HidekoBot.class);
private static final Logger logger = new Logger(HidekoBot.class);
public static void main(String[] args)
{
// load configuration
LOGGER.info("Loading configuration...");
logger.log("Loading configuration...");
String configFilePath = Cache.getExecPath() + File.separator + "config.yml";
ConfigurationSource configurationSource = new ConfigurationSource(configFilePath);
configurationSource.initConfig();
Cache.setConfigurationSource(configurationSource);
LOGGER.info("Configuration loaded!");
logger.log("Configuration loaded!");
// load properties
LOGGER.info("Loading properties...");
logger.log("Loading properties...");
PropertiesSource propertiesSource = new PropertiesSource();
propertiesSource.load();
Cache.setPropertiesSourceInstance(propertiesSource);
LOGGER.info("Properties loaded!");
logger.log("Properties loaded!");
// check loaded bot token
String botToken = Cache.getBotToken();
if(botToken == null || botToken.isEmpty())
{
LOGGER.error("Invalid bot token!");
logger.log("Invalid bot token!");
shutdown();
return;
}
@@ -76,14 +74,9 @@ public class HidekoBot
);
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)
{
LOGGER.error(e.getMessage()); // print the error message, omit the stack trace.
logger.log(e.getMessage()); // print the error message, omit the stack trace.
shutdown(); // if we failed connecting and authenticating, then quit.
}
@@ -94,10 +87,9 @@ public class HidekoBot
// store if we have to force refresh commands despite no apparent changes.
boolean forceUpdateCommands = false;
// if there is at least one arg, then iterate through them because we have additional things to do.
// if there is more than 1 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.
if (args.length > 0)
{
if(args.length > 1) {
List<String> argsList = new ArrayList<>(Arrays.asList(args));
@@ -114,19 +106,6 @@ public class HidekoBot
}
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
SlashCommandListener slashCommandListener = new SlashCommandListener();
SlashCommandCompletionListener slashCommandCompletionListener = new SlashCommandCompletionListener();
@@ -188,62 +167,54 @@ public class HidekoBot
// update slash commands (delayed)
final boolean finalForceUpdateCommands = forceUpdateCommands;
try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor())
{
executor.schedule(() -> CommandUtil.updateSlashCommands(finalForceUpdateCommands),
1, TimeUnit.SECONDS);
}
Executors.newSingleThreadScheduledExecutor().schedule(() -> // todo: try-with-resources
CommandUtil.updateSlashCommands(finalForceUpdateCommands), 1, TimeUnit.SECONDS);
// set the bot's status
jda.getPresence().setStatus(OnlineStatus.ONLINE);
// connect to database
LOGGER.info("Connecting 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.info("Database connection initialized!");
logger.log("Database connection initialized!");
Cache.setDatabaseSourceInstance(databaseSource);
// load data here...
LOGGER.info("Database data loaded into memory!");
} else
{
LOGGER.error("Error initializing database connection!");
logger.log("Database data loaded into memory!");
} else {
logger.log("Error initializing database connection!");
}
// start scheduled runnables
ScheduledExecutorService scheduler = Cache.getTaskScheduler();
ExpiredMessageTask expiredMessageTask = new ExpiredMessageTask();
scheduler.scheduleAtFixedRate(expiredMessageTask, 5L, 5L, TimeUnit.SECONDS); //every 5 seconds
scheduler.scheduleAtFixedRate(expiredMessageTask, 5, 5, TimeUnit.SECONDS); //every 5 seconds
HeartBeatTask heartBeatTask = new HeartBeatTask();
scheduler.scheduleAtFixedRate(heartBeatTask, 10L, 30L, TimeUnit.SECONDS); //every 30 seconds
scheduler.scheduleAtFixedRate(heartBeatTask, 10, 30, 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
}
scheduler.scheduleAtFixedRate(statusUpdateTask, 0, 60 * 5, TimeUnit.SECONDS); // every 5 minutes
RandomSeedTask randomSeedTask = new RandomSeedTask();
scheduler.scheduleAtFixedRate(randomSeedTask, 0, 60, TimeUnit.SECONDS); // every minute
// register shutdown interrupt signal listener for proper shutdown.
Runtime.getRuntime().addShutdownHook(new Thread(HidekoBot::preShutdown));
Signal.handle(new Signal("INT"), signal -> shutdown());
// set startup time.
Cache.setStartupTime(LocalDateTime.now());
// print the bot logo.
LOGGER.info("\n\n{}\nv{} - bot is ready!\n", FormatUtil.getLogo(), Cache.getBotVersion());
logger.log("\n\n" + logger.getLogo() + "\nv" + Cache.getBotVersion() + " - bot is ready!\n", 2);
// log the invite-link to console so noob users can just click on it.
LOGGER.info("Bot User ID: {}", botUserId);
LOGGER.info("Invite Link: {}", Cache.getInviteUrl());
logger.log("Bot User ID: " + botUserId, 3);
logger.log("Invite Link: " + Cache.getInviteUrl(), 4);
}
public static JDA getAPI()
{
return jda;
@@ -251,14 +222,9 @@ public class HidekoBot
public static void shutdown()
{
preShutdown();
logger.log("WARNING! Shutting down!");
if(jda != null) jda.shutdown();
System.exit(0);
}
private static void preShutdown()
{
LOGGER.warn("WARNING! Shutting down!");
if (jda != null) jda.shutdown();
}
}

View File

@@ -6,11 +6,6 @@ import java.util.LinkedList;
public class Alias
{
private Alias()
{
throw new IllegalStateException("Utility class");
}
public static String generateNiceAliases(MessageCommand command)
{
LinkedList<String> aliases = command.getCommandLabels();

View File

@@ -5,7 +5,6 @@ import net.dv8tion.jda.api.entities.MessageEmbed;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.util.FormatUtil;
import wtf.beatrice.hidekobot.util.RandomUtil;
import java.lang.management.ManagementFactory;
import java.text.DecimalFormat;
@@ -13,11 +12,6 @@ import java.util.List;
public class BotInfo
{
private BotInfo()
{
throw new IllegalStateException("Utility class");
}
public static MessageEmbed generateEmbed(List<String> commandLabels)
{
EmbedBuilder embedBuilder = new EmbedBuilder();
@@ -54,38 +48,25 @@ public class BotInfo
// keep track of how many total commands we have
int commandsCount = 0;
// message commands info field
String messageCommandsInfo;
// message commands info fields
StringBuilder messageCommandsInfoBuilder = new StringBuilder();
if(Cache.getMessageCommandListener() == null)
messageCommandsInfo = "❌ disabled";
else
{
messageCommandsInfo = "✅ available";
messageCommandsInfoBuilder.append("❌ disabled");
else {
messageCommandsInfoBuilder.append("✅ available");
commandsCount += Cache.getMessageCommandListener().getRegisteredCommands().size();
}
embedBuilder.addField("Message commands", messageCommandsInfo, true);
embedBuilder.addField("Message commands", messageCommandsInfoBuilder.toString(), true);
// slash commands info field
String slashCommandsInfo;
// slash commands info fields
StringBuilder slashCommandsInfoBuilder = new StringBuilder();
if(Cache.getMessageCommandListener() == null)
slashCommandsInfo = "❌ disabled";
else
{
slashCommandsInfo = "✅ available";
slashCommandsInfoBuilder.append("❌ disabled");
else {
slashCommandsInfoBuilder.append("✅ available");
commandsCount += Cache.getSlashCommandListener().getRegisteredCommands().size();
}
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);
embedBuilder.addField("Slash commands", slashCommandsInfoBuilder.toString(), true);
// commands count fields
embedBuilder.addField("Total commands", "Loaded: `" + commandsCount + "`", true);
@@ -113,10 +94,9 @@ public class BotInfo
embedBuilder.addField("Uptime", FormatUtil.getNiceTimeDiff(Cache.getStartupTime()), true);
// issue tracker field
String link = "[Issue tracker](" + Cache.getRepositoryUrl() + "issues)";
embedBuilder.addField("Support",
link, true);
"[Issue tracker](https://git.beatrice.wtf/mind-overflow/HidekoBot/issues)",
true); //todo: we should probably make this a final field in the config class
// bot birthday field
embedBuilder.addField("Bot age",

View File

@@ -14,32 +14,23 @@ import java.util.List;
public class ClearChat
{
private ClearChat()
{
throw new IllegalStateException("Utility class");
}
public static String getLabel()
{
public static String getLabel() {
return "clear";
}
public static String getDescription()
{
public static String getDescription() {
return "Clear the current channel's chat.";
}
public static Permission getPermission()
{
public static Permission getPermission() {
return Permission.MESSAGE_MANAGE;
}
public static String checkDMs(Channel channel)
{
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;
}
@@ -47,9 +38,7 @@ public class ClearChat
public static String checkDeleteAmount(int toDeleteAmount)
{
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;
}
@@ -89,10 +78,11 @@ public class ClearChat
// set how many messages to delete for this iteration (usually <limit> unless there's a remainder)
int iterationSize = limit;
// if we are at the last iteration... check if we have <limit> or fewer messages to delete
if (iteration + 1 == iterations && remainder != 0)
// if we are at the last iteration...
if(iteration+1 == iterations)
{
iterationSize = remainder;
// check if we have <limit> or fewer messages to delete
if(remainder != 0) iterationSize = remainder;
}
if(iterationSize == 1)
@@ -104,8 +94,7 @@ public class ClearChat
else outOfBounds = true;
// increase deleted counter by 1
deleted++;
} else
{
} else {
// get the last <iterationSize - 1> messages.
MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1);
// note: first one is the most recent, last one is the oldest message.
@@ -118,26 +107,25 @@ public class ClearChat
if(messages.size() <= 1)
{
outOfBounds = true;
} else
{
} else {
// before deleting, we need to grab the <previous to the oldest> message's id for next iteration.
action = channel.getHistoryBefore(messages.getLast().getIdLong(), 1);
action = channel.getHistoryBefore(messages.get(messages.size() - 1).getIdLong(), 1);
List<Message> previousMessage = action.complete().getRetrievedHistory();
// if that message exists (we are not out of bounds)... store it
if (!previousMessage.isEmpty()) messageId = previousMessage.getFirst().getIdLong();
if(!previousMessage.isEmpty()) messageId = previousMessage.get(0).getIdLong();
else outOfBounds = true;
}
// queue messages for deletion
if(messages.size() == 1)
{
messages.getFirst().delete().queue();
} else if (!messages.isEmpty())
{
try
messages.get(0).delete().queue();
}
else if(!messages.isEmpty())
{
try {
((TextChannel) channel).deleteMessages(messages).complete();
/* alternatively, we could use purgeMessages, which is smarter...
however, it also tries to delete messages older than 2 weeks
@@ -173,16 +161,12 @@ public class ClearChat
} else if(deleted == 1)
{
return "✂ Cleared 1 message!";
} else
{
} else {
return "✂ Cleared " + deleted + " messages!";
}
}
// cap the amount to avoid abuse.
public static int getMaxAmount()
{
return 1000;
}
public static int getMaxAmount() { return 1000; }
}

View File

@@ -14,13 +14,8 @@ import java.util.List;
public class CoinFlip
{
private CoinFlip()
{
throw new IllegalStateException("Utility class");
}
public static Button getReflipButton()
{
public static Button getReflipButton() {
return Button.primary("coinflip_reflip", "Flip again")
.withEmoji(Emoji.fromUnicode("\uD83E\uDE99"));
}
@@ -33,8 +28,7 @@ public class CoinFlip
if(rand == 1)
{
msg = ":coin: It's **Heads**!";
} else
{
} else {
msg = "It's **Tails**! :coin:";
}
@@ -62,8 +56,7 @@ public class CoinFlip
{
// set the command as expiring and restrict it to the user who ran it
trackAndRestrict(message, event.getUser());
}, (error) -> {
});
}, (error) -> {});
}
public static void trackAndRestrict(Message replyMessage, User user)

View File

@@ -5,26 +5,18 @@ import net.dv8tion.jda.api.entities.User;
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;
public class DiceRoll
{
private DiceRoll()
{
throw new IllegalStateException("Utility class");
}
public static MessageResponse buildResponse(User author, String[] args)
{
LinkedHashMap<Dice, Integer> dicesToRoll = new LinkedHashMap<>();
String diceRegex = "d\\d+";
String amountRegex = "\\d+";
String diceRegex = "d[0-9]+";
String amountRegex = "[0-9]+";
Dice currentDice = null;
int currentAmount;
@@ -45,8 +37,7 @@ public class DiceRoll
if(currentDice == null)
{
currentDice = new Dice(6);
} else
{
} else {
currentDice = new Dice(currentDice);
}
@@ -58,7 +49,8 @@ public class DiceRoll
lastPushedDice = currentDice.getUUID();
dicesToRoll.put(currentDice, currentAmount);
totalRolls += currentAmount;
} else if (arg.matches(diceRegex))
}
else if(arg.matches(diceRegex))
{
int sides = Integer.parseInt(arg.substring(1));
@@ -114,12 +106,9 @@ public class DiceRoll
totalRolls = 1;
}
for (Map.Entry<Dice, Integer> entry : dicesToRoll.entrySet())
for(Dice dice : dicesToRoll.keySet())
{
Dice dice = entry.getKey();
Integer rollsToMake = entry.getValue();
for (int roll = 0; roll < rollsToMake; roll++)
for(int roll = 0; roll < dicesToRoll.get(dice); roll++)
{
dice.roll();
rolledDices.add(new Dice(dice));
@@ -132,23 +121,17 @@ public class DiceRoll
embedBuilder.setAuthor(author.getAsTag(), 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)
{
for (Dice dice : rolledDices) {
int diceSize = dice.getSides();
if (previousDiceSides != diceSize)
{
if (previousDiceSides != diceSize) {
message.append("\nd").append(diceSize).append(": ");
previousDiceSides = diceSize;
} else if (previousDiceSides != 0)
{
} else if (previousDiceSides != 0) {
message.append(", ");
}

View File

@@ -10,11 +10,6 @@ import wtf.beatrice.hidekobot.HidekoBot;
public class Invite
{
private Invite()
{
throw new IllegalStateException("Utility class");
}
public static MessageEmbed generateEmbed()
{
EmbedBuilder embedBuilder = new EmbedBuilder();

View File

@@ -10,12 +10,6 @@ import java.util.concurrent.TimeUnit;
public class LoveCalculator
{
private LoveCalculator()
{
throw new IllegalStateException("Utility class");
}
public static MessageEmbed buildEmbedAndCacheResult(User author, User user1, User user2)
{
String userId1 = user1.getId();

View File

@@ -14,17 +14,12 @@ import java.util.List;
public class MagicBall
{
private MagicBall()
{
throw new IllegalStateException("Utility class");
}
public static LinkedList<String> getLabels()
{
return new LinkedList<>(Arrays.asList("8ball", "8b", "eightball", "magicball"));
}
private static final List<String> answers = new ArrayList<>(
private final static List<String> answers = new ArrayList<>(
Arrays.asList("It is certain.",
"It is decidedly so.",
"Without a doubt.",

View File

@@ -8,12 +8,6 @@ import wtf.beatrice.hidekobot.objects.MessageResponse;
public class ProfileImage
{
private ProfileImage()
{
throw new IllegalStateException("Utility class");
}
public static int parseResolution(int resolution)
{
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
@@ -21,11 +15,9 @@ public class ProfileImage
// 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++)
{
for(int c = 1; c < acceptedSizes.length; c++){
int cdistance = Math.abs(acceptedSizes[c] - resolution);
if (cdistance < distance)
{
if(cdistance < distance){
idx = c;
distance = cdistance;
}
@@ -47,8 +39,7 @@ public class ProfileImage
{
resolutionString = resolution + " × " + resolution;
imageLink = user.getEffectiveAvatar().getUrl(resolution);
} else
{
} else {
int verticalRes = 361 * resolution / 1024;
resolutionString = resolution + " × " + verticalRes;
if(bannerProxy != null)
@@ -76,8 +67,7 @@ public class ProfileImage
if(imageType == ImageType.AVATAR)
{
currLink = user.getEffectiveAvatar().getUrl(currSize);
} else
{
} else {
if(bannerProxy == null) break;
currLink = bannerProxy.getUrl(currSize);
}
@@ -92,16 +82,15 @@ public class ProfileImage
embedBuilder.addField("Available resolutions", links.toString(), false);
if(imageLink != null)
embedBuilder.setImage(imageLink);
if (imageLink == null)
{
if(imageLink == null) {
String error = "I couldn't find " + user.getAsMention() + "'s " + imageTypeName + "!";
return new MessageResponse(error, null);
} else
{
} else {
return new MessageResponse(null, embedBuilder.build());
}
}

View File

@@ -5,13 +5,7 @@ import net.dv8tion.jda.api.Permission;
public class Say
{
private Say()
{
throw new IllegalStateException("Utility class");
}
public static Permission getPermission()
{
public static Permission getPermission() {
return Permission.MESSAGE_MANAGE;
}
}

View File

@@ -11,7 +11,6 @@ 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;
@@ -35,15 +34,8 @@ 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";
private final static String triviaLink = "https://opentdb.com/api.php?amount=10&type=multiple&category=";
private final static String categoriesLink = "https://opentdb.com/api_category.php";
public static List<String> channelsRunningTrivia = new ArrayList<>();
@@ -53,36 +45,23 @@ public class Trivia
// first string is the channelId, the list contain all score records for that channel
public static HashMap<String, LinkedList<TriviaScore>> channelAndScores = new HashMap<>();
public static String getTriviaLink(int categoryId)
{
return TRIVIA_API_LINK + categoryId;
}
public static String getTriviaLink(int categoryId) {return triviaLink + categoryId; }
public static String getCategoriesLink() {return categoriesLink; }
public static String getCategoriesLink()
{
return TRIVIA_API_CATEGORIES_LINK;
}
public static String getNoDMsError()
{
public static String getNoDMsError() {
return "\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.";
}
public static String getTriviaAlreadyRunningError()
{
public static String getTriviaAlreadyRunningError() {
// todo nicer looking
return "Trivia is already running here!";
}
public static MessageResponse generateMainScreen()
{
// todo null checks
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();
@@ -112,8 +91,7 @@ public class Trivia
public static JSONObject fetchJson(String link)
{
try
{
try {
URL url = new URL(link);
URLConnection connection = url.openConnection();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
@@ -126,9 +104,8 @@ public class Trivia
}
bufferedReader.close();
return new JSONObject(jsonStrBuilder.toString());
} catch (IOException e)
{
LOGGER.error("JSON Parsing Exception", e);
} catch (IOException e) {
e.printStackTrace();
}
return null;
@@ -210,16 +187,14 @@ public class Trivia
event.reply(user.getAsMention() + " got it right! \uD83E\uDD73 (**+3**)").queue();
currentUserScore.changeScore(3);
} else
{
} else {
event.reply("" + user.getAsMention() + ", that's not the right answer! (**-1**)").queue();
currentUserScore.changeScore(-1);
}
scores.add(currentUserScore);
channelAndScores.put(channelId, scores);
} else
{
} else {
event.reply("☹️ " + user.getAsMention() + ", you can't answer twice!")
.queue(interaction ->
Cache.getTaskScheduler().schedule(() ->
@@ -244,8 +219,7 @@ public class Trivia
responders.add(userId);
channelAndWhoResponded.put(channelId, responders);
return true; // response was successfully tracked
} else
{
} else {
return false; // response wasn't tracked because there already was an entry
}
}
@@ -286,8 +260,7 @@ public class Trivia
Message err = event.reply("Trivia is already running here!").complete().retrieveOriginal().complete();
Cache.getTaskScheduler().schedule(() -> err.delete().queue(), 10, TimeUnit.SECONDS);
return;
} else
{
} else {
// todo nicer looking
event.reply("Starting new Trivia session!").queue();
}
@@ -304,8 +277,7 @@ public class Trivia
Trivia.channelsRunningTrivia.add(channel.getId());
}
public enum AnswerType
{
public enum AnswerType {
CORRECT, WRONG
}

View File

@@ -25,19 +25,11 @@ 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"));
}
{ return new LinkedList<>(Arrays.asList("urban", "urbandictionary", "ud")); }
public static String getBaseUrl()
{
public static String getBaseUrl() {
return "https://www.urbandictionary.com/define.php?term=";
}
@@ -59,8 +51,7 @@ public class UrbanDictionary
.withEmoji(Emoji.fromFormatted("\uD83D\uDDD1"));
}
public static String getNoArgsError()
{
public static String getNoArgsError() {
return "\uD83D\uDE22 I need to know what to search for!";
}
@@ -123,8 +114,7 @@ public class UrbanDictionary
DatabaseSource database = Cache.getDatabaseSource();
// check if the user interacting is the same one who ran the command
if (!(database.isUserTrackedFor(event.getUser().getId(), messageId)))
{
if (!(database.isUserTrackedFor(event.getUser().getId(), messageId))) {
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
return;
}
@@ -162,16 +152,14 @@ public class UrbanDictionary
if(page > 0)
{
components.add(UrbanDictionary.getPreviousPageButton().asEnabled());
} else
{
} else {
components.add(UrbanDictionary.getPreviousPageButton().asDisabled());
}
if(page + 1 == search.getPages())
{
components.add(UrbanDictionary.getNextPageButton().asDisabled());
} else
{
} else {
components.add(UrbanDictionary.getNextPageButton().asEnabled());
}
@@ -275,10 +263,10 @@ public class UrbanDictionary
htmlContributor.indexOf("</a>") + 4);
contributorsNames.add(htmlContributorName
.replaceAll("<.*?>", "")); // remove all html tags
.replaceAll("<.*?>", "")); // remove all html tags;
submissionDates.add(htmlSubmitDate
.replaceAll("<.*?>", "")); // remove all html tags
.replaceAll("<.*?>", "")); // remove all html tags;
}
}
@@ -290,48 +278,39 @@ public class UrbanDictionary
pages = submissionDates.size();
}
public List<String> getPlaintextMeanings()
{
public List<String> getPlaintextMeanings() {
return this.plaintextMeanings;
}
public List<String> getPlaintextExamples()
{
public List<String> getPlaintextExamples() {
return this.plaintextExamples;
}
public List<String> getContributorsNames()
{
public List<String> getContributorsNames() {
return this.contributorsNames;
}
public List<String> getSubmissionDates()
{
public List<String> getSubmissionDates() {
return this.submissionDates;
}
public String getSerializedMeanings()
{
public String getSerializedMeanings() {
return serializedMeanings;
}
public String getSerializedExamples()
{
public String getSerializedExamples() {
return serializedExamples;
}
public String getSerializedContributors()
{
public String getSerializedContributors() {
return serializedContributors;
}
public String getSerializedDates()
{
public String getSerializedDates() {
return serializedDates;
}
public int getPages()
{
public int getPages() {
return pages;
}
}

View File

@@ -20,6 +20,7 @@ import wtf.beatrice.hidekobot.util.FormatUtil;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -27,13 +28,8 @@ import java.util.concurrent.TimeUnit;
public class UserPunishment
{
private UserPunishment()
{
throw new IllegalStateException("Utility class");
}
private static final Duration maxTimeoutDuration = Duration.of(28, ChronoUnit.DAYS);
private static final Duration minTimeoutDuration = Duration.of(30, ChronoUnit.SECONDS);
private final static Duration maxTimeoutDuration = Duration.of(28, ChronoUnit.DAYS);
private final static Duration minTimeoutDuration = Duration.of(30, ChronoUnit.SECONDS);
public static void handle(SlashCommandInteractionEvent event, PunishmentType punishmentType)
{
@@ -133,8 +129,7 @@ public class UserPunishment
String mentionedId = mentions.get(0).getId();
User mentioned = null;
try
{
try {
mentioned = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
} catch (RuntimeException ignored)
{
@@ -172,16 +167,12 @@ public class UserPunishment
Duration duration = null;
AuditableRestAction<Void> punishmentAction = null;
boolean impossible = false;
try
{
switch (punishmentType)
{
try {
switch (punishmentType) {
case BAN -> punishmentAction = guild.ban(mentioned, 0, TimeUnit.SECONDS);
case KICK -> punishmentAction = guild.kick(mentioned);
case TIMEOUT ->
{
case TIMEOUT -> {
if(args != null)
{
String durationStr = args[1];
@@ -206,16 +197,7 @@ public class UserPunishment
punishmentAction = guild.timeoutFor(mentioned, duration);
}
}
} catch (RuntimeException ignored)
{
impossible = true;
}
if (punishmentAction == null)
impossible = true;
if (impossible)
{
} catch (RuntimeException ignored) {
// todo nicer looking with emojis
return new MessageResponse("Sorry, I couldn't " + punishmentTypeName + " " + mentioned.getAsMention() + "!",
null);
@@ -224,8 +206,7 @@ public class UserPunishment
if(!reason.isEmpty() && !reasonBuilder.isEmpty())
punishmentAction.reason("[" + author.getAsTag() + "] " + reason);
try
{
try {
punishmentAction.complete();
} catch (RuntimeException ignored)
{
@@ -255,8 +236,7 @@ public class UserPunishment
}
public enum PunishmentType
{
public enum PunishmentType {
KICK("kicked"),
BAN("banned"),
TIMEOUT("timed out"),

View File

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

View File

@@ -17,42 +17,36 @@ public class AliasCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Arrays.asList("alias", "aliases"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; // anyone can use it
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.TOOLS;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "See other command aliases.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "<command>";
}

View File

@@ -6,6 +6,7 @@ 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 wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.commands.base.ProfileImage;
import wtf.beatrice.hidekobot.objects.MessageResponse;
@@ -20,48 +21,44 @@ public class AvatarCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("avatar"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; // anyone can use it
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Get someone's avatar, or your own. You can additionally specify a resolution.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "[mentioned user] [resolution]";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.TOOLS;
}
@Override
public void runCommand(MessageReceivedEvent event, String label, String[] args)
{
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
User user;
int resolution = -1;
@@ -69,17 +66,13 @@ public class AvatarCommand implements MessageCommand
// (mentions are handled differently by a specific method)
boolean resFound = false;
for (String arg : args)
{
try
{
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
} catch (NumberFormatException ignored) {
}
}

View File

@@ -1,10 +1,19 @@
package wtf.beatrice.hidekobot.commands.message;
import net.dv8tion.jda.api.EmbedBuilder;
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.User;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -12,47 +21,42 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class BanCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("ban"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return new ArrayList<Permission>(Collections.singletonList(Permission.BAN_MEMBERS));
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.MODERATION;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Ban the mentioned user.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "<mentioned user> [reason]";
}

View File

@@ -20,42 +20,36 @@ public class BannerCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("banner"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; // anyone can use it
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Get someone's profile banner, or your own.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "[mentioned user] [resolution]";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.TOOLS;
}
@@ -69,17 +63,13 @@ public class BannerCommand implements MessageCommand
// (mentions are handled differently by a specific method)
boolean resFound = false;
for (String arg : args)
{
try
{
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
} catch (NumberFormatException ignored) {
}
}

View File

@@ -11,6 +11,7 @@ import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -18,54 +19,46 @@ public class BotInfoCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Arrays.asList("botinfo", "info"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; // anyone can use it
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Get general info about the bot.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return null;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.TOOLS;
}
@Override
public void runCommand(MessageReceivedEvent event, String label, String[] args)
{
public void runCommand(MessageReceivedEvent event, String label, String[] args) {
// get a list of message commands
LinkedList<MessageCommand> messageCommands = Cache.getMessageCommandListener().getRegisteredCommands();
LinkedList<String> commandNames = new LinkedList<>();
for (MessageCommand command : messageCommands)
{
for (MessageCommand command : messageCommands) {
commandNames.add(command.getCommandLabels().get(0));
}

View File

@@ -19,51 +19,44 @@ public class ClearCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList(ClearChat.getLabel()));
}
@Override
public List<Permission> getPermissions()
{
return Collections.singletonList(ClearChat.getPermission());
}
public List<Permission> getPermissions() { return Collections.singletonList(ClearChat.getPermission()); }
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.MODERATION;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Clear the current channel's chat history.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "[amount]";
}
@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)
{
if (error != null) {
event.getMessage().reply(error).queue();
return;
}
@@ -73,8 +66,7 @@ public class ClearCommand implements MessageCommand
if (args.length == 0) toDeleteAmount = 1;
else
{
try
{
try {
toDeleteAmount = Integer.parseInt(args[0]);
} catch (NumberFormatException e)
{
@@ -86,8 +78,7 @@ public class ClearCommand implements MessageCommand
if(toDeleteAmount > ClearChat.getMaxAmount()) toDeleteAmount = 0;
error = ClearChat.checkDeleteAmount(toDeleteAmount);
if (error != null)
{
if (error != null) {
event.getMessage().reply(error).queue();
return;
}

View File

@@ -16,48 +16,41 @@ public class CoinFlipCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Arrays.asList("coinflip", "flip", "flipcoin"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; // null because it can be used anywhere
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Flip a coin.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return null;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.FUN;
}
@Override
public void runCommand(MessageReceivedEvent event, String label, String[] args)
{
public void runCommand(MessageReceivedEvent event, String label, String[] args) {
// perform coin flip
event.getMessage().reply(CoinFlip.genRandom())
@@ -66,7 +59,6 @@ public class CoinFlipCommand implements MessageCommand
{
// set the command as expiring and restrict it to the user who ran it
CoinFlip.trackAndRestrict(message, event.getAuthor());
}, (error) -> {
});
}, (error) -> {});
}
}

View File

@@ -16,49 +16,41 @@ import java.util.List;
public class DiceRollCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Arrays.asList("diceroll", "droll", "roll"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; // anyone can use it
}
@Override
public boolean passRawArgs()
{
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.
""";
public String getDescription() {
return "Roll dice. You can roll multiple dice at the same time." +
"\nExamples:" +
"\n - `d8 10` to roll an 8-sided die 10 times." +
"\n - `d12 3 d5 10` to roll a 12-sided die 3 times, and then a 5-sided die 10 times." +
"\n - `30` to roll a standard 6-sided die 30 times." +
"\n - `d10` to roll a 10-sided die once.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "[dice size] [rolls]";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.FUN;
}

View File

@@ -15,41 +15,33 @@ public class HelloCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Arrays.asList("hi", "hello", "heya"));
}
@Override
public List<Permission> getPermissions()
{
return null;
}
public List<Permission> getPermissions() { return null; }
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Get pinged by the bot.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return null;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.FUN;
}

View File

@@ -11,49 +11,44 @@ import wtf.beatrice.hidekobot.commands.base.Alias;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import java.util.*;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
public class HelpCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("help"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
return null;
}
public List<Permission> getPermissions() { return null; }
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Get general help on the bot. Specify a command if you want specific help about that command.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "[command]";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.TOOLS;
}
@@ -87,11 +82,10 @@ public class HelpCommand implements MessageCommand
"\nYou will find a list of commands organized in categories below.",
false);
for (Map.Entry<CommandCategory, LinkedList<MessageCommand>> entry : commandCategories.entrySet())
for(CommandCategory category : commandCategories.keySet())
{
StringBuilder commandsList = new StringBuilder();
CommandCategory category = entry.getKey();
LinkedList<MessageCommand> commandsOfThisCategory = entry.getValue();
LinkedList<MessageCommand> commandsOfThisCategory = commandCategories.get(category);
for(int pos = 0; pos < commandsOfThisCategory.size(); pos++)
{
@@ -110,8 +104,7 @@ public class HelpCommand implements MessageCommand
}
event.getMessage().replyEmbeds(embedBuilder.build()).queue();
} else
{
} else {
String commandLabel = args[0].toLowerCase();
MessageCommand command = Cache.getMessageCommandListener().getRegisteredCommand(commandLabel);
@@ -134,8 +127,7 @@ public class HelpCommand implements MessageCommand
if(permissions == null)
{
permissionsStringBuilder = new StringBuilder("Available to everyone");
} else
{
} else {
for(int i = 0; i < permissions.size(); i++)
{
Permission permission = permissions.get(i);

View File

@@ -19,42 +19,36 @@ public class InviteCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("invite"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null;
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Get the bot's invite link.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return null;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.MODERATION;
}
@@ -76,8 +70,7 @@ public class InviteCommand implements MessageCommand
.queue();
event.getMessage().addReaction(Emoji.fromUnicode("")).queue();
}, error -> event.getMessage().addReaction(Emoji.fromUnicode("")).queue());
} else
{
} else {
event.getMessage()
.replyEmbeds(inviteEmbed)
.addActionRow(inviteButton)

View File

@@ -1,10 +1,19 @@
package wtf.beatrice.hidekobot.commands.message;
import net.dv8tion.jda.api.EmbedBuilder;
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.User;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
@@ -17,42 +26,36 @@ public class KickCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("kick"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return new ArrayList<Permission>(Collections.singletonList(Permission.KICK_MEMBERS));
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.MODERATION;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Kick the mentioned user from the guild.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "<mentioned user> [reason]";
}

View File

@@ -29,35 +29,30 @@ public class LoveCalculatorCommand implements MessageCommand
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; //anyone can use it
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
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()
{
public String getUsage() {
return "<person 1> [person 2]";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.FUN;
}
@@ -85,8 +80,7 @@ public class LoveCalculatorCommand implements MessageCommand
if(mentions.size() == 1)
{
user2 = event.getAuthor();
} else
{
} else {
mentionedUserId = mentions.get(1).getId();
user2 = HidekoBot.getAPI().retrieveUserById(mentionedUserId).complete();
}

View File

@@ -15,42 +15,36 @@ public class MagicBallCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return MagicBall.getLabels();
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; // anyone can use it
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Ask a question to the Magic Ball.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "<question>";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.FUN;
}

View File

@@ -1,7 +1,6 @@
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;
@@ -18,42 +17,34 @@ public class SayCommand implements MessageCommand
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("say"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
return Collections.singletonList(Say.getPermission());
}
public List<Permission> getPermissions() { return Collections.singletonList(Say.getPermission()); }
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return true;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Make the bot say something for you.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "<text>";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.TOOLS;
}
@@ -65,23 +56,14 @@ public class SayCommand implements MessageCommand
if(args.length != 0 && !args[0].isEmpty())
{
messageContent = args[0];
} else
{
} else {
event.getMessage().reply("\uD83D\uDE20 Hey, you have to tell me what to say!")
.queue();
return;
}
event.getChannel().sendMessage(messageContent).queue();
event.getMessage().delete().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
});
}
}
}

View File

@@ -17,42 +17,36 @@ public class TimeoutCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("timeout"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return new ArrayList<Permission>(Collections.singletonList(Permission.MODERATE_MEMBERS));
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.MODERATION;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Timeout the mentioned user.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "<mentioned user> <duration> [reason]";
}

View File

@@ -5,7 +5,6 @@ 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;
@@ -23,42 +22,36 @@ public class TriviaCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return new LinkedList<>(Collections.singletonList("trivia"));
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null;
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.FUN;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Start a Trivia session and play with others!";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return null;
}
@@ -84,20 +77,11 @@ public class TriviaCommand implements MessageCommand
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)
event.getMessage().replyEmbeds(response.embed()).addActionRow(response.components()).queue(message ->
{
if (response.components() != null) responseAction = responseAction.addActionRow(response.components());
responseAction.queue(message -> {
Cache.getDatabaseSource().trackRanCommandReply(message, event.getAuthor());
Cache.getDatabaseSource().queueDisabling(message);
});
}
}

View File

@@ -21,42 +21,36 @@ public class UrbanDictionaryCommand implements MessageCommand
{
@Override
public LinkedList<String> getCommandLabels()
{
public LinkedList<String> getCommandLabels() {
return UrbanDictionary.getCommandLabels();
}
@Nullable
@Override
public List<Permission> getPermissions()
{
public List<Permission> getPermissions() {
return null; //anyone can use it
}
@Override
public boolean passRawArgs()
{
public boolean passRawArgs() {
return false;
}
@NotNull
@Override
public String getDescription()
{
public String getDescription() {
return "Look something up in the Urban Dictionary.";
}
@Nullable
@Override
public String getUsage()
{
public String getUsage() {
return "<query>";
}
@NotNull
@Override
public CommandCategory getCategory()
{
public CommandCategory getCategory() {
return CommandCategory.FUN;
}
@@ -72,8 +66,7 @@ public class UrbanDictionaryCommand implements MessageCommand
// 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++)
{
for (int i = 0; i < args.length; i++) {
String arg = args[i];
termBuilder.append(arg);
@@ -86,11 +79,9 @@ public class UrbanDictionaryCommand implements MessageCommand
Document doc;
try
{
try {
doc = Jsoup.connect(url).get();
} catch (IOException e)
{
} catch (IOException e) {
event.getMessage().reply(UrbanDictionary.getTermNotFoundError()).queue();
return;
}

View File

@@ -14,8 +14,7 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class AvatarCommand extends SlashCommandImpl
{
@Override
public CommandData getSlashCommandData()
{
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.",
@@ -36,8 +35,7 @@ public class AvatarCommand extends SlashCommandImpl
if(userArg != null)
{
user = userArg.getAsUser();
} else
{
} else {
user = event.getUser();
}
@@ -45,8 +43,7 @@ public class AvatarCommand extends SlashCommandImpl
if(sizeArg != null)
{
resolution = ProfileImage.parseResolution(sizeArg.getAsInt());
} else
{
} else {
resolution = ProfileImage.parseResolution(512);
}

View File

@@ -14,8 +14,7 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class BannerCommand extends SlashCommandImpl
{
@Override
public CommandData getSlashCommandData()
{
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.",
@@ -36,8 +35,7 @@ public class BannerCommand extends SlashCommandImpl
if(userArg != null)
{
user = userArg.getAsUser();
} else
{
} else {
user = event.getUser();
}
@@ -45,8 +43,7 @@ public class BannerCommand extends SlashCommandImpl
if(sizeArg != null)
{
resolution = ProfileImage.parseResolution(sizeArg.getAsInt());
} else
{
} else {
resolution = ProfileImage.parseResolution(512);
}

View File

@@ -16,8 +16,7 @@ import java.util.List;
public class BotInfoCommand extends SlashCommandImpl
{
@Override
public CommandData getSlashCommandData()
{
public CommandData getSlashCommandData() {
return Commands.slash("botinfo", "Get info about the bot.");
}

View File

@@ -17,8 +17,7 @@ public class ClearCommand extends SlashCommandImpl
{
@Override
public CommandData getSlashCommandData()
{
public CommandData getSlashCommandData() {
return Commands.slash(ClearChat.getLabel(),
ClearChat.getDescription())
.addOption(OptionType.INTEGER, "amount", "The amount of messages to delete.")

View File

@@ -29,11 +29,12 @@ public class CoinFlipCommand extends SlashCommandImpl
interaction.retrieveOriginal().queue((message) ->
{
CoinFlip.trackAndRestrict(message, event.getUser());
}, (error) -> {
});
}, (error) -> {
});
}, (error) -> {});
}, (error) -> {});
}
}

View File

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

View File

@@ -1,15 +1,25 @@
package wtf.beatrice.hidekobot.commands.slash;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.IMentionable;
import net.dv8tion.jda.api.entities.User;
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.DefaultMemberPermissions;
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.commands.base.Say;
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
import wtf.beatrice.hidekobot.objects.MessageResponse;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class KickCommand extends SlashCommandImpl
{
@Override

View File

@@ -40,8 +40,7 @@ public class LoveCalculatorCommand extends SlashCommandImpl
if(firsUserArg != null)
{
firstUser = firsUserArg.getAsUser(); //todo null check?
} else
{
} else {
event.reply("\uD83D\uDE22 I need to know who to check! Please mention them.")
.setEphemeral(true)
.queue();
@@ -52,8 +51,7 @@ public class LoveCalculatorCommand extends SlashCommandImpl
if(secondUserArg != null)
{
secondUser = secondUserArg.getAsUser(); //todo null check?
} else
{
} else {
secondUser = event.getUser();
}

View File

@@ -54,11 +54,9 @@ public class UrbanDictionaryCommand extends SlashCommandImpl
Document doc;
try
{
try {
doc = Jsoup.connect(url).get();
} catch (IOException e)
{
} catch (IOException e) {
event.reply(UrbanDictionary.getTermNotFoundError())
.setEphemeral(true)
.queue();

View File

@@ -7,27 +7,18 @@ public enum ConfigurationEntry
BOT_OWNER_ID("bot-owner-id", 100000000000000000L),
BOT_COLOR("bot-color", "PINK"),
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 Object defaultValue;
ConfigurationEntry(String path, Object defaultValue)
{
this.path = path;
this.defaultValue = defaultValue;
}
public String getPath()
{
return path;
}
public Object getDefaultValue()
{
return defaultValue;
}
public String getPath() { return path; }
public Object getDefaultValue() { return defaultValue; }
}

View File

@@ -1,26 +1,26 @@
package wtf.beatrice.hidekobot.datasources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.util.Logger;
import java.io.*;
import java.util.LinkedHashMap;
import java.util.Map;
public class ConfigurationSource
{
private final LinkedHashMap<String, Object> configurationEntries = new LinkedHashMap<>();
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationSource.class);
private final Logger logger;
private final String configFilePath;
public ConfigurationSource(String configFilePath)
{
this.configFilePath = configFilePath;
logger = new Logger(getClass());
}
public void initConfig()
@@ -39,7 +39,7 @@ public class ConfigurationSource
if(internalConfigContents.isEmpty())
{
LOGGER.error("Error reading internal configuration!");
logger.log("Error reading internal configuration!");
HidekoBot.shutdown();
return;
}
@@ -49,32 +49,20 @@ public class ConfigurationSource
if(!fsConfigFile.exists())
{
// try to create config file
try
{
if (!fsConfigFile.createNewFile())
{
LOGGER.error("We tried creating a file that already exists!");
HidekoBot.shutdown();
return;
}
} catch (IOException e)
{
LOGGER.error("Error creating configuration file!", e);
try { fsConfigFile.createNewFile(); }
catch (IOException e) {
logger.log("Error creating configuration file!");
logger.log(e.getMessage());
HidekoBot.shutdown();
return;
}
}
// load the YAML file from the filesystem
LoaderOptions options = new LoaderOptions();
Yaml fsConfigYaml = new Yaml(new SafeConstructor(options));
Yaml fsConfigYaml = new Yaml(new SafeConstructor());
LinkedHashMap<String, Object> fsConfigContents = null; // map holding all file entries
try (InputStream fsConfigStream = new FileInputStream(fsConfigFile))
{
fsConfigContents = fsConfigYaml.load(fsConfigStream);
} catch (IOException e)
{
LOGGER.error(e.getMessage());
}
{ fsConfigContents = fsConfigYaml.load(fsConfigStream); }
catch (IOException e) { logger.log(e.getMessage()); }
if(fsConfigContents == null) // if file contents are empty or corrupted...
@@ -102,24 +90,19 @@ public class ConfigurationSource
// create a new mixed map that will take existing values from the non-missing keys
// and fill everything else with the default values
LinkedHashMap<String, Object> filledEntries = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : internalConfigContents.entrySet())
for(String key : internalConfigContents.keySet())
{
String key = entry.getKey();
if(fsConfigContents.containsKey(key))
{
// if the key already exists, copy the original value
filledEntries.put(key, fsConfigContents.get(key));
} else
{
} else {
// else, copy the value from the example config file
filledEntries.put(key, entry.getValue());
filledEntries.put(key, internalConfigContents.get(key));
}
}
try
{
try {
// new writer to actually write the contents to the file
PrintWriter missingKeysWriter = new PrintWriter(fsConfigFile);
@@ -132,17 +115,15 @@ public class ConfigurationSource
// create the yaml object and dump the values to filesystem
Yaml yaml = new Yaml(dumperOptions);
yaml.dump(filledEntries, missingKeysWriter);
} catch (FileNotFoundException e)
{
LOGGER.error(e.getMessage());
} catch (FileNotFoundException e) {
logger.log(e.getMessage());
HidekoBot.shutdown();
return;
}
// finally, dump all entries to cache.
loadConfig(filledEntries);
} else
{
} else {
// if no key is missing, just cache all entries and values from filesystem.
loadConfig(fsConfigContents);
}
@@ -153,7 +134,6 @@ public class ConfigurationSource
{
this.configurationEntries.putAll(configurationEntries);
}
public Object getConfigValue(ConfigurationEntry key)
{
return configurationEntries.get(key.getPath());

View File

@@ -3,8 +3,8 @@ package wtf.beatrice.hidekobot.datasources;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.util.Logger;
import java.sql.*;
import java.time.LocalDateTime;
@@ -15,35 +15,29 @@ import java.util.List;
public class DatabaseSource
{
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DatabaseSource.class);
private static final String JDBC_URL = "jdbc:sqlite:%path%";
private 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;
}
private void logException(SQLException e)
{
LOGGER.error("Database Exception", e);
this.logger = new Logger(getClass());
}
public boolean connect()
{
String url = JDBC_URL.replace("%path%", dbPath);
String url = sqliteURL.replace("%path%", dbPath);
if(!close()) return false;
try
{
try {
dbConnection = DriverManager.getConnection(url);
LOGGER.info("Database connection established!");
logger.log("Database connection established!");
return true;
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
return false;
}
@@ -54,15 +48,13 @@ public class DatabaseSource
{
if (dbConnection != null)
{
try
{
try {
if(!dbConnection.isClosed())
{
dbConnection.close();
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
return false;
}
@@ -98,48 +90,43 @@ public class DatabaseSource
*/
//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 pending_disabled_messages (" +
"guild_id TEXT NOT NULL, " +
"channel_id TEXT NOT NULL," +
"message_id TEXT NOT NULL," +
"expiry_timestamp TEXT NOT NULL " +
");");
newTables.add("""
CREATE TABLE IF NOT EXISTS command_runners (
guild_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message_id TEXT NOT NULL,
user_id TEXT NOT NULL,
channel_type TEXT NOT NULL);
""");
newTables.add("CREATE TABLE IF NOT EXISTS 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, ...)
");");
newTables.add("""
CREATE TABLE IF NOT EXISTS urban_dictionary (
message_id TEXT NOT NULL,
page INTEGER NOT NULL,
meanings TEXT NOT NULL,
examples TEXT NOT NULL,
contributors TEXT NOT NULL,
dates TEXT NOT NULL,
term TEXT NOT NULL
);
""");
newTables.add("CREATE TABLE IF NOT EXISTS urban_dictionary (" +
"message_id TEXT NOT NULL, " + // message id of the bot's response
"page INTEGER NOT NULL," + // page that we are currently on
"meanings TEXT NOT NULL," + // list of all meanings, serialized and encoded
"examples TEXT NOT NULL, " + // list of all examples, serialized and encoded
"contributors TEXT NOT NULL, " + // list of all contributors, serialized and encoded
"dates TEXT NOT NULL, " + // list of all submission dates, serialized and encoded
"term TEXT NOT NULL" + // the term that was searched
");");
for(String sql : newTables)
{
try (Statement stmt = dbConnection.createStatement())
{
try (Statement stmt = dbConnection.createStatement()) {
// execute the statement
stmt.execute(sql);
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
@@ -156,8 +143,7 @@ public class DatabaseSource
if(!(channelType.isGuild()))
{
guildId = userId;
} else
{
} else {
guildId = message.getGuild().getId();
}
@@ -165,11 +151,9 @@ public class DatabaseSource
String messageId = message.getId();
String query = """
INSERT INTO command_runners
(guild_id, channel_id, message_id, user_id, channel_type) VALUES
(?, ?, ?, ?, ?);
""";
String query = "INSERT INTO command_runners " +
"(guild_id, channel_id, message_id, user_id, channel_type) VALUES " +
" (?, ?, ?, ?, ?);";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -184,7 +168,7 @@ public class DatabaseSource
return true;
} catch (SQLException e)
{
logException(e);
e.printStackTrace();
}
return false;
@@ -199,11 +183,9 @@ public class DatabaseSource
public ChannelType getTrackedMessageChannelType(String messageId)
{
String query = """
SELECT channel_type
FROM command_runners
WHERE message_id = ?;
""";
String query = "SELECT channel_type " +
"FROM command_runners " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -216,9 +198,8 @@ public class DatabaseSource
return ChannelType.valueOf(channelTypeName);
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -227,11 +208,9 @@ public class DatabaseSource
public String getTrackedReplyUserId(String messageId)
{
String query = """
SELECT user_id
FROM command_runners
WHERE message_id = ?;
""";
String query = "SELECT user_id " +
"FROM command_runners " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -243,9 +222,8 @@ public class DatabaseSource
return resultSet.getString("user_id");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -261,8 +239,7 @@ public class DatabaseSource
if(!(channelType.isGuild()))
{
guildId = "PRIVATE";
} else
{
} else {
guildId = message.getGuild().getId();
}
@@ -271,11 +248,9 @@ public class DatabaseSource
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
(?, ?, ?, ?);
""";
String query = "INSERT INTO pending_disabled_messages " +
"(guild_id, channel_id, message_id, expiry_timestamp) VALUES " +
" (?, ?, ?, ?);";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -289,7 +264,7 @@ public class DatabaseSource
return true;
} catch (SQLException e)
{
logException(e);
e.printStackTrace();
}
return false;
@@ -299,10 +274,8 @@ public class DatabaseSource
{
List<String> messages = new ArrayList<>();
String query = """
SELECT message_id
FROM pending_disabled_messages;
""";
String query = "SELECT message_id " +
"FROM pending_disabled_messages ";
try (Statement statement = dbConnection.createStatement())
{
@@ -312,9 +285,8 @@ public class DatabaseSource
{
messages.add(resultSet.getString("message_id"));
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return messages;
@@ -330,7 +302,7 @@ public class DatabaseSource
preparedStatement.execute();
} catch (SQLException e)
{
logException(e);
e.printStackTrace();
return false;
}
@@ -341,7 +313,7 @@ public class DatabaseSource
preparedStatement.execute();
} catch (SQLException e)
{
logException(e);
e.printStackTrace();
return false;
}
@@ -352,7 +324,7 @@ public class DatabaseSource
preparedStatement.execute();
} catch (SQLException e)
{
logException(e);
e.printStackTrace();
return false;
}
@@ -361,11 +333,9 @@ public class DatabaseSource
public String getQueuedExpiringMessageExpiryDate(String messageId)
{
String query = """
SELECT expiry_timestamp
FROM pending_disabled_messages
WHERE message_id = ?;
""";
String query = "SELECT expiry_timestamp " +
"FROM pending_disabled_messages " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -377,9 +347,8 @@ public class DatabaseSource
return resultSet.getString("expiry_timestamp");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -387,11 +356,9 @@ public class DatabaseSource
public String getQueuedExpiringMessageChannel(String messageId)
{
String query = """
SELECT channel_id
FROM pending_disabled_messages
WHERE message_id = ?;
""";
String query = "SELECT channel_id " +
"FROM pending_disabled_messages " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -403,9 +370,8 @@ public class DatabaseSource
return resultSet.getString("channel_id");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -413,11 +379,9 @@ public class DatabaseSource
public String getQueuedExpiringMessageGuild(String messageId)
{
String query = """
SELECT guild_id
FROM pending_disabled_messages
WHERE message_id = ?;
""";
String query = "SELECT guild_id " +
"FROM pending_disabled_messages " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -429,9 +393,8 @@ public class DatabaseSource
return resultSet.getString("guild_id");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -442,11 +405,9 @@ public class DatabaseSource
Message message, String term)
{
String query = """
INSERT INTO urban_dictionary
(message_id, page, meanings, examples, contributors, dates, term) VALUES
(?, ?, ?, ?, ?, ?, ?);
""";
String query = "INSERT INTO urban_dictionary " +
"(message_id, page, meanings, examples, contributors, dates, term) VALUES " +
" (?, ?, ?, ?, ?, ?, ?);";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -463,7 +424,7 @@ public class DatabaseSource
return true;
} catch (SQLException e)
{
logException(e);
e.printStackTrace();
}
return false;
@@ -471,11 +432,9 @@ public class DatabaseSource
public int getUrbanPage(String messageId)
{
String query = """
SELECT page
FROM urban_dictionary
WHERE message_id = ?;
""";
String query = "SELECT page " +
"FROM urban_dictionary " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -487,9 +446,8 @@ public class DatabaseSource
return resultSet.getInt("page");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
@@ -497,11 +455,9 @@ public class DatabaseSource
public String getUrbanMeanings(String messageId)
{
String query = """
SELECT meanings
FROM urban_dictionary
WHERE message_id = ?;
""";
String query = "SELECT meanings " +
"FROM urban_dictionary " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -513,9 +469,8 @@ public class DatabaseSource
return resultSet.getString("meanings");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -523,11 +478,9 @@ public class DatabaseSource
public String getUrbanExamples(String messageId)
{
String query = """
SELECT examples
FROM urban_dictionary
WHERE message_id = ?;
""";
String query = "SELECT examples " +
"FROM urban_dictionary " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -539,9 +492,8 @@ public class DatabaseSource
return resultSet.getString("examples");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -549,11 +501,9 @@ public class DatabaseSource
public String getUrbanContributors(String messageId)
{
String query = """
SELECT contributors
FROM urban_dictionary
WHERE message_id = ?;
""";
String query = "SELECT contributors " +
"FROM urban_dictionary " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -565,9 +515,8 @@ public class DatabaseSource
return resultSet.getString("contributors");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -575,11 +524,9 @@ public class DatabaseSource
public String getUrbanDates(String messageId)
{
String query = """
SELECT dates
FROM urban_dictionary
WHERE message_id = ?;
""";
String query = "SELECT dates " +
"FROM urban_dictionary " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -591,9 +538,8 @@ public class DatabaseSource
return resultSet.getString("dates");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -601,11 +547,9 @@ public class DatabaseSource
public String getUrbanTerm(String messageId)
{
String query = """
SELECT term
FROM urban_dictionary
WHERE message_id = ?;
""";
String query = "SELECT term " +
"FROM urban_dictionary " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -617,9 +561,8 @@ public class DatabaseSource
return resultSet.getString("term");
}
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
@@ -627,11 +570,9 @@ public class DatabaseSource
public boolean setUrbanPage(String messageId, int page)
{
String query = """
UPDATE urban_dictionary
SET page = ?
WHERE message_id = ?;
""";
String query = "UPDATE urban_dictionary " +
"SET page = ? " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -641,9 +582,8 @@ public class DatabaseSource
return true;
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return false;
@@ -656,11 +596,9 @@ public class DatabaseSource
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Cache.getExpiryTimestampFormat());
String expiryTimeFormatted = dateTimeFormatter.format(expiryTime);
String query = """
UPDATE pending_disabled_messages
SET expiry_timestamp = ?
WHERE message_id = ?;
""";
String query = "UPDATE pending_disabled_messages " +
"SET expiry_timestamp = ? " +
"WHERE message_id = ?;";
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
{
@@ -670,9 +608,8 @@ public class DatabaseSource
return true;
} catch (SQLException e)
{
logException(e);
} catch (SQLException e) {
e.printStackTrace();
}
return false;

View File

@@ -1,8 +1,7 @@
package wtf.beatrice.hidekobot.datasources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.util.Logger;
import java.io.IOException;
import java.io.InputStream;
@@ -13,7 +12,7 @@ public class PropertiesSource
private Properties properties = null;
private final String fileName = "default.properties";
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesSource.class);
private final Logger logger = new Logger(getClass());
public void load()
{
@@ -25,16 +24,14 @@ public class PropertiesSource
{
properties.load(internalPropertiesStream);
} catch (IOException e)
{
LOGGER.error(e.getMessage());
}
catch (IOException e) {
logger.log(e.getMessage());
HidekoBot.shutdown();
return;
}
}
public String getProperty(String property)
{
return properties == null ? "" : properties.getProperty(property);
}
{ return properties == null ? "" : properties.getProperty(property); }
}

View File

@@ -2,8 +2,6 @@ package wtf.beatrice.hidekobot.listeners;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.commands.base.CoinFlip;
import wtf.beatrice.hidekobot.commands.base.Trivia;
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
@@ -12,14 +10,11 @@ import wtf.beatrice.hidekobot.util.CommandUtil;
public class ButtonInteractionListener extends ListenerAdapter
{
private static final Logger LOGGER = LoggerFactory.getLogger(ButtonInteractionListener.class);
@Override
public void onButtonInteraction(ButtonInteractionEvent event)
{
switch (event.getComponentId().toLowerCase())
{
switch (event.getComponentId().toLowerCase()) {
// coinflip
case "coinflip_reflip" -> CoinFlip.buttonReFlip(event);
@@ -36,10 +31,6 @@ public class ButtonInteractionListener extends ListenerAdapter
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());
}
}

View File

@@ -11,6 +11,7 @@ import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import wtf.beatrice.hidekobot.objects.comparators.MessageCommandAliasesComparator;
import wtf.beatrice.hidekobot.util.Logger;
import java.util.*;
@@ -19,13 +20,13 @@ public class MessageCommandListener extends ListenerAdapter
// map storing command labels and command object alphabetically.
private final TreeMap<LinkedList<String>, MessageCommand> registeredCommands =
new TreeMap<>(new MessageCommandAliasesComparator());
new TreeMap<LinkedList<String>, MessageCommand>(new MessageCommandAliasesComparator());
// 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";
private final String commandRegex = "(?i)^(hideko|hde)\\b";
// (?i) -> case insensitive flag
// ^ -> start of string (not in middle of a sentence)
// \b -> the word has to end here
@@ -38,16 +39,12 @@ public class MessageCommandListener extends ListenerAdapter
public MessageCommand getRegisteredCommand(String label)
{
for (Map.Entry<LinkedList<String>, MessageCommand> entry : registeredCommands.entrySet())
for(LinkedList<String> aliases : registeredCommands.keySet())
{
LinkedList<String> aliases = entry.getKey();
for(String currentAlias : aliases)
{
if(label.equals(currentAlias))
{
return entry.getValue();
}
{ return registeredCommands.get(aliases); }
}
}
@@ -55,9 +52,10 @@ public class MessageCommandListener extends ListenerAdapter
}
public LinkedList<MessageCommand> getRegisteredCommands()
{
return new LinkedList<>(registeredCommands.values());
}
{ return new LinkedList<>(registeredCommands.values()); }
private final Logger logger = new Logger(MessageCommandListener.class);
@Override
public void onMessageReceived(@NotNull MessageReceivedEvent event)
@@ -69,11 +67,11 @@ public class MessageCommandListener extends ListenerAdapter
String eventMessage = event.getMessage().getContentRaw();
// check if the sent message matches the bot activation regex (prefix, name, ...)
if (!eventMessage.toLowerCase().matches("(?s)" + COMMAND_REGEX + ".*"))
if(!eventMessage.toLowerCase().matches(commandRegex + "((.|\\n)*)"))
return;
// generate args from the string
String argsString = eventMessage.replaceAll(COMMAND_REGEX + "\\s*", "");
String argsString = eventMessage.replaceAll(commandRegex + "\\s*", "");
// if no args were specified apart from the bot prefix
@@ -117,13 +115,16 @@ public class MessageCommandListener extends ListenerAdapter
{
Member member = event.getMember();
GuildChannel channel = event.getGuildChannel(); //todo: what about forum post
if (member != null && !member.hasPermission(channel, requiredPermissions))
if(member != null)
{
if(!member.hasPermission(channel, requiredPermissions))
{
event.getMessage()
.reply("You do not have permissions to run this command!")
.queue(); // todo prettier
// todo: queue message deletion in 15 seconds or so
return;
}
}
}
@@ -137,7 +138,8 @@ public class MessageCommandListener extends ListenerAdapter
argsString = argsString.replaceAll("^[\\S]+\\s*", "");
// pass all other arguments as a single argument as the first array element
commandArgs = new String[]{argsString};
} else
}
else
{
// copy all split arguments to the array, except from the command label
commandArgs = Arrays.copyOfRange(argsRaw, 1, argsRaw.length);

View File

@@ -6,17 +6,16 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.util.Logger;
public class MessageLogger extends ListenerAdapter
{
// this class only gets loaded as a listener if verbosity is set to true on startup.
private static final String GUILD_MESSAGE_LOG_FORMAT = "[%guild%] [#%channel%] %user%: %message%";
private static final String DIRECT_MESSAGE_LOG_FORMAT = "[DM] %user%: %message%";
private final static String guildChannelFormat = "[%guild%] [#%channel%] %user%: %message%";
private final static String dmFormat = "[DM] %user%: %message%";
private static final Logger LOGGER = LoggerFactory.getLogger(MessageLogger.class);
private final Logger logger = new Logger(MessageLogger.class);
@Override
public void onMessageReceived(@NotNull MessageReceivedEvent event)
@@ -25,30 +24,31 @@ public class MessageLogger extends ListenerAdapter
String userName = event.getAuthor().getAsTag();
String message = event.getMessage().getContentDisplay();
if (event.getChannel() instanceof TextChannel channel)
if(event.getChannel() instanceof TextChannel)
{
String guildName = channel.getGuild().getName();
String guildName = ((TextChannel) event.getChannel()).getGuild().getName();
String channelName = event.getChannel().getName();
toLog = GUILD_MESSAGE_LOG_FORMAT
toLog = guildChannelFormat
.replace("%guild%", guildName)
.replace("%channel%", channelName);
} else if (event.getChannel() instanceof PrivateChannel)
}
else if(event.getChannel() instanceof PrivateChannel)
{
toLog = DIRECT_MESSAGE_LOG_FORMAT;
toLog = dmFormat;
}
toLog = toLog
.replace("%user%", userName)
.replace("%message%", message);
LOGGER.info(toLog);
logger.log(toLog);
if(!event.getMessage().getAttachments().isEmpty())
{
for(Message.Attachment atch : event.getMessage().getAttachments())
{
LOGGER.info(atch.getUrl());
logger.log(atch.getUrl());
}
}
}

View File

@@ -2,26 +2,18 @@ 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 wtf.beatrice.hidekobot.commands.base.Trivia;
public class SelectMenuInteractionListener extends ListenerAdapter
{
private static final Logger LOGGER = LoggerFactory.getLogger(SelectMenuInteractionListener.class);
@Override
public void onStringSelectInteraction(StringSelectInteractionEvent event)
{
switch (event.getComponentId().toLowerCase())
{
switch (event.getComponentId().toLowerCase()) {
// trivia
case "trivia_categories" -> Trivia.handleMenuSelection(event);
// error handling
default -> LOGGER.warn("Received unhandled {}", event.getClass().getSimpleName());
}
}
}

View File

@@ -21,15 +21,10 @@ public class SlashCommandCompletionListener extends ListenerAdapter
}
public SlashArgumentsCompleter getRegisteredCompleter(String label)
{
return registeredCompleters.get(label);
}
{ return registeredCompleters.get(label); }
public LinkedList<SlashArgumentsCompleter> getRegisteredCompleters()
{
return new LinkedList<>(registeredCompleters.values());
}
{ return new LinkedList<>(registeredCompleters.values()); }
@Override
public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent event)
{

View File

@@ -21,14 +21,10 @@ public class SlashCommandListener extends ListenerAdapter
}
public SlashCommand getRegisteredCommand(String label)
{
return registeredCommands.get(label);
}
{ return registeredCommands.get(label); }
public LinkedList<SlashCommand> getRegisteredCommands()
{
return new LinkedList<>(registeredCommands.values());
}
{ return new LinkedList<>(registeredCommands.values()); }
@Override
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event)

View File

@@ -4,41 +4,8 @@ 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) +
'}';
}
@Nullable ItemComponent... components) {
}

View File

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

View File

@@ -70,7 +70,9 @@ public interface MessageCommand
*
* @param event the received message event. It should not be used for parsing message contents data as
* 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 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,
* removed, or we switch to another method of triggering commands (ping, trigger words, ...).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,15 +18,9 @@ public class TriviaScore
score += add;
}
public int getScore()
{
return score;
}
public int getScore() { return score; }
public User getUser()
{
return user;
}
public User getUser() { return user; }
@Override
public String toString()

View File

@@ -1,20 +1,18 @@
package wtf.beatrice.hidekobot.runnables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
import wtf.beatrice.hidekobot.util.CommandUtil;
import wtf.beatrice.hidekobot.util.Logger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class ExpiredMessageTask implements Runnable
{
public class ExpiredMessageTask implements Runnable {
private final DateTimeFormatter formatter;
private static final Logger LOGGER = LoggerFactory.getLogger(ExpiredMessageTask.class);
private final Logger logger;
private DatabaseSource databaseSource;
@@ -23,12 +21,12 @@ public class ExpiredMessageTask implements Runnable
String format = Cache.getExpiryTimestampFormat();
formatter = DateTimeFormatter.ofPattern(format);
databaseSource = Cache.getDatabaseSource();
logger = new Logger(getClass());
}
@Override
public void run()
{
public void run() {
databaseSource = Cache.getDatabaseSource();
if(databaseSource == null) return;
@@ -41,10 +39,10 @@ public class ExpiredMessageTask implements Runnable
for(String messageId : expiringMessages)
{
if (Cache.isVerbose()) LOGGER.info("expired check: {}", messageId);
if(Cache.isVerbose()) logger.log("expired check: " + messageId);
String expiryTimestamp = databaseSource.getQueuedExpiringMessageExpiryDate(messageId);
if (expiryTimestamp == null || expiryTimestamp.isEmpty()) // if missing timestamp
if(expiryTimestamp == null || expiryTimestamp.equals("")) // if missing timestamp
{
// count it as already expired
databaseSource.untrackExpiredMessage(messageId);
@@ -56,7 +54,7 @@ public class ExpiredMessageTask implements Runnable
LocalDateTime expiryDate = LocalDateTime.parse(expiryTimestamp, formatter);
if(now.isAfter(expiryDate))
{
if (Cache.isVerbose()) LOGGER.info("expired: {}", messageId);
if(Cache.isVerbose()) logger.log("expired: " + messageId);
CommandUtil.disableExpired(messageId);
}
}

View File

@@ -1,8 +1,7 @@
package wtf.beatrice.hidekobot.runnables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.util.Logger;
import java.io.IOException;
import java.net.HttpURLConnection;
@@ -10,7 +9,14 @@ import java.net.URL;
public class HeartBeatTask implements Runnable
{
private static final Logger LOGGER = LoggerFactory.getLogger(HeartBeatTask.class);
private final Logger logger;
public HeartBeatTask()
{
logger = new Logger(getClass());
}
@Override
public void run()
@@ -18,8 +24,7 @@ public class HeartBeatTask implements Runnable
String urlString = Cache.getFullHeartBeatLink();
if(urlString == null || urlString.isEmpty()) return;
try
{
try {
URL heartbeatUrl = new URL(urlString);
@@ -32,15 +37,15 @@ public class HeartBeatTask implements Runnable
if(200 <= responseCode && responseCode < 300)
{
// only log ok response codes when verbosity is enabled
if (Cache.isVerbose()) LOGGER.info("Heartbeat response code: {}", responseCode);
} else
if(Cache.isVerbose()) logger.log("Heartbeat response code: " + responseCode);
}
else
{
LOGGER.error("Heartbeat returned problematic response code: {}", responseCode);
logger.log("Heartbeat returned problematic response code: " + responseCode);
}
} catch (IOException e)
{
LOGGER.error("Error while trying to push heartbeat", e);
} catch (IOException e) {
logger.log("Error while trying to push heartbeat: " + e.getMessage());
}
}

View File

@@ -1,30 +0,0 @@
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!");
}
}
}

View File

@@ -0,0 +1,12 @@
package wtf.beatrice.hidekobot.runnables;
import wtf.beatrice.hidekobot.Cache;
public class RandomSeedTask implements Runnable
{
@Override
public void run() {
Cache.setRandomSeed(System.currentTimeMillis());
}
}

View File

@@ -22,8 +22,7 @@ public class StatusUpdateTask implements Runnable
);
@Override
public void run()
{
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));

View File

@@ -93,14 +93,12 @@ public class TriviaTask implements Runnable
previousScore = score;
topScore = score;
pos = 1;
} else
{
} else {
if(score != previousScore) pos++;
}
if(pos == 1) winners.add(user);
else
{
else {
othersBuilder.append("\n").append(pos)
.append(" | ").append(user)
.append(": ").append(score).append(" points");
@@ -115,8 +113,7 @@ public class TriviaTask implements Runnable
if(i + 1 != winners.size())
{
winnersBuilder.append(", "); // separate with comma except on last run
} else
{
} else {
winnersBuilder.append(": ").append(topScore).append(" points \uD83C\uDF89");
}
}

View File

@@ -1,6 +1,5 @@
package wtf.beatrice.hidekobot.util;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
@@ -12,8 +11,6 @@ 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 wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
@@ -25,12 +22,7 @@ import java.util.List;
public class CommandUtil
{
private static final Logger LOGGER = LoggerFactory.getLogger(CommandUtil.class);
private CommandUtil()
{
throw new IllegalStateException("Utility class");
}
private static final Logger logger = new Logger(CommandUtil.class);
/**
* Function to delete a message when a user clicks the "delete" button attached to that message.
@@ -41,8 +33,7 @@ public class CommandUtil
public static void delete(ButtonInteractionEvent event)
{
// check if the user interacting is the same one who ran the command
if (!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
{
if (!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId()))) {
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
return;
}
@@ -66,9 +57,7 @@ public class CommandUtil
// populate commands list from registered commands
List<CommandData> allCommands = new ArrayList<>();
for(SlashCommand cmd : Cache.getSlashCommandListener().getRegisteredCommands())
{
allCommands.add(cmd.getSlashCommandData());
}
{ allCommands.add(cmd.getSlashCommandData()); }
JDA jdaInstance = HidekoBot.getAPI();
@@ -144,13 +133,13 @@ public class CommandUtil
}
}
LOGGER.info("Found {} commands.", registeredCommands.size());
logger.log("Found " + registeredCommands.size() + " commands.");
if(update)
{
// send updated command list.
jdaInstance.updateCommands().addCommands(allCommands).queue();
LOGGER.info("Commands updated. New total: {}.", allCommands.size());
logger.log("Commands updated. New total: " + allCommands.size() + ".");
}
}
@@ -193,7 +182,8 @@ public class CommandUtil
}
textChannel = user.openPrivateChannel().complete();
} else
}
else
{
String guildId = databaseSource.getQueuedExpiringMessageGuild(messageId);
Guild guild = guildId == null ? null : HidekoBot.getAPI().getGuildById(guildId);
@@ -218,7 +208,7 @@ public class CommandUtil
RestAction<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
if (Cache.isVerbose()) LOGGER.info("cleaning up: {}", messageId);
if(Cache.isVerbose()) logger.log("cleaning up: " + messageId);
retrieveAction.queue(
message -> {

View File

@@ -4,41 +4,18 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class FormatUtil
{
private FormatUtil()
{
throw new IllegalStateException("Utility class");
}
// cosmetic string to print on startup.
private static final String LOGO = """
\s
██╗░░██╗██╗██████╗░███████╗██╗░░██╗░█████╗░
██║░░██║██║██╔══██╗██╔════╝██║░██╔╝██╔══██╗
███████║██║██║░░██║█████╗░░█████═╝░██║░░██║
██╔══██║██║██║░░██║██╔══╝░░██╔═██╗░██║░░██║
██║░░██║██║██████╔╝███████╗██║░╚██╗╚█████╔╝
╚═╝░░╚═╝╚═╝╚═════╝░╚══════╝╚═╝░░╚═╝░╚════╝░
\s""";
/**
* Returns ASCII art saying the bot name.
*
* @return a String containing the logo
*/
public static String getLogo()
{
return LOGO;
}
/**
* Generate a nicely formatted time-diff String that omits unnecessary data
* (e.g. 0 days, 0 hours, 4 minutes, 32 seconds -> 4m 32s)
@@ -63,28 +40,32 @@ public class FormatUtil
*/
public static String getNiceDuration(Duration duration)
{
long days = duration.toDays();
long hours = duration.toHoursPart();
long minutes = duration.toMinutesPart();
long seconds = duration.toSecondsPart();
long minutes = duration.toMinutesPart();
long hours = duration.toHoursPart();
long days = duration.toDays();
StringBuilder sb = new StringBuilder();
if (days > 0)
StringBuilder uptimeStringBuilder = new StringBuilder();
if(days == 0)
{
sb.append(days).append("d ");
sb.append(hours).append("h ");
sb.append(minutes).append("m ");
} else if (hours > 0)
if(hours == 0)
{
sb.append(hours).append("h ");
sb.append(minutes).append("m ");
} else if (minutes > 0)
{
sb.append(minutes).append("m ");
if(minutes == 0)
{} else { // i know this if has an empty body but i feel like this reads better
uptimeStringBuilder.append(minutes).append("m ");
}
sb.append(seconds).append("s");
} else {
uptimeStringBuilder.append(hours).append("h ");
uptimeStringBuilder.append(minutes).append("m ");
}
} else {
uptimeStringBuilder.append(days).append("d ");
uptimeStringBuilder.append(hours).append("h ");
uptimeStringBuilder.append(minutes).append("m ");
}
uptimeStringBuilder.append(seconds).append("s ");
return sb.toString();
return uptimeStringBuilder.toString();
}
/**
@@ -106,11 +87,8 @@ public class FormatUtil
eg: 3d, 33hours, 32048dojg, 3d2h5m22s.
it does not match if the [digits and characters] blocks are missing.
eg: 33, asd, 3g5hj, 4 asd.
{1,10} is used to limit the size of the input to parse, to avoid stack overflows.
no one should be typing more than 10 arguments, or more than 10 digits for a single argument anyway.
*/
if (!duration.matches("(\\d{1,10}[a-zA-Z]{1,10}){1,10}"))
if(!duration.matches("(\\d+[a-zA-Z]+)+"))
return null;
String[] durationTimes = duration.split("[a-zA-Z]+");
@@ -143,7 +121,7 @@ public class FormatUtil
// we won't do any sanitization, because this is a private method, and
// we are only accessing it with things that we know for sure are already sanitized.
unitName = unitName.toLowerCase();
TemporalUnit timeUnit;
TemporalUnit timeUnit = null;
/*
parsing table
@@ -161,7 +139,6 @@ public class FormatUtil
case "m", "mi", "min", "minute", "minutes" -> timeUnit = ChronoUnit.MINUTES;
case "h", "ho", "hr", "hour", "hours" -> timeUnit = ChronoUnit.HOURS;
case "d", "day", "days" -> timeUnit = ChronoUnit.DAYS;
default -> timeUnit = null;
}
return timeUnit;

View File

@@ -3,22 +3,29 @@ package wtf.beatrice.hidekobot.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Deprecated(since = "0.5.16", forRemoval = true)
public class Logger<T>
public class Logger
{
// cosmetic string to print on startup.
private String logo =
"██╗░░██╗██╗██████╗░███████╗██╗░░██╗░█████╗░\n" +
"██║░░██║██║██╔══██╗██╔════╝██║░██╔╝██╔══██╗\n" +
"███████║██║██║░░██║█████╗░░█████═╝░██║░░██║\n" +
"██╔══██║██║██║░░██║██╔══╝░░██╔═██╗░██║░░██║\n" +
"██║░░██║██║██████╔╝███████╗██║░╚██╗╚█████╔╝\n" +
"╚═╝░░╚═╝╚═╝╚═════╝░╚══════╝╚═╝░░╚═╝░╚════╝░";
// objects that we need to have for a properly formatted message
private final String className;
private String className;
private final String format = "[%date% %time%] [%class%] %message%";
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd");
private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
// when initializing a new logger, save variables in that instance
public Logger(Class<T> logClass)
public Logger(Class logClass)
{
className = logClass.getSimpleName();
}
@@ -50,10 +57,7 @@ public class Logger<T>
{
// create a new scheduled executor with an anonymous runnable...
//... after waiting <delay> seconds.
try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor())
{
executor.schedule(() -> log(message), delay, TimeUnit.SECONDS);
}
Executors.newSingleThreadScheduledExecutor().schedule(() -> log(message), delay, TimeUnit.SECONDS);
}
@@ -67,5 +71,14 @@ public class Logger<T>
System.out.println(message);
}
/**
* Returns ASCII art saying the bot name.
*
* @return a String containing the logo
*/
public String getLogo()
{
return logo;
}
}

View File

@@ -1,24 +1,9 @@
package wtf.beatrice.hidekobot.util;
import org.random.util.RandomOrgRandom;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.datasources.ConfigurationEntry;
import java.security.SecureRandom;
import java.util.Random;
public class RandomUtil
{
private RandomUtil()
{
throw new IllegalStateException("Utility class");
}
// the Random instance that we should always use when looking for an RNG based thing.
// the seed is updated periodically, if the random.org integration is enabled.
private static Random randomInstance = new SecureRandom();
/**
* Returns a random integer picked in a range.
*
@@ -41,44 +26,9 @@ public class RandomUtil
int difference = (max - min) + 1;
// find a number between 0 and our range (eg. 5 -> 8 = 0 -> 3)
int randomTemp = getRandom().nextInt(difference);
int randomTemp = Cache.getRandom().nextInt(difference);
// add the minimum value, so we are sure to be in the original range (0->5, 1->6, 2->7, 3->8)
return randomTemp + min;
}
public static Random getRandom()
{
return randomInstance;
}
public static void initRandomOrg()
{
/* we use the random.org instance to generate 160 random bytes.
then, we're feeding those 160 bytes as a seed for a SecureRandom.
this is preferred to calling the RandomOrgRandom directly every time,
because it has to query the api and (1) takes a long time, especially with big
dice rolls, and (2) you'd run in the limits extremely quickly if the bot
was run publicly for everyone to use.
*/
String apiKey = Cache.getRandomOrgApiKey();
RandomOrgRandom randomOrg = new RandomOrgRandom(apiKey);
byte[] randomBytes = new byte[160];
randomOrg.nextBytes(randomBytes);
randomInstance = new SecureRandom(randomBytes);
}
public static boolean isRandomOrgKeyValid()
{
String apiKey = Cache.getRandomOrgApiKey();
return apiKey != null &&
!apiKey.isEmpty() &&
!apiKey.equals(ConfigurationEntry.RANDOM_ORG_API_KEY.getDefaultValue());
}
}

View File

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

View File

@@ -1,2 +1 @@
bot.version=${project.version}
repo.base_url=https://git.beatrice.wtf/bea/HidekoBot/

View File

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