corrade-lsl-templates – Blame information for rev 5

Subversion Repositories:
Rev:
Rev Author Line No. Line
4 office 1 ///////////////////////////////////////////////////////////////////////////
2 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
3 ///////////////////////////////////////////////////////////////////////////
4 //
5 // A greet and invite script made to work in conjunction with the Corrade
6 // the Second Life / OpenSim bot. You can find more details about the bot
7 // by following the URL: http://was.fm/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 demonstrate greeting and inviting avatars using Corrade
12 // and you are free to use, change, and commercialize it under the terms
13 // of the GNU/GPLv3 license at: http://www.gnu.org/licenses/gpl.html
14 //
15 ///////////////////////////////////////////////////////////////////////////
16  
17 ///////////////////////////////////////////////////////////////////////////
18 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
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(llList2ListStrided(a, 0, -1, 2), [ k ]);
25 if (i != -1) return llList2String(a, 2 * i + 1);
26 return "";
27 }
28  
29 ///////////////////////////////////////////////////////////////////////////
30 // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
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 ///////////////////////////////////////////////////////////////////////////
45 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
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 ///////////////////////////////////////////////////////////////////////////
69 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
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 // Helper function to extract values from tuples
93 string getConfigValue(string id) {
94 integer i = llListFindList(tuples, (list)id);
95 if (i == -1) return "";
96 return llList2String(
97 tuples,
98 i + 1
99 );
100 }
101  
102 // corrade data
103 key CORRADE = "";
104 string GROUP = "";
105 string PASSWORD = "";
106  
107 // instance variables
108 integer channel;
109 list rem = [];
110  
111 // for holding the callback URL
112 string callback = "";
113  
114 // for notecard reading
115 integer line = 0;
116  
117 // key-value data will be read into this list
118 list tuples = [];
119  
120 default {
121 state_entry() {
122 // Set-up commnuication channel.
123 channel = (integer)("0x8" + llGetSubString(llGetKey(), 0, 6));
124 // Read configuration.
125 if (llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
126 llOwnerSay("Sorry, could not find a configuration inventory notecard.");
127 return;
128 }
129 // DEBUG
130 llOwnerSay("Reading configuration file...");
131 llGetNotecardLine("configuration", line);
132 }
133 dataserver(key id, string data) {
134 if (data == EOF) {
135 // invariant, length(tuples) % 2 == 0
136 if (llGetListLength(tuples) % 2 != 0) {
137 llOwnerSay("Error in configuration notecard.");
138 return;
139 }
140 CORRADE = llList2Key(
141 tuples,
142 llListFindList(
143 tuples,
144 [
145 "corrade"
146 ]
147 ) + 1
148 );
149 if (CORRADE == NULL_KEY) {
150 llOwnerSay("Error in configuration notecard: corrade");
151 return;
152 }
153 GROUP = llList2String(
154 tuples,
155 llListFindList(
156 tuples,
157 [
158 "group"
159 ]
160 ) + 1
161 );
162 if (GROUP == "") {
163 llOwnerSay("Error in configuration notecard: group");
164 return;
165 }
166 PASSWORD = llList2String(
167 tuples,
168 llListFindList(
169 tuples,
170 [
171 "password"
172 ]
173 ) + 1
174 );
175 if (PASSWORD == "") {
176 llOwnerSay("Error in configuration notecard: password");
177 return;
178 }
179 // DEBUG
180 llOwnerSay("Read configuration notecard...");
181 state url;
182 }
183 if (data == "") jump continue;
184 integer i = llSubStringIndex(data, "#");
185 if (i != -1) data = llDeleteSubString(data, i, -1);
186 list o = llParseString2List(data, ["="], []);
187 // get rid of starting and ending quotes
188 string k = llDumpList2String(
189 llParseString2List(
190 llStringTrim(
191 llList2String(
192 o,
193  
194 ),
195 STRING_TRIM),
196 ["\""], []
197 ), "\"");
198 string v = llDumpList2String(
199 llParseString2List(
200 llStringTrim(
201 llList2String(
202 o,
203 1
204 ),
205 STRING_TRIM),
206 ["\""], []
207 ), "\"");
208 if (k == "" || v == "") jump continue;
209 tuples += k;
210 tuples += v;
211 @continue;
212 llGetNotecardLine("configuration", ++line);
213 }
214 on_rez(integer num) {
215 llResetScript();
216 }
217 changed(integer change) {
218 if ((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
219 llResetScript();
220 }
221 }
222 }
223  
224 state url {
225 state_entry() {
226 // DEBUG
227 llOwnerSay("Requesting URL...");
228 llRequestURL();
229 }
230 http_request(key id, string method, string body) {
231 if (method != URL_REQUEST_GRANTED) return;
232 callback = body;
233 // DEBUG
234 llOwnerSay("Got URL...");
235 state online;
236 }
237 on_rez(integer num) {
238 llResetScript();
239 }
240 changed(integer change) {
241 if ((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
242 llResetScript();
243 }
244 }
245 }
246  
247 state online {
248 state_entry() {
249 // DEBUG
250 llOwnerSay("Detecting if Corrade is online...");
251 llSetTimerEvent(5);
252 }
253 timer() {
254 llRequestAgentData((key)CORRADE, DATA_ONLINE);
255 }
256 dataserver(key id, string data) {
257 if (data != "1") {
258 // DEBUG
259 llOwnerSay("Corrade is not online, sleeping...");
260 llSetTimerEvent(30);
261 return;
262 }
263 state notify;
264 }
265 on_rez(integer num) {
266 llResetScript();
267 }
268 changed(integer change) {
269 if ((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
270 llResetScript();
271 }
272 }
273 }
274  
275 state notify {
276 state_entry() {
277 // DEBUG
278 llOwnerSay("Binding to the membership notification...");
279 llInstantMessage(
280 (key)CORRADE,
281 wasKeyValueEncode(
282 [
283 "command", "notify",
284 "group", wasURLEscape(GROUP),
285 "password", wasURLEscape(PASSWORD),
286 "action", "set",
287 "type", llList2CSV(["membership"]),
288 "URL", wasURLEscape(callback),
289 "callback", wasURLEscape(callback)
290 ]
291 )
292 );
293 }
294 http_request(key id, string method, string body) {
295 llHTTPResponse(id, 200, "OK");
296 if (wasKeyValueGet("command", body) != "notify" ||
297 wasKeyValueGet("success", body) != "True") {
298 // DEBUG
299 llOwnerSay("Failed to bind to the membership notification...");
300 state detect;
301 }
302 // DEBUG
303 llOwnerSay("Membership notification installed...");
304 state detect;
305 }
306 on_rez(integer num) {
307 llResetScript();
308 }
309 changed(integer change) {
310 if ((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
311 llResetScript();
312 }
313 }
314 }
315  
316 state detect {
317 state_entry() {
318 // DEBUG
319 llOwnerSay("Scanning...");
320 llSensorRepeat("", "", AGENT, (float)getConfigValue("range"), TWO_PI, 1);
321 llListen(channel, "", "", "");
322 // Poll for Corrade's online status.
323 llSetTimerEvent(5);
324 }
325 timer() {
326 llRequestAgentData((key)CORRADE, DATA_ONLINE);
327 }
328 dataserver(key id, string data) {
329 if (data == "1") return;
330 // DEBUG
331 llOwnerSay("Corrade is not online, sleeping...");
332 // Switch to detect loop and wait there for Corrade to come online.
333 state online;
334 }
335 touch_start(integer num) {
336 llDialog(
337 llDetectedKey(0),
338 getConfigValue("welcome"),
339 [
340 getConfigValue("join"),
341 getConfigValue("info"),
342 getConfigValue("exit"),
343 getConfigValue("mark"),
344 getConfigValue("gift"),
345 getConfigValue("site"),
346 getConfigValue("page")
347 ],
348 channel
349 );
350 }
351 listen(integer channel, string name, key id, string message) {
352 if (message == getConfigValue("exit")) return;
353 if (message == getConfigValue("join")) {
354 // DEBUG
355 llOwnerSay("Inviting: " + name + " to the group.");
356 llInstantMessage(CORRADE,
357 wasKeyValueEncode(
358 [
359 "command", "invite",
360 "group", wasURLEscape(GROUP),
361 "password", wasURLEscape(PASSWORD),
362 "agent", wasURLEscape(id)
363 ]
364 )
365 );
366 jump menu;
367 }
368 if (message == getConfigValue("page")) {
369 list a = llParseString2List(getConfigValue("help"), [", ", ","], []);
370 if (llGetListLength(a) == 0) {
371 llInstantMessage(id, getConfigValue("n/a"));
372 jump menu;
373 }
374 vector p = llGetPos();
375 string slurl = "secondlife://" + wasURLEscape(llGetRegionName()) + "/" +
376 (string)((integer)p.x) + "/" +
377 (string)((integer)p.y) + "/" +
378 (string)((integer)p.z);
379 do {
380 list n = llParseString2List(llList2String(a, 0), [" "], []);
381 if (llGetListLength(n) == 2) {
382 llInstantMessage(CORRADE,
383 wasKeyValueEncode(
384 [
385 "command", "tell",
386 "group", wasURLEscape(GROUP),
387 "password", wasURLEscape(PASSWORD),
388 "entity", "avatar",
389 "firstname", wasURLEscape(llList2String(n, 0)),
390 "lastname", wasURLEscape(llList2String(n, 1)),
391 "message", wasURLEscape(
392 name +
393 " is asking for help at: " +
394 slurl
395 )
396 ]
397 )
398 );
399 }
400 a = llDeleteSubList(a, 0, 0);
401 } while (llGetListLength(a) != 0);
402 llInstantMessage(id, getConfigValue("assist"));
403 jump menu;
404 }
405 if (message == getConfigValue("mark")) {
406 if (llGetInventoryNumber(INVENTORY_LANDMARK) == 0) {
407 llInstantMessage(id, getConfigValue("n/a"));
408 jump menu;
409 }
410 llGiveInventory(id, llGetInventoryName(INVENTORY_LANDMARK, 0));
411 jump menu;
412 }
413 if (message == getConfigValue("info")) {
414 integer i = llGetInventoryNumber(INVENTORY_NOTECARD) - 1;
415 do {
416 if (llGetInventoryName(INVENTORY_NOTECARD, i) != "configuration") {
417 llGiveInventory(id, llGetInventoryName(INVENTORY_NOTECARD, i));
418 jump menu;
419 }
420 } while (--i > -1);
421 llInstantMessage(id, getConfigValue("n/a"));
422 jump menu;
423 }
424 if (message == getConfigValue("gift")) {
425 if (llGetInventoryNumber(INVENTORY_OBJECT) == 0) {
426 llInstantMessage(id, getConfigValue("n/a"));
427 jump menu;
428 }
429 llGiveInventory(id, llGetInventoryName(INVENTORY_OBJECT, 0));
430 jump menu;
431 }
432 if (message == getConfigValue("site")) {
433 llLoadURL(id, getConfigValue("loadurl"), getConfigValue("website"));
434 jump menu;
435 }
436 @menu;
437 llDialog(
438 id,
439 getConfigValue("welcome"),
440 [
441 getConfigValue("join"),
442 getConfigValue("info"),
443 getConfigValue("exit"),
444 getConfigValue("mark"),
445 getConfigValue("gift"),
446 getConfigValue("site"),
447 getConfigValue("page")
448 ],
449 channel
450 );
451 }
452 no_sensor() {
453 rem = [];
454 }
455 sensor(integer i) {
456 --i;
457 do {
458 key id = llDetectedKey(i);
459 if (id == NULL_KEY) jump continue;
460 if (llListFindList(rem, (list)id) != -1) jump continue;
461 llDialog(
462 id,
463 getConfigValue("welcome"),
464 [
465 getConfigValue("join"),
466 getConfigValue("info"),
467 getConfigValue("exit"),
468 getConfigValue("mark"),
469 getConfigValue("gift"),
470 getConfigValue("site"),
471 getConfigValue("page")
472 ],
473 channel
474 );
475 rem += id;
476 @continue;
477 } while (--i > -1);
478 }
479 http_request(key id, string method, string body) {
480 llHTTPResponse(id, 200, "OK");
481 list a = llParseString2List(getConfigValue("notify"), [", ", ","], []);
482 if (llGetListLength(a) == 0) {
483 // DEBUG
484 llOwnerSay("Could not read list of avatars to notify about group membership...");
485 return;
486 }
487 do {
488 list n = llParseString2List(llList2String(a, 0), [" "], []);
489 if (llGetListLength(n) == 2) {
490 llInstantMessage(CORRADE,
491 wasKeyValueEncode(
492 [
493 "command", "tell",
494 "group", wasURLEscape(GROUP),
495 "password", wasURLEscape(PASSWORD),
496 "entity", "avatar",
497 "firstname", wasURLEscape(llList2String(n, 0)),
498 "lastname", wasURLEscape(llList2String(n, 1)),
499 "message", wasURLEscape(
500 wasURLUnescape(wasKeyValueGet("firstname", body)) +
501 " " +
502 wasURLUnescape(wasKeyValueGet("lastname", body)) +
503 " has " +
504 wasURLUnescape(wasKeyValueGet("action", body)) +
505 " the group."
506 )
507 ]
508 )
509 );
510 }
511 a = llDeleteSubList(a, 0, 0);
512 } while (llGetListLength(a) != 0);
513 }
514 on_rez(integer num) {
515 llResetScript();
516 }
517 changed(integer change) {
518 if ((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
519 llResetScript();
520 }
521 }
522 }