mirror of
https://github.com/Sheldan/Sissi.git
synced 2026-01-03 16:27:48 +00:00
Compare commits
29 Commits
sissi-1.5.
...
sissi-1.5.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
128710c847 | ||
|
|
ee0a49b00e | ||
|
|
ff4756e0ff | ||
|
|
68e4dc4235 | ||
|
|
7d827c6e1e | ||
|
|
8e292f76e4 | ||
|
|
a2db56853b | ||
|
|
abcd8e5c67 | ||
|
|
a05b9a8811 | ||
|
|
ec5a07cffb | ||
|
|
111a88b091 | ||
|
|
663d88475a | ||
|
|
8336d7fd9e | ||
|
|
a5d3b970df | ||
|
|
a47f399d36 | ||
|
|
29bb752367 | ||
|
|
efdb375a83 | ||
|
|
f158580897 | ||
|
|
4411995e65 | ||
|
|
c9b5258798 | ||
|
|
ea5a7509bc | ||
|
|
40703005e1 | ||
|
|
fe036d7010 | ||
|
|
2547d68dfc | ||
|
|
b8b59b09c4 | ||
|
|
774c142238 | ||
|
|
5b6160b0d2 | ||
|
|
453b2214cd | ||
|
|
071c603e54 |
4
.env
4
.env
@@ -1,4 +1,4 @@
|
||||
REGISTRY_PREFIX=harbor.sheldan.dev/sissi/
|
||||
ABSTRACTO_PREFIX=harbor.sheldan.dev/abstracto/
|
||||
VERSION=1.5.13
|
||||
ABSTRACTO_VERSION=1.6.15
|
||||
VERSION=1.5.19
|
||||
ABSTRACTO_VERSION=1.6.19
|
||||
3
README
3
README
@@ -1,4 +1 @@
|
||||
|
||||
|
||||
Attributions:
|
||||
Code for debra module has been inspired with approval of zinnsoldat91 by: https://github.com/zinnsoldat91/spendenbot. The code has been adapted to fit into the structure.
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<artifactId>application</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>executable</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi</groupId>
|
||||
<artifactId>sissi</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
<artifactId>rssreader</artifactId>
|
||||
<version>${rssreader.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>${jsoup.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.application.module.custom</groupId>
|
||||
<artifactId>sissi-customizations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>image-generation-custom</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.application.module.custom</groupId>
|
||||
<artifactId>sissi-customizations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>application</artifactId>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<artifactId>sissi-modules</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling-int</artifactId>
|
||||
<version>${abstracto.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
@@ -28,6 +33,10 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -7,10 +7,9 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/debra")
|
||||
public class DebraDonationStatusController {
|
||||
@@ -20,50 +19,43 @@ public class DebraDonationStatusController {
|
||||
|
||||
@GetMapping(value = "/latestDonations", produces = "application/json")
|
||||
public DonationStats getLatestDonations() {
|
||||
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
|
||||
DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount(serverId);
|
||||
DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount();
|
||||
List<DonationInfo> donations = donationService.getLatestDonations(donationResponse, Integer.MAX_VALUE)
|
||||
.stream()
|
||||
.map(DonationInfo::fromDonationItemModel)
|
||||
.toList();
|
||||
return DonationStats
|
||||
.builder()
|
||||
.totalAmount(donationResponse.getPage().getCollected())
|
||||
.totalAmount(donationResponse.getCurrentDonationAmount())
|
||||
.donations(donations)
|
||||
.build();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/highestDonations", produces = "application/json")
|
||||
public DonationStats getHighestDonations() {
|
||||
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
|
||||
DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount(serverId);
|
||||
DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount();
|
||||
List<DonationInfo> donations = donationService.getHighestDonations(donationResponse, Integer.MAX_VALUE)
|
||||
.stream()
|
||||
.map(DonationInfo::fromDonationItemModel)
|
||||
.toList();
|
||||
return DonationStats
|
||||
.builder()
|
||||
.totalAmount(donationResponse.getPage().getCollected())
|
||||
.totalAmount(donationResponse.getCurrentDonationAmount())
|
||||
.donations(donations)
|
||||
.build();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/campaignInfo", produces = "application/json")
|
||||
public CampaignInfo getCampaignInfo() {
|
||||
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
|
||||
DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount(serverId);
|
||||
DonationsResponse donationResponse = donationService.getSynchronizedCachedDonationAmount();
|
||||
|
||||
Description pageObject = donationResponse.getPage();
|
||||
return CampaignInfo
|
||||
.builder()
|
||||
.collected(pageObject.getCollected())
|
||||
.collectedNet(pageObject.getCollectedNet())
|
||||
.donationCount(donationResponse.getDonationCount())
|
||||
.currency(pageObject.getCurrency())
|
||||
.percent(pageObject.getPercent())
|
||||
.displayName(pageObject.getDisplayName())
|
||||
.slug(pageObject.getSlug())
|
||||
.target(pageObject.getTarget())
|
||||
.collected(donationResponse.getCurrentDonationAmount())
|
||||
.target(donationResponse.getDonationAmountGoal())
|
||||
.percent(donationResponse.getCurrentDonationAmount().divide(donationResponse.getDonationAmountGoal(), RoundingMode.CEILING))
|
||||
.currency("€")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ public class EndlessStreamController {
|
||||
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();
|
||||
DonationsResponse donationInfo = donationService.getSynchronizedCachedDonationAmount();
|
||||
BigDecimal collectedAmount = donationInfo.getCurrentDonationAmount();
|
||||
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
|
||||
|
||||
@@ -6,9 +6,9 @@ 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.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandParameterKey;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
@@ -26,7 +26,6 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEve
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -70,11 +69,12 @@ public class Donations extends AbstractConditionableCommand {
|
||||
Integer selectionValue = (Integer) parameters.get(1);
|
||||
Integer top = null;
|
||||
Integer latest = null;
|
||||
switch (type) {
|
||||
case "top": top = selectionValue; break;
|
||||
default:
|
||||
case "latest" :
|
||||
latest = selectionValue; break;
|
||||
if(type != null) {
|
||||
DonationsTypeParameterKey typeKey = CommandParameterKey.getEnumFromKey(DonationsTypeParameterKey.class, type);
|
||||
switch (typeKey) {
|
||||
case LATEST -> latest = selectionValue;
|
||||
case TOP -> top = selectionValue;
|
||||
}
|
||||
}
|
||||
messageToSend = getDonationMessageToSend(commandContext.getGuild().getIdLong(), top, latest);
|
||||
}
|
||||
@@ -98,11 +98,10 @@ public class Donations extends AbstractConditionableCommand {
|
||||
Integer top = null;
|
||||
Integer latest = null;
|
||||
if(selectionType != null) {
|
||||
switch (selectionType) {
|
||||
case "top": top = selectionValue; break;
|
||||
default:
|
||||
case "latest" :
|
||||
latest = selectionValue; break;
|
||||
DonationsTypeParameterKey typeKey = CommandParameterKey.getEnumFromKey(DonationsTypeParameterKey.class, selectionType);
|
||||
switch (typeKey) {
|
||||
case LATEST -> latest = selectionValue;
|
||||
case TOP -> top = selectionValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +112,7 @@ public class Donations extends AbstractConditionableCommand {
|
||||
|
||||
private MessageToSend getDonationMessageToSend(Long serverId, Integer top, Integer latest) {
|
||||
DonationsModel donationModel;
|
||||
DonationsResponse donationResponse = donationService.fetchCurrentDonationAmount(serverId);
|
||||
DonationsResponse donationResponse = donationService.fetchCurrentDonations();
|
||||
donationModel = donationConverter.convertDonationResponse(donationResponse);
|
||||
if(top != null) {
|
||||
donationModel.setDonations(donationService.getHighestDonations(donationResponse, top));
|
||||
@@ -146,7 +145,7 @@ public class Donations extends AbstractConditionableCommand {
|
||||
.templated(true)
|
||||
.name(SELECTION_PARAMETER)
|
||||
.optional(true)
|
||||
.type(String.class)
|
||||
.type(DonationsTypeParameterKey.class)
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.sheldan.sissi.module.debra.commands;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandParameterKey;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DonationsTypeParameterKey implements CommandParameterKey {
|
||||
TOP("top"), LATEST("latest");
|
||||
|
||||
private String key;
|
||||
}
|
||||
@@ -13,7 +13,6 @@ public class DebraFeatureConfig implements FeatureConfig {
|
||||
|
||||
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_NOTIFICATION_SERVER_ID_ENV_NAME = "DEBRA_DONATION_NOTIFICATION_SERVER_ID";
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
@@ -22,11 +21,11 @@ public class DebraFeatureConfig implements FeatureConfig {
|
||||
|
||||
@Override
|
||||
public List<PostTargetEnum> getRequiredPostTargets() {
|
||||
return Arrays.asList(DebraPostTarget.DEBRA_DONATION_NOTIFICATION, DebraPostTarget.DEBRA_DONATION_NOTIFICATION2);
|
||||
return Arrays.asList(DebraPostTarget.DEBRA_DONATION_NOTIFICATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequiredSystemConfigKeys() {
|
||||
return Arrays.asList(DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY, DEBRA_DONATION_API_FETCH_SIZE_KEY, ENDLESS_STREAM_MINUTE_RATE);
|
||||
return Arrays.asList(DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY, ENDLESS_STREAM_MINUTE_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum DebraPostTarget implements PostTargetEnum {
|
||||
DEBRA_DONATION_NOTIFICATION("debraDonationNotification"), DEBRA_DONATION_NOTIFICATION2("debraDonationNotification2");
|
||||
DEBRA_DONATION_NOTIFICATION("debraDonationNotification");
|
||||
|
||||
private String key;
|
||||
|
||||
|
||||
@@ -10,6 +10,5 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "sissi.debra")
|
||||
public class DebraProperties {
|
||||
private String websocketURL;
|
||||
private String donationAPIUrl;
|
||||
private String donationPageUrl;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.sheldan.sissi.module.debra.converter;
|
||||
|
||||
import dev.sheldan.sissi.module.debra.model.api.Donation;
|
||||
import dev.sheldan.sissi.module.debra.model.api.DonationDto;
|
||||
import dev.sheldan.sissi.module.debra.model.api.DonationsResponse;
|
||||
import dev.sheldan.sissi.module.debra.model.commands.DonationItemModel;
|
||||
import dev.sheldan.sissi.module.debra.model.commands.DonationsModel;
|
||||
@@ -9,20 +9,19 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DonationConverter {
|
||||
public DonationItemModel convertDonation(Donation donation) {
|
||||
public DonationItemModel convertDonation(DonationDto donation) {
|
||||
return DonationItemModel
|
||||
.builder()
|
||||
.donationAmount(donation.getAmount())
|
||||
.firstName(donation.getFirstname())
|
||||
.lastName(donation.getLastname())
|
||||
.anonymous(BooleanUtils.toBoolean(donation.getAnonym()))
|
||||
.name(donation.getName())
|
||||
.anonymous(BooleanUtils.toBoolean(donation.getAnonymous()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public DonationsModel convertDonationResponse(DonationsResponse response) {
|
||||
return DonationsModel
|
||||
.builder()
|
||||
.totalAmount(response.getPage().getCollected())
|
||||
.totalAmount(response.getCurrentDonationAmount())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package dev.sheldan.sissi.module.debra.job;
|
||||
|
||||
import dev.sheldan.sissi.module.debra.service.DonationService;
|
||||
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 DonationFetchJob extends QuartzJobBean {
|
||||
|
||||
@Autowired
|
||||
private DonationService donationService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
|
||||
try {
|
||||
log.info("Checking for new donations.");
|
||||
donationService.checkForNewDonations();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to check for new donations.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package dev.sheldan.sissi.module.debra.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.listener.AsyncStartupListener;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.sissi.module.debra.config.DebraProperties;
|
||||
import dev.sheldan.sissi.module.debra.model.listener.DonationResponseModel;
|
||||
import dev.sheldan.sissi.module.debra.service.DonationService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY;
|
||||
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class WebsocketListener extends WebSocketListener implements AsyncStartupListener {
|
||||
|
||||
@Autowired
|
||||
private DonationService donationService;
|
||||
|
||||
@Autowired
|
||||
private DebraProperties debraProperties;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
private WebSocket webSocketObj;
|
||||
private OkHttpClient clientObj;
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
log.info("Connected to donation websocket.");
|
||||
super.onOpen(webSocket, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, String text) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
log.info("Handling received message on websocket.");
|
||||
try {
|
||||
Long targetServerId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
|
||||
Long delayMillis = configService.getLongValueOrConfigDefault(DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY, targetServerId);
|
||||
log.info("Waiting {} milli seconds to send notification.", delayMillis);
|
||||
Thread.sleep(delayMillis);
|
||||
log.info("Loading new donation amount and sending notification.");
|
||||
DonationResponseModel donation = donationService.parseDonationFromMessage(text);
|
||||
donationService.sendDonationNotification(donation).thenAccept(unused -> {
|
||||
log.info("Successfully notified about donation.");
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to notify about donation.", throwable);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
log.error("Failed to handle websocket message.", exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {
|
||||
log.warn("Websocket connection failed...", t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosing(WebSocket webSocket, int code, String reason) {
|
||||
log.info("Closing websocket connection. It was closed with code {} and reason {}.", code, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if(clientObj != null) {
|
||||
clientObj.connectionPool().evictAll();
|
||||
clientObj.dispatcher().executorService().shutdownNow();
|
||||
}
|
||||
clientObj = new OkHttpClient.Builder()
|
||||
.readTimeout(0, TimeUnit.MILLISECONDS)
|
||||
.retryOnConnectionFailure(true)
|
||||
.build();
|
||||
startConnection(clientObj);
|
||||
clientObj.dispatcher().executorService().shutdown();
|
||||
}
|
||||
|
||||
private void startConnection(OkHttpClient client) {
|
||||
log.info("Starting websocket connection.");
|
||||
Request request = new Request.Builder()
|
||||
.url(debraProperties.getWebsocketURL())
|
||||
.build();
|
||||
this.webSocketObj = client.newWebSocket(request, this);
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,13 @@ import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class CampaignInfo {
|
||||
private BigInteger donationCount;
|
||||
private Integer donationCount;
|
||||
private BigDecimal collected;
|
||||
private BigDecimal target;
|
||||
private String currency;
|
||||
private String slug;
|
||||
private String displayName;
|
||||
private BigDecimal collectedNet;
|
||||
private BigDecimal percent;
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package dev.sheldan.sissi.module.debra.model.api;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class Description {
|
||||
@SerializedName("collected")
|
||||
private BigDecimal collected;
|
||||
@SerializedName("target")
|
||||
private BigDecimal target;
|
||||
@SerializedName("currency")
|
||||
private String currency;
|
||||
@SerializedName("slug")
|
||||
private String slug;
|
||||
@SerializedName("displayname")
|
||||
private String displayName;
|
||||
@SerializedName("collectednet")
|
||||
private BigDecimal collectedNet;
|
||||
@SerializedName("percent")
|
||||
private BigDecimal percent;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package dev.sheldan.sissi.module.debra.model.api;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class Donation {
|
||||
@SerializedName("amount")
|
||||
private BigDecimal amount;
|
||||
@SerializedName("currency")
|
||||
private String currency;
|
||||
@SerializedName("text")
|
||||
private String text;
|
||||
@SerializedName("anonym")
|
||||
private Integer anonym;
|
||||
@SerializedName("firstname")
|
||||
private String firstname;
|
||||
@SerializedName("lastname")
|
||||
private String lastname;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.sheldan.sissi.module.debra.model.api;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@ToString
|
||||
public class DonationDto {
|
||||
private BigDecimal amount;
|
||||
private String currency;
|
||||
private String text;
|
||||
private Boolean anonymous;
|
||||
private String name;
|
||||
private LocalDate date;
|
||||
|
||||
public String stringRepresentation() {
|
||||
return String.format("%s %s %s %s %s", name, amount, text, anonymous, date.format(DateTimeFormatter.ISO_DATE));
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import java.math.BigDecimal;
|
||||
@Setter
|
||||
@Builder
|
||||
public class DonationInfo {
|
||||
private String firstName;
|
||||
private String name;
|
||||
private BigDecimal donationAmount;
|
||||
private Boolean anonymous;
|
||||
|
||||
@@ -20,7 +20,7 @@ public class DonationInfo {
|
||||
.builder()
|
||||
.donationAmount(donationItemModel.getDonationAmount())
|
||||
.anonymous(donationItemModel.getAnonymous())
|
||||
.firstName(donationItemModel.getFirstName())
|
||||
.name(donationItemModel.getName())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
package dev.sheldan.sissi.module.debra.model.api;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class DonationsResponse {
|
||||
@SerializedName("page")
|
||||
private Description page;
|
||||
@SerializedName("donation_count")
|
||||
private BigInteger donationCount;
|
||||
@SerializedName("donations")
|
||||
private List<Donation> donations;
|
||||
private BigDecimal currentDonationAmount;
|
||||
private BigDecimal donationAmountGoal;
|
||||
private int donationCount;
|
||||
private List<DonationDto> donations;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class DonationItemModel {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String name;
|
||||
private LocalDate date;
|
||||
private BigDecimal donationAmount;
|
||||
private Boolean anonymous;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.sheldan.sissi.module.debra.model.database;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "donation")
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class Donation {
|
||||
@Id
|
||||
@Column(name = "id", nullable = false)
|
||||
private String id;
|
||||
|
||||
// we cant be sure about duplicates
|
||||
@Column(name = "count")
|
||||
private Integer count;
|
||||
|
||||
@Column(name = "created")
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated")
|
||||
private Instant updated;
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import java.math.BigDecimal;
|
||||
@ToString
|
||||
public class DonationResponseModel {
|
||||
private String donatorName;
|
||||
private Boolean anonymous;
|
||||
private BigDecimal amount;
|
||||
private String message;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.sissi.module.debra.repository;
|
||||
|
||||
import dev.sheldan.sissi.module.debra.model.database.Donation;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface DonationRepository extends JpaRepository<Donation, String> {
|
||||
}
|
||||
@@ -1,52 +1,52 @@
|
||||
package dev.sheldan.sissi.module.debra.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentPayloadService;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentService;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.HashService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||
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.core.utils.FutureUtils;
|
||||
import dev.sheldan.sissi.module.debra.exception.DonationAmountNotFoundException;
|
||||
import dev.sheldan.sissi.module.debra.config.DebraPostTarget;
|
||||
import dev.sheldan.sissi.module.debra.config.DebraProperties;
|
||||
import dev.sheldan.sissi.module.debra.converter.DonationConverter;
|
||||
import dev.sheldan.sissi.module.debra.model.api.Donation;
|
||||
import dev.sheldan.sissi.module.debra.model.api.DonationDto;
|
||||
import dev.sheldan.sissi.module.debra.model.api.DonationsResponse;
|
||||
import dev.sheldan.sissi.module.debra.model.commands.DebraInfoButtonPayload;
|
||||
import dev.sheldan.sissi.module.debra.model.commands.DebraInfoModel;
|
||||
import dev.sheldan.sissi.module.debra.model.commands.DonationItemModel;
|
||||
import dev.sheldan.sissi.module.debra.model.commands.DonationsModel;
|
||||
import dev.sheldan.sissi.module.debra.model.database.Donation;
|
||||
import dev.sheldan.sissi.module.debra.model.listener.DonationResponseModel;
|
||||
import dev.sheldan.sissi.module.debra.model.listener.DonationNotificationModel;
|
||||
import dev.sheldan.sissi.module.debra.service.management.DonationManagementServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_API_FETCH_SIZE_KEY;
|
||||
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME;
|
||||
|
||||
@Component
|
||||
@@ -62,15 +62,9 @@ public class DonationService {
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private OkHttpClient okHttpClient;
|
||||
|
||||
@Autowired
|
||||
private DonationConverter donationConverter;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@@ -83,110 +77,219 @@ public class DonationService {
|
||||
@Autowired
|
||||
private ServerManagementService serverManagementService;
|
||||
|
||||
@Autowired
|
||||
private DonationManagementServiceBean donationManagementServiceBean;
|
||||
|
||||
@Autowired
|
||||
private HashService hashService;
|
||||
|
||||
@Autowired
|
||||
private DonationService self;
|
||||
|
||||
private static final String DEBRA_DONATION_NOTIFICATION_TEMPLATE_KEY = "debra_donation_notification";
|
||||
|
||||
private static final Pattern MESSAGE_PATTERN = Pattern.compile("(.*) hat (\\d{1,9},\\d{2}) Euro gespendet!<br \\/>Vielen Dank!<br \\/>Nachricht:<br \\/>(.*)");
|
||||
private static final String DEBRA_DONATION_PING_NOTIFICATION_TEMPLATE_KEY = "debra_donation_notification_ping_notification";
|
||||
|
||||
private static final String DEBRA_INFO_BUTTON_MESSAGE_TEMPLATE_KEY = "debraInfoButton";
|
||||
public static final String DEBRA_INFO_BUTTON_ORIGIN = "DEBRA_INFO_BUTTON";
|
||||
|
||||
public DonationResponseModel parseDonationFromMessage(String message) {
|
||||
Matcher matcher = MESSAGE_PATTERN.matcher(message);
|
||||
if (matcher.find()) {
|
||||
String donatorName = matcher.group(1);
|
||||
String amountString = matcher.group(2);
|
||||
BigDecimal amount = new BigDecimal(amountString.replace(',', '.'));
|
||||
String donationMessage = Optional.ofNullable(matcher.group(3)).map(msg -> msg.replaceAll("(<br>)+", " ")).map(String::trim).orElse("");
|
||||
return DonationResponseModel
|
||||
.builder()
|
||||
.message(donationMessage)
|
||||
.donatorName(donatorName)
|
||||
.amount(amount)
|
||||
.build();
|
||||
} else {
|
||||
throw new IllegalArgumentException("String in wrong format");
|
||||
}
|
||||
}
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("d.M.y");
|
||||
|
||||
public List<DonationItemModel> getHighestDonations(DonationsResponse response, Integer maxCount) {
|
||||
List<Donation> topDonations = response
|
||||
return response
|
||||
.getDonations()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(Donation::getAmount)
|
||||
.sorted(Comparator.comparing(DonationDto::getAmount)
|
||||
.reversed())
|
||||
.collect(Collectors.toList());
|
||||
return topDonations
|
||||
.stream()
|
||||
.limit(maxCount)
|
||||
.map(donation -> donationConverter.convertDonation(donation))
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<DonationItemModel> getLatestDonations(DonationsResponse response, Integer maxCount) {
|
||||
return response
|
||||
.getDonations()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(DonationDto::getDate).reversed())
|
||||
.limit(maxCount)
|
||||
.map(donation -> donationConverter.convertDonation(donation))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public synchronized DonationsResponse getSynchronizedCachedDonationAmount(Long serverId) {
|
||||
return self.getCachedDonationAmount(serverId);
|
||||
public synchronized DonationsResponse getSynchronizedCachedDonationAmount() {
|
||||
return self.getCachedDonationAmount();
|
||||
}
|
||||
|
||||
@Cacheable(value = "donation-cache")
|
||||
public synchronized DonationsResponse getCachedDonationAmount(Long serverId) {
|
||||
return self.fetchCurrentDonationAmount(serverId);
|
||||
public synchronized DonationsResponse getCachedDonationAmount() {
|
||||
return self.fetchCurrentDonations();
|
||||
}
|
||||
|
||||
public DonationsResponse fetchCurrentDonationAmount(Long serverId) {
|
||||
public DonationsResponse fetchCurrentDonations() {
|
||||
try {
|
||||
Long fetchSize = configService.getLongValueOrConfigDefault(DEBRA_DONATION_API_FETCH_SIZE_KEY, serverId);
|
||||
Request request = new Request.Builder()
|
||||
.url(String.format(debraProperties.getDonationAPIUrl(), fetchSize))
|
||||
.get()
|
||||
.build();
|
||||
Response response = okHttpClient.newCall(request).execute();
|
||||
if(!response.isSuccessful()) {
|
||||
log.error("Failed to retrieve donation response. Response had code {} with body {} and headers {}.",
|
||||
response.code(), response.body().string(), response.headers());
|
||||
throw new DonationAmountNotFoundException();
|
||||
Document donationPage = Jsoup.connect(debraProperties.getDonationPageUrl()).get();
|
||||
DecimalFormat decimalFormat = getDecimalFormat();
|
||||
Element endValueElement = donationPage.getElementById("end-value");
|
||||
String endValueString = endValueElement.text();
|
||||
Elements currentValueElement = donationPage.getElementsByClass("current_amount").get(0).getElementsByClass("value");
|
||||
String[] valueArray = currentValueElement.text().split(" ");
|
||||
String currentValueString = valueArray[0];
|
||||
String currency = valueArray[1];
|
||||
BigDecimal currentValue = (BigDecimal) decimalFormat.parse(currentValueString);
|
||||
BigDecimal endValue = (BigDecimal) decimalFormat.parse(endValueString);
|
||||
Element list = donationPage.getElementsByClass("donor-list").first();
|
||||
Elements donationElements = list.getElementsByClass("list-item");
|
||||
List<DonationDto> donations = new ArrayList<>();
|
||||
for (Element donationMainElement : donationElements.asList()) {
|
||||
Elements nameElement = donationMainElement.getElementsByClass("donor-list-name");
|
||||
Elements dateElement = donationMainElement.getElementsByClass("donor-list-date");
|
||||
Elements amountElement = donationMainElement.getElementsByClass("donor-list-amount");
|
||||
Elements textElement = donationMainElement.getElementsByClass("donor-list-amount-text");
|
||||
LocalDate dateValue;
|
||||
if (dateElement.hasText()) {
|
||||
dateValue = LocalDate.parse(dateElement.text(), DATE_FORMAT);
|
||||
} else {
|
||||
dateValue = null;
|
||||
}
|
||||
BigDecimal amount;
|
||||
if (amountElement.hasText()) {
|
||||
String amountText = amountElement.text().split(" ")[0];
|
||||
amount = (BigDecimal) decimalFormat.parse(amountText);
|
||||
} else {
|
||||
amount = null;
|
||||
}
|
||||
String additionalText = textElement.text();
|
||||
String name = nameElement.text();
|
||||
boolean anonymous = name.isBlank();
|
||||
donations.add(DonationDto
|
||||
.builder()
|
||||
.anonymous(anonymous)
|
||||
.name(nameElement.text())
|
||||
.amount(amount)
|
||||
.currency(currency)
|
||||
.name(name)
|
||||
.text(additionalText)
|
||||
.date(dateValue)
|
||||
.build());
|
||||
}
|
||||
Gson gson = getGson();
|
||||
return gson.fromJson(response.body().string(), DonationsResponse.class);
|
||||
return DonationsResponse
|
||||
.builder()
|
||||
.donations(donations)
|
||||
.currentDonationAmount(currentValue)
|
||||
.donationAmountGoal(endValue)
|
||||
.donationCount(donations.size())
|
||||
.build();
|
||||
} catch (Exception exception) {
|
||||
throw new AbstractoRunTimeException(exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Gson getGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(BigDecimal.class, new BigDecimalGsonAdapter())
|
||||
.create();
|
||||
private DecimalFormat getDecimalFormat() {
|
||||
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
|
||||
symbols.setGroupingSeparator('.');
|
||||
symbols.setDecimalSeparator(',');
|
||||
String pattern = "#,##0.0#";
|
||||
|
||||
DecimalFormat decimalFormat = new DecimalFormat(pattern, symbols);
|
||||
decimalFormat.setParseBigDecimal(true);
|
||||
return decimalFormat;
|
||||
}
|
||||
|
||||
private DonationsModel getDonationInfoModel(Long serverId) {
|
||||
return donationConverter.convertDonationResponse(fetchCurrentDonationAmount(serverId));
|
||||
private DonationsModel getDonationInfoModel() {
|
||||
return donationConverter.convertDonationResponse(fetchCurrentDonations());
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> sendDonationNotification(DonationResponseModel donation) throws IOException {
|
||||
|
||||
private String hashDonation(DonationDto donation) {
|
||||
return hashService.sha256HashString(donation.stringRepresentation());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void checkForNewDonations() {
|
||||
List<Donation> allDonations = donationManagementServiceBean.getAllDonations();
|
||||
Map<String, Integer> existingHashes = allDonations
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Donation::getId, Donation::getCount));
|
||||
DonationsResponse donationResponse = fetchCurrentDonations();
|
||||
Map<String, Pair<Integer, DonationDto>> donationFromPageHashes = new HashMap<>();
|
||||
donationResponse.getDonations().forEach(donationDto -> {
|
||||
String thisHash = hashDonation(donationDto);
|
||||
if(donationFromPageHashes.containsKey(thisHash)) {
|
||||
donationFromPageHashes.put(thisHash, Pair.of(donationFromPageHashes.get(thisHash).getLeft() + 1, donationDto));
|
||||
} else {
|
||||
donationFromPageHashes.put(thisHash, Pair.of(1, donationDto));
|
||||
}
|
||||
});
|
||||
|
||||
Set<String> pageHashesToRemove = new HashSet<>();
|
||||
donationFromPageHashes.entrySet().forEach(pageHash -> {
|
||||
if(existingHashes.containsKey(pageHash.getKey())) {
|
||||
Integer existingDonation = existingHashes.get(pageHash.getKey());
|
||||
int amountDifference = pageHash.getValue().getKey() - existingDonation;
|
||||
if(amountDifference == 0) {
|
||||
pageHashesToRemove.add(pageHash.getKey()); // it matches 1:1, we know about all of them already
|
||||
} if(amountDifference < 0) {
|
||||
pageHashesToRemove.add(pageHash.getKey());
|
||||
log.warn("We have more donations than on the page of hash {}:{}.", pageHash.getKey(), amountDifference);
|
||||
} else {
|
||||
pageHash.setValue(Pair.of(amountDifference, pageHash.getValue().getRight()));
|
||||
}
|
||||
}
|
||||
});
|
||||
pageHashesToRemove.forEach(donationFromPageHashes::remove);
|
||||
if(donationFromPageHashes.isEmpty()) {
|
||||
log.info("No new donations - ending search.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<CompletableFuture<Void>> notificationFutures = new ArrayList<>();
|
||||
donationFromPageHashes.values().forEach(donationInfo -> {
|
||||
for (int i = 0; i < donationInfo.getLeft(); i++) {
|
||||
DonationDto donationDto = donationInfo.getRight();
|
||||
DonationResponseModel model = DonationResponseModel
|
||||
.builder()
|
||||
.message(donationDto.getText())
|
||||
.donatorName(donationDto.getName())
|
||||
.amount(donationDto.getAmount())
|
||||
.anonymous(donationDto.getAnonymous())
|
||||
.build();
|
||||
notificationFutures.add(sendDonationNotification(model));
|
||||
}
|
||||
});
|
||||
new CompletableFutureList<>(notificationFutures).getMainFuture().thenAccept(unused -> {
|
||||
log.info("All {} notifications send.", notificationFutures.size());
|
||||
}).exceptionally(throwable -> {
|
||||
log.warn("Failed to send notifications about {} new donations.", notificationFutures.size(), throwable);
|
||||
return null;
|
||||
});
|
||||
log.info("Creating/updating {} donation entries.", donationFromPageHashes.size());
|
||||
allDonations.forEach(donation -> {
|
||||
Set<String> donationsToRemoveBecauseUpdate = new HashSet<>();
|
||||
donationFromPageHashes.forEach((key, value) -> {
|
||||
// its assumed that donationFromPageHashes only contains donations that need to be created
|
||||
if (donation.getId().equals(key)) {
|
||||
donation.setCount(value.getLeft() + donation.getCount());
|
||||
donationManagementServiceBean.updateDonation(donation);
|
||||
donationsToRemoveBecauseUpdate.add(key);
|
||||
}
|
||||
});
|
||||
donationsToRemoveBecauseUpdate.forEach(donationFromPageHashes::remove);
|
||||
});
|
||||
donationFromPageHashes.forEach((key, value) ->
|
||||
donationManagementServiceBean.saveDonation(key, value.getLeft()));
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> sendDonationNotification(DonationResponseModel donation) {
|
||||
Long targetServerId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
|
||||
DonationsModel donationInfoModel = getDonationInfoModel(targetServerId);
|
||||
DonationsModel donationInfoModel = getDonationInfoModel();
|
||||
DonationNotificationModel model = DonationNotificationModel
|
||||
.builder()
|
||||
.donation(donation)
|
||||
.totalDonationAmount(donationInfoModel.getTotalAmount())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(DEBRA_DONATION_NOTIFICATION_TEMPLATE_KEY, model, targetServerId);
|
||||
List<CompletableFuture<Message>> firstMessage = postTargetService.sendEmbedInPostTarget(messageToSend, DebraPostTarget.DEBRA_DONATION_NOTIFICATION, targetServerId);
|
||||
List<CompletableFuture<Message>> secondMessage = postTargetService.sendEmbedInPostTarget(messageToSend, DebraPostTarget.DEBRA_DONATION_NOTIFICATION2, targetServerId);
|
||||
firstMessage.addAll(secondMessage);
|
||||
return FutureUtils.toSingleFutureGeneric(firstMessage);
|
||||
MessageToSend pingMessageToSend = templateService.renderEmbedTemplate(DEBRA_DONATION_PING_NOTIFICATION_TEMPLATE_KEY, model, targetServerId);
|
||||
return FutureUtils.toSingleFutureGenericList(postTargetService.sendEmbedInPostTarget(List.of(pingMessageToSend, messageToSend),
|
||||
DebraPostTarget.DEBRA_DONATION_NOTIFICATION, targetServerId));
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> sendDebraInfoButtonMessage(GuildMessageChannel guildMessageChannel) {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.sheldan.sissi.module.debra.service.management;
|
||||
|
||||
import dev.sheldan.sissi.module.debra.model.database.Donation;
|
||||
import dev.sheldan.sissi.module.debra.repository.DonationRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class DonationManagementServiceBean {
|
||||
|
||||
@Autowired
|
||||
private DonationRepository donationRepository;
|
||||
|
||||
public List<Donation> getAllDonations() {
|
||||
return donationRepository.findAll();
|
||||
}
|
||||
|
||||
public void updateDonation(Donation donation) {
|
||||
donationRepository.save(donation);
|
||||
}
|
||||
|
||||
public Donation saveDonation(String hash, Integer count) {
|
||||
Donation donation = Donation
|
||||
.builder()
|
||||
.id(hash)
|
||||
.count(count)
|
||||
.build();
|
||||
return donationRepository.save(donation);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,8 @@ abstracto.featureFlags.debra.featureName=debra
|
||||
abstracto.featureFlags.debra.enabled=false
|
||||
|
||||
abstracto.postTargets.debraDonationNotification.name=debraDonationNotification
|
||||
abstracto.postTargets.debraDonationNotification2.name=debraDonationNotification2
|
||||
|
||||
sissi.debra.websocketURL=ws://spenden.baba.fm:8765/
|
||||
sissi.debra.donationAPIUrl=https://www.altruja.de/api/page/discord-schmetterlingsaktion-2024?details=1&num=%s&ort=0
|
||||
sissi.debra.donationPageUrl=https://secure.sicherhelfen.org/campaigns/07a3baf6-5cdc-4300-854b-ea2b36b0b218/show
|
||||
|
||||
abstracto.systemConfigs.debraDonationNotificationDelayMillis.name=debraDonationNotificationDelayMillis
|
||||
abstracto.systemConfigs.debraDonationNotificationDelayMillis.longValue=60000
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" >
|
||||
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" >
|
||||
<include file="donation_fetch_job.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" >
|
||||
<changeSet author="Sheldan" id="donation_fetch_job-insert">
|
||||
<insert tableName="scheduler_job">
|
||||
<column name="name" value="donationFetchJob"/>
|
||||
<column name="group_name" value="debra"/>
|
||||
<column name="clazz" value="dev.sheldan.sissi.module.debra.job.DonationFetchJob"/>
|
||||
<column name="active" value="true"/>
|
||||
<column name="cron_expression" value="0 */2 * * * ?"/>
|
||||
<column name="recovery" value="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" >
|
||||
<changeSet author="Sheldan" id="donation-table">
|
||||
<createTable tableName="donation">
|
||||
<column name="id" type="TEXT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="count" type="NUMERIC">
|
||||
<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 donation_update_trigger ON donation;
|
||||
CREATE TRIGGER donation_update_trigger BEFORE UPDATE ON donation FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS donation_insert_trigger ON donation;
|
||||
CREATE TRIGGER donation_insert_trigger BEFORE INSERT ON donation FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" >
|
||||
<include file="donation.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -5,4 +5,5 @@
|
||||
<include file="1.3.6/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.21/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.29/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.5.16/collection.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<artifactId>sissi-modules</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -42,7 +42,14 @@ public class MeetupChangeTimeConfirmationListener implements ButtonClickedListen
|
||||
}
|
||||
if(model.getEvent().getComponentId().equals(payload.getConfirmationId())) {
|
||||
Meetup meetup = meetupManagementServiceBean.getMeetup(payload.getMeetupId(), payload.getGuildId());
|
||||
meetupServiceBean.changeMeetupTimeAndNotifyParticipants(meetup, Instant.ofEpochSecond(payload.getNewTime()));
|
||||
Long meetupId = meetup.getId().getId();
|
||||
Long serverId = meetup.getId().getServerId();
|
||||
meetupServiceBean.changeMeetupTimeAndNotifyParticipants(meetup, Instant.ofEpochSecond(payload.getNewTime())).thenAccept(unused -> {
|
||||
log.info("Successfully changed time of meetup {} in server {}.", meetupId, serverId);
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to update time of meetup {} in server {}.", meetupId, serverId);
|
||||
return null;
|
||||
});
|
||||
messageService.deleteMessage(model.getEvent().getMessage());
|
||||
cleanupConfirmationMessagePayloads(payload);
|
||||
} else if(model.getEvent().getComponentId().equals(payload.getCancelId())) {
|
||||
|
||||
@@ -103,18 +103,26 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
|
||||
String noButtonId = componentService.generateComponentId();
|
||||
String maybeButtonId = componentService.generateComponentId();
|
||||
String noTimeButtonId = componentService.generateComponentId();
|
||||
MeetupMessageModel messageModel = meetupServiceBean.getMeetupMessageModel(meetup);
|
||||
messageModel.setYesId(yesButtonId);
|
||||
messageModel.setNoId(noButtonId);
|
||||
messageModel.setMaybeId(maybeButtonId);
|
||||
messageModel.setNoTimeId(noTimeButtonId);
|
||||
Long meetupId = payload.getMeetupId();
|
||||
Long serverId = payload.getGuildId();
|
||||
meetup.setYesButtonId(yesButtonId);
|
||||
meetup.setMaybeButtonId(maybeButtonId);
|
||||
meetup.setNoTimeButtonId(noTimeButtonId);
|
||||
meetup.setNotInterestedButtonId(noButtonId);
|
||||
meetupServiceBean.getMeetupMessageModel(meetup).thenAccept(messageModel -> {
|
||||
self.postMeetupMessage(model, messageModel, yesButtonId, noButtonId, maybeButtonId, noTimeButtonId, meetupId, serverId);
|
||||
});
|
||||
return ButtonClickedListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void postMeetupMessage(ButtonClickedListenerModel model, MeetupMessageModel messageModel, String yesButtonId, String noButtonId,
|
||||
String maybeButtonId, String noTimeButtonId, Long meetupId, Long serverId) {
|
||||
messageModel.setYesId(yesButtonId);
|
||||
messageModel.setNoId(noButtonId);
|
||||
messageModel.setMaybeId(maybeButtonId);
|
||||
messageModel.setNoTimeId(noTimeButtonId);
|
||||
messageModel.setCancelled(false);
|
||||
Long meetupId = payload.getMeetupId();
|
||||
Long serverId = payload.getGuildId();
|
||||
MessageToSend messageToSend = meetupServiceBean.getMeetupMessage(messageModel, model.getServerId());
|
||||
List<CompletableFuture<Message>> messageFutures = channelService.sendMessageToSendToChannel(messageToSend, model.getEvent().getMessageChannel());
|
||||
FutureUtils.toSingleFutureGeneric(messageFutures).thenAccept(unused -> {
|
||||
@@ -129,7 +137,6 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
|
||||
log.error("Failed to send meetup message for meetup {}.", meetupId, throwable);
|
||||
return null;
|
||||
});
|
||||
return ButtonClickedListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
private void cleanupConfirmationMessagePayloads(MeetupConfirmationPayload payload) {
|
||||
|
||||
@@ -20,8 +20,10 @@ import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
|
||||
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
|
||||
import dev.sheldan.sissi.module.meetup.service.management.MeetupParticipatorManagementServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -45,11 +47,15 @@ public class MeetupDecisionListener implements ButtonClickedListener {
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private MeetupDecisionListener self;
|
||||
|
||||
@Override
|
||||
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
|
||||
MeetupDecisionPayload payload = (MeetupDecisionPayload) model.getDeserializedPayload();
|
||||
Meetup meetup = meetupManagementServiceBean.getMeetup(payload.getMeetupId(), payload.getGuildId());
|
||||
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(model.getEvent().getMember());
|
||||
Member member = model.getEvent().getMember();
|
||||
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
|
||||
Optional<MeetupParticipant> participationOptional = meetupParticipatorManagementServiceBean.getParticipation(meetup, userInAServer);
|
||||
if(participationOptional.isPresent()) {
|
||||
@@ -57,19 +63,28 @@ public class MeetupDecisionListener implements ButtonClickedListener {
|
||||
} else {
|
||||
meetupParticipatorManagementServiceBean.createParticipation(meetup, userInAServer, payload.getMeetupDecision());
|
||||
}
|
||||
MeetupMessageModel meetupMessageModel = meetupServiceBean.getMeetupMessageModel(meetup);
|
||||
addParticipationToModel(meetupMessageModel, userInAServer, payload.getMeetupDecision());
|
||||
MessageToSend messageToSend = meetupServiceBean.getMeetupMessage(meetupMessageModel, model.getServerId());
|
||||
channelService.editMessageInAChannelFuture(messageToSend, model.getEvent().getChannel(), meetup.getMessageId())
|
||||
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetup.getId().getId(), meetup.getMeetupChannel().getId(), meetup.getServer().getId()))
|
||||
.exceptionally(throwable -> {
|
||||
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetup.getId().getId(), meetup.getMeetupChannel().getId(), meetup.getServer().getId(), throwable);
|
||||
return null;
|
||||
});
|
||||
Long meetupMessageId = meetup.getMessageId();
|
||||
Long meetupId = meetup.getId().getId();
|
||||
Long meetupServerId = meetup.getServer().getId();
|
||||
meetupServiceBean.getMeetupMessageModel(meetup).thenAccept(meetupMessageModel -> {
|
||||
self.updateMeetupMessage(model, meetupMessageModel, member, payload, meetupMessageId, meetupId, meetupServerId);
|
||||
});
|
||||
return ButtonClickedListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
private void addParticipationToModel(MeetupMessageModel model, AUserInAServer aUserInAServer, MeetupDecision decision) {
|
||||
@Transactional
|
||||
public void updateMeetupMessage(ButtonClickedListenerModel model, MeetupMessageModel meetupMessageModel, Member member, MeetupDecisionPayload payload, Long meetupMessageId, Long meetupId, Long meetupServerId) {
|
||||
addParticipationToModel(meetupMessageModel, member, payload.getMeetupDecision());
|
||||
MessageToSend messageToSend = meetupServiceBean.getMeetupMessage(meetupMessageModel, model.getServerId());
|
||||
channelService.editMessageInAChannelFuture(messageToSend, model.getEvent().getChannel(), meetupMessageId)
|
||||
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetupMessageId, meetupServerId))
|
||||
.exceptionally(throwable -> {
|
||||
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetupMessageId, meetupServerId, throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void addParticipationToModel(MeetupMessageModel model, Member aUserInAServer, MeetupDecision decision) {
|
||||
if(decision.equals(MeetupDecision.NO)) {
|
||||
addIfMissing(model.getDeclinedParticipants(), aUserInAServer);
|
||||
} else if(decision.equals(MeetupDecision.YES)) {
|
||||
@@ -80,9 +95,9 @@ public class MeetupDecisionListener implements ButtonClickedListener {
|
||||
addIfMissing(model.getNoTimeParticipants(), aUserInAServer);
|
||||
}
|
||||
|
||||
private void addIfMissing(List<MemberDisplay> list, AUserInAServer aUserInAServer) {
|
||||
if(list.stream().noneMatch(memberDisplay -> memberDisplay.getUserId().equals(aUserInAServer.getUserReference().getId()))) {
|
||||
list.add(MemberDisplay.fromAUserInAServer(aUserInAServer));
|
||||
private void addIfMissing(List<MemberDisplay> list, Member aUserInAServer) {
|
||||
if(list.stream().noneMatch(memberDisplay -> memberDisplay.getUserId().equals(aUserInAServer.getIdLong()))) {
|
||||
list.add(MemberDisplay.fromMember(aUserInAServer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import dev.sheldan.abstracto.core.service.*;
|
||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||
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.core.utils.FileService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.scheduling.model.JobParameters;
|
||||
@@ -32,6 +33,8 @@ import dev.sheldan.sissi.module.meetup.service.management.MeetupComponentManagem
|
||||
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
|
||||
import dev.sheldan.sissi.module.meetup.service.management.MeetupParticipatorManagementServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -48,6 +51,8 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@@ -109,8 +114,16 @@ public class MeetupServiceBean {
|
||||
|
||||
@Autowired
|
||||
private MeetupComponentManagementServiceBean meetupComponentManagementServiceBean;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
private static final String ICS_TIME_STAMP_FORMAT = "yMMdd'T'kkmmss'Z'";
|
||||
private static final ZoneId UTC = ZoneId.of("UTC");
|
||||
private static final DateTimeFormatter ICS_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(ICS_TIME_STAMP_FORMAT);
|
||||
private static final Predicate<MeetupParticipant> MAYBE_OR_YES_PARTICIPATOR = meetupParticipator ->
|
||||
meetupParticipator.getDecision().equals(MeetupDecision.MAYBE) ||
|
||||
meetupParticipator.getDecision().equals(MeetupDecision.YES);
|
||||
|
||||
public void storeMeetupConfirmation(MeetupConfirmationModel model) {
|
||||
AServer server = serverManagementService.loadServer(model.getGuildId());
|
||||
@@ -142,11 +155,11 @@ public class MeetupServiceBean {
|
||||
}
|
||||
|
||||
private MeetupIcsModel getMeetupICSModel(Meetup meetup) {
|
||||
ZonedDateTime startTime = meetup.getMeetupTime().atZone(ZoneId.of("UTC"));
|
||||
ZonedDateTime endTime = meetup.getMeetupTime().plus(1, ChronoUnit.HOURS).atZone(ZoneId.of("UTC"));
|
||||
ZonedDateTime startTime = meetup.getMeetupTime().atZone(UTC);
|
||||
ZonedDateTime endTime = meetup.getMeetupTime().plus(1, ChronoUnit.HOURS).atZone(UTC);
|
||||
String icsFormattedStartTime = startTime.format(ICS_DATE_TIME_FORMATTER);
|
||||
String icsFormattedEndTime = endTime.format(ICS_DATE_TIME_FORMATTER);
|
||||
String icsFormattedMeetupCreationTime = Instant.now().atZone(ZoneId.of("UTC"))
|
||||
String icsFormattedMeetupCreationTime = Instant.now().atZone(UTC)
|
||||
.format(ICS_DATE_TIME_FORMATTER);
|
||||
boolean attachIcsFile = featureModeService.featureModeActive(MeetupFeatureDefinition.MEETUP, meetup.getServer().getId(), MeetupFeatureMode.ATTACH_ICS_FILE);
|
||||
return MeetupIcsModel
|
||||
@@ -158,26 +171,36 @@ public class MeetupServiceBean {
|
||||
.build();
|
||||
}
|
||||
|
||||
public MeetupMessageModel getMeetupMessageModel(Meetup meetup) {
|
||||
public CompletableFuture<MeetupMessageModel> getMeetupMessageModel(Meetup meetup) {
|
||||
List<MeetupParticipant> allParticipants = meetup.getParticipants();
|
||||
List<MeetupParticipant> participating = allParticipants
|
||||
Long serverId = meetup.getServer().getId();
|
||||
Function<MeetupParticipant, Long> mapToUserId = (p) -> p.getParticipator().getUserReference().getId();
|
||||
List<Long> participating = allParticipants
|
||||
.stream()
|
||||
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.YES))
|
||||
.collect(Collectors.toList());
|
||||
List<MeetupParticipant> maybe = allParticipants
|
||||
.map(mapToUserId)
|
||||
.toList();
|
||||
List<Long> maybe = allParticipants
|
||||
.stream()
|
||||
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.MAYBE))
|
||||
.collect(Collectors.toList());
|
||||
List<MeetupParticipant> notParticipating = allParticipants
|
||||
.map(mapToUserId)
|
||||
.toList();
|
||||
List<Long> notParticipating = allParticipants
|
||||
.stream()
|
||||
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.NO))
|
||||
.collect(Collectors.toList());
|
||||
List<MeetupParticipant> notTimeParticipating = allParticipants
|
||||
.map(mapToUserId)
|
||||
.toList();
|
||||
List<Long> notTimeParticipating = allParticipants
|
||||
.stream()
|
||||
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.NO_TIME))
|
||||
.collect(Collectors.toList());
|
||||
.map(mapToUserId)
|
||||
.toList();
|
||||
String rawLocation = java.net.URLDecoder.decode(meetup.getLocation(), StandardCharsets.UTF_8);
|
||||
return MeetupMessageModel
|
||||
List<Long> participantIds = allParticipants
|
||||
.stream()
|
||||
.map(mapToUserId)
|
||||
.collect(Collectors.toList());
|
||||
MeetupMessageModel.MeetupMessageModelBuilder builder = MeetupMessageModel
|
||||
.builder()
|
||||
.description(meetup.getDescription())
|
||||
.topic(meetup.getTopic())
|
||||
@@ -189,20 +212,63 @@ public class MeetupServiceBean {
|
||||
.noId(meetup.getNotInterestedButtonId())
|
||||
.meetupTime(meetup.getMeetupTime())
|
||||
.meetupId(meetup.getId().getId())
|
||||
.participants(getMemberDisplays(participating))
|
||||
.declinedParticipants(getMemberDisplays(notParticipating))
|
||||
.noTimeParticipants(getMemberDisplays(notTimeParticipating))
|
||||
.maybeParticipants(getMemberDisplays(maybe))
|
||||
.cancelled(meetup.getState().equals(MeetupState.CANCELLED))
|
||||
.organizer(MemberDisplay.fromAUserInAServer(meetup.getOrganizer()))
|
||||
.meetupIcsModel(getMeetupICSModel(meetup))
|
||||
.build();
|
||||
.meetupIcsModel(getMeetupICSModel(meetup));
|
||||
Long organizerId = meetup.getOrganizer().getUserReference().getId();
|
||||
participantIds.add(organizerId);
|
||||
// only supports 100 members at once, should be enough
|
||||
CompletableFuture<List<Member>> membersInServerAsync = memberService.getMembersInServerAsync(serverId, participantIds);
|
||||
return membersInServerAsync.thenCompose(members -> {
|
||||
Set<Long> foundMembers = members
|
||||
.stream()
|
||||
.map(Member::getIdLong)
|
||||
.collect(Collectors.toSet());
|
||||
Set<Long> participatingMembers = new HashSet<>(participantIds);
|
||||
participatingMembers.removeAll(foundMembers);
|
||||
CompletableFuture<List<User>> userLoading = new CompletableFuture<>();
|
||||
if(!participatingMembers.isEmpty()) {
|
||||
CompletableFutureList<User> userFutureList = userService.retrieveUsers(new ArrayList<>(participatingMembers));
|
||||
userFutureList.getMainFuture().thenAccept(unused -> {
|
||||
userLoading.complete(userFutureList.getObjects());
|
||||
});
|
||||
} else {
|
||||
userLoading.complete(new ArrayList<>());
|
||||
}
|
||||
|
||||
return userLoading.thenApply(users ->
|
||||
builder
|
||||
.participants(getMemberDisplays(members, users, participating, serverId))
|
||||
.declinedParticipants(getMemberDisplays(members, users, notParticipating, serverId))
|
||||
.noTimeParticipants(getMemberDisplays(members, users, notTimeParticipating, serverId))
|
||||
.maybeParticipants(getMemberDisplays(members, users, maybe, serverId))
|
||||
.organizer(getMemberDisplays(members, users, Arrays.asList(organizerId), serverId).get(0))
|
||||
.build());
|
||||
});
|
||||
}
|
||||
|
||||
private List<MemberDisplay> getMemberDisplays(List<MeetupParticipant> participants) {
|
||||
private List<MemberDisplay> getMemberDisplays(List<Member> members, List<User> users, List<Long> participants, Long serverId) {
|
||||
Map<Long, Member> memberMap = members.stream()
|
||||
.collect(Collectors.toMap(Member::getIdLong, Function.identity()));
|
||||
Map<Long, User> userMap = users.stream()
|
||||
.collect(Collectors.toMap(User::getIdLong, Function.identity()));
|
||||
return participants
|
||||
.stream()
|
||||
.map(meetupParticipator -> MemberDisplay.fromAUserInAServer(meetupParticipator.getParticipator()))
|
||||
.map(meetupParticipator -> {
|
||||
if(memberMap.containsKey(meetupParticipator)) {
|
||||
return MemberDisplay.fromMember(memberMap.get(meetupParticipator));
|
||||
} else if(userMap.containsKey(meetupParticipator)) {
|
||||
User user = userMap.get(meetupParticipator);
|
||||
// a user display would be more appropriate, but I dont want to deal with the implications
|
||||
return MemberDisplay
|
||||
.builder()
|
||||
.serverId(serverId)
|
||||
.userId(user.getIdLong())
|
||||
.name(user.getEffectiveName())
|
||||
.avatarUrl(user.getEffectiveAvatarUrl())
|
||||
.build();
|
||||
}
|
||||
return MemberDisplay.fromIds(serverId, meetupParticipator);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -214,40 +280,43 @@ public class MeetupServiceBean {
|
||||
Long serverId = meetup.getServer().getId();
|
||||
Long meetupId = meetup.getId().getId();
|
||||
GuildMessageChannel channel = channelService.getMessageChannelFromServer(serverId, meetup.getMeetupChannel().getId());
|
||||
MeetupMessageModel model = getMeetupMessageModel(meetup);
|
||||
List<String> componentPayloads = meetup
|
||||
.getMeetupComponents()
|
||||
.stream()
|
||||
.map(meetupComponent -> meetupComponent.getId().getComponentId())
|
||||
.collect(Collectors.toList());
|
||||
model.setCancelled(true);
|
||||
MessageToSend meetupMessage = getMeetupMessage(model, serverId);
|
||||
return messageService.editMessageInChannel(channel, meetupMessage, meetup.getMessageId())
|
||||
.thenAccept(unused -> self.notifyParticipants(meetupId, serverId))
|
||||
.thenAccept(unused -> self.cleanupMeetup(meetupId, serverId, componentPayloads));
|
||||
Long meetupMessageId = meetup.getMessageId();
|
||||
return getMeetupMessageModel(meetup).thenCompose(model -> {
|
||||
model.setCancelled(true);
|
||||
MessageToSend meetupMessage = getMeetupMessage(model, serverId);
|
||||
return messageService.editMessageInChannel(channel, meetupMessage, meetupMessageId)
|
||||
.thenAccept(unused -> self.notifyParticipants(meetupId, serverId))
|
||||
.thenAccept(unused -> self.cleanupMeetup(meetupId, serverId, componentPayloads));
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void notifyParticipants(Long meetupId, Long serverId) {
|
||||
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, serverId);
|
||||
MeetupMessageModel model = getMeetupMessageModel(meetup);
|
||||
List<Long> participatorIds = getYesOrMaybeParticipants(meetup);
|
||||
getMeetupMessageModel(meetup).thenAccept(model -> {
|
||||
self.sendNotifications(meetupId, serverId, model, participatorIds);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void sendNotifications(Long meetupId, Long serverId, MeetupMessageModel model, List<Long> participatorIds) {
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(MEETUP_CANCELLATION_TEMPLATE, model, serverId);
|
||||
meetup
|
||||
.getParticipants()
|
||||
.stream()
|
||||
.filter(meetupParticipator ->
|
||||
meetupParticipator.getDecision().equals(MeetupDecision.MAYBE) ||
|
||||
meetupParticipator.getDecision().equals(MeetupDecision.YES))
|
||||
.forEach(meetupParticipator -> {
|
||||
Long userId = meetupParticipator.getParticipator().getUserReference().getId();
|
||||
userService.retrieveUserForId(userId)
|
||||
.thenCompose(user -> messageService.sendMessageToSendToUser(user, messageToSend))
|
||||
.thenAccept(message -> log.info("Notified user {} about cancellation of meetup {} in server {}.", userId, meetupId, serverId))
|
||||
.exceptionally(throwable -> {
|
||||
log.warn("Failed to notify user {} about cancellation of meetup {} in server {}.", userId, meetupId, serverId);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
participatorIds.forEach(userId -> {
|
||||
userService.retrieveUserForId(userId)
|
||||
.thenCompose(user -> messageService.sendMessageToSendToUser(user, messageToSend))
|
||||
.thenAccept(message -> log.info("Notified user {} about cancellation of meetup {} in server {}.", userId, meetupId, serverId))
|
||||
.exceptionally(throwable -> {
|
||||
log.warn("Failed to notify user {} about cancellation of meetup {} in server {}.", userId, meetupId, serverId);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -318,20 +387,29 @@ public class MeetupServiceBean {
|
||||
@Transactional
|
||||
public void remindParticipants(Long meetupId, Long serverId) {
|
||||
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, serverId);
|
||||
MeetupMessageModel model = getMeetupMessageModel(meetup);
|
||||
List<Long> participatorIds = getYesOrMaybeParticipants(meetup);
|
||||
getMeetupMessageModel(meetup).thenAccept(model -> {
|
||||
self.sendMeetupReminderMessages(meetupId, serverId, model, participatorIds);
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void sendMeetupReminderMessages(Long meetupId, Long serverId, MeetupMessageModel model, List<Long> participatorIds) {
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(MEETUP_REMINDER_TEMPLATE, model, serverId);
|
||||
meetup
|
||||
participatorIds.forEach(userId -> {
|
||||
userService.retrieveUserForId(userId)
|
||||
.thenCompose(user -> messageService.sendMessageToSendToUser(user, messageToSend))
|
||||
.thenAccept(message -> log.info("Notified user {} about incoming meetup {} in server {}.", userId, meetupId, serverId));
|
||||
});
|
||||
}
|
||||
|
||||
private List<Long> getYesOrMaybeParticipants(Meetup meetup) {
|
||||
return meetup
|
||||
.getParticipants()
|
||||
.stream()
|
||||
.filter(meetupParticipator ->
|
||||
meetupParticipator.getDecision().equals(MeetupDecision.MAYBE) ||
|
||||
meetupParticipator.getDecision().equals(MeetupDecision.YES))
|
||||
.forEach(meetupParticipator -> {
|
||||
Long userId = meetupParticipator.getParticipator().getUserReference().getId();
|
||||
userService.retrieveUserForId(userId)
|
||||
.thenCompose(user -> messageService.sendMessageToSendToUser(user, messageToSend))
|
||||
.thenAccept(message -> log.info("Notified user {} about incoming meetup {} in server {}.", userId, meetupId, serverId));
|
||||
});
|
||||
.filter(MAYBE_OR_YES_PARTICIPATOR)
|
||||
.map(meetupParticipant -> meetupParticipant.getParticipator().getUserReference().getId())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -376,11 +454,12 @@ public class MeetupServiceBean {
|
||||
|
||||
Long serverId = meetup.getServer().getId();
|
||||
|
||||
Long meetupMessageId = meetup.getMessageId();
|
||||
ServerChannelMessage meetupMessage = ServerChannelMessage
|
||||
.builder()
|
||||
.serverId(serverId)
|
||||
.channelId(meetup.getMeetupChannel().getId())
|
||||
.messageId(meetup.getMessageId())
|
||||
.messageId(meetupMessageId)
|
||||
.build();
|
||||
MeetupTimeChangedNotificationModel notificationModel = MeetupTimeChangedNotificationModel
|
||||
.builder()
|
||||
@@ -418,18 +497,24 @@ public class MeetupServiceBean {
|
||||
.toList();
|
||||
meetup
|
||||
.getParticipants().removeIf(meetupParticipant -> userInServerIds.contains(meetupParticipant.getParticipator().getUserInServerId()));
|
||||
MeetupMessageModel meetupMessageModel = getMeetupMessageModel(meetup);
|
||||
Long meetupChannelId = meetup.getMeetupChannel().getId();
|
||||
return getMeetupMessageModel(meetup).thenCompose(meetupMessageModel ->
|
||||
self.changeMeetupTimeInternal(meetupMessageModel, serverId, meetupChannelId, meetupMessageId, meetupId));
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> changeMeetupTimeInternal(MeetupMessageModel meetupMessageModel, Long serverId, Long meetupChannelId, Long meetupMessageId, Long meetupId) {
|
||||
meetupMessageModel.setParticipants(new ArrayList<>());
|
||||
meetupMessageModel.setMaybeParticipants(new ArrayList<>());
|
||||
meetupMessageModel.setNoTimeParticipants(new ArrayList<>());
|
||||
|
||||
MessageToSend updatedMeetupMessage = getMeetupMessage(meetupMessageModel, serverId);
|
||||
GuildMessageChannel meetupChannel = channelService.getMessageChannelFromServer(serverId, meetup.getMeetupChannel().getId());
|
||||
return channelService.editMessageInAChannelFuture(updatedMeetupMessage, meetupChannel, meetup.getMessageId())
|
||||
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId))
|
||||
GuildMessageChannel meetupChannel = channelService.getMessageChannelFromServer(serverId, meetupChannelId);
|
||||
return channelService.editMessageInAChannelFuture(updatedMeetupMessage, meetupChannel, meetupMessageId)
|
||||
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetupChannelId, serverId))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(updatedMeetupMessage.getAttachedFiles().get(0).getFile()))
|
||||
.exceptionally(throwable -> {
|
||||
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId, throwable);
|
||||
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetupChannelId, serverId, throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@@ -456,14 +541,21 @@ public class MeetupServiceBean {
|
||||
private CompletableFuture<Void> updateMeetupMessage(Meetup meetup) {
|
||||
Long meetupId = meetup.getId().getId();
|
||||
Long serverId = meetup.getId().getServerId();
|
||||
MeetupMessageModel meetupMessageModel = getMeetupMessageModel(meetup);
|
||||
Long meetupChannelId = meetup.getMeetupChannel().getId();
|
||||
Long meetupMessageId = meetup.getMessageId();
|
||||
return getMeetupMessageModel(meetup).thenCompose(meetupMessageModel ->
|
||||
self.updateMessageInternal(meetupMessageModel, serverId, meetupChannelId, meetupMessageId, meetupId));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> updateMessageInternal(MeetupMessageModel meetupMessageModel, Long serverId, Long meetupChannelId, Long meetupMessageId, Long meetupId) {
|
||||
MessageToSend updatedMeetupMessage = getMeetupMessage(meetupMessageModel, serverId);
|
||||
GuildMessageChannel meetupChannel = channelService.getMessageChannelFromServer(serverId, meetup.getMeetupChannel().getId());
|
||||
return channelService.editMessageInAChannelFuture(updatedMeetupMessage, meetupChannel, meetup.getMessageId())
|
||||
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId))
|
||||
GuildMessageChannel meetupChannel = channelService.getMessageChannelFromServer(serverId, meetupChannelId);
|
||||
return channelService.editMessageInAChannelFuture(updatedMeetupMessage, meetupChannel, meetupMessageId)
|
||||
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetupChannelId, serverId))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(updatedMeetupMessage.getAttachedFiles().get(0).getFile()))
|
||||
.exceptionally(throwable -> {
|
||||
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId, throwable);
|
||||
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetupChannelId, serverId, throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>sissi-modules</artifactId>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,19 +5,17 @@ import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
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.core.utils.FutureUtils;
|
||||
import dev.sheldan.sissi.module.miepscord.weeklytext.config.WeeklyTextPostTarget;
|
||||
import dev.sheldan.sissi.module.miepscord.weeklytext.model.database.TextItem;
|
||||
import dev.sheldan.sissi.module.miepscord.weeklytext.model.template.TextItemPostModel;
|
||||
import dev.sheldan.sissi.module.miepscord.weeklytext.service.management.TextItemServiceManagementBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@@ -66,9 +64,7 @@ public class TextItemServiceBean {
|
||||
.text(textItem.getText())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(WEEKLY_TEXT_ITEM_POST_TEMPLATE, model, serverId);
|
||||
List<CompletableFuture<Message>> futures = postTargetService.sendEmbedInPostTarget(messageToSend, WeeklyTextPostTarget.TEXT_ITEM_TARGET, serverId);
|
||||
CompletableFutureList<Message> futureList = new CompletableFutureList<>(futures);
|
||||
return futureList.getMainFuture().thenAccept(unused -> {
|
||||
return FutureUtils.toSingleFutureGenericList(postTargetService.sendEmbedInPostTarget(messageToSend, WeeklyTextPostTarget.TEXT_ITEM_TARGET, serverId)).thenAccept(unused -> {
|
||||
self.setTexItemToDone(textItemId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<artifactId>application</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<artifactId>sissi-modules</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>sissi-modules</artifactId>
|
||||
<groupId>dev.sheldan.sissi.application</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ apiVersion: v2
|
||||
name: sissi
|
||||
description: A Helm chart for Kubernetes
|
||||
type: application
|
||||
version: 1.5.13
|
||||
version: 1.5.19
|
||||
|
||||
@@ -60,7 +60,7 @@ spec:
|
||||
- name: DB_SCHEMA
|
||||
value: {{ .Values.dbCredentials.schema }}
|
||||
- name: DEBRA_DONATION_NOTIFICATION_SERVER_ID
|
||||
value: "297910194841583616"
|
||||
value: "{{ .Values.bot.config.debraNotificationServerId }}"
|
||||
- name: WEEKLY_TEXT_SERVER_ID
|
||||
value: "{{ .Values.bot.config.weeklyTextServerId }}"
|
||||
- name: TOKEN
|
||||
|
||||
@@ -3,7 +3,7 @@ bot:
|
||||
repository: harbor.sheldan.dev/sissi
|
||||
pullPolicy: Always
|
||||
image: sissi-bot
|
||||
tag: 1.5.13
|
||||
tag: 1.5.19
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 5
|
||||
@@ -17,12 +17,13 @@ bot:
|
||||
host: null
|
||||
config:
|
||||
weeklyTextServerId: null
|
||||
debraNotificationServerId: null
|
||||
restApi:
|
||||
enabled: true
|
||||
repository: harbor.sheldan.dev/sissi
|
||||
pullPolicy: Always
|
||||
image: sissi-rest-api
|
||||
tag: 1.5.13
|
||||
tag: 1.5.19
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
@@ -62,7 +63,7 @@ privateRestApi:
|
||||
repository: harbor.sheldan.dev/sissi
|
||||
pullPolicy: Always
|
||||
image: sissi-private-rest-api
|
||||
tag: 1.5.13
|
||||
tag: 1.5.19
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
@@ -93,23 +94,23 @@ templateDeployment:
|
||||
repository: harbor.sheldan.dev/abstracto
|
||||
pullPolicy: Always
|
||||
image: abstracto-template-deployment
|
||||
tag: 1.6.15
|
||||
tag: 1.6.19
|
||||
templateDeploymentData:
|
||||
repository: harbor.sheldan.dev/sissi
|
||||
pullPolicy: Always
|
||||
image: sissi-template-data
|
||||
tag: 1.5.13
|
||||
tag: 1.5.19
|
||||
dbConfigDeployment:
|
||||
enabled: true
|
||||
repository: harbor.sheldan.dev/abstracto
|
||||
pullPolicy: Always
|
||||
image: abstracto-db-deployment
|
||||
tag: 1.6.15
|
||||
tag: 1.6.19
|
||||
dbConfigDeploymentData:
|
||||
repository: harbor.sheldan.dev/sissi
|
||||
pullPolicy: Always
|
||||
image: sissi-db-data
|
||||
tag: 1.5.13
|
||||
tag: 1.5.19
|
||||
dbCredentials:
|
||||
host: null
|
||||
port: null
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi</groupId>
|
||||
<artifactId>deployment</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi</groupId>
|
||||
<artifactId>sissi</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
9
pom.xml
9
pom.xml
@@ -13,15 +13,16 @@
|
||||
<groupId>dev.sheldan.sissi</groupId>
|
||||
<artifactId>sissi</artifactId>
|
||||
<name>Sissi</name>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<abstracto.version>1.6.15</abstracto.version>
|
||||
<abstracto.templates.version>1.4.61</abstracto.templates.version>
|
||||
<abstracto.version>1.6.19</abstracto.version>
|
||||
<abstracto.templates.version>1.4.65</abstracto.templates.version>
|
||||
<apache-jena.version>4.9.0</apache-jena.version>
|
||||
<rssreader.version>3.5.0</rssreader.version>
|
||||
<jsoup.version>1.21.2</jsoup.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
@@ -72,7 +73,7 @@
|
||||
<connection>scm:git:${project.scm.url}</connection>
|
||||
<developerConnection>scm:git:${project.scm.url}</developerConnection>
|
||||
<url>https://github.com/Sheldan/Sissi.git</url>
|
||||
<tag>sissi-1.5.14</tag>
|
||||
<tag>sissi-1.5.20</tag>
|
||||
</scm>
|
||||
|
||||
</project>
|
||||
@@ -239,7 +239,7 @@ def rendering_donation_image(donation_stats, parameters):
|
||||
height = parameters.font_size
|
||||
it = 0
|
||||
for donation in donations_to_draw:
|
||||
name = donation['firstName'] if not donation['anonymous'] else 'anonym'
|
||||
name = donation['name'] if not donation['anonymous'] and 'name' in donation else 'anonym'
|
||||
d1.text((0, height * it), f"{donation['donationAmount']}€ von {name}", fill=parameters.color, font=font)
|
||||
it += 1
|
||||
return flask_utils.serve_pil_image(img)
|
||||
|
||||
76
templates/custom/user_leave_embed.ftl
Normal file
76
templates/custom/user_leave_embed.ftl
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
<#assign mapping={
|
||||
'bp': '340380695585095680',
|
||||
'np': '400642855682572288',
|
||||
'bk': '299162473775366144',
|
||||
'mi': '400642854248120330',
|
||||
'na': '400642590267146240',
|
||||
'br': '400642587620278273',
|
||||
'lo': '400643490020851712',
|
||||
'lr': '400642584617418753',
|
||||
'lm': '400642579881787402',
|
||||
'bo': '400642539889229825',
|
||||
'bm': '299162333668835328',
|
||||
'gm': '400642460713484308',
|
||||
'up': '299162129284726784',
|
||||
'ap': '336786889589915659',
|
||||
'dd': '336786578103992323',
|
||||
'dr': '298790326943088670',
|
||||
'ms': '298790213453479937',
|
||||
'bs': '298790020192403456',
|
||||
'di': '298790014974820356',
|
||||
'in': '298789520084566018'
|
||||
}>
|
||||
<#assign groups={
|
||||
'1': {'bp': 1, 'np': 2, 'bk': 3, 'mi': 4, 'na': 5, 'br': 6, 'lo': 7, 'lr': 8, 'lm': 9, 'bo': 10, 'bm': 11, 'gm': 12},
|
||||
'2': {'up': 1, 'ap': 2},
|
||||
'3': {'dd': 1, 'dr': 2},
|
||||
'4': {'ms': 1, 'bs': 2},
|
||||
'5': {'di': 1},
|
||||
'6': {'in': 1}
|
||||
}>
|
||||
<#assign allowedRoleIds=[]>
|
||||
<#assign reverseMapping={}>
|
||||
<#list mapping as key, value>
|
||||
<#assign allowedRoleIds=allowedRoleIds + [value]>
|
||||
<#assign reverseMapping=reverseMapping + {value: key}>
|
||||
</#list>
|
||||
<#assign relevantRoles={}>
|
||||
<#assign relevantRoleIds=[]>
|
||||
<#list roles as role>
|
||||
<#if allowedRoleIds?seq_contains(role.roleId?c)>
|
||||
<#assign relevantRoles=relevantRoles + {role.roleId?c:role.roleName}>
|
||||
<#assign relevantRoleIds=relevantRoleIds+[role.roleId?c]>
|
||||
</#if>
|
||||
</#list>
|
||||
<#function is_printed role_id>
|
||||
<#assign group={}>
|
||||
<#assign found_prio=1>
|
||||
<#list groups as key,members>
|
||||
<#list members as roleshortcut,prio>
|
||||
<#if role_id=mapping[roleshortcut]>
|
||||
<#assign group=members>
|
||||
<#assign found_prio=prio>
|
||||
</#if>
|
||||
</#list>
|
||||
</#list>
|
||||
<#assign print=true>
|
||||
<#list group as shortcut,prio>
|
||||
<#if relevantRoleIds?seq_contains(mapping[shortcut])>
|
||||
<#if prio < found_prio>
|
||||
<#assign print=false>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<#return print>
|
||||
</#function>
|
||||
<#macro get_title key>
|
||||
<#assign role_id=mapping[key]><#rt>
|
||||
<#if is_printed(role_id) && relevantRoleIds?seq_contains(role_id)><#rt>
|
||||
${relevantRoles[role_id]}<#rt>
|
||||
</#if>
|
||||
</#macro>
|
||||
<#include "user_detail">
|
||||
<#assign userText><@get_title 'bp'/><@get_title 'np'/><@get_title 'bk'/><@get_title 'mi'/><@get_title 'na'/><@get_title 'br'/><@get_title 'lo'/><@get_title 'lr'/><@get_title 'lm'/><@get_title 'bo'/><@get_title 'bm'/><@get_title 'gm'/><@get_title 'up'/><@get_title 'ap'/><@get_title 'dd'/><@get_title 'dr'/><@get_title 'di'/><@get_title 'in'/> ${user.name} <@get_title 'ms'/><@get_title 'bs'/> (${user.id?c})</#assign>
|
||||
"additionalMessage": "<#compress><@safe_include "user_left_text"/></#compress>"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi</groupId>
|
||||
<artifactId>sissi</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>customization-templates</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>image-generation-customization-templates</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>customization-templates</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>sissi-templates</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>module-templates</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
,"fields": [
|
||||
<#list donations as donation>
|
||||
{
|
||||
"name": "<#if donation.anonymous><#include "donations_response_anonymous"><#else>${donation.firstName}</#if>",
|
||||
"name": "<#if donation.anonymous><#include "donations_response_anonymous"><#else>${donation.name}</#if>",
|
||||
"value": "${donation.donationAmount}€",
|
||||
"inline": true
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
"embeds": [
|
||||
{
|
||||
"title": {
|
||||
<#assign donatorName=donation.donatorName>
|
||||
<#assign donatorName><#if donation.anonymous><#include "donations_response_anonymous"><#else>${donation.donatorName}</#if></#assign>
|
||||
<#assign donationAmount=donation.amount>
|
||||
"title": "<@safe_include "debra_donation_notification_embed_title"/>"
|
||||
},
|
||||
<#assign donationMessage=donation.message>
|
||||
"description": "${donationMessage?json_string}",
|
||||
<#if donation.message != 'gespendet'>
|
||||
<#assign donationMessage=donation.message>
|
||||
"description": "${donationMessage?json_string}",
|
||||
</#if>
|
||||
"fields": [
|
||||
{
|
||||
<#assign totalDonationAmount=totalDonationAmount>
|
||||
@@ -24,7 +26,7 @@
|
||||
"buttons": [
|
||||
{
|
||||
"label": "<@safe_include "debra_donation_notification_link_button_label"/>",
|
||||
"url": "http://tiny.cc/schmetterling2024",
|
||||
"url": "https://tinyurl.com/debra25",
|
||||
"buttonStyle": "link",
|
||||
"metaConfig": {
|
||||
"persistCallback": false
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
<#assign userPing="<@186558865851154435>">
|
||||
"additionalMessage": "${userPing}",
|
||||
"embeds": [
|
||||
{
|
||||
"title": {
|
||||
<#assign donatorName><#if donation.anonymous><#include "donations_response_anonymous"><#else>${donation.donatorName}</#if></#assign>
|
||||
<#assign donationAmount=donation.amount>
|
||||
"title": "<@safe_include "debra_donation_notification_embed_title"/>"
|
||||
},
|
||||
<#if donation.message != 'gespendet'>
|
||||
<#assign donationMessage=donation.message>
|
||||
"description": "${donationMessage?json_string}",
|
||||
</#if>
|
||||
"fields": [
|
||||
{
|
||||
<#assign totalDonationAmount=totalDonationAmount>
|
||||
"name": "<@safe_include "debra_donation_notification_embed_field_amount_title"/>",
|
||||
"value": "<@safe_include "debra_donation_notification_embed_field_amount_value"/>"
|
||||
}
|
||||
],
|
||||
"imageUrl": "https://cdn.discordapp.com/attachments/299115929206390784/1047306670319079474/dotpict-1.png",
|
||||
"footer": {
|
||||
"text": "<@safe_include "debra_donation_notification_embed_footer"/>"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buttons": [
|
||||
{
|
||||
"label": "<@safe_include "debra_donation_notification_link_button_label"/>",
|
||||
"url": "https://tinyurl.com/debra25",
|
||||
"buttonStyle": "link",
|
||||
"metaConfig": {
|
||||
"persistCallback": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>module-templates</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<#include "format_instant">
|
||||
{
|
||||
<#macro display_user member_to_display><#if member_to_display.name??>${member_to_display.name}<#else>${member_to_display.memberMention}</#if></#macro>
|
||||
"components": [
|
||||
{
|
||||
"type": "textDisplay",
|
||||
<#assign roleMention="<@&371419588619141121>"/>
|
||||
"content": "<#if cancelled>~~</#if>${roleMention?json_string} ${topic?json_string} - <@safe_include "meetup_message_id_display"/><#if cancelled>~~</#if>"
|
||||
},
|
||||
<#if description?has_content>
|
||||
{
|
||||
<#assign descriptionText>${description?json_string}</#assign>
|
||||
<#assign organizerText>${organizer.memberMention}</#assign>
|
||||
<#assign descriptionText><#if description?has_content>${description?json_string}<#else><@safe_include "meetup_description_no_description"/></#if></#assign>
|
||||
<#assign organizerText><@display_user member_to_display=organizer/></#assign>
|
||||
"type": "textDisplay",
|
||||
"content": "<#if cancelled>~~</#if><@safe_include "meetup_description_component"/><#if cancelled>~~</#if>"
|
||||
},
|
||||
</#if>
|
||||
{
|
||||
<#assign time><@format_instant_long_date_time instant=meetupTime/></#assign>
|
||||
<#assign timeRelative><@format_instant_relative instant=meetupTime/></#assign>
|
||||
@@ -54,10 +53,10 @@
|
||||
}
|
||||
}
|
||||
</#macro>
|
||||
<#assign participantsText> (${participants?size}) <#list participants as member>${member.memberMention}<#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
<#assign maybeParticipantsText> (${maybeParticipants?size}) <#list maybeParticipants as member>${member.memberMention}<#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
<#assign noTimeParticipantsText> (${noTimeParticipants?size}) <#list noTimeParticipants as member>${member.memberMention}<#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
<#assign declinedParticipantsText> (${declinedParticipants?size}) <#list declinedParticipants as member>${member.memberMention}<#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
<#assign participantsText> (${participants?size}) <#list participants as member><@display_user member_to_display=member/><#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
<#assign maybeParticipantsText> (${maybeParticipants?size}) <#list maybeParticipants as member><@display_user member_to_display=member/><#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
<#assign noTimeParticipantsText> (${noTimeParticipants?size}) <#list noTimeParticipants as member><@display_user member_to_display=member/><#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
<#assign declinedParticipantsText> (${declinedParticipants?size}) <#list declinedParticipants as member><@display_user member_to_display=member/><#sep>, </#sep><#else><#include "meetup_message_no_member"></#list></#assign>
|
||||
,<@decision_component yesId "success" "meetup_message_yes_button_label" "meetup_user_display_participants" participantsText/>
|
||||
,<@decision_component maybeId "secondary" "meetup_message_maybe_button_label" "meetup_user_display_maybe_participants" maybeParticipantsText/>
|
||||
,<@decision_component noId "danger" "meetup_message_no_button_label" "meetup_user_display_declined_participants" declinedParticipantsText/>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>module-templates</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>miepscord-templates</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>sissi-templates</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>module-templates</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>module-templates</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>templates</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>template-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>template-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
<#assign userMention>${member.asMention}</#assign>
|
||||
<#assign user>${member.user.name} (${member.asMention}: ${member.id})</#assign>
|
||||
<#assign userText>${member.user.name} (${member.asMention}: ${member.id?c})</#assign>
|
||||
"additionalMessage": "<@safe_include "user_joined_text"/>"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
<#include "user_detail">
|
||||
<#assign user><@user_detail user=user/></#assign>
|
||||
<#assign userText>${user.name} (${user.userMention}: ${user.id?c})</#assign>
|
||||
"additionalMessage": "<@safe_include "user_left_text"/>"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>template-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>sissi-templates</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>template-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<artifactId>customization-translations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>sissi-translations</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>module-translations</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
Aktuell wurden **${donationAmount} Euro** für Debra Austria gespendet. Spende auch du unter https://tiny.cc/schmetterling2024.
|
||||
Aktuell wurden **${donationAmount} Euro** für Debra Austria gespendet. Spende auch du unter https://tinyurl.com/debra25.
|
||||
@@ -10,7 +10,7 @@ Alle Grafiken von diesem und den letzten Jahren findest du in diesem Ordner. All
|
||||
|
||||
Spendenlink
|
||||
Wir empfehlen neben dem Einrichten einer 'Kachel' auch, dass ihr einen !spenden Befehl bei eurem Bot (nightbot, moobot, self hosted etc.) hinzufügt.
|
||||
Bitte verlinke dabei auf <https://tiny.cc/schmetterling2024>
|
||||
Bitte verlinke dabei auf <https://tinyurl.com/debra25>
|
||||
|
||||
Live-ping am Discord
|
||||
Damit wir deinen Account zur Going Live Liste hinzufügen können, musst du uns nach der Einrichtung des Streams bescheid geben.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Spende auch du für Debra Austria unter http://tiny.cc/schmetterling2024.
|
||||
Spende auch du für Debra Austria unter https://tinyurl.com/debra25.
|
||||
@@ -0,0 +1 @@
|
||||
The latest donations
|
||||
@@ -0,0 +1 @@
|
||||
The highest donations
|
||||
@@ -1 +1 @@
|
||||
Also donate for Debra austria via http://tiny.cc/schmetterling2024.
|
||||
Also donate for Debra austria via https://tinyurl.com/debra25.
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<artifactId>module-translations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
No description
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<artifactId>module-translations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>miepscord-translations</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<artifactId>sissi-translations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<artifactId>module-translations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>module-translations</artifactId>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates</groupId>
|
||||
<artifactId>templates</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations.overrides</groupId>
|
||||
<artifactId>translation-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations.overrides</groupId>
|
||||
<artifactId>translation-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations.overrides</groupId>
|
||||
<artifactId>translation-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
Dere, pfiatdi und baba, ${user}!
|
||||
Dere, pfiatdi und baba, ${userText}!
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations.overrides</groupId>
|
||||
<artifactId>translation-overrides</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.sissi.templates.translations</groupId>
|
||||
<artifactId>sissi-translations</artifactId>
|
||||
<version>1.5.14</version>
|
||||
<version>1.5.20</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user