/*
Copyright (c) 2013-24 Miranda NG team (https://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"
static int sttCompareAsyncHttpRequest(const AsyncHttpRequest *p1, const AsyncHttpRequest *p2)
{
if (p1->m_priority == p2->m_priority)
return (int)p1->m_uReqNum - (int)p2->m_uReqNum;
return (int)p2->m_priority - (int)p1->m_priority;
}
mir_cs csInstances;
CVkProto::CVkProto(const char *szModuleName, const wchar_t *pwszUserName) :
PROTO(szModuleName, pwszUserName),
m_arRequestsQueue(10, sttCompareAsyncHttpRequest),
m_cookies(5),
m_iMsgId(1),
m_chats(1, NumericKeySortT),
m_ChatsTyping(1, NumericKeySortT),
m_iLoadHistoryTask(0),
m_bNotifyForEndLoadingHistory(false),
m_bNotifyForEndLoadingHistoryAllContact(false),
m_hAPIConnection(nullptr),
m_hPollingConn(nullptr),
m_bSetBroadcast(false),
m_bNeedSendOnline(false),
m_bErr404Return(false),
m_vkOptions(this)
{
bIint64IDCompatibility = getBool("Iint64IDCompatibility");
m_tWorkThreadTimer = m_tPoolThreadTimer = time(0);
InitQueue();
CheckUpdate();
CreateProtoService(PS_GETAVATARINFO, &CVkProto::SvcGetAvatarInfo);
CreateProtoService(PS_GETAVATARCAPS, &CVkProto::SvcGetAvatarCaps);
CreateProtoService(PS_GETMYAVATAR, &CVkProto::SvcGetMyAvatar);
CreateProtoService(PS_SET_LISTENINGTO, &CVkProto::SvcSetListeningTo);
HookProtoEvent(ME_OPT_INITIALISE, &CVkProto::OnOptionsInit);
NETLIBUSER nlu = {};
nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE;
nlu.szSettingsModule = m_szModuleName;
nlu.szDescriptiveName.w = m_tszUserName;
m_hNetlibUser = Netlib_RegisterUser(&nlu);
CMStringA szListeningTo(FORMAT, "%sEnabled", m_szModuleName);
db_set_b(0, "ListeningTo", szListeningTo, m_vkOptions.iMusicSendMetod == 0 ? 0 : 1);
delSetting("InviteGroupIds");
db_set_resident(m_szModuleName, "ActiveHistoryTask");
// Set all contacts offline -- in case we crashed
SetAllContactStatuses(ID_STATUS_OFFLINE);
// Avatars
CreateDirectoryTreeW(GetAvatarPath());
// Group chats
GCREGISTER gcr = {};
gcr.dwFlags = GC_DATABASE;
gcr.ptszDispName = m_tszUserName;
gcr.pszModule = m_szModuleName;
Chat_Register(&gcr);
CreateProtoService(PS_MENU_LOADHISTORY, &CVkProto::SvcGetAllServerHistoryForContact);
CreateProtoService(PS_EMPTY_SRV_HISTORY, &CVkProto::SvcEmptyServerHistory);
CreateProtoService(PS_LEAVECHAT, &CVkProto::OnLeaveChat);
CreateProtoService(PS_JOINCHAT, &CVkProto::OnJoinChat);
HookProtoEvent(ME_GC_EVENT, &CVkProto::OnChatEvent);
HookProtoEvent(ME_GC_BUILDMENU, &CVkProto::OnGcMenuHook);
}
CVkProto::~CVkProto()
{
debugLogA("CVkProto::~CVkProto");
UninitQueue();
UnInitMenus();
if (m_hPopupClassError)
Popup_UnregisterClass(m_hPopupClassError);
if (m_hPopupClassNotification)
Popup_UnregisterClass(m_hPopupClassNotification);
}
void CVkProto::OnModulesLoaded()
{
Clist_GroupCreate(0, m_vkOptions.pwszDefaultGroup);
// Other hooks
HookProtoEvent(ME_MSG_WINDOWEVENT, &CVkProto::OnProcessSrmmEvent);
HookProtoEvent(ME_DB_CONTACT_SETTINGCHANGED, &CVkProto::OnDbSettingChanged);
//Sounds
g_plugin.addSound("VKNewsFeed", m_tszUserName, LPGENW("Newsfeed & notification event"));
InitPopups();
InitMenus();
InitDBCustomEvents();
InitSmileys();
}
/////////////////////////////////////////////////////////////////////////////////////////
void CVkProto::InitSmileys()
{
if (!m_vkOptions.bStikersAsSmileys)
return;
if (m_vkOptions.bUseStikersAsStaticSmileys)
return;
CMStringW wszPath(FORMAT, L"%s\\%S\\Stickers\\*.png", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName);
SmileyAdd_LoadContactSmileys(SMADD_FOLDER, m_szModuleName, wszPath);
}
// Menu support
void CVkProto::OnBuildProtoMenu()
{
for (auto &it : m_hProtoMenuItems)
it = nullptr;
CMenuItem mi(&g_plugin);
mi.root = Menu_GetProtocolRoot(this);
// Proto menu
if (m_vkOptions.bShowProtoMenuItem0) {
mi.pszService = PS_CREATECHAT;
mi.position = 10009 + PMI_CREATECHAT;
mi.hIcolibItem = Skin_GetIconHandle(SKINICON_CHAT_JOIN);
mi.name.a = LPGEN("Create new chat");
SET_UID(mi, 0xc217748d, 0x7218, 0x4a62, 0xab, 0x37, 0x9, 0x58, 0x6a, 0x88, 0x71, 0x3e);
m_hProtoMenuItems[PMI_CREATECHAT] = Menu_AddProtoMenuItem(&mi, m_szModuleName);
}
if (m_vkOptions.bShowProtoMenuItem1) {
mi.pszService = PS_SETSTATUSMSG;
mi.position = 10009 + PMI_SETSTATUSMSG;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_STATUS);
mi.name.a = LPGEN("Status message");
SET_UID(mi, 0x696bd932, 0xeee4, 0x4c4c, 0xa6, 0xf8, 0xb3, 0x72, 0xdf, 0xc6, 0xfa, 0xd1);
m_hProtoMenuItems[PMI_SETSTATUSMSG] = Menu_AddProtoMenuItem(&mi, m_szModuleName);
}
if (m_vkOptions.bShowProtoMenuItem2) {
mi.pszService = PS_WALLPOST;
mi.position = 10009 + PMI_WALLPOST;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_WALL);
mi.name.a = LPGEN("Send message to my wall");
SET_UID(mi, 0x50a80044, 0xdddd, 0x47e6, 0x9e, 0x90, 0x32, 0x7b, 0x88, 0x13, 0x21, 0x4e);
m_hProtoMenuItems[PMI_WALLPOST] = Menu_AddProtoMenuItem(&mi, m_szModuleName);
}
if (m_vkOptions.bShowProtoMenuItem3) {
mi.pszService = PS_LOADVKNEWS;
mi.position = 10009 + PMI_LOADVKNEWS;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_NOTIFICATION);
mi.name.a = LPGEN("Load news from VK");
SET_UID(mi, 0x7c449456, 0xb731, 0x48cc, 0x9c, 0x4e, 0x20, 0xe4, 0x66, 0x7a, 0x16, 0x23);
m_hProtoMenuItems[PMI_LOADVKNEWS] = Menu_AddProtoMenuItem(&mi, m_szModuleName);
}
if (m_vkOptions.bShowProtoMenuItem4) {
mi.pszService = PS_GETALLSERVERHISTORY;
mi.position = 10009 + PMI_GETALLSERVERHISTORY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.a = LPGEN("Load history for all contacts from VK");
SET_UID(mi, 0xe5028605, 0x92eb, 0x4956, 0xa0, 0xd0, 0x53, 0xb, 0x11, 0x44, 0x8f, 0x14);
m_hProtoMenuItems[PMI_GETALLSERVERHISTORY] = Menu_AddProtoMenuItem(&mi, m_szModuleName);
}
if (m_vkOptions.bShowProtoMenuItem5) {
mi.pszService = PS_WIPENONFRIENDS;
mi.position = 10009 + PMI_WIPENONFRIENDS;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_FRIENDDEL);
mi.name.a = LPGEN("Wipe contacts missing in friend list");
SET_UID(mi, 0xcfe99159, 0xf237, 0x4546, 0x80, 0x3e, 0x51, 0x88, 0x26, 0x55, 0xdc, 0x5f);
m_hProtoMenuItems[PMI_WIPENONFRIENDS] = Menu_AddProtoMenuItem(&mi, m_szModuleName);
}
if (m_vkOptions.bShowProtoMenuItem6) {
mi.pszService = PS_VISITPROFILE;
mi.position = 10009 + PMI_VISITPROFILE;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_VISITPROFILE);
mi.name.a = LPGEN("Visit profile");
SET_UID(mi, 0x9550515e, 0x2a45, 0x4913, 0x95, 0x1a, 0x1e, 0xfa, 0x7, 0xc6, 0x2d, 0x60);
m_hProtoMenuItems[PMI_VISITPROFILE] = Menu_AddProtoMenuItem(&mi, m_szModuleName);
}
}
void CVkProto::InitMenus()
{
HookProtoEvent(ME_CLIST_PREBUILDCONTACTMENU, &CVkProto::OnPreBuildContactMenu);
if(HookProtoEvent(ME_NS_PREBUILDMENU, &CVkProto::OnPrebuildNSMenu)) {
CreateProtoService(PS_NSEXECMENU, &CVkProto::SvcNSExecMenu);
CMStringA szServiceName(FORMAT, "%s%s", m_szModuleName, PS_NSEXECMENU);
CMenuItem mi(&g_plugin);
mi.pszService = szServiceName;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_FORWARD);
mi.position = NS_PROTO_MENU_POS + NSMI_FORWARD;
mi.name.a = LPGEN("Forward");
m_hNewStoryMenuItems[NSMI_FORWARD] = Menu_AddNewStoryMenuItem(&mi, NSMI_FORWARD);
mi.hIcolibItem = g_plugin.getIconHandle(IDI_RELOADMESSAGE);
mi.position = NS_PROTO_MENU_POS + NSMI_RELOADMESSAGE;
mi.name.a = LPGEN("Reload messages");
m_hNewStoryMenuItems[NSMI_RELOADMESSAGE] = Menu_AddNewStoryMenuItem(&mi, NSMI_RELOADMESSAGE);
};
//Contact Menu Services
CreateProtoService(PS_GETSERVERHISTORYLAST1DAY, &CVkProto::SvcGetServerHistoryLastNDay<1>);
CreateProtoService(PS_GETSERVERHISTORYLAST3DAY, &CVkProto::SvcGetServerHistoryLastNDay<3>);
CreateProtoService(PS_GETSERVERHISTORYLAST7DAY, &CVkProto::SvcGetServerHistoryLastNDay<7>);
CreateProtoService(PS_GETSERVERHISTORYLAST30DAY, &CVkProto::SvcGetServerHistoryLastNDay<30>);
CreateProtoService(PS_GETSERVERHISTORYLAST90DAY, &CVkProto::SvcGetServerHistoryLastNDay<90>);
CreateProtoService(PS_GETALLSERVERHISTORYFORCONTACT, &CVkProto::SvcGetAllServerHistoryForContact);
CreateProtoService(PS_GETALLSERVERHISTORY, &CVkProto::SvcGetAllServerHistory);
CreateProtoService(PS_VISITPROFILE, &CVkProto::SvcVisitProfile);
CreateProtoService(PS_CREATECHAT, &CVkProto::SvcCreateChat);
CreateProtoService(PS_ADDASFRIEND, &CVkProto::SvcAddAsFriend);
CreateProtoService(PS_DELETEFRIEND, &CVkProto::SvcDeleteFriend);
CreateProtoService(PS_BANUSER, &CVkProto::SvcBanUser);
CreateProtoService(PS_REPORTABUSE, &CVkProto::SvcReportAbuse);
CreateProtoService(PS_OPENBROADCAST, &CVkProto::SvcOpenBroadcast);
CreateProtoService(PS_LOADVKNEWS, &CVkProto::SvcLoadVKNews);
CreateProtoService(PS_WIPENONFRIENDS, &CVkProto::SvcWipeNonFriendContacts);
CreateProtoService(PS_SETSTATUSMSG, &CVkProto::SvcSetStatusMsg);
CreateProtoService(PS_WALLPOST, &CVkProto::SvcWallPost);
CreateProtoService(PS_MARKMESSAGESASREAD, &CVkProto::SvcMarkMessagesAsRead);
CreateProtoService(PS_CHATCHANGETOPIC, &CVkProto::SvcChatChangeTopic);
CreateProtoService(PS_CHATINVITEUSER, &CVkProto::SvcChatInviteUser);
CreateProtoService(PS_CHATDESTROY, &CVkProto::SvcChatDestroy);
// Contact Menu Items
CMenuItem mi(&g_plugin);
mi.flags = CMIF_UNICODE;
mi.pszService = PS_VISITPROFILE;
mi.position = -200001000 + CMI_VISITPROFILE;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_VISITPROFILE);
mi.name.w = LPGENW("Visit profile");
SET_UID(mi, 0x828cc50e, 0x398d, 0x43a2, 0xbf, 0xd3, 0xa9, 0x96, 0x47, 0x9d, 0x52, 0xff);
m_hContactMenuItems[CMI_VISITPROFILE] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_MARKMESSAGESASREAD;
mi.position = -200001000 + CMI_MARKMESSAGESASREAD;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_MARKMESSAGESASREAD);
mi.name.w = LPGENW("Mark messages as read");
SET_UID(mi, 0x2587a649, 0xe5d5, 0x4e90, 0x8b, 0x35, 0x81, 0x4c, 0xb1, 0x5, 0x94, 0x7);
m_hContactMenuItems[CMI_MARKMESSAGESASREAD] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_WALLPOST;
mi.position = -200001000 + CMI_WALLPOST;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_WALL);
mi.name.w = LPGENW("Send message to user's wall");
SET_UID(mi, 0xd8841aaf, 0x15f6, 0x4be9, 0x9f, 0x4f, 0x16, 0xa9, 0x47, 0x6a, 0x19, 0x81);
m_hContactMenuItems[CMI_WALLPOST] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_ADDASFRIEND;
mi.position = -200001000 + CMI_ADDASFRIEND;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_FRIENDADD);
mi.name.w = LPGENW("Add as friend");
SET_UID(mi, 0xf11b9a7f, 0x569, 0x4023, 0xb0, 0xd6, 0xa3, 0x16, 0xf6, 0xd4, 0xfb, 0xb5);
m_hContactMenuItems[CMI_ADDASFRIEND] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_DELETEFRIEND;
mi.position = -200001000 + CMI_DELETEFRIEND;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_FRIENDDEL);
mi.name.w = LPGENW("Delete from friend list");
SET_UID(mi, 0x1e26514, 0x854f, 0x4e60, 0x8c, 0xf8, 0xab, 0xaa, 0xe0, 0xc3, 0xa5, 0xa7);
m_hContactMenuItems[CMI_DELETEFRIEND] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_BANUSER;
mi.position = -200001000 + CMI_BANUSER;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_BAN);
mi.name.w = LPGENW("Ban user");
SET_UID(mi, 0x7ba06bab, 0xf770, 0x4938, 0x9c, 0x76, 0xef, 0x40, 0xbc, 0x55, 0x0, 0x9b);
m_hContactMenuItems[CMI_BANUSER] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_REPORTABUSE;
mi.position = -200001000 + CMI_REPORTABUSE;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_ABUSE);
mi.name.w = LPGENW("Report abuse");
SET_UID(mi, 0x56454cb9, 0xd80, 0x4050, 0xbe, 0xfc, 0x2c, 0xf6, 0x10, 0x2a, 0x7d, 0x19);
m_hContactMenuItems[CMI_REPORTABUSE] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_OPENBROADCAST;
mi.position = -200001000 + CMI_OPENBROADCAST;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_BROADCAST);
mi.name.w = LPGENW("Open broadcast");
SET_UID(mi, 0x85251a06, 0xf734, 0x4985, 0x8c, 0x36, 0x6f, 0x66, 0x46, 0xf9, 0xa0, 0x10);
m_hContactMenuItems[CMI_OPENBROADCAST] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_CHATCHANGETOPIC;
mi.position = -200001000 + CMI_CHATCHANGETOPIC;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_STATUS);
mi.name.w = LPGENW("View/change topic");
SET_UID(mi, 0x104a176, 0xb66c, 0x45b6, 0xb8, 0x51, 0x30, 0x28, 0x36, 0x86, 0xfb, 0x4a);
m_hContactMenuItems[CMI_CHATCHANGETOPIC] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_CHATINVITEUSER;
mi.position = -200001000 + CMI_CHATINVITEUSER;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_FRIENDADD);
mi.name.w = LPGENW("Invite a user");
SET_UID(mi, 0xeeb5b4ce, 0xc151, 0x4f73, 0x8d, 0x75, 0x1a, 0x45, 0x55, 0x4c, 0x5f, 0x52);
m_hContactMenuItems[CMI_CHATINVITEUSER] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_CHATDESTROY;
mi.position = -200001000 + CMI_CHATDESTROY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_FRIENDDEL);
mi.name.w = LPGENW("Destroy room");
SET_UID(mi, 0x4fa6e75a, 0x30cd, 0x4482, 0xae, 0x8f, 0x0, 0x38, 0xd0, 0x17, 0x33, 0xcd);
m_hContactMenuItems[CMI_CHATDESTROY] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_LOADVKNEWS;
mi.position = -200001000 + CMI_LOADVKNEWS;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_NOTIFICATION);
mi.name.w = LPGENW("Load news from VK");
SET_UID(mi, 0xe1f6888b, 0x21ae, 0x409f, 0x82, 0xa2, 0x7b, 0x72, 0xef, 0x47, 0x9, 0xc0);
m_hContactMenuItems[CMI_LOADVKNEWS] = Menu_AddContactMenuItem(&mi, m_szModuleName);
// Sync history menu
mi.pszService = PS_GETSERVERHISTORY;
mi.position = -200001000 + CMI_GETSERVERHISTORY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.w = LPGENW("Reload messages from vk.com...");
SET_UID(mi, 0xc6b59e9f, 0x5250, 0x4146, 0xb6, 0xf3, 0x2d, 0xe1, 0x4, 0x3b, 0x95, 0xf5);
m_hContactMenuItems[CMI_GETSERVERHISTORY] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.root = m_hContactMenuItems[CMI_GETSERVERHISTORY];
mi.pszService = PS_GETSERVERHISTORYLAST1DAY;
mi.position = -200001000 + CMI_GETSERVERHISTORY + 100 + CHMI_GETSERVERHISTORYLAST1DAY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.w = LPGENW("for last 1 day");
SET_UID(mi, 0x508dce88, 0x1a9a, 0x4dd7, 0x90, 0xf4, 0x41, 0x35, 0x7b, 0xc3, 0x17, 0xed);
m_hContactHistoryMenuItems[CHMI_GETSERVERHISTORYLAST1DAY] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_GETSERVERHISTORYLAST3DAY;
mi.position = -200001000 + CMI_GETSERVERHISTORY + 100 + CHMI_GETSERVERHISTORYLAST3DAY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.w = LPGENW("for last 3 days");
SET_UID(mi, 0x9a878764, 0x5bbf, 0x433a, 0xbd, 0x50, 0xa9, 0xb9, 0x16, 0x1f, 0x99, 0x29);
m_hContactHistoryMenuItems[CHMI_GETSERVERHISTORYLAST3DAY] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_GETSERVERHISTORYLAST7DAY;
mi.position = -200001000 + CMI_GETSERVERHISTORY + 100 + CHMI_GETSERVERHISTORYLAST7DAY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.w = LPGENW("for last week");
SET_UID(mi, 0xc6482460, 0xd280, 0x4596, 0x97, 0x4b, 0xf7, 0xfa, 0x6d, 0xe, 0xd5, 0xda);
m_hContactHistoryMenuItems[CHMI_GETSERVERHISTORYLAST7DAY] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_GETSERVERHISTORYLAST30DAY;
mi.position = -200001000 + CMI_GETSERVERHISTORY + 100 + CHMI_GETSERVERHISTORYLAST30DAY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.w = LPGENW("for last 30 days");
SET_UID(mi, 0xc48e8a9f, 0x2860, 0x4d5b, 0xa8, 0xdf, 0xb8, 0x3f, 0xdf, 0x7b, 0xa2, 0xba);
m_hContactHistoryMenuItems[CHMI_GETSERVERHISTORYLAST30DAY] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_GETSERVERHISTORYLAST90DAY;
mi.position = -200001000 + CMI_GETSERVERHISTORY + 100 + CHMI_GETSERVERHISTORYLAST90DAY;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.w = LPGENW("for last 90 days");
SET_UID(mi, 0xd8e30530, 0xa585, 0x4672, 0xa6, 0x39, 0x18, 0xc9, 0xc9, 0xcb, 0xc7, 0x7d);
m_hContactHistoryMenuItems[CHMI_GETSERVERHISTORYLAST90DAY] = Menu_AddContactMenuItem(&mi, m_szModuleName);
mi.pszService = PS_GETALLSERVERHISTORYFORCONTACT;
mi.position = -200001000 + CMI_GETSERVERHISTORY + 100 + CHMI_GETALLSERVERHISTORYFORCONTACT;
mi.hIcolibItem = g_plugin.getIconHandle(IDI_HISTORY);
mi.name.w = LPGENW("for all time");
SET_UID(mi, 0xaee3d02b, 0x3667, 0x47c8, 0x9f, 0x43, 0x14, 0xb7, 0xab, 0x52, 0x14, 0x94);
m_hContactHistoryMenuItems[CHMI_GETALLSERVERHISTORYFORCONTACT] = Menu_AddContactMenuItem(&mi, m_szModuleName);
}
int CVkProto::OnPreBuildContactMenu(WPARAM hContact, LPARAM)
{
VKUserID_t iUserId = ReadVKUserID(hContact);
bool bisFriend = !getBool(hContact, "Auth", true);
bool bisBroadcast = !(IsEmpty(ptrW(db_get_wsa(hContact, m_szModuleName, "AudioUrl"))));
bool bIsGroup = IsGroupUser(hContact);
Menu_ShowItem(m_hContactMenuItems[CMI_VISITPROFILE], iUserId != VK_FEED_USER);
Menu_ShowItem(m_hContactMenuItems[CMI_MARKMESSAGESASREAD], iUserId != VK_FEED_USER);
Menu_ShowItem(m_hContactMenuItems[CMI_WALLPOST], !isChatRoom(hContact));
Menu_ShowItem(m_hContactMenuItems[CMI_ADDASFRIEND], !bisFriend && !isChatRoom(hContact) && iUserId != VK_FEED_USER && !bIsGroup);
Menu_ShowItem(m_hContactMenuItems[CMI_DELETEFRIEND], bisFriend && iUserId != VK_FEED_USER && !bIsGroup);
Menu_ShowItem(m_hContactMenuItems[CMI_BANUSER], !isChatRoom(hContact) && iUserId != VK_FEED_USER && !bIsGroup);
Menu_ShowItem(m_hContactMenuItems[CMI_REPORTABUSE], !isChatRoom(hContact) && iUserId != VK_FEED_USER && !bIsGroup);
Menu_ShowItem(m_hContactMenuItems[CMI_OPENBROADCAST], !isChatRoom(hContact) && bisBroadcast);
Menu_ShowItem(m_hContactMenuItems[CMI_CHATCHANGETOPIC], isChatRoom(hContact));
Menu_ShowItem(m_hContactMenuItems[CMI_CHATINVITEUSER], isChatRoom(hContact));
Menu_ShowItem(m_hContactMenuItems[CMI_CHATDESTROY], isChatRoom(hContact));
Menu_ShowItem(m_hContactMenuItems[CMI_GETSERVERHISTORY], iUserId != VK_FEED_USER);
Menu_ShowItem(m_hContactMenuItems[CMI_LOADVKNEWS], iUserId == VK_FEED_USER);
for (int i = 0; i < CHMI_COUNT; i++)
Menu_ShowItem(m_hContactHistoryMenuItems[i], iUserId != VK_FEED_USER);
return 0;
}
int CVkProto::OnPrebuildNSMenu(WPARAM hContact, LPARAM lParam)
{
auto &dbei = *(DB::EventInfo *)lParam;
Menu_ShowItem(m_hNewStoryMenuItems[NSMI_FORWARD], hContact != 0 && dbei);
Menu_ShowItem(m_hNewStoryMenuItems[NSMI_RELOADMESSAGE], Proto_IsProtoOnContact(hContact, m_szModuleName) && hContact != 0 && dbei);
return 0;
}
INT_PTR CVkProto::SvcNSExecMenu(WPARAM iCommand, LPARAM pHandle)
{
MEVENT hCurrentEvent = NS_GetCurrent((HANDLE)pHandle);
if (hCurrentEvent == -1)
return 1;
std::vector vIds = NS_GetSelection(HANDLE(pHandle));
if (vIds.empty())
return 1;
if (!vIds.size())
vIds.push_back(hCurrentEvent);
std::sort(vIds.begin(), vIds.end());
switch (iCommand) {
case NSMI_FORWARD:
{
wchar_t wszMsg[2048] = L"";
if (auto *pDlg = NS_GetSrmm((HANDLE)pHandle))
GetWindowText(pDlg->GetInput(), wszMsg, 2048);
CVkUserListForm dlg(
this,
wszMsg,
TranslateT("Mark contacts for forwarding messages"),
TranslateT("Mark contacts you want to forward messages"),
TranslateT("Enter accompanying message"),
0
);
if (!dlg.DoModal())
break;
T2Utf pszMsg(dlg.wszMessage.c_str());
for (auto &hContact : dlg.lContacts)
ForwardMsg((UINT_PTR)hContact, vIds, pszMsg);
}
break;
case NSMI_RELOADMESSAGE:
{
CMStringA szIds;
for (auto& mEvnt : vIds) {
DB::EventInfo dbei(mEvnt);
if (!dbei || dbei.eventType != EVENTTYPE_MESSAGE)
continue;
if (!szIds.IsEmpty())
szIds.AppendChar(',');
szIds += dbei.szId;
}
if (!szIds.IsEmpty())
RetrieveMessagesByIds(szIds);
}
break;
}
return 0;
}
void CVkProto::UnInitMenus()
{
debugLogA("CVkProto::UnInitMenus");
for (int i = 0; i < PMI_COUNT; i++)
if (m_hProtoMenuItems[i])
Menu_RemoveItem(m_hProtoMenuItems[i]);
for (int i = 0; i < CHMI_COUNT; i++)
Menu_RemoveItem(m_hContactHistoryMenuItems[i]);
for (int i = 0; i < CMI_COUNT; i++)
Menu_RemoveItem(m_hContactMenuItems[i]);
for (int i = 0; i < NSMI_COUNT; i++)
Menu_RemoveItem(m_hNewStoryMenuItems[i]);
}
/////////////////////////////////////////////////////////////////////////////////////////
// PopUp support
LRESULT CALLBACK PopupDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_COMMAND:
case WM_CONTEXTMENU:
{
CVkSendMsgParam *pd = (CVkSendMsgParam *)PUGetPluginData(hwnd);
if (pd != nullptr && pd->hContact != 0)
CallServiceSync(MS_MSG_SENDMESSAGE, (WPARAM)pd->hContact, 0);
PUDeletePopup(hwnd);
}
break;
case UM_FREEPLUGINDATA:
{
CVkSendMsgParam *pd = (CVkSendMsgParam *)PUGetPluginData(hwnd);
delete pd;
}
return FALSE;
default:
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
void CVkProto::InitPopups(void)
{
wchar_t desc[256];
char name[256];
POPUPCLASS ppc = {};
ppc.flags = PCF_UNICODE;
ppc.pszName = name;
ppc.pszDescription.w = desc;
ppc.PluginWindowProc = PopupDlgProc;
ppc.lParam = APF_RETURN_HWND;
mir_snwprintf(desc, L"%s/%s", m_tszUserName, TranslateT("Errors"));
mir_snprintf(name, "%s_%s", m_szModuleName, "Error");
ppc.hIcon = Skin_LoadIcon(SKINICON_ERROR);
ppc.colorBack = RGB(191, 0, 0); // Red
ppc.colorText = RGB(255, 245, 225); // Yellow
ppc.iSeconds = 60;
m_hPopupClassError = Popup_RegisterClass(&ppc);
mir_snwprintf(desc, L"%s/%s", m_tszUserName, TranslateT("Notifications"));
mir_snprintf(name, "%s_%s", m_szModuleName, "Notification");
ppc.hIcon = g_plugin.getIcon(IDI_NOTIFICATION);
ppc.colorBack = RGB(190, 225, 255); // Blue
ppc.colorText = RGB(255, 255, 255); // White
ppc.iSeconds = 4;
m_hPopupClassNotification = Popup_RegisterClass(&ppc);
}
void CVkProto::MsgPopup(MCONTACT hContact, const wchar_t *wszMsg, const wchar_t *wszTitle, bool err)
{
char name[256];
POPUPDATACLASS ppd = {};
ppd.szTitle.w = wszTitle;
ppd.szText.w = wszMsg;
ppd.pszClassName = name;
ppd.hContact = hContact;
ppd.PluginData = new CVkSendMsgParam(hContact);
mir_snprintf(name, "%s_%s", m_szModuleName, err ? "Error" : "Notification");
Popup_AddClass(&ppd);
}
void CVkProto::MsgPopup(const wchar_t *wszMsg, const wchar_t *wszTitle, bool err)
{
MsgPopup(0, wszMsg, wszTitle, err);
}
//////////////////////////////////////////////////////////////////////////////
void CVkProto::InitDBCustomEvents()
{
// custom event
DBEVENTTYPEDESCR dbEventType = {};
dbEventType.module = m_szModuleName;
dbEventType.flags = DETF_HISTORY | DETF_MSGWINDOW;
dbEventType.eventType = VK_USER_DEACTIVATE_ACTION;
dbEventType.descr = Translate("VK user activate/deactivate action");
DbEvent_RegisterType(&dbEventType);
}
//////////////////////////////////////////////////////////////////////////////
void CVkProto::OnShutdown()
{
debugLogA("CVkProto::OnPreShutdown");
m_bTerminated = true;
SetEvent(m_hEvRequestsQueue);
}
//////////////////////////////////////////////////////////////////////////////
INT_PTR CVkProto::GetCaps(int type, MCONTACT)
{
switch (type) {
case PFLAGNUM_1:
return PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ | PF1_BASICSEARCH
| PF1_SEARCHBYNAME | PF1_MODEMSG | PF1_FILESEND | PF1_FILERESUME;
case PFLAGNUM_2:
return PF2_ONLINE | PF2_INVISIBLE | PF2_IDLE;
case PFLAGNUM_3:
return PF2_ONLINE;
case PFLAGNUM_4:
return PF4_AVATARS | PF4_SUPPORTTYPING | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE
| PF4_OFFLINEFILES | PF4_READNOTIFY | PF4_GROUPCHATFILES | PF4_SERVERMSGID | PF4_REPLY;
case PFLAG_MAXLENOFMESSAGE:
return 4096;
case PFLAG_UNIQUEIDTEXT:
return (INT_PTR)L"VKontakte ID";
}
return 0;
}
//////////////////////////////////////////////////////////////////////////////
MCONTACT CVkProto::AddToList(int, PROTOSEARCHRESULT *psr)
{
debugLogA("CVkProto::AddToList");
VKUserID_t iUserId = _wtol(psr->id.w);
if (!iUserId)
return 0;
MCONTACT hContact = FindUser(iUserId, true);
Contact::Hide(hContact, false);
RetrieveUserInfo(iUserId);
return hContact;
}
int CVkProto::AuthRequest(MCONTACT hContact, const wchar_t *message)
{
debugLogA("CVkProto::AuthRequest");
if (!IsOnline())
return 1;
VKUserID_t iUserId = ReadVKUserID(hContact);
if (iUserId == VK_INVALID_USER || !hContact || iUserId == VK_FEED_USER)
return 1;
if (iUserId < 0)
return 1;
wchar_t msg[501] = { 0 };
if (message)
wcsncpy_s(msg, _countof(msg), message, _TRUNCATE);
Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/friends.add.json", true, &CVkProto::OnReceiveAuthRequest)
<< INT_PARAM("user_id", iUserId)
<< WCHAR_PARAM("text", msg))->pUserInfo = new CVkSendMsgParam(hContact);
return 0;
}
void CVkProto::OnReceiveAuthRequest(MHttpResponse *reply, AsyncHttpRequest *pReq)
{
debugLogA("CVkProto::OnReceiveAuthRequest %d", reply->resultCode);
CVkSendMsgParam *param = (CVkSendMsgParam*)pReq->pUserInfo;
if (reply->resultCode == 200 && param) {
JSONNode jnRoot;
const JSONNode &jnResponse = CheckJsonResponse(pReq, reply, jnRoot);
if (jnResponse) {
int iRet = jnResponse.as_int();
setByte(param->hContact, "Auth", 0);
if (iRet == 2) {
CMStringW msg, wszNick(db_get_wsm(param->hContact, m_szModuleName, "Nick"));
if (wszNick.IsEmpty())
wszNick = TranslateT("(Unknown contact)");
msg.AppendFormat(TranslateT("User %s added as friend"), wszNick.c_str());
MsgPopup(param->hContact, msg, wszNick);
}
}
else {
switch (pReq->m_iErrorCode) {
case VKERR_HIMSELF_AS_FRIEND:
MsgPopup(param->hContact, TranslateT("You cannot add yourself as friend"), TranslateT("Error"), true);
break;
case VKERR_YOU_ON_BLACKLIST:
MsgPopup(param->hContact, TranslateT("Cannot add this user to friends as they have put you on their blacklist"), TranslateT("Error"), true);
break;
case VKERR_USER_ON_BLACKLIST:
MsgPopup(param->hContact, TranslateT("Cannot add this user to friends as you put him on blacklist"), TranslateT("Error"), true);
break;
}
}
}
if (param && (!pReq->bNeedsRestart || m_bTerminated)) {
delete param;
pReq->pUserInfo = nullptr;
}
}
int CVkProto::Authorize(MEVENT hDbEvent)
{
debugLogA("CVkProto::Authorize");
if (!IsOnline())
return 1;
MCONTACT hContact = MContactFromDbEvent(hDbEvent);
if (hContact == INVALID_CONTACT_ID)
return 1;
return AuthRequest(hContact, nullptr);
}
int CVkProto::AuthDeny(MEVENT hDbEvent, const wchar_t*)
{
debugLogA("CVkProto::AuthDeny");
if (!IsOnline())
return 1;
MCONTACT hContact = MContactFromDbEvent(hDbEvent);
if (hContact == INVALID_CONTACT_ID)
return 1;
return SvcDeleteFriend(hContact, (LPARAM)true);
}
int CVkProto::UserIsTyping(MCONTACT hContact, int type)
{
debugLogA("CVkProto::UserIsTyping");
if (PROTOTYPE_SELFTYPING_ON == type) {
VKUserID_t iUserId = ReadVKUserID(hContact);
if (iUserId == VK_INVALID_USER || !IsOnline() || iUserId == VK_FEED_USER)
return 1;
if (!IsEmpty(ptrW(db_get_wsa(hContact, m_szModuleName, "Deactivated"))))
return 1;
if (m_vkOptions.iMarkMessageReadOn == MarkMsgReadOn::markOnTyping)
MarkMessagesRead(hContact);
if (m_iStatus == ID_STATUS_INVISIBLE)
return 1;
Push(new AsyncHttpRequest(this, REQUEST_GET, "/method/messages.setActivity.json", true, &CVkProto::OnReceiveSmth, AsyncHttpRequest::rpLow)
<< INT_PARAM("user_id", iUserId)
<< CHAR_PARAM("type", "typing"));
return 0;
}
return 1;
}
int CVkProto::GetInfo(MCONTACT hContact, int)
{
debugLogA("CVkProto::GetInfo");
VKUserID_t iUserId = ReadVKUserID(hContact);
if (iUserId == VK_INVALID_USER || iUserId == VK_FEED_USER)
return 1;
RetrieveUserInfo(iUserId);
return 0;
}
bool CVkProto::OnContactDeleted(MCONTACT hContact, uint32_t flags)
{
ptrW pwszNick(db_get_wsa(hContact, m_szModuleName, "Nick"));
debugLogW(L"CVkProto::OnContactDeleted %s %d", Clist_GetContactDisplayName(hContact), flags);
if (!Contact::OnList(hContact) || getBool(hContact, "SilentDelete") || isChatRoom((MCONTACT)hContact))
return true;
if (flags & CDF_DEL_CONTACT && !getBool(hContact, "Auth", true))
SvcDeleteFriend(hContact, 1);
return true;
}