Advanced Visitor Counter

From Fire And Ice Grid
Jump to navigation Jump to search


Introduction

The Covey Advanced Visitor is as complete a visitor tracking system as is reasonably possible to make using just inworld tools. It records the difference between the total number of visits each day and the number of unique visits. Additionally each day a list of the unique visitors is saved. To stop the memory requirements quickly spiralling out of control this information is saved to a notecard at the end of each day. Then at the end of each month, the daily notecards are combined into one for the previous month. Finally, at the end of the year, each of the monthly notecards is combined into one for the entire year. If in use it will also send instant messages to helpers alerting them when an avi arrives in that region for the first time that day. Similarly each time someone arrives in the region for the first time that day their name will be displayed on a visitors board.

How it works

This script will turn the prim it is in into a volumetric prim. Volumetric prims act like phantom prims while still allowing the recording of collision events. Each time an avi passes through the prim it checks to see if that avi has already visited the sim today. If they have it will increase the total visits count but not the unique count, nor will it save the avatars details again. If however the avatar has not visited yet today the counter stores the avatars details and increases both the unique and total visitors counter. In addition to the collision based detection, the region is periodically scanned. This scan is done to catch avatars who login into their last location or who bypass the sim landing point. If you want to have a displayed visitors list then add another prim with the display board script in it. If you wish to have helpers notified of first-time visits each day, include the HelpersMessages script and the helpers notecard.

