diff options
Diffstat (limited to 'scripts/toy.groovy')
| -rw-r--r-- | scripts/toy.groovy | 216 |
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(); |
