API

Text-to-speech update: Linden Lab has asked us to either make this a Premium Plus only service, or to discontinue the service. The text-to-speech service will be unavailable while we consider all of our options.

GET: /static-api/regions/[region_name].json

/static-api/regions/Bonnie Bay.json

GET: /static-api/regions/[region_x]/[region_y]/index.json

/static-api/regions/1003/1345/index.json
Response:
{
  "region_name": "Bonnie Bay",
  "region_map_image": "3a60ad94-9d62-35c7-091a-09cd216dd6b8",
  "region_x": 1003,
  "region_y": 1345,
  "region_owner": "cd97e3f8-9f75-40b4-ad5d-b9868d7595c5",
  "region_product_sku": "229",
  "region_product_name": "Estate / Homestead",
  "estate_id": 60025,
  "hard_max_agents": 25,
  "hard_max_objects": 5000,
  "deny_age_unverified": false,
  "region_access": 42,
  "deleted_at": null,
  "estate_name": "Bonnie Bay",
  "region_ip": "54.191.219.124",
  "region_port": 13043,
  "channel_version": "Second Life Server 2023-06-09.580543",
  "region_updated_at": "2023-07-10T22:04:00.833Z",
  "access_name": "Adult"
}
LSL example:
string API = "https://www.bonniebots.com/static-api/regions/";

default
{
    state_entry()
    {
        // Stores the region name then makes a request to the BonnieBots region API.
        string region = llGetRegionName();
        llHTTPRequest(API + region + ".json", [HTTP_METHOD, "GET", HTTP_MIMETYPE, "application/json"], "");
    }

    http_response(key request, integer s, list h, string m)
    {
        // Assigns the map texture to the entire linkset on all sides based on the region_map_image json value.
        llSetLinkPrimitiveParamsFast(
          LINK_SET, 
          [PRIM_TEXTURE, ALL_SIDES, llJsonGetValue(m, ["region_map_image"]), 
          <1,1,0>, 
          ZERO_VECTOR, 
          0
        ]);
    }
}

GET: /static-api/regions/[region_name]/parcels.json

/static-api/regions/Bonnie Bay/parcels.json

GET: /static-api/regions/[region_x]/[region_y]/index.json

/static-api/regions/1003/1345/parcels.json
Response:
[
  {
    "parcel_id": "c75e5e16-0628-de04-d641-51a98945b617",
    "parcel_area": 9216,
    "parcel_deleted": null,
    "parcel_description": "Cute things will be covered in syrup and devoured. You have been warned.",
    "parcel_name": "Pancake Isle",
    "parcel_status": 0,
    "parcel_updated_at": "2023-07-10T22:03:57.503Z"
  },
  {
    "parcel_id": "c7d50eba-d0ae-a8e1-0094-0017c9ff9479",
    "parcel_area": 56320,
    "parcel_deleted": null,
    "parcel_description": "Discover how bots are evil and definitely stalk you.",
    "parcel_name": "BonnieBots HQ",
    "parcel_status": 0,
    "parcel_updated_at": "2023-07-10T22:03:57.503Z"
  }
]

GET: /static-api/regions/popular/[i[1-100]].json

/static-api/regions/popular/1.json
/static-api/regions/popular/100.json
A list of the 100 most popular regions updated hourly.
LSL example:
// Select a random region from the 100 most popular regions.

integer random() {
  return (integer)(llFrand(100) + 1);
}

getRandomLocation() {
  llHTTPRequest("https://www.bonniebots.com/static-api/regions/popular/" + (string)random() + ".json", [], "");
}

default {
  state_entry() {
  }

  touch_start(integer total_number) {
      getRandomLocation();
  }
  
  http_response(key i, integer s, list m, string b) {
      string region_name = llJsonGetValue(b, ["region_name"]);
      string access_name = llJsonGetValue(b, ["access_name"]);
      llOwnerSay("https://maps.secondlife.com/secondlife/" + llEscapeURL(region_name) + "/128/128/30 " + access_name);
  }
}

GET: /static-api/terrain/[region_name].png

/static-api/terrain/North Forepaw.png
Example:

GET: /static-api/top-attachments/[year]/[month]/[day].csv

/static-api/top-attachments/2023/02/09.csv
Connection example:
import { io } from "socket.io-client";

const socket = io("https://realtime.bonniebots.com:3443");

socket.on('message', (data: any) => {
    console.log(data);
});
REGION_DATA message:
{
    "region_name": "Rainbow",
    "region_map_image": "428464f7-a3d8-f50e-6a68-f388b4f1df55",
    "region_x": 1004,
    "region_y": 1021,
    "scan_id": 5815,
    "region_created_at": "2022-06-26T16:40:22.096Z",
    "region_updated_at": "2023-01-29T03:32:13.483Z",
    "region_visited_at": "2023-01-28T22:31:38.853Z",
    "region_owner": "00000000-0000-0000-0000-000000000000",
    "region_cpu": 947,
    "region_cpu_ratio": 1,
    "region_colo_name": "aws-us-west-2a",
    "region_product_sku": "129",
    "region_product_name": "Mainland / Homestead",
    "estate_id": 1,
    "hard_max_agents": 25,
    "hard_max_objects": 5000,
    "deny_age_unverified": false,
    "region_visited_by": "bonniebelle90",
    "region_banned_at": null,
    "region_access": 21,
    "deleted_at": null,
    "region_banned_reason": null,
    "region_visited_height": 4000,
    "region_visited_nearby_objects": 0,
    "region_requested_drop_zone": null,
    "estate_name": "mainland",
    "covenant_uuid": "00000000-0000-0000-0000-000000000000",
    "avatarCount": 1,
    "messageType": "REGION_DATA"
}
Creates and uploads an audio clip of the requested text being read.

