summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2023-01-10 20:14:41 +0300
committerGeorge Hazan <ghazan@miranda.im>2023-01-10 20:14:41 +0300
commit605743d6e763b3aa2868133f50f99f15293a9a29 (patch)
tree415b27490160a9dd0ec3f5f4a085bf07ec5c5f34
parent83278f1c0a1b12b349617b7ebc5583be17e69daa (diff)
Telegram: support for avatars
-rw-r--r--protocols/Telegram/Telegram.vcxproj1
-rw-r--r--protocols/Telegram/Telegram.vcxproj.filters3
-rw-r--r--protocols/Telegram/src/avatars.cpp95
-rw-r--r--protocols/Telegram/src/mt_proto.cpp11
-rw-r--r--protocols/Telegram/src/mt_proto.h26
-rw-r--r--protocols/Telegram/src/server.cpp19
-rw-r--r--protocols/Telegram/src/stdafx.h1
7 files changed, 146 insertions, 10 deletions
diff --git a/protocols/Telegram/Telegram.vcxproj b/protocols/Telegram/Telegram.vcxproj
index eed1b057f3..9c1812a737 100644
--- a/protocols/Telegram/Telegram.vcxproj
+++ b/protocols/Telegram/Telegram.vcxproj
@@ -27,6 +27,7 @@
</ImportGroup>
<ItemGroup>
<ClCompile Include="src\auth.cpp" />
+ <ClCompile Include="src\avatars.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\mt_proto.cpp" />
<ClCompile Include="src\options.cpp" />
diff --git a/protocols/Telegram/Telegram.vcxproj.filters b/protocols/Telegram/Telegram.vcxproj.filters
index ffd20c351c..e6d8a2433a 100644
--- a/protocols/Telegram/Telegram.vcxproj.filters
+++ b/protocols/Telegram/Telegram.vcxproj.filters
@@ -20,6 +20,9 @@
<ClCompile Include="src\auth.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\avatars.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\stdafx.cxx">
diff --git a/protocols/Telegram/src/avatars.cpp b/protocols/Telegram/src/avatars.cpp
new file mode 100644
index 0000000000..b21ddf81d0
--- /dev/null
+++ b/protocols/Telegram/src/avatars.cpp
@@ -0,0 +1,95 @@
+/*
+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"
+
+INT_PTR CMTProto::SvcGetAvatarCaps(WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam) {
+ case AF_MAXSIZE:
+ ((POINT *)lParam)->x = 160;
+ ((POINT *)lParam)->y = 160;
+ break;
+
+ case AF_MAXFILESIZE:
+ return 32000;
+
+ case AF_PROPORTION:
+ return PIP_SQUARE;
+
+ case AF_FORMATSUPPORTED:
+ case AF_ENABLED:
+ case AF_DONTNEEDDELAYS:
+ case AF_FETCHIFPROTONOTVISIBLE:
+ case AF_FETCHIFCONTACTOFFLINE:
+ return 1;
+ }
+ return 0;
+}
+
+INT_PTR CMTProto::SvcGetAvatarInfo(WPARAM, LPARAM lParam)
+{
+ auto *pai = (PROTO_AVATAR_INFORMATION *)lParam;
+
+ ptrW wszPath(getWStringA(pai->hContact, DBKEY_AVATAR_PATH));
+ if (wszPath == nullptr)
+ return GAIR_NOAVATAR;
+
+ pai->format = getByte(pai->hContact, DBKEY_AVATAR_TYPE, PA_FORMAT_JPEG);
+ wcsncpy_s(pai->filename, wszPath, _TRUNCATE);
+
+ if (::_waccess(pai->filename, 0) == 0)
+ return GAIR_SUCCESS;
+
+ debugLogA("No avatar");
+ return GAIR_NOAVATAR;
+}
+
+INT_PTR CMTProto::SvcGetMyAvatar(WPARAM, LPARAM)
+{
+ return 1;
+}
+
+INT_PTR CMTProto::SvcSetMyAvatar(WPARAM, LPARAM)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMTProto::ProcessFile(TD::updateFile *pObj)
+{
+ if (auto *pFile = pObj->file_.get()) {
+ if (!pFile->local_->is_downloading_completed_)
+ return;
+
+ for (auto &it : m_arUsers) {
+ if (it->szAvatarHash == pFile->remote_->unique_id_.c_str()) {
+ PROTO_AVATAR_INFORMATION pai;
+ wcsncpy_s(pai.filename, Utf2T(pFile->local_->path_.c_str()), _TRUNCATE);
+ pai.hContact = it->hContact;
+ pai.format = ProtoGetAvatarFileFormat(pai.filename);
+
+ setByte(pai.hContact, DBKEY_AVATAR_TYPE, pai.format);
+ setWString(pai.hContact, DBKEY_AVATAR_PATH, pai.filename);
+
+ ProtoBroadcastAck(it->hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &pai);
+ break;
+ }
+ }
+ }
+}
diff --git a/protocols/Telegram/src/mt_proto.cpp b/protocols/Telegram/src/mt_proto.cpp
index d00f84c434..87e7f5c7d7 100644
--- a/protocols/Telegram/src/mt_proto.cpp
+++ b/protocols/Telegram/src/mt_proto.cpp
@@ -30,6 +30,10 @@ CMTProto::CMTProto(const char* protoName, const wchar_t* userName) :
m_iOwnId = _atoi64(getMStringA(DBKEY_ID));
CreateProtoService(PS_CREATEACCMGRUI, &CMTProto::SvcCreateAccMgrUI);
+ CreateProtoService(PS_GETAVATARCAPS, &CMTProto::SvcGetAvatarCaps);
+ CreateProtoService(PS_GETAVATARINFO, &CMTProto::SvcGetAvatarInfo);
+ CreateProtoService(PS_GETMYAVATAR, &CMTProto::SvcGetMyAvatar);
+ CreateProtoService(PS_SETMYAVATAR, &CMTProto::SvcSetMyAvatar);
HookProtoEvent(ME_OPT_INITIALISE, &CMTProto::OnOptionsInit);
HookProtoEvent(ME_DB_EVENT_MARKED_READ, &CMTProto::OnDbMarkedRead);
@@ -70,8 +74,11 @@ void CMTProto::OnModulesLoaded()
for (auto &cc : AccContacts()) {
bool isGroupChat = isChatRoom(cc);
szId = getMStringA(cc, isGroupChat ? "ChatRoomID" : DBKEY_ID);
- if (!szId.IsEmpty())
- m_arUsers.insert(new TG_USER(_atoi64(szId.c_str()), cc, isGroupChat));
+ if (!szId.IsEmpty()) {
+ auto *pUser = new TG_USER(_atoi64(szId.c_str()), cc, isGroupChat);
+ pUser->szAvatarHash = getMStringA(cc, DBKEY_AVATAR_HASH);
+ m_arUsers.insert(pUser);
+ }
}
}
diff --git a/protocols/Telegram/src/mt_proto.h b/protocols/Telegram/src/mt_proto.h
index 46439aee5f..99a1160b97 100644
--- a/protocols/Telegram/src/mt_proto.h
+++ b/protocols/Telegram/src/mt_proto.h
@@ -2,6 +2,10 @@
#define DBKEY_ID "id"
+#define DBKEY_AVATAR_HASH "AvatarHash"
+#define DBKEY_AVATAR_PATH "AvatarPath"
+#define DBKEY_AVATAR_TYPE "AvatarType"
+
class CMTProto;
typedef void (CMTProto:: *TG_QUERY_HANDLER)(td::ClientManager::Response &response);
typedef void (CMTProto:: *TG_QUERY_HANDLER_FULL)(td::ClientManager::Response &response, void *pUserInfo);
@@ -62,10 +66,11 @@ struct TG_USER
isGroupChat(_3)
{}
- uint64_t id;
- MCONTACT hContact;
- bool isGroupChat;
- time_t m_timer1 = 0, m_timer2 = 0;
+ uint64_t id;
+ MCONTACT hContact;
+ bool isGroupChat;
+ CMStringA szAvatarHash;
+ time_t m_timer1 = 0, m_timer2 = 0;
};
class CMTProto : public PROTO<CMTProto>
@@ -109,28 +114,29 @@ class CMTProto : public PROTO<CMTProto>
{ return CMStringW(VARSW(L"%miranda_userdata%")) + L"\\" + _A2T(m_szModuleName);
}
+ void OnEndSession(td::ClientManager::Response &response);
+ void OnSendMessage(td::ClientManager::Response &response, void *pUserInfo);
void OnUpdateAuth(td::ClientManager::Response &response);
void LogOut(void);
- void OnEndSession(td::ClientManager::Response &response);
void OnLoggedIn(void);
void ProcessResponse(td::ClientManager::Response);
+
void SendKeepAlive(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);
void ProcessAuth(TD::updateAuthorizationState *pObj);
void ProcessChat(TD::updateNewChat *pObj);
void ProcessChatPosition(TD::updateChatPosition *pObj);
+ void ProcessFile(TD::updateFile *pObj);
void ProcessGroups(TD::updateChatFilters *pObj);
void ProcessMarkRead(TD::updateChatReadInbox *pObj);
void ProcessMessage(TD::updateNewMessage *pObj);
void ProcessStatus(TD::updateUserStatus *pObj);
void ProcessUser(TD::updateUser *pObj);
- void OnSendMessage(td::ClientManager::Response &response, void *pUserInfo);
- int SendTextMessage(uint64_t chatId, const char *pszMessage);
-
void UpdateString(MCONTACT hContact, const char *pszSetting, const std::string &str);
// Users
@@ -169,6 +175,10 @@ public:
// Services //////////////////////////////////////////////////////////////////////////
INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM);
+ INT_PTR __cdecl SvcGetAvatarCaps(WPARAM, LPARAM);
+ INT_PTR __cdecl SvcGetAvatarInfo(WPARAM, LPARAM);
+ INT_PTR __cdecl SvcGetMyAvatar(WPARAM, LPARAM);
+ INT_PTR __cdecl SvcSetMyAvatar(WPARAM, LPARAM);
// Events ////////////////////////////////////////////////////////////////////////////
diff --git a/protocols/Telegram/src/server.cpp b/protocols/Telegram/src/server.cpp
index c3c54b3065..fbb6c53f3d 100644
--- a/protocols/Telegram/src/server.cpp
+++ b/protocols/Telegram/src/server.cpp
@@ -124,6 +124,10 @@ void CMTProto::ProcessResponse(td::ClientManager::Response response)
ProcessMarkRead((TD::updateChatReadInbox *)response.object.get());
break;
+ case TD::updateFile::ID:
+ ProcessFile((TD::updateFile*)response.object.get());
+ break;
+
case TD::updateNewChat::ID:
ProcessChat((TD::updateNewChat *)response.object.get());
break;
@@ -379,6 +383,21 @@ void CMTProto::ProcessUser(TD::updateUser *pObj)
else
ExtraIcon_SetIconByName(g_plugin.m_hIcon, pu->hContact, nullptr);
+ if (auto *pPhoto = pUser->profile_photo_.get()) {
+ if (auto *pSmall = pPhoto->small_.get()) {
+ auto remoteId = pSmall->remote_->unique_id_;
+ auto storedId = getMStringA(pu->hContact, DBKEY_AVATAR_HASH);
+ if (remoteId != storedId.c_str()) {
+ if (!remoteId.empty()) {
+ pu->szAvatarHash = remoteId.c_str();
+ setString(pu->hContact, DBKEY_AVATAR_HASH, remoteId.c_str());
+ SendQuery(new TD::downloadFile(pSmall->id_, 5, 0, 0, false));
+ }
+ else delSetting(pu->hContact, DBKEY_AVATAR_HASH);
+ }
+ }
+ }
+
if (pUser->status_) {
if (pUser->status_->get_id() == TD::userStatusOffline::ID) {
auto *pOffline = (TD::userStatusOffline *)pUser->status_.get();
diff --git a/protocols/Telegram/src/stdafx.h b/protocols/Telegram/src/stdafx.h
index ff63a27ac9..cae3629bf9 100644
--- a/protocols/Telegram/src/stdafx.h
+++ b/protocols/Telegram/src/stdafx.h
@@ -12,6 +12,7 @@
#include <m_protoint.h>
#include <m_protosvc.h>
+#include <m_avatars.h>
#include <m_clist.h>
#include <m_chat.h>
#include <m_contacts.h>