corrade-lsl-templates – Blame information for rev 8

Subversion Repositories:
Rev:
Rev Author Line No. Line
8 office 1 ///////////////////////////////////////////////////////////////////////////
2 // Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3 //
3 ///////////////////////////////////////////////////////////////////////////
4 //
5 // A wiki module that can memorize strings and recall them by path.
6 //
7 ///////////////////////////////////////////////////////////////////////////
8  
9 ///////////////////////////////////////////////////////////////////////////
10 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
11 ///////////////////////////////////////////////////////////////////////////
12 integer wasIsAlNum(string a) {
13 if(a == "") return FALSE;
14 integer x = llBase64ToInteger("AAAA" +
15 llStringToBase64(llGetSubString(a, 0, 0)));
16 return (x >= 65 && x <= 90) || (x >= 97 && x <= 122) ||
17 (x >= 48 && x <= 57);
18 }
19 ///////////////////////////////////////////////////////////////////////////
20 // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
21 ///////////////////////////////////////////////////////////////////////////
22 string wasKeyValueGet(string k, string data) {
23 if(llStringLength(data) == 0) return "";
24 if(llStringLength(k) == 0) return "";
25 list a = llParseString2List(data, ["&", "="], []);
26 integer i = llListFindList(a, [ k ]);
27 if(i != -1) return llList2String(a, i+1);
28 return "";
29 }
30  
31 ///////////////////////////////////////////////////////////////////////////
32 // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
33 ///////////////////////////////////////////////////////////////////////////
34 string wasKeyValueEncode(list data) {
35 list k = llList2ListStrided(data, 0, -1, 2);
36 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
37 data = [];
38 do {
39 data += llList2String(k, 0) + "=" + llList2String(v, 0);
40 k = llDeleteSubList(k, 0, 0);
41 v = llDeleteSubList(v, 0, 0);
42 } while(llGetListLength(k) != 0);
43 return llDumpList2String(data, "&");
44 }
45  
46 ///////////////////////////////////////////////////////////////////////////
47 // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 //
48 ///////////////////////////////////////////////////////////////////////////
49 // http://was.fm/secondlife/wanderer
50 vector wasCirclePoint(float radius) {
51 float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
52 float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
53 if(llPow(x,2) + llPow(y,2) <= llPow(radius,2))
54 return <x, y, 0>;
55 return wasCirclePoint(radius);
56 }
57  
58 ///////////////////////////////////////////////////////////////////////////
59 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
60 ///////////////////////////////////////////////////////////////////////////
61 // escapes a string in conformance with RFC1738
62 string wasURLEscape(string i) {
63 string o = "";
64 do {
65 string c = llGetSubString(i, 0, 0);
66 i = llDeleteSubString(i, 0, 0);
67 if(c == "") jump continue;
68 if(c == " ") {
69 o += "+";
70 jump continue;
71 }
72 if(c == "\n") {
73 o += "%0D" + llEscapeURL(c);
74 jump continue;
75 }
76 o += llEscapeURL(c);
77 @continue;
78 } while(i != "");
79 return o;
80 }
81  
82 ///////////////////////////////////////////////////////////////////////////
83 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
84 ///////////////////////////////////////////////////////////////////////////
85 list wasCSVToList(string csv) {
86 list l = [];
87 list s = [];
88 string m = "";
89 do {
90 string a = llGetSubString(csv, 0, 0);
91 csv = llDeleteSubString(csv, 0, 0);
92 if(a == ",") {
93 if(llList2String(s, -1) != "\"") {
94 l += m;
95 m = "";
96 jump continue;
97 }
98 m += a;
99 jump continue;
100 }
101 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
102 m += a;
103 csv = llDeleteSubString(csv, 0, 0);
104 jump continue;
105 }
106 if(a == "\"") {
107 if(llList2String(s, -1) != a) {
108 s += a;
109 jump continue;
110 }
111 s = llDeleteSubList(s, -1, -1);
112 jump continue;
113 }
114 m += a;
115 @continue;
116 } while(csv != "");
117 // postcondition: length(s) = 0
118 return l + m;
119 }
120  
121 ///////////////////////////////////////////////////////////////////////////
122 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
123 ///////////////////////////////////////////////////////////////////////////
124 string wasListToCSV(list l) {
125 list v = [];
126 do {
127 string a = llDumpList2String(
128 llParseStringKeepNulls(
129 llList2String(
130 l,
131  
132 ),
133 ["\""],
134 []
135 ),
136 "\"\""
137 );
138 if(llParseStringKeepNulls(
139 a,
140 [" ", ",", "\n", "\""], []
141 ) !=
142 (list) a
143 ) a = "\"" + a + "\"";
144 v += a;
145 l = llDeleteSubList(l, 0, 0);
146 } while(l != []);
147 return llDumpList2String(v, ",");
148 }
149  
150 ///////////////////////////////////////////////////////////////////////////
151 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
152 ///////////////////////////////////////////////////////////////////////////
153 // unescapes a string in conformance with RFC1738
154 string wasURLUnescape(string i) {
155 return llUnescapeURL(
156 llDumpList2String(
157 llParseString2List(
158 llDumpList2String(
159 llParseString2List(
160 i,
161 ["+"],
162 []
163 ),
164 " "
165 ),
166 ["%0D%0A"],
167 []
168 ),
169 "\n"
170 )
171 );
172 }
173  
174 // configuration data
175 string configuration = "";
176 // callback URL
177 string URL = "";
178 // store message over state.
179 string data = "";
180 string path = "";
181 string jump_state = "";
182  
183 default {
184 state_entry() {
185 llOwnerSay("[Wiki] Starting...");
186 llSetTimerEvent(10);
187 }
188 link_message(integer sender, integer num, string message, key id) {
189 if(id != "configuration") return;
190 llOwnerSay("[Wiki] Got configuration...");
191 configuration = message;
192 jump_state = "create_database";
193 state url;
194 }
195 timer() {
196 llOwnerSay("[Wiki] Requesting configuration...");
197 llMessageLinked(LINK_THIS, 0, "configuration", NULL_KEY);
198 }
199 on_rez(integer num) {
200 llResetScript();
201 }
202 changed(integer change) {
203 if((change & CHANGED_INVENTORY) ||
204 (change & CHANGED_REGION_START) ||
205 (change & CHANGED_OWNER)) {
206 llResetScript();
207 }
208 }
209 state_exit() {
210 llSetTimerEvent(0);
211 }
212 }
213  
214 state url {
215 state_entry() {
216 // DEBUG
217 llOwnerSay("[Wiki] Requesting URL...");
218 llRequestURL();
219 }
220 http_request(key id, string method, string body) {
221 if(method != URL_REQUEST_GRANTED) return;
222 URL = body;
223 // DEBUG
224 llOwnerSay("[Wiki] Got URL...");
225 if(jump_state == "create_database")
226 state create_database;
227 if(jump_state == "get")
228 state get;
229 if(jump_state == "set")
230 state set;
231 if(jump_state == "dir")
232 state dir;
233 if(jump_state == "listen_group")
234 state listen_group;
235  
236 // DEBUG
237 llOwnerSay("[Wiki] Jump table corrupted, please contact creator...");
238 llResetScript();
239 }
240 on_rez(integer num) {
241 llResetScript();
242 }
243 changed(integer change) {
244 if((change & CHANGED_INVENTORY) ||
245 (change & CHANGED_REGION_START) ||
246 (change & CHANGED_OWNER)) {
247 llResetScript();
248 }
249 }
250 }
251  
252 state create_database {
253 state_entry() {
254 // DEBUG
255 llOwnerSay("[Wiki] Creating database.");
256 llInstantMessage(
257 wasKeyValueGet(
258 "corrade",
259 configuration
260 ),
261 wasKeyValueEncode(
262 [
263 "command", "database",
264 "group", wasURLEscape(
265 wasKeyValueGet(
266 "group",
267 configuration
268 )
269 ),
270 "password", wasURLEscape(
271 wasKeyValueGet(
272 "password",
273 configuration
274 )
275 ),
276 "SQL", wasURLEscape("CREATE TABLE IF NOT EXISTS corradeeggdrop (path text unique collate nocase, data text)"),
277 "callback", wasURLEscape(URL)
278 ]
279 )
280 );
281 llSetTimerEvent(60);
282 }
283 http_request(key id, string method, string body) {
284 llHTTPResponse(id, 200, "OK");
285 llReleaseURL(URL);
286 if(wasKeyValueGet("command", body) != "database" ||
287 wasKeyValueGet("success", body) != "True") {
288 // DEBUG
289 llOwnerSay("[Wiki] Unable modify database: " +
290 wasURLUnescape(
291 wasKeyValueGet("error", body)
292 )
293 );
294 state listen_group;
295 }
296 llOwnerSay("[Wiki] Database created!");
297 state listen_group;
298 }
299 timer() {
300 llReleaseURL(URL);
301 state listen_group;
302 }
303 on_rez(integer num) {
304 llResetScript();
305 }
306 changed(integer change) {
307 if((change & CHANGED_INVENTORY) ||
308 (change & CHANGED_REGION_START) ||
309 (change & CHANGED_OWNER)) {
310 llResetScript();
311 }
312 }
313 state_exit() {
314 llSetTimerEvent(0);
315 }
316 }
317  
318 state listen_group {
319 state_entry() {
320 // DEBUG
321 llOwnerSay("[Wiki] Waiting for group messages...");
322 }
323 link_message(integer sender, integer num, string message, key id) {
324 // We only care about notifications now.
325 if(id != "notification")
326 return;
327  
328 // This script only processes group notifications.
329 if(wasKeyValueGet("type", message) != "group")
330 return;
331  
332 // Get the sent message.
333 data = wasURLUnescape(
334 wasKeyValueGet(
335 "message",
336 message
337 )
338 );
339  
340 if(llGetSubString(data, 0, 0) !=
341 wasKeyValueGet("command", configuration))
342 return;
343  
344 list command = llParseString2List(data, ["@", " "], []);
345 if(llList2String(command, 0) != "wiki")
346 return;
347  
348 // Remove command.
349 command = llDeleteSubList(command, 0, 0);
350  
351 // Check for supported sub-commands.
352 if(llList2String(command, 0) != "set" &&
353 llList2String(command, 0) != "get" &&
354 llList2String(command, 0) != "dir") {
355 data = "Subcommands are: get, set, dir";
356 state tell;
357 }
358  
359 // Get the sub-command and store it as a jump state.
360 jump_state = llList2String(command, 0);
361  
362 // Remove sub-command.
363 command = llDeleteSubList(command, 0, 0);
364  
365 // Get the path parts.
366 list path_parts = llParseString2List(
367 llList2String(command, 0), ["/"], []
368 );
369  
370 // Dump the path and store it over states.
371 path = llStringTrim(
372 llDumpList2String(
373 path_parts,
374 "/"
375 ),
376 STRING_TRIM
377 );
378  
379 if(path != "") {
380 integer i = llStringLength(path) - 1;
381 do {
382 string c = llGetSubString(path, i, i);
383 if(c != "/" && !wasIsAlNum(c)) {
384 data = "Only alpha-numerics accepted in the path string.";
385 state tell;
386 }
387 } while(--i > -1);
388 }
389  
390 path = "/" + path;
391  
392 // Remove path.
393 command = llDeleteSubList(command, 0, 0);
394  
395 // Dump the rest of the message.
396 data = llDumpList2String(command, " ");
397  
398 // Get an URL.
399 state url;
400 }
401 on_rez(integer num) {
402 llResetScript();
403 }
404 changed(integer change) {
405 if((change & CHANGED_INVENTORY) ||
406 (change & CHANGED_REGION_START) ||
407 (change & CHANGED_OWNER)) {
408 llResetScript();
409 }
410 }
411 }
412  
413 state set {
414 state_entry() {
415 if(data == "") {
416 // DEBUG
417 llOwnerSay("[Wiki] Removing from database.");
418 llInstantMessage(
419 wasKeyValueGet(
420 "corrade",
421 configuration
422 ),
423 wasKeyValueEncode(
424 [
425 "command", "database",
426 "group", wasURLEscape(
427 wasKeyValueGet(
428 "group",
429 configuration
430 )
431 ),
432 "password", wasURLEscape(
433 wasKeyValueGet(
434 "password",
435 configuration
436 )
437 ),
438 "SQL", wasURLEscape("DELETE FROM corradeeggdrop WHERE path=:path"),
439 "data", wasListToCSV(
440 [
441 "path",
442 wasURLEscape("path")
443 ]
444 ),
445 "callback", wasURLEscape(URL)
446 ]
447 )
448 );
449 llSetTimerEvent(60);
450 return;
451 }
452 // DEBUG
453 llOwnerSay("[Wiki] Adding to database.");
454 llInstantMessage(
455 wasKeyValueGet(
456 "corrade",
457 configuration
458 ),
459 wasKeyValueEncode(
460 [
461 "command", "database",
462 "group", wasURLEscape(
463 wasKeyValueGet(
464 "group",
465 configuration
466 )
467 ),
468 "password", wasURLEscape(
469 wasKeyValueGet(
470 "password",
471 configuration
472 )
473 ),
474 "SQL", wasURLEscape("REPLACE INTO corradeeggdrop (path, data) VALUES (:path, :data)"),
475 "data", wasListToCSV(
476 [
477 "path",
478 wasURLEscape("path"),
479 "data",
480 wasURLEscape("data")
481 ]
482 ),
483 "callback", wasURLEscape(URL)
484 ]
485 )
486 );
487 llSetTimerEvent(60);
488 }
489 http_request(key id, string method, string body) {
490 llHTTPResponse(id, 200, "OK");
491 llReleaseURL(URL);
492 if(wasKeyValueGet("command", body) != "database" ||
493 wasKeyValueGet("success", body) != "True") {
494 // DEBUG
495 llOwnerSay("[Wiki] Unable modify database: " +
496 wasURLUnescape(
497 wasKeyValueGet("error", body)
498 )
499 );
500 state listen_group;
501 }
502 if(data == "") {
503 data = "Deleted from " + path;
504 state tell;
505 }
506 data = "Stored into " + path;
507 state tell;
508 }
509 timer() {
510 llReleaseURL(URL);
511 state listen_group;
512 }
513 on_rez(integer num) {
514 llResetScript();
515 }
516 changed(integer change) {
517 if((change & CHANGED_INVENTORY) ||
518 (change & CHANGED_REGION_START) ||
519 (change & CHANGED_OWNER)) {
520 llResetScript();
521 }
522 }
523 state_exit() {
524 llSetTimerEvent(0);
525 }
526 }
527  
528 state get {
529 state_entry() {
530 // DEBUG
531 llOwnerSay("[Wiki] Retrieving from database.");
532 llInstantMessage(
533 wasKeyValueGet(
534 "corrade",
535 configuration
536 ),
537 wasKeyValueEncode(
538 [
539 "command", "database",
540 "group", wasURLEscape(
541 wasKeyValueGet(
542 "group",
543 configuration
544 )
545 ),
546 "password", wasURLEscape(
547 wasKeyValueGet(
548 "password",
549 configuration
550 )
551 ),
552 "SQL", wasURLEscape("SELECT data FROM corradeeggdrop WHERE path=:path"),
553 "data", wasListToCSV(
554 [
555 "path",
556 wasURLEscape(path)
557 ]
558 ),
559 "callback", wasURLEscape(URL)
560 ]
561 )
562 );
563 llSetTimerEvent(60);
564 }
565 http_request(key id, string method, string body) {
566 llHTTPResponse(id, 200, "OK");
567 llReleaseURL(URL);
568 if(wasKeyValueGet("command", body) != "database" ||
569 wasKeyValueGet("success", body) != "True") {
570 // DEBUG
571 llOwnerSay("[Wiki] Unable retrieve from database: " +
572 wasURLUnescape(
573 wasKeyValueGet("error", body)
574 )
575 );
576 state listen_group;
577 }
578  
579 data = llDumpList2String(
580 llDeleteSubList(
581 wasCSVToList(
582 wasURLUnescape(
583 wasKeyValueGet("data", body)
584 )
585 ),
586 0,
587  
588 ),
589 ""
590 );
591  
592 if(data == "") {
593 data = "Sorry, that path contains no data.";
594 state tell;
595 }
596  
597 data = path + ": " + data;
598 state tell;
599 }
600 timer() {
601 llReleaseURL(URL);
602 state listen_group;
603 }
604 on_rez(integer num) {
605 llResetScript();
606 }
607 changed(integer change) {
608 if((change & CHANGED_INVENTORY) ||
609 (change & CHANGED_REGION_START) ||
610 (change & CHANGED_OWNER)) {
611 llResetScript();
612 }
613 }
614 state_exit() {
615 llSetTimerEvent(0);
616 }
617 }
618  
619 state dir {
620 state_entry() {
621 // DEBUG
622 llOwnerSay("[Wiki] Listing paths from database.");
623 llInstantMessage(
624 wasKeyValueGet(
625 "corrade",
626 configuration
627 ),
628 wasKeyValueEncode(
629 [
630 "command", "database",
631 "group", wasURLEscape(
632 wasKeyValueGet(
633 "group",
634 configuration
635 )
636 ),
637 "password", wasURLEscape(
638 wasKeyValueGet(
639 "password",
640 configuration
641 )
642 ),
643 "SQL", wasURLEscape("SELECT path FROM corradeeggdrop WHERE path like :path"),
644 "data", wasListToCSV(
645 [
646 "path",
647 wasURLEscape(path + "%")
648 ]
649 ),
650 "callback", wasURLEscape(URL)
651 ]
652 )
653 );
654 llSetTimerEvent(60);
655 }
656 http_request(key id, string method, string body) {
657 llHTTPResponse(id, 200, "OK");
658 llReleaseURL(URL);
659 if(wasKeyValueGet("command", body) != "database" ||
660 wasKeyValueGet("success", body) != "True") {
661 // DEBUG
662 llOwnerSay("[Wiki] Unable retrieve from database: " +
663 wasURLUnescape(
664 wasKeyValueGet("error", body)
665 )
666 );
667 state listen_group;
668 }
669  
670 list paths = llList2ListStrided(
671 llDeleteSubList(
672 wasCSVToList(
673 wasURLUnescape(
674 wasKeyValueGet("data", body)
675 )
676 ),
677 0,
678  
679 ),
680 0,
681 -1,
682 2
683 );
684  
685 if(llGetListLength(paths) == 0) {
686 data = "Sorry, that path contains no sub-paths.";
687 state tell;
688 }
689  
690 // Eliminate path component.
691 if(path == "/")
692 path = "";
693  
694 list sibling = [];
695 do {
696 // Get the path part.
697 string part = llList2String(paths, 0);
698  
699 // Remove the path component.
700 string child = llStringTrim(
701 llDumpList2String(
702 llParseString2List(
703 part,
704 [path, "/"],
705 []
706 ),
707 "/"
708 ),
709 STRING_TRIM
710  
711 );
712  
713 integer i = llSubStringIndex(child, "/");
714 if(i == -1) {
715 sibling += path + "/" + child;
716 jump continue;
717 }
718 child = path + "/" + llDeleteSubString(child, i, -1) + "/";
719 if(llListFindList(sibling, (list)child) == -1)
720 sibling += child;
721 @continue;
722 paths = llDeleteSubList(paths, 0, 0);
723 } while(llGetListLength(paths) != 0);
724  
725 data = llList2CSV(sibling);
726 // GC
727 sibling = [];
728  
729 state tell;
730 }
731 timer() {
732 llReleaseURL(URL);
733 state listen_group;
734 }
735 on_rez(integer num) {
736 llResetScript();
737 }
738 changed(integer change) {
739 if((change & CHANGED_INVENTORY) ||
740 (change & CHANGED_REGION_START) ||
741 (change & CHANGED_OWNER)) {
742 llResetScript();
743 }
744 }
745 state_exit() {
746 llSetTimerEvent(0);
747 }
748 }
749  
750 state tell {
751 state_entry() {
752 // DEBUG
753 llOwnerSay("[Wiki] Sending to group.");
754 llInstantMessage(
755 wasKeyValueGet(
756 "corrade",
757 configuration
758 ),
759 wasKeyValueEncode(
760 [
761 "command", "tell",
762 "group", wasURLEscape(
763 wasKeyValueGet(
764 "group",
765 configuration
766 )
767 ),
768 "password", wasURLEscape(
769 wasKeyValueGet(
770 "password",
771 configuration
772 )
773 ),
774 "entity", "group",
775 "message", wasURLEscape(data)
776 ]
777 )
778 );
779 state listen_group;
780 }
781 }