Compare commits

37 Commits
Author SHA1 Message Date
anthorob 09d0edddff version 3.0 2025-03-09 15:55:00 -04:00
anthorob 0b7c1bfe4f handle events in a separate thread 2025-03-09 15:32:26 -04:00
anthorob 3466fb536d finish abstraction for StepTask 2025-03-09 15:29:43 -04:00
anthorob f01c48c61c create renovate config 2025-03-03 01:26:29 -05:00
anthorob 80104d3e9a update youtube-source to latest version 2025-02-27 04:35:01 -05:00
anthorob daac16ea47 fix udio source 2025-02-14 05:47:58 -05:00
anthorob e07c172a21 compile error whoops 2025-02-14 05:44:47 -05:00
anthorob 6429e42384 Bunch of improvements to tasks and DB logic
Use locks when executing tasks
Decouple GuildManager into services
Add more logging when failing to load a persistable task
Add ping method to WebModule
Improve Task interface and implementations
Start work on step tasks
2025-02-14 05:43:49 -05:00
anthorob 72d8bcef20 Update youtube source to latest version 2025-02-04 15:36:11 -05:00
anthorob fb872f2b0b change how events are handled by using a single class to register listeners onto 2024-12-31 20:45:16 -05:00
anthorob 6a869afb1d Add request caching on the WebModule 2024-12-24 09:03:31 -05:00
anthorob 88fa8aac23 add option constraints for some commands 2024-12-24 08:46:32 -05:00
anthorob 29eecd42a9 fix(MessageCommandContext): missing check for channel when option is of type STRING 2024-12-21 21:12:28 -05:00
anthorob b15ed9d577 better parsing for boolean options 2024-12-19 12:03:16 -05:00
anthorob 04bdb75087 show available choices, types, maximum and minimum length in command help message 2024-12-19 01:28:40 -05:00
anthorob 96e31a19ec fix: add missing checks for command usage 2024-12-18 11:44:23 -05:00
anthorob 9861fac769 use string chunking for method replyEphemeral(String) 2024-12-17 12:17:40 -05:00
anthorob 8479f719ab change package of core command classes so it makes more sense and rename getType() to getCategory() in command interface 2024-12-17 12:15:24 -05:00
anthorob 3314ef6bea Add unique ID for each context 2024-12-14 01:07:24 -05:00
anthorob 50430589e6 Add isSlashCommandOnly to command interface 2024-12-14 00:59:50 -05:00
anthorob 6b453bb7e2 Remove String.format() overload in Context 2024-12-12 21:22:11 -05:00
anthorob bae37fc8b1 Fixed message commands where a null optionnal argument was being parsed when it shouldn't be 2024-12-12 21:17:15 -05:00
anthorob 2afab8f1f5 Create abstraction to combine slash commands and message commands
Usage and options checking (arguments count, type checking)
Options parsing for message commands (both types of commands are now registered like slash commands and the abstraction will handle the parsing of all options as if it was a slash command)
Refactor of the help command for better readability and more helpful text, which is now based on the available options of a command
2024-12-11 04:00:55 -05:00
anthorob 4c27b80527 update youtube-source to 1.11.1 2024-12-10 09:06:41 -05:00
anthorob e6afe6e4c1 Implement built-in tasks, improve message commands with arguments, fix command context in DM, better help message 2024-11-21 01:33:55 -05:00
anthorob e0ef3f9915 Add getInteractionHook to InteractionContext abstraction 2024-11-20 04:43:04 -05:00
anthorob 6f2ba795d4 Improve cleanup for music player, fix resume logic, check if player is connected before checking url, refactor context 2024-11-16 03:14:23 -05:00
anthorob bcf78920e9 fix(MusicManager): missing checks and reworked logic of who is the DJ
DJ is now the first user who makes the bot connect to a voice channel and doesn't change until the voice channel changes
2024-10-09 12:27:41 -04:00
anthorob 0ccc2d5e9c fix(Udio): update html tag 2024-10-06 00:45:41 -04:00
anthorob 508c7acc67 build: switch to junit 5 2024-10-05 13:35:36 -04:00
anthorob 8be0f94b3e refactor(MusicManager): load track before connecting to voice channel 2024-10-05 13:31:41 -04:00
anthorob 5baa69ffe9 refactor: move db logic for tasks into service 2024-10-05 10:15:06 -04:00
anthorob 5a8e606a89 fix(CommandProcessor): missing guild check and if command exists before execution 2024-10-05 10:13:21 -04:00
anthorob 9ee4797aaf refactor: move db actions into repositories 2024-10-03 09:48:43 -04:00
anthorob 5294cb86ca Merge branch 'feat-tasks' into 'develop'
New feature: tasks

See merge request anthorob/netneutrality!2
2024-09-12 10:50:14 +00:00
anthorob 333d638f94 feat: implement button, modal and emoji tasks 2024-09-12 10:50:14 +00:00
anthorob 4378321c65 build: fix default values in startup script for docker 2024-07-16 21:17:18 -04:00
128 changed files with 1432 additions and 3233 deletions

No files matched your search

