Caution. Use of this script may violate the TOS. Think of it as a weapon
Don't use this to spy on people without their permission
NO REALLY - DON'T
SECURITY
I take the Open Source security software attitude to this. You can't increase security through obscurity. All users of SL should be aware that tiny, invisible objects might be relaying their conversations to other AVs, or even sending IMs or HTTP messages out of SL. A bit like real life really, it's just that bugging is easier in SL.
If you want a semblance of privacy then use a one time pad code end to end in a sim which is completely off limits to all except your invited guests; deny others the ability to execute scripts or place objects; return all objects and check for running scripts (using estate tools) before you start your conversation.
Scripting Lessons
This script shows you how to track an AV. How to make invisible (full alpha) objects. How to use a notecard for configuration.
// Mission Spy
//Simple OpenSource Licence (I am trusting you to be nice)
//1. PLEASE SEND ME UPDATES TO THE CODE
//2. You can do what you want with this code and object including selling it in other objects and so on.
//You can sell it in closed source objects if you must, but please try to send any updates or
//improvements back to me for possible inclusion in the main trunk of development.
//3. You must always leave these instructions in any object created; notecard written; posting to
//any electronic medium such as Forum, Email &c. of the source code & generally be nice (as
//already requested!)
//4. You must not claim that anyone apart from sparti Carroll wrote the original version of this software.
//5. You can add and edit things below =THE LINE= but please try to keep it all making sense.
//Thank you for your co-operation
//sparti Carroll
integer FLAGdebug = 0;
string version = "1.1";
list visitor_list;
key k_owner;
string my_notecard = "SETTINGS";
// idea is this object orbits around and every 10 seconds announces who is there by saying or IM to owner
// Notecard handler based on http://secondlife.com/badgeo/wakka.php?wakka=LibrarySettingsNotecard
key kCurrentDataRequest;
string sSettingsNotecard;
//notecard configurable parameters
float range; // sensor range, in m
integer channel;
integer h_listen;
integer h_zero; // listening on 0
integer FLAGincludeowner;
integer FLAGtrack; // move to specified person's location + offset then stay there
integer FLAGuseIM;
string trackname; // who to track
string my_label;
float t_scan; // how often to scan
vector track_offset = <0,0,3>;
// time slices
float t_slice; // number of seconds /slice
integer ts_count;
integer ts_clearlist;
// spy control
integer FLAGspy; // listen to ch 0
// reset function for params, use notecard values if present
reset() {
visitor_list = [];
range = 100.0; // sensor range, in m
channel = 55;
FLAGincludeowner = 0;
FLAGspy = 1;
FLAGtrack = 0;
trackname = "";
k_owner = llGetOwner();
FLAGuseIM = 0;
llSetAlpha(0.0,ALL_SIDES);
}
sayowner(string s) {
if (FLAGuseIM) llInstantMessage(k_owner,s);
else llOwnerSay(s);
}
// time itself and dependencies
reset_time() {
t_scan = 5.0; // how often to scan in seconds (independent of time slices)
t_slice = 5.0; // time slice - speed of object (smallest division of time)
ts_count = 0; // time slice counter
ts_clearlist = 360; // clear list every 30 mins
}
// Functions
integer isNameOnList( string name )
{
integer len = llGetListLength( visitor_list );
integer test_len = llStringLength(name);
integer i;
for( i = 0; i < len; i++ ) {
string list_name = llList2String(visitor_list,i);
if (llGetSubString(list_name,0,test_len) != name) return TRUE;
}
return FALSE;
}
integer iNoteCardLine;
set_position(vector targetpos) {
while (llVecDist(llGetPos(),targetpos) > 0.001) llSetPos(targetpos);
}
default
{
on_rez( integer param ) {
llResetScript();
reset();
}
state_entry() {
reset();
integer iNotecardCount = llGetInventoryNumber( INVENTORY_NOTECARD );
integer iNotecardIndex = 0;
integer FLAGfoundmycard = FALSE;
while( FLAGfoundmycard == FALSE && iNotecardCount > iNotecardIndex ) { // if any notecards exist, get name of them until find my_notecard
sSettingsNotecard = llGetInventoryName( INVENTORY_NOTECARD, iNotecardIndex );
if (sSettingsNotecard == my_notecard) {
iNoteCardLine = 0;
FLAGfoundmycard = TRUE;
// YYY get notecard lines one at a time (drops key as only one request ?)
// ZZZ should do timer here, in case dataserver fails to return
kCurrentDataRequest = llGetNotecardLine( sSettingsNotecard, iNoteCardLine );
} else {
iNotecardIndex += 1; // go try next notecard
}
}
if (FLAGfoundmycard == FALSE) { // there are no useable notecards... 0 or none called my_notecard
sayowner( "No notecard found. Please read configuration instructions. Using defaults." );
state running;
}
}
dataserver( key kQuery, string sData ) {
// YYY see above, notecard lines arrive one at a time
if( sData != EOF ) {
list cmdline = llParseString2List(sData,[" ","="],[]);
string cmd = llList2String(cmdline,0);
string par = llList2String(cmdline,1);
if (cmd == "range") range = (float)par;
if (cmd == "time") t_scan = (float)par;
if (cmd == "channel") channel = (integer)par;
if (cmd == "owner") FLAGincludeowner = (integer)par;
if (cmd == "useim") FLAGuseIM = (integer)par;
if (cmd == "spy") FLAGspy = (integer)par;
kCurrentDataRequest = llGetNotecardLine( sSettingsNotecard, ++iNoteCardLine ); // YYY get rest of card, if any
} else {
// We have failed, so run it anyway
state running;
}
}
} // end state default
state running {
on_rez(integer start_param) {
llResetScript();
state default;
}
state_entry()
{
reset_time();
llSetText(my_label,<1.0,1.0,1.0>,1.0);
llSetTimerEvent(t_slice);
llSensorRepeat( "", "", AGENT, range, TWO_PI, t_scan );
h_listen = llListen(channel, "", k_owner, "");
}
sensor( integer number_detected )
{
integer i;
integer trackname_len = llStringLength(trackname);
for( i = 0; i < number_detected; i++ ) {
key detected_key = llDetectedKey(i);
string detected_name = llDetectedName(i);
vector detected_pos = llDetectedPos(i);
if (FLAGtrack) {
string detected_subname = llToLower(llGetSubString(detected_name,0,trackname_len - 1));
if (detected_subname == trackname) {
set_position(detected_pos + track_offset);
}
}
string region_name = llGetRegionName();
if ( FLAGincludeowner || detected_key != k_owner ) {
if ( isNameOnList( detected_name ) == FALSE ) {
visitor_list += detected_name + " at " + (string)detected_pos + " in " + region_name;
llInstantMessage(k_owner,detected_name + " spotted at " + (string)detected_pos + " in " + region_name);
}
}
}
}
listen( integer chan, string name, key id, string message )
{
if (chan == 0) {
llInstantMessage(k_owner,">>" + name + "<< '" + message + "'");
}
else if (chan == channel) {
list cmdline = llParseString2List(message,[" ","="],[]);
string cmd = llList2String(cmdline,0);
string par = llList2String(cmdline,1);
string par2 = llList2String(cmdline,2);
if (cmd == "range") {
range = (float)par;
llSensorRepeat( "", "", AGENT, range, TWO_PI, t_scan );
sayowner("Scan range set to " + par);
}
else if (cmd == "channel") {
llListenRemove(h_listen);
channel = (integer)par;
h_listen = llListen(channel, "", k_owner, "");
sayowner("Command channel changed to " + (string)channel);
}
else if (cmd == "owner") {
par = llToLower(par);
if (par == "0" || par == "n" || par == "no") {
FLAGincludeowner = 0;
sayowner("Owner will no longer be detected");
}
else {
FLAGincludeowner = 1;
sayowner("Owner will be detected");
}
}
else if (cmd == "spy") {
par = llToLower(par);
if (h_zero != 0) {
llListenRemove(h_zero);
h_zero = 0;
}
if (par == "0" || par == "n" || par == "no") {
FLAGspy = 0;
sayowner("Spy mode deactivated");
}
else {
FLAGspy = 1;
h_zero = llListen(0,"","","");
sayowner("Spy mode activated");
}
}
else if (cmd == "useim") {
par = llToLower(par);
if (par == "1" || par == "y" || par == "yes") {
FLAGuseIM = 1;
sayowner("Using IM reporting channel");
}
else {
FLAGuseIM = 0;
sayowner("Using OwnerSay reporting channel");
}
}
else if (cmd == "track") {
if (par == "") {
if (trackname == "") sayowner("Not tracking");
else sayowner("Currently tracking " + trackname);
} else if (par == "off") {
FLAGtrack = 0;
} else {
if (par2 != "") trackname= par + " " + par2;
else trackname = par;
trackname = llToLower(trackname);
FLAGtrack = 1;
sayowner("Now tracking " + trackname);
}
}
else if( message == "report" ) {
sayowner("ScanReport:");
integer len = llGetListLength( visitor_list );
integer i;
for( i = 0; i < len; i++ ) {
sayowner(llList2String(visitor_list, i));
}
sayowner("Total = " + (string)len );
}
else if( message == "reset" ) {
state default;
}
else if (message == "die") {
sayowner( "Terminated");
llDie();
}
}
} // end listen()
timer() { // now under t_slice control
ts_count++;
if (ts_count % ts_clearlist == 0) { // clear list of people
visitor_list = [];
}
}
} // end state run_object
The SETTINGS notecard
//ZZ turn on stealth mode
stealth=1
//ZZ detect the owner
owner=1
Some instructions for use
Mission Spy 1.1
>> Supported Product <<
>> If you require any help or have feature requests or bug reports for Mission Spy please IM me <<
Please read through these instructions first in case your question is answered here, thank you !
Mission Spy has two roles:
1. It can report on visitors to an area, with a configurable range.
2. It can report conversations in an area.
PLEASE NOTE that misuse of either of these services could constitue contravention
of the terms of the Second Life SLA. It is suggested that the Mission Spy is only
used in Combat Zones, or otherwise with the consent of those in the area - e.g. for
relaying conversations.
Mission Spy is discreet, you will hardly notice it's 0.01 x 0.01 x 0.01 full alpha sphere.
Mission Spy can be configured by chatting or shouting on a command channel, 55 by default, but this is configurable. It only pays attention to the owner on the command channel.
All reporting to the owner is via llOwnerSay or llInstantMessage. Instant message may not be suitable for heavy conversation areas and llOwnerSay is the default.
GETTING STARTED
1. Rez the Mission Spy Box on the ground
2. Touch the box... you will be given a Mission Spy Tools folder
3. Drag a Mission Spy object onto the ground and then use the commands to control it.
4. The object is copy so don't worry about losing it.
5. To clear up say "/55 die" - see below.
COMMANDS
Commands available in version 1.1 are:
/55 channel n
Switch commands to channel n
/55 range n
Set the avatar detection range to n, up to 100 (metres) is valid.
/55 owner n
/55 owner y
Sets whether the owner will not "n", or will "y" be included in scans
/55 spy n
/55 spy y
Sets whether Mission Spy is reporting conversations back to the owner
/55 useim n
/55 useim y
Sets whether Mission Spy reports via llOwnerSay or llInstantMessage
neither will cause particles to be displayed
/55 track PARTIALNAME
Follow avatars matching PARTIALNAME (case insensitive) around the sim
Essential for following conversations in a fast moving battle
Will pickup and track again if the av. leaves and returns
Eg. "/55 track sparti" would follow me around (!)
/55 report
Report list of avs detected since last auto-reset
Owner will receive IM individual as well
/55 reset
Reset Mission Spy
/55 die
Delete the Mission Spy object
Some parameters can be configured via notecard - to reduce the setup time or allow you to create a number of separate trackers.
Of course Mission Spy is supplied COPY (+MOD for the notecard).
NOTECARD CONFIGURATION
The commands which can be put into the notecard are:
time T
Set time between scans to T seconds
range R
Set avatar detection range
channel C
Set command channel
owner O
Set whether owner is detected
useim U
Set whether instant messaging (or OwnerSay) is used
spy S
Set whether conversations are reported
>> Supported Product <<
>> If you require any help or have feature requests or bug reports for Mission Spy please IM me <<