SL Mail module. Lets you receive, reply and send mail from within second life.
// SL Mail.lsl
// (c) 2007 Logic Scripted Products and Script Services
// Flennan Roffo
// VERSION
// Version: 1.1
// HISTORY
// Date Version Author Comment
// --------------------------------------------------------------------------------------
// 28 aug 2007 V 1.0 Flennan Roffo Created
//
// 1 sep 2007 V 1.1 Flennan Roffo @Reply empties the recording buffer.
// @Send while recording ends recording
// and sends the mail if a mail address
// and subject are supplied.
// Fixed incorrect date in Unix2DateTime.
// LICENCE
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// (Also available at http://www.gnu.org/copyleft/gpl.html)
// DESCRIPTION
// Let's you record a message and sent to email and get messages and reply to messages.
// Must supply a valid mail adress and non-empty subject using chat commands.
// TODO:
// 1) Check for the maximum length of an email message (including newline and header).
// 2) Add adress book facility
// 3) Read adress book from notecard
string MailAdress = "";
string MailSubject = "";
string COMMAND_RECORD = "@RECORD";
string COMMAND_CONTINUE = "@CONTINUE";
string COMMAND_STOP = "@STOP";
string COMMAND_MAIL = "@MAIL";
string COMMAND_SUBJECT = "@SUBJECT";
string COMMAND_SEND = "@SEND";
string COMMAND_SEE = "@SEE";
string COMMAND_IGNORE = "@IGNORE";
string COMMAND_READ = "@READ";
string COMMAND_DELETE = "@DELETE";
string COMMAND_CLEAR = "@CLEAR";
string COMMAND_REPLY = "@REPLY";
string COMMAND_NEXT = "@NEXT";
string COMMAND_PREV = "@PREV";
string COMMAND_FIRST = "@FIRST";
string COMMAND_LAST = "@LAST";
string COMMAND_LIST = "@LIST";
string COMMAND_SELECT = "@SELECT";
string COMMAND_HELP = "@HELP";
string COMMAND_INFO = "@INFO";
string COMMAND_RESET = "@RESET";
string COMMAND_SHOW = "@SHOW";
string LSL_DOMAIN = "lsl.secondlife.com";
integer Recording = FALSE;
list ChatRecording=[];
integer NumLines=0;
integer Length=0;
list TimeList=[];
list AddressList=[];
list SubjectList=[];
list BodyList=[];
integer NumMessages=0;
integer CurrentMessage=-1;
///////////////////////////////////////// Unix Time conversion //////////////////
integer DAYS_PER_YEAR = 365; // Non leap year
integer SECONDS_PER_YEAR = 31536000; // Non leap year
integer SECONDS_PER_DAY = 86400;
integer SECONDS_PER_HOUR = 3600;
integer SECONDS_PER_MINUTE = 60;
///////////////////////////////////////// LeapYear /////////////////////////////////
integer LeapYear(integer year)
{
if (year % 4 == 0)
{
if (year % 100 == 0)
{
if (year % 400 == 0)
{
return 1;
}
else
{
return 0;
}
}
else
{
return 1;
}
}
else
{
return 0;
}
}
///////////////////////////////////// DaysPerMonth //////////////////////////////////////////
integer DaysPerMonth(integer year,integer month)
{
if (month < 8)
{
if (month % 2 == 0)
{
if (month == 2)
{
if (LeapYear(year))
{
return 29;
}
else
{
return 28;
}
}
else
{
return 30;
}
}
else
{
return 31;
}
}
else
{
if (month % 2 == 0)
{
return 31;
}
else
{
return 30;
}
}
}
/////////////////////////////////// DaysPerYear /////////////////////////////////////////////
integer DaysPerYear(integer year)
{
if (LeapYear(year))
return DAYS_PER_YEAR + 1;
else
return DAYS_PER_YEAR;
}
/////////////////////////////////////// Unix2DataTime ///////////////////////////////////////
list Unix2DateTime(integer unixtime)
{
integer days_since_1_1_1970 = unixtime / SECONDS_PER_DAY;
integer day = days_since_1_1_1970;
integer year = 1970;
integer days_per_year = DaysPerYear(year);
while (day > days_per_year)
{
day -= days_per_year;
++year;
days_per_year = DaysPerYear(year);
}
integer month = 1;
integer days_per_month = DaysPerMonth(year,month);
while (day > days_per_month)
{
day -= days_per_month;
if (++month > 12)
{
++year;
month = 1;
}
days_per_month = DaysPerMonth(year,month);
}
// Add 1 day since days are 1 based, not 0
if (++day > DaysPerMonth(year,month))
{
day = 1;
if (++month > 12)
{
month = 1;
++year;
}
}
integer seconds_since_midnight = unixtime % SECONDS_PER_DAY;
integer hour = seconds_since_midnight / SECONDS_PER_HOUR;
integer second = seconds_since_midnight % SECONDS_PER_HOUR;
integer minute = second / SECONDS_PER_MINUTE;
second = second % SECONDS_PER_MINUTE;
return [ year, month, day, hour, minute, second ];
}
///////////////////////////////////////// IsValidKeyFormat //////////////////////
integer IsValidKeyFormat(string str)
{
string keychars = "0123456789abcdef";
if (llStringLength(str) != 36)
return FALSE;
if( (llGetSubString( str, 8, 8 ) != "-" ||
llGetSubString( str, 13, 13 ) != "-" ||
llGetSubString( str, 18, 18 ) != "-" ||
llGetSubString( str, 23, 23 ) != "-" ) )
return FALSE;
integer i;
for (i = 0; i < 8; ++i)
{
if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
return FALSE;
}
for (i = 9; i < 13; ++i)
{
if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
return FALSE;
}
for (i = 14; i < 18; ++i)
{
if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
return FALSE;
}
for (i = 19; i < 23; ++i)
{
if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
return FALSE;
}
for (i = 24; i < 36; ++i)
{
if (llSubStringIndex(keychars, llGetSubString(str,i,i)) == -1)
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////// ValidMailAdress /////////////////
integer ValidMailAdress(string arg)
{
string adress = llToLower(llStringTrim(arg,STRING_TRIM));
if (adress == "")
return FALSE;
list parse = llParseStringKeepNulls(adress, [ "@" ], []);
string account = llList2String(parse,0);
string domain = llList2String(parse,1);
if (llGetListLength(parse) != 2 || llStringLength(account) == 0 || llStringLength(domain) == 0)
return FALSE;
if (domain == LSL_DOMAIN)
{
return IsValidKeyFormat(account);
}
else
{
list parsedomain = llParseString2List(domain, [ "." ], []);
if (llGetListLength(parsedomain) < 2)
return FALSE;
integer num = llGetListLength(parsedomain);
integer i;
for (i = 0; i < num; ++i)
{
if (llStringLength(llList2String(parsedomain,i)) < 2)
{
return FALSE;
}
}
}
return TRUE;
}
//////////////////////////////////// SendMail //////////////////////////////////////////
SendMail()
{
llOwnerSay("Sending message to '" + MailAdress + "' with subject '" + MailSubject +"'.");
llEmail(MailAdress, MailSubject, llDumpList2String(ChatRecording, "\n"));
}
//////////////////////////////////// Show //////////////////////////////////////////////
Show()
{
integer i;
llOwnerSay("Current chat recording is:");
for (i = 0; i < NumLines; ++i)
{
llOwnerSay(llList2String(ChatRecording,i));
}
llOwnerSay("Lines is " + (string)NumLines);
llOwnerSay("Message length is " + (string)Length);
}
//////////////////////////////////// Read //////////////////////////////////////////////
Read()
{
llOwnerSay("Reading message " + (string)CurrentMessage + " from mail buffer.");
integer time = (integer)llList2Integer(TimeList,CurrentMessage);
list datetime = Unix2DateTime(time);
string Address = llList2String(AddressList,CurrentMessage);
string Subject = llList2String(SubjectList,CurrentMessage);
string Body = llList2String(BodyList,CurrentMessage);
llOwnerSay("Date : " + (string)llList2Integer(datetime,0) + "-" + (string)llList2Integer(datetime,1) + "-" + (string)llList2Integer(datetime,2));
llOwnerSay("Time : " + (string)llList2Integer(datetime,3) + ":" + (string)llList2Integer(datetime,4) + ":" + (string)llList2Integer(datetime,5));
llOwnerSay("Address: " + Address);
llOwnerSay("Subject: " + Subject);
llOwnerSay("Begin of message body:");
llOwnerSay("-------------------------");
llOwnerSay(Body);
llOwnerSay("-------------------------");
llOwnerSay("End of message body.");
}
////////////////////////////////////// List ////////////////////////////////////////////
List()
{
integer i;
llOwnerSay("The mail buffer contains the following messages:\n");
for (i = 0; i < NumMessages; ++i)
{
list datetime=Unix2DateTime(llList2Integer(TimeList,i));
llOwnerSay("Message: " + (string)i);
llOwnerSay("Date : " + (string)llList2Integer(datetime,0) + "-" + (string)llList2Integer(datetime,1) + "-" + (string)llList2Integer(datetime,2));
llOwnerSay("Time : " + (string)llList2Integer(datetime,3) + ":" + (string)llList2Integer(datetime,4) + ":" + (string)llList2Integer(datetime,5));
llOwnerSay("From: " + llList2String(AddressList,i));
llOwnerSay("Subject: " + llList2String(SubjectList,i));
}
}
////////////////////////////////////// Help ////////////////////////////////////////////
Help()
{
llOwnerSay("HELP for SL Mail.");
llOwnerSay("COMMANDS:");
llOwnerSay("@HELP - displays help info.");
llOwnerSay("@INFO - display mailaddress of this object.");
llOwnerSay("@RECORD - start recording.");
llOwnerSay("@CONTINUE - continue recording.");
llOwnerSay("@STOP - stop recording.");
llOwnerSay("@IGNORE - ignore the recording.");
llOwnerSay("@SHOW - show what your recorded.");
llOwnerSay("@SEND - send the recording.");
llOwnerSay("@MAIL <mailadress> - set the mail adress.");
llOwnerSay("@SUBJECT subject - set the mail subject.");
llOwnerSay("@LIST - list messages in mail buffer.");
llOwnerSay("@SELECT <message> - select message from mail buffer.");
llOwnerSay("@READ - read current message from mail buffer.");
llOwnerSay("@NEXT - read next message from mail buffer.");
llOwnerSay("@PREV - read previous message from mail buffer.");
llOwnerSay("@FIRST - read first message from mail buffer.");
llOwnerSay("@LAST - read last message from mail buffer.");
llOwnerSay("@DELETE - delete current message from mail buffer.");
llOwnerSay("@CLEAR - clear the mail buffer.");
llOwnerSay("@REPLY - reply to current message.");
llOwnerSay("@RESET - reset the script.");
}
////////////////////////////////////////// Info ////////////////////////////////////////////
Info()
{
llOwnerSay("The mail address for this object is:");
llOwnerSay((string)llGetKey() + "@" + LSL_DOMAIN);
}
///////////////////////////////////////////// default //////////////////////////////////////
default
{
state_entry()
{
llListen(0, "", llGetOwner(), "");
llOwnerSay("SL mail ready.\nSend and Get mail in Second Life.\nType @HELP for more info.");
Info();
llSetTimerEvent(0.2);
}
listen(integer channel, string name, key id, string message)
{
list parse = llParseStringKeepNulls(message, [ " "], []);
string command = llToUpper(llList2String(parse,0));
string arg = llStringTrim(llList2String(parse,1),STRING_TRIM);
integer nargs = llGetListLength(parse);
if (Recording)
{
if (command == COMMAND_STOP)
{
llOwnerSay("Recording stopped. Type @SHOW to see the message you have entered, or @SEND to send it.");
Recording = FALSE;
}
else if (command == COMMAND_SEND)
{
Recording = FALSE;
if (MailAdress == "")
{
llOwnerSay("Must supply a mail adress. Type @MAIL <address> to enter a mail address.");
}
else if (MailSubject == "")
{
llOwnerSay("Must supply a subject. Type @SUBJECT <subject> to enter a subject.");
}
else
{
SendMail();
}
}
else
{
ChatRecording = (ChatRecording=[]) + ChatRecording + [ message ];
++NumLines;
Length += llStringLength(message) + 2;
}
}
else
{
if (command == COMMAND_RECORD)
{
llOwnerSay("Start recording your message. Type @STOP to end recording.");
ChatRecording = [];
NumLines=0;
Length=0;
Recording = TRUE;
}
else if (command == COMMAND_CONTINUE)
{
llOwnerSay("Continue recording your message. Type @STOP to end recording.");
Recording = TRUE;
}
else if (command == COMMAND_REPLY)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
MailAdress = llList2String(AddressList,CurrentMessage);
MailSubject = "Re: " + llList2String(SubjectList,CurrentMessage);
ChatRecording = [];
NumLines=0;
Length=0;
Recording = TRUE;
}
}
else if (command == COMMAND_MAIL)
{
if (nargs == 1)
{
llOwnerSay("Mail address is: '" + MailAdress + "'.");
}
else if (ValidMailAdress(arg))
{
MailAdress = arg;
}
else
{
llOwnerSay("Mail adress format not valid.");
}
}
else if (command == COMMAND_SUBJECT)
{
if (nargs == 1)
{
llOwnerSay("Subject is: '" + MailSubject + "'.");
}
else
{
MailSubject = arg;
}
}
else if (command == COMMAND_STOP)
{
llOwnerSay("Not recording.");
}
else if (command == COMMAND_SEND)
{
if (NumLines == 0)
{
llOwnerSay("No lines recorded.");
}
else if (MailAdress == "")
{
llOwnerSay("No mail adress supplied. Type @MAIL <adress> to enter a mail adress.");
}
else if (MailSubject == "")
{
llOwnerSay("No subject supplied Type @SUBJECT <subject> to enter a subject.");
}
else
{
SendMail();
}
}
else if (command == COMMAND_IGNORE)
{
if (NumLines == 0)
{
llOwnerSay("No lines recorded.");
}
else
{
ChatRecording = [];
NumLines = 0;
Length = 0;
}
}
else if (command == COMMAND_SHOW)
{
if (NumLines == 0)
{
llOwnerSay("No lines recorded.");
}
else
{
Show();
}
}
else if (command == COMMAND_READ)
{
if (NumMessages == 0)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
Read();
}
}
else if (command == COMMAND_NEXT)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
if (CurrentMessage == NumMessages - 1)
{
llOwnerSay("Already at last message.");
}
else
{
++CurrentMessage;
Read();
}
}
}
else if (command == COMMAND_PREV)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
if (CurrentMessage == 0)
{
llOwnerSay("Already at first message.");
}
else
{
--CurrentMessage;
Read();
}
}
}
else if (command == COMMAND_FIRST)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
if (CurrentMessage == 0)
{
llOwnerSay("Already at first message.");
}
else
{
CurrentMessage = 0;
Read();
}
}
}
else if (command == COMMAND_LAST)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
if (CurrentMessage == NumMessages - 1)
{
llOwnerSay("Already at last message.");
}
else
{
CurrentMessage = NumMessages - 1;
Read();
}
}
}
else if (command == COMMAND_LIST)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
List();
}
}
else if (command == COMMAND_SELECT)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else if (nargs == 1)
{
llOwnerSay("Need to supply a message number.");
}
else
{
integer msgnum = (integer)arg;
if (msgnum < 0 || msgnum >= NumMessages)
{
llOwnerSay("Message number " + arg + " not in mail buffer.");
}
else
{
CurrentMessage = msgnum;
}
}
}
else if (command == COMMAND_DELETE)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
TimeList = llDeleteSubList(TimeList,CurrentMessage,CurrentMessage);
AddressList = llDeleteSubList(AddressList,CurrentMessage,CurrentMessage);
SubjectList = llDeleteSubList(SubjectList,CurrentMessage,CurrentMessage);
BodyList = llDeleteSubList(BodyList,CurrentMessage,CurrentMessage);
--NumMessages;
if (NumMessages == 0)
{
CurrentMessage = -1;
}
else if (CurrentMessage >= NumMessages)
{
CurrentMessage = NumMessages - 1;
}
}
}
else if (command == COMMAND_CLEAR)
{
if (CurrentMessage == -1)
{
llOwnerSay("The mail buffer is empty.");
}
else
{
TimeList=[];
AddressList=[];
SubjectList=[];
BodyList=[];
NumMessages=0;
CurrentMessage=-1;
}
}
else if (command == COMMAND_HELP)
{
Help();
}
else if (command == COMMAND_INFO)
{
Info();
}
else if (command == COMMAND_SHOW)
{
Show();
}
else if (command == COMMAND_RESET)
{
llResetScript();
}
}
}
timer()
{
llGetNextEmail("", "");
}
email(string time, string address, string subject, string body, integer queued)
{
llOwnerSay("You received a new message.");
TimeList = (TimeList=[]) + TimeList + [time];
AddressList = (AddressList=[]) + AddressList + [address];
SubjectList = (SubjectList=[]) + SubjectList + [subject];
BodyList = (BodyList=[]) + BodyList + [body];
++NumMessages;
if (CurrentMessage == -1)
{
CurrentMessage = 0;
}
else
{
CurrentMessage = NumMessages - 1;
}
}
}
// End SL Mail.lsl