Compare commits
215
Commits
3.0
...
4d0d2dcf54
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d0d2dcf54 | ||
|
|
f4b5ad30b3 | ||
|
|
a4345d30da | ||
|
|
d0de70ab3f | ||
|
|
fdf262969a | ||
|
|
fb2835f427 | ||
|
|
54a27883d1 | ||
|
|
196f0be044 | ||
|
|
571713a9ec | ||
|
|
aa587ddf3c | ||
|
|
f8ebe28cb4 | ||
|
|
1544cda9d2 | ||
|
|
703f503191 | ||
|
|
52bb9c026b | ||
|
|
ad77cf630e | ||
|
|
62fc2097c2 | ||
|
|
b96e5b2880 | ||
|
|
b3bd6a45fe | ||
|
|
83e618c6a3 | ||
|
|
078c540327 | ||
|
|
e021a713b2 | ||
|
|
91aaafeb40 | ||
|
|
99457b40fc | ||
|
|
78a359ba09 | ||
|
|
2a4dc09a9e | ||
|
|
c442619531 | ||
|
|
d6fa27923b | ||
|
|
ec8cccf5e5 | ||
|
|
7ca6215f47 | ||
|
|
82f6ea4f4d | ||
|
|
1a0434ffed | ||
|
|
25a5df0743 | ||
|
|
d8bd5ecfdb | ||
|
|
8101a02807 | ||
|
|
d3da68f4d4 | ||
|
|
03a1920ccb | ||
|
|
4d753f3ddf | ||
|
|
71563abaf2 | ||
|
|
2c96f8ce85 | ||
|
|
cecd7baf3f | ||
|
|
e35f739cf9 | ||
|
|
f32968c0b5 | ||
|
|
c13b731cd5 | ||
|
|
4be5be2a47 | ||
|
|
3d0c4a169e | ||
|
|
e8c331fa63 | ||
|
|
303dc594b7 | ||
|
|
3d9c25e637 | ||
|
|
a4eb81d7de | ||
|
|
9c34f3a4f4 | ||
|
|
7ef68cf968 | ||
|
|
0224ba67fe | ||
|
|
1c5afca21c | ||
|
|
8c0062f31f | ||
|
|
2027ae296c | ||
|
|
3c50feb597 | ||
|
|
c3f5807cea | ||
|
|
6e8da65580 | ||
|
|
13b2f61261 | ||
|
|
6271e218cd | ||
|
|
6f4977b466 | ||
|
|
f691fcd2ee | ||
|
|
d03b02c9f9 | ||
|
|
db7be88f13 | ||
|
|
2b3ca9ae18 | ||
|
|
d555a3144d | ||
|
|
60d50d99bc | ||
|
|
a54f20e990 | ||
|
|
fac0aedc5e | ||
|
|
0a693f4bc3 | ||
|
|
8fa6de233e | ||
|
|
6fc3dc9577 | ||
|
|
c19287d580 | ||
|
|
3a678ae859 | ||
|
|
69e9ae982b | ||
|
|
3a846ed846 | ||
|
|
7d6ad97951 | ||
|
|
450444f37b | ||
|
|
b5bbbb7907 | ||
|
|
f830f8468c | ||
|
|
316e5f6b4a | ||
|
|
437cf3a460 | ||
|
|
822c6297a5 | ||
|
|
d459b3b735 | ||
|
|
6b2d64388d | ||
|
|
aad528c5c7 | ||
|
|
47ced9d0f2 | ||
|
|
86c94db9aa | ||
|
|
5171cee268 | ||
|
|
f345505578 | ||
|
|
4f64254e60 | ||
|
|
5678cf52d8 | ||
|
|
c96e0d88b5 | ||
|
|
3791667aac | ||
|
|
813d0dde02 | ||
|
|
b78322e521 | ||
|
|
78002d9299 | ||
|
|
78a149b546 | ||
|
|
178054dd23 | ||
|
|
9c09edc88d | ||
|
|
b5e8aa7bd0 | ||
|
|
230aca1620 | ||
|
|
508e3fb5da | ||
|
|
c195f1f56b | ||
|
|
55f7314b05 | ||
|
|
c3b9a36557 | ||
|
|
4b45f802f7 | ||
|
|
1b80d5aa5a | ||
|
|
83ae5bf657 | ||
|
|
e2a7aa79f6 | ||
|
|
e186c44185 | ||
|
|
de96a679e6 | ||
|
|
9589f484ff | ||
|
|
af7dec9c79 | ||
|
|
a6123b9656 | ||
|
|
e940da94e2 | ||
|
|
dad51621c5 | ||
|
|
7003cfbd39 | ||
|
|
0b057de598 | ||
|
|
ff70af988a | ||
|
|
f3c144d2b1 | ||
|
|
716c6e9d93 | ||
|
|
6172182ed5 | ||
|
|
fc4dd64dff | ||
|
|
c40430fa9b | ||
|
|
40a73fd62d | ||
|
|
39111235f7 | ||
|
|
aee731bebe | ||
|
|
e50730298d | ||
|
|
d26fea7b9c | ||
|
|
f6ba9233be | ||
|
|
1423b6105a | ||
|
|
eb765952e4 | ||
|
|
8e1154319b | ||
|
|
a7f5508c41 | ||
|
|
d2a14637f0 | ||
|
|
539cd723b2 | ||
|
|
c95f461300 | ||
|
|
52fcd8434e | ||
|
|
1d8b9eb873 | ||
|
|
91f8c2b6bb | ||
|
|
60e6453354 | ||
|
|
0120932e59 | ||
|
|
56df16c3d5 | ||
|
|
251ac6e171 | ||
|
|
194a19cec2 | ||
|
|
069002a534 | ||
|
|
7c960ddbdc | ||
|
|
13dabe4bda | ||
|
|
1d099ad67e | ||
|
|
acb11c3d9a | ||
|
|
86f9c6bc77 | ||
|
|
59106ea59e | ||
|
|
d04897d603 | ||
|
|
af8df43295 | ||
|
|
9d9a973201 | ||
|
|
5cc35c463e | ||
|
|
24053bf5a7 | ||
|
|
41d6de7219 | ||
|
|
fae59dd9bb | ||
|
|
c055ac84c6 | ||
|
|
d88c1b380a | ||
|
|
5b11e68f8a | ||
|
|
29be1c987a | ||
|
|
070eeb986f | ||
|
|
f9b385ec86 | ||
|
|
8e4a6841cc | ||
|
|
0d735f3744 | ||
|
|
030d816caf | ||
|
|
50e5e704b7 | ||
|
|
ffd2f05509 | ||
|
|
1efab2eb5b | ||
|
|
ef2cd9769e | ||
|
|
c6666bbbc1 | ||
|
|
85624a974d | ||
|
|
a37e0b47ab | ||
|
|
143e96ec46 | ||
|
|
19f889e885 | ||
|
|
864f92b766 | ||
|
|
6d1af8094e | ||
|
|
c145848c14 | ||
|
|
119c997265 | ||
|
|
b9051a0f74 | ||
|
|
649c1cc8ae | ||
|
|
2e45054ade | ||
|
|
65f17b67ec | ||
|
|
52eed29f27 | ||
|
|
bf4d0d7f6a | ||
|
|
35ee744673 | ||
|
|
ac4b0b52df | ||
|
|
04eb4d4c6e | ||
|
|
6aa5261a03 | ||
|
|
464cbc5fd7 | ||
|
|
ecd4af7dc6 | ||
|
|
6d312b664e | ||
|
|
d2207d4cac | ||
|
|
37d80f53ca | ||
|
|
e6d0354334 | ||
|
|
a209eab8ff | ||
|
|
c2b9f55408 | ||
|
|
3813ce3691 | ||
|
|
1eac4c96f2 | ||
|
|
b4d5ca75e6 | ||
|
|
1be3167cb9 | ||
|
|
61e96746be | ||
|
|
dfd678757b | ||
|
|
093796211c | ||
|
|
178cca5b1b | ||
|
|
970806213d | ||
|
|
0a069157b0 | ||
|
|
ef7282f777 | ||
|
|
eef8886a6d | ||
|
|
138385c895 | ||
|
|
4a14098ade | ||
|
|
1242118b0d |
No files matched your search
@@ -0,0 +1,15 @@
|
|||||||
|
.git
|
||||||
|
.github
|
||||||
|
.idea
|
||||||
|
.mvn
|
||||||
|
src
|
||||||
|
classes
|
||||||
|
logs
|
||||||
|
target/*
|
||||||
|
!target/netneutrality.jar
|
||||||
|
*.iml
|
||||||
|
README.md
|
||||||
|
checkstyle.xml
|
||||||
|
mvnw
|
||||||
|
mvnw.cmd
|
||||||
|
pom.xml
|
||||||
+16
-3
@@ -1,13 +1,26 @@
|
|||||||
|
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_VISITOR_DATA=
|
||||||
|
NETNEUTRALITY_CIPHER_URL=http://localhost:8001
|
||||||
|
NETNEUTRALITY_CIPHER_PASSWORD=
|
||||||
|
|
||||||
NETNEUTRALITY_SENTRY_DSN=
|
NETNEUTRALITY_SENTRY_DSN=
|
||||||
|
GITLAB_HOST=
|
||||||
|
GITLAB_TOKEN=
|
||||||
|
GITLAB_PROJECT_ID=
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "main"
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "main"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
package:
|
||||||
|
name: Package
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '25'
|
||||||
|
cache: 'maven'
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
run: |
|
||||||
|
chmod +x ./mvnw
|
||||||
|
./mvnw --batch-mode --update-snapshots verify
|
||||||
|
|
||||||
|
- name: Upload test reports
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
|
with:
|
||||||
|
name: junit-reports
|
||||||
|
path: |
|
||||||
|
**/target/*-reports/TEST-*.xml
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
- name: Upload Maven artifacts
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
|
with:
|
||||||
|
name: maven-artifacts
|
||||||
|
path: |
|
||||||
|
target/netneutrality.jar
|
||||||
|
startup.sh
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
maven-publish:
|
||||||
|
name: Publish Maven Package
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 15
|
||||||
|
needs: package
|
||||||
|
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||||
|
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '25'
|
||||||
|
cache: 'maven'
|
||||||
|
server-id: gitea
|
||||||
|
server-username: REGISTRY_USER
|
||||||
|
server-password: REGISTRY_PASSWORD
|
||||||
|
|
||||||
|
- name: Publish Maven package
|
||||||
|
run: |
|
||||||
|
chmod +x ./mvnw
|
||||||
|
./mvnw --batch-mode deploy -DskipTests
|
||||||
|
env:
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
docker:
|
||||||
|
name: Docker Build and Push
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 15
|
||||||
|
needs: package
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
||||||
|
|
||||||
|
- name: Download build artifacts
|
||||||
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
|
with:
|
||||||
|
name: maven-artifacts
|
||||||
|
path: .
|
||||||
|
|
||||||
|
- name: Compute Docker tags
|
||||||
|
id: meta
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
REGISTRY="${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/netneutrality"
|
||||||
|
if [[ "$GITHUB_REF" == refs/tags/* ]]; then
|
||||||
|
ver="${GITHUB_REF_NAME#v}"
|
||||||
|
echo "tags=${REGISTRY}:${ver},${REGISTRY}:latest" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "tags=${REGISTRY}:main" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
|
- name: Log in to registry
|
||||||
|
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
|
||||||
|
with:
|
||||||
|
registry: ${{ secrets.REGISTRY_URL }}
|
||||||
|
username: ${{ secrets.REGISTRY_USER }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
cache-from: type=registry,ref=${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/netneutrality:buildcache
|
||||||
|
cache-to: type=registry,ref=${{ secrets.REGISTRY_URL }}/${{ secrets.REGISTRY_USER }}/netneutrality:buildcache,mode=max
|
||||||
@@ -45,3 +45,4 @@ target/
|
|||||||
logs/
|
logs/
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
docker-compose.override.yml
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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"
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="JpaPluginProjectSettings">
|
|
||||||
<option name="lastSelectedLanguage" value="Java" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
+3
-18
@@ -1,18 +1,3 @@
|
|||||||
# Licensed to the Apache Software Foundation (ASF) under one
|
wrapperVersion=3.3.4
|
||||||
# or more contributor license agreements. See the NOTICE file
|
distributionType=only-script
|
||||||
# distributed with this work for additional information
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.15/apache-maven-3.9.15-bin.zip
|
||||||
# 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
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
## 2.0.1 (2024-07-14)
|
|
||||||
|
|
||||||
### Refactor
|
|
||||||
|
|
||||||
- fix inspection errors for deprecated calls and concatenation
|
|
||||||
|
|
||||||
## 2.0.0 (2024-07-14)
|
|
||||||
|
|
||||||
### BREAKING CHANGE
|
|
||||||
|
|
||||||
- removed TrackLoader amd LoadableSongs
|
|
||||||
|
|
||||||
### Feat
|
|
||||||
|
|
||||||
- **Settings**: add helpers to get version number of bot
|
|
||||||
|
|
||||||
### Fix
|
|
||||||
|
|
||||||
- fix relection package to new path
|
|
||||||
- fix wrongly named environment variables
|
|
||||||
- bump youtube-source to 1.4.0
|
|
||||||
|
|
||||||
### Refactor
|
|
||||||
|
|
||||||
- Remove unused command
|
|
||||||
- add release to Sentry config
|
|
||||||
- remove convoluted TrackLoader and LoadableSongs abstraction
|
|
||||||
- **LogHandler**: use the exception itself instead of a string for buildSentryErrorMessage
|
|
||||||
- change package path to reflect group id of pom.xml
|
|
||||||
+3
-14
@@ -1,17 +1,6 @@
|
|||||||
FROM eclipse-temurin:17-jdk-alpine as build
|
FROM eclipse-temurin:25-jre@sha256:ff82c74a630c08b417502c4c18189e62074edbb28eb1f2d31b799ac138c01058
|
||||||
|
|
||||||
COPY .mvn/ .mvn
|
RUN apt-get update && apt-get install -y --no-install-recommends netcat-openbsd curl && rm -rf /var/lib/apt/lists/*
|
||||||
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
|
||||||
|
|
||||||
@@ -19,7 +8,7 @@ ARG API_PORT=9000
|
|||||||
EXPOSE ${API_PORT}
|
EXPOSE ${API_PORT}
|
||||||
|
|
||||||
COPY startup.sh ./
|
COPY startup.sh ./
|
||||||
COPY --from=build /target/netneutrality-jar-with-dependencies.jar ./
|
COPY target/netneutrality.jar ./
|
||||||
|
|
||||||
RUN ["chmod", "+x", "./startup.sh"]
|
RUN ["chmod", "+x", "./startup.sh"]
|
||||||
CMD ["./startup.sh"]
|
CMD ["./startup.sh"]
|
||||||
+17
-4
@@ -1,13 +1,26 @@
|
|||||||
|
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_VISITOR_DATA=
|
||||||
|
NETNEUTRALITY_CIPHER_URL=http://localhost:8001
|
||||||
|
NETNEUTRALITY_CIPHER_PASSWORD=
|
||||||
|
|
||||||
NETNEUTRALITY_SENTRY_DSN=
|
NETNEUTRALITY_SENTRY_DSN=
|
||||||
|
GITLAB_HOST=
|
||||||
|
GITLAB_TOKEN=
|
||||||
|
GITLAB_PROJECT_ID=
|
||||||
Regular → Executable
File mode changed.
@@ -0,0 +1,37 @@
|
|||||||
|
<?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>
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
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
-26
@@ -1,43 +1,46 @@
|
|||||||
services:
|
services:
|
||||||
db:
|
netneutrality-db:
|
||||||
image: mariadb:lts
|
image: mariadb:11@sha256:1cac8492bd78b1ec693238dc600be173397efd7b55eabc725abc281dc855b482
|
||||||
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: 'java'
|
- MYSQL_USER=${MYSQL_USER:-java}
|
||||||
MYSQL_PASSWORD: 'password'
|
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-password}
|
||||||
MYSQL_DATABASE: 'netneutrality'
|
- MYSQL_DATABASE=${MYSQL_DATABASE:-netneutrality}
|
||||||
MYSQL_ROOT_PASSWORD: 'password'
|
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-password}
|
||||||
MARIADB_AUTO_UPGRADE: '1'
|
- MARIADB_AUTO_UPGRADE=1
|
||||||
ports:
|
|
||||||
- "3306"
|
|
||||||
networks:
|
|
||||||
- internal
|
|
||||||
|
|
||||||
bot:
|
netneutrality-bot:
|
||||||
|
image: gitea.bobert.me/anthorob/netneutrality:${NETNEUTRALITY_TAG:-latest}
|
||||||
container_name: bot
|
container_name: bot
|
||||||
build: ./
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file: .env
|
||||||
|
networks:
|
||||||
|
- netneutrality
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- netneutrality-db
|
||||||
volumes:
|
volumes:
|
||||||
- ./logs:/app/logs
|
- ./logs:/app/logs
|
||||||
environment:
|
environment:
|
||||||
MYSQL_HOST: 'db'
|
MYSQL_HOST: netneutrality-db
|
||||||
MYSQL_USER: 'java'
|
MYSQL_PORT: 3306
|
||||||
MYSQL_PASSWORD: 'password'
|
MYSQL_USER: java
|
||||||
MYSQL_DATABASE: 'netneutrality'
|
MYSQL_PASSWORD: password
|
||||||
env_file: ".env"
|
MYSQL_DATABASE: netneutrality
|
||||||
ports:
|
ejs-api:
|
||||||
- "9000:9000"
|
image: ghcr.io/kikkia/yt-cipher:master@sha256:4c5ec381ff57336cfc24822d2b526a5ad7cd0171f61065a1bbc87bb736842449
|
||||||
|
container_name: ejs_signature_api
|
||||||
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- netneutrality
|
||||||
|
# environment: # UNCOMMENT THIS LINE AND API_TOKEN TO ADD A PASSWORD
|
||||||
|
# - API_TOKEN=test
|
||||||
volumes:
|
volumes:
|
||||||
netneutrality-db:
|
netneutrality-db: null
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
internal:
|
netneutrality: null
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#!/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"
|
||||||
@@ -19,290 +19,277 @@
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
# Apache Maven Wrapper startup batch script, version 3.3.4
|
||||||
#
|
|
||||||
# Required ENV vars:
|
|
||||||
# ------------------
|
|
||||||
# JAVA_HOME - location of a JDK home dir
|
|
||||||
#
|
#
|
||||||
# Optional ENV vars
|
# Optional ENV vars
|
||||||
# -----------------
|
# -----------------
|
||||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||||
# e.g. to debug Maven itself, use
|
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
set -euf
|
||||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||||
|
|
||||||
if [ -f /usr/local/etc/mavenrc ] ; then
|
# OS specific support.
|
||||||
. /usr/local/etc/mavenrc
|
native_path() { printf %s\\n "$1"; }
|
||||||
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*) cygwin=true ;;
|
CYGWIN* | MINGW*)
|
||||||
MINGW*) mingw=true;;
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||||
Darwin*) darwin=true
|
native_path() { cygpath --path --windows "$1"; }
|
||||||
# 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
|
||||||
|
|
||||||
if [ -z "$JAVA_HOME" ] ; then
|
# set JAVACMD and JAVACCMD
|
||||||
if [ -r /etc/gentoo-release ] ; then
|
set_java_home() {
|
||||||
JAVA_HOME=$(java-config --jre-home)
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||||
fi
|
if [ -n "${JAVA_HOME-}" ]; then
|
||||||
fi
|
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||||
|
|
||||||
# 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
|
|
||||||
# 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"
|
||||||
fi
|
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||||
else
|
|
||||||
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||||
echo " We cannot execute $JAVACMD" >&2
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&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
|
||||||
|
)" || :
|
||||||
|
|
||||||
basedir="$1"
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||||
wdir="$1"
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||||
while [ "$wdir" != '/' ] ; do
|
return 1
|
||||||
if [ -d "$wdir"/.mvn ] ; then
|
fi
|
||||||
basedir=$wdir
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
set -f
|
||||||
}
|
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
if [ -z "$actualDistributionDir" ]; then
|
||||||
log "$MAVEN_PROJECTBASEDIR"
|
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
||||||
|
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
|
|
||||||
|
|
||||||
if command -v wget > /dev/null; then
|
|
||||||
log "Found wget ... using wget"
|
|
||||||
[ "$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
|
|
||||||
##########################################################################################
|
|
||||||
|
|
||||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
|
||||||
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
|
fi
|
||||||
|
|
||||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
||||||
|
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
clean || :
|
||||||
if $cygwin; then
|
exec_maven "$@"
|
||||||
[ -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 "$@"
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<# : 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
|
||||||
@@ -18,188 +19,171 @@
|
|||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
@REM Apache Maven Wrapper startup batch script, version 3.3.4
|
||||||
@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 MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||||
@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 ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||||
@echo off
|
@SET __MVNW_CMD__=
|
||||||
@REM set title of command window
|
@SET __MVNW_ERROR__=
|
||||||
title %0
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
@SET PSModulePath=
|
||||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
@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 "%%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 #>
|
||||||
|
|
||||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
$ErrorActionPreference = "Stop"
|
||||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
if ($env:MVNW_VERBOSE -eq "true") {
|
||||||
if exist %WRAPPER_JAR% (
|
$VerbosePreference = "Continue"
|
||||||
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%
|
|
||||||
)
|
|
||||||
|
|
||||||
powershell -Command "&{"^
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
"$webclient = new-object System.Net.WebClient;"^
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
if (!$distributionUrl) {
|
||||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
"}"^
|
}
|
||||||
"[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
|
|
||||||
|
|
||||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||||
SET WRAPPER_SHA_256_SUM=""
|
"maven-mvnd-*" {
|
||||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
$USE_MVND = $true
|
||||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||||
)
|
$MVN_CMD = "mvnd.cmd"
|
||||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
break
|
||||||
powershell -Command "&{"^
|
}
|
||||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
default {
|
||||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
$USE_MVND = $false
|
||||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
break
|
||||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
}
|
||||||
" exit 1;"^
|
}
|
||||||
"}"^
|
|
||||||
"}"
|
|
||||||
if ERRORLEVEL 1 goto error
|
|
||||||
)
|
|
||||||
|
|
||||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
@REM work with both Windows and non-Windows executions.
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
set MAVEN_CMD_LINE_ARGS=%*
|
if ($env:MVNW_REPOURL) {
|
||||||
|
$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_JAVA_EXE% ^
|
$MAVEN_M2_PATH = "$HOME/.m2"
|
||||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
if ($env:MAVEN_USER_HOME) {
|
||||||
%MAVEN_OPTS% ^
|
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
||||||
%MAVEN_DEBUG_OPTS% ^
|
}
|
||||||
-classpath %WRAPPER_JAR% ^
|
|
||||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
|
||||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
|
||||||
if ERRORLEVEL 1 goto error
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:error
|
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
||||||
set ERROR_CODE=1
|
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
:end
|
$MAVEN_WRAPPER_DISTS = $null
|
||||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
||||||
|
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
||||||
|
} else {
|
||||||
|
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
||||||
|
}
|
||||||
|
|
||||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
||||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
|
||||||
:skipRcPost
|
|
||||||
|
|
||||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
exit $?
|
||||||
|
}
|
||||||
|
|
||||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||||
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||||
|
}
|
||||||
|
|
||||||
cmd /C exit /B %ERROR_CODE%
|
# prepare tmp dir
|
||||||
|
$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"
|
||||||
@@ -6,17 +6,25 @@
|
|||||||
|
|
||||||
<groupId>me.bobert</groupId>
|
<groupId>me.bobert</groupId>
|
||||||
<artifactId>netneutrality</artifactId>
|
<artifactId>netneutrality</artifactId>
|
||||||
<version>2.0.1</version>
|
<version>3.0.0</version>
|
||||||
<name>netneutrality-discord-bot</name>
|
<name>netneutrality-discord-bot</name>
|
||||||
<description>Discord bot</description>
|
<description>Discord bot</description>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>gitea</id>
|
||||||
|
<url>https://gitea.bobert.me/api/packages/anthorob/maven</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<main.class>me.bobert.netneutrality.NetNeutrality</main.class>
|
<main.class>me.bobert.netneutrality.NetNeutrality</main.class>
|
||||||
<java.version>17</java.version>
|
<java.version>25</java.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.source>17</maven.compiler.source>
|
<maven.compiler.source>25</maven.compiler.source>
|
||||||
<maven.compiler.target>17</maven.compiler.target>
|
<maven.compiler.target>25</maven.compiler.target>
|
||||||
|
<mockito.version>5.23.0</mockito.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -28,42 +36,95 @@
|
|||||||
</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>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-maven-plugin</artifactId>
|
||||||
|
<version>11.20.3</version>
|
||||||
|
<configuration>
|
||||||
|
<url>${env.FLYWAY_URL}</url>
|
||||||
|
<user>${env.FLYWAY_USER}</user>
|
||||||
|
<password>${env.FLYWAY_PASSWORD}</password>
|
||||||
|
</configuration>
|
||||||
|
</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.3.1</version>
|
<version>3.5.0</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-assembly-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
<version>3.7.1</version>
|
<version>3.5.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifestEntries>
|
|
||||||
<Multi-Release>true</Multi-Release>
|
|
||||||
</manifestEntries>
|
|
||||||
<manifest>
|
<manifest>
|
||||||
<addClasspath>true</addClasspath>
|
|
||||||
<mainClass>${main.class}</mainClass>
|
<mainClass>${main.class}</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
<descriptorRefs>
|
|
||||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
|
||||||
</descriptorRefs>
|
|
||||||
<finalName>netneutrality</finalName>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.6.2</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>make-assembly</id> <!-- this is used for inheritance merges -->
|
<phase>package</phase>
|
||||||
<phase>package</phase> <!-- bind to the packaging phase -->
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>single</goal>
|
<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>
|
||||||
|
<Multi-Release>true</Multi-Release>
|
||||||
|
</manifestEntries>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<version>3.6.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>checkstyle</id>
|
||||||
|
<phase>test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>check</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>
|
||||||
@@ -82,60 +143,106 @@
|
|||||||
<repository>
|
<repository>
|
||||||
<id>arbjergDev-releases</id>
|
<id>arbjergDev-releases</id>
|
||||||
<name>Lavalink Repository</name>
|
<name>Lavalink Repository</name>
|
||||||
<url>https://maven.lavalink.dev/releases</url>
|
<url>https://maven.lavalink.dev/snapshots</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.dv8tion</groupId>
|
<groupId>net.dv8tion</groupId>
|
||||||
<artifactId>JDA</artifactId>
|
<artifactId>JDA</artifactId>
|
||||||
<version>5.0.0</version>
|
<version>6.4.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.arbjerg</groupId>
|
<groupId>dev.arbjerg</groupId>
|
||||||
<artifactId>lavaplayer</artifactId>
|
<artifactId>lavaplayer</artifactId>
|
||||||
<version>2.1.2</version>
|
<version>2.2.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.lavalink.youtube</groupId>
|
<groupId>dev.lavalink.youtube</groupId>
|
||||||
<artifactId>v2</artifactId>
|
<artifactId>v2</artifactId>
|
||||||
<version>1.4.0</version>
|
<version>f45bbb7aebfcbc1c553769e04af6cd43afa8b7c3-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>club.minnced</groupId>
|
||||||
|
<artifactId>jdave-api</artifactId>
|
||||||
|
<version>0.1.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>club.minnced</groupId>
|
||||||
|
<artifactId>jdave-native-linux-x86-64</artifactId>
|
||||||
|
<version>0.1.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>club.minnced</groupId>
|
||||||
|
<artifactId>jdave-native-win-x86-64</artifactId>
|
||||||
|
<version>0.1.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>club.minnced</groupId>
|
||||||
|
<artifactId>jdave-native-darwin</artifactId>
|
||||||
|
<version>0.1.8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.squareup.okhttp3</groupId>
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
<artifactId>okhttp</artifactId>
|
<artifactId>okhttp-jvm</artifactId>
|
||||||
<version>4.12.0</version>
|
<version>5.3.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate.orm</groupId>
|
<groupId>org.hibernate.orm</groupId>
|
||||||
<artifactId>hibernate-core</artifactId>
|
<artifactId>hibernate-core</artifactId>
|
||||||
<version>6.1.7.Final</version>
|
<version>7.1.9.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>6.1.7.Final</version>
|
<version>7.1.9.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>8.3.0</version>
|
<version>9.7.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
<version>11.20.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-mysql</artifactId>
|
||||||
|
<version>11.20.3</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.sentry</groupId>
|
<groupId>io.sentry</groupId>
|
||||||
<artifactId>sentry</artifactId>
|
<artifactId>sentry</artifactId>
|
||||||
<version>7.6.0</version>
|
<version>8.41.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gitlab4j</groupId>
|
||||||
|
<artifactId>gitlab4j-api</artifactId>
|
||||||
|
<version>6.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.fellbaum</groupId>
|
||||||
|
<artifactId>jemoji</artifactId>
|
||||||
|
<version>1.7.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.json</groupId>
|
<groupId>org.json</groupId>
|
||||||
<artifactId>json</artifactId>
|
<artifactId>json</artifactId>
|
||||||
<version>20240303</version>
|
<version>20250517</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.14.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -147,41 +254,48 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
<version>2.23.1</version>
|
<version>2.26.0</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.23.1</version>
|
<version>2.26.0</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.23.1</version>
|
<version>2.26.0</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.12.0</version>
|
<version>3.19.0</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-codec</groupId>
|
|
||||||
<artifactId>commons-codec</artifactId>
|
|
||||||
<version>1.15</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
<version>2.16.1</version>
|
<version>2.21.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
<version>4.13.2</version>
|
<version>6.0.3</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>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"local>renovate/renovate-config",
|
||||||
|
"group:monorepos",
|
||||||
|
"group:flyway"
|
||||||
|
],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchPackageNames": [
|
||||||
|
"mariadb"
|
||||||
|
],
|
||||||
|
"automerge": false,
|
||||||
|
"dependencyDashboardApproval": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["maven"],
|
||||||
|
"extends": ["schedule:monthly"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "maven plugins",
|
||||||
|
"matchManagers": ["maven"],
|
||||||
|
"matchDepTypes": ["plugin"],
|
||||||
|
"extends": ["schedule:weekly"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "hibernate orm",
|
||||||
|
"matchPackageNames": [
|
||||||
|
"org.hibernate.orm:**"
|
||||||
|
],
|
||||||
|
"extends": ["schedule:monthly"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "apache commons",
|
||||||
|
"matchPackageNames": [
|
||||||
|
"commons-**",
|
||||||
|
"org.apache.commons:**"
|
||||||
|
],
|
||||||
|
"extends": ["schedule:monthly"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": ["maven"],
|
||||||
|
"matchDepTypes": ["dependency", "parentPom"],
|
||||||
|
"matchPackageNames": [
|
||||||
|
"net.dv8tion:JDA",
|
||||||
|
"dev.arbjerg:lavaplayer",
|
||||||
|
"dev.lavalink.youtube:**",
|
||||||
|
"club.minnced:jdave-*"
|
||||||
|
],
|
||||||
|
"schedule": ["at any time"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "github actions (minor/patch)",
|
||||||
|
"matchManagers": ["github-actions"],
|
||||||
|
"matchUpdateTypes": ["minor", "patch", "digest"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "github actions (major)",
|
||||||
|
"matchManagers": ["github-actions"],
|
||||||
|
"matchUpdateTypes": ["major"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
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`
|
|
||||||
--
|
|
||||||
CREATE DATABASE IF NOT EXISTS netneutrality CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
USE netneutrality;
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `blacklist`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `blacklist`;
|
|
||||||
CREATE TABLE IF NOT EXISTS `blacklist` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`word` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`enabled` tinyint(1) NOT NULL,
|
|
||||||
`creator_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `fk_blacklist_guilds` (`guild_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `guilds`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `guilds`;
|
|
||||||
CREATE TABLE IF NOT EXISTS `guilds` (
|
|
||||||
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`logging` tinyint(1) NOT NULL DEFAULT 0,
|
|
||||||
`debug` tinyint(1) NOT NULL DEFAULT 1,
|
|
||||||
`log_channel` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
||||||
PRIMARY KEY (`guild_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `guild_settings`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `guild_settings`;
|
|
||||||
CREATE TABLE IF NOT EXISTS `guild_settings` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`value` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `fk_guildsetting_guilds` (`guild_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `logging`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `logging`;
|
|
||||||
CREATE TABLE IF NOT EXISTS `logging` (
|
|
||||||
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`channel_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`enabled` tinyint(1) NOT NULL DEFAULT 0,
|
|
||||||
PRIMARY KEY (`guild_id`,`channel_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `log_message`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `log_message`;
|
|
||||||
CREATE TABLE IF NOT EXISTS `log_message` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`channel_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`message_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`user_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`date_sent` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
|
||||||
`message` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
||||||
`edited_message` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE KEY `message_id` (`message_id`),
|
|
||||||
KEY `fk_log_guilds` (`guild_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `permissions`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `permissions`;
|
|
||||||
CREATE TABLE IF NOT EXISTS `permissions` (
|
|
||||||
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`role_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`type` int(11) DEFAULT NULL,
|
|
||||||
`command` int(11) DEFAULT NULL,
|
|
||||||
PRIMARY KEY (`guild_id`,`role_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `web_users`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `web_users`;
|
|
||||||
CREATE TABLE IF NOT EXISTS `web_users` (
|
|
||||||
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`email` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`expires` timestamp NOT NULL DEFAULT current_timestamp(),
|
|
||||||
`refresh_token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
||||||
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for dumped tables
|
|
||||||
--
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `blacklist`
|
|
||||||
--
|
|
||||||
ALTER TABLE `blacklist`
|
|
||||||
ADD CONSTRAINT `fk_blacklist_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `guild_settings`
|
|
||||||
--
|
|
||||||
ALTER TABLE `guild_settings`
|
|
||||||
ADD CONSTRAINT `fk_guildsetting_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `logging`
|
|
||||||
--
|
|
||||||
ALTER TABLE `logging`
|
|
||||||
ADD CONSTRAINT `fk_logging_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for 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;
|
|
||||||
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,40 +6,40 @@ 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 net.dv8tion.jda.api.JDA;
|
|
||||||
import net.dv8tion.jda.api.JDABuilder;
|
|
||||||
import net.dv8tion.jda.api.entities.Activity;
|
|
||||||
import net.dv8tion.jda.api.entities.User;
|
|
||||||
import net.dv8tion.jda.api.exceptions.InvalidTokenException;
|
|
||||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
|
||||||
import me.bobert.netneutrality.command.CommandManager;
|
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
import me.bobert.netneutrality.db.DBController;
|
import me.bobert.netneutrality.core.command.CommandManager;
|
||||||
import me.bobert.netneutrality.hooks.SlashCommandHooks;
|
import me.bobert.netneutrality.core.db.DatabaseMigration;
|
||||||
import me.bobert.netneutrality.hooks.guilds.ServerJoinHandler;
|
import me.bobert.netneutrality.core.db.SessionManager;
|
||||||
import me.bobert.netneutrality.hooks.message.EditHandler;
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
import me.bobert.netneutrality.hooks.message.MessageReceivedHandler;
|
import me.bobert.netneutrality.core.thread.ScheduledTaskManager;
|
||||||
|
import me.bobert.netneutrality.core.thread.ThreadManager;
|
||||||
|
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.BaseThreadFactory;
|
|
||||||
import me.bobert.netneutrality.thread.ShutdownThread;
|
|
||||||
import me.bobert.netneutrality.thread.ThreadManager;
|
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
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.exceptions.InvalidTokenException;
|
||||||
|
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
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.apache.logging.log4j.core.config.Configurator;
|
import org.apache.logging.log4j.core.config.Configurator;
|
||||||
|
|
||||||
public class NetNeutrality {
|
public class NetNeutrality {
|
||||||
|
private static final Logger logger = LogManager.getLogger(NetNeutrality.class);
|
||||||
|
public static final String VERSION = Settings.getVersion();
|
||||||
|
private static NetNeutrality instance;
|
||||||
private static JDA jda;
|
private static JDA jda;
|
||||||
public final static Logger logger = LogManager.getLogger(NetNeutrality.class);
|
|
||||||
public static final String VERSION = Settings.getInstance().getMavenVersion();
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
static void main() {
|
||||||
|
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");
|
||||||
|
|
||||||
String sentryDsn = Settings.getInstance().get("NETNEUTRALITY_SENTRY_DSN");
|
String sentryDsn = Settings.getInstance().get("NETNEUTRALITY_SENTRY_DSN");
|
||||||
if (sentryDsn != null) {
|
if (sentryDsn != null) {
|
||||||
logger.info("Found Sentry DSN in config, setting up the SDK..");
|
logger.info("Found Sentry DSN in config, setting up the SDK..");
|
||||||
@@ -55,32 +55,58 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
DBController.init();
|
instance = new NetNeutrality();
|
||||||
initJDA();
|
}
|
||||||
|
|
||||||
|
public NetNeutrality() {
|
||||||
|
startup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startup() {
|
||||||
|
String token = Settings.getInstance().get("NETNEUTRALITY_BOT_TOKEN");
|
||||||
|
if (token == null) {
|
||||||
|
throw new RuntimeException("No bot token provided, exiting.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionManager.getSessionFactory();
|
||||||
|
DatabaseMigration.migrate();
|
||||||
CommandManager.init();
|
CommandManager.init();
|
||||||
MusicManager.initPlayerManager();
|
MusicManager.initPlayerManager();
|
||||||
jda.addEventListener(new MessageReceivedHandler(), new EditHandler(), new ServerJoinHandler());
|
TaskManager.getInstance().init();
|
||||||
jda.addEventListener(new SlashCommandHooks());
|
initJDA();
|
||||||
|
CommandManager.registerSlashCommands(jda);
|
||||||
|
jda.addEventListener(new EventsHandler());
|
||||||
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 BaseThreadFactory("shutdown").newThread(new ShutdownThread()));
|
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void initJDA() {
|
public static NetNeutrality getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initJDA() {
|
||||||
String token = Settings.getInstance().get("NETNEUTRALITY_BOT_TOKEN");
|
String token = Settings.getInstance().get("NETNEUTRALITY_BOT_TOKEN");
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
DBController.shutdown();
|
SessionManager.shutdown();
|
||||||
throw new RuntimeException("No bot token provided, exiting.");
|
throw new RuntimeException("No bot token provided, exiting.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
jda = JDABuilder
|
jda = JDABuilder
|
||||||
.createDefault(token)
|
.createDefault(token)
|
||||||
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES, GatewayIntent.MESSAGE_CONTENT)
|
.enableIntents(
|
||||||
|
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();
|
||||||
@@ -90,30 +116,23 @@ public class NetNeutrality {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static 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();
|
||||||
DBController.shutdown();
|
SessionManager.shutdown();
|
||||||
System.out.println("[Net Neutrality] Shutdown complete");
|
System.out.println("[Net Neutrality] Shutdown complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JDA getJDA() {
|
public JDA getJDA() {
|
||||||
return jda;
|
return jda;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static User getSelf() {
|
private void disconnect() {
|
||||||
return jda.getSelfUser();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void disconnect() {
|
|
||||||
if (jda != null) {
|
if (jda != null) {
|
||||||
jda.shutdown();
|
jda.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package me.bobert.netneutrality.command;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
|
||||||
import me.bobert.netneutrality.utils.UserFunction;
|
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
|
||||||
import net.dv8tion.jda.api.entities.Role;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|
||||||
|
|
||||||
public abstract class AbstractMusicCommand extends AbstractCommand {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needAdmin() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isGuildOnly() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandType getType() {
|
|
||||||
return CommandType.MUSIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canBeExecutedBy(Guild guild, Member member) {
|
|
||||||
String roleDb = GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE);
|
|
||||||
|
|
||||||
if (roleDb != null && !roleDb.isEmpty()) {
|
|
||||||
if (UserFunction.isAdmin(member)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Role neededRole = guild.getRoleById(roleDb);
|
|
||||||
if (neededRole == null) {
|
|
||||||
// Role doesn't exist on guild anymore, remove from db
|
|
||||||
GuildManager.getManager(guild).getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return member.getRoles().contains(neededRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) {
|
|
||||||
if (channel.getType() != ChannelType.TEXT)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
String channelDb = GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
|
|
||||||
|
|
||||||
if (channelDb != null && !channelDb.isEmpty()) {
|
|
||||||
return channelDb.equals(channel.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package me.bobert.netneutrality.command;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.NetNeutrality;
|
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.reflections.Reflections;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class CommandManager {
|
|
||||||
|
|
||||||
private final static Logger logger = LogManager.getLogger(CommandManager.class);
|
|
||||||
public static final HashMap<String, AbstractCommand> commands = new HashMap<>();
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
initCommands();
|
|
||||||
registerSlashCommands();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isValidCommand(String commandString) {
|
|
||||||
return commands.containsKey(commandString);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AbstractCommand getCommand(String commandString) {
|
|
||||||
return commands.get(commandString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void initCommands() {
|
|
||||||
logger.info("Loading commands..");
|
|
||||||
|
|
||||||
Reflections reflections = new Reflections("me.bobert.netneutrality.command");
|
|
||||||
Set<Class<? extends AbstractCommand>> classes = reflections.getSubTypesOf(AbstractCommand.class);
|
|
||||||
|
|
||||||
for (Class<? extends AbstractCommand> s : classes) {
|
|
||||||
try {
|
|
||||||
if (Modifier.isAbstract(s.getModifiers())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractCommand command = s.getConstructor().newInstance();
|
|
||||||
|
|
||||||
if (!command.isEnabled())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!commands.containsKey(command.getCommandName())) {
|
|
||||||
commands.put(command.getCommandName(), command);
|
|
||||||
}
|
|
||||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
|
||||||
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
|
||||||
LogHandler.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("Loaded {} commands!", commands.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void registerSlashCommands() {
|
|
||||||
CommandListUpdateAction commandListUpdateAction = NetNeutrality.getJDA().updateCommands();
|
|
||||||
List<CommandData> slashCommands = new ArrayList<>();
|
|
||||||
|
|
||||||
for (AbstractCommand command : commands.values()) {
|
|
||||||
if (!command.isEnabled() || !command.isSlashCommand()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SlashCommandData data = Commands.slash(command.getCommandName(), command.getDescription());
|
|
||||||
data.setDefaultPermissions(command.getSlashCommandPermission());
|
|
||||||
data.setGuildOnly(command.isGuildOnly());
|
|
||||||
command.registerSlashCommandOptions(data);
|
|
||||||
|
|
||||||
slashCommands.add(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
commandListUpdateAction.addCommands(slashCommands).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package me.bobert.netneutrality.command;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
public enum CommandType {
|
|
||||||
MUSIC("Music", 1), ADMINISTRATION("Administration", 0), UTILITY("Utility", 5), CRYPTO("Crypto", 6), UNLISTED("", -1);
|
|
||||||
|
|
||||||
private final String description;
|
|
||||||
private final int id;
|
|
||||||
|
|
||||||
CommandType(String description, int id) {
|
|
||||||
this.description = description;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package me.bobert.netneutrality.command;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public interface ICommand {
|
|
||||||
|
|
||||||
boolean needAdmin();
|
|
||||||
|
|
||||||
boolean isEnabled();
|
|
||||||
|
|
||||||
boolean isGuildOnly();
|
|
||||||
|
|
||||||
boolean isSlashCommand();
|
|
||||||
|
|
||||||
String getCommandName();
|
|
||||||
|
|
||||||
String getDescription();
|
|
||||||
|
|
||||||
DefaultMemberPermissions getSlashCommandPermission();
|
|
||||||
|
|
||||||
void registerSlashCommandOptions(SlashCommandData data);
|
|
||||||
|
|
||||||
CommandType getType();
|
|
||||||
|
|
||||||
boolean canBeExecutedBy(Guild guild, Member member);
|
|
||||||
|
|
||||||
boolean canBeExecutedFrom(Guild guild, MessageChannel channel);
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
CommandExecutionResult execute(MessageCommandContext context);
|
|
||||||
|
|
||||||
CommandExecutionResult execute(SlashCommandContext context);
|
|
||||||
}
|
|
||||||
@@ -5,29 +5,20 @@ package me.bobert.netneutrality.command.admin;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.command.CommandType;
|
import me.bobert.netneutrality.core.command.impl.AdminCommand;
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
|
||||||
import me.bobert.netneutrality.db.DBController;
|
|
||||||
import me.bobert.netneutrality.db.controllers.DBBlacklist;
|
|
||||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
import me.bobert.netneutrality.db.entities.OBlacklist;
|
||||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
import me.bobert.netneutrality.db.service.BlacklistService;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
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.SubcommandData;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
|
|
||||||
public class BlacklistCommand extends AbstractCommand {
|
public class BlacklistCommand extends AdminCommand {
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needAdmin() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -50,62 +41,44 @@ public class BlacklistCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
return DefaultMemberPermissions.DISABLED;
|
builder.addSubcommands(
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
|
||||||
data.addSubcommands(
|
|
||||||
new SubcommandData("get", "Get all blacklisted words for this guild"),
|
new SubcommandData("get", "Get all blacklisted words for this guild"),
|
||||||
new SubcommandData("add", "Add a word to the blacklist")
|
new SubcommandData("add", "Add a word to the blacklist")
|
||||||
.addOption(OptionType.STRING, "word", "Word to add"),
|
.addOption(OptionType.STRING, "word", "Word to add", true),
|
||||||
new SubcommandData("remove", "Remove a word from the blacklist")
|
new SubcommandData("remove", "Remove a word from the blacklist")
|
||||||
.addOption(OptionType.STRING, "word", "Word to remove")
|
.addOption(OptionType.STRING, "word", "Word to remove", true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandType getType() {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
return CommandType.ADMINISTRATION;
|
String sub = context.getSubcommand();
|
||||||
}
|
String word = context.getOption("word", "", CommandOption::getAsString);
|
||||||
|
BlacklistService service = GuildService.get(context.getGuild()).getBlacklistService();
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
String sub = context.getEvent().getFullCommandName().split(" ")[1];
|
|
||||||
DBBlacklist controller = DBController.get(DBBlacklist.class);
|
|
||||||
String word = context.getEvent().getOption("word", "", OptionMapping::getAsString);
|
|
||||||
context.ackAndWait(true);
|
|
||||||
|
|
||||||
switch (sub) {
|
switch (sub) {
|
||||||
case "get" -> {
|
case "get" -> context.async("Retrieving list, please wait...", asyncMessage -> {
|
||||||
|
List<OBlacklist> blacklists = service.getBlacklistForGuild();
|
||||||
context.getHook().editOriginal("Retrieving list, please wait...").queue(t -> {
|
|
||||||
ArrayList<OBlacklist> blacklists = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
blacklists = controller.getBlacklistForGuild(context.getGuild());
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHandler.log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("List of blacklisted words for this server: \n");
|
||||||
for (OBlacklist bl : blacklists) {
|
for (OBlacklist bl : blacklists) {
|
||||||
sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n");
|
sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
t.editMessage("List of blacklisted words for this server [" + context.getGuild().getName() + "] :\n" + (blacklists.isEmpty() ? "No word found" : sb.toString())).queue();
|
if (blacklists.isEmpty()) {
|
||||||
|
sb.append("No word found");
|
||||||
});
|
}
|
||||||
|
|
||||||
|
asyncMessage.edit(sb.toString());
|
||||||
|
});
|
||||||
|
case "add" -> {
|
||||||
|
return service.add(context.getGuildUser(), word);
|
||||||
|
}
|
||||||
|
case "remove" -> {
|
||||||
|
return service.delete(word);
|
||||||
}
|
}
|
||||||
case "add" -> controller.add(word, context);
|
|
||||||
case "remove" -> controller.delete(word, context);
|
|
||||||
case "disable" -> controller.disable(word, context);
|
|
||||||
case "enable" -> controller.enable(word, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package me.bobert.netneutrality.command.admin;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
|
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.SubcommandData;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ReactionCommand extends AdminCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuildOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "reaction";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Manages emoji reactions on this server";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
builder.addSubcommandGroups(
|
||||||
|
new SubcommandGroupData("emoji", "Give roles to users using emojis").addSubcommands(
|
||||||
|
new SubcommandData("list", "Lists all registered tasks for this guild"),
|
||||||
|
new SubcommandData("setup", "Add a role to a message").addOptions(
|
||||||
|
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true)
|
||||||
|
.setRequiredLength(19, 19),
|
||||||
|
new OptionData(OptionType.STRING, "emoji", "Emoji associated with the role", true),
|
||||||
|
new OptionData(OptionType.ROLE, "role", "Role to add to the emoji", true)
|
||||||
|
),
|
||||||
|
new SubcommandData("add", "Add a role to an existing message").addOptions(
|
||||||
|
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true)
|
||||||
|
.setRequiredLength(19, 19),
|
||||||
|
new OptionData(OptionType.STRING, "emoji", "Emoji associated with the role", true),
|
||||||
|
new OptionData(OptionType.ROLE, "role", "Role to add to the emoji", true)
|
||||||
|
),
|
||||||
|
new SubcommandData("remove", "Remove a role from a message").addOptions(
|
||||||
|
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true)
|
||||||
|
.setRequiredLength(19, 19),
|
||||||
|
new OptionData(OptionType.STRING, "emoji", "Emoji to remove from the message", true)
|
||||||
|
),
|
||||||
|
new SubcommandData("delete", "Remove all roles from a message").addOptions(
|
||||||
|
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true)
|
||||||
|
.setRequiredLength(19, 19)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
|
context.ephemeral();
|
||||||
|
|
||||||
|
String guildId = context.getGuild().getId();
|
||||||
|
String messageId = context.getOption("messageid", CommandOption::getAsString);
|
||||||
|
String group = context.getSubcommandGroup();
|
||||||
|
String sub = context.getSubcommand();
|
||||||
|
|
||||||
|
return switch (group) {
|
||||||
|
case "emoji" -> executeEmoji(context, guildId, messageId, sub);
|
||||||
|
default -> CommandExecutionResult.none();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandExecutionResult executeEmoji(CommandContext context, String guildId, String messageId, String sub) {
|
||||||
|
String id = String.format("%s:%s", guildId, messageId);
|
||||||
|
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
|
||||||
|
|
||||||
|
switch (sub) {
|
||||||
|
case "list" -> {
|
||||||
|
List<EmojiReactionRoleTask> tasks = TaskManager.getInstance().findTasksByPartialId(EmojiReactionRoleTask.class, guildId);
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
return CommandExecutionResult.info("No tasks found for this guild");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("I have found the following tasks for this guild:");
|
||||||
|
|
||||||
|
for (EmojiReactionRoleTask item : tasks) {
|
||||||
|
Map<String, String> mapping = item.getEmojiMapping().entrySet().stream()
|
||||||
|
.map(entry -> {
|
||||||
|
Role role = context.getGuild().getRoleById(entry.getValue());
|
||||||
|
return role != null ? Map.entry(entry.getKey(), role.getAsMention()) : null;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
|
||||||
|
builder.append("\n- Task ID: ").append(item.getId()).append("\n")
|
||||||
|
.append(" Message: ")
|
||||||
|
.append(String.format(Message.JUMP_URL, item.getGuildId(), item.getChannelId(), item.getMessageId()))
|
||||||
|
.append("\n")
|
||||||
|
.append(" Emoji Mappings:\n");
|
||||||
|
|
||||||
|
if (mapping.isEmpty()) {
|
||||||
|
builder.append(" No emoji mappings found\n");
|
||||||
|
} else {
|
||||||
|
mapping.forEach((emoji, roleMention) ->
|
||||||
|
builder.append(" - ").append(emoji).append(" → ").append(roleMention).append("\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.success(builder.toString());
|
||||||
|
}
|
||||||
|
case "add" -> {
|
||||||
|
if (task == null) {
|
||||||
|
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
||||||
|
}
|
||||||
|
|
||||||
|
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
||||||
|
String role = context.getOption("role", CommandOption::getAsString);
|
||||||
|
task.addEmoji(emoji, role);
|
||||||
|
return CommandExecutionResult.success("Added emoji to message successfully!");
|
||||||
|
}
|
||||||
|
case "remove" -> {
|
||||||
|
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
||||||
|
|
||||||
|
if (task == null) {
|
||||||
|
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
||||||
|
}
|
||||||
|
|
||||||
|
task.removeEmoji(emoji);
|
||||||
|
return CommandExecutionResult.success("Removed emoji from message successfully!");
|
||||||
|
}
|
||||||
|
case "delete" -> {
|
||||||
|
if (task == null) {
|
||||||
|
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
||||||
|
}
|
||||||
|
|
||||||
|
task.delete();
|
||||||
|
return CommandExecutionResult.success("Unregistered the message ID successfully!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,15 +5,22 @@ package me.bobert.netneutrality.command.basic;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandContainer;
|
||||||
import me.bobert.netneutrality.command.CommandType;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.CommandManager;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.Task;
|
||||||
|
import me.bobert.netneutrality.tasks.help.BotInfo;
|
||||||
|
import me.bobert.netneutrality.tasks.help.CommandHelp;
|
||||||
|
import me.bobert.netneutrality.tasks.help.ReportBug;
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
import me.bobert.netneutrality.utils.MessageFunction;
|
||||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
import net.dv8tion.jda.api.components.actionrow.ActionRow;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
import net.dv8tion.jda.api.components.buttons.Button;
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -45,32 +52,33 @@ public class HelpCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
builder.addOption(OptionType.STRING, "command", "The name of the command you want help with", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandType getType() {
|
public CommandCategory getCategory() {
|
||||||
return CommandType.UTILITY;
|
return CommandCategory.UTILITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
if (context.getArgs().length == 0) {
|
String command = context.getOption("command", null, CommandOption::getAsString);
|
||||||
List<MessageEmbed> help = MessageFunction.buildHelpMessage(context.getGuildUser());
|
if (command == null) {
|
||||||
|
Button commands = Task.createButton(CommandHelp.class);
|
||||||
context.reply(new MessageCreateBuilder().addContent(context.getDiscordUser().getAsMention()).addContent(" help has been sent your way!").build());
|
Button info = Task.createButton(BotInfo.class);
|
||||||
context.replyPrivate(help);
|
Button reportbug = Task.createButton(ReportBug.class);
|
||||||
}
|
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));
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
CommandContainer container = CommandManager.getCommandContainer(command);
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
if (container.getCommand() == null) {
|
||||||
List<MessageEmbed> help = MessageFunction.buildHelpMessage(context.getGuildUser());
|
return CommandExecutionResult.warn("Command does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
context.sendMessageInPrivate(help);
|
context.replyEphemeral(MessageFunction.buildCommandHelpMessage(container));
|
||||||
return CommandExecutionResult.success("Help has been sent in DM!");
|
return CommandExecutionResult.none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,12 +5,11 @@ package me.bobert.netneutrality.command.basic;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandType;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.AbstractCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class PingCommand extends AbstractCommand {
|
public class PingCommand extends AbstractCommand {
|
||||||
|
|
||||||
@@ -40,25 +39,17 @@ public class PingCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandType getType() {
|
public CommandCategory getCategory() {
|
||||||
return CommandType.UTILITY;
|
return CommandCategory.UTILITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
if (context.getArgs().length == 0)
|
|
||||||
context.reply(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing()));
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
return CommandExecutionResult.success(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing()));
|
return CommandExecutionResult.success(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,34 +5,25 @@ package me.bobert.netneutrality.command.bot;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.command.CommandType;
|
import me.bobert.netneutrality.core.command.impl.AdminCommand;
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
import me.bobert.netneutrality.db.GSettings;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
import net.dv8tion.jda.api.entities.Role;
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
|
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
|
||||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.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.SlashCommandData;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ConfigureCommand extends AbstractCommand {
|
public class ConfigureCommand extends AdminCommand {
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needAdmin() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -55,15 +46,10 @@ public class ConfigureCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
return DefaultMemberPermissions.DISABLED;
|
builder.addSubcommands(
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
|
||||||
data.addSubcommands(
|
|
||||||
new SubcommandData("music-volume", "Volume for the music player")
|
new SubcommandData("music-volume", "Volume for the music player")
|
||||||
.addOptions(new OptionData(OptionType.INTEGER, "volume", "Integer between 0 and 100", true)
|
.addOptions(new OptionData(OptionType.INTEGER, "volume", "Playback volume for every session", true)
|
||||||
.setRequiredRange(0, 100)),
|
.setRequiredRange(0, 100)),
|
||||||
new SubcommandData("error-logging", "Send errors to #error and/or #log channel")
|
new SubcommandData("error-logging", "Send errors to #error and/or #log channel")
|
||||||
.addOption(OptionType.BOOLEAN, "debug", "Enable or disable the fonctionnality", true),
|
.addOption(OptionType.BOOLEAN, "debug", "Enable or disable the fonctionnality", true),
|
||||||
@@ -75,175 +61,40 @@ public class ConfigureCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandType getType() {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
return CommandType.ADMINISTRATION;
|
String sub = context.getSubcommand();
|
||||||
}
|
GuildService guildService = GuildService.get(context.getGuild().getId());
|
||||||
|
context.ephemeral();
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
|
||||||
String[] args = context.getArgs();
|
|
||||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
|
|
||||||
// Role for music commands
|
|
||||||
if (context.getArgument(0).equals("music-role")) {
|
|
||||||
if (args.length == 1) {
|
|
||||||
String roleDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE);
|
|
||||||
Role role = null;
|
|
||||||
if (!roleDb.isEmpty()) {
|
|
||||||
role = context.getGuild().getRoleById(roleDb);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.reply("Current role needed to use music commands (default is everyone): %s", (role == null ? "Everyone" : role.getAsMention()));
|
|
||||||
context.reply("Reset role using 'conf music-role reset' or set it using 'conf musicrole @'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 2) {
|
|
||||||
if (context.getArgument(1).equals("reset")) {
|
|
||||||
if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE).isEmpty()) {
|
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.reply("Successful reset of the role");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Role> roles = context.getMessage().getMentions().getRoles();
|
|
||||||
if (roles.size() == 1) {
|
|
||||||
Role role = roles.get(0);
|
|
||||||
if (role.getGuild().getId().equals(context.getGuild().getId())) {
|
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, role.getId());
|
|
||||||
context.reply("Successfully set %s as the role needed for music command", role.getAsMention());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Role for music commands
|
|
||||||
if (context.getArgument(0).equals("music-channel")) {
|
|
||||||
if (args.length == 1) {
|
|
||||||
String channelDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
|
|
||||||
TextChannel channel = null;
|
|
||||||
if (!channelDb.isEmpty()) {
|
|
||||||
channel = context.getGuild().getTextChannelById(channelDb);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.reply("Current channel where music commands can be executed (default is everywhere): %s", (channel == null ? "Everywhere" : channel.getAsMention()));
|
|
||||||
context.reply("Reset channel using 'conf music-channel reset' or set it using 'conf music-channel #'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 2) {
|
|
||||||
if (context.getArgument(1).equals("reset")) {
|
|
||||||
if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL).isEmpty()) {
|
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.reply("Successful reset of the channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TextChannel> channels = context.getMessage().getMentions().getChannels(TextChannel.class);
|
|
||||||
if (channels.size() == 1) {
|
|
||||||
TextChannel channel = channels.get(0);
|
|
||||||
if (channel.getGuild().getId().equals(context.getGuild().getId())) {
|
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId());
|
|
||||||
context.reply("Successfully set %s as the channel required to execute music command", channel.getAsMention());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default volume for music player
|
|
||||||
if (context.getArgument(0).equals("music-volume")) {
|
|
||||||
if (args.length == 1) {
|
|
||||||
int volume = Integer.parseInt(guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_VOLUME));
|
|
||||||
|
|
||||||
context.reply("Default volume for music player: %d", volume);
|
|
||||||
context.reply("Set using 'conf music-volume <value between 0 and 100>'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 2) {
|
|
||||||
try {
|
|
||||||
int volume = Integer.parseInt(args[1]);
|
|
||||||
|
|
||||||
if (volume > 0 && volume <= 100) {
|
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
|
|
||||||
MusicManager.get(context.getGuild()).setVolume(volume);
|
|
||||||
|
|
||||||
context.reply("Default volume for music player is now %d", volume);
|
|
||||||
} else
|
|
||||||
throw new NumberFormatException();
|
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
context.reply("Not a value between 0 and 100");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug
|
|
||||||
if (context.getArgument(0).equals("debug")) {
|
|
||||||
if (args.length == 1) {
|
|
||||||
boolean debug = guildManager.isDebugEnabled();
|
|
||||||
|
|
||||||
context.reply("Error logging is currently %s for this guild.", (debug ? "enabled" : "disabled"));
|
|
||||||
context.reply("Use 'conf debug 0/1' to toggle between states (0 = disabled, 1 = enabled).");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.length == 2) {
|
|
||||||
String str = args[1];
|
|
||||||
boolean debug = guildManager.isDebugEnabled();
|
|
||||||
if (str.equals("0") || str.equalsIgnoreCase("disable")) {
|
|
||||||
debug = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str.equals("1") || str.equalsIgnoreCase("enable")) {
|
|
||||||
debug = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
guildManager.setDebug(debug);
|
|
||||||
context.reply("Error logging is now %s for this guild", (debug ? "enabled" : "disabled"));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
String sub = context.getEvent().getFullCommandName().split(" ")[1];
|
|
||||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
|
||||||
context.ackAndWait(true);
|
|
||||||
|
|
||||||
if ("music-role".equals(sub)) {
|
if ("music-role".equals(sub)) {
|
||||||
Role role = context.getEvent().getOption("role", OptionMapping::getAsRole);
|
Role role = context.getOption("role", CommandOption::getAsRole);
|
||||||
assert role != null;
|
assert role != null;
|
||||||
|
|
||||||
if (role.getPosition() == -1) {
|
if (role.getPosition() == -1) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_ROLE, null);
|
||||||
|
|
||||||
return CommandExecutionResult.success("Everyone can now use music commands");
|
return CommandExecutionResult.success("Everyone can now use music commands");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role.getGuild().getId().equals(context.getGuild().getId())) {
|
if (role.getGuild().getId().equals(context.getGuild().getId())) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, role.getId());
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_ROLE, role.getId());
|
||||||
|
|
||||||
return CommandExecutionResult.success(String.format("Successfully set %s as the role needed for music command", role.getAsMention()));
|
return CommandExecutionResult.success(String.format("Successfully set %s as the role needed for music command", role.getAsMention()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("music-channel".equals(sub)) {
|
if ("music-channel".equals(sub)) {
|
||||||
GuildChannelUnion channel = context.getEvent().getOption("channel", OptionMapping::getAsChannel);
|
GuildChannelUnion channel = context.getOption("channel", CommandOption::getAsChannel);
|
||||||
|
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, null);
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_CHANNEL, null);
|
||||||
|
|
||||||
return CommandExecutionResult.success("No channel restrictions for music commands");
|
return CommandExecutionResult.success("No channel restrictions for music commands");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel instanceof TextChannel) {
|
if (channel instanceof TextChannel) {
|
||||||
if (channel.getGuild().getId().equals(context.getGuild().getId())) {
|
if (channel.getGuild().getId().equals(context.getGuild().getId())) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId());
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId());
|
||||||
|
|
||||||
return CommandExecutionResult.success(String.format("Successfully set %s as the channel required to execute music command", channel.getAsMention()));
|
return CommandExecutionResult.success(String.format("Successfully set %s as the channel required to execute music command", channel.getAsMention()));
|
||||||
}
|
}
|
||||||
@@ -252,22 +103,22 @@ public class ConfigureCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("music-volume".equals(sub)) {
|
if ("music-volume".equals(sub)) {
|
||||||
int volume = context.getEvent().getOption("volume", 30, OptionMapping::getAsInt);
|
int volume = context.getOption("volume", 30, CommandOption::getAsInt);
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
|
||||||
MusicManager.get(context.getGuild()).setVolume(volume);
|
MusicManager.get(context.getGuild()).setVolume(volume);
|
||||||
|
|
||||||
return CommandExecutionResult.success(String.format("Default volume for music player is now %d", volume));
|
return CommandExecutionResult.success(String.format("Default volume for music player is now %d", volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("debug".equals(sub)) {
|
if ("error-logging".equals(sub)) {
|
||||||
boolean debug = context.getEvent().getOption("debug", true, OptionMapping::getAsBoolean);
|
boolean debug = context.getOption("debug", true, CommandOption::getAsBoolean);
|
||||||
guildManager.setDebug(debug);
|
guildService.updateDebugMode(debug);
|
||||||
|
|
||||||
return CommandExecutionResult.success(String.format("Error logging is now %s for this guild", (debug ? "enabled" : "disabled")));
|
return CommandExecutionResult.success("Error logging is now %s for this guild", (debug ? "enabled" : "disabled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("show-all".equals(sub)) {
|
if ("show-all".equals(sub)) {
|
||||||
Map<String, String> settings = guildManager.getGuildConfigManager().getSettings();
|
Map<String, String> settings = guildService.getGuildSettingService().getSettings();
|
||||||
MessageCreateBuilder builder = new MessageCreateBuilder();
|
MessageCreateBuilder builder = new MessageCreateBuilder();
|
||||||
|
|
||||||
for (GSettings o : GSettings.values()) {
|
for (GSettings o : GSettings.values()) {
|
||||||
@@ -284,6 +135,6 @@ public class ConfigureCommand extends AbstractCommand {
|
|||||||
context.replyEphemeral(builder.build());
|
context.replyEphemeral(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.error("Unknown subcommand: " + sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,26 +5,28 @@ package me.bobert.netneutrality.command.crypto;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
|
||||||
import me.bobert.netneutrality.command.CommandType;
|
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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.context.CommandContext;
|
||||||
import me.bobert.netneutrality.http.WebModule;
|
import me.bobert.netneutrality.http.WebModule;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class CryptoPriceCommand extends AbstractCommand {
|
public class CryptoPriceCommand extends AbstractCommand {
|
||||||
|
|
||||||
private final String baseURL = "https://min-api.cryptocompare.com/data/price";
|
private final String exchange = "Coinbase";
|
||||||
|
private final String baseURL = "https://min-api.cryptocompare.com/data/price?fsym=%s&tsyms=%s";
|
||||||
|
private final String exchangeApi = String.format("https://min-api.cryptocompare.com/data/v4/all/exchanges?e=%s", exchange);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean needAdmin() {
|
public boolean needAdmin() {
|
||||||
@@ -52,68 +54,27 @@ public class CryptoPriceCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
data.addOption(OptionType.STRING, "symbol", "The crypto symbol to fetch price for", true);
|
builder.addOptions(
|
||||||
data.addOption(OptionType.STRING, "currency", "In which currency should the price be displayed");
|
new OptionData(OptionType.STRING, "symbol", "The crypto symbol to fetch price for", true),
|
||||||
|
new OptionData(OptionType.STRING, "currency", "In which currency should the price be displayed")
|
||||||
|
.setRequiredLength(3, 3)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandType getType() {
|
public CommandCategory getCategory() {
|
||||||
return CommandType.CRYPTO;
|
return CommandCategory.UTILITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
String[] args = context.getArgs();
|
|
||||||
String currency = "USD";
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
if (args.length == 2) {
|
|
||||||
try {
|
|
||||||
currency = Currency.getInstance(args[1].toUpperCase()).getCurrencyCode();
|
|
||||||
} catch (IllegalArgumentException exception) {
|
|
||||||
|
|
||||||
return CommandExecutionResult.warn("Please enter a valid currency");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String crypto = args[0].toUpperCase();
|
|
||||||
String finalCurrency = currency;
|
|
||||||
context.getChannel().sendMessage("Retreiving price, please wait...").queue(t -> {
|
|
||||||
String price;
|
|
||||||
Locale usa = new Locale("en", "US");
|
Locale usa = new Locale("en", "US");
|
||||||
NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa);
|
NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa);
|
||||||
dollarFormat.setMaximumFractionDigits(24);
|
dollarFormat.setMaximumFractionDigits(24);
|
||||||
|
|
||||||
try {
|
String symbol = context.getOption("symbol", CommandOption::getAsString).toUpperCase();
|
||||||
WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, crypto, finalCurrency.toUpperCase())).request();
|
String currency = context.getOption("currency", "USD", CommandOption::getAsString).toUpperCase();
|
||||||
JSONObject json = http.getAsObject();
|
|
||||||
if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) {
|
|
||||||
t.editMessage("Crypto doesn't exist.. yet").queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
price = dollarFormat.format(json.getDouble(finalCurrency));
|
|
||||||
t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue();
|
|
||||||
|
|
||||||
} catch (JSONException | IllegalArgumentException | ArithmeticException e) {
|
|
||||||
t.editMessage(String.format("Error getting price of %s!", crypto)).queue();
|
|
||||||
LogHandler.log(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
Locale usa = new Locale("en", "US");
|
|
||||||
NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa);
|
|
||||||
dollarFormat.setMaximumFractionDigits(24);
|
|
||||||
|
|
||||||
String symbol = context.getEvent().getOption("symbol", OptionMapping::getAsString);
|
|
||||||
String currency = context.getEvent().getOption("currency", "USD", OptionMapping::getAsString);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
currency = Currency.getInstance(currency).getCurrencyCode();
|
currency = Currency.getInstance(currency).getCurrencyCode();
|
||||||
@@ -122,30 +83,50 @@ public class CryptoPriceCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String finalCurrency = currency.toUpperCase();
|
String finalCurrency = currency.toUpperCase();
|
||||||
context.getEvent().deferReply().queue(interactionHook -> {
|
context.async(asyncMessage -> {
|
||||||
try {
|
try {
|
||||||
|
if (!validateSymbol(symbol)) {
|
||||||
|
asyncMessage.edit("Crypto doesn't exist.. yet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, symbol, finalCurrency)).request();
|
WebModule http = new WebModule.Builder(String.format(baseURL, symbol, finalCurrency)).cache(30, TimeUnit.MINUTES).build().request();
|
||||||
JSONObject json = http.getAsObject();
|
JSONObject json = http.getAsObject();
|
||||||
|
|
||||||
if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) {
|
if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) {
|
||||||
interactionHook.editOriginal("Crypto doesn't exist.. yet").queue();
|
asyncMessage.edit("Crypto doesn't exist.. yet");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double value = json.getDouble(finalCurrency);
|
double value = json.getDouble(finalCurrency);
|
||||||
String price = dollarFormat.format(value);
|
String price = dollarFormat.format(value);
|
||||||
|
|
||||||
interactionHook.editOriginal(String.format("The current price of %s in %s is %s", symbol, finalCurrency, price)).queue();
|
asyncMessage.edit(String.format("The current price of %s in %s is %s", symbol, finalCurrency, price));
|
||||||
|
|
||||||
} catch (JSONException | IllegalArgumentException | ArithmeticException e) {
|
} catch (Exception e) {
|
||||||
interactionHook.editOriginal(String.format("Error getting price of %s!", symbol)).queue();
|
asyncMessage.edit(String.format("Error getting price of %s!", symbol));
|
||||||
LogHandler.log(e);
|
LogHandler.log(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean validateSymbol(String symbol) {
|
||||||
|
try {
|
||||||
|
WebModule http = new WebModule.Builder(exchangeApi).get().cache(1, TimeUnit.DAYS).build().request();
|
||||||
|
JSONObject json = http.getAsObject();
|
||||||
|
|
||||||
|
JSONObject pairs = json.getJSONObject("Data")
|
||||||
|
.getJSONObject("exchanges")
|
||||||
|
.getJSONObject(exchange)
|
||||||
|
.getJSONObject("pairs");
|
||||||
|
|
||||||
|
return pairs.has(symbol);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogHandler.log(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package me.bobert.netneutrality.command.git;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
|
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.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.http.WebModule;
|
||||||
|
import me.bobert.netneutrality.tasks.git.ReportIssueTask;
|
||||||
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ReportIssueCommand extends AbstractCommand {
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuildOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSlashCommandOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "report-bug";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Used to report a bug with the bot.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory() {
|
||||||
|
return CommandCategory.UTILITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
|
String gitlabHost = Settings.getInstance().get("GITLAB_HOST");
|
||||||
|
String gitlabToken = Settings.getInstance().get("GITLAB_TOKEN");
|
||||||
|
String projectId = Settings.getInstance().get("GITLAB_PROJECT_ID");
|
||||||
|
|
||||||
|
if (gitlabHost == null || gitlabToken == null || projectId == null) {
|
||||||
|
return CommandExecutionResult.error("Command is not configured, failed to create issue.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new WebModule.Builder(gitlabHost).ping().timeout(2, TimeUnit.SECONDS).build().request().isSuccess()) {
|
||||||
|
return CommandExecutionResult.task(new ReportIssueTask(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.error("Unable to create bug reports at this time, please try again later.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,15 +6,14 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandType;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class BitconnectCommand extends AbstractMusicCommand {
|
public class BitconnectCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -32,30 +31,22 @@ public class BitconnectCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandType getType() {
|
public CommandCategory getCategory() {
|
||||||
return CommandType.UNLISTED;
|
return CommandCategory.UNLISTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true);
|
context.ephemeral();
|
||||||
|
|
||||||
context.deleteMessage();
|
context.deleteMessage();
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
manager.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, true);
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
context.ackAndWait();
|
|
||||||
|
|
||||||
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true);
|
|
||||||
return CommandExecutionResult.success("Ready to invest?");
|
return CommandExecutionResult.success("Ready to invest?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,16 +6,14 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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.MusicUtils;
|
import me.bobert.netneutrality.utils.MusicUtils;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class ClearQueueCommand extends AbstractMusicCommand {
|
public class ClearQueueCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean needAdmin() {
|
public boolean needAdmin() {
|
||||||
@@ -38,62 +36,20 @@ public class ClearQueueCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (!MusicUtils.checkIfConnected(player, context))
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
|
|
||||||
if (context.getArgs().length == 0) {
|
|
||||||
context.reply("Select the track you want to delete from the queue (track# or all)");
|
|
||||||
context.reply(MessageFunction.buildViewQueueMessage(player, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.getArgs().length > 0) {
|
|
||||||
|
|
||||||
int queueSize = player.getQueueSize();
|
|
||||||
if (context.getArgument(0).equals("all")) {
|
|
||||||
|
|
||||||
if (queueSize > 0) {
|
|
||||||
player.getScheduler().getQueueManager().clearQueue();
|
|
||||||
context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : "")));
|
|
||||||
} else {
|
|
||||||
context.reply("Queue is empty!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
try {
|
|
||||||
int num = Integer.parseInt(context.getArgument(0));
|
|
||||||
|
|
||||||
if (num > 0) {
|
|
||||||
return player.getScheduler().getQueueManager().removeFromQueue(num);
|
|
||||||
} else {
|
|
||||||
context.reply("Number out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
context.reply("Please enter a number");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
context.ackAndWait();
|
context.ack();
|
||||||
|
|
||||||
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
|
return manager.getQueueManager().clearQueue();
|
||||||
|
}
|
||||||
|
|
||||||
if (!MusicUtils.checkIfConnected(manager, context))
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|
||||||
return manager.getScheduler().getQueueManager().clearQueue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,16 +6,15 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class CurrentlyPlayingCommand extends AbstractMusicCommand {
|
public class CurrentlyPlayingCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -33,29 +32,20 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
// no arguments
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(player, context) && player.isCurrentlyPlaying()) {
|
|
||||||
context.reply(MessageFunction.buildCurrentlyPlayingMessage(player));
|
|
||||||
} else {
|
|
||||||
context.reply("Bot isn't playing any tracks right now");
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
context.ackAndWait();
|
context.ack();
|
||||||
|
|
||||||
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
|
if (!manager.isCurrentlyPlaying()) {
|
||||||
|
return CommandExecutionResult.info("Bot isn't playing any tracks right now");
|
||||||
|
}
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context) && manager.isCurrentlyPlaying()) {
|
|
||||||
context.reply(MessageFunction.buildCurrentlyPlayingMessage(manager));
|
context.reply(MessageFunction.buildCurrentlyPlayingMessage(manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,13 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
import me.bobert.netneutrality.utils.MusicUtils;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class JoinCommand extends AbstractMusicCommand {
|
public class JoinCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -33,55 +30,13 @@ public class JoinCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
if (MusicUtils.connect(context)) {
|
||||||
|
|
||||||
if (context.getGuildUser().getVoiceState() == null) {
|
|
||||||
throw new CommandExecutionException("Voice state is null, this should not happen", context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.getGuildUser().getVoiceState().inAudioChannel()) {
|
|
||||||
return CommandExecutionResult.warn("You are not in a voice channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player.isConnected()) {
|
|
||||||
return CommandExecutionResult.warn("Player is already in a voice channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel();
|
|
||||||
if (vChannel != null) {
|
|
||||||
player.connect(vChannel);
|
|
||||||
context.reply("Joining current voice channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
context.ackAndWait();
|
|
||||||
|
|
||||||
if (context.getGuildUser().getVoiceState() == null) {
|
|
||||||
throw new CommandExecutionException("Voice state is null, this should not happen", context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.getGuildUser().getVoiceState().inAudioChannel()) {
|
|
||||||
return CommandExecutionResult.warn("You are not in a voice channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player.isConnected()) {
|
|
||||||
return CommandExecutionResult.warn("Player is already in a voice channel");
|
|
||||||
}
|
|
||||||
|
|
||||||
VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel();
|
|
||||||
if (vChannel != null) {
|
|
||||||
player.connect(vChannel);
|
|
||||||
context.reply("Joining current voice channel");
|
context.reply("Joining current voice channel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,14 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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 net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class LeaveCommand extends AbstractMusicCommand {
|
public class LeaveCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -32,30 +31,17 @@ public class LeaveCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(player, context)) {
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
player.leave();
|
|
||||||
context.reply("Leaving voice channel and clearing queue");
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
context.ackAndWait();
|
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(player, context)) {
|
|
||||||
player.leave();
|
|
||||||
context.reply("Leaving voice channel and clearing queue");
|
context.reply("Leaving voice channel and clearing queue");
|
||||||
|
manager.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|||||||
@@ -6,16 +6,15 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
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.OptionMapping;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class PlayCommand extends AbstractMusicCommand {
|
public class PlayCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -33,28 +32,17 @@ public class PlayCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
data.addOption(OptionType.STRING, "query", "URL or search query", true);
|
builder.addOption(OptionType.STRING, "query", "URL or search query", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (context.getArgs().length > 0) {
|
|
||||||
player.play(context.getMessageText(), context, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
String query = context.getEvent().getOption("query", OptionMapping::getAsString);
|
String query = context.getOption("query", CommandOption::getAsString);
|
||||||
context.ackAndWait();
|
context.ack();
|
||||||
|
|
||||||
manager.play(query, context, false, false);
|
manager.play(query, context);
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,28 +5,31 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
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;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.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.SlashCommandData;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||||
|
|
||||||
public class QueueCommand extends AbstractMusicCommand {
|
public class QueueCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandName() {
|
public String getCommandName() {
|
||||||
return "queue";
|
return "queue";
|
||||||
@@ -38,13 +41,8 @@ public class QueueCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
return DefaultMemberPermissions.DISABLED;
|
builder.addSubcommands(
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
|
||||||
data.addSubcommands(
|
|
||||||
new SubcommandData("clear-all", "Clear all queued tracks"),
|
new SubcommandData("clear-all", "Clear all queued tracks"),
|
||||||
new SubcommandData("delete-track", "Remove a single track from the queue")
|
new SubcommandData("delete-track", "Remove a single track from the queue")
|
||||||
.addOptions(new OptionData(OptionType.INTEGER, "position", "Position of track", true)
|
.addOptions(new OptionData(OptionType.INTEGER, "position", "Position of track", true)
|
||||||
@@ -59,101 +57,45 @@ public class QueueCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (!MusicUtils.checkIfConnected(player, context))
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
|
|
||||||
if (context.getArgs().length == 0) {
|
|
||||||
context.reply("Select the track you want to delete from the queue (track# or all)");
|
|
||||||
context.reply(MessageFunction.buildViewQueueMessage(player, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.getArgs().length > 0) {
|
|
||||||
|
|
||||||
int queueSize = player.getQueueSize();
|
|
||||||
if (context.getArgument(0).equals("all")) {
|
|
||||||
|
|
||||||
if (queueSize > 0) {
|
|
||||||
player.getScheduler().getQueueManager().clearQueue();
|
|
||||||
context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : "")));
|
|
||||||
} else {
|
|
||||||
context.reply("Queue is empty!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
try {
|
|
||||||
int num = Integer.parseInt(context.getArgument(0));
|
|
||||||
|
|
||||||
if (num > 0) {
|
|
||||||
return player.getScheduler().getQueueManager().removeFromQueue(num);
|
|
||||||
} else {
|
|
||||||
context.reply("Number out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
context.reply("Please enter a number");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
context.ackAndWait();
|
String sub = context.getSubcommand();
|
||||||
String sub = context.getEvent().getFullCommandName().split(" ")[1];
|
context.ack();
|
||||||
|
|
||||||
if (!MusicUtils.checkIfConnected(manager, context)) {
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (sub) {
|
switch (sub) {
|
||||||
case "clear-all" -> {
|
case "clear-all" -> {
|
||||||
return manager.getScheduler().getQueueManager().clearQueue();
|
return manager.getQueueManager().clearQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
case "delete-track" -> {
|
case "delete-track" -> {
|
||||||
int position = context.getEvent().getOption("position", 0, OptionMapping::getAsInt);
|
int position = context.getOption("position", 0, CommandOption::getAsInt);
|
||||||
return manager.getScheduler().getQueueManager().removeFromQueue(position);
|
return manager.getQueueManager().removeFromQueue(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager, true));
|
case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager));
|
||||||
|
|
||||||
case "shuffle" -> {
|
case "shuffle" -> {
|
||||||
return manager.getScheduler().getQueueManager().shuffle();
|
return manager.getQueueManager().shuffle();
|
||||||
}
|
}
|
||||||
|
|
||||||
case "from-user" -> {
|
case "from-user" -> {
|
||||||
Member user = context.getEvent().getOption("user", OptionMapping::getAsMember);
|
Member user = context.getOption("user", CommandOption::getAsMember);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
context.reply("The user isn't from this guild");
|
return CommandExecutionResult.warn("The user isn't from this guild");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.reply(MessageFunction.buildUserTracksMessage(manager, user));
|
context.reply(MessageFunction.buildUserTracksMessage(manager, user));
|
||||||
}
|
}
|
||||||
|
|
||||||
case "remove-from-user" -> {
|
case "remove-from-user" -> {
|
||||||
Member member = context.getEvent().getOption("user", OptionMapping::getAsMember);
|
Member member = context.getOption("user", CommandOption::getAsMember);
|
||||||
if (member == null) {
|
if (member == null) {
|
||||||
context.reply("The user isn't from this guild");
|
return CommandExecutionResult.warn("The user isn't from this guild");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = manager.getScheduler()
|
return manager.getQueueManager().removeTracksFromUser(member);
|
||||||
.getQueueManager()
|
|
||||||
.getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester()))
|
|
||||||
.size();
|
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
context.reply("No tracks added by user %s", member.getEffectiveName());
|
|
||||||
} else {
|
|
||||||
manager.getScheduler().getQueueManager().removeTracksFromUser(member);
|
|
||||||
context.reply("Removed all tracks from user %s", member.getEffectiveName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package me.bobert.netneutrality.command.music;/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
|
import me.bobert.netneutrality.utils.MusicUtils;
|
||||||
|
|
||||||
|
public class ResumeCommand extends MusicCommand {
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "resume";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Resume the music player at the next track if it was stopped using the command /stop";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
|
|
||||||
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
|
return manager.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,14 +6,14 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
|
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 net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class ShuffleCommand extends AbstractMusicCommand {
|
public class ShuffleCommand extends MusicCommand {
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return true;
|
return true;
|
||||||
@@ -30,17 +30,17 @@ public class ShuffleCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
context.ackAndWait();
|
context.ack();
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
return manager.getScheduler().getQueueManager().shuffle();
|
return manager.getQueueManager().shuffle();
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|||||||
@@ -6,19 +6,18 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
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;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
|
||||||
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;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class SkipCommand extends AbstractMusicCommand {
|
public class SkipCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -36,47 +35,23 @@ public class SkipCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
data.addOptions(new OptionData(OptionType.INTEGER, "skip", "Number of tracks to skip")
|
builder.addOptions(new OptionData(OptionType.INTEGER, "skip", "Number of tracks to skip")
|
||||||
.setMinValue(1));
|
.setMinValue(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (!MusicUtils.checkIfConnected(player, context))
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
|
|
||||||
if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) {
|
|
||||||
if (UserFunction.isAdmin(context.getGuildUser()) && context.getArgs().length > 0) {
|
|
||||||
try {
|
|
||||||
int skips = Integer.parseInt(context.getArgument(0));
|
|
||||||
player.getScheduler().getQueueManager().skip(skips);
|
|
||||||
|
|
||||||
context.reply("Skipping %s track%s", skips, (skips > 1 ? "s" : ""));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
context.reply("Please enter how many track(s) to skip");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
player.getScheduler().getQueueManager().skip(1);
|
|
||||||
context.reply("Skipping current track");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
player.voteSkip(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
int skips = context.getEvent().getOption("skip", 1, OptionMapping::getAsInt);
|
context.ack();
|
||||||
context.ackAndWait();
|
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
return manager.getScheduler().getQueueManager().skip(skips);
|
int skips = context.getOption("skip", 1, CommandOption::getAsInt);
|
||||||
|
if (UserFunction.isAdmin(context) || MusicUtils.isUserDj(context)) {
|
||||||
|
return manager.getQueueManager().skip(skips);
|
||||||
|
} else {
|
||||||
|
return manager.voteSkip(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|||||||
@@ -6,16 +6,15 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class StopCommand extends AbstractMusicCommand {
|
public class StopCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -33,34 +32,20 @@ public class StopCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
|
context.ack();
|
||||||
|
|
||||||
if (!MusicUtils.checkIfConnected(player, context))
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
return CommandExecutionResult.none();
|
if (UserFunction.isAdmin(context)) {
|
||||||
|
manager.stopTrack();
|
||||||
// Need a resume command before
|
|
||||||
if (UserFunction.isAdmin(context.getGuildUser())) {
|
|
||||||
player.stopTrack();
|
|
||||||
context.reply("Stopping current track");
|
context.reply("Stopping current track");
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
|
||||||
context.ackAndWait();
|
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
|
||||||
manager.stopTrack();
|
|
||||||
return CommandExecutionResult.success("Stopping music playback");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|||||||
@@ -5,16 +5,15 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class ViewQueueCommand extends AbstractMusicCommand {
|
public class ViewQueueCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -32,39 +31,22 @@ public class ViewQueueCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(player, context)) {
|
|
||||||
int queueSize = player.getQueueSize();
|
|
||||||
if (queueSize > 0) {
|
|
||||||
context.reply(MessageFunction.buildViewQueueMessage(player, true));
|
|
||||||
} else {
|
|
||||||
context.reply("Queue is empty!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
context.ackAndWait();
|
context.ack();
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
int queueSize = manager.getQueueSize();
|
int queueSize = manager.getQueueSize();
|
||||||
|
if (queueSize == 0) {
|
||||||
if (queueSize > 0) {
|
|
||||||
context.reply(MessageFunction.buildViewQueueMessage(manager, true));
|
|
||||||
} else {
|
|
||||||
return CommandExecutionResult.warn("Queue is empty!");
|
return CommandExecutionResult.warn("Queue is empty!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.reply(MessageFunction.buildViewQueueMessage(manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|||||||
@@ -6,19 +6,18 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
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;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
|
||||||
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;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class VolumeCommand extends AbstractMusicCommand {
|
public class VolumeCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -36,45 +35,22 @@ public class VolumeCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
data.addOptions(new OptionData(OptionType.INTEGER, "volume", "Playback volume for this session", true)
|
builder.addOptions(new OptionData(OptionType.INTEGER, "volume", "Playback volume for this session", true)
|
||||||
.setRequiredRange(0, 100));
|
.setRequiredRange(0, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager player = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (!MusicUtils.checkIfConnected(player, context))
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
|
|
||||||
if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) {
|
|
||||||
try {
|
|
||||||
int volume = Integer.parseInt(context.getArgument(0));
|
|
||||||
|
|
||||||
if (volume > 0 && volume <= 100) {
|
|
||||||
player.setVolume(volume);
|
|
||||||
context.reply("Volume set to %s", volume);
|
|
||||||
} else
|
|
||||||
throw new NumberFormatException();
|
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
context.reply("Please enter a value from 0 to 100");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
int volume = context.getEvent().getOption("volume", 30, OptionMapping::getAsInt);
|
int volume = context.getOption("volume", 30, CommandOption::getAsInt);
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
|
if (UserFunction.isAdmin(context) || MusicUtils.isUserDj(context)) {
|
||||||
manager.setVolume(volume);
|
manager.setVolume(volume);
|
||||||
return CommandExecutionResult.success(String.format("Volume set to %s", volume));
|
return CommandExecutionResult.success(String.format("Volume set to %s", volume));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,14 @@ package me.bobert.netneutrality.command.music;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.impl.MusicCommand;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
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 net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
|
||||||
|
|
||||||
public class VoteSkipCommand extends AbstractMusicCommand {
|
public class VoteSkipCommand extends MusicCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -31,26 +30,15 @@ public class VoteSkipCommand extends AbstractMusicCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
if (MusicUtils.checkIfConnected(context)) {
|
||||||
manager.voteSkip(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
|
||||||
|
|
||||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
|
||||||
return manager.voteSkip(context);
|
return manager.voteSkip(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package me.bobert.netneutrality.command.react;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
||||||
|
import me.bobert.netneutrality.tasks.reaction.EmojiRoleFirstStep;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
public class CreateEmojiReaction extends MessageMenuCommand {
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(Message target, CommandContext context) {
|
||||||
|
EmojiReactionRoleTask task = TaskManager.getInstance().findTaskByPartialId(EmojiReactionRoleTask.class, target.getId()).orElse(null);
|
||||||
|
|
||||||
|
if (task != null) {
|
||||||
|
context.replyEphemeral("This message is already registered with the bot, please use the modify command instead");
|
||||||
|
return CommandExecutionResult.none();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.task(new EmojiRoleFirstStep(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "Create emoji role mapping";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package me.bobert.netneutrality.command.react;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
||||||
|
import me.bobert.netneutrality.tasks.reaction.EmojiRoleModify;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
public class ModifyEmojiReaction extends MessageMenuCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(Message target, CommandContext context) {
|
||||||
|
EmojiReactionRoleTask task = TaskManager.getInstance().findTaskByPartialId(EmojiReactionRoleTask.class, target.getId()).orElse(null);
|
||||||
|
if (task == null) {
|
||||||
|
context.replyEphemeral("This message is not registered with the bot, please use the create command instead");
|
||||||
|
return CommandExecutionResult.none();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.task(new EmojiRoleModify(target, task));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "Modify emoji role mapping";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package me.bobert.netneutrality.context;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
|
import net.dv8tion.jda.api.modals.Modal;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageEditData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class AsyncContext extends AbstractContext {
|
||||||
|
private final InteractionHook hook;
|
||||||
|
private final Message message;
|
||||||
|
|
||||||
|
public AsyncContext(InteractionHook hook) {
|
||||||
|
super(hook.getJDA(), hook.getInteraction().getGuild(), hook.getInteraction().getMessageChannel(), hook.getInteraction().getMember(), hook.getInteraction().getUser());
|
||||||
|
this.hook = hook;
|
||||||
|
this.message = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncContext(Message message) {
|
||||||
|
super(message.getJDA(), message.isFromGuild() ? message.getGuild() : null, message.getChannel(), message.getMember(), message.getAuthor());
|
||||||
|
this.hook = null;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageId() {
|
||||||
|
if (hook != null) {
|
||||||
|
return hook.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return message.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
if (hook != null) {
|
||||||
|
return hook.getInteraction().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("async:%s", message.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return ChannelType.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionHook getInteractionHook() {
|
||||||
|
return hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAcknowledged() {
|
||||||
|
if (hook != null) {
|
||||||
|
return hook.getInteraction().isAcknowledged();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFromGuild() {
|
||||||
|
return message != null ? message.isFromGuild() : hook.getInteraction().isFromGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
if (hook != null) {
|
||||||
|
hook.deleteOriginal().queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message != null && getType() != ChannelType.PRIVATE) {
|
||||||
|
message.delete().queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reply(Modal modal) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (hook != null) {
|
||||||
|
hook.sendMessage(messageCreateData).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message != null) {
|
||||||
|
message.reply(messageCreateData).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void defer(boolean ephemeral, Consumer<AsyncContext> consumer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void ack(boolean ephemeral) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void edit(MessageEditData messageEditData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (hook != null) {
|
||||||
|
hook.editOriginal(messageEditData).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message != null) {
|
||||||
|
message.editMessage(messageEditData).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package me.bobert.netneutrality.context;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.AbstractContext;
|
||||||
|
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.channel.middleman.GuildMessageChannel;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class MusicChannelContext extends AbstractContext implements NoMessageContext {
|
||||||
|
|
||||||
|
public MusicChannelContext(GuildMessageChannel channel, Member member) {
|
||||||
|
super(channel.getJDA(), channel.getGuild(), channel, member, member.getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return String.format("music:%s", getChannel().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFromGuild() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (getChannel().canTalk()) {
|
||||||
|
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
package me.bobert.netneutrality.context.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
import me.bobert.netneutrality.core.command.options.MessageCommandOption;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.MessageContext;
|
||||||
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
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.middleman.GuildChannel;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.Command;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
public class MessageCommandContext extends CommandContext implements MessageContext {
|
||||||
|
private final MessageReceivedEvent event;
|
||||||
|
private final Message message;
|
||||||
|
|
||||||
|
public MessageCommandContext(MessageReceivedEvent event) {
|
||||||
|
super(event.getJDA(), event.isFromGuild() ? event.getGuild() : null, event.getChannel(), event.getMember(), event.getAuthor());
|
||||||
|
this.event = event;
|
||||||
|
this.message = event.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageReceivedEvent getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return String.format("%s:%s:%s:%s",
|
||||||
|
getGuild() == null ? "PrivateMessage" : getGuild().getId(),
|
||||||
|
getChannel().getId(),
|
||||||
|
getDiscordUser().getId(),
|
||||||
|
message.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
|
List<CommandOption> options = new ArrayList<>();
|
||||||
|
int required = commandOptions.stream().filter(OptionData::isRequired).toList().size();
|
||||||
|
Mentions mentions = message.getMentions();
|
||||||
|
int roleMentionIndex = 0;
|
||||||
|
int userMentionIndex = 0;
|
||||||
|
int memberMentionIndex = 0;
|
||||||
|
int channelMentionIndex = 0;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (OptionData data : commandOptions) {
|
||||||
|
OptionType type = data.getType();
|
||||||
|
Set<ChannelType> requiredChannelTypes = data.getChannelTypes();
|
||||||
|
List<Command.Choice> choices = data.getChoices();
|
||||||
|
Object value = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (type) {
|
||||||
|
case STRING -> {
|
||||||
|
int currentOptionIndex = commandOptions.indexOf(data);
|
||||||
|
boolean nextOptionIsString = false;
|
||||||
|
if (currentOptionIndex + 1 < commandOptions.size()) {
|
||||||
|
OptionData nextOption = commandOptions.get(currentOptionIndex + 1);
|
||||||
|
nextOptionIsString = nextOption.getType() == OptionType.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder strBuilder = new StringBuilder();
|
||||||
|
if (!nextOptionIsString) {
|
||||||
|
while (index < args.length) {
|
||||||
|
String nextArg = args[index];
|
||||||
|
|
||||||
|
if ((nextArg.contains("<@") || nextArg.contains("<#") || nextArg.contains("<@&")) && nextArg.contains(">")) {
|
||||||
|
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()) {
|
||||||
|
List<Command.Choice> matched = choices.stream().filter(choice -> choice.getAsString().equalsIgnoreCase(str)).toList();
|
||||||
|
if (matched.isEmpty()) {
|
||||||
|
invalidateContext(data, "not a valid choice, valid choices are: " + validChoices(choices));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer min = data.getMinLength();
|
||||||
|
Integer max = data.getMaxLength();
|
||||||
|
|
||||||
|
if (min != null && str.length() < min) {
|
||||||
|
invalidateContext(data, "minimum length for argument is " + min);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max != null && str.length() > max) {
|
||||||
|
invalidateContext(data, "maximum length for argument is " + max);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = str;
|
||||||
|
}
|
||||||
|
case INTEGER -> {
|
||||||
|
long num = Long.parseLong(args[index]);
|
||||||
|
if (!choices.isEmpty()) {
|
||||||
|
List<Command.Choice> matched = choices.stream().filter(choice -> choice.getAsLong() == num).toList();
|
||||||
|
if (matched.isEmpty()) {
|
||||||
|
invalidateContext(data, "not a valid choice, valid choices are: " + validChoices(choices));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = num;
|
||||||
|
|
||||||
|
Number max = data.getMaxValue();
|
||||||
|
Number min = data.getMinValue();
|
||||||
|
|
||||||
|
if (min != null && num < min.longValue()) {
|
||||||
|
value = min.longValue();
|
||||||
|
} else if (max != null && num > max.longValue()) {
|
||||||
|
value = max.longValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case BOOLEAN -> {
|
||||||
|
String str = args[index];
|
||||||
|
if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("false")) {
|
||||||
|
value = Boolean.parseBoolean(str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateContext(data, "expected true or false");
|
||||||
|
}
|
||||||
|
case NUMBER -> {
|
||||||
|
double num = Double.parseDouble(args[index]);
|
||||||
|
if (!choices.isEmpty()) {
|
||||||
|
List<Command.Choice> matched = choices.stream().filter(choice -> choice.getAsDouble() == num).toList();
|
||||||
|
if (matched.isEmpty()) {
|
||||||
|
invalidateContext(data, "not a valid choice, valid choices are: " + validChoices(choices));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Number max = data.getMaxValue();
|
||||||
|
Number min = data.getMinValue();
|
||||||
|
value = num;
|
||||||
|
|
||||||
|
if (min != null && num < min.doubleValue()) {
|
||||||
|
value = min.doubleValue();
|
||||||
|
} else if (max != null && num > max.doubleValue()) {
|
||||||
|
value = max.doubleValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ROLE -> {
|
||||||
|
if (mentions.getRoles().size() > roleMentionIndex) {
|
||||||
|
Role role = mentions.getRoles().get(roleMentionIndex++);
|
||||||
|
if (role.getGuild().getId().equals(getGuild().getId())) {
|
||||||
|
value = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid role.");
|
||||||
|
}
|
||||||
|
case USER -> {
|
||||||
|
if (mentions.getMembers().size() > memberMentionIndex) {
|
||||||
|
Member member = mentions.getMembers().get(memberMentionIndex++);
|
||||||
|
if (member.getGuild().getId().equals(getGuild().getId())) {
|
||||||
|
value = member;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mentions.getUsers().size() > userMentionIndex) {
|
||||||
|
value = mentions.getUsers().get(userMentionIndex++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid user.");
|
||||||
|
}
|
||||||
|
case MENTIONABLE -> {
|
||||||
|
if (mentions.getRoles().size() > roleMentionIndex) {
|
||||||
|
Role role = mentions.getRoles().get(roleMentionIndex++);
|
||||||
|
if (role.getGuild().getId().equals(getGuild().getId())) {
|
||||||
|
value = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mentions.getMembers().size() > memberMentionIndex) {
|
||||||
|
Member member = mentions.getMembers().get(memberMentionIndex++);
|
||||||
|
if (member.getGuild().getId().equals(getGuild().getId())) {
|
||||||
|
value = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mentions.getUsers().size() > userMentionIndex) {
|
||||||
|
value = mentions.getUsers().get(userMentionIndex++);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid user or role.");
|
||||||
|
}
|
||||||
|
case CHANNEL -> {
|
||||||
|
if (mentions.getChannels().size() > channelMentionIndex) {
|
||||||
|
GuildChannel channel = mentions.getChannels().get(channelMentionIndex++);
|
||||||
|
|
||||||
|
if (!requiredChannelTypes.isEmpty() && !requiredChannelTypes.contains(channel.getType())) {
|
||||||
|
invalidateContext(data, "not a valid channel type at index " + (index + 1) +
|
||||||
|
" , valid types are: " + validTypes(requiredChannelTypes));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel.getGuild().getId().equals(getGuild().getId())) {
|
||||||
|
value = channel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateContext(data, "provided argument at position " + (index + 1) + " is not a valid channel.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
if (options.size() < required) {
|
||||||
|
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()
|
||||||
|
throw new IllegalArgumentException("Required option " + data.getName() + " was not found at index " + index);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
invalidateContext(data, "the argument provided is not a number.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
options.add(new MessageCommandOption(data, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String validChoices(List<Command.Choice> choices) {
|
||||||
|
StringJoiner joiner = new StringJoiner(", ");
|
||||||
|
for (Command.Choice choice : choices) {
|
||||||
|
joiner.add(choice.getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return joiner.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String validTypes(Set<ChannelType> types) {
|
||||||
|
StringJoiner joiner = new StringJoiner(", ");
|
||||||
|
for (ChannelType type : types) {
|
||||||
|
joiner.add(type.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return joiner.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseCommandName() {
|
||||||
|
String rawMessage = message.getContentRaw().trim();
|
||||||
|
String prefix = Settings.getInstance().getPrefix();
|
||||||
|
|
||||||
|
if (rawMessage.startsWith(prefix)) {
|
||||||
|
return getFullCommand().split(" ", 2)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullCommand() {
|
||||||
|
String rawMessage = message.getContentRaw().trim();
|
||||||
|
String prefix = Settings.getInstance().getPrefix();
|
||||||
|
|
||||||
|
return rawMessage.replaceFirst("^[" + prefix + "\\s]+", "").trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package me.bobert.netneutrality.context.command;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MessageMenuContext extends CommandContext implements InteractionContext {
|
||||||
|
private final MessageContextInteractionEvent event;
|
||||||
|
|
||||||
|
public MessageMenuContext(MessageContextInteractionEvent event) {
|
||||||
|
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return event.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
|
return List.of(); // no options for context menu commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseCommandName() {
|
||||||
|
return getFullCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullCommand() {
|
||||||
|
return event.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message getTarget() {
|
||||||
|
return event.getTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package me.bobert.netneutrality.context.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
import me.bobert.netneutrality.core.command.options.SlashCommandOption;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SlashCommandContext extends CommandContext implements InteractionContext {
|
||||||
|
private final SlashCommandInteractionEvent event;
|
||||||
|
|
||||||
|
public SlashCommandContext(SlashCommandInteractionEvent event) {
|
||||||
|
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SlashCommandInteractionEvent getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return event.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
// not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
|
return event.getOptions().stream().map(SlashCommandOption::new).collect(Collectors.toUnmodifiableList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseCommandName() {
|
||||||
|
return event.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullCommand() {
|
||||||
|
return event.getFullCommandName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSubcommandGroup() {
|
||||||
|
return event.getSubcommandGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSubcommand() {
|
||||||
|
return event.getSubcommandName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package me.bobert.netneutrality.context.command;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UserMenuContext extends CommandContext implements InteractionContext {
|
||||||
|
private final UserContextInteractionEvent event;
|
||||||
|
|
||||||
|
public UserMenuContext(UserContextInteractionEvent event) {
|
||||||
|
super(event.getJDA(), event.getGuild(), (MessageChannel) event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return event.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
|
return List.of(); // no options for context menu commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseCommandName() {
|
||||||
|
return getFullCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullCommand() {
|
||||||
|
return event.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getTarget() {
|
||||||
|
return event.getTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package me.bobert.netneutrality.context.tasks;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
|
|
||||||
|
public class ButtonTaskContext extends TaskContext implements InteractionContext {
|
||||||
|
private final ButtonInteractionEvent event;
|
||||||
|
|
||||||
|
public ButtonTaskContext(ButtonInteractionEvent event) {
|
||||||
|
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ButtonInteractionEvent getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return event.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return event.getComponentId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package me.bobert.netneutrality.context.tasks;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
|
import me.bobert.netneutrality.core.context.NoMessageContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import net.dv8tion.jda.api.components.MessageTopLevelComponent;
|
||||||
|
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class EmojiTaskContext extends TaskContext implements NoMessageContext {
|
||||||
|
public static final String ID = "et:%s:%s";
|
||||||
|
private final MessageReactionAddEvent event;
|
||||||
|
|
||||||
|
public EmojiTaskContext(MessageReactionAddEvent event) {
|
||||||
|
super(event.getJDA(), event.isFromGuild() ? event.getGuild() : null, event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
addContextData("emoji", event.getEmoji());
|
||||||
|
addContextData("emoji-code", event.getEmoji().getAsReactionCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageReactionAddEvent getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return String.format(ID, getGuild().getId(), event.getMessageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFromGuild() {
|
||||||
|
return event.isFromGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (getChannel() != null && getChannel().canTalk()) {
|
||||||
|
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package me.bobert.netneutrality.context.tasks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.MessageContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageReference;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
|
public class MessageTaskContext extends TaskContext implements MessageContext {
|
||||||
|
private final MessageReceivedEvent event;
|
||||||
|
private final Message message;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public MessageTaskContext(MessageReceivedEvent event) {
|
||||||
|
super(event.getJDA(), event.isFromGuild() ? event.getGuild() : null, event.getChannel(), event.getMember(), event.getAuthor());
|
||||||
|
this.event = event;
|
||||||
|
this.message = event.getMessage();
|
||||||
|
|
||||||
|
MessageReference reference = message.getMessageReference();
|
||||||
|
this.id = reference == null ? event.getMessageId() : reference.getMessageId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replyEphemeral(MessageCreateData messageCreateData) {
|
||||||
|
if (getType() == ChannelType.PRIVATE) {
|
||||||
|
reply(messageCreateData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessageInPrivate(messageCreateData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package me.bobert.netneutrality.context.tasks;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
||||||
|
|
||||||
|
public class ModalTaskContext extends TaskContext implements InteractionContext {
|
||||||
|
private final ModalInteractionEvent event;
|
||||||
|
|
||||||
|
public ModalTaskContext(ModalInteractionEvent event) {
|
||||||
|
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
addContextData("values", event.getValues());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModalInteractionEvent getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return event.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return event.getModalId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ package me.bobert.netneutrality.core;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
import me.bobert.netneutrality.music.TrackScheduler;
|
import me.bobert.netneutrality.music.TrackScheduler;
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@@ -21,25 +21,28 @@ public class BotState {
|
|||||||
|
|
||||||
//TODO: is it needed?
|
//TODO: is it needed?
|
||||||
private static void saveGuildsState() {
|
private static void saveGuildsState() {
|
||||||
Map<String, GuildManager> managers = GuildManager.getManagers();
|
Map<String, GuildService> services = GuildService.getServices();
|
||||||
|
|
||||||
JSONObject main = new JSONObject();
|
JSONObject main = new JSONObject();
|
||||||
JSONArray guildsArray = new JSONArray();
|
JSONArray guildsArray = new JSONArray();
|
||||||
|
|
||||||
for (GuildManager manager : managers.values()) {
|
for (MusicManager manager : MusicManager.getManagers()) {
|
||||||
MusicManager musicManager = MusicManager.get(manager.getGuild());
|
TrackScheduler trackScheduler = manager.getScheduler();
|
||||||
TrackScheduler trackScheduler = musicManager.getScheduler();
|
}
|
||||||
|
|
||||||
|
for (GuildService service : services.values()) {
|
||||||
|
|
||||||
|
|
||||||
JSONObject guild = new JSONObject();
|
JSONObject guild = new JSONObject();
|
||||||
JSONObject music = new JSONObject();
|
JSONObject music = new JSONObject();
|
||||||
guild.put("id", manager.getGuild().getId());
|
guild.put("id", service.getEntity().getGuildId());
|
||||||
|
|
||||||
MessageChannel currentChannel = trackScheduler.getTextChannel();
|
// MessageChannel currentChannel = trackScheduler.getTextChannel();
|
||||||
if (currentChannel == null) {
|
// if (currentChannel == null) {
|
||||||
music.put("current_channel", "null");
|
// music.put("current_channel", "null");
|
||||||
} else {
|
// } else {
|
||||||
music.put("current_channel", currentChannel.getId());
|
// music.put("current_channel", currentChannel.getId());
|
||||||
}
|
// }
|
||||||
|
|
||||||
guild.put("music", music);
|
guild.put("music", music);
|
||||||
|
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
package me.bobert.netneutrality.core;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.sentry.Sentry;
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
|
||||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
|
||||||
import me.bobert.netneutrality.command.CommandManager;
|
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
|
||||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
|
||||||
import me.bobert.netneutrality.utils.UserFunction;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import static me.bobert.netneutrality.command.CommandExecutionResult.Result.ERROR;
|
|
||||||
import static me.bobert.netneutrality.command.CommandExecutionResult.Result.NOACTION;
|
|
||||||
|
|
||||||
public class CommandProcessor {
|
|
||||||
|
|
||||||
private final static Logger logger = LogManager.getLogger(CommandProcessor.class);
|
|
||||||
private final String commandName;
|
|
||||||
private final CommandContext context;
|
|
||||||
|
|
||||||
public CommandProcessor(String commandName, CommandContext context) {
|
|
||||||
this.commandName = commandName;
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispatch() {
|
|
||||||
|
|
||||||
if (!CommandManager.isValidCommand(commandName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractCommand command = CommandManager.getCommand(commandName);
|
|
||||||
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild().getId());
|
|
||||||
|
|
||||||
if (context instanceof SlashCommandContext slashCommandContext)
|
|
||||||
logger.debug("SlashCommandInteractionId: {}", slashCommandContext.getEvent().getInteraction().getId());
|
|
||||||
|
|
||||||
configureSentry(command);
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (context instanceof MessageCommandContext && command.needAdmin() && !UserFunction.isAdmin(context.getGuildUser())) {
|
|
||||||
context.reply("You are not powerful enough, scrub");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.canBeExecutedBy(context.getGuild(), context.getGuildUser())) {
|
|
||||||
if (command.canBeExecutedFrom(context.getGuild(), context.getChannel())) {
|
|
||||||
CommandExecutionResult commandExecutionResult = null;
|
|
||||||
|
|
||||||
if (context instanceof SlashCommandContext slashCommandContext) {
|
|
||||||
commandExecutionResult = command.execute(slashCommandContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context instanceof MessageCommandContext messageCommandContext) {
|
|
||||||
commandExecutionResult = command.execute(messageCommandContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandExecutionResult == null) {
|
|
||||||
throw new RuntimeException("Command type is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandExecutionResult.Result result = commandExecutionResult.getResult();
|
|
||||||
String message = commandExecutionResult.getMessage();
|
|
||||||
if (result == NOACTION) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == ERROR) {
|
|
||||||
Exception exception = commandExecutionResult.getException();
|
|
||||||
|
|
||||||
logger.error("Error from command {}: {}", command.getCommandName(), message, exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.reply(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: make better
|
|
||||||
} catch (CommandExecutionException exception) {
|
|
||||||
LogHandler.log(exception);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
LogHandler.log(new CommandExecutionException("Error during command execution", context, exception));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureSentry(AbstractCommand command) {
|
|
||||||
|
|
||||||
Sentry.configureScope(scope -> {
|
|
||||||
scope.setContexts("command", command.getCommandName());
|
|
||||||
scope.setContexts("isAdmin", UserFunction.isAdmin(context.getGuildUser()));
|
|
||||||
scope.setContexts("canBeExecuted", command.canBeExecutedBy(context.getGuild(), context.getGuildUser()));
|
|
||||||
scope.setTag("channel", context.getChannel().getId());
|
|
||||||
|
|
||||||
if (context instanceof SlashCommandContext slashCommandContext)
|
|
||||||
scope.setTag("interactionId", slashCommandContext.getEvent().getInteraction().getId());
|
|
||||||
|
|
||||||
if (context instanceof MessageCommandContext messageCommandContext)
|
|
||||||
scope.setTag("message", messageCommandContext.getMessage().getId());
|
|
||||||
|
|
||||||
scope.setTag("guild", context.getGuild().getId());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package me.bobert.netneutrality.core;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
|
||||||
import me.bobert.netneutrality.db.entities.OGuild;
|
|
||||||
import me.bobert.netneutrality.db.entities.OGuildSetting;
|
|
||||||
import me.bobert.netneutrality.utils.JpaUtils;
|
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.hibernate.Session;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class GuildConfigManager {
|
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(GuildConfigManager.class);
|
|
||||||
private final Guild guild;
|
|
||||||
private final OGuild guildEntity;
|
|
||||||
|
|
||||||
private final Map<String, String> settingsCache = new HashMap<>();
|
|
||||||
|
|
||||||
public GuildConfigManager(Guild guild, OGuild guildEntity) {
|
|
||||||
this.guild = guild;
|
|
||||||
this.guildEntity = guildEntity;
|
|
||||||
|
|
||||||
reloadSettingsCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getSettings() {
|
|
||||||
return settingsCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSetting(GSettings setting) {
|
|
||||||
String value = settingsCache.get(setting.getName());
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
value = setting.getDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setSettingValue(GSettings setting, String value) {
|
|
||||||
Session session = SessionManager.getSessionFactory().openSession();
|
|
||||||
JpaUtils.tryLoad(session, guildEntity, guildEntity.getGuildId());
|
|
||||||
|
|
||||||
OGuildSetting oGuildSetting = guildEntity.getSettingByName(setting.getName());
|
|
||||||
|
|
||||||
session.beginTransaction();
|
|
||||||
if (value == null || setting.getDefaultValue().equals(value)) {
|
|
||||||
if (oGuildSetting != null) {
|
|
||||||
session.remove(oGuildSetting);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (oGuildSetting == null) {
|
|
||||||
oGuildSetting = new OGuildSetting();
|
|
||||||
oGuildSetting.setGuild(guildEntity);
|
|
||||||
oGuildSetting.setName(setting.getName());
|
|
||||||
oGuildSetting.setValue(value);
|
|
||||||
|
|
||||||
session.persist(oGuildSetting);
|
|
||||||
} else {
|
|
||||||
oGuildSetting.setValue(value);
|
|
||||||
|
|
||||||
session.merge(oGuildSetting);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
|
|
||||||
reloadSettingsCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reloadSettingsCache() {
|
|
||||||
logger.debug("Reloading settings cache for guild: {}", guild.getId());
|
|
||||||
settingsCache.clear();
|
|
||||||
|
|
||||||
JpaUtils.getWithSession(guildEntity, session -> {
|
|
||||||
Set<OGuildSetting> settingSet = guildEntity.getGuildSettings();
|
|
||||||
|
|
||||||
for (OGuildSetting setting : settingSet) {
|
|
||||||
settingsCache.put(setting.getName(), setting.getValue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
package me.bobert.netneutrality.core;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.NetNeutrality;
|
|
||||||
import me.bobert.netneutrality.db.DBController;
|
|
||||||
import me.bobert.netneutrality.db.controllers.DBMessage;
|
|
||||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
|
||||||
import me.bobert.netneutrality.db.entities.OGuild;
|
|
||||||
import me.bobert.netneutrality.thread.ThreadManager;
|
|
||||||
import me.bobert.netneutrality.utils.JpaUtils;
|
|
||||||
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.channel.middleman.MessageChannel;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.hibernate.Session;
|
|
||||||
import org.hibernate.SessionFactory;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class GuildManager {
|
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(GuildManager.class);
|
|
||||||
private static final Map<String, GuildManager> managers = new ConcurrentHashMap<>();
|
|
||||||
private static final SessionFactory sessionFactory = SessionManager.getSessionFactory();
|
|
||||||
private final Set<String> blacklistCache = new HashSet<>();
|
|
||||||
private final GuildConfigManager guildConfigManager;
|
|
||||||
private final Guild guild;
|
|
||||||
private OGuild guildEntity;
|
|
||||||
|
|
||||||
private GuildManager(Guild guild) {
|
|
||||||
this.guild = guild;
|
|
||||||
this.guildEntity = loadGuildFromDb(guild.getId());
|
|
||||||
this.guildConfigManager = new GuildConfigManager(guild, this.guildEntity);
|
|
||||||
|
|
||||||
managers.put(guild.getId(), this);
|
|
||||||
reloadBlacklistCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
private GuildManager(String guildId) {
|
|
||||||
Guild temp = NetNeutrality.getJDA().getGuildById(guildId);
|
|
||||||
if (temp == null)
|
|
||||||
throw new IllegalArgumentException("Guild does not exist in current cache");
|
|
||||||
|
|
||||||
this.guild = temp;
|
|
||||||
this.guildEntity = loadGuildFromDb(guild.getId());
|
|
||||||
this.guildConfigManager = new GuildConfigManager(guild, this.guildEntity);
|
|
||||||
|
|
||||||
managers.put(guild.getId(), this);
|
|
||||||
reloadBlacklistCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GuildManager getManager(Guild guild) {
|
|
||||||
if (managers.containsKey(guild.getId())) {
|
|
||||||
return managers.get(guild.getId());
|
|
||||||
} else {
|
|
||||||
return new GuildManager(guild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GuildManager getManager(String guildId) {
|
|
||||||
if (managers.containsKey(guildId)) {
|
|
||||||
return managers.get(guildId);
|
|
||||||
} else {
|
|
||||||
return new GuildManager(guildId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter
|
|
||||||
public static Map<String, GuildManager> getManagers() {
|
|
||||||
return managers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guild getGuild() {
|
|
||||||
return guild;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OGuild getGuildEntity() {
|
|
||||||
return guildEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getBlacklistCache() {
|
|
||||||
return blacklistCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GuildConfigManager getGuildConfigManager() {
|
|
||||||
return guildConfigManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
public boolean isLoggingEnabled() {
|
|
||||||
return guildEntity.getLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDebugEnabled() {
|
|
||||||
return guildEntity.getDebug();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDebug(boolean toggle) {
|
|
||||||
if (toggle == isDebugEnabled())
|
|
||||||
return;
|
|
||||||
|
|
||||||
JpaUtils.updateWithTransaction(guildEntity, () -> guildEntity.setDebug(toggle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reloadBlacklistCache() {
|
|
||||||
logger.debug("Reloading blacklist cache for guild: {}", guild.getId());
|
|
||||||
blacklistCache.clear();
|
|
||||||
|
|
||||||
JpaUtils.getWithSession(guildEntity, session -> {
|
|
||||||
Set<OBlacklist> oBlacklistSet = guildEntity.getBlacklists();
|
|
||||||
|
|
||||||
for (OBlacklist bl : oBlacklistSet) {
|
|
||||||
if (bl.getEnabled()) {
|
|
||||||
blacklistCache.add(bl.getWord());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private OGuild loadGuildFromDb(String guildId) {
|
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
OGuild entity = session.get(OGuild.class, guildId);
|
|
||||||
|
|
||||||
if (entity == null) {
|
|
||||||
logger.debug("Guild[{}] doesn't exist in DB, creating entity..", guildId);
|
|
||||||
session.beginTransaction();
|
|
||||||
entity = new OGuild();
|
|
||||||
|
|
||||||
entity.setGuildId(guildId);
|
|
||||||
|
|
||||||
session.persist(entity);
|
|
||||||
session.getTransaction().commit();
|
|
||||||
|
|
||||||
guild.getTextChannels()
|
|
||||||
.stream()
|
|
||||||
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
|
||||||
.forEach(textChannel -> textChannel.sendMessage("Error logging enabled on channel " + textChannel.getAsMention()).queue());
|
|
||||||
}
|
|
||||||
|
|
||||||
session.close();
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reloadEntity() {
|
|
||||||
this.guildEntity = loadGuildFromDb(guild.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeFromDB() {
|
|
||||||
Session session = sessionFactory.openSession();
|
|
||||||
session.beginTransaction();
|
|
||||||
|
|
||||||
session.remove(guildEntity);
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
|
|
||||||
managers.remove(guild.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -7,9 +7,10 @@ package me.bobert.netneutrality.core;
|
|||||||
|
|
||||||
import io.sentry.Sentry;
|
import io.sentry.Sentry;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
import me.bobert.netneutrality.exceptions.CommandException;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
|
import me.bobert.netneutrality.exceptions.ContextException;
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
import me.bobert.netneutrality.utils.MessageFunction;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
@@ -18,7 +19,7 @@ import org.apache.logging.log4j.Logger;
|
|||||||
|
|
||||||
public class LogHandler {
|
public class LogHandler {
|
||||||
|
|
||||||
private final static Logger logger = LogManager.getLogger(LogHandler.class);
|
private static final Logger logger = LogManager.getLogger(LogHandler.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log an exception accordingly
|
* Log an exception accordingly
|
||||||
@@ -28,12 +29,10 @@ public class LogHandler {
|
|||||||
public static void log(Exception e) {
|
public static void log(Exception e) {
|
||||||
Sentry.captureException(e);
|
Sentry.captureException(e);
|
||||||
|
|
||||||
if (e instanceof CommandExecutionException exception) {
|
if (e instanceof ContextException exception) {
|
||||||
CommandContext context = exception.getCommandContext();
|
logInternally(exception);
|
||||||
|
tryLogToDebugChannel(exception);
|
||||||
logInternally(exception, context);
|
tryGracefulContextError(exception);
|
||||||
tryLogToDebugChannel(exception, context);
|
|
||||||
tryGracefulSlashContextError(exception, context);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -51,35 +50,48 @@ public class LogHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void sendMessageToDebugChannel(Guild guild, String message) {
|
public static void sendMessageToDebugChannel(Guild guild, String message) {
|
||||||
if (GuildManager.getManager(guild).isDebugEnabled()) {
|
|
||||||
guild.getTextChannels()
|
guild.getTextChannels()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
||||||
.filter(TextChannel::canTalk)
|
.filter(TextChannel::canTalk)
|
||||||
.forEach(textChannel -> textChannel.sendMessage(message).queue());
|
.forEach(textChannel -> textChannel.sendMessage(message).queue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void logInternally(ContextException exception) {
|
||||||
|
Context context = exception.getContext();
|
||||||
|
String guildId = context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId();
|
||||||
|
|
||||||
|
logger.error("Exception thrown from context (guild: {}, user: {}): {}", guildId,
|
||||||
|
context.getDiscordUser().getId(),
|
||||||
|
exception.getMessage()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (context instanceof CommandContext commandContext) {
|
||||||
|
logger.error("Caused by command: {}", commandContext.getCommand().getCommandName(), exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context instanceof TaskContext taskContext) {
|
||||||
|
logger.error("Caused by task with ID: {}", taskContext.getId(), exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void logInternally(CommandException exception, CommandContext context) {
|
private static void tryLogToDebugChannel(ContextException exception) {
|
||||||
logger.error("Exception thrown from command [{}] in guild [{}]: {}", context.getCommand().getCommandName(), context.getGuild().getId(), exception.getMessage(), exception);
|
Context context = exception.getContext();
|
||||||
}
|
|
||||||
|
|
||||||
private static void tryLogToDebugChannel(CommandException exception, CommandContext context) {
|
|
||||||
if (context.getGuild() != null) {
|
if (context.getGuild() != null) {
|
||||||
if (GuildManager.getManager(context.getGuild()).isDebugEnabled()) {
|
if (GuildService.get(context.getGuild()).isDebugEnabled()) {
|
||||||
context.getGuild()
|
context.getGuild()
|
||||||
.getTextChannels()
|
.getTextChannels()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
||||||
.filter(TextChannel::canTalk)
|
.filter(TextChannel::canTalk)
|
||||||
.forEach(textChannel -> textChannel.sendMessageEmbeds(MessageFunction.buildDebugMessage(context, exception)).queue());
|
.forEach(textChannel -> textChannel.sendMessageEmbeds(MessageFunction.buildDebugMessage(exception)).queue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void tryGracefulSlashContextError(CommandException exception, CommandContext context) {
|
private static void tryGracefulContextError(ContextException exception) {
|
||||||
if (context instanceof SlashCommandContext slashCommandContext) {
|
exception.getContext().reply(MessageFunction.buildSentryErrorMessage(exception));
|
||||||
slashCommandContext.reply(MessageFunction.buildSentryErrorMessage(exception));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||||
|
|
||||||
|
public interface Command {
|
||||||
|
enum Type {
|
||||||
|
SLASH, USER, MESSAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean needAdmin();
|
||||||
|
|
||||||
|
boolean isEnabled();
|
||||||
|
|
||||||
|
boolean isGuildOnly();
|
||||||
|
|
||||||
|
boolean isSlashCommand();
|
||||||
|
|
||||||
|
boolean isSlashCommandOnly();
|
||||||
|
|
||||||
|
String getCommandName();
|
||||||
|
|
||||||
|
String getDescription();
|
||||||
|
|
||||||
|
void registerCommandOptions(CommandOptionsBuilder builder);
|
||||||
|
|
||||||
|
DefaultMemberPermissions getSlashCommandPermission();
|
||||||
|
|
||||||
|
CommandCategory getCategory();
|
||||||
|
|
||||||
|
Type getType();
|
||||||
|
|
||||||
|
boolean canBeExecutedBy(Member member);
|
||||||
|
|
||||||
|
boolean canBeExecutedFrom(MessageChannel channel);
|
||||||
|
|
||||||
|
CommandExecutionResult execute(CommandContext context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum CommandCategory {
|
||||||
|
MUSIC("Music"), ADMINISTRATION("Administration"), UTILITY("Utility"), CRYPTO("Crypto"), UNLISTED("");
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
CommandCategory(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 CommandContainer {
|
||||||
|
public static final CommandContainer NULL_COMMAND = new CommandContainer(null);
|
||||||
|
private final Command command;
|
||||||
|
private CommandDataImpl commandData;
|
||||||
|
private List<OptionData> options;
|
||||||
|
private List<SubcommandData> subcommands;
|
||||||
|
private List<SubcommandGroupData> subcommandGroups;
|
||||||
|
|
||||||
|
public CommandContainer(Command command) {
|
||||||
|
this.command = command;
|
||||||
|
|
||||||
|
if (command != null) {
|
||||||
|
CommandOptionsBuilder commandOptionsBuilder = new CommandOptionsBuilder(command.getType(), command);
|
||||||
|
command.registerCommandOptions(commandOptionsBuilder);
|
||||||
|
initContainer(commandOptionsBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
-3
@@ -1,26 +1,34 @@
|
|||||||
package me.bobert.netneutrality.command;
|
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.tasks.Task;
|
||||||
|
|
||||||
public class CommandExecutionResult {
|
public class CommandExecutionResult {
|
||||||
|
|
||||||
public enum Result {
|
public enum Result {
|
||||||
SUCCESS, INFO, WARN, ERROR, NOACTION
|
SUCCESS, INFO, WARN, ERROR, NOACTION, TASK
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Result result;
|
private final Result result;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
private final Task task;
|
||||||
private final Exception exception;
|
private final Exception exception;
|
||||||
|
|
||||||
private CommandExecutionResult(Result result, String message, Exception exception) {
|
private CommandExecutionResult(Result result, String message, Task task, Exception exception) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
this.task = task;
|
||||||
this.exception = exception;
|
this.exception = exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CommandExecutionResult(Result result, String message, Exception exception) {
|
||||||
|
this(result, message, null, exception);
|
||||||
|
}
|
||||||
|
|
||||||
private CommandExecutionResult(Result result, String message) {
|
private CommandExecutionResult(Result result, String message) {
|
||||||
this(result, message, null);
|
this(result, message, null);
|
||||||
}
|
}
|
||||||
@@ -33,6 +41,10 @@ public class CommandExecutionResult {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task getTask() {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
public Exception getException() {
|
public Exception getException() {
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
@@ -41,6 +53,10 @@ public class CommandExecutionResult {
|
|||||||
return new CommandExecutionResult(Result.SUCCESS, message);
|
return new CommandExecutionResult(Result.SUCCESS, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CommandExecutionResult success(String format, Object... args) {
|
||||||
|
return success(String.format(format, args));
|
||||||
|
}
|
||||||
|
|
||||||
public static CommandExecutionResult info(String message) {
|
public static CommandExecutionResult info(String message) {
|
||||||
return new CommandExecutionResult(Result.INFO, message);
|
return new CommandExecutionResult(Result.INFO, message);
|
||||||
}
|
}
|
||||||
@@ -61,4 +77,7 @@ public class CommandExecutionResult {
|
|||||||
return new CommandExecutionResult(Result.NOACTION, null);
|
return new CommandExecutionResult(Result.NOACTION, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CommandExecutionResult task(Task task) {
|
||||||
|
return new CommandExecutionResult(Result.TASK, null, task, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.utils.ReflectionHelper;
|
||||||
|
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.SlashCommandData;
|
||||||
|
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class CommandManager {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(CommandManager.class);
|
||||||
|
public static final Map<String, CommandContainer> commands = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
logger.info("Loading commands..");
|
||||||
|
|
||||||
|
initCommands();
|
||||||
|
|
||||||
|
logger.info("Loaded {} commands!", commands.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CommandContainer getCommandContainer(String commandString) {
|
||||||
|
return commands.getOrDefault(commandString, CommandContainer.NULL_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initCommands() {
|
||||||
|
ReflectionHelper.loadCommands(Command.class, command -> {
|
||||||
|
if (!command.isEnabled()) {
|
||||||
|
logger.info("Skipping command {} as it is disabled", command.getClass().getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.getCommandName() == null) {
|
||||||
|
logger.error("Failed to load command {}: Command name is null", command.getClass().getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.getDescription() == null && command.getType() == Command.Type.SLASH) {
|
||||||
|
logger.error("Failed to load command {}: Command description is null", command.getClass().getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandContainer container = new CommandContainer(command);
|
||||||
|
if (!commands.containsKey(command.getCommandName())) {
|
||||||
|
commands.put(command.getCommandName(), container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerSlashCommands(JDA jda) {
|
||||||
|
CommandListUpdateAction commandListUpdateAction = jda.updateCommands();
|
||||||
|
List<CommandData> slashCommands = new ArrayList<>();
|
||||||
|
|
||||||
|
for (CommandContainer container : commands.values()) {
|
||||||
|
Command command = container.getCommand();
|
||||||
|
if (!command.isEnabled() || !command.isSlashCommand()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logger.debug("Registering slash command: {}", command.getCommandName());
|
||||||
|
|
||||||
|
try {
|
||||||
|
SlashCommandData data = container.getSlashCommandData();
|
||||||
|
data.setDefaultPermissions(command.getSlashCommandPermission());
|
||||||
|
data.setContexts(command.isGuildOnly() ? Set.of(InteractionContextType.GUILD) : Set.of(InteractionContextType.GUILD, InteractionContextType.BOT_DM));
|
||||||
|
|
||||||
|
slashCommands.add(data);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error registering slash command: {}", command.getCommandName(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
commandListUpdateAction.addCommands(slashCommands).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.build.Commands;
|
||||||
|
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.LocalizationMap;
|
||||||
|
import net.dv8tion.jda.api.utils.data.DataObject;
|
||||||
|
import net.dv8tion.jda.internal.interactions.CommandDataImpl;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class CommandOptionsBuilder implements SlashCommandData {
|
||||||
|
private CommandDataImpl commandData;
|
||||||
|
|
||||||
|
public CommandOptionsBuilder(Command.Type type, Command command) {
|
||||||
|
|
||||||
|
if (type == Command.Type.SLASH) {
|
||||||
|
commandData = (CommandDataImpl) Commands.slash(command.getCommandName(), command.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Command.Type.USER) {
|
||||||
|
commandData = (CommandDataImpl) Commands.user(command.getCommandName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Command.Type.MESSAGE) {
|
||||||
|
commandData = (CommandDataImpl) Commands.message(command.getCommandName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandDataImpl build() {
|
||||||
|
return commandData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setLocalizationFunction(@Nonnull LocalizationFunction localizationFunction) {
|
||||||
|
return commandData.setLocalizationFunction(localizationFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setName(@Nonnull String name) {
|
||||||
|
return commandData.setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setNameLocalization(@Nonnull DiscordLocale locale, @Nonnull String name) {
|
||||||
|
return commandData.setNameLocalization(locale, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setNameLocalizations(@Nonnull Map<DiscordLocale, String> map) {
|
||||||
|
return commandData.setNameLocalizations(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setDefaultPermissions(@Nonnull DefaultMemberPermissions permission) {
|
||||||
|
return commandData.setDefaultPermissions(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setContexts(@Nonnull Collection<InteractionContextType> contexts) {
|
||||||
|
return commandData.setContexts(contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setIntegrationTypes(@Nonnull Collection<IntegrationType> integrationTypes) {
|
||||||
|
return commandData.setIntegrationTypes(integrationTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setNSFW(boolean nsfw) {
|
||||||
|
return commandData.setNSFW(nsfw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull String getName() {
|
||||||
|
return commandData.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull LocalizationMap getNameLocalizations() {
|
||||||
|
return commandData.getNameLocalizations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull net.dv8tion.jda.api.interactions.commands.Command.Type getType() {
|
||||||
|
return commandData.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull DefaultMemberPermissions getDefaultPermissions() {
|
||||||
|
return commandData.getDefaultPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull Set<InteractionContextType> getContexts() {
|
||||||
|
return commandData.getContexts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull Set<IntegrationType> getIntegrationTypes() {
|
||||||
|
return commandData.getIntegrationTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNSFW() {
|
||||||
|
return commandData.isNSFW();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setDescription(@Nonnull String description) {
|
||||||
|
return commandData.setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setDescriptionLocalization(@Nonnull DiscordLocale locale, @Nonnull String description) {
|
||||||
|
return commandData.setDescriptionLocalization(locale, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData setDescriptionLocalizations(@Nonnull Map<DiscordLocale, String> map) {
|
||||||
|
return commandData.setDescriptionLocalizations(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull String getDescription() {
|
||||||
|
return commandData.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull LocalizationMap getDescriptionLocalizations() {
|
||||||
|
return commandData.getDescriptionLocalizations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeOptions(@Nonnull Predicate<? super OptionData> condition) {
|
||||||
|
return commandData.removeOptions(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeSubcommands(@Nonnull Predicate<? super SubcommandData> condition) {
|
||||||
|
return commandData.removeSubcommands(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeSubcommandGroups(@Nonnull Predicate<? super SubcommandGroupData> condition) {
|
||||||
|
return commandData.removeSubcommandGroups(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull List<SubcommandData> getSubcommands() {
|
||||||
|
return commandData.getSubcommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull List<SubcommandGroupData> getSubcommandGroups() {
|
||||||
|
return commandData.getSubcommandGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull List<OptionData> getOptions() {
|
||||||
|
return commandData.getOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData addOptions(@Nonnull OptionData... options) {
|
||||||
|
return commandData.addOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData addSubcommands(@Nonnull SubcommandData... subcommands) {
|
||||||
|
return commandData.addSubcommands(subcommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull SlashCommandData addSubcommandGroups(@Nonnull SubcommandGroupData... groups) {
|
||||||
|
return commandData.addSubcommandGroups(groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nonnull DataObject toData() {
|
||||||
|
return commandData.toData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.sentry.Sentry;
|
||||||
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||||
|
import me.bobert.netneutrality.exceptions.GracefulContextError;
|
||||||
|
import me.bobert.netneutrality.utils.UserFunction;
|
||||||
|
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.SubcommandGroupData;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
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.NOACTION;
|
||||||
|
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.TASK;
|
||||||
|
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.WARN;
|
||||||
|
|
||||||
|
public class CommandProcessor {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(CommandProcessor.class);
|
||||||
|
private final CommandContext context;
|
||||||
|
|
||||||
|
public CommandProcessor(CommandContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispatch() {
|
||||||
|
if (!CommandManager.commands.containsKey(context.parseCommandName())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandContainer container = CommandManager.getCommandContainer(context.parseCommandName());
|
||||||
|
Command command = container.getCommand();
|
||||||
|
if (command == null) {
|
||||||
|
throw new IllegalArgumentException("Context has no command");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setCommand(command);
|
||||||
|
if (command.isGuildOnly() && !context.isFromGuild()) {
|
||||||
|
context.replyEphemeral("This command is only available in a guild");
|
||||||
|
context.deleteMessage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.isSlashCommandOnly() && !(context instanceof InteractionContext)) {
|
||||||
|
context.replyEphemeral("This command is only available as a slash command");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.debug("Context ID: {}", context.getId());
|
||||||
|
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId());
|
||||||
|
configureSentry(command);
|
||||||
|
|
||||||
|
if (command.getType() == Command.Type.SLASH && !canExecute(command)) {
|
||||||
|
context.reply("You are not powerful enough, scrub");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.getType() == Command.Type.SLASH && !checkUsage(container)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.isValid()) {
|
||||||
|
StringBuilder reply = new StringBuilder("Invalid arguments provided for this command:\n");
|
||||||
|
for (Map.Entry<OptionData, String> invalid : context.getInvalidOptions()) {
|
||||||
|
reply.append("- ").append(invalid.getKey().getName()).append(": ").append(invalid.getValue()).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.reply(reply.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandExecutionResult commandExecutionResult = command.execute(context);
|
||||||
|
if (commandExecutionResult == null) {
|
||||||
|
throw new RuntimeException("CommandExecutionResult is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandExecutionResult.Result result = commandExecutionResult.getResult();
|
||||||
|
if (result == NOACTION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == TASK) {
|
||||||
|
TaskManager.getInstance().insert(context, commandExecutionResult.getTask());
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = commandExecutionResult.getMessage();
|
||||||
|
if (result == ERROR) {
|
||||||
|
logger.error("Context[{}]: Error from command: {}", context.getId(), message, commandExecutionResult.getException());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == WARN) {
|
||||||
|
logger.warn("Context[{}]: Warning from command: {}", context.getId(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message != null) {
|
||||||
|
context.reply(message);
|
||||||
|
}
|
||||||
|
} catch (GracefulContextError exception) {
|
||||||
|
logger.error(exception.getMessage());
|
||||||
|
context.replyEphemeral(exception.getMessage());
|
||||||
|
} catch (CommandExecutionException exception) {
|
||||||
|
LogHandler.log(exception);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
LogHandler.log(new CommandExecutionException("Error during command execution", context, exception));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkUsage(CommandContainer container) {
|
||||||
|
List<SubcommandGroupData> groups = container.getSubcommandGroups();
|
||||||
|
List<SubcommandData> subcommands = container.getSubcommands();
|
||||||
|
List<OptionData> options = container.getOptions();
|
||||||
|
String[] possibleCommands = Arrays.stream(context.getFullCommand().split(" ")).skip(1).toArray(String[]::new);
|
||||||
|
if (possibleCommands.length == 0) {
|
||||||
|
if (!groups.isEmpty()) {
|
||||||
|
// missing subcommand group
|
||||||
|
context.reply(invalidUsage("missing subcommand group"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subcommands.isEmpty()) {
|
||||||
|
// missing subcommand
|
||||||
|
context.reply(invalidUsage("missing subcommand"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkOptions(possibleCommands, 0, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubcommandGroupData group = groups.stream().filter(data -> data.getName().equals(possibleCommands[0])).findFirst().orElse(null);
|
||||||
|
if (group == null) {
|
||||||
|
SubcommandData subcommand = subcommands.stream().filter(data -> data.getName().equals(possibleCommands[0])).findFirst().orElse(null);
|
||||||
|
if (subcommand == null) {
|
||||||
|
if (!groups.isEmpty()) {
|
||||||
|
// no group found but one is expected
|
||||||
|
context.reply(invalidUsage("unknown subcommand group"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subcommands.isEmpty()) {
|
||||||
|
// no subcommand found but one is expected
|
||||||
|
context.reply(invalidUsage("unknown subcommand"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no group found, no subcommand found, command with options only
|
||||||
|
return checkOptions(possibleCommands, 0, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no group found, subcommand found
|
||||||
|
context.setSubcommand(subcommand);
|
||||||
|
return checkOptions(possibleCommands, 1, subcommand.getOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (possibleCommands.length < 2) {
|
||||||
|
// missing subcommand for group
|
||||||
|
context.reply(invalidUsage("missing subcommand for command group"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubcommandData subcommand = group.getSubcommands().stream().filter(data -> data.getName().equals(possibleCommands[1])).findFirst().orElse(null);
|
||||||
|
if (subcommand == null) {
|
||||||
|
// group found, no subcommand found
|
||||||
|
context.reply(invalidUsage("unknown subcommand in command group"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// group found, subcommand found
|
||||||
|
context.setSubcommandGroup(group);
|
||||||
|
context.setSubcommand(subcommand);
|
||||||
|
return checkOptions(possibleCommands, 2, subcommand.getOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkOptions(String[] args, int skip, List<OptionData> options) {
|
||||||
|
if (context instanceof InteractionContext) {
|
||||||
|
context.setCurrentCommandOptions(args, options);
|
||||||
|
return true; // slash commands are validated by discord
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> arguments = new ArrayList<>(Arrays.stream(args).skip(skip).toList());
|
||||||
|
int required = options.stream().filter(OptionData::isRequired).toList().size();
|
||||||
|
int optional = options.stream().filter(not(OptionData::isRequired)).toList().size();
|
||||||
|
|
||||||
|
if (arguments.size() < required) {
|
||||||
|
context.reply(invalidUsage("missing one or more required arguments"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.setCurrentCommandOptions(arguments.toArray(String[]::new), options);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canExecute(Command command) {
|
||||||
|
if (command.canBeExecutedFrom(context.getChannel())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context instanceof SlashCommandContext) {
|
||||||
|
return true; // no further checks since slash perms are handled by guild settings on discord
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.canBeExecutedBy(context.getGuildUser())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return command.needAdmin() && UserFunction.isAdmin(context.getGuildUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String invalidUsage(String message) {
|
||||||
|
return String.format("Invalid usage for command: %s\n" +
|
||||||
|
"Use **/help %s** for information on how to use this command", message, context.getCommand().getCommandName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureSentry(Command command) {
|
||||||
|
Sentry.configureScope(scope -> {
|
||||||
|
scope.setTag("command", command.getCommandName());
|
||||||
|
scope.setTag("user", context.getGuildUser().getId());
|
||||||
|
scope.setTag("channel", context.getChannel().getId());
|
||||||
|
|
||||||
|
if (context.getGuild() != null) {
|
||||||
|
scope.setTag("guild", context.getGuild().getId());
|
||||||
|
scope.setTag("isAdmin", String.valueOf(UserFunction.isAdmin(context.getGuildUser())));
|
||||||
|
scope.setTag("canBeExecutedBy", String.valueOf(command.canBeExecutedBy(context.getGuildUser())));
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.setTag("contextId", context.getId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
-13
@@ -1,4 +1,4 @@
|
|||||||
package me.bobert.netneutrality.command;
|
package me.bobert.netneutrality.core.command.impl;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) anthorob
|
* Copyright (c) anthorob
|
||||||
@@ -6,38 +6,44 @@ package me.bobert.netneutrality.command;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
import me.bobert.netneutrality.core.command.Command;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
|
||||||
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;
|
||||||
|
|
||||||
public abstract class AbstractCommand implements ICommand {
|
public abstract class AbstractCommand implements Command {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSlashCommand() {
|
public boolean isSlashCommand() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSlashCommandOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
public DefaultMemberPermissions getSlashCommandPermission() {
|
||||||
|
if (needAdmin()) {
|
||||||
|
return DefaultMemberPermissions.DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
return DefaultMemberPermissions.ENABLED;
|
return DefaultMemberPermissions.ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canBeExecutedBy(Guild guild, Member member) {
|
public Type getType() {
|
||||||
|
return Type.SLASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeExecutedBy(Member member) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) {
|
public boolean canBeExecutedFrom(MessageChannel channel) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@Override
|
|
||||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
|
||||||
// Prefer slash commands
|
|
||||||
return CommandExecutionResult.none();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package me.bobert.netneutrality.core.command.impl;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
|
|
||||||
|
public abstract class AdminCommand extends AbstractCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory() {
|
||||||
|
return CommandCategory.ADMINISTRATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package me.bobert.netneutrality.core.command.impl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.command.MessageMenuContext;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
public abstract class MessageMenuCommand extends AbstractCommand {
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuildOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSlashCommandOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
// no options for context commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory() {
|
||||||
|
return CommandCategory.UNLISTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
|
if (context instanceof MessageMenuContext mc) {
|
||||||
|
Message message = mc.getTarget();
|
||||||
|
return execute(message, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Wrong context type for this command");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract CommandExecutionResult execute(Message target, CommandContext context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package me.bobert.netneutrality.core.command.impl;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
|
import me.bobert.netneutrality.db.GSettings;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
|
import me.bobert.netneutrality.utils.UserFunction;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
|
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.MessageChannel;
|
||||||
|
|
||||||
|
public abstract class MusicCommand extends AbstractCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuildOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory() {
|
||||||
|
return CommandCategory.MUSIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeExecutedBy(Member member) {
|
||||||
|
if (member == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Guild guild = member.getGuild();
|
||||||
|
|
||||||
|
String roleDb = GuildService.get(guild.getId()).getGuildSettingService().getSetting(GSettings.MUSIC_ROLE);
|
||||||
|
|
||||||
|
if (roleDb != null && !roleDb.isEmpty()) {
|
||||||
|
if (UserFunction.isAdmin(member)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Role neededRole = guild.getRoleById(roleDb);
|
||||||
|
if (neededRole == null) {
|
||||||
|
// Role doesn't exist on guild anymore, remove from db
|
||||||
|
GuildService.get(guild.getId()).getGuildSettingService().setSettingValue(GSettings.MUSIC_ROLE, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return member.getRoles().contains(neededRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBeExecutedFrom(MessageChannel channel) {
|
||||||
|
if (channel instanceof GuildMessageChannel guildMessageChannel) {
|
||||||
|
if (channel.getType() != ChannelType.TEXT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String channelDb = GuildService.get(guildMessageChannel.getGuild()).getGuildSettingService().getSetting(GSettings.MUSIC_CHANNEL);
|
||||||
|
if (channelDb != null && !channelDb.isEmpty()) {
|
||||||
|
return channelDb.equals(channel.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package me.bobert.netneutrality.core.command.impl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.command.UserMenuContext;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandOptionsBuilder;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
|
||||||
|
public abstract class UserMenuCommand extends AbstractCommand {
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSlashCommandOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
// no options for context commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
|
if (context instanceof UserMenuContext uc) {
|
||||||
|
User user = uc.getTarget();
|
||||||
|
return execute(user, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Wrong context type for this command");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract CommandExecutionResult execute(User target, CommandContext context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package me.bobert.netneutrality.core.command.options;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.IMentionable;
|
||||||
|
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.unions.GuildChannelUnion;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a wrapper around {@link OptionData} that can support both slash and message commands
|
||||||
|
*/
|
||||||
|
public interface CommandOption {
|
||||||
|
OptionType getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of this option.
|
||||||
|
*
|
||||||
|
* @return The option name
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file uploaded for this option.
|
||||||
|
* <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.
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The String representation of this option value.
|
||||||
|
* <br>This will automatically convert the value to a string if the type is not {@link OptionType#STRING OptionType.STRING}.
|
||||||
|
* <br>This will be the ID of any resolved entity such as {@link Role} or {@link Member}.
|
||||||
|
*
|
||||||
|
* @return The String representation of this option value
|
||||||
|
*/
|
||||||
|
String getAsString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The boolean value.
|
||||||
|
*
|
||||||
|
* @return The boolean value
|
||||||
|
* @throws IllegalStateException If this option is not of type {@link OptionType#BOOLEAN BOOLEAN}
|
||||||
|
*/
|
||||||
|
boolean getAsBoolean();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The long value for this option.
|
||||||
|
* <br>This will be the ID of any resolved entity such as {@link Role} or {@link Member}.
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The int value for this option.
|
||||||
|
* <br>This will be the ID of any resolved entity such as {@link Role} or {@link Member}.
|
||||||
|
*
|
||||||
|
* <p><b>It is highly recommended to assert int values by using {@link OptionData#setRequiredRange(long, long)}</b>
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The double value for this option.
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resolved {@link IMentionable} instance for this option value.
|
||||||
|
*
|
||||||
|
* @return The resolved {@link IMentionable}
|
||||||
|
* @throws IllegalStateException If the mentioned entity is not resolvable
|
||||||
|
*/
|
||||||
|
IMentionable getAsMentionable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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!
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resolved {@link User} for this option value.
|
||||||
|
*
|
||||||
|
* @return The resolved {@link User}
|
||||||
|
* @throws IllegalStateException If this option is not of type {@link OptionType#USER USER} or
|
||||||
|
* {@link OptionType#MENTIONABLE MENTIONABLE} without a resolved user
|
||||||
|
*/
|
||||||
|
User getAsUser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resolved {@link Role} for this option value.
|
||||||
|
*
|
||||||
|
* @return The resolved {@link Role}
|
||||||
|
* @throws IllegalStateException If this option is not of type {@link OptionType#ROLE ROLE} or
|
||||||
|
* {@link OptionType#MENTIONABLE MENTIONABLE} without a resolved role
|
||||||
|
*/
|
||||||
|
Role getAsRole();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ChannelType} for the resolved channel.
|
||||||
|
*
|
||||||
|
* @return The {@link ChannelType}
|
||||||
|
* @throws IllegalStateException If this option is not of type {@link OptionType#CHANNEL CHANNEL}
|
||||||
|
*/
|
||||||
|
ChannelType getChannelType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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!
|
||||||
|
*
|
||||||
|
* @return The resolved {@link net.dv8tion.jda.api.entities.channel.middleman.GuildChannel}
|
||||||
|
* @throws IllegalStateException If this option is not of type {@link OptionType#CHANNEL CHANNEL}
|
||||||
|
* or could not be resolved for unexpected reasons
|
||||||
|
*/
|
||||||
|
GuildChannelUnion getAsChannel();
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
package me.bobert.netneutrality.core.command.options;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.IMentionable;
|
||||||
|
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.unions.GuildChannelUnion;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
public class MessageCommandOption implements CommandOption {
|
||||||
|
private final OptionData data;
|
||||||
|
private final OptionType type;
|
||||||
|
private final Object value;
|
||||||
|
|
||||||
|
public MessageCommandOption(OptionData data, Object value) {
|
||||||
|
this.data = data;
|
||||||
|
this.type = data.getType();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OptionType getType() {
|
||||||
|
return data.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return data.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message.Attachment getAsAttachment() {
|
||||||
|
throw new UnsupportedOperationException("Not supported for message commands");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAsString() {
|
||||||
|
return switch (type) {
|
||||||
|
case STRING -> (String) value;
|
||||||
|
case BOOLEAN -> String.valueOf((boolean) value);
|
||||||
|
case NUMBER -> String.valueOf((double) value);
|
||||||
|
case MENTIONABLE -> ((IMentionable) value).getId();
|
||||||
|
case CHANNEL -> ((GuildChannelUnion) value).getId();
|
||||||
|
case ROLE -> ((Role) value).getId();
|
||||||
|
case USER -> ((User) value).getId();
|
||||||
|
case INTEGER -> String.valueOf((long) value);
|
||||||
|
case ATTACHMENT -> ((Message.Attachment) value).getId();
|
||||||
|
default -> throw new IllegalStateException("Cannot convert option of type " + type + " to long");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getAsBoolean() {
|
||||||
|
if (type != OptionType.BOOLEAN) {
|
||||||
|
throw new IllegalStateException("Cannot convert option of type " + type + " to boolean");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (boolean) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getAsLong() {
|
||||||
|
return switch (type) {
|
||||||
|
case STRING -> Long.parseLong((String) value);
|
||||||
|
case MENTIONABLE -> ((IMentionable) value).getIdLong();
|
||||||
|
case CHANNEL -> ((GuildChannelUnion) value).getIdLong();
|
||||||
|
case ROLE -> ((Role) value).getIdLong();
|
||||||
|
case USER -> ((User) value).getIdLong();
|
||||||
|
case INTEGER -> (long) value;
|
||||||
|
case NUMBER -> ((Double) value).longValue();
|
||||||
|
case ATTACHMENT -> ((Message.Attachment) value).getIdLong();
|
||||||
|
default -> throw new IllegalStateException("Cannot convert option of type " + type + " to long");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAsInt() {
|
||||||
|
return Math.toIntExact(getAsLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getAsDouble() {
|
||||||
|
return switch (type) {
|
||||||
|
case STRING -> Double.parseDouble((String) value);
|
||||||
|
case NUMBER -> (double) value;
|
||||||
|
case INTEGER -> (int) value;
|
||||||
|
default -> throw new IllegalStateException("Cannot convert option of type " + type + " to double");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMentionable getAsMentionable() {
|
||||||
|
if (value instanceof IMentionable) {
|
||||||
|
return (IMentionable) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Cannot resolve option of type " + type + " to IMentionable");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Member getAsMember() {
|
||||||
|
if (value instanceof Member) {
|
||||||
|
return (Member) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof User) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Cannot resolve option of type " + type + " to Member");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getAsUser() {
|
||||||
|
if (value instanceof Member) {
|
||||||
|
return ((Member) value).getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof User) {
|
||||||
|
return (User) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Cannot resolve option of type " + type + " to User");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Role getAsRole() {
|
||||||
|
if (value instanceof Role) {
|
||||||
|
return (Role) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Cannot resolve option of type " + type + " to Role");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getChannelType() {
|
||||||
|
return getAsChannel().getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GuildChannelUnion getAsChannel() {
|
||||||
|
if (value instanceof GuildChannelUnion) {
|
||||||
|
return (GuildChannelUnion) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Cannot resolve option of type " + type + " to GuildChannelUnion");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MessageCommandOption{" +
|
||||||
|
"name=" + data.getName() +
|
||||||
|
", type=" + type +
|
||||||
|
", value=" + value +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package me.bobert.netneutrality.core.command.options;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.entities.IMentionable;
|
||||||
|
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.unions.GuildChannelUnion;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
|
|
||||||
|
public class SlashCommandOption implements CommandOption {
|
||||||
|
private final OptionMapping mapping;
|
||||||
|
|
||||||
|
public SlashCommandOption(OptionMapping mapping) {
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OptionType getType() {
|
||||||
|
return mapping.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return mapping.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message.Attachment getAsAttachment() {
|
||||||
|
return mapping.getAsAttachment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAsString() {
|
||||||
|
return mapping.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getAsBoolean() {
|
||||||
|
return mapping.getAsBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getAsLong() {
|
||||||
|
return mapping.getAsLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAsInt() {
|
||||||
|
return mapping.getAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getAsDouble() {
|
||||||
|
return mapping.getAsDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMentionable getAsMentionable() {
|
||||||
|
return mapping.getAsMentionable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Member getAsMember() {
|
||||||
|
return mapping.getAsMember();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getAsUser() {
|
||||||
|
return mapping.getAsUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Role getAsRole() {
|
||||||
|
return mapping.getAsRole();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getChannelType() {
|
||||||
|
return mapping.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GuildChannelUnion getAsChannel() {
|
||||||
|
return mapping.getAsChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import io.sentry.Sentry;
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
|
||||||
|
public abstract class AbstractContext implements Context {
|
||||||
|
private final JDA jda;
|
||||||
|
private final Guild guild;
|
||||||
|
private final MessageChannel channel;
|
||||||
|
private final Member member;
|
||||||
|
private final User user;
|
||||||
|
|
||||||
|
public AbstractContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
|
||||||
|
this.jda = jda;
|
||||||
|
this.guild = guild;
|
||||||
|
this.channel = channel;
|
||||||
|
this.member = member;
|
||||||
|
this.user = user;
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
io.sentry.protocol.User sentryUser = new io.sentry.protocol.User();
|
||||||
|
sentryUser.setId(user.getId());
|
||||||
|
sentryUser.setUsername(user.getEffectiveName());
|
||||||
|
Sentry.setUser(sentryUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JDA getJDA() {
|
||||||
|
return jda;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Guild getGuild() {
|
||||||
|
return guild;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageChannel getChannel() {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Member getGuildUser() {
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getDiscordUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,60 +1,144 @@
|
|||||||
package me.bobert.netneutrality.core.context;
|
package me.bobert.netneutrality.core.context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) anthorob
|
* Copyright (c) anthorob
|
||||||
*
|
*
|
||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
import me.bobert.netneutrality.core.command.Command;
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
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;
|
||||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
|
||||||
import net.dv8tion.jda.api.entities.User;
|
import net.dv8tion.jda.api.entities.User;
|
||||||
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.interactions.commands.build.OptionData;
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
|
||||||
|
|
||||||
public interface CommandContext {
|
import java.util.ArrayList;
|
||||||
|
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.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
AbstractCommand getCommand();
|
public abstract class CommandContext extends AbstractContext {
|
||||||
|
private Command command;
|
||||||
|
private SubcommandGroupData subcommandGroup;
|
||||||
|
private SubcommandData subcommand;
|
||||||
|
private final Map<OptionData, String> invalidOptions = new HashMap<>();
|
||||||
|
private final List<CommandOption> options;
|
||||||
|
|
||||||
Event getEvent();
|
public CommandContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
|
||||||
|
super(jda, guild, channel, member, user);
|
||||||
Guild getGuild();
|
this.options = new ArrayList<>();
|
||||||
|
|
||||||
MessageChannel getChannel();
|
|
||||||
|
|
||||||
Member getGuildUser();
|
|
||||||
|
|
||||||
User getDiscordUser();
|
|
||||||
|
|
||||||
JDA getJDA();
|
|
||||||
|
|
||||||
void deleteMessage();
|
|
||||||
|
|
||||||
void reply(MessageCreateData message);
|
|
||||||
|
|
||||||
void reply(String message);
|
|
||||||
|
|
||||||
void reply(MessageEmbed... embeds);
|
|
||||||
|
|
||||||
default void reply(String format, Object... arguments) {
|
|
||||||
reply(String.format(format, arguments));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void replyToMessage(String message);
|
public Command getCommand() {
|
||||||
|
return command;
|
||||||
default void replyToMessage(MessageCreateData message) {
|
|
||||||
reply(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default void replyToMessage(String format, Object... arguments) {
|
public void setCommand(Command command) {
|
||||||
replyToMessage(String.format(format, arguments));
|
this.command = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void replyToMessage(MessageEmbed... embeds) {
|
public void setSubcommandGroup(SubcommandGroupData subcommandGroup) {
|
||||||
replyToMessage(MessageCreateData.fromEmbeds(embeds));
|
this.subcommandGroup = subcommandGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSubcommand(SubcommandData subcommand) {
|
||||||
|
this.subcommand = subcommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentCommandOptions(String[] args, List<OptionData> options) {
|
||||||
|
this.options.clear();
|
||||||
|
this.options.addAll(parseOptions(args, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void invalidateContext(OptionData option, String message) {
|
||||||
|
invalidOptions.put(option, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return invalidOptions.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Map.Entry<OptionData, String>> getInvalidOptions() {
|
||||||
|
return invalidOptions.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getOption(String name, Supplier<? extends T> fallback, Function<? super CommandOption, ? extends T> resolver) {
|
||||||
|
Objects.requireNonNull(resolver, "resolver cannot be null");
|
||||||
|
CommandOption mapping = getOption(name);
|
||||||
|
if (mapping != null) {
|
||||||
|
return resolver.apply(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback == null ? null : fallback.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getOption(String name, T fallback, Function<? super CommandOption, ? extends T> resolver) {
|
||||||
|
Objects.requireNonNull(resolver, "resolver cannot be null");
|
||||||
|
CommandOption mapping = getOption(name);
|
||||||
|
if (mapping != null) {
|
||||||
|
return resolver.apply(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getOption(String name, Function<? super CommandOption, ? extends T> resolver) {
|
||||||
|
return getOption(name, null, resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandOption getOption(String name) {
|
||||||
|
List<CommandOption> options = getOptionsByName(name);
|
||||||
|
return options.isEmpty() ? null : options.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CommandOption> getOptionsByName(String name) {
|
||||||
|
Objects.requireNonNull(name, "name cannot be null");
|
||||||
|
return options.stream().filter(opt -> opt.getName().equals(name)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubcommandGroup() {
|
||||||
|
if (subcommandGroup == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subcommandGroup.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubcommand() {
|
||||||
|
if (subcommand == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return subcommand.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMessageFromOwner() {
|
||||||
|
if (getGuildUser() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getGuildUser().isOwner();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMessageFromCreator() {
|
||||||
|
String creatorId = Settings.getInstance().get("NETNEUTRALITY_BOT_OWNER");
|
||||||
|
|
||||||
|
return creatorId.equals(getDiscordUser().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions);
|
||||||
|
|
||||||
|
public abstract String parseCommandName();
|
||||||
|
|
||||||
|
public abstract String getFullCommand();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
|
import me.bobert.netneutrality.utils.ContextUtils;
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.components.MessageTopLevelComponent;
|
||||||
|
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.middleman.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
|
import net.dv8tion.jda.api.modals.Modal;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageEditData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public interface Context {
|
||||||
|
Event getEvent();
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
|
||||||
|
ChannelType getType();
|
||||||
|
|
||||||
|
JDA getJDA();
|
||||||
|
|
||||||
|
InteractionHook getInteractionHook();
|
||||||
|
|
||||||
|
Guild getGuild();
|
||||||
|
|
||||||
|
MessageChannel getChannel();
|
||||||
|
|
||||||
|
Member getGuildUser();
|
||||||
|
|
||||||
|
User getDiscordUser();
|
||||||
|
|
||||||
|
boolean isAcknowledged();
|
||||||
|
|
||||||
|
boolean isFromGuild();
|
||||||
|
|
||||||
|
void deleteMessage();
|
||||||
|
|
||||||
|
void reply(Modal modal);
|
||||||
|
|
||||||
|
void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer);
|
||||||
|
|
||||||
|
void defer(boolean ephemeral, Consumer<AsyncContext> consumer);
|
||||||
|
|
||||||
|
void ack(boolean ephemeral);
|
||||||
|
|
||||||
|
default void ack() {
|
||||||
|
ack(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void ephemeral() {
|
||||||
|
ack(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void reply(MessageCreateData messageCreateData) {
|
||||||
|
reply(messageCreateData, false, List.of(), asyncMessage -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default void reply(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components) {
|
||||||
|
reply(messageCreateData, false, components, asyncMessage -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default void reply(MessageCreateData messageCreateData, Consumer<AsyncContext> consumer) {
|
||||||
|
reply(messageCreateData, false, List.of(), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void reply(String message) {
|
||||||
|
replyWithLimit(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void reply(String message, Consumer<AsyncContext> consumer) {
|
||||||
|
reply(MessageCreateData.fromContent(message), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void reply(MessageEmbed embed) {
|
||||||
|
reply(List.of(embed));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void reply(List<MessageEmbed> embeds) {
|
||||||
|
reply(MessageCreateData.fromEmbeds(embeds));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void edit(MessageEditData messageEditData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (isAcknowledged() && getInteractionHook() != null) {
|
||||||
|
getInteractionHook().editOriginal(messageEditData).setComponents(components).queue(message1 -> consumer.accept(new AsyncContext(message1)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply(MessageCreateData.fromEditData(messageEditData), ephemeral, components, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void edit(MessageEditData messageEditData, List<MessageTopLevelComponent> components) {
|
||||||
|
edit(messageEditData, false, components, asyncMessage -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default void edit(MessageEditData messageEditData) {
|
||||||
|
edit(messageEditData, false, List.of(), asyncMessage -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default void edit(String message) {
|
||||||
|
edit(MessageEditData.fromContent(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void edit(MessageEmbed embed) {
|
||||||
|
edit(List.of(embed), List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
default void edit(List<MessageEmbed> embeds, List<MessageTopLevelComponent> components) {
|
||||||
|
edit(MessageEditData.fromEmbeds(embeds), components);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void edit(MessageEmbed embed, List<MessageTopLevelComponent> components) {
|
||||||
|
edit(List.of(embed), components);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void async(MessageCreateData messageCreateData, Consumer<AsyncContext> consumer) {
|
||||||
|
reply(messageCreateData, false, List.of(), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void async(String message, Consumer<AsyncContext> consumer) {
|
||||||
|
async(MessageCreateData.fromContent(message), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void async(Consumer<AsyncContext> consumer) {
|
||||||
|
defer(false, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void replyEphemeral(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components) {
|
||||||
|
reply(messageCreateData, true, components, asyncMessage -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default void replyEphemeral(MessageCreateData messageCreateData) {
|
||||||
|
replyEphemeral(messageCreateData, List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
default void replyEphemeral(String message) {
|
||||||
|
replyEphemeralWithLimit(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void replyEphemeral(String message, MessageTopLevelComponent component) {
|
||||||
|
replyEphemeral(message, List.of(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void replyEphemeral(String message, List<MessageTopLevelComponent> components) {
|
||||||
|
replyEphemeral(MessageCreateData.fromContent(message), components);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void replyEphemeral(List<MessageEmbed> embeds) {
|
||||||
|
replyEphemeral(MessageCreateData.fromEmbeds(embeds));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
User user = getDiscordUser();
|
||||||
|
if (user != null) {
|
||||||
|
user.openPrivateChannel()
|
||||||
|
.flatMap(channel -> channel.sendMessage(messageCreateData).setComponents(components))
|
||||||
|
.queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(String message, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
sendMessageInPrivate(MessageCreateData.fromContent(message), components, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(MessageCreateData messageCreateData, List<MessageTopLevelComponent> components) {
|
||||||
|
sendMessageInPrivate(messageCreateData, components, asyncContext -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(String message, Consumer<AsyncContext> consumer) {
|
||||||
|
sendMessageInPrivate(message, List.of(), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(MessageCreateData messageCreateData) {
|
||||||
|
sendMessageInPrivate(messageCreateData, List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(List<MessageEmbed> embeds) {
|
||||||
|
sendMessageInPrivate(MessageCreateData.fromEmbeds(embeds));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(String message) {
|
||||||
|
sendMessageInPrivateWithLimit(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replyWithLimit(String message) {
|
||||||
|
for (String chunk : ContextUtils.splitMessage(message, Message.MAX_CONTENT_LENGTH)) {
|
||||||
|
reply(MessageCreateData.fromContent(chunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replyEphemeralWithLimit(String message) {
|
||||||
|
for (String chunk : ContextUtils.splitMessage(message, Message.MAX_CONTENT_LENGTH)) {
|
||||||
|
replyEphemeral(MessageCreateData.fromContent(chunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessageInPrivateWithLimit(String message) {
|
||||||
|
for (String chunk : ContextUtils.splitMessage(message, Message.MAX_CONTENT_LENGTH)) {
|
||||||
|
sendMessageInPrivate(MessageCreateData.fromContent(chunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.InteractionHook;
|
||||||
|
import net.dv8tion.jda.api.interactions.callbacks.IModalCallback;
|
||||||
|
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
|
||||||
|
import net.dv8tion.jda.api.modals.Modal;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public interface InteractionContext extends Context {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default String getId() {
|
||||||
|
return getInteraction().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default InteractionHook getInteractionHook() {
|
||||||
|
if (!isAcknowledged()) {
|
||||||
|
throw new RuntimeException("Interaction isn't acknowledged yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
return getReplyCallback().getHook();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void ack(boolean ephemeral) {
|
||||||
|
defer(ephemeral, asyncMessage -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void defer(boolean ephemeral, Consumer<AsyncContext> consumer) {
|
||||||
|
if (isAcknowledged()) {
|
||||||
|
throw new RuntimeException("Interaction is already acknowledged, please use getInteractionHook() instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
getReplyCallback().deferReply(ephemeral).queue(hook -> consumer.accept(new AsyncContext(hook)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isFromGuild() {
|
||||||
|
return getInteraction().isFromGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isAcknowledged() {
|
||||||
|
return getInteraction().isAcknowledged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (isAcknowledged()) {
|
||||||
|
getInteractionHook().sendMessage(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getReplyCallback().reply(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void reply(Modal modal) {
|
||||||
|
if (getInteraction() instanceof IModalCallback) {
|
||||||
|
((IModalCallback) getInteraction()).replyModal(modal).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReplyCallback getReplyCallback() {
|
||||||
|
try {
|
||||||
|
return (IReplyCallback) getEvent();
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new IllegalArgumentException(getEvent().getClass().getName() + " does not implement class 'IReplyCallback'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Interaction getInteraction() {
|
||||||
|
try {
|
||||||
|
return (Interaction) getEvent();
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new IllegalArgumentException(getEvent().getClass().getName() + " does not implement class 'Interaction'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
package me.bobert.netneutrality.core.context;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.sentry.Breadcrumb;
|
|
||||||
import io.sentry.Sentry;
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.thread.ThreadManager;
|
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
|
||||||
import net.dv8tion.jda.api.JDA;
|
|
||||||
import net.dv8tion.jda.api.entities.*;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class MessageCommandContext implements CommandContext {
|
|
||||||
|
|
||||||
private static final ExecutorService commandExecutor = ThreadManager.getExecutor();
|
|
||||||
|
|
||||||
private AbstractCommand command;
|
|
||||||
private final MessageReceivedEvent event;
|
|
||||||
private final Message message;
|
|
||||||
private final Guild guild;
|
|
||||||
private final MessageChannel channel;
|
|
||||||
private final Member member;
|
|
||||||
private final User user;
|
|
||||||
private final JDA jda;
|
|
||||||
|
|
||||||
public MessageCommandContext(MessageReceivedEvent event, Message message, Member member, User user, Guild guild, MessageChannel channel, JDA jda) {
|
|
||||||
this.event = event;
|
|
||||||
this.message = message;
|
|
||||||
this.member = member;
|
|
||||||
this.guild = guild;
|
|
||||||
this.channel = channel;
|
|
||||||
this.user = user;
|
|
||||||
this.jda = jda;
|
|
||||||
|
|
||||||
io.sentry.protocol.User sentryUser = new io.sentry.protocol.User();
|
|
||||||
sentryUser.setId(member.getId());
|
|
||||||
sentryUser.setUsername(member.getEffectiveName());
|
|
||||||
Sentry.setUser(sentryUser);
|
|
||||||
Sentry.addBreadcrumb(Breadcrumb.info("New context for message id " + message.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MessageCommandContext createContext(MessageReceivedEvent event) {
|
|
||||||
Message message = event.getMessage();
|
|
||||||
Member member = event.getMember();
|
|
||||||
User user = event.getAuthor();
|
|
||||||
Guild guild = event.getGuild();
|
|
||||||
MessageChannel channel = event.getChannel();
|
|
||||||
JDA jda = event.getJDA();
|
|
||||||
|
|
||||||
return new MessageCommandContext(event, message, member, user, guild, channel, jda);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MessageReceivedEvent getEvent() {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getArgument(int index) {
|
|
||||||
return getArgs()[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getArgs() {
|
|
||||||
String msg = message.getContentDisplay().trim();
|
|
||||||
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
|
|
||||||
String cmd = msg.split(" ", 2)[0];
|
|
||||||
String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split("\s");
|
|
||||||
|
|
||||||
if (args.length == 1 && args[0].isEmpty())
|
|
||||||
args = new String[0];
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessageText() {
|
|
||||||
String msg = message.getContentDisplay().trim();
|
|
||||||
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
|
|
||||||
String cmd = msg.split(" ", 2)[0];
|
|
||||||
|
|
||||||
return msg.replaceFirst("(" + cmd + ")(\s)?", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Message getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guild getGuild() {
|
|
||||||
return guild;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MessageChannel getChannel() {
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Member getGuildUser() {
|
|
||||||
return member;
|
|
||||||
}
|
|
||||||
|
|
||||||
public User getDiscordUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JDA getJDA() {
|
|
||||||
return jda;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AbstractCommand getCommand() {
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GuildManager getGuildManager() {
|
|
||||||
return GuildManager.getManager(guild);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reply(String message) {
|
|
||||||
reply(MessageCreateData.fromContent(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reply(String format, Object... arguments) {
|
|
||||||
reply(String.format(format, arguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reply(MessageEmbed... embeds) {
|
|
||||||
reply(MessageCreateData.fromEmbeds(embeds));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reply(MessageCreateData msg) {
|
|
||||||
if (channel != null && channel.canTalk()) {
|
|
||||||
channel.sendMessage(msg).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replyToMessage(String message) {
|
|
||||||
replyToMessage(MessageCreateData.fromContent(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replyToMessage(String format, Object... arguments) {
|
|
||||||
replyToMessage(String.format(format, arguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyToMessage(MessageCreateData msg) {
|
|
||||||
if (channel != null && channel.canTalk() && message != null) {
|
|
||||||
message.reply(msg).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyPrivate(String message) {
|
|
||||||
replyPrivate(MessageCreateData.fromContent(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyPrivate(MessageCreateData msg) {
|
|
||||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyPrivate(MessageEmbed... embeds) {
|
|
||||||
replyPrivate(MessageCreateData.fromEmbeds(embeds));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyPrivate(Collection<? extends MessageEmbed> embeds) {
|
|
||||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteMessage() {
|
|
||||||
if (message != null) {
|
|
||||||
message.delete().queueAfter(1, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMessageFromOwner() {
|
|
||||||
return member.isOwner();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMessageFromCreator() {
|
|
||||||
String creatorId = Settings.getInstance().get("NETNEUTRALITY_BOT_OWNER");
|
|
||||||
|
|
||||||
return creatorId.equals(member.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public interface MessageContext extends NoMessageContext {
|
||||||
|
Message getMessage();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isFromGuild() {
|
||||||
|
return getMessage().isFromGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void deleteMessage() {
|
||||||
|
if (getMessage() != null && getType() != ChannelType.PRIVATE) {
|
||||||
|
getMessage().delete().queueAfter(1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void reply(MessageCreateData messageCreateData, boolean ephemeral, List<MessageTopLevelComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (ephemeral && getType() != ChannelType.PRIVATE) {
|
||||||
|
sendMessageInPrivate(messageCreateData, components);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getChannel() != null && getChannel().canTalk()) {
|
||||||
|
getMessage().reply(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
|
import net.dv8tion.jda.api.modals.Modal;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public interface NoMessageContext extends Context {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default InteractionHook getInteractionHook() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isAcknowledged() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default ChannelType getType() {
|
||||||
|
return getChannel().getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void reply(Modal modal) {
|
||||||
|
// only available for interactions
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void defer(boolean ephemeral, Consumer<AsyncContext> consumer) {
|
||||||
|
reply("Bot is thinking...", consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void ack(boolean ephemeral) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
package me.bobert.netneutrality.core.context;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.command.AbstractCommand;
|
|
||||||
import net.dv8tion.jda.api.JDA;
|
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
|
||||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
|
||||||
import net.dv8tion.jda.api.entities.User;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageEditData;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public class SlashCommandContext implements CommandContext {
|
|
||||||
|
|
||||||
private final SlashCommandInteractionEvent event;
|
|
||||||
private AbstractCommand command;
|
|
||||||
private final JDA jda;
|
|
||||||
private final Guild guild;
|
|
||||||
private final MessageChannel channel;
|
|
||||||
private final Member member;
|
|
||||||
private final User user;
|
|
||||||
|
|
||||||
private SlashCommandContext(JDA jda, SlashCommandInteractionEvent event, Guild guild, MessageChannel channel, Member member, User user) {
|
|
||||||
this.jda = jda;
|
|
||||||
this.event = event;
|
|
||||||
this.guild = guild;
|
|
||||||
this.channel = channel;
|
|
||||||
this.member = member;
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SlashCommandContext createContext(SlashCommandInteractionEvent event) {
|
|
||||||
Member member = event.getMember();
|
|
||||||
User user = event.getUser();
|
|
||||||
Guild guild = event.getGuild();
|
|
||||||
MessageChannel channel = event.getChannel();
|
|
||||||
JDA jda = event.getJDA();
|
|
||||||
|
|
||||||
return new SlashCommandContext(jda, event, guild, channel, member, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SlashCommandInteractionEvent getEvent() {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractCommand getCommand() {
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JDA getJDA() {
|
|
||||||
return jda;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Guild getGuild() {
|
|
||||||
return guild;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MessageChannel getChannel() {
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Member getGuildUser() {
|
|
||||||
return member;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User getDiscordUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteMessage() {
|
|
||||||
// not implemented
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendMessageInPrivate(String msg) {
|
|
||||||
sendMessageInPrivate(new MessageCreateBuilder().setContent(msg).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendMessageInPrivate(MessageCreateData msg) {
|
|
||||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendMessageInPrivate(MessageEmbed embed, MessageEmbed... embeds) {
|
|
||||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embed, embeds)).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendMessageInPrivate(Collection<? extends MessageEmbed> embeds) {
|
|
||||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reply(MessageCreateData message) {
|
|
||||||
if (event.isAcknowledged()) {
|
|
||||||
getHook().editOriginal(MessageEditData.fromCreateData(message)).queue();
|
|
||||||
//getHook().sendMessage(message).setEphemeral(false).queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.reply(message).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reply(MessageEmbed... embeds) {
|
|
||||||
reply(MessageCreateData.fromEmbeds(embeds));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reply(String message) {
|
|
||||||
reply(MessageCreateData.fromContent(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyEphemeral(MessageCreateData message) {
|
|
||||||
if (event.isAcknowledged()) {
|
|
||||||
getHook().editOriginal(MessageEditData.fromCreateData(message)).queue();
|
|
||||||
//getHook().sendMessage(message).setEphemeral(true).queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.reply(message).setEphemeral(true).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyEphemeral(MessageEmbed... embeds) {
|
|
||||||
replyEphemeral(MessageCreateData.fromEmbeds(embeds));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyEphemeral(String format, Object... arguments) {
|
|
||||||
replyEphemeral(String.format(format, arguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replyEphemeral(String message) {
|
|
||||||
replyEphemeral(MessageCreateData.fromContent(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replyToMessage(String message) {
|
|
||||||
replyEphemeral(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public InteractionHook getHook() {
|
|
||||||
if (!event.isAcknowledged())
|
|
||||||
throw new RuntimeException("Interaction isn't acknowledged yet");
|
|
||||||
|
|
||||||
return event.getHook();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ackAndWait(boolean ephemeral) {
|
|
||||||
if (event.isAcknowledged())
|
|
||||||
throw new RuntimeException("Interaction is already acknowledged, please use getHook() instead.");
|
|
||||||
|
|
||||||
event.deferReply(ephemeral).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ackAndWait() {
|
|
||||||
ackAndWait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ack() {
|
|
||||||
ackAndWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ephemeral() {
|
|
||||||
ackAndWait(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public abstract class TaskContext extends AbstractContext {
|
||||||
|
private final Map<String, Object> contextData;
|
||||||
|
|
||||||
|
public TaskContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
|
||||||
|
super(jda, guild, channel, member, user);
|
||||||
|
this.contextData = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getId();
|
||||||
|
|
||||||
|
public void addContextData(String key, Object value) {
|
||||||
|
contextData.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getContextData(String key) {
|
||||||
|
return contextData.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package me.bobert.netneutrality.core.db;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.flywaydb.core.Flyway;
|
||||||
|
import org.flywaydb.core.api.output.MigrateResult;
|
||||||
|
|
||||||
|
public class DatabaseMigration {
|
||||||
|
private final static Logger logger = LogManager.getLogger(DatabaseMigration.class);
|
||||||
|
|
||||||
|
public static void migrate() {
|
||||||
|
String dbName = Settings.getInstance().get("MYSQL_DATABASE", "netneutrality");
|
||||||
|
String dbUser = Settings.getInstance().get("MYSQL_USER", "root");
|
||||||
|
String dbPass = Settings.getInstance().get("MYSQL_PASSWORD", "");
|
||||||
|
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 {
|
||||||
|
logger.info("Running database migrations..");
|
||||||
|
Flyway flyway = Flyway.configure()
|
||||||
|
.dataSource(dbUrl, dbUser, dbPass)
|
||||||
|
.load();
|
||||||
|
|
||||||
|
MigrateResult result = flyway.migrate();
|
||||||
|
logger.info("Database migrations finished successfully! (total: {})", result.migrationsExecuted);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e);
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-6
@@ -1,4 +1,4 @@
|
|||||||
package me.bobert.netneutrality.core;
|
package me.bobert.netneutrality.core.db;
|
||||||
/*
|
/*
|
||||||
* Copyright (c) anthorob
|
* Copyright (c) anthorob
|
||||||
*
|
*
|
||||||
@@ -20,7 +20,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class SessionManager {
|
public class SessionManager {
|
||||||
private final static Logger logger = LogManager.getLogger(SessionManager.class);
|
private static final Logger logger = LogManager.getLogger(SessionManager.class);
|
||||||
private static StandardServiceRegistry registry;
|
private static StandardServiceRegistry registry;
|
||||||
private static SessionFactory sessionFactory;
|
private static SessionFactory sessionFactory;
|
||||||
|
|
||||||
@@ -33,11 +33,12 @@ 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");
|
||||||
logger.info(String.format("Connecting to database '%s' at %s:3306 with user '%s'", dbName, dbHost, dbUser));
|
String dbPort = Settings.getInstance().get("MYSQL_PORT", "3306");
|
||||||
|
logger.info("Connecting to database '{}' at {}:{} with user '{}'", dbName, dbHost, dbPort, dbUser);
|
||||||
|
|
||||||
settings.put(Environment.URL, String.format("jdbc:mysql://%s:3306/%s", dbHost, dbName));
|
settings.put(Environment.JAKARTA_JDBC_URL, String.format("jdbc:mysql://%s:%s/%s", dbHost, dbPort, dbName));
|
||||||
settings.put(Environment.USER, dbUser);
|
settings.put(Environment.JAKARTA_JDBC_USER, dbUser);
|
||||||
settings.put(Environment.PASS, dbPass);
|
settings.put(Environment.JAKARTA_JDBC_PASSWORD, dbPass);
|
||||||
registryBuilder.applySettings(settings);
|
registryBuilder.applySettings(settings);
|
||||||
|
|
||||||
registry = registryBuilder.build();
|
registry = registryBuilder.build();
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a task that has a known ID and is loaded at runtime.
|
||||||
|
* This can be any task and is not necessarily a button.
|
||||||
|
*/
|
||||||
|
public interface BuiltInTask extends Task {
|
||||||
|
|
||||||
|
void executeTask(TaskContext context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a task that can be persisted and restored later.
|
||||||
|
* The persistable task provides functionality to serialize its state
|
||||||
|
* so that it can be stored in a database.
|
||||||
|
*/
|
||||||
|
public interface PersistableTask extends Task {
|
||||||
|
void insert(CommandContext context);
|
||||||
|
|
||||||
|
void save();
|
||||||
|
|
||||||
|
void delete();
|
||||||
|
|
||||||
|
String toPayload();
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface StepTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the type of input expected for a step.
|
||||||
|
*/
|
||||||
|
enum StepType {
|
||||||
|
/**
|
||||||
|
* Expects a string input from the user.
|
||||||
|
*/
|
||||||
|
STRING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expects a valid role ID for the current guild.
|
||||||
|
* The role must exist in the guild where the command was executed.
|
||||||
|
*/
|
||||||
|
ROLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expects a valid emoji.
|
||||||
|
*/
|
||||||
|
EMOJI
|
||||||
|
}
|
||||||
|
|
||||||
|
class StepResult {
|
||||||
|
private enum ResultType {
|
||||||
|
SUCCESS, FAILURE
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ResultType type;
|
||||||
|
private final String message;
|
||||||
|
private final Task nextStep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of a step execution. Each step can either succeed, fail, or transition to another step.
|
||||||
|
*/
|
||||||
|
private StepResult(ResultType type, String message, Task nextStep) {
|
||||||
|
this.type = type;
|
||||||
|
this.message = message;
|
||||||
|
this.nextStep = nextStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the step was completed successfully, with an optional follow-up step.
|
||||||
|
*
|
||||||
|
* @param message the success message
|
||||||
|
* @param nextStep the next step in the workflow, or null if this is the final step
|
||||||
|
* @return a successful result
|
||||||
|
*/
|
||||||
|
public static StepResult success(String message, Task nextStep) {
|
||||||
|
return new StepResult(ResultType.SUCCESS, message, nextStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the step was completed successfully.
|
||||||
|
*
|
||||||
|
* @param message the success message
|
||||||
|
* @return a successful result without a next step
|
||||||
|
*/
|
||||||
|
public static StepResult success(String message) {
|
||||||
|
return new StepResult(ResultType.SUCCESS, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the step has failed.
|
||||||
|
*
|
||||||
|
* @param message the failure message
|
||||||
|
* @return a failed result
|
||||||
|
*/
|
||||||
|
public static StepResult failure(String message) {
|
||||||
|
return new StepResult(ResultType.FAILURE, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves directly to the next step without displaying a message.
|
||||||
|
*
|
||||||
|
* @param nextTask the next task in the workflow
|
||||||
|
* @return a successful result that advances to the next step
|
||||||
|
*/
|
||||||
|
public static StepResult next(Task nextTask) {
|
||||||
|
return new StepResult(ResultType.SUCCESS, null, nextTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the message associated with the step result.
|
||||||
|
*
|
||||||
|
* @return the result message, or null if there is no message
|
||||||
|
*/
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next step in the task workflow.
|
||||||
|
*
|
||||||
|
* @return the next step, or null if this is the last step
|
||||||
|
*/
|
||||||
|
public Task getNextStep() {
|
||||||
|
return nextStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the step execution was successful.
|
||||||
|
*
|
||||||
|
* @return true if the step was successful, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return type == ResultType.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of step input expected.
|
||||||
|
*
|
||||||
|
* @return the step type
|
||||||
|
*/
|
||||||
|
StepType getStepType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a general description of the step. This will be displayed before asking the question.
|
||||||
|
*
|
||||||
|
* @return the step description
|
||||||
|
*/
|
||||||
|
String getStepDescription();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the specific question for this step.
|
||||||
|
*
|
||||||
|
* @return the step question
|
||||||
|
*/
|
||||||
|
String getStepQuestion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the step using the provided answer.
|
||||||
|
*
|
||||||
|
* @param answer the user's input, validated against the expected step type
|
||||||
|
* @return the result of executing this step
|
||||||
|
*/
|
||||||
|
StepResult executeStep(Object answer);
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
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 java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
public interface Task {
|
||||||
|
enum Lifecycle {
|
||||||
|
BUILTIN,
|
||||||
|
PARENT,
|
||||||
|
KEEP,
|
||||||
|
REMOVE_SELF,
|
||||||
|
REMOVE_SELF_AND_CHILDREN
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should generate a unique ID to differentiate it from other tasks.
|
||||||
|
* <p>
|
||||||
|
* This determines what triggers a task to be executed.
|
||||||
|
* For example, a modal or button would use the component id.
|
||||||
|
* For an emoji reaction, the guild and message id would be combined.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return a unique ID for this task
|
||||||
|
*/
|
||||||
|
String getId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the lock ID that is used to synchronize tasks
|
||||||
|
* that require a locking mechanism.
|
||||||
|
*
|
||||||
|
* @return the lock ID associated with this task
|
||||||
|
*/
|
||||||
|
default String getLockId() {
|
||||||
|
return getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is only used for debugging purpose.
|
||||||
|
*
|
||||||
|
* @return Name of the task
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>If this returns false, the task will not get inserted and executed.</p>
|
||||||
|
*/
|
||||||
|
boolean isInsertable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When this returns true, the task will get removed by the cleanup thread.
|
||||||
|
*
|
||||||
|
* @return If the task is expired
|
||||||
|
*/
|
||||||
|
boolean isExpired();
|
||||||
|
|
||||||
|
OffsetDateTime getCreationTime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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>
|
||||||
|
*/
|
||||||
|
void replyToInteraction(CommandContext context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the task.
|
||||||
|
*
|
||||||
|
* @param context The context for this task.
|
||||||
|
*/
|
||||||
|
Lifecycle execute(TaskContext 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.Deletable;
|
||||||
|
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.WithParent;
|
||||||
|
import me.bobert.netneutrality.core.thread.ScheduledTaskManager;
|
||||||
|
import me.bobert.netneutrality.db.service.TaskService;
|
||||||
|
import me.bobert.netneutrality.exceptions.GracefulContextError;
|
||||||
|
import me.bobert.netneutrality.exceptions.TaskExecutionException;
|
||||||
|
import me.bobert.netneutrality.utils.ReflectionHelper;
|
||||||
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class TaskManager {
|
||||||
|
private static final Logger logger = LogManager.getLogger(TaskManager.class);
|
||||||
|
private static TaskManager instance;
|
||||||
|
private final TaskService taskService;
|
||||||
|
private final Map<String, Task> tasks;
|
||||||
|
private final List<Task> pendingTasks;
|
||||||
|
private final Map<String, CommandContext> deletableTasksContext;
|
||||||
|
private final Map<String, ReentrantLock> tasksLock;
|
||||||
|
|
||||||
|
private TaskManager() {
|
||||||
|
this.tasks = new ConcurrentHashMap<>();
|
||||||
|
this.pendingTasks = new CopyOnWriteArrayList<>();
|
||||||
|
this.deletableTasksContext = new ConcurrentHashMap<>();
|
||||||
|
this.tasksLock = new ConcurrentHashMap<>();
|
||||||
|
this.taskService = new TaskService();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TaskManager getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new TaskManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
logger.info("Initializing task manager...");
|
||||||
|
|
||||||
|
loadBuiltInTasks();
|
||||||
|
loadScheduledTasks();
|
||||||
|
taskService.loadTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskService getTaskService() {
|
||||||
|
return taskService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task getTask(String id) {
|
||||||
|
return tasks.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTask(String id) {
|
||||||
|
return tasks.containsKey(id) || pendingTasks.stream().anyMatch(task -> task.getId().equals(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Collection<Task> getTasks() {
|
||||||
|
return tasks.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(CommandContext context, Task task) {
|
||||||
|
String id = task.getId();
|
||||||
|
|
||||||
|
if (hasTask(id)) {
|
||||||
|
context.reply("A task with the same ID already exists!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
task.replyToInteraction(context);
|
||||||
|
|
||||||
|
if (task.isInsertable()) {
|
||||||
|
logger.info("Inserting task: {}", task);
|
||||||
|
|
||||||
|
if (task instanceof PersistableTask persistableTask) {
|
||||||
|
taskService.persistTask(persistableTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task instanceof StepTask) {
|
||||||
|
pendingTasks.add(task);
|
||||||
|
} else {
|
||||||
|
tasks.put(id, task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task instanceof Deletable) {
|
||||||
|
deletableTasksContext.put(task.getId(), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task instanceof WithChildren taskWithChildren) {
|
||||||
|
List<Task> childrens = taskWithChildren.getChildTasks();
|
||||||
|
if (childrens == null || childrens.isEmpty()) {
|
||||||
|
logger.warn("Parent task '{}' doesn't create any childs", task.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Task child : childrens) {
|
||||||
|
insert(context, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activateTask(Task task, String oldId) {
|
||||||
|
if (pendingTasks.remove(task)) {
|
||||||
|
tasks.put(task.getId(), task);
|
||||||
|
logger.info("Updating task {} with new id: {} (was: {})",
|
||||||
|
task.getName(), task.getId(), oldId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTask(Task task) {
|
||||||
|
String id = task.getId();
|
||||||
|
|
||||||
|
if (tasks.remove(id) != null) {
|
||||||
|
logger.debug("Removing task: {}", id);
|
||||||
|
|
||||||
|
ReentrantLock lock = tasksLock.remove(task.getLockId());
|
||||||
|
if (lock != null) {
|
||||||
|
logger.debug("Cleaning up lock for removed task: {}", task.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(TaskContext context) {
|
||||||
|
String taskId = context.getId();
|
||||||
|
Task task = getTask(taskId);
|
||||||
|
|
||||||
|
if (task == null && context instanceof InteractionContext) {
|
||||||
|
context.replyEphemeral("This task is no longer available, someone might have already used it or it might have expired");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task != null) {
|
||||||
|
String lockId = task.getLockId();
|
||||||
|
ReentrantLock lock = tasksLock.computeIfAbsent(lockId, k -> {
|
||||||
|
if (task instanceof PersistableTask || task instanceof BuiltInTask) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReentrantLock();
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (lock != null) {
|
||||||
|
logger.debug("Aquiring lock for task: {}", taskId);
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
if (!tasks.containsKey(taskId)) {
|
||||||
|
logger.debug("Task {} is no longer available after aquiring lock", taskId);
|
||||||
|
tasksLock.remove(lockId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Executing task: {}", taskId);
|
||||||
|
Task.Lifecycle status = task.execute(context);
|
||||||
|
handleTaskLifecycle(task, status);
|
||||||
|
|
||||||
|
deleteInteraction(task);
|
||||||
|
|
||||||
|
// If interaction is still not acknowledged after execution, send a placeholder response.
|
||||||
|
if (!context.isAcknowledged() && !(task instanceof NotAcknowledgeable)) {
|
||||||
|
logger.warn("Interaction not acknowledged");
|
||||||
|
context.replyEphemeral("Task executed successfully but interaction wasn't acknowledged, report this bug by using the command /report-bug");
|
||||||
|
}
|
||||||
|
} catch (GracefulContextError exception) {
|
||||||
|
logger.error(exception.getMessage());
|
||||||
|
context.replyEphemeral(exception.getMessage());
|
||||||
|
} catch (TaskExecutionException exception) {
|
||||||
|
LogHandler.log(exception);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
LogHandler.log(new TaskExecutionException("Error during task execution", context, exception));
|
||||||
|
} finally {
|
||||||
|
if (lock != null) {
|
||||||
|
logger.debug("Releasing lock for task: {}", taskId);
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends Task> Optional<E> findTaskByPartialId(Class<E> clazz, String partialId) {
|
||||||
|
List<E> tasks = findTasksByPartialId(clazz, partialId);
|
||||||
|
return Optional.ofNullable(tasks.isEmpty() ? null : tasks.getFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E extends Task> List<E> findTasksByPartialId(Class<E> clazz, String partialId) {
|
||||||
|
return tasks.values().stream()
|
||||||
|
.filter(clazz::isInstance)
|
||||||
|
.map(clazz::cast)
|
||||||
|
.filter(task -> task.getId().contains(partialId))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public WithButton findButtonTaskByClass(Class<? extends WithButton> clazz) {
|
||||||
|
return tasks.values().stream()
|
||||||
|
.filter(clazz::isInstance)
|
||||||
|
.map(task -> (WithButton) task)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertPersistableTask(PersistableTask task) {
|
||||||
|
if (tasks.containsKey(task.getId())) {
|
||||||
|
logger.error("Trying to load persistable task but ID already exists: {}", task.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Loaded persistable task: {}", task);
|
||||||
|
tasks.put(task.getId(), task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadBuiltInTasks() {
|
||||||
|
logger.info("Loading built-in tasks...");
|
||||||
|
|
||||||
|
ReflectionHelper.loadTasks(BuiltInTask.class, builtInTask -> {
|
||||||
|
if (builtInTask.isInsertable()) {
|
||||||
|
logger.info("Loaded task: {}", builtInTask.getClass().getName());
|
||||||
|
tasks.put(builtInTask.getId(), builtInTask);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadScheduledTasks() {
|
||||||
|
logger.info("Loading scheduled tasks...");
|
||||||
|
|
||||||
|
ReflectionHelper.loadTasks(ScheduledTask.class, scheduledTask -> {
|
||||||
|
String taskId = scheduledTask.getId();
|
||||||
|
|
||||||
|
if (taskId == null || taskId.trim().isEmpty()) {
|
||||||
|
logger.error("Cannot load scheduled task {}: task ID is null or empty", scheduledTask.getClass().getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduledTaskManager.schedule(scheduledTask);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteInteraction(Task task) {
|
||||||
|
if (task instanceof WithParent<?> taskWithParent) {
|
||||||
|
Task parent = taskWithParent.getParentTask();
|
||||||
|
|
||||||
|
if (parent instanceof Deletable deletable && task.isInsertable()) {
|
||||||
|
deleteInteraction(parent.getId(), deletable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task instanceof Deletable deletable && task.isInsertable()) {
|
||||||
|
deleteInteraction(task.getId(), deletable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteInteraction(String taskId, Deletable deletable) {
|
||||||
|
logger.debug("Deleting interaction: {}", taskId);
|
||||||
|
|
||||||
|
CommandContext context = deletableTasksContext.get(taskId);
|
||||||
|
if (context != null) {
|
||||||
|
deletableTasksContext.remove(taskId);
|
||||||
|
|
||||||
|
InteractionHook hook = context.getInteractionHook();
|
||||||
|
if (hook != null) {
|
||||||
|
if (hook.isExpired()) {
|
||||||
|
context.replyEphemeral("This interaction has expired, can't remove it");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hook.isExpired()) {
|
||||||
|
hook.deleteOriginal().queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deletable.onInteractionDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTaskLifecycle(Task task, Task.Lifecycle status) {
|
||||||
|
if (status == null) {
|
||||||
|
throw new IllegalArgumentException("Task lifecycle status cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case REMOVE_SELF:
|
||||||
|
removeTask(task);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REMOVE_SELF_AND_CHILDREN:
|
||||||
|
if (task instanceof WithChildren taskWithChildren) {
|
||||||
|
for (Task child : taskWithChildren.getChildTasks()) {
|
||||||
|
removeTask(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeTask(task);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEEP:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.BuiltInTask;
|
||||||
|
|
||||||
|
public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltInTask {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsertable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExpired() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replyToInteraction(CommandContext context) {
|
||||||
|
// Task is loaded at runtime, this will never be called
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle execute(TaskContext context) {
|
||||||
|
executeTask(context);
|
||||||
|
return Lifecycle.BUILTIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public abstract class AbstractPersistableTask extends AbstractTask implements PersistableTask {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsertable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExpired() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replyToInteraction(CommandContext context) {
|
||||||
|
// Task loaded from database, this will never be called
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insert(CommandContext context) {
|
||||||
|
if (TaskManager.getInstance().hasTask(getId())) {
|
||||||
|
throw new RuntimeException("Cannot insert a task that already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskManager.getInstance().insert(context, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
if (!TaskManager.getInstance().hasTask(getId())) {
|
||||||
|
throw new RuntimeException("Cannot save a task that doesn't exist yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskManager.getInstance().getTaskService().persistTask(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() {
|
||||||
|
TaskManager.getInstance().getTaskService().unpersistTask(this);
|
||||||
|
TaskManager.getInstance().removeTask(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toPayload() {
|
||||||
|
JSONObject payload = new JSONObject(this);
|
||||||
|
|
||||||
|
return payload.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.MessageContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.StepTask;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
|
import net.fellbaum.jemoji.Emoji;
|
||||||
|
import net.fellbaum.jemoji.EmojiManager;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractStepTask extends AbstractTask implements StepTask {
|
||||||
|
private final Object stepLock = new Object();
|
||||||
|
private CommandContext commandContext;
|
||||||
|
private Guild guild;
|
||||||
|
private String id = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsertable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guild getGuild() {
|
||||||
|
return guild;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replyToInteraction(CommandContext context) {
|
||||||
|
synchronized (stepLock) {
|
||||||
|
this.commandContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.isFromGuild()) {
|
||||||
|
guild = context.getGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guild == null && getStepType() == StepType.ROLE) {
|
||||||
|
throw new IllegalArgumentException("Step task is of type role but it was inserted in a non-guild context");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.replyEphemeral("Please follow the instructions sent in DM.");
|
||||||
|
context.sendMessageInPrivate(getStepDescription() + "\n\n" + getStepQuestion(),
|
||||||
|
asyncContext -> assignId(asyncContext.getMessageId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle execute(TaskContext context) {
|
||||||
|
synchronized (stepLock) {
|
||||||
|
if (!context.getDiscordUser().equals(commandContext.getDiscordUser())) {
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context instanceof MessageContext messageContext) {
|
||||||
|
Message message = messageContext.getMessage();
|
||||||
|
String input = message.getContentRaw();
|
||||||
|
|
||||||
|
|
||||||
|
Object answer = validateInput(context, input);
|
||||||
|
if (answer == null) {
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
StepResult result = executeStep(answer);
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
context.replyEphemeral(result.getMessage());
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.getMessage() != null) {
|
||||||
|
context.replyEphemeral(result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.getNextStep() != null) {
|
||||||
|
result.getNextStep().insert(commandContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lifecycle.REMOVE_SELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Step task is not of type MessageContext");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExpired() {
|
||||||
|
// is it really needed to expire this type of task?
|
||||||
|
return getCreationTime().plusMinutes(60).isBefore(OffsetDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the user input against the expected step type.
|
||||||
|
*
|
||||||
|
* @param context the task execution context
|
||||||
|
* @param input the user-provided input
|
||||||
|
* @return the parsed input if valid, or null if invalid
|
||||||
|
*/
|
||||||
|
private Object validateInput(TaskContext context, String input) {
|
||||||
|
switch (getStepType()) {
|
||||||
|
case ROLE -> {
|
||||||
|
try {
|
||||||
|
Role role = guild.getRoleById(input);
|
||||||
|
if (role == null) {
|
||||||
|
context.replyEphemeral("The role doesn't exist");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return role;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
context.replyEphemeral("Not a valid role ID");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EMOJI -> {
|
||||||
|
Optional<Emoji> parsed = EmojiManager.getEmoji(input);
|
||||||
|
|
||||||
|
if (parsed.isPresent()) {
|
||||||
|
return parsed.get().getEmoji();
|
||||||
|
}
|
||||||
|
|
||||||
|
context.replyEphemeral("Not a valid emoji");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case STRING -> {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
default -> throw new IllegalStateException("Unknown step type: " + getStepType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assignId(String newId) {
|
||||||
|
if (newId == null || newId.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Task ID cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String oldId = id;
|
||||||
|
synchronized (this) {
|
||||||
|
this.id = newId;
|
||||||
|
TaskManager.getInstance().activateTask(this, oldId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.util.Objects;
|
||||||
|
|
||||||
|
public abstract class AbstractTask implements Task {
|
||||||
|
private final OffsetDateTime creationTime;
|
||||||
|
|
||||||
|
public AbstractTask() {
|
||||||
|
creationTime = OffsetDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OffsetDateTime getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insert(CommandContext context) {
|
||||||
|
TaskManager.getInstance().insert(context, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractTask that = (AbstractTask) o;
|
||||||
|
return Objects.equals(getId(), that.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("Task{id=%s, lockId=%s, class=%s, name=%s, creationTime=%s}", getId(), getLockId(), getClass().getSimpleName(), getName(), getCreationTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 200 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user