diff options
Diffstat (limited to 'protocols/Discord/src/dispatch.cpp')
-rw-r--r-- | protocols/Discord/src/dispatch.cpp | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/protocols/Discord/src/dispatch.cpp b/protocols/Discord/src/dispatch.cpp new file mode 100644 index 0000000000..8d314e0836 --- /dev/null +++ b/protocols/Discord/src/dispatch.cpp @@ -0,0 +1,221 @@ +/* +Copyright © 2016-17 Miranda NG team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +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"MESSAGE_CREATE", &CDiscordProto::OnCommandMessage }, + { L"MESSAGE_UPDATE", &CDiscordProto::OnCommandMessage }, + + { L"PRESENCE_UPDATE", &CDiscordProto::OnCommandPresence }, + + { L"READY", &CDiscordProto::OnCommandReady }, + + { L"TYPING_START", &CDiscordProto::OnCommandTyping }, + + { L"USER_UPDATE", &CDiscordProto::OnCommandUserUpdate }, +}; + +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); + + SnowFlake lastId = getId(pUser->hContact, DB_KEY_LASTMSGID); // as stored in a database + if (lastId < _wtoi64(msgId)) + setId(pUser->hContact, DB_KEY_LASTMSGID, _wtoi64(msgId)); +} + +////////////////////////////////////////////////////////////////////////////////////// +// 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(); +} + +static void __stdcall sttStartTimer(void *param) +{ + CDiscordProto *ppro = (CDiscordProto*)param; + SetTimer(g_hwndHeartbeat, (UINT_PTR)param, ppro->getHeartbeatInterval(), &CDiscordProto::HeartbeatTimerProc); +} + +void CDiscordProto::OnCommandReady(const JSONNode &pRoot) +{ + GatewaySendHeartbeat(); + CallFunctionAsync(sttStartTimer, this); + + m_szGatewaySessionId = pRoot["session_id"].as_mstring(); + + const JSONNode &relations = pRoot["relationships"]; + for (auto it = relations.begin(); it != relations.end(); ++it) { + const JSONNode &p = *it; + + const JSONNode &user = p["user"]; + if (user) + PrepareUser(user); + } + + const JSONNode &channels = pRoot["private_channels"]; + for (auto it = channels.begin(); it != channels.end(); ++it) { + const JSONNode &p = *it; + + CDiscordUser *pUser = NULL; + const JSONNode &recipients = p["recipients"]; + for (auto it2 = recipients.begin(); it2 != recipients.end(); ++it2) + pUser = PrepareUser(*it2); + + if (pUser == NULL) + continue; + + pUser->channelId = _wtoi64(p["id"].as_mstring()); + pUser->lastMessageId = _wtoi64(p["last_message_id"].as_mstring()); + pUser->bIsPrivate = true; + + setId(pUser->hContact, DB_KEY_CHANNELID, pUser->channelId); + + SnowFlake oldMsgId = getId(pUser->hContact, DB_KEY_LASTMSGID); + if (pUser->lastMessageId > oldMsgId) + RetrieveHistory(pUser->hContact, MSG_AFTER, oldMsgId, 99); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// 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"); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// UTN support + +void CDiscordProto::OnCommandUserUpdate(const JSONNode &pRoot) +{ + SnowFlake id = _wtoi64(pRoot["id"].as_mstring()); + + MCONTACT hContact; + if (id != m_ownId) { + CDiscordUser *pUser = FindUser(id); + if (pUser == NULL) + return; + + hContact = pUser->hContact; + } + else hContact = 0; + + // force rereading avatar + ptrW wszOldHash(getWStringA(hContact, DB_KEY_AVHASH)); + CMStringW wszNewHash(pRoot["avatar"].as_mstring()); + if (mir_wstrcmp(wszOldHash, wszNewHash)) { + setWString(hContact, DB_KEY_AVHASH, wszNewHash); + RetrieveAvatar(hContact); + } +} |