Don't click here unless you want to be banned.

LSL Wiki : LibraryEmailModule

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl805.us.archive.org

Email Module

This module provides a wrapper for LL's email functionality. It allows scripts to receive email events without declaring their own timer and send emails without llEmail's 20 second delay.

// Copyright (c) 2006 Francisco V. Saldana (Christopher Omega)
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in 
// the Software without restriction, including without limitation the rights to use, 
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 
// Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all 
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// This module is responsible for coordinating outbound
// and inbound email. It uses a timer that calls llGetNextEmail
// every 5 seconds, invoking receivedEmailData if new mail arrives.
// Invoking sendEmailData or sendObjectEmail causes the module to 
// rez a temporary-on-rez worker object that does the actual llEmail call.

// In order to preserve the integrity of the sent data, a seperater
// (declared below as a global constant) is prepended the message before it is sent.
// This is so programs can easily discard the additional data Linden Lab's email
// server prepends to the message passed to llEmail. 

// The suffix added to an object's UUID to send an email
// to it via llEmail.
string OBJECT_EMAIL_SUFFIX = "@lsl.secondlife.com";

// Specifies the amount of time between object email polls.
float EMAIL_POLL_INTERVAL = 5.0;

// Seperator between crap LL prepends to llEmail-sent messages and the real message.
string REAL_DATA_SEPERATOR = "#_|#|_#";

// Name of llEmail-calling worker object. 
string EMAIL_OBJECT_NAME = "email";


// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20; 
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

// Events triggered by this module:
m_receivedEmailData(string timestamp, string senderAddress, string senderSubject, string senderMessage) {
    callMethod(0, "receivedEmailData", [timestamp, senderAddress, senderSubject, senderMessage]);
}

m_moduleReset(string moduleName) {
    callMethod(0, "moduleReset", [moduleName]);
}

// Methods called:
m_chat(integer channel, string message) {
    callMethod(0, "chat", [channel, message]);
}

sendEmail(string address, string subject, string message) {
    if (llGetInventoryType(EMAIL_OBJECT_NAME) != INVENTORY_NONE) {
        integer chan = (integer) llFrand(10394) + 1;
        // Scripts in the email object do the actual sending.
        llRezObject(EMAIL_OBJECT_NAME, llGetPos(), ZERO_VECTOR, ZERO_ROTATION, chan);
        m_chat(chan, listToString([address, subject, REAL_DATA_SEPERATOR + message]));
    } else {
        // Unable to delegate llEmail call.
        llEmail(address, subject, message);
    }
}

// Global variables:
    string this;

default {
    state_entry() {
        this = llGetScriptName();
        m_moduleReset(this);
        if (llGetInventoryType(EMAIL_OBJECT_NAME) == INVENTORY_NONE
                || llGetInventoryType("ChatCodec") == INVENTORY_NONE) {
            llOwnerSay("Warning: Rapid llEmail optimization unavailable,"
                + "missing ChatCodec module or email object.");
        }
        state main;
    }
}

state main {
    state_entry() {
        llSetTimerEvent(EMAIL_POLL_INTERVAL);
    }
    
    link_message(integer sender, integer callId, string parameters, key methodName) {
        if (methodName == "sendEmailData") {
            list paramList = stringToList(parameters); 
            // Method signature:
            // sendEmailData(string address, string subject, string message);
            string address = llList2String(paramList, 0);
            string subject = llList2String(paramList, 1);
            string message = llList2String(paramList, 2);
            sendEmail(address, subject, message);
        } else if (methodName == "sendObjectEmail") {
            list paramList = stringToList(parameters); 
            // Method signature:
            // sendObjectEmail(key objectKey, string subject, string message)
            key    objectKey = (key) llList2String(paramList, 0);
            string subject   = llList2String(paramList, 1);
            string message   = llList2String(paramList, 2);
            sendEmail((string)objectKey + OBJECT_EMAIL_SUFFIX, subject, message);
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this) 
                returnValue(callId, methodName, [TRUE]);
        }
    }
    
    timer() {
        llGetNextEmail("", "");
    }
    
    email(string timestamp, string senderAddress, string senderSubject, 
            string senderMessage, integer numQueued) {
        llSetTimerEvent(0);
        if (llSubStringIndex(senderMessage, REAL_DATA_SEPERATOR) != -1)
            senderMessage = llDumpList2String(llDeleteSubList(llParseStringKeepNulls(senderMessage, [REAL_DATA_SEPERATOR], []), 0, 0), REAL_DATA_SEPERATOR);
        m_receivedEmailData(timestamp, senderAddress, senderSubject, senderMessage);
        
        if (numQueued > 0) {
            llGetNextEmail("", "");
        } else {
            llSetTimerEvent(EMAIL_POLL_INTERVAL);
        }
    }
}

