Compare commits

..

70 Commits

Author SHA1 Message Date
release-bot
a61a128236 [maven-release-plugin] prepare release sissi-1.4.39 2024-02-03 17:56:06 +00:00
Sheldan
a1b2700092 [SIS-xxx] updating general version due to release
updating more versions when doing manual release job
2024-02-03 18:53:04 +01:00
Sheldan
da7cc2f6a7 [SIS-xxx] fixing scm config 2024-02-03 18:47:40 +01:00
Sheldan
4cb118339f [SIS-xxx] adding missing variables to release job 2024-02-03 18:39:27 +01:00
Sheldan
11460f1e19 [SIS-xxx] fixing name in release job 2024-02-03 18:27:35 +01:00
Sheldan
c9b856b300 [SIS-xxx] adding manual release job 2024-02-03 18:13:25 +01:00
Sheldan
c513087d30 [SIS-xxx] updating abstracto version 2024-02-03 18:07:24 +01:00
Sheldan
8a110215f1 [maven-release-plugin] prepare for next development iteration 2024-01-24 08:50:30 +01:00
Sheldan
90b9c3674c [maven-release-plugin] prepare release sissi-1.4.37 2024-01-24 08:50:26 +01:00
Sheldan
68771a7b26 [SIS-xxx] updating abstracto version and preparing for release 2024-01-24 08:28:39 +01:00
Sheldan
72e7bb57e3 [maven-release-plugin] prepare for next development iteration 2023-12-29 00:51:26 +01:00
Sheldan
3e147a37d9 [maven-release-plugin] prepare release sissi-1.4.36 2023-12-29 00:51:22 +01:00
Sheldan
99dca4906a [SIS-xxx] updating abstracto and preparing for release 2023-12-29 00:27:23 +01:00
Sheldan
32bfefc5b2 [maven-release-plugin] prepare for next development iteration 2023-12-28 22:56:31 +01:00
Sheldan
aa135b4663 [maven-release-plugin] prepare release sissi-1.4.35 2023-12-28 22:56:27 +01:00
Sheldan
4ffb5e5640 [SIS-xxx] updating abstracto and preparing for release 2023-12-28 22:40:16 +01:00
Sheldan
9376ac416f [maven-release-plugin] prepare for next development iteration 2023-12-26 21:29:35 +01:00
Sheldan
7cf44c99b7 [maven-release-plugin] prepare release sissi-1.4.34 2023-12-26 21:29:31 +01:00
Sheldan
5dce059d2b [SIS-xxx] updating abstracto and preparing for release 2023-12-26 21:28:51 +01:00
Sheldan
da30a120d7 [maven-release-plugin] prepare for next development iteration 2023-12-25 01:29:54 +01:00
Sheldan
49875b2467 [maven-release-plugin] prepare release sissi-1.4.33 2023-12-25 01:29:50 +01:00
Sheldan
2236710086 [SIS-xxx] preparing for release 2023-12-25 01:24:36 +01:00
Sheldan
99b266e94a [SIS-xxx] updating abstracto version 2023-12-25 01:24:07 +01:00
Sheldan
974ab230ac [SIS-xxx] fixing orange sun doge command alternative not handling replies correctly 2023-12-24 01:08:34 +01:00
Sheldan
e5239f971c [maven-release-plugin] prepare for next development iteration 2023-12-23 23:43:04 +01:00
Sheldan
23db0b7c9e [maven-release-plugin] prepare release sissi-1.4.32 2023-12-23 23:43:01 +01:00
Sheldan
8167accd34 [SIS-xxx] preparing for release
updating more abstracto versions
2023-12-23 23:42:23 +01:00
Sheldan
375045f508 [SIS-xxx] updating abstracto version 2023-12-23 23:41:16 +01:00
Sheldan
3f4d5510d8 [maven-release-plugin] prepare for next development iteration 2023-12-23 22:35:20 +01:00
Sheldan
ba67f65bea [maven-release-plugin] prepare release sissi-1.4.31 2023-12-23 22:35:16 +01:00
Sheldan
1b7c2263ae [SIS-xxx] preparing for release 2023-12-23 22:34:53 +01:00
Sheldan
f065bf5244 [SIS-xxx] fixing docker compose file 2023-12-23 22:33:56 +01:00
Sheldan
e036fd0867 [maven-release-plugin] prepare for next development iteration 2023-12-23 22:21:45 +01:00
Sheldan
96960d77ac [maven-release-plugin] prepare release sissi-1.4.30 2023-12-23 22:21:41 +01:00
Sheldan
3894d4567e [SIS-xxx] preparing for release 2023-12-23 22:21:07 +01:00
Sheldan
503fd2520d [SIS-xxx] updating abstracto version
restructuring api to private and public rest api
adding custom image generation module
2023-12-23 22:20:18 +01:00
Sheldan
58d6b12a67 [SIS-xxx] refactoring to use a base image for rest api instead of a zipfile
changing image pull policy for rest api and bot to always
renaming rest api container
2023-12-20 21:07:59 +01:00
Sheldan
4128274a7d [SIS-xxx] refactoring rest api to use a common code base 2023-12-19 01:26:27 +01:00
Sheldan
3b498c5d3f [SIS-xxx] fixing abstracto deployment versions after upgrade 2023-12-12 21:29:39 +01:00
Sheldan
c6bdcaa84c [maven-release-plugin] prepare for next development iteration 2023-12-12 20:17:10 +01:00
Sheldan
90f3dd0ae9 [maven-release-plugin] prepare release sissi-1.4.29 2023-12-12 20:17:06 +01:00
Sheldan
cd482d640f [SIS-xxx] prepare for release 2023-12-12 20:16:06 +01:00
Sheldan
ae2f88daa3 [SIS-xxx] adding endless stream feature
updating abstracto version
2023-12-12 20:14:55 +01:00
Sheldan
c13f40fd3b [maven-release-plugin] prepare for next development iteration 2023-12-10 14:59:33 +01:00
Sheldan
37147a866f [maven-release-plugin] prepare release sissi-1.4.28 2023-12-10 14:59:29 +01:00
Sheldan
9a7d0613b1 [SIS-xxx] enabling giveaway module
updating to newer abstracto version
preparing for release
2023-12-10 14:58:19 +01:00
Sheldan
7e60447ae6 [maven-release-plugin] prepare for next development iteration 2023-12-01 23:39:45 +01:00
Sheldan
e4a899a125 [maven-release-plugin] prepare release sissi-1.4.27 2023-12-01 23:39:41 +01:00
Sheldan
ce8341e69a [SIS-xxx] changing caching
prepare for release
2023-12-01 23:38:38 +01:00
Sheldan
fa6333fa49 [maven-release-plugin] prepare for next development iteration 2023-12-01 23:16:02 +01:00
Sheldan
3393dea591 [maven-release-plugin] prepare release sissi-1.4.26 2023-12-01 23:15:57 +01:00
Sheldan
3c3bdfaed9 [SIS-xxx] preparing for release 2023-12-01 23:15:31 +01:00
Sheldan
172e3c4190 [SIS-xxx] adding uuid to clear cache 2023-12-01 23:14:57 +01:00
Sheldan
52b86804b9 [maven-release-plugin] prepare for next development iteration 2023-12-01 22:54:18 +01:00
Sheldan
de8d9982f3 [maven-release-plugin] prepare release sissi-1.4.25 2023-12-01 22:54:14 +01:00
Sheldan
29e1b22783 [SIS-xxx] preparing for release 2023-12-01 22:53:37 +01:00
Sheldan
5852d4837e [SIS-xxx] synchronizing the lookup for donations, so that simultaneous calls are not possible 2023-12-01 22:49:34 +01:00
Sheldan
b345fa5502 [SIS-xxx] fixing logging setup in case of a debra donation loading error 2023-11-30 21:02:16 +01:00
Sheldan
d6470e3714 [SIS-xxx] changing meetup time display to include week day name 2023-11-30 19:55:47 +01:00
Sheldan
b4cebe2b41 [maven-release-plugin] prepare for next development iteration 2023-11-29 21:08:14 +01:00
Sheldan
f3dd85d7d5 [maven-release-plugin] prepare release sissi-1.4.24 2023-11-29 21:08:10 +01:00
Sheldan
db318afb2b [SIS-xxx] preparing for release 2023-11-29 21:07:34 +01:00
Sheldan
ddd710d1c2 [SIS-xxx] changing wording of debra campaign info image 2023-11-29 21:05:02 +01:00
Sheldan
81824db1f1 [SIS-xxx] adding html wrapper for debra images
restructuring and splitting image generation into multiple files
adding doge sun image generation
2023-11-29 00:04:29 +01:00
Sheldan
0390d7c8ca [maven-release-plugin] prepare for next development iteration 2023-11-23 01:04:11 +01:00
Sheldan
61412f434c [maven-release-plugin] prepare release sissi-1.4.23 2023-11-23 01:04:07 +01:00
Sheldan
12e69a18fb [SIS-xxx] preparing for release 2023-11-23 01:02:35 +01:00
Sheldan
170ddd9c33 [SIS-xxx] fixing cache setup to add additional caches instead of overwriting the provided configuration 2023-11-23 01:01:40 +01:00
Sheldan
de8bbdcbee [SIS-xxx] fixing rest api image version 2023-11-21 00:39:07 +01:00
Sheldan
b5bf53fb6a [maven-release-plugin] prepare for next development iteration 2023-11-21 00:25:40 +01:00
98 changed files with 1788 additions and 1040 deletions

