From 0f00dc2f658869cc73a18b03b024a6cc88964e0b Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Wed, 22 May 2024 20:29:55 +0300
Subject: Add support for sending messages to Nearby chat from Lua script

---
 indra/llui/llchat.h                             |  6 +++-
 indra/newview/llchathistory.cpp                 | 39 +++++++++++++++++--------
 indra/newview/llchathistory.h                   |  3 +-
 indra/newview/llchatitemscontainerctrl.cpp      |  5 ++--
 indra/newview/llchatitemscontainerctrl.h        |  6 +++-
 indra/newview/llfloaterimnearbychat.cpp         |  3 ++
 indra/newview/llfloaterimnearbychathandler.cpp  | 12 ++++++--
 indra/newview/llfloaterimnearbychatlistener.cpp |  9 +++---
 indra/newview/llfloaterimnearbychatlistener.h   |  4 +--
 indra/newview/llviewerchat.cpp                  |  9 ++++--
 indra/newview/llviewermessage.cpp               |  4 ++-
 indra/newview/scripts/lua/LLChat.lua            | 17 +++++++++++
 indra/newview/skins/default/xui/en/strings.xml  |  4 ++-
 13 files changed, 89 insertions(+), 32 deletions(-)
 create mode 100644 indra/newview/scripts/lua/LLChat.lua

diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h
index 56105add7e..8fd92c2550 100644
--- a/indra/llui/llchat.h
+++ b/indra/llui/llchat.h
@@ -89,7 +89,8 @@ public:
         mPosAgent(),
         mURL(),
         mChatStyle(CHAT_STYLE_NORMAL),
-        mSessionID()
+        mSessionID(),
+        mIsScript(false)
     { }
 
     std::string     mText;      // UTF-8 line of text
@@ -107,6 +108,9 @@ public:
     std::string     mURL;
     EChatStyle      mChatStyle;
     LLUUID          mSessionID;
+
+    bool            mIsScript;
 };
+static const std::string LUA_PREFIX("[LUA]");
 
 #endif
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 4a08eace62..6f02a08020 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -125,6 +125,7 @@ public:
         mUserNameTextBox(NULL),
         mTimeBoxTextBox(NULL),
         mNeedsTimeBox(true),
+        mIsFromScript(false),
         mAvatarNameCacheConnection()
     {}
 
@@ -658,11 +659,12 @@ public:
 
     const LLUUID&       getAvatarId () const { return mAvatarID;}
 
-    void setup(const LLChat& chat, const LLStyle::Params& style_params, const LLSD& args)
+    void setup(const LLChat& chat, const LLStyle::Params& style_params, const LLSD& args, bool is_script)
     {
         mAvatarID = chat.mFromID;
         mSessionID = chat.mSessionID;
         mSourceType = chat.mSourceType;
+        mIsFromScript = is_script;
 
         // To be able to report a message, we need a copy of it's text
         // and it's easier to store text directly than trying to get
@@ -732,7 +734,7 @@ public:
                 username_end == (chat.mFromName.length() - 1))
             {
                 mFrom = chat.mFromName.substr(0, username_start);
-                user_name->setValue(mFrom);
+                user_name->setValue(mIsFromScript ? LLTrans::getString("ScriptBy") + mFrom : mFrom);
 
                 if (gSavedSettings.getBOOL("NameTagShowUsernames"))
                 {
@@ -774,7 +776,7 @@ public:
         switch (mSourceType)
         {
             case CHAT_SOURCE_AGENT:
-                icon->setValue(chat.mFromID);
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : chat.mFromID);
                 break;
             case CHAT_SOURCE_OBJECT:
                 icon->setValue(LLSD("OBJECT_Icon"));
@@ -787,7 +789,7 @@ public:
                 icon->setValue(LLSD("Command_Destinations_Icon"));
                 break;
             case CHAT_SOURCE_UNKNOWN:
-                icon->setValue(LLSD("Unknown_Icon"));
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : chat.mFromID);
         }
 
         // In case the message came from an object, save the object info
@@ -1029,7 +1031,14 @@ private:
         mFrom = av_name.getDisplayName();
 
         LLTextBox* user_name = getChild<LLTextBox>("user_name");
-        user_name->setValue( LLSD(av_name.getDisplayName() ) );
+        if(mIsFromScript) 
+        {
+            user_name->setValue(LLSD(LLTrans::getString("ScriptBy") + av_name.getDisplayName()));
+        }
+        else 
+        {
+            user_name->setValue(LLSD(av_name.getDisplayName()));
+        }
         user_name->setToolTip( av_name.getUserName() );
 
         if (gSavedSettings.getBOOL("NameTagShowUsernames") &&
@@ -1071,6 +1080,8 @@ protected:
 
     bool                mNeedsTimeBox;
 
+    bool mIsFromScript;
+
 private:
     boost::signals2::connection mAvatarNameCacheConnection;
 };
@@ -1088,6 +1099,7 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
     mTopHeaderPad(p.top_header_pad),
     mBottomHeaderPad(p.bottom_header_pad),
     mIsLastMessageFromLog(false),
+    mIsLastFromScript(false),
     mNotifyAboutUnreadMsg(p.notify_unread_msg)
 {
     LLTextEditor::Params editor_params(p);
@@ -1185,11 +1197,11 @@ LLView* LLChatHistory::getSeparator()
     return separator;
 }
 
-LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args)
+LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args, bool is_script)
 {
     LLChatHistoryHeader* header = LLChatHistoryHeader::createInstance(mMessageHeaderFilename);
     if (header)
-        header->setup(chat, style_params, args);
+        header->setup(chat, style_params, args, is_script);
     return header;
 }
 
@@ -1258,8 +1270,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
     name_params.color(name_color);
     name_params.readonly_color(name_color);
 
-    std::string prefix = chat.mText.substr(0, 4);
-
+    bool is_lua = (chat.mText.substr(0, LUA_PREFIX.size()) == LUA_PREFIX);
+    std::string prefix = chat.mText.substr(is_lua ? LUA_PREFIX.size() : 0, 4);
     //IRC styled /me messages.
     bool irc_me = prefix == "/me " || prefix == "/me'";
 
