From 0a5de30626a30971cb99387a0624dc19c53f528a Mon Sep 17 00:00:00 2001
From: George Hazan <ghazan@miranda.im>
Date: Sun, 27 Dec 2020 19:03:28 +0300
Subject: Discord: first 100 guild users are fetched from the server

---
 protocols/Discord/src/dispatch.cpp | 55 ++++++++++++++++++++++++++++++++++++++
 protocols/Discord/src/gateway.cpp  | 20 ++++++++++++++
 protocols/Discord/src/guilds.cpp   | 46 +++++--------------------------
 protocols/Discord/src/menus.cpp    |  2 +-
 protocols/Discord/src/proto.h      |  5 ++--
 protocols/Discord/src/version.h    |  2 +-
 6 files changed, 86 insertions(+), 44 deletions(-)

(limited to 'protocols/Discord/src')

diff --git a/protocols/Discord/src/dispatch.cpp b/protocols/Discord/src/dispatch.cpp
index b5094b04ad..1e1704eb4a 100644
--- a/protocols/Discord/src/dispatch.cpp
+++ b/protocols/Discord/src/dispatch.cpp
@@ -39,6 +39,7 @@ static handlers[] = // these structures must me sorted alphabetically
 	{ L"GUILD_CREATE", &CDiscordProto::OnCommandGuildCreated },
 	{ L"GUILD_DELETE", &CDiscordProto::OnCommandGuildDeleted },
 	{ L"GUILD_MEMBER_ADD", &CDiscordProto::OnCommandGuildMemberAdded },
+	{ L"GUILD_MEMBER_LIST_UPDATE", &CDiscordProto::OnCommandGuildMemberListUpdate },
 	{ L"GUILD_MEMBER_REMOVE", &CDiscordProto::OnCommandGuildMemberRemoved },
 	{ L"GUILD_MEMBER_UPDATE", &CDiscordProto::OnCommandGuildMemberUpdated },
 	{ L"GUILD_ROLE_CREATE", &CDiscordProto::OnCommandRoleCreated },
@@ -198,6 +199,60 @@ void CDiscordProto::OnCommandGuildMemberAdded(const JSONNode&)
 {
 }
 