4
.env Normal file
View File

@@ -0,0 +1,4 @@
REGISTRY_PREFIX=harbor.sheldan.dev/sissi/
ABSTRACTO_PREFIX=harbor.sheldan.dev/abstracto/
VERSION=1.4.38
ABSTRACTO_VERSION=1.5.23

View File

@@ -33,9 +33,8 @@ jobs:
id: dotenv id: dotenv
uses: falti/dotenv-action@v1.0.4 uses: falti/dotenv-action@v1.0.4
with: with:
path: ./deployment/image-packaging/src/main/docker/.env path: .env
- name: Push container - name: Push container
working-directory: ./deployment/image-packaging/src/main/docker
run: docker-compose build && docker-compose push run: docker-compose build && docker-compose push
env: env:
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }} REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}

83
.github/workflows/release_manual.yaml vendored Normal file
View File

@@ -0,0 +1,83 @@
name: Publishes a new version of Sissi
on: workflow_dispatch
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: master
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: 'corretto'
java-version: 17
- name: Load current version
id: version
run: echo "version=$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec | cut -d- -f1)" >> $GITHUB_ENV
- name: Release maven packages
uses: qcastel/github-actions-maven-release@v1.12.41
env:
JAVA_HOME: /usr/lib/jvm/java-17-openjdk/
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
user: Sheldan
token: ${{ secrets.ABSTRACTO_PAT }}
with:
git-release-bot-name: "release-bot"
git-release-bot-email: "release-bot@sheldan.dev"
release-branch-name: master
maven-args: "-Dmaven.javadoc.skip=true -s settings.xml -DskipTests"
access-token: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Harbor
uses: docker/login-action@v2
with:
registry: harbor.sheldan.dev
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_TOKEN }}
- name: Update Chart version file
uses: fjogeleit/yaml-update-action@v0.13.2
with:
valueFile: 'deployment/helm/sissi/Chart.yaml'
propertyPath: 'version'
value: ${{ env.version }}
commitChange: false
- name: Update value files version
uses: fjogeleit/yaml-update-action@v0.13.2
with:
valueFile: 'deployment/helm/sissi/values.yaml'
commitChange: false
changes: |
{
"bot.tag": "${{ env.version }}",
"restApi.tag": "${{ env.version }}"
"privateRestApi.tag": "${{ env.version }}"
"templateDeploymentData.tag": "${{ env.version }}"
"dbConfigDeploymentData.tag": "${{ env.version }}"
}
- name: Update .env version
run:
sed -i '3s/.*/VERSION=${{ env.version }}/' .env
- name: Load env file
id: dotenv
uses: falti/dotenv-action@v1.0.4
with:
path: .env
- name: Build and push Docker containers
run: docker-compose build && docker-compose push
env:
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}
VERSION: ${{ steps.dotenv.outputs.version }}
- name: Helm package and push
working-directory: ./deployment/helm/
run: |-
helm registry login -u '${{ secrets.HARBOR_USERNAME }}' -p '${{ secrets.HARBOR_TOKEN }}' harbor.sheldan.dev
helm package sissi
helm push sissi*.tgz oci://harbor.sheldan.dev/sissi
- name: Fix file permissions
run:
sudo chmod -R ugo+rwX . # https://github.com/actions/checkout/issues/164
- name: Commit updated versions
uses: EndBug/add-and-commit@v9
with:
author_name: "release-bot"
author_email: "release-bot@sheldan.dev"

View File