@@ -1393,7 +1405,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
             && mLastFromID == chat.mFromID
             && mLastMessageTime.notNull()
             && (new_message_time.secondsSinceEpoch() - mLastMessageTime.secondsSinceEpoch()) < 60.0
-            && mIsLastMessageFromLog == message_from_log)  //distinguish between current and previous chat session's histories
+            && mIsLastMessageFromLog == message_from_log  //distinguish between current and previous chat session's histories
+            && mIsLastFromScript == is_lua)
         {
             view = getSeparator();
             if (!view)
@@ -1408,7 +1421,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
         }
         else
         {
-            view = getHeader(chat, name_params, args);
+            view = getHeader(chat, name_params, args, is_lua);
             if (!view)
             {
                 LL_WARNS() << "Failed to create header from " << mMessageHeaderFilename << ": can't append to history" << LL_ENDL;
@@ -1437,6 +1450,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
         mLastFromID = chat.mFromID;
         mLastMessageTime = new_message_time;
         mIsLastMessageFromLog = message_from_log;
+        mIsLastFromScript = is_lua;
     }
 
     // body of the message processing
@@ -1491,7 +1505,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
     // usual messages showing
     else if (!teleport_separator)
     {
-        std::string message = irc_me ? chat.mText.substr(3) : chat.mText;
+        std::string message = is_lua ? chat.mText.substr(LUA_PREFIX.size()) : chat.mText;
+        message = irc_me ? message.substr(3) : message;
 
         //MESSAGE TEXT PROCESSING
         //*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010)
diff --git a/indra/newview/llchathistory.h b/indra/newview/llchathistory.h
index b8364ff860..8acbea6ed5 100644
--- a/indra/newview/llchathistory.h
+++ b/indra/newview/llchathistory.h
@@ -101,7 +101,7 @@ class LLChatHistory : public LLUICtrl
          * Builds a message header.
          * @return pointer to LLView header object.
          */
-        LLView* getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args);
+        LLView* getHeader(const LLChat& chat,const LLStyle::Params& style_params, const LLSD& args, bool is_script = false);
     public:
         ~LLChatHistory();
         LLSD getValue() const;
@@ -127,6 +127,7 @@ class LLChatHistory : public LLUICtrl
         LLDate mLastMessageTime;
         bool mIsLastMessageFromLog;
         bool mNotifyAboutUnreadMsg;
+        bool mIsLastFromScript;
         //std::string mLastMessageTimeStr;
 
         std::string mMessageHeaderFilename;
diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp
index 2b875c708d..8eabe3d222 100644
--- a/indra/newview/llchatitemscontainerctrl.cpp
+++ b/indra/newview/llchatitemscontainerctrl.cpp
@@ -187,6 +187,7 @@ void LLFloaterIMNearbyChatToastPanel::init(LLSD& notification)
 
     int sType = notification["source"].asInteger();
     mSourceType = (EChatSourceType)sType;
+    mIsFromScript = notification["is_lua"].asBoolean();
 
     std::string color_name = notification["text_color"].asString();
 
@@ -214,7 +215,7 @@ void LLFloaterIMNearbyChatToastPanel::init(LLSD& notification)
     {
         std::string str_sender;
 
-        str_sender = fromName;
+        str_sender = mIsFromScript ? LLTrans::getString("ScriptBy") + fromName : fromName;
 
         str_sender+=" ";
 
@@ -401,7 +402,7 @@ void LLFloaterIMNearbyChatToastPanel::draw()
             else if(mSourceType == CHAT_SOURCE_SYSTEM)
                 icon->setValue(LLSD("SL_Logo"));
             else if(mSourceType == CHAT_SOURCE_AGENT)
-                icon->setValue(mFromID);
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : mFromID);
             else if(!mFromID.isNull())
                 icon->setValue(mFromID);
         }
diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h
index d041615060..7df00e8fd9 100644
--- a/indra/newview/llchatitemscontainerctrl.h
+++ b/indra/newview/llchatitemscontainerctrl.h
@@ -48,7 +48,8 @@ protected:
         LLFloaterIMNearbyChatToastPanel()
         :
     mIsDirty(false),
-    mSourceType(CHAT_SOURCE_OBJECT)
+    mSourceType(CHAT_SOURCE_OBJECT),
+    mIsFromScript(false)
     {};
 public:
     ~LLFloaterIMNearbyChatToastPanel(){}
@@ -58,6 +59,8 @@ public:
     const LLUUID& getFromID() const { return mFromID;}
     const std::string& getFromName() const { return mFromName; }
 
+    bool isFromScript() { return mIsFromScript; }
+
     //void  addText     (const std::string& message ,  const LLStyle::Params& input_params = LLStyle::Params());
     //void  setMessage  (const LLChat& msg);
     void    snapToMessageHeight ();
@@ -88,6 +91,7 @@ private:
     std::string     mFromName;
     EChatSourceType mSourceType;
     LLChatMsgBox*   mMsgText;
+    bool mIsFromScript;
 
 
 
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index e64f468cbe..6b817c7cf1 100644
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -52,6 +52,7 @@
 
 #include "llfirstuse.h"
 #include "llfloaterimnearbychat.h"
+#include "llfloaterimnearbychatlistener.h"
 #include "llagent.h" // gAgent
 #include "llgesturemgr.h"
 #include "llmultigesture.h"
@@ -71,6 +72,8 @@
 
 S32 LLFloaterIMNearbyChat::sLastSpecialChatChannel = 0;
 
+static LLFloaterIMNearbyChatListener sChatListener;
+
 const S32 EXPANDED_HEIGHT = 266;
 const S32 COLLAPSED_HEIGHT = 60;
 const S32 EXPANDED_MIN_HEIGHT = 150;
diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index 77ceea19af..91d9cd56cf 100644
--- a/indra/newview/llfloaterimnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -301,12 +301,13 @@ void LLFloaterIMNearbyChatScreenChannel::addChat(LLSD& chat)
     {
         LLUUID fromID = chat["from_id"].asUUID();       // agent id or object id
         std::string from = chat["from"].asString();
+        bool is_lua = chat["is_lua"].asBoolean();
         LLToast* toast = m_active_toasts[0].get();
         if (toast)
         {
             LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast<LLFloaterIMNearbyChatToastPanel*>(toast->getPanel());
 
-            if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText())
+            if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->isFromScript() == is_lua && panel->canAddText())
             {
                 panel->addMessage(chat);
                 toast->reshapeToPanel();
@@ -600,15 +601,19 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
         std::string toast_msg;
         if (chat_msg.mChatStyle == CHAT_STYLE_IRC)
         {
+            if (chat_msg.mIsScript)
+            {
+                toast_msg += LLTrans::getString("ScriptStr");
+            }
             if (!chat_msg.mFromName.empty())
             {
                 toast_msg += chat_msg.mFromName;
             }
-            toast_msg += chat_msg.mText.substr(3);
+            toast_msg += chat_msg.mText.substr(chat_msg.mIsScript ? LUA_PREFIX.size() + 3 : 3);
         }
         else
         {
-            toast_msg = chat_msg.mText;
+            toast_msg = chat_msg.mIsScript ? chat_msg.mText.substr(LUA_PREFIX.size()) : chat_msg.mText;
         }
 
         bool chat_overlaps = false;
@@ -658,6 +663,7 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
             chat["color_alpha"] = r_color_alpha;
             chat["font_size"] = (S32)LLViewerChat::getChatFontSize() ;
             chat["message"] = toast_msg;
+            chat["is_lua"] = chat_msg.mIsScript;
             channel->addChat(chat);
         }
 
diff --git a/indra/newview/llfloaterimnearbychatlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp
index 616acf0eae..d6dfe42639 100644
--- a/indra/newview/llfloaterimnearbychatlistener.cpp
+++ b/indra/newview/llfloaterimnearbychatlistener.cpp
@@ -36,10 +36,9 @@
 #include "llviewercontrol.h"
 
 
-LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar)
+LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener()
   : LLEventAPI("LLChatBar",
-               "LLChatBar listener to (e.g.) sendChat, etc."),
-    mChatbar(chatbar)
+               "LLChatBar listener to (e.g.) sendChat, etc.")
 {
     add("sendChat",
         "Send chat to the simulator:\n"
@@ -54,7 +53,7 @@ LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyCh
 void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
 {
     // Extract the data
-    std::string chat_text = chat_data["message"].asString();
+    std::string chat_text = LUA_PREFIX + chat_data["message"].asString();
 
     S32 channel = 0;
     if (chat_data.has("channel"))
@@ -95,6 +94,6 @@ void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
     }
 
     // Send it as if it was typed in
-    mChatbar.sendChatFromViewer(chat_to_send, type_o_chat, ((BOOL)(channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim"));
+    LLFloaterIMNearbyChat::sendChatFromViewer(chat_to_send, type_o_chat, ((BOOL) (channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim"));
 }
 
diff --git a/indra/newview/llfloaterimnearbychatlistener.h b/indra/newview/llfloaterimnearbychatlistener.h
index 96184d95b3..0df0341d36 100644
--- a/indra/newview/llfloaterimnearbychatlistener.h
+++ b/indra/newview/llfloaterimnearbychatlistener.h
@@ -38,12 +38,10 @@ class LLFloaterIMNearbyChat;
 class LLFloaterIMNearbyChatListener : public LLEventAPI
 {
 public:
-    LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar);
+    LLFloaterIMNearbyChatListener();
 
 private:
     void sendChat(LLSD const & chat_data) const;
-
-    LLFloaterIMNearbyChat & mChatbar;
 };
 
 #endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H
diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp
index 597cf3c98c..70c426740b 100644
--- a/indra/newview/llviewerchat.cpp
+++ b/indra/newview/llviewerchat.cpp
@@ -30,6 +30,7 @@
 // newview includes
 #include "llagent.h"    // gAgent
 #include "llslurl.h"
+#include "lltrans.h"
 #include "lluicolor.h"
 #include "lluicolortable.h"
 #include "llviewercontrol.h" // gSavedSettings
@@ -216,8 +217,7 @@ S32 LLViewerChat::getChatFontSize()
 //static
 void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
 {
-    std::string tmpmsg = chat.mText;
-
+    std::string tmpmsg = chat.mIsScript ? chat.mText.substr(LUA_PREFIX.size()) : chat.mText;
     if(chat.mChatStyle == CHAT_STYLE_IRC)
     {
         formated_msg = chat.mFromName + tmpmsg.substr(3);
@@ -227,6 +227,11 @@ void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
         formated_msg = tmpmsg;
     }
 
+    if (chat.mIsScript) 
+    {
+        formated_msg = LLTrans::getString("ScriptStr") + formated_msg;
+    }
+
 }
 
 //static
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 52bc2d9654..4fe5beceae 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2600,8 +2600,10 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
 
         BOOL ircstyle = FALSE;
 
+        chat.mIsScript = (mesg.substr(0, LUA_PREFIX.size()) == LUA_PREFIX);
+
         // Look for IRC-style emotes here so chatbubbles work
-        std::string prefix = mesg.substr(0, 4);
+        std::string prefix = mesg.substr(chat.mIsScript ? LUA_PREFIX.size() : 0, 4);
         if (prefix == "/me " || prefix == "/me'")
         {
             ircstyle = TRUE;
diff --git a/indra/newview/scripts/lua/LLChat.lua b/indra/newview/scripts/lua/LLChat.lua
new file mode 100644
index 0000000000..7db538e837
--- /dev/null
+++ b/indra/newview/scripts/lua/LLChat.lua
@@ -0,0 +1,17 @@
+leap = require 'leap'
+
+local LLChat = {}
+
+function LLChat.sendNearby(msg)
+    leap.send('LLChatBar', {op='sendChat', message=msg})
+end
+
+function LLChat.sendWhisper(msg)
+    leap.send('LLChatBar', {op='sendChat', type='whisper', message=msg})
+end
+
+function LLChat.sendShout(msg)
+    leap.send('LLChatBar', {op='sendChat', type='shout', message=msg})
+end
+
+return LLChat
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index bee58da6b0..76a2660dbb 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -534,7 +534,9 @@ http://secondlife.com/support for help fixing this problem.
 	<string name="ChangeYourDefaultAnimations">Change your default animations</string>
 	<string name="ForceSitAvatar">Force your avatar to sit</string>
 	<string name="ChangeEnvSettings">Change your environment settings</string>
-	
+	<string name="ScriptBy" value="Script by "/>
+	<string name="ScriptStr" value="Script: "/>
+
 	<string name="NotConnected">Not Connected</string>
 	<string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name -->
 	<string name="JoinAnExperience"/><!-- intentionally blank -->
-- 
cgit v1.2.3


From 85664b011e37c6c9926924f6fb72ceeb10d07833 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Wed, 22 May 2024 21:03:45 +0300
Subject: add throttle for sending messages; add simple demo script

---
 indra/newview/llfloaterimnearbychatlistener.cpp | 11 +++++++++++
 indra/newview/scripts/lua/test_LLChat.lua       | 18 ++++++++++++++++++
 2 files changed, 29 insertions(+)
 create mode 100644 indra/newview/scripts/lua/test_LLChat.lua

diff --git a/indra/newview/llfloaterimnearbychatlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp
index d6dfe42639..e8fb510111 100644
--- a/indra/newview/llfloaterimnearbychatlistener.cpp
+++ b/indra/newview/llfloaterimnearbychatlistener.cpp
@@ -35,6 +35,7 @@
 #include "llchat.h"
 #include "llviewercontrol.h"
 
+static const F32 CHAT_THROTTLE_PERIOD = 1.f;
 
 LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener()
   : LLEventAPI("LLChatBar",
@@ -52,6 +53,16 @@ LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener()
 // "sendChat" command
 void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
 {
+    static F64 last_throttle_time = 0.0;
+    F64 cur_time = LLTimer::getElapsedSeconds();
+
+    if (cur_time < last_throttle_time + CHAT_THROTTLE_PERIOD)
+    {
+        LL_DEBUGS("LLFloaterIMNearbyChatListener") << "'sendChat' was  throttled" << LL_ENDL;
+        return;
+    }
+    last_throttle_time = cur_time;
+
     // Extract the data
     std::string chat_text = LUA_PREFIX + chat_data["message"].asString();
 
diff --git a/indra/newview/scripts/lua/test_LLChat.lua b/indra/newview/scripts/lua/test_LLChat.lua
new file mode 100644
index 0000000000..95bd218baa
--- /dev/null
+++ b/indra/newview/scripts/lua/test_LLChat.lua
@@ -0,0 +1,18 @@
+LLChat = require 'LLChat'
+
+function generateRandomWord(length)
+    local alphabet = "abcdefghijklmnopqrstuvwxyz"
+    local word = ""
+    for i = 1, length do
+        local randomIndex = math.random(1, #alphabet)
+        word = word .. alphabet:sub(randomIndex, randomIndex)
+    end
+    return word
+end
+
+local msg = ""
+math.randomseed(os.time())
+for i = 1, math.random(1, 10) do
+    msg = msg .. " ".. generateRandomWord(math.random(1, 8))
+end
+LLChat.sendNearby(msg)
-- 
cgit v1.2.3


From c38e71411080af9e03646bbe34f766201d942afd Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <maximnproductengine@lindenlab.com>
Date: Thu, 23 May 2024 13:13:47 +0300
Subject: mac build fix

---
 indra/newview/llchathistory.cpp            | 4 ++--
 indra/newview/llchatitemscontainerctrl.cpp | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 6f02a08020..54b0c171f1 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -776,7 +776,7 @@ public:
         switch (mSourceType)
         {
             case CHAT_SOURCE_AGENT:
-                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : chat.mFromID);
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD(chat.mFromID));
                 break;
             case CHAT_SOURCE_OBJECT:
                 icon->setValue(LLSD("OBJECT_Icon"));
@@ -789,7 +789,7 @@ public:
                 icon->setValue(LLSD("Command_Destinations_Icon"));
                 break;
             case CHAT_SOURCE_UNKNOWN:
-                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : chat.mFromID);
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD(chat.mFromID));
         }
 
         // In case the message came from an object, save the object info
diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp
index 8eabe3d222..0561d56fe1 100644
--- a/indra/newview/llchatitemscontainerctrl.cpp
+++ b/indra/newview/llchatitemscontainerctrl.cpp
@@ -402,7 +402,7 @@ void LLFloaterIMNearbyChatToastPanel::draw()
             else if(mSourceType == CHAT_SOURCE_SYSTEM)
                 icon->setValue(LLSD("SL_Logo"));
             else if(mSourceType == CHAT_SOURCE_AGENT)
-                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : mFromID);
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD(mFromID));
             else if(!mFromID.isNull())
                 icon->setValue(mFromID);
         }
-- 
cgit v1.2.3


From e4b7d2f463d19cab28985414365fd8415e3dc700 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Fri, 24 May 2024 15:24:50 +0300
Subject: Mark script messages in compact mode too; code clean up

---
 indra/llui/llchat.h                             | 12 +++++++++++
 indra/newview/llchathistory.cpp                 | 28 ++++++++++++-------------
 indra/newview/llfloaterimnearbychathandler.cpp  |  2 +-
 indra/newview/llfloaterimnearbychatlistener.cpp | 12 +++++------
 indra/newview/llfloaterimnearbychatlistener.h   |  4 +++-
 indra/newview/llviewerchat.cpp                  |  2 +-
 indra/newview/scripts/lua/test_LLChat.lua       |  8 +++----
 7 files changed, 40 insertions(+), 28 deletions(-)

diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h
index 8fd92c2550..343b81ba8d 100644
--- a/indra/llui/llchat.h
+++ b/indra/llui/llchat.h
@@ -113,4 +113,16 @@ public:
 };
 static const std::string LUA_PREFIX("[LUA]");
 
+inline std::string remove_LUA_PREFIX(const std::string &string, bool is_lua)
+{
+    if (is_lua)
+    {
+        return string.substr(LUA_PREFIX.size());
+    }
+    else
+    {
+        return string;
+    }
+}
+
 #endif
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 54b0c171f1..7ee0f3dd7a 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -665,6 +665,7 @@ public:
         mSessionID = chat.mSessionID;
         mSourceType = chat.mSourceType;
         mIsFromScript = is_script;
+        mPrefix = mIsFromScript ? LLTrans::getString("ScriptBy") : "";
 
         // To be able to report a message, we need a copy of it's text
         // and it's easier to store text directly than trying to get
@@ -734,7 +735,7 @@ public:
                 username_end == (chat.mFromName.length() - 1))
             {
                 mFrom = chat.mFromName.substr(0, username_start);
-                user_name->setValue(mIsFromScript ? LLTrans::getString("ScriptBy") + mFrom : mFrom);
+                user_name->setValue(mPrefix + mFrom);
 
                 if (gSavedSettings.getBOOL("NameTagShowUsernames"))
                 {
@@ -789,7 +790,7 @@ public:
                 icon->setValue(LLSD("Command_Destinations_Icon"));
                 break;
             case CHAT_SOURCE_UNKNOWN:
-                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD(chat.mFromID));
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD("Unknown_Icon"));
         }
 
         // In case the message came from an object, save the object info
