summaryrefslogtreecommitdiff
path: root/scripts/toy.groovy
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/toy.groovy')
-rw-r--r--scripts/toy.groovy216
1 files changed, 153 insertions, 63 deletions
diff --git a/scripts/toy.groovy b/scripts/toy.groovy
index bba3932..5624a29 100644
--- a/scripts/toy.groovy
+++ b/scripts/toy.groovy
@@ -8,10 +8,9 @@
// This script, from https://ss.deviatenow.com, is protected by this licence :
// CC by-NC, see http://creativecommons.org/licenses/by-nc/3.0/
//
-setInfos(9, "Toy", "Become my new plaything.", "rascalDan", "WIP", 0xFFFFFF, "en", ["bondage", "femaledom", "formale", "long", "pain", "toys", "joi"]);
+setInfos(9, "Toy II", "Become my new plaything.", "rascalDan", "WIP", 0xFFFFFF, "en", ["bondage", "femaledom", "formale", "maledom", "forfemale", "long", "pain", "humiliation", "toys", "joi", "AI"]);
return new Object() {
- final VERSION = 1;
final DAY = 86400;
final HOUR = 3600.0;
final soonFormatter = java.time.format.DateTimeFormatter.ofPattern("EEEE 'at' h:mma");
@@ -47,18 +46,7 @@ return new Object() {
final DRESSED = "dressed", TITS = "tits", PUSSY = "pussy", LINGERIE = "lingerie", TEASE = "tease", SIT = "sit", BOOTS = "boots", KNEEL = "kneel", STOOD = "stood", MEAN = "mean", CROP = "crop", ASS = "ass", SQUAT = "squat", SSSH = "sssh";
final nDRESSED = "!$DRESSED", nTITS = "!$TITS", nTEASE = "!$TEASE", nKNEEL = "!$KNEEL", nSQUAT = "!$SQUAT";
final getDomme = { domme, set = null ->
- final readDomme = { path ->
- if (!path) return [:];
- final f = new File(path);
- if (!f.exists()) return [:];
- return Eval.me(f.text);
- };
- def d = [
- "$DATAFOLDER/images/toy/domme.groovy",
- "$DATAFOLDER/images/toy/$domme/person.groovy",
- "$DATAFOLDER/images/toy/$domme/$set/set.groovy" ]
- .collect { readDomme(it) }
- .sum();
+ def d = parseJsonFile("images/toy/$domme/person.json");
d.id = domme;
return d;
};
@@ -70,8 +58,10 @@ return new Object() {
.listFiles()
.collect { s ->
Eval.me(s.text)(toy);
- }
- .findAll { p -> p };
+ };
+ };
+ final eachModule = { modules, method, ... args ->
+ modules.collect { it[method]?.call(args) };
};
// Utils
@@ -232,22 +222,6 @@ return new Object() {
}
}
- def sessionAborted = null;
- def sessionToys = [:];
- final gagText = { t, p ->
- if (!stateIs(GAGGED)) return t.toString();
- return t.split(/\s+/)
- .collect {
- 'm' * getRandom((int)Math.max(1.0, it.length() * 1.0)) +
- 'p' * getRandom((int)Math.max(1.0, it.length() * 0.4)) +
- 'h' * getRandom((int)Math.max(1.0, it.length() * 0.8))
- }
- .join(" ")
- .capitalize() + " ($p)";
- };
- final showButtonG = { s, p, t = null ->
- return t != null ? showButton(gagText(s, p), t) : showButton(gagText(s, p));
- };
final compose = { texts ->
if (!texts || texts.isEmpty()) return null;
return texts.collect {
@@ -263,22 +237,19 @@ return new Object() {
}
return null;
};
- final showButtonGT = { s, p, t, pc ->
- def tt = showButton(gagText(s, p), t);
- if (tt == t) {
- present(null, [
- ["Don't keep me waiting!", "Hurry up!", "Quicker, toy!"]]);
- def pt = showButton(gagText(s, p));
- adjustPunish(pt * pc);
- return pt + tt;
- }
- return tt;
+ final llmPresent = { text ->
+ show(text);
+ def image = showImage(callAPI_imageMatch(text));
+ return [
+ message: text,
+ image: image
+ ];
};
final matchTags = { spec ->
return { obj ->
- spec.every({ s ->
- return (s[0] == "!") ? obj.tags.indexOf(s.drop(1)) == -1 : obj.tags.indexOf(s) != -1;
- });
+ return spec.collect({ tag, score ->
+ return (obj.tags.contains(tag.toString()) ? 1 : -1) * score;
+ }).sum();
};
};
@@ -297,32 +268,26 @@ return new Object() {
};
// Session
- final sessionSummon = { imageSpec ->
+ final summon = { messages ->
final limit = 5;
final timeMax = 10;
executeTrigger("toySummoned");
for (def n = 1; n <= limit; n++) {
switch (n) {
- case limit:
- present(imageSpec, [
- ["TOY!", "SLAVE!", "SLUT!"],
- ["Last chance!", "Final warning!"]]);
- adjustPunish(5);
+ case 1:
+ messages += [role: "user", content: "I am in another room. Write only a demand from my presence."];
break;
- case limit - 1:
- case limit - 2:
- present(imageSpec, [
- ["Toy!", "Slave!", "Slut!"],
- ["Where are you!?", "Get yourself here, now!"]]);
+ case limit:
+ messages += [role: "user", content: "I am still in another. Write only a final demand from my presence."];
break;
default:
- present(imageSpec, [
- ["Toy!", "Slave!", "Slut!"]]);
+ messages += [role: "user", content: "I am still in another. Write only another demand from my presence."];
break;
}
- playBackgroundSound(n == limit ? "shortwhip.wav" : "bell.wav");
- if (showButton("Yes, ${dommeTitle()}?", timeMax) < timeMax) {
+ messages += [role: "assistant", content: llmPresent(callAPI_chat(messages)).message];
+ //playBackgroundSound(n == limit ? "shortwhip.wav" : "bell.wav");
+ if (showButton("...", timeMax) < timeMax) {
executeTrigger("toySummonSuccess", n);
return true;
};
@@ -358,17 +323,142 @@ return new Object() {
requestables.remove(name);
};
+ // JSON helpers
+ final parseJsonFile = { path ->
+ return new groovy.json.JsonSlurper().parse(new File(path));
+ }
+
+ final parseJsonStream = { stream ->
+ return new groovy.json.JsonSlurper().parse(stream);
+ }
+
+ final parseJsonString = { string ->
+ return new groovy.json.JsonSlurper().parseText(string);
+ }
+
+ // LLM Rest API helpers
+ final callAPI = { String method, Object payload = null ->
+ def url = new URL("${loadString("toy.baseURL")}/$method");
+ def conn = url.openConnection() as HttpURLConnection;
+ conn.requestMethod = payload ? 'POST' : 'GET';
+ conn.doOutput = true;
+ conn.setRequestProperty('Content-Type', 'application/json; charset=UTF-8');
+ conn.setRequestProperty('Accept', 'application/json');
+ conn.setRequestProperty('Accept-Language', 'en');
+ conn.connectTimeout = 5000;
+ if (payload) {
+ conn.outputStream.withWriter('UTF-8') { writer ->
+ writer.write(new groovy.json.JsonOutput().toJson(payload));
+ }
+ }
+ if (conn.responseCode / 100 == 2) {
+ return parseJsonStream(conn.inputStream);
+ }
+ conn.disconnect();
+ showPopup("URL: ${url}\nPayload: ${payload}\nStatus code: ${conn.responseCode}\nResponse: ${conn.errorStream?.text}");
+ return null;
+ }
+
+ final callAPI_loadModel = { model ->
+ callAPI("models/load", [
+ model: model
+ ])
+ }
+
+ final callAPI_unloadModel = { model ->
+ callAPI("models/unload", [
+ model: model
+ ])
+ }
+
+ final callAPI_getModel = { model ->
+ callAPI("models").data.find { m -> m.id == model }
+ }
+
+ final callAPI_getModelStatus = { model ->
+ callAPI("models").data.find { m -> m.id == model }?.status?.value
+ }
+
+ final callAPI_chat = { messages ->
+ return callAPI("chat/completions", [
+ model: loadString("toy.chatModel"),
+ stop: "<",
+ messages: messages
+ ])?.choices?[0]?.message?.content;
+ }
+
+ final callAPI_chatJSON = { messages, prompt, jspec ->
+ messages += [role: "user", content: """
+Write only a JSON object.
+${prompt}
+ {
+${jspec.collect{ k, v -> "\"${k}\": ${v}" }.join(",\n")}
+ }"""];
+ return parseJsonString(callAPI("chat/completions", [
+ model: loadString("toy.chatModel"),
+ stop: "<",
+ messages: messages
+ ])?.choices?[0]?.message?.content);
+ }
+
+ // TODO add module support
+ final chatInitialMessages = { String ... extra ->
+ def messages = [
+ [role: "system", content: """
+Identify as ${DOMME.title} ${DOMME.fullName}.
+You are ${DOMME.title} ${DOMME.fullName}.
+Your responses should be kept short.
+Speak in the first person only.
+Forbidden from using quotes.
+You are permanently in role-play mode as ${DOMME.title} ${DOMME.fullName}.
+${DOMME.prompt}
+${loadString("intro.name")} is your slave.
+${extra.join('\n')}
+ """],
+ [role: "user", content: "I am ${loadString("intro.name")}"]
+ ];
+ def relationship = loadString("toy.${DOMME.id}.relationship");
+ if (relationship) {
+ messages += [role: "assistant", content: relationship];
+ }
+ return messages;
+ }
+
+ final callAPI_imageMatch = { message ->
+ def tags = [DRESSED, TITS, PUSSY, LINGERIE, TEASE, SIT, BOOTS, KNEEL, STOOD, MEAN, CROP, ASS, SQUAT, SSSH];
+ def request = chatInitialMessages();
+ request += [role: "assistant", content: message]
+ request += [role: "user", content: """
+Write only a JSON object.
+Complete the following JSON object, scoring each keyword between -10 and 10 against the previous messsage:
+{
+ ${ tags.collect { tag -> "\"$tag\": number" }.join(',\n') }
+}
+ """ ];
+ return parseJsonString(callAPI("chat/completions", [
+ model: loadString("toy.chatModel"),
+ stop: "<",
+ messages: request
+ ])?.choices?[0]?.message?.content);
+ }
String main()
{
// GO!
setDefault("toy.owner", "ancilla");
+ setDefault("toy.baseURL", "http://localhost:8080");
+ setDefault("toy.chatModel", "Lewdiculous/Erosumika-7B-v3-0.2-GGUF-IQ-Imatrix:IMAT");
+
OWNER = loadString("toy.owner");
loadDomme(OWNER);
- def moduleSetup = loadModules(this);
- setDefault("toy.punishment", 0);
+ def modules = loadModules(this);
execEvents(false);
- moduleSetup.each { p -> p() };
+ eachModule(modules, "setup");
+
+ if (!loadEvents()) {
+ show("Nothing to do.");
+ return;
+ }
final cycleTime = 60;
showLounge();