@@ -2,6 +2,7 @@ allow_k8s_contexts('k8s-cluster')
load('ext://restart_process', 'docker_build_with_restart') load('ext://restart_process', 'docker_build_with_restart')
registry = 'harbor.sheldan.dev/sissi/' registry = 'harbor.sheldan.dev/sissi/'
abstracto_registry = 'harbor.sheldan.dev/abstracto/'
local_resource( local_resource(
'sissi-java-compile', 'sissi-java-compile',
@@ -28,7 +29,12 @@ docker_build_with_restart(
) )
docker_build(registry + 'sissi-db-data', 'deployment/image-packaging/src/main/docker/db-data/') docker_build(registry + 'sissi-db-data', 'deployment/image-packaging/src/main/docker/db-data/')
docker_build(registry + 'sissi-rest-api', 'deployment/image-packaging/src/main/docker/rest-api/') docker_build(registry + 'sissi-debra-rest-api', 'python/modules/debra-rest-api/', dockerfile='python/modules/debra-rest-api/docker/Dockerfile')
update_settings(suppress_unused_image_warnings=[registry + "sissi-debra-rest-api"]) # only used in docker image building
docker_build(registry + 'sissi-image-gen-api', 'python/modules/image-gen-api/', dockerfile='python/modules/image-gen-api/docker/Dockerfile')
update_settings(suppress_unused_image_warnings=[registry + "sissi-image-gen-api"]) # only used in docker image building
docker_build(registry + 'sissi-rest-api', 'deployment/image-packaging/src/main/docker/rest-api/', build_args={'REGISTRY_PREFIX': abstracto_registry, 'SISSI_REGISTRY_PREFIX': registry})
docker_build(registry + 'sissi-private-rest-api', 'deployment/image-packaging/src/main/docker/private-rest-api/', build_args={'REGISTRY_PREFIX': abstracto_registry, 'SISSI_REGISTRY_PREFIX': registry})
docker_build(registry + 'sissi-template-data', 'deployment/image-packaging/src/main/docker/template-data/') docker_build(registry + 'sissi-template-data', 'deployment/image-packaging/src/main/docker/template-data/')

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.application</groupId> <groupId>dev.sheldan.sissi.application</groupId>
<artifactId>application</artifactId> <artifactId>application</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>executable</artifactId> <artifactId>executable</artifactId>
@@ -136,6 +136,16 @@
<artifactId>statistic-impl</artifactId> <artifactId>statistic-impl</artifactId>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>giveaway-impl</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>image-generation-impl</artifactId>
</dependency>
<!-- sissi modules --> <!-- sissi modules -->
<dependency> <dependency>
@@ -170,6 +180,12 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.sissi.application.module.custom</groupId>
<artifactId>image-generation-custom</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi</groupId> <groupId>dev.sheldan.sissi</groupId>
<artifactId>sissi</artifactId> <artifactId>sissi</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,25 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.sissi.application.module.custom</groupId>
<artifactId>sissi-customizations</artifactId>
<version>1.4.39</version>
</parent>
<artifactId>image-generation-custom</artifactId>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>image-generation-int</artifactId>
<version>${abstracto.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${abstracto.version}</version>
</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,80 @@
package dev.sheldan.sissi.module.custom.imagegeneration.command;
import dev.sheldan.abstracto.core.command.CommandAlternative;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.command.service.CommandRegistry;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.templating.model.AttachedFile;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureConfig;
import dev.sheldan.sissi.module.custom.imagegeneration.service.ImageGenerationService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Arrays;
import java.util.List;
@Component
public class OrangeSunDogeCommandAlternative implements CommandAlternative {
@Autowired
private ImageGenerationService imageGenerationService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Autowired
private FileService fileService;
@Autowired
private CommandRegistry commandRegistry;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private ImageGenerationFeatureConfig imageGenerationFeatureConfig;
private static final String DOGE_ORANGE_SUN_RESPONSE_TEMPLATE_KEY = "doge_orangeSun_response";
@Override
public boolean shouldExecute(UnParsedCommandParameter parameter, Guild guild, Message message) {
String contentStripped = message.getContentRaw();
String[] parameters = contentStripped.split(" ");
return parameters.length == 1 && featureFlagService.isFeatureEnabled(imageGenerationFeatureConfig, guild.getIdLong());
}
@Override
public void execute(UnParsedCommandParameter parameter, Message message) {
String contentStripped = message.getContentRaw();
List<String> parameters = Arrays.asList(contentStripped.split(" "));
String inputText = commandRegistry.getCommandName(parameters.get(0), message.getGuild().getIdLong());
File triggeredGifFile = imageGenerationService.getOrangeSunDogeImage(inputText);
MessageToSend messageToSend = templateService.renderEmbedTemplate(DOGE_ORANGE_SUN_RESPONSE_TEMPLATE_KEY, new Object());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
.file(triggeredGifFile)
.fileName("doge.png")
.build();
messageToSend.getAttachedFiles().add(file);
FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, message.getGuildChannel()))
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()));
}
@Override
public Integer getPriority() {
return ListenerPriority.LOW;
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.sissi.module.custom.imagegeneration.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:custom-image-generation-config.properties")
public class CustomImageGenerationConfig {
}

View File

@@ -0,0 +1,29 @@
package dev.sheldan.sissi.module.custom.imagegeneration.service;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.service.HttpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
@Component
public class ImageGenerationService {
@Value("${abstracto.feature.imagegeneration.doge.orangeSun.url}")
private String dogeOrangeSunUrl;
@Autowired
private HttpService httpService;
public File getOrangeSunDogeImage(String inputText) {
try {
return httpService.downloadFileToTempFile(dogeOrangeSunUrl.replace("{1}", inputText));
} catch (IOException e) {
throw new AbstractoRunTimeException(String.format("Failed to download orange doge image for url %s with error %s", inputText, e.getMessage()));
}
}
}

View File

@@ -0,0 +1 @@
abstracto.feature.imagegeneration.doge.orangeSun.url=http://${PRIVATE_REST_API_HOST}:${PRIVATE_REST_API_PORT}/memes/doge/orangeSun/?text={1}

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.application.module.custom</groupId> <groupId>dev.sheldan.sissi.application.module.custom</groupId>
<artifactId>sissi-customizations</artifactId> <artifactId>sissi-customizations</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>application</artifactId> <artifactId>application</artifactId>
<groupId>dev.sheldan.sissi.application</groupId> <groupId>dev.sheldan.sissi.application</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -13,6 +13,7 @@
<modules> <modules>
<module>moderation-custom</module> <module>moderation-custom</module>
<module>image-generation-custom</module>
</modules> </modules>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.application</groupId> <groupId>dev.sheldan.sissi.application</groupId>
<artifactId>sissi-modules</artifactId> <artifactId>sissi-modules</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -1,15 +1,12 @@
package dev.sheldan.sissi.module.debra.api; package dev.sheldan.sissi.module.debra.api;
import dev.sheldan.sissi.module.debra.model.api.*; import dev.sheldan.sissi.module.debra.model.api.*;
import dev.sheldan.sissi.module.debra.model.commands.DonationItemModel;
import dev.sheldan.sissi.module.debra.model.commands.DonationsModel;
import dev.sheldan.sissi.module.debra.service.DonationService; import dev.sheldan.sissi.module.debra.service.DonationService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME; import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME;
@@ -24,7 +21,7 @@ public class DebraDonationStatusController {
@GetMapping(value = "/latestDonations", produces = "application/json") @GetMapping(value = "/latestDonations", produces = "application/json")
public DonationStats getLatestDonations() { public DonationStats getLatestDonations() {
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME)); Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
DonationsResponse donationResponse = donationService.getCachedDonationAmount(serverId); DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount(serverId);
List<DonationInfo> donations = donationService.getLatestDonations(donationResponse, Integer.MAX_VALUE) List<DonationInfo> donations = donationService.getLatestDonations(donationResponse, Integer.MAX_VALUE)
.stream() .stream()
.map(DonationInfo::fromDonationItemModel) .map(DonationInfo::fromDonationItemModel)
@@ -39,7 +36,7 @@ public class DebraDonationStatusController {
@GetMapping(value = "/highestDonations", produces = "application/json") @GetMapping(value = "/highestDonations", produces = "application/json")
public DonationStats getHighestDonations() { public DonationStats getHighestDonations() {
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME)); Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
DonationsResponse donationResponse = donationService.getCachedDonationAmount(serverId); DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount(serverId);
List<DonationInfo> donations = donationService.getHighestDonations(donationResponse, Integer.MAX_VALUE) List<DonationInfo> donations = donationService.getHighestDonations(donationResponse, Integer.MAX_VALUE)
.stream() .stream()
.map(DonationInfo::fromDonationItemModel) .map(DonationInfo::fromDonationItemModel)
@@ -54,7 +51,7 @@ public class DebraDonationStatusController {
@GetMapping(value = "/campaignInfo", produces = "application/json") @GetMapping(value = "/campaignInfo", produces = "application/json")
public CampaignInfo getCampaignInfo() { public CampaignInfo getCampaignInfo() {
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME)); Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
DonationsResponse donationResponse = donationService.getCachedDonationAmount(serverId); DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount(serverId);
Description pageObject = donationResponse.getPage(); Description pageObject = donationResponse.getPage();
return CampaignInfo return CampaignInfo

View File

@@ -0,0 +1,51 @@
package dev.sheldan.sissi.module.debra.api;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.sissi.module.debra.config.DebraFeatureConfig;
import dev.sheldan.sissi.module.debra.model.api.DonationsResponse;
import dev.sheldan.sissi.module.debra.model.api.EndlessStreamInfo;
import dev.sheldan.sissi.module.debra.model.database.EndlessStream;
import dev.sheldan.sissi.module.debra.service.DonationService;
import dev.sheldan.sissi.module.debra.service.management.EndlessStreamManagementServiceBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME;
@RestController
@RequestMapping(value = "/stream")
public class EndlessStreamController {
@Autowired
private EndlessStreamManagementServiceBean endlessStreamManagementServiceBean;
@Autowired
private DonationService donationService;
@Autowired
private ConfigService configService;
@GetMapping(value = "/endlessStream/{id}", produces = "application/json")
public EndlessStreamInfo getLatestDonations(@PathVariable("id") Long id) {
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
EndlessStream endlessStream = endlessStreamManagementServiceBean.getEndlessStream(id);
DonationsResponse donationInfo = donationService.getSynchronizedCachedDonationAmount(serverId);
BigDecimal collectedAmount = donationInfo.getPage().getCollected();
Long minuteRate = configService.getLongValueOrConfigDefault(DebraFeatureConfig.ENDLESS_STREAM_MINUTE_RATE, serverId);
Instant endDate = endlessStream.getStartTime().plus(collectedAmount.multiply(new BigDecimal(minuteRate)).toBigInteger().longValue(), ChronoUnit.MINUTES);
return EndlessStreamInfo
.builder()
.startDate(endlessStream.getStartTime())
.endDate(endDate)
.donationAmount(collectedAmount.longValue())
.minuteRate(minuteRate)
.build();
}
}

View File

@@ -1,33 +1,33 @@
package dev.sheldan.sissi.module.debra.config; package dev.sheldan.sissi.module.debra.config;
import lombok.extern.slf4j.Slf4j;
import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.jsr107.EhcacheCachingProvider; import org.ehcache.jsr107.Eh107Configuration;
import org.ehcache.xml.XmlConfiguration; import org.ehcache.xml.XmlConfiguration;
import org.springframework.cache.jcache.JCacheCacheManager; import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import javax.cache.CacheManager;
import javax.cache.Caching;
import java.net.URL; import java.net.URL;
@Configuration @Configuration
@Slf4j
@EnableCaching
public class CacheConfig { public class CacheConfig {
@Bean("donationCacheManager")
public JCacheCacheManager jCacheCacheManager() {
return new JCacheCacheManager(getDonationCacheManager());
}
@Bean @Bean
public CacheManager getDonationCacheManager() { public JCacheManagerCustomizer cacheManagerCustomizer() {
URL myUrl = getClass().getResource("/donation-cache-config.xml"); URL myUrl = getClass().getResource("/donation-cache-config.xml");
XmlConfiguration xmlConfig = new XmlConfiguration(myUrl); XmlConfiguration xmlConfig = new XmlConfiguration(myUrl);
org.ehcache.CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig); org.ehcache.CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig);
EhcacheCachingProvider provider = (EhcacheCachingProvider) Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider"); return cm -> {
myCacheManager.getRuntimeConfiguration().getCacheConfigurations().entrySet().forEach(cacheConfiguration -> {
return provider.getCacheManager(provider.getDefaultURI(), myCacheManager.getRuntimeConfiguration()); javax.cache.configuration.Configuration<?, ?> jConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration.getValue());
log.info("Creating custom cache: " + cacheConfiguration.getKey());
cm.createCache(cacheConfiguration.getKey(), jConfiguration);
});
};
} }
} }

View File

@@ -12,6 +12,7 @@ import java.util.List;
public class DebraFeatureConfig implements FeatureConfig { public class DebraFeatureConfig implements FeatureConfig {
public static final String DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY = "debraDonationNotificationDelayMillis"; public static final String DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY = "debraDonationNotificationDelayMillis";
public static final String ENDLESS_STREAM_MINUTE_RATE = "endlessStreamMinuteRate";
public static final String DEBRA_DONATION_API_FETCH_SIZE_KEY = "debraDonationApiFetchSize"; public static final String DEBRA_DONATION_API_FETCH_SIZE_KEY = "debraDonationApiFetchSize";
public static final String DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME = "DEBRA_DONATION_NOTIFICATION_SERVER_ID"; public static final String DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME = "DEBRA_DONATION_NOTIFICATION_SERVER_ID";
@Override @Override
@@ -26,6 +27,6 @@ public class DebraFeatureConfig implements FeatureConfig {
@Override @Override
public List<String> getRequiredSystemConfigKeys() { public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY, DEBRA_DONATION_API_FETCH_SIZE_KEY); return Arrays.asList(DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY, DEBRA_DONATION_API_FETCH_SIZE_KEY, ENDLESS_STREAM_MINUTE_RATE);
} }
} }

