In my last posting i introduced my Twitter-application in Second Life. Now i want to tell something about the implementation of it.
First of all, there is a little Ruby on Rails server application working in the background, which fulfills two tasks:
- Authenticate in Twitter and get the new public updates.
The idea is to parse the xml-result of Twitter and extract the update-text and the author of this text. And after that to build a compact string seperated by delimiters. - Authenticate in Twitter and post a new personal update
For XML-processing in Ruby we will use the great REXML-processor.
The following code gets the public-updates.
###username### and ###password### have to be replaced with your Twitter username and password.
def get_messages
# get public updates
# doesnt need authentication actually but no problem
# if its in the code. but i integrate it, because i want
# to integrate also private/friends updates later
urlStr = 'http://twitter.com/statuses/public_timeline.xml'
url = URI.parse(urlStr)
req = Net::HTTP::Get.new(url.path)
req.basic_auth ###username###, ###password###
res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
@content = res.body
resultStr = ""
case res
when Net::HTTPSuccess, Net::HTTPRedirection
# build a dom from the xml-string and parse it
xmlDoc = REXML::Document.new(@content)
# building the delimiter string for the later in-world processing
# every update is line-separated and the name and text in each update
# is delimited with the pipe-character ('|')
xmlDoc.elements.each("statuses/status") do |status|
resultStr += status.elements["text"].get_text.value + "|" + status.elements["user/name"].get_text.value + "\n"
end
render :text => resultStr
else
render :text => 'error'
end
end
For posting a new update i use the following code.
def post_message
# get the message as parameter
message = params[:msg]
# authenticate using basic-auth and make a post request using
# the 'parameter' for the new message. see twitter API for
# details
url = URI.parse('http://twitter.com/statuses/update.xml')
req = Net::HTTP::Post.new(url.path)
req.basic_auth ###username###, ###password###
req.set_form_data({'status' => message})
res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
# print out some little message in case of failing
# actual just for debugging purpose, because it wont be used
# in the application later
case res
when Net::HTTPSuccess, Net::HTTPRedirection
render :text => 'ok'
else
render :text => 'error'
end
end
Ok, now with this application on a running RoR-Server we can get the updates from Twitter and are able to send new updates to Twitter.
In Second Life the idea is, to have a little box which creates for every Twitter-update a little sphere which displays the update-text and the author. When the spheres are created they should be updated every n seconds (n=30 in this example).
So we have actually two prims:
- The main box
which handles the connection to the Rails-application, creates the spheres, links them together and sends a message to every sphere if an update occurs. - The sphere
Listen for messages and updates its text.
integer childNumber;
string text;
string name;
default
{
// if the prin is created print a message and sets the number of the
// child. this is used for later accessing the message parts
on_rez(integer start_param) {
llSetText("creating... ", <1,1,1>, 1.0);
childNumber = start_param;
}
// if a link message is received, get the parts of the delimited string and
// print them above the prim
link_message(integer sender_num, integer num, string str, key id) {
llSetText("updating... ", <1,1,1>, 1.0);
list resultList = llParseString2List(str,["\n"],[]);
string statusLineWithName = llList2String(resultList, childNumber);
list statusParts = llParseString2List(statusLineWithName, ["|"], []);
text = llList2String(statusParts, 0);
name = llList2String(statusParts, 1);
llSetText(name + " is doing the following right now:\n" + text, <1,1,1>, 1.0);
}
}
The main Twitter-prim script-code looks like this:
key requestId;
list resultList;
string rawList;
integer firstRun;
default
{
state_entry()
{
integer i;
rawList = "";
firstRun = 1;
// set permissions so the prim is able to link the objects
llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
// update every 30 seconds
llSetTimerEvent(30.0);
// listen on a channel so that the owner can post updates
llListen(4001, "", llGetOwner(), "" );
}
// if the owner wants to submit an update to Twitter
listen(integer channel, string name, key id, string message) {
llSay(0, "trying to send your status message: " + message);
// post the received chat-message to our server.
// use a special mimetype for submitting post-variables
llHTTPRequest("###YOUR RAILS URL###/post_message",[HTTP_METHOD,"POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],"msg="+message);
llSay(0, "ok");
}
// request the new updates
timer() {
llSay(0, "loading statuses...");
requestId = llHTTPRequest("###YOUR RAILS URL###/get_messages",[HTTP_METHOD,"GET"],"");
}
http_response(key request_id, integer status, list metadata, string body) {
integer i;
if (request_id == requestId) {
rawList = body;
// create the boxes from the list information
resultList = llParseString2List(body,["\n"],[]);
integer listlength = llGetListLength(resultList);
float boxPosition = 1;
for(i=0;i<listlength;i+=1) {
if(firstRun == 1) {
// rez objects only in the first run
llSay(0, "rez object "+(string)i);
llRezObject("status", llGetPos() + <0,>, ZERO_VECTOR, ZERO_ROTATION, i);
boxPosition+= 0.7;
} else {
// after that only update with link messages
llMessageLinked(LINK_ALL_CHILDREN, 0, rawList, NULL_KEY);
}
}
firstRun = 0;
} else {
// llSay(0,(string)status+" error");
}
}
object_rez(key id) {
llCreateLink(id, 1);
llMessageLinked(LINK_ALL_CHILDREN, 0, rawList, NULL_KEY);
}
}
So, that was the whole code, which works really nice as you can see in the screenshot in the old posting. If you want to see it in in-world-action, please visit me at http://slurl.com/secondlife/Oz/199/242/26.
4 comments:
Thanks for the nice post!
Would it be possible to use the online Ruby service Heroku to do the Ruby part of this sort of stuff?
Hi, i never heard of "Heroku" up to now. ;) But i think, yes. I should work.
Post a Comment