+void CDiscordProto::OnCommandGuildMemberListUpdate(const JSONNode &pRoot)
+{
+	auto *pGuild = FindGuild(::getId(pRoot["guild_id"]));
+	if (pGuild == nullptr)
+		return;
+
+	int iStatus = 0;
+
+	for (auto &ops: pRoot["ops"]) {
+		for (auto &it : ops["items"]) {
+			auto &item = it.at((size_t)0);
+			if (!mir_strcmp(item .name(), "group")) {
+				iStatus = item ["id"].as_string() == "online" ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE;
+				continue;
+			}
+
+			if (!mir_strcmp(item .name(), "member")) {
+				bool bNew = false;
+				CMStringW wszUserId = item["user"]["id"].as_mstring();
+				SnowFlake userId = _wtoi64(wszUserId);
+				CDiscordGuildMember *pm = pGuild->FindUser(userId);
+				if (pm == nullptr) {
+					pm = new CDiscordGuildMember(userId);
+					pGuild->arChatUsers.insert(pm);
+					pm->wszNick = item["user"]["username"].as_mstring() + L"#" + item["user"]["discriminator"].as_mstring();
+					bNew = true;
+				}
+
+				pm->iStatus = iStatus;
+
+				if (bNew)
+					AddGuildUser(pGuild, *pm);
+				else if (iStatus) {
+					GCEVENT gce = { m_szModuleName, 0, GC_EVENT_SETCONTACTSTATUS };
+					gce.time = time(0);
+					gce.pszUID.w = wszUserId;
+					Chat_Event(&gce);
+
+					for (auto &cc : pGuild->arChannels) {
+						if (!cc->bIsGroup)
+							continue;
+						
+						gce.pszID.w = cc->wszChannelName;
+						gce.dwItemData = iStatus;
+						Chat_Event(&gce);
+					}
+				}
+			}
+		}
+	}
+
+	pGuild->bSynced = true;
+}
+
 void CDiscordProto::OnCommandGuildMemberRemoved(const JSONNode &pRoot)
 {
 	CDiscordGuild *pGuild = FindGuild(::getId(pRoot["guild_id"]));
diff --git a/protocols/Discord/src/gateway.cpp b/protocols/Discord/src/gateway.cpp
index c65492569d..1358f57b41 100644
--- a/protocols/Discord/src/gateway.cpp
+++ b/protocols/Discord/src/gateway.cpp
@@ -236,6 +236,26 @@ bool CDiscordProto::GatewayProcess(const JSONNode &pRoot)
 //////////////////////////////////////////////////////////////////////////////////////
 // requests to be sent to a gateway
 
+void CDiscordProto::GatewaySendGuildInfo(CDiscordGuild *pGuild)
+{
+	if (!pGuild->arChannels.getCount())
+		return;
+
+	JSONNode a1(JSON_ARRAY); a1 << INT_PARAM("", 0) << INT_PARAM("", 99);
+
+	CMStringA szId(FORMAT, "%lld", pGuild->arChannels[0]->id);
+	JSONNode chl(JSON_ARRAY); chl.set_name(szId.c_str()); chl << a1;
+
+	JSONNode channels; channels.set_name("channels"); channels << chl;
+
+	JSONNode payload; payload.set_name("d");
+	payload << SINT64_PARAM("guild_id", pGuild->id) << BOOL_PARAM("typing", true) << BOOL_PARAM("activities", true) << BOOL_PARAM("presences", true) << channels;
+		
+	JSONNode root;
+	root << INT_PARAM("op", OPCODE_REQUEST_SYNC_CHANNEL) << payload;
+	GatewaySend(root);
+}
+
 void CDiscordProto::GatewaySendHeartbeat()
 {
 	// we don't send heartbeat packets until we get logged in
diff --git a/protocols/Discord/src/guilds.cpp b/protocols/Discord/src/guilds.cpp
index 8ce9ceac61..71ebd098b4 100644
--- a/protocols/Discord/src/guilds.cpp
+++ b/protocols/Discord/src/guilds.cpp
@@ -173,9 +173,6 @@ void CDiscordProto::ProcessGuild(const JSONNode &pRoot)
 	pGuild->hContact = si->hContact;
 	setId(pGuild->hContact, DB_KEY_CHANNELID, guildId);
 
-	if (!pGuild->bSynced && getByte(si->hContact, "EnableSync"))
-		LoadGuildInfo(pGuild);
-
 	Chat_Control(m_szModuleName, pGuild->wszName, WINDOW_HIDDEN);
 	Chat_Control(m_szModuleName, pGuild->wszName, SESSION_ONLINE);
 
@@ -184,6 +181,12 @@ void CDiscordProto::ProcessGuild(const JSONNode &pRoot)
 
 	BuildStatusList(pGuild, si);
 
+	for (auto &it : pRoot["channels"])
+		ProcessGuildChannel(pGuild, it);
+
+	if (!pGuild->bSynced && getByte(si->hContact, "EnableSync"))
+		GatewaySendGuildInfo(pGuild);
+
 	// store all guild members
 	for (auto &it : pRoot["members"]) {
 		CMStringW wszUserId = it["user"]["id"].as_mstring();
@@ -222,9 +225,6 @@ void CDiscordProto::ProcessGuild(const JSONNode &pRoot)
 	for (auto &it : pGuild->arChatUsers)
 		AddGuildUser(pGuild, *it);
 
-	for (auto &it : pRoot["channels"])
-		ProcessGuildChannel(pGuild, it);
-
 	if (m_bUseGroupchats)
 		ForkThread(&CDiscordProto::BatchChatCreate, pGuild);
 
@@ -311,37 +311,3 @@ void CDiscordProto::AddGuildUser(CDiscordGuild *pGuild, const CDiscordGuildMembe
 	if (pUser.userId == m_ownId)
 		pGuild->pParentSi->pMe = pu;
 }
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static CMStringW GetCacheFileName(SnowFlake guildId)
-{
-	VARSW wszCacheDir(L"%miranda_userdata%\\Discord");
-	CreateDirectoryTreeW(wszCacheDir);
-
-	return CMStringW(FORMAT, L"%s\\%lld.cache", wszCacheDir.get(), guildId);
-}
-
-void CDiscordProto::LoadGuildInfo(CDiscordGuild *pGuild)
-{
-	CMStringW wszCacheFile(GetCacheFileName(pGuild->id));
-	int fileId = _wopen(wszCacheFile, _O_BINARY | _O_RDONLY);
-	if (fileId != -1) {
-		size_t length = _filelength(fileId);
-		ptrA buf((char *)mir_alloc(length+1));
-		int result = _read(fileId, buf, (unsigned)length);
-		_close(fileId);
-		if (result == -1)
-			return;
-
-		JSONNode root(JSONNode::parse(buf));
-		for (auto &cc : root) {
-			auto *pUser = new CDiscordGuildMember(_wtoi64(cc["id"].as_mstring()));
-			pUser->wszNick = cc["nick"].as_mstring();
-			pUser->wszRole = cc["role"].as_mstring();
-			pGuild->arChatUsers.insert(pUser);
-
-			AddGuildUser(pGuild, *pUser);
-		}
-	}
-}
diff --git a/protocols/Discord/src/menus.cpp b/protocols/Discord/src/menus.cpp
index 59dcc683a5..d4762e649c 100644
--- a/protocols/Discord/src/menus.cpp
+++ b/protocols/Discord/src/menus.cpp
@@ -95,7 +95,7 @@ INT_PTR CDiscordProto::OnMenuToggleSync(WPARAM hContact, LPARAM)
 
 	if (bEnabled)
 		if (auto *pGuild = FindGuild(getId(hContact, DB_KEY_CHANNELID)))
-			LoadGuildInfo(pGuild);
+			GatewaySendGuildInfo(pGuild);
 	return 0;
 }
 
diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h
index 8b43d85aeb..5943e01500 100644
--- a/protocols/Discord/src/proto.h
+++ b/protocols/Discord/src/proto.h
@@ -234,6 +234,7 @@ class CDiscordProto : public PROTO<CDiscordProto>
 	void  GatewaySend(const JSONNode &pNode);
 	bool  GatewayProcess(const JSONNode &pNode);
 
+	void  GatewaySendGuildInfo(CDiscordGuild *pGuild);
 	void  GatewaySendHeartbeat(void);
 	void  GatewaySendIdentify(void);
 	void  GatewaySendResume(void);
@@ -299,8 +300,7 @@ class CDiscordProto : public PROTO<CDiscordProto>
 	}
 
 	void AddGuildUser(CDiscordGuild *guild, const CDiscordGuildMember &pUser);
-	void LoadGuildInfo(CDiscordGuild *guild);
-
+	
 	void ProcessGuild(const JSONNode &json);
 	CDiscordUser* ProcessGuildChannel(CDiscordGuild *guild, const JSONNode &json);
 	void ProcessPresence(const JSONNode &json);
@@ -398,6 +398,7 @@ public:
 	void OnCommandGuildCreated(const JSONNode &json);
 	void OnCommandGuildDeleted(const JSONNode &json);
 	void OnCommandGuildMemberAdded(const JSONNode &json);
+	void OnCommandGuildMemberListUpdate(const JSONNode &json);
 	void OnCommandGuildMemberRemoved(const JSONNode &json);
 	void OnCommandGuildMemberUpdated(const JSONNode &json);
 	void OnCommandFriendAdded(const JSONNode &json);
diff --git a/protocols/Discord/src/version.h b/protocols/Discord/src/version.h
index 00d257e060..2691f79ea8 100644
--- a/protocols/Discord/src/version.h
+++ b/protocols/Discord/src/version.h
@@ -1,7 +1,7 @@
 #define __MAJOR_VERSION            0
 #define __MINOR_VERSION            6
 #define __RELEASE_NUM              2
-#define __BUILD_NUM                5
+#define __BUILD_NUM                6
 
 #include <stdver.h>
 
-- 
cgit v1.2.3