Text-to-speech update: Linden Lab has asked us to either make this a Premium Plus only service, or to discontinue the service. The text-to-speech service will be unavailable while we consider all of our options.

For a list of available voices see https://mycroftai.github.io/mimic3-voices/#en_US.
LSL example:
// Request a text-to-speech sound clip from Bonnie and play it.

// Choose your own voice!
string voice = "en_US/vctk_low#p310";
// Examples:

// Mimic3 voices can be found at https://mycroftai.github.io/mimic3-voices/
// string voice = "en_US/cmu-arctic_low#ljm";
// string voice = "de_DE/thorsten-emotion_low#drunk";

// Edge TTS voices.
// string voice = "edge-tts/en-US-GuyNeural";
// string voice = "edge-tts/ja-JP-KeitaNeural";

// Eleven Labs voices (requires registration with third party).
// string voice = "elevenlabs/YOUR VOICE HERE";
// token required only for elevenlabs.
string token = "YOUR TOKEN HERE";

// Customize your voice!
// Adjust pitch: larger numbers are higher, smaller numbers are deeper.
integer pitch = 0;

// Adjust scale: larger numbers are slower, smaller numbers are faster.
float scale = 1.0; 

// ----- 

key bonnieBelle86 = "5a6b0045-12db-4bf6-8108-7c27f024ca5b";
string URL = "";

requestTTS(string text) {
    // Truncate text so we don't overflow the llInstantMessage limit.
    text = llBase64ToString(llGetSubString(llStringToBase64(text), 0, 512));

    string message = llList2Json(JSON_OBJECT, [
        "command", "tts",
        "voice", voice,
        "scale", scale,
        "pitch", pitch,
        "body", text,
        "token", token,
        "url", URL
    ]);
    
    llInstantMessage(bonnieBelle86, message);
}

default {
    on_rez(integer n) {
        llResetScript();
    }
    
    state_entry() {
        llRequestURL();
        llListen(0, "", "", "");
        llSetTimerEvent(3600);
    }
    
    listen(integer c, string n, key i, string m) {
        if (llGetOwner() != llGetOwnerKey(i)) return;
        
        // Uncomment line below to disable reading all emotes.
        // if (llGetSubString(m, 0, 2) == "/me") return;
        
        // Only read text from attachments.
        integer attachmentPoint = llList2Integer(llGetObjectDetails(i, [OBJECT_ATTACHED_POINT]), 0); 
        if (attachmentPoint == 0 && llGetOwner() != i) return;

        // Do not read /me in emotes.
        if (llGetSubString(m, 0, 2) == "/me") {
            m = llGetSubString(m, 3, -1);
        }
        
        llSetTimerEvent(60);
        requestTTS(m);
    }
    
    http_request(key id, string method, string body) {
        if (method == URL_REQUEST_GRANTED) {
            URL = body;
        } else {
            llHTTPResponse(id, 200, "OK");
            llSetTimerEvent(0);
            llSetTimerEvent(3600);
            string sound = llJsonGetValue(body, ["uuid"]);
            float duration = (float)llJsonGetValue(body, ["duration"]);
            llTriggerSound(sound, 1.0);
            llSleep(duration);
        }
    }
    
    changed(integer change) { 
        if (change & CHANGED_TELEPORT) {
            llResetScript();
        }
        if (change & CHANGED_REGION) 
        {
            llResetScript();
        }
        if (change & CHANGED_OWNER)
        {
            llResetScript();
        }
    }
    
    timer() {
        llResetScript();
    }
}
Creates and uploads a texture containing the text requested.

Text-to-texture update: Linden Lab has asked us to either make this a Premium Plus only service, or to discontinue the service. The text-to-texture service will be unavailable while we consider all of our options.

LSL example:
// Requests a texture from Bonnie and displays it on a prim.
key bonnieBelle = "5a6b0045-12db-4bf6-8108-7c27f024ca5b";
string URL = "";

request(string text) {
    // Truncate text so we don't overflow the llInstantMessage limit.
    text = llBase64ToString(llGetSubString(llStringToBase64(text), 0, 512));

    string message = llList2Json(JSON_OBJECT, [
        "command", "text-to-texture",
        "key", "optional request/response id",
        "body", text,
        "bgColor", "#00000088",
        "textColor", "#FFFFFF",
        "fontWeight", 400,
        "lineHeight", 40,
        "margin", 10,
        "customHeight", 1024,
        "maxWidth", 1024,
        "fontSize", 36,
        "textAlign", "center",
        // "textAlign", "left",
        "verticalAlign", "center",
        // "verticalAlign", "top",
        "url", URL
    ]);

    llInstantMessage(bonnieBelle, message);
}

set(string uuid) {
    llSetTexture(uuid, 0);
}

default {
    on_rez(integer n) {
        llResetScript();
    }
    
    state_entry() {
        llRequestURL();
        llListen(0, "", "", "");
        llSetTimerEvent(3600);
    }
    
    listen(integer c, string n, key i, string m) {
        llSetTimerEvent(60);
        request(m);
    }
    
    http_request(key id, string method, string body) {
        if (method == URL_REQUEST_GRANTED) {
            URL = body;
        } else {
            llHTTPResponse(id, 200, "OK");
            llSetTimerEvent(0);
            llSetTimerEvent(3600);
            string requestId = llJsonGetValue(body, ["key"]);
            string uuid = llJsonGetValue(body, ["uuid"]);
            set(uuid);
        }
    }

    changed(integer change) { 
        if (change & CHANGED_TELEPORT) {
            llResetScript();
        }
        if (change & CHANGED_REGION) 
        {
            llResetScript();
        }
        if (change & CHANGED_OWNER)
        {
            llResetScript();
        }
    }
    
    timer() {
        llResetScript();
    }
}