Instructions

  1. Make a prim which covers the landing point of your region
  2. Make a notecard called 'Admin' and use the template below to add in the keys (UUID's) of the avatars you wish to be able to see the contents of the counter.
  3. Make a notecard called 'Ignore' and use the template below to add in the keys (UUID's) of the avatars you wish the counter to ignore.
  4. Make a notecard called 'Helpers' and use the template below to add in the keys (UUID's) of the avatars you wish to be helpers.
  5. Add the two notecards you just created to the prim
  6. Create a new script inside the prim you created
  7. Copy-paste the script below into the one you just created and save.
  8. Make a new prim for the display board
  9. Make a new script and copy the contents from the display board script into the script in the new board

Admin List

Any avi on the admin list will be able to click the counter to get a folder with all notecards currently held inside the visitor counter.

Ignore List

Any avi on the ignore list will not be counted at all, and will not be recorded. It will be as though they never visited.

Helpers List

Any avi on the helper's list will be sent an IM notification the first time someone visits the region each day.

Licence

 1 BSD 3-Clause License
 2 Copyright (c) 2020, Sara Payne
 3 All rights reserved.
 4 Redistribution and use in source and binary forms, with or without
 5 modification, are permitted provided that the following conditions are met:
 6 1. Redistributions of source code must retain the above copyright notice, this
 7    list of conditions and the following disclaimer.
 8 2. Redistributions in binary form must reproduce the above copyright notice,
 9    this list of conditions and the following disclaimer in the documentation
10    and/or other materials provided with the distribution.
11 3. Neither the name of the copyright holder nor the names of its
12    contributors may be used to endorse or promote products derived from
13    this software without specific prior written permission.
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Script - Visitor Counter

  1 list todaysVisitors; //list contains names and grid uri as CSV (uniquire visits in the day)
  2 list todaysVisitorsUUIDs;//list contains all unique UUIDS detected today. 
  3 key lastCollider;//uuid of the last avi to collide with this object
  4 integer lastDay; //day of the month at the last point we checked
  5 integer lastMonth; //month at the year at the last point we checked 
  6 integer lastYear; //year at the last point we checked
  7 list daysOfMonthNotecards;//used while processing change of month
  8 list monthsOfYearNotecards; //used while processing change of year
  9 integer totalVisitorsCalculation; //used while processing change of month
 10 list lastPeriodsVisitors; //used while processing change of month  
 11 list admins;//list of people allowed to access the counters menu
 12 list ignore;//list of people who will not be counted by the visitor counter. 
 13 integer menuChannel; //channel the menu listens on
 14 integer menuChannelListen; //handle to turn the listener on and off
 15 key lastAdmin; //uuid of the last admin to use the menu, used to send time out warning
 16 integer timeInterval = 30; //how frequently the sim is checked for visitors
 17 integer menuListen; //used to aid in tracking the listener...shouldn't be needed working aorund OS bugs
 18 list notecardsToProcess; //used when processing a new month or year, temp storage of notecard names
 19 integer debug = FALSE;
 20 integer displayComsChannel = -6827416;
 21   
 22 integer GetDate (string dayMonthYear) 
 23 {   //fetches the date, and breaks it down into component parts returning the requested prt
 24     list dateComponents = llParseString2List(llGetDate(), ["-"], []);
 25     integer toReturn;
 26     if (dayMonthYear == "Day") toReturn = llList2Integer(dateComponents, 2);//day of month
 27     else if (dayMonthYear == "Month") toReturn =  llList2Integer(dateComponents, 1); //month of year
 28     else if (dayMonthYear == "Year") toReturn =  llList2Integer(dateComponents, 0); //year
 29     return toReturn; 
 30 } //close GetDate 
 31   
 32 CheckDate()
 33 {   //checks to see if the day, month or year has changed form the last check, calling appropirate methods if it has
 34     integer year = GetDate("Year");
 35     integer month = GetDate("Month");
 36     integer day = GetDate("Day");
 37     if (day != lastDay) ProcessNewDay(day, month, year);
 38 }//close check the date 
 39 
 40 ProcessNewDay(integer day, integer month, integer year)
 41 {   //makes yesterdays visitors note card then resets the lists for today
 42     totalVisitorsCalculation = 0; //reset total visitors calc to 0 ready for next time
 43     ProcessLastPeriodVisitors("DayOfMonth");
 44     GenerateNewNoteCard("DayOfMonth");
 45     lastDay = day; //make last day equal today ready for tomorrow
 46     todaysVisitors =  []; //clear todays visitors list. 
 47     todaysVisitorsUUIDs = []; //clear the list of visitors uuid's
 48     if (lastMonth != month) ProcessNewMonth(month, year);
 49     llRegionSay(displayComsChannel, "Reset");
 50 }//close process new day
 51  
 52 ProcessNewMonth(integer month, integer year)
 53 {
 54     totalVisitorsCalculation = 0; //reset total visitors calc to 0 ready for next time
 55     PopulateDaysAndMonthsNoteardLists("ProcessNewMonth");//clear lists and generate new ones to work from
 56     ProcessLastPeriodVisitors("MonthOfYear");
 57     GenerateNewNoteCard("MonthOfYear");
 58     lastMonth = month; //make last month this month ready for next month
 59     lastPeriodsVisitors = []; //clear to keep memory down while not processing
 60     totalVisitorsCalculation = 0; //reset total visitors calc to 0 ready for next time
 61     daysOfMonthNotecards = []; //clear list to keep memory use down
 62     if (year != lastYear) ProcessNewYear(year);
 63     else monthsOfYearNotecards = [];//change of year has not occured so this is not needed, clear to keep memory use down
 64 }//close process new month
 65 
 66 ProcessNewYear(integer year)
 67 {
 68     totalVisitorsCalculation = 0; //reset total visitors calc to 0 ready for next time
 69     PopulateDaysAndMonthsNoteardLists("ProcessNewYear");//clear lists and generate new ones to work from
 70     ProcessLastPeriodVisitors("Year");
 71     GenerateNewNoteCard("Year");
 72     lastYear = year;
 73     lastPeriodsVisitors = []; //clear to keep memory down while not processing
 74     totalVisitorsCalculation = 0; //reset total visitors calc to 0 ready for next time
 75     monthsOfYearNotecards = [];//change of year has not occured so this is not needed, clear to keep memory use down
 76 }//close process new year
 77 
 78 ProcessLastPeriodVisitors(string type)
 79 {
 80     notecardsToProcess = [];
 81     if (type == "DayOfMonth")
 82     { 
 83         list yesterdaysVisitors = todaysVisitors;
 84         string visitorsUnique = "*Unique Visitors = " + (string)llGetListLength(yesterdaysVisitors);
 85         string visitorsAll = "*All Visitors = " + (string)llGetListLength(yesterdaysVisitors);
 86         yesterdaysVisitors += visitorsUnique; //adds the line above to the list
 87         yesterdaysVisitors += visitorsAll; //adds the line above to the list
 88         if (llGetInventoryType("Yesterday") == INVENTORY_NOTECARD) llRemoveInventory("Yesterday");
 89         osMakeNotecard("Yesterday", yesterdaysVisitors); //save the notecard
 90         EnsureNotecardWritten("Yesterday"); 
 91         notecardsToProcess += "Yesterday";
 92     }
 93     else if (type == "MonthOfYear")
 94     {
 95         notecardsToProcess = daysOfMonthNotecards;
 96         daysOfMonthNotecards = []; //clear to keep memory use down
 97     }
 98     else if (type == "Year")
 99     {
100         notecardsToProcess = monthsOfYearNotecards;
101         monthsOfYearNotecards = [];//clear to keep memory down
102     }
103     lastPeriodsVisitors = []; //ensure the list is clear at the start 
104     integer numberOfNotecardsToProcess = llGetListLength(notecardsToProcess);
105     integer noteCardIndex;
106     totalVisitorsCalculation = 0;
107     for (noteCardIndex = numberOfNotecardsToProcess-1; noteCardIndex >= 0; noteCardIndex--)
108     {
109         string notecardName = llList2String(notecardsToProcess, noteCardIndex);
110         ProcessVisitorsNotecard(notecardName); //adds contentents to the period list and total visitors figures preventing duplicates in the list
111         llRemoveInventory(notecardName);
112     } 
113 } 
114  
115 ProcessVisitorsNotecard(string notecardName)
116 {   //loops through the named notecard, adding the total visitors together and adding new uninque visitors to a period list
117     string currentLine;
118     integer notecardLength = osGetNumberOfNotecardLines(notecardName);
119     integer lineIndex;
120     for (lineIndex = 0; lineIndex < notecardLength; lineIndex++)
121     {   //loops through the selected notecard
122         currentLine = osGetNotecardLine(notecardName, lineIndex);
123         string firstTwoChars = llGetSubString(currentLine, 0, 1);
124         if (currentLine != "")
125         {
126             if (firstTwoChars == "*A" || firstTwoChars == "*U") //process this line as a total for notecard
127             {   //do this the long way, assume people are idiots and manually change an auto generated notecard. 
128                 if (firstTwoChars == "*A")
129                 {   //only process the all figures when adding together. 
130                     integer equalsIndex = llSubStringIndex (currentLine, "="); //get the position of the equals sign
131                     string strVisitors = llGetSubString(currentLine, equalsIndex+1, -1); //everything after the equals sign
132                     strVisitors = llStringTrim(strVisitors, STRING_TRIM); //remove any white space
133                     integer visitors = (integer) strVisitors; //convert to an integer
134                     totalVisitorsCalculation += visitors; //add value to total visitors calc figure      
135                 }   //close if first two charas are *A
136             }//close if first char is an *
137             else 
138             { 
139                 if (!(~llListFindList(lastPeriodsVisitors, (list)currentLine)))
140                 {   //if this visitor is not the last periods list add them
141                     currentLine = osGetNotecardLine(notecardName, lineIndex);
142                     lastPeriodsVisitors += currentLine;
143                 }//close if not on list
144             }//close if line does not start with an asterix
145         }//close if line is not blank
146     }//close loop through notecard
147 }//close process visitors notecard
148 
149 GenerateNewNoteCard(string notecardType)
150 {   //saves yesterdays vistors, clears the lists and sets last day to todays day ;
151     integer lastTimePeriod; 
152     if (notecardType == "DayOfMonth") lastTimePeriod = lastDay;
153     else if (notecardType == "MonthOfYear") lastTimePeriod = lastMonth;
154     else if (notecardType == "Year") lastTimePeriod = lastYear;
155     string visitorsUnique = "*Unique Visitors = " + (string)llGetListLength(lastPeriodsVisitors);
156     string visitorsAll = "*All Visitors = " + (string)totalVisitorsCalculation;
157     lastPeriodsVisitors += visitorsUnique; //adds the line above to the list
158     lastPeriodsVisitors += visitorsAll; //adds the line above to the list
159     string tail; 
160     if (lastTimePeriod < 10) tail = "0" + (string)lastTimePeriod; //keep the tail to always be 2 characters 
161     else tail = (string)lastTimePeriod; //set the tail string based on the day of the month yesterday
162     string notecardName = notecardType + "-" + tail;
163     if (llGetInventoryType(notecardName) == INVENTORY_NOTECARD) llRemoveInventory(notecardName);
164     osMakeNotecard(notecardName, lastPeriodsVisitors); //save the notecard
165     EnsureNotecardWritten(notecardName);
166     lastPeriodsVisitors = [];
167 }//close process new day 
168 
169 PopulateDaysAndMonthsNoteardLists(string callingMethod)
170 {   //goes through the inventory making lists of the notecard names for days of the month and months of the year
171     daysOfMonthNotecards = []; //ensure the list starts empty
172     monthsOfYearNotecards = []; //ensure the list starts empty
173     integer totalNotecards = llGetInventoryNumber(INVENTORY_NOTECARD);
174     integer notecardIndex;
175     for (notecardIndex = 0; notecardIndex < totalNotecards; notecardIndex++)
176     {   //loops through all notecards and makes a list of the day of the month notecards
177         string notecardName = llGetInventoryName(INVENTORY_NOTECARD, notecardIndex);
178         integer hyphenIndex = llSubStringIndex(notecardName, "-");
179         if (hyphenIndex >= 0)
180         {   //only come here if the name contains a hypen, otherwise ignore as its not part of the system
181             string notecardType = llGetSubString(notecardName, 0, hyphenIndex-1); //everything before the hypen
182             if (notecardType == "DayOfMonth") 
183             {
184                 daysOfMonthNotecards += notecardName; //add this card to the days of the month list
185             }
186             else if (notecardType == "MonthOfYear") 
187             {
188                 monthsOfYearNotecards += notecardName; //add this card to the months of the year list
189             }
190             //no else as its not part of the system so gets ignored
191         }//close if its a notecard belonging to the ones we need to processs
192     }//close loop through all notecards
193 }//populate days and months lists.  
194 
195 string ParseName(string detectedName)
196 { // parse name so both local and hg visitors are displayed nicely
197 //hypergrid name example firstName.LastName@SomeGrid.com:8002
198 string firstName;
199 string lastName;
200 string cleanName;
201 integer atIndex = llSubStringIndex(detectedName, "@"); //get the index position of the "@" symbol if present
202 integer periodIndex = llSubStringIndex(detectedName, ".");//get the index position of the "." if present
203 list nameSegments;
204 if ((periodIndex >= 0) && (atIndex >= 0))
205     {   //the detected name contains both an "@"" and "." so this avi is a hypergrid visitor
206         nameSegments = llParseString2List(detectedName,[" "],["@"]);//split the dected name into two list elements
207         string hGGridName = llList2String(nameSegments,0); //everything before the @ 
208         nameSegments = llParseStringKeepNulls(hGGridName, [" "], ["."]); //split the hg name into two list elements
209         firstName = llList2String(nameSegments,0); //retrieve the first name from the 1st index in the list
210         lastName = llList2String(nameSegments,2); //retrieve  the last name form the 2nd index in the list
211         cleanName = firstName + " " + lastName; //combines the names to look like a local visitors name
212     }//close if hg visitor
213 else
214     {   //this is a local visitor the name is already clean
215         cleanName = detectedName;
216     }//close if local visitor
217 return cleanName; //returns the cleaned name to the calling method
218 }//close parse name
219 
220 SetUpListeners()
221 {//sets the coms channel and the random menu channel then turns the listeners on.
222     menuChannel = (integer)(llFrand(-1000000000.0) - 1000000000.0); //generates random main menu channel
223     menuChannelListen = llListen(menuChannel, "", NULL_KEY, "");//sets up main menu listen integer
224     llListenControl (menuChannelListen, FALSE); //turns off listeners for main menu channel
225 }//close set up listeners
226 
227 DialogAdminMenu(key aviUUID)
228 {   //deliver the menu to the admin provided
229     list buttons = ["Visitors", "Done"]; //list of buttons on the menu
230     string message = "Will deliver a folder with the visitors details in it."; //message on the menu
231     llDialog(aviUUID, message, buttons, menuChannel); //delivers the actual menu
232 }//close deliver dialog menu
233 
234 ShowVisitors (key aviUUID)
235 {   //adds an extra card to show todays visitors then delivers a folder of all visitor details. 
236     list notecardToMake = todaysVisitors;
237     integer numberOfVisitorsToday = llGetListLength(notecardToMake);
238     string notecardName = "Todays-Visitors";
239     string visitorsUnique = "*Unique Visitors = " + (string)numberOfVisitorsToday;
240     string visitorsAll = "*All Visitors = " + (string)numberOfVisitorsToday;
241     notecardToMake += visitorsUnique; //adds the line above to the list
242     notecardToMake += visitorsAll; //adds the line above to the list
243     if (llGetInventoryType(notecardName) != -1)
244     {   //if this notecard already exists delete it
245         llRemoveInventory(notecardName);
246     }//close if notecard already exists
247     osMakeNotecard(notecardName, notecardToMake); //save the notecard
248     list itemsToDeliver = [];
249     integer notecardIndex;
250     for (notecardIndex = 0; notecardIndex < llGetInventoryNumber(INVENTORY_NOTECARD); notecardIndex++)
251     {   //loops through all notecards in the inventory. 
252         string notecardToProcess = llGetInventoryName(INVENTORY_NOTECARD, notecardIndex);
253         integer hyphenIndex = llSubStringIndex(notecardToProcess, "-");
254         if (hyphenIndex != -1)
255         {   //ignore all notecards which do not have a hyphen in them
256             string notecardType  = llGetSubString(notecardToProcess, 0, hyphenIndex-1);
257             if (notecardType == "DayOfMonth" || notecardType == "MonthOfYear" || notecardType == "Year" || notecardType == "Todays")
258             {   //only process notecards starting with "DayOfMonth, MonthOfYear or Year
259                 itemsToDeliver += notecardToProcess;
260             }//close if name matechs our criteria
261         }//close if there is a hypen
262     }//close loop through all notecards in the inventory. 
263     llGiveInventoryList(aviUUID, llGetObjectName(), itemsToDeliver);
264     llListenControl (menuChannelListen, FALSE); //turns off listeners for main menu channel
265     menuListen = FALSE;
266     llRemoveInventory(notecardName);
267     CleanTodaysVisitorsList(); //clean up pass by reference mess    
268 }//close show visitors.
269 
270 CleanTodaysVisitorsList()
271 {   //this list is passed by refernece earlier, either we clean it now or we do a loop copy earlier
272     integer index = llGetListLength(todaysVisitors)-1;
273     for (; index >=0; index--)
274     {
275         string toTest = llList2String(todaysVisitors,index);
276         if (llGetSubString(toTest,0,0) == "*")
277         {   //removes unique visitos and all visitors lines which get added earlier.
278             todaysVisitors = llDeleteSubList(todaysVisitors, index,index);
279         }
280     }
281 }//close clean todays visitors list. 
282 
283 ProcessInstructionLine(string instruction, string data, string notecardName)
284 {   //we only need the data, add it to the admins list
285     if (notecardName == "Admin") 
286     {
287         if (!(~llListFindList(admins, (list)data)))
288         {
289             admins += data;
290         }
291     }
292     else if (notecardName == "Ignore") 
293     {
294         if (!(~llListFindList(ignore, (list)data)))
295         {
296             ignore +=data;
297         }
298     }
299 }//close process instruction line
300 
301 string CleanUpString(string inputString)
302 {   //takes in the string provided by the sending method, removes white space and converts it to lower case then returns the string to the sending method 
303     string cleanString = llStringTrim( llToLower(inputString), STRING_TRIM ); //does the clean up
304     return cleanString; //returns the string to the sending method now its cleaned up  
305 }//close clean up string. 
306 
307 ReadConfigCards(string notecardName)
308 {   //Reads the named config card if it exists
309     if (llGetInventoryType(notecardName) == INVENTORY_NOTECARD)
310     {   //only come here if the name notecard actually exists, otherwise give the user an error
311         integer notecardLength = osGetNumberOfNotecardLines(notecardName); //gets the length of the notecard
312         integer index; //defines the index for the next line
313         for (index = 0; index < notecardLength; ++index)
314         {    //loops through the notecard line by line  
315             string currentLine = osGetNotecardLine(notecardName,index); //contents of the current line exactly as it is in the notecard
316             string firstChar = llGetSubString(currentLine, 0,0); //gets the first character of this line
317             integer equalsIndex = llSubStringIndex(currentLine, "="); //gets the position of hte equals sign on this line if it exists
318             if (currentLine != "" && firstChar != "#" && equalsIndex != -1 )
319             {   //only come here if the line has content, it does not start with # and it contains an equal sign
320                 string instruction = llGetSubString (currentLine, 0, equalsIndex-1); //everything before the equals sign
321                 string data = llGetSubString(currentLine, equalsIndex+1, -1); //everything after the equals sign    
322                 instruction = CleanUpString (instruction); //sends the instruvtion to the cleanup method to remove white space and turn to lower case
323                 data = CleanUpString (data); //sends the data to the cleanup method to remove white space and turn to lower case
324                 ProcessInstructionLine(instruction, data, notecardName); //sends the instruction and the data to the Process instruction method
325             }//close if the line is valid
326             else
327             {   //come here if the above condition is not met
328                 if ( (currentLine != "") && (firstChar != "#") && (equalsIndex == -1))
329                 {   // if the line is not blank and it does not begin with a #, and there is no = sign send an error telling the user which line is invalid. 
330                     llOwnerSay("Line number: " + (string)index + " is malformed. It is not blank, and does not begin with a #, yet it contains no equals sign.");
331                 }//close line is invalid
332             }//close invalid line
333         }
334     }//close if the notecard exists
335     else 
336     {   //the named notecard does not exist, send an error to the user. 
337         //llOwnerSay ("The notecard called " + notecardName + " is missing, auto generating one with just the owner added");
338         GenerateMissingConfigCard(notecardName);
339         
340     }//close error the notecard does not exist
341 }//close read config card.
342 
343 GenerateMissingConfigCard(string notecardName)
344 {
345     string dataToAdd = notecardName + " = " + (string)llGetOwner();
346     string title = "# " + notecardName + "'s" ;
347     list newNotecardContents = [title, dataToAdd];
348     osMakeNotecard(notecardName, newNotecardContents); //save the notecard
349 } 
350 
351 ProcessDetectedAvatars()
352 {   //processes avatars detected by either the region list or collission event. 
353     list avatarsInRegion = llGetAgentList(AGENT_LIST_REGION, []); //generates a list of all avatar uuids in the region
354     integer avatarIndex;
355     for (avatarIndex = 0; avatarIndex < llGetListLength(avatarsInRegion); avatarIndex++)
356     {   //loop through all detected avis
357         key uuidToCheck = llList2Key(avatarsInRegion, avatarIndex); //avi we are currently dealing with
358         string aviName = llKey2Name(uuidToCheck);
359         string cleanName = ParseName (aviName); //get avi name without hg stuff if present
360         if (llDetectedType(avatarIndex) != 0)
361         {
362             if (!(~llListFindList(ignore, (list)uuidToCheck)))
363             {   //if the avi is not on the ignore list come here
364                 if (debug) llOwnerSay("Debug:ProcessDetectedAvis: " + uuidToCheck + " is not on the ignore list"); 
365                 if (!(~llListFindList(todaysVisitorsUUIDs, (list)uuidToCheck)))
366                 {   //if avi has not already visited today add them to both daily visitors and UUID lists
367                     todaysVisitorsUUIDs += uuidToCheck; //add this uuid to the list of visitors today, has to be uuid as names could match with hg visitors
368                     string homeUri = osGetAvatarHomeURI(uuidToCheck);//get the avatars home grid
369                     string newVisitor = cleanName + "," + homeUri; //this is the line we add to the visitors list
370                     todaysVisitors += newVisitor;//add the line abive to todays visitors list.
371                     llMessageLinked(LINK_THIS, 93827334, uuidToCheck, NULL_KEY); //linked message to helpers
372                     llRegionSay(displayComsChannel, uuidToCheck);
373                     if (debug) llOwnerSay("Debug:ProcessDetectedAvatars:" + newVisitor + "added to todays visitors list");
374                 }//close if not on the list already
375             } 
376         }
377         
378     }//close loop through detected list
379 }//close process avatars in region 
380 
381 EnsureNotecardWritten(string notecardName)
382 {   //holds the scrit in a loop untill the card is written
383     integer notecardWritten = FALSE;
384     while (!notecardWritten)
385     {   //if the status is not written come here
386         if (llGetInventoryType(notecardName) == INVENTORY_NOTECARD) notecardWritten = TRUE; //change to true if its written
387     }  //close while not written 
388 }//close ensure notecard is written. 
389 
390 default
391 {
392     changed( integer change )
393     {   //if we have been moved to a new region or changed owne reset the script
394         if (change & (CHANGED_OWNER | CHANGED_REGION)) llResetScript();
395     }//close changed
396 
397     state_entry()
398     {
399         osVolumeDetect(TRUE); //makes item volumetric
400         SetUpListeners();
401         //start fake test data
402         //==========================
403         //lastYear = 2018;
404         //lastMonth = 11;
405         //lastDay = 22;
406         //==========================
407         //end fake test data
408         lastYear = GetDate("Year");
409         lastMonth = GetDate("Month");
410         lastDay = GetDate("Day");
411         ReadConfigCards("Admin");
412         ReadConfigCards("Ignore");
413         llSetTimerEvent(timeInterval); //every 30 mins
414     }//close state entry
415 
416     collision_start(integer total_number)
417     {
418         integer colliderIndex = 0;
419         for (colliderIndex; colliderIndex < total_number; colliderIndex++)
420         {
421             integer detectedType = llDetectedType(colliderIndex);
422             if (detectedType == 1 || detectedType == 3 || detectedType == 5)
423             {   //only process avatars, no bots or physical objects
424                 key detectedUUID = llDetectedKey(colliderIndex);
425                 if (detectedUUID != lastCollider)
426                 {   //if this is the same avi just standing on the dector don't process them
427                     lastCollider = detectedUUID;
428                     ProcessDetectedAvatars();
429                 }//close if not last collider
430             }//close if detected type is an avatar
431         }
432     }//close collision event
433 
434     touch_start(integer num_detected)
435     {   //come here any time the object is clicked
436          admins = [];
437          ReadConfigCards("Admin");
438          integer detectedIndex = 0;
439          key toucher = llDetectedKey(0);
440          if (~llListFindList(admins, (list)toucher))
441          {  //if the toucher is on the admin list deliver the menu
442              lastAdmin = toucher; //store the last admin toucher incase of a time out. 
443              llListenControl (menuChannelListen, TRUE); //turns on listeners for main menu channel
444              menuListen = TRUE;
445              DialogAdminMenu(toucher);
446          }//close if toucher is on list
447          //else llRegionSayTo(toucher, PUBLIC_CHANNEL, "Sorry you are not on the admin list and can not use this item.");
448     }//close touch start event 
449 
450     listen( integer channel, string name, key id, string message )
451     {
452         if (channel == menuChannel)
453         {   //come here if the channel is the menu channel
454             if (~llListFindList(admins, (list)id))
455             {   //if avi sending the message is on the admins list send them the menu
456                 if (message == "Visitors") ShowVisitors(id);
457             }//close if found on admins list
458         }//close channel is the menu channel
459     }//close listen
460  
461     timer()
462     {   //come here based on the timer event time 
463         if (menuListen)
464         {   //come here if the menu listener is on
465             menuListen = FALSE;//set listener tracker to false
466             llRegionSayTo(lastAdmin, PUBLIC_CHANNEL, "Menu timed out, please click again"); //warn the last user
467             llListenControl (menuChannelListen, FALSE); //turns on listeners for main menu channel
468         }//close if menu listener is on
469         ProcessDetectedAvatars(); //scan the sim and process all found avis
470         CheckDate(); //check the date to see if its a new day, if it is process the changes (includes month/year if they also changed)
471     }//close timer
472 }//close state default

Script - Display Board

  1 integer displayComsChannel = -6827416;
  2 integer displayComsChannelListen;
  3 list todaysVisitors =[];
  4 integer debug = FALSE;
  5 
  6 SetUpListeners()
  7 {//sets the coms channel and the random menu channel then turns the listeners on.
  8     displayComsChannelListen = llListen(displayComsChannel, "", NULL_KEY, "");
  9     llListenControl (displayComsChannelListen, TRUE);    
 10 }//close set up listeners
 11 
 12 ApplyDynamicTexture(float rotationRAD, string text)
 13 {   //sets the shape of the box and textures it rotating the arrow to the specified position
 14     string sDynamicID = "";                          // not implemented yet
 15     string sContentType = "vector";                  // vector = text/lines,etc.  image = texture only
 16     string sData = "";                               // Storage for our drawing commands
 17     string sExtraParams = "width:1024,height:512";    // optional parameters in the following format: [param]:[value],[param]:[value]
 18     integer iTimer = 0;                               // timer is not implemented yet, leave @ 0
 19     integer iAlpha = 100;                            // 0 = 100% Alpha, 255 = 100% Solid
 20     // draw a rectangle
 21     sData = osSetPenSize(sData, 3);                   // Set the pen width to 3 pixels
 22     sData = osSetPenColor(sData, "Black");             // Set the pen color to red
 23     sData = osMovePen(sData, 0, 0);                 // Upper left corner at <28,78>
 24     sData = osDrawFilledRectangle(sData, 1024, 512);   // 200 pixels by 100 pixels
 25     // setup text to go in the drawn box
 26     sData = osMovePen(sData, 30, 10);                 // place pen @ X,Y coordinates 
 27     sData = osSetFontName(sData, "Arial");            // Set the Fontname to use
 28     sData = osSetFontSize(sData, 20);                 // Set the Font Size in pixels
 29     sData = osSetPenColor(sData, "White");           // Set the pen color to Green
 30     sData = osDrawText(sData, text); // The text to write
 31     //do the draw multiple times so its actually black and not grey
 32     osSetDynamicTextureDataBlend( sDynamicID, sContentType, sData, sExtraParams, iTimer, iAlpha ); // Now draw it out
 33     osSetDynamicTextureDataBlend( sDynamicID, sContentType, sData, sExtraParams, iTimer, iAlpha ); // Now draw it out
 34     osSetDynamicTextureDataBlend( sDynamicID, sContentType, sData, sExtraParams, iTimer, iAlpha ); // Now draw it out
 35 }//close apply shape texture
 36 
 37 SetBaseImage()
 38 {
 39     llSetLinkPrimitiveParamsFast(LINK_ROOT, [ PRIM_TEXTURE, ALL_SIDES, "802934bf-fcfb-4540-b7fa-b17585880d2b", <1,1,1>, <1,1,1>, 0 ]); //set the image
 40 }
 41 
 42 ProcessListenMessage(string message)
 43 {
 44     if (debug)
 45     {
 46         llOwnerSay("Debug:ProcessListenMessage:Entered");
 47     }
 48     if (message == "Reset")
 49     {
 50         if(debug)
 51         {
 52             llOwnerSay("Debug:ProcessListenMessage:Reset");
 53         }
 54         llResetScript();
 55     }
 56     else
 57     {
 58         if (debug)
 59         {
 60             llOwnerSay("Debug:ProcessListenMessage:UUID:" + message);
 61         }
 62         UpdateDisplay(message);
 63     }
 64 }
 65 
 66 UpdateDisplay(string message)
 67 {
 68     string name = llKey2Name((key)message);
 69     if(!(~llListFindList(todaysVisitors, (list)name)))
 70     {
 71         todaysVisitors += name;
 72         UpdateDisplayText();
 73     }
 74 }
 75 
 76 UpdateDisplayText()
 77 {
 78     string displayString = GenDisplayString();
 79     SetBaseImage();
 80     ApplyDynamicTexture(0, displayString);
 81 }
 82 
 83 string GenDisplayString ()
 84 {
 85     if (debug)
 86     {
 87         llOwnerSay("Debug:GenDisplayString:Entered");
 88     }
 89     string title = "Recent Visitors\n";
 90     string display = title;
 91     integer nameIndex = llGetListLength(todaysVisitors)-1;
 92     for (nameIndex; nameIndex >= 0; nameIndex--)
 93     {
 94         display += llList2String(todaysVisitors, nameIndex);
 95         display += "\n";
 96     }
 97     if (debug)
 98     {
 99         llOwnerSay("Debug:GenDisplayString:DisplayString: " + display);
100     }
101     return display;
102 }
103 
104 default
105 {
106     state_entry()
107     {
108         SetUpListeners();
109         UpdateDisplayText();
110     }
111 
112     listen(integer channel, string name, key id, string message)
113     {//listens on the set channels, then depending on the heard channel sends the message for processing. 
114         if(debug)
115         {
116             llOwnerSay("Debug:Listen:Message: message");
117         }
118         if (llGetOwner() == llGetOwnerKey(id) && channel == displayComsChannel)
119         {
120             if (debug)
121             {
122                 llOwnerSay("Debug:Listen:IsOwner And Correct Channel");
123             }
124             ProcessListenMessage(message);
125         } //close if sending object is owned by the same person
126     }//close listen 
127 }

Script - HelperMessages

  1 list Helpers = [];
  2 
  3 ProcessInstructionLine(string instruction, string data)
  4 {
  5     if (llToLower(instruction) == "helper")
  6     {
  7         Helpers += data;
  8     }
  9 }
 10 
 11 string CleanUpString(string inputString)
 12 
 13 { 
 14 
 15     string cleanString = llStringTrim( llToLower(inputString), STRING_TRIM );
 16 
 17     return cleanString;   
 18 
 19 }
 20 
 21  
 22 
 23 ReadConfigCards(string notecardName)
 24 
 25 {   //Reads the named config card if it exists
 26 
 27     if (llGetInventoryType(notecardName) == INVENTORY_NOTECARD)
 28 
 29     {   //only come here if the name notecard actually exists, otherwise give the user an error
 30 
 31         integer notecardLength = osGetNumberOfNotecardLines(notecardName); //gets the length of the notecard
 32 
 33         integer index; //defines the index for the next line
 34 
 35         for (index = 0; index < notecardLength; ++index)
 36 
 37         {    //loops through the notecard line by line  
 38 
 39             string currentLine = osGetNotecardLine(notecardName,index); //contents of the current line exactly as it is in the notecard
 40 
 41             string firstChar = llGetSubString(currentLine, 0,0); //gets the first character of this line
 42 
 43             integer equalsIndex = llSubStringIndex(currentLine, "="); //gets the position of hte equals sign on this line if it exists
 44 
 45             if (currentLine != "" && firstChar != "#" && equalsIndex != -1 )
 46 
 47             {   //only come here if the line has content, it does not start with # and it contains an equal sign
 48 
 49                 string instruction = llGetSubString (currentLine, 0, equalsIndex-1); //everything before the equals sign
 50 
 51                 string data = llGetSubString(currentLine, equalsIndex+1, -1); //everything after the equals sign    
 52 
 53                 instruction = CleanUpString (instruction); //sends the instruvtion to the cleanup method to remove white space and turn to lower case
 54 
 55                 data = CleanUpString (data); //sends the data to the cleanup method to remove white space and turn to lower case
 56 
 57                 ProcessInstructionLine(instruction, data); //sends the instruction and the data to the Process instruction method
 58 
 59             }//close if the line is valid
 60 
 61             else
 62 
 63             {
 64 
 65                 if ( (currentLine != "") && (firstChar != "#") && (equalsIndex == -1))
 66 
 67                 {
 68 
 69                     llOwnerSay("Line number: " + (string)index + " is malformed. It is not blank, and does not begin with a #, yet it contains no equals sign.");
 70 
 71                 }
 72 
 73             }
 74 
 75         }
 76 
 77     }//close if the notecard exists
 78 
 79     else 
 80 
 81     {   //the named notecard does not exist, send an error to the user. 
 82 
 83         llOwnerSay ("The notecard called " + notecardName + " is missing, please address this");
 84 
 85     }//close error the notecard does not exist
 86 
 87 }//close read config card. 
 88 
 89 MessageHelpers(string aviUUID)
 90 {
 91     integer index;
 92     for (index = 0; index < llGetListLength(Helpers); index++)
 93     {
 94         string pre = "hop://fireandicegrid.net:8002/app/agent/";
 95         string tail = "/about";
 96         string message = pre + aviUUID + tail + " has entered " + llGetRegionName() + " for the first time today"; 
 97         llInstantMessage(llList2Key(Helpers, index), message);
 98     }
 99     
100 }
101 
102 default
103 
104 {
105     changed(integer change)
106     {
107         if (change & CHANGED_INVENTORY) //note that it's & and not &&... it's bitwise!
108         {
109             llResetScript();
110         }
111     }
112         
113     state_entry()
114 
115     {   //main entry point of the script, this runs when the script starts
116 
117         ReadConfigCards("Helpers"); //calls the read config card method passing the name of the card defined in the global variables above
118 
119     }//close state entry
120 
121     link_message(integer Sender, integer Number, string String, key Key) // This script is in the object too.
122     {
123         if (Number == 93827334)
124         {
125             MessageHelpers(String);
126         }
127     }
128  
129 }//close default

Admin Notecard Sample

 1 # Allowed Admins
 2 
 3 #Blank lines are ignored
 4 #Lines starting with # are ignored
 5 
 6 #Admin lines should start with the word Admin, have an equals sign and then the UUID / Key of the avatar. 
 7 #e.g. Admin = cbd17d01-4a7e-437a-ac57-0c8313508aae
 8 #If you wish to add the name as well, just put a hash tag on the line above or bellow the uuid with the name of the avatar. 
 9 
10 Admin = cbd17d01-4a7e-437a-ac57-0c8313508aae

Ignore Notecard Sample

 1 # Ignored Avi's
 2 
 3 #Blank lines are ignored
 4 #Lines starting with # are ignored
 5 
 6 #Ignored avi entries should start with the word Ignore, then an equals sign followed by the UUID/Key of the avi to be ignored
 7 #E.g. Ignore = cbd17d01-4a7e-437a-ac57-0c8313508aae
 8 #If you wish to add the name as well, just put a hash tag on the line above or bellow the uuid with the name of the avatar. 
 9 
10 Ignore = cbd17d01-4a7e-437a-ac57-0c8313508aae

Helpers Notecard Sample

1 #Helpers
2 #All lines with a # at the start are ignored
3 #List Helpers Avatars listed here will get a notification the first time someone arives in the region that day
4 #Helper entries should start with the word Helper, then an equals sign, followed by the UUID/Key of the avatar
5 #E.g. Helper = cbd17d01-4a7e-437a-ac57-0c8313508aae
6 #If you wish to add the name as well, just put a hash tag on the line above or bellow the uuid with the name of the avatar. 
7 
8 Helper = cbd17d01-4a7e-437a-ac57-0c8313508aae


Syntax Highlighting

GeShi syntax highlighting is enabled on this wiki. <be>

When using the tabs described in the link, change the language part to "lsl". Eg.
syntaxhighlight lang="lsl" line
inside angled brackets <>