-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathaccount-data.h
487 lines (411 loc) · 18.5 KB
/
account-data.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
#ifndef _ACCOUNT_DATA_H
#define _ACCOUNT_DATA_H
#include "buildopt.h"
#include "identifiers.h"
#include "transceiver.h"
#include <td/telegram/td_api.h>
#include <map>
#include <mutex>
#include <set>
#include <list>
#include <purple.h>
#ifndef NoVoip
#include <VoIPController.h>
#else
namespace tgvoip {
struct VoIPController {};
}
#endif
bool isPhoneNumber(const char *s);
const char *getCanonicalPhoneNumber(const char *s);
UserId purpleBuddyNameToUserId(const char *s);
SecretChatId purpleBuddyNameToSecretChatId(const char *s);
bool isPrivateChat(const td::td_api::chat &chat);
UserId getUserIdByPrivateChat(const td::td_api::chat &chat);
bool isChatInContactList(const td::td_api::chat &chat, const td::td_api::user *privateChatUser);
BasicGroupId getBasicGroupId(const td::td_api::chat &chat);
SupergroupId getSupergroupId(const td::td_api::chat &chat);
SecretChatId getSecretChatId(const td::td_api::chat &chat);
bool isGroupMember(const td::td_api::object_ptr<td::td_api::ChatMemberStatus> &status);
bool isSameUser(const td::td_api::MessageSender &member1, const td::td_api::MessageSender &member2);
enum {
CHAT_HISTORY_REQUEST_LIMIT = 50,
CHAT_HISTORY_RETRIEVE_LIMIT = 100
};
class PendingRequest {
public:
uint64_t requestId;
PendingRequest(uint64_t requestId) : requestId(requestId) {}
virtual ~PendingRequest() {}
};
class GroupInfoRequest: public PendingRequest {
public:
BasicGroupId groupId;
GroupInfoRequest(uint64_t requestId, BasicGroupId groupId)
: PendingRequest(requestId), groupId(groupId) {}
};
class SupergroupInfoRequest: public PendingRequest {
public:
SupergroupId groupId;
SupergroupInfoRequest(uint64_t requestId, SupergroupId groupId)
: PendingRequest(requestId), groupId(groupId) {}
};
class GroupMembersRequestCont: public PendingRequest {
public:
SupergroupId groupId;
td::td_api::object_ptr<td::td_api::chatMembers> members;
GroupMembersRequestCont(uint64_t requestId, SupergroupId groupId, td::td_api::chatMembers *members)
: PendingRequest(requestId), groupId(groupId), members(std::move(members)) {}
};
class ContactRequest: public PendingRequest {
public:
std::string phoneNumber;
std::string alias;
std::string groupName;
UserId userId;
ContactRequest(uint64_t requestId, const std::string &phoneNumber, const std::string &alias,
const std::string &groupName, UserId userId)
: PendingRequest(requestId), phoneNumber(phoneNumber), alias(alias), groupName(groupName),
userId(userId) {}
};
class GroupJoinRequest: public PendingRequest {
public:
enum class Type {
InviteLink,
Username,
};
std::string joinString;
Type type;
ChatId chatId;
GroupJoinRequest(uint64_t requestId, const std::string &joinString, Type type,
ChatId chatId = ChatId::invalid)
: PendingRequest(requestId), joinString(joinString), type(type), chatId(chatId) {}
};
class SendMessageRequest: public PendingRequest {
public:
ChatId chatId;
std::string tempFile;
SendMessageRequest(uint64_t requestId, ChatId chatId, const char *tempFile)
: PendingRequest(requestId), chatId(chatId), tempFile(tempFile ? tempFile : "") {}
};
class UploadRequest: public PendingRequest {
public:
PurpleXfer *xfer;
ChatId chatId;
UploadRequest(uint64_t requestId, PurpleXfer *xfer, ChatId chatId)
: PendingRequest(requestId), xfer(xfer), chatId(chatId) {}
};
struct TgMessageInfo {
enum class Type {
Photo,
Sticker,
Other
};
MessageId id;
Type type;
std::string incomingGroupchatSender;
time_t timestamp;
bool outgoing;
bool sentLocally = false; // For outgoing messages, whether sent by this very client
MessageId repliedMessageId;
td::td_api::object_ptr<td::td_api::message> repliedMessage;
std::string forwardedFrom;
void assign(const TgMessageInfo &other)
{
id = other.id;
type = other.type;
incomingGroupchatSender = other.incomingGroupchatSender;
timestamp = other.timestamp;
outgoing = other.outgoing;
sentLocally = other.sentLocally;
repliedMessageId = other.repliedMessageId;
repliedMessage = nullptr;
forwardedFrom = other.forwardedFrom;
}
};
class PurpleTdClient;
// Used for matching completed downloads to chats they belong to, and for starting PurpleXfer for
// time-consuming downloads
class DownloadRequest: public PendingRequest {
public:
ChatId chatId;
// For inline downloads this is a copy of original TgMessageInfo from IncomingMessage.
TgMessageInfo message;
int32_t fileId;
int32_t fileSize;
int32_t downloadedSize;
std::string fileDescription;
int tempFd = -1;
std::string tempFileName;
td::td_api::object_ptr<td::td_api::file> thumbnail;
// Could not pass object_ptr through variadic funciton :(
DownloadRequest(uint64_t requestId, ChatId chatId, TgMessageInfo &message,
int32_t fileId, int32_t fileSize, const std::string &fileDescription,
td::td_api::file *thumbnail)
: PendingRequest(requestId), chatId(chatId), fileId(fileId),
fileSize(fileSize), downloadedSize(0), fileDescription(fileDescription),
thumbnail(thumbnail)
{
// If download is started while the message is in PendingMessageQueue, repliedMessage will
// be on IncomingMessage, and one here in TgMessageInfo will be NULL. In this case,
// repliedMessage will be moved onto DownloadRequest if message leaves PendingMessageQueue
// before download is complete (meaning it took more than 1 second to download).
this->message.assign(message);
if (message.repliedMessage)
this->message.repliedMessage = std::move(message.repliedMessage);
}
};
class AvatarDownloadRequest: public PendingRequest {
public:
UserId userId;
ChatId chatId;
AvatarDownloadRequest(uint64_t requestId, const td::td_api::user *user)
: PendingRequest(requestId), userId(getId(*user)), chatId(ChatId::invalid) {}
AvatarDownloadRequest(uint64_t requestId, const td::td_api::chat *chat)
: PendingRequest(requestId), userId(UserId::invalid), chatId(getId(*chat)) {}
};
class NewPrivateChatForMessage: public PendingRequest {
public:
std::string username;
std::string message;
PurpleXfer *fileUpload;
NewPrivateChatForMessage(uint64_t requestId, const char *username, const char *message)
: PendingRequest(requestId), username(username), message(message ? message : nullptr),
fileUpload(nullptr) {}
NewPrivateChatForMessage(uint64_t requestId, const char *username, PurpleXfer *upload)
: PendingRequest(requestId), username(username), fileUpload(upload) {}
};
class ChatActionRequest: public PendingRequest {
public:
enum class Type: uint8_t {
Kick,
Invite,
GenerateInviteLink
};
Type type;
ChatId chatId;
ChatActionRequest(uint64_t requestId, Type type, ChatId chatId)
: PendingRequest(requestId), type(type), chatId(chatId) {}
};
struct IncomingMessage {
td::td_api::object_ptr<td::td_api::message> message;
td::td_api::object_ptr<td::td_api::message> repliedMessage;
td::td_api::object_ptr<td::td_api::file> thumbnail;
std::string inlineDownloadedFilePath;
// This doesn't have to be a separate struct, it exists for historical reasons.
// Could be refactored.
TgMessageInfo messageInfo;
int32_t selectedPhotoSizeId;
unsigned inlineFileSizeLimit;
bool standardDownloadConfigured;
bool repliedMessageFetchDoneOrFailed;
bool inlineDownloadComplete;
bool inlineDownloadTimeout;
bool animatedStickerConverted;
bool animatedStickerConvertSuccess;
int animatedStickerImageId;
};
class PendingMessageQueue {
public:
enum class MessageAction {
Append,
Prepend
};
static constexpr MessageAction Append = MessageAction::Append;
static constexpr MessageAction Prepend = MessageAction::Prepend;
using TdMessagePtr = td::td_api::object_ptr<td::td_api::message>;
IncomingMessage &addPendingMessage(IncomingMessage &&message, MessageAction action);
void setMessageReady(ChatId chatId, MessageId messageId,
std::vector<IncomingMessage> &readyMessages);
IncomingMessage addReadyMessage(IncomingMessage &&message, MessageAction action);
IncomingMessage *findPendingMessage(ChatId chatId, MessageId messageId);
void flush(std::vector<IncomingMessage> &messages);
void setChatNotReady(ChatId chatId);
void setChatReady(ChatId chatId, std::vector<IncomingMessage> &readyMessages);
bool isChatReady(ChatId chatId);
private:
struct Message {
IncomingMessage message;
bool ready;
};
struct ChatQueue {
ChatId chatId;
bool ready = true;
std::list<Message> messages;
};
std::vector<ChatQueue> m_queues;
std::vector<ChatQueue>::iterator getChatQueue(ChatId chatId);
Message &addMessage(ChatQueue &queue, MessageAction action);
void extractReadyMessages(std::vector<ChatQueue>::iterator pQueue,
std::vector<IncomingMessage> &readyMessages);
};
struct ReadReceipt {
ChatId chatId;
MessageId messageId;
};
class TdAccountData {
public:
using TdUserPtr = td::td_api::object_ptr<td::td_api::user>;
using TdChatPtr = td::td_api::object_ptr<td::td_api::chat>;
using TdGroupPtr = td::td_api::object_ptr<td::td_api::basicGroup>;
using TdGroupInfoPtr = td::td_api::object_ptr<td::td_api::basicGroupFullInfo>;
using TdSupergroupPtr = td::td_api::object_ptr<td::td_api::supergroup>;
using TdSupergroupInfoPtr = td::td_api::object_ptr<td::td_api::supergroupFullInfo>;
using TdChatMembersPtr = td::td_api::object_ptr<td::td_api::chatMembers>;
using SecretChatPtr = td::td_api::object_ptr<td::td_api::secretChat>;
struct {
unsigned maxCaptionLength = 0;
unsigned maxMessageLength = 0;
} options;
PurpleAccount *const purpleAccount;
TdTransceiver &transceiver;
TdAccountData(PurpleAccount *purpleAccount, TdTransceiver &transceiver)
: purpleAccount(purpleAccount), transceiver(transceiver) {}
void updateUser(TdUserPtr user);
void setUserStatus(UserId UserId, td::td_api::object_ptr<td::td_api::UserStatus> status);
void updateSmallProfilePhoto(UserId userId, td::td_api::object_ptr<td::td_api::file> photo);
void updateBasicGroup(TdGroupPtr group);
void setBasicGroupInfoRequested(BasicGroupId groupId);
bool isBasicGroupInfoRequested(BasicGroupId groupId);
void updateBasicGroupInfo(BasicGroupId groupId, TdGroupInfoPtr groupInfo);
void updateSupergroup(TdSupergroupPtr group);
void setSupergroupInfoRequested(SupergroupId groupId);
bool isSupergroupInfoRequested(SupergroupId groupId);
void updateSupergroupInfo(SupergroupId groupId, TdSupergroupInfoPtr groupInfo);
void updateSupergroupMembers(SupergroupId groupId, TdChatMembersPtr members);
void addChat(TdChatPtr chat); // Updates existing chat if any
void updateChatPosition(ChatId chatId, td::td_api::object_ptr<td::td_api::chatPosition> &&position);
void updateChatTitle(ChatId chatId, const std::string &title);
void updateSmallChatPhoto(ChatId chatId, td::td_api::object_ptr<td::td_api::file> photo);
void setContacts(const td::td_api::users &users);
void getContactsWithNoChat(std::vector<UserId> &userIds);
void getChats(std::vector<const td::td_api::chat *> &chats) const;
void deleteChat(ChatId id);
void addExpectedChat(ChatId id);
bool isExpectedChat(ChatId chatId);
void removeExpectedChat(ChatId id);
const td::td_api::chat *getChat(ChatId chatId) const;
int getPurpleChatId(ChatId tdChatId);
const td::td_api::chat *getChatByPurpleId(int32_t purpleChatId) const;
const td::td_api::chat *getPrivateChatByUserId(UserId userId) const;
const td::td_api::user *getUser(UserId userId) const;
const td::td_api::user *getUserByPhone(const char *phoneNumber) const;
const td::td_api::user *getUserByPrivateChat(const td::td_api::chat &chat);
std::string getDisplayName(const td::td_api::user &user) const;
std::string getDisplayName(UserId userId) const;
void getUsersByDisplayName(const char *displayName,
std::vector<const td::td_api::user*> &users);
const td::td_api::basicGroup *getBasicGroup(BasicGroupId groupId) const;
const td::td_api::basicGroupFullInfo *getBasicGroupInfo(BasicGroupId groupId) const;
const td::td_api::supergroup *getSupergroup(SupergroupId groupId) const;
const td::td_api::supergroupFullInfo *getSupergroupInfo(SupergroupId groupId) const;
const td::td_api::chatMembers*getSupergroupMembers(SupergroupId groupId) const;
const td::td_api::chat *getBasicGroupChatByGroup(BasicGroupId groupId) const;
const td::td_api::chat *getSupergroupChatByGroup(SupergroupId groupId) const;
bool isGroupChatWithMembership(const td::td_api::chat &chat) const;
const td::td_api::chat *getChatBySecretChat(SecretChatId secretChatId);
template<typename ReqType, typename... ArgsType>
void addPendingRequest(ArgsType... args)
{
m_requests.push_back(std::make_unique<ReqType>(args...));
}
template<typename ReqType>
void addPendingRequest(uint64_t requestId, std::unique_ptr<ReqType> &&request)
{
m_requests.push_back(std::move(request));
m_requests.back()->requestId = requestId;
}
template<typename ReqType>
std::unique_ptr<ReqType> getPendingRequest(uint64_t requestId)
{
return std::unique_ptr<ReqType>(dynamic_cast<ReqType *>(getPendingRequestImpl(requestId).release()));
}
template<typename ReqType>
ReqType *findPendingRequest(uint64_t requestId)
{
return dynamic_cast<ReqType *>(findPendingRequestImpl(requestId));
}
const ContactRequest * findContactRequest(UserId userId);
void addTempFileUpload(int64_t messageId, const std::string &path);
std::string extractTempFileUpload(int64_t messageId);
DownloadRequest * findDownloadRequest(int32_t fileId);
void extractFileTransferRequests(std::vector<PurpleXfer *> &transfers);
void addFileTransfer(int32_t fileId, PurpleXfer *xfer, ChatId chatId);
bool getFileTransfer(int32_t fileId, PurpleXfer *&xfer, ChatId &chatId);
bool getFileIdForTransfer(PurpleXfer *xfer, int &fileId);
void removeFileTransfer(int32_t fileId);
void removeAllFileTransfers(std::vector<PurpleXfer *> &transfers);
void addSecretChat(td::td_api::object_ptr<td::td_api::secretChat> secretChat);
const td::td_api::secretChat *getSecretChat(SecretChatId id);
void deleteSecretChat(SecretChatId id);
auto getBasicGroupsWithMember(UserId userId) ->
std::vector<std::pair<BasicGroupId, const td::td_api::basicGroupFullInfo *>>;
bool hasActiveCall();
void setActiveCall(int32_t id);
int32_t getActiveCallId() const { return m_callId; }
tgvoip::VoIPController *getCallData();
void removeActiveCall();
PendingMessageQueue pendingMessages;
void addPendingReadReceipt(ChatId chatId, MessageId messageId);
void extractPendingReadReceipts(ChatId chatId, std::vector<ReadReceipt> &receipts);
private:
TdAccountData(const TdAccountData &other) = delete;
TdAccountData &operator=(const TdAccountData &other) = delete;
struct UserInfo {
TdUserPtr user;
std::string displayName;
};
struct ChatInfo {
int32_t purpleId;
TdChatPtr chat;
ChatInfo() : purpleId(0), chat() {}
};
struct GroupInfo {
TdGroupPtr group;
TdGroupInfoPtr fullInfo;
bool fullInfoRequested = false;
};
struct SupergroupInfo {
TdSupergroupPtr group;
TdSupergroupInfoPtr fullInfo;
TdChatMembersPtr members;
bool fullInfoRequested = false;
};
struct SendMessageInfo {
int64_t messageId;
std::string tempFile;
};
struct FileTransferInfo {
int32_t fileId;
ChatId chatId;
PurpleXfer *xfer;
};
using ChatMap = std::map<ChatId, ChatInfo>;
using UserMap = std::map<UserId, UserInfo>;
UserMap m_userInfo;
ChatMap m_chatInfo;
std::map<BasicGroupId, GroupInfo> m_groups;
std::map<SupergroupId, SupergroupInfo> m_supergroups;
std::map<SecretChatId, SecretChatPtr> m_secretChats;
int m_lastChatPurpleId = 0;
// List of contacts for which private chat is not known yet.
std::vector<UserId> m_contactUserIdsNoChat;
// Used to remember stuff during asynchronous communication when adding contact
std::vector<ContactRequest> m_addContactRequests;
// Chats we want to libpurple-join when we get an updateNewChat about them
std::vector<ChatId> m_expectedChats;
std::vector<std::unique_ptr<PendingRequest>> m_requests;
// Newly sent messages containing inline images, for which a temporary file must be removed when
// transfer is completed
std::vector<SendMessageInfo> m_sentMessages;
// Currently active file transfers for which PurpleXfer is used
std::vector<FileTransferInfo> m_fileTransfers;
// Voice call data
std::unique_ptr<tgvoip::VoIPController> m_callData;
int32_t m_callId;
std::unique_ptr<PendingRequest> getPendingRequestImpl(uint64_t requestId);
PendingRequest * findPendingRequestImpl(uint64_t requestId);
// Read receipts not sent immediately due to away status (grouped per chat)
std::vector<std::vector<ReadReceipt>> m_pendingReadReceipts;
};
#endif