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 is a script that uses the Corrade Second Life / OpenSim bot which
6 // is able to detect the gender of avatar shapes. You can find more details
7 // about Corrade at: http://grimore.org/secondlife/scripted_agents/corrade
8 //
9 // This script works together with a "configuration" notecard that must be
10 // placed in the same primitive as this script. The purpose of this script
11 // is to demonstrate detecting avatar genders with Corrade and you are free
12 // to use, change, and commercialize it provided that you follow 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 ///////////////////////////////////////////////////////////////////////////
29 office 93 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 94 ///////////////////////////////////////////////////////////////////////////
95 list wasCSVToList(string csv) {
96 list l = [];
97 list s = [];
98 string m = "";
99 do {
100 string a = llGetSubString(csv, 0, 0);
101 csv = llDeleteSubString(csv, 0, 0);
102 if(a == ",") {
103 if(llList2String(s, -1) != "\"") {
104 l += m;
105 m = "";
106 jump continue;
107 }
108 m += a;
109 jump continue;
110 }
111 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
112 m += a;
113 csv = llDeleteSubString(csv, 0, 0);
114 jump continue;
115 }
116 if(a == "\"") {
117 if(llList2String(s, -1) != a) {
118 s += a;
119 jump continue;
120 }
121 s = llDeleteSubList(s, -1, -1);
122 jump continue;
123 }
124 m += a;
125 @continue;
126 } while(csv != "");
127 // postcondition: length(s) = 0
128 return l + m;
129 }
130  
131 ///////////////////////////////////////////////////////////////////////////
29 office 132 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 133 ///////////////////////////////////////////////////////////////////////////
134 string wasListToCSV(list l) {
135 list v = [];
136 do {
137 string a = llDumpList2String(
138 llParseStringKeepNulls(
139 llList2String(
140 l,
141  
142 ),
143 ["\""],
144 []
145 ),
146 "\"\""
147 );
148 if(llParseStringKeepNulls(
149 a,
150 [" ", ",", "\n", "\""], []
151 ) !=
152 (list) a
153 ) a = "\"" + a + "\"";
154 v += a;
155 l = llDeleteSubList(l, 0, 0);
156 } while(l != []);
157 return llDumpList2String(v, ",");
158 }
159  
160 // corrade data
161 string CORRADE = "";
162 string GROUP = "";
163 string PASSWORD = "";
164 integer INTERVAL = 0;
165 integer MEMORY = 0;
166  
167 // for holding the callback URL
168 string callback = "";
169  
170 // for notecard reading
171 integer line = 0;
172  
173 // key-value data will be read into this list
174 list tuples = [];
175 // store names and uuids for gender detect
176 list names = [];
177 list uuids = [];
178 // store the keys of detected agents in
179 // order to prevent scanning them again
180 list found = [];
181 // temporary storage over event handler scope
182 string name = "";
183 key uuid = NULL_KEY;
184  
185 default {
186 state_entry() {
187 if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
188 llOwnerSay("Sorry, could not find a configuration inventory notecard.");
189 return;
190 }
191 // DEBUG
192 llOwnerSay("Reading configuration file...");
193 llGetNotecardLine("configuration", line);
194 }
195 dataserver(key id, string data) {
196 if(data == EOF) {
197 // invariant, length(tuples) % 2 == 0
198 if(llGetListLength(tuples) % 2 != 0) {
199 llOwnerSay("Error in configuration notecard.");
200 return;
201 }
202 CORRADE = llList2String(
203 tuples,
204 llListFindList(
205 tuples,
206 [
207 "corrade"
208 ]
209 )
210 +1);
211 if(CORRADE == "") {
212 llOwnerSay("Error in configuration notecard: corrade");
213 return;
214 }
215 GROUP = llList2String(
216 tuples,
217 llListFindList(
218 tuples,
219 [
220 "group"
221 ]
222 )
223 +1);
224 if(GROUP == "") {
225 llOwnerSay("Error in configuration notecard: group");
226 return;
227 }
228 PASSWORD = llList2String(
229 tuples,
230 llListFindList(
231 tuples,
232 [
233 "password"
234 ]
235 )
236 +1);
237 if(PASSWORD == "") {
238 llOwnerSay("Error in configuration notecard: password");
239 return;
240 }
241 INTERVAL = llList2Integer(
242 tuples,
243 llListFindList(
244 tuples,
245 [
246 "interval"
247 ]
248 )
249 +1);
250 if(INTERVAL == 0) {
251 llOwnerSay("Error in configuration notecard: interval");
252 return;
253 }
254 MEMORY = llList2Integer(
255 tuples,
256 llListFindList(
257 tuples,
258 [
259 "memory"
260 ]
261 )
262 +1);
263 if(MEMORY == 0) {
264 llOwnerSay("Error in configuration notecard: memory");
265 return;
266 }
267 // DEBUG
268 llOwnerSay("Read configuration notecard...");
269 state url;
270 }
271 if(data == "") jump continue;
272 integer i = llSubStringIndex(data, "#");
273 if(i != -1) data = llDeleteSubString(data, i, -1);
274 list o = llParseString2List(data, ["="], []);
275 // get rid of starting and ending quotes
276 string k = llDumpList2String(
277 llParseString2List(
278 llStringTrim(
279 llList2String(
280 o,
281  
282 ),
283 STRING_TRIM),
284 ["\""], []
285 ), "\"");
286 string v = llDumpList2String(
287 llParseString2List(
288 llStringTrim(
289 llList2String(
290 o,
291 1
292 ),
293 STRING_TRIM),
294 ["\""], []
295 ), "\"");
296 if(k == "" || v == "") jump continue;
297 tuples += k;
298 tuples += v;
299 @continue;
300 llGetNotecardLine("configuration", ++line);
301 }
302 on_rez(integer num) {
303 llResetScript();
304 }
305 changed(integer change) {
306 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
307 llResetScript();
308 }
309 }
310 }
311  
312 state url {
313 state_entry() {
314 // DEBUG
315 llOwnerSay("Requesting URL...");
316 llRequestURL();
317 }
318 http_request(key id, string method, string body) {
319 if(method != URL_REQUEST_GRANTED) return;
320 callback = body;
321 // DEBUG
322 llOwnerSay("Got URL...");
323 state detect;
324 }
325 on_rez(integer num) {
326 llResetScript();
327 }
328 changed(integer change) {
329 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
330 llResetScript();
331 }
332 }
333 }
334  
335 state detect {
336 state_entry() {
337 // DEBUG
338 llOwnerSay("Detecting if Corrade is online...");
339 llSetTimerEvent(5);
340 }
341 timer() {
342 llRequestAgentData((key)CORRADE, DATA_ONLINE);
343 }
344 dataserver(key id, string data) {
345 if(data != "1") {
346 // DEBUG
347 llOwnerSay("Corrade is not online, sleeping...");
348 llSetTimerEvent(30);
349 return;
350 }
351 state scan;
352 }
353 on_rez(integer num) {
354 llResetScript();
355 }
356 changed(integer change) {
357 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
358 llResetScript();
359 }
360 }
361 }
362  
363 state scan {
364 state_entry() {
365 // DEBUG
366 llOwnerSay("Scanning agents...");
367 list tmp = llGetAgentList(AGENT_LIST_REGION, []);
368 do {
369 key id = llList2Key(tmp, 0);
370 // skip avatars that we have previously detected
371 if(llListFindList(found, (list)id) != -1) jump continue;
372 uuids += id;
373 names += llKey2Name(id);
374 @continue;
375 tmp = llDeleteSubList(tmp, 0, 0);
376 } while(llGetListLength(tmp) != 0);
377 // recurse over timer, start.
378 llSetTimerEvent(1);
379 }
380 timer() {
381 // Pause the timer.
382 llSetTimerEvent(0);
383 // if the scanning list is empty, switch to detect
384 if(llGetListLength(names) == 0) state detect;
385 // pop the first name and UUID off the stack
386 name = llList2String(names, 0);
387 uuid = llList2Key(uuids, 0);
388 names = llDeleteSubList(names, 0, 0);
389 uuids = llDeleteSubList(uuids, 0, 0);
390 // get the full name and send it to Corrade for scanning
391 list full = llParseString2List(name, [" "], []);
392 llInstantMessage(CORRADE,
393 wasKeyValueEncode(
394 [
395 "command", "getavatardata",
396 "group", wasURLEscape(GROUP),
397 "password", wasURLEscape(PASSWORD),
398 "firstname", wasURLEscape(
399 llList2String(
400 full,
401  
402 )
403 ),
404 "lastname", wasURLEscape(
405 llList2String(
406 full,
407 1
408 )
409 ),
410 "sift", wasURLEscape(
411 wasListToCSV(
412 [
413 "match", wasURLEscape(
414 ",Index,31,([^,$]+)"
415 )
416 ]
417 )
418 ),
419 "data", "VisualParameters",
420 "callback", wasURLEscape(callback)
421 ]
422 )
423 );
424 }
425 http_request(key id, string method, string body) {
426 llHTTPResponse(id, 200, "OK");
427  
428 if(wasURLUnescape(
429 wasKeyValueGet("success", body)) != "True")
430 return;
431  
432 // request succeeded, so grab the
433 // sex from the visual parameters
434 integer sex = (integer) wasURLUnescape(
435 wasKeyValueGet(
436 "data",
437 body
438 )
439 );
440  
441 // at this point we know the following:
442 // - the name of the scanned avatar stored in "name"
443 // - the UUID of the scanned avatar stored in "uuid"
444 // - the gender of the avatar shape:
445 // - if sex is 0, then the avatar has a female shape
446 // - otherwise, the avatar has a male shape
447 if(sex != 0) {
448 llSay(0, "The avatar " + name + "(" + (string)uuid + ")" + " has a male shape!");
449 jump continue_2;
450 }
451 llSay(0, "The avatar " + name + "(" + (string)uuid + ")" + " has a female shape!");
452  
453 @continue_2;
454  
455 // this keeps the amount of free memory available to the script above a specified treshold
456 if (llGetFreeMemory() < MEMORY)
457 found = llDeleteSubList(found, 0, 0);
458  
459 // to prevent scanning the same avatar again, add the uuid to the found list
460 found += uuid;
461  
462 // Resetart the timer.
463 llSetTimerEvent(INTERVAL);
464 }
465 on_rez(integer num) {
466 llResetScript();
467 }
468 changed(integer change) {
469 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
470 llResetScript();
471 }
472 }
473 }