diff options
author | George Hazan <ghazan@miranda.im> | 2023-02-01 16:58:06 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2023-02-01 16:58:06 +0300 |
commit | 55cae2b76c0f4bebd69ca50113fbda8b7fd4b641 (patch) | |
tree | 6d086cfbddce119fab635fa9de3a66965d5598b6 /protocols/Telegram/src | |
parent | e24a98864045837ff4a7d22b770b3768e90b86a1 (diff) |
Telegram: group chats support (initial version)
Diffstat (limited to 'protocols/Telegram/src')
-rw-r--r-- | protocols/Telegram/src/groupchat.cpp | 125 | ||||
-rw-r--r-- | protocols/Telegram/src/proto.cpp | 22 | ||||
-rw-r--r-- | protocols/Telegram/src/proto.h | 45 | ||||
-rw-r--r-- | protocols/Telegram/src/server.cpp | 124 | ||||
-rw-r--r-- | protocols/Telegram/src/utils.cpp | 10 |
5 files changed, 298 insertions, 28 deletions
diff --git a/protocols/Telegram/src/groupchat.cpp b/protocols/Telegram/src/groupchat.cpp new file mode 100644 index 0000000000..c0f815aa8a --- /dev/null +++ b/protocols/Telegram/src/groupchat.cpp @@ -0,0 +1,125 @@ +/* +Copyright (C) 2012-23 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 CTelegramProto::InitGroupChat(TG_USER *pUser, const TD::chat *pChat, bool bUpdateMembers) +{ + if (pUser->m_si) + return; + + wchar_t wszId[100]; + _i64tow(pUser->id, wszId, 10); + auto *si = pUser->m_si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, wszId, Utf2T(pChat->title_.c_str()), pUser); + + if (bUpdateMembers) { + Chat_AddGroup(si, TranslateT("Creator")); + Chat_AddGroup(si, TranslateT("Admin")); + Chat_AddGroup(si, TranslateT("Participant")); + + // push async query to fetch users + SendQuery(new TD::getBasicGroupFullInfo(pUser->id), &CTelegramProto::StartGroupChat, pUser); + } + else { + Chat_AddGroup(si, TranslateT("SuperAdmin")); + Chat_AddGroup(si, TranslateT("Visitor")); + + ptrW wszUserId(getWStringA(DBKEY_ID)), wszNick(Contact::GetInfo(CNF_DISPLAY, 0, m_szModuleName)); + + GCEVENT gce = { si, GC_EVENT_JOIN }; + gce.pszUID.w = wszUserId; + gce.pszNick.w = wszNick; + gce.bIsMe = true; + gce.pszStatus.w = TranslateT("Visitor"); + Chat_Event(&gce); + + gce.bIsMe = false; + gce.pszUID.w = L"---"; + gce.pszNick.w = TranslateT("Admin"); + gce.pszStatus.w = TranslateT("SuperAdmin"); + Chat_Event(&gce); + + Chat_Control(si, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE); + Chat_Control(si, SESSION_ONLINE); + } +} + +void CTelegramProto::StartGroupChat(td::ClientManager::Response &response, void *pUserData) +{ + if (!response.object) + return; + + if (response.object->get_id() != TD::basicGroupFullInfo::ID) { + debugLogA("Gotten class ID %d instead of %d, exiting", response.object->get_id(), TD::basicGroupFullInfo::ID); + return; + } + + auto *pInfo = ((TD::basicGroupFullInfo *)response.object.get()); + auto *pUser = (TG_USER *)pUserData; + + for (auto &it : pInfo->members_) { + auto *pMember = it.get(); + const wchar_t *pwszRole; + + switch (pMember->status_->get_id()) { + case TD::chatMemberStatusCreator::ID: + pwszRole = TranslateT("Creator"); + break; + case TD::chatMemberStatusAdministrator::ID: + pwszRole = TranslateT("Admin"); + break; + case TD::chatMemberStatusMember::ID: + default: + pwszRole = TranslateT("Participant"); + break; + } + + if (pMember->member_id_->get_id() != TD::messageSenderUser::ID) + continue; + + int64_t memberId = ((TD::messageSenderUser *)pMember->member_id_.get())->user_id_; + auto *pChatUser = FindUser(memberId); + if (pChatUser == nullptr) + continue; + + wchar_t wszUserId[100]; + _i64tow(memberId, wszUserId, 10); + + GCEVENT gce = { pUser->m_si, GC_EVENT_JOIN }; + gce.pszUID.w = wszUserId; + gce.pszStatus.w = pwszRole; + + switch (pChatUser->hContact) { + case 0: + gce.bIsMe = true; + __fallthrough; + + case INVALID_CONTACT_ID: + gce.pszNick.w = pChatUser->wszNick.c_str(); + break; + + default: + gce.pszNick.w = Clist_GetContactDisplayName(pChatUser->hContact); + break; + } + + Chat_Event(&gce); + } + + Chat_Control(pUser->m_si, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE); + Chat_Control(pUser->m_si, SESSION_ONLINE); +} diff --git a/protocols/Telegram/src/proto.cpp b/protocols/Telegram/src/proto.cpp index 715e511ea3..2044462846 100644 --- a/protocols/Telegram/src/proto.cpp +++ b/protocols/Telegram/src/proto.cpp @@ -1,5 +1,13 @@ #include "stdafx.h" +int __forceinline CompareId(int64_t id1, int64_t id2) +{ + if (id1 == id2) + return 0; + + return (id1 < id2) ? -1 : 1; +} + static int CompareRequests(const TG_REQUEST_BASE *p1, const TG_REQUEST_BASE *p2) { if (p1->requestId == p2->requestId) @@ -9,11 +17,15 @@ static int CompareRequests(const TG_REQUEST_BASE *p1, const TG_REQUEST_BASE *p2) } static int CompareUsers(const TG_USER *p1, const TG_USER *p2) -{ - if (p1->id == p2->id) - return 0; +{ return CompareId(p1->id, p2->id); +} + +static int CompareBasicGroups(const TG_BASIC_GROUP *p1, const TG_BASIC_GROUP *p2) +{ return CompareId(p1->id, p2->id); +} - return (p1->id < p2->id) ? -1 : 1; +static int CompareSuperGroups(const TG_SUPER_GROUP *p1, const TG_SUPER_GROUP *p2) +{ return CompareId(p1->id, p2->id); } CTelegramProto::CTelegramProto(const char* protoName, const wchar_t* userName) : @@ -22,6 +34,8 @@ CTelegramProto::CTelegramProto(const char* protoName, const wchar_t* userName) : m_arFiles(1), m_arUsers(10, CompareUsers), m_arRequests(10, CompareRequests), + m_arBasicGroups(10, CompareBasicGroups), + m_arSuperGroups(10, CompareSuperGroups), m_szOwnPhone(this, "Phone"), m_iStatus1(this, "Status1", ID_STATUS_AWAY), m_iStatus2(this, "Status2", ID_STATUS_NA), diff --git a/protocols/Telegram/src/proto.h b/protocols/Telegram/src/proto.h index 0b7fdd5779..d77c5d04fc 100644 --- a/protocols/Telegram/src/proto.h +++ b/protocols/Telegram/src/proto.h @@ -76,17 +76,41 @@ struct TG_FILE_REQUEST struct TG_USER { - TG_USER(uint64_t _1, MCONTACT _2, bool _3 = false) : + TG_USER(int64_t _1, MCONTACT _2, bool _3 = false) : id(_1), hContact(_2), isGroupChat(_3) {} - uint64_t id; + int64_t id; MCONTACT hContact; bool isGroupChat; CMStringA szAvatarHash; + CMStringW wszNick; time_t m_timer1 = 0, m_timer2 = 0; + SESSION_INFO *m_si = nullptr; +}; + +struct TG_SUPER_GROUP +{ + TG_SUPER_GROUP(int64_t _1, TD::object_ptr<TD::supergroup> _2) : + id(_1), + group(std::move(_2)) + {} + + int64_t id; + TD::object_ptr<TD::supergroup> group; +}; + +struct TG_BASIC_GROUP +{ + TG_BASIC_GROUP(int64_t _1, TD::object_ptr<TD::basicGroup> _2) : + id(_1), + group(std::move(_2)) + {} + + int64_t id; + TD::object_ptr<TD::basicGroup> group; }; class CTelegramProto : public PROTO<CTelegramProto> @@ -128,7 +152,7 @@ class CTelegramProto : public PROTO<CTelegramProto> bool m_bAuthorized, m_bTerminated, m_bUnregister = false, m_bSmileyAdd = false; int32_t m_iClientId, m_iMsgId; - uint64_t m_iQueryId; + int64_t m_iQueryId; OBJLIST<TG_REQUEST_BASE> m_arRequests; OBJLIST<TG_FILE_REQUEST> m_arFiles; @@ -154,9 +178,10 @@ class CTelegramProto : public PROTO<CTelegramProto> void SendMarkRead(void); void SendQuery(TD::Function *pFunc, TG_QUERY_HANDLER pHandler = nullptr); void SendQuery(TD::Function *pFunc, TG_QUERY_HANDLER_FULL pHandler, void *pUserInfo); - int SendTextMessage(uint64_t chatId, const char *pszMessage); + int SendTextMessage(int64_t chatId, const char *pszMessage); void ProcessAuth(TD::updateAuthorizationState *pObj); + void ProcessBasicGroup(TD::updateBasicGroup *pObj); void ProcessChat(TD::updateNewChat *pObj); void ProcessChatPosition(TD::updateChatPosition *pObj); void ProcessFile(TD::updateFile *pObj); @@ -164,6 +189,7 @@ class CTelegramProto : public PROTO<CTelegramProto> void ProcessMarkRead(TD::updateChatReadInbox *pObj); void ProcessMessage(TD::updateNewMessage *pObj); void ProcessStatus(TD::updateUserStatus *pObj); + void ProcessSuperGroup(TD::updateSupergroup *pObj); void ProcessUser(TD::updateUser *pObj); CMStringA GetMessageText(TD::MessageContent *pBody); @@ -178,13 +204,20 @@ class CTelegramProto : public PROTO<CTelegramProto> INT_PTR __cdecl SvcGetMyAvatar(WPARAM, LPARAM); INT_PTR __cdecl SvcSetMyAvatar(WPARAM, LPARAM); + // Group chats + OBJLIST<TG_BASIC_GROUP> m_arBasicGroups; + OBJLIST<TG_SUPER_GROUP> m_arSuperGroups; + + void InitGroupChat(TG_USER *pUser, const TD::chat *pChat, bool bUpdateMembers); + void StartGroupChat(td::ClientManager::Response &response, void *pUserData); + // Users int64_t m_iOwnId; MGROUP m_iBaseGroup; OBJLIST<TG_USER> m_arUsers; - TG_USER* FindUser(uint64_t id); - TG_USER* AddUser(uint64_t id, bool bIsChat); + TG_USER* FindUser(int64_t id); + TG_USER* AddUser(int64_t id, bool bIsChat); // Popups HANDLE m_hPopupClass; diff --git a/protocols/Telegram/src/server.cpp b/protocols/Telegram/src/server.cpp index b8567a320b..1c30f98fb9 100644 --- a/protocols/Telegram/src/server.cpp +++ b/protocols/Telegram/src/server.cpp @@ -98,7 +98,7 @@ void CTelegramProto::SendMarkRead() m_impl.m_markRead.Stop();
mir_cslock lck(m_csMarkRead);
- uint64_t userId = _atoi64(getMStringA(m_markContact, DBKEY_ID));
+ int64_t userId = _atoi64(getMStringA(m_markContact, DBKEY_ID));
SendQuery(new TD::viewMessages(userId, 0, std::move(m_markIds), true));
m_markContact = 0;
}
@@ -127,6 +127,10 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response) ProcessAuth((TD::updateAuthorizationState *)response.object.get());
break;
+ case TD::updateBasicGroup::ID:
+ ProcessBasicGroup((TD::updateBasicGroup*)response.object.get());
+ break;
+
case TD::updateChatFilters::ID:
ProcessGroups((TD::updateChatFilters *)response.object.get());
break;
@@ -140,7 +144,7 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response) break;
case TD::updateFile::ID:
- ProcessFile((TD::updateFile*)response.object.get());
+ ProcessFile((TD::updateFile *)response.object.get());
break;
case TD::updateNewChat::ID:
@@ -151,6 +155,10 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response) ProcessMessage((TD::updateNewMessage *)response.object.get());
break;
+ case TD::updateSupergroup::ID:
+ ProcessSuperGroup((TD::updateSupergroup *)response.object.get());
+ break;
+
case TD::updateUserStatus::ID:
ProcessStatus((TD::updateUserStatus *)response.object.get());
break;
@@ -182,7 +190,7 @@ void CTelegramProto::OnSendMessage(td::ClientManager::Response &response, void * }
}
-int CTelegramProto::SendTextMessage(uint64_t chatId, const char *pszMessage)
+int CTelegramProto::SendTextMessage(int64_t chatId, const char *pszMessage)
{
int ret = m_iMsgId++;
@@ -193,7 +201,7 @@ int CTelegramProto::SendTextMessage(uint64_t chatId, const char *pszMessage) auto *pMessage = new TD::sendMessage();
pMessage->chat_id_ = chatId;
pMessage->input_message_content_ = std::move(pContent);
- SendQuery(pMessage, &CTelegramProto::OnSendMessage, (void*)ret);
+ SendQuery(pMessage, &CTelegramProto::OnSendMessage, (void *)ret);
return ret;
}
@@ -204,7 +212,7 @@ void CTelegramProto::SendQuery(TD::Function *pFunc, TG_QUERY_HANDLER pHandler) auto szDescr = to_string(*pFunc);
debugLogA("Sending query %d:\n%s", queryId, szDescr.c_str());
-
+
m_pClientMmanager->send(m_iClientId, queryId, TD::object_ptr<TD::Function>(pFunc));
if (pHandler)
@@ -226,17 +234,60 @@ void CTelegramProto::SendQuery(TD::Function *pFunc, TG_QUERY_HANDLER_FULL pHandl ///////////////////////////////////////////////////////////////////////////////
+void CTelegramProto::ProcessBasicGroup(TD::updateBasicGroup *pObj)
+{
+ auto iStatusId = pObj->basic_group_->status_->get_id();
+ if (iStatusId == TD::chatMemberStatusBanned::ID) {
+ debugLogA("We are banned here, skipping");
+ return;
+ }
+
+ TG_BASIC_GROUP tmp(pObj->basic_group_->id_, 0);
+ auto *pGroup = m_arBasicGroups.find(&tmp);
+ if (pGroup == nullptr)
+ m_arBasicGroups.insert(new TG_BASIC_GROUP(tmp.id, std::move(pObj->basic_group_)));
+ else
+ pGroup->group = std::move(pObj->basic_group_);
+
+ auto *pUser = AddUser(tmp.id, true);
+ if (iStatusId == TD::chatMemberStatusLeft::ID)
+ Contact::Hide(pUser->hContact);
+}
+
void CTelegramProto::ProcessChat(TD::updateNewChat *pObj)
{
- auto &pChat = pObj->chat_;
- if (pChat->type_->get_id() != TD::chatTypePrivate::ID) {
- debugLogA("Only private chats are currently supported");
+ bool bIsBasicGroup = false;
+ int64_t chatId;
+ auto *pChat = pObj->chat_.get();
+
+ switch(pChat->type_->get_id()) {
+ case TD::chatTypePrivate::ID:
+ case TD::chatTypeSecret::ID:
+ chatId = pChat->id_;
+ break;
+
+ case TD::chatTypeBasicGroup::ID:
+ bIsBasicGroup = true;
+ chatId = ((TD::chatTypeBasicGroup*)pChat->type_.get())->basic_group_id_;
+ break;
+
+ case TD::chatTypeSupergroup::ID:
+ chatId = ((TD::chatTypeSupergroup*)pChat->type_.get())->supergroup_id_;
+ break;
+
+ default:
+ debugLogA("Invalid chat type %d, ignoring", pChat->type_->get_id());
return;
}
- if (auto *pUser = FindUser(pChat->id_))
- if (!pChat->title_.empty())
+ if (auto *pUser = FindUser(chatId)) {
+ if (!pChat->title_.empty() && pUser->hContact != INVALID_CONTACT_ID)
setUString(pUser->hContact, "Nick", pChat->title_.c_str());
+
+ if (pUser->isGroupChat)
+ InitGroupChat(pUser, pChat, bIsBasicGroup);
+ }
+ else debugLogA("Unknown chat id %lld, ignoring", chatId);
}
void CTelegramProto::ProcessChatPosition(TD::updateChatPosition *pObj)
@@ -252,19 +303,24 @@ void CTelegramProto::ProcessChatPosition(TD::updateChatPosition *pObj) return;
}
+ if (pUser->hContact == INVALID_CONTACT_ID)
+ return;
+
auto *pPos = (TD::chatPosition *)pObj->position_.get();
if (pPos->list_) {
- auto *pList = (TD::chatListFilter*)pPos->list_.get();
-
+ auto *pList = (TD::chatListFilter *)pPos->list_.get();
+
CMStringA szSetting(FORMAT, "ChatFilter%d", pList->chat_filter_id_);
CMStringW wszGroup(getMStringW(szSetting));
if (!wszGroup.IsEmpty()) {
ptrW pwszExistingGroup(Clist_GetGroup(pUser->hContact));
- if (!pwszExistingGroup || !mir_wstrcmp(pwszExistingGroup, m_wszDefaultGroup)) {
+ if (!pwszExistingGroup
+ || (!pUser->isGroupChat && !mir_wstrcmp(pwszExistingGroup, m_wszDefaultGroup))
+ || (pUser->isGroupChat && !mir_wstrcmp(pwszExistingGroup, ptrW(Chat_GetGroup())))) {
CMStringW wszNewGroup(FORMAT, L"%s\\%s", (wchar_t *)m_wszDefaultGroup, wszGroup.c_str());
Clist_SetGroup(pUser->hContact, wszNewGroup);
}
- }
+ }
}
}
@@ -282,7 +338,7 @@ void CTelegramProto::ProcessGroups(TD::updateChatFilters *pObj) setWString(szSetting, wszNewValue);
}
else if (wszOldValue != wszNewValue) {
- CMStringW wszFullGroup(FORMAT, L"%s\\%s", (wchar_t*)m_wszDefaultGroup, wszNewValue);
+ CMStringW wszFullGroup(FORMAT, L"%s\\%s", (wchar_t *)m_wszDefaultGroup, wszNewValue.get());
MGROUP oldGroup = Clist_GroupExists(wszFullGroup);
if (!oldGroup)
Clist_GroupCreate(m_iBaseGroup, wszFullGroup);
@@ -362,6 +418,9 @@ void CTelegramProto::ProcessMessage(TD::updateNewMessage *pObj) void CTelegramProto::ProcessStatus(TD::updateUserStatus *pObj)
{
if (auto *pUser = FindUser(pObj->user_id_)) {
+ if (pUser->hContact == INVALID_CONTACT_ID)
+ return;
+
if (pObj->status_->get_id() == TD::userStatusOnline::ID)
setWord(pUser->hContact, "Status", ID_STATUS_ONLINE);
else if (pObj->status_->get_id() == TD::userStatusOffline::ID) {
@@ -372,6 +431,27 @@ void CTelegramProto::ProcessStatus(TD::updateUserStatus *pObj) }
}
+void CTelegramProto::ProcessSuperGroup(TD::updateSupergroup *pObj)
+{
+ auto iStatusId = pObj->supergroup_->status_->get_id();
+ if (iStatusId == TD::chatMemberStatusBanned::ID) {
+ debugLogA("We are banned here, skipping");
+ return;
+ }
+
+ TG_SUPER_GROUP tmp(pObj->supergroup_->id_, 0);
+
+ auto *pGroup = m_arSuperGroups.find(&tmp);
+ if (pGroup == nullptr)
+ m_arSuperGroups.insert(new TG_SUPER_GROUP(tmp.id, std::move(pObj->supergroup_)));
+ else
+ pGroup->group = std::move(pObj->supergroup_);
+
+ auto *pUser = AddUser(tmp.id, true);
+ if (iStatusId == TD::chatMemberStatusLeft::ID)
+ Contact::Hide(pUser->hContact);
+}
+
void CTelegramProto::ProcessUser(TD::updateUser *pObj)
{
auto *pUser = pObj->user_.get();
@@ -384,6 +464,16 @@ void CTelegramProto::ProcessUser(TD::updateUser *pObj) }
if (!pUser->is_contact_) {
+ auto *pu = FindUser(pUser->id_);
+ if (pu == nullptr) {
+ pu = new TG_USER(pUser->id_, INVALID_CONTACT_ID, false);
+ m_arUsers.insert(pu);
+ }
+
+ pu->wszNick = Utf2T(pUser->first_name_.c_str());
+ if (!pUser->last_name_.empty())
+ pu->wszNick.AppendFormat(L" %s", Utf2T(pUser->last_name_.c_str()).get());
+
debugLogA("User doesn't belong to your contacts, skipping");
return;
}
@@ -394,6 +484,8 @@ void CTelegramProto::ProcessUser(TD::updateUser *pObj) UpdateString(pu->hContact, "Phone", pUser->phone_number_);
if (pUser->usernames_)
UpdateString(pu->hContact, "Nick", pUser->usernames_->editable_username_);
+ if (pu->hContact == 0)
+ pu->wszNick = Contact::GetInfo(CNF_DISPLAY, 0, m_szModuleName);
if (pUser->is_premium_)
ExtraIcon_SetIconByName(g_plugin.m_hIcon, pu->hContact, "tg_premium");
@@ -413,7 +505,7 @@ void CTelegramProto::ProcessUser(TD::updateUser *pObj) else delSetting(pu->hContact, DBKEY_AVATAR_HASH);
}
}
- }
+ }
if (pUser->status_) {
if (pUser->status_->get_id() == TD::userStatusOffline::ID) {
diff --git a/protocols/Telegram/src/utils.cpp b/protocols/Telegram/src/utils.cpp index bcf222ecab..d921927a60 100644 --- a/protocols/Telegram/src/utils.cpp +++ b/protocols/Telegram/src/utils.cpp @@ -28,15 +28,21 @@ void CTelegramProto::UpdateString(MCONTACT hContact, const char *pszSetting, con /////////////////////////////////////////////////////////////////////////////////////////
// Users
-TG_USER* CTelegramProto::FindUser(uint64_t id)
+TG_USER* CTelegramProto::FindUser(int64_t id)
{
if (auto *pCache = m_arUsers.find((TG_USER *)&id))
return pCache;
+ if (id < 0) {
+ id = -id;
+ if (auto *pCache = m_arUsers.find((TG_USER *)&id))
+ return pCache;
+ }
+
return nullptr;
}
-TG_USER* CTelegramProto::AddUser(uint64_t id, bool bIsChat)
+TG_USER* CTelegramProto::AddUser(int64_t id, bool bIsChat)
{
auto *pUser = FindUser(id);
if (pUser != nullptr)
|