From 0fee7f62bbd255e643b5e07e7c085e4d19503e3d Mon Sep 17 00:00:00 2001 From: George Hazan Date: Tue, 11 Jul 2023 15:30:59 +0300 Subject: for #3566 - getUserLastseen to detect offline status --- protocols/ICQ-WIM/src/poll.cpp | 18 +--- protocols/ICQ-WIM/src/proto.cpp | 36 +++++++ protocols/ICQ-WIM/src/proto.h | 205 +++++++++++++++++++++------------------- protocols/ICQ-WIM/src/utils.cpp | 35 ++++++- 4 files changed, 179 insertions(+), 115 deletions(-) (limited to 'protocols') diff --git a/protocols/ICQ-WIM/src/poll.cpp b/protocols/ICQ-WIM/src/poll.cpp index f48be9b2f9..fbcfd178c0 100644 --- a/protocols/ICQ-WIM/src/poll.cpp +++ b/protocols/ICQ-WIM/src/poll.cpp @@ -332,22 +332,8 @@ void CIcqProto::ProcessPresence(const JSONNode &ev) return; int iNewStatus = StatusFromPresence(ev, pUser->m_hContact); - if (iNewStatus == -1) - iNewStatus = ID_STATUS_OFFLINE; - - // major crutch dedicated to the official client behaviour to go offline - // when its window gets closed. we change the status of a contact to the - // first chosen one from options and initialize a timer - if (iNewStatus == ID_STATUS_OFFLINE) { - if (m_iTimeDiff1) { - iNewStatus = m_iStatus1; - pUser->m_timer1 = time(0); - } - } - // if a client returns back online, we clear timers not to play with statuses anymore - else pUser->m_timer1 = pUser->m_timer2 = 0; - - setWord(pUser->m_hContact, "Status", iNewStatus); + if (iNewStatus != -1) + ProcessStatus(pUser, iNewStatus); Json2string(pUser->m_hContact, ev, "friendly", "Nick", true); CheckAvatarChange(pUser->m_hContact, ev); diff --git a/protocols/ICQ-WIM/src/proto.cpp b/protocols/ICQ-WIM/src/proto.cpp index 09496b46c4..f6a605d51e 100644 --- a/protocols/ICQ-WIM/src/proto.cpp +++ b/protocols/ICQ-WIM/src/proto.cpp @@ -45,6 +45,7 @@ CIcqProto::CIcqProto(const char *aProtoName, const wchar_t *aUserName) : m_arOwnIds(1, PtrKeySortT), m_arCache(20, &CompareCache), m_arGroups(10, NumericKeySortT), + m_arLastSeenQueue(10, NumericKeySortT), m_arMarkReadQueue(10, NumericKeySortT), m_evRequestsQueue(CreateEvent(nullptr, FALSE, FALSE, nullptr)), m_szOwnId(this, DB_KEY_ID), @@ -304,6 +305,41 @@ INT_PTR CIcqProto::GotoInbox(WPARAM, LPARAM) ///////////////////////////////////////////////////////////////////////////////////////// +void CIcqProto::OnLastSeen(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *) +{ + RobustReply root(pReply); + if (root.error() != 20000) + return; + + const JSONNode &results = root.results(); + for (auto &it : results["entries"]) { + if (auto *pUser = FindUser(it["sn"].as_mstring())) { + int iLastSeen = it["lastseen"].as_int(); + if (iLastSeen != 0) { + setDword(pUser->m_hContact, DB_KEY_LASTSEEN, iLastSeen); + ProcessStatus(pUser, ID_STATUS_OFFLINE); + } + else ProcessStatus(pUser, ID_STATUS_ONLINE); + } + } +} + +void CIcqProto::SendLastSeen() +{ + mir_cslock lck(m_csLastSeenQueue); + + auto *pReq = new AsyncRapiRequest(this, "getUserLastseen", &CIcqProto::OnLastSeen); + JSONNode ids(JSON_ARRAY); ids.set_name("ids"); + for (auto &it: m_arLastSeenQueue) + ids << WCHAR_PARAM("", GetUserId(it->m_hContact)); + pReq->params.push_back(ids); + Push(pReq); + + m_arLastSeenQueue.destroy(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + void CIcqProto::SendMarkRead() { mir_cslock lck(m_csMarkReadQueue); diff --git a/protocols/ICQ-WIM/src/proto.h b/protocols/ICQ-WIM/src/proto.h index 018a4e1399..a10f27c065 100644 --- a/protocols/ICQ-WIM/src/proto.h +++ b/protocols/ICQ-WIM/src/proto.h @@ -170,7 +170,7 @@ class CIcqProto : public PROTO friend class CIcqProto; CIcqProto &m_proto; - CTimer m_heartBeat, m_markRead; + CTimer m_heartBeat, m_markRead, m_lastSeen; void OnHeartBeat(CTimer *) { m_proto.CheckStatus(); @@ -181,12 +181,19 @@ class CIcqProto : public PROTO pTimer->Stop(); } + void OnLastSeen(CTimer *pTimer) { + m_proto.SendLastSeen(); + pTimer->Stop(); + } + CIcqProtoImpl(CIcqProto &pro) : m_proto(pro), m_markRead(Miranda_GetSystemWindow(), UINT_PTR(this)), - m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this) + 1) + m_lastSeen(Miranda_GetSystemWindow(), UINT_PTR(this) + 1), + m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this) + 2) { m_markRead.OnEvent = Callback(this, &CIcqProtoImpl::OnMarkRead); + m_lastSeen.OnEvent = Callback(this, &CIcqProtoImpl::OnLastSeen); m_heartBeat.OnEvent = Callback(this, &CIcqProtoImpl::OnHeartBeat); } } m_impl; @@ -200,106 +207,112 @@ class CIcqProto : public PROTO friend AsyncHttpRequest* operator <<(AsyncHttpRequest*, const AIMSID&); - bool m_bOnline, m_bTerminated, m_bFirstBos, m_isMra, m_bError462; - int m_iTimeShift; - - MCONTACT CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove); - void CheckPassword(void); - void ConnectionFailed(int iReason, int iErrorCode = 0); - void EmailNotification(const wchar_t *pwszText); - void GetPermitDeny(); - wchar_t* GetUIN(MCONTACT hContact); - void MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup); - bool RetrievePassword(); - void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead); - void RetrieveUserInfo(MCONTACT hContact); - void SendMrimLogin(NETLIBHTTPREQUEST *pReply); - void SetServerStatus(int iNewStatus); - void ShutdownSession(void); - void StartSession(void); - - void CheckAvatarChange(MCONTACT hContact, const JSONNode&); - IcqFileInfo* CheckFile(MCONTACT hContact, CMStringW &wszFileName, bool &bIsFile); - void CheckLastId(MCONTACT hContact, const JSONNode&); - void Json2int(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial); - void Json2string(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial); - MCONTACT ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact = INVALID_CONTACT_ID, bool bIsPartial = false); - void ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &msg, bool bCreateRead, bool bLocalTime); - int StatusFromPresence(const JSONNode &presence, MCONTACT hContact); - - void OnLoggedIn(void); - void OnLoggedOut(void); - - mir_cs m_csMarkReadQueue; + bool m_bOnline, m_bTerminated, m_bFirstBos, m_isMra, m_bError462; + int m_iTimeShift; + + MCONTACT CheckOwnMessage(const CMStringA &reqId, const CMStringA &msgId, bool bRemove); + void CheckPassword(void); + void ConnectionFailed(int iReason, int iErrorCode = 0); + void EmailNotification(const wchar_t *pwszText); + void GetPermitDeny(); + wchar_t* GetUIN(MCONTACT hContact); + void MoveContactToGroup(MCONTACT hContact, const wchar_t *pwszGroup, const wchar_t *pwszNewGroup); + bool RetrievePassword(); + void RetrieveUserHistory(MCONTACT, __int64 startMsgId, bool bCreateRead); + void RetrieveUserInfo(MCONTACT hContact); + void SendMrimLogin(NETLIBHTTPREQUEST *pReply); + void SetServerStatus(int iNewStatus); + void ShutdownSession(void); + void StartSession(void); + + void CheckAvatarChange(MCONTACT hContact, const JSONNode&); + IcqFileInfo* CheckFile(MCONTACT hContact, CMStringW &wszFileName, bool &bIsFile); + void CheckLastId(MCONTACT hContact, const JSONNode&); + void Json2int(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial); + void Json2string(MCONTACT, const JSONNode&, const char *szJson, const char *szSetting, bool bIsPartial); + MCONTACT ParseBuddyInfo(const JSONNode &buddy, MCONTACT hContact = INVALID_CONTACT_ID, bool bIsPartial = false); + void ParseMessage(MCONTACT hContact, __int64 &lastMsgId, const JSONNode &msg, bool bCreateRead, bool bLocalTime); + int StatusFromPresence(const JSONNode &presence, MCONTACT hContact); + void ProcessStatus(IcqUser *pUser, int iStatus); + + void OnLoggedIn(void); + void OnLoggedOut(void); + + mir_cs m_csMarkReadQueue; LIST m_arMarkReadQueue; - void SendMarkRead(); - - __int64 getId(MCONTACT hContact, const char *szSetting); - void setId(MCONTACT hContact, const char *szSetting, __int64 iValue); - - void OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - void OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); - - void ProcessBuddyList(const JSONNode &pRoot); - void ProcessDiff(const JSONNode &pRoot); - void ProcessEvent(const JSONNode &pRoot); - void ProcessGroupChat(const JSONNode &pRoot); - void ProcessHistData(const JSONNode &pRoot); - void ProcessImState(const JSONNode &pRoot); - void ProcessMyInfo(const JSONNode &pRoot); - void ProcessNotification(const JSONNode &pRoot); - void ProcessPermissions(const JSONNode &pRoot); - void ProcessPresence(const JSONNode &pRoot); - void ProcessSessionEnd(const JSONNode &pRoot); - void ProcessTyping(const JSONNode &pRoot); - - IcqConn m_ConnPool[CONN_LAST]; - CMStringA m_szPassword; - CMStringA m_szSessionKey; - CMStringA m_szAToken; - CMStringA m_szRToken; - CMStringA m_fetchBaseURL; - CMStringA m_aimsid; - CMStringA m_szMraCookie; - LONG m_msgId = 1; - int m_iRClientId; - HGENMENU m_hUploadGroups; - - mir_cs m_csOwnIds; + void SendMarkRead(); + + mir_cs m_csLastSeenQueue; + LIST m_arLastSeenQueue; + void SendLastSeen(); + + __int64 getId(MCONTACT hContact, const char *szSetting); + void setId(MCONTACT hContact, const char *szSetting, __int64 iValue); + + void OnAddBuddy(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnAddClient(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnCheckMraAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnCheckMraAuthFinal(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnCheckMrimLogin(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnCheckPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnFetchEvents(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnFileContinue(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnFileInit(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnFileInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnFileRecv(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnGenToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnGetPermitDeny(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnGetSticker(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnGetUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnLastSeen(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnLoginViaPhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnNormalizePhone(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnReceiveAvatar(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnSearchResults(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnSendMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnSessionEnd(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnStartSession(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + void OnValidateSms(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq); + + void ProcessBuddyList(const JSONNode &pRoot); + void ProcessDiff(const JSONNode &pRoot); + void ProcessEvent(const JSONNode &pRoot); + void ProcessGroupChat(const JSONNode &pRoot); + void ProcessHistData(const JSONNode &pRoot); + void ProcessImState(const JSONNode &pRoot); + void ProcessMyInfo(const JSONNode &pRoot); + void ProcessNotification(const JSONNode &pRoot); + void ProcessPermissions(const JSONNode &pRoot); + void ProcessPresence(const JSONNode &pRoot); + void ProcessSessionEnd(const JSONNode &pRoot); + void ProcessTyping(const JSONNode &pRoot); + + IcqConn m_ConnPool[CONN_LAST]; + CMStringA m_szPassword; + CMStringA m_szSessionKey; + CMStringA m_szAToken; + CMStringA m_szRToken; + CMStringA m_fetchBaseURL; + CMStringA m_aimsid; + CMStringA m_szMraCookie; + LONG m_msgId = 1; + int m_iRClientId; + HGENMENU m_hUploadGroups; + + mir_cs m_csOwnIds; OBJLIST m_arOwnIds; OBJLIST m_arGroups; - int m_unreadEmails = -1; - CMStringA m_szMailBox; + int m_unreadEmails = -1; + CMStringA m_szMailBox; - bool m_bIgnoreListEmpty = true; - bool m_bRememberPwd; // store password in a database - bool m_bDlgActive; + bool m_bIgnoreListEmpty = true; + bool m_bRememberPwd; // store password in a database + bool m_bDlgActive; //////////////////////////////////////////////////////////////////////////////////////// // group chats diff --git a/protocols/ICQ-WIM/src/utils.cpp b/protocols/ICQ-WIM/src/utils.cpp index 4c5d494115..5adde04e81 100644 --- a/protocols/ICQ-WIM/src/utils.cpp +++ b/protocols/ICQ-WIM/src/utils.cpp @@ -281,6 +281,8 @@ bool IsValidType(const JSONNode &n) return type == "icq" || type == "aim" || type == "interop" || type == ""; } +///////////////////////////////////////////////////////////////////////////////////////// + int CIcqProto::StatusFromPresence(const JSONNode &presence, MCONTACT hContact) { CMStringW wszStatus = presence["state"].as_mstring(); @@ -298,17 +300,44 @@ int CIcqProto::StatusFromPresence(const JSONNode &presence, MCONTACT hContact) else if (wszStatus == L"dnd") iStatus = ID_STATUS_DND; else - iStatus = -1; + return -1; int iLastSeen = presence["lastseen"].as_int(); if (iLastSeen != 0) setDword(hContact, DB_KEY_LASTSEEN, iLastSeen); - else if (getDword(hContact, DB_KEY_ONLINETS)) - iStatus = ID_STATUS_ONLINE; + else { + if (getDword(hContact, DB_KEY_ONLINETS)) + iStatus = ID_STATUS_ONLINE; + else { + if (auto *pUser = FindUser(GetUserId(hContact))) { + mir_cslock lck(m_csLastSeenQueue); + m_arLastSeenQueue.insert(pUser); + m_impl.m_lastSeen.Start(500); + } + return -1; + } + } return iStatus; } +void CIcqProto::ProcessStatus(IcqUser *pUser, int iStatus) +{ + // major crutch dedicated to the official client behaviour to go offline + // when its window gets closed. we change the status of a contact to the + // first chosen one from options and initialize a timer + if (iStatus == ID_STATUS_OFFLINE) { + if (m_iTimeDiff1) { + iStatus = m_iStatus1; + pUser->m_timer1 = time(0); + } + } + // if a client returns back online, we clear timers not to play with statuses anymore + else pUser->m_timer1 = pUser->m_timer2 = 0; + + setWord(pUser->m_hContact, "Status", iStatus); +} + ///////////////////////////////////////////////////////////////////////////////////////// __int64 CIcqProto::getId(MCONTACT hContact, const char *szSetting) -- cgit v1.2.3