summaryrefslogtreecommitdiff
path: root/protocols/Steam/src/steam_chats.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Steam/src/steam_chats.cpp')
-rw-r--r--protocols/Steam/src/steam_chats.cpp462
1 files changed, 462 insertions, 0 deletions
diff --git a/protocols/Steam/src/steam_chats.cpp b/protocols/Steam/src/steam_chats.cpp
new file mode 100644
index 0000000000..c56a34e9b1
--- /dev/null
+++ b/protocols/Steam/src/steam_chats.cpp
@@ -0,0 +1,462 @@
+/*
+Copyright (C) 2012-25 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+void CSteamProto::SendGetChatsRequest()
+{
+ CChatRoomGetMyChatRoomGroupsRequest request;
+ WSSendService(GetMyChatRoomGroups, request);
+}
+
+void CSteamProto::OnGetMyChats(const CChatRoomGetMyChatRoomGroupsResponse &reply, const CMsgProtoBufHeader &hdr)
+{
+ if (hdr.failed())
+ return;
+
+ std::map<MCONTACT, bool> chatIds;
+ for (unsigned i = 0; i < reply.n_chat_room_groups; i++) {
+ auto *pGroup = reply.chat_room_groups[i]->group_summary;
+ ProcessGroupChat(pGroup);
+
+ for (unsigned k = 0; k < pGroup->n_chat_rooms; k++) {
+ auto *pChat = pGroup->chat_rooms[k];
+ CMStringW wszId(FORMAT, L"%lld_%lld", pGroup->chat_group_id, pChat->chat_id);
+
+ if (auto *si = Chat_Find(wszId, m_szModuleName))
+ chatIds[si->hContact] = true;
+ }
+ }
+
+ // clean garbage
+ for (auto &cc : AccContacts())
+ if (Contact::IsGroupChat(cc, m_szModuleName) == GCW_CHATROOM && chatIds.find(cc) == chatIds.end())
+ db_delete_contact(cc, CDF_DEL_CONTACT);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSteamProto::OnChatChanged(const ChatRoomClientNotifyChatGroupUserStateChangedNotification &reply, const CMsgProtoBufHeader &hdr)
+{
+ if (hdr.failed())
+ return;
+
+ switch (reply.user_action) {
+ case ECHAT_ROOM_MEMBER_STATE_CHANGE__k_EChatRoomMemberStateChange_Joined:
+ ProcessGroupChat(reply.group_summary);
+ break;
+
+ case ECHAT_ROOM_MEMBER_STATE_CHANGE__k_EChatRoomMemberStateChange_Parted:
+ LeaveGroupChat(reply.chat_group_id);
+ break;
+ }
+}
+
+void CSteamProto::ProcessGroupChat(const CChatRoomGetChatRoomGroupSummaryResponse *pGroup)
+{
+ CMStringW wszGrpName;
+ if (pGroup->n_chat_rooms > 1 && pGroup->chat_group_name) {
+ wszGrpName = CMStringW(m_wszGroupName) + L"\\" + Utf2T(pGroup->chat_group_name);
+ if (!Clist_GroupExists(wszGrpName))
+ Clist_GroupCreate(0, wszGrpName);
+ }
+
+ SESSION_INFO *pOwner = 0;
+
+ for (unsigned k = 0; k < pGroup->n_chat_rooms; k++) {
+ std::vector<uint64_t> ids;
+
+ auto *pChat = pGroup->chat_rooms[k];
+ CMStringW wszId(FORMAT, L"%lld_%lld", pGroup->chat_group_id, pChat->chat_id);
+
+ CMStringW wszTitle(Utf2T(pChat->chat_name));
+ if (wszTitle.IsEmpty())
+ wszTitle = Utf2T(pGroup->chat_group_name);
+
+ auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, wszId, wszTitle);
+ if (pOwner == 0) {
+ if (!si->arStatuses.getCount()) {
+ Chat_AddGroup(si, TranslateT("Owner"));
+ Chat_AddGroup(si, TranslateT("Participant"));
+
+ for (unsigned j = 0; j < pGroup->n_top_members; j++) {
+ uint64_t iSteamId = AccountIdToSteamId(pGroup->top_members[j]);
+ CMStringW wszUserId(FORMAT, L"%lld", iSteamId), wszNick;
+
+ GCEVENT gce = { si, GC_EVENT_JOIN };
+ gce.pszUID.w = wszUserId;
+
+ if (iSteamId == m_iSteamId) {
+ gce.bIsMe = true;
+ wszNick = getMStringW("Nick");
+ }
+ else if (MCONTACT hContact = GetContact(iSteamId))
+ wszNick = Clist_GetContactDisplayName(hContact);
+ else {
+ CMStringA szSetting(FORMAT, "UserInfo_%lld", iSteamId);
+ ptrW szName(g_plugin.getWStringA(szSetting));
+ if (szName)
+ wszNick = szName;
+ else {
+ ids.push_back(iSteamId);
+ {
+ mir_cslock lck(m_csChats);
+ m_chatContactInfo[iSteamId] = si;
+ }
+ wszNick = L"@" + wszUserId;
+ }
+ }
+
+ gce.pszNick.w = wszNick;
+ gce.pszStatus.w = (pGroup->top_members[j] == pGroup->accountid_owner) ? TranslateT("Owner") : TranslateT("Participant");
+ Chat_Event(&gce);
+ }
+ }
+ pOwner = si;
+ }
+ else si->pParent = pOwner;
+
+ setDword(si->hContact, DBKEY_CHAT_ID, pChat->chat_id);
+ if (!wszGrpName.IsEmpty())
+ Clist_SetGroup(si->hContact, wszGrpName);
+
+ if (mir_strlen(pGroup->chat_group_tagline)) {
+ Utf2T wszTopic(pGroup->chat_group_tagline);
+ Chat_SetStatusbarText(si, wszTopic);
+
+ GCEVENT gce = { si, GC_EVENT_TOPIC };
+ gce.pszText.w = wszTopic;
+ gce.time = time(0);
+ Chat_Event(&gce);
+ }
+
+ Chat_Control(si, WINDOW_HIDDEN);
+ Chat_Control(si, SESSION_ONLINE);
+
+ if (!ids.empty())
+ SendUserInfoRequest(ids);
+
+ if (pChat->voice_allowed)
+ ExtraIcon_SetIcon(hExtraXStatus, si->hContact, Skin_GetIconHandle(SKINICON_OTHER_SOUND));
+
+ uint32_t dwLastMsgId = getDword(si->hContact, DBKEY_LASTMSG);
+ if (pChat->time_last_message > dwLastMsgId)
+ SendGetChatHistory(si->hContact, dwLastMsgId);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSteamProto::OnGotClanInfo(const CMsgClientClanState &reply, const CMsgProtoBufHeader &hdr)
+{
+ if (hdr.failed())
+ return;
+
+ CMStringW wszId(FORMAT, L"%lld", reply.steamid_clan);
+ auto *si = Chat_Find(wszId, m_szModuleName);
+ if (si == nullptr) {
+ si = Chat_NewSession(GCW_SERVER, m_szModuleName, wszId, reply.name_info ? Utf2T(reply.name_info->clan_name) : wszId.c_str());
+ Chat_Control(si, WINDOW_HIDDEN);
+ Chat_Control(si, SESSION_ONLINE);
+ }
+
+ GCEVENT gce = { si, GC_EVENT_INFORMATION };
+ gce.time = time(0);
+
+ if (reply.user_counts) {
+ CMStringW wszText;
+ auto &C = *reply.user_counts;
+
+ if (C.has_members)
+ wszText.AppendFormat(L"%d %s\r\n", C.members, TranslateT("total members"));
+ if (C.has_online)
+ wszText.AppendFormat(L"%d %s\r\n", C.online, TranslateT("online members"));
+ if (C.has_chatting)
+ wszText.AppendFormat(L"%d %s\r\n", C.chatting, TranslateT("chatting"));
+ if (C.has_in_game)
+ wszText.AppendFormat(L"%d %s\r\n", C.in_game, TranslateT("in game"));
+
+ if (!wszText.IsEmpty()) {
+ gce.pszText.w = wszText;
+ Chat_Event(&gce);
+ }
+ }
+
+ for (unsigned n = 0; n < reply.n_announcements; n++) {
+ auto *E = reply.announcements[n];
+ Utf2T wszText(E->headline);
+
+ gce.time = E->event_time;
+ gce.pszText.w = wszText;
+ Chat_Event(&gce);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSteamProto::SendGetChatHistory(MCONTACT hContact, uint32_t iLastMsgId)
+{
+ CChatRoomGetMessageHistoryRequest request;
+ request.chat_group_id = GetId(hContact, DBKEY_STEAM_ID); request.has_chat_group_id = true;
+ request.chat_id = getDword(hContact, DBKEY_CHAT_ID); request.has_chat_id = true;
+ request.start_time = iLastMsgId; request.has_start_time = true;
+ WSSendService(GetChatHistory, request, (void*)hContact);
+}
+
+void CSteamProto::OnGetChatHistory(const CChatRoomGetMessageHistoryResponse &reply, const CMsgProtoBufHeader &hdr)
+{
+ if (hdr.failed())
+ return;
+
+ std::vector<uint64_t> ids;
+
+ if (auto *si = Chat_Find(UINT_PTR(GetRequestInfo(hdr.jobid_target)), m_szModuleName)) {
+ uint32_t iLastMsg = getDword(si->hContact, DBKEY_LASTMSG);
+ uint32_t iChatId = getDword(si->hContact, DBKEY_CHAT_ID);
+
+ for (int i = (int)reply.n_messages - 1; i >= 0; i--) {
+ auto *pMsg = reply.messages[i];
+ if (pMsg->server_timestamp > iLastMsg)
+ iLastMsg = pMsg->server_timestamp;
+
+ // some slack, skip it
+ if (pMsg->server_message)
+ continue;
+
+ auto iSteamId = AccountIdToSteamId(pMsg->sender);
+ char szMsgId[100], szUserId[100];
+ mir_snprintf(szMsgId, "%d_%d", iChatId, pMsg->server_timestamp);
+ _i64toa(iSteamId, szUserId, 10);
+
+ _A2T wszUserId(szUserId);
+ USERINFO ui = {};
+ ui.pszUID = wszUserId;
+ if (!si->getUserList().find(&ui)) {
+ ids.push_back(iSteamId);
+
+ mir_cslock lck(m_csChats);
+ m_chatContactInfo[iSteamId] = si;
+ }
+
+ CMStringA szText(pMsg->message);
+ DecodeBbcodes(si, szText);
+
+ DB::EventInfo dbei(db_event_getById(m_szModuleName, szMsgId));
+ dbei.flags |= DBEF_UTF;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.szModule = m_szModuleName;
+ dbei.cbBlob = szText.GetLength();
+ replaceStr(dbei.pBlob, szText.Detach());
+ dbei.iTimestamp = pMsg->server_timestamp;
+ dbei.szId = szMsgId;
+ dbei.szUserId = szUserId;
+
+ if (dbei.getEvent())
+ db_event_edit(dbei.getEvent(), &dbei, true);
+ else
+ db_event_add(si->hContact, &dbei);
+ }
+
+ setDword(si->hContact, DBKEY_LASTMSG, iLastMsg);
+ }
+
+ if (!ids.empty())
+ SendUserInfoRequest(ids);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSteamProto::OnGetChatMessage(const CChatRoomIncomingChatMessageNotification &reply, const CMsgProtoBufHeader &hdr)
+{
+ if (hdr.failed())
+ return;
+
+ std::vector<uint64_t> ids;
+
+ CMStringW wszId(FORMAT, L"%lld_%lld", reply.chat_group_id, reply.chat_id);
+ if (auto *si = Chat_Find(wszId, m_szModuleName)) {
+ char szMsgId[100], szUserId[100];
+ mir_snprintf(szMsgId, "%lld_%d", reply.chat_id, reply.timestamp);
+ _i64toa(reply.steamid_sender, szUserId, 10);
+
+ CMStringA szText(reply.message);
+ DecodeBbcodes(si, szText);
+
+ _A2T wszUserId(szUserId);
+ USERINFO ui = {};
+ ui.pszUID = wszUserId;
+ if (!si->getUserList().find(&ui)) {
+ ids.push_back(reply.steamid_sender);
+
+ mir_cslock lck(m_csChats);
+ m_chatContactInfo[reply.steamid_sender] = si;
+ }
+
+ DB::EventInfo dbei(db_event_getById(m_szModuleName, szMsgId));
+ dbei.flags |= DBEF_UTF;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.szModule = m_szModuleName;
+ dbei.cbBlob = szText.GetLength();
+ replaceStr(dbei.pBlob, szText.Detach());
+ dbei.iTimestamp = reply.timestamp;
+ dbei.szId = szMsgId;
+ dbei.szUserId = szUserId;
+
+ if (dbei.getEvent())
+ db_event_edit(dbei.getEvent(), &dbei, true);
+ else
+ db_event_add(si->hContact, &dbei);
+ }
+
+ if (!ids.empty())
+ SendUserInfoRequest(ids);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CSteamProto::SvcLeaveChat(WPARAM hContact, LPARAM)
+{
+ CChatRoomLeaveChatRoomGroupRequest request;
+ if (Contact::IsGroupChat(hContact) == GCW_SERVER)
+ request.chat_group_id = GetId(hContact, DBKEY_GROUP_ID);
+ else
+ request.chat_group_id = GetId(hContact, DBKEY_STEAM_ID);
+ request.has_chat_group_id = true;
+ WSSendService(LeaveChatGroup, request);
+ return 0;
+}
+
+void CSteamProto::LeaveGroupChat(int64_t chatGroupId)
+{
+ std::vector<MCONTACT> ids;
+
+ for (auto &cc : AccContacts()) {
+ if (!Contact::IsGroupChat(cc) || GetId(cc, DBKEY_STEAM_ID) != chatGroupId)
+ continue;
+
+ CMStringW wszId(FORMAT, L"%lld_%d", chatGroupId, getDword(cc, DBKEY_CHAT_ID));
+ if (auto *si = Chat_Find(wszId, m_szModuleName))
+ Chat_Terminate(si);
+
+ ids.push_back(cc);
+ }
+
+ for (auto &cc: ids)
+ db_delete_contact(cc, CDF_FROM_SERVER);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+enum
+{
+ IDM_LEAVE = 1,
+};
+
+int CSteamProto::GcEventHook(WPARAM, LPARAM lParam)
+{
+ GCHOOK *gch = (GCHOOK *)lParam;
+ if (gch == nullptr)
+ return 0;
+
+ auto *si = gch->si;
+ if (mir_strcmpi(si->pszModule, m_szModuleName))
+ return 0;
+
+ switch (gch->iType) {
+ case GC_USER_MESSAGE:
+ if (gch->ptszText && mir_wstrlen(gch->ptszText) > 0) {
+ CMStringW wszText(gch->ptszText);
+ wszText.TrimRight();
+ EncodeBbcodes(si, wszText);
+ T2Utf szText(wszText);
+
+ CChatRoomSendChatMessageRequest request;
+ request.chat_group_id = _wtoi64(si->ptszID); request.has_chat_group_id = true;
+ request.chat_id = getDword(si->hContact, DBKEY_CHAT_ID); request.has_chat_id = true;
+ request.echo_to_sender = request.has_echo_to_sender = true;
+ request.message = szText;
+ WSSendService(SendChatMessage, request);
+ }
+ break;
+
+ case GC_USER_PRIVMESS:
+ Chat_SendPrivateMessage(gch);
+ break;
+
+ case GC_USER_LOGMENU:
+ Chat_LogMenu(gch);
+ break;
+
+ case GC_USER_NICKLISTMENU:
+ break;
+ }
+ return 0;
+}
+
+void CSteamProto::Chat_SendPrivateMessage(GCHOOK *gch)
+{
+ uint64_t iSteamId = _wtoi64(gch->ptszUID);
+ MCONTACT hContact = GetContact(iSteamId);
+ if (!hContact) {
+ PROTOSEARCHRESULT psr = { sizeof(psr) };
+ psr.id.w = (wchar_t *)gch->ptszUID;
+ psr.firstName.w = (wchar_t *)gch->ptszNick;
+
+ hContact = AddToList(PALF_TEMPORARY, &psr);
+ if (hContact == 0)
+ return;
+
+ setWString(hContact, "Nick", gch->ptszNick);
+ Contact::Hide(hContact);
+ db_set_dw(hContact, "Ignore", "Mask1", 0);
+ }
+
+ CallService(MS_MSG_SENDMESSAGE, hContact, 0);
+}
+
+void CSteamProto::Chat_LogMenu(GCHOOK *gch)
+{
+ switch (gch->dwData) {
+ case IDM_LEAVE:
+ SvcLeaveChat(gch->si->hContact, 0);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static gc_item sttLogListItems[] =
+{
+ { LPGENW("&Leave chat session"), IDM_LEAVE, MENU_ITEM }
+};
+
+int CSteamProto::GcMenuHook(WPARAM, LPARAM lParam)
+{
+ GCMENUITEMS *gcmi = (GCMENUITEMS *)lParam;
+ if (gcmi == nullptr)
+ return 0;
+
+ if (mir_strcmpi(gcmi->pszModule, m_szModuleName))
+ return 0;
+
+ if (gcmi->Type == MENU_ON_LOG) {
+ Chat_AddMenuItems(gcmi->hMenu, _countof(sttLogListItems), sttLogListItems, &g_plugin);
+ }
+ else if (gcmi->Type == MENU_ON_NICKLIST) {
+ }
+ return 0;
+}