summaryrefslogtreecommitdiff
path: root/protocols/Telegram/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2023-02-01 16:58:06 +0300
committerGeorge Hazan <ghazan@miranda.im>2023-02-01 16:58:06 +0300
commit55cae2b76c0f4bebd69ca50113fbda8b7fd4b641 (patch)
tree6d086cfbddce119fab635fa9de3a66965d5598b6 /protocols/Telegram/src
parente24a98864045837ff4a7d22b770b3768e90b86a1 (diff)
Telegram: group chats support (initial version)
Diffstat (limited to 'protocols/Telegram/src')
-rw-r--r--protocols/Telegram/src/groupchat.cpp125
-rw-r--r--protocols/Telegram/src/proto.cpp22
-rw-r--r--protocols/Telegram/src/proto.h45
-rw-r--r--protocols/Telegram/src/server.cpp124
-rw-r--r--protocols/Telegram/src/utils.cpp10
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)