diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0e7cc62f6..86a003bfc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,7 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
-name: Execute build and Sonar
+name: Execute Build
on:
push:
@@ -20,26 +20,13 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: Set up JDK 1.8
+ - name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'corretto'
- java-version: 8
+ java-version: 17
- name: Build with Maven
run: mvn -B install --file abstracto-application/pom.xml
- - name: Setup sonarqube
- uses: warchant/setup-sonar-scanner@v3
- - name: Run sonarqube
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: sonar-scanner
- -Dsonar.login=${{ secrets.SONAR_TOKEN }}
- -Dsonar.organization=sheldan
- -Dsonar.host.url=https://sonarcloud.io/
- -Dsonar.projectKey=abstracto-core
- -Dsonar.java.binaries=**/target/classes
- -Dsonar.coverage.jacoco.xmlReportPaths=abstracto-application/coverage/target/site/jacoco-aggregate/jacoco.xml
- -Dsonar.coverage.exclusions=**/*Test.java
- uses: actions/setup-ruby@v1
- name: Send Webhook Notification
if: always()
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 492a3b78c..3d459156d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,7 +13,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'corretto'
- java-version: 8
+ java-version: 17
- name: Load current version
id: version
run: echo "version=$(mvn --file abstracto-application/pom.xml -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec)" >> $GITHUB_ENV
diff --git a/README.md b/README.md
index fae9b0b10..af2d720e4 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,6 @@
# Abstracto
-
-[](https://sonarcloud.io/dashboard?id=abstracto-core)
-[](https://sonarcloud.io/dashboard?id=abstracto-core)
+
[](https://github.com/Sheldan/abstracto/blob/master/LICENSE)
diff --git a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRole.java b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRole.java
index 7ea5d6ce8..f776a12a9 100644
--- a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRole.java
+++ b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRole.java
@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRoleCondition.java b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRoleCondition.java
index 4efe4cbb1..a8016b806 100644
--- a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRoleCondition.java
+++ b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRoleCondition.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.assignableroles.model.database;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
@Entity
@Table(name = "assignable_role_condition")
diff --git a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRolePlace.java b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRolePlace.java
index 0afb70f3f..a092671b2 100644
--- a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRolePlace.java
+++ b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignableRolePlace.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignedRoleUser.java b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignedRoleUser.java
index 7d1fc8c25..baa183e8d 100644
--- a/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignedRoleUser.java
+++ b/abstracto-application/abstracto-modules/assignable-roles/assignable-roles-int/src/main/java/dev/sheldan/abstracto/assignableroles/model/database/AssignedRoleUser.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.assignableroles.model.database;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/abstracto-modules/custom-command/custom-command-int/src/main/java/dev/sheldan/abstracto/customcommand/model/database/CustomCommand.java b/abstracto-application/abstracto-modules/custom-command/custom-command-int/src/main/java/dev/sheldan/abstracto/customcommand/model/database/CustomCommand.java
index 405893bc0..bed7ec312 100644
--- a/abstracto-application/abstracto-modules/custom-command/custom-command-int/src/main/java/dev/sheldan/abstracto/customcommand/model/database/CustomCommand.java
+++ b/abstracto-application/abstracto-modules/custom-command/custom-command-int/src/main/java/dev/sheldan/abstracto/customcommand/model/database/CustomCommand.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.customcommand.model.database;
import dev.sheldan.abstracto.core.models.database.*;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/dynamic-activity/dynamic-activity-int/src/main/java/dev/sheldan/abstracto/activity/models/CustomActivity.java b/abstracto-application/abstracto-modules/dynamic-activity/dynamic-activity-int/src/main/java/dev/sheldan/abstracto/activity/models/CustomActivity.java
index 63210ccb4..29870afd4 100644
--- a/abstracto-application/abstracto-modules/dynamic-activity/dynamic-activity-int/src/main/java/dev/sheldan/abstracto/activity/models/CustomActivity.java
+++ b/abstracto-application/abstracto-modules/dynamic-activity/dynamic-activity-int/src/main/java/dev/sheldan/abstracto/activity/models/CustomActivity.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.activity.models;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Builder
diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/database/EconomyUser.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/database/EconomyUser.java
index 4255ec459..289d66adb 100644
--- a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/database/EconomyUser.java
+++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/database/EconomyUser.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/ADisabledExpRole.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/ADisabledExpRole.java
index 27d645421..f8383acf2 100644
--- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/ADisabledExpRole.java
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/ADisabledExpRole.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.experience.model.database;
import dev.sheldan.abstracto.core.models.database.ARole;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceLevel.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceLevel.java
index f3632f68e..185a8ed97 100644
--- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceLevel.java
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceLevel.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.experience.model.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceRole.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceRole.java
index 1de4b84e9..e9929f988 100644
--- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceRole.java
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AExperienceRole.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AUserExperience.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AUserExperience.java
index 8dff2bcb5..4d85cd352 100644
--- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AUserExperience.java
+++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-int/src/main/java/dev/sheldan/abstracto/experience/model/database/AUserExperience.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/AllowedInviteLink.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/AllowedInviteLink.java
index b71cd8e92..faad2305e 100644
--- a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/AllowedInviteLink.java
+++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/AllowedInviteLink.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.invitefilter.model.database;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/FilteredInviteLink.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/FilteredInviteLink.java
index bd06e17ff..44d619650 100644
--- a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/FilteredInviteLink.java
+++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/FilteredInviteLink.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.invitefilter.model.database;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/InviteFilterChannelGroup.java b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/InviteFilterChannelGroup.java
index 8b8d67b45..791b9842d 100644
--- a/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/InviteFilterChannelGroup.java
+++ b/abstracto-application/abstracto-modules/invite-filter/invite-filter-int/src/main/java/dev/sheldan/abstracto/invitefilter/model/database/InviteFilterChannelGroup.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.invitefilter.model.database;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Builder
diff --git a/abstracto-application/abstracto-modules/link-embed/link-embed-int/src/main/java/dev/sheldan/abstracto/linkembed/model/database/EmbeddedMessage.java b/abstracto-application/abstracto-modules/link-embed/link-embed-int/src/main/java/dev/sheldan/abstracto/linkembed/model/database/EmbeddedMessage.java
index db46cb981..d48ef3465 100644
--- a/abstracto-application/abstracto-modules/link-embed/link-embed-int/src/main/java/dev/sheldan/abstracto/linkembed/model/database/EmbeddedMessage.java
+++ b/abstracto-application/abstracto-modules/link-embed/link-embed-int/src/main/java/dev/sheldan/abstracto/linkembed/model/database/EmbeddedMessage.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Infraction.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Infraction.java
index 6051d8fe5..3064cf7c6 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Infraction.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Infraction.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/InfractionParameter.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/InfractionParameter.java
index 7a0ba1084..fd94e5d7f 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/InfractionParameter.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/InfractionParameter.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.moderation.model.database;
import dev.sheldan.abstracto.moderation.model.database.embedded.InfractionParameterId;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ModerationUser.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ModerationUser.java
index 94339d935..5c2bd0dfe 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ModerationUser.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ModerationUser.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Mute.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Mute.java
index 2d51fbed6..90aa26cfe 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Mute.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Mute.java
@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/MuteRole.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/MuteRole.java
index 15863524c..679229d2d 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/MuteRole.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/MuteRole.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ReactionReport.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ReactionReport.java
index 0b214bb84..b9d2aecf2 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ReactionReport.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/ReactionReport.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Builder
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/UserNote.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/UserNote.java
index 98845009b..9944fccf9 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/UserNote.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/UserNote.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Warning.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Warning.java
index c6d12acfe..c64001065 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Warning.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/Warning.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/embedded/InfractionParameterId.java b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/embedded/InfractionParameterId.java
index 46bd87217..7d3fe98c6 100644
--- a/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/embedded/InfractionParameterId.java
+++ b/abstracto-application/abstracto-modules/moderation/moderation-int/src/main/java/dev/sheldan/abstracto/moderation/model/database/embedded/InfractionParameterId.java
@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.moderation.model.database.embedded;
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
@Embeddable
diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailMessage.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailMessage.java
index 501562245..9c9f44ecb 100644
--- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailMessage.java
+++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailMessage.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailRole.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailRole.java
index 209f7bff2..71b0db9f3 100644
--- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailRole.java
+++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailRole.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThread.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThread.java
index 1642a0bd7..e49716646 100644
--- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThread.java
+++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThread.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThreadSubscriber.java b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThreadSubscriber.java
index 4f15f820b..b03f73110 100644
--- a/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThreadSubscriber.java
+++ b/abstracto-application/abstracto-modules/modmail/modmail-int/src/main/java/dev/sheldan/abstracto/modmail/model/database/ModMailThreadSubscriber.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/pom.xml b/abstracto-application/abstracto-modules/pom.xml
index 2e65a08be..b6e6ca46e 100644
--- a/abstracto-application/abstracto-modules/pom.xml
+++ b/abstracto-application/abstracto-modules/pom.xml
@@ -31,6 +31,7 @@
dynamic-activity
anti-raid
custom-command
+ twitch
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUse.java b/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUse.java
index 83c3b684f..daefeea26 100644
--- a/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUse.java
+++ b/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUse.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.ProfanityGroup;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Builder
diff --git a/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUserInAServer.java b/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUserInAServer.java
index 99642acc4..daba935a6 100644
--- a/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUserInAServer.java
+++ b/abstracto-application/abstracto-modules/profanity-filter/profanity-filter-int/src/main/java/dev/sheldan/abstracto/profanityfilter/model/database/ProfanityUserInAServer.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/Reminder.java b/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/Reminder.java
index b95777a96..bbc9986f9 100644
--- a/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/Reminder.java
+++ b/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/Reminder.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/ReminderParticipant.java b/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/ReminderParticipant.java
index e721e8da5..63f52425f 100644
--- a/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/ReminderParticipant.java
+++ b/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/ReminderParticipant.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.remind.model.database.embed.ReminderUserId;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/embed/ReminderUserId.java b/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/embed/ReminderUserId.java
index a935ea395..544836d83 100644
--- a/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/embed/ReminderUserId.java
+++ b/abstracto-application/abstracto-modules/remind/remind-int/src/main/java/dev/sheldan/abstracto/remind/model/database/embed/ReminderUserId.java
@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.remind.model.database.embed;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;
diff --git a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/PostedImage.java b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/PostedImage.java
index 52030251e..e7d17af72 100644
--- a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/PostedImage.java
+++ b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/PostedImage.java
@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.repostdetection.model.database.embed.PostIdentifier;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/Repost.java b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/Repost.java
index eb219ac23..b5704aaaa 100644
--- a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/Repost.java
+++ b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/Repost.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.repostdetection.model.database.embed.RepostIdentifier;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/RepostCheckChannelGroup.java b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/RepostCheckChannelGroup.java
index 347492085..55216f682 100644
--- a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/RepostCheckChannelGroup.java
+++ b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/RepostCheckChannelGroup.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.repostdetection.model.database;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Builder
diff --git a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/PostIdentifier.java b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/PostIdentifier.java
index f1700dd14..ccd8a40a9 100644
--- a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/PostIdentifier.java
+++ b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/PostIdentifier.java
@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.repostdetection.model.database.embed;
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
@Embeddable
diff --git a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/RepostIdentifier.java b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/RepostIdentifier.java
index 41fd46d46..1e021fab5 100644
--- a/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/RepostIdentifier.java
+++ b/abstracto-application/abstracto-modules/repost-detection/repost-detection-int/src/main/java/dev/sheldan/abstracto/repostdetection/model/database/embed/RepostIdentifier.java
@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.repostdetection.model.database.embed;
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
@Embeddable
diff --git a/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPost.java b/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPost.java
index d542ef99f..5bc14c603 100644
--- a/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPost.java
+++ b/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPost.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPostReaction.java b/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPostReaction.java
index c490dbaab..e1829c6bc 100644
--- a/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPostReaction.java
+++ b/abstracto-application/abstracto-modules/starboard/starboard-int/src/main/java/dev/sheldan/abstracto/starboard/model/database/StarboardPostReaction.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/TrackedEmote.java b/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/TrackedEmote.java
index 6dd506983..580b2553c 100644
--- a/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/TrackedEmote.java
+++ b/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/TrackedEmote.java
@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingFeatureConfig;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/UsedEmote.java b/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/UsedEmote.java
index 040a069c5..e0f755663 100644
--- a/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/UsedEmote.java
+++ b/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/UsedEmote.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.statistic.emote.model.database;
import dev.sheldan.abstracto.statistic.emote.model.database.embed.UsedEmoteDay;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
/**
diff --git a/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/embed/UsedEmoteDay.java b/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/embed/UsedEmoteDay.java
index 9ac004ca9..57a5292cd 100644
--- a/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/embed/UsedEmoteDay.java
+++ b/abstracto-application/abstracto-modules/statistic/statistic-int/src/main/java/dev/sheldan/abstracto/statistic/emote/model/database/embed/UsedEmoteDay.java
@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.statistic.emote.model.database.embed;
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Poll.java b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Poll.java
index 610e238ec..1b33b3edb 100644
--- a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Poll.java
+++ b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Poll.java
@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollOption.java b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollOption.java
index 51cc41287..2e8c7125e 100644
--- a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollOption.java
+++ b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollOption.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecision.java b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecision.java
index f6140e613..fea2d9087 100644
--- a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecision.java
+++ b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecision.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecisionOption.java b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecisionOption.java
index 6f38d7bcf..789af7a44 100644
--- a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecisionOption.java
+++ b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/PollUserDecisionOption.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.suggestion.model.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Suggestion.java b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Suggestion.java
index 3b9c1b966..991d1f4d7 100644
--- a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Suggestion.java
+++ b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/Suggestion.java
@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/SuggestionVote.java b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/SuggestionVote.java
index 7f66f3b9b..358ce5b73 100644
--- a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/SuggestionVote.java
+++ b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/SuggestionVote.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.suggestion.model.database.embed.SuggestionVoterId;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/embed/SuggestionVoterId.java b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/embed/SuggestionVoterId.java
index 4dd0b4321..47fb5dd25 100644
--- a/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/embed/SuggestionVoterId.java
+++ b/abstracto-application/abstracto-modules/suggestion/suggestion-int/src/main/java/dev/sheldan/abstracto/suggestion/model/database/embed/SuggestionVoterId.java
@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.suggestion.model.database.embed;
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
@Embeddable
diff --git a/abstracto-application/abstracto-modules/twitch/pom.xml b/abstracto-application/abstracto-modules/twitch/pom.xml
new file mode 100644
index 000000000..a6e9908ac
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/pom.xml
@@ -0,0 +1,35 @@
+
+
+
+ abstracto-modules
+ dev.sheldan.abstracto.modules
+ 1.4.27-SNAPSHOT
+
+ 4.0.0
+
+ twitch
+ pom
+
+ twitch-int
+ twitch-impl
+
+
+
+
+
+ dev.sheldan.abstracto.core
+ core-int
+ ${project.version}
+ compile
+
+
+
+ dev.sheldan.abstracto.scheduling
+ scheduling-int
+ ${project.version}
+ compile
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/pom.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/pom.xml
new file mode 100644
index 000000000..a453ead8f
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+ twitch
+ dev.sheldan.abstracto.modules
+ 1.4.27-SNAPSHOT
+
+ 4.0.0
+
+ twitch-impl
+
+
+
+
+ maven-assembly-plugin
+
+
+ src/main/assembly/liquibase.xml
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
+
+ dev.sheldan.abstracto.modules
+ twitch-int
+ ${project.version}
+
+
+
+ dev.sheldan.abstracto.core
+ core-int
+ ${project.version}
+ test-jar
+ test
+
+
+
+ dev.sheldan.abstracto.scheduling
+ scheduling-int
+ ${project.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/assembly/liquibase.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/assembly/liquibase.xml
new file mode 100644
index 000000000..8b4774fa0
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/assembly/liquibase.xml
@@ -0,0 +1,18 @@
+
+ liquibase
+
+ zip
+
+ false
+
+
+ .
+ ${project.basedir}/src/main/resources/migrations
+
+ **/*
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/AddTwitchStreamer.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/AddTwitchStreamer.java
new file mode 100644
index 000000000..753195ad9
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/AddTwitchStreamer.java
@@ -0,0 +1,131 @@
+package dev.sheldan.abstracto.twitch.command;
+
+import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
+import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
+import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
+import dev.sheldan.abstracto.core.command.config.HelpInfo;
+import dev.sheldan.abstracto.core.command.config.Parameter;
+import dev.sheldan.abstracto.core.command.execution.CommandResult;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.core.interaction.InteractionService;
+import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
+import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
+import dev.sheldan.abstracto.core.utils.FutureUtils;
+import dev.sheldan.abstracto.twitch.config.TwitchFeatureDefinition;
+import dev.sheldan.abstracto.twitch.config.TwitchSlashCommandNames;
+import dev.sheldan.abstracto.twitch.service.StreamerService;
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
+import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import net.dv8tion.jda.api.interactions.InteractionHook;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+@Component
+public class AddTwitchStreamer extends AbstractConditionableCommand {
+
+ private static final String ADD_TWITCH_STREAMER_COMMAND = "addTwitchStreamer";
+ private static final String STREAMER_NAME_PARAMETER = "streamerName";
+ private static final String TARGET_CHANNEL_PARAMETER = "targetChannel";
+ private static final String SERVER_MEMBER_PARAMETER = "streamerMember";
+ private static final String ADD_TWITCH_STREAMER_RESPONSE = "addTwitchStreamer_response";
+
+ @Autowired
+ private SlashCommandParameterService slashCommandParameterService;
+
+ @Autowired
+ private InteractionService interactionService;
+
+ @Autowired
+ private StreamerService streamerService;
+
+ @Autowired
+ private AddTwitchStreamer self;
+
+ @Override
+ public CompletableFuture executeSlash(SlashCommandInteractionEvent event) {
+ // its likely that this command times out
+ return event.deferReply().submit().thenCompose(interactionHook -> self.executeAddStreamerCommandLogic(event, interactionHook));
+
+ }
+
+ @Transactional
+ public CompletableFuture executeAddStreamerCommandLogic(SlashCommandInteractionEvent event, InteractionHook interactionHook) {
+ String streamerName = slashCommandParameterService.getCommandOption(STREAMER_NAME_PARAMETER, event, String.class);
+ GuildMessageChannel guildMessageChannel = null;
+ if(slashCommandParameterService.hasCommandOption(TARGET_CHANNEL_PARAMETER, event)) {
+ guildMessageChannel = slashCommandParameterService.getCommandOption(TARGET_CHANNEL_PARAMETER, event, TextChannel.class, GuildMessageChannel.class);
+ }
+ Member streamerMember = null;
+ if(slashCommandParameterService.hasCommandOption(SERVER_MEMBER_PARAMETER, event)) {
+ streamerMember = slashCommandParameterService.getCommandOption(SERVER_MEMBER_PARAMETER, event, Member.class);
+ }
+ streamerService.createStreamer(streamerName, guildMessageChannel, event.getMember(), streamerMember);
+ return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(ADD_TWITCH_STREAMER_RESPONSE, new Object(), interactionHook))
+ .thenApply(unused -> CommandResult.fromSuccess());
+ }
+
+ @Override
+ public CommandConfiguration getConfiguration() {
+ Parameter streamerNameParameter = Parameter
+ .builder()
+ .name(STREAMER_NAME_PARAMETER)
+ .type(String.class)
+ .templated(true)
+ .build();
+
+ Parameter targetChannelParameter = Parameter
+ .builder()
+ .name(TARGET_CHANNEL_PARAMETER)
+ .type(TextChannel.class)
+ .optional(true)
+ .templated(true)
+ .build();
+
+ Parameter streamerMemberParameter = Parameter
+ .builder()
+ .name(SERVER_MEMBER_PARAMETER)
+ .type(Member.class)
+ .optional(true)
+ .templated(true)
+ .build();
+
+ List parameters = Arrays.asList(streamerNameParameter, targetChannelParameter, streamerMemberParameter);
+
+ HelpInfo helpInfo = HelpInfo
+ .builder()
+ .templated(true)
+ .build();
+
+ SlashCommandConfig slashCommandConfig = SlashCommandConfig
+ .builder()
+ .enabled(true)
+ .rootCommandName(TwitchSlashCommandNames.TWITCH)
+ .groupName("streamer")
+ .commandName("add")
+ .build();
+
+ return CommandConfiguration.builder()
+ .name(ADD_TWITCH_STREAMER_COMMAND)
+ .module(UtilityModuleDefinition.UTILITY)
+ .templated(true)
+ .async(true)
+ .slashCommandConfig(slashCommandConfig)
+ .supportsEmbedException(true)
+ .causesReaction(true)
+ .parameters(parameters)
+ .help(helpInfo)
+ .build();
+ }
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return TwitchFeatureDefinition.TWITCH;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/ChangeTwitchStreamer.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/ChangeTwitchStreamer.java
new file mode 100644
index 000000000..94cbf29e4
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/ChangeTwitchStreamer.java
@@ -0,0 +1,168 @@
+package dev.sheldan.abstracto.twitch.command;
+
+import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
+import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
+import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
+import dev.sheldan.abstracto.core.command.config.HelpInfo;
+import dev.sheldan.abstracto.core.command.config.Parameter;
+import dev.sheldan.abstracto.core.command.execution.CommandResult;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.core.interaction.InteractionService;
+import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
+import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.service.management.ServerManagementService;
+import dev.sheldan.abstracto.twitch.config.TwitchFeatureDefinition;
+import dev.sheldan.abstracto.twitch.config.TwitchSlashCommandNames;
+import dev.sheldan.abstracto.twitch.exception.StreamerNotFoundInServerException;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+import dev.sheldan.abstracto.twitch.service.StreamerService;
+import dev.sheldan.abstracto.twitch.service.management.StreamerManagementService;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import org.apache.commons.lang3.BooleanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.regex.Matcher;
+import java.util.stream.Collectors;
+
+@Component
+public class ChangeTwitchStreamer extends AbstractConditionableCommand {
+
+ private static final String CHANGE_STREAMER_COMMAND = "changeTwitchStreamer";
+ private static final String STREAMER_NAME_PARAMETER = "streamerName";
+ private static final String STREAMER_NEW_VALUE_PARAMETER = "newValue";
+ private static final String STREAMER_PROPERTY_PARAMETER = "property";
+ private static final String CHANGE_TWITCH_STREAMER_RESPONSE = "changeTwitchStreamer_response";
+
+ @Autowired
+ private SlashCommandParameterService slashCommandParameterService;
+
+ @Autowired
+ private InteractionService interactionService;
+
+ @Autowired
+ private StreamerManagementService streamerManagementService;
+
+ @Autowired
+ private ServerManagementService serverManagementService;
+
+ @Autowired
+ private StreamerService streamerService;
+
+ @Override
+ public CompletableFuture executeSlash(SlashCommandInteractionEvent event) {
+ String streamerName = slashCommandParameterService.getCommandOption(STREAMER_NAME_PARAMETER, event, String.class);
+ String property = slashCommandParameterService.getCommandOption(STREAMER_PROPERTY_PARAMETER, event, String.class);
+ AServer server = serverManagementService.loadServer(event.getGuild());
+ Streamer streamerInServerByName = streamerManagementService.getStreamerInServerByName(streamerName, server).orElseThrow(StreamerNotFoundInServerException::new);
+ StreamerProperty propertyEnum = StreamerProperty.valueOf(property);
+ String newValue = slashCommandParameterService.getCommandOption(STREAMER_NEW_VALUE_PARAMETER, event, String.class);
+ updateStreamer(streamerInServerByName, propertyEnum, newValue);
+ return interactionService.replyEmbed(CHANGE_TWITCH_STREAMER_RESPONSE, event)
+ .thenApply(interactionHook -> CommandResult.fromSuccess());
+ }
+
+ private void updateStreamer(Streamer streamer, StreamerProperty propertyEnum, String newValue) {
+ switch (propertyEnum) {
+ case TARGET_CHANNEL -> {
+ Matcher channelMatcher = Message.MentionType.CHANNEL.getPattern().matcher(newValue);
+ if (channelMatcher.matches()) {
+ Long channelId = Long.parseLong(channelMatcher.group(1));
+ streamerService.changeStreamerNotificationToChannel(streamer, channelId);
+ } else {
+ streamerService.changeStreamerNotificationToChannel(streamer, null);
+ }
+ }
+ case STREAMER_MEMBER -> {
+ Matcher memberMatcher = Message.MentionType.USER.getPattern().matcher(newValue);
+ if (memberMatcher.matches()) {
+ Long channelId = Long.parseLong(memberMatcher.group(1));
+ streamerService.changeStreamerMemberToUserId(streamer, channelId);
+ } else {
+ streamerService.changeStreamerMemberToUserId(streamer, null);
+ }
+ }
+ case TEMPLATE_KEY -> {
+ String newTemplateKey = newValue;
+ if ("default".equals(newTemplateKey)) {
+ newTemplateKey = null;
+ }
+ streamerService.changeTemplateKeyTo(streamer, newTemplateKey);
+ }
+ case DISABLE_NOTIFICATIONS -> {
+ Boolean newState = BooleanUtils.toBoolean(newValue);
+ streamerService.disableNotificationsForStreamer(streamer, newState);
+ }
+ }
+ }
+
+ @Override
+ public CommandConfiguration getConfiguration() {
+ Parameter streamerNameParameter = Parameter
+ .builder()
+ .templated(true)
+ .name(STREAMER_NAME_PARAMETER)
+ .type(String.class)
+ .build();
+
+ List streamerProperties = Arrays
+ .stream(StreamerProperty.values())
+ .map(Enum::name)
+ .collect(Collectors.toList());
+
+ Parameter streamerPropertyParameter = Parameter
+ .builder()
+ .templated(true)
+ .name(STREAMER_PROPERTY_PARAMETER)
+ .type(String.class)
+ .choices(streamerProperties)
+ .build();
+
+ Parameter newValueParameter = Parameter
+ .builder()
+ .templated(true)
+ .name(STREAMER_NEW_VALUE_PARAMETER)
+ .type(String.class)
+ .build();
+
+ List parameters = Arrays.asList(streamerNameParameter, streamerPropertyParameter, newValueParameter);
+ HelpInfo helpInfo = HelpInfo
+ .builder()
+ .templated(true)
+ .build();
+
+ SlashCommandConfig slashCommandConfig = SlashCommandConfig
+ .builder()
+ .enabled(true)
+ .rootCommandName(TwitchSlashCommandNames.TWITCH)
+ .groupName("streamer")
+ .commandName("edit")
+ .build();
+
+ return CommandConfiguration.builder()
+ .name(CHANGE_STREAMER_COMMAND)
+ .module(UtilityModuleDefinition.UTILITY)
+ .templated(true)
+ .async(true)
+ .slashCommandConfig(slashCommandConfig)
+ .supportsEmbedException(true)
+ .causesReaction(true)
+ .parameters(parameters)
+ .help(helpInfo)
+ .build();
+ }
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return TwitchFeatureDefinition.TWITCH;
+ }
+
+ public enum StreamerProperty {
+ TARGET_CHANNEL, STREAMER_MEMBER, DISABLE_NOTIFICATIONS, TEMPLATE_KEY
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/ListTwitchStreamer.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/ListTwitchStreamer.java
new file mode 100644
index 000000000..738cfe588
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/ListTwitchStreamer.java
@@ -0,0 +1,72 @@
+package dev.sheldan.abstracto.twitch.command;
+
+import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
+import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
+import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
+import dev.sheldan.abstracto.core.command.config.HelpInfo;
+import dev.sheldan.abstracto.core.command.execution.CommandResult;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.core.interaction.InteractionService;
+import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
+import dev.sheldan.abstracto.twitch.config.TwitchFeatureDefinition;
+import dev.sheldan.abstracto.twitch.config.TwitchSlashCommandNames;
+import dev.sheldan.abstracto.twitch.model.template.ListTwitchStreamerResponseModel;
+import dev.sheldan.abstracto.twitch.service.StreamerService;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.CompletableFuture;
+
+@Component
+public class ListTwitchStreamer extends AbstractConditionableCommand {
+
+ private static final String LIST_TWITCH_STREAMER_COMMAND = "listTwitchStreamer";
+ private static final String LIST_TWITCH_STREAMER_RESPONSE = "listTwitchStreamer_response";
+
+ @Autowired
+ private StreamerService streamerService;
+
+ @Autowired
+ private InteractionService interactionService;
+
+ @Override
+ public CompletableFuture executeSlash(SlashCommandInteractionEvent event) {
+ ListTwitchStreamerResponseModel model = streamerService.getStreamersFromServer(event.getGuild());
+ return interactionService.replyEmbed(LIST_TWITCH_STREAMER_RESPONSE, model, event)
+ .thenApply(interactionHook -> CommandResult.fromSuccess());
+ }
+
+ @Override
+ public CommandConfiguration getConfiguration() {
+
+ HelpInfo helpInfo = HelpInfo
+ .builder()
+ .templated(true)
+ .build();
+
+ SlashCommandConfig slashCommandConfig = SlashCommandConfig
+ .builder()
+ .enabled(true)
+ .rootCommandName(TwitchSlashCommandNames.TWITCH)
+ .groupName("streamer")
+ .commandName("list")
+ .build();
+
+ return CommandConfiguration.builder()
+ .name(LIST_TWITCH_STREAMER_COMMAND)
+ .module(UtilityModuleDefinition.UTILITY)
+ .templated(true)
+ .async(true)
+ .slashCommandConfig(slashCommandConfig)
+ .supportsEmbedException(true)
+ .causesReaction(true)
+ .help(helpInfo)
+ .build();
+ }
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return TwitchFeatureDefinition.TWITCH;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/RemoveTwitchStreamer.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/RemoveTwitchStreamer.java
new file mode 100644
index 000000000..d4b50a0b3
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/command/RemoveTwitchStreamer.java
@@ -0,0 +1,89 @@
+package dev.sheldan.abstracto.twitch.command;
+
+import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
+import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
+import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
+import dev.sheldan.abstracto.core.command.config.HelpInfo;
+import dev.sheldan.abstracto.core.command.config.Parameter;
+import dev.sheldan.abstracto.core.command.execution.CommandResult;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.core.interaction.InteractionService;
+import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
+import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
+import dev.sheldan.abstracto.twitch.config.TwitchFeatureDefinition;
+import dev.sheldan.abstracto.twitch.config.TwitchSlashCommandNames;
+import dev.sheldan.abstracto.twitch.service.StreamerService;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+@Component
+public class RemoveTwitchStreamer extends AbstractConditionableCommand {
+
+ private static final String REMOVE_TWITCH_STREAMER_COMMAND = "removeTwitchStreamer";
+ private static final String STREAMER_NAME_PARAMETER = "streamerName";
+ private static final String REMOVE_TWITCH_STREAMER_RESPONSE = "removeTwitchStreamer_response";
+
+ @Autowired
+ private SlashCommandParameterService slashCommandParameterService;
+
+ @Autowired
+ private InteractionService interactionService;
+
+ @Autowired
+ private StreamerService streamerService;
+
+ @Override
+ public CompletableFuture executeSlash(SlashCommandInteractionEvent event) {
+ String streamerName = slashCommandParameterService.getCommandOption(STREAMER_NAME_PARAMETER, event, String.class);
+ streamerService.removeStreamer(streamerName, event.getGuild());
+ return interactionService.replyEmbed(REMOVE_TWITCH_STREAMER_RESPONSE, event)
+ .thenApply(interactionHook -> CommandResult.fromSuccess());
+ }
+
+ @Override
+ public CommandConfiguration getConfiguration() {
+ Parameter streamerNameParameter = Parameter
+ .builder()
+ .name(STREAMER_NAME_PARAMETER)
+ .type(String.class)
+ .templated(true)
+ .build();
+
+ List parameters = Arrays.asList(streamerNameParameter);
+
+ HelpInfo helpInfo = HelpInfo
+ .builder()
+ .templated(true)
+ .build();
+
+ SlashCommandConfig slashCommandConfig = SlashCommandConfig
+ .builder()
+ .enabled(true)
+ .rootCommandName(TwitchSlashCommandNames.TWITCH)
+ .groupName("streamer")
+ .commandName("remove")
+ .build();
+
+ return CommandConfiguration.builder()
+ .name(REMOVE_TWITCH_STREAMER_COMMAND)
+ .module(UtilityModuleDefinition.UTILITY)
+ .templated(true)
+ .async(true)
+ .slashCommandConfig(slashCommandConfig)
+ .supportsEmbedException(true)
+ .causesReaction(true)
+ .parameters(parameters)
+ .help(helpInfo)
+ .build();
+ }
+
+ @Override
+ public FeatureDefinition getFeature() {
+ return TwitchFeatureDefinition.TWITCH;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchConfig.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchConfig.java
new file mode 100644
index 000000000..53ac7c8df
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchConfig.java
@@ -0,0 +1,10 @@
+package dev.sheldan.abstracto.twitch.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+
+@Configuration
+@PropertySource("classpath:twitch-config.properties")
+public class TwitchConfig {
+}
+
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/job/StreamerOnlineCheckJob.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/job/StreamerOnlineCheckJob.java
new file mode 100644
index 000000000..c75677ef2
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/job/StreamerOnlineCheckJob.java
@@ -0,0 +1,31 @@
+package dev.sheldan.abstracto.twitch.job;
+
+import dev.sheldan.abstracto.twitch.service.StreamerService;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.PersistJobDataAfterExecution;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@DisallowConcurrentExecution
+@Component
+@PersistJobDataAfterExecution
+public class StreamerOnlineCheckJob extends QuartzJobBean {
+
+ @Autowired
+ private StreamerService streamerService;
+
+ @Override
+ protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
+ log.info("Executing streamer online check");
+ try {
+ streamerService.checkAndNotifyAboutOnlineStreamers();
+ } catch (Exception exception) {
+ log.error("Failed to check online status for streamers.", exception);
+ }
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/job/TwitchRefreshTokenJob.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/job/TwitchRefreshTokenJob.java
new file mode 100644
index 000000000..d040b20d1
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/job/TwitchRefreshTokenJob.java
@@ -0,0 +1,31 @@
+package dev.sheldan.abstracto.twitch.job;
+
+import dev.sheldan.abstracto.twitch.service.StreamerService;
+import dev.sheldan.abstracto.twitch.service.TwitchService;
+import lombok.extern.slf4j.Slf4j;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.PersistJobDataAfterExecution;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@DisallowConcurrentExecution
+@Component
+@PersistJobDataAfterExecution
+public class TwitchRefreshTokenJob extends QuartzJobBean {
+ @Autowired
+ private TwitchService twitchService;
+
+ @Override
+ protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
+ log.info("Refreshing twitch token..");
+ try {
+ twitchService.refreshToken();
+ } catch (Exception exception) {
+ log.error("Failed to refresh twitch token.", exception);
+ }
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamSessionRepository.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamSessionRepository.java
new file mode 100644
index 000000000..3a770bf31
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamSessionRepository.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.twitch.repository;
+
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface StreamSessionRepository extends JpaRepository {
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamSessionSectionRepository.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamSessionSectionRepository.java
new file mode 100644
index 000000000..fc2cd63ab
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamSessionSectionRepository.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.twitch.repository;
+
+import dev.sheldan.abstracto.twitch.model.database.StreamSessionSection;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface StreamSessionSectionRepository extends JpaRepository {
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamerRepository.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamerRepository.java
new file mode 100644
index 000000000..885cfe279
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/repository/StreamerRepository.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.twitch.repository;
+
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface StreamerRepository extends JpaRepository {
+ boolean existsByServerAndUserId(AServer server, String userId);
+ boolean existsByServerAndName(AServer server, String name);
+ Optional findByServerAndName(AServer server, String name);
+ Optional findByServer_IdAndUserId(Long serverId, String userId);
+ List getByUserId(String userId);
+ List getStreamerByServer(AServer server);
+ void deleteByUserId(String id);
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/StreamSessionSectionServiceBean.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/StreamSessionSectionServiceBean.java
new file mode 100644
index 000000000..5b98e4a5a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/StreamSessionSectionServiceBean.java
@@ -0,0 +1,21 @@
+package dev.sheldan.abstracto.twitch.service;
+
+import com.github.twitch4j.helix.domain.Stream;
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import dev.sheldan.abstracto.twitch.model.database.StreamSessionSection;
+import dev.sheldan.abstracto.twitch.service.management.StreamSessionSectionManagementService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StreamSessionSectionServiceBean implements StreamSessionSectionService {
+
+ @Autowired
+ private StreamSessionSectionManagementService sessionSectionManagementService;
+
+ @Override
+ public StreamSessionSection createSectionFromStream(StreamSession session, Stream stream) {
+ return sessionSectionManagementService.addSection(session, stream.getGameId(), stream.getGameName(),
+ stream.getStartedAtInstant(), stream.getTitle(), stream.getViewerCount());
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/StreamerServiceBean.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/StreamerServiceBean.java
new file mode 100644
index 000000000..64b5e5687
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/StreamerServiceBean.java
@@ -0,0 +1,437 @@
+package dev.sheldan.abstracto.twitch.service;
+
+import com.github.twitch4j.helix.domain.Stream;
+import com.github.twitch4j.helix.domain.User;
+import dev.sheldan.abstracto.core.models.database.AChannel;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.core.models.template.display.ChannelDisplay;
+import dev.sheldan.abstracto.core.service.*;
+import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
+import dev.sheldan.abstracto.core.service.management.ServerManagementService;
+import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
+import dev.sheldan.abstracto.core.templating.model.MessageToSend;
+import dev.sheldan.abstracto.core.templating.service.TemplateService;
+import dev.sheldan.abstracto.core.utils.CompletableFutureList;
+import dev.sheldan.abstracto.twitch.config.TwitchFeatureConfig;
+import dev.sheldan.abstracto.twitch.config.TwitchFeatureDefinition;
+import dev.sheldan.abstracto.twitch.config.TwitchFeatureMode;
+import dev.sheldan.abstracto.twitch.config.TwitchPostTarget;
+import dev.sheldan.abstracto.twitch.exception.StreamerExistsException;
+import dev.sheldan.abstracto.twitch.exception.StreamerNotFoundInServerException;
+import dev.sheldan.abstracto.twitch.model.database.StreamSessionSection;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import dev.sheldan.abstracto.twitch.model.template.*;
+import dev.sheldan.abstracto.twitch.service.management.StreamSessionManagementService;
+import dev.sheldan.abstracto.twitch.service.management.StreamSessionSectionManagementService;
+import dev.sheldan.abstracto.twitch.service.management.StreamerManagementService;
+import lombok.extern.slf4j.Slf4j;
+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.GuildMessageChannel;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Component
+@Slf4j
+public class StreamerServiceBean implements StreamerService {
+
+ @Autowired
+ private TwitchService twitchService;
+
+ @Autowired
+ private StreamerManagementService streamerManagementService;
+
+ @Autowired
+ private UserInServerManagementService userInServerManagementService;
+
+ @Autowired
+ private ChannelManagementService channelManagementService;
+
+ @Autowired
+ private ServerManagementService serverManagementService;
+
+ @Autowired
+ private PostTargetService postTargetService;
+
+ @Autowired
+ private ChannelService channelService;
+
+ @Autowired
+ private TemplateService templateService;
+
+ @Autowired
+ private FeatureFlagService featureFlagService;
+
+ @Autowired
+ private FeatureModeService featureModeService;
+
+ @Autowired
+ private TwitchFeatureConfig twitchFeatureConfig;
+
+ @Autowired
+ private StreamSessionSectionService streamSessionSectionService;
+
+ @Autowired
+ private StreamSessionManagementService streamSessionManagementService;
+
+ @Autowired
+ private StreamSessionSectionManagementService streamSessionSectionManagementService;
+
+ @Autowired
+ private MessageService messageService;
+
+ @Autowired
+ private StreamerServiceBean self;
+
+ private static final String DEFAULT_NOTIFICATION_TEMPLATE = "twitch_streamer_go_live_notification";
+ private static final String WENT_OFF_LINE_MESSAGE_TEMPLATE = "twitch_streamer_went_offline_message";
+
+ @Override
+ public void createStreamer(String name, GuildMessageChannel targetChannel, Member creator, Member streamerMember) {
+ User streamerByName = twitchService.getStreamerByName(name);
+ Optional optionalStream = twitchService.getStreamOfUser(streamerByName.getId());
+ AServer server = serverManagementService.loadServer(creator.getGuild().getIdLong());
+ if(streamerManagementService.streamerExistsInServerByID(streamerByName.getId(), server)) {
+ throw new StreamerExistsException();
+ }
+ AChannel aTargetChannel = null;
+ if(targetChannel != null) {
+ aTargetChannel = channelManagementService.loadChannel(targetChannel);
+ }
+ AUserInAServer creatorUser = userInServerManagementService.loadOrCreateUser(creator);
+ AUserInAServer streamerUser = null;
+ if(streamerMember != null) {
+ streamerUser = userInServerManagementService.loadOrCreateUser(streamerMember);
+ }
+ Streamer createdStreamer = streamerManagementService.createStreamer(streamerByName.getId(), name, aTargetChannel, creatorUser, streamerUser, optionalStream.isPresent());
+ log.info("User {} created streamer {} in server {}.", creatorUser.getUserReference().getId(),
+ createdStreamer.getId(), creatorUser.getServerReference().getId());
+ optionalStream.ifPresent(stream -> createdStreamer.setCurrentGameId(stream.getGameId()));
+ }
+
+ @Override
+ public void removeStreamer(String name, Guild guild) {
+ AServer server = serverManagementService.loadServer(guild.getIdLong());
+ Streamer streamerInServerByName = streamerManagementService.getStreamerInServerByName(name, server).orElseThrow(StreamerNotFoundInServerException::new);
+ log.info("Removing streamer {} for server {}.", streamerInServerByName.getId(), guild.getIdLong());
+ streamSessionSectionManagementService.deleteSectionsOfStreamer(streamerInServerByName);
+ streamSessionManagementService.deleteSessionsOfStreamer(streamerInServerByName);
+ streamerManagementService.removeStreamer(streamerInServerByName);
+ }
+
+ @Override
+ public CompletableFutureList notifyAboutOnlineStream(Stream stream, Streamer streamer, User streamerUser) {
+ GoLiveNotificationModel model = GoLiveNotificationModel
+ .builder()
+ .channelName(stream.getUserName())
+ .mature(stream.isMature())
+ .currentSection(StreamSectionDisplay.fromStream(stream))
+ .streamerAvatarURL(streamerUser.getProfileImageUrl())
+ .streamURL(formatStreamUrl(stream.getUserName()))
+ .build();
+ MessageToSend messagetoSend;
+ if(streamer.getTemplateKey() == null) {
+ messagetoSend = templateService.renderEmbedTemplate(DEFAULT_NOTIFICATION_TEMPLATE, model, streamer.getServer().getId());
+ } else {
+ messagetoSend = templateService.renderEmbedTemplate(streamer.getTemplateKey(), model, streamer.getServer().getId());
+ }
+ if(Boolean.FALSE.equals(streamer.getShowNotifications())) {
+ log.info("Not announcing streamer {} in server {}.", streamer.getId(), streamer.getServer().getId());
+ return new CompletableFutureList<>(Arrays.asList(CompletableFuture.completedFuture(null)));
+ }
+ if(streamer.getNotificationChannel() != null) {
+ log.info("Announcing streamer {} in server {} to channel {}.", streamer.getId(), streamer.getServer().getId(), streamer.getNotificationChannel().getId());
+ List> futures = channelService.sendMessageEmbedToSendToAChannel(messagetoSend, streamer.getNotificationChannel());
+ return new CompletableFutureList<>(futures);
+ } else {
+ log.info("Announcing streamer {} in server {}.", streamer.getId(), streamer.getServer().getId());
+ List> futures = postTargetService.sendEmbedInPostTarget(messagetoSend, TwitchPostTarget.TWITCH_LIVE_NOTIFICATION, streamer.getServer().getId());
+ return new CompletableFutureList<>(futures);
+ }
+ }
+
+ public CompletableFuture updateExistingNotification(Stream stream, Streamer streamer, User streamerUser) {
+ List pastSections = streamer
+ .getCurrentSession()
+ .getSections()
+ .stream()
+ .sorted(Comparator.comparing(StreamSessionSection::getId).reversed())
+ .map(StreamSectionDisplay::fromSection)
+ .toList();
+ GoLiveNotificationModel model = GoLiveNotificationModel
+ .builder()
+ .channelName(stream.getUserName())
+ .mature(stream.isMature())
+ .currentSection(StreamSectionDisplay.fromStream(stream))
+ .streamerAvatarURL(streamerUser.getProfileImageUrl())
+ .pastSections(pastSections)
+ .streamURL(formatStreamUrl(stream.getUserName()))
+ .build();
+ MessageToSend messagetoSend;
+ if(streamer.getTemplateKey() == null) {
+ messagetoSend = templateService.renderEmbedTemplate(DEFAULT_NOTIFICATION_TEMPLATE, model, streamer.getServer().getId());
+ } else {
+ messagetoSend = templateService.renderEmbedTemplate(streamer.getTemplateKey(), model, streamer.getServer().getId());
+ }
+ if(Boolean.FALSE.equals(streamer.getShowNotifications())) {
+ log.info("Not editing notification, because notifications are disabled for streamer {} in server {}.", streamer.getId(), streamer.getServer().getId());
+ return CompletableFuture.completedFuture(null);
+ }
+ StreamSession currentSession = streamer.getCurrentSession();
+ log.info("Updating notification {} for streamer {} in server {} in channel {}.", currentSession.getId(), streamer.getId(), streamer.getServer().getId(), currentSession.getChannel().getId());
+ return channelService.editMessageInAChannelFuture(messagetoSend, streamer.getServer().getId(), currentSession.getChannel().getId(), currentSession.getId())
+ .thenAccept(message -> {});
+ }
+
+ @Override
+ public void changeStreamerNotificationToChannel(Streamer streamer, Long channelId) {
+ log.info("Changing notification channel of streamer {} to channel {} in server {}.", streamer.getId(), channelId, streamer.getServer().getId());
+ if(channelId != null) {
+ AChannel channel = channelManagementService.loadChannel(channelId);
+ streamer.setNotificationChannel(channel);
+ } else {
+ streamer.setNotificationChannel(null);
+ }
+ }
+
+ @Override
+ public void disableNotificationsForStreamer(Streamer streamer, Boolean newState) {
+ log.info("Setting notifications of streamer {} to {} in server {}.", streamer.getId(), newState, streamer.getServer().getId());
+ streamer.setShowNotifications(newState);
+ }
+
+ @Override
+ public void changeStreamerMemberToUserId(Streamer streamer, Long userId) {
+ log.info("Changing user id of streamer {} to channel {} in server {}.", streamer.getId(), userId, streamer.getServer().getId());
+ if(userId != null) {
+ AUserInAServer user = userInServerManagementService.loadOrCreateUser(streamer.getServer(), userId);
+ streamer.setStreamerUser(user);
+ } else {
+ streamer.setStreamerUser(null);
+ }
+ }
+
+ @Override
+ public void changeTemplateKeyTo(Streamer streamer, String templateKey) {
+ log.info("Changing template key of streamer {} in server {}.", streamer.getId(), streamer.getServer().getId());
+ streamer.setTemplateKey(templateKey);
+ }
+
+ @Override
+ public ListTwitchStreamerResponseModel getStreamersFromServer(Guild guild) {
+ log.info("Loading streamers into a model for server {}.", guild.getIdLong());
+ AServer server = serverManagementService.loadServer(guild.getIdLong());
+ List streamers = streamerManagementService.getStreamersForServer(server);
+ GuildMessageChannel postTargetChannel = postTargetService.getPostTargetChannel(TwitchPostTarget.TWITCH_LIVE_NOTIFICATION, server.getId()).orElse(null);
+ List models = streamers
+ .stream()
+ .map(streamer -> {
+ GuildMessageChannel notificationChannel;
+ if(streamer.getNotificationChannel() != null) {
+ notificationChannel = channelService.getMessageChannelFromServer(server.getId(), streamer.getNotificationChannel().getId());
+ } else {
+ notificationChannel = postTargetChannel;
+ }
+ return TwitchStreamerDisplayModel
+ .builder()
+ .name(streamer.getName())
+ .targetChannel(ChannelDisplay.fromChannel(notificationChannel))
+ .streamerURL(formatStreamUrl(streamer.getName()))
+ .showNotifications(streamer.getShowNotifications())
+ .build();
+
+ }).collect(Collectors.toList());
+ return ListTwitchStreamerResponseModel
+ .builder()
+ .streamers(models)
+ .build();
+ }
+
+ private String formatStreamUrl(String name) {
+ return String.format("https://twitch.tv/%s", name);
+ }
+
+ @Override
+ @Transactional
+ public void checkAndNotifyAboutOnlineStreamers() {
+ log.info("Checking if stream notifications need to be sent.");
+ List servers = serverManagementService.getAllServers();
+ List serversWithEnabledFeature = servers
+ .stream()
+ .filter(server -> featureFlagService.isFeatureEnabled(twitchFeatureConfig, server))
+ .toList();
+ log.debug("Searching through {} servers for twitch notifications and {} have twitch feature enabled.", servers.size(), serversWithEnabledFeature.size());
+ serversWithEnabledFeature.forEach(server -> {
+ List streamersInServer = streamerManagementService.getStreamersForServer(server);
+ Map streamerIdMap = streamersInServer
+ .stream()
+ .collect(Collectors.toMap(Streamer::getId, Function.identity()));
+ Map streamerMap = streamersInServer
+ .stream()
+ .collect(Collectors.toMap(Streamer::getUserId, Function.identity()));
+ log.debug("Found {} streamers for server {}.", streamersInServer.size(), server.getId());
+ if(streamersInServer.isEmpty()) {
+ return;
+ }
+ List userIds = streamersInServer
+ .stream()
+ .map(Streamer::getUserId)
+ .distinct()
+ .toList();
+ List streamsOfUsers = twitchService.getStreamsByUserIds(userIds);
+ Set onlineStreamers = new HashSet<>();
+ Map updateNotificationFlagValues = new HashMap<>();
+ streamsOfUsers.forEach(stream -> self.processOnlineStreamer(server, streamerMap, onlineStreamers, updateNotificationFlagValues, stream));
+ Set allStreamersInServer = streamerIdMap.keySet();
+ allStreamersInServer.removeAll(onlineStreamers); // then we have those that went offline
+ Map deleteFlagValues = new HashMap<>();
+ allStreamersInServer.forEach(streamerId -> {
+ Streamer streamer = streamerIdMap.get(streamerId);
+ self.processOfflineStreamer(server, streamer, deleteFlagValues);
+ });
+ });
+ }
+
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
+ public void processOfflineStreamer(AServer server, Streamer streamer, Map deleteFlagValues) {
+ Long streamerId = streamer.getId();
+ if(!streamer.getOnline()) {
+ return;
+ }
+ log.info("Streamer {} went offline.", streamerId);
+ if (deleteFlagValues.computeIfAbsent(streamer.getServer().getId(),
+ aLong -> featureModeService.featureModeActive(TwitchFeatureDefinition.TWITCH, aLong, TwitchFeatureMode.DELETE_NOTIFICATION))) {
+ Long channelId = streamer.getCurrentSession().getChannel().getId();
+ Long messageId = streamer.getCurrentSession().getId();
+ messageService.deleteMessageInChannelInServer(streamer.getServer().getId(), channelId, messageId).thenAccept(unused -> {
+ log.info("Deleted notification message for streamer {}", streamerId);
+ }).exceptionally(throwable -> {
+ log.warn("Failed to delete notification message for streamer {}", streamerId, throwable);
+ return null;
+ });
+ } else {
+ User streamerUser = twitchService.getStreamerById(streamer.getUserId());
+ if(streamer.getCurrentSession() == null) {
+ log.warn("No session found for streamer {} - nothing to update or delete.", streamer.getId());
+ streamer.setCurrentSession(null);
+ streamer.setOnline(false);
+ streamer.setCurrentGameId(null);
+ streamerManagementService.saveStreamer(streamer);
+ return;
+ }
+ List pastSections = streamer
+ .getCurrentSession()
+ .getSections()
+ .stream()
+ .sorted(Comparator.comparing(StreamSessionSection::getId).reversed())
+ .map(StreamSectionDisplay::fromSection)
+ .toList();
+ String offlineImageURL = StringUtils.isBlank(streamerUser.getOfflineImageUrl()) ? null : streamerUser.getOfflineImageUrl();
+ GoOfflineNotificationModel model = GoOfflineNotificationModel
+ .builder()
+ .channelName(streamer.getName())
+ .avatarURL(streamerUser.getProfileImageUrl())
+ .offlineImageURL(offlineImageURL)
+ .pastSections(pastSections)
+ .build();
+ log.info("Updating existing notification for streamer {} in server {}.", streamer.getId(), server.getId());
+ MessageToSend messageToSend = templateService.renderEmbedTemplate(WENT_OFF_LINE_MESSAGE_TEMPLATE, model, server.getId());
+ Long channelId = streamer.getCurrentSession().getChannel().getId();
+ Long messageId = streamer.getCurrentSession().getId();
+ channelService.editMessageInAChannelFuture(messageToSend, server.getId(), channelId, messageId).thenAccept(message -> {
+ log.debug("Successfully updated notification {}.", messageId);
+ }).exceptionally(throwable -> {
+ log.debug("Failed to update notification {}", messageId, throwable);
+ return null;
+ });
+ if(!streamer.getName().equals(streamerUser.getLogin())) {
+ streamer.setName(streamerUser.getLogin());
+ }
+ }
+ streamer.setCurrentSession(null);
+ streamer.setOnline(false);
+ streamer.setCurrentGameId(null);
+ streamerManagementService.saveStreamer(streamer);
+ }
+
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
+ public void processOnlineStreamer(AServer server, Map streamerMap, Set onlineStreamers, Map updateNotificationFlagValues, Stream stream) {
+ Streamer streamer = streamerMap.get(stream.getUserId());
+ Long streamerId = streamer.getId();
+ onlineStreamers.add(streamerId);
+ // it could be that the streamer is already online, but we have no sessions yet
+ // that is the case if you add an online streamer
+ User streamerUser = twitchService.getStreamerById(stream.getUserId());
+ StreamSession currentSession = streamer.getCurrentSession();
+ if (Boolean.TRUE.equals(streamer.getOnline()) && currentSession != null) {
+ // we already know that this streamer is online
+ log.debug("Not notifying for streamer {} in server {} - streamer is already online.", streamerId, server.getId());
+ if(!stream.getGameId().equals(streamer.getCurrentGameId())) {
+ log.info("Streamer {} changed game from {} to {} - storing new section.", streamerId, streamer.getCurrentGameId(), stream.getGameId());
+ streamSessionSectionService.createSectionFromStream(currentSession, stream);
+ if (updateNotificationFlagValues.computeIfAbsent(streamer.getServer().getId(),
+ aLong -> featureModeService.featureModeActive(TwitchFeatureDefinition.TWITCH, aLong, TwitchFeatureMode.UPDATE_NOTIFICATION))) {
+ log.info("Updating notification is enabled - updating notification for streamer {}.", streamer.getId());
+ Long notificationMessageId = currentSession.getId();
+ Long notificationChannelId = currentSession.getChannel().getId();
+ Long serverId = streamer.getServer().getId();
+ updateExistingNotification(stream, streamer, streamerUser).thenAccept(unused -> {
+ log.info("Updating existing notification {} for server {} in channel {} about streamer {}.",
+ notificationMessageId, serverId, notificationChannelId, streamerId);
+ }).exceptionally(throwable -> {
+ log.error("Failed to update existing notification {} for server {} in channel {} about streamer {}.",
+ notificationMessageId, serverId, notificationChannelId, streamerId, throwable);
+ return null;
+ });
+ }
+ streamer.setCurrentGameId(stream.getGameId());
+ }
+ return;
+ }
+ CompletableFutureList messages = notifyAboutOnlineStream(stream, streamer, streamerUser);
+ messages.getMainFuture()
+ .thenAccept(unused -> {
+ Message message = messages.getFutures().get(0).join();
+ if(message != null) {
+ try {
+ self.storeStreamNotificationMessage(message, streamerId, stream);
+ } catch (Exception exception) {
+ log.error("Failed to store stream notification message of streamer {}.", streamerId, exception);
+ }
+ }
+ }).exceptionally(throwable -> {
+ log.error("Failed to notify about online stream of streamer {}.", streamerId, throwable);
+ return null;
+ });
+ }
+
+ @Transactional
+ public void storeStreamNotificationMessage(Message message, Long streamerId, Stream stream) {
+ log.info("Storing notification for streamer {} in in server {} in channel {} using message {}.",
+ streamerId, message.getGuild().getIdLong(), message.getChannel().getIdLong(), message.getIdLong());
+ Optional streamerOptional = streamerManagementService.getStreamerById(streamerId);
+ streamerOptional.
+ ifPresent(streamer -> {
+ streamer.setCurrentGameId(stream.getGameId());
+ StreamSession session = streamSessionManagementService.startSession(streamer, message.getIdLong(), message.getChannel().getIdLong(), stream);
+ streamer.setCurrentSession(session);
+ streamer.setOnline(true);
+ if(!streamer.getName().equals(stream.getUserLogin())) {
+ streamer.setName(stream.getUserLogin());
+ }
+ streamSessionSectionService.createSectionFromStream(session, stream);
+ streamerManagementService.saveStreamer(streamer);
+ });
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchServiceBean.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchServiceBean.java
new file mode 100644
index 000000000..9ebbc53df
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchServiceBean.java
@@ -0,0 +1,68 @@
+package dev.sheldan.abstracto.twitch.service;
+
+import com.github.philippheuer.credentialmanager.domain.OAuth2Credential;
+import com.github.twitch4j.ITwitchClient;
+import com.github.twitch4j.auth.providers.TwitchIdentityProvider;
+import com.github.twitch4j.helix.domain.Stream;
+import com.github.twitch4j.helix.domain.User;
+import dev.sheldan.abstracto.twitch.exception.StreamerNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Component
+public class TwitchServiceBean implements TwitchService {
+
+ @Autowired
+ private ITwitchClient twitchClient;
+
+ @Autowired
+ private TwitchIdentityProvider identityProvider;
+
+ @Autowired
+ private OAuth2Credential oAuth2Credential;
+
+ @Override
+ public User getStreamerByName(String name) {
+ return getStreamerByNameOptional(name).orElseThrow(StreamerNotFoundException::new);
+ }
+
+ @Override
+ public Optional getStreamerByNameOptional(String name) {
+ List allUsersWithName = twitchClient.getClientHelper().getTwitchHelix().getUsers(null, null, Collections.singletonList(name)).execute().getUsers();
+ if(allUsersWithName.size() != 1) {
+ return Optional.empty();
+ } else {
+ return Optional.of(allUsersWithName.get(0));
+ }
+ }
+
+ @Override
+ public User getStreamerById(String userId) {
+ return twitchClient.getClientHelper().getTwitchHelix().getUsers(null, Collections.singletonList(userId), null).execute().getUsers().get(0);
+ }
+
+ @Override
+ public List getStreamsByUserIds(List userIds) {
+ return twitchClient.getClientHelper().getTwitchHelix().getStreams(null, null, null, null, null, null, userIds, null).execute().getStreams();
+ }
+
+ @Override
+ public Optional getStreamOfUser(String userId) {
+ List allStreams = getStreamsByUserIds(Arrays.asList(userId));
+ return allStreams.stream().findFirst();
+ }
+
+ @Override
+ public Map getStreamsByUserIdsMapped(List userIds) {
+ return getStreamsByUserIds(userIds).stream().collect(Collectors.toMap(Stream::getUserId, Function.identity()));
+ }
+
+ @Override
+ public void refreshToken() {
+ oAuth2Credential.updateCredential(identityProvider.getAppAccessToken());
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchServiceConfig.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchServiceConfig.java
new file mode 100644
index 000000000..8c958c668
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchServiceConfig.java
@@ -0,0 +1,40 @@
+package dev.sheldan.abstracto.twitch.service;
+
+import com.github.philippheuer.credentialmanager.domain.OAuth2Credential;
+import com.github.twitch4j.ITwitchClient;
+import com.github.twitch4j.TwitchClientBuilder;
+import com.github.twitch4j.auth.providers.TwitchIdentityProvider;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class TwitchServiceConfig {
+
+ @Value("${abstracto.feature.twitch.clientId}")
+ private String twitchClientId;
+
+ @Value("${abstracto.feature.twitch.clientSecret}")
+ private String twitchSecret;
+
+ @Bean
+ public TwitchIdentityProvider twitchIdentityProvider() {
+ return new TwitchIdentityProvider(twitchClientId, twitchSecret, null);
+ }
+
+ @Bean
+ public OAuth2Credential credential(TwitchIdentityProvider tip) {
+ return tip.getAppAccessToken();
+ }
+
+ @Bean
+ public ITwitchClient twitchClient(OAuth2Credential oAuth2Credential) {;
+ return TwitchClientBuilder.builder()
+ .withClientId(twitchClientId)
+ .withClientSecret(twitchSecret)
+ .withDefaultAuthToken(oAuth2Credential)
+ .withEnableHelix(true)
+ .build();
+ }
+
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionManagementServiceBean.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionManagementServiceBean.java
new file mode 100644
index 000000000..48a3e0abc
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionManagementServiceBean.java
@@ -0,0 +1,52 @@
+package dev.sheldan.abstracto.twitch.service.management;
+
+import com.github.twitch4j.helix.domain.Stream;
+import dev.sheldan.abstracto.core.models.database.AChannel;
+import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import dev.sheldan.abstracto.twitch.repository.StreamSessionRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StreamSessionManagementServiceBean implements StreamSessionManagementService {
+
+ @Autowired
+ private StreamSessionRepository repository;
+
+ @Autowired
+ private ChannelManagementService channelManagementService;
+
+ @Override
+ public StreamSession startSession(Streamer streamer, Long messageId, Long channelId, Stream stream) {
+ AChannel channel = channelManagementService.loadChannel(channelId);
+ StreamSession notification = StreamSession
+ .builder()
+ .id(messageId)
+ .startTime(stream.getStartedAtInstant())
+ .channel(channel)
+ .streamer(streamer)
+ .streamId(stream.getId())
+ .build();
+ return repository.save(notification);
+ }
+
+ @Override
+ public void deleteSessionsOfStreamer(Streamer streamer) {
+ repository.deleteAll(streamer.getSessions());
+ streamer.getSessions().clear();
+ }
+
+ @Override
+ public void deleteSession(StreamSession notification) {
+ notification.getStreamer().getSessions().remove(notification);
+ repository.delete(notification);
+ }
+
+ @Override
+ public void deleteSession(Streamer streamer, Long messageId) {
+ streamer.getSessions().removeIf(notification -> notification.getId().equals(messageId));
+ repository.deleteById(messageId);
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionSectionManagementServiceBean.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionSectionManagementServiceBean.java
new file mode 100644
index 000000000..44483d7dc
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionSectionManagementServiceBean.java
@@ -0,0 +1,51 @@
+package dev.sheldan.abstracto.twitch.service.management;
+
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import dev.sheldan.abstracto.twitch.model.database.StreamSessionSection;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+import dev.sheldan.abstracto.twitch.repository.StreamSessionSectionRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.util.List;
+
+@Component
+public class StreamSessionSectionManagementServiceBean implements StreamSessionSectionManagementService {
+
+ @Autowired
+ private StreamSessionSectionRepository repository;
+
+ @Override
+ public StreamSessionSection addSection(StreamSession session, String gameId, String gameName, Instant startTime, String title, Integer viewerCount) {
+ StreamSessionSection section = StreamSessionSection
+ .builder()
+ .streamer(session.getStreamer())
+ .gameId(gameId)
+ .gameName(gameName)
+ .title(title)
+ .viewerCount(viewerCount)
+ .session(session)
+ .build();
+ return repository.save(section);
+ }
+
+ @Override
+ public void deleteSectionsOfSession(StreamSession session) {
+ repository.deleteAll(session.getSections());
+ session.getSections().clear();
+ }
+
+ @Override
+ public void deleteSectionsOfStreamer(Streamer streamer) {
+ List sections = streamer
+ .getSessions()
+ .stream()
+ .flatMap(session -> session.getSections().stream())
+ .toList();
+ repository.deleteAll(sections);
+ streamer.getSessions().forEach(session -> {
+ session.getSections().clear();
+ });
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamerManagementServiceBean.java b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamerManagementServiceBean.java
new file mode 100644
index 000000000..2c385a33b
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamerManagementServiceBean.java
@@ -0,0 +1,90 @@
+package dev.sheldan.abstracto.twitch.service.management;
+
+import dev.sheldan.abstracto.core.models.database.AChannel;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.twitch.exception.StreamerExistsException;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+import dev.sheldan.abstracto.twitch.repository.StreamerRepository;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Optional;
+
+@Component
+@Slf4j
+public class StreamerManagementServiceBean implements StreamerManagementService {
+
+ @Autowired
+ private StreamerRepository streamerRepository;
+
+ @Override
+ public Streamer createStreamer(String id, String name, AChannel targetChannel, AUserInAServer creator, AUserInAServer member, Boolean isOnline) {
+ Streamer streamer = Streamer
+ .builder()
+ .name(name)
+ .showNotifications(true)
+ .userId(id)
+ .online(isOnline)
+ .creator(creator)
+ .server(creator.getServerReference())
+ .streamerUser(member)
+ .notificationChannel(targetChannel)
+ .build();
+ log.info("Creating streamer {} because of user {} in server {}.", id, creator.getServerReference().getId(), creator.getUserReference().getId());
+ return streamerRepository.save(streamer);
+ }
+
+ @Override
+ public void removeStreamer(String id) {
+ streamerRepository.deleteByUserId(id);
+ }
+
+ @Override
+ public Optional getStreamerById(Long id) {
+ return streamerRepository.findById(id);
+ }
+
+ @Override
+ public void removeStreamer(Streamer streamer) {
+ streamerRepository.delete(streamer);
+ }
+
+ @Override
+ public void saveStreamer(Streamer streamer) {
+ streamerRepository.save(streamer);
+ }
+
+ @Override
+ public Streamer getStreamerInServerById(String id, Long serverId) {
+ return streamerRepository.findByServer_IdAndUserId(serverId, id)
+ .orElseThrow(StreamerExistsException::new);
+ }
+
+ @Override
+ public List getStreamerInServers(String id) {
+ return streamerRepository.getByUserId(id);
+ }
+
+ @Override
+ public boolean streamerExistsInServerByID(String id, AServer server) {
+ return streamerRepository.existsByServerAndUserId(server, id);
+ }
+
+ @Override
+ public boolean streamerExistsInServerByName(String name, AServer server) {
+ return streamerRepository.existsByServerAndName(server, name);
+ }
+
+ @Override
+ public Optional getStreamerInServerByName(String name, AServer server) {
+ return streamerRepository.findByServerAndName(server, name);
+ }
+
+ @Override
+ public List getStreamersForServer(AServer server) {
+ return streamerRepository.getStreamerByServer(server);
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/collection.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/collection.xml
new file mode 100644
index 000000000..30263db2b
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/collection.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/command.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/command.xml
new file mode 100644
index 000000000..90a16f2b3
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/command.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/data.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/data.xml
new file mode 100644
index 000000000..32c18edff
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/data.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/feature.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/feature.xml
new file mode 100644
index 000000000..d72b4113a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/feature.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/streamer_online_check_job.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/streamer_online_check_job.xml
new file mode 100644
index 000000000..a5dd978b6
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/streamer_online_check_job.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/twitch_refresh_token_job.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/twitch_refresh_token_job.xml
new file mode 100644
index 000000000..ff4179d77
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/seedData/twitch_refresh_token_job.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/stream_session.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/stream_session.xml
new file mode 100644
index 000000000..5d2620c67
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/stream_session.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DROP TRIGGER IF EXISTS stream_session_update_trigger ON stream_session;
+ CREATE TRIGGER stream_session_update_trigger BEFORE UPDATE ON stream_session FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
+
+
+ DROP TRIGGER IF EXISTS stream_session_insert_trigger ON stream_session;
+ CREATE TRIGGER stream_session_insert_trigger BEFORE INSERT ON stream_session FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/stream_session_section.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/stream_session_section.xml
new file mode 100644
index 000000000..4068fb36e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/stream_session_section.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DROP TRIGGER IF EXISTS stream_session_section_update_trigger ON stream_session_section;
+ CREATE TRIGGER stream_session_section_update_trigger BEFORE UPDATE ON stream_session_section FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
+
+
+ DROP TRIGGER IF EXISTS stream_session_section_insert_trigger ON stream_session_section;
+ CREATE TRIGGER stream_session_section_insert_trigger BEFORE INSERT ON stream_session_section FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/streamer.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/streamer.xml
new file mode 100644
index 000000000..03adb5a62
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/streamer.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DROP TRIGGER IF EXISTS streamer_update_trigger ON streamer;
+ CREATE TRIGGER streamer_update_trigger BEFORE UPDATE ON streamer FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
+
+
+ DROP TRIGGER IF EXISTS streamer_insert_trigger ON streamer;
+ CREATE TRIGGER streamer_insert_trigger BEFORE INSERT ON streamer FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/tables.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/tables.xml
new file mode 100644
index 000000000..5322f8c9e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/1.4.27/tables/tables.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/dbchangelog.xsd b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/dbchangelog.xsd
new file mode 100644
index 000000000..83483a5ec
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/dbchangelog.xsd
@@ -0,0 +1,1386 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Extension to standard XSD boolean type to allow ${} parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Extension to standard XSD integer type to allow ${} parameters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onChangeLogPreconditionOnSqlOutput determines what should
+ happen when evaluating this precondition in updateSQL mode. TEST: Run
+ precondition, FAIL: Fail precondition, IGNORE: Skip precondition check
+ [DEFAULT]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Used with valueClobFile to specify file encoding explicitly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true for a cycling sequence, false for a non-cycling sequence.
+ Default is false.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/twitch-changeLog.xml b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/twitch-changeLog.xml
new file mode 100644
index 000000000..1da2de3a8
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/migrations/twitch-changeLog.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/twitch-config.properties b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/twitch-config.properties
new file mode 100644
index 000000000..8675b58d6
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-impl/src/main/resources/twitch-config.properties
@@ -0,0 +1,15 @@
+abstracto.featureFlags.twitch.featureName=twitch
+abstracto.featureFlags.twitch.enabled=false
+
+abstracto.postTargets.twitchLiveNotification.name=twitchLiveNotification
+
+abstracto.feature.twitch.clientId=${TWITCH_CLIENT_ID}
+abstracto.feature.twitch.clientSecret=${TWITCH_SECRET}
+
+abstracto.featureModes.deleteNotification.featureName=twitch
+abstracto.featureModes.deleteNotification.mode=deleteNotification
+abstracto.featureModes.deleteNotification.enabled=true
+
+abstracto.featureModes.updateNotification.featureName=twitch
+abstracto.featureModes.updateNotification.mode=updateNotification
+abstracto.featureModes.updateNotification.enabled=true
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/pom.xml b/abstracto-application/abstracto-modules/twitch/twitch-int/pom.xml
new file mode 100644
index 000000000..3ee187031
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/pom.xml
@@ -0,0 +1,26 @@
+
+
+
+ dev.sheldan.abstracto.modules
+ twitch
+ 1.4.27-SNAPSHOT
+
+ 4.0.0
+
+ twitch-int
+
+
+
+ com.github.twitch4j
+ twitch4j
+
+
+
+
+ 8
+ 8
+
+
+
\ No newline at end of file
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureConfig.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureConfig.java
new file mode 100644
index 000000000..fc22278f1
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureConfig.java
@@ -0,0 +1,28 @@
+package dev.sheldan.abstracto.twitch.config;
+
+import dev.sheldan.abstracto.core.config.FeatureConfig;
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import dev.sheldan.abstracto.core.config.FeatureMode;
+import dev.sheldan.abstracto.core.config.PostTargetEnum;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Component
+public class TwitchFeatureConfig implements FeatureConfig {
+ @Override
+ public FeatureDefinition getFeature() {
+ return TwitchFeatureDefinition.TWITCH;
+ }
+
+ @Override
+ public List getRequiredPostTargets() {
+ return Arrays.asList(TwitchPostTarget.TWITCH_LIVE_NOTIFICATION);
+ }
+
+ @Override
+ public List getAvailableModes() {
+ return Arrays.asList(TwitchFeatureMode.DELETE_NOTIFICATION, TwitchFeatureMode.UPDATE_NOTIFICATION);
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureDefinition.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureDefinition.java
new file mode 100644
index 000000000..09d188338
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureDefinition.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.twitch.config;
+
+import dev.sheldan.abstracto.core.config.FeatureDefinition;
+import lombok.Getter;
+
+@Getter
+public enum TwitchFeatureDefinition implements FeatureDefinition {
+ TWITCH("twitch");
+
+ private String key;
+
+ TwitchFeatureDefinition(String key) {
+ this.key = key;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureMode.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureMode.java
new file mode 100644
index 000000000..d74f45ac6
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchFeatureMode.java
@@ -0,0 +1,16 @@
+package dev.sheldan.abstracto.twitch.config;
+
+import dev.sheldan.abstracto.core.config.FeatureMode;
+import lombok.Getter;
+
+@Getter
+public enum TwitchFeatureMode implements FeatureMode {
+ DELETE_NOTIFICATION("deleteNotification"), UPDATE_NOTIFICATION("updateNotification");
+
+ private final String key;
+
+ TwitchFeatureMode(String key) {
+ this.key = key;
+ }
+
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchPostTarget.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchPostTarget.java
new file mode 100644
index 000000000..ec06107dc
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchPostTarget.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.twitch.config;
+
+import dev.sheldan.abstracto.core.config.PostTargetEnum;
+import lombok.Getter;
+
+@Getter
+public enum TwitchPostTarget implements PostTargetEnum {
+ TWITCH_LIVE_NOTIFICATION("twitchLiveNotification");
+
+ private String key;
+
+ TwitchPostTarget(String key) {
+ this.key = key;
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchSlashCommandNames.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchSlashCommandNames.java
new file mode 100644
index 000000000..9cab785e5
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/config/TwitchSlashCommandNames.java
@@ -0,0 +1,8 @@
+package dev.sheldan.abstracto.twitch.config;
+
+public class TwitchSlashCommandNames {
+ private TwitchSlashCommandNames() {
+
+ }
+ public static final String TWITCH = "twitch";
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerExistsException.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerExistsException.java
new file mode 100644
index 000000000..2fb60ed5a
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerExistsException.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.twitch.exception;
+
+import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.templating.Templatable;
+
+public class StreamerExistsException extends AbstractoRunTimeException implements Templatable {
+ public StreamerExistsException() {
+ super("Streamer is already setup in server.");
+ }
+
+ @Override
+ public String getTemplateName() {
+ return "streamer_already_exists_in_server_exception";
+ }
+
+ @Override
+ public Object getTemplateModel() {
+ return new Object();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerNotFoundException.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerNotFoundException.java
new file mode 100644
index 000000000..8f2103f7d
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerNotFoundException.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.twitch.exception;
+
+import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.templating.Templatable;
+
+public class StreamerNotFoundException extends AbstractoRunTimeException implements Templatable {
+ public StreamerNotFoundException() {
+ super("Streamer was not found on Twitch..");
+ }
+
+ @Override
+ public String getTemplateName() {
+ return "streamer_not_exists_exception";
+ }
+
+ @Override
+ public Object getTemplateModel() {
+ return new Object();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerNotFoundInServerException.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerNotFoundInServerException.java
new file mode 100644
index 000000000..2f4ba5977
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/exception/StreamerNotFoundInServerException.java
@@ -0,0 +1,20 @@
+package dev.sheldan.abstracto.twitch.exception;
+
+import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
+import dev.sheldan.abstracto.core.templating.Templatable;
+
+public class StreamerNotFoundInServerException extends AbstractoRunTimeException implements Templatable {
+ public StreamerNotFoundInServerException() {
+ super("Streamer was not set up in server.");
+ }
+
+ @Override
+ public String getTemplateName() {
+ return "streamer_not_exists_in_server_exception";
+ }
+
+ @Override
+ public Object getTemplateModel() {
+ return new Object();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/StreamSession.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/StreamSession.java
new file mode 100644
index 000000000..130b0f63e
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/StreamSession.java
@@ -0,0 +1,52 @@
+package dev.sheldan.abstracto.twitch.model.database;
+
+import dev.sheldan.abstracto.core.models.database.AChannel;
+import lombok.*;
+
+import jakarta.persistence.*;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name="stream_session")
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+@EqualsAndHashCode
+public class StreamSession {
+ @Id
+ @Column(name = "id", nullable = false)
+ private Long id;
+
+ @EqualsAndHashCode.Exclude
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "streamer_id", nullable = false)
+ private Streamer streamer;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "channel_id", nullable = false)
+ private AChannel channel;
+
+ @Column(name = "stream_id", nullable = false)
+ private String streamId;
+
+ @Column(name = "created", nullable = false, insertable = false, updatable = false)
+ private Instant created;
+
+ @Column(name = "updated", insertable = false, updatable = false)
+ private Instant updated;
+
+ @Column(name = "startTime", nullable = false)
+ private Instant startTime;
+
+ @EqualsAndHashCode.Exclude
+ @OneToMany(
+ fetch = FetchType.LAZY,
+ cascade = {CascadeType.PERSIST, CascadeType.MERGE},
+ mappedBy = "session")
+ @Builder.Default
+ private List sections = new ArrayList<>();
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/StreamSessionSection.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/StreamSessionSection.java
new file mode 100644
index 000000000..e5b04e23d
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/StreamSessionSection.java
@@ -0,0 +1,51 @@
+package dev.sheldan.abstracto.twitch.model.database;
+
+import jakarta.persistence.*;
+import lombok.*;
+
+import java.time.Instant;
+
+@Entity
+@Table(name="stream_session_section")
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+@EqualsAndHashCode
+public class StreamSessionSection {
+
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id", nullable = false)
+ private Long id;
+
+ @EqualsAndHashCode.Exclude
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "streamer_id", nullable = false)
+ private Streamer streamer;
+
+ @EqualsAndHashCode.Exclude
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "stream_session_id", nullable = false)
+ private StreamSession session;
+
+ @Column(name = "game_id", nullable = false)
+ private String gameId;
+
+ @Column(name = "game_name", nullable = false)
+ private String gameName;
+
+ @Column(name = "viewer_count", nullable = false)
+ private Integer viewerCount;
+
+ @Column(name = "title", nullable = false)
+ private String title;
+
+ @Column(name = "created", nullable = false, insertable = false, updatable = false)
+ private Instant created;
+
+ @Column(name = "updated", insertable = false, updatable = false)
+ private Instant updated;
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/Streamer.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/Streamer.java
new file mode 100644
index 000000000..1f3d80b17
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/database/Streamer.java
@@ -0,0 +1,79 @@
+package dev.sheldan.abstracto.twitch.model.database;
+
+import dev.sheldan.abstracto.core.models.database.AChannel;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import lombok.*;
+
+import jakarta.persistence.*;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name="streamer")
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+@EqualsAndHashCode
+public class Streamer {
+ @Id
+ @Column(name = "id", nullable = false)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "user_id", nullable = false)
+ private String userId;
+
+ @Column(name = "name", nullable = false)
+ private String name;
+
+ @Column(name = "template_key")
+ private String templateKey;
+
+ @Column(name = "current_game_id")
+ private String currentGameId;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "notification_channel_id")
+ private AChannel notificationChannel;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "creator_user_in_server_id", nullable = false)
+ private AUserInAServer creator;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "server_id", nullable = false)
+ private AServer server;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "streamer_user_in_server_id")
+ private AUserInAServer streamerUser;
+
+ @EqualsAndHashCode.Exclude
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "current_session_id")
+ private StreamSession currentSession;
+
+ @Column(name = "show_notifications", nullable = false)
+ private Boolean showNotifications;
+
+ @Column(name = "online", nullable = false)
+ private Boolean online;
+
+ @Column(name = "created", nullable = false, insertable = false, updatable = false)
+ private Instant created;
+
+ @Column(name = "updated", insertable = false, updatable = false)
+ private Instant updated;
+
+ @EqualsAndHashCode.Exclude
+ @OneToMany(
+ fetch = FetchType.LAZY,
+ cascade = {CascadeType.PERSIST, CascadeType.MERGE},
+ mappedBy = "streamer")
+ @Builder.Default
+ private List sessions = new ArrayList<>();
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/GoLiveNotificationModel.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/GoLiveNotificationModel.java
new file mode 100644
index 000000000..5b623fc57
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/GoLiveNotificationModel.java
@@ -0,0 +1,17 @@
+package dev.sheldan.abstracto.twitch.model.template;
+
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+@Builder
+@Getter
+public class GoLiveNotificationModel {
+ private String channelName;
+ private StreamSectionDisplay currentSection;
+ private List pastSections;
+ private Boolean mature;
+ private String streamURL;
+ private String streamerAvatarURL;
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/GoOfflineNotificationModel.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/GoOfflineNotificationModel.java
new file mode 100644
index 000000000..e9dffc58f
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/GoOfflineNotificationModel.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.twitch.model.template;
+
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+@Builder
+@Getter
+public class GoOfflineNotificationModel {
+ private String channelName;
+ private List pastSections;
+ private String offlineImageURL;
+ private String avatarURL;
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/ListTwitchStreamerResponseModel.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/ListTwitchStreamerResponseModel.java
new file mode 100644
index 000000000..8b44c2c1d
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/ListTwitchStreamerResponseModel.java
@@ -0,0 +1,12 @@
+package dev.sheldan.abstracto.twitch.model.template;
+
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+@Builder
+public class ListTwitchStreamerResponseModel {
+ private List streamers;
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/StreamSectionDisplay.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/StreamSectionDisplay.java
new file mode 100644
index 000000000..ab99001b0
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/StreamSectionDisplay.java
@@ -0,0 +1,42 @@
+package dev.sheldan.abstracto.twitch.model.template;
+
+import com.github.twitch4j.helix.domain.Stream;
+import dev.sheldan.abstracto.twitch.model.database.StreamSessionSection;
+import lombok.Builder;
+import lombok.Getter;
+
+import java.time.Instant;
+
+@Builder
+@Getter
+public class StreamSectionDisplay {
+ private String gameName;
+ private String gameId;
+ private String title;
+ private Integer viewerCount;
+ private Instant startedAt;
+ private String thumbnailURL;
+
+ public static StreamSectionDisplay fromStream(Stream stream) {
+ return StreamSectionDisplay
+ .builder()
+ .startedAt(stream.getStartedAtInstant())
+ .gameName(stream.getGameName())
+ .thumbnailURL(stream.getThumbnailUrl(1280, 720))
+ .viewerCount(stream.getViewerCount())
+ .title(stream.getTitle())
+ .gameId(stream.getGameId())
+ .build();
+ }
+
+ public static StreamSectionDisplay fromSection(StreamSessionSection section) {
+ return StreamSectionDisplay
+ .builder()
+ .startedAt(section.getCreated())
+ .gameName(section.getGameName())
+ .viewerCount(section.getViewerCount())
+ .title(section.getTitle())
+ .gameId(section.getGameId())
+ .build();
+ }
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/TwitchStreamerDisplayModel.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/TwitchStreamerDisplayModel.java
new file mode 100644
index 000000000..01197fefa
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/model/template/TwitchStreamerDisplayModel.java
@@ -0,0 +1,15 @@
+package dev.sheldan.abstracto.twitch.model.template;
+
+import dev.sheldan.abstracto.core.models.template.display.ChannelDisplay;
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+@Builder
+public class TwitchStreamerDisplayModel {
+ private String name;
+ private ChannelDisplay targetChannel;
+ private Boolean deleteNotifications;
+ private Boolean showNotifications;
+ private String streamerURL;
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/StreamSessionSectionService.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/StreamSessionSectionService.java
new file mode 100644
index 000000000..5b89b7f72
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/StreamSessionSectionService.java
@@ -0,0 +1,9 @@
+package dev.sheldan.abstracto.twitch.service;
+
+import com.github.twitch4j.helix.domain.Stream;
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import dev.sheldan.abstracto.twitch.model.database.StreamSessionSection;
+
+public interface StreamSessionSectionService {
+ StreamSessionSection createSectionFromStream(StreamSession session, Stream stream);
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/StreamerService.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/StreamerService.java
new file mode 100644
index 000000000..6f477227d
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/StreamerService.java
@@ -0,0 +1,23 @@
+package dev.sheldan.abstracto.twitch.service;
+
+import com.github.twitch4j.helix.domain.Stream;
+import com.github.twitch4j.helix.domain.User;
+import dev.sheldan.abstracto.core.utils.CompletableFutureList;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+import dev.sheldan.abstracto.twitch.model.template.ListTwitchStreamerResponseModel;
+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.GuildMessageChannel;
+
+public interface StreamerService {
+ void createStreamer(String name, GuildMessageChannel targetChannel, Member creator, Member streamerMember);
+ void removeStreamer(String name, Guild guild);
+ CompletableFutureList notifyAboutOnlineStream(Stream stream, Streamer streamer, User streamerUser);
+ void changeStreamerNotificationToChannel(Streamer streamer, Long channelId);
+ void disableNotificationsForStreamer(Streamer streamer, Boolean newState);
+ void changeStreamerMemberToUserId(Streamer streamer, Long userId);
+ void changeTemplateKeyTo(Streamer streamer, String templateKey);
+ ListTwitchStreamerResponseModel getStreamersFromServer(Guild guild);
+ void checkAndNotifyAboutOnlineStreamers();
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchService.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchService.java
new file mode 100644
index 000000000..f21666ced
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/TwitchService.java
@@ -0,0 +1,18 @@
+package dev.sheldan.abstracto.twitch.service;
+
+import com.github.twitch4j.helix.domain.Stream;
+import com.github.twitch4j.helix.domain.User;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public interface TwitchService {
+ User getStreamerByName(String name);
+ Optional getStreamerByNameOptional(String name);
+ User getStreamerById(String userId);
+ List getStreamsByUserIds(List userIds);
+ Optional getStreamOfUser(String userId);
+ Map getStreamsByUserIdsMapped(List userIds);
+ void refreshToken();
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionManagementService.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionManagementService.java
new file mode 100644
index 000000000..f92c25dae
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionManagementService.java
@@ -0,0 +1,12 @@
+package dev.sheldan.abstracto.twitch.service.management;
+
+import com.github.twitch4j.helix.domain.Stream;
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+
+public interface StreamSessionManagementService {
+ StreamSession startSession(Streamer streamer, Long messageId, Long channelId, Stream stream);
+ void deleteSessionsOfStreamer(Streamer streamer);
+ void deleteSession(StreamSession session);
+ void deleteSession(Streamer streamer, Long messageId);
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionSectionManagementService.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionSectionManagementService.java
new file mode 100644
index 000000000..5f5fdaee9
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamSessionSectionManagementService.java
@@ -0,0 +1,13 @@
+package dev.sheldan.abstracto.twitch.service.management;
+
+import dev.sheldan.abstracto.twitch.model.database.StreamSession;
+import dev.sheldan.abstracto.twitch.model.database.StreamSessionSection;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+
+import java.time.Instant;
+
+public interface StreamSessionSectionManagementService {
+ StreamSessionSection addSection(StreamSession session, String gameId, String gameName, Instant startTime, String title, Integer viewerCount);
+ void deleteSectionsOfSession(StreamSession session);
+ void deleteSectionsOfStreamer(Streamer streamer);
+}
diff --git a/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamerManagementService.java b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamerManagementService.java
new file mode 100644
index 000000000..d3691ee2b
--- /dev/null
+++ b/abstracto-application/abstracto-modules/twitch/twitch-int/src/main/java/dev/sheldan/abstracto/twitch/service/management/StreamerManagementService.java
@@ -0,0 +1,23 @@
+package dev.sheldan.abstracto.twitch.service.management;
+
+import dev.sheldan.abstracto.core.models.database.AChannel;
+import dev.sheldan.abstracto.core.models.database.AServer;
+import dev.sheldan.abstracto.core.models.database.AUserInAServer;
+import dev.sheldan.abstracto.twitch.model.database.Streamer;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface StreamerManagementService {
+ Streamer createStreamer(String id, String name, AChannel targetChannel, AUserInAServer creator, AUserInAServer member, Boolean isOnline);
+ void removeStreamer(String id);
+ Optional getStreamerById(Long id);
+ void removeStreamer(Streamer streamer);
+ void saveStreamer(Streamer streamer);
+ Streamer getStreamerInServerById(String id, Long serverId);
+ List getStreamerInServers(String id);
+ boolean streamerExistsInServerByID(String id, AServer server);
+ boolean streamerExistsInServerByName(String name, AServer server);
+ Optional getStreamerInServerByName(String name, AServer server);
+ List getStreamersForServer(AServer server);
+}
diff --git a/abstracto-application/abstracto-modules/voice-channel-context/voice-channel-context-int/src/main/java/dev/sheldan/abstracto/vccontext/model/VoiceChannelContext.java b/abstracto-application/abstracto-modules/voice-channel-context/voice-channel-context-int/src/main/java/dev/sheldan/abstracto/vccontext/model/VoiceChannelContext.java
index c0e53dbd7..c34ae99e0 100644
--- a/abstracto-application/abstracto-modules/voice-channel-context/voice-channel-context-int/src/main/java/dev/sheldan/abstracto/vccontext/model/VoiceChannelContext.java
+++ b/abstracto-application/abstracto-modules/voice-channel-context/voice-channel-context-int/src/main/java/dev/sheldan/abstracto/vccontext/model/VoiceChannelContext.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.ARole;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/bundle/pom.xml b/abstracto-application/bundle/pom.xml
index 7fd6230da..54919984a 100644
--- a/abstracto-application/bundle/pom.xml
+++ b/abstracto-application/bundle/pom.xml
@@ -216,6 +216,18 @@
${project.version}
+
+ dev.sheldan.abstracto.modules
+ twitch-int
+ ${project.version}
+
+
+
+ dev.sheldan.abstracto.modules
+ twitch-impl
+ ${project.version}
+
+
dev.sheldan.abstracto.modules
suggestion-impl
diff --git a/abstracto-application/core/core-impl/pom.xml b/abstracto-application/core/core-impl/pom.xml
index a3c58a6d9..48cf47366 100644
--- a/abstracto-application/core/core-impl/pom.xml
+++ b/abstracto-application/core/core-impl/pom.xml
@@ -123,7 +123,7 @@
org.ehcache
ehcache
- runtime
+ jakarta
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java
index 6b9fc4031..54e07ec18 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java
@@ -46,6 +46,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
@@ -327,12 +328,10 @@ public class CommandReceivedHandler extends ListenerAdapter {
});
}
- @Transactional
public void reportException(CommandContext context, Command foundCommand, Throwable throwable, String s) {
reportException(context.getMessage(), context.getChannel(), context.getAuthor(), foundCommand, throwable, s);
}
- @Transactional
public void reportException(Message message, MessageChannel channel, Member member, Command foundCommand, Throwable throwable, String s) {
UserInitiatedServerContext userInitiatedContext = buildUserInitiatedServerContext(member, channel, member.getGuild());
CommandContext.CommandContextBuilder commandContextBuilder = CommandContext.builder()
@@ -349,7 +348,6 @@ public class CommandReceivedHandler extends ListenerAdapter {
self.executePostCommandListener(foundCommand, commandContext, commandResult);
}
- @Transactional
public void reportException(MessageReceivedEvent event, Command foundCommand, Throwable throwable, String s) {
reportException(event.getMessage(), event.getChannel(), event.getMember(), foundCommand, throwable, s);
}
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java
index 94443d50d..c61665ae0 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java
@@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
@@ -158,7 +159,7 @@ public class SlashCommandListenerBean extends ListenerAdapter {
});
}
- @Transactional
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
public void executePostCommandListener(Command foundCommand, SlashCommandInteractionEvent event, CommandResult result) {
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.executeSlash(event, result, foundCommand);
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java
index 89961d102..638b472c3 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java
@@ -84,7 +84,9 @@ public class SlashCommandServiceBean implements SlashCommandService {
.findAny();
SubcommandGroupData groupData = commandGroup.orElseGet(() -> new SubcommandGroupData(groupName, description));
groupData.addSubcommands(slashCommand);
- rootCommand.addSubcommandGroups(groupData);
+ if(commandGroup.isEmpty()) {
+ rootCommand.addSubcommandGroups(groupData);
+ }
}
List requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName, serverId);
slashCommand.addOptions(requiredParameters);
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/database/ALock.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/database/ALock.java
index 9e1dbdc86..15374f547 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/database/ALock.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/model/database/ALock.java
@@ -5,10 +5,10 @@ import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
import java.io.Serializable;
@Entity
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/EmoteRepository.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/EmoteRepository.java
index e6aafdbe7..88dae05ee 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/EmoteRepository.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/EmoteRepository.java
@@ -19,7 +19,7 @@ public interface EmoteRepository extends JpaRepository {
boolean existsByEmoteId(Long emoteId);
- boolean existsByEmoteIdAndServerRef(String emoteId, AServer server);
+ boolean existsByEmoteIdAndServerRef(Long emoteId, AServer server);
@Override
Optional findById(@NonNull Integer aLong);
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/LockRepository.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/LockRepository.java
index a3e4da8be..b3fdb66c9 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/LockRepository.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/LockRepository.java
@@ -7,7 +7,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
-import javax.persistence.LockModeType;
+import jakarta.persistence.LockModeType;
@Repository
public interface LockRepository extends JpaRepository {
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java
index d11393577..afa699365 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java
@@ -355,6 +355,17 @@ public class ChannelServiceBean implements ChannelService {
return messageAction.submit();
}
+ @Override
+ public CompletableFuture editMessageInAChannelFuture(MessageToSend messageToSend, Long serverId, Long channelId, Long messageId) {
+ Optional textChannelFromServer = getGuildChannelFromServerOptional(serverId, channelId);
+ if(textChannelFromServer.isPresent() && textChannelFromServer.get() instanceof GuildMessageChannel) {
+ GuildMessageChannel messageChannel = (GuildMessageChannel) textChannelFromServer.get();
+ return editMessageInAChannelFuture(messageToSend, messageChannel, messageId);
+ } else {
+ throw new ChannelNotInGuildException(channelId);
+ }
+ }
+
@Override
public CompletableFuture editEmbedMessageInAChannel(MessageEmbed embedToSend, MessageChannel channel, Long messageId) {
metricService.incrementCounter(MESSAGE_EDIT_METRIC);
diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java
index 119af92a5..7491a15e2 100644
--- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java
+++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java
@@ -13,11 +13,11 @@ import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel;
+import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import net.dv8tion.jda.api.requests.restaction.MessageEditAction;
-import net.dv8tion.jda.api.utils.messages.MessageEditData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -87,6 +87,17 @@ public class MessageServiceBean implements MessageService {
return channelService.getMessageChannelFromServer(serverId, channelId).deleteMessageById(messageId).submit();
}
+ @Override
+ public CompletableFuture deleteMessagesInChannelInServer(Long serverId, Long channelId, List messageId) {
+ List messageIds = messageId.stream().map(Object::toString).toList();
+ GuildMessageChannel guildMessageChannel = channelService.getMessageChannelFromServer(serverId, channelId);
+ if(messageIds.size() == 1) {
+ return guildMessageChannel.deleteMessageById(messageId.get(0)).submit();
+ } else {
+ return guildMessageChannel.deleteMessagesByIds(messageIds).submit();
+ }
+ }
+
@Override
public CompletableFuture createStatusMessage(MessageToSend messageToSend, AChannel channel) {
return channelService.sendMessageEmbedToSendToAChannel(messageToSend, channel).get(0);
diff --git a/abstracto-application/core/core-impl/src/main/resources/config/application.properties b/abstracto-application/core/core-impl/src/main/resources/config/application.properties
index 8fa69daa6..8ea0d54d6 100644
--- a/abstracto-application/core/core-impl/src/main/resources/config/application.properties
+++ b/abstracto-application/core/core-impl/src/main/resources/config/application.properties
@@ -7,7 +7,7 @@ spring.jpa.properties.hibernate.generate_statistics = false
spring.jpa.properties.hibernate.cache.use_second_level_cache=false
spring.jpa.properties.hibernate.cache.use_query_cache=false
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory
-spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
+spring.jpa.properties.jakarta.persistence.sharedCache.mode=ENABLE_SELECTIVE
spring.jpa.properties.hibernate.javax.cache.missing_cache_strategy = create
spring.cache.jcache.config=classpath:ehcache.xml
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommand.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommand.java
index 9bcab3656..ba07c1ab4 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommand.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommand.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AChannelGroupCommand;
import dev.sheldan.abstracto.core.models.database.AFeature;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInAServer.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInAServer.java
index ba813998c..bd48bdba5 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInAServer.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInAServer.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInServerAlias.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInServerAlias.java
index 8462276e0..292f8c1a9 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInServerAlias.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/ACommandInServerAlias.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.command.model.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/AModule.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/AModule.java
index 5939e50b3..ce308ff92 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/AModule.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/AModule.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.command.model.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandDisabledChannelGroup.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandDisabledChannelGroup.java
index 98cc1005b..659e318ca 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandDisabledChannelGroup.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandDisabledChannelGroup.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.command.model.database;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Builder
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandInServerAliasId.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandInServerAliasId.java
index 6246fce6a..604f8ffbc 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandInServerAliasId.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CommandInServerAliasId.java
@@ -2,8 +2,8 @@ package dev.sheldan.abstracto.core.command.model.database;
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
@Embeddable
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CoolDownChannelGroup.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CoolDownChannelGroup.java
index 10f8c4e71..ddaace98a 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CoolDownChannelGroup.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/model/database/CoolDownChannelGroup.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.command.model.database;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Builder
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/CounterId.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/CounterId.java
index 6dfb1b238..26dc7791d 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/CounterId.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/CounterId.java
@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.models;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/RoleImmunityId.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/RoleImmunityId.java
index 026a0c56b..9db1ce844 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/RoleImmunityId.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/RoleImmunityId.java
@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.models;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/ServerSpecificId.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/ServerSpecificId.java
index e007c3bcf..a4594a46c 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/ServerSpecificId.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/ServerSpecificId.java
@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.models;
-import javax.persistence.Column;
-import javax.persistence.Embeddable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
import java.io.Serializable;
import java.util.Objects;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java
index 4f3173d96..382edac5c 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.models.SnowFlake;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java
index 1d85483ad..b896c09a4 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroup.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroupCommand.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroupCommand.java
index a68746754..a4568951e 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroupCommand.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AChannelGroupCommand.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.command.model.database.ACommand;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AConfig.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AConfig.java
index 587df45b1..aed5c11ca 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AConfig.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AConfig.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java
index 370c30cdc..3b52309b4 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AEmote.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.models.Fakeable;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
@@ -16,7 +16,7 @@ import java.time.Instant;
@EqualsAndHashCode
public class AEmote implements Serializable, Fakeable {
- @javax.persistence.Id
+ @jakarta.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeature.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeature.java
index bfb2d964d..147277d23 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeature.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeature.java
@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.command.model.database.ACommand;
import dev.sheldan.abstracto.core.models.SnowFlake;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java
index cc91f8308..d15cefb2d 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureFlag.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureMode.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureMode.java
index b8dfbf8a4..5582d8f82 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureMode.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AFeatureMode.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java
index 3358400e6..fe353bf38 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ARole.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.models.SnowFlake;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java
index 6f8f5729a..87000348a 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.models.SnowFlake;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java
index 7646b579a..30167584d 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUser.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
@@ -23,8 +23,8 @@ public class AUser implements Serializable {
@OneToMany(
fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
- mappedBy = "serverReference")
- private List servers;
+ mappedBy = "userReference")
+ private List usersInServers;
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java
index 354ead6ba..c1492fc72 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AUserInAServer.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AllowedMention.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AllowedMention.java
index 18adc9442..03d48db0b 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AllowedMention.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/AllowedMention.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ChannelGroupType.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ChannelGroupType.java
index b04fa9765..82fd1d72c 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ChannelGroupType.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ChannelGroupType.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ComponentPayload.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ComponentPayload.java
index d6b8fb77d..61b694d5f 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ComponentPayload.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ComponentPayload.java
@@ -2,7 +2,10 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+
import java.time.Instant;
@Entity
@@ -25,7 +28,7 @@ public class ComponentPayload {
private AServer server;
@Lob
- @org.hibernate.annotations.Type(type = "org.hibernate.type.TextType")
+ @JdbcTypeCode(SqlTypes.LONGVARCHAR)
@Column(name = "payload")
private String payload;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommand.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommand.java
index 0427e5fa6..7f16a11e0 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommand.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommand.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommandInServer.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommandInServer.java
index 3165f5ef5..5a5ae7e0f 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommandInServer.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ContextCommandInServer.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/Counter.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/Counter.java
index 03e73b3c1..d8617bd08 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/Counter.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/Counter.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.models.CounterId;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
@Entity
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/DefaultEmote.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/DefaultEmote.java
index bbd0f438a..ee46456bf 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/DefaultEmote.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/DefaultEmote.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
@@ -15,7 +15,7 @@ import java.time.Instant;
@EqualsAndHashCode
public class DefaultEmote implements Serializable {
- @javax.persistence.Id
+ @jakarta.persistence.Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/EffectType.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/EffectType.java
index c9f50028c..e783aa6bf 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/EffectType.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/EffectType.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/PostTarget.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/PostTarget.java
index c8e12c4df..b1bc4a75d 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/PostTarget.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/PostTarget.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityGroup.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityGroup.java
index 51242249f..b30bf69da 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityGroup.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityGroup.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityRegex.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityRegex.java
index ad8f3984b..562c2ae8b 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityRegex.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/ProfanityRegex.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.models.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/RoleImmunity.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/RoleImmunity.java
index 3214c0f5c..17db36f50 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/RoleImmunity.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/models/database/RoleImmunity.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.models.database;
import dev.sheldan.abstracto.core.models.RoleImmunityId;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.time.Instant;
@Entity
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java
index c0ef73aed..06faa1eff 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/ChannelService.java
@@ -34,6 +34,7 @@ public interface ChannelService {
void editMessageInAChannel(MessageToSend messageToSend, AChannel channel, Long messageId);
void editMessageInAChannel(MessageToSend messageToSend, MessageChannel channel, Long messageId);
CompletableFuture editMessageInAChannelFuture(MessageToSend messageToSend, MessageChannel channel, Long messageId);
+ CompletableFuture editMessageInAChannelFuture(MessageToSend messageToSend, Long serverId, Long channelId, Long messageId);
CompletableFuture editEmbedMessageInAChannel(MessageEmbed embedToSend, MessageChannel channel, Long messageId);
CompletableFuture editTextMessageInAChannel(String text, MessageChannel channel, Long messageId);
CompletableFuture editTextMessageInAChannel(String text, MessageEmbed messageEmbed, MessageChannel channel, Long messageId);
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java
index 37d26ad66..211771946 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/service/MessageService.java
@@ -18,6 +18,7 @@ import java.util.concurrent.CompletableFuture;
public interface MessageService {
CompletableFuture deleteMessageInChannelInServer(Long serverId, Long channelId, Long messageId);
+ CompletableFuture deleteMessagesInChannelInServer(Long serverId, Long channelId, List messageId);
CompletableFuture createStatusMessage(MessageToSend messageToSend, AChannel channel);
CompletableFuture createStatusMessage(MessageToSend messageToSend, MessageChannel channel);
CompletableFuture createStatusMessageId(MessageToSend messageToSend, MessageChannel channel);
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/AutoLoadMacro.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/AutoLoadMacro.java
index 44b38eea3..94043688b 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/AutoLoadMacro.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/AutoLoadMacro.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.templating.model.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
@Builder
@Entity
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/CustomTemplate.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/CustomTemplate.java
index 0f2896ccb..42e567167 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/CustomTemplate.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/CustomTemplate.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.templating.model.database;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/Template.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/Template.java
index 5b8510565..9407fc61e 100644
--- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/Template.java
+++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/templating/model/database/Template.java
@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.templating.model.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
import java.time.Instant;
diff --git a/abstracto-application/core/metrics-impl/pom.xml b/abstracto-application/core/metrics-impl/pom.xml
index f05de4561..f83ba6890 100644
--- a/abstracto-application/core/metrics-impl/pom.xml
+++ b/abstracto-application/core/metrics-impl/pom.xml
@@ -35,6 +35,11 @@
metrics-int
${project.version}
+
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+
\ No newline at end of file
diff --git a/abstracto-application/core/metrics-impl/src/main/java/dev/sheldan/abstracto/core/metric/config/WebSecurityConfiguration.java b/abstracto-application/core/metrics-impl/src/main/java/dev/sheldan/abstracto/core/metric/config/WebSecurityConfiguration.java
deleted file mode 100644
index 86ac3c0a5..000000000
--- a/abstracto-application/core/metrics-impl/src/main/java/dev/sheldan/abstracto/core/metric/config/WebSecurityConfiguration.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package dev.sheldan.abstracto.core.metric.config;
-
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
-@Configuration
-@EnableWebSecurity
-public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
-
- public void configure(HttpSecurity http) throws Exception {
- http.cors().and()
- .csrf().disable().authorizeRequests()
- .antMatchers("/actuator/**").hasRole("USER")
- .anyRequest().authenticated()
- .and()
- .httpBasic();
- }
-}
\ No newline at end of file
diff --git a/abstracto-application/coverage/pom.xml b/abstracto-application/coverage/pom.xml
deleted file mode 100644
index 6f280592b..000000000
--- a/abstracto-application/coverage/pom.xml
+++ /dev/null
@@ -1,279 +0,0 @@
-
-
-
- dev.sheldan.abstracto
- abstracto-application
- 1.4.27-SNAPSHOT
-
- 4.0.0
- coverage
-
-
- jacoco
- reuseReports
- java
- ${basedir}/../
- ${basedir}/../target/jacoco.exec
-
-
-
-
-
- dev.sheldan.abstracto
- bundle
- ${project.version}
- import
- pom
-
-
-
-
-
-
- dev.sheldan.abstracto.core
- core-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.core
- core-int
- ${project.version}
-
-
-
-
- dev.sheldan.abstracto.modules
- moderation-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- moderation-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- modmail-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- modmail-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- utility-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- utility-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- experience-tracking-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- experience-tracking-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.scheduling
- scheduling-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.scheduling
- scheduling-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- assignable-roles-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- assignable-roles-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- statistic-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- statistic-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.core
- metrics-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.core
- metrics-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- entertainment-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- entertainment-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- link-embed-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- link-embed-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- remind-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- remind-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- repost-detection-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- repost-detection-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- starboard-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- starboard-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- suggestion-impl
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- suggestion-int
- ${project.version}
-
-
-
- dev.sheldan.abstracto.modules
- webservices-impl
- ${project.version}
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 2.19.1
-
-
-
-
- ${argLine} -Xms256m -Xmx2048m
- 1
- random
-
-
-
- org.jacoco
- jacoco-maven-plugin
- 0.8.5
-
-
- **/*Test.java
-
- ${code.coverage.overall.data.folder}
- true
-
-
-
- report-aggregate
- verify
-
- report-aggregate
-
-
-
- merge-results
- verify
-
- merge
-
-
-
-
- ${code.coverage.project.folder}
-
- **/target/jacoco.exec
-
-
-
- ${code.coverage.overall.data.folder}/jacoco.exec
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/abstracto-application/pom.xml b/abstracto-application/pom.xml
index d243feeb8..11f2bb770 100644
--- a/abstracto-application/pom.xml
+++ b/abstracto-application/pom.xml
@@ -5,8 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.3.1.RELEASE
-
+ 3.1.1
dev.sheldan.abstracto
@@ -21,7 +20,6 @@
documentation
installer
bundle
- coverage
@@ -35,8 +33,7 @@
https://maven.pkg.github.com/Sheldan/abstracto
scm:git:git@github.com:Sheldan/abstracto.git
- abstracto-application-1.4.5
-
+
@@ -57,67 +54,46 @@
yyyy/MM/dd HH:mm
- 5.0.0-beta.5
+ 5.0.0-beta.12
2.0.0-RC.1
1.5.3
2.3.0
9.2.11.1
- 1.8
- 1.8
- 30.0-jre
- 2.8.9
- 3.9
- 2.7
- 3.3.3
- 4.3
- 4.3
- 2.3.1.RELEASE
- 0.9.0
+ 17
+ 17
+ 32.1.1-jre
+ 2.10.1
+ 3.12.0
+ 2.13.0
+ 5.4.0
+ 4.4
+ 3.1.1
+ 0.16.0
4.13.1
- 1.6.3
- ${basedir}/../target/jacoco.exec
+ 1.11.1
1.31.3
v3-rev222-1.25.0
1.12.2
+ 1.15.0
+
+ 2.15.2
+ 3.10.8
+ 6.2.5.Final
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 2.19.1
-
-
-
-
- ${argLine} -Xms256m -Xmx2048m
- 1
- true
- random
-
-
- org.jacoco
- jacoco-maven-plugin
- 0.8.5
-
-
- default-prepare-agent
-
- prepare-agent
-
-
-
- default-report
- prepare-package
-
- report
-
-
-
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ 17
+ 17
+ true
+
@@ -197,12 +173,37 @@
${commons-collections.version}
+
+ com.github.twitch4j
+ twitch4j
+ ${twitch4j.version}
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
com.google.code.gson
gson
${gson.version}
+
+ org.ehcache
+ ehcache
+ ${ehcache.version}
+ jakarta
+
+
+
+ org.hibernate
+ hibernate-jcache
+ ${hibernate-jcache.version}
+
+
org.mockito
mockito-core
@@ -265,6 +266,18 @@
${everit.json.schema.version}
+
+ org.springframework.boot
+ spring-boot-actuator-autoconfigure
+ ${spring-boot-starter.version}
+
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+
+
@@ -278,6 +291,16 @@
commons-lang3
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ javax.annotation
+ javax.annotation-api
+
+
com.google.guava
guava
diff --git a/abstracto-application/scheduling/scheduling-impl/src/main/resources/scheduling.properties b/abstracto-application/scheduling/scheduling-impl/src/main/resources/scheduling.properties
index 66117f446..f5a87e686 100644
--- a/abstracto-application/scheduling/scheduling-impl/src/main/resources/scheduling.properties
+++ b/abstracto-application/scheduling/scheduling-impl/src/main/resources/scheduling.properties
@@ -6,7 +6,6 @@ spring.quartz.properties.org.quartz.scheduler.instanceName=quartz-abstracto-app
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
spring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class=dev.sheldan.abstracto.scheduling.service.IdGenerationService
spring.quartz.properties.org.quartz.threadPool.threadCount=4
-spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
spring.quartz.properties.org.quartz.jobStore.useProperties=true
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=45000
diff --git a/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/model/database/SchedulerJob.java b/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/model/database/SchedulerJob.java
index 96d27b92d..3b04b2ab5 100644
--- a/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/model/database/SchedulerJob.java
+++ b/abstracto-application/scheduling/scheduling-int/src/main/java/dev/sheldan/abstracto/scheduling/model/database/SchedulerJob.java
@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.scheduling.model.database;
import lombok.*;
-import javax.persistence.*;
+import jakarta.persistence.*;
import java.io.Serializable;
/**