SL-18268 - Viewer update to read group chat history.
Feature described at https://community.secondlife.com/blogs/entry/12652-coming-soon-to-a-viewer-near-you-group-chat-history/master
parent
f60f12d94e
commit
f257ee7d3c
|
|
@ -521,7 +521,9 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
|
|||
dialog,
|
||||
parent_estate_id,
|
||||
region_id,
|
||||
position);
|
||||
position,
|
||||
false, // is_region_msg
|
||||
timestamp);
|
||||
|
||||
if (!gIMMgr->isDNDMessageSend(session_id))
|
||||
{
|
||||
|
|
@ -592,7 +594,8 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
|
|||
parent_estate_id,
|
||||
region_id,
|
||||
position,
|
||||
region_message);
|
||||
region_message,
|
||||
timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1111,7 +1114,9 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
|
|||
IM_SESSION_INVITE,
|
||||
parent_estate_id,
|
||||
region_id,
|
||||
position);
|
||||
position,
|
||||
false, // is_region_msg
|
||||
timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1131,12 +1136,14 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
|
|||
from_id,
|
||||
name,
|
||||
buffer,
|
||||
IM_OFFLINE == offline,
|
||||
ll_safe_string((char*)binary_bucket),
|
||||
(IM_OFFLINE == offline),
|
||||
ll_safe_string((char*)binary_bucket), // session name
|
||||
IM_SESSION_INVITE,
|
||||
parent_estate_id,
|
||||
region_id,
|
||||
position);
|
||||
position,
|
||||
false, // is_region_msg
|
||||
timestamp);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -42,6 +42,7 @@ class LLAvatarName;
|
|||
class LLFriendObserver;
|
||||
class LLCallDialogManager;
|
||||
class LLIMSpeakerMgr;
|
||||
|
||||
/**
|
||||
* Timeout Timer for outgoing Ad-Hoc/Group IM sessions which being initialized by the server
|
||||
*/
|
||||
|
|
@ -63,11 +64,14 @@ private:
|
|||
class LLIMModel : public LLSingleton<LLIMModel>
|
||||
{
|
||||
LLSINGLETON(LLIMModel);
|
||||
|
||||
public:
|
||||
|
||||
struct LLIMSession : public boost::signals2::trackable
|
||||
typedef std::list<LLSD> chat_message_list_t;
|
||||
|
||||
struct LLIMSession : public boost::signals2::trackable
|
||||
{
|
||||
typedef enum e_session_type
|
||||
typedef enum e_session_type
|
||||
{ // for now we have 4 predefined types for a session
|
||||
P2P_SESSION,
|
||||
GROUP_SESSION,
|
||||
|
|
@ -75,15 +79,23 @@ public:
|
|||
NONE_SESSION,
|
||||
} SType;
|
||||
|
||||
LLIMSession(const LLUUID& session_id, const std::string& name,
|
||||
LLIMSession(const LLUUID& session_id, const std::string& name,
|
||||
const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg);
|
||||
virtual ~LLIMSession();
|
||||
|
||||
void sessionInitReplyReceived(const LLUUID& new_session_id);
|
||||
void addMessagesFromHistory(const std::list<LLSD>& history);
|
||||
void addMessage(const std::string& from, const LLUUID& from_id, const std::string& utf8_text, const std::string& time, const bool is_history = false, bool is_region_msg = false);
|
||||
void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction);
|
||||
|
||||
void addMessagesFromHistoryCache(const std::list<LLSD>& history); // From local file
|
||||
void addMessagesFromServerHistory(const LLSD& history, const std::string& target_from, const std::string& target_message, U32 timestamp); // From chat server
|
||||
void addMessage(const std::string& from,
|
||||
const LLUUID& from_id,
|
||||
const std::string& utf8_text,
|
||||
const std::string& time,
|
||||
const bool is_history,
|
||||
const bool is_region_msg,
|
||||
U32 timestamp);
|
||||
|
||||
void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state, const LLVoiceChannel::EDirection& direction);
|
||||
|
||||
/** @deprecated */
|
||||
static void chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata);
|
||||
|
||||
|
|
@ -112,6 +124,10 @@ public:
|
|||
uuid_vec_t mInitialTargetIDs;
|
||||
std::string mHistoryFileName;
|
||||
|
||||
// Saved messages from the last minute of history read from the local group chat cache file
|
||||
std::string mLastHistoryCacheDateTime;
|
||||
chat_message_list_t mLastHistoryCacheMsgs;
|
||||
|
||||
// connection to voice channel state change signal
|
||||
boost::signals2::connection mVoiceChannelStateChangeConnection;
|
||||
|
||||
|
|
@ -121,7 +137,7 @@ public:
|
|||
// does include all incoming messages
|
||||
S32 mNumUnread;
|
||||
|
||||
std::list<LLSD> mMsgs;
|
||||
chat_message_list_t mMsgs;
|
||||
|
||||
LLVoiceChannel* mVoiceChannel;
|
||||
LLIMSpeakerMgr* mSpeakers;
|
||||
|
|
@ -208,14 +224,27 @@ public:
|
|||
* and also saved into a file if log2file is specified.
|
||||
* It sends new message signal for each added message.
|
||||
*/
|
||||
void addMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text, bool log2file = true, bool is_region_msg = false);
|
||||
void processAddingMessage(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text, bool log2file = true, bool is_region_msg = false);
|
||||
void addMessage(const LLUUID& session_id,
|
||||
const std::string& from,
|
||||
const LLUUID& other_participant_id,
|
||||
const std::string& utf8_text,
|
||||
bool log2file = true,
|
||||
bool is_region_msg = false,
|
||||
U32 time_stamp = 0);
|
||||
|
||||
void processAddingMessage(const LLUUID& session_id,
|
||||
const std::string& from,
|
||||
const LLUUID& from_id,
|
||||
const std::string& utf8_text,
|
||||
bool log2file,
|
||||
bool is_region_msg,
|
||||
U32 time_stamp);
|
||||
|
||||
/**
|
||||
* Similar to addMessage(...) above but won't send a signal about a new message added
|
||||
*/
|
||||
LLIMModel::LLIMSession* addMessageSilently(const LLUUID& session_id, const std::string& from, const LLUUID& from_id,
|
||||
const std::string& utf8_text, bool log2file = true, bool is_region_msg = false);
|
||||
LLIMModel::LLIMSession* addMessageSilently(const LLUUID& session_id, const std::string& from, const LLUUID& from_id,
|
||||
const std::string& utf8_text, bool log2file = true, bool is_region_msg = false, U32 timestamp = 0);
|
||||
|
||||
/**
|
||||
* Add a system message to an IM Model
|
||||
|
|
@ -223,15 +252,15 @@ public:
|
|||
void proccessOnlineOfflineNotification(const LLUUID& session_id, const std::string& utf8_text);
|
||||
|
||||
/**
|
||||
* Get a session's name.
|
||||
* For a P2P chat - it's an avatar's name,
|
||||
* Get a session's name.
|
||||
* For a P2P chat - it's an avatar's name,
|
||||
* For a group chat - it's a group's name
|
||||
* For an incoming ad-hoc chat - is received from the server and is in a from of "<Avatar's name> Conference"
|
||||
* It is updated in LLIMModel::LLIMSession's constructor to localize the "Conference".
|
||||
*/
|
||||
const std::string getName(const LLUUID& session_id) const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Get number of unread messages in a session with session_id
|
||||
* Returns -1 if the session with session_id doesn't exist
|
||||
*/
|
||||
|
|
@ -283,7 +312,7 @@ public:
|
|||
bool logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/**
|
||||
* Populate supplied std::list with messages starting from index specified by start_index without
|
||||
* emitting no unread messages signal.
|
||||
|
|
@ -293,7 +322,7 @@ private:
|
|||
/**
|
||||
* Add message to a list of message associated with session specified by session_id
|
||||
*/
|
||||
bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text, bool is_region_msg = false);
|
||||
bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text, bool is_region_msg, U32 timestamp);
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -335,7 +364,8 @@ public:
|
|||
U32 parent_estate_id = 0,
|
||||
const LLUUID& region_id = LLUUID::null,
|
||||
const LLVector3& position = LLVector3::zero,
|
||||
bool is_region_msg = false);
|
||||
bool is_region_msg = false,
|
||||
U32 timestamp = 0);
|
||||
|
||||
void addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
const S32 LOG_RECALL_SIZE = 2048;
|
||||
|
||||
const std::string LL_IM_TIME("time");
|
||||
const std::string LL_IM_DATE_TIME("datetime");
|
||||
const std::string LL_IM_TEXT("message");
|
||||
const std::string LL_IM_FROM("from");
|
||||
const std::string LL_IM_FROM_ID("from_id");
|
||||
|
|
@ -133,14 +134,14 @@ void append_to_last_message(std::list<LLSD>& messages, const std::string& line)
|
|||
messages.back()[LL_IM_TEXT] = im_text;
|
||||
}
|
||||
|
||||
std::string remove_utf8_bom(const char* buf)
|
||||
const char* remove_utf8_bom(const char* buf)
|
||||
{
|
||||
std::string res(buf);
|
||||
if (res[0] == (char)0xEF && res[1] == (char)0xBB && res[2] == (char)0xBF)
|
||||
{
|
||||
res.erase(0, 3);
|
||||
const char* start = buf;
|
||||
if (start[0] == (char)0xEF && start[1] == (char)0xBB && start[2] == (char)0xBF)
|
||||
{ // If string starts with the magic bytes, return pointer after it.
|
||||
start += 3;
|
||||
}
|
||||
return res;
|
||||
return start;
|
||||
}
|
||||
|
||||
class LLLogChatTimeScanner: public LLSingleton<LLLogChatTimeScanner>
|
||||
|
|
@ -315,7 +316,7 @@ std::string LLLogChat::cleanFileName(std::string filename)
|
|||
return filename;
|
||||
}
|
||||
|
||||
std::string LLLogChat::timestamp(bool withdate)
|
||||
std::string LLLogChat::timestamp2LogString(U32 timestamp, bool withdate)
|
||||
{
|
||||
std::string timeStr;
|
||||
if (withdate)
|
||||
|
|
@ -333,7 +334,14 @@ std::string LLLogChat::timestamp(bool withdate)
|
|||
}
|
||||
|
||||
LLSD substitution;
|
||||
substitution["datetime"] = (S32)time_corrected();
|
||||
if (timestamp == 0)
|
||||
{
|
||||
substitution["datetime"] = (S32)time_corrected();
|
||||
}
|
||||
else
|
||||
{ // timestamp is correct utc already
|
||||
substitution["datetime"] = (S32)timestamp;
|
||||
}
|
||||
|
||||
LLStringUtil::format (timeStr, substitution);
|
||||
return timeStr;
|
||||
|
|
@ -355,7 +363,7 @@ void LLLogChat::saveHistory(const std::string& filename,
|
|||
llassert(tmp_filename.size());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
llofstream file(LLLogChat::makeLogFileName(filename).c_str(), std::ios_base::app);
|
||||
if (!file.is_open())
|
||||
{
|
||||
|
|
@ -366,7 +374,7 @@ void LLLogChat::saveHistory(const std::string& filename,
|
|||
LLSD item;
|
||||
|
||||
if (gSavedPerAccountSettings.getBOOL("LogTimestamp"))
|
||||
item["time"] = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate"));
|
||||
item["time"] = LLLogChat::timestamp2LogString(0, gSavedPerAccountSettings.getBOOL("LogTimestampDate"));
|
||||
|
||||
item["from_id"] = from_id;
|
||||
item["message"] = line;
|
||||
|
|
@ -374,7 +382,7 @@ void LLLogChat::saveHistory(const std::string& filename,
|
|||
//adding "Second Life:" for all system messages to make chat log history parsing more reliable
|
||||
if (from.empty() && from_id.isNull())
|
||||
{
|
||||
item["from"] = SYSTEM_FROM;
|
||||
item["from"] = SYSTEM_FROM;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -393,37 +401,60 @@ void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& m
|
|||
{
|
||||
if (file_name.empty())
|
||||
{
|
||||
LL_WARNS("LLLogChat::loadChatHistory") << "Session name is Empty!" << LL_ENDL;
|
||||
LL_WARNS("LLLogChat::loadChatHistory") << "Local history file name is empty!" << LL_ENDL;
|
||||
return ;
|
||||
}
|
||||
|
||||
bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false;
|
||||
|
||||
LLFILE* fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
// Stat the file to find it and get the last history entry time
|
||||
llstat stat_data;
|
||||
|
||||
std::string log_file_name = LLLogChat::makeLogFileName(file_name);
|
||||
LL_DEBUGS("ChatHistory") << "First attempt to stat chat history file " << log_file_name << LL_ENDL;
|
||||
|
||||
S32 no_stat = LLFile::stat(log_file_name, &stat_data);
|
||||
|
||||
if (no_stat)
|
||||
{
|
||||
if (is_group)
|
||||
{
|
||||
std::string old_name(file_name);
|
||||
old_name.erase(old_name.size() - GROUP_CHAT_SUFFIX.size()); // trim off " (group)"
|
||||
log_file_name = LLLogChat::makeLogFileName(old_name);
|
||||
LL_DEBUGS("ChatHistory") << "Attempting to stat adjusted chat history file " << log_file_name << LL_ENDL;
|
||||
no_stat = LLFile::stat(log_file_name, &stat_data);
|
||||
if (!no_stat)
|
||||
{ // Found it without "(group)", copy to new naming style. We already have the mod time in stat_data
|
||||
log_file_name = LLLogChat::makeLogFileName(file_name);
|
||||
LL_DEBUGS("ChatHistory") << "Attempt to stat copied history file " << log_file_name << LL_ENDL;
|
||||
LLFile::copy(LLLogChat::makeLogFileName(old_name), log_file_name);
|
||||
}
|
||||
}
|
||||
if (no_stat)
|
||||
{
|
||||
log_file_name = LLLogChat::oldLogFileName(file_name);
|
||||
LL_DEBUGS("ChatHistory") << "Attempt to stat old history file name " << log_file_name << LL_ENDL;
|
||||
no_stat = LLFile::stat(log_file_name, &stat_data);
|
||||
if (no_stat)
|
||||
{
|
||||
LL_DEBUGS("ChatHistory") << "No previous conversation log file found for " << file_name << LL_ENDL;
|
||||
return; //No previous conversation with this name.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here, we managed to stat the file.
|
||||
// Open the file to read
|
||||
LLFILE* fptr = LLFile::fopen(log_file_name, "r"); /*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
if (is_group)
|
||||
{
|
||||
std::string old_name(file_name);
|
||||
old_name.erase(old_name.size() - GROUP_CHAT_SUFFIX.size());
|
||||
fptr = LLFile::fopen(LLLogChat::makeLogFileName(old_name), "r");
|
||||
if (fptr)
|
||||
{
|
||||
fclose(fptr);
|
||||
LLFile::copy(LLLogChat::makeLogFileName(old_name), LLLogChat::makeLogFileName(file_name));
|
||||
}
|
||||
fptr = LLFile::fopen(LLLogChat::makeLogFileName(file_name), "r");
|
||||
}
|
||||
if (!fptr)
|
||||
{
|
||||
fptr = LLFile::fopen(LLLogChat::oldLogFileName(file_name), "r");/*Flawfinder: ignore*/
|
||||
if (!fptr)
|
||||
{
|
||||
return; //No previous conversation with this name.
|
||||
}
|
||||
}
|
||||
{ // Ok, this is strange but not really tragic in the big picture of things
|
||||
LL_WARNS("ChatHistory") << "Unable to read file " << log_file_name << " after stat was successful" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
S32 save_num_messages = messages.size();
|
||||
|
||||
char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/
|
||||
char *bptr;
|
||||
S32 len;
|
||||
|
|
@ -441,6 +472,7 @@ void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& m
|
|||
while (fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr))
|
||||
{
|
||||
len = strlen(buffer) - 1; /*Flawfinder: ignore*/
|
||||
// backfill any end of line characters with nulls
|
||||
for (bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
|
||||
|
||||
if (firstline)
|
||||
|
|
@ -473,6 +505,10 @@ void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& m
|
|||
}
|
||||
}
|
||||
fclose(fptr);
|
||||
|
||||
LL_DEBUGS("ChatHistory") << "Read " << (messages.size() - save_num_messages)
|
||||
<< " messages of chat history from " << log_file_name
|
||||
<< " file mod time " << (F64)stat_data.st_mtime << LL_ENDL;
|
||||
}
|
||||
|
||||
bool LLLogChat::historyThreadsFinished(LLUUID session_id)
|
||||
|
|
@ -837,7 +873,8 @@ bool LLLogChat::isTranscriptFileFound(std::string fullname)
|
|||
{
|
||||
//matching a timestamp
|
||||
boost::match_results<std::string::const_iterator> matches;
|
||||
if (ll_regex_match(remove_utf8_bom(buffer), matches, TIMESTAMP))
|
||||
std::string line(remove_utf8_bom(buffer));
|
||||
if (ll_regex_match(line, matches, TIMESTAMP))
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
|
|
@ -847,7 +884,7 @@ bool LLLogChat::isTranscriptFileFound(std::string fullname)
|
|||
return result;
|
||||
}
|
||||
|
||||
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
|
||||
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
|
||||
//which are more strict by its nature (only firstname and secondname)
|
||||
//Example, an object's name can be written like "Object <actual_object's_name>"
|
||||
void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const
|
||||
|
|
@ -865,7 +902,7 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const
|
|||
ostr << '[' << timestamp << ']' << TWO_SPACES;
|
||||
}
|
||||
|
||||
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
|
||||
//*TODO mark object's names in a special way so that they will be distinguishable from avatar name
|
||||
//which are more strict by its nature (only firstname and secondname)
|
||||
//Example, an object's name can be written like "Object <actual_object's_name>"
|
||||
if (im[LL_IM_FROM].isDefined())
|
||||
|
|
@ -928,7 +965,9 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params
|
|||
timestamp.erase(0, 1);
|
||||
timestamp.erase(timestamp.length()-1, 1);
|
||||
|
||||
if (cut_off_todays_date)
|
||||
im[LL_IM_DATE_TIME] = timestamp; // Retain full date-time for merging chat histories
|
||||
|
||||
if (cut_off_todays_date)
|
||||
{
|
||||
LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp);
|
||||
}
|
||||
|
|
@ -936,9 +975,9 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params
|
|||
im[LL_IM_TIME] = timestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
//timestamp is optional
|
||||
im[LL_IM_TIME] = "";
|
||||
{ //timestamp is optional
|
||||
im[LL_IM_DATE_TIME] = "";
|
||||
im[LL_IM_TIME] = "";
|
||||
}
|
||||
|
||||
bool has_stuff = matches[IDX_STUFF].matched;
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ public:
|
|||
LOG_END
|
||||
};
|
||||
|
||||
static std::string timestamp(bool withdate = false);
|
||||
static std::string timestamp2LogString(U32 timestamp, bool withdate);
|
||||
static std::string makeLogFileName(std::string(filename));
|
||||
static void renameLogFile(const std::string& old_filename, const std::string& new_filename);
|
||||
/**
|
||||
|
|
@ -201,6 +201,7 @@ extern const std::string GROUP_CHAT_SUFFIX;
|
|||
|
||||
// LLSD map lookup constants
|
||||
extern const std::string LL_IM_TIME; //("time");
|
||||
extern const std::string LL_IM_DATE_TIME; //("datetime");
|
||||
extern const std::string LL_IM_TEXT; //("message");
|
||||
extern const std::string LL_IM_FROM; //("from");
|
||||
extern const std::string LL_IM_FROM_ID; //("from_id");
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ void LLHandlerUtil::addNotifPanelToIM(const LLNotificationPtr& notification)
|
|||
LLSD offer;
|
||||
offer["notification_id"] = notification->getID();
|
||||
offer["from"] = SYSTEM_FROM;
|
||||
offer["time"] = LLLogChat::timestamp(false);
|
||||
offer["time"] = LLLogChat::timestamp2LogString(0, false); // Use current time
|
||||
offer["index"] = (LLSD::Integer)session->mMsgs.size();
|
||||
session->mMsgs.push_front(offer);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue