corrade-lsl-templates

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 4  →  ?path2? @ 5
/source/scheduled-group-notices/crontab.lsl
@@ -0,0 +1,421 @@
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
// rights of fair usage, the disclaimer and warranty conditions. //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
list wasListReverse(list lst) {
if(llGetListLength(lst)<=1) return lst;
return wasListReverse(
llList2List(lst, 1, llGetListLength(lst))
) + llList2List(lst,0,0);
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasDayOfWeek(integer year, integer month, integer day) {
return llList2String(
[ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday" ],
(
day
+ ((153 * (month + 12 * ((14 - month) / 12) - 3) + 2) / 5)
+ (365 * (year + 4800 - ((14 - month) / 12)))
+ ((year + 4800 - ((14 - month) / 12)) / 4)
- ((year + 4800 - ((14 - month) / 12)) / 100)
+ ((year + 4800 - ((14 - month) / 12)) / 400)
- 32045
) % 7
);
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
integer wasGetYearDays(integer year) {
integer leap = (year % 4 == 0 && year % 100 != 0) ||
(year % 400 == 0);
if(leap == TRUE) {
return 366;
}
return 365;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
integer wasGetMonthDays(integer month, integer year) {
if (month == 4 || month == 6 || month == 9 || month == 11) {
return 30;
}
if(month == 2) {
integer leap = (year % 4 == 0 && year % 100 != 0) ||
(year % 400 == 0);
if(leap == TRUE) {
return 29;
}
return 28;
}
return 31;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasUnixTimeToStamp(integer unix) {
integer year = 1970;
integer dayno = unix / 86400;
do {
dayno -= wasGetYearDays(year);
++year;
} while (dayno >= wasGetYearDays(year));
integer month = 1;
do {
dayno -= wasGetMonthDays(month, year);
++month;
} while (dayno >= wasGetMonthDays(month, year));
return (string)year + "-" +
(string)month + "-" +
(string)(dayno + 1) + "T" +
(string)((unix % 86400) / 3600) + ":" +
(string)(((unix % 86400) % 3600) / 60) + ":" +
(string)(unix % 60) + ".0Z";
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
integer wasMenuIndex = 0;
list wasDialogMenu(list input, list actions, string direction) {
integer cut = 11-wasListCountExclude(actions, [""]);
if(direction == ">" && (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) {
++wasMenuIndex;
jump slice;
}
if(direction == "<" && wasMenuIndex-1 >= 0) {
--wasMenuIndex;
jump slice;
}
@slice;
integer multiple = wasMenuIndex*cut;
input = llList2List(input, multiple+wasMenuIndex, multiple+cut+wasMenuIndex);
input = wasListMerge(input, actions, "");
return input;
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
integer wasListCountExclude(list input, list exclude) {
if(llGetListLength(input) == 0) return 0;
if(llListFindList(exclude, (list)llList2String(input, 0)) == -1)
return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
list wasListMerge(list l, list m, string merge) {
if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return [];
string a = llList2String(m, 0);
if(a != merge) return [ a ] +
wasListMerge(l, llDeleteSubList(m, 0, 0), merge);
return [ llList2String(l, 0) ] +
wasListMerge(
llDeleteSubList(l, 0, 0),
llDeleteSubList(m, 0, 0),
merge
);
}
 
// for notecard reading
integer line = 0;
// time to execute
list time = [];
// message to send
list exec = [];
// subject to send
list subj = [];
// item to send
list item = [];
// inventory notecards
list notes = [];
// current notecard
string note = "";
key agent = NULL_KEY;
// minute store
integer minute = -1;
default {
state_entry() {
// get all the inventory notecards
integer i = llGetInventoryNumber(INVENTORY_NOTECARD)-1;
if(i == -1) {
llSay(0, "No notecards found, idling...");
return;
}
do {
notes += llGetInventoryName(INVENTORY_NOTECARD, i);
} while(--i>-1);
note = llList2String(notes, 0);
notes = llDeleteSubList(notes, 0, 0);
line = 0;
llSay(0, "Reading notecard: " + note);
llGetNotecardLine(note, line);
}
dataserver(key id, string data) {
if(data == EOF) {
// if we have read all the notecards,
// start processing
if(llGetListLength(notes) == 0) {
// check if the crons are set-up properly or bail
if(llGetListLength(time) == 0 ||
llGetListLength(exec) == 0 ||
llGetListLength(time) != llGetListLength(exec)) {
llSay(0, "No valid schedules found...");
return;
}
llSay(0, "All notecards have been read...");
state cron;
}
// otherwise permute
note = llList2String(notes, 0);
notes = llDeleteSubList(notes, 0, 0);
line = 0;
llSay(0, "Reading notecard: " + note);
llGetNotecardLine(note, line);
return;
}
if(data == "") jump continue;
integer i = llSubStringIndex(data, "#");
if(i != -1) data = llDeleteSubString(data, i, -1);
if(data == "") jump continue;
list data = llParseString2List(data, [" "], []);
// * * * * * message to send to the link-set
// sanity check a little
if(llGetListLength(data) < 6 ||
(llList2String(data, 2) != "*" && llList2Integer(data, 2) == 0) ||
(llList2String(data, 3) != "*" && llList2Integer(data, 3) == 0)) jump continue;
list t = llList2List(data, 0, 4);
// normalize 0x
i = llGetListLength(t)-1;
do {
if(llList2String(t, i) == "*") jump wildcard;
t = llListReplaceList(t, [ llList2Integer(t, i) ], i, i);
@wildcard;
} while(--i>-1);
time += llDumpList2String(t, " ");
list ms = llParseString2List(llDumpList2String(llList2List(data, 5, -1), " "), ["|"], []);
// check if subject and message are present
if(llGetListLength(ms) > 0) {
subj += llList2String(ms, 0);
jump message;
}
subj += "";
@message;
if(llGetListLength(ms) > 1) {
exec += llList2String(ms, 1);
jump attachment;
}
exec += "";
@attachment;
if(llGetListLength(ms) >= 2) {
item += llList2String(ms, 2);
jump continue;
}
item += "";
@continue;
llGetNotecardLine(note, ++line);
}
changed(integer change) {
if(change & CHANGED_INVENTORY) llResetScript();
}
on_rez(integer num) {
llResetScript();
}
}
///////////////////////////////////////////////////////////////////////////
// [WaS-K] Cron @ http://was.fm/secondlife/cron //
///////////////////////////////////////////////////////////////////////////
state cron {
state_entry() {
// cron runs every minute so we bind the event handler
// to the second in order to be precisely on the minute
llSetTimerEvent(1);
llSay(0, "Scheduler activated...");
}
touch_start(integer num) {
/////////////////////////////////////////////////////////////////////////// ENABLE ME
// not part of the same group, so bail
//if(llDetectedGroup(0) == FALSE) return;
integer comChannel = (integer)("0x8" + llGetSubString(llGetKey(), 0, 6));
llListen(comChannel, "", "", "");
llDialog(llDetectedKey(0), "\n Welcome to the Scheduler.\nCreated in 2014 by Wizardry and Steamworks\n 10 September 2014: Version: 1.0\n\n", ["⌚ Show", "⎙ Remove"], comChannel);
}
listen(integer channel, string name, key toucher, string message) {
if(message == "⌚ Show") {
llInstantMessage(toucher, "-----------------------------------------");
integer i = llGetListLength(time)-1;
do {
llInstantMessage(toucher, llList2String(time, i) + " ▶︎ " + llList2String(exec, i));
} while(--i>-1);
llInstantMessage(toucher, "-----------------------------------------");
return;
}
if(message == "⎙ Remove") {
llSetTimerEvent(0);
agent = toucher;
state remove;
}
}
timer() {
// build the current date
list stamp = llParseString2List(
wasUnixTimeToStamp(llGetUnixTime() - ((integer) llGetGMTclock() - (integer) llGetWallclock())),
["-",":","T"],[""]
);
// only once per minute
if(llList2Integer(stamp, 4) == minute) return;
 
list ymd = llList2List(stamp, 0, 2);
integer weekDay = llListFindList(
// convert to cron syntax where Sunday counts as day 0 or 7
[ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
[
wasDayOfWeek(
llList2Integer(ymd, 0),
llList2Integer(ymd, 1),
llList2Integer(ymd, 2)
)
]
);
// minute, hour, day, month, day of week
list date = wasListReverse(llList2List(stamp, 1, -2)) + weekDay;
integer i = llGetListLength(date)-1;
// normalize 0x
do {
date = llListReplaceList(date, [ llList2Integer(date, i) ], i, i);
} while(--i>-1);
// check if it is time
list times = time;
list execs = exec;
list subjs = subj;
list items = item;
do {
list cron = llParseString2List(llList2String(times, 0), [" "], []);
do {
// crontab syntax counts Sunday as day 0 or 7
// so we add an exception on the week day element
if(llGetListLength(times) == 1 &&
llList2Integer(date, 0) == 0 &&
llList2Integer(cron, 0) != 0 &&
llList2Integer(cron, 7) != 7) {
jump continue;
}
if(llList2String(date, 0) != llList2String(cron, 0) && llList2String(cron, 0) != "*") {
jump continue;
}
date = llDeleteSubList(date, 0, 0);
cron = llDeleteSubList(cron, 0, 0);
} while(llGetListLength(cron));
// Send the notice information.
llMessageLinked(
LINK_SET,
0,
llList2CSV(
[
llList2String(subjs, 0),
llList2String(execs, 0),
llList2String(items, 0)
]
),
NULL_KEY
);
@continue;
times = llDeleteSubList(times, 0, 0);
execs = llDeleteSubList(execs, 0, 0);
subjs = llDeleteSubList(subjs, 0, 0);
items = llDeleteSubList(items, 0, 0);
} while(llGetListLength(times));
// only once per minute
minute = llList2Integer(stamp, 4);
}
changed(integer change) {
if(change & CHANGED_INVENTORY) llResetScript();
}
on_rez(integer num) {
llResetScript();
}
}
 
///////////////////////////////////////////////////////////////////////////
// Remove Notecard //
///////////////////////////////////////////////////////////////////////////
state remove {
state_entry() {
// get all the inventory notecards
integer i = llGetInventoryNumber(INVENTORY_NOTECARD)-1;
do {
notes += llGetSubString(llGetInventoryName(INVENTORY_NOTECARD, i), 0, 8);
} while(--i>-1);
integer channel = (integer)("0x8" + llGetSubString(llGetKey(), 0, 6));
llListen(channel, "", "", "");
llDialog(agent, "\n Welcome to the Scheduler.\nCreated in 2014 by Wizardry and Steamworks\n 10 September 2014: Version: 1.0\n\n", wasDialogMenu(notes, ["⟵ Back", "⏏ Exit", "Next ⟶"], ""), channel);
llSetTimerEvent(60);
}
listen(integer channel, string name, key id, string message) {
if(message == "⟵ Back") {
llSetTimerEvent(60);
llDialog(id, "\n Welcome to the Scheduler.\nCreated in 2014 by Wizardry and Steamworks\n 10 September 2014: Version: 1.0\n\n", wasDialogMenu(notes, ["⟵ Back", "⏏ Exit", "Next ⟶"], "<"), -10);
return;
}
if(message == "Next ⟶") {
llSetTimerEvent(60);
llDialog(id, "\n Welcome to the Scheduler.\nCreated in 2014 by Wizardry and Steamworks\n 10 September 2014: Version: 1.0\n\n", wasDialogMenu(notes, ["⟵ Back", "⏏ Exit", "Next ⟶"], ">"), -10);
return;
}
if(message == "⏏ Exit") {
llInstantMessage(id, "Resuming operations...");
llResetScript();
}
integer i = llGetInventoryNumber(INVENTORY_NOTECARD)-1;
do {
string name = llGetInventoryName(INVENTORY_NOTECARD, i);
if(llSubStringIndex(name, message) == -1) jump continue_inventory;
llInstantMessage(id, "Deleting notecard: " + message);
llRemoveInventory(name);
integer j = llGetListLength(notes)-1;
do {
note = llList2String(notes, j);
if(llSubStringIndex(name, note) == -1) jump continue_notes;
notes = llDeleteSubList(notes, j, j);
jump menu;
@continue_notes;
} while(--j>-1);
@continue_inventory;
} while(--i>-1);
@menu;
llSetTimerEvent(60);
llDialog(id, "\n Welcome to the Scheduler.\nCreated in 2014 by Wizardry and Steamworks\n 10 September 2014: Version: 1.0\n\n", wasDialogMenu(notes, ["⟵ Back", "⏏ Exit", "Next ⟶"], ""), channel);
}
timer() {
llInstantMessage(agent, "Dialog expired, resuming operations...");
llResetScript();
}
changed(integer change) {
if(change & CHANGED_INVENTORY) llResetScript();
}
on_rez(integer num) {
llResetScript();
}
}
/source/scheduled-group-notices/scheduled-group-notices.lsl
@@ -0,0 +1,188 @@
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
// rights of fair usage, the disclaimer and warranty conditions. //
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// CONFIGURATION //
///////////////////////////////////////////////////////////////////////////
// The UUID / Key of the scripted agent.
string CORRADE = "63c44c23-9f46-4f0d-b00a-5b0e3180a015";
// The name of the group to invite to.
string GROUP = "My Group";
// The password for that group in Corrade.ini.
string PASSWORD = "mypassword";
 
///////////////////////////////////////////////////////////////////////////
// END CONFIGURATION //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
if(llStringLength(data) == 0) return "";
if(llStringLength(k) == 0) return "";
list a = llParseString2List(data, ["&", "="], []);
integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
if(i != -1) return llList2String(a, 2*i+1);
return "";
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueSet(string k, string v, string data) {
if(llStringLength(k) == 0) return "";
if(llStringLength(v) == 0) return "";
if(llStringLength(data) == 0) return k + "=" + v;
integer i = llListFindList(
llList2ListStrided(
llParseString2List(data, ["&", "="], []),
0, -1, 2
),
[ k ]);
if(i != -1) return llDumpList2String(
llListReplaceList(
llParseString2List(data, ["&"], []),
[ k + "=" + v ],
i, i),
"&");
return data + "&" + k + "=" + v;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
list k = llList2ListStrided(data, 0, -1, 2);
list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
data = [];
do {
data += llList2String(k, 0) + "=" + llList2String(v, 0);
k = llDeleteSubList(k, 0, 0);
v = llDeleteSubList(v, 0, 0);
} while(llGetListLength(k) != 0);
return llDumpList2String(data, "&");
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
// escapes a string in conformance with RFC1738
string wasURLEscape(string i) {
string o = "";
do {
string c = llGetSubString(i, 0, 0);
i = llDeleteSubString(i, 0, 0);
if(c == "") jump continue;
if(c == " ") {
o += "+";
jump continue;
}
if(c == "\n") {
o += "%0D" + llEscapeURL(c);
jump continue;
}
o += llEscapeURL(c);
@continue;
} while(i != "");
return o;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
// unescapes a string in conformance with RFC1738
string wasURLUnescape(string i) {
return llUnescapeURL(
llDumpList2String(
llParseString2List(
llDumpList2String(
llParseString2List(
i,
["+"],
[]
),
" "
),
["%0D%0A"],
[]
),
"\n"
)
);
}
 
// Store the URL for the callback.
string callback = "";
 
default {
state_entry() {
// DEBUG
llOwnerSay("Requesting URL...");
llRequestURL();
}
http_request(key id, string method, string body) {
if(method != URL_REQUEST_GRANTED) return;
callback = body;
// DEBUG
llOwnerSay("Got URL...");
state sleep;
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_REGION_START) {
llResetScript();
}
}
}
 
state sleep {
state_entry() {
// DEBUG
llOwnerSay("Waiting for linked-message...");
}
link_message(integer sender, integer num, string message, key id) {
list data = llCSV2List(message);
// Build a partial key-value pair string with the data we know.
string kvp = wasKeyValueEncode(
[
"command", "notice",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
"callback", wasURLEscape(callback)
]
);
// Add the subject if there is one.
string subject = llList2String(data, 0);
if(subject != "") kvp = wasKeyValueSet("subject", wasURLEscape(subject), kvp);
// Add the message if there is one.
message = llList2String(data, 1);
if(message != "") kvp = wasKeyValueSet("message", wasURLEscape(message), kvp);
// Add the attachment if there is one.
string item = llList2String(data, 2);
if(item != "") kvp = wasKeyValueSet("item", wasURLEscape(item), kvp);
// Send off the notice to Corrade.
llInstantMessage(CORRADE, kvp);
}
http_request(key id, string method, string body) {
llHTTPResponse(id, 200, "OK");
// If sending the notice succeeded, do not complain...
if(wasKeyValueGet("command", body) == "notice" &&
wasKeyValueGet("success", body) == "True") return;
// Announce the owner that sending the notice failed
llInstantMessage(llGetOwner(), "Failed to send the notice: " + wasURLUnescape(wasKeyValueGet("error", body)));
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if(change & CHANGED_REGION_START) {
llResetScript();
}
}
}
/source/scheduled-group-notices/schedules.txt
@@ -0,0 +1,28 @@
###########################################################################
# The crontab script can send a linked message to the link set periodically
# at a specified time. The syntax is similar to the Unix crontab where
# asterisks * represent any possible value. Contrasted to some crontab
# implementations, this crontab does not support ranges (1-5) or
# enumerations (3,4,5) as you can find in some Unix implementation of
# crontab. However, you can always add as many crontab lines as you wish to
# be executed at a specified time. The file supports comments starting with
# the hash character "#" which can be placed in-line or at the start. Any
# text after the hash sign will be ignored.
###########################################################################
###########################################################################
# Syntax:
###########################################################################
# * * * * * message to send to the link-set
# | | | | |
# | | | | +-- day of week (0-7) (Sunday=0 or 7)
# | | | +----- month (1-12)
# | | +-------- day of month (1-31)
# | +----------- hour (0-23)
# +-------------- minute (0-59)
 
# ALL TIMES ARE IN SLT!
 
# at minute 10, hour 6, on the 7th day of the 9th month on a Sunday
# send a notice with the subject "Hello" and the message "Good day!"
10 6 7 9 7 Hello|Good day!
12 14 8 4 3 Test|Working!|Testcard