diff options
author | George Hazan <ghazan@miranda.im> | 2019-01-11 19:36:19 +0200 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-01-11 19:36:19 +0200 |
commit | 506986c49afe013ec1d54f8b8eadcf318bfbab4d (patch) | |
tree | 5431d993c83f03201f40e0d629545008a052ef81 /protocols | |
parent | 90f88c6910ad50f363fcbf9d6d2b22d7b6a7b34e (diff) |
ICQ10: ability to invite users to groupchats
Diffstat (limited to 'protocols')
-rw-r--r-- | protocols/Icq10/res/resources.rc | 19 | ||||
-rw-r--r-- | protocols/Icq10/src/groupchats.cpp | 286 | ||||
-rw-r--r-- | protocols/Icq10/src/proto.cpp | 84 | ||||
-rw-r--r-- | protocols/Icq10/src/proto.h | 19 | ||||
-rw-r--r-- | protocols/Icq10/src/resource.h | 2 | ||||
-rw-r--r-- | protocols/Icq10/src/server.cpp | 60 |
6 files changed, 323 insertions, 147 deletions
diff --git a/protocols/Icq10/res/resources.rc b/protocols/Icq10/res/resources.rc index 7fd47458c3..db9bcc84e6 100644 --- a/protocols/Icq10/res/resources.rc +++ b/protocols/Icq10/res/resources.rc @@ -112,6 +112,16 @@ BEGIN CONTROL "Hide group chats on startup",IDC_HIDECHATS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,57,285,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 + ///////////////////////////////////////////////////////////////////////////// // @@ -129,6 +139,10 @@ BEGIN BEGIN BOTTOMMARGIN, 78 END + + IDD_GROUPCHAT_INVITE, DIALOG + BEGIN + END END #endif // APSTUDIO_INVOKED @@ -148,6 +162,11 @@ BEGIN 0 END +IDD_GROUPCHAT_INVITE AFX_DIALOG_LAYOUT +BEGIN + 0 +END + #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/protocols/Icq10/src/groupchats.cpp b/protocols/Icq10/src/groupchats.cpp new file mode 100644 index 0000000000..4ba45ac6e4 --- /dev/null +++ b/protocols/Icq10/src/groupchats.cpp @@ -0,0 +1,286 @@ +// ----------------------------------------------------------------------------- +// ICQ plugin for Miranda NG +// ----------------------------------------------------------------------------- +// Copyright © 2018-19 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// ----------------------------------------------------------------------------- + +#include "stdafx.h" + +void CIcqProto::LoadChatInfo(SESSION_INFO *si) +{ + int memberCount = getDword(si->hContact, "MemberCount"); + for (int i = 0; i < memberCount; i++) { + char buf[100]; + mir_snprintf(buf, "m%d", i); + ptrW szSetting(getWStringA(si->hContact, buf)); + JSONNode *node = json_parse(T2Utf(szSetting)); + if (node == nullptr) + continue; + + CMStringW nick((*node)["nick"].as_mstring()); + CMStringW role((*node)["role"].as_mstring()); + CMStringW sn((*node)["sn"].as_mstring()); + + GCEVENT gce = { m_szModuleName, si->ptszID, GC_EVENT_JOIN }; + gce.ptszNick = nick; + gce.ptszUID = sn; + gce.time = ::time(0); + gce.bIsMe = _wtoi(sn) == (int)m_dwUin; + gce.ptszStatus = TranslateW(role); + Chat_Event(&gce); + + json_delete(node); + } +} + +void CIcqProto::OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) +{ + SESSION_INFO *si = (SESSION_INFO*)pReq->pUserInfo; + + RobustReply root(pReply); + if (root.error() != 20000) + return; + + int n = 0; + char buf[100]; + const JSONNode &results = root.results(); + for (auto &it : results["members"]) { + mir_snprintf(buf, "m%d", n++); + + CMStringW friendly = it["friendly"].as_mstring(); + CMStringW role = it["role"].as_mstring(); + CMStringW sn = it["sn"].as_mstring(); + + JSONNode member; + member << WCHAR_PARAM("nick", friendly) << WCHAR_PARAM("role", role) << WCHAR_PARAM("sn", sn); + ptrW text(json_write(&member)); + setWString(si->hContact, buf, text); + } + + setDword(si->hContact, "MemberCount", n); + setId(si->hContact, "InfoVersion", _wtoi64(results["infoVersion"].as_mstring())); + setId(si->hContact, "MembersVersion", _wtoi64(results["membersVersion"].as_mstring())); + + LoadChatInfo(si); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Invitation dialog + +class CGroupchatInviteDlg : public CProtoDlgBase<CIcqProto> +{ + typedef CProtoDlgBase<CIcqProto> CSuper; + + CCtrlClc m_clc; + SESSION_INFO *m_si; + + void FilterList(CCtrlClc*) + { + for (auto &hContact : Contacts()) { + char *proto = GetContactProto(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(CIcqProto *ppro, SESSION_INFO *si) : + CSuper(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 + { + CMStringA szMembers; + for (auto &hContact : m_proto->AccContacts()) { + if (m_proto->isChatRoom(hContact)) + continue; + + if (HANDLE hItem = m_clc.FindContact(hContact)) { + if (m_clc.GetCheck(hItem)) { + if (!szMembers.IsEmpty()) + szMembers.AppendChar(','); + szMembers.AppendFormat("%d", m_proto->getDword(hContact, DB_KEY_UIN)); + } + } + } + + auto *pReq = new AsyncHttpRequest(CONN_MAIN, REQUEST_GET, ICQ_API_SERVER "/mchat/AddChat"); + pReq << CHAR_PARAM("f", "json") << WCHAR_PARAM("chat_id", m_si->ptszID) << CHAR_PARAM("aimsid", m_proto->m_aimsid) << CHAR_PARAM("r", pReq->m_reqId) << CHAR_PARAM("members", szMembers); + m_proto->Push(pReq); + return true; + } +}; + +void CIcqProto::InviteUserToChat(SESSION_INFO *si) +{ + CGroupchatInviteDlg dlg(this, si); + if (si->pDlg) + dlg.SetParent(((CDlgBase*)si->pDlg)->GetHwnd()); + dlg.DoModal(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Group chats + +static gc_item sttLogListItems[] = +{ + { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM }, +}; + +int CIcqProto::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 CIcqProto::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 CIcqProto::Chat_ProcessLogMenu(SESSION_INFO *si, int iChoice) +{ + switch (iChoice) { + case IDM_INVITE: + InviteUserToChat(si); + break; + } +} + +void CIcqProto::Chat_SendPrivateMessage(GCHOOK *gch) +{ + MCONTACT hContact; + DWORD dwUin = _wtoi(gch->ptszUID); + auto *pCache = FindContactByUIN(dwUin); + if (pCache == nullptr) { + hContact = CreateContact(dwUin, true); + setWString(hContact, "Nick", gch->ptszNick); + db_set_b(hContact, "CList", "Hidden", 1); + db_set_dw(hContact, "Ignore", "Mask1", 0); + } + else hContact = pCache->m_hContact; + + CallService(MS_MSG_SENDMESSAGE, hContact, 0); +} + +void CIcqProto::ProcessGroupChat(const JSONNode &ev) +{ + for (auto &it : ev["mchats"]) { + CMStringW wszId(it["sender"].as_mstring()); + SESSION_INFO *si = g_chatApi.SM_FindSession(wszId, m_szModuleName); + if (si == nullptr) + continue; + + CMStringW method(it["method"].as_mstring()); + GCEVENT gce = { m_szModuleName, si->ptszID, (method == "add_members") ? GC_EVENT_JOIN : GC_EVENT_PART }; + + int iStart = 0; + CMStringW members(it["members"].as_mstring()); + while (true) { + CMStringW member = members.Tokenize(L",", iStart); + if (member.IsEmpty()) + break; + + auto *pCache = FindContactByUIN(_wtoi(member)); + if (pCache == nullptr) + continue; + + gce.ptszNick = Clist_GetContactDisplayName(pCache->m_hContact); + gce.ptszUID = member; + gce.time = ::time(0); + gce.bIsMe = _wtoi(member) == (int)m_dwUin; + Chat_Event(&gce); + } + } +} diff --git a/protocols/Icq10/src/proto.cpp b/protocols/Icq10/src/proto.cpp index 978bf847dc..1d849d68c9 100644 --- a/protocols/Icq10/src/proto.cpp +++ b/protocols/Icq10/src/proto.cpp @@ -107,90 +107,6 @@ void CIcqProto::OnContactDeleted(MCONTACT hContact) } ///////////////////////////////////////////////////////////////////////////////////////// -// Group chats - -static gc_item sttLogListItems[] = -{ - { LPGENW("&Invite a user"), IDM_INVITE, MENU_ITEM }, -}; - -int CIcqProto::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 CIcqProto::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: - SendPrivateMessage(gch); - break; - - case GC_USER_LOGMENU: - // Chat_ProcessLogMenu(gch); - break; - - case GC_USER_NICKLISTMENU: - break; - } - - return 0; -} - -void CIcqProto::SendPrivateMessage(GCHOOK *gch) -{ - MCONTACT hContact; - DWORD dwUin = _wtoi(gch->ptszUID); - auto *pCache = FindContactByUIN(dwUin); - if (pCache == nullptr) { - hContact = CreateContact(dwUin, true); - setWString(hContact, "Nick", gch->ptszNick); - db_set_b(hContact, "CList", "Hidden", 1); - db_set_dw(hContact, "Ignore", "Mask1", 0); - } - else hContact = pCache->m_hContact; - - CallService(MS_MSG_SENDMESSAGE, hContact, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// void CIcqProto::MarkReadTimerProc(HWND hwnd, UINT, UINT_PTR id, DWORD) { diff --git a/protocols/Icq10/src/proto.h b/protocols/Icq10/src/proto.h index a0fe63fa96..96dc779199 100644 --- a/protocols/Icq10/src/proto.h +++ b/protocols/Icq10/src/proto.h @@ -76,6 +76,7 @@ struct IcqConn class CIcqProto : public PROTO<CIcqProto> { friend struct CIcqRegistrationDlg; + friend class CGroupchatInviteDlg; bool m_bOnline = false, m_bTerminated = false; void CheckAvatarChange(MCONTACT hContact, const JSONNode&); @@ -96,9 +97,6 @@ class CIcqProto : public PROTO<CIcqProto> void OnLoggedIn(void); void OnLoggedOut(void); - void LoadChatInfo(SESSION_INFO *si); - void SendPrivateMessage(GCHOOK *gch); - mir_cs csMarkReadQueue; LIST<IcqCacheItem> arMarkReadQueue; static void CALLBACK MarkReadTimerProc(HWND hwnd, UINT, UINT_PTR id, DWORD); @@ -125,6 +123,7 @@ class CIcqProto : public PROTO<CIcqProto> void ProcessBuddyList(const JSONNode&); void ProcessDiff(const JSONNode&); void ProcessEvent(const JSONNode&); + void ProcessGroupChat(const JSONNode&); void ProcessHistData(const JSONNode&); void ProcessImState(const JSONNode&); void ProcessMyInfo(const JSONNode&); @@ -143,6 +142,18 @@ class CIcqProto : public PROTO<CIcqProto> OBJLIST<IcqOwnMessage> m_arOwnIds; //////////////////////////////////////////////////////////////////////////////////////// + // group chats + + int __cdecl GroupchatEventHook(WPARAM, LPARAM); + int __cdecl GroupchatMenuHook(WPARAM, LPARAM); + + void Chat_ProcessLogMenu(SESSION_INFO *si, int); + void Chat_SendPrivateMessage(GCHOOK *gch); + + void InviteUserToChat(SESSION_INFO *si); + void LoadChatInfo(SESSION_INFO *si); + + //////////////////////////////////////////////////////////////////////////////////////// // http queue mir_cs m_csHttpQueue; @@ -187,8 +198,6 @@ class CIcqProto : public PROTO<CIcqProto> //////////////////////////////////////////////////////////////////////////////////////// // events - int __cdecl GroupchatEventHook(WPARAM, LPARAM); - int __cdecl GroupchatMenuHook(WPARAM, LPARAM); int __cdecl OnDbEventRead(WPARAM, LPARAM); int __cdecl OnOptionsInit(WPARAM, LPARAM); diff --git a/protocols/Icq10/src/resource.h b/protocols/Icq10/src/resource.h index 964e8a2095..beeac3d899 100644 --- a/protocols/Icq10/src/resource.h +++ b/protocols/Icq10/src/resource.h @@ -4,6 +4,7 @@ // #define IDD_OPTIONS_FULL 101 #define IDD_OPTIONS_ACCMGR 102 +#define IDD_GROUPCHAT_INVITE 103 #define IDD_REGISTER 105 #define IDC_PASSWORD 1001 #define IDC_UIN 1002 @@ -12,6 +13,7 @@ #define IDC_HIDECHATS 1004 #define IDC_REGISTER 1005 #define IDC_PHONE 1006 +#define IDC_CLIST 1007 #define IDC_SENDSMS 1008 #define IDC_CODE 1009 diff --git a/protocols/Icq10/src/server.cpp b/protocols/Icq10/src/server.cpp index 550f0d6a0e..65a3edda89 100644 --- a/protocols/Icq10/src/server.cpp +++ b/protocols/Icq10/src/server.cpp @@ -95,33 +95,6 @@ void CIcqProto::ConnectionFailed(int iReason) ShutdownSession(); } -void CIcqProto::LoadChatInfo(SESSION_INFO *si) -{ - int memberCount = getDword(si->hContact, "MemberCount"); - for (int i = 0; i < memberCount; i++) { - char buf[100]; - mir_snprintf(buf, "m%d", i); - ptrW szSetting(getWStringA(si->hContact, buf)); - JSONNode *node = json_parse(T2Utf(szSetting)); - if (node == nullptr) - continue; - - CMStringW nick((*node)["nick"].as_mstring()); - CMStringW role((*node)["role"].as_mstring()); - CMStringW sn((*node)["sn"].as_mstring()); - - GCEVENT gce = { m_szModuleName, si->ptszID, GC_EVENT_JOIN }; - gce.ptszNick = nick; - gce.ptszUID = sn; - gce.time = ::time(0); - gce.bIsMe = _wtoi(sn) == (int)m_dwUin; - gce.ptszStatus = TranslateW(role); - Chat_Event(&gce); - - json_delete(node); - } -} - void CIcqProto::OnLoggedIn() { debugLogA("CIcqProto::OnLoggedIn"); @@ -509,37 +482,6 @@ void CIcqProto::OnCheckPassword(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest*) StartSession(); } -void CIcqProto::OnGetChatInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) -{ - SESSION_INFO *si = (SESSION_INFO*)pReq->pUserInfo; - - RobustReply root(pReply); - if (root.error() != 20000) - return; - - int n = 0; - char buf[100]; - const JSONNode &results = root.results(); - for (auto &it : results["members"]) { - mir_snprintf(buf, "m%d", n++); - - CMStringW friendly = it["friendly"].as_mstring(); - CMStringW role = it["role"].as_mstring(); - CMStringW sn = it["sn"].as_mstring(); - - JSONNode member; - member << WCHAR_PARAM("nick", friendly) << WCHAR_PARAM("role", role) << WCHAR_PARAM("sn", sn); - ptrW text(json_write(&member)); - setWString(si->hContact, buf, text); - } - - setDword(si->hContact, "MemberCount", n); - setId(si->hContact, "InfoVersion", _wtoi64(results["infoVersion"].as_mstring())); - setId(si->hContact, "MembersVersion", _wtoi64(results["membersVersion"].as_mstring())); - - LoadChatInfo(si); -} - void CIcqProto::OnGetUserHistory(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest *pReq) { MCONTACT hContact = (MCONTACT)pReq->pUserInfo; @@ -755,6 +697,8 @@ void CIcqProto::ProcessEvent(const JSONNode &ev) ProcessHistData(pData); else if (szType == L"imState") ProcessImState(pData); + else if (szType == L"mchat") + ProcessGroupChat(pData); else if (szType == L"myInfo") ProcessMyInfo(pData); else if (szType == L"presence") |