corrade-lsl-templates – Blame information for rev 11

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