corrade-lsl-templates – Blame information for rev 13

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.
12 office 179 string path = "";
8 office 180 string data = "";
12 office 181 string action = "";
182 string statement = "";
183 string parameters = "";
8 office 184  
185 default {
186 state_entry() {
187 llOwnerSay("[Wiki] Starting...");
188 llSetTimerEvent(10);
189 }
190 link_message(integer sender, integer num, string message, key id) {
191 if(id != "configuration") return;
192 llOwnerSay("[Wiki] Got configuration...");
193 configuration = message;
12 office 194 action = "create";
8 office 195 state url;
196 }
197 timer() {
198 llOwnerSay("[Wiki] Requesting configuration...");
199 llMessageLinked(LINK_THIS, 0, "configuration", NULL_KEY);
200 }
201 on_rez(integer num) {
202 llResetScript();
203 }
204 changed(integer change) {
205 if((change & CHANGED_INVENTORY) ||
206 (change & CHANGED_REGION_START) ||
207 (change & CHANGED_OWNER)) {
208 llResetScript();
209 }
210 }
211 state_exit() {
212 llSetTimerEvent(0);
213 }
214 }
215  
216 state url {
217 state_entry() {
218 // DEBUG
219 llOwnerSay("[Wiki] Requesting URL...");
220 llRequestURL();
221 }
222 http_request(key id, string method, string body) {
223 if(method != URL_REQUEST_GRANTED) return;
224 URL = body;
225 // DEBUG
12 office 226 llOwnerSay("[Wiki] Got URL.");
8 office 227  
12 office 228 if(action == "create") {
229 statement = wasURLEscape("CREATE TABLE IF NOT EXISTS \"" +
230 wasKeyValueGet("wiki table", configuration) +
231 "\" (path text unique collate nocase, data text)");
232 state query;
8 office 233 }
12 office 234  
235 if(action == "get") {
236 statement = wasURLEscape("SELECT data FROM \"" +
237 wasKeyValueGet("wiki table", configuration) +
238 "\" WHERE path=:path");
239 parameters = wasURLEscape(
240 wasListToCSV(
241 [
242 "path",
243 wasURLEscape(path)
244 ]
8 office 245 )
246 );
12 office 247 state query;
8 office 248 }
12 office 249  
250 if(action == "set") {
251 if(data == "") {
252 statement = wasURLEscape("DELETE FROM \"" +
253 wasKeyValueGet("wiki table", configuration) +
254 "\" WHERE path=:path");
255 parameters = wasURLEscape(
256 wasListToCSV(
257 [
258 "path",
259 wasURLEscape(path)
260 ]
261 )
262 );
263 state query;
264 }
265 statement = wasURLEscape("REPLACE INTO \"" +
266 wasKeyValueGet("wiki table", configuration) +
267 "\" (path, data) VALUES (:path, :data)");
268 parameters = wasURLEscape(
269 wasListToCSV(
270 [
271 "path",
272 wasURLEscape(path),
273 "data",
274 wasURLEscape(data)
275 ]
276 )
277 );
278 state query;
279 }
280  
281 if(action == "dir") {
282 if(path == "/") {
283 path = "";
284 statement = wasURLEscape("SELECT DISTINCT SUBSTR(path, 1, LENGTH(path) - LENGTH(LTRIM(SUBSTR(path,2), 'abcdefghijklmnopqrstuvwxyz'))) AS path FROM \"" + wasKeyValueGet("wiki table", configuration) + "\" WHERE path LIKE '/%'");
285 state query;
286 }
287 statement = wasURLEscape("SELECT DISTINCT SUBSTR(REPLACE(path, :base, ''),1, LENGTH(REPLACE(path, :base, '')) - LENGTH(LTRIM(REPLACE(path, :base, ''), 'abcdefghijklmnopqrstuvwxyz'))) AS path FROM \"" + wasKeyValueGet("wiki table", configuration) + "\" WHERE path LIKE :path");
288 parameters = wasURLEscape(
289 wasListToCSV(
290 [
291 "path",
292 wasURLEscape(path + "/" + "%"),
293 "base",
294 wasURLEscape("/" + llDumpList2String(llParseString2List(path, ["/"], []), "/") + "/")
295 ]
296 )
297 );
298 state query;
299 }
300  
301 // DEBUG
302 llOwnerSay("[Wiki] Jump table corrupted, please contact creator...");
303 llResetScript();
8 office 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 }
316  
317 state listen_group {
318 state_entry() {
319 // DEBUG
320 llOwnerSay("[Wiki] Waiting for group messages...");
321 }
322 link_message(integer sender, integer num, string message, key id) {
323 // We only care about notifications now.
324 if(id != "notification")
325 return;
326  
327 // This script only processes group notifications.
328 if(wasKeyValueGet("type", message) != "group")
329 return;
330  
331 // Get the sent message.
332 data = wasURLUnescape(
333 wasKeyValueGet(
334 "message",
335 message
336 )
337 );
338  
11 office 339 // Check if this is an eggdrop command.
8 office 340 if(llGetSubString(data, 0, 0) !=
341 wasKeyValueGet("command", configuration))
342 return;
11 office 343  
344 // Check if the command matches the current module.
12 office 345 list command = llParseString2List(data, [" "], []);
346 if(llList2String(command, 0) !=
347 wasKeyValueGet("command", configuration) + "wiki")
8 office 348 return;
349  
350 // Remove command.
351 command = llDeleteSubList(command, 0, 0);
352  
353 // Check for supported sub-commands.
354 if(llList2String(command, 0) != "set" &&
355 llList2String(command, 0) != "get" &&
356 llList2String(command, 0) != "dir") {
357 data = "Subcommands are: get, set, dir";
358 state tell;
359 }
360  
361 // Get the sub-command and store it as a jump state.
12 office 362 action = llList2String(command, 0);
8 office 363  
364 // Remove sub-command.
365 command = llDeleteSubList(command, 0, 0);
366  
367 // Get the path parts.
368 list path_parts = llParseString2List(
369 llList2String(command, 0), ["/"], []
370 );
371  
372 // Dump the path and store it over states.
373 path = llStringTrim(
374 llDumpList2String(
375 path_parts,
376 "/"
377 ),
378 STRING_TRIM
379 );
380  
381 if(path != "") {
382 integer i = llStringLength(path) - 1;
383 do {
384 string c = llGetSubString(path, i, i);
385 if(c != "/" && !wasIsAlNum(c)) {
386 data = "Only alpha-numerics accepted in the path string.";
387 state tell;
388 }
389 } while(--i > -1);
390 }
391  
392 path = "/" + path;
393  
394 // Remove path.
395 command = llDeleteSubList(command, 0, 0);
396  
397 // Dump the rest of the message.
398 data = llDumpList2String(command, " ");
399  
400 // Get an URL.
401 state url;
402 }
403 on_rez(integer num) {
404 llResetScript();
405 }
406 changed(integer change) {
407 if((change & CHANGED_INVENTORY) ||
408 (change & CHANGED_REGION_START) ||
409 (change & CHANGED_OWNER)) {
410 llResetScript();
411 }
412 }
413 }
414  
12 office 415 state query {
8 office 416 state_entry() {
417 // DEBUG
12 office 418 llOwnerSay("[Wiki] Executing action: " + action);
8 office 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 ),
12 office 439 "SQL", statement,
440 "data", parameters,
8 office 441 "callback", wasURLEscape(URL)
442 ]
443 )
444 );
445 llSetTimerEvent(60);
446 }
447 http_request(key id, string method, string body) {
448 llHTTPResponse(id, 200, "OK");
449 llReleaseURL(URL);
450 if(wasKeyValueGet("command", body) != "database" ||
451 wasKeyValueGet("success", body) != "True") {
452 // DEBUG
12 office 453 llOwnerSay("[Wiki] Unable query database: " +
8 office 454 wasURLUnescape(
455 wasKeyValueGet("error", body)
456 )
457 );
458 state listen_group;
459 }
12 office 460  
461 // Process actions.
462  
463 if(action == "set") {
464 if(data == "") {
465 data = "Deleted from " + path;
466 state tell;
467 }
468 data = "Stored into " + path;
8 office 469 state tell;
470 }
12 office 471  
472 if(action == "get") {
473 data = llDumpList2String(
474 llDeleteSubList(
475 wasCSVToList(
476 wasURLUnescape(
477 wasKeyValueGet("data", body)
8 office 478 )
479 ),
12 office 480 0,
481  
482 ),
483 ""
8 office 484 );
485  
12 office 486 if(data == "") {
487 data = "Sorry, that path contains no data.";
488 state tell;
489 }
8 office 490  
12 office 491 data = path + ": " + data;
8 office 492 state tell;
493 }
494  
12 office 495 if(action == "dir") {
496 list paths = llList2ListStrided(
497 llDeleteSubList(
498 wasCSVToList(
499 wasURLUnescape(
500 wasKeyValueGet("data", body)
8 office 501 )
502 ),
12 office 503 0,
504  
8 office 505 ),
506 0,
12 office 507 -1,
508 2
509 );
8 office 510  
12 office 511 if(llGetListLength(paths) == 0) {
512 data = "Sorry, that path contains no sub-paths.";
513 state tell;
514 }
8 office 515  
12 office 516 // Eliminate path component.
517 if(path == "/")
518 path = "";
8 office 519  
12 office 520 list sibling = [];
521 do {
522 // Get the path part.
523 string part = llList2String(paths, 0);
8 office 524  
12 office 525 // Remove the path component.
526 string child = llStringTrim(
527 llDumpList2String(
528 llParseString2List(
529 part,
530 [path, "/"],
531 []
532 ),
533 "/"
8 office 534 ),
12 office 535 STRING_TRIM
8 office 536  
12 office 537 );
8 office 538  
12 office 539 integer i = llSubStringIndex(child, "/");
540 if(i == -1) {
541 sibling += path + "/" + child;
542 jump continue_dir;
543 }
544 child = path + "/" + llDeleteSubString(child, i, -1) + "/";
545 if(llListFindList(sibling, (list)child) == -1)
546 sibling += child;
547 @continue_dir;
548 paths = llDeleteSubList(paths, 0, 0);
549 } while(llGetListLength(paths) != 0);
8 office 550  
12 office 551 data = llList2CSV(sibling);
552 // GC
553 sibling = [];
8 office 554  
12 office 555 state tell;
556 }
557  
558 // Don't announce creating table.
559 if(action == "create")
560 state listen_group;
561  
562 // DEBUG
563 llOwnerSay("[Wiki] Jump table corrupted, please contact creator...");
564 state listen_group;
8 office 565 }
566 timer() {
567 llReleaseURL(URL);
568 state listen_group;
569 }
570 on_rez(integer num) {
571 llResetScript();
572 }
573 changed(integer change) {
574 if((change & CHANGED_INVENTORY) ||
575 (change & CHANGED_REGION_START) ||
576 (change & CHANGED_OWNER)) {
577 llResetScript();
578 }
579 }
580 state_exit() {
581 llSetTimerEvent(0);
582 }
583 }
584  
585 state tell {
586 state_entry() {
587 // DEBUG
588 llOwnerSay("[Wiki] Sending to group.");
589 llInstantMessage(
590 wasKeyValueGet(
591 "corrade",
592 configuration
593 ),
594 wasKeyValueEncode(
595 [
596 "command", "tell",
597 "group", wasURLEscape(
598 wasKeyValueGet(
599 "group",
600 configuration
601 )
602 ),
603 "password", wasURLEscape(
604 wasKeyValueGet(
605 "password",
606 configuration
607 )
608 ),
609 "entity", "group",
610 "message", wasURLEscape(data)
611 ]
612 )
613 );
614 state listen_group;
615 }
616 }