corrade-lsl-templates – Blame information for rev 29

Subversion Repositories:
Rev:
Rev Author Line No. Line
4 office 1 ///////////////////////////////////////////////////////////////////////////
29 office 2 // Copyright (C) Wizardry and Steamworks 2014 - License: CC BY 2.0 //
4 office 3 ///////////////////////////////////////////////////////////////////////////
4 //
5 // This project makes Corrade, the Second Life / OpenSim bot look at the
6 // avatars typing in local chat. You can find more details about the bot
7 // at the URL: http://grimore.org/secondlife/scripted_agents/corrade
8 //
9 // The script works in combination with a "configuration" notecard that
10 // must be placed in the same primitive as this script. The purpose of this
11 // script is to show setting and disposing of viewer effects with Corrade
12 // and you are free to use, change, and commercialize it under the terms
29 office 13 // of the CC BY 2.0 license at: https://creativecommons.org/licenses/by/2.0
4 office 14 //
15 ///////////////////////////////////////////////////////////////////////////
16  
17 ///////////////////////////////////////////////////////////////////////////
29 office 18 // Copyright (C) 2014 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 19 ///////////////////////////////////////////////////////////////////////////
20 string wasKeyValueGet(string k, string data) {
21 if(llStringLength(data) == 0) return "";
22 if(llStringLength(k) == 0) return "";
23 list a = llParseString2List(data, ["&", "="], []);
24 integer i = llListFindList(a, [ k ]);
25 if(i != -1) return llList2String(a, i+1);
26 return "";
27 }
28  
29 ///////////////////////////////////////////////////////////////////////////
29 office 30 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 31 ///////////////////////////////////////////////////////////////////////////
32 string wasKeyValueEncode(list data) {
33 list k = llList2ListStrided(data, 0, -1, 2);
34 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
35 data = [];
36 do {
37 data += llList2String(k, 0) + "=" + llList2String(v, 0);
38 k = llDeleteSubList(k, 0, 0);
39 v = llDeleteSubList(v, 0, 0);
40 } while(llGetListLength(k) != 0);
41 return llDumpList2String(data, "&");
42 }
43  
44 ///////////////////////////////////////////////////////////////////////////
29 office 45 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 46 ///////////////////////////////////////////////////////////////////////////
47 // escapes a string in conformance with RFC1738
48 string wasURLEscape(string i) {
49 string o = "";
50 do {
51 string c = llGetSubString(i, 0, 0);
52 i = llDeleteSubString(i, 0, 0);
53 if(c == "") jump continue;
54 if(c == " ") {
55 o += "+";
56 jump continue;
57 }
58 if(c == "\n") {
59 o += "%0D" + llEscapeURL(c);
60 jump continue;
61 }
62 o += llEscapeURL(c);
63 @continue;
64 } while(i != "");
65 return o;
66 }
67  
68 ///////////////////////////////////////////////////////////////////////////
29 office 69 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 70 ///////////////////////////////////////////////////////////////////////////
71 // unescapes a string in conformance with RFC1738
72 string wasURLUnescape(string i) {
73 return llUnescapeURL(
74 llDumpList2String(
75 llParseString2List(
76 llDumpList2String(
77 llParseString2List(
78 i,
79 ["+"],
80 []
81 ),
82 " "
83 ),
84 ["%0D%0A"],
85 []
86 ),
87 "\n"
88 )
89 );
90 }
91  
92 // corrade data
93 key CORRADE = NULL_KEY;
94 string GROUP = "";
95 string PASSWORD = "";
96 // the look UUID
97 key LOOK = NULL_KEY;
98 // the effect to use
99 string EFFECT = "look";
100 key EFFECT_UUID = NULL_KEY;
101 string EFFECT_TYPE = "";
102 integer EFFECT_DURATION = 10;
103  
104 // for holding the callback URL
105 string callback = "";
106  
107 // for notecard reading
108 integer line = 0;
109  
110 // key-value data will be read into this list
111 list tuples = [];
112  
113 // the name to look at
114 string firstName = "";
115 string lastName = "";
116  
117 default {
118 state_entry() {
119 if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
120 llOwnerSay("Sorry, could not find a configuration inventory notecard.");
121 return;
122 }
123 // DEBUG
124 llOwnerSay("Reading configuration file...");
125 llGetNotecardLine("configuration", line);
126 }
127 dataserver(key id, string data) {
128 if(data == EOF) {
129 // invariant, length(tuples) % 2 == 0
130 if(llGetListLength(tuples) % 2 != 0) {
131 llOwnerSay("Error in configuration notecard.");
132 return;
133 }
134 CORRADE = llList2Key(
135 tuples,
136 llListFindList(
137 tuples,
138 [
139 "corrade"
140 ]
141 )
142 +1
143 );
144 if(CORRADE == NULL_KEY) {
145 llOwnerSay("Error in configuration notecard: corrade");
146 return;
147 }
148 GROUP = llList2String(
149 tuples,
150 llListFindList(
151 tuples,
152 [
153 "group"
154 ]
155 )
156 +1
157 );
158 if(GROUP == "") {
159 llOwnerSay("Error in configuration notecard: group");
160 return;
161 }
162 PASSWORD = llList2String(
163 tuples,
164 llListFindList(
165 tuples,
166 [
167 "password"
168 ]
169 )
170 +1
171 );
172 if(PASSWORD == "") {
173 llOwnerSay("Error in configuration notecard: password");
174 return;
175 }
176 EFFECT = llToLower(
177 llList2String(
178 tuples,
179 llListFindList(
180 tuples,
181 [
182 "effect"
183 ]
184 )
185 +1
186 )
187 );
188 if(EFFECT != "look" && EFFECT != "point") {
189 llOwnerSay("Error in configuration notecard: effect");
190 return;
191 }
192 if(EFFECT == "look") {
193 EFFECT_UUID = llList2Key(
194 tuples,
195 llListFindList(
196 tuples,
197 [
198 "lookUUID"
199 ]
200 )
201 +1
202 );
203 if(EFFECT_UUID == NULL_KEY) {
204 llOwnerSay("Error in configuration notecard: lookUUID");
205 return;
206 }
207 EFFECT_TYPE = "Focus";
208 }
209 if(EFFECT == "point") {
210 EFFECT_UUID = llList2Key(
211 tuples,
212 llListFindList(
213 tuples,
214 [
215 "pointUUID"
216 ]
217 )
218 +1
219 );
220 if(EFFECT_UUID == NULL_KEY) {
221 llOwnerSay("Error in configuration notecard: pointUUID");
222 return;
223 }
224 EFFECT_TYPE = "Select";
225 }
226 EFFECT_DURATION = llList2Integer(
227 tuples,
228 llListFindList(
229 tuples,
230 [
231 "duration"
232 ]
233 )
234 +1
235 );
236 if(EFFECT_DURATION == 0) {
237 llOwnerSay("Error in configuration notecard: duration");
238 return;
239 }
240 // DEBUG
241 llOwnerSay("Read configuration notecard...");
242 state url;
243 }
244 if(data == "") jump continue;
245 integer i = llSubStringIndex(data, "#");
246 if(i != -1) data = llDeleteSubString(data, i, -1);
247 list o = llParseString2List(data, ["="], []);
248 // get rid of starting and ending quotes
249 string k = llDumpList2String(
250 llParseString2List(
251 llStringTrim(
252 llList2String(
253 o,
254  
255 ),
256 STRING_TRIM),
257 ["\""], []
258 ), "\"");
259 string v = llDumpList2String(
260 llParseString2List(
261 llStringTrim(
262 llList2String(
263 o,
264 1
265 ),
266 STRING_TRIM),
267 ["\""], []
268 ), "\"");
269 if(k == "" || v == "") jump continue;
270 tuples += k;
271 tuples += v;
272 @continue;
273 llGetNotecardLine("configuration", ++line);
274 }
275 on_rez(integer num) {
276 llResetScript();
277 }
278 changed(integer change) {
279 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
280 llResetScript();
281 }
282 }
283 }
284  
285 state url {
286 state_entry() {
287 // DEBUG
288 llOwnerSay("Requesting URL...");
289 llRequestURL();
290 }
291 http_request(key id, string method, string body) {
292 if(method != URL_REQUEST_GRANTED) return;
293 callback = body;
294 // DEBUG
295 llOwnerSay("Got URL...");
296 state detect;
297 }
298 on_rez(integer num) {
299 llResetScript();
300 }
301 changed(integer change) {
302 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
303 llResetScript();
304 }
305 }
306 }
307  
308 state detect {
309 state_entry() {
310 // DEBUG
311 llOwnerSay("Detecting if Corrade is online...");
312 llSetTimerEvent(5);
313 }
314 timer() {
315 llRequestAgentData((key)CORRADE, DATA_ONLINE);
316 }
317 dataserver(key id, string data) {
318 if(data != "1") {
319 // DEBUG
320 llOwnerSay("Corrade is not online, sleeping...");
321 llSetTimerEvent(30);
322 return;
323 }
324 state notify;
325 }
326 on_rez(integer num) {
327 llResetScript();
328 }
329 changed(integer change) {
330 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
331 llResetScript();
332 }
333 }
334 }
335  
336 state notify {
337 state_entry() {
338 // DEBUG
339 llOwnerSay("Binding to the typing notification for local chat...");
340 llInstantMessage(
341 (key)CORRADE,
342 wasKeyValueEncode(
343 [
344 "command", "notify",
345 "group", wasURLEscape(GROUP),
346 "password", wasURLEscape(PASSWORD),
347 "action", "set",
348 "type", "typing",
349 "URL", wasURLEscape(callback),
350 "callback", wasURLEscape(callback)
351 ]
352 )
353 );
354 }
355 http_request(key id, string method, string body) {
356 llHTTPResponse(id, 200, "OK");
357 if(wasKeyValueGet("command", body) != "notify") return;
358 if(wasKeyValueGet("success", body) != "True") {
359 // DEBUG
360 llOwnerSay("Failed to bind to the typing notification...");
361 state sense;
362 }
363 // DEBUG
364 llOwnerSay("Typing notification installed...");
365 state sense;
366 }
367 on_rez(integer num) {
368 llResetScript();
369 }
370 changed(integer change) {
371 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
372 llResetScript();
373 }
374 }
375 }
376  
377 state sense {
378 state_entry() {
379 // DEBUG
380 llOwnerSay("Waiting for typing messages...");
381 }
382 timer() {
383 llRequestAgentData((key)CORRADE, DATA_ONLINE);
384 }
385 dataserver(key id, string data) {
386 if(data == "1") return;
387 // DEBUG
388 llOwnerSay("Corrade is not online, sleeping...");
389 // Switch to detect loop and wait there for Corrade to come online.
390 state detect;
391 }
392 http_request(key id, string method, string body) {
393 llHTTPResponse(id, 200, "OK");
394 firstName = wasURLUnescape(
395 wasKeyValueGet(
396 "firstname",
397 body
398 )
399 );
400 lastName = wasURLUnescape(
401 wasKeyValueGet(
402 "lastname",
403 body
404 )
405 );
406 state delete;
407 }
408 on_rez(integer num) {
409 llResetScript();
410 }
411 changed(integer change) {
412 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
413 llResetScript();
414 }
415 }
416 }
417  
418 state delete {
419 state_entry() {
420 llInstantMessage(
421 (key)CORRADE, wasKeyValueEncode(
422 [
423 "command", "deleteviewereffect",
424 "group", wasURLEscape(GROUP),
425 "password", wasURLEscape(PASSWORD),
426 "effect", EFFECT,
427 "id", EFFECT_UUID,
428 "callback", wasURLEscape(callback)
429 ]
430 )
431 );
432 // alarm 10
433 llSetTimerEvent(10);
434 }
435 timer() {
436 state effect;
437 }
438 http_request(key id, string method, string body) {
439 llHTTPResponse(id, 200, "OK");
440 state effect;
441 }
442 on_rez(integer num) {
443 llResetScript();
444 }
445 changed(integer change) {
446 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
447 llResetScript();
448 }
449 }
450 state_exit() {
451 llSetTimerEvent(0);
452 }
453 }
454  
455 state effect {
456 state_entry() {
457 llInstantMessage(
458 (key)CORRADE, wasKeyValueEncode(
459 [
460 "command", "setviewereffect",
461 "group", wasURLEscape(GROUP),
462 "password", wasURLEscape(PASSWORD),
463 "effect", EFFECT,
464 "offset", ZERO_VECTOR,
465 "firstname", firstName,
466 "lastname", lastName,
467 "type", EFFECT_TYPE,
468 "id", EFFECT_UUID,
469 "callback", wasURLEscape(callback)
470 ]
471 )
472 );
473 // alarm 10
474 llSetTimerEvent(10);
475 }
476 timer() {
477 state sense;
478 }
479 http_request(key id, string method, string body) {
480 llHTTPResponse(id, 200, "OK");
481 if(wasKeyValueGet("command", body)
482 != "setviewereffect") return;
483 if(wasKeyValueGet("success", body) != "True") {
484 // DEBUG
485 llOwnerSay("Failed to look at...");
486 state sense;
487 }
488 state wait;
489 }
490 on_rez(integer num) {
491 llResetScript();
492 }
493 changed(integer change) {
494 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
495 llResetScript();
496 }
497 }
498 state_exit() {
499 llSetTimerEvent(0);
500 }
501 }
502  
503 state wait {
504 state_entry() {
505 llSetTimerEvent(EFFECT_DURATION);
506 }
507 timer() {
508 llSetTimerEvent(0);
509 llInstantMessage(
510 (key)CORRADE, wasKeyValueEncode(
511 [
512 "command", "deleteviewereffect",
513 "group", wasURLEscape(GROUP),
514 "password", wasURLEscape(PASSWORD),
515 "effect", EFFECT,
516 "id", EFFECT_UUID
517 ]
518 )
519 );
520 state sense;
521 }
522 on_rez(integer num) {
523 llResetScript();
524 }
525 changed(integer change) {
526 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
527 llResetScript();
528 }
529 }
530 state_exit() {
531 llSetTimerEvent(0);
532 }
533 }