[AB-98] adding twitch support

upgrading to java 17
upgrade of dependencies
This commit is contained in:
Sheldan
2023-07-06 23:05:51 +02:00
parent 346e462185
commit 6409bbaa1d
167 changed files with 3964 additions and 489 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -1,8 +1,6 @@
# Abstracto
![Build](https://github.com/Sheldan/abstracto/workflows/Execute%20build%20and%20Sonar/badge.svg)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=abstracto-core&metric=coverage)](https://sonarcloud.io/dashboard?id=abstracto-core)
[![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-white.svg)](https://sonarcloud.io/dashboard?id=abstracto-core)
![Build](https://github.com/Sheldan/abstracto/workflows/Execute%20Build/badge.svg)
[![GitHub license](https://img.shields.io/github/license/Sheldan/abstracto)](https://github.com/Sheldan/abstracto/blob/master/LICENSE)

View File

@@ -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;

View File

@@ -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")

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.activity.models;
import lombok.*;
import javax.persistence.*;
import jakarta.persistence.*;
import java.time.Instant;
@Builder

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -31,6 +31,7 @@
<module>dynamic-activity</module>
<module>anti-raid</module>
<module>custom-command</module>
<module>twitch</module>
</modules>
</project>

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
/**

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.suggestion.model.database;
import lombok.*;
import javax.persistence.*;
import jakarta.persistence.*;
import java.time.Instant;
@Entity

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.27-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>twitch</artifactId>
<packaging>pom</packaging>
<modules>
<module>twitch-int</module>
<module>twitch-impl</module>
</modules>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>twitch</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.27-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>twitch-impl</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/liquibase.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>twitch-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -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<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
// its likely that this command times out
return event.deferReply().submit().thenCompose(interactionHook -> self.executeAddStreamerCommandLogic(event, interactionHook));
}
@Transactional
public CompletableFuture<CommandResult> 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<Parameter> 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;
}
}

View File

@@ -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<CommandResult> 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<String> 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<Parameter> 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
}
}

View File

@@ -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<CommandResult> 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;
}
}

View File

@@ -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<CommandResult> 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<Parameter> 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;
}
}

View File

@@ -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 {
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<StreamSession, Long> {
}

View File

@@ -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<StreamSessionSection, Long> {
}

View File

@@ -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<Streamer, Long> {
boolean existsByServerAndUserId(AServer server, String userId);
boolean existsByServerAndName(AServer server, String name);
Optional<Streamer> findByServerAndName(AServer server, String name);
Optional<Streamer> findByServer_IdAndUserId(Long serverId, String userId);
List<Streamer> getByUserId(String userId);
List<Streamer> getStreamerByServer(AServer server);
void deleteByUserId(String id);
}

View File

@@ -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());
}
}

View File

@@ -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<Stream> 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<Message> 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<CompletableFuture<Message>> futures = channelService.sendMessageEmbedToSendToAChannel(messagetoSend, streamer.getNotificationChannel());
return new CompletableFutureList<>(futures);
} else {
log.info("Announcing streamer {} in server {}.", streamer.getId(), streamer.getServer().getId());
List<CompletableFuture<Message>> futures = postTargetService.sendEmbedInPostTarget(messagetoSend, TwitchPostTarget.TWITCH_LIVE_NOTIFICATION, streamer.getServer().getId());
return new CompletableFutureList<>(futures);
}
}
public CompletableFuture<Void> updateExistingNotification(Stream stream, Streamer streamer, User streamerUser) {
List<StreamSectionDisplay> 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<Streamer> streamers = streamerManagementService.getStreamersForServer(server);
GuildMessageChannel postTargetChannel = postTargetService.getPostTargetChannel(TwitchPostTarget.TWITCH_LIVE_NOTIFICATION, server.getId()).orElse(null);
List<TwitchStreamerDisplayModel> 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<AServer> servers = serverManagementService.getAllServers();
List<AServer> 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<Streamer> streamersInServer = streamerManagementService.getStreamersForServer(server);
Map<Long, Streamer> streamerIdMap = streamersInServer
.stream()
.collect(Collectors.toMap(Streamer::getId, Function.identity()));
Map<String, Streamer> 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<String> userIds = streamersInServer
.stream()
.map(Streamer::getUserId)
.distinct()
.toList();
List<Stream> streamsOfUsers = twitchService.getStreamsByUserIds(userIds);
Set<Long> onlineStreamers = new HashSet<>();
Map<Long, Boolean> updateNotificationFlagValues = new HashMap<>();
streamsOfUsers.forEach(stream -> self.processOnlineStreamer(server, streamerMap, onlineStreamers, updateNotificationFlagValues, stream));
Set<Long> allStreamersInServer = streamerIdMap.keySet();
allStreamersInServer.removeAll(onlineStreamers); // then we have those that went offline
Map<Long, Boolean> 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<Long, Boolean> 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<StreamSectionDisplay> 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<String, Streamer> streamerMap, Set<Long> onlineStreamers, Map<Long, Boolean> 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<Message> 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<Streamer> 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);
});
}
}

View File

@@ -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<User> getStreamerByNameOptional(String name) {
List<User> 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<Stream> getStreamsByUserIds(List<String> userIds) {
return twitchClient.getClientHelper().getTwitchHelix().getStreams(null, null, null, null, null, null, userIds, null).execute().getStreams();
}
@Override
public Optional<Stream> getStreamOfUser(String userId) {
List<Stream> allStreams = getStreamsByUserIds(Arrays.asList(userId));
return allStreams.stream().findFirst();
}
@Override
public Map<String, Stream> getStreamsByUserIdsMapped(List<String> userIds) {
return getStreamsByUserIds(userIds).stream().collect(Collectors.toMap(Stream::getUserId, Function.identity()));
}
@Override
public void refreshToken() {
oAuth2Credential.updateCredential(identityProvider.getAppAccessToken());
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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<StreamSessionSection> sections = streamer
.getSessions()
.stream()
.flatMap(session -> session.getSections().stream())
.toList();
repository.deleteAll(sections);
streamer.getSessions().forEach(session -> {
session.getSections().clear();
});
}
}

View File

@@ -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<Streamer> 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<Streamer> 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<Streamer> getStreamerInServerByName(String name, AServer server) {
return streamerRepository.findByServerAndName(server, name);
}
@Override
public List<Streamer> getStreamersForServer(AServer server) {
return streamerRepository.getStreamerByServer(server);
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,35 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="twitchFeature" value="(SELECT id FROM feature WHERE key = 'twitch')"/>
<changeSet author="Sheldan" id="twitch-commands">
<insert tableName="command">
<column name="name" value="addTwitchStreamer"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${twitchFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="listTwitchStreamer"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${twitchFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="changeTwitchStreamer"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${twitchFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="removeTwitchStreamer"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${twitchFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,13 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="feature.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
<include file="streamer_online_check_job.xml" relativeToChangelogFile="true"/>
<include file="twitch_refresh_token_job.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="twitch_feature-insertion">
<insert tableName="feature">
<column name="key" value="twitch"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,21 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="streamer_online_check_job-insert">
<insert tableName="scheduler_job">
<column name="name" value="streamerOnlineCheckJob"/>
<column name="group_name" value="twitch"/>
<column name="clazz" value="dev.sheldan.abstracto.twitch.job.StreamerOnlineCheckJob"/>
<column name="active" value="true"/>
<column name="cron_expression" value="0 * * * * ?"/>
<column name="recovery" value="false"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,21 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="twitch_refresh_token_job-insert">
<insert tableName="scheduler_job">
<column name="name" value="twitchRefreshTokenJob"/>
<column name="group_name" value="twitch"/>
<column name="clazz" value="dev.sheldan.abstracto.twitch.job.TwitchRefreshTokenJob"/>
<column name="active" value="true"/>
<column name="cron_expression" value="55 0 0 * * ?"/>
<column name="recovery" value="false"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,58 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="stream_session-table">
<createTable tableName="stream_session">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="streamer_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="stream_id" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="channel_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="start_time" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="streamer_id" baseTableName="stream_session" constraintName="fk_stream_session_streamer"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="streamer" validate="true"/>
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="stream_session" constraintName="fk_stream_session_channel"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="channel" validate="true"/>
<sql>
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();
</sql>
<sql>
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();
</sql>
<addColumn tableName="streamer">
<column name="current_session_id" type="BIGINT">
<constraints nullable="true"/>
</column>
</addColumn>
<addForeignKeyConstraint baseColumnNames="current_session_id" baseTableName="streamer" constraintName="fk_streamer_current_session"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="stream_session" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,56 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="stream_session_section-table">
<createTable tableName="stream_session_section">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="stream_session_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="streamer_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="game_id" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="game_name" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="viewer_count" type="INT">
<constraints nullable="false"/>
</column>
<column name="title" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="streamer_id" baseTableName="stream_session_section" constraintName="fk_stream_session_section_streamer"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="streamer" validate="true"/>
<addForeignKeyConstraint baseColumnNames="stream_session_id" baseTableName="stream_session_section" constraintName="fk_stream_session_section_session"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="stream_session" validate="true"/>
<sql>
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();
</sql>
<sql>
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();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,75 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="streamer-table">
<createTable tableName="streamer">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="user_id" type="VARCHAR(128)">
<constraints nullable="false"/>
</column>
<column name="template_key" type="VARCHAR(128)">
<constraints nullable="true"/>
</column>
<column name="name" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="notification_channel_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="creator_user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="streamer_user_in_server_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="current_game_id" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="online" type="BOOLEAN">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="show_notifications" type="BOOLEAN">
<constraints nullable="true"/>
</column>
</createTable>
<addUniqueConstraint
columnNames="user_id, server_id"
constraintName="uq_streamer_id"
tableName="streamer"
/>
<addForeignKeyConstraint baseColumnNames="notification_channel_id" baseTableName="streamer" constraintName="fk_streamer_notification_channel"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="channel" validate="true"/>
<addForeignKeyConstraint baseColumnNames="creator_user_in_server_id" baseTableName="streamer" constraintName="fk_streamer_creator"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="streamer_user_in_server_id" baseTableName="streamer" constraintName="fk_streamer_user"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="streamer" constraintName="fk_streamer_server"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="server" validate="true"/>
<sql>
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();
</sql>
<sql>
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();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,12 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="streamer.xml" relativeToChangelogFile="true"/>
<include file="stream_session.xml" relativeToChangelogFile="true"/>
<include file="stream_session_section.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.4.27/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -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

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>twitch</artifactId>
<version>1.4.27-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>twitch-int</artifactId>
<dependencies>
<dependency>
<groupId>com.github.twitch4j</groupId>
<artifactId>twitch4j</artifactId>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

View File

@@ -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<PostTargetEnum> getRequiredPostTargets() {
return Arrays.asList(TwitchPostTarget.TWITCH_LIVE_NOTIFICATION);
}
@Override
public List<FeatureMode> getAvailableModes() {
return Arrays.asList(TwitchFeatureMode.DELETE_NOTIFICATION, TwitchFeatureMode.UPDATE_NOTIFICATION);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.twitch.config;
public class TwitchSlashCommandNames {
private TwitchSlashCommandNames() {
}
public static final String TWITCH = "twitch";
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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<StreamSessionSection> sections = new ArrayList<>();
}

View File

@@ -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;
}

View File

@@ -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<StreamSession> sessions = new ArrayList<>();
}

View File

@@ -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<StreamSectionDisplay> pastSections;
private Boolean mature;
private String streamURL;
private String streamerAvatarURL;
}

Some files were not shown because too many files have changed in this diff Show More