From e940fb548345fb5e926a0d87f8024257c692726d Mon Sep 17 00:00:00 2001
From: George Hazan <ghazan@miranda.im>
Date: Wed, 22 Feb 2017 13:29:14 +0300
Subject: Discord: a rusty crutch to receive updates for sent messages

---
 protocols/Discord/src/dispatch.cpp | 48 ++++++++++++++++-------------
 protocols/Discord/src/proto.cpp    |  4 +--
 protocols/Discord/src/proto.h      | 19 +++++++++---
 protocols/Discord/src/server.cpp   | 62 +++-----------------------------------
 protocols/Discord/src/version.h    |  2 +-
 5 files changed, 49 insertions(+), 86 deletions(-)

(limited to 'protocols/Discord')

diff --git a/protocols/Discord/src/dispatch.cpp b/protocols/Discord/src/dispatch.cpp
index e43b70d0fc..46af932fbf 100644
--- a/protocols/Discord/src/dispatch.cpp
+++ b/protocols/Discord/src/dispatch.cpp
@@ -79,7 +79,7 @@ void CDiscordProto::OnCommandChannelCreated(const JSONNode &pRoot)
 	const JSONNode &members = pRoot["recipients"];
 	for (auto it = members.begin(); it != members.end(); ++it) {
 		CDiscordUser *pUser = PrepareUser(*it);
-		pUser->lastMessageId = ::getId(pRoot["last_message_id"]);
+		pUser->lastMsg = CDiscordMessage(::getId(pRoot["last_message_id"]));
 		pUser->channelId = ::getId(pRoot["id"]);
 		setId(pUser->hContact, DB_KEY_CHANNELID, pUser->channelId);
 	}
@@ -89,7 +89,8 @@ void CDiscordProto::OnCommandChannelDeleted(const JSONNode &pRoot)
 {
 	CDiscordUser *pUser = FindUserByChannel(::getId(pRoot["id"]));
 	if (pUser != NULL) {
-		pUser->channelId = pUser->lastMessageId = 0;
+		pUser->channelId = 0;
+		pUser->lastMsg = CDiscordMessage();
 		delSetting(pUser->hContact, DB_KEY_CHANNELID);
 	}
 }
@@ -100,7 +101,7 @@ void CDiscordProto::OnCommandChannelUpdated(const JSONNode &pRoot)
 	if (pUser == NULL)
 		return;
 
-	pUser->lastMessageId = ::getId(pRoot["last_message_id"]);
+	pUser->lastMsg = CDiscordMessage(::getId(pRoot["last_message_id"]));
 
 	CMStringW wszTopic = pRoot["topic"].as_mstring();
 	if (!wszTopic.IsEmpty()) {
@@ -235,14 +236,14 @@ void CDiscordProto::ProcessGuild(const JSONNode &readState, const JSONNode &p)
 		}
 		pUser->wszUsername = wszChannelId;
 		pUser->guildId = guildId;
-		pUser->lastMessageId = ::getId(pch["last_message_id"]);
+		pUser->lastMsg = CDiscordMessage(::getId(pch["last_message_id"]));
 		pUser->lastReadId = sttGetLastRead(readState, wszChannelId);
 
 		setId(pUser->hContact, DB_KEY_ID, channelId);
 		setId(pUser->hContact, DB_KEY_CHANNELID, channelId);
 
 		SnowFlake oldMsgId = getId(pUser->hContact, DB_KEY_LASTMSGID);
-		if (oldMsgId != 0 && pUser->lastMessageId > oldMsgId)
+		if (oldMsgId != 0 && pUser->lastMsg.id > oldMsgId)
 			RetrieveHistory(pUser->hContact, MSG_AFTER, oldMsgId, 99);
 	}
 }
