diff options
| -rw-r--r-- | protocols/Discord/res/discord.rc | 9 | ||||
| -rw-r--r-- | protocols/Discord/src/connection.cpp | 3 | ||||
| -rw-r--r-- | protocols/Discord/src/options.cpp | 4 | ||||
| -rw-r--r-- | protocols/Discord/src/proto.cpp | 145 | ||||
| -rw-r--r-- | protocols/Discord/src/proto.h | 55 | ||||
| -rw-r--r-- | protocols/Discord/src/resource.h | 4 | ||||
| -rw-r--r-- | protocols/Discord/src/server.cpp | 88 | ||||
| -rw-r--r-- | protocols/Discord/src/stdafx.h | 11 | ||||
| -rw-r--r-- | protocols/Discord/src/utils.cpp | 70 | ||||
| -rw-r--r-- | protocols/Discord/src/version.h | 2 | 
10 files changed, 349 insertions, 42 deletions
| diff --git a/protocols/Discord/res/discord.rc b/protocols/Discord/res/discord.rc index 41366bcd7b..fc31363075 100644 --- a/protocols/Discord/res/discord.rc +++ b/protocols/Discord/res/discord.rc @@ -76,6 +76,15 @@ BEGIN      EDITTEXT        IDC_GROUP,84,89,123,13,ES_AUTOHSCROLL  END +IDD_EXTSEARCH DIALOGEX 0, 0, 114, 55 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +EXSTYLE WS_EX_TRANSPARENT | WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +    LTEXT           "Nick:",IDC_STATIC,6,7,99,8 +    EDITTEXT        IDC_NICK,3,18,103,12,0,WS_EX_CLIENTEDGE +END +  #endif    // English (United States) resources  ///////////////////////////////////////////////////////////////////////////// diff --git a/protocols/Discord/src/connection.cpp b/protocols/Discord/src/connection.cpp index df3dbfa440..7b2cc097c9 100644 --- a/protocols/Discord/src/connection.cpp +++ b/protocols/Discord/src/connection.cpp @@ -66,6 +66,7 @@ void CDiscordProto::OnLoggedIn()  	Push(new AsyncHttpRequest(this, REQUEST_GET, "/users/@me/guilds", &CDiscordProto::OnReceiveGuilds));  	Push(new AsyncHttpRequest(this, REQUEST_GET, "/users/@me/channels", &CDiscordProto::OnReceiveChannels)); +	Push(new AsyncHttpRequest(this, REQUEST_GET, "/users/@me/relationships", &CDiscordProto::OnReceiveFriends));  }  void CDiscordProto::OnLoggedOut() @@ -109,7 +110,7 @@ void CDiscordProto::ServerThread(void*)  	if (m_szAccessToken != NULL)  		// try to receive a response from server -		RetrieveMyInfo(); +		RetrieveUserInfo(NULL);  	else {  		if (mir_wstrlen(m_wszEmail) == NULL) {  			ConnectionFailed(LOGINERR_BADUSERID); diff --git a/protocols/Discord/src/options.cpp b/protocols/Discord/src/options.cpp index d57f8d27af..b6cde0f39d 100644 --- a/protocols/Discord/src/options.cpp +++ b/protocols/Discord/src/options.cpp @@ -17,6 +17,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  #include "stdafx.h" +///////////////////////////////////////////////////////////////////////////////////////// +  class CDiscardAccountOptions : public CProtoDlgBase<CDiscordProto>  {  	CCtrlEdit m_edGroup, m_edUserName, m_edPassword; @@ -51,6 +53,8 @@ public:  	}  }; +///////////////////////////////////////////////////////////////////////////////////////// +  int CDiscordProto::OnOptionsInit(WPARAM wParam, LPARAM)  {  	OPTIONSDIALOGPAGE odp = { 0 }; diff --git a/protocols/Discord/src/proto.cpp b/protocols/Discord/src/proto.cpp index 39ec101779..e2947e0594 100644 --- a/protocols/Discord/src/proto.cpp +++ b/protocols/Discord/src/proto.cpp @@ -22,12 +22,18 @@ static int compareRequests(const AsyncHttpRequest *p1, const AsyncHttpRequest *p  	return p1->m_iReqNum - p2->m_iReqNum;  } +static int compareUsers(const CDiscordUser *p1, const CDiscordUser *p2) +{ +	return p1->id - p2->id; +} +  CDiscordProto::CDiscordProto(const char *proto_name, const wchar_t *username) :  	PROTO<CDiscordProto>(proto_name, username),  	m_arHttpQueue(10, compareRequests),  	m_evRequestsQueue(CreateEvent(NULL, FALSE, FALSE, NULL)),  	m_wszDefaultGroup(this, DB_KEY_GROUP, DB_KEYVAL_GROUP), -	m_wszEmail(this, DB_KEY_EMAIL, L"") +	m_wszEmail(this, DB_KEY_EMAIL, L""), +	arUsers(50, compareUsers)  {  	// Services  	CreateProtoService(PS_GETNAME, &CDiscordProto::GetName); @@ -39,6 +45,17 @@ CDiscordProto::CDiscordProto(const char *proto_name, const wchar_t *username) :  	// Clist  	Clist_GroupCreate(NULL, m_wszDefaultGroup); +	// Fill users list +	for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { +		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->wszUsername = ptrW(getWStringA(hContact, DB_KEY_NICK)); +		pNew->iDiscriminator = getDword(hContact, DB_KEY_DISCR); +		arUsers.insert(pNew); +	} +  	// Network initialization  	CMStringW descr(FORMAT, TranslateT("%s server connection"), m_tszUserName); @@ -63,15 +80,14 @@ DWORD_PTR CDiscordProto::GetCaps(int type, MCONTACT)  {  	switch (type) {  	case PFLAGNUM_1: -		return PF1_IM | PF1_MODEMSGRECV | PF1_SERVERCLIST; +		return PF1_IM | PF1_MODEMSGRECV | PF1_SERVERCLIST | PF1_BASICSEARCH | PF1_EXTSEARCH | PF1_ADDSEARCHRES;  	case PFLAGNUM_2: -		return PF2_ONLINE;  	case PFLAGNUM_3: -		return PF2_ONLINE; +		return PF2_ONLINE | PF2_HEAVYDND | PF2_INVISIBLE | PF2_IDLE;  	case PFLAGNUM_4: -		return PF4_NOCUSTOMAUTH | PF4_AVATARS; +		return PF4_FORCEADDED | PF4_FORCEAUTH | PF4_NOCUSTOMAUTH | PF4_NOAUTHDENYREASON | PF4_SUPPORTTYPING | PF4_SUPPORTIDLE | PF4_AVATARS | PF4_IMSENDOFFLINE;  	case PFLAG_UNIQUEIDTEXT: -		return (DWORD_PTR)"E-mail"; +		return (DWORD_PTR)Translate("User ID");  	case PFLAG_UNIQUEIDSETTING:  		return (DWORD_PTR)DB_KEY_EMAIL;  	} @@ -125,6 +141,123 @@ int CDiscordProto::SetStatus(int iNewStatus)  ///////////////////////////////////////////////////////////////////////////////////////// +static INT_PTR CALLBACK AdvancedSearchDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM) +{ +	switch (msg) { +	case WM_INITDIALOG: +		TranslateDialogDefault(hwndDlg); +		SetFocus(GetDlgItem(hwndDlg, IDC_NICK)); +		return TRUE; + +	case WM_COMMAND: +		if (HIWORD(wParam) == EN_SETFOCUS) +			PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg); +	} +	return FALSE; +} + +HWND CDiscordProto::CreateExtendedSearchUI(HWND hwndParent) +{ +	if (hwndParent) +		return CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_EXTSEARCH), hwndParent, AdvancedSearchDlgProc, 0); + +	return NULL; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CDiscordProto::SearchThread(void *param) +{ +	Sleep(100); + +	PROTOSEARCHRESULT psr = { 0 }; +	psr.cbSize = sizeof(psr); +	psr.flags = PSR_UNICODE; +	psr.nick.w = (wchar_t*)param; +	psr.firstName.w = L""; +	psr.lastName.w = L""; +	psr.id.w = (wchar_t*)param; +	ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)param, (LPARAM)&psr); + +	ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)param, 0); +	mir_free(param); +} + +HWND CDiscordProto::SearchAdvanced(HWND hwndDlg) +{ +	if (!m_bOnline || !IsWindow(hwndDlg)) +		return NULL; + +	wchar_t wszNick[200]; +	GetDlgItemTextW(hwndDlg, IDC_NICK, wszNick, _countof(wszNick)); +	if (wszNick[0] == 0) // empty string? reject +		return NULL; + +	wchar_t *p = wcschr(wszNick, '#'); +	if (p == NULL) // wrong user id +		return NULL; + +	p = mir_wstrdup(wszNick); +	ForkThread(&CDiscordProto::SearchThread, p); +	return (HWND)p; +} + +HANDLE CDiscordProto::SearchBasic(const wchar_t *wszId) +{ +	if (!m_bOnline) +		return NULL; + +	CMStringA szUrl = "/users/"; +	szUrl.AppendFormat(ptrA(mir_utf8encodeW(wszId))); +	AsyncHttpRequest *pReq = new AsyncHttpRequest(this, REQUEST_GET, szUrl, &CDiscordProto::OnReceiveUserInfo); +	pReq->pUserInfo = (void*)-1; +	Push(pReq); +	return (HANDLE)1; // Success +} + +int CDiscordProto::AuthRequest(MCONTACT hContact, const wchar_t*) +{ +	ptrW wszUsername(getWStringA(hContact, DB_KEY_NICK)); +	int iDiscriminator(getDword(hContact, DB_KEY_DISCR, -1)); +	if (wszUsername == NULL || iDiscriminator == -1) +		return 1; // error + +	JSONNode root; root << WCHAR_PARAM("username", wszUsername) << INT_PARAM("discriminator", iDiscriminator); +	AsyncHttpRequest *pReq = new AsyncHttpRequest(this, REQUEST_POST, "/users/@me/relationships", &CDiscordProto::OnReceiveAuth, &root); +	pReq->pUserInfo = (void*)hContact; +	Push(pReq); +	return 0; +} + +MCONTACT CDiscordProto::AddToList(int flags, PROTOSEARCHRESULT *psr) +{ +	if (psr->id.w == NULL) +		return 0; + +	wchar_t *p = wcschr(psr->id.w, '#'); +	if (p == NULL) +		return 0; + +	MCONTACT hContact = db_add_contact(); +	Proto_AddToContact(hContact, m_szModuleName); +	if (flags & PALF_TEMPORARY) +		db_set_b(hContact, "CList", "NotOnList", 1); +	 +	*p = 0; +	CDiscordUser *pUser = new CDiscordUser(0); +	pUser->hContact = hContact; +	pUser->wszUsername = psr->id.w; +	pUser->iDiscriminator = _wtoi(p + 1); +	*p = '#'; + +	setWString(hContact, DB_KEY_NICK, pUser->wszUsername); +	setDword(hContact, DB_KEY_DISCR, pUser->iDiscriminator); + +	return hContact; +} + +///////////////////////////////////////////////////////////////////////////////////////// +  int CDiscordProto::OnModulesLoaded(WPARAM, LPARAM)  {  	return 0; diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h index fe525284f3..83046a9c5c 100644 --- a/protocols/Discord/src/proto.h +++ b/protocols/Discord/src/proto.h @@ -59,12 +59,36 @@ JSONNode& operator<<(JSONNode &json, const WCHAR_PARAM ¶m);  ///////////////////////////////////////////////////////////////////////////////////////// +struct CDiscordUser : public MZeroedObject +{ +	CDiscordUser(SnowFlake _id) : +		id(_id) +		{} + +	SnowFlake id; +	MCONTACT  hContact; + +	SnowFlake channelId; +	SnowFlake lastMessageId; +	bool      bIsPrivate; + +	CMStringW wszUsername; +	int       iDiscriminator; +}; +  class CDiscordProto : public PROTO<CDiscordProto>  {  	friend struct AsyncHttpRequest;  	friend class CDiscardAccountOptions; +	////////////////////////////////////////////////////////////////////////////////////// +	// threads +  	void __cdecl ServerThread(void*); +	void __cdecl SearchThread(void *param); + +	////////////////////////////////////////////////////////////////////////////////////// +	// session control  	void SetAllContactStatuses(int iStatus);  	void ConnectionFailed(int iReason); @@ -87,11 +111,25 @@ class CDiscordProto : public PROTO<CDiscordProto>  		m_bOnline,         // protocol is online  		m_bTerminated;     // Miranda's going down +	////////////////////////////////////////////////////////////////////////////////////// +	// options +  	CMOption<wchar_t*> m_wszEmail;        // my own email  	CMOption<wchar_t*> m_wszDefaultGroup; // clist group to store contacts  +	////////////////////////////////////////////////////////////////////////////////////// +	// common data +  	SnowFlake m_ownId; +	OBJLIST<CDiscordUser> arUsers; +	CDiscordUser* FindUser(SnowFlake id); +	CDiscordUser* FindUser(const wchar_t *pwszUsername, int iDiscriminator); +	CDiscordUser* PrepareUser(const JSONNode&); + +	////////////////////////////////////////////////////////////////////////////////////// +	// misc methods +  	SnowFlake getId(const char *szName);  	SnowFlake getId(MCONTACT hContact, const char *szName); @@ -105,8 +143,15 @@ public:  	// PROTO_INTERFACE  	virtual DWORD_PTR __cdecl GetCaps(int, MCONTACT = 0) override; -	virtual int __cdecl SetStatus(int iNewStatus) override; +	virtual HWND __cdecl CreateExtendedSearchUI(HWND owner) override; +	virtual HWND __cdecl SearchAdvanced(HWND owner) override; +	virtual HANDLE __cdecl SearchBasic(const wchar_t* id) override; +	virtual MCONTACT __cdecl AddToList(int flags, PROTOSEARCHRESULT* psr) override; +	 +	virtual int __cdecl AuthRequest(MCONTACT hContact, const wchar_t*) override; + +	virtual int __cdecl SetStatus(int iNewStatus) override;  	virtual int __cdecl OnEvent(PROTOEVENTTYPE, WPARAM, LPARAM) override;  	// Services @@ -120,13 +165,15 @@ public:  	void OnLoggedIn();  	void OnLoggedOut(); -	 + +	void OnReceiveAuth(NETLIBHTTPREQUEST*, AsyncHttpRequest*);  	void OnReceiveToken(NETLIBHTTPREQUEST*, AsyncHttpRequest*); -	void OnReceiveMyInfo(NETLIBHTTPREQUEST*, AsyncHttpRequest*); +	void OnReceiveUserInfo(NETLIBHTTPREQUEST*, AsyncHttpRequest*);  	void OnReceiveGuilds(NETLIBHTTPREQUEST*, AsyncHttpRequest*);  	void OnReceiveChannels(NETLIBHTTPREQUEST*, AsyncHttpRequest*); +	void OnReceiveFriends(NETLIBHTTPREQUEST*, AsyncHttpRequest*); -	void RetrieveMyInfo(); +	void RetrieveUserInfo(MCONTACT hContact);  	// Misc  	void SetServerStatus(int iStatus); diff --git a/protocols/Discord/src/resource.h b/protocols/Discord/src/resource.h index 44d79b91ae..840b444d17 100644 --- a/protocols/Discord/src/resource.h +++ b/protocols/Discord/src/resource.h @@ -6,10 +6,12 @@  #define IDI_MAIN                        101  #define IDI_OFFLINE                     102  #define IDD_OPTIONS_ACCOUNT             103 +#define IDD_EXTSEARCH                   104  #define IDC_PASSWORD                   1001  #define IDC_USERNAME                   1002  #define IDC_GROUP                      1003 +#define IDC_NICK								1004  // Next default values for new objects  //  @@ -17,7 +19,7 @@  #ifndef APSTUDIO_READONLY_SYMBOLS  #define _APS_NEXT_RESOURCE_VALUE        103  #define _APS_NEXT_COMMAND_VALUE         40001 -#define _APS_NEXT_CONTROL_VALUE         1004 +#define _APS_NEXT_CONTROL_VALUE         1005  #define _APS_NEXT_SYMED_VALUE           101  #endif  #endif diff --git a/protocols/Discord/src/server.cpp b/protocols/Discord/src/server.cpp index 8f8f551890..3997742ccd 100644 --- a/protocols/Discord/src/server.cpp +++ b/protocols/Discord/src/server.cpp @@ -17,34 +17,40 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  #include "stdafx.h" -void CDiscordProto::RetrieveMyInfo() +void CDiscordProto::RetrieveUserInfo(MCONTACT hContact)  { -	Push(new AsyncHttpRequest(this, REQUEST_GET, "/users/@me", &CDiscordProto::OnReceiveMyInfo)); +	AsyncHttpRequest *pReq = new AsyncHttpRequest(this, REQUEST_GET, "/users/@me", &CDiscordProto::OnReceiveUserInfo); +	pReq->pUserInfo = (void*)hContact; +	Push(pReq);  } -void CDiscordProto::OnReceiveMyInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) +void CDiscordProto::OnReceiveUserInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq)  { +	MCONTACT hContact = (MCONTACT)pReq->pUserInfo;  	if (pReply->resultCode != 200) { -		ConnectionFailed(LOGINERR_WRONGPASSWORD); +		if (hContact == NULL) +			ConnectionFailed(LOGINERR_WRONGPASSWORD);  		return;  	} -	JSONNode *root = json_parse(pReply->pData); -	if (root == NULL) { -		ConnectionFailed(LOGINERR_NOSERVER); +	JSONNode root = JSONNode::parse(pReply->pData); +	if (!root) { +		if (hContact == NULL) +			ConnectionFailed(LOGINERR_NOSERVER);  		return;  	} -	m_ownId = _wtoi64(root->at("id").as_mstring()); -	setId("id", m_ownId); +	m_ownId = _wtoi64(root["id"].as_mstring()); +	setId(hContact, DB_KEY_ID, m_ownId); -	setWString("Username", root->at("username").as_mstring()); -	setByte("MfaEnabled", root->at("mfa_enabled").as_bool()); -	setWString("AvatarHash", root->at("avatar").as_mstring()); -	setDword("Discriminator", root->at("discriminator").as_int()); -	setWString("Email", root->at("email").as_mstring()); +	setByte(hContact, DB_KEY_MFA, root["mfa_enabled"].as_bool()); +	setDword(hContact, DB_KEY_DISCR, root["discriminator"].as_int()); +	setWString(hContact, DB_KEY_NICK, root["username"].as_mstring()); +	setWString(hContact, DB_KEY_AVHASH, root["avatar"].as_mstring()); +	setWString(hContact, DB_KEY_EMAIL, root["email"].as_mstring()); -	OnLoggedIn(); +	if (hContact == NULL) +		OnLoggedIn();  }  ///////////////////////////////////////////////////////////////////////////////////////// @@ -61,12 +67,56 @@ void CDiscordProto::SetServerStatus(int iStatus)  	ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)iOldStatus, m_iStatus);  } +void CDiscordProto::OnReceiveAuth(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) +{ +	MCONTACT hContact = (MCONTACT)pReq->pUserInfo; +	if (pReply->resultCode == 204) +		RetrieveUserInfo(hContact); +} +  /////////////////////////////////////////////////////////////////////////////////////////  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) { +		JSONNode &p = *it; + +		JSONNode &user = p["recipient"]; +		if (!user) +			continue; + +		CDiscordUser *pUser = PrepareUser(user); +		pUser->lastMessageId = _wtoi64(p["last_message_id"].as_mstring()); +		pUser->channelId = _wtoi64(p["id"].as_mstring()); +		pUser->bIsPrivate = p["is_private"].as_bool(); +	} +} + +///////////////////////////////////////////////////////////////////////////////////////// + +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); +	}  }  ///////////////////////////////////////////////////////////////////////////////////////// @@ -86,21 +136,21 @@ void CDiscordProto::OnReceiveToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*)  		return;  	} -	JSONNode *root = json_parse(pReply->pData); -	if (root == NULL) { +	JSONNode root = JSONNode::parse(pReply->pData); +	if (!root) {  LBL_Error:  		ConnectionFailed(LOGINERR_NOSERVER);  		return;  	} -	CMStringA szToken = root->at("token").as_mstring(); +	CMStringA szToken = root["token"].as_mstring();  	if (szToken.IsEmpty())  		goto LBL_Error;  	m_szAccessToken = szToken.Detach();  	setString("AccessToken", m_szAccessToken); -	RetrieveMyInfo(); +	RetrieveUserInfo(NULL);  }  ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/protocols/Discord/src/stdafx.h b/protocols/Discord/src/stdafx.h index 414b542c0f..e0177716ab 100644 --- a/protocols/Discord/src/stdafx.h +++ b/protocols/Discord/src/stdafx.h @@ -44,8 +44,15 @@ extern HINSTANCE g_hInstance;  #include "version.h"  #include "proto.h" -#define DB_KEY_EMAIL    "Email" -#define DB_KEY_PASSWORD "Password" +#define DB_KEY_ID        "id" +#define DB_KEY_EMAIL     "Email" +#define DB_KEY_PASSWORD  "Password" +#define DB_KEY_DISCR     "Discriminator" +#define DB_KEY_MFA       "MfaEnabled" +#define DB_KEY_NICK      "Nick" +#define DB_KEY_AVHASH    "AvatarHash" +#define DB_KEY_CHANNELID "ChannelID" +#define DB_KEY_LASTMSGID "LastMessageID"  #define DB_KEY_GROUP    "GroupName"  #define DB_KEYVAL_GROUP L"Discord" diff --git a/protocols/Discord/src/utils.cpp b/protocols/Discord/src/utils.cpp index 4d69a6f40f..1074291b36 100644 --- a/protocols/Discord/src/utils.cpp +++ b/protocols/Discord/src/utils.cpp @@ -48,22 +48,26 @@ JSONNode& operator<<(JSONNode &json, const WCHAR_PARAM ¶m)  SnowFlake CDiscordProto::getId(const char *szSetting)  { -	SnowFlake result;  	DBVARIANT dbv;  	dbv.type = DBVT_BLOB; -	dbv.pbVal = (BYTE*)&result; -	dbv.cpbVal = sizeof(result); -	return (db_get(NULL, m_szModuleName, szSetting, &dbv)) ? 0 : result; +	if (db_get(NULL, m_szModuleName, szSetting, &dbv)) +		return 0; +	 +	SnowFlake result = (dbv.cpbVal == sizeof(SnowFlake)) ? *(SnowFlake*)dbv.pbVal : 0; +	db_free(&dbv); +	return result;  }  SnowFlake CDiscordProto::getId(MCONTACT hContact, const char *szSetting)  { -	SnowFlake result;  	DBVARIANT dbv;  	dbv.type = DBVT_BLOB; -	dbv.pbVal = (BYTE*)&result; -	dbv.cpbVal = sizeof(result); -	return (db_get(hContact, m_szModuleName, szSetting, &dbv)) ? 0 : result; +	if (db_get(hContact, m_szModuleName, szSetting, &dbv)) +		return 0; + +	SnowFlake result = (dbv.cpbVal == sizeof(SnowFlake)) ? *(SnowFlake*)dbv.pbVal : 0; +	db_free(&dbv); +	return result;  }  void CDiscordProto::setId(const char *szSetting, SnowFlake iValue) @@ -75,3 +79,53 @@ void CDiscordProto::setId(MCONTACT hContact, const char *szSetting, SnowFlake iV  {  	db_set_blob(hContact, m_szModuleName, szSetting, &iValue, sizeof(iValue));  } + +///////////////////////////////////////////////////////////////////////////////////////// + +CDiscordUser* CDiscordProto::FindUser(SnowFlake id) +{ +	return arUsers.find((CDiscordUser*)&id); +} + +CDiscordUser* CDiscordProto::FindUser(const wchar_t *pwszUsername, int iDiscriminator) +{ +	for (int i = 0; i < arUsers.getCount(); i++) { +		CDiscordUser &p = arUsers[i]; +		if (p.wszUsername == pwszUsername && p.iDiscriminator == iDiscriminator) +			return &p; +	} + +	return NULL; +} + +CDiscordUser* CDiscordProto::PrepareUser(const JSONNode &user) +{ +	SnowFlake id = _wtoi64(user["id"].as_mstring()); +	int iDiscriminator = _wtoi(user["discriminator"].as_mstring()); +	CMStringW avatar = user["avatar"].as_mstring(); +	CMStringW username = user["username"].as_mstring(); + +	CDiscordUser *pUser = FindUser(id); +	if (pUser == NULL) +		pUser = FindUser(username, iDiscriminator); +	if (pUser == NULL) { +		pUser = new CDiscordUser(id); +		pUser->wszUsername = username; +		pUser->iDiscriminator = iDiscriminator; +		arUsers.insert(pUser); +	} + +	if (pUser->hContact == 0) { +		MCONTACT hContact = db_add_contact(); +		Proto_AddToContact(hContact, m_szModuleName); + +		setId(hContact, DB_KEY_ID, id); +		setWString(hContact, DB_KEY_NICK, username); +		setDword(hContact, DB_KEY_DISCR, iDiscriminator); + +		pUser->hContact = hContact; +	} + +	setWString(pUser->hContact, DB_KEY_AVHASH, avatar); +	return pUser; +} diff --git a/protocols/Discord/src/version.h b/protocols/Discord/src/version.h index 056cd06fa5..c4be71667a 100644 --- a/protocols/Discord/src/version.h +++ b/protocols/Discord/src/version.h @@ -1,7 +1,7 @@  #define __MAJOR_VERSION            0  #define __MINOR_VERSION            0  #define __RELEASE_NUM              0 -#define __BUILD_NUM                2 +#define __BUILD_NUM                3  #include <stdver.h> | 
