/* Copyright (c) 2013-17 Miranda NG project (http://miranda-ng.org) 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 version 2 of the License. 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 . */ #include "stdafx.h" ////////////////////////////////////////////////////////////////////////////// int CVkProto::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre) { debugLogA("CVkProto::RecvMsg"); Proto_RecvMessage(hContact, pre); return 0; } ////////////////////////////////////////////////////////////////////////////// void CVkProto::SendMsgAck(void *param) { debugLogA("CVkProto::SendMsgAck"); CVkSendMsgParam *ack = (CVkSendMsgParam *)param; Sleep(100); ProtoBroadcastAck(ack->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)ack->iMsgID); delete ack; } int CVkProto::SendMsg(MCONTACT hContact, int, const char *szMsg) { debugLogA("CVkProto::SendMsg"); if (!IsOnline()) return 0; bool bIsChat = isChatRoom(hContact); LONG iUserID = getDword(hContact, bIsChat ? "vk_chat_id" : "ID" , VK_INVALID_USER); if (iUserID == VK_INVALID_USER || iUserID == VK_FEED_USER) { ForkThread(&CVkProto::SendMsgAck, new CVkSendMsgParam(hContact)); return 0; } int StickerId = 0; ptrA pszRetMsg(GetStickerId(szMsg, StickerId)); ULONG uMsgId = ::InterlockedIncrement(&m_msgId); AsyncHttpRequest *pReq = new AsyncHttpRequest(this, REQUEST_POST, "/method/messages.send.json", true, bIsChat? &CVkProto::OnSendChatMsg : &CVkProto::OnSendMessage, AsyncHttpRequest::rpHigh) << INT_PARAM(bIsChat ? "chat_id" : "peer_id", iUserID) << INT_PARAM("random_id", ((LONG) time(NULL)) * 100 + uMsgId % 100); pReq->AddHeader("Content-Type", "application/x-www-form-urlencoded"); if (StickerId) pReq << INT_PARAM("sticker_id", StickerId); else { pReq << CHAR_PARAM("message", szMsg); if (m_vkOptions.bSendVKLinksAsAttachments) { CMStringA szAttachments = GetAttachmentsFromMessage(szMsg); if (!szAttachments.IsEmpty()) { debugLogA("CVkProto::SendMsg Attachments = %s", szAttachments); pReq << CHAR_PARAM("attachment", szAttachments); } } } if (!bIsChat) pReq->pUserInfo = new CVkSendMsgParam(hContact, uMsgId); Push(pReq); if (!m_bServerDelivery && !bIsChat) ForkThread(&CVkProto::SendMsgAck, new CVkSendMsgParam(hContact, uMsgId)); if (!IsEmpty(pszRetMsg)) SendMsg(hContact, 0, pszRetMsg); return uMsgId; } void CVkProto::OnSendMessage(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq) { int iResult = ACKRESULT_FAILED; if (pReq->pUserInfo == NULL) { debugLogA("CVkProto::OnSendMessage failed! (pUserInfo == NULL)"); return; } CVkSendMsgParam *param = (CVkSendMsgParam *)pReq->pUserInfo; debugLogA("CVkProto::OnSendMessage %d", reply->resultCode); if (reply->resultCode == 200) { JSONNode jnRoot; const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot); if (jnResponse) { UINT mid; if (jnResponse.type() != JSON_STRING) mid = jnResponse.as_int(); else if (swscanf(jnResponse.as_mstring(), L"%d", &mid) != 1) mid = 0; if (param->iMsgID != -1) m_sendIds.insert((HANDLE)mid); if (mid > getDword(param->hContact, "lastmsgid")) setDword(param->hContact, "lastmsgid", mid); if (m_vkOptions.iMarkMessageReadOn >= MarkMsgReadOn::markOnReply) MarkMessagesRead(param->hContact); iResult = ACKRESULT_SUCCESS; } } if (param->pFUP) { ProtoBroadcastAck(param->hContact, ACKTYPE_FILE, iResult, (HANDLE)(param->pFUP)); if (!pReq->bNeedsRestart || m_bTerminated) delete param->pFUP; } else if (m_bServerDelivery) ProtoBroadcastAck(param->hContact, ACKTYPE_MESSAGE, iResult, (HANDLE)(param->iMsgID)); if (!pReq->bNeedsRestart || m_bTerminated) { delete param; pReq->pUserInfo = NULL; } } ///////////////////////////////////////////////////////////////////////////////////////// int CVkProto::OnDbEventRead(WPARAM, LPARAM hDbEvent) { debugLogA("CVkProto::OnDbEventRead"); MCONTACT hContact = db_event_getContact(hDbEvent); if (!hContact) return 0; CMStringA szProto(GetContactProto(hContact)); if (szProto.IsEmpty() || szProto != m_szModuleName) return 0; if (m_vkOptions.iMarkMessageReadOn == MarkMsgReadOn::markOnRead) MarkMessagesRead(hContact); return 0; } INT_PTR CVkProto::SvcMarkMessagesAsRead(WPARAM hContact, LPARAM) { MarkDialogAsRead(hContact); MarkMessagesRead(hContact); return 0; } void CVkProto::MarkMessagesRead(const CMStringA &mids) { debugLogA("CVkProto::MarkMessagesRead (mids)"); if (!IsOnline() || mids.IsEmpty()) return; Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/messages.markAsRead.json", true, &CVkProto::OnReceiveSmth, AsyncHttpRequest::rpLow) << CHAR_PARAM("message_ids", mids)); } void CVkProto::MarkMessagesRead(const MCONTACT hContact) { debugLogA("CVkProto::MarkMessagesRead (hContact)"); if (!IsOnline() || !hContact) return; LONG userID = getDword(hContact, "ID", VK_INVALID_USER); if (userID == VK_INVALID_USER || userID == VK_FEED_USER) return; Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/messages.markAsRead.json", true, &CVkProto::OnReceiveSmth, AsyncHttpRequest::rpLow) << INT_PARAM("peer_id", userID)); } void CVkProto::RetrieveMessagesByIds(const CMStringA &mids) { debugLogA("CVkProto::RetrieveMessagesByIds"); if (!IsOnline() || mids.IsEmpty()) return; CMStringA code(FORMAT, "var Mids=\"%s\";" "var Msgs=API.messages.getById({\"message_ids\":Mids});" "var FMsgs=Msgs.items@.fwd_messages;" "var Idx=0;var Uids=[];" "while(IdxresultCode); if (reply->resultCode != 200) return; JSONNode jnRoot; const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot); if (!jnResponse) return; if (!jnResponse["Msgs"]) return; CMStringA mids; int numMessages = jnResponse["Msgs"]["count"].as_int(); const JSONNode &jnMsgs = jnResponse["Msgs"]["items"]; const JSONNode &jnFUsers = jnResponse["fwd_users"]; debugLogA("CVkProto::OnReceiveMessages numMessages = %d", numMessages); for (auto it = jnMsgs.begin(); it != jnMsgs.end(); ++it) { const JSONNode &jnMsg = (*it); if (!jnMsg) { debugLogA("CVkProto::OnReceiveMessages pMsg == NULL"); break; } UINT mid = jnMsg["id"].as_int(); CMStringW wszBody(jnMsg["body"].as_mstring()); int datetime = jnMsg["date"].as_int(); int isOut = jnMsg["out"].as_int(); int isRead = jnMsg["read_state"].as_int(); int uid = jnMsg["user_id"].as_int(); const JSONNode &jnFwdMessages = jnMsg["fwd_messages"]; if (jnFwdMessages) { CMStringW wszFwdMessages = GetFwdMessages(jnFwdMessages, jnFUsers, m_vkOptions.BBCForAttachments()); if (!wszBody.IsEmpty()) wszFwdMessages = L"\n" + wszFwdMessages; wszBody += wszFwdMessages; } CMStringW wszAttachmentDescr; const JSONNode &jnAttachments = jnMsg["attachments"]; if (jnAttachments) { wszAttachmentDescr = GetAttachmentDescr(jnAttachments, m_vkOptions.BBCForAttachments()); if (!wszBody.IsEmpty()) wszBody += L"\n"; wszBody += wszAttachmentDescr; } MCONTACT hContact = NULL; int chat_id = jnMsg["chat_id"].as_int(); if (chat_id == 0) hContact = FindUser(uid, true); char szMid[40]; _itoa(mid, szMid, 10); if (m_vkOptions.iMarkMessageReadOn == MarkMsgReadOn::markOnReceive || chat_id != 0) { if (!mids.IsEmpty()) mids.AppendChar(','); mids.Append(szMid); } if (chat_id != 0) { debugLogA("CVkProto::OnReceiveMessages chat_id != 0"); CMStringW action_chat = jnMsg["action"].as_mstring(); int action_mid = _wtoi(jnMsg["action_mid"].as_mstring()); if ((action_chat == L"chat_kick_user") && (action_mid == m_myUserId)) KickFromChat(chat_id, uid, jnMsg, jnFUsers); else { MCONTACT chatContact = FindChat(chat_id); if (chatContact && getBool(chatContact, "kicked", true)) db_unset(chatContact, m_szModuleName, "kicked"); AppendChatMessage(chat_id, jnMsg, jnFUsers, false); } continue; } PROTORECVEVENT recv = { 0 }; bool bUseServerReadFlag = m_vkOptions.bSyncReadMessageStatusFromServer ? true : !m_vkOptions.bMesAsUnread; if (isRead && bUseServerReadFlag) recv.flags |= PREF_CREATEREAD; if (isOut) recv.flags |= PREF_SENT; else if (m_vkOptions.bUserForceInvisibleOnActivity && time(NULL) - datetime < 60 * m_vkOptions.iInvisibleInterval) SetInvisible(hContact); T2Utf pszBody(wszBody); recv.timestamp = m_vkOptions.bUseLocalTime ? time(NULL) : datetime; recv.szMessage = pszBody; recv.lParam = isOut; recv.pCustomData = szMid; recv.cbCustomDataSize = (int)mir_strlen(szMid); Sleep(100); debugLogA("CVkProto::OnReceiveMessages mid = %d, datetime = %d, isOut = %d, isRead = %d, uid = %d", mid, datetime, isOut, isRead, uid); if (!CheckMid(m_sendIds, mid)) { debugLogA("CVkProto::OnReceiveMessages ProtoChainRecvMsg"); ProtoChainRecvMsg(hContact, &recv); if (mid > getDword(hContact, "lastmsgid", -1)) setDword(hContact, "lastmsgid", mid); if (!isOut) m_incIds.insert((HANDLE)mid); } else if (m_vkOptions.bLoadSentAttachments && !wszAttachmentDescr.IsEmpty() && isOut) { T2Utf pszAttach(wszAttachmentDescr); recv.timestamp = time(NULL); // only local time recv.szMessage = pszAttach; ProtoChainRecvMsg(hContact, &recv); } } if (!mids.IsEmpty()) MarkMessagesRead(mids); } void CVkProto::OnReceiveDlgs(NETLIBHTTPREQUEST *reply, AsyncHttpRequest *pReq) { debugLogA("CVkProto::OnReceiveDlgs %d", reply->resultCode); if (reply->resultCode != 200) return; JSONNode jnRoot; const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot); if (!jnResponse) return; const JSONNode &jnDlgs = jnResponse["items"]; if (!jnDlgs) return; CMStringA szGroupIds; for (auto it = jnDlgs.begin(); it != jnDlgs.end(); ++it) { if (!(*it)) break; int numUnread = (*it)["unread"].as_int(); const JSONNode &jnDlg = (*it)["message"]; if (jnDlg == NULL) break; int uid = 0; MCONTACT hContact(NULL); int chatid = jnDlg["chat_id"].as_int(); if (!chatid) { uid = jnDlg["user_id"].as_int(); hContact = FindUser(uid, true); if (IsGroupUser(hContact)) szGroupIds.AppendFormat(szGroupIds.IsEmpty() ? "%d" : ",%d", -1 * uid); if (ServiceExists(MS_MESSAGESTATE_UPDATE)) { time_t tLastReadMessageTime = jnDlg["date"].as_int(); bool isOut = jnDlg["out"].as_bool(); bool isRead = jnDlg["read_state"].as_bool(); if (isRead && isOut) { MessageReadData data(tLastReadMessageTime, MRD_TYPE_MESSAGETIME); CallService(MS_MESSAGESTATE_UPDATE, hContact, (LPARAM)&data); } } } if (chatid) { debugLogA("CVkProto::OnReceiveDlgs chatid = %d", chatid); if (m_chats.find((CVkChatInfo*)&chatid) == NULL) AppendChat(chatid, jnDlg); } else if (m_vkOptions.iSyncHistoryMetod) { int mid = jnDlg["id"].as_int(); m_bNotifyForEndLoadingHistory = false; if (getDword(hContact, "lastmsgid", -1) == -1 && numUnread) GetServerHistory(hContact, 0, numUnread, 0, 0, true); else GetHistoryDlg(hContact, mid); if (m_vkOptions.iMarkMessageReadOn == MarkMsgReadOn::markOnReceive && numUnread) MarkMessagesRead(hContact); } else if (numUnread) { m_bNotifyForEndLoadingHistory = false; GetServerHistory(hContact, 0, numUnread, 0, 0, true); if (m_vkOptions.iMarkMessageReadOn == MarkMsgReadOn::markOnReceive) MarkMessagesRead(hContact); } } RetrieveUsersInfo(); RetrieveGroupInfo(szGroupIds); }