@@ -1031,14 +1032,7 @@ private:
         mFrom = av_name.getDisplayName();
 
         LLTextBox* user_name = getChild<LLTextBox>("user_name");
-        if(mIsFromScript) 
-        {
-            user_name->setValue(LLSD(LLTrans::getString("ScriptBy") + av_name.getDisplayName()));
-        }
-        else 
-        {
-            user_name->setValue(LLSD(av_name.getDisplayName()));
-        }
+        user_name->setValue(LLSD(mPrefix + av_name.getDisplayName()));
         user_name->setToolTip( av_name.getUserName() );
 
         if (gSavedSettings.getBOOL("NameTagShowUsernames") &&
@@ -1081,6 +1075,7 @@ protected:
     bool                mNeedsTimeBox;
 
     bool mIsFromScript;
+    std::string mPrefix;
 
 private:
     boost::signals2::connection mAvatarNameCacheConnection;
@@ -1270,8 +1265,11 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
     name_params.color(name_color);
     name_params.readonly_color(name_color);
 
-    bool is_lua = (chat.mText.substr(0, LUA_PREFIX.size()) == LUA_PREFIX);
-    std::string prefix = chat.mText.substr(is_lua ? LUA_PREFIX.size() : 0, 4);
+    bool is_lua = LLStringUtil::startsWith(chat.mText, LUA_PREFIX);
+
+    std::string message = remove_LUA_PREFIX(chat.mText, is_lua); 
+    std::string prefix = message.substr(0, 4);
+
     //IRC styled /me messages.
     bool irc_me = prefix == "/me " || prefix == "/me'";
 
@@ -1347,6 +1345,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
         // names showing
         if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size())
         {
+            std::string script_prefix = is_lua ? LLTrans::getString("ScriptBy") : "";
             // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text.
             if (chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull())
             {
@@ -1371,7 +1370,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
                 link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID));
 
                 // Add link to avatar's inspector and delimiter to message.
-                mEditor->appendText(std::string(link_params.link_href) + delimiter,
+                mEditor->appendText(script_prefix + std::string(link_params.link_href) + delimiter,
                     prependNewLineState, link_params);
                 prependNewLineState = false;
             }
@@ -1384,7 +1383,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
             }
             else
             {
-                mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter,
+                mEditor->appendText(script_prefix + "<nolink>" + chat.mFromName + "</nolink>" + delimiter,
                         prependNewLineState, body_message_params);
                 prependNewLineState = false;
             }
@@ -1505,7 +1504,6 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
     // usual messages showing
     else if (!teleport_separator)
     {
-        std::string message = is_lua ? chat.mText.substr(LUA_PREFIX.size()) : chat.mText;
         message = irc_me ? message.substr(3) : message;
 
         //MESSAGE TEXT PROCESSING
diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index 91d9cd56cf..0c7cd3aec4 100644
--- a/indra/newview/llfloaterimnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -613,7 +613,7 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
         }
         else
         {
-            toast_msg = chat_msg.mIsScript ? chat_msg.mText.substr(LUA_PREFIX.size()) : chat_msg.mText;
+            toast_msg = remove_LUA_PREFIX(chat_msg.mText, chat_msg.mIsScript);
         }
 
         bool chat_overlaps = false;
