corrade-lsl-templates – Blame information for rev 10

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  
10 office 344 list command = llParseString2List(data,
345 [wasKeyValueGet("command", configuration), " "], []);
8 office 346 if(llList2String(command, 0) != "wiki")
347 return;
348  
349 // Remove command.
350 command = llDeleteSubList(command, 0, 0);
351  
352 // Check for supported sub-commands.
353 if(llList2String(command, 0) != "set" &&
354 llList2String(command, 0) != "get" &&
355 llList2String(command, 0) != "dir") {
356 data = "Subcommands are: get, set, dir";
357 state tell;
358 }
359  
360 // Get the sub-command and store it as a jump state.
361 jump_state = llList2String(command, 0);
362  
363 // Remove sub-command.
364 command = llDeleteSubList(command, 0, 0);
365  
366 // Get the path parts.
367 list path_parts = llParseString2List(
368 llList2String(command, 0), ["/"], []
369 );
370  
371 // Dump the path and store it over states.
372 path = llStringTrim(
373 llDumpList2String(
374 path_parts,
375 "/"
376 ),
377 STRING_TRIM
378 );
379  
380 if(path != "") {
381 integer i = llStringLength(path) - 1;
382 do {
383 string c = llGetSubString(path, i, i);
384 if(c != "/" && !wasIsAlNum(c)) {
385 data = "Only alpha-numerics accepted in the path string.";
386 state tell;
387 }
388 } while(--i > -1);
389 }
390  
391 path = "/" + path;
392  
393 // Remove path.
394 command = llDeleteSubList(command, 0, 0);
395  
396 // Dump the rest of the message.
397 data = llDumpList2String(command, " ");
398  
399 // Get an URL.
400 state url;
401 }
402 on_rez(integer num) {
403 llResetScript();
404 }
405 changed(integer change) {
406 if((change & CHANGED_INVENTORY) ||
407 (change & CHANGED_REGION_START) ||
408 (change & CHANGED_OWNER)) {
409 llResetScript();
410 }
411 }
412 }
413  
414 state set {
415 state_entry() {
416 if(data == "") {
417 // DEBUG
418 llOwnerSay("[Wiki] Removing from database.");
419 llInstantMessage(
420 wasKeyValueGet(
421 "corrade",
422 configuration
423 ),
424 wasKeyValueEncode(
425 [
426 "command", "database",
427 "group", wasURLEscape(
428 wasKeyValueGet(
429 "group",
430 configuration
431 )
432 ),
433 "password", wasURLEscape(
434 wasKeyValueGet(
435 "password",
436 configuration
437 )
438 ),
439 "SQL", wasURLEscape("DELETE FROM corradeeggdrop WHERE path=:path"),
440 "data", wasListToCSV(
441 [
442 "path",
443 wasURLEscape("path")
444 ]
445 ),
446 "callback", wasURLEscape(URL)
447 ]
448 )
449 );
450 llSetTimerEvent(60);
451 return;
452 }
453 // DEBUG
454 llOwnerSay("[Wiki] Adding to database.");
455 llInstantMessage(
456 wasKeyValueGet(
457 "corrade",
458 configuration
459 ),
460 wasKeyValueEncode(
461 [
462 "command", "database",
463 "group", wasURLEscape(
464 wasKeyValueGet(
465 "group",
466 configuration
467 )
468 ),
469 "password", wasURLEscape(
470 wasKeyValueGet(
471 "password",
472 configuration
473 )
474 ),
475 "SQL", wasURLEscape("REPLACE INTO corradeeggdrop (path, data) VALUES (:path, :data)"),
476 "data", wasListToCSV(
477 [
478 "path",
479 wasURLEscape("path"),
480 "data",
481 wasURLEscape("data")
482 ]
483 ),
484 "callback", wasURLEscape(URL)
485 ]
486 )
487 );
488 llSetTimerEvent(60);
489 }
490 http_request(key id, string method, string body) {
491 llHTTPResponse(id, 200, "OK");
492 llReleaseURL(URL);
493 if(wasKeyValueGet("command", body) != "database" ||
494 wasKeyValueGet("success", body) != "True") {
495 // DEBUG
496 llOwnerSay("[Wiki] Unable modify database: " +
497 wasURLUnescape(
498 wasKeyValueGet("error", body)
499 )
500 );
501 state listen_group;
502 }
503 if(data == "") {
504 data = "Deleted from " + path;
505 state tell;
506 }
507 data = "Stored into " + path;
508 state tell;
509 }
510 timer() {
511 llReleaseURL(URL);
512 state listen_group;
513 }
514 on_rez(integer num) {
515 llResetScript();
516 }
517 changed(integer change) {
518 if((change & CHANGED_INVENTORY) ||
519 (change & CHANGED_REGION_START) ||
520 (change & CHANGED_OWNER)) {
521 llResetScript();
522 }
523 }
524 state_exit() {
525 llSetTimerEvent(0);
526 }
527 }
528  
529 state get {
530 state_entry() {
531 // DEBUG
532 llOwnerSay("[Wiki] Retrieving from database.");
533 llInstantMessage(
534 wasKeyValueGet(
535 "corrade",
536 configuration
537 ),
538 wasKeyValueEncode(
539 [
540 "command", "database",
541 "group", wasURLEscape(
542 wasKeyValueGet(
543 "group",
544 configuration
545 )
546 ),
547 "password", wasURLEscape(
548 wasKeyValueGet(
549 "password",
550 configuration
551 )
552 ),
553 "SQL", wasURLEscape("SELECT data FROM corradeeggdrop WHERE path=:path"),
554 "data", wasListToCSV(
555 [
556 "path",
557 wasURLEscape(path)
558 ]
559 ),
560 "callback", wasURLEscape(URL)
561 ]
562 )
563 );
564 llSetTimerEvent(60);
565 }
566 http_request(key id, string method, string body) {
567 llHTTPResponse(id, 200, "OK");
568 llReleaseURL(URL);
569 if(wasKeyValueGet("command", body) != "database" ||
570 wasKeyValueGet("success", body) != "True") {
571 // DEBUG
572 llOwnerSay("[Wiki] Unable retrieve from database: " +
573 wasURLUnescape(
574 wasKeyValueGet("error", body)
575 )
576 );
577 state listen_group;
578 }
579  
580 data = llDumpList2String(
581 llDeleteSubList(
582 wasCSVToList(
583 wasURLUnescape(
584 wasKeyValueGet("data", body)
585 )
586 ),
587 0,
588  
589 ),
590 ""
591 );
592  
593 if(data == "") {
594 data = "Sorry, that path contains no data.";
595 state tell;
596 }
597  
598 data = path + ": " + data;
599 state tell;
600 }
601 timer() {
602 llReleaseURL(URL);
603 state listen_group;
604 }
605 on_rez(integer num) {
606 llResetScript();
607 }
608 changed(integer change) {
609 if((change & CHANGED_INVENTORY) ||
610 (change & CHANGED_REGION_START) ||
611 (change & CHANGED_OWNER)) {
612 llResetScript();
613 }
614 }
615 state_exit() {
616 llSetTimerEvent(0);
617 }
618 }
619  
620 state dir {
621 state_entry() {
622 // DEBUG
623 llOwnerSay("[Wiki] Listing paths from database.");
624 llInstantMessage(
625 wasKeyValueGet(
626 "corrade",
627 configuration
628 ),
629 wasKeyValueEncode(
630 [
631 "command", "database",
632 "group", wasURLEscape(
633 wasKeyValueGet(
634 "group",
635 configuration
636 )
637 ),
638 "password", wasURLEscape(
639 wasKeyValueGet(
640 "password",
641 configuration
642 )
643 ),
644 "SQL", wasURLEscape("SELECT path FROM corradeeggdrop WHERE path like :path"),
645 "data", wasListToCSV(
646 [
647 "path",
648 wasURLEscape(path + "%")
649 ]
650 ),
651 "callback", wasURLEscape(URL)
652 ]
653 )
654 );
655 llSetTimerEvent(60);
656 }
657 http_request(key id, string method, string body) {
658 llHTTPResponse(id, 200, "OK");
659 llReleaseURL(URL);
660 if(wasKeyValueGet("command", body) != "database" ||
661 wasKeyValueGet("success", body) != "True") {
662 // DEBUG
663 llOwnerSay("[Wiki] Unable retrieve from database: " +
664 wasURLUnescape(
665 wasKeyValueGet("error", body)
666 )
667 );
668 state listen_group;
669 }
670  
671 list paths = llList2ListStrided(
672 llDeleteSubList(
673 wasCSVToList(
674 wasURLUnescape(
675 wasKeyValueGet("data", body)
676 )
677 ),
678 0,
679  
680 ),
681 0,
682 -1,
683 2
684 );
685  
686 if(llGetListLength(paths) == 0) {
687 data = "Sorry, that path contains no sub-paths.";
688 state tell;
689 }
690  
691 // Eliminate path component.
692 if(path == "/")
693 path = "";
694  
695 list sibling = [];
696 do {
697 // Get the path part.
698 string part = llList2String(paths, 0);
699  
700 // Remove the path component.
701 string child = llStringTrim(
702 llDumpList2String(
703 llParseString2List(
704 part,
705 [path, "/"],
706 []
707 ),
708 "/"
709 ),
710 STRING_TRIM
711  
712 );
713  
714 integer i = llSubStringIndex(child, "/");
715 if(i == -1) {
716 sibling += path + "/" + child;
717 jump continue;
718 }
719 child = path + "/" + llDeleteSubString(child, i, -1) + "/";
720 if(llListFindList(sibling, (list)child) == -1)
721 sibling += child;
722 @continue;
723 paths = llDeleteSubList(paths, 0, 0);
724 } while(llGetListLength(paths) != 0);
725  
726 data = llList2CSV(sibling);
727 // GC
728 sibling = [];
729  
730 state tell;
731 }
732 timer() {
733 llReleaseURL(URL);
734 state listen_group;
735 }
736 on_rez(integer num) {
737 llResetScript();
738 }
739 changed(integer change) {
740 if((change & CHANGED_INVENTORY) ||
741 (change & CHANGED_REGION_START) ||
742 (change & CHANGED_OWNER)) {
743 llResetScript();
744 }
745 }
746 state_exit() {
747 llSetTimerEvent(0);
748 }
749 }
750  
751 state tell {
752 state_entry() {
753 // DEBUG
754 llOwnerSay("[Wiki] Sending to group.");
755 llInstantMessage(
756 wasKeyValueGet(
757 "corrade",
758 configuration
759 ),
760 wasKeyValueEncode(
761 [
762 "command", "tell",
763 "group", wasURLEscape(
764 wasKeyValueGet(
765 "group",
766 configuration
767 )
768 ),
769 "password", wasURLEscape(
770 wasKeyValueGet(
771 "password",
772 configuration
773 )
774 ),
775 "entity", "group",
776 "message", wasURLEscape(data)
777 ]
778 )
779 );
780 state listen_group;
781 }
782 }