t2-scripts/t2csri/community/mail.cs
2015-02-08 00:57:57 -05:00

484 lines
15 KiB
C#

// TribesNext Project
// http://www.tribesnext.com/
// Copyright 2011-2013
// Tribes 2 Community System
// Robot Mail Client
// This script implements a network data interface to the TribesNext community system mail robot data interface.
// The "robot" data interface provides the data in a way that is easy to parse with the meager and medicore
// string processing and parsing features present in the Tribes 2 game. If you are reading this script and desire
// to make some sort of third party client for web access or other purposes, you will have a much easier time
// if you use the JSON API to access the same data.
// Currently available methods (as of RC3) are as follow:
// - Viewing the inbox.
// - Viewing the sentbox.
// - Viewing the deleted messages box.
// - Viewing messages.
// - Viewing ignore list.
// - Viewing buddy list.
// - Adding users to an ignore list.
// - Adding users to a buddy list.
// - Deleting users from an ignore list.
// - Deleting users from a buddy list.
// - Deleting (and undeleting) messages.
// - Getting a message count (both read and unread).
// - Sending messages.
// Since the API is asynchronous, this interface will cache results to the various inboxes and viewed
// messages for the purposes of display. Temporary data (elipses) will be provided to the drawing code
// until all fields are filled in.
$TribesNext::Community::Mail::Active = 0;
$TribesNext::Community::Mail::ChunkSize = 25;
function CommunityMailInterface::onConnected(%this)
{
echo("Sending: " @ %this.data);
%this.primed = 0;
%this.send(%this.data);
}
function CommunityMailInterface::onDisconnect(%this)
{
$TribesNext::Community::Mail::Active = 0;
tn_community_mail_executeNextRequest();
}
function CommunityMailInterface::onLine(%this, %line)
{
if (trim(%line) $= "")
{
%this.primed = 1;
tn_community_mail_requestCompleted();
return;
}
if (!%this.primed)
return;
warn("mail: " @ %line);
%message = getField(%line, 0);
switch$ (%message)
{
// display errors to the user -- some of these should never actually happen
case "ERR":
if (getField(%line, 1) $= "MAIL")
{
%type = getField(%line, 2);
switch$ (%type)
{
case "INVALID_RECIP":
%message = "Invalid recipient in mail send request.";
case "INVALID_SBJ":
%message = "Blank or invalid subject in mail send request.";
case "INVALID_BODY":
%message = "Blank or invalid body in message send request.";
case "UNAUTHENTICATED":
%message = "Session authentication error in mail request.";
case "NO_METHOD":
%message = "Internal error: no mail method specified in request.";
case "UNKNOWN_METHOD":
%message = "Internal error: unknown mail method specified in request.";
case "READ":
%message = "Access denied on message ID #" @ getField(%line, 3) @ ".";
default:
%message = "Unknown error in mail system: " @ %line;
}
schedule(500, 0, MessageBoxOK, "ERROR", %message);
}
// success is sent when a message is sent out
case "SUCCESS":
schedule(500, 0, MessageBoxOK, "SENT", "Your message has been sent.");
// the rest of these should be handled and accepted quietly to populate the various data objects
// message format sent as part of a box search
case "MSG":
%msg = tn_community_mail_getMessageObject(getField(%line, 1));
%msg.box = getField(%line, 2);
%msg.read = getField(%line, 3);
%msg.type = getField(%line, 4);
%msg.time = getField(%line, 5);
%box = tn_community_mail_getMailboxObject(%msg.box);
if (!%box.isMember(%msg))
{
if (%box.newest < %msg.id)
%box.newest = %msg.id;
%box.add(%msg);
}
// check if we're getting new messages
if (%box.gettingNew)
{
%since = %box.since;
if (%msg.id <= %since)
{
// found the desired message
%box.gettingNew = 0;
%box.since = %box.newest;
}
else
{
// not yet found desired message, try the next chunk
// first make sure that the chunk exists and we're not at the end of the mailbox
%box.chunk = %box.chunk + 1;
if ($TMail::MessageBoxCount[%box.name] > (%box.chunk * $TribesNext::Community::Mail::ChunkSize))
tn_community_mail_request_boxList(%box.chunk * $TribesNext::Community::Mail::ChunkSize, (%box.chunk + 1) * $TribesNext::Community::Mail::ChunkSize, %box.name, %since);
else
{
%box.since = %box.newest;
}
}
}
// message format sent as part of a message view
case "MSG2":
%msg = tn_community_mail_getMessageObject(getField(%line, 1));
%msg.deleted = getField(%line, 2);
%msg.type = getField(%line, 3);
%msg.time = getField(%line, 4);
%msg.read = "true";
// message subject
case "SBJ":
tn_community_mail_getMessageObject(getField(%line, 1)).subject = getField(%line, 2);
// sender of a message
case "SNDR":
tn_community_mail_getMessageObject(getField(%line, 1)).sender = tn_community_util_extractPlayer(%line, 2);
// body of a message
case "BDY":
tn_community_mail_getMessageObject(getField(%line, 1)).body = collapseEscape(getField(%line, 2));
// "to" recipient of a message
case "TO":
%msg = tn_community_mail_getMessageObject(getField(%line, 1));
%index = getField(%line, 2);
%msg.to[%index] = tn_community_util_extractPlayer(%line, 3);
if (%msg.toMax < %index)
%msg.toMax = %index;
// "cc" recipient of a message
case "CC":
%msg = tn_community_mail_getMessageObject(getField(%line, 1));
%index = getField(%line, 2);
%msg.cc[%index] = tn_community_util_extractPlayer(%line, 3);
if (%msg.ccMax < %index)
%msg.ccMax = %index;
// entries of a buddy or ignore list
case "LIST":
$TMail::ListVals[getField(%line, 1), getField(%line, 2)] = tn_community_util_extractPlayer(%line, 3);
if ($TMail::ListMax[getField(%line, 1)] < getField(%line, 2))
$TMail::ListMax[getField(%line, 1)] = getField(%line, 2);
// search results for player name queries
case "SEARCH":
$TMail::SearchVals[getField(%line, 2)] = tn_community_util_extractPlayer(%line, 3);
if ($TMail::SearchMax < getField(%line, 2))
$TMail::SearchMax = getField(%line, 2);
// unread message count for a box
case "COUNT_U":
$TMail::MessageBoxUnread[getField(%line, 1)] = getField(%line, 2);
// message count for a box
case "COUNT_A":
$TMail::MessageBoxCount[getField(%line, 1)] = getField(%line, 2);
}
}
// extract four fields from a string that correspond to a player
function tn_community_util_extractPlayer(%string, %fInit)
{
return getField(%string, %fInit) @ "\t" @ getField(%string, %fInit + 1) @ "\t" @ getField(%string, %fInit + 2) @ "\t" @ getField(%string, %fInit + 3);
}
function tn_community_mail_getMessageObject(%id)
{
if (isObject($TMail::MessageTable[%id]))
return $TMail::MessageTable[%id];
%obj = new SimObject()
{
class = TMailMessage;
id = %id;
};
$TMail::MessageTable[%id] = %obj;
$TMailMessageSet.add(%obj);
return %obj;
}
function tn_community_mail_getMailboxObject(%name)
{
if (isObject($TMail::MailboxTable[%name]))
return $TMail::MailboxTable[%name];
%obj = new SimSet()
{
class = TMailBox;
name = %name;
since = 0;
};
$TMail::MailboxTable[%name] = %obj;
return %obj;
}
function tn_community_mail_initMessageSet()
{
if (isObject($TMailMessageSet))
{
while ($TMailMessageSet.getCount() > 0)
$TMailMessageSet.getObject(0).delete();
$TMailMessageSet.delete();
}
$TMailMessageSet = new SimSet("TMailMessageSet");
}
tn_community_mail_initMessageSet();
function tn_community_mail_initQueue()
{
if (isObject($TMailRequestQueue))
$TMailRequestQueue.delete();
$TMailRequestQueue = new MessageVector();
}
tn_community_mail_initQueue();
function tn_community_mail_processRequest(%request, %payload)
{
if (%request !$= "")
{
%request = "?guid=" @ getField($LoginCertificate, 1) @ "&uuid=" @ $TribesNext::Community::UUID @ "&" @ %request;
}
if (%payload $= "")
{
%data = "GET " @ $TribesNext::Community::BaseURL @ $TribesNext::Community::MailScript @ %request;
%data = %data @ " HTTP/1.1\r\nHost: " @ $TribesNext::Community::Host @ "\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n\r\n";
}
else
{
%data = "POST " @ $TribesNext::Community::BaseURL @ $TribesNext::Community::MailScript @ " HTTP/1.1\r\n";
%data = %data @ "Host: " @ $TribesNext::Community::Host @ "\r\nUser-Agent: Tribes 2\r\nConnection: close\r\n";
%data = %data @ %payload;
}
$TMailRequestQueue.pushBackLine(%data);
if (!$TribesNext::Community::Mail::Active)
tn_community_mail_executeNextRequest();
}
function tn_community_mail_executeNextRequest()
{
if ($TMailRequestQueue.getNumLines() <= 0)
return;
%data = $TMailRequestQueue.getLineText(0);
$TMailRequestQueue.popFrontLine();
$TribesNext::Community::Mail::Active = 1;
if (isObject(CommunityMailInterface))
{
CommunityMailInterface.disconnect();
}
else
{
new TCPObject(CommunityMailInterface);
}
CommunityMailInterface.data = %data;
CommunityMailInterface.connect($TribesNext::Community::Host @ ":" @ $TribesNext::Community::Port);
}
// implementation of API requests
// this isn't strictly an API request -- this gets the latest messages since the last check
function tn_community_mail_request_getNew(%box)
{
%obj = tn_community_mail_getMailboxObject(%box);
tn_community_mail_request_count(%box, "all");
%since = %obj.since;
%obj.gettingNew = 1;
%obj.chunk = 0;
tn_community_mail_request_boxList(0, $TribesNext::Community::Mail::ChunkSize, %box, %since);
}
function tn_community_mail_request_boxList(%first, %last, %box, %since)
{
tn_community_mail_processRequest("method=box&first=" @ %first @ "&last=" @ %last @ "&box=" @ %box @ "&since=" @ %since);
}
function tn_community_mail_request_read(%messageId)
{
tn_community_mail_processRequest("method=read&id=" @ %messageId);
}
function tn_community_mail_request_viewList(%list)
{
$TMail::ListMax[%list] = 0;
deleteVariables("$TMail::ListVals" @ %list @ "*");
tn_community_mail_processRequest("method=viewlist&list=" @ %list);
}
function tn_community_mail_request_addListEntry(%list, %target)
{
tn_community_mail_processRequest("method=addlist&list=" @ %list @ "&target=" @ %target);
tn_community_mail_request_viewList(%list); // refresh the list
}
function tn_community_mail_request_delListEntry(%list, %target)
{
tn_community_mail_processRequest("method=dellist&list=" @ %list @ "&target=" @ %target);
tn_community_mail_request_viewList(%list); // refresh the list
}
function tn_community_mail_request_deleteMessage(%messageId, %set)
{
%msg = tn_community_mail_getMessageObject(%messageId);
if (%set $= "0")
{
%add = "&set=0";
%msg.deleted = "false";
}
else
{
%add = "&set=1";
%msg.deleted = "true";
}
tn_community_mail_processRequest("method=delete&id=" @ %messageId @ %add);
tn_community_mail_request_read(%messageId); // refresh the message status
// move the message to the right box
if (%set !$= "0")
{
// been deleted, make sure it's in the deleted set
%box = tn_community_mail_getMailboxObject(%msg.box);
%box.remove(%msg);
tn_community_mail_getMailboxObject("deleted").add(%msg);
%msg.box = "deleted";
}
else
{
// been undeleted? make sure it's not in the deleted set
tn_community_mail_getMailboxObject("deleted").remove(%msg);
if (getField(%msg.sender, 3) !$= getField($LoginCertificate, 1))
%box = tn_community_mail_getMailboxObject("inbox");
else
%box = tn_community_mail_getMailboxObject("sentbox");
%box.add(%msg);
%msg.box = %box.name;
}
}
function tn_community_mail_request_count(%box, %mode)
{
tn_community_mail_processRequest("method=count&box=" @ %box @ "&mode=" @ %mode);
}
function tn_community_mail_request_search(%query)
{
$TMail::SearchMax = 0;
deleteVariables("$TMail::SearchVals*");
tn_community_mail_processRequest("method=search&query=" @ %query);
}
function tn_community_mail_request_send(%subject, %contents, %to, %cc)
{
// sending messages themselves is done with a POST,
// since the contents can be longer than URI length limits
%guid = getField($LoginCertificate, 1);
%uuid = $TribesNext::Community::UUID;
%boundary = "-------------------------";
%rand = getRandom(10000, 99999) @ getRandom(10000, 99999) @ getRandom(10, 9999);
%formelem = "Content-Disposition: form-data; name=\"";
%payload = "--" @ %boundary @ %rand @ "\r\n";
// GUID element
%payload = %payload @ %formelem @ "guid\"\r\n\r\n" @ %guid @ "\r\n";
%payload = %payload @ "--" @ %boundary @ %rand @ "\r\n";
// UUID
%payload = %payload @ %formelem @ "uuid\"\r\n\r\n" @ %uuid @ "\r\n";
%payload = %payload @ "--" @ %boundary @ %rand @ "\r\n";
// method
%payload = %payload @ %formelem @ "method\"\r\n\r\nsend\r\n";
%payload = %payload @ "--" @ %boundary @ %rand @ "\r\n";
// subject
%payload = %payload @ %formelem @ "subject\"\r\n\r\n" @ %subject @ "\r\n";
%payload = %payload @ "--" @ %boundary @ %rand @ "\r\n";
// contents
%payload = %payload @ %formelem @ "contents\"\r\n\r\n" @ %contents @ "\r\n";
%payload = %payload @ "--" @ %boundary @ %rand @ "\r\n";
// to
%payload = %payload @ %formelem @ "to\"\r\n\r\n" @ %to @ "\r\n";
%payload = %payload @ "--" @ %boundary @ %rand @ "\r\n";
// cc
if (trim(%cc) $= "")
%cc = 0; // DarkDragonDX: No CC?
%payload = %payload @ %formelem @ "cc\"\r\n\r\n" @ %cc @ "\r\n";
%payload = %payload @ "--" @ %boundary @ %rand @ "\r\n";
%header = "Content-Type: multipart/form-data; boundary=" @ %boundary @ %rand @ "\r\n";
%header = %header @ "Content-Length: " @ strlen(%payload) @ "\r\n\r\n";
tn_community_mail_processRequest("", %header @ %payload);
}
function tn_community_isOnList(%searchguid, %list)
{
if ($TMail::ListMax[%list] $= "")
return "";
%count = $TMail::ListMax[%list];
for (%i = 0; %i <= %count; %i++)
{
%player = $TMail::ListVals[%list, %i];
%guid = getField(%player, 3);
if (%guid == %searchguid)
return %player;
}
return "";
}
function tn_community_isUserBuddy(%searchguid)
{
return tn_community_isOnList(%searchguid, "buddy");
}
function tn_community_isUserBlocked(%searchguid)
{
return tn_community_isOnList(%searchguid, "ignore");
}
// DarkDragonDX: Hookable script callback for when a request with the mail system completes
function tn_community_mail_requestCompleted(){ }
// DarkDragonDX: Helpers function to work with the JSON (somewhat)
function tn_community_mail_explodeJSONObject(%json)
{
%json = trim(%json);
%json = stripChars(%json, "{}\"'");
// The EMail contents of a tribal invite shouldn't contain spaces so this should be safe
%json = strReplace(%json, ",", " ");
return %json;
}
// %processed should have been processed with tn_community_mail_explodeJSONObject
function tn_community_mail_getJSONElement(%processed, %element)
{
%element = strlwr(%element);
for (%i = 0; %i < getWordCount(%processed); %i++)
{
%word = strReplace(getWord(%processed, %i), ":", " ");
if (strlwr(getWord(%word, 0)) $= %element)
return getWord(%word, 1);
}
return -1;
}