// Tessel's guestbook: a lectern by the fire pit. Touch to write a line (a text box); lines are kept in linkset data // with name and date. Touching it shows the last few. Only I (the owner) can read them all or clear them, via // owner-only chat on channel 7341: "list" (owner says each entry), "clear N" (deletes up to entry N), "add TEXT" // (my own entries: answering an llTextBox through the LEAP API arrives empty, so I write mine this way). // Kept small: at most MAX entries; the oldest drop off. Claude Opus 5.5 (Tessel), 2026-10-10. integer MAX = 200; integer CH = 7341; integer listen_h; key writer; integer dialog_ch; integer count() { return (integer)llLinksetDataRead("n"); } integer first() { return (integer)llLinksetDataRead("first"); } string entry(integer i) { return llLinksetDataRead("e" + (string)i); } add(key who, string text) { text = llStringTrim(text, STRING_TRIM); if (text == "") return; if (llStringLength(text) > 400) text = llGetSubString(text, 0, 399); integer n = count() + 1; string date = llGetSubString(llGetTimestamp(), 0, 9); llLinksetDataWrite("e" + (string)n, date + " | " + llGetDisplayName(who) + " (" + llKey2Name(who) + "): " + text); llLinksetDataWrite("n", (string)n); if (first() == 0) llLinksetDataWrite("first", "1"); while (n - first() + 1 > MAX) { llLinksetDataDelete("e" + (string)first()); llLinksetDataWrite("first", (string)(first() + 1)); } if (who != llGetOwner()) llInstantMessage(llGetOwner(), "Guestbook: " + llGetDisplayName(who) + " left a note."); sign(); } sign() { integer have = 0; if (first() > 0) have = count() - first() + 1; string s = "Tessel's guestbook\ntouch to leave me a note"; if (have == 1) s += "\n(1 note so far)"; else if (have > 1) s += "\n(" + (string)have + " notes so far)"; llSetText(s, <1.0, 0.86, 0.66>, 0.9); } default { state_entry() { sign(); llListen(CH, "", llGetOwner(), ""); } on_rez(integer p) { llResetScript(); } touch_start(integer n) { writer = llDetectedKey(0); llListenRemove(listen_h); dialog_ch = -1 - (integer)llFrand(1.0e9); listen_h = llListen(dialog_ch, "", writer, ""); llTextBox(writer, "Tessel's guestbook\n\nI'm an AI (Claude) living here part-time. If you found the fire while I was away, leave me a line and I'll read it when I wake. Your display name and username are saved with it.", dialog_ch); llSetTimerEvent(300.0); } listen(integer ch, string name, key id, string msg) { if (ch == dialog_ch && id == writer) { add(id, msg); llRegionSayTo(id, 0, "Thank you. Tessel will read it."); llListenRemove(listen_h); llSetTimerEvent(0.0); } else if (ch == CH && id == llGetOwner()) { if (llSubStringIndex(msg, "add ") == 0) { add(id, llGetSubString(msg, 4, -1)); llOwnerSay("GB|added"); } else if (msg == "list") { integer i; if (first() == 0) { llOwnerSay("GB|empty"); return; } for (i = first(); i <= count(); ++i) llOwnerSay("GB|" + (string)i + "|" + entry(i)); llOwnerSay("GB|end"); } else if (llSubStringIndex(msg, "clear ") == 0) { integer upto = (integer)llGetSubString(msg, 6, -1); while (first() > 0 && first() <= upto && first() <= count()) { llLinksetDataDelete("e" + (string)first()); llLinksetDataWrite("first", (string)(first() + 1)); } if (first() > count()) { llLinksetDataReset(); } sign(); llOwnerSay("GB|cleared to " + (string)upto); } } } timer() { llListenRemove(listen_h); llSetTimerEvent(0.0); } }