To use this module, copy and paste the above into a script and name it "EmailModule". The script requires ChatCodec in the same object's inventory. To send emails, an object named "email" must be in the same object's inventory as EmailModule. The object named "email" must also contain ChatCodec and the script below:

// Copyright (c) 2006 Francisco V. Saldana (Christopher Omega)
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in 
// the Software without restriction, including without limitation the rights to use, 
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 
// Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all 
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Deligates llEmail-calling duties, so EmailModule doesn't need to incur the 20-second delay.
// Requires ChatCodec.

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20; 
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

m_addChatHandle(string moduleName, integer channel) {
    callMethod(0, "addChatHandle", [moduleName, channel]);
}

m_moduleReset(string moduleName) {
    callMethod(0, "moduleName", []);
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        m_moduleReset(this);
    }
    on_rez(integer param) {
        if (param != 0) {
            llSetPrimitiveParams([PRIM_TEMP_ON_REZ, TRUE]);
            m_addChatHandle(this, param);
        }
    }
    link_message(integer sender, integer call, string params, key methodName) {
        if (methodName == "receivedChatData") {
            list paramList = stringToList(params);
            // Method sig:
            // receivedChatData(integer channel, string name, key id, string message)
            integer channel = (integer) llList2String(paramList, 0);
            string  name    = llList2String(paramList, 1);
            key     id      = (key) llList2String(paramList, 2);
            string  message = llList2String(paramList, 3);
            if (channel == llGetStartParameter()) {
                list paramList = stringToList(message);
                llEmail(llList2String(paramList, 0), llList2String(paramList, 1), llList2String(paramList, 2));
                llDie();
            }
        }
    }
}

Here's an example script that utilizes this module:
// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20; 
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

// Replacement for llEmail:
m_sendEmailData(string address, string subject, string message) {
    callMethod(0, "sendEmailData", [address, subject, message]);
}

// Handy wrapper that makes object<->object emails simpler:
m_sendObjectEmail(key uuid, string subject, string message) {
    callMethod(0, "sendObjectEmail", [uuid, subject, message]);
}

default {
    state_entry() {
        m_sendEmailData("foo.bar@fubar.com", "BOOM!", "Happy April Fools Day!");
        m_sendObjectEmail("a003e27c-1623-ae7d-3bdb-19e26d4986e2", "explode", "confetti");
    }
    link_message(integer sender, integer callIdent, string params, key methodName) {
        if (methodName == "receivedEmailData") {
            list paramList = stringToList(params);
            string timestamp = llList2String(paramList, 0);
            string from = llList2String(paramList, 1);
            string subject = llList2String(paramList, 2);
            string message = llList2String(paramList, 3);
            // email event code goes here.
            if (subject == "damn you" && from == "foo.bar@fubar.com") {
                m_sendEmailData("foo.bar@fubar.com", ":-P", "haha");
            }
        }
    }
}

Changelog:
Comments [Hide comments/form]
Warning: This is subject to some flaws, see the comments in LibraryChatCodec for more details.
-- ChristopherOmega (2006-04-02 21:17:54)
Attach a comment to this page: