From 73e3dcfe7e6287ade5abfbdad25d9b9cacf23b5f Mon Sep 17 00:00:00 2001
From: LittleBitwise <92210138+LittleBitwise@users.noreply.github.com>
Date: Thu, 13 Oct 2022 15:59:08 +0300
Subject: [PATCH] Create LovenseRemoteRelay.lsl
---
LovenseRemoteRelay.lsl | 223 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 223 insertions(+)
create mode 100644 LovenseRemoteRelay.lsl
diff --git a/LovenseRemoteRelay.lsl b/LovenseRemoteRelay.lsl
new file mode 100644
index 0000000..c5231a9
--- /dev/null
+++ b/LovenseRemoteRelay.lsl
@@ -0,0 +1,223 @@
+string gObjectUrl;
+string gIpAddress;
+string gDomain;
+string gToysJson;
+
+list gHttpParams = [
+ HTTP_METHOD, "POST",
+ HTTP_MIMETYPE, "application/json",
+ HTTP_VERIFY_CERT, FALSE
+];
+
+string HTML_BODY = "
+
+
+Your HUD is about to connect
to your Lovense Remote!
+Make sure the app is open and
connected to the same Wi-Fi as your computer.
+(in case you're using the app on your phone)
+If the HUD is unable to connect:
Make sure the ports are open in your router!
+(You can see which ports and local IP are needed
+when you enable Game Mode in Lovense Remote.)
+
+";
+
+string TEXT_BODY = "
+Your HUD is about to connect to your Lovense Remote!
+
+Make sure Game Mode is enabled.
+
+If you're using a mobile device:
+Make sure it's connected to the same network as your computer.
+
+If the HUD is unable to connect:
+Make sure the ports are open in your router!
+(You can see which ports and local IP are needed when you enable Game Mode in Lovense Remote.)
+";
+
+createMessage(list parts) {
+ llOwnerSay((string)parts);
+}
+
+requestUrl() {
+ createMessage(["Please wait a moment!"]);
+ llRequestSecureURL();
+}
+
+getToys() {
+ // todo: what if domain not set
+ string json = llList2Json(JSON_OBJECT, ["command", "GetToys"]);
+ llHTTPRequest(gDomain+"/command", gHttpParams, json);
+}
+
+pattern(float duration, float interval, list steps) {
+ if (0 < duration && duration < 1) duration = 1;
+ if (interval < 0.1) interval = 0.1;
+
+ integer ms = (integer)(interval * 1000);
+ integer stepCount = llGetListLength(steps);
+ if (stepCount > 50) {
+ createMessage([
+ "Patterns can only use the first 50 steps! ",
+ "(Had ", stepCount, ")"
+ ]);
+ steps = llList2List(steps, 0, 49);
+ stepCount = 50;
+ }
+
+ // llOwnerSay(llList2CSV([
+ // duration, interval, stepCount
+ // ]));
+
+ // llOwnerSay(llList2CSV([
+ // "rule", "V:1;F:v;S:" + (string)ms + "#",
+ // "strength", llDumpList2String(steps, ";")
+ // ]));
+
+ string json = llList2Json(JSON_OBJECT, [
+ "command", "Pattern",
+ "rule", "V:1;F:v;S:" + (string)ms + "#",
+ "strength", llDumpList2String(steps, ";"),
+ "timeSec", duration,
+ "apiVer", 1
+ ]);
+ llHTTPRequest(gDomain+"/command", gHttpParams, json);
+}
+
+list jsonGetkeys(string json) {
+ list data = llJson2List(json);
+ return llList2ListStrided(data, 0, -1, 2);
+}
+
+default
+{
+ state_entry()
+ {
+ requestUrl();
+ llListen(0, "", "", "");
+ }
+
+ touch_start(integer n)
+ {
+ if (gObjectUrl == "") {
+ requestUrl();
+ return;
+ }
+
+ gToysJson = "";
+ getToys();
+ }
+
+ listen(integer channel, string name, key id, string message)
+ {
+ if (id != llGetOwner()) return;
+
+ list steps = llCSV2List(message);
+ pattern(0, 0.3, steps);
+ }
+
+ http_response(key id, integer status, list metadata, string body)
+ {
+ // llOwnerSay((string)[
+ // "http_response status:", status,
+ // "http_response body: ", body
+ // ]);
+
+ if (status != 200) {
+ createMessage([
+ "Could not connect! (Code ",status,")"
+ ]);
+ if (status == 499) {
+ createMessage([
+ "Make sure the app is on and in the same network as your computer, ",
+ "and that the correct ports are forwarded to your the device's local IP."
+ ]);
+ }
+ return;
+ }
+
+ string bodyContentType = llJsonValueType(body, []);
+ if (bodyContentType != JSON_OBJECT) {
+ createMessage([
+ "Could not read toy data! (Code ",status,")"
+ ]);
+ return;
+ }
+
+ string loveCode = llJsonGetValue(body, ["code"]);
+ string loveType = llJsonGetValue(body, ["type"]);
+ if (loveCode != "200") {
+ createMessage([
+ "love error! ",
+ "(Code",loveCode,", Type: ",loveType,")"
+ ]);
+ }
+
+ string dataToys = llJsonGetValue(body, ["data", "toys"]);
+ if (dataToys != JSON_INVALID) {
+ gToysJson = body;
+ // llOwnerSay((string)[
+ // "Toy response saved"
+ // ]);
+ list toyKeys = jsonGetkeys(dataToys);
+ integer toyCount = llGetListLength(toyKeys);
+
+ list toys;
+ integer i;
+ for (i = 0; i < toyCount; ++i) {
+ string value = llJsonGetValue(dataToys, (list)llList2String(toyKeys, i));
+ // llOwnerSay((string)["toy ", i, ": ", value]);
+ if (value == JSON_INVALID) jump break;
+ if (value == JSON_NULL) jump break;
+ toys += [
+ llJsonGetValue(value, ["name"]),
+ llJsonGetValue(value, ["id"])
+ ];
+ } @break;
+
+ llOwnerSay("Toys: " + llList2CSV(toys));
+ createMessage([
+ llGetListLength(toys) / 2, " connected toys found!"
+ ]);
+ }
+ }
+
+ http_request(key id, string method, string body)
+ {
+ // llOwnerSay((string)[
+ // "http_request method: ", method, " ",
+ // "http_request body: ", body
+ // ]);
+
+ if (method == URL_REQUEST_GRANTED) {
+ gObjectUrl = body;
+ createMessage([
+ "Ready! You can now ",
+ "[",gObjectUrl," connect to Lovense Remote!]"
+ ]);
+ return;
+ } else if (method == URL_REQUEST_DENIED) {
+ createMessage([
+ "Could't get a URL for this script! ",
+ "You can try again by clicking the HUD.",
+ "Reason: ", body
+ ]);
+ return;
+ }
+
+ if (gDomain == "") {
+ gIpAddress = llGetHTTPHeader(id, "x-remote-ip");
+ gDomain = (string)["https://",gIpAddress,":30010"];
+ }
+
+ llSetContentType(id, CONTENT_TYPE_TEXT);
+ llHTTPResponse(id, 200, TEXT_BODY);
+
+ getToys();
+
+ // llOwnerSay(llList2CSV([
+ // "IP: ", gIpAddress,
+ // "Domain: ", gDomain,
+ // "JSON: ", json
+ // ]));
+ }
+}