corrade-lsl-templates – Blame information for rev 42

Subversion Repositories:
Rev:
Rev Author Line No. Line
18 office 1 ///////////////////////////////////////////////////////////////////////////
30 office 2 // Copyright (C) Wizardry and Steamworks 2018 - License: GNU GPLv3 //
18 office 3 ///////////////////////////////////////////////////////////////////////////
4 //
5 // A database-based joke module for Corrade Eggdrop.
6 //
7 ///////////////////////////////////////////////////////////////////////////
42 office 8  
18 office 9 ///////////////////////////////////////////////////////////////////////////
30 office 10 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
18 office 11 ///////////////////////////////////////////////////////////////////////////
12 string wasKeyValueGet(string k, string data) {
13 if(llStringLength(data) == 0) return "";
14 if(llStringLength(k) == 0) return "";
41 office 15 list a = llParseStringKeepNulls(data, ["&", "="], []);
30 office 16 integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
17 if(i != -1) return llList2String(a, 2*i+1);
18 office 18 return "";
19 }
42 office 20  
18 office 21 ///////////////////////////////////////////////////////////////////////////
30 office 22 // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
18 office 23 ///////////////////////////////////////////////////////////////////////////
24 string wasKeyValueEncode(list data) {
25 list k = llList2ListStrided(data, 0, -1, 2);
26 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
27 data = [];
28 do {
29 data += llList2String(k, 0) + "=" + llList2String(v, 0);
30 k = llDeleteSubList(k, 0, 0);
31 v = llDeleteSubList(v, 0, 0);
32 } while(llGetListLength(k) != 0);
33 return llDumpList2String(data, "&");
34 }
42 office 35  
18 office 36 ///////////////////////////////////////////////////////////////////////////
30 office 37 // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 //
18 office 38 ///////////////////////////////////////////////////////////////////////////
39 // http://was.fm/secondlife/wanderer
40 vector wasCirclePoint(float radius) {
41 float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
42 float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
43 if(llPow(x,2) + llPow(y,2) <= llPow(radius,2))
44 return <x, y, 0>;
45 return wasCirclePoint(radius);
46 }
42 office 47  
18 office 48 ///////////////////////////////////////////////////////////////////////////
30 office 49 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
18 office 50 ///////////////////////////////////////////////////////////////////////////
51 // escapes a string in conformance with RFC1738
52 string wasURLEscape(string i) {
53 string o = "";
54 do {
55 string c = llGetSubString(i, 0, 0);
56 i = llDeleteSubString(i, 0, 0);
57 if(c == "") jump continue;
58 if(c == " ") {
59 o += "+";
60 jump continue;
61 }
62 if(c == "\n") {
63 o += "%0D" + llEscapeURL(c);
64 jump continue;
65 }
66 o += llEscapeURL(c);
67 @continue;
68 } while(i != "");
69 return o;
70 }
42 office 71  
18 office 72 ///////////////////////////////////////////////////////////////////////////
30 office 73 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
18 office 74 ///////////////////////////////////////////////////////////////////////////
75 list wasCSVToList(string csv) {
76 list l = [];
77 list s = [];
78 string m = "";
79 do {
80 string a = llGetSubString(csv, 0, 0);
81 csv = llDeleteSubString(csv, 0, 0);
82 if(a == ",") {
83 if(llList2String(s, -1) != "\"") {
84 l += m;
85 m = "";
86 jump continue;
87 }
88 m += a;
89 jump continue;
90 }
91 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
92 m += a;
93 csv = llDeleteSubString(csv, 0, 0);
94 jump continue;
95 }
96 if(a == "\"") {
97 if(llList2String(s, -1) != a) {
98 s += a;
99 jump continue;
100 }
101 s = llDeleteSubList(s, -1, -1);
102 jump continue;
103 }
104 m += a;
105 @continue;
106 } while(csv != "");
107 // postcondition: length(s) = 0
108 return l + m;
109 }
42 office 110  
18 office 111 ///////////////////////////////////////////////////////////////////////////
30 office 112 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
18 office 113 ///////////////////////////////////////////////////////////////////////////
114 string wasListToCSV(list l) {
115 list v = [];
116 do {
117 string a = llDumpList2String(
118 llParseStringKeepNulls(
119 llList2String(
42 office 120 l,
18 office 121  
42 office 122 ),
123 ["\""],
18 office 124 []
125 ),
126 "\"\""
127 );
128 if(llParseStringKeepNulls(
42 office 129 a,
18 office 130 [" ", ",", "\n", "\""], []
42 office 131 ) !=
18 office 132 (list) a
133 ) a = "\"" + a + "\"";
134 v += a;
135 l = llDeleteSubList(l, 0, 0);
136 } while(l != []);
137 return llDumpList2String(v, ",");
138 }
42 office 139  
18 office 140 ///////////////////////////////////////////////////////////////////////////
30 office 141 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
18 office 142 ///////////////////////////////////////////////////////////////////////////
143 // unescapes a string in conformance with RFC1738
144 string wasURLUnescape(string i) {
145 return llUnescapeURL(
146 llDumpList2String(
147 llParseString2List(
148 llDumpList2String(
149 llParseString2List(
42 office 150 i,
151 ["+"],
18 office 152 []
42 office 153 ),
18 office 154 " "
42 office 155 ),
156 ["%0D%0A"],
18 office 157 []
42 office 158 ),
18 office 159 "\n"
160 )
161 );
162 }
42 office 163  
18 office 164 // configuration data
165 string configuration = "";
166 // store message over state.
167 string firstname = "";
168 string lastname = "";
169 string group = "";
170 string data = "";
42 office 171 string action = "";
172  
173 string statement = "";
174 string parameters = "";
175  
18 office 176 default {
177 state_entry() {
178 llOwnerSay("[Joke] Starting module...");
179 llSetTimerEvent(10);
180 }
181 link_message(integer sender, integer num, string message, key id) {
182 if(id != "configuration") return;
183 llOwnerSay("[Joke] Got configuration...");
184 configuration = message;
42 office 185 action = "create_database";
186 state trampoline;
18 office 187 }
188 timer() {
189 llOwnerSay("[Joke] Requesting configuration...");
190 llMessageLinked(LINK_THIS, 0, "configuration", NULL_KEY);
191 }
192 on_rez(integer num) {
193 llResetScript();
194 }
195 changed(integer change) {
42 office 196 if((change & CHANGED_INVENTORY) ||
197 (change & CHANGED_REGION_START) ||
18 office 198 (change & CHANGED_OWNER)) {
199 llResetScript();
200 }
201 }
202 state_exit() {
203 llSetTimerEvent(0);
204 }
205 }
42 office 206  
207 state trampoline {
18 office 208 state_entry() {
42 office 209 if(action == "create_database") {
210 statement = wasURLEscape("CREATE TABLE IF NOT EXISTS \"" +
211 wasKeyValueGet("joke table", configuration) +
212 "\" (data text(1023), name text(35), firstname text(31), lastname text(31), id integer NOT NULL PRIMARY KEY)");
213 state query;
214 }
215  
216 if(action == "random") {
217 statement = wasURLEscape("SELECT * FROM \"" +
218 wasKeyValueGet("joke table", configuration) +
219 "\" WHERE name=:group ORDER BY RANDOM() LIMIT 1");
220 parameters = wasURLEscape(
221 wasListToCSV(
222 [
223 "group",
224 wasURLEscape(
225 wasKeyValueGet(
226 "group",
227 configuration
228 )
229 )
230 ]
231 )
232 );
233  
234 state query;
235 }
236  
237 if(action == "id") {
238 statement = wasURLEscape("SELECT * FROM \"" +
239 wasKeyValueGet("joke table", configuration) +
240 "\" WHERE name=:group AND id=:id");
241 parameters = wasURLEscape(
242 wasListToCSV(
243 [
244 "group",
245 wasURLEscape(
246 wasKeyValueGet(
247 "group",
248 configuration
249 )
250 ),
251 "id",
252 data
253 ]
254 )
255 );
256 state query;
257 }
258  
259 if(action == "search") {
260 statement = wasURLEscape("SELECT * FROM \"" +
261 wasKeyValueGet("joke table", configuration) +
262 "\" WHERE name=:group AND data LIKE :data COLLATE NOCASE ORDER BY RANDOM() LIMIT 1");
263 parameters = wasURLEscape(
264 wasListToCSV(
265 [
266 "group",
267 wasURLEscape(
268 wasKeyValueGet(
269 "group",
270 configuration
271 )
272 ),
273 "data",
274 wasURLEscape("%" + llDumpList2String(llParseString2List(data, [" "], []), "%") + "%")
275 ]
276 )
277 );
278 state query;
279 }
280  
281 if(action == "remove") {
282 statement = wasURLEscape("DELETE FROM \"" +
283 wasKeyValueGet("joke table", configuration) +
284 "\" WHERE name=:name AND id=:id");
285 parameters = wasURLEscape(
286 wasListToCSV(
287 [
288 "name",
289 wasURLEscape(
290 wasKeyValueGet(
291 "group",
292 configuration
293 )
294 ),
295 "id",
296 data
297 ]
298 )
299 );
300 state query;
301 }
302  
303 if(action == "add") {
304 statement = wasURLEscape("INSERT INTO \"" +
305 wasKeyValueGet("joke table", configuration) +
306 "\" (name, data, firstname, lastname) VALUES (:name, :data, :firstname, :lastname)");
307 parameters = wasURLEscape(
308 wasListToCSV(
309 [
310 "name",
311 wasURLEscape(
312 wasKeyValueGet(
313 "group",
314 configuration
315 )
316 ),
317 "data",
318 wasURLEscape(data),
319 "firstname",
320 wasURLEscape(firstname),
321 "lastname",
322 wasURLEscape(lastname)
323 ]
324 )
325 );
326 state query;
327 }
328  
329 if(action == "get_joke_id") {
330 statement = wasURLEscape("SELECT MAX(id) AS LAST FROM \"" +
331 wasKeyValueGet("joke table", configuration) + "\"");
332 state query;
333 }
334  
335 if(action == "by") {
336 statement = wasURLEscape("SELECT * FROM \"" +
337 wasKeyValueGet("joke table", configuration) +
338 "\" WHERE name=:group AND firstname=:firstname AND lastname=:lastname ORDER BY RANDOM() LIMIT 1");
339 parameters = wasURLEscape(
340 wasListToCSV(
341 [
342 "group",
343 wasURLEscape(
344 wasKeyValueGet(
345 "group",
346 configuration
347 )
348 ),
349 "firstname",
350 wasURLEscape(firstname),
351 "lastname",
352 wasURLEscape(lastname)
353 ]
354 )
355 );
356  
357 state query;
358 }
359  
18 office 360 // DEBUG
361 llOwnerSay("[Joke] Jump table corrupted, please contact creator...");
362 llResetScript();
363 }
364 on_rez(integer num) {
365 llResetScript();
366 }
367 changed(integer change) {
42 office 368 if((change & CHANGED_INVENTORY) ||
369 (change & CHANGED_REGION_START) ||
18 office 370 (change & CHANGED_OWNER)) {
371 llResetScript();
372 }
373 }
374 }
42 office 375  
376 state query {
18 office 377 state_entry() {
42 office 378 // Check messge length.
379 string message = wasKeyValueEncode(
380 [
381 "command", "database",
382 "group", wasURLEscape(
383 wasKeyValueGet(
384 "group",
385 configuration
386 )
387 ),
388 "password", wasURLEscape(
389 wasKeyValueGet(
390 "password",
391 configuration
392 )
393 ),
394 "SQL", statement,
395 "data", parameters,
396 "callback", wasURLEscape(
397 wasKeyValueGet(
398 "URL",
399 configuration
400 )
401 )
402 ]
403 );
404  
405 // GC - none of these are needed anymore.
406 statement = "";
407 parameters = "";
408  
409 // Message length check.
410 if(llStringLength(message) > 1023) {
411 data = "Message length exceeded 1023 characters.";
412 state tell;
413 }
414  
18 office 415 // DEBUG
42 office 416 llOwnerSay("[Joke] Executing action: " + action);
18 office 417 llInstantMessage(
418 wasKeyValueGet(
42 office 419 "corrade",
18 office 420 configuration
42 office 421 ),
422 message
18 office 423 );
42 office 424 // GC
425 message = "";
18 office 426 llSetTimerEvent(60);
427 }
42 office 428 link_message(integer sender, integer num, string body, key id) {
429 // Only process callbacks for the database command.
430 if(id != "callback" || wasKeyValueGet("command", body) != "database")
431 return;
432  
433 if(wasKeyValueGet("success", body) != "True") {
18 office 434 // DEBUG
42 office 435 llOwnerSay("[Joke] Unable query database: " +
18 office 436 wasURLUnescape(
437 wasKeyValueGet("error", body)
42 office 438 )
439 );
440 state listen_group;
441 }
442  
443 if(action == "create_database") {
444 llOwnerSay("[Joke] Database created!");
445 state listen_group;
446 }
447  
448 if(action == "random" || action == "id" || action == "search" || action == "by") {
449 list result = wasCSVToList(
18 office 450 wasURLUnescape(
451 wasKeyValueGet("data", body)
452 )
453 );
42 office 454  
455 if(llGetListLength(result) != 10) {
456 data = "No joke found. . .";
457 state tell;
458 }
459  
460 data = llList2String(
461 result,
462 llListFindList(result, ["data"]) + 1
463 );
464  
465 firstname = llList2String(
466 result,
467 llListFindList(result, ["firstname"]) + 1
468 );
469  
470 lastname = llList2String(
471 result,
472 llListFindList(result, ["lastname"]) + 1
473 );
474  
475 string id = llList2String(
476 result,
477 llListFindList(result, ["id"]) + 1
478 );
479  
480 // Build data to be sent.
481 data += " " + "[" + firstname + " " + lastname + "/" + id + "]";
482  
483 state tell;
18 office 484 }
42 office 485  
486 if(action == "remove") {
487 data = "Joke " + data + " has been removed.";
488 state tell;
18 office 489 }
490  
42 office 491 if(action == "add") {
492 action = "get_joke_id";
493 data = body;
494 state trampoline;
495 }
496  
497 if(action == "get_joke_id") {
498 list result = wasCSVToList(
18 office 499 wasURLUnescape(
500 wasKeyValueGet("data", body)
501 )
502 );
42 office 503  
504 data = llList2String(
505 result,
506 llListFindList(result, ["LAST"]) + 1
507 );
508  
509 data = "Joke " + data + " has been stored.";
510 state tell;
18 office 511 }
42 office 512  
513 // DEBUG
514 llOwnerSay("[Joke] Jump table corrupted, please contact creator...");
18 office 515 state listen_group;
516 }
517 timer() {
42 office 518 state listen_group;
18 office 519 }
520 on_rez(integer num) {
521 llResetScript();
522 }
523 changed(integer change) {
42 office 524 if((change & CHANGED_INVENTORY) ||
525 (change & CHANGED_REGION_START) ||
18 office 526 (change & CHANGED_OWNER)) {
527 llResetScript();
528 }
529 }
530 state_exit() {
531 llSetTimerEvent(0);
532 }
533 }
42 office 534  
18 office 535 state listen_group {
536 state_entry() {
537 // DEBUG
538 llOwnerSay("[Joke] Waiting for group messages.");
539 }
540 link_message(integer sender, integer num, string message, key id) {
541 // We only care about notifications now.
542 if(id != "notification")
543 return;
42 office 544  
18 office 545 // This script only processes group notifications.
30 office 546 if(wasKeyValueGet("type", message) != "group" ||
547 (wasKeyValueGet("type", message) == "group" &&
42 office 548 wasURLUnescape(wasKeyValueGet("group", message)) !=
30 office 549 wasKeyValueGet("group", configuration)))
18 office 550 return;
42 office 551  
18 office 552 // Get the message sender.
553 firstname = wasURLUnescape(
554 wasKeyValueGet(
42 office 555 "firstname",
18 office 556 message
557 )
558 );
42 office 559  
18 office 560 lastname = wasURLUnescape(
561 wasKeyValueGet(
42 office 562 "lastname",
18 office 563 message
564 )
565 );
42 office 566  
18 office 567 // Get the sent message.
568 data = wasURLUnescape(
569 wasKeyValueGet(
42 office 570 "message",
18 office 571 message
572 )
573 );
42 office 574  
18 office 575 // Check if this is an eggdrop command.
42 office 576 if(llGetSubString(data, 0, 0) !=
18 office 577 wasKeyValueGet("command", configuration))
578 return;
42 office 579  
18 office 580 // Check if the command matches the current module.
581 list command = llParseString2List(data, [" "], []);
42 office 582 if(llList2String(command, 0) !=
18 office 583 wasKeyValueGet("command", configuration) + "joke")
584 return;
42 office 585  
18 office 586 // Remove command.
587 command = llDeleteSubList(command, 0, 0);
42 office 588  
18 office 589 // Remove action.
42 office 590 action = llList2String(command, 0);
591 // Jump to the "add" state for adding
18 office 592 if(action == "add") {
593 command = llDeleteSubList(command, 0, 0);
594 data = llDumpList2String(command, " ");
595 if(data == "") {
596 data = "The joke's too short to be funny.";
597 state tell;
598 }
42 office 599 action = "add";
600 state trampoline;
18 office 601 }
42 office 602  
603 // Jump to the "remove" state for removing
18 office 604 if(action == "remove") {
605 command = llDeleteSubList(command, 0, 0);
606 data = llDumpList2String(command, " ");
607 if((integer)data == 0) {
608 data = "Which one though? Please provide a joke id.";
609 state tell;
610 }
42 office 611 action = "remove";
612 state trampoline;
18 office 613 }
42 office 614  
615 if(action == "by") {
616 command = llDeleteSubList(command, 0, 0);
617 firstname = llList2String(command, 0);
618 command = llDeleteSubList(command, 0, 0);
619 lastname = llList2String(command, 0);
620 command = llDeleteSubList(command, 0, 0);
621 data = llDumpList2String(command, " ");
622  
623 if(llStringLength(firstname) == 0 ||
624 llStringLength(lastname) == 0) {
625 data = "First and Last name is required.";
626 state tell;
627 }
628  
629 action = "by";
630 state trampoline;
631 }
632  
633 string index = llList2String(command, 0);
634 command = llDeleteSubList(command, 0, 0);
635  
18 office 636 data = llDumpList2String(command, " ");
42 office 637  
638 if(index == "search") {
639 action = "search";
640 state trampoline;
18 office 641 }
42 office 642  
643 if((integer)index <= 0) {
644 action = "random";
645 state trampoline;
20 office 646 }
42 office 647  
648 data = index;
649 action = "id";
650 state trampoline;
18 office 651 }
652 on_rez(integer num) {
653 llResetScript();
654 }
655 changed(integer change) {
42 office 656 if((change & CHANGED_INVENTORY) ||
657 (change & CHANGED_REGION_START) ||
18 office 658 (change & CHANGED_OWNER)) {
659 llResetScript();
660 }
661 }
662 }
663  
664 state tell {
665 state_entry() {
666 // DEBUG
667 llOwnerSay("[Joke] Sending to group.");
668 llInstantMessage(
669 wasKeyValueGet(
42 office 670 "corrade",
18 office 671 configuration
42 office 672 ),
18 office 673 wasKeyValueEncode(
674 [
675 "command", "tell",
676 "group", wasURLEscape(
677 wasKeyValueGet(
42 office 678 "group",
18 office 679 configuration
680 )
681 ),
682 "password", wasURLEscape(
683 wasKeyValueGet(
42 office 684 "password",
18 office 685 configuration
686 )
687 ),
688 "entity", "group",
689 "message", wasURLEscape(data)
690 ]
691 )
692 );
693 state listen_group;
694 }
695 }