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