BTW, the logging script on my web server is a trivial perl script that just appends what it gets to a file.
I've obscured the URL in this code.
TIA.
---------------------
CODE
// ZyzzyVendor
//
// Simple single-prim vendor with logging.
//
// Written April 29, 2007 by Zyzzy Zarf
//
// Intended for objects that vend copies of themselves.
// One partner is allowed, who will get a cut of the proceeds.
// The first object (alphabetically) in the inventory of the vendor is the vended object.
//
// Some code derived from the 1-prim vendor by Hiro Pendragon.
//
// Licensed under Creative Commons Attribution 3.0
// see <http://creativecommons.org/licenses/by/3.0/>
// Any re-use or adaptation must credit Zyzzy Zarf as a source.
// a marker that there is no price. A high positive number to avoid bugs resulting in free objects.
integer kNoPrice = 9999999;
// this script will never give out more than this amount. Ensure that it's reasonable.
integer kMaxMoneyGive = 1000;
// log to this URL with POST calls
// ***** CHANGE THIS TO YOUR CGI URL *****
string kLoggingURL = "http://yourdomain.com/yourlogger.cgi";
// bump to distinguish versions in the field
string kVersionString = "ZyzzyVendor V1.0";
// channel on which to listen for commands
integer kCmdChan = 9;
key gOwnerKey = NULL_KEY;
key gPartnerKey = NULL_KEY;
string gPartnerName = "";
integer gSplitPercent = 0;
integer gPrice = kNoPrice;
key gLastToucherKey = NULL_KEY;
string gVendedObjectName = "";
// set to 1 when an HTTP request has been made, but not yet ack'd
integer gOutstandingAcks = 0;
// the location of the vendor
string gLocation = "";
// returns an integer location string
string simpleLoc()
{
vector myLocation = llGetPos();
integer xloc = (integer)myLocation.x;
integer yloc = (integer)myLocation.y;
integer zloc = (integer)myLocation.z;
return llGetRegionName() + " <" + (string)xloc + ", " + (string)yloc + ", " + (string)zloc + ">";
}
reset()
{
// user-set state
gPrice = kNoPrice;
gPartnerKey = NULL_KEY;
gPartnerName = "";
gSplitPercent = 0;
gLastToucherKey = NULL_KEY;
}
initialize()
{
gOwnerKey = llGetOwner();
gVendedObjectName = llGetInventoryName( INVENTORY_OBJECT, 0 );
gOutstandingAcks = 0;
reset();
gLocation = simpleLoc();
// so that we can split money
llRequestPermissions( gOwnerKey, PERMISSION_DEBIT );
}
integer checkStatusValidity()
{
// we must have an object to vend and a price to be usable
return gVendedObjectName != "" && gPrice != kNoPrice && gPrice >= 0 && gSplitPercent <= 100 && gSplitPercent >= 0;
}
// returns whether there's still a vendable object in inventory
integer checkChange( integer change )
{
if ( change != CHANGED_INVENTORY )
return TRUE;
gVendedObjectName = llGetInventoryName( INVENTORY_OBJECT, 0 );
return gVendedObjectName != "";
}
logEvent( string reason, key avKey, integer amtPaid )
{
if ( gOutstandingAcks > 0 )
llInstantMessage( gOwnerKey, llGetObjectName() + " - Missed an HTTP log response" );
// log the sale to my web server
string avName;
if ( avKey == NULL_KEY )
avName = "Testing Testing";
else if ( avKey == gPartnerKey ) {
// partner may not be in same sim
avName = gPartnerName;
}
else
avName = llKey2Name( avKey );
if ( avName == "" ) {
// shouldn't happen, but use key if no name
avName = (string)avKey;
}
// comment out this block if you're not doing HTTP logging
integer utime = llGetUnixTime();
string postData = "time=" + (string)utime + "&reason=" + reason + "&avname=" + avName + "&vendorname=" + llGetObjectName() + "&location=" + gLocation + "&paid=" + (string)amtPaid + "&price=" + (string)gPrice;
postData = llEscapeURL( postData );
gOutstandingAcks = 1;
llHTTPRequest( kLoggingURL, [HTTP_METHOD, "POST"], postData );
// IM the owner and partner about the event
string msg = reason + ", Av: " + avName + ", amount: L$" + (string)amtPaid + ", price: L$" + (string)gPrice + ", vendor: " + llGetObjectName() + ", location: " + gLocation;
llInstantMessage( gOwnerKey, msg );
if ( gPartnerKey != NULL_KEY )
llInstantMessage( gPartnerKey, msg );
}
// add some sanity checks to giveMoney so I don't go broke because of a bug
giveMoney( string reason, key recipient, integer amount )
{
if ( amount < 1 )
return;
if ( amount > kMaxMoneyGive || amount > gPrice ) {
logEvent( "InvalidPayoutError for " + reason, recipient, amount );
return;
}
logEvent( reason, recipient, amount );
llGiveMoney( recipient, amount );
}
default
{
state_entry()
{
initialize();
state setupMode;
}
on_rez( integer param )
{
initialize();
state setupMode;
}
}
state setupMode
{
state_entry()
{
// listen only to owner, and on channel 3
// listens are automatically removed when exiting a state
llListen( kCmdChan, "", gOwnerKey, "" );
gLastToucherKey = NULL_KEY;
llOwnerSay( kVersionString );
llOwnerSay( llGetObjectName() + " is listening for vendor commands on channel " + (string)kCmdChan + ". Say '/" + (string)kCmdChan + " v help' for help." );
// revert to run if possible and owner has not given any commands
llSetTimerEvent( 120 );
}
// ensure that we start fresh when rezzed
on_rez( integer param )
{
state default;
}
listen( integer channel, string name, key id, string message )
{
// all our commands start with 'v '
if ( llGetSubString( message, 0, 1 ) != "v " )
return;
// turn off the timer since the owner is clearly entering commands
llSetTimerEvent( 0 );
message = llGetSubString( message, 2, -1 );
string cmd = message;
string args = "";
// find the first space char, which delimits the command
integer delimiterPos = llSubStringIndex( message, " " );
if ( delimiterPos != -1 ) {
cmd = llGetSubString( message, 0, delimiterPos - 1 );
args = llGetSubString( message, delimiterPos + 1, -1 );
}
cmd = llToLower( cmd );
if ( cmd == "help" ) {
llOwnerSay( kVersionString );
llOwnerSay( "Vendor commands. Chat on channel " + (string)kCmdChan );
llOwnerSay( "v help - this message" );
llOwnerSay( "v price <num> - set price to num" );
llOwnerSay( "v partner - sets the partner to get a split. Partner must touch before command.");
llOwnerSay( "v split <%> - sets % of revenue split. % is an integer from 1 to 100." );
llOwnerSay( "v nosplit - turns off splitting" );
llOwnerSay( "v reset - resets all settings" );
llOwnerSay( "v status - lists settings" );
llOwnerSay( "v test - tests logger" );
llOwnerSay( "v run - stop listening and vend. Owner touch to start listening again" );
return;
}
if ( cmd == "run" ) {
if ( checkStatusValidity() ) {
llOwnerSay( "Running vendor" );
state runMode;
}
llOwnerSay( "Invalid setup. Can't run.");
return;
}
if ( cmd == "price" ) {
gPrice = (integer)args;
llOwnerSay( "Price is now " + (string)gPrice );
return;
}
if ( cmd == "nosplit" ) {
gPartnerKey = NULL_KEY;
gSplitPercent = 0;
llOwnerSay( "Removed partner and split" );
return;
}
if ( cmd == "partner" ) {
if ( gLastToucherKey == NULL_KEY ) {
llOwnerSay( "Partner must touch vendor before you give the partner command" );
return;
}
gPartnerKey = gLastToucherKey;
gPartnerName = llKey2Name( gPartnerKey );
llOwnerSay( "Partner set to " + gPartnerName );
// let the partner hear this
llWhisper( 0, gPartnerName + " will receive " + (string)gSplitPercent + "% of each sale" );
return;
}
if ( cmd == "split" ) {
integer splitPercent = (integer)args;
if ( splitPercent < 1 || splitPercent > 100 ) {
llOwnerSay( "Split percent must be an integer between 1 and 100" );
return;
}
gSplitPercent = splitPercent;
string partner = "Partner";
if ( gPartnerKey != NULL_KEY )
partner = gPartnerName;
llOwnerSay( partner + "will receive " + (string)gSplitPercent + "% of each sale" );
return;
}
if ( cmd == "reset" ) {
reset();
llOwnerSay( "All settings reset" );
return;
}
if ( cmd == "status" ) {
string str;
llOwnerSay( "Status:" );
str = "No object to vend in contents";
if ( gVendedObjectName != "" )
str = "Vending object named " + gVendedObjectName;
llOwnerSay( str );
str = "No price set";
if ( gPrice != kNoPrice )
str = "Price: " + (string)gPrice;
llOwnerSay( str );
str = "No partner set";
if ( gPartnerKey != NULL_KEY )
str = gPartnerName + " will receive " + (string)gSplitPercent + "% of each sale";
llOwnerSay( str );
return;
}
if ( cmd == "test" ) {
logEvent( "Test", NULL_KEY, 12345 );
return;
}
llOwnerSay( "Unrecognized command: '" + message + "'" );
}
touch_start( integer param )
{
key toucher = llDetectedKey( 0 );
// ignore owner touches - can't be partner too
if ( toucher == gOwnerKey )
return;
gLastToucherKey = toucher;
// tell that we've been set for a partner
llWhisper( 0, "Ready to partner with " + llKey2Name( gLastToucherKey ));
}
changed( integer change )
{
checkChange( change );
}
http_response( key request_id, integer status, list metadata, string body )
{
gOutstandingAcks = 0;
llOwnerSay( llGetObjectName() + " - HTTP response: " + (string)status );
}
timer()
{
// turn off the timer
llSetTimerEvent( 0 );
// if we can, switch back to run mode
if ( checkStatusValidity() ) {
llOwnerSay( "No commands seen after 2 minutes. Running vendor" );
state runMode;
}
}
}
state runMode
{
state_entry()
{
llOwnerSay( llGetObjectName() + " has stopped listening for commands. Touch to listen again." );
// allow only one pay price
llSetPayPrice( PAY_HIDE, [gPrice, PAY_HIDE, PAY_HIDE, PAY_HIDE] );
}
// ensure that we start fresh when rezzed
on_rez( integer param )
{
state default;
}
touch_start( integer param )
{
key toucher = llDetectedKey( 0 );
// only the owner's touch makes it listen
if ( toucher != gOwnerKey )
return;
state setupMode;
}
changed( integer change )
{
if ( !checkChange( change )) {
llOwnerSay( "No object to vend" );
state setupMode;
}
}
money( key giver, integer amount )
{
if ( gOutstandingAcks > 0 )
llInstantMessage( gOwnerKey, llGetObjectName() + " - Missed an HTTP log response" );
if ( amount < gPrice ) {
// underpayment, return money
llWhisper( 0, "This item costs L$" + (string)gPrice );
llWhisper( 0, "You paid only L$" + (string)amount );
giveMoney( "Underpayment", giver, amount );
return;
}
string giverName = llKey2Name( giver );
string firstName = "Unknown";
firstName = llGetSubString( giverName, 0, llSubStringIndex( giverName, " " ));
llWhisper( 0, "Please enjoy your new " + gVendedObjectName + ", " + firstName);
llGiveInventory( giver, gVendedObjectName );
if ( amount > gPrice ) {
// overpayment, return change
integer overpayment = amount - gPrice;
llWhisper( 0, "You overpaid by L$" + (string)overpayment + " - returning change" );
giveMoney( "Overpayment", giver, overpayment );
}
// log the sale to my web server
logEvent( "Sale", giver, amount );
// it would be bad to split if the owner paid
if ( giver == gOwnerKey )
return;
if ( gPartnerKey != NULL_KEY ) {
// note that this keeps the fractional L$ for the owner
integer splitAmount = (integer)(((float)gPrice * gSplitPercent) / 100.0);
giveMoney( "Split", gPartnerKey, splitAmount );
}
}
http_response( key request_id, integer status, list metadata, string body )
{
gOutstandingAcks = 0;
if ( status < 200 || status > 299 )
llInstantMessage( gOwnerKey, llGetObjectName() + " - HTTP error response: " + (string)status );
}
}