Timelock weiter gebastelt

This commit is contained in:
2026-03-21 17:46:08 +01:00
parent 173302e0ee
commit 53e7bcbbcc
84 changed files with 8688 additions and 2225 deletions

View File

@@ -1,5 +1,5 @@
#Fri Mar 20 15:47:06 CET 2026 #Sat Mar 21 08:14:24 CET 2026
display=\:0 display=\:0
host=Mario-Linux host=Mario-Linux
process-id=112524 process-id=23243
user=mario user=mario

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,7 @@
[ { [ {
"version" : "9.5.0-20260320031124+0000", "version" : "9.6.0-20260321035213+0000",
"buildTime" : "20260320031124+0000", "buildTime" : "20260321035213+0000",
"commitId" : "97faa73152fb6d4ea37edf6b3f7590dcbce8b952", "commitId" : "cc9b7c946cf24a57b2119d39f1a33cf5493ea930",
"current" : false,
"snapshot" : true,
"nightly" : false,
"releaseNightly" : true,
"activeRc" : false,
"rcFor" : "",
"milestoneFor" : "",
"broken" : false,
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260320031124+0000-bin.zip",
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260320031124+0000-bin.zip.sha256",
"checksum" : "d28982b60bd15c7f3e13032152fc384f30465713b9c439bd3e159ad758461393",
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260320031124+0000-wrapper.jar.sha256",
"wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a"
}, {
"version" : "9.6.0-20260319194115+0000",
"buildTime" : "20260319194115+0000",
"commitId" : "eaac62111b6cbb05984176b52e4be56d5249ebf8",
"current" : false, "current" : false,
"snapshot" : true, "snapshot" : true,
"nightly" : true, "nightly" : true,
@@ -27,10 +10,27 @@
"rcFor" : "", "rcFor" : "",
"milestoneFor" : "", "milestoneFor" : "",
"broken" : false, "broken" : false,
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260319194115+0000-bin.zip", "downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260321035213+0000-bin.zip",
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260319194115+0000-bin.zip.sha256", "checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260321035213+0000-bin.zip.sha256",
"checksum" : "a72c6e0f1a5ecc7d81768c65a5bdcd8f0af37ba5b05c83df4c45d08b8ce79fce", "checksum" : "e533696ad1e80c2878ed39c18b5252ac7e0bad6394ead2b93663656cd6591059",
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260319194115+0000-wrapper.jar.sha256", "wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260321035213+0000-wrapper.jar.sha256",
"wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a"
}, {
"version" : "9.5.0-20260321014114+0000",
"buildTime" : "20260321014114+0000",
"commitId" : "e7d13113f033cc35f5b8c7e0eeb88906259a1410",
"current" : false,
"snapshot" : true,
"nightly" : false,
"releaseNightly" : true,
"activeRc" : false,
"rcFor" : "",
"milestoneFor" : "",
"broken" : false,
"downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260321014114+0000-bin.zip",
"checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260321014114+0000-bin.zip.sha256",
"checksum" : "eeb50f4468d73f74a68fe62a16d371ccc7c54088d7d2f672adb12c4bce4104d5",
"wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260321014114+0000-wrapper.jar.sha256",
"wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a" "wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a"
}, { }, {
"version" : "9.4.1", "version" : "9.4.1",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,14 +4,13 @@ INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.ec
176453541.index 176453541.index
677104696.index 677104696.index
341080888.index 341080888.index
4134502745.index
774576701.index 774576701.index
4134502745.index
41199409.index 41199409.index
2217896880.index 2217896880.index
134995224.index 134995224.index
4025319337.index 4025319337.index
900586112.index 900586112.index
9341915.index
2929476459.index 2929476459.index
2065500052.index 2065500052.index
3051047092.index 3051047092.index
@@ -28,8 +27,8 @@ INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.ec
2032345814.index 2032345814.index
3839581777.index 3839581777.index
2466743981.index 2466743981.index
673436610.index
13999064.index 13999064.index
673436610.index
3972616808.index 3972616808.index
1914043487.index 1914043487.index
3154281632.index 3154281632.index
@@ -45,10 +44,10 @@ INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.ec
4020783879.index 4020783879.index
2900482015.index 2900482015.index
3059431983.index 3059431983.index
13156219.index
833027591.index 833027591.index
4088356365.index 13156219.index
37241354.index 37241354.index
4088356365.index
1295630681.index 1295630681.index
2701419231.index 2701419231.index
3939420913.index 3939420913.index
@@ -56,8 +55,8 @@ INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.ec
1318022262.index 1318022262.index
773718761.index 773718761.index
2311226047.index 2311226047.index
1865797976.index
3539841425.index 3539841425.index
1865797976.index
2455962971.index 2455962971.index
2576972120.index 2576972120.index
2389383899.index 2389383899.index

View File

@@ -12,11 +12,17 @@
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.GreenCard"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.GreenCard"/>
<fullyQualifiedTypeName name="java.util.Random"/> <fullyQualifiedTypeName name="java.util.Random"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.CardLockService"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.CardLockService"/>
<fullyQualifiedTypeName name="jakarta.persistence.Column"/>
<fullyQualifiedTypeName name="java.time.LocalDateTime"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.history.LockHistoryRepository"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.history.LockHistoryRepository"/>
<fullyQualifiedTypeName name="lombok.Getter"/>
<fullyQualifiedTypeName name="lombok.Setter"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.Test"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.Test"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.KeyholderCardLock"/> <fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.KeyholderCardLock"/>
<fullyQualifiedTypeName name="lombok.Getter"/>
<fullyQualifiedTypeName name="lombok.Setter"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.timelock.TimeLockService"/>
<fullyQualifiedTypeName name="jakarta.persistence.Column"/>
<fullyQualifiedTypeName name="java.time.temporal.ChronoUnit"/>
<fullyQualifiedTypeName name="java.time.LocalDateTime"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.AbstractLockService"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.timelock.TimeLockEntity"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.timelock.TimeLockRepository"/>
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.verification.VerificationVoteRepository"/>
</qualifiedTypeNameHistroy> </qualifiedTypeNameHistroy>

View File

@@ -28,6 +28,8 @@
<item key="updateTextualMatches" value="false"/> <item key="updateTextualMatches" value="false"/>
<item key="updateQualifiedNames" value="false"/> <item key="updateQualifiedNames" value="false"/>
<item key="patterns" value="*"/> <item key="patterns" value="*"/>
<item key="updateSimilarElements" value="false"/>
<item key="updateSimilarElementsMatchStrategy" value="1"/>
</section> </section>
<section name="org.eclipse.jdt.internal.ui.dialogs.OpenTypeSelectionDialog2"> <section name="org.eclipse.jdt.internal.ui.dialogs.OpenTypeSelectionDialog2">
<item key="ShowStatusLine" value="true"/> <item key="ShowStatusLine" value="true"/>
@@ -81,4 +83,11 @@
<section name="NewPackageWizardPage"> <section name="NewPackageWizardPage">
<item key="create_package_info_java" value="false"/> <item key="create_package_info_java" value="false"/>
</section> </section>
<section name="NewEnumCreationWizard.dialogBounds">
<item key="DIALOG_X_ORIGIN" value="20"/>
<item key="DIALOG_Y_ORIGIN" value="20"/>
<item key="DIALOG_WIDTH" value="631"/>
<item key="DIALOG_HEIGHT" value="577"/>
<item key="DIALOG_FONT_NAME" value="1|Ubuntu Sans|11.0|0|GTK|1|"/>
</section>
</section> </section>

View File

@@ -31,3 +31,7 @@
2026-03-20 10:34:17,517 [Worker-5: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. 2026-03-20 10:34:17,517 [Worker-5: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
2026-03-20 12:08:01,037 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. 2026-03-20 12:08:01,037 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
2026-03-20 15:47:08,644 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. 2026-03-20 15:47:08,644 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
2026-03-20 16:26:08,485 [Worker-7: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
2026-03-21 08:03:15,921 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update.
2026-03-21 08:10:51,183 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.
2026-03-21 08:14:27,502 [Worker-8: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read.

View File

@@ -6,4 +6,5 @@
<workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1772786688998_3" label="Window Working Set" name="Aggregate for window 1772786688997"/> <workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1772786688998_3" label="Window Working Set" name="Aggregate for window 1772786688997"/>
<workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1772792014092_4" label="Window Working Set" name="Aggregate for window 1772792014092"/> <workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1772792014092_4" label="Window Working Set" name="Aggregate for window 1772792014092"/>
<workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1773603878078_5" label="Window Working Set" name="Aggregate for window 1773600542055"/> <workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1773603878078_5" label="Window Working Set" name="Aggregate for window 1773600542055"/>
<workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1774020460693_6" label="Window Working Set" name="Aggregate for window 1774020460693"/>
</workingSetManager> </workingSetManager>

View File

@@ -1,3 +1,3 @@
#Fri Mar 20 15:47:06 CET 2026 #Sat Mar 21 08:14:24 CET 2026
org.eclipse.core.runtime=2 org.eclipse.core.runtime=2
org.eclipse.platform=4.39.0.v20260226-0420 org.eclipse.platform=4.39.0.v20260226-0420

BIN
bilder/card_cum.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

BIN
bilder/card_cum_caged.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

View File

@@ -8,6 +8,8 @@ Verknüpfung der Spiele, wenn das Finish eines aus dem Bereich Chastity ist, wir
Wenn ein Lock für einen User existiert, wird er ohne entsprechend Penis/Vagina zu dem Spiel erstellt. Wenn ein Lock für einen User existiert, wird er ohne entsprechend Penis/Vagina zu dem Spiel erstellt.
TODO: Im Time Lock, wenn im Spinning Wheel tasks drin sind, dürfen keine sonst keine Tasks gefordert sein und umgekehrt
Ich kann Spieler einladen zu spielen, dann kriegt die Person eine E-Mail und muss bestätigen, dass es diese PErson ist, sie wird dann ins spiel übernommen Ich kann Spieler einladen zu spielen, dann kriegt die Person eine E-Mail und muss bestätigen, dass es diese PErson ist, sie wird dann ins spiel übernommen
-- Falls fall mit Chastity auftritt wird die Spielpartnerin als Keyholder eingetragen, diese Person darf entscheiden, was für ein Lock das wird. -- Falls fall mit Chastity auftritt wird die Spielpartnerin als Keyholder eingetragen, diese Person darf entscheiden, was für ein Lock das wird.

View File

@@ -138,8 +138,8 @@ public class BdsmGameService {
newLock.setTestLock(false); newLock.setTestLock(false);
newLock.setTaskCardMode(template.getTaskCardMode()); newLock.setTaskCardMode(template.getTaskCardMode());
int codeLines = template.getUnlockCodeLines() != null ? template.getUnlockCodeLines() : 5; int codeLines = template.getUnlockCodeLength() != null ? template.getUnlockCodeLength() : 5;
newLock.setUnlockCodeLines(codeLines); newLock.setUnlockCodeLength(codeLines);
StringBuilder codeBuilder = new StringBuilder(); StringBuilder codeBuilder = new StringBuilder();
java.util.Random rng = new java.util.Random(); java.util.Random rng = new java.util.Random();
for (int i = 0; i < codeLines; i++) codeBuilder.append(rng.nextInt(10)); for (int i = 0; i < codeLines; i++) codeBuilder.append(rng.nextInt(10));

View File

@@ -1,45 +0,0 @@
package de.oaa.xxx.games.chastity;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.oaa.xxx.games.chastity.cardlock.CardEnum;
import de.oaa.xxx.games.chastity.cardlock.CardLockEntity;
import de.oaa.xxx.games.chastity.tasks.Task;
public class KeyholderCardLock {
private static final Logger LOGGER = LoggerFactory.getLogger(KeyholderCardLock.class);
private CardLockEntity lock;
public void addCards(List<CardEnum> cards) {
lock.getAvailableCards().addAll(cards);
}
public void freeze() {
var multiplier = lock.getPickEveryMinute() * new Random().nextDouble(1.0, 4.0);
LocalDateTime frozenTill = LocalDateTime.now().plus((long) multiplier, ChronoUnit.MINUTES);
lock.setFrozenUntill(frozenTill);
LOGGER.debug("Frozen by keyholder until {}", lock.getFrozenUntill());
}
public void freeze(LocalDateTime until) {
lock.setFrozenUntill(until);
LOGGER.debug("Frozen by keyholder until {}", lock.getFrozenUntill());
}
public void task() {
}
public void task(Task task) {
}
public void penalty() {
}
}

View File

@@ -1,6 +0,0 @@
package de.oaa.xxx.games.chastity;
public enum LockType {
CARD, TIMED;
}

View File

@@ -1,9 +0,0 @@
package de.oaa.xxx.games.chastity;
public class ProcessLock {
public void oeffnen() {
// TODO Auto-generated method stub
}
}

View File

@@ -1,5 +0,0 @@
package de.oaa.xxx.games.chastity;
public class ProcessTimedLock extends ProcessLock {
}

View File

@@ -1,5 +0,0 @@
package de.oaa.xxx.games.chastity;
public class Timer {
}

View File

@@ -43,6 +43,21 @@ public enum CardEnum {
public Card get() { public Card get() {
return new DoubleUpCard(); return new DoubleUpCard();
} }
},
CUM {
@Override
public Card get() {
// TODO Auto-generated method stub
return null;
}
},
CUM_IN_CAGE {
@Override
public Card get() {
// TODO Auto-generated method stub
return null;
}
}; };

View File

@@ -36,18 +36,26 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import de.oaa.xxx.games.chastity.CodeCreator; import de.oaa.xxx.games.chastity.common.CodeCreator;
import de.oaa.xxx.games.chastity.KeyholderInvitationEntity; import de.oaa.xxx.games.chastity.keyholder.KeyholderInvitationEntity;
import de.oaa.xxx.games.chastity.KeyholderInvitationRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderInvitationRepository;
import de.oaa.xxx.games.chastity.LockeeInvitationEntity; import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository;
import de.oaa.xxx.games.chastity.LockeeInvitationRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceEntity;
import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository;
import de.oaa.xxx.games.chastity.lockee.LockeeInvitationEntity;
import de.oaa.xxx.games.chastity.lockee.LockeeInvitationRepository;
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity; import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository; import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.unlock.TempOpeningReason;
import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryRepository;
import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService;
import de.oaa.xxx.games.chastity.verification.VerificationEntity; import de.oaa.xxx.games.chastity.verification.VerificationEntity;
import de.oaa.xxx.games.chastity.verification.VerificationRepository; import de.oaa.xxx.games.chastity.verification.VerificationRepository;
import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity; import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity;
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteEntity;
import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
@@ -60,7 +68,7 @@ public class CardLockController {
private final KeyholderInvitationRepository invitationRepository; private final KeyholderInvitationRepository invitationRepository;
private final VerificationRepository verificationRepository; private final VerificationRepository verificationRepository;
private final VerificationVoteRepository verificationVoteRepository; private final VerificationVoteRepository verificationVoteRepository;
private final HygieneViolationRepository hygieneViolationRepository; private final KeyholderNotificationRepository keyholderNotificationRepository;
private final LockeeInvitationRepository lockeeInvitationRepository; private final LockeeInvitationRepository lockeeInvitationRepository;
private final AssignedTaskRepository assignedTaskRepository; private final AssignedTaskRepository assignedTaskRepository;
private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository;
@@ -76,7 +84,7 @@ public class CardLockController {
public CardLockController(CardlockRepository cardlockRepository, UserRepository userRepository, public CardLockController(CardlockRepository cardlockRepository, UserRepository userRepository,
KeyholderInvitationRepository invitationRepository, VerificationRepository verificationRepository, KeyholderInvitationRepository invitationRepository, VerificationRepository verificationRepository,
VerificationVoteRepository verificationVoteRepository, VerificationVoteRepository verificationVoteRepository,
HygieneViolationRepository hygieneViolationRepository, KeyholderNotificationRepository keyholderNotificationRepository,
LockeeInvitationRepository lockeeInvitationRepository, AssignedTaskRepository assignedTaskRepository, LockeeInvitationRepository lockeeInvitationRepository, AssignedTaskRepository assignedTaskRepository,
KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, KeyholderTaskChoiceRepository keyholderTaskChoiceRepository,
CommunityTaskVoteRepository communityTaskVoteRepository, CommunityTaskVoteRepository communityTaskVoteRepository,
@@ -88,7 +96,7 @@ public class CardLockController {
this.invitationRepository = invitationRepository; this.invitationRepository = invitationRepository;
this.verificationRepository = verificationRepository; this.verificationRepository = verificationRepository;
this.verificationVoteRepository = verificationVoteRepository; this.verificationVoteRepository = verificationVoteRepository;
this.hygieneViolationRepository = hygieneViolationRepository; this.keyholderNotificationRepository = keyholderNotificationRepository;
this.lockeeInvitationRepository = lockeeInvitationRepository; this.lockeeInvitationRepository = lockeeInvitationRepository;
this.assignedTaskRepository = assignedTaskRepository; this.assignedTaskRepository = assignedTaskRepository;
this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository;
@@ -197,7 +205,7 @@ public class CardLockController {
lock.setRequiresVerification(req.requiresVerification()); lock.setRequiresVerification(req.requiresVerification());
lock.setTestLock(req.testLock()); lock.setTestLock(req.testLock());
lock.setTaskCardMode(req.taskCardMode() != null ? req.taskCardMode() : "RANDOM"); lock.setTaskCardMode(req.taskCardMode() != null ? req.taskCardMode() : "RANDOM");
lock.setUnlockCodeLines(codeLines); lock.setUnlockCodeLength(codeLines);
lock.setUnlockCode(unlockCode); lock.setUnlockCode(unlockCode);
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
@@ -322,11 +330,7 @@ public class CardLockController {
if (!l.getLockee().equals(myId)) if (!l.getLockee().equals(myId))
return ResponseEntity.status(403).build(); return ResponseEntity.status(403).build();
l.setHygineOpeningtime(LocalDateTime.now()); cardLockServiceFactory.create(l).startHygieneOpening();
cardlockRepository.save(l);
unlockCodeHistoryService.save(myId, l.getLockId(), l.getName(), l.getUnlockCode(), "HYGIENE_OPEN");
return ResponseEntity.ok(Map.of("unlockCode", l.getUnlockCode(), "durationMinutes", return ResponseEntity.ok(Map.of("unlockCode", l.getUnlockCode(), "durationMinutes",
l.getHygineOpeningDurationMinutes() != null ? l.getHygineOpeningDurationMinutes() : 30)); l.getHygineOpeningDurationMinutes() != null ? l.getHygineOpeningDurationMinutes() : 30));
} }
@@ -346,46 +350,8 @@ public class CardLockController {
if (!l.getLockee().equals(myId)) if (!l.getLockee().equals(myId))
return ResponseEntity.status(403).build(); return ResponseEntity.status(403).build();
LocalDateTime now = LocalDateTime.now(); String code = cardLockServiceFactory.create(l).endHygieneOpening();
return ResponseEntity.ok(Map.of("newUnlockCode", code));
// Overtime berechnen
if (l.getHygineOpeningtime() != null && l.getHygineOpeningDurationMinutes() != null) {
LocalDateTime dueTime = l.getHygineOpeningtime().plusMinutes(l.getHygineOpeningDurationMinutes());
if (now.isAfter(dueTime)) {
long overtimeMinutes = ChronoUnit.MINUTES.between(dueTime, now);
if (l.getKeyholder() == null) {
// Self-Lock: 4-fache Überschreitungszeit einfrieren
if (l.getFrozenUntill() != null) {
l.setFrozenUntill(l.getFrozenUntill().plusMinutes(overtimeMinutes * 4));
} else {
l.setFrozenUntill(now.plusMinutes(overtimeMinutes * 4));
}
} else {
// Keyholder vorhanden: Verletzung protokollieren
HygieneViolationEntity violation = new HygieneViolationEntity();
violation.setLockId(lockId);
violation.setLockeeId(myId);
violation.setKeyholderUserId(l.getKeyholder());
violation.setViolationTime(now);
violation.setOvertimeMinutes(overtimeMinutes);
hygieneViolationRepository.save(violation);
}
}
}
// Nächsten Öffnungszeitpunkt setzen
l.setLastHygineOpening(LocalDateTime.now());
l.setHygineOpeningtime(null);
// Neuen Entsperrcode generieren
int codeLines = l.getUnlockCodeLines() != null ? l.getUnlockCodeLines() : 5;
String newCode = generateUnlockCode(codeLines);
l.setUnlockCode(newCode);
cardlockRepository.save(l);
unlockCodeHistoryService.save(myId, l.getLockId(), l.getName(), newCode, "HYGIENE_CLOSE");
return ResponseEntity.ok(Map.of("newUnlockCode", newCode));
} }
@PostMapping("/cardlock/{lockId}/task/complete") @PostMapping("/cardlock/{lockId}/task/complete")
@@ -496,9 +462,9 @@ public class CardLockController {
result.put("hygieneEnabled", hygieneEnabled); result.put("hygieneEnabled", hygieneEnabled);
result.put("hygieneOpeningDue", hygieneOpeningDue); result.put("hygieneOpeningDue", hygieneOpeningDue);
result.put("hygieneSecondsRemaining", hygieneSecondsRemaining); result.put("hygieneSecondsRemaining", hygieneSecondsRemaining);
result.put("hygieneOpeningActive", l.getHygineOpeningtime() != null); result.put("hygieneOpeningActive", l.getTempOpeningTime() != null && TempOpeningReason.HYGIENE == l.getTempOpeningReason());
result.put("hygieneOpeningStarted", result.put("hygieneOpeningStarted",
l.getHygineOpeningtime() != null ? l.getHygineOpeningtime().toString() : null); l.getTempOpeningTime() != null ? l.getTempOpeningTime().toString() : null);
result.put("hygieneDurationMinutes", result.put("hygieneDurationMinutes",
l.getHygineOpeningDurationMinutes() != null ? l.getHygineOpeningDurationMinutes() : 0); l.getHygineOpeningDurationMinutes() != null ? l.getHygineOpeningDurationMinutes() : 0);
result.put("hasKeyholder", l.getKeyholder() != null); result.put("hasKeyholder", l.getKeyholder() != null);
@@ -973,7 +939,7 @@ public class CardLockController {
} }
} }
var recentViolations = hygieneViolationRepository.findByLockId(lockId).stream() var notification = keyholderNotificationRepository.findByLockId(lockId).stream()
.sorted((a, b) -> b.getViolationTime().compareTo(a.getViolationTime())).limit(5) .sorted((a, b) -> b.getViolationTime().compareTo(a.getViolationTime())).limit(5)
.map(v -> Map.of("time", v.getViolationTime().toString(), "overtimeMinutes", v.getOvertimeMinutes())) .map(v -> Map.of("time", v.getViolationTime().toString(), "overtimeMinutes", v.getOvertimeMinutes()))
.toList(); .toList();
@@ -998,7 +964,7 @@ public class CardLockController {
result.put("hygieneEnabled", hygieneEnabled); result.put("hygieneEnabled", hygieneEnabled);
result.put("hygieneOpeningDue", hygieneOpeningDue); result.put("hygieneOpeningDue", hygieneOpeningDue);
result.put("hygieneSecondsRemaining", hygieneSecondsRemaining); result.put("hygieneSecondsRemaining", hygieneSecondsRemaining);
result.put("hygieneOpeningActive", l.getHygineOpeningtime() != null); result.put("hygieneOpeningActive", l.getTempOpeningTime() != null && TempOpeningReason.HYGIENE == l.getTempOpeningReason());
result.put("requiresVerification", l.isRequiresVerification()); result.put("requiresVerification", l.isRequiresVerification());
result.put("verificationDue", verificationDue); result.put("verificationDue", verificationDue);
result.put("verificationDoneToday", verificationDoneToday); result.put("verificationDoneToday", verificationDoneToday);
@@ -1007,7 +973,7 @@ public class CardLockController {
result.put("verificationImage", verificationImage); result.put("verificationImage", verificationImage);
result.put("verificationUpvotes", verificationUpvotes); result.put("verificationUpvotes", verificationUpvotes);
result.put("verificationDownvotes", verificationDownvotes); result.put("verificationDownvotes", verificationDownvotes);
result.put("hygieneViolations", recentViolations); result.put("hygieneViolations", notification);
result.put("hasTasks", l.getTasks() != null && !l.getTasks().isEmpty()); result.put("hasTasks", l.getTasks() != null && !l.getTasks().isEmpty());
if (l.getTasks() != null) { if (l.getTasks() != null) {
var taskList = l.getTasks().stream().map(t -> { var taskList = l.getTasks().stream().map(t -> {
@@ -1589,6 +1555,8 @@ public class CardLockController {
case FREEZE -> "Freeze"; case FREEZE -> "Freeze";
case RESET -> "Reset"; case RESET -> "Reset";
case DOUBLE_UP -> "Double Up"; case DOUBLE_UP -> "Double Up";
case CUM -> "Kommen";
case CUM_IN_CAGE -> "Kommen im Käfig";
}; };
} }

View File

@@ -6,6 +6,7 @@ import java.util.UUID;
import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskListConverter; import de.oaa.xxx.games.chastity.tasks.TaskListConverter;
import de.oaa.xxx.games.chastity.unlock.TempOpeningReason;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Convert; import jakarta.persistence.Convert;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@@ -60,7 +61,7 @@ public class CardLockEntity {
@Column @Column
private boolean testLock; private boolean testLock;
@Column @Column
private Integer unlockCodeLines; private Integer unlockCodeLength;
// State // State
@Column @Column
@@ -75,7 +76,11 @@ public class CardLockEntity {
@Column @Column
private LocalDateTime lastHygineOpening; private LocalDateTime lastHygineOpening;
@Column @Column
private LocalDateTime hygineOpeningtime; // If null, not while hygine opening private LocalDateTime tempOpeningTime; // If null, not while hygine opening
@Column
private Integer tempOpeningDuration;
@Column
private TempOpeningReason tempOpeningReason;
@Column @Column
private LocalDateTime unlockTime; private LocalDateTime unlockTime;
@Column @Column

View File

@@ -11,33 +11,41 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import de.oaa.xxx.games.chastity.ProcessLock; import de.oaa.xxx.games.chastity.common.AbstractLockService;
import de.oaa.xxx.games.chastity.common.CodeCreator;
import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationEntity;
import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository;
import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.unlock.TempOpeningReason;
import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService;
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
import de.oaa.xxx.games.history.GameHistoryEntity; import de.oaa.xxx.games.history.GameHistoryEntity;
import de.oaa.xxx.games.history.GameHistoryRepository; import de.oaa.xxx.games.history.GameHistoryRepository;
import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.verification.VerificationEntity;
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity;
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
public class CardLockService extends ProcessLock { public class CardLockService extends AbstractLockService {
private static final Logger LOGGER = LoggerFactory.getLogger(CardLockService.class); private static final Logger LOGGER = LoggerFactory.getLogger(CardLockService.class);
private final CardLockEntity lock; private final CardLockEntity lock;
private VerificationRepository verificationRepository;
private VerificationVoteRepository verificationVoteRepository;
private CardLockRepository cardLockRepository; private CardLockRepository cardLockRepository;
private VerificationRepository verificationRepository;
private GameHistoryRepository gameHistoryRepository; private GameHistoryRepository gameHistoryRepository;
private UserRepository userRepository; private UserRepository userRepository;
private UnlockCodeHistoryService unlockCodeHistoryService;
private KeyholderNotificationRepository keyholderNotificationRepository;
public CardLockService(CardLockEntity lock, VerificationRepository verificationRepository, VerificationVoteRepository verificationVoteRepository, CardLockRepository cardLockRepository, GameHistoryRepository gameHistoryRepository, UserRepository userRepository) { public CardLockService(CardLockEntity lock, VerificationRepository verificationRepository,
VerificationVoteRepository verificationVoteRepository, CardLockRepository cardLockRepository,
GameHistoryRepository gameHistoryRepository, UserRepository userRepository, KeyholderNotificationRepository keyholderNotificationRepository,UnlockCodeHistoryService unlockCodeHistoryService) {
super(verificationVoteRepository);
this.lock = lock; this.lock = lock;
this.verificationRepository = verificationRepository;
this.verificationVoteRepository = verificationVoteRepository;
this.cardLockRepository = cardLockRepository; this.cardLockRepository = cardLockRepository;
this.verificationRepository = verificationRepository;
this.gameHistoryRepository = gameHistoryRepository; this.gameHistoryRepository = gameHistoryRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.keyholderNotificationRepository = keyholderNotificationRepository;
this.unlockCodeHistoryService = unlockCodeHistoryService;
} }
public CardDTO getNextCard() { public CardDTO getNextCard() {
@@ -166,19 +174,6 @@ public class CardLockService extends ProcessLock {
cardLockRepository.save(lock); cardLockRepository.save(lock);
} }
private boolean isValid(VerificationEntity entity) {
int count = 0;
for (VerificationVoteEntity vote : verificationVoteRepository.findAllByVerificationId(entity.getVerficationId())) {
if (vote.isUpvote()) {
count++;
} else {
count--;
}
}
return count >= 0;
}
public String freeze() { public String freeze() {
var multiplier = lock.getPickEveryMinute() * new Random().nextDouble(1.0, 4.0); var multiplier = lock.getPickEveryMinute() * new Random().nextDouble(1.0, 4.0);
freeze(multiplier); freeze(multiplier);
@@ -244,4 +239,94 @@ public class CardLockService extends ProcessLock {
} }
return ""; return "";
} }
public void startHygieneOpening() {
tempOperning(TempOpeningReason.HYGIENE, lock.getHygineOpeningDurationMinutes());
}
private Long calcOvertime() {
LocalDateTime now = LocalDateTime.now();
Long overtime = null;
if (lock.getTempOpeningTime() != null && lock.getTempOpeningDuration() != null) {
LocalDateTime dueTime = lock.getTempOpeningTime().plusMinutes(lock.getTempOpeningDuration());
if (LocalDateTime.now().isAfter(dueTime)) {
overtime = ChronoUnit.MINUTES.between(dueTime, now);
}
}
return overtime;
}
public String endHygieneOpening() {
LocalDateTime now = LocalDateTime.now();
Long overtime = calcOvertime();
if (overtime != null) {
if (lock.getKeyholder() != null) {
reportKeyholder(overtime);
}
freezeForOvertime(overtime);
}
lock.setLastHygineOpening(now);
lock.setTempOpeningDuration(null);
lock.setTempOpeningTime(null);
var code = CodeCreator.createAlphanumericCode(lock.getUnlockCodeLength());
lock.setUnlockCode(code);
cardLockRepository.save(lock);
return code;
}
private void reportKeyholder(Long overtime) {
KeyholderNotificationEntity notification = new KeyholderNotificationEntity();
notification.setLockId(lock.getLockId());
notification.setLockeeId(lock.getLockee());
notification.setKeyholderUserId(lock.getKeyholder());
notification.setViolationTime(LocalDateTime.now());
notification.setOvertimeMinutes(overtime);
keyholderNotificationRepository.save(notification);
}
private void freezeForOvertime(Long overtime) {
if (lock.getFrozenUntill() != null) {
lock.setFrozenUntill(lock.getFrozenUntill().plusMinutes(overtime * 4));
} else {
lock.setFrozenUntill(LocalDateTime.now().plusMinutes(overtime * 4));
}
}
private void tempOperning(TempOpeningReason reason, Integer duration) {
assert duration != null;
lock.setTempOpeningReason(reason);
lock.setTempOpeningTime(LocalDateTime.now());;
lock.setTempOpeningDuration(duration);
cardLockRepository.save(lock);
unlockCodeHistoryService.save(lock.getLockee(), lock.getLockId(), lock.getName(), lock.getUnlockCode(), reason.toString());
}
public String cum(boolean tempUnlock) {
if (tempUnlock) {
tempOperning(TempOpeningReason.CARD, 0); // Je länger man braucht, desto länger wird gefreezed
}
return lock.getUnlockCode();
}
public String endCumming() {
Long overtime = calcOvertime();
if (overtime != null) {
if (lock.getKeyholder() == null) {
freezeForOvertime(overtime);
} else {
reportKeyholder(overtime);
}
}
lock.setTempOpeningDuration(null);
lock.setTempOpeningTime(null);
lock.setTempOpeningReason(null);
var code = CodeCreator.createAlphanumericCode(lock.getUnlockCodeLength());
lock.setUnlockCode(code);
cardLockRepository.save(lock);
return code;
}
} }

View File

@@ -1,6 +1,8 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.cardlock;
import de.oaa.xxx.games.history.GameHistoryRepository; import de.oaa.xxx.games.history.GameHistoryRepository;
import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository;
import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService;
import de.oaa.xxx.games.chastity.verification.VerificationRepository; import de.oaa.xxx.games.chastity.verification.VerificationRepository;
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
@@ -21,17 +23,23 @@ public class CardLockServiceFactory {
private final CardLockRepository cardLockRepository; private final CardLockRepository cardLockRepository;
private final GameHistoryRepository gameHistoryRepository; private final GameHistoryRepository gameHistoryRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private KeyholderNotificationRepository keyholderNotificationRepository;
private final UnlockCodeHistoryService unlockCodeHistoryService;
public CardLockServiceFactory(VerificationRepository verificationRepository, public CardLockServiceFactory(VerificationRepository verificationRepository,
VerificationVoteRepository verificationVoteRepository, VerificationVoteRepository verificationVoteRepository,
CardLockRepository cardLockRepository, CardLockRepository cardLockRepository,
GameHistoryRepository gameHistoryRepository, GameHistoryRepository gameHistoryRepository,
UserRepository userRepository) { UserRepository userRepository,
KeyholderNotificationRepository keyholderNotificationRepository,
UnlockCodeHistoryService unlockCodeHistoryService) {
this.verificationRepository = verificationRepository; this.verificationRepository = verificationRepository;
this.verificationVoteRepository = verificationVoteRepository; this.verificationVoteRepository = verificationVoteRepository;
this.cardLockRepository = cardLockRepository; this.cardLockRepository = cardLockRepository;
this.gameHistoryRepository = gameHistoryRepository; this.gameHistoryRepository = gameHistoryRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.keyholderNotificationRepository = keyholderNotificationRepository;
this.unlockCodeHistoryService = unlockCodeHistoryService;
} }
/** /**
@@ -44,7 +52,9 @@ public class CardLockServiceFactory {
verificationVoteRepository, verificationVoteRepository,
cardLockRepository, cardLockRepository,
gameHistoryRepository, gameHistoryRepository,
userRepository userRepository,
keyholderNotificationRepository,
unlockCodeHistoryService
); );
} }
} }

View File

@@ -0,0 +1,10 @@
package de.oaa.xxx.games.chastity.cardlock;
public class CumCard implements Card {
@Override
public CardDTO processCard(CardLockService lock) {
return new CardDTO(CardEnum.CUM, lock.cum(true));
}
}

View File

@@ -0,0 +1,10 @@
package de.oaa.xxx.games.chastity.cardlock;
public class CumInCageCard implements Card {
@Override
public CardDTO processCard(CardLockService lock) {
return new CardDTO(CardEnum.CUM_IN_CAGE, lock.cum(false));
}
}

View File

@@ -1,10 +0,0 @@
package de.oaa.xxx.games.chastity.cardlock;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface HygieneViolationRepository extends JpaRepository<HygieneViolationEntity, UUID> {
List<HygieneViolationEntity> findByKeyholderUserIdAndNotifiedKeyholderFalse(UUID keyholderUserId);
List<HygieneViolationEntity> findByLockId(UUID lockId);
}

View File

@@ -1,8 +1,12 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.cardlock;
import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository;
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity; import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository; import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteEntryEntity;
import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteEntryRepository;
import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;

View File

@@ -16,6 +16,8 @@ import org.springframework.transaction.annotation.Transactional;
import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity; import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity;
import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository; import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository;
import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteEntryRepository;
import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteRepository;
import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.social.SystemMessageService;
@Component @Component

View File

@@ -0,0 +1,30 @@
package de.oaa.xxx.games.chastity.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.oaa.xxx.games.chastity.verification.VerificationEntity;
import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity;
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
public abstract class AbstractLockService {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLockService.class);
private final VerificationVoteRepository verificationVoteRepository;
public AbstractLockService(VerificationVoteRepository verificationVoteRepository) {
this.verificationVoteRepository = verificationVoteRepository;
}
protected boolean isValid(VerificationEntity entity) {
LOGGER.trace("isValid");
int count = 0;
for (VerificationVoteEntity vote : verificationVoteRepository.findAllByVerificationId(entity.getVerficationId())) {
if (vote.isUpvote()) {
count++;
} else {
count--;
}
}
return count >= 0;
}
}

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity; package de.oaa.xxx.games.chastity.common;
import java.util.Random; import java.util.Random;

View File

@@ -0,0 +1,6 @@
package de.oaa.xxx.games.chastity.common;
public enum LockType {
CARD, TIMED;
}

View File

@@ -0,0 +1,6 @@
package de.oaa.xxx.games.chastity.common;
public enum PenaltyType {
ADD, FREEZE, PILLORY;
}

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity; package de.oaa.xxx.games.chastity.keyholder;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity; package de.oaa.xxx.games.chastity.keyholder;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.keyholder;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;
@@ -7,11 +7,13 @@ import lombok.Setter;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.UUID; import java.util.UUID;
import de.oaa.xxx.games.chastity.unlock.TempOpeningReason;
@Getter @Getter
@Setter @Setter
@Entity @Entity
@Table(name = "hygiene_violation") @Table(name = "keyholder_notification")
public class HygieneViolationEntity { public class KeyholderNotificationEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.UUID) @GeneratedValue(strategy = GenerationType.UUID)
@@ -34,4 +36,7 @@ public class HygieneViolationEntity {
@Column(nullable = false) @Column(nullable = false)
private boolean notifiedKeyholder = false; private boolean notifiedKeyholder = false;
@Column(nullable = false)
private TempOpeningReason openingReason;
} }

View File

@@ -0,0 +1,10 @@
package de.oaa.xxx.games.chastity.keyholder;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface KeyholderNotificationRepository extends JpaRepository<KeyholderNotificationEntity, UUID> {
List<KeyholderNotificationEntity> findByKeyholderUserIdAndNotifiedKeyholderFalse(UUID keyholderUserId);
List<KeyholderNotificationEntity> findByLockId(UUID lockId);
}

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.keyholder;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.keyholder;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List; import java.util.List;

View File

@@ -0,0 +1,16 @@
package de.oaa.xxx.games.chastity.lockcontroll;
public abstract class LockControl {
protected final LockControlCallback callback;
public LockControl(LockControlCallback callback) {
this.callback = callback;
}
public abstract boolean init();
public abstract boolean unlock();
public abstract boolean lock();
}

View File

@@ -0,0 +1,8 @@
package de.oaa.xxx.games.chastity.lockcontroll;
public interface LockControlCallback {
void setUnlockCode(String code);
int getUnlockcodeLenght();
}

View File

@@ -0,0 +1,6 @@
package de.oaa.xxx.games.chastity.lockcontroll;
public enum LockControllType {
TTLOCK, UNLOCK_CODE, TRUST;
}

View File

@@ -0,0 +1,13 @@
package de.oaa.xxx.games.chastity.lockcontroll;
class NoInteractionCallback implements LockControlCallback {
@Override
public void setUnlockCode(String code) {
}
@Override
public int getUnlockcodeLenght() {
return 0;
}
}

View File

@@ -0,0 +1,32 @@
package de.oaa.xxx.games.chastity.lockcontroll;
public class TTLockControl extends LockControl {
public TTLockControl() {
super(new NoInteractionCallback());
}
private static final String BASE_URL = "https://euapi.ttlock.com/";
@Override
public boolean init() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean unlock() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean lock() {
// TODO Auto-generated method stub
return false;
}
}

View File

@@ -0,0 +1,23 @@
package de.oaa.xxx.games.chastity.lockcontroll;
public class TrustLockControl extends LockControl {
public TrustLockControl() {
super(new NoInteractionCallback());
}
@Override
public boolean init() {
return true;
}
@Override
public boolean unlock() {
return true;
}
@Override
public boolean lock() {
return true;
}
}

View File

@@ -0,0 +1,27 @@
package de.oaa.xxx.games.chastity.lockcontroll;
import de.oaa.xxx.games.chastity.common.CodeCreator;
public class UnlockcodeLockControl extends LockControl {
public UnlockcodeLockControl(LockControlCallback callback) {
super(callback);
}
@Override
public boolean init() {
return true;
}
@Override
public boolean unlock() {
return true;
}
@Override
public boolean lock() {
var code = CodeCreator.createAlphanumericCode(callback.getUnlockcodeLenght());
callback.setUnlockCode(code);
return true;
}
}

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity; package de.oaa.xxx.games.chastity.lockee;
import java.security.Principal; import java.security.Principal;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -220,7 +220,7 @@ public class LockeeInvitationController {
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
lock.setStartTime(now); lock.setStartTime(now);
lock.setUnlockCode(unlockCode); lock.setUnlockCode(unlockCode);
lock.setUnlockCodeLines(codeLines); lock.setUnlockCodeLength(codeLines);
lock.setAvailableCards(new ArrayList<>(lock.getInitialCards())); lock.setAvailableCards(new ArrayList<>(lock.getInitialCards()));
lock.setOpenPicks(0); lock.setOpenPicks(0);
lock.setNextCardIn(now.plusMinutes(lock.getPickEveryMinute())); lock.setNextCardIn(now.plusMinutes(lock.getPickEveryMinute()));

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity; package de.oaa.xxx.games.chastity.lockee;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity; package de.oaa.xxx.games.chastity.lockee;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;

View File

@@ -0,0 +1,34 @@
package de.oaa.xxx.games.chastity.pillory;
import java.time.LocalDateTime;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
@Table(name = "pillory")
public class PilloryEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column
@Setter(lombok.AccessLevel.NONE)
private UUID pilloryId;
@Column(nullable = false)
private UUID lockId;
@Column(nullable = false)
private UUID lockeeUserId;
@Column(nullable = false)
private LocalDateTime createdAt;
@Column
private PilloryReason reason;
}

View File

@@ -0,0 +1,7 @@
package de.oaa.xxx.games.chastity.pillory;
public enum PilloryReason {
HYGIENE_OPENING_EXEEDED,
KEYHOLDER_DESCESSION;
}

View File

@@ -0,0 +1,9 @@
package de.oaa.xxx.games.chastity.pillory;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PilloryRepository extends JpaRepository<PilloryEntity, UUID> {
}

View File

@@ -0,0 +1,51 @@
package de.oaa.xxx.games.chastity.spinningwheel;
import de.oaa.xxx.games.chastity.timelock.TimeLockService;
public enum EntryType {
ADD_TIME {
@Override
public void apply(TimeLockService service, Integer intVal, String stringVal) {
service.addTime(intVal);
}
},
REMOVE_TIME {
@Override
public void apply(TimeLockService service, Integer intVal, String stringVal) {
service.removeTime(intVal);
}
},
FREEZE_TIME {
@Override
public void apply(TimeLockService service, Integer intVal, String stringVal) {
service.freeze(intVal);
}
},
FREEZE {
@Override
public void apply(TimeLockService service, Integer intVal, String stringVal) {
service.freeze();
}
},
UNFREEZE {
@Override
public void apply(TimeLockService service, Integer intVal, String stringVal) {
service.unfreeze();
}
},
TASK {
@Override
public void apply(TimeLockService service, Integer intVal, String stringVal) {
service.task();
}
},
TEXT {
@Override
public void apply(TimeLockService service, Integer intVal, String stringVal) {
service.text(intVal, stringVal);
}
};
public abstract void apply(TimeLockService service, Integer intVal, String stringVal);
}

View File

@@ -0,0 +1,39 @@
package de.oaa.xxx.games.chastity.spinningwheel;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter
public class SpinningWheelConverter implements AttributeConverter<List<SpinningWheelEntry>, String> {
private static final ObjectMapper mapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(List<SpinningWheelEntry> list) {
if (list == null || list.isEmpty())
return null;
try {
return mapper.writeValueAsString(list);
} catch (Exception e) {
return null;
}
}
@Override
public List<SpinningWheelEntry> convertToEntityAttribute(String json) {
if (json == null || json.isBlank())
return new ArrayList<>();
try {
return new ArrayList<>(mapper.readValue(json, new TypeReference<List<SpinningWheelEntry>>() {
}));
} catch (Exception e) {
return new ArrayList<>();
}
}
}

View File

@@ -0,0 +1,16 @@
package de.oaa.xxx.games.chastity.spinningwheel;
import java.util.List;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SpinningWheelEntity {
private UUID wheelId;
private String name;
private List<SpinningWheelEntity> entries;
}

View File

@@ -0,0 +1,17 @@
package de.oaa.xxx.games.chastity.spinningwheel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class SpinningWheelEntry {
private EntryType type;
private Integer intVal;
private String stringVal;
}

View File

@@ -0,0 +1,20 @@
package de.oaa.xxx.games.chastity.spinningwheel;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SpinningWheelEntryEntity {
private UUID entryId;
private EntryType type;
private Integer intVal;
private String stringVal;
public SpinningWheelEntry toSpinningWheelEntry() {
return new SpinningWheelEntry(type, intVal, stringVal);
}
}

View File

@@ -46,6 +46,9 @@ public class AssignedTaskEntity {
@Column @Column
private Integer penaltyRedCards; private Integer penaltyRedCards;
@Column
private Integer penaltyAddTime;
/** PENDING | ACCEPTED | DECLINED | EXPIRED */ /** PENDING | ACCEPTED | DECLINED | EXPIRED */
@Column(nullable = false) @Column(nullable = false)
private String status = "PENDING"; private String status = "PENDING";

View File

@@ -0,0 +1,5 @@
package de.oaa.xxx.games.chastity.tasks;
public enum TaskMode {
RANDOM, KEYHOLDER, COMMUNITY;
}

View File

@@ -0,0 +1,9 @@
package de.oaa.xxx.games.chastity.timelock;
import java.util.UUID;
import de.oaa.xxx.games.chastity.lockcontroll.LockControllType;
public record TimeLockAdditionalSettings(LockControllType controllType, UUID lockee, UUID keyholder, boolean testlock, Integer unlockCodeLength) {
}

View File

@@ -0,0 +1,117 @@
package de.oaa.xxx.games.chastity.timelock;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import de.oaa.xxx.games.chastity.common.PenaltyType;
import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelConverter;
import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry;
import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskListConverter;
import de.oaa.xxx.games.chastity.tasks.TaskMode;
import de.oaa.xxx.games.chastity.unlock.TempOpeningReason;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
@Table(name = "time_lock")
public class TimeLockEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column
private UUID lockId;
@Column
private String name;
// Settings
@Column(nullable = false)
private UUID lockee;
@Column
private UUID keyholder;
@Column
private LocalDateTime startTime;
@Column
private LocalDateTime unlockTime;
@Column
private boolean endTimeVisible;
@Convert(converter = TaskListConverter.class)
@Column(columnDefinition = "TEXT")
private List<Task> tasks;
@Column
private Integer taskEveryMinutes;
@Column
private Integer minTasksPerDay;
@Convert(converter = SpinningWheelConverter.class)
@Column(columnDefinition = "TEXT")
private List<SpinningWheelEntry> spinningWheelEntries;
@Column
private Integer hygineOpeningDurationMinutes;
@Column
private Integer hygineOpeningEveryMinites;
@Column
private boolean requiresVerification;
@Column
private boolean testLock;
@Column
private Integer unlockCodeLength;
@Column(nullable = false)
private TaskMode taskMode = TaskMode.RANDOM;
@Column
private Integer spinsEveryMinutes;
@Column
private Integer minSpinsPerDay;
@Column
private PenaltyType penaltyType;
@Column
private Integer penaltyValue;
@Column
private LocalDateTime lastHygineOpening;
@Column
private LocalDateTime tempOpeningTime; // If null, not while hygine opening
@Column
private Integer tempOpeningDuration;
@Column
private TempOpeningReason tempOpeningReason;
@Column
private LocalDateTime frozenFrom;
@Column
private LocalDateTime frozenUntil;
@Column
private String currentTask;
@Column(columnDefinition = "TEXT")
private String currentTaskDescription;
@Column
private LocalDateTime taskUntil;
@Column
private String unlockCode;
/** Keyholder hat Unlock angefordert nächste Aktion der Lockee zeigt grüne Karte */
@Column(nullable = false)
private boolean keyholderRequestedUnlock = false;
/** Lockee hat Notfall-Entsperrung angefordert */
@Column
private java.time.LocalDateTime emergencyUnlockRequestedAt;
/** true = System hat automatisch entsperrt (Keyholderin nicht reagiert) */
@Column(nullable = false)
private boolean emergencyAutoUnlocked = false;
@Column
private List<LocalDateTime> taskTimes;
@Column
private List<LocalDateTime> spinningWheelTimes;
@Column
private LocalDate lastCheck;
public TaskMode getTaskMode() { return taskMode != null ? taskMode : TaskMode.RANDOM; }
}

View File

@@ -0,0 +1,9 @@
package de.oaa.xxx.games.chastity.timelock;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TimeLockRepository extends JpaRepository<TimeLockEntity, UUID> {
}

View File

@@ -0,0 +1,381 @@
package de.oaa.xxx.games.chastity.timelock;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.oaa.xxx.games.chastity.common.AbstractLockService;
import de.oaa.xxx.games.chastity.common.CodeCreator;
import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationEntity;
import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository;
import de.oaa.xxx.games.chastity.lockcontroll.LockControl;
import de.oaa.xxx.games.chastity.lockcontroll.LockControlCallback;
import de.oaa.xxx.games.chastity.lockcontroll.TTLockControl;
import de.oaa.xxx.games.chastity.lockcontroll.TrustLockControl;
import de.oaa.xxx.games.chastity.lockcontroll.UnlockcodeLockControl;
import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry;
import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskMode;
import de.oaa.xxx.games.chastity.unlock.TempOpeningReason;
import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService;
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
import de.oaa.xxx.games.history.GameHistoryEntity;
import de.oaa.xxx.games.history.GameHistoryRepository;
import de.oaa.xxx.user.UserRepository;
public class TimeLockService extends AbstractLockService implements LockControlCallback {
private static final Logger LOGGER = LoggerFactory.getLogger(TimeLockService.class);
private final TimeLockEntity lock;
private final TimeLockRepository timeLockRepository;
private final VerificationRepository verificationRepository;
private final GameHistoryRepository gameHistoryRepository;
private final UserRepository userRepository;
private final KeyholderNotificationRepository keyholderNotificationRepository;
private final UnlockCodeHistoryService unlockCodeHistoryService;
private LockControl lockControl;
public TimeLockService(TimeLockEntity lock, VerificationRepository verificationRepository,
VerificationVoteRepository verificationVoteRepository,
TimeLockRepository timeLockRepository,
GameHistoryRepository gameHistoryRepository,
UserRepository userRepository,
KeyholderNotificationRepository keyholderNotificationRepository,
UnlockCodeHistoryService unlockCodeHistoryService) {
super(verificationVoteRepository);
this.lock = lock;
this.timeLockRepository = timeLockRepository;
this.verificationRepository = verificationRepository;
this.gameHistoryRepository = gameHistoryRepository;
this.userRepository = userRepository;
this.keyholderNotificationRepository = keyholderNotificationRepository;
this.unlockCodeHistoryService = unlockCodeHistoryService;
}
public void init(TimeLockTemplate template, TimeLockAdditionalSettings settings) {
switch (settings.controllType()) {
case TTLOCK -> lockControl = new TTLockControl();
case TRUST -> lockControl = new TrustLockControl();
case UNLOCK_CODE -> lockControl = new UnlockcodeLockControl(this);
}
lock.setLockee(UUID.randomUUID());
lock.setName(template.name());
lock.setLockee(settings.lockee());
lock.setKeyholder(settings.keyholder());
lock.setRequiresVerification(template.requiresVerification());
lock.setTestLock(settings.testlock());
lock.setUnlockCodeLength(settings.unlockCodeLength());
lock.setStartTime(LocalDateTime.now());
Integer unlockTimeMinutes = template.maxTimeInMinutes();
if (template.minTimeInMinutes() != null) {
unlockTimeMinutes = new Random().nextInt(template.minTimeInMinutes(), template.maxTimeInMinutes());
}
lock.setUnlockTime(LocalDateTime.now().plusMinutes(unlockTimeMinutes));
lock.setEndTimeVisible(template.endTimeVisible());
lock.setTasks(template.tasks());
lock.setTaskEveryMinutes(template.taskEveryMinutes());
lock.setMinTasksPerDay(template.minTasksPerDay());
lock.setSpinningWheelEntries(template.spinningWheelEntries());
lock.setSpinsEveryMinutes(template.spinsEveryMinutes());
lock.setMinSpinsPerDay(template.minSpinsPerDay());
lock.setHygineOpeningDurationMinutes(template.hygineOpeningDurationMinutes());
lock.setHygineOpeningEveryMinites(template.hygineOpeningEveryMinites());
lock.setTaskMode(template.taskMode());
lockControl.lock();
}
public SpinningWheelEntry spinWheel() {
if (TempOpeningReason.HYGIENE != lock.getTempOpeningReason() ) {
var entries = lock.getSpinningWheelEntries();
var entry = entries.get(new Random().nextInt(entries.size()));
entry.getType().apply(this, entry.getIntVal(), entry.getStringVal());
return entry;
}
// Nicht während der Hyhiene Öffnung
return null;
}
public void addTime(Integer intVal) {
LOGGER.debug("Lock addTime: %s minutes", intVal);
lock.setUnlockTime(lock.getUnlockTime().plusMinutes(intVal));
}
public void removeTime(Integer intVal) {
LOGGER.debug("Lock removeTime: %s minutes", intVal);
lock.setUnlockTime(lock.getUnlockTime().minusMinutes(intVal));
}
public void freeze(Integer intVal) {
LOGGER.debug("Lock frozen for %s minutes", intVal);
lock.setFrozenFrom(LocalDateTime.now());
lock.setFrozenUntil(LocalDateTime.now().plusMinutes(intVal));
}
public void freeze() {
LOGGER.debug("Lock frozen");
lock.setFrozenFrom(LocalDateTime.now());
}
public void unfreeze() {
if (lock.getFrozenFrom() != null) {
var unfreeTime = lock.getFrozenUntil() != null ? lock.getFrozenUntil() : LocalDateTime.now();
var diff = ChronoUnit.MINUTES.between(lock.getFrozenFrom(), unfreeTime);
LOGGER.debug("Lock unfrozen - adding %s minutes to the lock", diff);
lock.setUnlockTime(lock.getUnlockTime().plusMinutes(diff));
} else {
LOGGER.debug("Lock not frozen - ignore Call");
}
}
public void task() {
if (TempOpeningReason.HYGIENE != lock.getTempOpeningReason() ) {
switch (lock.getTaskMode()) {
case TaskMode.RANDOM -> applyRandomTask();
case TaskMode.KEYHOLDER -> startKeyHolderVote();
case TaskMode.COMMUNITY -> startCommunityVode();
}
}
// Nicht während der Hyhiene Öffnung
}
private void startKeyHolderVote() {
// Keyholder Vote starten
}
private void startCommunityVode() {
// Community Vote starten
}
public void applyRandomTask() {
LOGGER.debug("Apply random task");
var tasks = lock.getTasks();
if (!tasks.isEmpty()) {
task(tasks.get(new Random().nextInt(tasks.size())));
}
}
public void task(Task task) {
LOGGER.debug("Apply task {}", task);
lock.setCurrentTask(task.resolveTitle());
lock.setCurrentTaskDescription(task.getDescription());
if (task.getMinutes() != null && task.getMinutes() > 0) {
lock.setTaskUntil(LocalDateTime.now().plusMinutes(task.getMinutes()));
}
}
public void text(Integer intVal, String stringVal) {
LOGGER.debug("Apply text {}", stringVal);
lock.setCurrentTask(stringVal);
if (intVal != null && intVal > 0) {
lock.setTaskUntil(LocalDateTime.now().plusMinutes(intVal));
}
}
public String clearTask() {
LOGGER.debug("Clear task");
lock.setCurrentTask(null);
lock.setCurrentTaskDescription(null);
lock.setTaskUntil(null);
return "";
}
public void testUnfreeze() {
if (lock.getFrozenUntil().isAfter(LocalDateTime.now())) {
unfreeze();
}
}
public void unlock(String unlockCode) {
lockControl.unlock();
this.lock.setUnlockTime(LocalDateTime.now());
boolean valid = true;
if (lock.isEmergencyAutoUnlocked()) {
valid = false;
LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)");
}
if (lock.isTestLock()) {
valid = false;
} else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) {
Set<LocalDate> verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream()
.filter(verification -> isValid(verification))
.map(verification -> verification.getVerificationTime().toLocalDate()).collect(Collectors.toSet());
LocalDate current = this.lock.getStartTime().toLocalDate();
LocalDate last = this.lock.getUnlockTime().toLocalDate().minusDays(1);
while (!current.isAfter(last)) {
if (!verifications.contains(current)) {
valid = false;
LOGGER.debug("Lock invalid - no daily verification on %s", current.toString());
break;
}
current = current.plusDays(1);
}
}
lock.setUnlockTime(LocalDateTime.now());
LOGGER.debug("Unlocked at {}", lock.getUnlockTime());
timeLockRepository.save(lock);
if (valid) {
long durationMinutes = Duration.between(lock.getStartTime(), lock.getUnlockTime()).toMinutes();
// Gemeinsamer History-Eintrag mit Teilnehmerliste
GameHistoryEntity entry = new GameHistoryEntity();
entry.setGameType(de.oaa.xxx.games.history.GameType.CARDLOCK);
entry.setGameName(lock.getName());
entry.setStartTime(lock.getStartTime());
entry.setEndTime(lock.getUnlockTime());
entry.setDurationMinutes(durationMinutes);
entry.addParticipant(lock.getLockee(), de.oaa.xxx.games.history.GameRole.LOCKEE);
if (lock.getKeyholder() != null) {
entry.addParticipant(lock.getKeyholder(), de.oaa.xxx.games.history.GameRole.KEYHOLDER);
}
gameHistoryRepository.save(entry);
int minutes = (int) durationMinutes;
userRepository.findById(lock.getLockee()).ifPresent(u -> {
u.setLockeeXp(u.getLockeeXp() + minutes);
userRepository.save(u);
});
if (lock.getKeyholder() != null) {
userRepository.findById(lock.getKeyholder()).ifPresent(u -> {
u.setKeyholderXp(u.getKeyholderXp() + minutes);
userRepository.save(u);
});
}
}
}
public void applyPenalty() {
if (lock.getPenaltyType() != null) {
switch (lock.getPenaltyType()) {
case ADD -> addTime(lock.getPenaltyValue());
case FREEZE -> freeze();
case PILLORY -> pillory();
}
}
}
public void check() {
LocalDate today = LocalDate.now();
if (!lock.getStartTime().toLocalDate().equals(today)) {
if (lock.getLastCheck() != null || today.isAfter(lock.getLastCheck())) {
LOGGER.info("Check the day before for violations");
LocalDate yesterday = today.minusDays(1);
boolean violation = false;
if (lock.getMinTasksPerDay() != null) {
if (lock.getMinTasksPerDay() > lock.getTaskTimes().stream().map(LocalDateTime::toLocalDate)
.filter(yesterday::equals).count()) {
violation = true;
}
}
if (lock.getMinSpinsPerDay() != null) {
if (lock.getMinSpinsPerDay() > lock.getSpinningWheelTimes().stream().map(LocalDateTime::toLocalDate)
.filter(yesterday::equals).count()) {
violation = true;
}
}
if (violation) {
applyPenalty();
}
lock.setLastCheck(today);
timeLockRepository.save(lock);
}
}
}
public void pillory() {
// TODO an den Pranger stellen
}
@Override
public void setUnlockCode(String code) {
lock.setUnlockCode(code);
timeLockRepository.save(lock);
}
@Override
public int getUnlockcodeLenght() {
return lock.getUnlockCodeLength();
}
public void startHygieneOpening() {
tempOperning(TempOpeningReason.HYGIENE, lock.getHygineOpeningDurationMinutes());
}
private Long calcOvertime() {
LocalDateTime now = LocalDateTime.now();
Long overtime = null;
if (lock.getTempOpeningTime() != null && lock.getTempOpeningDuration() != null) {
LocalDateTime dueTime = lock.getTempOpeningTime().plusMinutes(lock.getTempOpeningDuration());
if (LocalDateTime.now().isAfter(dueTime)) {
overtime = ChronoUnit.MINUTES.between(dueTime, now);
}
}
return overtime;
}
public String endHygieneOpening() {
lockControl.lock();
LocalDateTime now = LocalDateTime.now();
Long overtime = calcOvertime();
if (overtime != null) {
if (lock.getKeyholder() != null) {
reportKeyholder(overtime);
}
addOvertime(overtime);
}
lock.setLastHygineOpening(now);
lock.setTempOpeningDuration(null);
lock.setTempOpeningTime(null);
var code = CodeCreator.createAlphanumericCode(lock.getUnlockCodeLength());
lock.setUnlockCode(code);
timeLockRepository.save(lock);
return code;
}
private void reportKeyholder(Long overtime) {
KeyholderNotificationEntity notification = new KeyholderNotificationEntity();
notification.setLockId(lock.getLockId());
notification.setLockeeId(lock.getLockee());
notification.setKeyholderUserId(lock.getKeyholder());
notification.setViolationTime(LocalDateTime.now());
notification.setOvertimeMinutes(overtime);
keyholderNotificationRepository.save(notification);
}
private void addOvertime(Long overtime) {
lock.setUnlockTime(lock.getUnlockTime().plusMinutes(overtime * 4));
}
private void tempOperning(TempOpeningReason reason, Integer duration) {
assert duration != null;
lockControl.unlock();
lock.setTempOpeningReason(reason);
lock.setTempOpeningTime(LocalDateTime.now());;
lock.setTempOpeningDuration(duration);
timeLockRepository.save(lock);
unlockCodeHistoryService.save(lock.getLockee(), lock.getLockId(), lock.getName(), lock.getUnlockCode(), reason.toString());
}
}

View File

@@ -0,0 +1,54 @@
package de.oaa.xxx.games.chastity.timelock;
import org.springframework.stereotype.Service;
import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository;
import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService;
import de.oaa.xxx.games.chastity.verification.VerificationRepository;
import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository;
import de.oaa.xxx.games.history.GameHistoryRepository;
import de.oaa.xxx.user.UserRepository;
@Service
public class TimeLockServiceFactory {
private final VerificationRepository verificationRepository;
private final VerificationVoteRepository verificationVoteRepository;
private final TimeLockRepository timeLockRepository;
private final GameHistoryRepository gameHistoryRepository;
private final UserRepository userRepository;
private KeyholderNotificationRepository keyholderNotificationRepository;
private final UnlockCodeHistoryService unlockCodeHistoryService;
public TimeLockServiceFactory(VerificationRepository verificationRepository,
VerificationVoteRepository verificationVoteRepository,
TimeLockRepository timeLockRepository,
GameHistoryRepository gameHistoryRepository,
UserRepository userRepository,
KeyholderNotificationRepository keyholderNotificationRepository,
UnlockCodeHistoryService unlockCodeHistoryService) {
this.verificationRepository = verificationRepository;
this.verificationVoteRepository = verificationVoteRepository;
this.timeLockRepository = timeLockRepository;
this.gameHistoryRepository = gameHistoryRepository;
this.userRepository = userRepository;
this.keyholderNotificationRepository = keyholderNotificationRepository;
this.unlockCodeHistoryService = unlockCodeHistoryService;
}
/**
* Erstellt eine neue CardLockService-Instanz für das gegebene Lock.
*/
public TimeLockService create(TimeLockEntity lock) {
return new TimeLockService(
lock,
verificationRepository,
verificationVoteRepository,
timeLockRepository,
gameHistoryRepository,
userRepository,
keyholderNotificationRepository,
unlockCodeHistoryService
);
}
}

View File

@@ -0,0 +1,16 @@
package de.oaa.xxx.games.chastity.timelock;
import java.util.List;
import java.util.UUID;
import de.oaa.xxx.games.chastity.common.PenaltyType;
import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry;
import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskMode;
public record TimeLockTemplate(UUID templateId, UUID owner, String name, Integer minTimeInMinutes,
Integer maxTimeInMinutes, boolean endTimeVisible, Integer hygineOpeningDurationMinutes,
Integer hygineOpeningEveryMinites, List<Task> tasks, Integer taskEveryMinutes, Integer minTasksPerDay,
List<SpinningWheelEntry> spinningWheelEntries, Integer spinsEveryMinutes, Integer minSpinsPerDay,
boolean requiresVerification, TaskMode taskMode, PenaltyType penaltyType, Integer penaltyValue) {
}

View File

@@ -0,0 +1,107 @@
package de.oaa.xxx.games.chastity.timelock;
import java.util.List;
import java.util.UUID;
import de.oaa.xxx.games.chastity.common.PenaltyType;
import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelConverter;
import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry;
import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskListConverter;
import de.oaa.xxx.games.chastity.tasks.TaskMode;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
@Table(name = "timelock_template")
public class TimeLockTemplateEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column
private UUID templateId;
@Column(nullable = false)
private UUID owner;
@Column
private String name;
@Column
private Integer minTimeInMinutes;
@Column
private Integer maxTimeInMinutes;
@Column
private boolean endTimeVisible;
@Column
private Integer hygineOpeningDurationMinutes;
@Column
private Integer hygineOpeningEveryMinites;
@Convert(converter = TaskListConverter.class)
@Column(columnDefinition = "TEXT")
private List<Task> tasks;
@Column
private Integer taskEveryMinutes;
@Column
private Integer minTasksPerDay;
@Convert(converter = SpinningWheelConverter.class)
@Column(columnDefinition = "TEXT")
private List<SpinningWheelEntry> spinningWheelEntries;
@Column
private Integer spinsEveryMinutes;
@Column
private Integer minSpinsPerDay;
@Column
private boolean requiresVerification;
@Column(nullable = false)
private TaskMode taskMode = TaskMode.RANDOM;
@Column
private PenaltyType penaltyType;
@Column
private Integer penaltyValue;
public TaskMode getTaskCardMode() {
return taskMode != null ? taskMode : TaskMode.RANDOM;
}
public TimeLockTemplate toTimeLockTemplate() {
return new TimeLockTemplate(templateId, owner, name, minTimeInMinutes, maxTimeInMinutes, endTimeVisible,
hygineOpeningDurationMinutes, hygineOpeningEveryMinites, tasks, taskEveryMinutes, minTasksPerDay,
spinningWheelEntries, spinsEveryMinutes, minSpinsPerDay, requiresVerification, taskMode, penaltyType,
penaltyValue);
}
public static TimeLockTemplateEntity fromTemplate(TimeLockTemplate template) {
if (template != null) {
TimeLockTemplateEntity entity = new TimeLockTemplateEntity();
entity.setOwner(template.owner());
entity.setName(template.name());
entity.setMinTimeInMinutes(template.minTimeInMinutes());
entity.setMaxTimeInMinutes(template.maxTimeInMinutes());
entity.setEndTimeVisible(template.endTimeVisible());
entity.setHygineOpeningDurationMinutes(template.hygineOpeningDurationMinutes());
entity.setHygineOpeningEveryMinites(template.hygineOpeningEveryMinites());
entity.setTasks(template.tasks());
entity.setTaskEveryMinutes(template.taskEveryMinutes());
entity.setMinTasksPerDay(template.minTasksPerDay());
entity.setSpinningWheelEntries(template.spinningWheelEntries());
entity.setSpinsEveryMinutes(template.spinsEveryMinutes());
entity.setMinSpinsPerDay(template.minSpinsPerDay());
entity.setRequiresVerification(template.requiresVerification());
entity.setTaskMode(template.taskMode() != null ? template.taskMode() : TaskMode.RANDOM);
entity.setPenaltyType(template.penaltyType());
entity.setPenaltyValue(template.penaltyValue());
return entity;
}
return null;
}
}

View File

@@ -0,0 +1,92 @@
package de.oaa.xxx.games.chastity.timelock;
import static de.oaa.xxx.util.ValidationResult.ERROR;
import static de.oaa.xxx.util.ValidationResult.OK;
import static de.oaa.xxx.util.ValidationResult.WARNING;
import java.util.List;
import java.util.UUID;
import de.oaa.xxx.games.chastity.spinningwheel.EntryType;
import de.oaa.xxx.games.chastity.tasks.TaskMode;
import de.oaa.xxx.util.ValidationResult;
public class TimeLockTemplateService {
private TimelockTemplateRepository timelockTemplateRepository;
public TimeLockTemplateService(TimelockTemplateRepository timelockTemplateRepository) {
this.timelockTemplateRepository = timelockTemplateRepository;
}
public List<TimeLockTemplate> all(UUID ownderId) {
return timelockTemplateRepository.findByOwner(ownderId).stream().map(template -> template.toTimeLockTemplate()).toList();
}
public boolean safe(TimeLockTemplate template) {
var result = validate(template);
if (ValidationResult.OK == result || ValidationResult.WARNING == result) {
timelockTemplateRepository.save(TimeLockTemplateEntity.fromTemplate(template));
return true;
}
return false;
}
public ValidationResult validate(TimeLockTemplate template) {
if (template == null) {
return ERROR;
}
if (template.owner() == null ||
template.name() == null ||
template.maxTimeInMinutes() == null) {
return ERROR;
}
if (template.taskEveryMinutes() != null) {
if (template.tasks() == null || template.tasks().isEmpty()) {
return ERROR;
}
if (template.spinningWheelEntries() != null && !template.spinningWheelEntries().isEmpty()
&& template.spinningWheelEntries().stream().anyMatch(entry -> EntryType.TASK == entry.getType())) {
return ERROR;
}
if (template.minTasksPerDay() != null) {
int minTime = getMinimumTime(template);
if (minTime > (24*60)) {
return ERROR;
} else if (minTime > (12*60)) {
return WARNING;
}
}
} else if (template.minTasksPerDay() != null) {
return ERROR;
}
if (template.spinsEveryMinutes() != null) {
if (template.spinningWheelEntries() == null || template.spinningWheelEntries().isEmpty()) {
return ERROR;
}
if (template.minSpinsPerDay() != null) {
int minTime = template.spinsEveryMinutes() * template.minSpinsPerDay();
if (minTime > (24*60)) {
return ERROR;
} else if (minTime > (12*60)) {
return WARNING;
}
}
} else if (template.minSpinsPerDay() != null) {
return ERROR;
}
if (template.hygineOpeningEveryMinites() != null && template.hygineOpeningDurationMinutes() == null) {
return ERROR;
}
return OK;
}
private Integer getMinimumTime(TimeLockTemplate template) {
int votetime = 0;
if (TaskMode.COMMUNITY == template.taskMode() || TaskMode.KEYHOLDER == template.taskMode()) {
votetime = 60 * template.minTasksPerDay();
}
int waittime = template.taskEveryMinutes() * template.minTasksPerDay();
return votetime + waittime;
}
}

View File

@@ -0,0 +1,10 @@
package de.oaa.xxx.games.chastity.timelock;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface TimelockTemplateRepository extends JpaRepository<TimeLockTemplateEntity, UUID> {
List<TimeLockTemplateEntity> findByOwner(UUID owner);
}

View File

@@ -0,0 +1,5 @@
package de.oaa.xxx.games.chastity.unlock;
public enum TempOpeningReason {
HYGIENE, CARD, TASK;
}

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.unlock;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.unlock;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.unlock;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -23,7 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import de.oaa.xxx.games.chastity.CodeCreator; import de.oaa.xxx.games.chastity.common.CodeCreator;
import de.oaa.xxx.user.UserRepository; import de.oaa.xxx.user.UserRepository;
@RestController @RestController

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.vote;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.vote;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.vote;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List; import java.util.List;

View File

@@ -1,4 +1,4 @@
package de.oaa.xxx.games.chastity.cardlock; package de.oaa.xxx.games.chastity.vote;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@@ -0,0 +1,5 @@
package de.oaa.xxx.util;
public enum ValidationResult {
OK, INFO, WARNING, ERROR;
}

View File

@@ -0,0 +1,150 @@
package de.oaa.xxx.games.chastity.timelock;
import de.oaa.xxx.games.chastity.spinningwheel.EntryType;
import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry;
import de.oaa.xxx.games.chastity.tasks.Task;
import de.oaa.xxx.games.chastity.tasks.TaskMode;
import de.oaa.xxx.util.ValidationResult;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
class TimeLockTemplateServiceTest {
private final TimeLockTemplateService service = new TimeLockTemplateService(null);
/**
* Zentrale Factory-Methode für den Test.
* Erzeugt immer ein frisches Record-Objekt.
*/
private TimeLockTemplate createTemplate(
UUID owner, String name, Integer maxTime,
Integer taskEvery, Integer minTasks, List<Task> tasks,
Integer spinsEvery, Integer minSpins, List<SpinningWheelEntry> entries,
Integer hygEvery, Integer hygDuration) {
return createTemplate(owner, name, maxTime, taskEvery, minTasks, tasks, spinsEvery, minSpins, entries, hygEvery, hygDuration, TaskMode.RANDOM);
}
private TimeLockTemplate createTemplate(
UUID owner, String name, Integer maxTime,
Integer taskEvery, Integer minTasks, List<Task> tasks,
Integer spinsEvery, Integer minSpins, List<SpinningWheelEntry> entries,
Integer hygEvery, Integer hygDuration, TaskMode taskMode) {
return new TimeLockTemplate(null,
owner,
name,
0, // minTimeInMinutes
maxTime,
true, // endTimeVisible
hygDuration,
hygEvery,
tasks,
taskEvery,
minTasks,
entries,
spinsEvery,
minSpins,
false, // requiresVerification
taskMode,
null, // penaltyType
null // penaltyValue
);
}
// Hilfsmethode für ein schnelles Standard-Template
private TimeLockTemplate validBase() {
return createTemplate(UUID.randomUUID(), "Standard", 60, null, null, null, null, null, null, null, null);
}
@Test
void validate_OK_Minimal() {
assertThat(service.validate(validBase())).isEqualTo(ValidationResult.OK);
}
@Test
void validate_ERROR_MissingMandatoryFields() {
assertThat(service.validate(createTemplate(null, "Name", 10, null, null, null, null, null, null, null, null))).isEqualTo(ValidationResult.ERROR);
assertThat(service.validate(createTemplate(UUID.randomUUID(), null, 10, null, null, null, null, null, null, null, null))).isEqualTo(ValidationResult.ERROR);
assertThat(service.validate(createTemplate(UUID.randomUUID(), "Name", null, null, null, null, null, null, null, null, null))).isEqualTo(ValidationResult.ERROR);
}
@Test
void validate_ERROR_TasksButNoList() {
// Intervall gesetzt, aber Liste ist null oder leer
TimeLockTemplate tNull = createTemplate(UUID.randomUUID(), "T", 60, 10, null, null, null, null, null, null, null);
TimeLockTemplate tEmpty = createTemplate(UUID.randomUUID(), "T", 60, 10, null, Collections.emptyList(), null, null, null, null, null);
assertThat(service.validate(tNull)).isEqualTo(ValidationResult.ERROR);
assertThat(service.validate(tEmpty)).isEqualTo(ValidationResult.ERROR);
}
@Test
void validate_ERROR_SpinningWheelTaskConflict() {
SpinningWheelEntry taskEntry = new SpinningWheelEntry();
taskEntry.setType(EntryType.TASK);
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, 10, null, List.of(new Task()), null, null, List.of(taskEntry), null, null);
assertThat(service.validate(t)).isEqualTo(ValidationResult.ERROR);
}
@Test
void validate_WARNING_TaskTimeLimitReached() {
// 120 Min Intervall * 7 Tasks = 840 Min (> 12h)
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, 120, 7, List.of(new Task()), null, null, null, null, null);
assertThat(service.validate(t)).isEqualTo(ValidationResult.WARNING);
}
@Test
void validate_ERROR_MinTasksWithoutInterval() {
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, null, 5, null, null, null, null, null, null);
assertThat(service.validate(t)).isEqualTo(ValidationResult.ERROR);
}
@Test
void validate_ERROR_SpinsButNoEntries() {
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, null, null, null, 30, 2, null, null, null);
assertThat(service.validate(t)).isEqualTo(ValidationResult.ERROR);
}
@Test
void validate_WARNING_SpinTimeLimitReached() {
// 200 Min Intervall * 4 Spins = 800 Min (> 12h)
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, null, null, null, 200, 4, List.of(new SpinningWheelEntry()), null, null);
assertThat(service.validate(t)).isEqualTo(ValidationResult.WARNING);
}
@Test
void validate_ERROR_HygieneIncomplete() {
// Intervall gesetzt, aber Dauer fehlt
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, null, null, null, null, null, null, 60, null);
assertThat(service.validate(t)).isEqualTo(ValidationResult.ERROR);
}
@Test
public void validate_communityVotes() {
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, 60, 6, List.of(new Task()), null, null, null, null, null,TaskMode.RANDOM);
assertThat(service.validate(t)).isEqualTo(ValidationResult.OK);
t = createTemplate(UUID.randomUUID(), "T", 60, 60, 7, List.of(new Task()), null, null, null, null, null,TaskMode.COMMUNITY);
assertThat(service.validate(t)).isEqualTo(ValidationResult.WARNING);
t = createTemplate(UUID.randomUUID(), "T", 60, 60, 12, List.of(new Task()), null, null, null, null, null,TaskMode.COMMUNITY);
assertThat(service.validate(t)).isEqualTo(ValidationResult.WARNING);
}
public void testErrorOnTasksAndTaskInWheel() {
SpinningWheelEntry entry = Mockito.mock(SpinningWheelEntry.class);
Mockito.when(entry.getType()).thenReturn(EntryType.TASK);
TimeLockTemplate t = createTemplate(UUID.randomUUID(), "T", 60, 60, 6, List.of(new Task()), null, null, List.of(), null, null);
assertThat(service.validate(t)).isEqualTo(ValidationResult.ERROR);
}
}