-15
View File
@@ -1,15 +0,0 @@
.git
.gitea
.idea
.mvn
src
classes
logs
target/*
!target/netneutrality.jar
*.iml
README.md
checkstyle.xml
mvnw
mvnw.cmd
pom.xml
+3 -10
View File
@@ -1,24 +1,17 @@
NETNEUTRALITY_TAG=latest
NETNEUTRALITY_BOT_ENV=dev NETNEUTRALITY_BOT_ENV=dev
NETNEUTRALITY_BOT_TOKEN= NETNEUTRALITY_BOT_TOKEN=
NETNEUTRALITY_BOT_PREFIX=$ NETNEUTRALITY_BOT_PREFIX=$
NETNEUTRALITY_BOT_OWNER= NETNEUTRALITY_BOT_OWNER=
NETNEUTRALITY_BOT_INVITE=
NETNEUTRALITY_API_PORT=9000 NETNEUTRALITY_API_PORT=9000
MYSQL_HOST=127.0.0.1 MYSQL_HOST=127.0.0.1
MYSQL_USER=root
MYSQL_PASSWORD=
MYSQL_DATABASE=
MYSQL_PORT=3306 MYSQL_PORT=3306
MYSQL_USER=java
MYSQL_PASSWORD=password
MYSQL_DATABASE=netneutrality
MYSQL_ROOT_PASSWORD=password
MARIADB_AUTO_UPGRADE=1
NETNEUTRALITY_PO_TOKEN= NETNEUTRALITY_PO_TOKEN=
NETNEUTRALITY_VISITOR_DATA= NETNEUTRALITY_VISITOR_DATA=
NETNEUTRALITY_CIPHER_URL=http://localhost:8001
NETNEUTRALITY_CIPHER_PASSWORD=
NETNEUTRALITY_SENTRY_DSN= NETNEUTRALITY_SENTRY_DSN=
GITLAB_HOST= GITLAB_HOST=
-201
View File
@@ -1,201 +0,0 @@
name: CI
on:
push:
branches:
- "main"
tags:
- 'v*'
pull_request:
branches:
- "main"
env:
MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=${{ github.workspace }}/.m2/repository -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version --no-transfer-progress -DinstallAtEnd=true -DdeployAtEnd=true"
# Docker image name; expects REGISTRY_URL like registry.example.com
CI_REGISTRY_IMAGE: "${{ secrets.REGISTRY_URL }}/${{ github.repository }}"
jobs:
compile:
name: Compile
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '25'
cache: 'maven'
- name: Cache Maven local repo and wrapper
uses: actions/cache@v5
with:
path: |
${{ github.workspace }}/.m2/repository
.mvn
key: m2-${{ runner.os }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
m2-${{ runner.os }}-
- name: Compile
run: |
chmod +x ./mvnw
./mvnw $MAVEN_CLI_OPTS compile
test:
name: Test
runs-on: ubuntu-latest
needs: compile
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '25'
cache: 'maven'
- name: Cache Maven local repo and wrapper
uses: actions/cache@v5
with:
path: |
${{ github.workspace }}/.m2/repository
.mvn
key: m2-${{ runner.os }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
m2-${{ runner.os }}-
- name: Run tests
run: |
chmod +x ./mvnw
./mvnw $MAVEN_CLI_OPTS test
- name: Upload test reports (artifact)
if: always()
uses: actions/upload-artifact@v5
with:
name: junit-reports
path: |
**/target/*-reports/TEST-*.xml
if-no-files-found: ignore
package:
name: Package
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '25'
cache: 'maven'
- name: Cache Maven local repo and wrapper
uses: actions/cache@v5
with:
path: |
${{ github.workspace }}/.m2/repository
.mvn
key: m2-${{ runner.os }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
m2-${{ runner.os }}-
- name: Build package (skip tests)
run: |
chmod +x ./mvnw
./mvnw $MAVEN_CLI_OPTS package -DskipTests=true
- name: Upload Maven artifacts
uses: actions/upload-artifact@v5
with:
name: maven-artifacts
path: |
target/netneutrality.jar
startup.sh
if-no-files-found: error
docker-main:
name: Docker Build and Push (main)
runs-on: ubuntu-latest
needs: package
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download build artifacts
uses: actions/download-artifact@v8
with:
name: maven-artifacts
path: .
- name: Log in to registry
uses: docker/login-action@v4
with:
registry: ${{ secrets.REGISTRY_URL }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push (main)
uses: docker/build-push-action@v7
with:
context: .
file: ./Dockerfile
push: true
tags: "${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/netneutrality:main"
docker-release:
name: Docker Build and Push (release)
runs-on: ubuntu-latest
needs: package
if: ${{ startsWith(github.ref, 'refs/tags/') }}
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download build artifacts
uses: actions/download-artifact@v8
with:
name: maven-artifacts
path: .
- name: Compute release version
id: rel
shell: bash
run: |
# Strip leading 'v' if present (e.g., v1.2.3 -> 1.2.3)
ver="${GITHUB_REF_NAME#v}"
echo "version=$ver" >> "$GITHUB_OUTPUT"
- name: Log in to registry
uses: docker/login-action@v4
with:
registry: ${{ secrets.REGISTRY_URL }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push (release + latest)
uses: docker/build-push-action@v7
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/netneutrality:${{ steps.rel.outputs.version }}
${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/netneutrality:latest
-1
View File
@@ -45,4 +45,3 @@ target/
logs/ logs/
.env .env
docker-compose.override.yml
+54
View File
@@ -0,0 +1,54 @@
image: eclipse-temurin:17-jdk-alpine
variables:
MAVEN_OPTS: >-
-Dhttps.protocols=TLSv1.2
-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
-Dorg.slf4j.simpleLogger.showDateTime=true
-Djava.awt.headless=true
MAVEN_CLI_OPTS: >-
--batch-mode
--errors
--fail-at-end
--show-version
--no-transfer-progress
-DinstallAtEnd=true
-DdeployAtEnd=true
cache:
key: "maven-$CI_COMMIT_REF_SLUG"
paths:
- .m2/repository
stages:
- compile
- test
- package
compile:
stage: compile
script:
- "chmod +x ./mvnw"
- "./mvnw $MAVEN_CLI_OPTS compile"
test:
stage: test
script:
- "chmod +x ./mvnw"
- "./mvnw $MAVEN_CLI_OPTS test"
artifacts:
when: always
reports:
junit:
- "**/target/*-reports/TEST-*.xml"
package:
stage: package
script:
- "chmod +x ./mvnw"
- "./mvnw $MAVEN_CLI_OPTS package -DskipTests=true"
artifacts:
name: "Maven artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
paths:
- "**/target"
+18 -3
View File
@@ -1,3 +1,18 @@
wrapperVersion=3.3.4 # Licensed to the Apache Software Foundation (ASF) under one
distributionType=only-script # or more contributor license agreements. See the NOTICE file
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip # distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
+14 -3
View File
@@ -1,6 +1,17 @@
FROM eclipse-temurin:25-jre FROM eclipse-temurin:17-jdk-alpine AS build
RUN apt-get update && apt-get install -y --no-install-recommends netcat-openbsd curl && rm -rf /var/lib/apt/lists/* COPY .mvn/ .mvn
COPY mvnw pom.xml ./
COPY src ./src
RUN ["chmod", "+x", "./mvnw"]
RUN ./mvnw install
FROM eclipse-temurin:17-jre-alpine
RUN apk add netcat-openbsd
RUN apk add curl
WORKDIR /app WORKDIR /app
@@ -8,7 +19,7 @@ ARG API_PORT=9000
EXPOSE ${API_PORT} EXPOSE ${API_PORT}
COPY startup.sh ./ COPY startup.sh ./
COPY target/netneutrality.jar ./ COPY --from=build /target/netneutrality-jar-with-dependencies.jar ./
RUN ["chmod", "+x", "./startup.sh"] RUN ["chmod", "+x", "./startup.sh"]
CMD ["./startup.sh"] CMD ["./startup.sh"]
+4 -11
View File
@@ -1,24 +1,17 @@
NETNEUTRALITY_TAG=latest
NETNEUTRALITY_BOT_ENV=dev NETNEUTRALITY_BOT_ENV=dev
NETNEUTRALITY_BOT_TOKEN= NETNEUTRALITY_BOT_TOKEN=
NETNEUTRALITY_BOT_PREFIX=$ NETNEUTRALITY_BOT_PREFIX=
NETNEUTRALITY_BOT_OWNER= NETNEUTRALITY_BOT_OWNER=
NETNEUTRALITY_BOT_INVITE=
NETNEUTRALITY_API_PORT=9000 NETNEUTRALITY_API_PORT=9000
MYSQL_HOST=127.0.0.1 MYSQL_HOST=127.0.0.1
MYSQL_USER=root
MYSQL_PASSWORD=
MYSQL_DATABASE=
MYSQL_PORT=3306 MYSQL_PORT=3306
MYSQL_USER=java
MYSQL_PASSWORD=password
MYSQL_DATABASE=netneutrality
MYSQL_ROOT_PASSWORD=password
MARIADB_AUTO_UPGRADE=1
NETNEUTRALITY_PO_TOKEN= NETNEUTRALITY_PO_TOKEN=
NETNEUTRALITY_VISITOR_DATA= NETNEUTRALITY_VISITOR_DATA=
NETNEUTRALITY_CIPHER_URL=http://localhost:8001
NETNEUTRALITY_CIPHER_PASSWORD=
NETNEUTRALITY_SENTRY_DSN= NETNEUTRALITY_SENTRY_DSN=
GITLAB_HOST= GITLAB_HOST=
-37
View File
@@ -1,37 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="severity" value="warning"/>
<!-- Allows tab characters -->
<module name="FileTabCharacter">
<property name="eachLine" value="false"/>
</module>
<module name="TreeWalker">
<!-- Indentation with tabs -->
<module name="Indentation">
<property name="basicOffset" value="4"/>
<property name="tabWidth" value="4"/>
<property name="forceStrictCondition" value="false"/>
</module>
<!-- Common useful checks -->
<module name="EmptyBlock">
<property name="option" value="text"/>
</module>
<module name="NeedBraces"/>
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<module name="EqualsHashCode"/>
<!-- Relaxed Javadoc rules -->
<module name="JavadocMethod">
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
</module>
</module>
</module>
+64
View File
@@ -0,0 +1,64 @@
services:
db:
image: mariadb:lts
container_name: db
restart: unless-stopped
volumes:
- netneutrality-db:/var/lib/mysql
- ./sql:/docker-entrypoint-initdb.d
environment:
MYSQL_USER: 'java'
MYSQL_PASSWORD: 'password'
MYSQL_DATABASE: 'netneutrality'
MYSQL_ROOT_PASSWORD: 'password'
MARIADB_AUTO_UPGRADE: '1'
ports:
- "3306"
networks:
- internal
bot:
container_name: bot
build: ./
restart: unless-stopped
depends_on:
- db
volumes:
- ./logs:/app/logs
environment:
MYSQL_HOST: 'db'
MYSQL_USER: 'java'
MYSQL_PASSWORD: 'password'
MYSQL_DATABASE: 'netneutrality'
ports:
- "9000:9000"
networks:
- internal
webapp:
image: bot-webapp:dev
build: ../netneutrality-webapp
restart: always
depends_on:
- db
- bot
volumes:
- ./logs:/app/logs
environment:
DB_CONNECTION: 'mysql'
DB_HOST: 'db'
DB_PORT: '3306'
DB_DATABASE: 'netneutrality'
DB_USERNAME: 'java'
DB_PASSWORD: 'password'
BOT_URL: 'http://bot:9000'
ports:
- '8080:80'
networks:
- internal
volumes:
netneutrality-db:
networks:
internal:
+29 -31
View File
@@ -1,46 +1,44 @@
services: services:
netneutrality-db: db:
image: mariadb:11@sha256:1cac8492bd78b1ec693238dc600be173397efd7b55eabc725abc281dc855b482 image: mariadb:lts
container_name: db container_name: db
restart: unless-stopped restart: unless-stopped
networks:
- netneutrality
volumes: volumes:
- netneutrality-db:/var/lib/mysql - netneutrality-db:/var/lib/mysql
- ./sql:/docker-entrypoint-initdb.d
environment: environment:
- MYSQL_USER=${MYSQL_USER:-java} MYSQL_USER: 'java'
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-password} MYSQL_PASSWORD: 'password'
- MYSQL_DATABASE=${MYSQL_DATABASE:-netneutrality} MYSQL_DATABASE: 'netneutrality'
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-password} MYSQL_ROOT_PASSWORD: 'password'
- MARIADB_AUTO_UPGRADE=1 MARIADB_AUTO_UPGRADE: '1'
ports:
netneutrality-bot: - "3306:3306"
image: gitea.bobert.me/anthorob/netneutrality:${NETNEUTRALITY_TAG:-latest}
container_name: bot
restart: unless-stopped
env_file: .env
networks: networks:
- netneutrality - internal
bot:
container_name: bot
build: ./
restart: unless-stopped
depends_on: depends_on:
- netneutrality-db - db
volumes: volumes:
- ./logs:/app/logs - ./logs:/app/logs
environment: environment:
MYSQL_HOST: netneutrality-db MYSQL_HOST: 'db'
MYSQL_PORT: 3306 MYSQL_PORT: '3306'
MYSQL_USER: java MYSQL_USER: 'java'
MYSQL_PASSWORD: password MYSQL_PASSWORD: 'password'
MYSQL_DATABASE: netneutrality MYSQL_DATABASE: 'netneutrality'
ejs-api: env_file: ".env"
image: ghcr.io/kikkia/yt-cipher:master@sha256:4fbb0d94cb4b9d2e5028986853825921d8c30975c0d948391fb9f4c7bf8520f6 ports:
container_name: ejs_signature_api - "9000:9000"
restart: unless-stopped
networks: networks:
- netneutrality - internal
# environment: # UNCOMMENT THIS LINE AND API_TOKEN TO ADD A PASSWORD
# - API_TOKEN=test
volumes: volumes:
netneutrality-db: null netneutrality-db:
networks: networks:
netneutrality: null internal:
-42
View File
@@ -1,42 +0,0 @@
param (
[ValidateSet("migrate", "info", "validate", "clean", "baseline", "repair")]
[string]$Command = "info"
)
$envFile = ".env"
if (-not (Test-Path $envFile)) {
Write-Error ".env file not found in the current directory."
exit 1
}
Get-Content $envFile | ForEach-Object {
if ($_ -match "^\s*([A-Za-z_][A-Za-z0-9_]*)=(.*)$") {
$name = $matches[1]
$value = $matches[2]
[System.Environment]::SetEnvironmentVariable($name, $value)
}
}
$MYSQL_HOST = $env:MYSQL_HOST
$MYSQL_PORT = $env:MYSQL_PORT
$MYSQL_DATABASE = $env:MYSQL_DATABASE
if (-not $MYSQL_HOST -or -not $MYSQL_PORT -or -not $MYSQL_DATABASE) {
Write-Error "MYSQL_HOST, MYSQL_PORT, or MYSQL_DATABASE missing from .env"
exit 1
}
$env:FLYWAY_URL = "jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}"
$env:FLYWAY_USER = $env:MYSQL_USER
$env:FLYWAY_PASSWORD = $env:MYSQL_PASSWORD
Write-Host "Ensuring database '$MYSQL_DATABASE' exists..."
$mysqlCommand = "CREATE DATABASE IF NOT EXISTS $MYSQL_DATABASE CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
& mysql --host=$MYSQL_HOST --port=$MYSQL_PORT --user=$env:MYSQL_USER --password=$env:MYSQL_PASSWORD -e $mysqlCommand
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to create database or connect to MySQL. Check your credentials or server status."
exit 1
}
.\mvnw flyway:$Command
-33
View File
@@ -1,33 +0,0 @@
#!/bin/bash
set -e
COMMAND=${1:-info}
ENV_FILE=".env"
if [[ ! -f "$ENV_FILE" ]]; then
echo ".env file not found in the current directory."
exit 1
fi
# Export variables from .env
export $(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$ENV_FILE" | xargs)
if [[ -z "$MYSQL_HOST" || -z "$MYSQL_PORT" || -z "$MYSQL_DATABASE" ]]; then
echo "MYSQL_HOST, MYSQL_PORT, or MYSQL_DATABASE missing from .env"
exit 1
fi
export FLYWAY_URL="jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}"
export FLYWAY_USER="$MYSQL_USER"
export FLYWAY_PASSWORD="$MYSQL_PASSWORD"
echo "Ensuring database '$MYSQL_DATABASE' exists..."
mysql -h"$MYSQL_HOST" -P"$MYSQL_PORT" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" \
-e "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
if [[ $? -ne 0 ]]; then
echo "Failed to create database or connect to MySQL. Check your credentials or server status."
exit 1
fi
./mvnw flyway:"$COMMAND"
Vendored
+259 -246
View File
@@ -19,277 +19,290 @@
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.4 # Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
# #
# Optional ENV vars # Optional ENV vars
# ----------------- # -----------------
# JAVA_HOME - location of a JDK home dir, required when download maven via java source # MAVEN_OPTS - parameters passed to the Java VM when running Maven
# MVNW_REPOURL - repo url base for downloading maven distribution # e.g. to debug Maven itself, use
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
set -euf if [ -z "$MAVEN_SKIP_RC" ] ; then
[ "${MVNW_VERBOSE-}" != debug ] || set -x
# OS specific support. if [ -f /usr/local/etc/mavenrc ] ; then
native_path() { printf %s\\n "$1"; } . /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "$(uname)" in case "$(uname)" in
CYGWIN* | MINGW*) CYGWIN*) cygwin=true ;;
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" MINGW*) mingw=true;;
native_path() { cygpath --path --windows "$1"; } Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;; ;;
esac esac
# set JAVACMD and JAVACCMD if [ -z "$JAVA_HOME" ] ; then
set_java_home() { if [ -r /etc/gentoo-release ] ; then
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched JAVA_HOME=$(java-config --jre-home)
if [ -n "${JAVA_HOME-}" ]; then fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
fi
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACCMD="$JAVA_HOME/jre/sh/javac"
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD="$JAVA_HOME/bin/java"
JAVACCMD="$JAVA_HOME/bin/javac" fi
else
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
fi
fi
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then if [ ! -x "$JAVACMD" ] ; then
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 echo "Error: JAVA_HOME is not defined correctly." >&2
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1 return 1
fi fi
fi
else
JAVACMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v java
)" || :
JAVACCMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v javac
)" || :
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then basedir="$1"
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 wdir="$1"
return 1 while [ "$wdir" != '/' ] ; do
fi if [ -d "$wdir"/.mvn ] ; then
fi basedir=$wdir
}
# hash string like Java String::hashCode
hash_string() {
str="${1:-}" h=0
while [ -n "$str" ]; do
char="${str%"${str#?}"}"
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
str="${str#?}"
done
printf %x\\n $h
}
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
scriptDir="$(dirname "$0")"
scriptName="$(basename "$0")"
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
*)
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
distributionPlatform=linux-amd64
;;
esac
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
fi
case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac
# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
trap clean HUP INT TERM EXIT
else
die "cannot create temp dir"
fi
mkdir -p -- "${MAVEN_HOME%/*}"
# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
verbose "Found wget ... using wget"
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
verbose "Found curl ... using curl"
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
verbose "Falling back to use Java to download"
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
cat >"$javaSource" <<-END
public class Downloader extends java.net.Authenticator
{
protected java.net.PasswordAuthentication getPasswordAuthentication()
{
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
}
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
verbose " - Compiling Downloader.java ..."
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
verbose " - Running Downloader.java ..."
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi
# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
distributionSha256Result=false
if [ "$MVN_CMD" = mvnd.sh ]; then
echo "Checksum validation is not supported for maven-mvnd." >&2
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $distributionSha256Result = false ]; then
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
# unzip and move
if command -v unzip >/dev/null; then
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
# Find the actual extracted directory name (handles snapshots where filename != directory name)
actualDistributionDir=""
# First try the expected directory name (for regular distributions)
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
actualDistributionDir="$distributionUrlNameMain"
fi
fi
# If not found, search for any directory with the Maven executable (for snapshots)
if [ -z "$actualDistributionDir" ]; then
# enable globbing to iterate over items
set +f
for dir in "$TMP_DOWNLOAD_DIR"/*; do
if [ -d "$dir" ]; then
if [ -f "$dir/bin/$MVN_CMD" ]; then
actualDistributionDir="$(basename "$dir")"
break break
fi fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi fi
# end of workaround
done done
set -f printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi fi
if [ -z "$actualDistributionDir" ]; then MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
verbose "Contents of $TMP_DOWNLOAD_DIR:" log "$MAVEN_PROJECTBASEDIR"
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
die "Could not find Maven distribution directory in extracted archive" ##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi fi
verbose "Found extracted Maven distribution directory: $actualDistributionDir" if command -v wget > /dev/null; then
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" log "Found wget ... using wget"
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
clean || : # If specified, validate the SHA-256 sum of the Maven wrapper jar file
exec_maven "$@" wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
Vendored
+169 -153
View File
@@ -1,4 +1,3 @@
<# : batch portion
@REM ---------------------------------------------------------------------------- @REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one @REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file @REM or more contributor license agreements. See the NOTICE file
@@ -19,171 +18,188 @@
@REM ---------------------------------------------------------------------------- @REM ----------------------------------------------------------------------------
@REM ---------------------------------------------------------------------------- @REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.4 @REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM @REM
@REM Optional ENV vars @REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ---------------------------------------------------------------------------- @REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@SET __MVNW_CMD__= @echo off
@SET __MVNW_ERROR__= @REM set title of command window
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% title %0
@SET PSModulePath= @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
) )
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop" @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
if ($env:MVNW_VERBOSE -eq "true") { @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
$VerbosePreference = "Continue" if exist %WRAPPER_JAR% (
} if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties powershell -Command "&{"^
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl "$webclient = new-object System.Net.WebClient;"^
if (!$distributionUrl) { "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
} "}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
"maven-mvnd-*" { SET WRAPPER_SHA_256_SUM=""
$USE_MVND = $true FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
$MVN_CMD = "mvnd.cmd" )
break IF NOT %WRAPPER_SHA_256_SUM%=="" (
} powershell -Command "&{"^
default { "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
$USE_MVND = $false "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
$MVN_CMD = $script -replace '^mvnw','mvn' " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
break " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
} " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
} " exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
# apply MVNW_REPOURL and calculate MAVEN_HOME @REM Provide a "standardized" way to retrieve the CLI args that will
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash> @REM work with both Windows and non-Windows executions.
if ($env:MVNW_REPOURL) { set MAVEN_CMD_LINE_ARGS=%*
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_M2_PATH = "$HOME/.m2" %MAVEN_JAVA_EXE% ^
if ($env:MAVEN_USER_HOME) { %JVM_CONFIG_MAVEN_PROPS% ^
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" %MAVEN_OPTS% ^
} %MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
if (-not (Test-Path -Path $MAVEN_M2_PATH)) { :error
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null set ERROR_CODE=1
}
$MAVEN_WRAPPER_DISTS = $null :end
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { @endlocal & set ERROR_CODE=%ERROR_CODE%
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
} else {
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
}
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' @REM check for post script, once with legacy .bat ending and once with .cmd ending
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" if "%MAVEN_BATCH_PAUSE%"=="on" pause
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir cmd /C exit /B %ERROR_CODE%
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
# Find the actual extracted directory name (handles snapshots where filename != directory name)
$actualDistributionDir = ""
# First try the expected directory name (for regular distributions)
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
$actualDistributionDir = $distributionUrlNameMain
}
# If not found, search for any directory with the Maven executable (for snapshots)
if (!$actualDistributionDir) {
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
if (Test-Path -Path $testPath -PathType Leaf) {
$actualDistributionDir = $_.Name
}
}
}
if (!$actualDistributionDir) {
Write-Error "Could not find Maven distribution directory in extracted archive"
}
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+48 -126
View File
@@ -13,11 +13,10 @@
<properties> <properties>
<main.class>me.bobert.netneutrality.NetNeutrality</main.class> <main.class>me.bobert.netneutrality.NetNeutrality</main.class>
<java.version>25</java.version> <java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>25</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<mockito.version>5.20.0</mockito.version>
</properties> </properties>
<build> <build>
@@ -29,95 +28,52 @@
</resources> </resources>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.5</version>
<configuration>
<argLine>
-javaagent:"${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar"
</argLine>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId> <artifactId>flyway-maven-plugin</artifactId>
<version>11.14.1</version> <version>10.17.0</version>
<configuration> <configuration>
<url>${env.FLYWAY_URL}</url> <url>jdbc:mysql://localhost:3306/netneutrality</url>
<user>${env.FLYWAY_USER}</user> <user>root</user>
<password>${env.FLYWAY_PASSWORD}</password> <password></password>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId> <artifactId>maven-resources-plugin</artifactId>
<version>3.4.0</version> <version>3.3.1</version>
<configuration> <configuration>
<propertiesEncoding>UTF-8</propertiesEncoding> <propertiesEncoding>UTF-8</propertiesEncoding>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<version>3.5.0</version> <version>3.7.1</version>
<configuration> <configuration>
<archive> <archive>
<manifest>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>netneutrality</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${main.class}</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries> <manifestEntries>
<Multi-Release>true</Multi-Release> <Multi-Release>true</Multi-Release>
</manifestEntries> </manifestEntries>
</transformer> <manifest>
</transformers> <addClasspath>true</addClasspath>
<createDependencyReducedPom>false</createDependencyReducedPom> <mainClass>${main.class}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>netneutrality</finalName>
</configuration> </configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.6.0</version>
<executions> <executions>
<execution> <execution>
<id>checkstyle</id> <id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>test</phase> <phase>package</phase> <!-- bind to the packaging phase -->
<goals> <goals>
<goal>check</goal> <goal>single</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<failOnViolation>true</failOnViolation>
<linkXRef>false</linkXRef>
<outputFile>target/checkstyle-result.xml</outputFile>
<outputFormat>xml</outputFormat>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
@@ -144,98 +100,71 @@
<dependency> <dependency>
<groupId>net.dv8tion</groupId> <groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId> <artifactId>JDA</artifactId>
<version>6.3.1</version> <version>5.1.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>dev.arbjerg</groupId> <groupId>dev.arbjerg</groupId>
<artifactId>lavaplayer</artifactId> <artifactId>lavaplayer</artifactId>
<version>2.2.6</version> <version>2.2.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>dev.lavalink.youtube</groupId> <groupId>dev.lavalink.youtube</groupId>
<artifactId>v2</artifactId> <artifactId>v2</artifactId>
<version>1.18.0</version> <version>1.11.5</version>
</dependency>
<dependency>
<groupId>club.minnced</groupId>
<artifactId>jdave-api</artifactId>
<version>0.1.7</version>
</dependency>
<dependency>
<groupId>club.minnced</groupId>
<artifactId>jdave-native-linux-x86-64</artifactId>
<version>0.1.7</version>
</dependency>
<dependency>
<groupId>club.minnced</groupId>
<artifactId>jdave-native-win-x86-64</artifactId>
<version>0.1.7</version>
</dependency>
<dependency>
<groupId>club.minnced</groupId>
<artifactId>jdave-native-darwin</artifactId>
<version>0.1.7</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.squareup.okhttp3</groupId> <groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-jvm</artifactId> <artifactId>okhttp</artifactId>
<version>5.3.2</version> <version>4.12.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.hibernate.orm</groupId> <groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId> <artifactId>hibernate-core</artifactId>
<version>7.1.9.Final</version> <version>6.5.2.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.hibernate.orm</groupId> <groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-hikaricp</artifactId> <artifactId>hibernate-hikaricp</artifactId>
<version>7.1.9.Final</version> <version>6.5.2.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>mysql-connector-j</artifactId>
<version>9.5.0</version> <version>9.0.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
<version>11.14.1</version> <version>10.17.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId> <artifactId>flyway-mysql</artifactId>
<version>11.14.1</version> <version>10.17.0</version>
<scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.sentry</groupId> <groupId>io.sentry</groupId>
<artifactId>sentry</artifactId> <artifactId>sentry</artifactId>
<version>8.25.0</version> <version>7.12.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.gitlab4j</groupId> <groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId> <artifactId>gitlab4j-api</artifactId>
<version>6.2.0</version> <version>6.0.0-rc.5</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.fellbaum</groupId> <groupId>net.fellbaum</groupId>
<artifactId>jemoji</artifactId> <artifactId>jemoji</artifactId>
<version>1.7.5</version> <version>1.7.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.json</groupId> <groupId>org.json</groupId>
<artifactId>json</artifactId> <artifactId>json</artifactId>
<version>20250517</version> <version>20240303</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.2</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -247,48 +176,41 @@
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId> <artifactId>log4j-api</artifactId>
<version>2.25.2</version> <version>2.23.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId> <artifactId>log4j-core</artifactId>
<version>2.25.2</version> <version>2.23.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId> <artifactId>log4j-slf4j2-impl</artifactId>
<version>2.25.2</version> <version>2.23.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
<version>3.19.0</version> <version>3.15.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.17.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<version>2.21.0</version> <version>2.16.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<version>6.0.1</version> <version>5.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
+1 -31
View File
@@ -1,36 +1,6 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": [
"local>renovate/renovate-config", "renovate/renovate-config"
"group:monorepos",
"group:flyway"
],
"packageRules": [
{
"matchPackageNames": [
"quay.io/buildah/stable"
],
"enabled": false
},
{
"matchPackageNames": [
"mariadb"
],
"automerge": false,
"dependencyDashboardApproval": true
},
{
"groupName": "hibernate orm",
"matchPackageNames": [
"org.hibernate.orm:**"
]
},
{
"groupName": "apache commons",
"matchPackageNames": [
"commons-**",
"org.apache.commons:**"
]
}
] ]
} }
@@ -1,14 +1,27 @@
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
-- --
-- Database: `netneutrality` -- Database: `netneutrality`
-- --
CREATE DATABASE IF NOT EXISTS `netneutrality` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS `netneutrality` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `netneutrality`;
-- -------------------------------------------------------- -- --------------------------------------------------------
-- --
-- Table structure for table `blacklist` -- Table structure for table `blacklist`
-- --
CREATE TABLE `blacklist` (
DROP TABLE IF EXISTS `blacklist`;
CREATE TABLE IF NOT EXISTS `blacklist` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) NOT NULL, `guild_id` varchar(255) NOT NULL,
`word` varchar(255) NOT NULL, `word` varchar(255) NOT NULL,
@@ -23,7 +36,9 @@ CREATE TABLE `blacklist` (
-- --
-- Table structure for table `guilds` -- Table structure for table `guilds`
-- --
CREATE TABLE `guilds` (
DROP TABLE IF EXISTS `guilds`;
CREATE TABLE IF NOT EXISTS `guilds` (
`guild_id` varchar(255) NOT NULL, `guild_id` varchar(255) NOT NULL,
`logging` tinyint(1) NOT NULL DEFAULT 0, `logging` tinyint(1) NOT NULL DEFAULT 0,
`debug` tinyint(1) NOT NULL DEFAULT 1, `debug` tinyint(1) NOT NULL DEFAULT 1,
@@ -36,7 +51,9 @@ CREATE TABLE `guilds` (
-- --
-- Table structure for table `guild_settings` -- Table structure for table `guild_settings`
-- --
CREATE TABLE `guild_settings` (
DROP TABLE IF EXISTS `guild_settings`;
CREATE TABLE IF NOT EXISTS `guild_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) NOT NULL, `guild_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL, `name` varchar(255) NOT NULL,
@@ -50,7 +67,9 @@ CREATE TABLE `guild_settings` (
-- --
-- Table structure for table `logging` -- Table structure for table `logging`
-- --
CREATE TABLE `logging` (
DROP TABLE IF EXISTS `logging`;
CREATE TABLE IF NOT EXISTS `logging` (
`guild_id` varchar(255) NOT NULL, `guild_id` varchar(255) NOT NULL,
`channel_id` varchar(255) NOT NULL, `channel_id` varchar(255) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT 0, `enabled` tinyint(1) NOT NULL DEFAULT 0,
@@ -62,7 +81,9 @@ CREATE TABLE `logging` (
-- --
-- Table structure for table `log_message` -- Table structure for table `log_message`
-- --
CREATE TABLE `log_message` (
DROP TABLE IF EXISTS `log_message`;
CREATE TABLE IF NOT EXISTS `log_message` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) NOT NULL, `guild_id` varchar(255) NOT NULL,
`channel_id` varchar(255) NOT NULL, `channel_id` varchar(255) NOT NULL,
@@ -81,7 +102,9 @@ CREATE TABLE `log_message` (
-- --
-- Table structure for table `tasks` -- Table structure for table `tasks`
-- --
CREATE TABLE `tasks` (
DROP TABLE IF EXISTS `tasks`;
CREATE TABLE IF NOT EXISTS `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`task_id` varchar(255) NOT NULL, `task_id` varchar(255) NOT NULL,
`task_class` varchar(255) NOT NULL, `task_class` varchar(255) NOT NULL,
@@ -95,7 +118,9 @@ CREATE TABLE `tasks` (
-- --
-- Table structure for table `web_users` -- Table structure for table `web_users`
-- --
CREATE TABLE `web_users` (
DROP TABLE IF EXISTS `web_users`;
CREATE TABLE IF NOT EXISTS `web_users` (
`id` varchar(255) NOT NULL, `id` varchar(255) NOT NULL,
`email` mediumtext NOT NULL, `email` mediumtext NOT NULL,
`token` varchar(255) NOT NULL, `token` varchar(255) NOT NULL,
@@ -105,6 +130,10 @@ CREATE TABLE `web_users` (
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Constraints for dumped tables
--
-- --
-- Constraints for table `blacklist` -- Constraints for table `blacklist`
-- --
@@ -128,3 +157,8 @@ ALTER TABLE `logging`
-- --
ALTER TABLE `log_message` ALTER TABLE `log_message`
ADD CONSTRAINT `fk_log_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE; ADD CONSTRAINT `fk_log_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
@@ -6,22 +6,19 @@ package me.bobert.netneutrality;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import club.minnced.discord.jdave.interop.JDaveSessionFactory;
import io.sentry.Sentry; import io.sentry.Sentry;
import me.bobert.netneutrality.core.LogHandler; import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.command.CommandManager; import me.bobert.netneutrality.core.command.CommandManager;
import me.bobert.netneutrality.core.db.DatabaseMigration;
import me.bobert.netneutrality.core.db.SessionManager; import me.bobert.netneutrality.core.db.SessionManager;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.core.thread.ScheduledTaskManager;
import me.bobert.netneutrality.core.thread.ThreadManager; import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.hooks.EventsHandler; import me.bobert.netneutrality.hooks.EventsHandler;
import me.bobert.netneutrality.http.server.WebApi; import me.bobert.netneutrality.http.server.WebApi;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.thread.ShutdownThread;
import me.bobert.netneutrality.utils.Settings; import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.audio.AudioModuleConfig;
import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.exceptions.InvalidTokenException; import net.dv8tion.jda.api.exceptions.InvalidTokenException;
import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.requests.GatewayIntent;
@@ -36,7 +33,7 @@ public class NetNeutrality {
private static NetNeutrality instance; private static NetNeutrality instance;
private static JDA jda; private static JDA jda;
static void main() { public static void main(String[] args) {
System.out.println("Starting NetNeutrality v" + VERSION); System.out.println("Starting NetNeutrality v" + VERSION);
String env = Settings.getInstance().get("NETNEUTRALITY_BOT_ENV", "dev"); String env = Settings.getInstance().get("NETNEUTRALITY_BOT_ENV", "dev");
@@ -55,8 +52,6 @@ public class NetNeutrality {
if ("dev".equals(env)) { if ("dev".equals(env)) {
Configurator.setLevel("net.dv8tion.jda", Level.DEBUG); Configurator.setLevel("net.dv8tion.jda", Level.DEBUG);
Configurator.setLevel("dev.lavalink.youtube", Level.DEBUG);
Configurator.setLevel("com.sedmelluq.discord", Level.DEBUG);
} }
instance = new NetNeutrality(); instance = new NetNeutrality();
@@ -72,8 +67,8 @@ public class NetNeutrality {
throw new RuntimeException("No bot token provided, exiting."); throw new RuntimeException("No bot token provided, exiting.");
} }
// DatabaseMigration.migrate();
SessionManager.getSessionFactory(); SessionManager.getSessionFactory();
DatabaseMigration.migrate();
CommandManager.init(); CommandManager.init();
MusicManager.initPlayerManager(); MusicManager.initPlayerManager();
TaskManager.getInstance().init(); TaskManager.getInstance().init();
@@ -83,7 +78,7 @@ public class NetNeutrality {
new WebApi(Integer.parseInt(Settings.getInstance().get("NETNEUTRALITY_API_PORT", "9000"))); new WebApi(Integer.parseInt(Settings.getInstance().get("NETNEUTRALITY_API_PORT", "9000")));
jda.getPresence().setActivity(Activity.watching("you")); jda.getPresence().setActivity(Activity.watching("you"));
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); Runtime.getRuntime().addShutdownHook(new ShutdownThread(this));
} }
public static NetNeutrality getInstance() { public static NetNeutrality getInstance() {
@@ -100,13 +95,7 @@ public class NetNeutrality {
try { try {
jda = JDABuilder jda = JDABuilder
.createDefault(token) .createDefault(token)
.enableIntents( .enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES, GatewayIntent.MESSAGE_CONTENT)
GatewayIntent.GUILD_MEMBERS,
GatewayIntent.GUILD_MESSAGE_REACTIONS,
GatewayIntent.GUILD_VOICE_STATES,
GatewayIntent.MESSAGE_CONTENT)
.setAudioModuleConfig(
new AudioModuleConfig().withDaveSessionFactory(new JDaveSessionFactory()))
.setActivity(Activity.watching("me load...")) .setActivity(Activity.watching("me load..."))
.build(); .build();
jda.awaitReady(); jda.awaitReady();
@@ -118,7 +107,6 @@ public class NetNeutrality {
public void shutdown() { public void shutdown() {
System.out.println("[Net Neutrality] Shutting down..."); System.out.println("[Net Neutrality] Shutting down...");
ScheduledTaskManager.cancelAll();
MusicManager.disconnectPlayers(); MusicManager.disconnectPlayers();
ThreadManager.shutdownExecutor(); ThreadManager.shutdownExecutor();
disconnect(); disconnect();
@@ -5,10 +5,10 @@ package me.bobert.netneutrality.command.admin;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractAdminCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.AdminCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.db.entities.OBlacklist; import me.bobert.netneutrality.db.entities.OBlacklist;
import me.bobert.netneutrality.db.service.BlacklistService; import me.bobert.netneutrality.db.service.BlacklistService;
@@ -18,7 +18,7 @@ import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import java.util.List; import java.util.List;
public class BlacklistCommand extends AdminCommand { public class BlacklistCommand extends AbstractAdminCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -5,10 +5,10 @@ package me.bobert.netneutrality.command.admin;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractAdminCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.AdminCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask; import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
@@ -24,7 +24,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class ReactionCommand extends AdminCommand { public class ReactionCommand extends AbstractAdminCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -5,22 +5,18 @@ package me.bobert.netneutrality.command.basic;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.CommandCategory; import me.bobert.netneutrality.core.command.*;
import me.bobert.netneutrality.core.command.CommandContainer;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandManager;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.AbstractCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.Task; import me.bobert.netneutrality.core.tasks.BuiltInTask;
import me.bobert.netneutrality.tasks.help.BotInfo; import me.bobert.netneutrality.tasks.help.BotInfo;
import me.bobert.netneutrality.tasks.help.CommandHelp; import me.bobert.netneutrality.tasks.help.CommandHelp;
import me.bobert.netneutrality.tasks.help.ReportBug; import me.bobert.netneutrality.tasks.help.ReportBug;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MessageFunction;
import net.dv8tion.jda.api.components.actionrow.ActionRow;
import net.dv8tion.jda.api.components.buttons.Button;
import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import java.util.List; import java.util.List;
@@ -65,9 +61,9 @@ public class HelpCommand extends AbstractCommand {
public CommandExecutionResult execute(CommandContext context) { public CommandExecutionResult execute(CommandContext context) {
String command = context.getOption("command", null, CommandOption::getAsString); String command = context.getOption("command", null, CommandOption::getAsString);
if (command == null) { if (command == null) {
Button commands = Task.createButton(CommandHelp.class); Button commands = BuiltInTask.createButton(CommandHelp.class);
Button info = Task.createButton(BotInfo.class); Button info = BuiltInTask.createButton(BotInfo.class);
Button reportbug = Task.createButton(ReportBug.class); Button reportbug = BuiltInTask.createButton(ReportBug.class);
ActionRow row = ActionRow.of(commands, info, reportbug); ActionRow row = ActionRow.of(commands, info, reportbug);
context.replyEphemeral("Thanks for using the Net Neutrality Bot! What do you need help with?", List.of(row)); context.replyEphemeral("Thanks for using the Net Neutrality Bot! What do you need help with?", List.of(row));
return CommandExecutionResult.none(); return CommandExecutionResult.none();
@@ -5,10 +5,10 @@ package me.bobert.netneutrality.command.basic;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractCommand;
import me.bobert.netneutrality.core.command.CommandCategory; import me.bobert.netneutrality.core.command.CommandCategory;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.AbstractCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
public class PingCommand extends AbstractCommand { public class PingCommand extends AbstractCommand {
@@ -5,10 +5,10 @@ package me.bobert.netneutrality.command.bot;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractAdminCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.AdminCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.db.GSettings; import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.db.service.GuildService; import me.bobert.netneutrality.db.service.GuildService;
@@ -23,7 +23,7 @@ import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
import java.util.Map; import java.util.Map;
public class ConfigureCommand extends AdminCommand { public class ConfigureCommand extends AbstractAdminCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,11 +6,11 @@ package me.bobert.netneutrality.command.crypto;
*/ */
import me.bobert.netneutrality.core.LogHandler; import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.command.AbstractCommand;
import me.bobert.netneutrality.core.command.CommandCategory; import me.bobert.netneutrality.core.command.CommandCategory;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.AbstractCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.http.WebModule; import me.bobert.netneutrality.http.WebModule;
import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.OptionType;
@@ -5,10 +5,11 @@ package me.bobert.netneutrality.command.git;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.context.command.SlashCommandContext;
import me.bobert.netneutrality.core.command.AbstractCommand;
import me.bobert.netneutrality.core.command.CommandCategory; import me.bobert.netneutrality.core.command.CommandCategory;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.AbstractCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.http.WebModule; import me.bobert.netneutrality.http.WebModule;
import me.bobert.netneutrality.tasks.git.ReportIssueTask; import me.bobert.netneutrality.tasks.git.ReportIssueTask;
@@ -6,14 +6,14 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandCategory; import me.bobert.netneutrality.core.command.CommandCategory;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
public class BitconnectCommand extends MusicCommand { public class BitconnectCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,14 +6,14 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class ClearQueueCommand extends MusicCommand { public class ClearQueueCommand extends AbstractMusicCommand {
@Override @Override
public boolean needAdmin() { public boolean needAdmin() {
@@ -6,15 +6,15 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class CurrentlyPlayingCommand extends MusicCommand { public class CurrentlyPlayingCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,13 +6,13 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class JoinCommand extends MusicCommand { public class JoinCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,14 +6,14 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class LeaveCommand extends MusicCommand { public class LeaveCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,15 +6,15 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.OptionType;
public class PlayCommand extends MusicCommand { public class PlayCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -5,10 +5,10 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MessageFunction;
@@ -18,7 +18,7 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
public class QueueCommand extends MusicCommand { public class QueueCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -4,14 +4,14 @@ package me.bobert.netneutrality.command.music;/*
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class ResumeCommand extends MusicCommand { public class ResumeCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return true; return true;
@@ -6,14 +6,14 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class ShuffleCommand extends MusicCommand { public class ShuffleCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return true; return true;
@@ -6,10 +6,10 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
@@ -17,7 +17,7 @@ import me.bobert.netneutrality.utils.UserFunction;
import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.OptionData;
public class SkipCommand extends MusicCommand { public class SkipCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,15 +6,15 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
import me.bobert.netneutrality.utils.UserFunction; import me.bobert.netneutrality.utils.UserFunction;
public class StopCommand extends MusicCommand { public class StopCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -5,15 +5,15 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MessageFunction; import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class ViewQueueCommand extends MusicCommand { public class ViewQueueCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,10 +6,10 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.command.options.CommandOption; import me.bobert.netneutrality.core.command.options.CommandOption;
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
@@ -17,7 +17,7 @@ import me.bobert.netneutrality.utils.UserFunction;
import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.OptionData;
public class VolumeCommand extends MusicCommand { public class VolumeCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -5,14 +5,14 @@ package me.bobert.netneutrality.command.music;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMusicCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.impl.MusicCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.music.MusicManager; import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
public class VoteSkipCommand extends MusicCommand { public class VoteSkipCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -6,15 +6,15 @@ package me.bobert.netneutrality.command.react;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMessageMenuCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.impl.MessageMenuCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask; import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
import me.bobert.netneutrality.tasks.reaction.EmojiRoleFirstStep; import me.bobert.netneutrality.tasks.reaction.EmojiRoleFirstStep;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
public class CreateEmojiReaction extends MessageMenuCommand { public class CreateEmojiReaction extends AbstractMessageMenuCommand {
@Override @Override
public CommandExecutionResult execute(Message target, CommandContext context) { public CommandExecutionResult execute(Message target, CommandContext context) {
EmojiReactionRoleTask task = TaskManager.getInstance().findTaskByPartialId(EmojiReactionRoleTask.class, target.getId()).orElse(null); EmojiReactionRoleTask task = TaskManager.getInstance().findTaskByPartialId(EmojiReactionRoleTask.class, target.getId()).orElse(null);
@@ -6,15 +6,15 @@ package me.bobert.netneutrality.command.react;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.AbstractMessageMenuCommand;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.impl.MessageMenuCommand;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask; import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
import me.bobert.netneutrality.tasks.reaction.EmojiRoleModify; import me.bobert.netneutrality.tasks.reaction.EmojiRoleModify;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
public class ModifyEmojiReaction extends MessageMenuCommand { public class ModifyEmojiReaction extends AbstractMessageMenuCommand {
@Override @Override
public CommandExecutionResult execute(Message target, CommandContext context) { public CommandExecutionResult execute(Message target, CommandContext context) {
@@ -6,12 +6,12 @@ package me.bobert.netneutrality.context;
*/ */
import me.bobert.netneutrality.core.context.AbstractContext; import me.bobert.netneutrality.core.context.AbstractContext;
import net.dv8tion.jda.api.components.MessageTopLevelComponent;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.events.Event; import net.dv8tion.jda.api.events.Event;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData; import net.dv8tion.jda.api.utils.messages.MessageEditData;
@@ -97,7 +97,7 @@ public class AsyncContext extends AbstractContext {
} }
@Override @Override
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
if (hook != null) { if (hook != null) {
hook.sendMessage(messageCreateData).queue(); hook.sendMessage(messageCreateData).queue();
} }
@@ -118,7 +118,7 @@ public class AsyncContext extends AbstractContext {
} }
@Override @Override
public void edit(MessageEditData messageEditData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { public void edit(MessageEditData messageEditData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
if (hook != null) { if (hook != null) {
hook.editOriginal(messageEditData).queue(); hook.editOriginal(messageEditData).queue();
} }
@@ -7,10 +7,10 @@ package me.bobert.netneutrality.context;
import me.bobert.netneutrality.core.context.AbstractContext; import me.bobert.netneutrality.core.context.AbstractContext;
import me.bobert.netneutrality.core.context.NoMessageContext; import me.bobert.netneutrality.core.context.NoMessageContext;
import net.dv8tion.jda.api.components.MessageTopLevelComponent;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.events.Event; import net.dv8tion.jda.api.events.Event;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.List; import java.util.List;
@@ -43,7 +43,7 @@ public class MusicChannelContext extends AbstractContext implements NoMessageCon
} }
@Override @Override
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
if (getChannel().canTalk()) { if (getChannel().canTalk()) {
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
} }
@@ -10,10 +10,7 @@ import me.bobert.netneutrality.core.command.options.MessageCommandOption;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.MessageContext; import me.bobert.netneutrality.core.context.MessageContext;
import me.bobert.netneutrality.utils.Settings; import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Mentions;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
@@ -58,7 +55,6 @@ public class MessageCommandContext extends CommandContext implements MessageCont
@Override @Override
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) { public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
List<CommandOption> options = new ArrayList<>(); List<CommandOption> options = new ArrayList<>();
int required = commandOptions.stream().filter(OptionData::isRequired).toList().size();
Mentions mentions = message.getMentions(); Mentions mentions = message.getMentions();
int roleMentionIndex = 0; int roleMentionIndex = 0;
int userMentionIndex = 0; int userMentionIndex = 0;
@@ -75,59 +71,13 @@ public class MessageCommandContext extends CommandContext implements MessageCont
try { try {
switch (type) { switch (type) {
case STRING -> { case STRING -> {
int currentOptionIndex = commandOptions.indexOf(data); String str = args[index];
boolean nextOptionIsString = false;
if (currentOptionIndex + 1 < commandOptions.size()) {
OptionData nextOption = commandOptions.get(currentOptionIndex + 1);
nextOptionIsString = nextOption.getType() == OptionType.STRING;
}
StringBuilder strBuilder = new StringBuilder(); if ((str.contains("<@") || str.contains("<#")) && str.contains(">")) {
if (!nextOptionIsString) { invalidateContext(data, "expected a string at position " + (index + 1) + " but a role/user/channel was provided");
while (index < args.length) {
String nextArg = args[index];
if ((nextArg.contains("<@") || nextArg.contains("<#") || nextArg.contains("<@&")) && nextArg.contains(">")) {
break; break;
} }
if (nextArg.equalsIgnoreCase("true") || nextArg.equalsIgnoreCase("false")) {
break;
}
try {
Long.parseLong(nextArg);
break;
} catch (NumberFormatException ignored) {}
try {
Double.parseDouble(nextArg);
break;
} catch (NumberFormatException ignored) {}
if (!strBuilder.isEmpty()) {
strBuilder.append(" ");
}
strBuilder.append(nextArg);
index++;
}
// Update index to skip the concatenated strings
index = index - 1;
} else {
// Next option is also a string, so just take the current argument
if (index < args.length) {
strBuilder.append(args[index]);
}
}
if (strBuilder.isEmpty()) {
break;
}
String str = strBuilder.toString();
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
List<Command.Choice> matched = choices.stream().filter(choice -> choice.getAsString().equalsIgnoreCase(str)).toList(); List<Command.Choice> matched = choices.stream().filter(choice -> choice.getAsString().equalsIgnoreCase(str)).toList();
if (matched.isEmpty()) { if (matched.isEmpty()) {
@@ -166,6 +116,8 @@ public class MessageCommandContext extends CommandContext implements MessageCont
} else if (max != null && num > max.longValue()) { } else if (max != null && num > max.longValue()) {
value = max.longValue(); value = max.longValue();
} }
value = num;
} }
case BOOLEAN -> { case BOOLEAN -> {
String str = args[index]; String str = args[index];
@@ -177,7 +129,7 @@ public class MessageCommandContext extends CommandContext implements MessageCont
invalidateContext(data, "expected true or false"); invalidateContext(data, "expected true or false");
} }
case NUMBER -> { case NUMBER -> {
double num = Double.parseDouble(args[index]); double num = Long.parseLong(args[index]);
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
List<Command.Choice> matched = choices.stream().filter(choice -> choice.getAsDouble() == num).toList(); List<Command.Choice> matched = choices.stream().filter(choice -> choice.getAsDouble() == num).toList();
if (matched.isEmpty()) { if (matched.isEmpty()) {
@@ -254,7 +206,7 @@ public class MessageCommandContext extends CommandContext implements MessageCont
if (mentions.getChannels().size() > channelMentionIndex) { if (mentions.getChannels().size() > channelMentionIndex) {
GuildChannel channel = mentions.getChannels().get(channelMentionIndex++); GuildChannel channel = mentions.getChannels().get(channelMentionIndex++);
if (!requiredChannelTypes.isEmpty() && !requiredChannelTypes.contains(channel.getType())) { if (!requiredChannelTypes.contains(channel.getType())) {
invalidateContext(data, "not a valid channel type at index " + (index + 1) + invalidateContext(data, "not a valid channel type at index " + (index + 1) +
" , valid types are: " + validTypes(requiredChannelTypes)); " , valid types are: " + validTypes(requiredChannelTypes));
break; break;
@@ -270,11 +222,7 @@ public class MessageCommandContext extends CommandContext implements MessageCont
} }
} }
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
if (options.size() < required) { if (data.isRequired()) {
invalidateContext(data, "missing required argument at index " + (index + 1));
}
if (data.isRequired() && index < required) {
// This should never happen because required options are validated before calling parseOptions() // This should never happen because required options are validated before calling parseOptions()
throw new IllegalArgumentException("Required option " + data.getName() + " was not found at index " + index); throw new IllegalArgumentException("Required option " + data.getName() + " was not found at index " + index);
} }
@@ -289,13 +237,6 @@ public class MessageCommandContext extends CommandContext implements MessageCont
index++; index++;
} }
// Check if there are leftover arguments that weren't consumed
if (index < args.length) {
if (options.size() >= required) {
invalidateContext(commandOptions.getLast(), "too many arguments");
}
}
return options; return options;
} }
@@ -8,8 +8,11 @@ package me.bobert.netneutrality.context.tasks;
import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.context.AsyncContext;
import me.bobert.netneutrality.core.context.NoMessageContext; import me.bobert.netneutrality.core.context.NoMessageContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import net.dv8tion.jda.api.components.MessageTopLevelComponent; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.List; import java.util.List;
@@ -42,7 +45,7 @@ public class EmojiTaskContext extends TaskContext implements NoMessageContext {
} }
@Override @Override
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
if (getChannel() != null && getChannel().canTalk()) { if (getChannel() != null && getChannel().canTalk()) {
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
} }
@@ -1,13 +1,11 @@
package me.bobert.netneutrality.core.command.impl; package me.bobert.netneutrality.core.command;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.CommandCategory; public abstract class AbstractAdminCommand extends AbstractCommand {
public abstract class AdminCommand extends AbstractCommand {
@Override @Override
public boolean needAdmin() { public boolean needAdmin() {
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.command.impl; package me.bobert.netneutrality.core.command;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
@@ -6,7 +6,6 @@ package me.bobert.netneutrality.core.command.impl;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.Command;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.command.impl; package me.bobert.netneutrality.core.command;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
@@ -7,13 +7,11 @@ package me.bobert.netneutrality.core.command.impl;
*/ */
import me.bobert.netneutrality.context.command.MessageMenuContext; import me.bobert.netneutrality.context.command.MessageMenuContext;
import me.bobert.netneutrality.core.command.CommandCategory; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
public abstract class MessageMenuCommand extends AbstractCommand { public abstract class AbstractMessageMenuCommand extends AbstractCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return null;
@@ -1,11 +1,10 @@
package me.bobert.netneutrality.core.command.impl; package me.bobert.netneutrality.core.command;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.CommandCategory;
import me.bobert.netneutrality.db.GSettings; import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.db.service.GuildService; import me.bobert.netneutrality.db.service.GuildService;
import me.bobert.netneutrality.utils.UserFunction; import me.bobert.netneutrality.utils.UserFunction;
@@ -16,7 +15,7 @@ import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
public abstract class MusicCommand extends AbstractCommand { public abstract class AbstractMusicCommand extends AbstractCommand {
@Override @Override
public boolean needAdmin() { public boolean needAdmin() {
@@ -64,9 +63,8 @@ public abstract class MusicCommand extends AbstractCommand {
@Override @Override
public boolean canBeExecutedFrom(MessageChannel channel) { public boolean canBeExecutedFrom(MessageChannel channel) {
if (channel instanceof GuildMessageChannel guildMessageChannel) { if (channel instanceof GuildMessageChannel guildMessageChannel) {
if (channel.getType() != ChannelType.TEXT) { if (channel.getType() != ChannelType.TEXT)
return false; return false;
}
String channelDb = GuildService.get(guildMessageChannel.getGuild()).getGuildSettingService().getSetting(GSettings.MUSIC_CHANNEL); String channelDb = GuildService.get(guildMessageChannel.getGuild()).getGuildSettingService().getSetting(GSettings.MUSIC_CHANNEL);
if (channelDb != null && !channelDb.isEmpty()) { if (channelDb != null && !channelDb.isEmpty()) {
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.command.impl; package me.bobert.netneutrality.core.command;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
@@ -7,12 +7,11 @@ package me.bobert.netneutrality.core.command.impl;
*/ */
import me.bobert.netneutrality.context.command.UserMenuContext; import me.bobert.netneutrality.context.command.UserMenuContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
public abstract class UserMenuCommand extends AbstractCommand { public abstract class AbstractUserMenuCommand extends AbstractCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return null;
@@ -5,6 +5,7 @@ package me.bobert.netneutrality.core.command;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
@@ -5,178 +5,35 @@ package me.bobert.netneutrality.core.command;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import net.dv8tion.jda.api.entities.channel.ChannelType; import me.bobert.netneutrality.core.command.options.CommandOptions;
import net.dv8tion.jda.api.interactions.commands.build.OptionData; import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
import net.dv8tion.jda.internal.interactions.CommandDataImpl;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
public class CommandContainer { public class CommandContainer {
public static final CommandContainer NULL_COMMAND = new CommandContainer(null); public static final CommandContainer NULL_COMMAND = new CommandContainer(null);
private final Command command; private final Command command;
private CommandDataImpl commandData; private CommandOptions commandOptions;
private List<OptionData> options;
private List<SubcommandData> subcommands;
private List<SubcommandGroupData> subcommandGroups;
public CommandContainer(Command command) { public CommandContainer(Command command) {
this.command = command; this.command = command;
if (command != null) {
CommandOptionsBuilder commandOptionsBuilder = new CommandOptionsBuilder(command.getType(), command);
command.registerCommandOptions(commandOptionsBuilder);
initContainer(commandOptionsBuilder);
}
} }
public Command getCommand() { public Command getCommand() {
return command; return command;
} }
public SlashCommandData getSlashCommandData() { public CommandOptions getCommandOptions() {
return commandData; return commandOptions;
} }
public List<OptionData> getOptions() { public void init() {
return options; initOptions();
} }
public List<SubcommandData> getSubcommands() { private void initOptions() {
return subcommands; if (command != null) {
} CommandOptionsBuilder commandOptionsBuilder = new CommandOptionsBuilder(command.getType(), command);
command.registerCommandOptions(commandOptionsBuilder);
public List<SubcommandGroupData> getSubcommandGroups() { commandOptions = commandOptionsBuilder.build();
return subcommandGroups;
}
private void initContainer(CommandOptionsBuilder builder) {
this.commandData = builder.build();
this.options = commandData.getOptions();
this.subcommands = commandData.getSubcommands();
this.subcommandGroups = commandData.getSubcommandGroups();
}
public boolean isEmpty() {
return options.isEmpty() && subcommands.isEmpty() && subcommandGroups.isEmpty();
}
public String printUsage() {
StringBuilder usage = new StringBuilder();
if (!options.isEmpty()) {
usage.append("- Usage: /").append(command.getCommandName());
for (OptionData option : options) {
usage.append(" <").append(option.getName()).append(">");
}
usage.append(printOptions(options, null, false));
}
if (!subcommands.isEmpty()) {
for (SubcommandData subcommand : subcommands) {
usage.append("- Usage: /").append(command.getCommandName()).append(" ").append(subcommand.getName());
for (OptionData option : subcommand.getOptions()) {
usage.append(" <").append(option.getName()).append(">");
}
usage.append(" (").append(subcommand.getDescription()).append(")");
usage.append(printOptions(subcommand.getOptions(), null, false));
usage.append("\n");
}
}
if (!subcommandGroups.isEmpty()) {
for (SubcommandGroupData subcommandGroup : subcommandGroups) {
usage.append("- Subcommand: /").append(command.getCommandName())
.append(" ").append(subcommandGroup.getName())
.append(" (").append(subcommandGroup.getDescription()).append(")")
.append("\n");
for (SubcommandData subcommand : subcommandGroup.getSubcommands()) {
usage.append(" - Usage: /").append(command.getCommandName())
.append(" ").append(subcommandGroup.getName())
.append(" ").append(subcommand.getName());
for (OptionData option : subcommand.getOptions()) {
usage.append(" <").append(option.getName()).append(">");
}
usage.append(printOptions(subcommand.getOptions(), subcommand.getDescription(), true));
usage.append("\n");
} }
} }
} }
if (!isEmpty()) {
usage.append("\n\n-# *: required argument");
}
return usage.toString();
}
private String printOptions(List<OptionData> options, String description, boolean group) {
StringBuilder usage = new StringBuilder();
if (description != null) {
usage.append(" (").append(description).append(")");
}
for (OptionData option : options) {
usage.append("\n");
if (group) {
usage.append(" ");
}
usage.append(" - ");
if (option.isRequired()) {
usage.append("*");
}
usage.append(option.getName()).append(": ").append(option.getDescription());
if (option.getMinValue() != null) {
usage.append(" [Min value: ").append(option.getMinValue().longValue()).append("]");
}
if (option.getMaxValue() != null) {
usage.append(" [Max value: ").append(option.getMaxValue().longValue()).append("]");
}
if (option.getMinLength() != null) {
usage.append(" [Min length: ").append(option.getMinLength().longValue()).append("]");
}
if (option.getMaxLength() != null) {
usage.append(" [Max length: ").append(option.getMaxLength().longValue()).append("]");
}
List<net.dv8tion.jda.api.interactions.commands.Command.Choice> choices = option.getChoices();
if (!choices.isEmpty()) {
StringJoiner joiner = new StringJoiner(", ");
for (net.dv8tion.jda.api.interactions.commands.Command.Choice choice : choices) {
joiner.add(choice.getAsString());
}
usage.append(" [Available choices: ").append(joiner).append("]");
}
Set<ChannelType> requiredChannelTypes = option.getChannelTypes();
if (!requiredChannelTypes.isEmpty()) {
StringJoiner joiner = new StringJoiner(", ");
for (ChannelType type : requiredChannelTypes) {
joiner.add(type.name());
}
usage.append(" [Required channel types: ").append(joiner).append("]");
}
}
return usage.toString();
}
}
@@ -5,19 +5,16 @@ package me.bobert.netneutrality.core.command;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.utils.ReflectionHelper;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.interactions.InteractionContextType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData; import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction; import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
import java.util.ArrayList; import java.lang.reflect.Modifier;
import java.util.List; import java.util.*;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
public class CommandManager { public class CommandManager {
@@ -33,32 +30,45 @@ public class CommandManager {
logger.info("Loaded {} commands!", commands.size()); logger.info("Loaded {} commands!", commands.size());
} }
public static Command getCommand(String commandString) {
return getCommandContainer(commandString).getCommand();
}
public static CommandContainer getCommandContainer(String commandString) { public static CommandContainer getCommandContainer(String commandString) {
return commands.getOrDefault(commandString, CommandContainer.NULL_COMMAND); return commands.getOrDefault(commandString, CommandContainer.NULL_COMMAND);
} }
public static void initCommands() { public static void initCommands() {
ReflectionHelper.loadCommands(Command.class, command -> { Set<Class<? extends Command>> classes = new Reflections("me.bobert.netneutrality.command").getSubTypesOf(Command.class);
for (Class<? extends Command> s : classes) {
try {
if (Modifier.isAbstract(s.getModifiers())) {
continue;
}
Command command = s.getConstructor().newInstance();
if (!command.isEnabled()) { if (!command.isEnabled()) {
logger.info("Skipping command {} as it is disabled", command.getClass().getName()); continue;
return;
} }
if (command.getCommandName() == null) { if (command.getCommandName() == null) {
logger.error("Failed to load command {}: Command name is null", command.getClass().getName()); throw new IllegalArgumentException("Command name is null");
return;
} }
if (command.getDescription() == null && command.getType() == Command.Type.SLASH) { if (command.getDescription() == null && command.getType() == Command.Type.SLASH) {
logger.error("Failed to load command {}: Command description is null", command.getClass().getName()); throw new IllegalArgumentException("Command description is null");
return;
} }
CommandContainer container = new CommandContainer(command); CommandContainer container = new CommandContainer(command);
container.init();
if (!commands.containsKey(command.getCommandName())) { if (!commands.containsKey(command.getCommandName())) {
commands.put(command.getCommandName(), container); commands.put(command.getCommandName(), container);
} }
}); } catch (Exception e) {
logger.error("Failed to load command {}: {}", s.getSimpleName(), e.getMessage());
}
}
} }
public static void registerSlashCommands(JDA jda) { public static void registerSlashCommands(JDA jda) {
@@ -73,9 +83,9 @@ public class CommandManager {
logger.debug("Registering slash command: {}", command.getCommandName()); logger.debug("Registering slash command: {}", command.getCommandName());
try { try {
SlashCommandData data = container.getSlashCommandData(); SlashCommandData data = container.getCommandOptions().getSlashCommandData();
data.setDefaultPermissions(command.getSlashCommandPermission()); data.setDefaultPermissions(command.getSlashCommandPermission());
data.setContexts(command.isGuildOnly() ? Set.of(InteractionContextType.GUILD) : Set.of(InteractionContextType.GUILD, InteractionContextType.BOT_DM)); data.setGuildOnly(command.isGuildOnly());
slashCommands.add(data); slashCommands.add(data);
} catch (Exception e) { } catch (Exception e) {
@@ -8,12 +8,15 @@ package me.bobert.netneutrality.core.command;
import io.sentry.Sentry; import io.sentry.Sentry;
import me.bobert.netneutrality.context.command.SlashCommandContext; import me.bobert.netneutrality.context.command.SlashCommandContext;
import me.bobert.netneutrality.core.LogHandler; import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.command.options.CommandOptions;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.InteractionContext; import me.bobert.netneutrality.core.context.InteractionContext;
import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.exceptions.CommandExecutionException; import me.bobert.netneutrality.exceptions.CommandExecutionException;
import me.bobert.netneutrality.exceptions.GracefulContextError; import me.bobert.netneutrality.exceptions.GracefulContextError;
import me.bobert.netneutrality.utils.UserFunction; import me.bobert.netneutrality.utils.UserFunction;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
@@ -26,10 +29,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import static java.util.function.Predicate.not; import static java.util.function.Predicate.not;
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.ERROR; import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.*;
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.NOACTION;
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.TASK;
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.WARN;
public class CommandProcessor { public class CommandProcessor {
@@ -64,7 +64,7 @@ public class CommandProcessor {
} }
try { try {
logger.debug("Context ID: {}", context.getId()); logger.info("Context ID: {}", context.getId());
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId()); logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId());
configureSentry(command); configureSentry(command);
@@ -73,7 +73,7 @@ public class CommandProcessor {
return; return;
} }
if (command.getType() == Command.Type.SLASH && !checkUsage(container)) { if (command.getType() == Command.Type.SLASH && !checkUsage(container.getCommandOptions())) {
return; return;
} }
@@ -123,10 +123,10 @@ public class CommandProcessor {
} }
} }
private boolean checkUsage(CommandContainer container) { private boolean checkUsage(CommandOptions commandOptions) {
List<SubcommandGroupData> groups = container.getSubcommandGroups(); List<SubcommandGroupData> groups = commandOptions.getSubcommandGroups();
List<SubcommandData> subcommands = container.getSubcommands(); List<SubcommandData> subcommands = commandOptions.getSubcommands();
List<OptionData> options = container.getOptions(); List<OptionData> options = commandOptions.getOptions();
String[] possibleCommands = Arrays.stream(context.getFullCommand().split(" ")).skip(1).toArray(String[]::new); String[] possibleCommands = Arrays.stream(context.getFullCommand().split(" ")).skip(1).toArray(String[]::new);
if (possibleCommands.length == 0) { if (possibleCommands.length == 0) {
if (!groups.isEmpty()) { if (!groups.isEmpty()) {
@@ -203,6 +203,22 @@ public class CommandProcessor {
return false; return false;
} }
// check if last option is a string, if it is then combine all remaining strings into one
// ["@user", "you", "are", "banned"] becomes ["@user", "you are banned"]
if (!options.isEmpty() && arguments.size() > 1) {
OptionData last = options.get(options.size() - 1);
if (last.getType() == OptionType.STRING) {
List<String> lastArgs = arguments.stream().skip(options.size() - 1).toList();
arguments.removeAll(lastArgs);
arguments.add(String.join(" ", lastArgs));
}
}
if (arguments.size() > required + optional) {
context.reply(invalidUsage("too many arguments"));
return false;
}
context.setCurrentCommandOptions(arguments.toArray(String[]::new), options); context.setCurrentCommandOptions(arguments.toArray(String[]::new), options);
return true; return true;
} }
@@ -5,11 +5,7 @@ package me.bobert.netneutrality.core.command.options;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import net.dv8tion.jda.api.entities.IMentionable; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.OptionType;
@@ -33,8 +29,10 @@ public interface CommandOption {
* <br>This is represented as an {@link Message.Attachment#isEphemeral() ephemeral} attachment which will only be hosted for up to 2 weeks. * <br>This is represented as an {@link Message.Attachment#isEphemeral() ephemeral} attachment which will only be hosted for up to 2 weeks.
* If you want a permanent reference, you must download it. * If you want a permanent reference, you must download it.
* *
* @throws IllegalStateException
* If this option {@link #getType() type} is not {@link OptionType#ATTACHMENT}
*
* @return {@link net.dv8tion.jda.api.entities.Message.Attachment Attachment} * @return {@link net.dv8tion.jda.api.entities.Message.Attachment Attachment}
* @throws IllegalStateException If this option {@link #getType() type} is not {@link OptionType#ATTACHMENT}
*/ */
Message.Attachment getAsAttachment(); Message.Attachment getAsAttachment();
@@ -50,8 +48,10 @@ public interface CommandOption {
/** /**
* The boolean value. * The boolean value.
* *
* @throws IllegalStateException
* If this option is not of type {@link OptionType#BOOLEAN BOOLEAN}
*
* @return The boolean value * @return The boolean value
* @throws IllegalStateException If this option is not of type {@link OptionType#BOOLEAN BOOLEAN}
*/ */
boolean getAsBoolean(); boolean getAsBoolean();
@@ -59,9 +59,12 @@ public interface CommandOption {
* The long value for this option. * The long value for this option.
* <br>This will be the ID of any resolved entity such as {@link Role} or {@link Member}. * <br>This will be the ID of any resolved entity such as {@link Role} or {@link Member}.
* *
* @throws IllegalStateException
* If this option {@link #getType() type} cannot be converted to a long
* @throws NumberFormatException
* If this option is of type {@link OptionType#STRING STRING} and could not be parsed to a valid long value
*
* @return The long value * @return The long value
* @throws IllegalStateException If this option {@link #getType() type} cannot be converted to a long
* @throws NumberFormatException If this option is of type {@link OptionType#STRING STRING} and could not be parsed to a valid long value
*/ */
long getAsLong(); long getAsLong();
@@ -71,27 +74,36 @@ public interface CommandOption {
* *
* <p><b>It is highly recommended to assert int values by using {@link OptionData#setRequiredRange(long, long)}</b> * <p><b>It is highly recommended to assert int values by using {@link OptionData#setRequiredRange(long, long)}</b>
* *
* @throws IllegalStateException
* If this option {@link #getType() type} cannot be converted to a long
* @throws NumberFormatException
* If this option is of type {@link OptionType#STRING STRING} and could not be parsed to a valid long value
* @throws ArithmeticException
* If the provided integer value cannot fit into a 32bit signed int
*
* @return The int value * @return The int value
* @throws IllegalStateException If this option {@link #getType() type} cannot be converted to a long
* @throws NumberFormatException If this option is of type {@link OptionType#STRING STRING} and could not be parsed to a valid long value
* @throws ArithmeticException If the provided integer value cannot fit into a 32bit signed int
*/ */
int getAsInt(); int getAsInt();
/** /**
* The double value for this option. * The double value for this option.
* *
* @throws IllegalStateException
* If this option {@link #getType() type} cannot be converted to a double
* @throws NumberFormatException
* If this option is of type {@link OptionType#STRING STRING} and could not be parsed to a valid double value
*
* @return The double value * @return The double value
* @throws IllegalStateException If this option {@link #getType() type} cannot be converted to a double
* @throws NumberFormatException If this option is of type {@link OptionType#STRING STRING} and could not be parsed to a valid double value
*/ */
double getAsDouble(); double getAsDouble();
/** /**
* The resolved {@link IMentionable} instance for this option value. * The resolved {@link IMentionable} instance for this option value.
* *
* @throws IllegalStateException
* If the mentioned entity is not resolvable
*
* @return The resolved {@link IMentionable} * @return The resolved {@link IMentionable}
* @throws IllegalStateException If the mentioned entity is not resolvable
*/ */
IMentionable getAsMentionable(); IMentionable getAsMentionable();
@@ -99,34 +111,42 @@ public interface CommandOption {
* The resolved {@link Member} for this option value. * The resolved {@link Member} for this option value.
* <br>Note that {@link OptionType#USER OptionType.USER} can also accept users that are not members of a guild, in which case this will be null! * <br>Note that {@link OptionType#USER OptionType.USER} can also accept users that are not members of a guild, in which case this will be null!
* *
* @throws IllegalStateException
* If this option is not of type {@link OptionType#USER USER} or {@link OptionType#MENTIONABLE MENTIONABLE}
*
* @return The resolved {@link Member}, or null * @return The resolved {@link Member}, or null
* @throws IllegalStateException If this option is not of type {@link OptionType#USER USER} or {@link OptionType#MENTIONABLE MENTIONABLE}
*/ */
Member getAsMember(); Member getAsMember();
/** /**
* The resolved {@link User} for this option value. * The resolved {@link User} for this option value.
* *
* @return The resolved {@link User} * @throws IllegalStateException
* @throws IllegalStateException If this option is not of type {@link OptionType#USER USER} or * If this option is not of type {@link OptionType#USER USER} or
* {@link OptionType#MENTIONABLE MENTIONABLE} without a resolved user * {@link OptionType#MENTIONABLE MENTIONABLE} without a resolved user
*
* @return The resolved {@link User}
*/ */
User getAsUser(); User getAsUser();
/** /**
* The resolved {@link Role} for this option value. * The resolved {@link Role} for this option value.
* *
* @return The resolved {@link Role} * @throws IllegalStateException
* @throws IllegalStateException If this option is not of type {@link OptionType#ROLE ROLE} or * If this option is not of type {@link OptionType#ROLE ROLE} or
* {@link OptionType#MENTIONABLE MENTIONABLE} without a resolved role * {@link OptionType#MENTIONABLE MENTIONABLE} without a resolved role
*
* @return The resolved {@link Role}
*/ */
Role getAsRole(); Role getAsRole();
/** /**
* The {@link ChannelType} for the resolved channel. * The {@link ChannelType} for the resolved channel.
* *
* @throws IllegalStateException
* If this option is not of type {@link OptionType#CHANNEL CHANNEL}
*
* @return The {@link ChannelType} * @return The {@link ChannelType}
* @throws IllegalStateException If this option is not of type {@link OptionType#CHANNEL CHANNEL}
*/ */
ChannelType getChannelType(); ChannelType getChannelType();
@@ -134,9 +154,11 @@ public interface CommandOption {
* The resolved {@link net.dv8tion.jda.api.entities.channel.middleman.GuildChannel} for this option value. * The resolved {@link net.dv8tion.jda.api.entities.channel.middleman.GuildChannel} for this option value.
* <br>Note that {@link OptionType#CHANNEL OptionType.CHANNEL} can accept channels of any type! * <br>Note that {@link OptionType#CHANNEL OptionType.CHANNEL} can accept channels of any type!
* *
* @return The resolved {@link net.dv8tion.jda.api.entities.channel.middleman.GuildChannel} * @throws IllegalStateException
* @throws IllegalStateException If this option is not of type {@link OptionType#CHANNEL CHANNEL} * If this option is not of type {@link OptionType#CHANNEL CHANNEL}
* or could not be resolved for unexpected reasons * or could not be resolved for unexpected reasons
*
* @return The resolved {@link net.dv8tion.jda.api.entities.channel.middleman.GuildChannel}
*/ */
GuildChannelUnion getAsChannel(); GuildChannelUnion getAsChannel();
} }
@@ -0,0 +1,170 @@
package me.bobert.netneutrality.core.command.options;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.command.Command;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
import net.dv8tion.jda.internal.interactions.CommandDataImpl;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
public class CommandOptions {
private final Command command;
private final CommandDataImpl commandData;
private final List<OptionData> options;
private final List<SubcommandData> subcommands;
private final List<SubcommandGroupData> subcommandGroups;
public CommandOptions(Command command, CommandDataImpl commandData) {
this.command = command;
this.commandData = commandData;
this.options = commandData.getOptions();
this.subcommands = commandData.getSubcommands();
this.subcommandGroups = commandData.getSubcommandGroups();
}
public Command getCommand() {
return command;
}
public SlashCommandData getSlashCommandData() {
return commandData;
}
public List<OptionData> getOptions() {
return options;
}
public List<SubcommandData> getSubcommands() {
return subcommands;
}
public List<SubcommandGroupData> getSubcommandGroups() {
return subcommandGroups;
}
public boolean isEmpty() {
return options.isEmpty() && subcommands.isEmpty() && subcommandGroups.isEmpty();
}
public String printUsage() {
StringBuilder usage = new StringBuilder();
if (!options.isEmpty()) {
usage.append("- Usage: /").append(command.getCommandName());
for (OptionData option : options) {
usage.append(" <").append(option.getName()).append(">");
}
usage.append(printOptions(options, null, false));
}
if (!subcommands.isEmpty()) {
for (SubcommandData subcommand : subcommands) {
usage.append("- Usage: /").append(command.getCommandName()).append(" ").append(subcommand.getName());
for (OptionData option : subcommand.getOptions()) {
usage.append(" <").append(option.getName()).append(">");
}
usage.append(" (").append(subcommand.getDescription()).append(")");
usage.append(printOptions(subcommand.getOptions(), null, false));
usage.append("\n");
}
}
if (!subcommandGroups.isEmpty()) {
for (SubcommandGroupData subcommandGroup : subcommandGroups) {
usage.append("- Subcommand: /").append(command.getCommandName())
.append(" ").append(subcommandGroup.getName())
.append(" (").append(subcommandGroup.getDescription()).append(")")
.append("\n");
for (SubcommandData subcommand : subcommandGroup.getSubcommands()) {
usage.append(" - Usage: /").append(command.getCommandName())
.append(" ").append(subcommandGroup.getName())
.append(" ").append(subcommand.getName());
for (OptionData option : subcommand.getOptions()) {
usage.append(" <").append(option.getName()).append(">");
}
usage.append(printOptions(subcommand.getOptions(), subcommand.getDescription(), true));
usage.append("\n");
}
}
}
if (!isEmpty()) {
usage.append("\n\n-# *: required argument");
}
return usage.toString();
}
private String printOptions(List<OptionData> options, String description, boolean group) {
StringBuilder usage = new StringBuilder();
if (description != null) {
usage.append(" (").append(description).append(")");
}
for (OptionData option : options) {
usage.append("\n");
if (group) usage.append(" ");
usage.append(" - ");
if (option.isRequired()) {
usage.append("*");
}
usage.append(option.getName()).append(": ").append(option.getDescription());
if (option.getMinValue() != null) {
usage.append(" [Min value: ").append(option.getMinValue().longValue()).append("]");
}
if (option.getMaxValue() != null) {
usage.append(" [Max value: ").append(option.getMaxValue().longValue()).append("]");
}
if (option.getMinLength() != null) {
usage.append(" [Min length: ").append(option.getMinLength().longValue()).append("]");
}
if (option.getMaxLength() != null) {
usage.append(" [Max length: ").append(option.getMaxLength().longValue()).append("]");
}
List<net.dv8tion.jda.api.interactions.commands.Command.Choice> choices = option.getChoices();
if (!choices.isEmpty()) {
StringJoiner joiner = new StringJoiner(", ");
for (net.dv8tion.jda.api.interactions.commands.Command.Choice choice : choices) {
joiner.add(choice.getAsString());
}
usage.append(" [Available choices: ").append(joiner).append("]");
}
Set<ChannelType> requiredChannelTypes = option.getChannelTypes();
if (!requiredChannelTypes.isEmpty()) {
StringJoiner joiner = new StringJoiner(", ");
for (ChannelType type : requiredChannelTypes) {
joiner.add(type.name());
}
usage.append(" [Required channel types: ").append(joiner).append("]");
}
}
return usage.toString();
}
}
@@ -1,35 +1,30 @@
package me.bobert.netneutrality.core.command; package me.bobert.netneutrality.core.command.options;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.command.Command;
import net.dv8tion.jda.api.interactions.DiscordLocale; import net.dv8tion.jda.api.interactions.DiscordLocale;
import net.dv8tion.jda.api.interactions.IntegrationType;
import net.dv8tion.jda.api.interactions.InteractionContextType;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.*;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
import net.dv8tion.jda.api.interactions.commands.localization.LocalizationFunction; import net.dv8tion.jda.api.interactions.commands.localization.LocalizationFunction;
import net.dv8tion.jda.api.interactions.commands.localization.LocalizationMap; import net.dv8tion.jda.api.interactions.commands.localization.LocalizationMap;
import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.interactions.CommandDataImpl; import net.dv8tion.jda.internal.interactions.CommandDataImpl;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
public class CommandOptionsBuilder implements SlashCommandData { public class CommandOptionsBuilder implements SlashCommandData {
private final Command command;
private CommandDataImpl commandData; private CommandDataImpl commandData;
public CommandOptionsBuilder(Command.Type type, Command command) { public CommandOptionsBuilder(Command.Type type, Command command) {
this.command = command;
if (type == Command.Type.SLASH) { if (type == Command.Type.SLASH) {
commandData = (CommandDataImpl) Commands.slash(command.getCommandName(), command.getDescription()); commandData = (CommandDataImpl) Commands.slash(command.getCommandName(), command.getDescription());
@@ -44,8 +39,8 @@ public class CommandOptionsBuilder implements SlashCommandData {
} }
} }
public CommandDataImpl build() { public CommandOptions build() {
return commandData; return new CommandOptions(command, commandData);
} }
@Override @Override
@@ -74,13 +69,8 @@ public class CommandOptionsBuilder implements SlashCommandData {
} }
@Override @Override
public @Nonnull SlashCommandData setContexts(@Nonnull Collection<InteractionContextType> contexts) { public @Nonnull SlashCommandData setGuildOnly(boolean guildOnly) {
return commandData.setContexts(contexts); return commandData.setGuildOnly(guildOnly);
}
@Override
public @Nonnull SlashCommandData setIntegrationTypes(@Nonnull Collection<IntegrationType> integrationTypes) {
return commandData.setIntegrationTypes(integrationTypes);
} }
@Override @Override
@@ -109,13 +99,8 @@ public class CommandOptionsBuilder implements SlashCommandData {
} }
@Override @Override
public @Nonnull Set<InteractionContextType> getContexts() { public boolean isGuildOnly() {
return commandData.getContexts(); return commandData.isGuildOnly();
}
@Override
public @Nonnull Set<IntegrationType> getIntegrationTypes() {
return commandData.getIntegrationTypes();
} }
@Override @Override
@@ -5,11 +5,7 @@ package me.bobert.netneutrality.core.command.options;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import net.dv8tion.jda.api.entities.IMentionable; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.OptionType;
@@ -120,10 +116,6 @@ public class MessageCommandOption implements CommandOption {
@Override @Override
public User getAsUser() { public User getAsUser() {
if (value instanceof Member) {
return ((Member) value).getUser();
}
if (value instanceof User) { if (value instanceof User) {
return (User) value; return (User) value;
} }
@@ -5,11 +5,7 @@ package me.bobert.netneutrality.core.command.options;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import net.dv8tion.jda.api.entities.IMentionable; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion; import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionMapping;
@@ -6,6 +6,7 @@ package me.bobert.netneutrality.core.context;
*/ */
import io.sentry.Sentry; import io.sentry.Sentry;
import me.bobert.netneutrality.context.command.SlashCommandContext;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
@@ -17,12 +17,7 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -75,20 +70,16 @@ public abstract class CommandContext extends AbstractContext {
public <T> T getOption(String name, Supplier<? extends T> fallback, Function<? super CommandOption, ? extends T> resolver) { public <T> T getOption(String name, Supplier<? extends T> fallback, Function<? super CommandOption, ? extends T> resolver) {
Objects.requireNonNull(resolver, "resolver cannot be null"); Objects.requireNonNull(resolver, "resolver cannot be null");
CommandOption mapping = getOption(name); CommandOption mapping = getOption(name);
if (mapping != null) { if (mapping != null)
return resolver.apply(mapping); return resolver.apply(mapping);
}
return fallback == null ? null : fallback.get(); return fallback == null ? null : fallback.get();
} }
public <T> T getOption(String name, T fallback, Function<? super CommandOption, ? extends T> resolver) { public <T> T getOption(String name, T fallback, Function<? super CommandOption, ? extends T> resolver) {
Objects.requireNonNull(resolver, "resolver cannot be null"); Objects.requireNonNull(resolver, "resolver cannot be null");
CommandOption mapping = getOption(name); CommandOption mapping = getOption(name);
if (mapping != null) { if (mapping != null)
return resolver.apply(mapping); return resolver.apply(mapping);
}
return fallback; return fallback;
} }
@@ -141,4 +132,7 @@ public abstract class CommandContext extends AbstractContext {
public abstract String parseCommandName(); public abstract String parseCommandName();
public abstract String getFullCommand(); public abstract String getFullCommand();
} }
@@ -8,17 +8,13 @@ package me.bobert.netneutrality.core.context;
import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.context.AsyncContext;
import me.bobert.netneutrality.utils.ContextUtils; import me.bobert.netneutrality.utils.ContextUtils;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.components.MessageTopLevelComponent; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.Event; import net.dv8tion.jda.api.events.Event;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData; import net.dv8tion.jda.api.utils.messages.MessageEditData;
@@ -52,7 +48,7 @@ public interface Context {
void reply(Modal modal); void reply(Modal modal);
void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer); void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer);
void defer(boolean ephemeral, Consumer<AsyncContext> consumer); void defer(boolean ephemeral, Consumer<AsyncContext> consumer);
@@ -70,7 +66,7 @@ public interface Context {
reply(messageCreateData, false, List.of(), asyncMessage -> {}); reply(messageCreateData, false, List.of(), asyncMessage -> {});
} }
default void reply(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components) { default void reply(MessageCreateData messageCreateData, List<LayoutComponent> components) {
reply(messageCreateData, false, components, asyncMessage -> {}); reply(messageCreateData, false, components, asyncMessage -> {});
} }
@@ -94,7 +90,7 @@ public interface Context {
reply(MessageCreateData.fromEmbeds(embeds)); reply(MessageCreateData.fromEmbeds(embeds));
} }
default void edit(MessageEditData messageEditData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { default void edit(MessageEditData messageEditData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
if (isAcknowledged() && getInteractionHook() != null) { if (isAcknowledged() && getInteractionHook() != null) {
getInteractionHook().editOriginal(messageEditData).setComponents(components).queue(message1 -> consumer.accept(new AsyncContext(message1))); getInteractionHook().editOriginal(messageEditData).setComponents(components).queue(message1 -> consumer.accept(new AsyncContext(message1)));
return; return;
@@ -103,7 +99,7 @@ public interface Context {
reply(MessageCreateData.fromEditData(messageEditData), ephemeral, components, consumer); reply(MessageCreateData.fromEditData(messageEditData), ephemeral, components, consumer);
} }
default void edit(MessageEditData messageEditData, List<MessageTopLevelComponent> components) { default void edit(MessageEditData messageEditData, List<LayoutComponent> components) {
edit(messageEditData, false, components, asyncMessage -> {}); edit(messageEditData, false, components, asyncMessage -> {});
} }
@@ -119,11 +115,11 @@ public interface Context {
edit(List.of(embed), List.of()); edit(List.of(embed), List.of());
} }
default void edit(List<MessageEmbed> embeds, List<MessageTopLevelComponent> components) { default void edit(List<MessageEmbed> embeds, List<LayoutComponent> components) {
edit(MessageEditData.fromEmbeds(embeds), components); edit(MessageEditData.fromEmbeds(embeds), components);
} }
default void edit(MessageEmbed embed, List<MessageTopLevelComponent> components) { default void edit(MessageEmbed embed, List<LayoutComponent> components) {
edit(List.of(embed), components); edit(List.of(embed), components);
} }
@@ -139,7 +135,7 @@ public interface Context {
defer(false, consumer); defer(false, consumer);
} }
default void replyEphemeral(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components) { default void replyEphemeral(MessageCreateData messageCreateData, List<LayoutComponent> components) {
reply(messageCreateData, true, components, asyncMessage -> {}); reply(messageCreateData, true, components, asyncMessage -> {});
} }
@@ -151,11 +147,7 @@ public interface Context {
replyEphemeralWithLimit(message); replyEphemeralWithLimit(message);
} }
default void replyEphemeral(String message, MessageTopLevelComponent component) { default void replyEphemeral(String message, List<LayoutComponent> components) {
replyEphemeral(message, List.of(component));
}
default void replyEphemeral(String message, List<MessageTopLevelComponent> components) {
replyEphemeral(MessageCreateData.fromContent(message), components); replyEphemeral(MessageCreateData.fromContent(message), components);
} }
@@ -163,7 +155,7 @@ public interface Context {
replyEphemeral(MessageCreateData.fromEmbeds(embeds)); replyEphemeral(MessageCreateData.fromEmbeds(embeds));
} }
default void sendMessageInPrivate(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { default void sendMessageInPrivate(MessageCreateData messageCreateData, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
User user = getDiscordUser(); User user = getDiscordUser();
if (user != null) { if (user != null) {
user.openPrivateChannel() user.openPrivateChannel()
@@ -172,11 +164,11 @@ public interface Context {
} }
} }
default void sendMessageInPrivate(String message, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { default void sendMessageInPrivate(String message, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
sendMessageInPrivate(MessageCreateData.fromContent(message), components, consumer); sendMessageInPrivate(MessageCreateData.fromContent(message), components, consumer);
} }
default void sendMessageInPrivate(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components) { default void sendMessageInPrivate(MessageCreateData messageCreateData, List<LayoutComponent> components) {
sendMessageInPrivate(messageCreateData, components, asyncContext -> {}); sendMessageInPrivate(messageCreateData, components, asyncContext -> {});
} }
@@ -6,12 +6,12 @@ package me.bobert.netneutrality.core.context;
*/ */
import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.context.AsyncContext;
import net.dv8tion.jda.api.components.MessageTopLevelComponent;
import net.dv8tion.jda.api.interactions.Interaction; import net.dv8tion.jda.api.interactions.Interaction;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.callbacks.IModalCallback; import net.dv8tion.jda.api.interactions.callbacks.IModalCallback;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.List; import java.util.List;
@@ -58,7 +58,7 @@ public interface InteractionContext extends Context {
} }
@Override @Override
default void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { default void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
if (isAcknowledged()) { if (isAcknowledged()) {
getInteractionHook().sendMessage(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message))); getInteractionHook().sendMessage(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
return; return;
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.core.context;
*/ */
import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.context.AsyncContext;
import net.dv8tion.jda.api.components.MessageTopLevelComponent;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.List; import java.util.List;
@@ -32,7 +32,7 @@ public interface MessageContext extends NoMessageContext {
} }
@Override @Override
default void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) { default void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
if (ephemeral && getType() != ChannelType.PRIVATE) { if (ephemeral && getType() != ChannelType.PRIVATE) {
sendMessageInPrivate(messageCreateData, components); sendMessageInPrivate(messageCreateData, components);
return; return;
@@ -9,7 +9,7 @@ package me.bobert.netneutrality.core.context;
import me.bobert.netneutrality.context.AsyncContext; import me.bobert.netneutrality.context.AsyncContext;
import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.interactions.modals.Modal;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -9,7 +9,6 @@ import me.bobert.netneutrality.utils.Settings;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.output.MigrateResult;
public class DatabaseMigration { public class DatabaseMigration {
private final static Logger logger = LogManager.getLogger(DatabaseMigration.class); private final static Logger logger = LogManager.getLogger(DatabaseMigration.class);
@@ -19,17 +18,14 @@ public class DatabaseMigration {
String dbUser = Settings.getInstance().get("MYSQL_USER", "root"); String dbUser = Settings.getInstance().get("MYSQL_USER", "root");
String dbPass = Settings.getInstance().get("MYSQL_PASSWORD", ""); String dbPass = Settings.getInstance().get("MYSQL_PASSWORD", "");
String dbHost = Settings.getInstance().get("MYSQL_HOST", "localhost"); String dbHost = Settings.getInstance().get("MYSQL_HOST", "localhost");
String dbPort = Settings.getInstance().get("MYSQL_PORT", "3306");
String dbUrl = String.format("jdbc:mysql://%s:%s/%s", dbHost, dbPort, dbName);
try { try {
logger.info("Running database migrations..");
Flyway flyway = Flyway.configure() Flyway flyway = Flyway.configure()
.dataSource(dbUrl, dbUser, dbPass) .dataSource(String.format("jdbc:mysql://%s:3306/%s", dbHost, dbName), dbUser, dbPass)
.baselineOnMigrate(true)
.load(); .load();
MigrateResult result = flyway.migrate(); flyway.migrate();
logger.info("Database migrations finished successfully! (total: {})", result.migrationsExecuted);
} catch (Exception e) { } catch (Exception e) {
logger.error(e); logger.error(e);
System.exit(1); System.exit(1);
@@ -33,10 +33,9 @@ public class SessionManager {
String dbUser = Settings.getInstance().get("MYSQL_USER", "root"); String dbUser = Settings.getInstance().get("MYSQL_USER", "root");
String dbPass = Settings.getInstance().get("MYSQL_PASSWORD", ""); String dbPass = Settings.getInstance().get("MYSQL_PASSWORD", "");
String dbHost = Settings.getInstance().get("MYSQL_HOST", "localhost"); String dbHost = Settings.getInstance().get("MYSQL_HOST", "localhost");
String dbPort = Settings.getInstance().get("MYSQL_PORT", "3306"); logger.info(String.format("Connecting to database '%s' at %s:3306 with user '%s'", dbName, dbHost, dbUser));
logger.info("Connecting to database '{}' at {}:{} with user '{}'", dbName, dbHost, dbPort, dbUser);
settings.put(Environment.JAKARTA_JDBC_URL, String.format("jdbc:mysql://%s:%s/%s", dbHost, dbPort, dbName)); settings.put(Environment.JAKARTA_JDBC_URL, String.format("jdbc:mysql://%s:3306/%s", dbHost, dbName));
settings.put(Environment.JAKARTA_JDBC_USER, dbUser); settings.put(Environment.JAKARTA_JDBC_USER, dbUser);
settings.put(Environment.JAKARTA_JDBC_PASSWORD, dbPass); settings.put(Environment.JAKARTA_JDBC_PASSWORD, dbPass);
registryBuilder.applySettings(settings); registryBuilder.applySettings(settings);
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.tasks.impl; package me.bobert.netneutrality.core.tasks;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
@@ -7,7 +7,6 @@ package me.bobert.netneutrality.core.tasks.impl;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.BuiltInTask;
public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltInTask { public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltInTask {
@@ -36,4 +35,6 @@ public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltI
executeTask(context); executeTask(context);
return Lifecycle.BUILTIN; return Lifecycle.BUILTIN;
} }
public abstract void executeTask(TaskContext context);
} }
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.tasks.impl; package me.bobert.netneutrality.core.tasks;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
@@ -6,8 +6,6 @@ package me.bobert.netneutrality.core.tasks.impl;
*/ */
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.PersistableTask;
import me.bobert.netneutrality.core.tasks.TaskManager;
import org.json.JSONObject; import org.json.JSONObject;
public abstract class AbstractPersistableTask extends AbstractTask implements PersistableTask { public abstract class AbstractPersistableTask extends AbstractTask implements PersistableTask {
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.tasks.impl; package me.bobert.netneutrality.core.tasks;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
@@ -6,8 +6,6 @@ package me.bobert.netneutrality.core.tasks.impl;
*/ */
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.TaskManager;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.Objects; import java.util.Objects;
@@ -31,12 +29,8 @@ public abstract class AbstractTask implements Task {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) return true;
return true; if (o == null || getClass() != o.getClass()) return false;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractTask that = (AbstractTask) o; AbstractTask that = (AbstractTask) o;
return Objects.equals(getId(), that.getId()); return Objects.equals(getId(), that.getId());
@@ -5,7 +5,7 @@ package me.bobert.netneutrality.core.tasks;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.context.TaskContext; import net.dv8tion.jda.api.interactions.components.buttons.Button;
/** /**
* Represents a task that has a known ID and is loaded at runtime. * Represents a task that has a known ID and is loaded at runtime.
@@ -13,5 +13,13 @@ import me.bobert.netneutrality.core.context.TaskContext;
*/ */
public interface BuiltInTask extends Task { public interface BuiltInTask extends Task {
void executeTask(TaskContext context); static Button createButton(Class<? extends BuiltInTask> clazz) {
BuiltInTask builtInTask = TaskManager.getInstance().findBuiltInTaskByClass(clazz);
if (builtInTask == null) {
throw new IllegalArgumentException("Task class " + clazz.getName() + " was not registered by the task manager.");
}
return builtInTask.createButton();
}
} }
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.tasks.impl; package me.bobert.netneutrality.core.tasks;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
@@ -6,6 +6,8 @@ package me.bobert.netneutrality.core.tasks.impl;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import net.dv8tion.jda.api.interactions.components.buttons.Button;
public abstract class GenericCustomIdTask extends AbstractTask { public abstract class GenericCustomIdTask extends AbstractTask {
private final String customId; private final String customId;
@@ -17,4 +19,9 @@ public abstract class GenericCustomIdTask extends AbstractTask {
public String getId() { public String getId() {
return customId; return customId;
} }
@Override
public Button createButton() {
return null;
}
} }
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.tasks.impl; package me.bobert.netneutrality.core.tasks;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
@@ -6,7 +6,7 @@ package me.bobert.netneutrality.core.tasks;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
public interface StepTask { public interface IStepTask {
/** /**
* Defines the type of input expected for a step. * Defines the type of input expected for a step.
@@ -1,64 +0,0 @@
package me.bobert.netneutrality.core.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import java.util.concurrent.TimeUnit;
public interface ScheduledTask {
class TimeFrame {
private final int interval;
private final TimeUnit unit;
private TimeFrame(int interval, TimeUnit unit) {
this.interval = interval;
this.unit = unit;
}
public static TimeFrame minutely() {
return new TimeFrame(1, TimeUnit.MINUTES);
}
public static TimeFrame minutely(int interval) {
return new TimeFrame(interval, TimeUnit.MINUTES);
}
public static TimeFrame hourly() {
return new TimeFrame(1, TimeUnit.HOURS);
}
public static TimeFrame hourly(int interval) {
return new TimeFrame(interval, TimeUnit.HOURS);
}
public static TimeFrame daily() {
return new TimeFrame(1, TimeUnit.DAYS);
}
public static TimeFrame daily(int interval) {
return new TimeFrame(interval, TimeUnit.DAYS);
}
public static TimeFrame custom(int interval, TimeUnit unit) {
return new TimeFrame(interval, unit);
}
public int getInterval() {
return interval;
}
public TimeUnit getUnit() {
return unit;
}
}
String getId();
TimeFrame getTimeFrame();
void run();
}
@@ -7,9 +7,8 @@ package me.bobert.netneutrality.core.tasks;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.trait.WithButton;
import me.bobert.netneutrality.core.tasks.trait.WithChildren; import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import net.dv8tion.jda.api.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
@@ -21,7 +20,6 @@ public interface Task {
REMOVE_SELF, REMOVE_SELF,
REMOVE_SELF_AND_CHILDREN REMOVE_SELF_AND_CHILDREN
} }
/** /**
* This method should generate a unique ID to differentiate it from other tasks. * This method should generate a unique ID to differentiate it from other tasks.
* <p> * <p>
@@ -46,7 +44,6 @@ public interface Task {
/** /**
* This is only used for debugging purpose. * This is only used for debugging purpose.
*
* @return Name of the task * @return Name of the task
*/ */
String getName(); String getName();
@@ -58,13 +55,14 @@ public interface Task {
/** /**
* When this returns true, the task will get removed by the cleanup thread. * When this returns true, the task will get removed by the cleanup thread.
*
* @return If the task is expired * @return If the task is expired
*/ */
boolean isExpired(); boolean isExpired();
OffsetDateTime getCreationTime(); OffsetDateTime getCreationTime();
Button createButton();
/** /**
* <p>This method is used to reply to the interaction that triggered this task.</p> * <p>This method is used to reply to the interaction that triggered this task.</p>
* <p>If the task is implementing {@link WithChildren}, the child tasks must be instanciated in this method.</p> * <p>If the task is implementing {@link WithChildren}, the child tasks must be instanciated in this method.</p>
@@ -73,20 +71,9 @@ public interface Task {
/** /**
* Executes the task. * Executes the task.
*
* @param context The context for this task. * @param context The context for this task.
*/ */
Lifecycle execute(TaskContext context); Lifecycle execute(TaskContext context);
void insert(CommandContext context); void insert(CommandContext context);
static Button createButton(Class<? extends WithButton> clazz) {
WithButton withButton = TaskManager.getInstance().findButtonTaskByClass(clazz);
if (withButton == null) {
throw new IllegalArgumentException("Task " + clazz.getName() + " was not registered by the task manager.");
}
return withButton.createButton();
}
} }
@@ -11,24 +11,24 @@ import me.bobert.netneutrality.core.context.InteractionContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.trait.Deletable; import me.bobert.netneutrality.core.tasks.trait.Deletable;
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable; import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
import me.bobert.netneutrality.core.tasks.trait.WithButton;
import me.bobert.netneutrality.core.tasks.trait.WithChildren; import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import me.bobert.netneutrality.core.tasks.trait.WithParent; import me.bobert.netneutrality.core.tasks.trait.WithParent;
import me.bobert.netneutrality.core.thread.ScheduledTaskManager; import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.db.service.TaskService; import me.bobert.netneutrality.db.service.TaskService;
import me.bobert.netneutrality.exceptions.GracefulContextError; import me.bobert.netneutrality.exceptions.GracefulContextError;
import me.bobert.netneutrality.exceptions.TaskExecutionException; import me.bobert.netneutrality.exceptions.TaskExecutionException;
import me.bobert.netneutrality.utils.ReflectionHelper; import me.bobert.netneutrality.thread.CleanupTaskThread;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
import java.util.Collection; import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.lang.reflect.Modifier;
import java.util.Map; import java.util.*;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -58,14 +58,13 @@ public class TaskManager {
} }
public void init() { public void init() {
logger.info("Initializing task manager...");
loadBuiltInTasks(); loadBuiltInTasks();
loadScheduledTasks();
taskService.loadTasks(); taskService.loadTasks();
ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new CleanupTaskThread(), 1, 5, TimeUnit.MINUTES);
} }
public TaskService getTaskService() { protected TaskService getTaskService() {
return taskService; return taskService;
} }
@@ -98,7 +97,7 @@ public class TaskManager {
taskService.persistTask(persistableTask); taskService.persistTask(persistableTask);
} }
if (task instanceof StepTask) { if (task instanceof IStepTask) {
pendingTasks.add(task); pendingTasks.add(task);
} else { } else {
tasks.put(id, task); tasks.put(id, task);
@@ -173,7 +172,7 @@ public class TaskManager {
} }
} }
logger.info("Executing task: {}", taskId); logger.debug("Executing task: {}", taskId);
Task.Lifecycle status = task.execute(context); Task.Lifecycle status = task.execute(context);
handleTaskLifecycle(task, status); handleTaskLifecycle(task, status);
@@ -202,7 +201,7 @@ public class TaskManager {
public <E extends Task> Optional<E> findTaskByPartialId(Class<E> clazz, String partialId) { public <E extends Task> Optional<E> findTaskByPartialId(Class<E> clazz, String partialId) {
List<E> tasks = findTasksByPartialId(clazz, partialId); List<E> tasks = findTasksByPartialId(clazz, partialId);
return Optional.ofNullable(tasks.isEmpty() ? null : tasks.getFirst()); return Optional.ofNullable(tasks.isEmpty() ? null : tasks.get(0));
} }
public <E extends Task> List<E> findTasksByPartialId(Class<E> clazz, String partialId) { public <E extends Task> List<E> findTasksByPartialId(Class<E> clazz, String partialId) {
@@ -213,10 +212,10 @@ public class TaskManager {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public WithButton findButtonTaskByClass(Class<? extends WithButton> clazz) { public BuiltInTask findBuiltInTaskByClass(Class<? extends BuiltInTask> clazz) {
return tasks.values().stream() return tasks.values().stream()
.filter(clazz::isInstance) .filter(clazz::isInstance)
.map(task -> (WithButton) task) .map(task -> (BuiltInTask) task)
.findFirst() .findFirst()
.orElse(null); .orElse(null);
} }
@@ -234,27 +233,22 @@ public class TaskManager {
private void loadBuiltInTasks() { private void loadBuiltInTasks() {
logger.info("Loading built-in tasks..."); logger.info("Loading built-in tasks...");
ReflectionHelper.loadTasks(BuiltInTask.class, builtInTask -> { Set<Class<? extends BuiltInTask>> classes = new Reflections("me.bobert.netneutrality.tasks").getSubTypesOf(BuiltInTask.class);
if (builtInTask.isInsertable()) { for (Class<? extends BuiltInTask> s : classes) {
logger.info("Loaded task: {}", builtInTask.getClass().getName()); try {
tasks.put(builtInTask.getId(), builtInTask); if (Modifier.isAbstract(s.getModifiers())) {
} continue;
});
} }
private void loadScheduledTasks() { BuiltInTask task = s.getConstructor().newInstance();
logger.info("Loading scheduled tasks...");
ReflectionHelper.loadTasks(ScheduledTask.class, scheduledTask -> { logger.info("Loaded task: {}", task);
String taskId = scheduledTask.getId(); tasks.put(task.getId(), task);
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
if (taskId == null || taskId.trim().isEmpty()) { | InvocationTargetException | NoSuchMethodException | SecurityException e) {
logger.error("Cannot load scheduled task {}: task ID is null or empty", scheduledTask.getClass().getName()); logger.error("Failed to instantiate built-in task", e);
return; }
} }
ScheduledTaskManager.schedule(scheduledTask);
});
} }
private void deleteInteraction(Task task) { private void deleteInteraction(Task task) {
@@ -273,7 +267,7 @@ public class TaskManager {
} }
private void deleteInteraction(String taskId, Deletable deletable) { private void deleteInteraction(String taskId, Deletable deletable) {
logger.debug("Deleting interaction: {}", taskId); logger.debug("Deleting interaction: {}", deletable);
CommandContext context = deletableTasksContext.get(taskId); CommandContext context = deletableTasksContext.get(taskId);
if (context != null) { if (context != null) {
@@ -5,40 +5,49 @@ package me.bobert.netneutrality.core.tasks.impl;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import me.bobert.netneutrality.core.tasks.GenericRandomIdTask;
import me.bobert.netneutrality.core.tasks.Task; import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.trait.WithButton; import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import net.dv8tion.jda.api.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.List;
public abstract class ButtonTask extends GenericRandomIdTask implements WithButton { public abstract class ButtonTask extends GenericRandomIdTask {
private final ButtonStyle buttonStyle; private final ButtonStyle buttonStyle;
private final String buttonLabel; private final String buttonLabel;
private Emoji emoji;
public ButtonTask(ButtonStyle buttonStyle, String buttonLabel) { public ButtonTask(ButtonStyle buttonStyle, String buttonLabel) {
this.buttonStyle = buttonStyle; this.buttonStyle = buttonStyle;
this.buttonLabel = buttonLabel; this.buttonLabel = buttonLabel;
} }
public Task setEmoji(Emoji emoji) {
this.emoji = emoji;
return this;
}
@Override @Override
public Button createButton() { public Button createButton() {
if (buttonStyle == null || buttonLabel == null) { if (buttonStyle == null || buttonLabel == null) {
return null; return null;
} }
return Button.of(buttonStyle, getId(), buttonLabel, emoji); return Button.of(buttonStyle, getId(), buttonLabel);
} }
@Override @Override
public boolean isExpired() { public boolean isExpired() {
return getCreationTime().plusMinutes(15).isBefore(OffsetDateTime.now()); return getCreationTime().plusMinutes(15).isBefore(OffsetDateTime.now());
} }
public ActionRow createActionRow(List<Task> tasks) {
ActionRow row = ActionRow.of(tasks.stream().map(Task::createButton).toList());
if (this instanceof WithChildren parent) {
parent.addChild(tasks);
}
return row;
}
public ActionRow createActionRow(Task... tasks) {
return createActionRow(List.of(tasks));
}
} }
@@ -6,6 +6,8 @@ package me.bobert.netneutrality.core.tasks.impl;
*/ */
import me.bobert.netneutrality.context.tasks.EmojiTaskContext; import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
import me.bobert.netneutrality.core.tasks.AbstractPersistableTask;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -52,4 +54,9 @@ public abstract class EmojiTask extends AbstractPersistableTask {
public String getId() { public String getId() {
return String.format(EmojiTaskContext.ID, getGuildId(), getMessageId()); return String.format(EmojiTaskContext.ID, getGuildId(), getMessageId());
} }
@Override
public Button createButton() {
return null;
}
} }
@@ -6,11 +6,11 @@ package me.bobert.netneutrality.core.tasks.impl;
*/ */
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.GenericRandomIdTask;
import net.dv8tion.jda.api.interactions.modals.ModalMapping; import net.dv8tion.jda.api.interactions.modals.ModalMapping;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.List; import java.util.List;
import java.util.Optional;
public abstract class ModalTask extends GenericRandomIdTask { public abstract class ModalTask extends GenericRandomIdTask {
@@ -19,12 +19,12 @@ public abstract class ModalTask extends GenericRandomIdTask {
return getCreationTime().plusMinutes(5).isBefore(OffsetDateTime.now()); return getCreationTime().plusMinutes(5).isBefore(OffsetDateTime.now());
} }
public Optional<ModalMapping> getValue(String key, TaskContext context) { public ModalMapping getValue(String key, TaskContext context) {
List<?> mappings = (List<?>) context.getContextData("values"); List<?> mappings = (List<?>) context.getContextData("values");
return mappings.stream() return mappings.stream()
.filter(o -> o instanceof ModalMapping) .filter(o -> o instanceof ModalMapping)
.map(o -> (ModalMapping) o) .map(o -> (ModalMapping) o)
.filter(mapping -> mapping.getCustomId().equals(key)) .filter(mapping -> mapping.getId().equals(key))
.findFirst(); .findFirst().orElse(null);
} }
} }
@@ -9,11 +9,13 @@ package me.bobert.netneutrality.core.tasks.impl;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.MessageContext; import me.bobert.netneutrality.core.context.MessageContext;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.StepTask; import me.bobert.netneutrality.core.tasks.AbstractTask;
import me.bobert.netneutrality.core.tasks.IStepTask;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.fellbaum.jemoji.Emoji; import net.fellbaum.jemoji.Emoji;
import net.fellbaum.jemoji.EmojiManager; import net.fellbaum.jemoji.EmojiManager;
@@ -25,7 +27,7 @@ import java.util.UUID;
* Represents a task that consists of multiple sequential steps. The task starts with an initial message (question) * Represents a task that consists of multiple sequential steps. The task starts with an initial message (question)
* and expects a user response. Each step can lead to another step, forming a workflow. * and expects a user response. Each step can lead to another step, forming a workflow.
*/ */
public abstract class AbstractStepTask extends AbstractTask implements StepTask { public abstract class StepTask extends AbstractTask implements IStepTask {
private final Object stepLock = new Object(); private final Object stepLock = new Object();
private CommandContext commandContext; private CommandContext commandContext;
private Guild guild; private Guild guild;
@@ -108,6 +110,11 @@ public abstract class AbstractStepTask extends AbstractTask implements StepTask
return getCreationTime().plusMinutes(60).isBefore(OffsetDateTime.now()); return getCreationTime().plusMinutes(60).isBefore(OffsetDateTime.now());
} }
@Override
public Button createButton() {
return null;
}
/** /**
* Validates the user input against the expected step type. * Validates the user input against the expected step type.
* *
@@ -1,31 +0,0 @@
package me.bobert.netneutrality.core.tasks.trait;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.tasks.Task;
import net.dv8tion.jda.api.components.actionrow.ActionRow;
import net.dv8tion.jda.api.components.buttons.Button;
import java.util.List;
public interface WithButton {
Button createButton();
default ActionRow createActionRow(List<Task> tasks) {
ActionRow row = ActionRow.of(tasks.stream().filter(task -> task instanceof WithButton).map(task -> (WithButton) task).map(WithButton::createButton).toList());
if (this instanceof WithChildren parent) {
parent.addChild(tasks);
}
return row;
}
default ActionRow createActionRow(Task... tasks) {
return createActionRow(List.of(tasks));
}
}
@@ -7,8 +7,8 @@ package me.bobert.netneutrality.core.thread;
*/ */
import me.bobert.netneutrality.exceptions.ExecutorException; import me.bobert.netneutrality.exceptions.ExecutorException;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@@ -22,7 +22,7 @@ public class BaseThreadFactory implements ThreadFactory {
} }
@Override @Override
public Thread newThread(@Nonnull Runnable r) { public Thread newThread(@NotNull Runnable r) {
Thread thread = new Thread(r); Thread thread = new Thread(r);
thread.setName(name + "-" + threadId.getAndIncrement()); thread.setName(name + "-" + threadId.getAndIncrement());
thread.setUncaughtExceptionHandler(new ExecutorException()); thread.setUncaughtExceptionHandler(new ExecutorException());
@@ -1,64 +0,0 @@
package me.bobert.netneutrality.core.thread;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.tasks.ScheduledTask;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
public class ScheduledTaskManager {
private static final Logger logger = LogManager.getLogger(ScheduledTaskManager.class);
private static final ScheduledExecutorService executorService = ThreadManager.getScheduledExecutor();
private static final Map<String, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
private ScheduledTaskManager() {}
public static void schedule(ScheduledTask task) {
String taskId = task.getId();
if (scheduledTasks.containsKey(taskId)) {
logger.warn("Task {} is already scheduled.", taskId);
return;
}
ScheduledTask.TimeFrame timeFrame = task.getTimeFrame();
Runnable runnable = () -> {
logger.info("Executing scheduled task: {}", taskId);
try {
task.run();
} catch (Exception e) {
logger.error("An exception was thrown while executing scheduled task {}", taskId);
LogHandler.log(e);
}
};
ScheduledFuture<?> future = executorService.scheduleWithFixedDelay(
runnable,
1,
timeFrame.getInterval(),
timeFrame.getUnit()
);
scheduledTasks.put(taskId, future);
logger.info("Successfully scheduled task: {} with interval {} {}",
taskId, timeFrame.getInterval(), timeFrame.getUnit());
}
public static void cancelAll() {
logger.info("Cancelling all {} scheduled tasks", scheduledTasks.size());
scheduledTasks.forEach((id, future) -> future.cancel(false));
scheduledTasks.clear();
}
}
@@ -6,11 +6,7 @@ package me.bobert.netneutrality.core.thread;
*/ */
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadManager { public class ThreadManager {
@@ -42,9 +38,11 @@ public class ThreadManager {
ex.shutdown(); ex.shutdown();
ex.awaitTermination(1, TimeUnit.SECONDS); ex.awaitTermination(1, TimeUnit.SECONDS);
} }
} catch (InterruptedException e) { }
catch (InterruptedException e) {
System.err.println("[Executor] Running tasks interrupted"); System.err.println("[Executor] Running tasks interrupted");
} finally { }
finally {
executors.forEach((name, ex) -> { executors.forEach((name, ex) -> {
if (!ex.isTerminated()) { if (!ex.isTerminated()) {
System.err.println("[Executor: " + name + "] Forcing shutdown of running tasks.."); System.err.println("[Executor: " + name + "] Forcing shutdown of running tasks..");
@@ -6,15 +6,7 @@ package me.bobert.netneutrality.db.entities;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@Entity @Entity
@Table(name = "blacklist") @Table(name = "blacklist")
@@ -6,11 +6,7 @@ package me.bobert.netneutrality.db.entities;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Optional; import java.util.Optional;
@@ -6,18 +6,7 @@ package me.bobert.netneutrality.db.entities;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;
@Entity @Entity
@Table(name = "guild_settings") @Table(name = "guild_settings")
@@ -6,16 +6,7 @@ package me.bobert.netneutrality.db.entities;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.time.Instant; import java.time.Instant;
@@ -6,14 +6,7 @@ package me.bobert.netneutrality.db.entities;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapsId;
import jakarta.persistence.Table;
@Entity @Entity
@Table(name = "logging") @Table(name = "logging")
@@ -46,12 +46,8 @@ public class OLoggingId implements Serializable {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) return true;
return true; if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
}
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) {
return false;
}
OLoggingId entity = (OLoggingId) o; OLoggingId entity = (OLoggingId) o;
return Objects.equals(this.guildId, entity.guildId) && return Objects.equals(this.guildId, entity.guildId) &&
Objects.equals(this.channelId, entity.channelId); Objects.equals(this.channelId, entity.channelId);
@@ -5,14 +5,7 @@ package me.bobert.netneutrality.db.entities;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
@Entity @Entity
@Table(name = "tasks", uniqueConstraints = {@UniqueConstraint(columnNames = {"task_id"})}) @Table(name = "tasks", uniqueConstraints = {@UniqueConstraint(columnNames = {"task_id"})})
@@ -6,11 +6,7 @@ package me.bobert.netneutrality.db.entities;
* https://discord.gg/qUa6zNFZsd * https://discord.gg/qUa6zNFZsd
*/ */
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import java.time.Instant; import java.time.Instant;
@@ -20,6 +20,7 @@ import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.Context; import me.bobert.netneutrality.core.context.Context;
import me.bobert.netneutrality.core.context.TaskContext; import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.TaskManager; import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.db.service.GuildService; import me.bobert.netneutrality.db.service.GuildService;
import me.bobert.netneutrality.utils.Settings; import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
@@ -35,27 +36,25 @@ import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.hooks.EventListener; import net.dv8tion.jda.api.hooks.EventListener;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
public class EventsHandler implements EventListener { public class EventsHandler implements EventListener {
private static final Map<Class<? extends Event>, Function<? extends Event, Context>> listeners = new ConcurrentHashMap<>(); private static final Map<Class<? extends Event>, Function<? extends Event, Context>> listeners = new ConcurrentHashMap<>();
private static final Logger logger = LoggerFactory.getLogger(EventsHandler.class); private static final Logger logger = LoggerFactory.getLogger(EventsHandler.class);
private final ExecutorService executorService = ThreadManager.getExecutor("events-handler");
static { static {
addListener(MessageReceivedEvent.class, event -> { addListener(MessageReceivedEvent.class, event -> {
if (event.getAuthor().isBot()) { if (event.getAuthor().isBot()) return null;
return null; if (event.isWebhookMessage()) return null;
}
if (event.isWebhookMessage()) {
return null;
}
Message message = event.getMessage(); Message message = event.getMessage();
String rawMessage = message.getContentDisplay().trim(); String rawMessage = message.getContentDisplay().trim();
@@ -112,8 +111,8 @@ public class EventsHandler implements EventListener {
} }
@Override @Override
public void onEvent(@Nonnull GenericEvent event) { public void onEvent(@NotNull GenericEvent event) {
handleEvent((Event) event); executorService.submit(() -> handleEvent((Event) event));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -10,23 +10,14 @@ import com.google.gson.Gson;
import io.sentry.Breadcrumb; import io.sentry.Breadcrumb;
import io.sentry.Sentry; import io.sentry.Sentry;
import me.bobert.netneutrality.utils.Settings; import me.bobert.netneutrality.utils.Settings;
import okhttp3.Headers; import okhttp3.*;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class WebModule { public class WebModule {
@@ -13,12 +13,14 @@ import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager; import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import dev.lavalink.youtube.YoutubeAudioSourceManager; import dev.lavalink.youtube.YoutubeAudioSourceManager;
import dev.lavalink.youtube.YoutubeSourceOptions;
import me.bobert.netneutrality.core.command.CommandExecutionResult; import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.CommandContext; import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.db.GSettings; import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.db.service.GuildService; import me.bobert.netneutrality.db.service.GuildService;
import me.bobert.netneutrality.music.source.UdioAudioSourceManager; import me.bobert.netneutrality.music.source.UdioAudioSourceManager;
import me.bobert.netneutrality.thread.MusicManagerCleanupThread;
import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread;
import me.bobert.netneutrality.utils.MusicUtils; import me.bobert.netneutrality.utils.MusicUtils;
import me.bobert.netneutrality.utils.Settings; import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
@@ -29,6 +31,7 @@ import org.apache.logging.log4j.Logger;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class MusicManager { public class MusicManager {
@@ -61,16 +64,7 @@ public class MusicManager {
} }
public static void initPlayerManager() { public static void initPlayerManager() {
logger.debug("Initializing music player sources.."); playerManager.registerSourceManager(new YoutubeAudioSourceManager());
String cipherUrl = Settings.getInstance().get("NETNEUTRALITY_CIPHER_URL");
String cipherPassword = Settings.getInstance().get("NETNEUTRALITY_CIPHER_PASSWORD");
YoutubeSourceOptions options = new YoutubeSourceOptions()
.setAllowSearch(true)
.setAllowDirectVideoIds(true)
.setAllowDirectPlaylistIds(true)
.setRemoteCipher(cipherUrl, cipherPassword, "");
playerManager.registerSourceManager(new YoutubeAudioSourceManager(options, YoutubeAudioSourceManager.DEFAULT_CLIENTS));
playerManager.registerSourceManager(SoundCloudAudioSourceManager.createDefault()); playerManager.registerSourceManager(SoundCloudAudioSourceManager.createDefault());
playerManager.registerSourceManager(new UdioAudioSourceManager()); playerManager.registerSourceManager(new UdioAudioSourceManager());
playerManager.registerSourceManager(new HttpAudioSourceManager()); playerManager.registerSourceManager(new HttpAudioSourceManager());
@@ -78,6 +72,9 @@ public class MusicManager {
String po = Settings.getInstance().get("NETNEUTRALITY_PO_TOKEN"); String po = Settings.getInstance().get("NETNEUTRALITY_PO_TOKEN");
String visitor = Settings.getInstance().get("NETNEUTRALITY_VISITOR_DATA"); String visitor = Settings.getInstance().get("NETNEUTRALITY_VISITOR_DATA");
dev.lavalink.youtube.clients.Web.setPoTokenAndVisitorData(po, visitor); dev.lavalink.youtube.clients.Web.setPoTokenAndVisitorData(po, visitor);
ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new MusicPlayerAutoDisconnectThread(), 15, 15, TimeUnit.MINUTES);
ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new MusicManagerCleanupThread(), 1, 1, TimeUnit.HOURS);
} }
public static void disconnectPlayers() { public static void disconnectPlayers() {
@@ -13,10 +13,7 @@ import net.dv8tion.jda.api.entities.Member;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -37,29 +37,23 @@ public class SourceFinder {
Matcher udioMatcher = udioPattern.matcher(url); Matcher udioMatcher = udioPattern.matcher(url);
Matcher mp3Matcher = mp3Pattern.matcher(url); Matcher mp3Matcher = mp3Pattern.matcher(url);
if (youtubeMatcher.find()) { if (youtubeMatcher.find())
return AudioSourceType.Youtube; return AudioSourceType.Youtube;
}
if (youtubeMusicMatcher.find()) { if (youtubeMusicMatcher.find())
return AudioSourceType.YTMusic; return AudioSourceType.YTMusic;
}
if (soundcloudMatcher.find()) { if (soundcloudMatcher.find())
return AudioSourceType.Soundcloud; return AudioSourceType.Soundcloud;
}
if (udioMatcher.find()) { if (udioMatcher.find())
return AudioSourceType.Udio; return AudioSourceType.Udio;
}
if (mp3Matcher.find()) { if (mp3Matcher.find())
return AudioSourceType.DirectLinkMp3; return AudioSourceType.DirectLinkMp3;
}
if (!(url.startsWith("http://") || url.startsWith("https://"))) { if (!(url.startsWith("http://") || url.startsWith("https://")))
return AudioSourceType.YTSearch; return AudioSourceType.YTSearch;
}
return AudioSourceType.Nothing; return AudioSourceType.Nothing;
} }
@@ -50,12 +50,8 @@ public class TrackInfo {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) return true;
return true; if (o == null || getClass() != o.getClass()) return false;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TrackInfo trackInfo = (TrackInfo) o; TrackInfo trackInfo = (TrackInfo) o;
return track.equals(trackInfo.track); return track.equals(trackInfo.track);
} }
Loaded 100 of 128 files, more files were not shown because too many files have changed in this diff. Show more