@@ -419,29 +420,32 @@ void CDiscordProto::OnCommandRoleDeleted(const JSONNode &pRoot)
 
 void CDiscordProto::OnCommandMessage(const JSONNode &pRoot)
 {
-	PROTORECVEVENT recv = {};
 	CMStringW wszMessageId = pRoot["id"].as_mstring();
-	SnowFlake messageId = _wtoi64(wszMessageId);
-	SnowFlake nonce = ::getId(pRoot["nonce"]);
+	CMStringW wszUserId = pRoot["author"]["id"].as_mstring();
+	CDiscordMessage msg(_wtoi64(wszMessageId), _wtoi64(wszUserId));
 
+	SnowFlake nonce = ::getId(pRoot["nonce"]);
 	SnowFlake *p = arOwnMessages.find(&nonce);
 	if (p != NULL) { // own message? skip it
-		debugLogA("skipping own message with nonce=%lld, id=%lld", nonce, messageId);
+		debugLogA("skipping own message with nonce=%lld, id=%lld", nonce, msg.id);
 		return;
 	}
 
 	// try to find a sender by his channel
-	CMStringW wszChannelId = pRoot["channel_id"].as_mstring();
-	SnowFlake channelId = _wtoi64(wszChannelId);
+	SnowFlake channelId = ::getId(pRoot["channel_id"]);
 	CDiscordUser *pUser = FindUserByChannel(channelId);
 	if (pUser == NULL) {
 		debugLogA("skipping message with unknown channel id=%lld", channelId);
 		return;
 	}
 
+	// restore partially received updated message
+	if (pUser->lastMsg.id == msg.id)
+		msg = pUser->lastMsg;
+
 	// if a message has myself as an author, add some flags
-	CMStringW wszUserId = pRoot["author"]["id"].as_mstring();
-	if (_wtoi64(wszUserId) == m_ownId)
+	PROTORECVEVENT recv = {};
+	if (msg.authorId == m_ownId)
 		recv.flags = PREF_CREATEREAD | PREF_SENT;
 
 	CMStringW wszText = PrepareMessageText(pRoot);
@@ -461,7 +465,7 @@ void CDiscordProto::OnCommandMessage(const JSONNode &pRoot)
 	else {
 		debugLogA("store a message into the group channel id %lld", channelId);
 
-		SESSION_INFO *si = pci->SM_FindSession(wszChannelId, m_szModuleName);
+		SESSION_INFO *si = pci->SM_FindSession(pUser->wszUsername, m_szModuleName);
 		if (si == NULL) {
 			debugLogA("nessage to unknown channal %lld ignored", channelId);
 			return;
@@ -469,19 +473,21 @@ void CDiscordProto::OnCommandMessage(const JSONNode &pRoot)
 
 		ParseSpecialChars(si, wszText);
 
-		GCDEST gcd = { m_szModuleName, wszChannelId, GC_EVENT_MESSAGE };
+		GCDEST gcd = { m_szModuleName, pUser->wszUsername, GC_EVENT_MESSAGE };
 		GCEVENT gce = { &gcd };
 		gce.dwFlags = GCEF_ADDTOLOG;
 		gce.ptszUID = wszUserId;
 		gce.ptszText = wszText;
 		gce.time = (DWORD)StringToDate(pRoot["timestamp"].as_mstring());
-		gce.bIsMe = _wtoi64(wszUserId) == m_ownId;
+		gce.bIsMe = msg.authorId == m_ownId;
 		Chat_Event(&gce);
 	}
 
+	pUser->lastMsg = msg;
+
 	SnowFlake lastId = getId(pUser->hContact, DB_KEY_LASTMSGID); // as stored in a database
-	if (lastId < messageId)
-		setId(pUser->hContact, DB_KEY_LASTMSGID, messageId);
+	if (lastId < msg.id)
+		setId(pUser->hContact, DB_KEY_LASTMSGID, msg.id);
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
@@ -491,7 +497,7 @@ void CDiscordProto::OnCommandMessageAck(const JSONNode &pRoot)
 {
 	CDiscordUser *pUser = FindUserByChannel(pRoot["channel_id"]);
 	if (pUser != NULL)
-		pUser->lastMessageId = ::getId(pRoot["message_id"]);
+		pUser->lastMsg = CDiscordMessage(::getId(pRoot["message_id"]));
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////
@@ -568,14 +574,14 @@ void CDiscordProto::OnCommandReady(const JSONNode &pRoot)
 		
 		CMStringW wszChannelId = p["id"].as_mstring();
 		pUser->channelId = _wtoi64(wszChannelId);
-		pUser->lastMessageId = ::getId(p["last_message_id"]);
+		pUser->lastMsg = CDiscordMessage(::getId(p["last_message_id"]));
 		pUser->lastReadId = sttGetLastRead(readState, wszChannelId);
 		pUser->bIsPrivate = true;
 
 		setId(pUser->hContact, DB_KEY_CHANNELID, pUser->channelId);
 
 		SnowFlake oldMsgId = getId(pUser->hContact, DB_KEY_LASTMSGID);
-		if (pUser->lastMessageId > oldMsgId)
+		if (pUser->lastMsg.id > oldMsgId)
 			RetrieveHistory(pUser->hContact, MSG_AFTER, oldMsgId, 99);
 	}
 }
diff --git a/protocols/Discord/src/proto.cpp b/protocols/Discord/src/proto.cpp
index d4ac7c11da..80331edbda 100644
--- a/protocols/Discord/src/proto.cpp
+++ b/protocols/Discord/src/proto.cpp
@@ -72,7 +72,7 @@ CDiscordProto::CDiscordProto(const char *proto_name, const wchar_t *username) :
 		CDiscordUser *pNew = new CDiscordUser(getId(hContact, DB_KEY_ID));
 		pNew->hContact = hContact;
 		pNew->channelId = getId(hContact, DB_KEY_CHANNELID);
-		pNew->lastMessageId = getId(hContact, DB_KEY_LASTMSGID);
+		pNew->lastMsg.id = getId(hContact, DB_KEY_LASTMSGID);
 		pNew->wszUsername = ptrW(getWStringA(hContact, DB_KEY_NICK));
 		pNew->iDiscriminator = getDword(hContact, DB_KEY_DISCR);
 		arUsers.insert(pNew);
@@ -413,7 +413,7 @@ void CDiscordProto::MarkReadTimerProc(HWND hwnd, UINT, UINT_PTR id, DWORD)
 	mir_cslock lck(ppro->csMarkReadQueue);
 	while (ppro->arMarkReadQueue.getCount()) {
 		CDiscordUser *pUser = ppro->arMarkReadQueue[0];
-		CMStringA szUrl(FORMAT, "/channels/%lld/messages/%lld/ack", pUser->channelId, pUser->lastMessageId);
+		CMStringA szUrl(FORMAT, "/channels/%lld/messages/%lld/ack", pUser->channelId, pUser->lastMsg.id);
 		ppro->Push(new AsyncHttpRequest(ppro, REQUEST_POST, szUrl, nullptr));
 		ppro->arMarkReadQueue.remove(0);
 	}
diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h
index fbe1b9d2b3..9ab553e5a4 100644
--- a/protocols/Discord/src/proto.h
+++ b/protocols/Discord/src/proto.h
@@ -92,6 +92,18 @@ struct CDiscordRole : public MZeroedObject
 
 /////////////////////////////////////////////////////////////////////////////////////////
 
+struct CDiscordMessage
+{
+	SnowFlake id, authorId;
+
+	CDiscordMessage(SnowFlake _id = 0, SnowFlake _authorId = 0) :
+		id(_id),
+		authorId(_authorId)
+	{}
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
 enum CDiscordHistoryOp
 {
 	MSG_NOFILTER, MSG_AFTER, MSG_BEFORE
@@ -108,9 +120,11 @@ struct CDiscordUser : public MZeroedObject
 
 	SnowFlake guildId;
 	SnowFlake channelId;
-	SnowFlake lastMessageId, lastReadId;
+	SnowFlake lastReadId;
 	bool      bIsPrivate;
 
+	CDiscordMessage lastMsg;
+
 	CMStringW wszUsername;
 	int       iDiscriminator;
 };
@@ -303,11 +317,8 @@ public:
 
 	void OnReceiveCreateChannel(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
 	void OnReceiveAuth(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
-	void OnReceiveChannels(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
 	void OnReceiveFile(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
-	void OnReceiveFriends(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
 	void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
-	void OnReceiveGuilds(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
 	void OnReceiveMessage(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
 	void OnReceiveMessageAck(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
 	void OnReceiveToken(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
diff --git a/protocols/Discord/src/server.cpp b/protocols/Discord/src/server.cpp
index 1765d2b610..169827044c 100644
--- a/protocols/Discord/src/server.cpp
+++ b/protocols/Discord/src/server.cpp
@@ -134,7 +134,7 @@ void CDiscordProto::OnReceiveHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest
 	setId(pUser->hContact, DB_KEY_LASTMSGID, lastId);
 
 	// if we fetched 99 messages, but have smth more to go, continue fetching
-	if (iNumMessages == 99 && lastId < pUser->lastMessageId)
+	if (iNumMessages == 99 && lastId < pUser->lastMsg.id)
 		RetrieveHistory(pUser->hContact, MSG_AFTER, lastId, 99);
 }
 
@@ -263,59 +263,6 @@ void CDiscordProto::OnReceiveCreateChannel(NETLIBHTTPREQUEST *pReply, AsyncHttpR
 		OnCommandChannelCreated(root);
 }
 
-void CDiscordProto::OnReceiveChannels(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
-{
-	if (pReply->resultCode != 200)
-		return;
-
-	JSONNode root = JSONNode::parse(pReply->pData);
-	if (!root)
-		return;
-
-	for (auto it = root.begin(); it != root.end(); ++it) {
-		const JSONNode &p = *it;
-
-		const JSONNode &user = p["recipient"];
-		if (!user)
-			continue;
-
-		CDiscordUser *pUser = PrepareUser(user);
-		pUser->lastMessageId = ::getId(p["last_message_id"]);
-		pUser->channelId = ::getId(p["id"]);
-		pUser->bIsPrivate = p["is_private"].as_bool();
-
-		setId(pUser->hContact, DB_KEY_CHANNELID, pUser->channelId);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CDiscordProto::OnReceiveFriends(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
-{
-	if (pReply->resultCode != 200)
-		return;
-
-	JSONNode root = JSONNode::parse(pReply->pData);
-	if (!root)
-		return;
-
-	for (auto it = root.begin(); it != root.end(); ++it) {
-		JSONNode &p = *it;
-
-		JSONNode &user = p["user"];
-		if (user)
-			PrepareUser(user);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CDiscordProto::OnReceiveGuilds(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)
-{
-	if (pReply->resultCode != 200)
-		return;
-}
-
 /////////////////////////////////////////////////////////////////////////////////////////
 
 void CDiscordProto::OnReceiveMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)
@@ -328,10 +275,9 @@ void CDiscordProto::OnReceiveMessage(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest
 
 	JSONNode root = JSONNode::parse(pReply->pData);
 	if (root) {
-		SnowFlake newLastId = ::getId(root["id"]);
-		SnowFlake oldLastId = getId(hContact, DB_KEY_LASTMSGID); // as stored in a database
-		if (oldLastId < newLastId)
-			setId(hContact, DB_KEY_LASTMSGID, newLastId);
+		CDiscordUser *pUser = FindUserByChannel(::getId(root["channel_id"]));
+		if (pUser != nullptr)
+			pUser->lastMsg = CDiscordMessage(::getId(root["id"]), ::getId(root["author"]["id"]));
 	}
 
 	ProtoBroadcastAck(hContact, ACKTYPE_MESSAGE, bSucceeded ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)pReq->m_iReqNum, 0);
diff --git a/protocols/Discord/src/version.h b/protocols/Discord/src/version.h
index 1232d84168..ebddf302fd 100644
--- a/protocols/Discord/src/version.h
+++ b/protocols/Discord/src/version.h
@@ -1,7 +1,7 @@
 #define __MAJOR_VERSION            0
 #define __MINOR_VERSION            4
 #define __RELEASE_NUM              0
-#define __BUILD_NUM                2
+#define __BUILD_NUM                3
 
 #include <stdver.h>
 
-- 
cgit v1.2.3