/*
Facebook plugin for Miranda NG
Copyright © 2019-20 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 .
*/
#include "stdafx.h"
/////////////////////////////////////////////////////////////////////////////////////////
// Invitation dialog
class CGroupchatInviteDlg : public CFBDlgBase
{
CCtrlClc m_clc;
SESSION_INFO *m_si;
void FilterList(CCtrlClc *)
{
for (auto &hContact : Contacts()) {
char *proto = Proto_GetBaseAccountName(hContact);
if (mir_strcmp(proto, m_proto->m_szModuleName) || m_proto->isChatRoom(hContact))
if (HANDLE hItem = m_clc.FindContact(hContact))
m_clc.DeleteItem(hItem);
}
}
void ResetListOptions(CCtrlClc *)
{
m_clc.SetBkBitmap(0, nullptr);
m_clc.SetBkColor(GetSysColor(COLOR_WINDOW));
m_clc.SetGreyoutFlags(0);
m_clc.SetLeftMargin(4);
m_clc.SetIndent(10);
m_clc.SetHideEmptyGroups(1);
m_clc.SetHideOfflineRoot(1);
for (int i = 0; i <= FONTID_MAX; i++)
m_clc.SetTextColor(i, GetSysColor(COLOR_WINDOWTEXT));
}
public:
CGroupchatInviteDlg(FacebookProto *ppro, SESSION_INFO *si) :
CFBDlgBase(ppro, IDD_GROUPCHAT_INVITE),
m_si(si),
m_clc(this, IDC_CLIST)
{
m_clc.OnNewContact =
m_clc.OnListRebuilt = Callback(this, &CGroupchatInviteDlg::FilterList);
m_clc.OnOptionsChanged = Callback(this, &CGroupchatInviteDlg::ResetListOptions);
}
bool OnInitDialog() override
{
SetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE,
GetWindowLongPtr(m_clc.GetHwnd(), GWL_STYLE) | CLS_SHOWHIDDEN | CLS_HIDEOFFLINE | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES);
m_clc.SendMsg(CLM_SETEXSTYLE, CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT, 0);
ResetListOptions(&m_clc);
FilterList(&m_clc);
return true;
}
bool OnApply() override
{
JSONNode list(JSON_ARRAY);
for (auto &hContact : m_proto->AccContacts()) {
if (m_proto->isChatRoom(hContact))
continue;
if (HANDLE hItem = m_clc.FindContact(hContact)) {
if (m_clc.GetCheck(hItem)) {
JSONNode user; user << CHAR_PARAM("type", "id") << CHAR_PARAM("id", m_proto->getMStringA(hContact, DBKEY_ID));
list << user;
}
}
}
auto *pReq = m_proto->CreateRequest(FB_API_URL_PARTS, "addMembers", "POST");
pReq << CHAR_PARAM("to", list.write().c_str()) << WCHAR_PARAM("id", CMStringW(FORMAT, L"t_%s", m_si->ptszID));
pReq->CalcSig();
JsonReply reply(m_proto->ExecuteRequest(pReq));
return true;
}
};
void FacebookProto::Chat_InviteUser(SESSION_INFO *si)
{
CGroupchatInviteDlg dlg(this, si);
if (si->pDlg)
dlg.SetParent(((CDlgBase *)si->pDlg)->GetHwnd());
dlg.DoModal();
}
/////////////////////////////////////////////////////////////////////////////////////////
// Group chats
enum ChatMenuItems
{
IDM_INVITE = 10, IDM_LEAVE,
IDM_KICK = 20
};
static gc_item sttLogListItems[] =
{
{ LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM },
{ nullptr, 0, MENU_SEPARATOR },
{ LPGENW("&Leave/destroy chat"), IDM_LEAVE, MENU_ITEM },
};
static gc_item sttNickListItems[] =
{
{ LPGENW("&Kick user"), IDM_KICK, MENU_ITEM },
};
int FacebookProto::GroupchatMenuHook(WPARAM, LPARAM lParam)
{
GCMENUITEMS *gcmi = (GCMENUITEMS *)lParam;
if (gcmi == nullptr)
return 0;
if (mir_strcmpi(gcmi->pszModule, m_szModuleName))
return 0;
if (SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule)) {
if (gcmi->Type == MENU_ON_LOG)
Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin);
if (gcmi->Type == MENU_ON_NICKLIST)
Chat_AddMenuItems(gcmi->hMenu, _countof(sttNickListItems), sttNickListItems, &g_plugin);
}
return 0;
}
int FacebookProto::GroupchatEventHook(WPARAM, LPARAM lParam)
{
GCHOOK *gch = (GCHOOK *)lParam;
if (gch == nullptr)
return 0;
if (mir_strcmpi(gch->si->pszModule, m_szModuleName))
return 0;
SESSION_INFO *si = g_chatApi.SM_FindSession(gch->si->ptszID, gch->si->pszModule);
if (si == nullptr)
return 1;
switch (gch->iType) {
case GC_USER_MESSAGE:
rtrimw(gch->ptszText);
if (!mir_wstrlen(gch->ptszText))
break;
if (m_bOnline) {
wchar_t *wszText = NEWWSTR_ALLOCA(gch->ptszText);
Chat_UnescapeTags(wszText);
int mid = SendMsg(si->hContact, 0, T2Utf(wszText));
for (auto &msg : arOwnMessages)
if (msg->reqId == mid)
msg->wszText = wszText;
}
break;
case GC_USER_PRIVMESS:
Chat_SendPrivateMessage(gch);
break;
case GC_USER_LOGMENU:
Chat_ProcessLogMenu(si, gch);
break;
case GC_USER_NICKLISTMENU:
Chat_ProcessNickMenu(si, gch);
break;
}
return 1;
}
void FacebookProto::Chat_ProcessLogMenu(SESSION_INFO *si, GCHOOK *gch)
{
switch (gch->dwData) {
case IDM_INVITE:
Chat_InviteUser(si);
break;
case IDM_LEAVE:
Chat_Leave(si);
break;
}
}
void FacebookProto::Chat_ProcessNickMenu(SESSION_INFO *si, GCHOOK *gch)
{
switch (gch->dwData) {
case IDM_KICK:
Chat_KickUser(si, gch->ptszUID);
break;
}
}
void FacebookProto::Chat_SendPrivateMessage(GCHOOK *gch)
{
auto *pUser = FindUser(_wtoi64(gch->ptszUID));
if (pUser == nullptr) {
pUser = AddContact(gch->ptszUID, true);
setWString(pUser->hContact, "Nick", gch->ptszNick);
db_set_b(pUser->hContact, "CList", "Hidden", 1);
db_set_dw(pUser->hContact, "Ignore", "Mask1", 0);
}
CallService(MS_MSG_SENDMESSAGE, pUser->hContact, 0);
}
int FacebookProto::Chat_KickUser(SESSION_INFO *si, const wchar_t *pwszUid)
{
auto *pReq = CreateRequest(FB_API_URL_PARTS, "removeMembers", "DELETE");
pReq << WCHAR_PARAM("id", CMStringW(FORMAT, L"t_%s", si->ptszID));
if (pwszUid != nullptr) {
JSONNode list(JSON_ARRAY);
JSONNode user; user << CHAR_PARAM("type", "id") << WCHAR_PARAM("id", pwszUid);
list << user;
pReq << CHAR_PARAM("to", list.write().c_str());
}
pReq->CalcSig();
JsonReply reply(ExecuteRequest(pReq));
return reply.error();
}
/////////////////////////////////////////////////////////////////////////////////////////
static void __cdecl DestroyRoomThread(SESSION_INFO *si)
{
::Sleep(100);
Chat_Terminate(si->pszModule, si->ptszID, true);
}
void FacebookProto::Chat_Leave(SESSION_INFO *si)
{
if (Chat_KickUser(si, nullptr) == 0)
mir_forkThread(DestroyRoomThread, si);
}