corrade-lsl-templates – Blame information for rev 43

Subversion Repositories:
Rev:
Rev Author Line No. Line
2 office 1 ///////////////////////////////////////////////////////////////////////////
29 office 2 // Copyright (C) Wizardry and Steamworks 2016 - License: CC BY 2.0 //
2 office 3 ///////////////////////////////////////////////////////////////////////////
4 //
5 // This script is able to send messages to avatars, groups or talk in local
6 // using Corrade.
7 //
8 // For more information on Corrade, please see:
9 // http://grimore.org/secondlife/scripted_agents/corrade
10 //
11 ///////////////////////////////////////////////////////////////////////////
12  
13 ///////////////////////////////////////////////////////////////////////////
29 office 14 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 15 ///////////////////////////////////////////////////////////////////////////
16 string wasKeyValueGet(string k, string data) {
17 if(llStringLength(data) == 0) return "";
18 if(llStringLength(k) == 0) return "";
19 list a = llParseString2List(data, ["&", "="], []);
20 integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
21 if(i != -1) return llList2String(a, 2*i+1);
22 return "";
23 }
24  
25 ///////////////////////////////////////////////////////////////////////////
29 office 26 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 27 ///////////////////////////////////////////////////////////////////////////
28 string wasKeyValueEncode(list data) {
29 list k = llList2ListStrided(data, 0, -1, 2);
30 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
31 data = [];
32 do {
33 data += llList2String(k, 0) + "=" + llList2String(v, 0);
34 k = llDeleteSubList(k, 0, 0);
35 v = llDeleteSubList(v, 0, 0);
36 } while(llGetListLength(k) != 0);
37 return llDumpList2String(data, "&");
38 }
39  
40 ///////////////////////////////////////////////////////////////////////////
29 office 41 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 42 ///////////////////////////////////////////////////////////////////////////
43 // escapes a string in conformance with RFC1738
44 string wasURLEscape(string i) {
45 string o = "";
46 do {
47 string c = llGetSubString(i, 0, 0);
48 i = llDeleteSubString(i, 0, 0);
49 if(c == "") jump continue;
50 if(c == " ") {
51 o += "+";
52 jump continue;
53 }
54 if(c == "\n") {
55 o += "%0D" + llEscapeURL(c);
56 jump continue;
57 }
58 o += llEscapeURL(c);
59 @continue;
60 } while(i != "");
61 return o;
62 }
63  
64 ///////////////////////////////////////////////////////////////////////////
29 office 65 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 66 ///////////////////////////////////////////////////////////////////////////
67 list wasCSVToList(string csv) {
68 list l = [];
69 list s = [];
70 string m = "";
71 do {
72 string a = llGetSubString(csv, 0, 0);
73 csv = llDeleteSubString(csv, 0, 0);
74 if(a == ",") {
75 if(llList2String(s, -1) != "\"") {
76 l += m;
77 m = "";
78 jump continue;
79 }
80 m += a;
81 jump continue;
82 }
83 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
84 m += a;
85 csv = llDeleteSubString(csv, 0, 0);
86 jump continue;
87 }
88 if(a == "\"") {
89 if(llList2String(s, -1) != a) {
90 s += a;
91 jump continue;
92 }
93 s = llDeleteSubList(s, -1, -1);
94 jump continue;
95 }
96 m += a;
97 @continue;
98 } while(csv != "");
99 // postcondition: length(s) = 0
100 return l + m;
101 }
102  
103 ///////////////////////////////////////////////////////////////////////////
29 office 104 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 105 ///////////////////////////////////////////////////////////////////////////
106 string wasListToCSV(list l) {
107 list v = [];
108 do {
109 string a = llDumpList2String(
110 llParseStringKeepNulls(
111 llList2String(
112 l,
113  
114 ),
115 ["\""],
116 []
117 ),
118 "\"\""
119 );
120 if(llParseStringKeepNulls(
121 a,
122 [" ", ",", "\n", "\""], []
123 ) !=
124 (list) a
125 ) a = "\"" + a + "\"";
126 v += a;
127 l = llDeleteSubList(l, 0, 0);
128 } while(l != []);
129 return llDumpList2String(v, ",");
130 }
131  
132 ///////////////////////////////////////////////////////////////////////////
29 office 133 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 134 ///////////////////////////////////////////////////////////////////////////
135 // unescapes a string in conformance with RFC1738
136 string wasURLUnescape(string i) {
137 return llUnescapeURL(
138 llDumpList2String(
139 llParseString2List(
140 llDumpList2String(
141 llParseString2List(
142 i,
143 ["+"],
144 []
145 ),
146 " "
147 ),
148 ["%0D%0A"],
149 []
150 ),
151 "\n"
152 )
153 );
154 }
155  
156  
157 ///////////////////////////////////////////////////////////////////////////
29 office 158 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 159 ///////////////////////////////////////////////////////////////////////////
160 integer wasMenuIndex = 0;
161 list wasDialogMenu(list input, list actions, string direction) {
162 integer cut = 11-wasListCountExclude(actions, [""]);
163 if(direction == ">" && (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) {
164 ++wasMenuIndex;
165 jump slice;
166 }
167 if(direction == "<" && wasMenuIndex-1 >= 0) {
168 --wasMenuIndex;
169 jump slice;
170 }
171 @slice;
172 integer multiple = wasMenuIndex*cut;
173 input = llList2List(input, multiple+wasMenuIndex, multiple+cut+wasMenuIndex);
174 input = wasListMerge(input, actions, "");
175 return input;
176 }
177  
178 ///////////////////////////////////////////////////////////////////////////
29 office 179 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 180 ///////////////////////////////////////////////////////////////////////////
181 integer wasListCountExclude(list input, list exclude) {
182 if(llGetListLength(input) == 0) return 0;
183 if(llListFindList(exclude, (list)llList2String(input, 0)) == -1)
184 return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
185 return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
186 }
187  
188 ///////////////////////////////////////////////////////////////////////////
29 office 189 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 190 ///////////////////////////////////////////////////////////////////////////
191 list wasListMerge(list l, list m, string merge) {
192 if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return [];
193 string a = llList2String(m, 0);
194 if(a != merge) return [ a ] + wasListMerge(l, llDeleteSubList(m, 0, 0), merge);
195 return [ llList2String(l, 0) ] + wasListMerge(llDeleteSubList(l, 0, 0), llDeleteSubList(m, 0, 0), merge);
196 }
197  
198 // callback URL
199 string callback = "";
200 // configuration data
201 string configuration = "";
202 // listen handle
203 integer listenHandle = 0;
204 string firstname = "";
205 string lastname = "";
206 list names = [];
207 list UUIDs = [];
208 // temporary list for button name normalization
209 list menu = [];
210 integer select = -1;
211 string entity = "";
212  
213 default {
214 state_entry() {
215 llSetTimerEvent(1);
216 }
217 link_message(integer sender, integer num, string message, key id) {
218 if(sender != 1 || id != "configuration") return;
219 configuration = message;
220 state off;
221 }
222 timer() {
223 llMessageLinked(LINK_ROOT, 0, "configuration", NULL_KEY);
224 }
225 attach(key id) {
226 llResetScript();
227 }
228 on_rez(integer num) {
229 llResetScript();
230 }
231 changed(integer change) {
232 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
233 llResetScript();
234 }
235 }
236 state_exit() {
237 llSetTimerEvent(0);
238 }
239 }
240  
241 state off {
242 state_entry() {
243 llReleaseControls();
244 llSetColor(<.5,0,0>, ALL_SIDES);
245 }
246 touch_end(integer num) {
247 state on;
248 }
249 attach(key id) {
250 llResetScript();
251 }
252 on_rez(integer num) {
253 llResetScript();
254 }
255 changed(integer change) {
256 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
257 llResetScript();
258 }
259 }
260 }
261  
262 state on {
263 state_entry() {
264 llSetColor(<0,.5,0>, ALL_SIDES);
265 state url;
266 }
267 attach(key id) {
268 llResetScript();
269 }
270 on_rez(integer num) {
271 llResetScript();
272 }
273 changed(integer change) {
274 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
275 llResetScript();
276 }
277 }
278 }
279  
280 state url {
281 state_entry() {
282 // DEBUG
283 llOwnerSay("Requesting URL...");
284 llRequestURL();
285 }
286 touch_end(integer num) {
287 llSetColor(<.5,0,0>, ALL_SIDES);
288 llResetScript();
289 }
290 http_request(key id, string method, string body) {
291 if(method != URL_REQUEST_GRANTED) return;
292 callback = body;
293 // DEBUG
294 llOwnerSay("Got URL...");
295 state choose;
296 }
297 attach(key id) {
298 llResetScript();
299 }
300 on_rez(integer num) {
301 llResetScript();
302 }
303 changed(integer change) {
304 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
305 llResetScript();
306 }
307 }
308 }
309  
310 state choose {
311 state_entry() {
312 // DEBUG
313 llOwnerSay("Tell...");
314 llSetTimerEvent(1);
315 listenHandle = llListen(-24, "", llGetOwner(), "");
316 llDialog(
317 llGetOwner(),
318 "[CORRADE] Select avatar, local or group:",
319 [
320 "avatar",
321 "local",
322 "group"
323 ],
324 -24
325 );
326 }
327 touch_end(integer num) {
328 llResetScript();
329 }
330 listen(integer channel, string name, key id, string message) {
331 llListenRemove(listenHandle);
332  
333 entity = message;
334  
335 if(message == "local") state tell;
336 if(message == "avatar") state select_avatar;
337 if(message == "group") state get_groups;
338  
339 llResetScript();
340 }
341 attach(key id) {
342 llResetScript();
343 }
344 on_rez(integer num) {
345 llResetScript();
346 }
347 changed(integer change) {
348 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
349 llResetScript();
350 }
351 }
352 state_exit() {
353 llSetTimerEvent(0);
354 }
355 }
356  
357 state get_groups {
358 state_entry() {
359 // DEBUG
360 llOwnerSay("Getting groups...");
361 llSetTimerEvent(1);
362 llInstantMessage(
363 wasKeyValueGet(
364 "corrade",
365 configuration
366 ),
367 wasKeyValueEncode(
368 [
369 "command", "getcurrentgroups",
370 "group", wasURLEscape(
371 wasKeyValueGet(
372 "group",
373 configuration
374 )
375 ),
376 "password", wasURLEscape(
377 wasKeyValueGet(
378 "password",
379 configuration
380 )
381 ),
382 "sift", wasListToCSV(
383 [
384 "take", 16
385 ]
386 ),
387 "callback", wasURLEscape(callback)
388 ]
389 )
390 );
391 }
392 http_request(key id, string method, string body) {
393 llHTTPResponse(id, 200, "OK");
394 if(wasKeyValueGet("command", body) != "getcurrentgroups" ||
395 wasKeyValueGet("success", body) != "True") {
396 // DEBUG
397 llOwnerSay("Too many groups or unable to groups...");
398 llResetScript();
399 }
400 list data = wasCSVToList(
401 wasURLUnescape(
402 wasKeyValueGet(
403 "data",
404 body
405 )
406 )
407 );
408 // Copy the names and UUIDs of the groups for the menu.
409 names = [];
410 UUIDs = [];
411 do {
412 UUIDs += llList2String(data, -1);
413 data = llDeleteSubList(data, -1, -1);
414 names += llList2String(data, -1);
415 data = llDeleteSubList(data, -1, -1);
416 } while(llGetListLength(data));
417 state select_group;
418 }
419 timer() {
420 llRequestAgentData(
421 (key)wasKeyValueGet(
422 "corrade",
423 configuration
424 ), DATA_ONLINE);
425 }
426 dataserver(key id, string data) {
427 if(data != "1") llResetScript();
428 }
429 attach(key id) {
430 llResetScript();
431 }
432 on_rez(integer num) {
433 llResetScript();
434 }
435 changed(integer change) {
436 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
437 llResetScript();
438 }
439 }
440 state_exit() {
441 llSetTimerEvent(0);
442 }
443 }
444  
445 state select_group {
446 state_entry() {
447 // DEBUG
448 llOwnerSay("Sending menu...");
449 menu = [];
450 integer i = 0;
451 do {
452 menu += llGetSubString(llList2String(names, i), 0, 23);
453 } while(++i < llGetListLength(names));
454 llListen(-10, "", llGetOwner(), "");
455 llDialog(llGetOwner(), "\nPlease choose a group for Corrade to talk in from the list below:\n", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], ""), -10);
456 llSetTimerEvent(60);
457 }
458 touch_end(integer num) {
459 llSetColor(<.5,0,0>, ALL_SIDES);
460 llResetScript();
461 }
462 listen(integer channel, string name, key id, string message) {
463 if(message == "⟵ Back") {
464 llDialog(id, "\nPlease choose a group for Corrade to talk in from the list below:\n", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], "<"), -10);
465 return;
466 }
467 if(message == "Next ⟶") {
468 llDialog(id, "\nPlease choose a primitive for Corrade to sit on from the list below:\n", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], ">"), -10);
469 return;
470 }
471 integer i = llGetListLength(menu) - 1;
472 do {
473 string v = llList2String(menu, i);
474 if(llSubStringIndex(v, message) != -1)
475 jump sit;
476 } while(--i > -1);
477 // GC
478 menu = [];
479 // DEBUG
480 llOwnerSay("Invalid menu item selected...");
481 llResetScript();
482 @sit;
483 // GC
484 menu = [];
485 select = i;
486  
487 state tell;
488  
489 }
490 timer() {
491 // DEBUG
492 llOwnerSay("Dialog menu timeout...");
493 llResetScript();
494 }
495 attach(key id) {
496 llResetScript();
497 }
498 on_rez(integer num) {
499 llResetScript();
500 }
501 changed(integer change) {
502 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
503 llResetScript();
504 }
505 }
506 state_exit() {
507 llSetTimerEvent(0);
508 }
509 }
510  
511 state select_avatar {
512 state_entry() {
513 // DEBUG
514 llOwnerSay("Select avatar...");
515 llSetTimerEvent(1);
516 listenHandle = llListen(-24, "", llGetOwner(), "");
517 llTextBox(llGetOwner(), "[CORRADE] Enter the avatar name:", -24);
518 }
519 touch_end(integer num) {
520 llResetScript();
521 }
522 listen(integer channel, string name, key id, string message) {
523 llListenRemove(listenHandle);
524 list name = llParseString2List(message, [" "], [""]);
525 if(llGetListLength(name) != 2) {
526 llOwnerSay("Invalid avatar name...");
527 state tell;
528 }
529 firstname = llList2String(name, 0);
530 lastname = llList2String(name, 1);
531 state tell;
532 }
533 timer() {
534 llRequestAgentData(
535 (key)wasKeyValueGet(
536 "corrade",
537 configuration
538 ), DATA_ONLINE);
539 }
540 dataserver(key id, string data) {
541 if(data != "1") llResetScript();
542 }
543 attach(key id) {
544 llResetScript();
545 }
546 on_rez(integer num) {
547 llResetScript();
548 }
549 changed(integer change) {
550 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
551 llResetScript();
552 }
553 }
554 state_exit() {
555 llSetTimerEvent(0);
556 }
557 }
558  
559 state tell {
560 state_entry() {
561 // DEBUG
562 llOwnerSay("Tell...");
563 llSetTimerEvent(1);
564 listenHandle = llListen(-24, "", llGetOwner(), "");
565 llTextBox(llGetOwner(), "[CORRADE] Enter the message:", -24);
566 }
567 touch_end(integer num) {
568 llResetScript();
569 }
570 timer() {
571 llRequestAgentData(
572 (key)wasKeyValueGet(
573 "corrade",
574 configuration
575 ), DATA_ONLINE);
576 }
577 dataserver(key id, string data) {
578 if(data != "1") llResetScript();
579 }
580 listen(integer channel, string name, key id, string message) {
581 llListenRemove(listenHandle);
43 office 582 // Assume local chat channel.
583 channel = 0;
584 if(llGetSubString(message, 0, 0) == "/") {
585 integer i = 1;
586 string num = "";
587 do {
588 string t = llGetSubString(message, i, i);
589 if(llGetListLength(
590 llParseString2List(
591 t,
592 ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
593 ,
594 []
595 )) != 0) {
596 jump got_channel;
597 }
598 num += t;
599 } while(++i < llStringLength(message));
600 @got_channel;
601 channel = (integer) num;
602 message = llGetSubString(message, i, -1);
603 }
2 office 604 llInstantMessage(
605 wasKeyValueGet(
606 "corrade",
607 configuration
608 ),
609 wasKeyValueEncode(
610 [
611 "command", "tell",
612 "group", wasURLEscape(
613 wasKeyValueGet(
614 "group",
615 configuration
616 )
617 ),
618 "password", wasURLEscape(
619 wasKeyValueGet(
620 "password",
621 configuration
622 )
623 ),
43 office 624 "channel", channel,
2 office 625 "entity", entity,
626 "type", "normal",
627 "target", wasURLEscape(
628 llList2String(UUIDs, select)
629 ),
630 "firstname", wasURLEscape(firstname),
631 "lastname", wasURLEscape(lastname),
632 "message", wasURLEscape(message),
633 "callback", wasURLEscape(callback)
634 ]
635 )
636 );
637 }
638 http_request(key id, string method, string body) {
639 llHTTPResponse(id, 200, "OK");
640 if(wasKeyValueGet("command", body) != "tell" ||
641 wasKeyValueGet("success", body) != "True") {
642 // DEBUG
643 llOwnerSay("Failed to say message: " + wasKeyValueGet("error", body));
644 }
645 llResetScript();
646 }
647 attach(key id) {
648 llResetScript();
649 }
650 on_rez(integer num) {
651 llResetScript();
652 }
653 changed(integer change) {
654 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
655 llResetScript();
656 }
657 }
658 state_exit() {
659 llSetTimerEvent(0);
660 }
661 }