corrade-lsl-templates – Blame information for rev 12

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 llOwnerSay(wasURLUnescape(
497 wasKeyValueGet("data", body)
498 ));
499 list paths = llList2ListStrided(
500 llDeleteSubList(
501 wasCSVToList(
502 wasURLUnescape(
503 wasKeyValueGet("data", body)
8 office 504 )
505 ),
12 office 506 0,
507  
8 office 508 ),
509 0,
12 office 510 -1,
511 2
512 );
8 office 513  
12 office 514 if(llGetListLength(paths) == 0) {
515 data = "Sorry, that path contains no sub-paths.";
516 state tell;
517 }
8 office 518  
12 office 519 // Eliminate path component.
520 if(path == "/")
521 path = "";
8 office 522  
12 office 523 list sibling = [];
524 do {
525 // Get the path part.
526 string part = llList2String(paths, 0);
8 office 527  
12 office 528 // Remove the path component.
529 string child = llStringTrim(
530 llDumpList2String(
531 llParseString2List(
532 part,
533 [path, "/"],
534 []
535 ),
536 "/"
8 office 537 ),
12 office 538 STRING_TRIM
8 office 539  
12 office 540 );
8 office 541  
12 office 542 integer i = llSubStringIndex(child, "/");
543 if(i == -1) {
544 sibling += path + "/" + child;
545 jump continue_dir;
546 }
547 child = path + "/" + llDeleteSubString(child, i, -1) + "/";
548 if(llListFindList(sibling, (list)child) == -1)
549 sibling += child;
550 @continue_dir;
551 paths = llDeleteSubList(paths, 0, 0);
552 } while(llGetListLength(paths) != 0);
8 office 553  
12 office 554 data = llList2CSV(sibling);
555 // GC
556 sibling = [];
8 office 557  
12 office 558 state tell;
559 }
560  
561 // Don't announce creating table.
562 if(action == "create")
563 state listen_group;
564  
565 // DEBUG
566 llOwnerSay("[Wiki] Jump table corrupted, please contact creator...");
567 state listen_group;
8 office 568 }
569 timer() {
570 llReleaseURL(URL);
571 state listen_group;
572 }
573 on_rez(integer num) {
574 llResetScript();
575 }
576 changed(integer change) {
577 if((change & CHANGED_INVENTORY) ||
578 (change & CHANGED_REGION_START) ||
579 (change & CHANGED_OWNER)) {
580 llResetScript();
581 }
582 }
583 state_exit() {
584 llSetTimerEvent(0);
585 }
586 }
587  
588 state tell {
589 state_entry() {
590 // DEBUG
591 llOwnerSay("[Wiki] Sending to group.");
592 llInstantMessage(
593 wasKeyValueGet(
594 "corrade",
595 configuration
596 ),
597 wasKeyValueEncode(
598 [
599 "command", "tell",
600 "group", wasURLEscape(
601 wasKeyValueGet(
602 "group",
603 configuration
604 )
605 ),
606 "password", wasURLEscape(
607 wasKeyValueGet(
608 "password",
609 configuration
610 )
611 ),
612 "entity", "group",
613 "message", wasURLEscape(data)
614 ]
615 )
616 );
617 state listen_group;
618 }
619 }