diff --git a/indra/newview/llfloaterimnearbychatlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp
index e8fb510111..e1be6f7281 100644
--- a/indra/newview/llfloaterimnearbychatlistener.cpp
+++ b/indra/newview/llfloaterimnearbychatlistener.cpp
@@ -39,7 +39,8 @@ static const F32 CHAT_THROTTLE_PERIOD = 1.f;
 
 LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener()
   : LLEventAPI("LLChatBar",
-               "LLChatBar listener to (e.g.) sendChat, etc.")
+               "LLChatBar listener to (e.g.) sendChat, etc."),
+    mLastThrottleTime(0)
 {
     add("sendChat",
         "Send chat to the simulator:\n"
@@ -51,17 +52,16 @@ LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener()
 
 
 // "sendChat" command
-void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
+void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data)
 {
-    static F64 last_throttle_time = 0.0;
     F64 cur_time = LLTimer::getElapsedSeconds();
 
-    if (cur_time < last_throttle_time + CHAT_THROTTLE_PERIOD)
+    if (cur_time < mLastThrottleTime + CHAT_THROTTLE_PERIOD)
     {
         LL_DEBUGS("LLFloaterIMNearbyChatListener") << "'sendChat' was  throttled" << LL_ENDL;
         return;
     }
-    last_throttle_time = cur_time;
+    mLastThrottleTime = cur_time;
 
     // Extract the data
     std::string chat_text = LUA_PREFIX + chat_data["message"].asString();
@@ -105,6 +105,6 @@ void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
     }
 
     // Send it as if it was typed in
-    LLFloaterIMNearbyChat::sendChatFromViewer(chat_to_send, type_o_chat, ((BOOL) (channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim"));
+    LLFloaterIMNearbyChat::sendChatFromViewer(chat_to_send, type_o_chat, ((channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim"));
 }
 
diff --git a/indra/newview/llfloaterimnearbychatlistener.h b/indra/newview/llfloaterimnearbychatlistener.h
index 0df0341d36..2d7c851f2c 100644
--- a/indra/newview/llfloaterimnearbychatlistener.h
+++ b/indra/newview/llfloaterimnearbychatlistener.h
@@ -41,7 +41,9 @@ public:
     LLFloaterIMNearbyChatListener();
 
 private:
-    void sendChat(LLSD const & chat_data) const;
+    void sendChat(LLSD const & chat_data);
+
+    F64 mLastThrottleTime;
 };
 
 #endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H
diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp
index 70c426740b..ce29ceac1a 100644
--- a/indra/newview/llviewerchat.cpp
+++ b/indra/newview/llviewerchat.cpp
@@ -217,7 +217,7 @@ S32 LLViewerChat::getChatFontSize()
 //static
 void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
 {
-    std::string tmpmsg = chat.mIsScript ? chat.mText.substr(LUA_PREFIX.size()) : chat.mText;
+    std::string tmpmsg = remove_LUA_PREFIX(chat.mText, chat.mIsScript);
     if(chat.mChatStyle == CHAT_STYLE_IRC)
     {
         formated_msg = chat.mFromName + tmpmsg.substr(3);
diff --git a/indra/newview/scripts/lua/test_LLChat.lua b/indra/newview/scripts/lua/test_LLChat.lua
index 95bd218baa..883b82fafb 100644
--- a/indra/newview/scripts/lua/test_LLChat.lua
+++ b/indra/newview/scripts/lua/test_LLChat.lua
@@ -2,15 +2,15 @@ LLChat = require 'LLChat'
 
 function generateRandomWord(length)
     local alphabet = "abcdefghijklmnopqrstuvwxyz"
-    local word = ""
+    local wordTable = {}
     for i = 1, length do
         local randomIndex = math.random(1, #alphabet)
-        word = word .. alphabet:sub(randomIndex, randomIndex)
+        table.insert(wordTable, alphabet:sub(randomIndex, randomIndex))
     end
-    return word
+    return table.concat(wordTable)
 end
 
-local msg = ""
+local msg = "AI says:"
 math.randomseed(os.time())
 for i = 1, math.random(1, 10) do
     msg = msg .. " ".. generateRandomWord(math.random(1, 8))
-- 
cgit v1.2.3


From b0ef843fe0702482e843379b4975b2dda4d58935 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Fri, 24 May 2024 09:22:40 -0400
Subject: Nat's ideas from PR #1547

---
 indra/llcommon/llstring.h                       | 62 +++++++++++++++++++++++++
 indra/llui/llchat.h                             | 13 ++++++
 indra/newview/llchathistory.cpp                 | 22 ++++-----
 indra/newview/llfloaterimnearbychathandler.cpp  |  5 +-
 indra/newview/llfloaterimnearbychatlistener.cpp | 24 ++++------
 indra/newview/llfloaterimnearbychatlistener.h   |  4 +-
 indra/newview/llviewerchat.cpp                  |  2 +-
 indra/newview/llviewermessage.cpp               |  5 +-
 indra/newview/scripts/lua/test_LLChat.lua       | 12 ++---
 9 files changed, 108 insertions(+), 41 deletions(-)

diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 21b0da4822..cd8b2a2dcd 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -315,6 +315,14 @@ public:
     static void trim(string_type& string)   { trimHead(string); trimTail(string); }
     static void truncate(string_type& string, size_type count);
 
+    // if string startsWith prefix, remove it and return true
+    static bool removePrefix(string_type& string, const string_type& prefix);
+    // if string startsWith prefix, return (string without prefix, true), else (string, false)
+    static std::pair<string_type, bool> withoutPrefix(const string_type& string, const string_type& prefix);
+    // like removePrefix()
+    static bool removeSuffix(string_type& string, const string_type& suffix);
+    static std::pair<string_type, bool> withoutSuffix(const string_type& string, const string_type& suffix);
+
     static void toUpper(string_type& string);
     static void toLower(string_type& string);
 
@@ -1479,6 +1487,60 @@ void LLStringUtilBase<T>::trimTail(string_type& string)
     }
 }
 
+// if string startsWith prefix, remove it and return true
+template<class T>
+bool LLStringUtilBase<T>::removePrefix(string_type& string, const string_type& prefix)
+{
+    bool found{ startsWith(string, prefix) };
+    if (found)
+    {
+        string.erase(0, prefix.length());
+    }
+    return found;
+}
+
+// if string startsWith prefix, return (string without prefix, true), else (string, false)
+template<class T>
+std::pair<typename LLStringUtilBase<T>::string_type, bool>
+LLStringUtilBase<T>::withoutPrefix(const string_type& string, const string_type& prefix)
+{
+    bool found{ startsWith(string, prefix) };
+    if (! found)
+    {
+        return { string, false };
+    }
+    else
+    {
+        return { string.substr(prefix.length()), true };
+    }
+}
+
+// like removePrefix()
+template<class T>
+bool LLStringUtilBase<T>::removeSuffix(string_type& string, const string_type& suffix)
+{
+    bool found{ endsWith(string, suffix) };
+    if (found)
+    {
+        string.erase(string.length() - suffix.length());
+    }
+    return found;
+}
+
+template<class T>
+std::pair<typename LLStringUtilBase<T>::string_type, bool>
+LLStringUtilBase<T>::withoutSuffix(const string_type& string, const string_type& suffix)
+{
+    bool found{ endsWith(string, suffix) };
+    if (! found)
+    {
+        return { string, false };
+    }
+    else
+    {
+        return { string.substr(0, string.length() - suffix.length()), true };
+    }
+}
 
 // Replace line feeds with carriage return-line feed pairs.
 //static
diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h
index 8fd92c2550..70d7e82970 100644
--- a/indra/llui/llchat.h
+++ b/indra/llui/llchat.h
@@ -113,4 +113,17 @@ public:
 };
 static const std::string LUA_PREFIX("[LUA]");
 
+inline
+std::string without_LUA_PREFIX(const std::string& string, bool is_lua)
+{
+    if (is_lua)
+    {
+        return string.substr(LUA_PREFIX.size());
+    }
+    else
+    {
+        return string;
+    }
+}
+
 #endif
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 54b0c171f1..f16377d7e3 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -665,6 +665,7 @@ public:
         mSessionID = chat.mSessionID;
         mSourceType = chat.mSourceType;
         mIsFromScript = is_script;
+        mScriptPrefix = is_script? LLTrans::getString("ScriptBy") : "";
 
         // To be able to report a message, we need a copy of it's text
         // and it's easier to store text directly than trying to get
@@ -734,7 +735,7 @@ public:
                 username_end == (chat.mFromName.length() - 1))
             {
                 mFrom = chat.mFromName.substr(0, username_start);
-                user_name->setValue(mIsFromScript ? LLTrans::getString("ScriptBy") + mFrom : mFrom);
+                user_name->setValue(mScriptPrefix + mFrom);
 
                 if (gSavedSettings.getBOOL("NameTagShowUsernames"))
                 {
@@ -789,7 +790,7 @@ public:
                 icon->setValue(LLSD("Command_Destinations_Icon"));
                 break;
             case CHAT_SOURCE_UNKNOWN:
-                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD(chat.mFromID));
+                icon->setValue(mIsFromScript ? LLSD("Inv_Script") : LLSD("Unknown_Icon"));
         }
 
         // In case the message came from an object, save the object info
@@ -1031,14 +1032,7 @@ private:
         mFrom = av_name.getDisplayName();
 
         LLTextBox* user_name = getChild<LLTextBox>("user_name");
-        if(mIsFromScript) 
-        {
-            user_name->setValue(LLSD(LLTrans::getString("ScriptBy") + av_name.getDisplayName()));
-        }
-        else 
-        {
-            user_name->setValue(LLSD(av_name.getDisplayName()));
-        }
+        user_name->setValue(LLSD(mScriptPrefix + av_name.getDisplayName()));
         user_name->setToolTip( av_name.getUserName() );
 
         if (gSavedSettings.getBOOL("NameTagShowUsernames") &&
@@ -1070,6 +1064,7 @@ protected:
     std::string         mFrom;
     LLUUID              mSessionID;
     std::string         mText;
+    std::string         mScriptPrefix;
     F64                 mTime; // IM's frame time
     time_t              mCreationTime; // Views's time
 
@@ -1080,7 +1075,7 @@ protected:
 
     bool                mNeedsTimeBox;
 
-    bool mIsFromScript;
+    bool                mIsFromScript;
 
 private:
     boost::signals2::connection mAvatarNameCacheConnection;
@@ -1270,8 +1265,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
     name_params.color(name_color);
     name_params.readonly_color(name_color);
 
-    bool is_lua = (chat.mText.substr(0, LUA_PREFIX.size()) == LUA_PREFIX);
-    std::string prefix = chat.mText.substr(is_lua ? LUA_PREFIX.size() : 0, 4);
+    auto [message, is_lua] = LLStringUtil::withoutPrefix(chat.mText, LUA_PREFIX);
+    std::string prefix = message.substr(0, 4);
     //IRC styled /me messages.
     bool irc_me = prefix == "/me " || prefix == "/me'";
 
@@ -1505,7 +1500,6 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
     // usual messages showing
     else if (!teleport_separator)
     {
-        std::string message = is_lua ? chat.mText.substr(LUA_PREFIX.size()) : chat.mText;
         message = irc_me ? message.substr(3) : message;
 
         //MESSAGE TEXT PROCESSING
diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index 91d9cd56cf..cda71f97d4 100644
--- a/indra/newview/llfloaterimnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -599,6 +599,7 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
     {
         // Handle IRC styled messages.
         std::string toast_msg;
+        std::string msg_text = without_LUA_PREFIX(chat_msg.mText, chat_msg.mIsScript);
         if (chat_msg.mChatStyle == CHAT_STYLE_IRC)
         {
             if (chat_msg.mIsScript)
@@ -609,11 +610,11 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
             {
                 toast_msg += chat_msg.mFromName;
             }
-            toast_msg += chat_msg.mText.substr(chat_msg.mIsScript ? LUA_PREFIX.size() + 3 : 3);
+            toast_msg += msg_text.substr(3);
         }
         else
         {
-            toast_msg = chat_msg.mIsScript ? chat_msg.mText.substr(LUA_PREFIX.size()) : chat_msg.mText;
+            toast_msg = msg_text;
         }
 
         bool chat_overlaps = false;
diff --git a/indra/newview/llfloaterimnearbychatlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp
index e8fb510111..9093db282a 100644
--- a/indra/newview/llfloaterimnearbychatlistener.cpp
+++ b/indra/newview/llfloaterimnearbychatlistener.cpp
@@ -34,6 +34,7 @@
 #include "llagent.h"
 #include "llchat.h"
 #include "llviewercontrol.h"
+#include "stringize.h"
 
 static const F32 CHAT_THROTTLE_PERIOD = 1.f;
 
@@ -51,17 +52,16 @@ LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener()
 
 
 // "sendChat" command
-void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
+void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data)
 {
-    static F64 last_throttle_time = 0.0;
     F64 cur_time = LLTimer::getElapsedSeconds();
 
-    if (cur_time < last_throttle_time + CHAT_THROTTLE_PERIOD)
+    if (cur_time < mLastThrottleTime + CHAT_THROTTLE_PERIOD)
     {
         LL_DEBUGS("LLFloaterIMNearbyChatListener") << "'sendChat' was  throttled" << LL_ENDL;
         return;
     }
-    last_throttle_time = cur_time;
+    mLastThrottleTime = cur_time;
 
     // Extract the data
     std::string chat_text = LUA_PREFIX + chat_data["message"].asString();
@@ -91,20 +91,14 @@ void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
     }
 
     // Have to prepend /42 style channel numbers
-    std::string chat_to_send;
-    if (channel == 0)
+    if (channel)
     {
-        chat_to_send = chat_text;
-    }
-    else
-    {
-        chat_to_send += "/";
-        chat_to_send += chat_data["channel"].asString();
-        chat_to_send += " ";
-        chat_to_send += chat_text;
+        chat_text = stringize("/", chat_data["channel"].asString(), " ", chat_text);
     }
 
     // Send it as if it was typed in
-    LLFloaterIMNearbyChat::sendChatFromViewer(chat_to_send, type_o_chat, ((BOOL) (channel == 0)) && gSavedSettings.getBOOL("PlayChatAnim"));
+    LLFloaterIMNearbyChat::sendChatFromViewer(chat_text, type_o_chat,
+                                              (channel == 0) &&
+                                              gSavedSettings.getBOOL("PlayChatAnim"));
 }
 
diff --git a/indra/newview/llfloaterimnearbychatlistener.h b/indra/newview/llfloaterimnearbychatlistener.h
index 0df0341d36..34d4effd48 100644
--- a/indra/newview/llfloaterimnearbychatlistener.h
+++ b/indra/newview/llfloaterimnearbychatlistener.h
@@ -41,7 +41,9 @@ public:
     LLFloaterIMNearbyChatListener();
 
 private:
-    void sendChat(LLSD const & chat_data) const;
+    F64 mLastThrottleTime{ 0.0 };
+
+    void sendChat(LLSD const & chat_data);
 };
 
 #endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H
diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp
index 70c426740b..00520f100e 100644
--- a/indra/newview/llviewerchat.cpp
+++ b/indra/newview/llviewerchat.cpp
@@ -217,7 +217,7 @@ S32 LLViewerChat::getChatFontSize()
 //static
 void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
 {
-    std::string tmpmsg = chat.mIsScript ? chat.mText.substr(LUA_PREFIX.size()) : chat.mText;
+    std::string tmpmsg = without_LUA_PREFIX(chat.mText, chat.mIsScript);
     if(chat.mChatStyle == CHAT_STYLE_IRC)
     {
         formated_msg = chat.mFromName + tmpmsg.substr(3);
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 4fe5beceae..d1c773171b 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2600,10 +2600,11 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
 
         BOOL ircstyle = FALSE;
 
-        chat.mIsScript = (mesg.substr(0, LUA_PREFIX.size()) == LUA_PREFIX);
+        auto [message, is_script] = LLStringUtil::withoutPrefix(mesg, LUA_PREFIX);
+        chat.mIsScript = is_script;
 
         // Look for IRC-style emotes here so chatbubbles work
-        std::string prefix = mesg.substr(chat.mIsScript ? LUA_PREFIX.size() : 0, 4);
+        std::string prefix = message.substr(0, 4);
         if (prefix == "/me " || prefix == "/me'")
         {
             ircstyle = TRUE;
diff --git a/indra/newview/scripts/lua/test_LLChat.lua b/indra/newview/scripts/lua/test_LLChat.lua
index 95bd218baa..3c5cbeeeb2 100644
--- a/indra/newview/scripts/lua/test_LLChat.lua
+++ b/indra/newview/scripts/lua/test_LLChat.lua
@@ -2,17 +2,17 @@ LLChat = require 'LLChat'
 
 function generateRandomWord(length)
     local alphabet = "abcdefghijklmnopqrstuvwxyz"
-    local word = ""
+    local word = {}
     for i = 1, length do
         local randomIndex = math.random(1, #alphabet)
-        word = word .. alphabet:sub(randomIndex, randomIndex)
+        table.insert(word, alphabet:sub(randomIndex, randomIndex))
     end
-    return word
+    return table.concat(word)
 end
 
-local msg = ""
+local msg = {'AI says:'}
 math.randomseed(os.time())
 for i = 1, math.random(1, 10) do
-    msg = msg .. " ".. generateRandomWord(math.random(1, 8))
+    table.insert(msg, generateRandomWord(math.random(1, 8)))
 end
-LLChat.sendNearby(msg)
+LLChat.sendNearby(table.concat(msg, ' '))
-- 
cgit v1.2.3


From 94cac4c443b5acc189b5223c78eb278648846338 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Fri, 24 May 2024 10:15:24 -0400
Subject: Fix merge glitches

---
 indra/newview/llchathistory.cpp                | 11 -----------
 indra/newview/llfloaterimnearbychathandler.cpp |  4 ----
 2 files changed, 15 deletions(-)

diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index e5878a9a24..d549f372e3 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -1265,19 +1265,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL
     name_params.color(name_color);
     name_params.readonly_color(name_color);
 
-<<<<<<< variant A
     auto [message, is_lua] = LLStringUtil::withoutPrefix(chat.mText, LUA_PREFIX);
     std::string prefix = message.substr(0, 4);
->>>>>>> variant B
-    bool is_lua = LLStringUtil::startsWith(chat.mText, LUA_PREFIX);
-
-    std::string message = remove_LUA_PREFIX(chat.mText, is_lua); 
-    std::string prefix = message.substr(0, 4);
-
-####### Ancestor
-    bool is_lua = (chat.mText.substr(0, LUA_PREFIX.size()) == LUA_PREFIX);
-    std::string prefix = chat.mText.substr(is_lua ? LUA_PREFIX.size() : 0, 4);
-======= end
     //IRC styled /me messages.
     bool irc_me = prefix == "/me " || prefix == "/me'";
 
diff --git a/indra/newview/llfloaterimnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index 6a9dccf2f4..cda71f97d4 100644
--- a/indra/newview/llfloaterimnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -614,11 +614,7 @@ void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
         }
         else
         {
-<<<<<<< HEAD
             toast_msg = msg_text;
-=======
-            toast_msg = remove_LUA_PREFIX(chat_msg.mText, chat_msg.mIsScript);
->>>>>>> release/luau-scripting
         }
 
         bool chat_overlaps = false;
-- 
cgit v1.2.3