mirror of
https://github.com/TribesNext/t2-scripts.git
synced 2026-01-30 23:11:03 +00:00
484 lines
15 KiB
C#
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;
|
|
}
|