diff options
author | George Hazan <ghazan@miranda.im> | 2017-01-07 22:57:57 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2017-01-07 22:57:57 +0300 |
commit | f1a204292b895a94f142c32b2f3dc69fcc053e67 (patch) | |
tree | 465127e97ce90f457c90b8cfc78ae8f0e00b5064 /protocols/Discord/src | |
parent | e012650957335d5f4295441debf849b3b9b2f4ef (diff) |
first version of Discord that sends messages, catches presence packets and displays typing notification
version bump
Diffstat (limited to 'protocols/Discord/src')
-rw-r--r-- | protocols/Discord/src/dispatch.cpp | 120 | ||||
-rw-r--r-- | protocols/Discord/src/gateway.cpp | 26 | ||||
-rw-r--r-- | protocols/Discord/src/proto.cpp | 47 | ||||
-rw-r--r-- | protocols/Discord/src/proto.h | 12 | ||||
-rw-r--r-- | protocols/Discord/src/server.cpp | 11 | ||||
-rw-r--r-- | protocols/Discord/src/stdafx.h | 1 | ||||
-rw-r--r-- | protocols/Discord/src/utils.cpp | 25 | ||||
-rw-r--r-- | protocols/Discord/src/version.h | 4 |
8 files changed, 217 insertions, 29 deletions
diff --git a/protocols/Discord/src/dispatch.cpp b/protocols/Discord/src/dispatch.cpp index f9cf006a09..e96471f116 100644 --- a/protocols/Discord/src/dispatch.cpp +++ b/protocols/Discord/src/dispatch.cpp @@ -19,6 +19,102 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. extern HWND g_hwndHeartbeat; +#pragma pack(4) + +struct CDiscordCommand +{ + const wchar_t *szCommandId; + GatewayHandlerFunc pFunc; +} +static handlers[] = // these structures must me sorted alphabetically +{ + { L"MESSAGE_CREATE", &CDiscordProto::OnCommandMessage }, + { L"MESSAGE_UPDATE", &CDiscordProto::OnCommandMessage }, + + { L"PRESENCE_UPDATE", &CDiscordProto::OnCommandPresence }, + + { L"READY", &CDiscordProto::OnCommandReady }, + + { L"TYPING_START", &CDiscordProto::OnCommandTyping }, +}; + +static int __cdecl pSearchFunc(const void *p1, const void *p2) +{ + return wcscmp(((CDiscordCommand*)p1)->szCommandId, ((CDiscordCommand*)p2)->szCommandId); +} + +GatewayHandlerFunc CDiscordProto::GetHandler(const wchar_t *pwszCommand) +{ + CDiscordCommand tmp = { pwszCommand, NULL }; + CDiscordCommand *p = (CDiscordCommand*)bsearch(&tmp, handlers, _countof(handlers), sizeof(handlers[0]), pSearchFunc); + return (p != NULL) ? p->pFunc : NULL; +} + +////////////////////////////////////////////////////////////////////////////////////// +// reading a new message + +void CDiscordProto::OnCommandMessage(const JSONNode &pRoot) +{ + PROTORECVEVENT recv = {}; + + CDiscordUser *pUser = PrepareUser(pRoot["author"]); + SnowFlake channelId = _wtoi64(pRoot["channel_id"].as_mstring()); + CMStringW msgId = pRoot["id"].as_mstring(); + CMStringW wszText = pRoot["content"].as_mstring(); + + // if a message has myself as an author, mark it as sent + if (pUser->id == 0) + return; + + const JSONNode &edited = pRoot["edited_timestamp"]; + if (!edited.isnull()) + wszText.AppendFormat(L" (%s %s)", TranslateT("edited at"), edited.as_mstring().c_str()); + + if (pUser->channelId != channelId) { + debugLogA("failed to process a groupchat message, exiting"); + return; + } + + ptrA buf(mir_utf8encodeW(wszText)); + recv.timestamp = (DWORD)StringToDate(pRoot["timestamp"].as_mstring()); + recv.szMessage = buf; + recv.lParam = (LPARAM)msgId.c_str(); + ProtoChainRecvMsg(pUser->hContact, &recv); +} + +////////////////////////////////////////////////////////////////////////////////////// +// someone changed its status + +void CDiscordProto::OnCommandPresence(const JSONNode &pRoot) +{ + CDiscordUser *pUser = PrepareUser(pRoot["user"]); + if (pUser == NULL) + return; + + int iStatus; + CMStringW wszStatus = pRoot["status"].as_mstring(); + if (wszStatus == L"idle") + iStatus = ID_STATUS_IDLE; + else if (wszStatus == L"online") + iStatus = ID_STATUS_ONLINE; + else if (wszStatus == L"offline") + iStatus = ID_STATUS_OFFLINE; + else + iStatus = 0; + + if (iStatus != 0) + setWord(pUser->hContact, "Status", iStatus); + + CMStringW wszGame = pRoot["game"]["name"].as_mstring(); + if (!wszGame.IsEmpty()) + setWString(pUser->hContact, "XStatusMsg", wszGame); + else + delSetting(pUser->hContact, "XStatusMsg"); +} + +////////////////////////////////////////////////////////////////////////////////////// +// gateway session start + void CALLBACK CDiscordProto::HeartbeatTimerProc(HWND, UINT, UINT_PTR id, DWORD) { ((CDiscordProto*)id)->GatewaySendHeartbeat(); @@ -62,3 +158,27 @@ void CDiscordProto::OnCommandReady(const JSONNode &pRoot) setId(pUser->hContact, DB_KEY_CHANNELID, pUser->channelId); } } + +////////////////////////////////////////////////////////////////////////////////////// +// UTN support + +void CDiscordProto::OnCommandTyping(const JSONNode &pRoot) +{ + SnowFlake userId = _wtoi64(pRoot["user_id"].as_mstring()); + SnowFlake channelId = _wtoi64(pRoot["channel_id"].as_mstring()); + debugLogA("user typing notification: userid=%lld, channelid=%lld", userId, channelId); + + CDiscordUser *pUser = FindUser(userId); + if (pUser == NULL) { + debugLogA("user with id=%lld is not found", userId); + return; + } + + if (pUser->channelId == channelId) { + debugLogA("user is typing in his private channel"); + CallService(MS_PROTO_CONTACTISTYPING, pUser->hContact, 20); + } + else { + debugLogA("user is typing in a group channel, skipped"); + } +} diff --git a/protocols/Discord/src/gateway.cpp b/protocols/Discord/src/gateway.cpp index b8049e3a1b..db07b18069 100644 --- a/protocols/Discord/src/gateway.cpp +++ b/protocols/Discord/src/gateway.cpp @@ -17,30 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "stdafx.h" -#pragma pack(4) - -struct CDiscordCommand -{ - const wchar_t *szCommandId; - GatewayHandlerFunc pFunc; -} -static handlers[] = // these structures must me sorted alphabetically -{ - { L"READY", &CDiscordProto::OnCommandReady } -}; - -static int __cdecl pSearchFunc(const void *p1, const void *p2) -{ - return wcscmp(((CDiscordCommand*)p1)->szCommandId, ((CDiscordCommand*)p2)->szCommandId); -} - -GatewayHandlerFunc CDiscordProto::GetHandler(const wchar_t *pwszCommand) -{ - CDiscordCommand tmp = { pwszCommand, NULL }; - CDiscordCommand *p = (CDiscordCommand*)bsearch(&tmp, handlers, _countof(handlers), sizeof(handlers[0]), pSearchFunc); - return (p != NULL) ? p->pFunc : NULL; -} - ////////////////////////////////////////////////////////////////////////////////////// // sends a piece of JSON to a server via a websocket, masked @@ -176,7 +152,7 @@ void CDiscordProto::GatewayThreadWorker() break; unsigned char buf[2048]; - int bufSize = Netlib_Recv(m_hGatewayConnection, (char*)buf + offset, _countof(buf) - offset, MSG_DUMPASTEXT); + int bufSize = Netlib_Recv(m_hGatewayConnection, (char*)buf + offset, _countof(buf) - offset, 0); if (bufSize == 0) { debugLogA("Gateway connection gracefully closed"); break; diff --git a/protocols/Discord/src/proto.cpp b/protocols/Discord/src/proto.cpp index f85df56fe3..d44f97cb8b 100644 --- a/protocols/Discord/src/proto.cpp +++ b/protocols/Discord/src/proto.cpp @@ -42,6 +42,9 @@ CDiscordProto::CDiscordProto(const char *proto_name, const wchar_t *username) : HookProtoEvent(ME_OPT_INITIALISE, &CDiscordProto::OnOptionsInit); HookProtoEvent(ME_MSG_WINDOWEVENT, &CDiscordProto::OnSrmmEvent); + // database + db_set_resident(m_szModuleName, "XStatusMsg"); + // Clist Clist_GroupCreate(NULL, m_wszDefaultGroup); @@ -258,6 +261,50 @@ MCONTACT CDiscordProto::AddToList(int flags, PROTOSEARCHRESULT *psr) return hContact; } +//////////////////////////////////////////////////////////////////////////////////////// +// RecvMsg + +int CDiscordProto::RecvMsg(MCONTACT hContact, PROTORECVEVENT *evt) +{ + T2Utf szResUtf((const wchar_t*)evt->lParam); + evt->pCustomData = (char*)szResUtf; + evt->cbCustomDataSize = (DWORD)mir_strlen(szResUtf); + Proto_RecvMessage(hContact, evt); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// SendMsg + +void __cdecl CDiscordProto::SendMessageAckThread(void *param) +{ + Sleep(100); + ProtoBroadcastAck((MCONTACT)param, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)1, (LPARAM)Translate("Protocol is offline or no JID")); +} + +int CDiscordProto::SendMsg(MCONTACT hContact, int /*flags*/, const char *pszSrc) +{ + if (!m_bOnline) { + ForkThread(&CDiscordProto::SendMessageAckThread, (void*)hContact); + return 1; + } + + ptrW wszText(mir_utf8decodeW(pszSrc)); + if (wszText == NULL) + return 0; + + CDiscordUser *pUser = FindUser(getId(hContact, DB_KEY_ID)); + if (pUser == NULL || pUser->channelId == NULL) + return 0; + + CMStringA szUrl(FORMAT, "/channels/%lld/messages", pUser->channelId); + JSONNode body; body << WCHAR_PARAM("content", wszText); + AsyncHttpRequest *pReq = new AsyncHttpRequest(this, REQUEST_POST, szUrl, &CDiscordProto::OnReceiveMessageAck, &body); + pReq->pUserInfo = (void*)hContact; + Push(pReq); + return pReq->m_iReqNum; +} + ///////////////////////////////////////////////////////////////////////////////////////// int CDiscordProto::OnModulesLoaded(WPARAM, LPARAM) diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h index 73e48a806a..a9a948ce7d 100644 --- a/protocols/Discord/src/proto.h +++ b/protocols/Discord/src/proto.h @@ -102,6 +102,7 @@ class CDiscordProto : public PROTO<CDiscordProto> void __cdecl ServerThread(void*); void __cdecl SearchThread(void *param); + void __cdecl SendMessageAckThread(void* param); ////////////////////////////////////////////////////////////////////////////////////// // session control @@ -147,8 +148,6 @@ class CDiscordProto : public PROTO<CDiscordProto> void GatewaySendHeartbeat(void); void GatewaySendIdentify(void); - void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*); - GatewayHandlerFunc GetHandler(const wchar_t*); int m_iHartbeatInterval; // in milliseconds @@ -168,6 +167,7 @@ class CDiscordProto : public PROTO<CDiscordProto> OBJLIST<CDiscordUser> arUsers; CDiscordUser* FindUser(SnowFlake id); CDiscordUser* FindUser(const wchar_t *pwszUsername, int iDiscriminator); + CDiscordUser* FindUserByChannel(SnowFlake channelId); CDiscordUser* PrepareUser(const JSONNode&); ////////////////////////////////////////////////////////////////////////////////////// @@ -194,6 +194,9 @@ public: virtual int __cdecl AuthRequest(MCONTACT hContact, const wchar_t*) override; + virtual int __cdecl RecvMsg(MCONTACT hContact, PROTORECVEVENT *evt) override; + virtual int __cdecl SendMsg(MCONTACT hContact, int flags, const char* pszSrc) override; + virtual int __cdecl SetStatus(int iNewStatus) override; virtual int __cdecl OnEvent(PROTOEVENTTYPE, WPARAM, LPARAM) override; @@ -207,7 +210,10 @@ public: int __cdecl OnSrmmEvent(WPARAM, LPARAM); // dispatch commands + void OnCommandMessage(const JSONNode&); + void OnCommandPresence(const JSONNode&); void OnCommandReady(const JSONNode&); + void OnCommandTyping(const JSONNode&); void OnLoggedIn(); void OnLoggedOut(); @@ -217,6 +223,8 @@ public: void OnReceiveGuilds(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void OnReceiveChannels(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void OnReceiveFriends(NETLIBHTTPREQUEST*, AsyncHttpRequest*); + void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*); + void OnReceiveMessageAck(NETLIBHTTPREQUEST*, AsyncHttpRequest*); void RetrieveUserInfo(MCONTACT hContact); void OnReceiveUserInfo(NETLIBHTTPREQUEST*, AsyncHttpRequest*); diff --git a/protocols/Discord/src/server.cpp b/protocols/Discord/src/server.cpp index 855565d327..39ad13bc50 100644 --- a/protocols/Discord/src/server.cpp +++ b/protocols/Discord/src/server.cpp @@ -221,6 +221,17 @@ void CDiscordProto::OnReceiveGuilds(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest* ///////////////////////////////////////////////////////////////////////////////////////// +void CDiscordProto::OnReceiveMessageAck(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) +{ + bool bSucceeded = true; + if (pReply->resultCode != 200 && pReply->resultCode != 204) + bSucceeded = false; + + ProtoBroadcastAck((MCONTACT)pReq->pUserInfo, ACKTYPE_MESSAGE, bSucceeded ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)pReq->m_iReqNum, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + void CDiscordProto::OnReceiveToken(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) { if (pReply->resultCode != 200) { diff --git a/protocols/Discord/src/stdafx.h b/protocols/Discord/src/stdafx.h index b91bf0008d..4bfc9448ae 100644 --- a/protocols/Discord/src/stdafx.h +++ b/protocols/Discord/src/stdafx.h @@ -60,3 +60,4 @@ extern HINSTANCE g_hInstance; #define DB_KEYVAL_GROUP L"Discord" time_t StringToDate(const CMStringW &str); +int SerialNext(void);
\ No newline at end of file diff --git a/protocols/Discord/src/utils.cpp b/protocols/Discord/src/utils.cpp index ce000c0989..ae610b8d21 100644 --- a/protocols/Discord/src/utils.cpp +++ b/protocols/Discord/src/utils.cpp @@ -70,6 +70,15 @@ time_t StringToDate(const CMStringW &str) ///////////////////////////////////////////////////////////////////////////////////////// +static LONG volatile g_counter = 1; + +int SerialNext() +{ + return InterlockedIncrement(&g_counter); +} + +///////////////////////////////////////////////////////////////////////////////////////// + SnowFlake CDiscordProto::getId(const char *szSetting) { DBVARIANT dbv; @@ -106,6 +115,8 @@ void CDiscordProto::setId(MCONTACT hContact, const char *szSetting, SnowFlake iV ///////////////////////////////////////////////////////////////////////////////////////// +static CDiscordUser *g_myUser = new CDiscordUser(0); + CDiscordUser* CDiscordProto::FindUser(SnowFlake id) { return arUsers.find((CDiscordUser*)&id); @@ -122,9 +133,23 @@ CDiscordUser* CDiscordProto::FindUser(const wchar_t *pwszUsername, int iDiscrimi return NULL; } +CDiscordUser* CDiscordProto::FindUserByChannel(SnowFlake channelId) +{ + for (int i = 0; i < arUsers.getCount(); i++) { + CDiscordUser &p = arUsers[i]; + if (p.channelId == channelId) + return &p; + } + + return NULL; +} + CDiscordUser* CDiscordProto::PrepareUser(const JSONNode &user) { SnowFlake id = _wtoi64(user["id"].as_mstring()); + if (id == m_ownId) + return g_myUser; + int iDiscriminator = _wtoi(user["discriminator"].as_mstring()); CMStringW avatar = user["avatar"].as_mstring(); CMStringW username = user["username"].as_mstring(); diff --git a/protocols/Discord/src/version.h b/protocols/Discord/src/version.h index c4be71667a..4753e0f013 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 __MINOR_VERSION 1 #define __RELEASE_NUM 0 -#define __BUILD_NUM 3 +#define __BUILD_NUM 1 #include <stdver.h> |