View File

@@ -1,4 +1,4 @@
package dev.sheldan.sissi.module.debra; package dev.sheldan.sissi.module.debra.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException; import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;

View File

@@ -0,0 +1,15 @@
package dev.sheldan.sissi.module.debra.model.api;
import lombok.Builder;
import lombok.Getter;
import java.time.Instant;
@Builder
@Getter
public class EndlessStreamInfo {
private Instant endDate;
private Instant startDate;
private Long donationAmount;
private Long minuteRate;
}

View File

@@ -0,0 +1,31 @@
package dev.sheldan.sissi.module.debra.model.database;
import jakarta.persistence.*;
import lombok.*;
import java.time.Instant;
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "endless_stream")
@Getter
@Setter
@EqualsAndHashCode
public class EndlessStream {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@Column(name = "start_time")
private Instant startTime;
@Column(name = "created")
private Instant created;
@Column(name = "updated")
private Instant updated;
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.sissi.module.debra.repository;
import dev.sheldan.sissi.module.debra.model.database.EndlessStream;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EndlessStreamRepository extends JpaRepository<EndlessStream, Long> {
}

View File

@@ -1,18 +0,0 @@
package dev.sheldan.sissi.module.debra.service;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
@Component
public class DonationCacheService implements InitializingBean {
@Autowired
private CacheManager cacheManager;
@Override
public void afterPropertiesSet() throws Exception {
cacheManager.getCache("donations");
}
}

View File

@@ -13,7 +13,7 @@ import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend; import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService; import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.sissi.module.debra.DonationAmountNotFoundException; import dev.sheldan.sissi.module.debra.exception.DonationAmountNotFoundException;
import dev.sheldan.sissi.module.debra.config.DebraPostTarget; import dev.sheldan.sissi.module.debra.config.DebraPostTarget;
import dev.sheldan.sissi.module.debra.config.DebraProperties; import dev.sheldan.sissi.module.debra.config.DebraProperties;
import dev.sheldan.sissi.module.debra.converter.DonationConverter; import dev.sheldan.sissi.module.debra.converter.DonationConverter;
@@ -134,9 +134,13 @@ public class DonationService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Cacheable(value = "donation-cache", cacheManager = "donationCacheManager") public synchronized DonationsResponse getSynchronizedCachedDonationAmount(Long serverId) {
public DonationsResponse getCachedDonationAmount(Long serverId) { return self.getCachedDonationAmount(serverId);
return fetchCurrentDonationAmount(serverId); }
@Cacheable(value = "donation-cache")
public synchronized DonationsResponse getCachedDonationAmount(Long serverId) {
return self.fetchCurrentDonationAmount(serverId);
} }
public DonationsResponse fetchCurrentDonationAmount(Long serverId) { public DonationsResponse fetchCurrentDonationAmount(Long serverId) {
@@ -148,10 +152,8 @@ public class DonationService {
.build(); .build();
Response response = okHttpClient.newCall(request).execute(); Response response = okHttpClient.newCall(request).execute();
if(!response.isSuccessful()) { if(!response.isSuccessful()) {
if (log.isDebugEnabled()) { log.error("Failed to retrieve donation response. Response had code {} with body {} and headers {}.",
log.error("Failed to retrieve donation response. Response had code {} with body {}.", response.code(), response.body().string(), response.headers());
response.code(), response.body());
}
throw new DonationAmountNotFoundException(); throw new DonationAmountNotFoundException();
} }
Gson gson = getGson(); Gson gson = getGson();

View File

@@ -0,0 +1,17 @@
package dev.sheldan.sissi.module.debra.service.management;
import dev.sheldan.sissi.module.debra.model.database.EndlessStream;
import dev.sheldan.sissi.module.debra.repository.EndlessStreamRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class EndlessStreamManagementServiceBean {
@Autowired
private EndlessStreamRepository endlessStreamRepository;
public EndlessStream getEndlessStream(Long id) {
return endlessStreamRepository.getReferenceById(id);
}
}

View File

@@ -10,5 +10,8 @@ sissi.debra.donationAPIUrl=https://www.altruja.de/api/page/discord-gg-austria-fu
abstracto.systemConfigs.debraDonationNotificationDelayMillis.name=debraDonationNotificationDelayMillis abstracto.systemConfigs.debraDonationNotificationDelayMillis.name=debraDonationNotificationDelayMillis
abstracto.systemConfigs.debraDonationNotificationDelayMillis.longValue=60000 abstracto.systemConfigs.debraDonationNotificationDelayMillis.longValue=60000
abstracto.systemConfigs.endlessStreamMinuteRate.name=endlessStreamMinuteRate
abstracto.systemConfigs.endlessStreamMinuteRate.longValue=1
abstracto.systemConfigs.debraDonationApiFetchSize.name=debraDonationApiFetchSize abstracto.systemConfigs.debraDonationApiFetchSize.name=debraDonationApiFetchSize
abstracto.systemConfigs.debraDonationApiFetchSize.longValue=1000 abstracto.systemConfigs.debraDonationApiFetchSize.longValue=1000

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="tables/tables.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,32 @@
<?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="endless_stream-table">
<createTable tableName="endless_stream">
<column name="id" autoIncrement="true" 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>
<sql>
DROP TRIGGER IF EXISTS endless_stream_update_trigger ON endless_stream;
CREATE TRIGGER endless_stream_update_trigger BEFORE UPDATE ON endless_stream FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS endless_stream_insert_trigger ON endless_stream;
CREATE TRIGGER endless_stream_insert_trigger BEFORE INSERT ON endless_stream FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</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="endless_stream.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -8,4 +8,5 @@
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" > http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.3.6/collection.xml" relativeToChangelogFile="true"/> <include file="1.3.6/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.21/collection.xml" relativeToChangelogFile="true"/> <include file="1.4.21/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.29/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog> </databaseChangeLog>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.application</groupId> <groupId>dev.sheldan.sissi.application</groupId>
<artifactId>sissi-modules</artifactId> <artifactId>sissi-modules</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.application</groupId> <groupId>dev.sheldan.sissi.application</groupId>
<artifactId>application</artifactId> <artifactId>application</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.application</groupId> <groupId>dev.sheldan.sissi.application</groupId>
<artifactId>sissi-modules</artifactId> <artifactId>sissi-modules</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>sissi-modules</artifactId> <artifactId>sissi-modules</artifactId>
<groupId>dev.sheldan.sissi.application</groupId> <groupId>dev.sheldan.sissi.application</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -1,24 +1,5 @@
apiVersion: v2 apiVersion: v2
name: sissi name: sissi
description: A Helm chart for Kubernetes description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application type: application
version: 1.4.38
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 1.4.22
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.4.22"

View File

@@ -84,6 +84,10 @@ spec:
secretKeyRef: secretKeyRef:
name: api-keys name: api-keys
key: twitchSecret key: twitchSecret
- name: PRIVATE_REST_API_HOST
value: "{{ .Values.privateRestApi.service.name }}.{{ .Release.Namespace }}.svc.cluster.local"
- name: PRIVATE_REST_API_PORT
value: "{{ .Values.privateRestApi.service.targetPort }}"
{{- range $key, $value := .Values.bot.propertyConfig }} {{- range $key, $value := .Values.bot.propertyConfig }}
- name: {{ $key | quote }} - name: {{ $key | quote }}
value: {{ $value | quote}} value: {{ $value | quote}}

View File

@@ -0,0 +1,70 @@
{{- if .Values.privateRestApi.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "sissi.fullname" . }}-private-rest-api
spec:
replicas: 1
selector:
matchLabels:
app: private-rest-api
template:
metadata:
{{- with .Values.privateRestApi.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app: private-rest-api
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "sissi.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.privateRestApi.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}-private-rest-api
securityContext:
{{- toYaml .Values.privateRestApi.securityContext | nindent 12 }}
image: "{{ .Values.privateRestApi.repository }}/{{ .Values.privateRestApi.image }}:{{ .Values.privateRestApi.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.privateRestApi.pullPolicy }}
env:
- name: SISSI_HOST
value: "{{ include "sissi.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local"
- name: SISSI_PORT
value: "{{ .Values.service.port }}"
ports:
- name: {{ .Values.privateRestApi.service.name }}
containerPort: {{ .Values.privateRestApi.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: {{ .Values.privateRestApi.service.port }}
initialDelaySeconds: {{ $.Values.privateRestApi.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ $.Values.privateRestApi.livenessProbe.periodSeconds }}
failureThreshold: {{ $.Values.privateRestApi.livenessProbe.failureThreshold }}
readinessProbe:
httpGet:
path: /
port: {{ .Values.privateRestApi.service.port }}
initialDelaySeconds: {{ $.Values.privateRestApi.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ $.Values.privateRestApi.readinessProbe.periodSeconds }}
failureThreshold: {{ $.Values.privateRestApi.readinessProbe.failureThreshold }}
resources:
{{- toYaml .Values.privateRestApi.resources | nindent 12 }}
{{- with .Values.privateRestApi.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.privateRestApi.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.privateRestApi.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,12 @@
{{- if .Values.privateRestApi.enabled -}}
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.privateRestApi.service.name }}
spec:
selector:
app: private-rest-api
ports:
- port: {{ .Values.privateRestApi.service.targetPort }}
targetPort: {{ .Values.privateRestApi.service.port }}
{{- end }}

View File

@@ -25,7 +25,7 @@ spec:
securityContext: securityContext:
{{- toYaml .Values.restApi.podSecurityContext | nindent 8 }} {{- toYaml .Values.restApi.podSecurityContext | nindent 8 }}
containers: containers:
- name: {{ .Chart.Name }} - name: {{ .Chart.Name }}-rest-api
securityContext: securityContext:
{{- toYaml .Values.restApi.securityContext | nindent 12 }} {{- toYaml .Values.restApi.securityContext | nindent 12 }}
image: "{{ .Values.restApi.repository }}/{{ .Values.restApi.image }}:{{ .Values.restApi.tag | default .Chart.AppVersion }}" image: "{{ .Values.restApi.repository }}/{{ .Values.restApi.image }}:{{ .Values.restApi.tag | default .Chart.AppVersion }}"

View File

@@ -6,9 +6,9 @@ replicaCount: 1
bot: bot:
repository: harbor.sheldan.dev/sissi repository: harbor.sheldan.dev/sissi
pullPolicy: IfNotPresent pullPolicy: Always
image: sissi-bot image: sissi-bot
tag: 1.4.22 tag: 1.4.38
livenessProbe: livenessProbe:
initialDelaySeconds: 60 initialDelaySeconds: 60
periodSeconds: 5 periodSeconds: 5
@@ -23,9 +23,9 @@ bot:
restApi: restApi:
enabled: true enabled: true
repository: harbor.sheldan.dev/sissi repository: harbor.sheldan.dev/sissi
pullPolicy: IfNotPresent pullPolicy: Always
image: sissi-rest-api image: sissi-rest-api
tag: 1.4.20 tag: 1.4.38
podAnnotations: {} podAnnotations: {}
podSecurityContext: {} podSecurityContext: {}
securityContext: {} securityContext: {}
@@ -63,31 +63,63 @@ restApi:
extraPaths: [] extraPaths: []
tls: [] tls: []
privateRestApi:
enabled: true
repository: harbor.sheldan.dev/sissi
pullPolicy: Always
image: sissi-private-rest-api
tag: 1.4.38
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
livenessProbe:
initialDelaySeconds: 2
periodSeconds: 5
failureThreshold: 3
readinessProbe:
initialDelaySeconds: 2
periodSeconds: 5
failureThreshold: 3
service:
port: 8080
targetPort: 80
name: private-restapi # must be less than 16 chars
resources:
limits:
cpu:
memory:
requests:
cpu:
memory:
nodeSelector: {}
tolerations: []
affinity: {}
templateDeployment: templateDeployment:
enabled: true enabled: true
repository: harbor.sheldan.dev/abstracto repository: harbor.sheldan.dev/abstracto
pullPolicy: Always pullPolicy: Always
image: abstracto-template-deployment image: abstracto-template-deployment
tag: 1.5.12 tag: 1.5.23
templateDeploymentData: templateDeploymentData:
repository: harbor.sheldan.dev/sissi repository: harbor.sheldan.dev/sissi
pullPolicy: Always pullPolicy: Always
image: sissi-template-data image: sissi-template-data
tag: 1.4.22 tag: 1.4.38
dbConfigDeployment: dbConfigDeployment:
enabled: true enabled: true
repository: harbor.sheldan.dev/abstracto repository: harbor.sheldan.dev/abstracto
pullPolicy: Always pullPolicy: Always
image: abstracto-db-deployment image: abstracto-db-deployment
tag: 1.5.12 tag: 1.5.23
dbConfigDeploymentData: dbConfigDeploymentData:
repository: harbor.sheldan.dev/sissi repository: harbor.sheldan.dev/sissi
pullPolicy: Always pullPolicy: Always
image: sissi-db-data image: sissi-db-data
tag: 1.4.22 tag: 1.4.38
dbCredentials: dbCredentials:
host: host:

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
REGISTRY_PREFIX=harbor.sheldan.dev/sissi/
VERSION=1.4.22

View File

@@ -8,6 +8,8 @@
{ "zip": "statistic", "file": "statistic-changeLog.xml"}, { "zip": "statistic", "file": "statistic-changeLog.xml"},
{ "zip": "starboard", "file": "starboard-changeLog.xml"}, { "zip": "starboard", "file": "starboard-changeLog.xml"},
{ "zip": "quotes", "file": "quotes-changeLog.xml"}, { "zip": "quotes", "file": "quotes-changeLog.xml"},
{ "zip": "giveaway", "file": "giveaway-changeLog.xml"},
{ "zip": "image-generation", "file": "imageGeneration-changeLog.xml"},
{ "zip": "meetup", "file": "meetup-changeLog.xml"}, { "zip": "meetup", "file": "meetup-changeLog.xml"},
{ "zip": "rss-news", "file": "rssNews-changeLog.xml"}, { "zip": "rss-news", "file": "rssNews-changeLog.xml"},
{ "zip": "debra", "file": "debra-changeLog.xml"}, { "zip": "debra", "file": "debra-changeLog.xml"},

View File

@@ -1,19 +0,0 @@
version: "3.7"
services:
bot:
build:
context: sissi
image: ${REGISTRY_PREFIX}sissi-bot:${VERSION:-latest}
rest-api:
build:
context: rest-api
image: ${REGISTRY_PREFIX}sissi-rest-api:${VERSION:-latest}
db-data:
build:
context: db-data
image: ${REGISTRY_PREFIX}sissi-db-data:${VERSION:-latest}
template-data:
build:
context: template-data
image: ${REGISTRY_PREFIX}sissi-template-data:${VERSION:-latest}

View File

@@ -0,0 +1,11 @@
ARG REGISTRY_PREFIX
ARG SISSI_REGISTRY_PREFIX
ARG VERSION
ARG ABSTRACTO_VERSION
FROM ${REGISTRY_PREFIX}abstracto-rest-api-image-gen:${ABSTRACTO_VERSION:-latest} AS image-gen-api
FROM ${SISSI_REGISTRY_PREFIX}sissi-image-gen-api:${VERSION:-latest} AS sissi-image-gen-api
FROM ${REGISTRY_PREFIX}abstracto-rest-api:${ABSTRACTO_VERSION:-latest} AS running-image
COPY --from=image-gen-api /python /python
COPY --from=sissi-image-gen-api /python /python

View File

@@ -1,9 +1,9 @@
FROM python:3.10.13-alpine3.18 ARG REGISTRY_PREFIX
ADD requirements.txt / ARG SISSI_REGISTRY_PREFIX
RUN pip install -r requirements.txt ARG VERSION
RUN apk --no-cache add msttcorefonts-installer fontconfig && \ ARG ABSTRACTO_VERSION
update-ms-fonts && \ FROM ${SISSI_REGISTRY_PREFIX}sissi-debra-rest-api:${VERSION:-latest} AS debra-api
fc-cache -f
ADD wrapper /
ADD python /python FROM ${REGISTRY_PREFIX}abstracto-rest-api:${ABSTRACTO_VERSION:-latest}
CMD ["/run.sh"] COPY --from=debra-api /python /python

View File

@@ -1,16 +0,0 @@
blinker==1.7.0
certifi==2023.7.22
charset-normalizer==3.3.2
click==8.1.7
Flask==3.0.0
idna==3.4
importlib-metadata==6.8.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.3
Pillow==10.1.0
requests==2.31.0
urllib3==2.0.7
waitress==2.1.2
Werkzeug==3.0.1
zipp==3.17.0

View File

@@ -1,4 +0,0 @@
#!/bin/sh
echo "Starting python server..."
python3 -u python/main.py

View File

@@ -1,13 +1,17 @@
{ {
"template_artifacts": [ "template_artifacts": [
"core","starboard", "link-embed", "moderation", "entertainment", "custom-command", "utility", "webservices", "remind", "suggestion", "modmail", "assignable-roles", "experience-tracking", "logging", "statistic", "twitch", "core","starboard", "link-embed", "moderation", "entertainment", "custom-command", "utility", "webservices", "remind",
"suggestion", "modmail", "assignable-roles", "experience-tracking", "logging", "statistic", "twitch", "giveaway",
"image-generation",
"quotes", "meetup", "debra", "rss-news", "quotes", "meetup", "debra", "rss-news",
"moderation-custom", "moderation-custom", "image-generation-custom",
"moderation-template-overrides", "experience-template-overrides", "logging-template-overrides" "moderation-template-overrides", "experience-template-overrides", "logging-template-overrides"
], ],
"translation_artifacts": [ "translation_artifacts": [
"core", "core",
"starboard", "link-embed", "moderation", "entertainment", "custom-command", "utility", "webservices", "suggestion", "remind", "modmail", "assignable-roles", "experience-tracking", "logging", "statistic", "twitch", "starboard", "link-embed", "moderation", "entertainment", "custom-command", "utility", "webservices",
"suggestion", "remind", "modmail", "assignable-roles", "experience-tracking", "logging", "statistic", "twitch", "giveaway",
"image-generation",
"quotes", "meetup", "debra", "rss-news", "quotes", "meetup", "debra", "rss-news",
"moderation-custom", "moderation-custom",
"moderation-translation-overrides", "experience-translation-overrides", "logging-translation-overrides" "moderation-translation-overrides", "experience-translation-overrides", "logging-translation-overrides"

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi</groupId> <groupId>dev.sheldan.sissi</groupId>
<artifactId>sissi</artifactId> <artifactId>sissi</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

43
docker-compose.yml Normal file
View File

@@ -0,0 +1,43 @@
version: "3.7"
services:
bot-packaging:
build:
context: deployment/image-packaging/src/main/docker/sissi
image: ${REGISTRY_PREFIX}sissi-bot:${VERSION:-latest}
debra-rest-api:
build:
context: python/modules/debra-rest-api
dockerfile: docker/Dockerfile
image: ${REGISTRY_PREFIX}sissi-debra-rest-api:${VERSION:-latest}
image-gen-api:
build:
context: python/modules/image-gen-api
dockerfile: docker/Dockerfile
image: ${REGISTRY_PREFIX}sissi-image-gen-api:${VERSION:-latest}
rest-api-packaging:
build:
context: deployment/image-packaging/src/main/docker/rest-api
args:
SISSI_REGISTRY_PREFIX: ${REGISTRY_PREFIX}
REGISTRY_PREFIX: ${ABSTRACTO_PREFIX}
VERSION: ${VERSION}
ABSTRACTO_VERSION: ${ABSTRACTO_VERSION}
image: ${REGISTRY_PREFIX}sissi-rest-api:${VERSION:-latest}
private-rest-api-packaging:
build:
context: deployment/image-packaging/src/main/docker/private-rest-api
args:
REGISTRY_PREFIX: ${ABSTRACTO_PREFIX}
SISSI_REGISTRY_PREFIX: ${REGISTRY_PREFIX}
ABSTRACTO_VERSION: ${ABSTRACTO_VERSION}
VERSION: ${VERSION}
image: ${REGISTRY_PREFIX}sissi-private-rest-api:${VERSION:-latest}
db-data:
build:
context: deployment/image-packaging/src/main/docker/db-data
image: ${REGISTRY_PREFIX}sissi-db-data:${VERSION:-latest}
template-data:
build:
context: deployment/image-packaging/src/main/docker/template-data
image: ${REGISTRY_PREFIX}sissi-template-data:${VERSION:-latest}

15
pom.xml
View File

@@ -13,15 +13,13 @@
<groupId>dev.sheldan.sissi</groupId> <groupId>dev.sheldan.sissi</groupId>
<artifactId>sissi</artifactId> <artifactId>sissi</artifactId>
<name>Sissi</name> <name>Sissi</name>
<version>1.4.22</version> <version>1.4.39</version>
<properties> <properties>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<!-- edit in release.yml as well --> <abstracto.version>1.5.23</abstracto.version>
<!-- when releasing a new bot version, update the .env as well--> <abstracto.templates.version>1.4.31</abstracto.templates.version>
<abstracto.version>1.5.12</abstracto.version>
<abstracto.templates.version>1.4.23</abstracto.templates.version>
<apache-jena.version>4.9.0</apache-jena.version> <apache-jena.version>4.9.0</apache-jena.version>
<rssreader.version>3.5.0</rssreader.version> <rssreader.version>3.5.0</rssreader.version>
</properties> </properties>
@@ -57,9 +55,10 @@
</distributionManagement> </distributionManagement>
<scm> <scm>
<url>https://maven.pkg.github.com/Sheldan/Sissi</url> <connection>scm:git:${project.scm.url}</connection>
<developerConnection>scm:git:git@github.com:Sheldan/Sissi.git</developerConnection> <developerConnection>scm:git:${project.scm.url}</developerConnection>
<tag>sissi-1.4.22</tag> <url>https://github.com/Sheldan/Sissi.git</url>
<tag>sissi-1.4.39</tag>
</scm> </scm>
</project> </project>

View File

@@ -0,0 +1,3 @@
FROM alpine:3.19.0
ADD resources/ /python/resources
ADD python /python

View File

@@ -1,51 +1,22 @@
from flask import Flask, send_file, request from flask import request, render_template
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
import requests import requests
import os import os
import json import json
import logging import logging
import uuid
from __main__ import app
from utils import flask_utils
from datetime import timezone, datetime
import pytz
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
logging.basicConfig(encoding='utf-8', level=logging.INFO, format=FORMAT)
app = Flask(__name__)
sissi_host = os.getenv('SISSI_HOST') sissi_host = os.getenv('SISSI_HOST')
sissi_port = os.getenv('SISSI_PORT') sissi_port = os.getenv('SISSI_PORT')
latest_donations_url = f'http://{sissi_host}:{sissi_port}/debra/latestDonations' latest_donations_url = f'http://{sissi_host}:{sissi_port}/debra/latestDonations'
highest_donations_url = f'http://{sissi_host}:{sissi_port}/debra/highestDonations' highest_donations_url = f'http://{sissi_host}:{sissi_port}/debra/highestDonations'
campaign_info_url = f'http://{sissi_host}:{sissi_port}/debra/campaignInfo' campaign_info_url = f'http://{sissi_host}:{sissi_port}/debra/campaignInfo'
endless_stream_info_url = f'http://{sissi_host}:{sissi_port}/stream/endlessStream'
class ValidationException(Exception):
def __init__(self, provided_value, message):
self.provided_value = provided_value
self.message = message
super().__init__(f'{self.message}: provided value: {provided_value}')
class DonationImageGenerationConstants:
allowed_fonts = ['Andale_Mono', 'Courier_New', 'Impact', 'Trebuchet_MS_Italic',
'Arial', 'Courier_New_Bold', 'Times_New_Roman', 'Verdana',
'Arial_Black', 'Courier_New_Bold_Italic', 'Times_New_Roman_Bold', 'Verdana_Bold',
'Arial_Bold', 'Courier_New_Italic', 'Times_New_Roman_Bold_Italic', 'Verdana_Bold_Italic',
'Arial_Bold_Italic', 'Georgia', 'Times_New_Roman_Italic', 'Verdana_Italic',
'Arial_Italic', 'Georgia_Bold', 'Trebuchet_MS',
'Comic_Sans_MS', 'Georgia_Bold_Italic', 'Trebuchet_MS_Bold',
'Comic_Sans_MS_Bold', 'Georgia_Italic', 'Trebuchet_MS_Bold_Italic']
font_range = (2, 150)
canvas_width_range = (1, 2500)
canvas_height_range = (1, 2500)
donation_count_range = (1, 25)
color_range = (0, 255)
font_default = 'Arial'
font_size_default = 40
canvas_width_default = 1024
canvas_height_default = 300
donation_count_default = 5
r_default = 0
g_default = 0
b_default = 0
class DonationImageGenerationParameters: class DonationImageGenerationParameters:
@@ -104,12 +75,6 @@ class DonationImageGenerationParameters:
self.validation_value = self.color[2] self.validation_value = self.color[2]
@app.route('/')
def hello():
return 'Hello, World?'
@app.route('/debra/latestDonations') @app.route('/debra/latestDonations')
def latest_donations(): def latest_donations():
donation_stats = requests.get(latest_donations_url) donation_stats = requests.get(latest_donations_url)
@@ -164,8 +129,62 @@ def total_donations_image():
img = Image.new('RGBA', (parameters.canvas_width, parameters.canvas_height), (255, 0, 0, 0)) img = Image.new('RGBA', (parameters.canvas_width, parameters.canvas_height), (255, 0, 0, 0))
d1 = ImageDraw.Draw(img) d1 = ImageDraw.Draw(img)
font = ImageFont.truetype(f'{parameters.font_name}.ttf', parameters.font_size) font = ImageFont.truetype(f'{parameters.font_name}.ttf', parameters.font_size)
d1.text((0, 0), f"Aktuell {campaign_info['collected']}/{campaign_info['target']}", fill=parameters.color, font=font) d1.text((0, 0), f"{campaign_info['collected']}/{campaign_info['target']}", fill=parameters.color, font=font)
return serve_pil_image(img) return flask_utils.serve_pil_image(img)
@app.route('/debra/image/endlessStream/end')
def endless_stream_image():
stream_id = int(request.args.get('streamId', type=int))
endless_stream_info = json.loads(requests.get(f'{endless_stream_info_url}/{stream_id}').text)
logging.info(f'rendering endless stream end image')
parameters = parse_image_parameters()
if not parameters.validated:
return parameters.validation_message, 400
img = Image.new('RGBA', (parameters.canvas_width, parameters.canvas_height), (255, 0, 0, 0))
d1 = ImageDraw.Draw(img)
font = ImageFont.truetype(f'{parameters.font_name}.ttf', parameters.font_size)
end_time = datetime.strptime(endless_stream_info['endDate'], "%Y-%m-%dT%H:%M:%S%z")
tz = pytz.timezone('Europe/Vienna')
end_time_formatted = end_time.astimezone(tz).strftime('%d.%m %H:%M')
d1.text((0, 0), f"{end_time_formatted}", fill=parameters.color, font=font)
return flask_utils.serve_pil_image(img)
@app.route('/debra/image/endlessStream/end/html')
def endless_stream_html():
refresh_interval = int(request.args.get('refreshInterval', 30, type=int))
random_bit = str(uuid.uuid4())
parameters_query = request.query_string.decode()
return render_template('image_refresh_wrapper.html', imagePath=f'/debra/image/endlessStream/end?{parameters_query}&{random_bit}', refreshInterval=refresh_interval)
@app.route('/debra/image/endlessStream/remaining')
def endless_stream_remaining():
stream_id = int(request.args.get('streamId', type=int))
endless_stream_info = json.loads(requests.get(f'{endless_stream_info_url}/{stream_id}').text)
logging.info(f'rendering endless stream remaining image')
parameters = parse_image_parameters()
if not parameters.validated:
return parameters.validation_message, 400
img = Image.new('RGBA', (parameters.canvas_width, parameters.canvas_height), (255, 0, 0, 0))
d1 = ImageDraw.Draw(img)
font = ImageFont.truetype(f'{parameters.font_name}.ttf', parameters.font_size)
end_time = datetime.strptime(endless_stream_info['endDate'], "%Y-%m-%dT%H:%M:%S%z").replace(tzinfo=pytz.utc)
current_time = datetime.now(timezone.utc)
remaining_time = end_time - current_time
total_seconds = remaining_time.total_seconds()
remaining_time_formatted = f'{int(total_seconds // 3600):02d}:{int((total_seconds % 3600) // 60):02d}:{int(total_seconds % 60):02d}'
d1.text((0, 0), f"{remaining_time_formatted}", fill=parameters.color, font=font)
return flask_utils.serve_pil_image(img)
@app.route('/debra/image/endlessStream/remaining/html')
def endless_stream_remaining_html():
refresh_interval = int(request.args.get('refreshInterval', 30, type=int))
random_bit = str(uuid.uuid4())
parameters_query = request.query_string.decode()
return render_template('image_refresh_wrapper.html', imagePath=f'/debra/image/endlessStream/remaining?{parameters_query}&{random_bit}', refreshInterval=refresh_interval)
@app.route('/debra/image/latestDonations') @app.route('/debra/image/latestDonations')
@@ -188,6 +207,30 @@ def highest_donation_image():
return rendering_donation_image(donation_stats, parameters) return rendering_donation_image(donation_stats, parameters)
@app.route('/debra/image/highestDonations/html')
def highest_donations_image_html_wrapper():
refresh_interval = int(request.args.get('refreshInterval', 30, type=int))
random_bit = str(uuid.uuid4())
parameters_query = request.query_string.decode()
return render_template('image_refresh_wrapper.html', imagePath=f'/debra/image/highestDonations?{parameters_query}&{random_bit}', refreshInterval=refresh_interval)
@app.route('/debra/image/latestDonations/html')
def latest_donations_image_html_wrapper():
refresh_interval = int(request.args.get('refreshInterval', 30, type=int))
random_bit = str(uuid.uuid4())
parameters_query = request.query_string.decode()
return render_template('image_refresh_wrapper.html', imagePath=f'/debra/image/latestDonations?{parameters_query}&{random_bit}', refreshInterval=refresh_interval)
@app.route('/debra/image/info/html')
def total_donations_image_html_wrapper():
refresh_interval = int(request.args.get('refreshInterval', 30, type=int))
random_bit = str(uuid.uuid4())
parameters_query = request.query_string.decode()
return render_template('image_refresh_wrapper.html', imagePath=f'/debra/image/info?{parameters_query}&{random_bit}', refreshInterval=refresh_interval)
def rendering_donation_image(donation_stats, parameters): def rendering_donation_image(donation_stats, parameters):
img = Image.new('RGBA', (parameters.canvas_width, parameters.canvas_height), (255, 0, 0, 0)) img = Image.new('RGBA', (parameters.canvas_width, parameters.canvas_height), (255, 0, 0, 0))
d1 = ImageDraw.Draw(img) d1 = ImageDraw.Draw(img)
@@ -198,32 +241,43 @@ def rendering_donation_image(donation_stats, parameters):
for donation in donations_to_draw: for donation in donations_to_draw:
name = donation['firstName'] if not donation['anonymous'] else 'anonym' name = donation['firstName'] if not donation['anonymous'] else 'anonym'
d1.text((0, height * it), f"{donation['donationAmount']}€ von {name}", fill=parameters.color, font=font) d1.text((0, height * it), f"{donation['donationAmount']}€ von {name}", fill=parameters.color, font=font)
it += 1 it += 1
return serve_pil_image(img) return flask_utils.serve_pil_image(img)
def parse_image_parameters() -> DonationImageGenerationParameters: def parse_image_parameters() -> DonationImageGenerationParameters:
font = request.args.get('font', DonationImageGenerationConstants.font_default) font = request.args.get('font', DonationImageGenerationConstants.font_default, type=str)
font_size = int(request.args.get('fontSize', DonationImageGenerationConstants.font_size_default)) font_size = int(request.args.get('fontSize', DonationImageGenerationConstants.font_size_default, type=int))
canvas_width = int(request.args.get('canvasWidth', DonationImageGenerationConstants.canvas_width_default)) canvas_width = int(request.args.get('canvasWidth', DonationImageGenerationConstants.canvas_width_default, type=int))
canvas_height = int(request.args.get('canvasHeight', DonationImageGenerationConstants.canvas_height_default)) canvas_height = int(request.args.get('canvasHeight', DonationImageGenerationConstants.canvas_height_default, type=int))
donation_count = int(request.args.get('donationCount', DonationImageGenerationConstants.donation_count_default)) donation_count = int(request.args.get('donationCount', DonationImageGenerationConstants.donation_count_default, type=int))
r = int(request.args.get('r', DonationImageGenerationConstants.r_default)) r = int(request.args.get('r', DonationImageGenerationConstants.r_default, type=int))
g = int(request.args.get('g', DonationImageGenerationConstants.g_default)) g = int(request.args.get('g', DonationImageGenerationConstants.g_default, type=int))
b = int(request.args.get('b', DonationImageGenerationConstants.b_default)) b = int(request.args.get('b', DonationImageGenerationConstants.b_default, type=int))
parameters = DonationImageGenerationParameters(font, font_size, canvas_width, canvas_height, donation_count, r, g, b) parameters = DonationImageGenerationParameters(font, font_size, canvas_width, canvas_height, donation_count, r, g, b)
parameters.validate() parameters.validate()
return parameters return parameters
def serve_pil_image(pil_img): class DonationImageGenerationConstants:
img_io = BytesIO() allowed_fonts = ['Andale_Mono', 'Courier_New', 'Impact', 'Trebuchet_MS_Italic',
pil_img.save(img_io, 'PNG') 'Arial', 'Courier_New_Bold', 'Times_New_Roman', 'Verdana',
img_io.seek(0) 'Arial_Black', 'Courier_New_Bold_Italic', 'Times_New_Roman_Bold', 'Verdana_Bold',
return send_file(img_io, mimetype='image/png') 'Arial_Bold', 'Courier_New_Italic', 'Times_New_Roman_Bold_Italic', 'Verdana_Bold_Italic',
'Arial_Bold_Italic', 'Georgia', 'Times_New_Roman_Italic', 'Verdana_Italic',
'Arial_Italic', 'Georgia_Bold', 'Trebuchet_MS',
if __name__ == "__main__": 'Comic_Sans_MS', 'Georgia_Bold_Italic', 'Trebuchet_MS_Bold',
from waitress import serve 'Comic_Sans_MS_Bold', 'Georgia_Italic', 'Trebuchet_MS_Bold_Italic']
font_range = (2, 150)
serve(app, host="0.0.0.0", port=8080) canvas_width_range = (1, 2500)
canvas_height_range = (1, 2500)
donation_count_range = (1, 25)
color_range = (0, 255)
font_default = 'Arial'
font_size_default = 40
canvas_width_default = 1024
canvas_height_default = 300
donation_count_default = 5
r_default = 0
g_default = 0
b_default = 0

View File

@@ -0,0 +1,8 @@
<html>
<head>
<meta http-equiv="refresh" content="{{ refreshInterval }}">
</head>
<body>
<img src="{{ imagePath }}">
</body>
</html>

View File

@@ -0,0 +1,3 @@
FROM alpine:3.19.0
ADD resources/ /python/resources
ADD python /python

View File

@@ -0,0 +1,24 @@
from __main__ import app
from PIL import Image, ImageDraw, ImageFont
from flask import request
import logging
from utils import flask_utils
@app.route('/memes/doge/orangeSun/')
def doge_orange_sun():
text = request.args.get('text')
if text is None:
return 'no text', 400
logging.info(f'Rendering doge orange sun.')
with Image.open('resources/img/semf_template.jpg') as im:
d1 = ImageDraw.Draw(im)
text_box_size = (300, 240)
W, H = text_box_size
font = ImageFont.truetype(f'Impact.ttf', 60)
_, _, w, h = d1.textbbox((0, 0), text, font=font)
d1.text(((W-w)/2 + 320, (H-h)/2 + 120), text, font=font, fill=(255, 255, 255))
return flask_utils.serve_pil_image(im)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi</groupId> <groupId>dev.sheldan.sissi</groupId>
<artifactId>sissi</artifactId> <artifactId>sissi</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>customization-templates</artifactId>
<groupId>dev.sheldan.sissi.templates</groupId>
<version>1.4.39</version>
</parent>
<artifactId>image-generation-customization-templates</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>image-generation-custom-templates-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,15 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>zip</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources</directory>
</fileSet>
</fileSets>
</assembly>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>customization-templates</artifactId> <artifactId>customization-templates</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>sissi-templates</artifactId> <artifactId>sissi-templates</artifactId>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -11,6 +11,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>moderation-customization-templates</module> <module>moderation-customization-templates</module>
<module>image-generation-customization-templates</module>
</modules> </modules>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>module-templates</artifactId> <artifactId>module-templates</artifactId>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>module-templates</artifactId> <artifactId>module-templates</artifactId>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
"embeds": [ "embeds": [
{ {
<#include "abstracto_color">, <#include "abstracto_color">,
<#assign time><@format_instant_date_time instant=meetupTime/></#assign> <#assign time><@format_instant_long_date_time instant=meetupTime/></#assign>
<#assign topicText>${topic?json_string}</#assign> <#assign topicText>${topic?json_string}</#assign>
"description": "<#include "meetup_cancel_notification_description">" "description": "<#include "meetup_cancel_notification_description">"
} }

View File

@@ -6,7 +6,7 @@
"title": { "title": {
"title": "${topic?json_string}" "title": "${topic?json_string}"
}, },
"description": "<@format_instant_date_time instant=meetupTime/> "description": "<@format_instant_long_date_time instant=meetupTime/>
${description?json_string}" ${description?json_string}"
} }
], ],

View File

@@ -4,8 +4,8 @@
{ {
<#include "abstracto_color">, <#include "abstracto_color">,
<#assign topicText=meetupTopic> <#assign topicText=meetupTopic>
<#assign oldTime><@format_instant_date_time instant=oldDate/></#assign> <#assign oldTime><@format_instant_long_date_time instant=oldDate/></#assign>
<#assign newTime><@format_instant_date_time instant=newDate/></#assign> <#assign newTime><@format_instant_long_date_time instant=newDate/></#assign>
<#assign messageLink=meetupMessage.jumpUrl> <#assign messageLink=meetupMessage.jumpUrl>
"description": "<@safe_include "changeMeetupTime_notification_text"/>" "description": "<@safe_include "changeMeetupTime_notification_text"/>"
} }

View File

@@ -6,7 +6,7 @@
"title": { "title": {
"title": "${topic?json_string}" "title": "${topic?json_string}"
}, },
"description": "<@format_instant_date_time instant=meetupTime/> "description": "<@format_instant_long_date_time instant=meetupTime/>
${description?json_string}" ${description?json_string}"
<#if location?? && location != "%22%22">, <#if location?? && location != "%22%22">,
"fields": [ "fields": [

View File

@@ -3,7 +3,7 @@
"embeds": [ "embeds": [
{ {
<#include "abstracto_color">, <#include "abstracto_color">,
"description": "<#list meetups as meetup><#assign meetup=meetup><#assign topic=meetup.topic><#assign time><@format_instant_date_time instant=meetup.meetupTime/></#assign><#assign timeRelative><@format_instant_relative instant=meetup.meetupTime/></#assign><#assign link=meetup.meetupMessage.jumpUrl><#include "meetup_list_meetup_display"> "description": "<#list meetups as meetup><#assign meetup=meetup><#assign topic=meetup.topic><#assign time><@format_instant_long_date_time instant=meetup.meetupTime/></#assign><#assign timeRelative><@format_instant_relative instant=meetup.meetupTime/></#assign><#assign link=meetup.meetupMessage.jumpUrl><#include "meetup_list_meetup_display">
<#else><#include "meetup_list_no_meetups"></#list>" <#else><#include "meetup_list_no_meetups"></#list>"
} }
] ]

View File

@@ -8,7 +8,7 @@
"title": { "title": {
"title": "${topic?json_string} - <@safe_include "meetup_message_id_display"/>" "title": "${topic?json_string} - <@safe_include "meetup_message_id_display"/>"
}, },
<#assign time><@format_instant_date_time instant=meetupTime/></#assign> <#assign time><@format_instant_long_date_time instant=meetupTime/></#assign>
<#assign timeRelative><@format_instant_relative instant=meetupTime/></#assign> <#assign timeRelative><@format_instant_relative instant=meetupTime/></#assign>
<#assign organizerText>${organizer.memberMention}</#assign> <#assign organizerText>${organizer.memberMention}</#assign>
<#assign meetupId=meetupId/> <#assign meetupId=meetupId/>

View File

@@ -3,7 +3,7 @@
"embeds": [ "embeds": [
{ {
<#include "abstracto_color">, <#include "abstracto_color">,
<#assign time><@format_instant_date_time instant=meetupTime/></#assign> <#assign time><@format_instant_long_date_time instant=meetupTime/></#assign>
<#assign topicText>${topic?json_string}</#assign> <#assign topicText>${topic?json_string}</#assign>
"description": "<#include "meetup_reminder_notification_description">" "description": "<#include "meetup_reminder_notification_description">"
} }

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>sissi-templates</artifactId> <artifactId>sissi-templates</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>module-templates</artifactId> <artifactId>module-templates</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>module-templates</artifactId> <artifactId>module-templates</artifactId>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>templates</artifactId> <artifactId>templates</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>template-overrides</artifactId> <artifactId>template-overrides</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>template-overrides</artifactId> <artifactId>template-overrides</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>template-overrides</artifactId> <artifactId>template-overrides</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>sissi-templates</artifactId> <artifactId>sissi-templates</artifactId>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<artifactId>customization-translations</artifactId> <artifactId>customization-translations</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>sissi-translations</artifactId> <artifactId>sissi-translations</artifactId>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>module-translations</artifactId> <artifactId>module-translations</artifactId>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1 @@
The amount of records fetched from the Debra api. Default: ${defaultValue}

View File

@@ -0,0 +1 @@
The amount of time (in milliseconds) after which the donation notification should be sent. Default: ${defaultValue}

View File

@@ -0,0 +1 @@
The amount of minutes per euro donation. Default: ${defaultValue}

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<artifactId>module-translations</artifactId> <artifactId>module-translations</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<artifactId>sissi-translations</artifactId> <artifactId>sissi-translations</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<artifactId>module-translations</artifactId> <artifactId>module-translations</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<artifactId>module-translations</artifactId> <artifactId>module-translations</artifactId>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates</groupId> <groupId>dev.sheldan.sissi.templates</groupId>
<artifactId>templates</artifactId> <artifactId>templates</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations.overrides</groupId> <groupId>dev.sheldan.sissi.templates.translations.overrides</groupId>
<artifactId>translation-overrides</artifactId> <artifactId>translation-overrides</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations.overrides</groupId> <groupId>dev.sheldan.sissi.templates.translations.overrides</groupId>
<artifactId>translation-overrides</artifactId> <artifactId>translation-overrides</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations.overrides</groupId> <groupId>dev.sheldan.sissi.templates.translations.overrides</groupId>
<artifactId>translation-overrides</artifactId> <artifactId>translation-overrides</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.sissi.templates.translations</groupId> <groupId>dev.sheldan.sissi.templates.translations</groupId>
<artifactId>sissi-translations</artifactId> <artifactId>sissi-translations</artifactId>
<version>1.4.22</version> <version>1.4.39</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>