summaryrefslogtreecommitdiff
path: root/protocols/Telegram
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram')
-rw-r--r--protocols/Telegram/Telegram.vcxproj1
-rw-r--r--protocols/Telegram/Telegram.vcxproj.filters3
-rw-r--r--protocols/Telegram/res/resource.rc49
-rw-r--r--protocols/Telegram/src/add_phone.cpp2
-rw-r--r--protocols/Telegram/src/auth.cpp2
-rw-r--r--protocols/Telegram/src/avatars.cpp136
-rw-r--r--protocols/Telegram/src/groupchat.cpp145
-rw-r--r--protocols/Telegram/src/menus.cpp2
-rw-r--r--protocols/Telegram/src/options.cpp8
-rw-r--r--protocols/Telegram/src/proto.cpp122
-rw-r--r--protocols/Telegram/src/proto.h27
-rw-r--r--protocols/Telegram/src/resource.h13
-rw-r--r--protocols/Telegram/src/server.cpp172
-rw-r--r--protocols/Telegram/src/stdafx.cxx2
-rw-r--r--protocols/Telegram/src/stdafx.h1
-rw-r--r--protocols/Telegram/src/userinfo.cpp151
-rw-r--r--protocols/Telegram/src/utils.cpp131
-rw-r--r--protocols/Telegram/src/version.h2
18 files changed, 725 insertions, 244 deletions
diff --git a/protocols/Telegram/Telegram.vcxproj b/protocols/Telegram/Telegram.vcxproj
index 243fd85388..3a09c9db55 100644
--- a/protocols/Telegram/Telegram.vcxproj
+++ b/protocols/Telegram/Telegram.vcxproj
@@ -38,6 +38,7 @@
<ClCompile Include="src\stdafx.cxx">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\userinfo.cpp" />
<ClCompile Include="src\utils.cpp" />
</ItemGroup>
<ItemDefinitionGroup>
diff --git a/protocols/Telegram/Telegram.vcxproj.filters b/protocols/Telegram/Telegram.vcxproj.filters
index 10ea2069d9..1dfbf2f168 100644
--- a/protocols/Telegram/Telegram.vcxproj.filters
+++ b/protocols/Telegram/Telegram.vcxproj.filters
@@ -32,6 +32,9 @@
<ClCompile Include="src\menus.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\userinfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\stdafx.cxx">
diff --git a/protocols/Telegram/res/resource.rc b/protocols/Telegram/res/resource.rc
index cb6181fb51..65a4b91a35 100644
--- a/protocols/Telegram/res/resource.rc
+++ b/protocols/Telegram/res/resource.rc
@@ -99,8 +99,10 @@ BEGIN
PUSHBUTTON "Log out",IDC_LOGOUT,212,168,86,14
CONTROL "Include URL preview into message text",IDC_USE_PREVIEW,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,109,294,10
- CONTROL "Store channel messages in memory only",IDC_RESIDENT_CHANNELS,
+ CONTROL "Do not store channel history in the database",IDC_RESIDENT_CHANNELS,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,122,294,10
+ CONTROL "Delete contacts in Miranda when they are deleted from server",IDC_DELETE_CONTACTS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,4,135,294,10
END
IDD_OPTIONS_ADV DIALOGEX 0, 0, 310, 86
@@ -166,6 +168,28 @@ BEGIN
PUSHBUTTON "Cancel",IDCANCEL,158,22,50,14
END
+IDD_MYPROFILE DIALOGEX 0, 0, 221, 152
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "Birthday",IDC_STATIC,2,3,217,8
+ CONTROL "",IDC_BIRTHDAY,"SysDateTimePick32",DTS_RIGHTALIGN | WS_DISABLED | WS_TABSTOP,0,14,100,15
+ LTEXT "Biography",IDC_STATIC,2,34,217,8
+ EDITTEXT IDC_BIO,0,46,217,101,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY,WS_EX_DLGMODALFRAME
+END
+
+IDD_CHANNEL_INFO DIALOGEX 0, 0, 221, 152
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "User count",IDC_STATIC,2,6,149,8
+ EDITTEXT IDC_USER_COUNT,157,4,59,14,ES_AUTOHSCROLL | ES_NUMBER | WS_DISABLED
+ LTEXT "Channel link",IDC_STATIC,2,27,217,8
+ CONTROL "",IDC_LINK,"Hyperlink",WS_TABSTOP,2,38,217,12
+END
+
/////////////////////////////////////////////////////////////////////////////
//
@@ -219,6 +243,14 @@ BEGIN
BEGIN
BOTTOMMARGIN, 262
END
+
+ IDD_MYPROFILE, DIALOG
+ BEGIN
+ END
+
+ IDD_CHANNEL_INFO, DIALOG
+ BEGIN
+ END
END
#endif // APSTUDIO_INVOKED
@@ -258,6 +290,21 @@ BEGIN
0
END
+IDD_USERINFO AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_MYPROFILE AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_CHANNEL_INFO AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
#endif // English (Neutral) resources
/////////////////////////////////////////////////////////////////////////////
diff --git a/protocols/Telegram/src/add_phone.cpp b/protocols/Telegram/src/add_phone.cpp
index fbb61560c9..f799f91d77 100644
--- a/protocols/Telegram/src/add_phone.cpp
+++ b/protocols/Telegram/src/add_phone.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
diff --git a/protocols/Telegram/src/auth.cpp b/protocols/Telegram/src/auth.cpp
index b23e5a4c20..c09d5b20e0 100644
--- a/protocols/Telegram/src/auth.cpp
+++ b/protocols/Telegram/src/auth.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
diff --git a/protocols/Telegram/src/avatars.cpp b/protocols/Telegram/src/avatars.cpp
index 96e861419b..6b7e0bbd09 100644
--- a/protocols/Telegram/src/avatars.cpp
+++ b/protocols/Telegram/src/avatars.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
@@ -191,6 +191,29 @@ TG_FILE_REQUEST* CTelegramProto::FindFile(const char *pszUniqueId)
return nullptr;
}
+TG_FILE_REQUEST* CTelegramProto::FindFile(int id)
+{
+ mir_cslock lck(m_csFiles);
+
+ for (auto &it : m_arFiles)
+ if (it->m_fileId == id)
+ return it;
+
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CTelegramProto::ShowFileProgress(const TD::file *pFile, TG_FILE_REQUEST *ft)
+{
+ PROTOFILETRANSFERSTATUS fts = {};
+ fts.hContact = ft->m_hContact;
+ fts.totalFiles = 1;
+ fts.totalBytes = fts.currentFileSize = pFile->size_;
+ fts.totalProgress = fts.currentFileProgress = pFile->remote_->uploaded_size_;
+ ProtoBroadcastAck((UINT_PTR)ft->m_hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&fts);
+}
+
/////////////////////////////////////////////////////////////////////////////////////////
// Extracts a photo/avatar to a file
@@ -227,30 +250,33 @@ void CTelegramProto::ProcessFile(TD::updateFile *pObj)
return;
if (!pLocal->is_downloading_completed_) {
- if (auto *F = FindFile(pRemote->unique_id_.c_str())) {
- if (F->m_type != F->AVATAR && F->ofd) {
- DBVARIANT dbv = { DBVT_DWORD };
- dbv.dVal = pLocal->downloaded_size_;
- db_event_setJson(F->ofd->hDbEvent, "ft", &dbv);
- }
+ auto *ft = FindFile(pRemote->unique_id_.c_str());
+ if (ft && ft->m_type != ft->AVATAR && ft->ofd) {
+ DBVARIANT dbv = { DBVT_DWORD };
+ dbv.dVal = pLocal->downloaded_size_;
+ db_event_setJson(ft->ofd->hDbEvent, "ft", &dbv);
}
return;
}
// file upload is not completed, skip it
- if (pRemote->is_uploading_active_)
+ if (pRemote->is_uploading_active_) {
+ auto *ft = FindFile(pFile->id_);
+ if (ft)
+ ShowFileProgress(pFile, ft);
return;
+ }
Utf2T wszExistingFile(pLocal->path_.c_str());
- if (auto *F = FindFile(pRemote->unique_id_.c_str())) {
- if (F->m_type == F->AVATAR) {
- CMStringW wszFullName = F->m_destPath;
+ if (auto *ft = FindFile(pRemote->unique_id_.c_str())) {
+ if (ft->m_type == ft->AVATAR) {
+ CMStringW wszFullName = ft->m_destPath;
if (!wszFullName.IsEmpty())
wszFullName += L"\\";
- wszFullName += F->m_fileName;
+ wszFullName += ft->m_fileName;
- if (F->m_fileName.Right(5).MakeLower() == L".webp") {
+ if (ft->m_fileName.Right(5).MakeLower() == L".webp") {
if (auto *pImage = FreeImage_LoadU(FIF_WEBP, wszExistingFile)) {
wszFullName.Truncate(wszFullName.GetLength() - 5);
wszFullName += L".png";
@@ -258,7 +284,7 @@ void CTelegramProto::ProcessFile(TD::updateFile *pObj)
FreeImage_Unload(pImage);
}
}
- else if (F->m_fileName.Right(4).MakeLower() == L".tga") {
+ else if (ft->m_fileName.Right(4).MakeLower() == L".tga") {
if (auto *pImage = FreeImage_LoadU(FIF_TARGA, wszExistingFile)) {
wszFullName.Truncate(wszFullName.GetLength() - 5);
wszFullName += L".png";
@@ -268,54 +294,53 @@ void CTelegramProto::ProcessFile(TD::updateFile *pObj)
}
else MoveFileW(wszExistingFile, wszFullName);
- if (F->m_isSmiley)
+ if (ft->m_isSmiley)
SmileyAdd_LoadContactSmileys(SMADD_FILE, m_szModuleName, wszFullName);
else
NS_NotifyFileReady(wszFullName);
}
else { // FILE, PICTURE, VIDEO, VOICE
- if (F->ofd == nullptr)
- return;
-
- DBVARIANT dbv = { DBVT_DWORD };
- dbv.dVal = pLocal->downloaded_size_;
- db_event_setJson(F->ofd->hDbEvent, "ft", &dbv);
-
- CMStringW wszFullName(F->ofd->wszPath);
- int idxSlash = wszFullName.ReverseFind('\\') + 1;
- if (wszFullName.Find('.', idxSlash) == -1) {
- auto *pSlash = strrchr(pLocal->path_.c_str(), '\\');
- if (!pSlash)
- pSlash = pLocal->path_.c_str();
- else
- pSlash++;
-
- if (strchr(pSlash, '.')) {
- dbv.type = DBVT_UTF8;
- dbv.pszVal = (char *)pSlash;
- db_event_setJson(F->ofd->hDbEvent, "f", &dbv);
-
- wszFullName.Truncate(idxSlash);
- wszFullName.Append(Utf2T(pSlash));
- F->ofd->ResetFileName(wszFullName); // resulting ofd->wszPath may differ from wszFullName
- }
- else {
- int iFormat = ProtoGetAvatarFileFormat(wszExistingFile);
- if (iFormat != PA_FORMAT_UNKNOWN) {
- wszFullName.AppendChar('.');
- wszFullName.Append(ProtoGetAvatarExtension(iFormat));
- F->ofd->ResetFileName(wszFullName);
+ if (ft->ofd) {
+ DBVARIANT dbv = { DBVT_DWORD };
+ dbv.dVal = pLocal->downloaded_size_;
+ db_event_setJson(ft->ofd->hDbEvent, "ft", &dbv);
+
+ CMStringW wszFullName(ft->ofd->wszPath);
+ int idxSlash = wszFullName.ReverseFind('\\') + 1;
+ if (wszFullName.Find('.', idxSlash) == -1) {
+ auto *pSlash = strrchr(pLocal->path_.c_str(), '\\');
+ if (!pSlash)
+ pSlash = pLocal->path_.c_str();
+ else
+ pSlash++;
+
+ if (strchr(pSlash, '.')) {
+ dbv.type = DBVT_UTF8;
+ dbv.pszVal = (char *)pSlash;
+ db_event_setJson(ft->ofd->hDbEvent, "f", &dbv);
+
+ wszFullName.Truncate(idxSlash);
+ wszFullName.Append(Utf2T(pSlash));
+ ft->ofd->ResetFileName(wszFullName); // resulting ofd->wszPath may differ from wszFullName
+ }
+ else {
+ int iFormat = ProtoGetAvatarFileFormat(wszExistingFile);
+ if (iFormat != PA_FORMAT_UNKNOWN) {
+ wszFullName.AppendChar('.');
+ wszFullName.Append(ProtoGetAvatarExtension(iFormat));
+ ft->ofd->ResetFileName(wszFullName);
+ }
}
}
- }
- MoveFileW(wszExistingFile, F->ofd->wszPath);
- F->ofd->Finish();
+ MoveFileW(wszExistingFile, ft->ofd->wszPath);
+ ft->ofd->Finish();
+ }
}
mir_cslock lck(m_csFiles);
- m_arFiles.remove(F);
- delete F;
+ m_arFiles.remove(ft);
+ delete ft;
return;
}
@@ -331,6 +356,15 @@ void CTelegramProto::ProcessFile(TD::updateFile *pObj)
db_event_delivered(it->hContact, hDbEvent);
}
}
+
+ if (auto *ft = FindFile(pFile->id_)) {
+ // file being uploaded is finished
+ ProtoBroadcastAck(ft->m_hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft);
+
+ mir_cslock lck(m_csFiles);
+ m_arFiles.remove(ft);
+ delete ft;
+ }
return;
}
}
diff --git a/protocols/Telegram/src/groupchat.cpp b/protocols/Telegram/src/groupchat.cpp
index e3a646dc03..c4f134f6d9 100644
--- a/protocols/Telegram/src/groupchat.cpp
+++ b/protocols/Telegram/src/groupchat.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
@@ -46,7 +46,7 @@ void CTelegramProto::InitGroupChat(TG_USER *pUser, const wchar_t *pwszTitle)
if (pUser->bLoadMembers) {
pUser->m_si = si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, wszId, pwszTitle, pUser);
- if (!si->pStatuses) {
+ if (!si->arStatuses.getCount()) {
Chat_AddGroup(si, TranslateT("Creator"));
Chat_AddGroup(si, TranslateT("Admin"));
Chat_AddGroup(si, TranslateT("Participant"));
@@ -61,7 +61,7 @@ void CTelegramProto::InitGroupChat(TG_USER *pUser, const wchar_t *pwszTitle)
}
else {
pUser->m_si = si = Chat_NewSession(GCW_CHANNEL, m_szModuleName, wszId, pwszTitle, pUser);
- if (!si->pStatuses) {
+ if (!si->arStatuses.getCount()) {
Chat_AddGroup(si, TranslateT("SuperAdmin"));
Chat_AddGroup(si, TranslateT("Visitor"));
@@ -391,67 +391,36 @@ void CTelegramProto::ProcessBasicGroup(TD::updateBasicGroup *pObj)
pUser->bLoadMembers = true;
}
-void CTelegramProto::ProcessBasicGroupInfo(TD::updateBasicGroupFullInfo *pObj)
+void CTelegramProto::ProcessBasicGroupInfo(TG_USER *pChat, TD::basicGroupFullInfo *pInfo)
{
- auto *pChat = FindUser(pObj->basic_group_id_);
- if (pChat == nullptr || pChat->m_si == nullptr)
- return;
-
- if (auto *pInfo = pObj->basic_group_full_info_.get()) {
- if (!pInfo->description_.empty())
- GcChangeTopic(pChat, pInfo->description_);
-
- g_chatApi.UM_RemoveAll(pChat->m_si);
- GcAddMembers(pChat, pInfo->members_, true);
- }
-}
-
-void CTelegramProto::ProcessForum(TD::updateForumTopicInfo *pForum)
-{
- auto *pUser = FindChat(pForum->chat_id_);
- if (!pUser) {
- debugLogA("Uknown chat id %lld, skipping", pForum->chat_id_);
- return;
- }
-
- auto *pInfo = pForum->info_.get();
- if (pUser->m_si == nullptr) {
- debugLogA("No parent chat for id %lld, skipping", pForum->chat_id_);
- return;
- }
-
- if (pInfo->is_general_) {
- SetId(pUser->m_si->hContact, pForum->info_->message_thread_id_, DBKEY_THREAD);
- return;
+ if (!pInfo->description_.empty()) {
+ setUString(pChat->hContact, "About", pInfo->description_.c_str());
+ GcChangeTopic(pChat, pInfo->description_);
}
- wchar_t wszId[100];
- mir_snwprintf(wszId, L"%lld_%lld", pForum->chat_id_, pForum->info_->message_thread_id_);
-
- auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, wszId, Utf2T(pForum->info_->name_.c_str()), pUser);
- si->pParent = pUser->m_si;
-
- SetId(si->hContact, pForum->info_->message_thread_id_, DBKEY_THREAD);
- SetId(si->hContact, pUser->id, DBKEY_OWNER);
-
- Chat_Mute(si->hContact, Chat_IsMuted(pUser->hContact));
- Clist_SetGroup(si->hContact, ptrW(Clist_GetGroup(pUser->hContact)));
-
- Chat_Control(si, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE);
- Chat_Control(si, SESSION_ONLINE);
+ g_chatApi.UM_RemoveAll(pChat->m_si);
+ GcAddMembers(pChat, pInfo->members_, true);
}
-void CTelegramProto::ProcessSuperGroupInfo(TD::updateSupergroupFullInfo *pObj)
+void CTelegramProto::ProcessSuperGroupInfo(TG_USER *pUser, TD::supergroupFullInfo *pInfo)
{
- auto *pChat = FindUser(pObj->supergroup_id_);
- if (pChat == nullptr) {
- debugLogA("Uknown super group id %lld, skipping", pObj->supergroup_id_);
- return;
+ setDword(pUser->hContact, "MemberCount", pInfo->member_count_);
+
+ if (auto *pLink = pInfo->invite_link_.get())
+ setUString(pUser->hContact, "Link", pLink->invite_link_.c_str());
+ else if (auto *pGroup = FindSuperGroup(pUser->id)) {
+ if (pGroup->group->usernames_) {
+ CMStringA szLink(FORMAT, "https://t.me/%s", pGroup->group->usernames_->editable_username_.c_str());
+ setString(pUser->hContact, "Link", szLink);
+ }
+ else delSetting(pUser->hContact, "Link");
}
+ else delSetting(pUser->hContact, "Link");
- auto *pInfo = pObj->supergroup_full_info_.get();
- if (!pInfo->description_.empty())
- GcChangeTopic(pChat, pInfo->description_);
+ if (!pInfo->description_.empty()) {
+ setUString(pUser->hContact, "About", pInfo->description_.c_str());
+ GcChangeTopic(pUser, pInfo->description_);
+ }
}
void CTelegramProto::ProcessSuperGroup(TD::updateSupergroup *pObj)
@@ -462,16 +431,16 @@ void CTelegramProto::ProcessSuperGroup(TD::updateSupergroup *pObj)
return;
}
- TG_SUPER_GROUP tmp(pObj->supergroup_->id_, 0);
- auto *pGroup = m_arSuperGroups.find(&tmp);
+ auto id = pObj->supergroup_->id_;
+ auto *pGroup = FindSuperGroup(id);
if (pGroup == nullptr) {
- pGroup = new TG_SUPER_GROUP(tmp.id, std::move(pObj->supergroup_));
+ pGroup = new TG_SUPER_GROUP(id, std::move(pObj->supergroup_));
m_arSuperGroups.insert(pGroup);
}
else pGroup->group = std::move(pObj->supergroup_);
if (iStatusId == TD::chatMemberStatusLeft::ID) {
- auto *pUser = AddFakeUser(tmp.id, true);
+ auto *pUser = AddFakeUser(id, true);
pUser->isForum = pGroup->group->is_forum_;
if (pUser->hContact == INVALID_CONTACT_ID) {
// cache some information for the search
@@ -482,7 +451,7 @@ void CTelegramProto::ProcessSuperGroup(TD::updateSupergroup *pObj)
else RemoveFromClist(pUser);
}
else {
- auto *pChat = AddUser(tmp.id, true);
+ auto *pChat = AddUser(id, true);
pChat->isForum = pGroup->group->is_forum_;
if (!pGroup->group->is_channel_)
pChat->bLoadMembers = true;
@@ -506,3 +475,57 @@ void CTelegramProto::ProcessSuperGroup(TD::updateSupergroup *pObj)
}
}
}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// forums
+
+void CTelegramProto::OnGetTopics(td::ClientManager::Response &response, void *pUserInfo)
+{
+ if (!response.object)
+ return;
+
+ if (response.object->get_id() != TD::forumTopics::ID)
+ return;
+
+ auto *pUser = (TG_USER *)pUserInfo;
+
+ auto *pInfo = (TD::forumTopics *)response.object.get();
+ if (pInfo->topics_.size() >= 100)
+ SendQuery(new TD::getForumTopics(pUser->chatId, "", pInfo->next_offset_date_, pInfo->next_offset_message_id_, pInfo->next_offset_message_thread_id_, 100),
+ &CTelegramProto::OnGetTopics, pUser);
+}
+
+void CTelegramProto::ProcessForum(TD::updateForumTopicInfo *pForum)
+{
+ auto *pUser = FindChat(pForum->chat_id_);
+ if (!pUser) {
+ debugLogA("Uknown chat id %lld, skipping", pForum->chat_id_);
+ return;
+ }
+
+ auto *pInfo = pForum->info_.get();
+ if (pUser->m_si == nullptr) {
+ debugLogA("No parent chat for id %lld, skipping", pForum->chat_id_);
+ return;
+ }
+
+ if (pInfo->is_general_) {
+ SetId(pUser->m_si->hContact, pForum->info_->message_thread_id_, DBKEY_THREAD);
+ return;
+ }
+
+ wchar_t wszId[100];
+ mir_snwprintf(wszId, L"%lld_%lld", pForum->chat_id_, pForum->info_->message_thread_id_);
+
+ auto *si = Chat_NewSession(GCW_CHATROOM, m_szModuleName, wszId, Utf2T(pForum->info_->name_.c_str()), pUser);
+ si->pParent = pUser->m_si;
+
+ SetId(si->hContact, pForum->info_->message_thread_id_, DBKEY_THREAD);
+ SetId(si->hContact, pUser->id, DBKEY_OWNER);
+
+ Chat_Mute(si->hContact, Chat_IsMuted(pUser->hContact));
+ Clist_SetGroup(si->hContact, ptrW(Clist_GetGroup(pUser->hContact)));
+
+ Chat_Control(si, m_bHideGroupchats ? WINDOW_HIDDEN : SESSION_INITDONE);
+ Chat_Control(si, SESSION_ONLINE);
+}
diff --git a/protocols/Telegram/src/menus.cpp b/protocols/Telegram/src/menus.cpp
index 413193d8b0..f636ad0cb3 100644
--- a/protocols/Telegram/src/menus.cpp
+++ b/protocols/Telegram/src/menus.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
diff --git a/protocols/Telegram/src/options.cpp b/protocols/Telegram/src/options.cpp
index be588e33ea..04a741e829 100644
--- a/protocols/Telegram/src/options.cpp
+++ b/protocols/Telegram/src/options.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
@@ -22,7 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class COptionsDlg : public CTelegramDlgBase
{
CCtrlButton btnLogout;
- CCtrlCheck chkHideChats, chkCompressFiles, chkIncludePreviews, chkResidentChannels;
+ CCtrlCheck chkHideChats, chkCompressFiles, chkIncludePreviews, chkResidentChannels, chkDeleteContacts;
CCtrlCombo cmbCountry;
CCtrlEdit edtGroup, edtPhone, edtDeviceName;
ptrW m_wszOldGroup;
@@ -37,6 +37,7 @@ public:
edtGroup(this, IDC_DEFGROUP),
edtDeviceName(this, IDC_DEVICE_NAME),
chkCompressFiles(this, IDC_COMPRESS_FILES),
+ chkDeleteContacts(this, IDC_DELETE_CONTACTS),
chkIncludePreviews(this, IDC_USE_PREVIEW),
chkResidentChannels(this, IDC_RESIDENT_CHANNELS),
m_wszOldGroup(mir_wstrdup(ppro->m_wszDefaultGroup))
@@ -48,6 +49,7 @@ public:
if (bFullDlg) {
CreateLink(chkCompressFiles, ppro->m_bCompressFiles);
+ CreateLink(chkDeleteContacts, ppro->m_bDeleteContacts);
CreateLink(chkIncludePreviews, ppro->m_bIncludePreviews);
CreateLink(chkResidentChannels, ppro->m_bResidentChannels);
}
@@ -170,7 +172,7 @@ public:
void onChange_Value1(int val)
{
- bool bEnabled = val != 0;
+ bool bEnabled = val > 0;
spin2.Enable(bEnabled);
edtDiff2.Enable(bEnabled);
cmbStatus1.Enable(bEnabled);
diff --git a/protocols/Telegram/src/proto.cpp b/protocols/Telegram/src/proto.cpp
index efb92742fb..66fab24119 100644
--- a/protocols/Telegram/src/proto.cpp
+++ b/protocols/Telegram/src/proto.cpp
@@ -17,7 +17,10 @@ static int CompareRequests(const TG_REQUEST_BASE *p1, const TG_REQUEST_BASE *p2)
}
static int CompareChats(const TG_USER *p1, const TG_USER *p2)
-{ return CompareId(p1->chatId, p2->chatId);
+{
+ if (p1->chatId != p2->chatId)
+ return CompareId(p1->chatId, p2->chatId);
+ return CompareId(p1->forumId, p2->forumId);
}
static int CompareUsers(const TG_USER *p1, const TG_USER *p2)
@@ -57,6 +60,7 @@ CTelegramProto::CTelegramProto(const char* protoName, const wchar_t* userName) :
m_bUsePopups(this, "UsePopups", true),
m_bCompressFiles(this, "CompressFiles", true),
m_bHideGroupchats(this, "HideChats", true),
+ m_bDeleteContacts(this, "DeleteContacts", false),
m_bIncludePreviews(this, "IncludePreview", true),
m_bResidentChannels(this, "ResidentChannels", false)
{
@@ -108,6 +112,8 @@ CTelegramProto::CTelegramProto(const char* protoName, const wchar_t* userName) :
HookProtoEvent(ME_GC_MUTE, &CTelegramProto::GcMuteHook);
HookProtoEvent(ME_GC_EVENT, &CTelegramProto::GcEventHook);
HookProtoEvent(ME_GC_BUILDMENU, &CTelegramProto::GcMenuHook);
+
+ CheckCompatibility();
}
CTelegramProto::~CTelegramProto()
@@ -125,6 +131,12 @@ void CTelegramProto::OnCacheInit()
if (int64_t id = GetId(cc)) {
bool isGroupChat = isChatRoom(cc);
auto *pUser = new TG_USER(id, cc, isGroupChat);
+ if (auto iThreadId = GetId(cc, DBKEY_THREAD)) {
+ pUser->isForum = true;
+ pUser->chatId = pUser->id;
+ pUser->forumId = iThreadId;
+ m_arChats.insert(pUser);
+ }
pUser->szAvatarHash = getMStringA(cc, DBKEY_AVATAR_HASH);
m_arUsers.insert(pUser);
if (isGroupChat && iCompatLevel < 3)
@@ -174,6 +186,8 @@ void CTelegramProto::OnModulesLoaded()
{
InitPopups();
+ HookProtoEvent(ME_USERINFO_INITIALISE, &CTelegramProto::OnUserInfoInit);
+
m_bSmileyAdd = ServiceExists(MS_SMILEYADD_REPLACESMILEYS);
if (m_bSmileyAdd)
SmileyAdd_LoadContactSmileys(SMADD_FOLDER, m_szModuleName, GetAvatarPath() + L"\\Stickers\\*.*");
@@ -183,9 +197,10 @@ void CTelegramProto::OnShutdown()
{
m_bTerminated = true;
- for (auto &cc : m_arUsers)
- if (cc->isBot && !cc->chatId && cc->hContact != INVALID_CONTACT_ID)
- Contact::RemoveFromList(cc->hContact);
+ if (m_bDeleteContacts)
+ for (auto &cc : m_arUsers)
+ if (cc->isBot && !cc->chatId && cc->hContact != INVALID_CONTACT_ID)
+ Contact::RemoveFromList(cc->hContact);
}
int CTelegramProto::OnWindowEvent(WPARAM wParam, LPARAM lParam)
@@ -264,9 +279,9 @@ void CTelegramProto::OnEventEdited(MCONTACT hContact, MEVENT, const DBEVENTINFO
return;
if (dbei.szId && dbei.cbBlob && dbei.pBlob && dbei.eventType == EVENTTYPE_MESSAGE) {
- auto text = formatBbcodes((char*)dbei.pBlob);
- // auto content = TD::make_object<TD::inputMessageText>(std::move(text), 0, false);
- // SendQuery(new TD::editMessageText(pUser->chatId, dbei2id(dbei), 0, std::move(content)));
+ auto content = TD::make_object<TD::inputMessageText>();
+ content->text_ = formatBbcodes((char *)dbei.pBlob);
+ SendQuery(new TD::editMessageText(pUser->chatId, dbei2id(dbei), 0, std::move(content)));
}
}
@@ -377,6 +392,50 @@ INT_PTR CTelegramProto::GetCaps(int type, MCONTACT hContact)
/////////////////////////////////////////////////////////////////////////////////////////
+int CTelegramProto::GetInfo(MCONTACT hContact, int)
+{
+ if (auto *pUser = FindUser(GetId(hContact))) {
+ if (!pUser->isGroupChat)
+ SendQuery(new TD::getUserFullInfo(pUser->id), &CTelegramProto::OnGetUserInfo, pUser);
+ else {
+ if (FindSuperGroup(pUser->id))
+ SendQuery(new TD::getSupergroupFullInfo(pUser->id), &CTelegramProto::OnGetUserInfo, pUser);
+ else
+ SendQuery(new TD::getBasicGroupFullInfo(pUser->id), &CTelegramProto::OnGetUserInfo, pUser);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+void CTelegramProto::OnGetUserInfo(td::ClientManager::Response &response, void *pUserInfo)
+{
+ if (!response.object)
+ return;
+
+ auto *pUser = (TG_USER *)pUserInfo;
+
+ switch (response.object->get_id()) {
+ case TD::basicGroupFullInfo::ID:
+ ProcessBasicGroupInfo(pUser, (TD::basicGroupFullInfo *)response.object.get());
+ break;
+ case TD::supergroupFullInfo::ID:
+ ProcessSuperGroupInfo(pUser, (TD::supergroupFullInfo *)response.object.get());
+ break;
+ case TD::userFullInfo::ID:
+ ProcessUserInfo(pUser->id, (TD::userFullInfo *)response.object.get());
+ break;
+
+ default:
+ debugLogA("Gotten class ID %d instead of %d, exiting", response.object->get_id(), TD::chats::ID);
+ return;
+ }
+
+ ProtoBroadcastAck(pUser->hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, HANDLE(1));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTelegramProto::OnSearchResults(td::ClientManager::Response &response)
{
int iCount = ::InterlockedDecrement(&m_iSearchCount);
@@ -447,6 +506,16 @@ void CTelegramProto::ProcessFileMessage(TG_FILE_REQUEST *ft, const TD::message *
return;
}
+ // if that file was sent once, it might be cached at the server & reused
+ if (auto *pRemote = pFile->remote_.get()) {
+ if (pRemote->is_uploading_completed_) {
+ ShowFileProgress(pFile, ft);
+ ProtoBroadcastAck(ft->m_hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft);
+ delete ft;
+ return;
+ }
+ }
+
char szUserId[100];
auto szMsgId(msg2id(pMsg));
@@ -462,24 +531,32 @@ void CTelegramProto::ProcessFileMessage(TG_FILE_REQUEST *ft, const TD::message *
dbei.szModule = Proto_GetBaseAccountName(ft->m_hContact);
dbei.eventType = EVENTTYPE_FILE;
dbei.flags = DBEF_SENT | DBEF_UTF;
- dbei.timestamp = time(0);
-
- TG_FILE_REQUEST localft(TG_FILE_REQUEST::FILE, 0, 0);
- localft.m_fileName = Utf2T(pFile->local_->path_.c_str());
- localft.m_fileSize = pFile->size_;
- localft.m_uniqueId = szMsgId;
- localft.m_szUserId = szUserId;
-
- DB::FILE_BLOB blob(localft.m_fileName, ft->m_wszDescr);
- OnSendOfflineFile(dbei, blob, &localft);
+ dbei.iTimestamp = time(0);
+
+ auto *localft = new TG_FILE_REQUEST(TG_FILE_REQUEST::FILE, 0, 0);
+ localft->m_hContact = ft->m_hContact;
+ localft->m_fileName = Utf2T(pFile->local_->path_.c_str());
+ localft->m_fileSize = pFile->size_;
+ localft->m_uniqueId = szMsgId;
+ localft->m_szUserId = szUserId;
+ localft->m_fileId = pFile->id_;
+
+ DB::FILE_BLOB blob(localft->m_fileName, ft->m_wszDescr);
+ OnSendOfflineFile(dbei, blob, localft);
blob.write(dbei);
db_event_add(ft->m_hContact, &dbei);
+
+ mir_cslock lck(m_csFiles);
+ m_arFiles.insert(localft);
}
else {
ft->m_szUserId = szUserId;
ft->m_uniqueId = szMsgId;
- ProtoBroadcastAck(ft->m_hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft);
+ ft->m_fileId = pFile->id_;
+
+ mir_cslock lck(m_csFiles);
+ m_arFiles.insert(ft);
}
}
}
@@ -490,7 +567,6 @@ void CTelegramProto::OnSendFile(td::ClientManager::Response &response, void *pUs
if (response.object->get_id() == TD::message::ID) {
ProcessFileMessage(ft, (TD::message *)response.object.get(), false);
- ProtoBroadcastAck(ft->m_hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ft);
}
else if (response.object->get_id() == TD::messages::ID) {
int i = 0;
@@ -500,8 +576,6 @@ void CTelegramProto::OnSendFile(td::ClientManager::Response &response, void *pUs
i++;
}
}
-
- delete ft;
}
HANDLE CTelegramProto::SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles)
@@ -586,8 +660,8 @@ HANDLE CTelegramProto::SendFile(MCONTACT hContact, const wchar_t *szDescription,
pPart = std::move(pContent);
}
else if (iFileType == TG_FILE_REQUEST::VOICE) {
- auto pContent = TD::make_object<TD::inputMessageVoiceNote>();
- pContent->voice_note_ = makeFile(it->pwszName);
+ auto pContent = TD::make_object<TD::inputMessageAudio>();
+ pContent->audio_ = makeFile(it->pwszName);
pContent->caption_ = std::move(caption);
pContent->duration_ = 0;
pPart = std::move(pContent);
@@ -654,6 +728,8 @@ int CTelegramProto::SetStatus(int iNewStatus)
// Routing statuses not supported by Telegram
switch (iNewStatus) {
case ID_STATUS_OFFLINE:
+ case ID_STATUS_AWAY:
+ case ID_STATUS_NA:
m_iDesiredStatus = iNewStatus;
break;
diff --git a/protocols/Telegram/src/proto.h b/protocols/Telegram/src/proto.h
index 69737ba35c..a465104f01 100644
--- a/protocols/Telegram/src/proto.h
+++ b/protocols/Telegram/src/proto.h
@@ -99,9 +99,9 @@ struct TG_USER : public MZeroedObject
delete pReactions;
}
- int64_t id, chatId = -1;
+ int64_t id, chatId = -1, forumId = -1, lastReadId;
MCONTACT hContact;
- int folderId = -1, nHistoryChunks;
+ int folderId = -1, nTopics;
bool isGroupChat, isChannel, isBot, isForum, bLoadMembers, bStartChat, bInited, bDelOwn = true, bDelAll = true;
CMStringA szAvatarHash;
CMStringW wszNick, wszFirstName, wszLastName;
@@ -154,6 +154,7 @@ class CTelegramProto : public PROTO<CTelegramProto>
friend class CReplyDlg;
friend class CForwardDlg;
friend class CReactionsDlg;
+ friend class CMyProfileDlg;
friend class COptionsDlg;
friend class COptSessionsDlg;
friend class CAddPhoneContactDlg;
@@ -215,11 +216,12 @@ class CTelegramProto : public PROTO<CTelegramProto>
OBJLIST<TG_OWN_MESSAGE> m_arOwnMsg;
mir_cs m_csRequests;
- OBJLIST<TG_REQUEST_BASE> m_arRequests;
+ LIST<TG_REQUEST_BASE> m_arRequests;
mir_cs m_csFiles;
LIST<TG_FILE_REQUEST> m_arFiles;
TG_FILE_REQUEST* FindFile(const char *pszUniqueId);
+ TG_FILE_REQUEST* FindFile(int id);
static INT_PTR CALLBACK EnterEmail(void *param);
static INT_PTR CALLBACK EnterEmailCode(void *param);
@@ -236,6 +238,7 @@ class CTelegramProto : public PROTO<CTelegramProto>
void OnGetFileLink(td::ClientManager::Response &response);
void OnGetHistory(td::ClientManager::Response &response, void *pUserInfo);
void OnGetSessions(td::ClientManager::Response &response, void *pUserInfo);
+ void OnGetUserInfo(td::ClientManager::Response &response, void *pUserInfo);
void OnKillSession(td::ClientManager::Response &response, void *pUserInfo);
void OnLeaveChat(td::ClientManager::Response &response, void *pUserInfo);
void OnSendFile(td::ClientManager::Response &response, void *pUserInfo);
@@ -257,7 +260,7 @@ class CTelegramProto : public PROTO<CTelegramProto>
void ProcessAvatar(const TD::file *pFile, TG_USER *pUser);
void ProcessAuth(TD::updateAuthorizationState *pObj);
void ProcessBasicGroup(TD::updateBasicGroup *pObj);
- void ProcessBasicGroupInfo(TD::updateBasicGroupFullInfo *pObj);
+ void ProcessBasicGroupInfo(TG_USER *pUser, TD::basicGroupFullInfo *pObj);
void ProcessChat(TD::updateNewChat *pObj);
void ProcessChatAction(TD::updateChatAction *pObj);
void ProcessChatHasProtected(TD::updateChatHasProtectedContent *pObj);
@@ -282,8 +285,9 @@ class CTelegramProto : public PROTO<CTelegramProto>
void ProcessServiceNotification(TD::updateServiceNotification *pObj);
void ProcessStatus(TD::updateUserStatus *pObj);
void ProcessSuperGroup(TD::updateSupergroup *pObj);
- void ProcessSuperGroupInfo(TD::updateSupergroupFullInfo *pObj);
+ void ProcessSuperGroupInfo(TG_USER *pUser, TD::supergroupFullInfo *pObj);
void ProcessUser(TD::updateUser *pObj);
+ void ProcessUserInfo(TD::int53 userId, TD::userFullInfo *pObj);
void UnregisterSession();
@@ -300,6 +304,8 @@ class CTelegramProto : public PROTO<CTelegramProto>
bool bRead;
};
+ void ShowFileProgress(const TD::file *pFile, TG_FILE_REQUEST *ft);
+
bool GetMessageFile(const EmbeddedFile &embed, TG_FILE_REQUEST::Type, const TD::file *pFile, const char *pszFileName, const char *pszCaption);
CMStringA GetFormattedText(TD::object_ptr<TD::formattedText> &pText);
@@ -340,6 +346,7 @@ class CTelegramProto : public PROTO<CTelegramProto>
TD::array<TD::int53> m_searchIds;
void OnSearchResults(td::ClientManager::Response &response);
+ void OnGetTopics(td::ClientManager::Response &response, void *pUserInfo);
bool CheckSearchUser(TG_USER *pUser);
void ReportSearchUser(TG_USER *pUser);
@@ -352,17 +359,21 @@ class CTelegramProto : public PROTO<CTelegramProto>
OBJLIST<TG_USER> m_arUsers;
TG_USER* FindChat(int64_t id);
+ TG_USER* FindChat(int64_t id, int64_t forumId);
TG_USER* FindUser(int64_t id);
TG_USER* AddUser(int64_t id, bool bIsChat);
TG_USER* AddFakeUser(int64_t id, bool bIsChat);
TG_USER* GetSender(const TD::MessageSender *pSender);
+ TG_SUPER_GROUP* FindSuperGroup(int64_t id);
+
int64_t GetId(MCONTACT, const char *pszSetting = DBKEY_ID);
void SetId(MCONTACT, int64_t id, const char *pszSetting = DBKEY_ID);
int GetDefaultMute(const TG_USER *pUser);
- MCONTACT GetRealContact(const TG_USER *pUser);
+ void CheckCompatibility(void);
+ MCONTACT GetRealContact(const TG_USER *pUser, int64_t threadId = 0);
void RemoveFromClist(TG_USER *pUser);
void MarkRead(MCONTACT hContact, const CMStringA &szMaxId, bool bSent);
@@ -394,6 +405,7 @@ public:
int AuthRequest(MCONTACT hContact, const wchar_t *) override;
INT_PTR GetCaps(int type, MCONTACT hContact = NULL) override;
+ int GetInfo(MCONTACT hContact, int type) override;
HANDLE SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) override;
@@ -426,6 +438,8 @@ public:
int __cdecl GcMuteHook(WPARAM, LPARAM);
int __cdecl GcEventHook(WPARAM, LPARAM);
+ int __cdecl OnUserInfoInit(WPARAM, LPARAM);
+
// Services //////////////////////////////////////////////////////////////////////////
INT_PTR __cdecl SvcAddByPhone(WPARAM, LPARAM);
@@ -446,6 +460,7 @@ public:
CMOption<bool> m_bIncludePreviews; // include URL previews into message text
CMOption<bool> m_bResidentChannels; // don't store channel messages in a database
CMOption<bool> m_bCompressFiles; // embed pictures & videos into a message on send
+ CMOption<bool> m_bDeleteContacts; // delete contacts from Miranda when they are deleted from server
CMOption<uint32_t> m_iTimeDiff1; // set this status to m_iStatus1 after this interval of secs
CMOption<uint32_t> m_iStatus1;
CMOption<uint32_t> m_iTimeDiff2; // set this status to m_iStatus2 after this interval of secs
diff --git a/protocols/Telegram/src/resource.h b/protocols/Telegram/src/resource.h
index 9e6bebafce..5b55af12a3 100644
--- a/protocols/Telegram/src/resource.h
+++ b/protocols/Telegram/src/resource.h
@@ -13,10 +13,13 @@
#define IDI_REACTION 108
#define IDD_REACTIONS 109
#define IDI_BOT 110
+#define IDD_MYPROFILE 111
#define IDD_OPTIONS_SESSIONS 112
+#define IDD_CHANNEL_INFO 113
#define IDC_PHONE 1001
#define IDC_DEFGROUP 1002
#define IDC_HIDECHATS 1003
+#define IDC_DELETE_CONTACTS 1004
#define IDC_DEVICE_NAME 1005
#define IDC_DIFF1 1006
#define IDC_DIFF2 1007
@@ -33,17 +36,21 @@
#define IDC_REACTIONS 1018
#define IDC_USE_PREVIEW 1018
#define IDC_RESIDENT_CHANNELS 1019
+#define IDC_BIO 1020
+#define IDC_BIRTHDAY 1021
#define IDC_SESSIONS 1023
+#define IDC_LINK 1024
#define IDC_SOFTWARE 1025
-#define IDC_LOGOUT 1025
+#define IDC_LOGOUT 1026
+#define IDC_USER_COUNT 1027
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 116
+#define _APS_NEXT_RESOURCE_VALUE 120
#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1027
+#define _APS_NEXT_CONTROL_VALUE 1030
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
diff --git a/protocols/Telegram/src/server.cpp b/protocols/Telegram/src/server.cpp
index 952e076940..82e7225f01 100644
--- a/protocols/Telegram/src/server.cpp
+++ b/protocols/Telegram/src/server.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
@@ -194,11 +194,14 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
if (response.request_id) {
TG_REQUEST tmp(response.request_id, 0);
- mir_cslock lck(m_csRequests);
+ mir_cslockfull lck(m_csRequests);
auto *p = m_arRequests.find(&tmp);
if (p) {
- p->Execute(this, response);
m_arRequests.remove(p);
+ lck.unlock();
+
+ p->Execute(this, response);
+ delete p;
}
return;
}
@@ -213,11 +216,15 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
break;
case TD::updateBasicGroup::ID:
- ProcessBasicGroup((TD::updateBasicGroup*)response.object.get());
+ ProcessBasicGroup((TD::updateBasicGroup *)response.object.get());
break;
case TD::updateBasicGroupFullInfo::ID:
- ProcessBasicGroupInfo((TD::updateBasicGroupFullInfo *)response.object.get());
+ {
+ auto *pObj = (TD::updateBasicGroupFullInfo *)response.object.get();
+ if (auto *pChat = FindUser(pObj->basic_group_id_))
+ ProcessBasicGroupInfo(pChat, pObj->basic_group_full_info_.get());
+ }
break;
case TD::updateChatAction::ID:
@@ -241,7 +248,7 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
break;
case TD::updateChatNotificationSettings::ID:
- ProcessChatNotification((TD::updateChatNotificationSettings*)response.object.get());
+ ProcessChatNotification((TD::updateChatNotificationSettings *)response.object.get());
break;
case TD::updateChatPosition::ID:
@@ -257,9 +264,9 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
break;
case TD::updateDeleteMessages::ID:
- ProcessDeleteMessage((TD::updateDeleteMessages*)response.object.get());
+ ProcessDeleteMessage((TD::updateDeleteMessages *)response.object.get());
break;
-
+
case TD::updateConnectionState::ID:
ProcessConnectionState((TD::updateConnectionState *)response.object.get());
break;
@@ -269,7 +276,7 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
break;
case TD::updateForumTopicInfo::ID:
- ProcessForum((TD::updateForumTopicInfo*)response.object.get());
+ ProcessForum((TD::updateForumTopicInfo *)response.object.get());
break;
case TD::updateMessageContent::ID:
@@ -309,8 +316,7 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
ProcessChat((TD::updateNewChat *)response.object.get());
break;
- case TD::updateNewMessage::ID:
- {
+ case TD::updateNewMessage::ID: {
auto *pMessage = ((TD::updateNewMessage *)response.object.get())->message_.get();
TG_OWN_MESSAGE tmp(0, 0, msg2id(pMessage));
if (!m_arOwnMsg.find(&tmp))
@@ -335,7 +341,13 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
break;
case TD::updateSupergroupFullInfo::ID:
- ProcessSuperGroupInfo((TD::updateSupergroupFullInfo *)response.object.get());
+ {
+ auto *pObj = (TD::updateSupergroupFullInfo *)response.object.get();
+ if (auto *pUser = FindUser(pObj->supergroup_id_))
+ ProcessSuperGroupInfo(pUser, pObj->supergroup_full_info_.get());
+ else
+ debugLogA("Uknown super group id %lld, skipping", pObj->supergroup_id_);
+ }
break;
case TD::updateUserStatus::ID:
@@ -345,6 +357,11 @@ void CTelegramProto::ProcessResponse(td::ClientManager::Response response)
case TD::updateUser::ID:
ProcessUser((TD::updateUser *)response.object.get());
break;
+
+ case TD::updateUserFullInfo::ID:
+ if (auto *pObj = (TD::updateUserFullInfo *)response.object.get())
+ ProcessUserInfo(pObj->user_id_, pObj->user_full_info_.get());
+ break;
}
}
@@ -448,6 +465,12 @@ void CTelegramProto::OnGetHistory(td::ClientManager::Response &response, void *p
auto *pUser = (TG_USER *)pUserInfo;
TD::int53 lastMsgId = INT64_MAX;
auto *pMessages = (TD::messages *)response.object.get();
+ if (pMessages->messages_.size() == 0) {
+ if (pUser->isForum)
+ delete pUser;
+ return;
+ }
+
for (auto &it : pMessages->messages_) {
auto *pMsg = it.get();
if (pMsg->id_ < lastMsgId)
@@ -465,11 +488,13 @@ void CTelegramProto::OnGetHistory(td::ClientManager::Response &response, void *p
DBEVENTINFO dbei = {};
dbei.eventType = EVENTTYPE_MESSAGE;
dbei.szModule = m_szModuleName;
- dbei.timestamp = pMsg->date_;
+ dbei.iTimestamp = pMsg->date_;
dbei.cbBlob = szBody.GetLength();
dbei.pBlob = szBody.GetBuffer();
dbei.szId = szMsgId;
- dbei.flags = DBEF_READ | DBEF_UTF;
+ dbei.bRead = dbei.bUtf = true;
+ if (pMsg->edit_date_)
+ dbei.bEdited = true;
if (pMsg->is_outgoing_)
dbei.flags |= DBEF_SENT;
if (this->GetGcUserId(pUser, pMsg, szUserId))
@@ -481,27 +506,15 @@ void CTelegramProto::OnGetHistory(td::ClientManager::Response &response, void *p
db_event_add(GetRealContact(pUser), &dbei);
}
- pUser->nHistoryChunks--;
-
- if (pUser->isForum) {
- if (lastMsgId != INT64_MAX && pUser->nHistoryChunks > 0)
- SendQuery(new TD::getMessageThreadHistory(pUser->chatId, lastMsgId, lastMsgId, 0, 100), &CTelegramProto::OnGetHistory, pUser);
- else
- delete pUser;
- }
- else if (lastMsgId != INT64_MAX && pUser->nHistoryChunks > 0)
+ // fetch next portion
+ if (pUser->isForum)
+ SendQuery(new TD::getMessageThreadHistory(pUser->chatId, lastMsgId, lastMsgId, 0, 100), &CTelegramProto::OnGetHistory, pUser);
+ else
SendQuery(new TD::getChatHistory(pUser->chatId, lastMsgId, 0, 100, false), &CTelegramProto::OnGetHistory, pUser);
}
INT_PTR CTelegramProto::SvcLoadServerHistory(WPARAM hContact, LPARAM)
{
- TD::int53 lastMsgId = 0;
-
- if (MEVENT hEvent = db_event_first(hContact)) {
- DB::EventInfo dbei(hEvent, false);
- lastMsgId = dbei2id(dbei);
- }
-
auto userId = GetId(hContact);
if (TD::int53 threadId = GetId(hContact, DBKEY_THREAD)) {
@@ -509,16 +522,13 @@ INT_PTR CTelegramProto::SvcLoadServerHistory(WPARAM hContact, LPARAM)
auto *pUser = new TG_USER(-1, hContact, true);
pUser->chatId = userId;
pUser->isForum = pUser->isGroupChat = true;
- pUser->nHistoryChunks = 5;
- SendQuery(new TD::getMessageThreadHistory(pUser->chatId, lastMsgId, lastMsgId, 0, 100), &CTelegramProto::OnGetHistory, pUser);
+ SendQuery(new TD::getMessageThreadHistory(pUser->chatId, 0, 0, 0, 100), &CTelegramProto::OnGetHistory, pUser);
return 0;
}
}
- if (auto *pUser = FindUser(userId)) {
- pUser->nHistoryChunks = 5;
- SendQuery(new TD::getChatHistory(pUser->chatId, lastMsgId, 0, 100, false), &CTelegramProto::OnGetHistory, pUser);
- }
+ if (auto *pUser = FindUser(userId))
+ SendQuery(new TD::getChatHistory(pUser->chatId, 0, 0, 100, false), &CTelegramProto::OnGetHistory, pUser);
return 0;
}
@@ -528,8 +538,7 @@ INT_PTR CTelegramProto::SvcLoadServerHistory(WPARAM hContact, LPARAM)
INT_PTR CTelegramProto::SvcCanEmptyHistory(WPARAM hContact, LPARAM bIncoming)
{
if (auto *pUser = FindUser(GetId(hContact))) {
- TG_SUPER_GROUP tmp(pUser->id, 0);
- if (auto *pGroup = m_arSuperGroups.find(&tmp))
+ if (auto *pGroup = FindSuperGroup(pUser->id))
if (pGroup->group->is_channel_)
return 0;
@@ -594,6 +603,7 @@ void CTelegramProto::ProcessChat(TD::updateNewChat *pObj)
}
pUser->chatId = pChat->id_;
+ pUser->forumId = -1;
pUser->isChannel = isChannel;
pUser->bDelAll = pChat->can_be_deleted_for_all_users_;
pUser->bDelOwn = pChat->can_be_deleted_only_for_self_;
@@ -603,11 +613,12 @@ void CTelegramProto::ProcessChat(TD::updateNewChat *pObj)
if (!m_arChats.find(pUser))
m_arChats.insert(pUser);
+ // small or super group
if (!szTitle.empty()) {
if (hContact != INVALID_CONTACT_ID) {
if (pUser->isForum) {
pUser->wszNick = Utf2T(szTitle.c_str());
- SendQuery(new TD::getForumTopics(pUser->chatId, "", 0, 0, 0, 100));
+ SendQuery(new TD::getForumTopics(pUser->chatId, "", 0, 0, 0, 100), &CTelegramProto::OnGetTopics, pUser);
}
else GcChangeTopic(pUser, szTitle);
}
@@ -633,6 +644,15 @@ void CTelegramProto::ProcessChat(TD::updateNewChat *pObj)
if (pUser->isGroupChat && pUser->m_si == nullptr)
InitGroupChat(pUser, (pUser->isForum) ? TranslateT("General") : Utf2T(pChat->title_.c_str()));
}
+ else if (!pUser->isGroupChat) {
+ pUser = AddUser(pChat->id_, false);
+ hContact = pUser->hContact;
+ setWString(hContact, "Nick", pUser->wszNick);
+ if (!pUser->wszFirstName.IsEmpty())
+ setWString(hContact, "FirstName", pUser->wszFirstName);
+ if (!pUser->wszLastName.IsEmpty())
+ setWString(hContact, "LastName", pUser->wszLastName);
+ }
}
void CTelegramProto::ProcessChatAction(TD::updateChatAction *pObj)
@@ -675,11 +695,14 @@ void CTelegramProto::ProcessChatAction(TD::updateChatAction *pObj)
Srmm_SetStatusText(hContact, TranslateT("Uploading voice note..."));
break;
case TD::chatActionTyping::ID:
- CallService(MS_PROTO_CONTACTISTYPING, pChat->hContact, 30);
+ if (!pChat->isGroupChat)
+ CallService(MS_PROTO_CONTACTISTYPING, pChat->hContact, 30);
break;
case TD::chatActionCancel::ID:
- Srmm_SetStatusText(hContact, 0);
- CallService(MS_PROTO_CONTACTISTYPING, pChat->hContact, PROTOTYPE_CONTACTTYPING_OFF);
+ if (!pChat->isGroupChat) {
+ Srmm_SetStatusText(hContact, 0);
+ CallService(MS_PROTO_CONTACTISTYPING, pChat->hContact, PROTOTYPE_CONTACTTYPING_OFF);
+ }
break;
}
}
@@ -780,8 +803,9 @@ void CTelegramProto::ProcessChatPosition(TD::updateChatPosition *pObj)
debugLogW(L"Existing contact group <%s>, calculated <%s>", pwszExistingGroup.get(), wszGroup.c_str());
wchar_t *pwszDefaultGroup = m_wszDefaultGroup;
+ size_t defLen = mir_wstrlen(pwszDefaultGroup);
if (!pwszExistingGroup || pUser->isForum
- || !mir_wstrncmp(pwszExistingGroup, pwszDefaultGroup, mir_wstrlen(pwszDefaultGroup))
+ || (defLen && !mir_wstrncmp(pwszExistingGroup, pwszDefaultGroup, defLen))
|| (pUser->isGroupChat && !mir_wstrcmp(pwszExistingGroup, Chat_GetGroup())))
{
CMStringW wszNewGroup(pwszDefaultGroup);
@@ -790,6 +814,9 @@ void CTelegramProto::ProcessChatPosition(TD::updateChatPosition *pObj)
if (pUser->isForum)
wszNewGroup.AppendFormat(L"\\%s", pUser->wszNick.c_str());
+ if (wszNewGroup[0] == '\\')
+ wszNewGroup.Delete(0, 1);
+
debugLogW(L"Setting group for %d to %s", hContact, wszNewGroup.c_str());
Clist_GroupCreate(0, wszNewGroup);
Clist_SetGroup(hContact, wszNewGroup);
@@ -916,8 +943,10 @@ void CTelegramProto::ProcessMarkRead(TD::updateChatReadInbox *pObj)
return;
}
- if (pObj->last_read_inbox_message_id_)
+ if (pObj->last_read_inbox_message_id_) {
pUser->bInited = true;
+ pUser->lastReadId = pObj->last_read_inbox_message_id_;
+ }
CMStringA szMaxId(msg2id(pObj->chat_id_, pObj->last_read_inbox_message_id_));
if (db_event_getById(m_szModuleName, szMaxId) == 0) {
@@ -952,12 +981,6 @@ void CTelegramProto::ProcessMessage(const TD::message *pMessage)
auto szMsgId(msg2id(pMessage));
MEVENT hOldEvent = db_event_getById(m_szModuleName, szMsgId);
- CMStringA szText(GetMessageText(pUser, pMessage)), szReplyId;
- if (szText.IsEmpty()) {
- debugLogA("this message was not processed, ignored");
- return;
- }
-
// make a temporary contact if needed
if (pUser->hContact == INVALID_CONTACT_ID) {
if (pUser->isGroupChat) {
@@ -969,14 +992,14 @@ void CTelegramProto::ProcessMessage(const TD::message *pMessage)
Contact::RemoveFromList(pUser->hContact);
}
- MCONTACT hContact = GetRealContact(pUser);
- if (pMessage->message_thread_id_) {
- wchar_t buf[100];
- mir_snwprintf(buf, L"%lld_%lld", pMessage->chat_id_, pMessage->message_thread_id_);
- if (auto *si = Chat_Find(buf, m_szModuleName))
- hContact = si->hContact;
+ CMStringA szText(GetMessageText(pUser, pMessage)), szReplyId;
+ if (szText.IsEmpty()) {
+ debugLogA("this message was not processed, ignored");
+ return;
}
+ MCONTACT hContact = GetRealContact(pUser, pMessage->message_thread_id_);
+
if (m_bResidentChannels && pUser->isChannel && pUser->m_si) {
GCEVENT gce = { pUser->m_si, GC_EVENT_MESSAGE };
gce.dwFlags = GCEF_ADDTOLOG | GCEF_UTF8;
@@ -990,11 +1013,15 @@ void CTelegramProto::ProcessMessage(const TD::message *pMessage)
DB::EventInfo dbei(hOldEvent);
dbei.szId = szMsgId;
dbei.cbBlob = szText.GetLength();
- dbei.timestamp = pMessage->date_;
+ dbei.iTimestamp = pMessage->date_;
if (pMessage->is_outgoing_)
- dbei.flags |= DBEF_SENT;
+ dbei.bSent = dbei.bRead = true;
+ else if (pMessage->id_ <= pUser->lastReadId)
+ dbei.bRead = true;
+ if (pMessage->edit_date_)
+ dbei.bEdited = true;
if (!pUser->bInited)
- dbei.flags |= DBEF_READ;
+ dbei.bRead = true;
if (GetGcUserId(pUser, pMessage, szUserId))
dbei.szUserId = szUserId;
if (auto iReplyId = getReplyId(pMessage->reply_to_.get())) {
@@ -1079,15 +1106,7 @@ void CTelegramProto::ProcessMessageReactions(TD::updateMessageInteractionInfo *p
}
}
- auto &json = dbei.setJson();
- auto it = json.find("r");
- if (it != json.end())
- json.erase(it);
-
- json << reactions;
- dbei.flushJson();
-
- db_event_edit(dbei.getEvent(), &dbei, true);
+ dbei.setReactions(reactions);
}
/////////////////////////////////////////////////////////////////////////////////////////
@@ -1219,6 +1238,8 @@ void CTelegramProto::ProcessStatus(TD::updateUserStatus *pObj)
}
}
+/////////////////////////////////////////////////////////////////////////////////////////
+
void CTelegramProto::ProcessUser(TD::updateUser *pObj)
{
auto *pUser = pObj->user_.get();
@@ -1230,6 +1251,7 @@ void CTelegramProto::ProcessUser(TD::updateUser *pObj)
case TD::userTypeDeleted::ID:
return;
+ case TD::userTypeBot::ID:
case TD::userTypeRegular::ID:
auto *pu = AddFakeUser(pUser->id_, false);
if (pu->hContact != INVALID_CONTACT_ID)
@@ -1315,3 +1337,17 @@ void CTelegramProto::ProcessUser(TD::updateUser *pObj)
}
}
}
+
+void CTelegramProto::ProcessUserInfo(TD::int53 userId, TD::userFullInfo *pObj)
+{
+ if (auto *pUser = FindUser(userId)) {
+ if (auto *pBirthday = pObj->birthdate_.get())
+ Contact::SetBirthday(pUser->hContact, pBirthday->day_, pBirthday->month_, pBirthday->year_);
+
+ if (pObj->bio_) {
+ CMStringA szNotes(GetFormattedText(pObj->bio_));
+ if (!szNotes.IsEmpty())
+ setUString(pUser->hContact, "About", szNotes);
+ }
+ }
+}
diff --git a/protocols/Telegram/src/stdafx.cxx b/protocols/Telegram/src/stdafx.cxx
index 87b7477097..72e87c2758 100644
--- a/protocols/Telegram/src/stdafx.cxx
+++ b/protocols/Telegram/src/stdafx.cxx
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
diff --git a/protocols/Telegram/src/stdafx.h b/protocols/Telegram/src/stdafx.h
index ba59150fbb..2db1d8d4b4 100644
--- a/protocols/Telegram/src/stdafx.h
+++ b/protocols/Telegram/src/stdafx.h
@@ -34,6 +34,7 @@
#include <m_skin.h>
#include <m_smileyadd.h>
#include <m_timezones.h>
+#include <m_userinfo.h>
#include "../../libs/freeimage/src/FreeImage.h"
diff --git a/protocols/Telegram/src/userinfo.cpp b/protocols/Telegram/src/userinfo.cpp
new file mode 100644
index 0000000000..30b3af0050
--- /dev/null
+++ b/protocols/Telegram/src/userinfo.cpp
@@ -0,0 +1,151 @@
+/*
+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"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Dialog for my own profile in Telegram
+
+class CMyProfileDlg : public CUserInfoPageDlg
+{
+ CTelegramProto *m_proto;
+ CCtrlEdit edtNotes;
+
+public:
+ CMyProfileDlg(CTelegramProto *ppro) :
+ CUserInfoPageDlg(g_plugin, IDD_MYPROFILE),
+ m_proto(ppro),
+ edtNotes(this, IDC_BIO)
+ {}
+
+ bool OnRefresh() override
+ {
+ HWND _hwndDate = GetDlgItem(m_hwnd, IDC_BIRTHDAY);
+ SYSTEMTIME st = {};
+ st.wDay = m_proto->getWord(m_hContact, "BirthDay");
+ st.wMonth = m_proto->getWord(m_hContact, "BirthMonth");
+ st.wYear = m_proto->getWord(m_hContact, "BirthYear");
+
+ if (st.wDay && st.wMonth && st.wYear) {
+ DateTime_SetSystemtime(_hwndDate, GDT_VALID, &st);
+ DateTime_SetFormat(_hwndDate, NULL);
+ }
+ else DateTime_SetFormat(_hwndDate, TranslateT("Unspecified"));
+
+ edtNotes.SetText(m_proto->getMStringW(m_hContact, "About"));
+ return true;
+ }
+
+ bool OnInitDialog() override
+ {
+ m_proto->WindowSubscribe(m_hwnd);
+
+ m_proto->GetInfo(0, 0);
+ return true;
+ }
+
+ int Resizer(UTILRESIZECONTROL *urc) override
+ {
+ switch (urc->wId) {
+ case IDC_BIO:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+ }
+
+ void OnDestroy() override
+ {
+ m_proto->WindowUnsubscribe(m_hwnd);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Channel info dialog
+
+class CChannelInfoDlg : public CUserInfoPageDlg
+{
+ CMStringA m_szLink;
+ CTelegramProto *m_proto;
+
+ CCtrlEdit edtUserCount;
+ CCtrlHyperlink m_link;
+
+public:
+ CChannelInfoDlg(CTelegramProto *ppro) :
+ CUserInfoPageDlg(g_plugin, IDD_CHANNEL_INFO),
+ m_proto(ppro),
+ m_link(this, IDC_LINK),
+ edtUserCount(this, IDC_USER_COUNT)
+ {}
+
+ bool OnInitDialog() override
+ {
+ m_proto->WindowSubscribe(m_hwnd);
+ return true;
+ }
+
+ int Resizer(UTILRESIZECONTROL *urc) override
+ {
+ switch (urc->wId) {
+ case IDC_LINK:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+ }
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+ }
+
+ void OnDestroy() override
+ {
+ m_proto->WindowUnsubscribe(m_hwnd);
+ }
+
+ bool OnRefresh() override
+ {
+ edtUserCount.SetInt(m_proto->getDword(m_hContact, "MemberCount"));
+
+ m_szLink = m_proto->getMStringA(m_hContact, "Link");
+ m_link.SetUrl(m_szLink);
+ m_link.SetTextA(m_szLink);
+ return true;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// module entry point
+
+int CTelegramProto::OnUserInfoInit(WPARAM wParam, LPARAM hContact)
+{
+ USERINFOPAGE uip = {};
+ uip.flags = ODPF_UNICODE | ODPF_ICON;
+ uip.szProto = m_szModuleName;
+ uip.szTitle.w = m_tszUserName;
+ uip.dwInitParam = (INT_PTR)Skin_GetProtoIcon(m_szModuleName, ID_STATUS_ONLINE);
+
+ if (hContact != 0) {
+ // we display this tab only for our contacts
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (!szProto || mir_strcmp(szProto, m_szModuleName))
+ return 0;
+
+ auto *pUser = FindUser(GetId(hContact));
+ if (pUser && pUser->isChannel)
+ uip.pDialog = new CChannelInfoDlg(this);
+ else
+ return 0;
+ }
+ else uip.pDialog = new CMyProfileDlg(this);
+ return g_plugin.addUserInfo(wParam, &uip);
+}
diff --git a/protocols/Telegram/src/utils.cpp b/protocols/Telegram/src/utils.cpp
index 4d06f7b807..7589abd500 100644
--- a/protocols/Telegram/src/utils.cpp
+++ b/protocols/Telegram/src/utils.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+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
@@ -22,7 +22,7 @@ enum class BBCODE
BOLD, ITALIC, STRIKE, UNDERLINE, URL, CODE, QUOTE
};
-struct
+struct Bbcode
{
BBCODE type;
const wchar_t *begin, *end;
@@ -75,6 +75,13 @@ TD::object_ptr<TD::formattedText> formatBbcodes(const char *pszText)
case BBCODE::UNDERLINE: pNew = TD::make_object<TD::textEntityTypeUnderline>(); break;
}
+ for (auto &jt : res->entities_) {
+ if (i1 >= jt->offset_ && i1 < jt->offset_ + jt->length_)
+ jt->length_ -= it.len1;
+ if (i2 >= jt->offset_ && i2 < jt->offset_ + jt->length_)
+ jt->length_ -= it.len2;
+ }
+
res->entities_.push_back(TD::make_object<TD::textEntity>(TD::int32(i1), TD::int32(i2 - i1), std::move(pNew)));
}
}
@@ -87,7 +94,12 @@ TD::object_ptr<TD::formattedText> formatBbcodes(const char *pszText)
CMStringA CTelegramProto::GetFormattedText(TD::object_ptr<TD::formattedText> &pText)
{
CMStringW ret(Utf2T(pText->text_.c_str()));
- unsigned offset = 0;
+
+ struct HistItem {
+ HistItem(int _1, int _2, const Bbcode &_3) : start(_1), length(_2), l1(_3.len1), l2(_3.len2) {}
+ int start, length, l1, l2;
+ };
+ std::vector<HistItem> history;
for (auto &it : pText->entities_) {
int iCode;
@@ -103,10 +115,30 @@ CMStringA CTelegramProto::GetFormattedText(TD::object_ptr<TD::formattedText> &pT
continue;
}
+ int off1 = 0, off2 = 0;
+ for (auto &h : history) {
+ if (it->offset_ >= h.start)
+ off1 += h.l1;
+ if (it->offset_ + it->length_ > h.start)
+ off2 += h.l1;
+ if (it->offset_ >= h.start + h.length)
+ off1 += h.l2;
+ if (it->offset_ + it->length_ > h.start + h.length)
+ off2 += h.l2;
+ }
+
auto &bb = bbCodes[iCode];
- ret.Insert(offset + it->offset_ + it->length_, bb.end);
- ret.Insert(offset + it->offset_, bb.begin);
- offset += bb.len1 + bb.len2;
+ HistItem histItem(it->offset_, it->length_, bb);
+ ret.Insert(off2 + it->offset_ + it->length_, bb.end);
+ ret.Insert(off1 + it->offset_, bb.begin);
+ if (iCode == 4) {
+ auto *pUrl = (TD::textEntityTypeTextUrl *)it->type_.get();
+ Utf2T wszUrl(pUrl->url_.c_str());
+ ret.Insert(off1 + it->offset_ + 4, wszUrl);
+ ret.Insert(off1 + it->offset_ + 4, L"=");
+ histItem.l1 += 1 + (int)mir_wstrlen(wszUrl);
+ }
+ history.push_back(histItem);
}
return T2Utf(ret).get();
}
@@ -120,7 +152,11 @@ CMStringA msg2id(TD::int53 chatId, TD::int53 msgId)
CMStringA msg2id(const TD::message *pMsg)
{
- return CMStringA(FORMAT, "%lld_%lld", pMsg->chat_id_, pMsg->id_);
+ auto iChatId = pMsg->chat_id_;
+ if (!iChatId && pMsg->sender_id_->get_id() == TD::messageSenderChat::ID)
+ iChatId = ((TD::messageSenderChat *)pMsg->sender_id_.get())->chat_id_;
+
+ return CMStringA(FORMAT, "%lld_%lld", iChatId, pMsg->id_);
}
TD::int53 dbei2id(const DBEVENTINFO &dbei)
@@ -147,19 +183,29 @@ TD::object_ptr<TD::inputFileLocal> makeFile(const wchar_t *pwszFilename)
TG_FILE_REQUEST::Type AutoDetectType(const wchar_t *pwszFilename)
{
- if (ProtoGetAvatarFileFormat(pwszFilename) != PA_FORMAT_UNKNOWN)
- return TG_FILE_REQUEST::PICTURE;
+ if (int iFormat = ProtoGetAvatarFileFormat(pwszFilename)) {
+ if (iFormat != PA_FORMAT_GIF)
+ return TG_FILE_REQUEST::PICTURE;
+
+ if (auto *pBitmap = FreeImage_OpenMultiBitmapU(FIF_GIF, pwszFilename, FALSE, TRUE)) {
+ int iPages = FreeImage_GetPageCount(pBitmap);
+ FreeImage_CloseMultiBitmap(pBitmap);
+ if (iPages <= 1)
+ return TG_FILE_REQUEST::PICTURE;
+ }
+ }
CMStringW path(pwszFilename);
int idx = path.ReverseFind('.');
if (idx == -1 || path.Find('\\', idx) != -1)
return TG_FILE_REQUEST::FILE;
- auto wszExt = path.Right(path.GetLength() - idx);
+ auto wszExt = path.Right(path.GetLength() - idx - 1);
wszExt.MakeLower();
- if (wszExt == L"mp4" || wszExt == L"webm")
+ if (wszExt == L"mp4" || wszExt == L"webm" || wszExt == L"gif")
return TG_FILE_REQUEST::VIDEO;
- else if (wszExt == L"mp3" || wszExt == "ogg" || wszExt == "oga" || wszExt == "wav")
+
+ if (wszExt == L"mp3" || wszExt == "ogg" || wszExt == "oga" || wszExt == "wav")
return TG_FILE_REQUEST::VOICE;
return TG_FILE_REQUEST::FILE;
@@ -216,11 +262,10 @@ void CTelegramProto::MarkRead(MCONTACT hContact, const CMStringA &szMaxId, bool
if (dbei.szId > szMaxId)
break;
- bool isSent = (dbei.flags & DBEF_SENT) != 0;
- if (isSent != bSent)
+ if (dbei.bSent != bSent)
continue;
- if (!dbei.markedRead())
+ if (!dbei.bRead)
db_event_markRead(hContact, hEvent, true);
}
}
@@ -232,8 +277,12 @@ int CTelegramProto::GetDefaultMute(const TG_USER *pUser)
return m_iDefaultMutePrivate;
}
-MCONTACT CTelegramProto::GetRealContact(const TG_USER *pUser)
+MCONTACT CTelegramProto::GetRealContact(const TG_USER *pUser, int64_t threadId)
{
+ if (threadId)
+ if (auto *pu = FindChat(pUser->chatId, threadId))
+ return pu->hContact;
+
return (pUser->hContact != 0) ? pUser->hContact : m_iSavedMessages;
}
@@ -252,6 +301,21 @@ TG_USER* CTelegramProto::GetSender(const TD::MessageSender *pSender)
/////////////////////////////////////////////////////////////////////////////////////////
+void CTelegramProto::CheckCompatibility()
+{
+ int iLevel = db_get_b(0, "Compatibility", m_szModuleName);
+
+ if (iLevel < 1) {
+ for (auto &cc : AccContacts())
+ delSetting(cc, "Notes");
+ delSetting("Notes");
+ }
+
+ db_set_b(0, "Compatibility", m_szModuleName, 1);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
bool CTelegramProto::CheckSearchUser(TG_USER *pUser)
{
auto pSearchId = std::find(m_searchIds.begin(), m_searchIds.end(), pUser->chatId);
@@ -317,6 +381,12 @@ void CTelegramProto::UpdateString(MCONTACT hContact, const char *pszSetting, con
setUString(hContact, pszSetting, str.c_str());
}
+TG_SUPER_GROUP* CTelegramProto::FindSuperGroup(int64_t id)
+{
+ TG_SUPER_GROUP tmp(id, 0);
+ return m_arSuperGroups.find(&tmp);
+}
+
/////////////////////////////////////////////////////////////////////////////////////////
// Users
@@ -324,6 +394,15 @@ TG_USER* CTelegramProto::FindChat(int64_t id)
{
auto *tmp = (TG_USER *)_alloca(sizeof(TG_USER));
tmp->chatId = id;
+ tmp->forumId = -1;
+ return m_arChats.find(tmp);
+}
+
+TG_USER* CTelegramProto::FindChat(int64_t id, int64_t forumId)
+{
+ auto *tmp = (TG_USER *)_alloca(sizeof(TG_USER));
+ tmp->chatId = id;
+ tmp->forumId = forumId;
return m_arChats.find(tmp);
}
@@ -366,6 +445,8 @@ TG_USER* CTelegramProto::AddUser(int64_t id, bool bIsChat)
}
else {
pUser->hContact = hContact;
+ if (pUser->wszNick[0] == '@')
+ pUser->wszNick.Delete(0, 1);
setWString(hContact, "Nick", pUser->wszNick);
if (!pUser->isGroupChat) {
setWString(hContact, "FirstName", pUser->wszFirstName);
@@ -452,7 +533,7 @@ bool CTelegramProto::GetMessageFile(const EmbeddedFile &F, TG_FILE_REQUEST::Type
pRequest->m_fileName = Utf2T(pszFileName);
pRequest->m_fileSize = pFile->size_;
pRequest->m_bRecv = !F.pMsg->is_outgoing_;
- pRequest->m_hContact = GetRealContact(F.pUser);
+ pRequest->m_hContact = GetRealContact(F.pUser, F.pMsg->message_thread_id_);
if (mir_strlen(pszCaption))
F.szBody += pszCaption;
@@ -460,14 +541,15 @@ bool CTelegramProto::GetMessageFile(const EmbeddedFile &F, TG_FILE_REQUEST::Type
char szReplyId[100];
DB::EventInfo dbei(db_event_getById(m_szModuleName, F.pszId));
- dbei.flags = DBEF_TEMPORARY;
- dbei.timestamp = F.pMsg->date_;
+ dbei.bTemporary = true;
dbei.szId = F.pszId;
dbei.szUserId = F.pszUser;
+ if (F.pMsg->date_)
+ dbei.iTimestamp = F.pMsg->date_;
if (F.pMsg->is_outgoing_)
- dbei.flags |= DBEF_SENT;
+ dbei.bSent = dbei.bRead = true;
if (!F.pUser->bInited || F.bRead)
- dbei.flags |= DBEF_READ;
+ dbei.bRead = true;
if (auto iReplyId = getReplyId(F.pMsg->reply_to_.get())) {
_i64toa(iReplyId, szReplyId, 10);
dbei.szReplyId = szReplyId;
@@ -476,6 +558,7 @@ bool CTelegramProto::GetMessageFile(const EmbeddedFile &F, TG_FILE_REQUEST::Type
if (dbei) {
if (!Ignore_IsIgnored(pRequest->m_hContact, IGNOREEVENT_FILE)) {
DB::FILE_BLOB blob(dbei);
+ blob.setDescr(Utf2T(pszCaption));
OnReceiveOfflineFile(dbei, blob);
blob.write(dbei);
db_event_edit(dbei.getEvent(), &dbei, true);
@@ -587,8 +670,10 @@ CMStringA CTelegramProto::GetMessageText(TG_USER *pUser, const TD::message *pMsg
wszNick = Utf2T(p->sender_name_.c_str());
break;
case TD::messageOriginChannel::ID:
- if (auto *p = FindChat(((TD::messageOriginChannel *)pForward->origin_.get())->chat_id_))
- wszNick = p->getDisplayName();
+ if (auto *p = FindChat(((TD::messageOriginChannel *)pForward->origin_.get())->chat_id_)) {
+ auto str = p->getDisplayName();
+ wszNick.Format(L"[url=https://t.me/%s]%s[/url]", str.c_str(), str.c_str());
+ }
break;
default:
wszNick = TranslateT("Unknown");
diff --git a/protocols/Telegram/src/version.h b/protocols/Telegram/src/version.h
index ea7e3f3bab..b26ac7e7fb 100644
--- a/protocols/Telegram/src/version.h
+++ b/protocols/Telegram/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Telegram protocol support for Miranda NG."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Telegram"
-#define __COPYRIGHT "© 2018-24 Miranda NG team"
+#define __COPYRIGHT "© 2018-25 Miranda NG team"