diff options
-rw-r--r-- | protocols/Facebook/res/facebook.rc | 9 | ||||
-rw-r--r-- | protocols/Facebook/src/groupchats.cpp | 235 | ||||
-rw-r--r-- | protocols/Facebook/src/options.cpp | 2 | ||||
-rw-r--r-- | protocols/Facebook/src/proto.cpp | 2 | ||||
-rw-r--r-- | protocols/Facebook/src/proto.h | 14 | ||||
-rw-r--r-- | protocols/Facebook/src/resource.h | 8 | ||||
-rw-r--r-- | protocols/Facebook/src/server.cpp | 2 | ||||
-rw-r--r-- | protocols/Facebook/src/stdafx.h | 3 |
8 files changed, 268 insertions, 7 deletions
diff --git a/protocols/Facebook/res/facebook.rc b/protocols/Facebook/res/facebook.rc index 825fea836d..5044c073ae 100644 --- a/protocols/Facebook/res/facebook.rc +++ b/protocols/Facebook/res/facebook.rc @@ -93,6 +93,15 @@ BEGIN "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,130,277,10 END +IDD_GROUPCHAT_INVITE DIALOGEX 0, 0, 215, 263 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Send group chat invitation" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_CLIST,"CListControl",WS_TABSTOP | 0x1,7,7,201,231,WS_EX_CLIENTEDGE + DEFPUSHBUTTON "&Invite",IDOK,104,243,50,14 + PUSHBUTTON "Cancel",IDCANCEL,158,243,50,14 +END ///////////////////////////////////////////////////////////////////////////// // diff --git a/protocols/Facebook/src/groupchats.cpp b/protocols/Facebook/src/groupchats.cpp new file mode 100644 index 0000000000..281fc49611 --- /dev/null +++ b/protocols/Facebook/src/groupchats.cpp @@ -0,0 +1,235 @@ +/* + +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 <http://www.gnu.org/licenses/>. + +*/ + +#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(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 +}; + +static gc_item sttLogListItems[] = +{ + { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM }, + { nullptr, 0, MENU_SEPARATOR }, + { LPGENW("&Leave/destroy chat"), IDM_LEAVE, 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; + + SESSION_INFO *si = g_chatApi.SM_FindSession(gcmi->pszID, gcmi->pszModule); + if (si == nullptr) + return 0; + + if (gcmi->Type == MENU_ON_LOG) + Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin); + + return 0; +} + +int FacebookProto::GroupchatEventHook(WPARAM, LPARAM lParam) +{ + GCHOOK *gch = (GCHOOK *)lParam; + if (gch == nullptr) + return 0; + + if (mir_strcmpi(gch->pszModule, m_szModuleName)) + return 0; + + SESSION_INFO *si = g_chatApi.SM_FindSession(gch->ptszID, gch->pszModule); + if (si == nullptr) + return 0; + + 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); + SendMsg(si->hContact, 0, T2Utf(wszText)); + } + break; + + case GC_USER_PRIVMESS: + Chat_SendPrivateMessage(gch); + break; + + case GC_USER_LOGMENU: + Chat_ProcessLogMenu(si, gch->dwData); + break; + } + + return 0; +} + +void FacebookProto::Chat_ProcessLogMenu(SESSION_INFO *si, int iChoice) +{ + switch (iChoice) { + case IDM_INVITE: + Chat_InviteUser(si); + break; + + case IDM_LEAVE: + Chat_Leave(si); + 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<SESSION_INFO>(DestroyRoomThread, si); +} diff --git a/protocols/Facebook/src/options.cpp b/protocols/Facebook/src/options.cpp index 9e9f778298..cb353caf70 100644 --- a/protocols/Facebook/src/options.cpp +++ b/protocols/Facebook/src/options.cpp @@ -20,8 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "stdafx.h" -typedef CProtoDlgBase<FacebookProto> CFBDlgBase; - class CFacebookAccOptsDlg : public CFBDlgBase { CCtrlEdit edtGroup; diff --git a/protocols/Facebook/src/proto.cpp b/protocols/Facebook/src/proto.cpp index 0e1697705b..dd7ef0903d 100644 --- a/protocols/Facebook/src/proto.cpp +++ b/protocols/Facebook/src/proto.cpp @@ -106,6 +106,8 @@ FacebookProto::FacebookProto(const char *proto_name, const wchar_t *username) : CreateProtoService(PS_GETAVATARCAPS, &FacebookProto::GetAvatarCaps); // Events + HookProtoEvent(ME_GC_EVENT, &FacebookProto::GroupchatEventHook); + HookProtoEvent(ME_GC_BUILDMENU, &FacebookProto::GroupchatMenuHook); HookProtoEvent(ME_OPT_INITIALISE, &FacebookProto::OnOptionsInit); // Default group diff --git a/protocols/Facebook/src/proto.h b/protocols/Facebook/src/proto.h index 4f6923431d..950408c34b 100644 --- a/protocols/Facebook/src/proto.h +++ b/protocols/Facebook/src/proto.h @@ -368,6 +368,8 @@ struct COwnMessage class FacebookProto : public PROTO<FacebookProto> { + friend class CGroupchatInviteDlg; + class FacebookImpl { friend class FacebookProto; @@ -401,6 +403,13 @@ class FacebookProto : public PROTO<FacebookProto> void __cdecl AvatarsUpdate(void *); void GetAvatarFilename(MCONTACT hContact, wchar_t *pwszFileName); + // Group chats + void Chat_InviteUser(SESSION_INFO *si); + int Chat_KickUser(SESSION_INFO *si, const wchar_t *pwszUid); + void Chat_Leave(SESSION_INFO *si); + void Chat_SendPrivateMessage(GCHOOK *gch); + void Chat_ProcessLogMenu(SESSION_INFO *si, int iChoice); + // MQTT void MqttLogin(); @@ -496,6 +505,9 @@ public: // Events int __cdecl OnOptionsInit(WPARAM, LPARAM); + + int __cdecl GroupchatMenuHook(WPARAM, LPARAM); + int __cdecl GroupchatEventHook(WPARAM, LPARAM); //////////////////////////////////////////////////////////////////////////////////////// // Services @@ -505,6 +517,8 @@ public: INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM); }; +typedef CProtoDlgBase<FacebookProto> CFBDlgBase; + struct CMPlugin : public ACCPROTOPLUGIN<FacebookProto> { CMPlugin(); diff --git a/protocols/Facebook/src/resource.h b/protocols/Facebook/src/resource.h index 86512c93e7..b0819aeff8 100644 --- a/protocols/Facebook/src/resource.h +++ b/protocols/Facebook/src/resource.h @@ -4,7 +4,8 @@ // #define IDI_FACEBOOK 101 #define IDD_OPTIONS 102 -#define IDD_FACEBOOKACCOUNT 111 +#define IDD_GROUPCHAT_INVITE 103 +#define IDD_FACEBOOKACCOUNT 104 #define IDC_EDIT1 1000 #define IDC_UN 1001 #define IDC_PW 1002 @@ -12,14 +13,15 @@ #define IDC_GROUP 1004 #define IDC_ENABLECHATS 1005 #define IDC_HIDECHATS 1006 +#define IDC_CLIST 1007 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1006 +#define _APS_NEXT_CONTROL_VALUE 1008 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/protocols/Facebook/src/server.cpp b/protocols/Facebook/src/server.cpp index 2dd06bab7b..2e21d01bf1 100644 --- a/protocols/Facebook/src/server.cpp +++ b/protocols/Facebook/src/server.cpp @@ -175,7 +175,7 @@ void FacebookProto::RefreshThreads() if (!n["is_group_thread"].as_bool()) continue; - CMStringW chatId(n["id"].as_mstring()); + CMStringW chatId(n["thread_key"]["thread_fbid"].as_mstring()); CMStringW name(n["name"].as_mstring()); auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, chatId, name); diff --git a/protocols/Facebook/src/stdafx.h b/protocols/Facebook/src/stdafx.h index 8a9e9e579c..e591cc3ec3 100644 --- a/protocols/Facebook/src/stdafx.h +++ b/protocols/Facebook/src/stdafx.h @@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #pragma once #include <windows.h> +#include <malloc.h> #include <time.h> #include <stdio.h> #include <stdarg.h> @@ -30,7 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include <win2k.h> #include <newpluginapi.h> #include <m_avatars.h> -#include <m_chat.h> +#include <m_chat_int.h> #include <m_clistint.h> #include <m_contacts.h> #include <m_database.h> |