mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Export full messages information.
Also add some more .match() calls to MTP codegen-ed classes.
This commit is contained in:
parent
83786ddeaf
commit
241fee80a7
16 changed files with 888 additions and 149 deletions
|
@ -767,19 +767,20 @@ for restype in typesList:
|
|||
else:
|
||||
typesText += ' = default;\n';
|
||||
|
||||
if (withData):
|
||||
typesText += getters;
|
||||
if (withType):
|
||||
typesText += '\n';
|
||||
typesText += '\ttemplate <typename Method, typename ...Methods>\n';
|
||||
typesText += '\tdecltype(auto) match(Method &&method, Methods &&...methods) const;\n';
|
||||
visitorMethods += 'template <typename Method, typename ...Methods>\n';
|
||||
visitorMethods += 'decltype(auto) MTP' + restype + '::match(Method &&method, Methods &&...methods) const {\n';
|
||||
visitorMethods += '\tswitch (_type) {\n';
|
||||
visitorMethods += visitor;
|
||||
visitorMethods += '\t}\n';
|
||||
visitorMethods += '\tUnexpected("Type in MTP' + restype + '::match.");\n';
|
||||
visitorMethods += '}\n\n';
|
||||
typesText += getters;
|
||||
typesText += '\n';
|
||||
typesText += '\ttemplate <typename Method, typename ...Methods>\n';
|
||||
typesText += '\tdecltype(auto) match(Method &&method, Methods &&...methods) const;\n';
|
||||
visitorMethods += 'template <typename Method, typename ...Methods>\n';
|
||||
visitorMethods += 'decltype(auto) MTP' + restype + '::match(Method &&method, Methods &&...methods) const {\n';
|
||||
if (withType):
|
||||
visitorMethods += '\tswitch (_type) {\n';
|
||||
visitorMethods += visitor;
|
||||
visitorMethods += '\t}\n';
|
||||
visitorMethods += '\tUnexpected("Type in MTP' + restype + '::match.");\n';
|
||||
else:
|
||||
visitorMethods += '\treturn base::match_method(c_' + v[0][0] + '(), std::forward<Method>(method), std::forward<Methods>(methods)...);\n';
|
||||
visitorMethods += '}\n\n';
|
||||
|
||||
typesText += '\n\tuint32 innerLength() const;\n'; # size method
|
||||
methods += '\nuint32 MTP' + restype + '::innerLength() const {\n';
|
||||
|
@ -843,7 +844,7 @@ for restype in typesList:
|
|||
|
||||
if (withData):
|
||||
typesText += constructsText;
|
||||
methods += constructsBodies;
|
||||
methods += constructsBodies;
|
||||
|
||||
if (friendDecl):
|
||||
typesText += '\n' + friendDecl;
|
||||
|
|
|
@ -7,14 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
#include "logs.h"
|
||||
#include "core/basic_types.h"
|
||||
#include "base/flags.h"
|
||||
#include "base/algorithm.h"
|
||||
#include "base/assertion.h"
|
||||
|
||||
#include <QtCore/QReadWriteLock>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtNetwork/QNetworkProxy>
|
||||
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
|
||||
// Define specializations for QByteArray for Qt 5.3.2, because
|
||||
// QByteArray in Qt 5.3.2 doesn't declare "pointer" subtype.
|
||||
#ifdef OS_MAC_OLD
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace App { // Hackish..
|
||||
QString formatPhone(QString phone);
|
||||
} // namespace App
|
||||
QString FillAmountAndCurrency(uint64 amount, const QString ¤cy);
|
||||
|
||||
namespace Export {
|
||||
namespace Data {
|
||||
|
@ -272,21 +273,46 @@ Document ParseDocument(
|
|||
return result;
|
||||
}
|
||||
|
||||
Utf8String FormatDateTime(
|
||||
TimeId date,
|
||||
QChar dateSeparator,
|
||||
QChar timeSeparator,
|
||||
QChar separator) {
|
||||
const auto value = QDateTime::fromTime_t(date);
|
||||
return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3"
|
||||
+ separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6"
|
||||
).arg(value.date().year()
|
||||
).arg(value.date().month(), 2, 10, QChar('0')
|
||||
).arg(value.date().day(), 2, 10, QChar('0')
|
||||
).arg(value.time().hour(), 2, 10, QChar('0')
|
||||
).arg(value.time().minute(), 2, 10, QChar('0')
|
||||
).arg(value.time().second(), 2, 10, QChar('0')
|
||||
).toUtf8();
|
||||
GeoPoint ParseGeoPoint(const MTPGeoPoint &data) {
|
||||
auto result = GeoPoint();
|
||||
data.match([&](const MTPDgeoPoint &data) {
|
||||
result.latitude = data.vlat.v;
|
||||
result.longitude = data.vlong.v;
|
||||
result.valid = true;
|
||||
}, [](const MTPDgeoPointEmpty &data) {});
|
||||
return result;
|
||||
}
|
||||
|
||||
Venue ParseVenue(const MTPDmessageMediaVenue &data) {
|
||||
auto result = Venue();
|
||||
result.point = ParseGeoPoint(data.vgeo);
|
||||
result.title = ParseString(data.vtitle);
|
||||
result.address = ParseString(data.vaddress);
|
||||
return result;
|
||||
}
|
||||
|
||||
Game ParseGame(const MTPGame &data, int32 botId) {
|
||||
return data.match([&](const MTPDgame &data) {
|
||||
auto result = Game();
|
||||
result.id = data.vid.v;
|
||||
result.title = ParseString(data.vtitle);
|
||||
result.description = ParseString(data.vdescription);
|
||||
result.shortName = ParseString(data.vshort_name);
|
||||
result.botId = botId;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
Invoice ParseInvoice(const MTPDmessageMediaInvoice &data) {
|
||||
auto result = Invoice();
|
||||
result.title = ParseString(data.vtitle);
|
||||
result.description = ParseString(data.vdescription);
|
||||
result.currency = ParseString(data.vcurrency);
|
||||
result.amount = data.vtotal_amount.v;
|
||||
if (data.has_receipt_msg_id()) {
|
||||
result.receiptMsgId = data.vreceipt_msg_id.v;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data) {
|
||||
|
@ -303,10 +329,10 @@ UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data) {
|
|||
return result;
|
||||
}
|
||||
|
||||
User ParseUser(const MTPUser &data) {
|
||||
auto result = User();
|
||||
ContactInfo ParseContactInfo(const MTPUser &data) {
|
||||
auto result = ContactInfo();
|
||||
data.match([&](const MTPDuser &data) {
|
||||
result.id = data.vid.v;
|
||||
result.userId = data.vid.v;
|
||||
if (data.has_first_name()) {
|
||||
result.firstName = ParseString(data.vfirst_name);
|
||||
}
|
||||
|
@ -316,15 +342,36 @@ User ParseUser(const MTPUser &data) {
|
|||
if (data.has_phone()) {
|
||||
result.phoneNumber = ParseString(data.vphone);
|
||||
}
|
||||
}, [&](const MTPDuserEmpty &data) {
|
||||
result.userId = data.vid.v;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
ContactInfo ParseContactInfo(const MTPDmessageMediaContact &data) {
|
||||
auto result = ContactInfo();
|
||||
result.userId = data.vuser_id.v;
|
||||
result.firstName = ParseString(data.vfirst_name);
|
||||
result.lastName = ParseString(data.vlast_name);
|
||||
result.phoneNumber = ParseString(data.vphone_number);
|
||||
return result;
|
||||
}
|
||||
|
||||
User ParseUser(const MTPUser &data) {
|
||||
auto result = User();
|
||||
result.info = ParseContactInfo(data);
|
||||
data.match([&](const MTPDuser &data) {
|
||||
if (data.has_username()) {
|
||||
result.username = ParseString(data.vusername);
|
||||
}
|
||||
if (data.has_bot_info_version()) {
|
||||
result.isBot = true;
|
||||
}
|
||||
const auto access_hash = data.has_access_hash()
|
||||
? data.vaccess_hash
|
||||
: MTP_long(0);
|
||||
result.input = MTP_inputUser(data.vid, access_hash);
|
||||
}, [&](const MTPDuserEmpty &data) {
|
||||
result.id = data.vid.v;
|
||||
result.input = MTP_inputUser(data.vid, MTP_long(0));
|
||||
});
|
||||
return result;
|
||||
|
@ -334,7 +381,7 @@ std::map<int32, User> ParseUsersList(const MTPVector<MTPUser> &data) {
|
|||
auto result = std::map<int32, User>();
|
||||
for (const auto &user : data.v) {
|
||||
auto parsed = ParseUser(user);
|
||||
result.emplace(parsed.id, std::move(parsed));
|
||||
result.emplace(parsed.info.userId, std::move(parsed));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -382,6 +429,21 @@ std::map<int32, Chat> ParseChatsList(const MTPVector<MTPChat> &data) {
|
|||
return result;
|
||||
}
|
||||
|
||||
Utf8String ContactInfo::name() const {
|
||||
return firstName.isEmpty()
|
||||
? (lastName.isEmpty()
|
||||
? Utf8String()
|
||||
: lastName)
|
||||
: (lastName.isEmpty()
|
||||
? firstName
|
||||
: firstName + ' ' + lastName);
|
||||
|
||||
}
|
||||
|
||||
Utf8String User::name() const {
|
||||
return info.name();
|
||||
}
|
||||
|
||||
const User *Peer::user() const {
|
||||
return base::get_if<User>(&data);
|
||||
}
|
||||
|
@ -391,7 +453,7 @@ const Chat *Peer::chat() const {
|
|||
|
||||
PeerId Peer::id() const {
|
||||
if (const auto user = this->user()) {
|
||||
return UserPeerId(user->id);
|
||||
return UserPeerId(user->info.userId);
|
||||
} else if (const auto chat = this->chat()) {
|
||||
return ChatPeerId(chat->id);
|
||||
}
|
||||
|
@ -400,13 +462,7 @@ PeerId Peer::id() const {
|
|||
|
||||
Utf8String Peer::name() const {
|
||||
if (const auto user = this->user()) {
|
||||
return user->firstName.isEmpty()
|
||||
? (user->lastName.isEmpty()
|
||||
? Utf8String()
|
||||
: user->lastName)
|
||||
: (user->lastName.isEmpty()
|
||||
? user->firstName
|
||||
: user->firstName + ' ' + user->lastName);
|
||||
return user->name();
|
||||
} else if (const auto chat = this->chat()) {
|
||||
return chat->title;
|
||||
}
|
||||
|
@ -432,7 +488,9 @@ std::map<PeerId, Peer> ParsePeersLists(
|
|||
auto result = std::map<PeerId, Peer>();
|
||||
for (const auto &user : users.v) {
|
||||
auto parsed = ParseUser(user);
|
||||
result.emplace(UserPeerId(parsed.id), Peer{ std::move(parsed) });
|
||||
result.emplace(
|
||||
UserPeerId(parsed.info.userId),
|
||||
Peer{ std::move(parsed) });
|
||||
}
|
||||
for (const auto &chat : chats.v) {
|
||||
auto parsed = ParseChat(chat);
|
||||
|
@ -446,7 +504,7 @@ File &Media::file() {
|
|||
return data.image.file;
|
||||
}, [](Document &data) -> File& {
|
||||
return data.file;
|
||||
}, [](base::none_type &) -> File& {
|
||||
}, [](auto&) -> File& {
|
||||
static File result;
|
||||
return result;
|
||||
});
|
||||
|
@ -457,7 +515,7 @@ const File &Media::file() const {
|
|||
return data.image.file;
|
||||
}, [](const Document &data) -> const File& {
|
||||
return data.file;
|
||||
}, [](const base::none_type &) -> const File& {
|
||||
}, [](const auto &) -> const File& {
|
||||
static const File result;
|
||||
return result;
|
||||
});
|
||||
|
@ -466,7 +524,8 @@ const File &Media::file() const {
|
|||
Media ParseMedia(
|
||||
const MTPMessageMedia &data,
|
||||
const QString &folder,
|
||||
TimeId date) {
|
||||
TimeId date,
|
||||
int32 botId) {
|
||||
Expects(folder.isEmpty() || folder.endsWith(QChar('/')));
|
||||
|
||||
auto result = Media();
|
||||
|
@ -480,8 +539,11 @@ Media ParseMedia(
|
|||
result.ttl = data.vttl_seconds.v;
|
||||
}
|
||||
}, [&](const MTPDmessageMediaGeo &data) {
|
||||
result.content = ParseGeoPoint(data.vgeo);
|
||||
}, [&](const MTPDmessageMediaContact &data) {
|
||||
result.content = ParseContactInfo(data);
|
||||
}, [&](const MTPDmessageMediaUnsupported &data) {
|
||||
result.content = UnsupportedMedia();
|
||||
}, [&](const MTPDmessageMediaDocument &data) {
|
||||
result.content = data.has_document()
|
||||
? ParseDocument(
|
||||
|
@ -489,12 +551,20 @@ Media ParseMedia(
|
|||
folder + "Files/",
|
||||
date)
|
||||
: Document();
|
||||
if (data.has_ttl_seconds()) {
|
||||
result.ttl = data.vttl_seconds.v;
|
||||
}
|
||||
}, [&](const MTPDmessageMediaWebPage &data) {
|
||||
// Ignore web pages.
|
||||
}, [&](const MTPDmessageMediaVenue &data) {
|
||||
result.content = ParseVenue(data);
|
||||
}, [&](const MTPDmessageMediaGame &data) {
|
||||
result.content = ParseGame(data.vgame, botId);
|
||||
}, [&](const MTPDmessageMediaInvoice &data) {
|
||||
result.content = ParseInvoice(data);
|
||||
}, [&](const MTPDmessageMediaGeoLive &data) {
|
||||
result.content = ParseGeoPoint(data.vgeo);
|
||||
result.ttl = data.vperiod.v;
|
||||
}, [](const MTPDmessageMediaEmpty &data) {});
|
||||
return result;
|
||||
}
|
||||
|
@ -505,30 +575,155 @@ ServiceAction ParseServiceAction(
|
|||
TimeId date) {
|
||||
auto result = ServiceAction();
|
||||
data.match([&](const MTPDmessageActionChatCreate &data) {
|
||||
auto content = ActionChatCreate();
|
||||
content.title = ParseString(data.vtitle);
|
||||
content.userIds.reserve(data.vusers.v.size());
|
||||
for (const auto &userId : data.vusers.v) {
|
||||
content.userIds.push_back(userId.v);
|
||||
}
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChatEditTitle &data) {
|
||||
auto content = ActionChatEditTitle();
|
||||
content.title = ParseString(data.vtitle);
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChatEditPhoto &data) {
|
||||
auto content = ActionChatEditPhoto();
|
||||
content.photo = ParsePhoto(
|
||||
data.vphoto,
|
||||
mediaFolder + "Photos/" + PreparePhotoFileName(date));
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChatDeletePhoto &data) {
|
||||
result.content = ActionChatDeletePhoto();
|
||||
}, [&](const MTPDmessageActionChatAddUser &data) {
|
||||
auto content = ActionChatAddUser();
|
||||
content.userIds.reserve(data.vusers.v.size());
|
||||
for (const auto &user : data.vusers.v) {
|
||||
content.userIds.push_back(user.v);
|
||||
}
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChatDeleteUser &data) {
|
||||
auto content = ActionChatDeleteUser();
|
||||
content.userId = data.vuser_id.v;
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChatJoinedByLink &data) {
|
||||
auto content = ActionChatJoinedByLink();
|
||||
content.inviterId = data.vinviter_id.v;
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChannelCreate &data) {
|
||||
auto content = ActionChannelCreate();
|
||||
content.title = ParseString(data.vtitle);
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChatMigrateTo &data) {
|
||||
auto content = ActionChatMigrateTo();
|
||||
content.channelId = data.vchannel_id.v;
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionChannelMigrateFrom &data) {
|
||||
auto content = ActionChannelMigrateFrom();
|
||||
content.title = ParseString(data.vtitle);
|
||||
content.chatId = data.vchat_id.v;
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionPinMessage &data) {
|
||||
result.content = ActionPinMessage();
|
||||
}, [&](const MTPDmessageActionHistoryClear &data) {
|
||||
result.content = ActionHistoryClear();
|
||||
}, [&](const MTPDmessageActionGameScore &data) {
|
||||
auto content = ActionGameScore();
|
||||
content.gameId = data.vgame_id.v;
|
||||
content.score = data.vscore.v;
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionPaymentSentMe &data) {
|
||||
// Should not be in user inbox.
|
||||
}, [&](const MTPDmessageActionPaymentSent &data) {
|
||||
auto content = ActionPaymentSent();
|
||||
content.currency = ParseString(data.vcurrency);
|
||||
content.amount = data.vtotal_amount.v;
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionPhoneCall &data) {
|
||||
auto content = ActionPhoneCall();
|
||||
if (data.has_duration()) {
|
||||
content.duration = data.vduration.v;
|
||||
}
|
||||
if (data.has_reason()) {
|
||||
using Reason = ActionPhoneCall::DiscardReason;
|
||||
content.discardReason = data.vreason.match(
|
||||
[](const MTPDphoneCallDiscardReasonMissed &data) {
|
||||
return Reason::Missed;
|
||||
}, [](const MTPDphoneCallDiscardReasonDisconnect &data) {
|
||||
return Reason::Disconnect;
|
||||
}, [](const MTPDphoneCallDiscardReasonHangup &data) {
|
||||
return Reason::Hangup;
|
||||
}, [](const MTPDphoneCallDiscardReasonBusy &data) {
|
||||
return Reason::Busy;
|
||||
});
|
||||
}
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionScreenshotTaken &data) {
|
||||
result.content = ActionScreenshotTaken();
|
||||
}, [&](const MTPDmessageActionCustomAction &data) {
|
||||
auto content = ActionCustomAction();
|
||||
content.message = ParseString(data.vmessage);
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionBotAllowed &data) {
|
||||
auto content = ActionBotAllowed();
|
||||
content.domain = ParseString(data.vdomain);
|
||||
result.content = content;
|
||||
}, [&](const MTPDmessageActionSecureValuesSentMe &data) {
|
||||
// Should not be in user inbox.
|
||||
}, [&](const MTPDmessageActionSecureValuesSent &data) {
|
||||
auto content = ActionSecureValuesSent();
|
||||
content.types.reserve(data.vtypes.v.size());
|
||||
using Type = ActionSecureValuesSent::Type;
|
||||
for (const auto &type : data.vtypes.v) {
|
||||
content.types.push_back(type.match(
|
||||
[](const MTPDsecureValueTypePersonalDetails &data) {
|
||||
return Type::PersonalDetails;
|
||||
}, [](const MTPDsecureValueTypePassport &data) {
|
||||
return Type::Passport;
|
||||
}, [](const MTPDsecureValueTypeDriverLicense &data) {
|
||||
return Type::DriverLicense;
|
||||
}, [](const MTPDsecureValueTypeIdentityCard &data) {
|
||||
return Type::IdentityCard;
|
||||
}, [](const MTPDsecureValueTypeInternalPassport &data) {
|
||||
return Type::InternalPassport;
|
||||
}, [](const MTPDsecureValueTypeAddress &data) {
|
||||
return Type::Address;
|
||||
}, [](const MTPDsecureValueTypeUtilityBill &data) {
|
||||
return Type::UtilityBill;
|
||||
}, [](const MTPDsecureValueTypeBankStatement &data) {
|
||||
return Type::BankStatement;
|
||||
}, [](const MTPDsecureValueTypeRentalAgreement &data) {
|
||||
return Type::RentalAgreement;
|
||||
}, [](const MTPDsecureValueTypePassportRegistration &data) {
|
||||
return Type::PassportRegistration;
|
||||
}, [](const MTPDsecureValueTypeTemporaryRegistration &data) {
|
||||
return Type::TemporaryRegistration;
|
||||
}, [](const MTPDsecureValueTypePhone &data) {
|
||||
return Type::Phone;
|
||||
}, [](const MTPDsecureValueTypeEmail &data) {
|
||||
return Type::Email;
|
||||
}));
|
||||
}
|
||||
result.content = content;
|
||||
}, [](const MTPDmessageActionEmpty &data) {});
|
||||
return result;
|
||||
}
|
||||
|
||||
File &Message::file() {
|
||||
const auto service = &action.content;
|
||||
if (const auto photo = base::get_if<ActionChatEditPhoto>(service)) {
|
||||
return photo->photo.image.file;
|
||||
}
|
||||
return media.file();
|
||||
}
|
||||
|
||||
const File &Message::file() const {
|
||||
const auto service = &action.content;
|
||||
if (const auto photo = base::get_if<ActionChatEditPhoto>(service)) {
|
||||
return photo->photo.image.file;
|
||||
}
|
||||
return media.file();
|
||||
}
|
||||
|
||||
Message ParseMessage(const MTPMessage &data, const QString &mediaFolder) {
|
||||
auto result = Message();
|
||||
data.match([&](const MTPDmessage &data) {
|
||||
|
@ -540,6 +735,22 @@ Message ParseMessage(const MTPMessage &data, const QString &mediaFolder) {
|
|||
if (data.has_from_id()) {
|
||||
result.fromId = data.vfrom_id.v;
|
||||
}
|
||||
if (data.has_fwd_from()) {
|
||||
result.forwardedFromId = data.vfwd_from.match(
|
||||
[](const MTPDmessageFwdHeader &data) {
|
||||
if (data.has_channel_id()) {
|
||||
return ChatPeerId(data.vchannel_id.v);
|
||||
} else if (data.has_saved_from_peer()) {
|
||||
return ParsePeerId(data.vsaved_from_peer);
|
||||
} else if (data.has_from_id()) {
|
||||
return UserPeerId(data.vfrom_id.v);
|
||||
}
|
||||
return PeerId(0);
|
||||
});
|
||||
}
|
||||
if (data.has_post_author()) {
|
||||
result.signature = ParseString(data.vpost_author);
|
||||
}
|
||||
if (data.has_reply_to_msg_id()) {
|
||||
result.replyToMsgId = data.vreply_to_msg_id.v;
|
||||
}
|
||||
|
@ -547,7 +758,15 @@ Message ParseMessage(const MTPMessage &data, const QString &mediaFolder) {
|
|||
result.viaBotId = data.vvia_bot_id.v;
|
||||
}
|
||||
if (data.has_media()) {
|
||||
result.media = ParseMedia(data.vmedia, mediaFolder, date);
|
||||
result.media = ParseMedia(
|
||||
data.vmedia,
|
||||
mediaFolder,
|
||||
date,
|
||||
(result.viaBotId
|
||||
? result.viaBotId
|
||||
: result.forwardedFromId
|
||||
? result.forwardedFromId
|
||||
: result.fromId));
|
||||
}
|
||||
result.text = ParseString(data.vmessage);
|
||||
}, [&](const MTPDmessageService &data) {
|
||||
|
@ -599,9 +818,9 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) {
|
|||
for (const auto &contact : contacts.vcontacts.v) {
|
||||
const auto userId = contact.c_contact().vuser_id.v;
|
||||
if (const auto i = map.find(userId); i != end(map)) {
|
||||
result.list.push_back(i->second);
|
||||
result.list.push_back(i->second.info);
|
||||
} else {
|
||||
result.list.push_back(User());
|
||||
result.list.push_back(ContactInfo());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -610,10 +829,10 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) {
|
|||
std::vector<int> SortedContactsIndices(const ContactsList &data) {
|
||||
const auto names = ranges::view::all(
|
||||
data.list
|
||||
) | ranges::view::transform([](const Data::User &user) {
|
||||
return (QString::fromUtf8(user.firstName)
|
||||
) | ranges::view::transform([](const Data::ContactInfo &info) {
|
||||
return (QString::fromUtf8(info.firstName)
|
||||
+ ' '
|
||||
+ QString::fromUtf8(user.lastName)).toLower();
|
||||
+ QString::fromUtf8(info.lastName)).toLower();
|
||||
}) | ranges::to_vector;
|
||||
|
||||
auto indices = ranges::view::ints(0, int(data.list.size()))
|
||||
|
@ -716,5 +935,28 @@ Utf8String FormatPhoneNumber(const Utf8String &phoneNumber) {
|
|||
: App::formatPhone(QString::fromUtf8(phoneNumber)).toUtf8();
|
||||
}
|
||||
|
||||
Utf8String FormatDateTime(
|
||||
TimeId date,
|
||||
QChar dateSeparator,
|
||||
QChar timeSeparator,
|
||||
QChar separator) {
|
||||
const auto value = QDateTime::fromTime_t(date);
|
||||
return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3"
|
||||
+ separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6"
|
||||
).arg(value.date().year()
|
||||
).arg(value.date().month(), 2, 10, QChar('0')
|
||||
).arg(value.date().day(), 2, 10, QChar('0')
|
||||
).arg(value.time().hour(), 2, 10, QChar('0')
|
||||
).arg(value.time().minute(), 2, 10, QChar('0')
|
||||
).arg(value.time().second(), 2, 10, QChar('0')
|
||||
).toUtf8();
|
||||
}
|
||||
|
||||
Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy) {
|
||||
return FillAmountAndCurrency(
|
||||
amount,
|
||||
QString::fromUtf8(currency)).toUtf8();
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
} // namespace Export
|
||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QByteArray>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Export {
|
||||
|
@ -95,20 +96,61 @@ struct Document {
|
|||
bool isAudioFile = false;
|
||||
};
|
||||
|
||||
struct GeoPoint {
|
||||
float64 latitude = 0.;
|
||||
float64 longitude = 0.;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
struct Venue {
|
||||
GeoPoint point;
|
||||
Utf8String title;
|
||||
Utf8String address;
|
||||
};
|
||||
|
||||
struct Game {
|
||||
uint64 id = 0;
|
||||
Utf8String shortName;
|
||||
Utf8String title;
|
||||
Utf8String description;
|
||||
|
||||
int32 botId = 0;
|
||||
};
|
||||
|
||||
struct Invoice {
|
||||
Utf8String title;
|
||||
Utf8String description;
|
||||
Utf8String currency;
|
||||
uint64 amount = 0;
|
||||
int32 receiptMsgId = 0;
|
||||
};
|
||||
|
||||
struct UserpicsSlice {
|
||||
std::vector<Photo> list;
|
||||
};
|
||||
|
||||
UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data);
|
||||
|
||||
struct User {
|
||||
int32 id = 0;
|
||||
struct ContactInfo {
|
||||
int32 userId = 0;
|
||||
Utf8String firstName;
|
||||
Utf8String lastName;
|
||||
Utf8String phoneNumber;
|
||||
Utf8String username;
|
||||
|
||||
MTPInputUser input;
|
||||
Utf8String name() const;
|
||||
};
|
||||
|
||||
ContactInfo ParseContactInfo(const MTPUser &data);
|
||||
ContactInfo ParseContactInfo(const MTPDmessageMediaContact &data);
|
||||
|
||||
struct User {
|
||||
ContactInfo info;
|
||||
Utf8String username;
|
||||
bool isBot = false;
|
||||
|
||||
MTPInputUser input = MTP_inputUserEmpty();
|
||||
|
||||
Utf8String name() const;
|
||||
};
|
||||
|
||||
User ParseUser(const MTPUser &data);
|
||||
|
@ -120,7 +162,7 @@ struct Chat {
|
|||
Utf8String username;
|
||||
bool broadcast = false;
|
||||
|
||||
MTPInputPeer input;
|
||||
MTPInputPeer input = MTP_inputPeerEmpty();
|
||||
};
|
||||
|
||||
Chat ParseChat(const MTPChat &data);
|
||||
|
@ -150,7 +192,7 @@ struct PersonalInfo {
|
|||
PersonalInfo ParsePersonalInfo(const MTPUserFull &data);
|
||||
|
||||
struct ContactsList {
|
||||
std::vector<User> list;
|
||||
std::vector<ContactInfo> list;
|
||||
};
|
||||
|
||||
ContactsList ParseContactsList(const MTPcontacts_Contacts &data);
|
||||
|
@ -175,8 +217,19 @@ struct SessionsList {
|
|||
|
||||
SessionsList ParseSessionsList(const MTPaccount_Authorizations &data);
|
||||
|
||||
struct UnsupportedMedia {
|
||||
};
|
||||
|
||||
struct Media {
|
||||
base::optional_variant<Photo, Document> content;
|
||||
base::optional_variant<
|
||||
Photo,
|
||||
Document,
|
||||
ContactInfo,
|
||||
GeoPoint,
|
||||
Venue,
|
||||
Game,
|
||||
Invoice,
|
||||
UnsupportedMedia> content;
|
||||
TimeId ttl = 0;
|
||||
|
||||
File &file();
|
||||
|
@ -188,8 +241,126 @@ Media ParseMedia(
|
|||
const QString &folder,
|
||||
TimeId date);
|
||||
|
||||
struct ActionChatCreate {
|
||||
Utf8String title;
|
||||
std::vector<int32> userIds;
|
||||
};
|
||||
|
||||
struct ActionChatEditTitle {
|
||||
Utf8String title;
|
||||
};
|
||||
|
||||
struct ActionChatEditPhoto {
|
||||
Photo photo;
|
||||
};
|
||||
|
||||
struct ActionChatDeletePhoto {
|
||||
};
|
||||
|
||||
struct ActionChatAddUser {
|
||||
std::vector<int32> userIds;
|
||||
};
|
||||
|
||||
struct ActionChatDeleteUser {
|
||||
int32 userId = 0;
|
||||
};
|
||||
|
||||
struct ActionChatJoinedByLink {
|
||||
int32 inviterId = 0;
|
||||
};
|
||||
|
||||
struct ActionChannelCreate {
|
||||
Utf8String title;
|
||||
};
|
||||
|
||||
struct ActionChatMigrateTo {
|
||||
int32 channelId = 0;
|
||||
};
|
||||
|
||||
struct ActionChannelMigrateFrom {
|
||||
Utf8String title;
|
||||
int32 chatId = 0;
|
||||
};
|
||||
|
||||
struct ActionPinMessage {
|
||||
};
|
||||
|
||||
struct ActionHistoryClear {
|
||||
};
|
||||
|
||||
struct ActionGameScore {
|
||||
uint64 gameId = 0;
|
||||
int score = 0;
|
||||
};
|
||||
|
||||
struct ActionPaymentSent {
|
||||
Utf8String currency;
|
||||
uint64 amount = 0;
|
||||
};
|
||||
|
||||
struct ActionPhoneCall {
|
||||
enum class DiscardReason {
|
||||
Unknown,
|
||||
Missed,
|
||||
Disconnect,
|
||||
Hangup,
|
||||
Busy,
|
||||
};
|
||||
DiscardReason discardReason = DiscardReason::Unknown;
|
||||
int duration = 0;
|
||||
};
|
||||
|
||||
struct ActionScreenshotTaken {
|
||||
};
|
||||
|
||||
struct ActionCustomAction {
|
||||
Utf8String message;
|
||||
};
|
||||
|
||||
struct ActionBotAllowed {
|
||||
Utf8String domain;
|
||||
};
|
||||
|
||||
struct ActionSecureValuesSent {
|
||||
enum class Type {
|
||||
PersonalDetails,
|
||||
Passport,
|
||||
DriverLicense,
|
||||
IdentityCard,
|
||||
InternalPassport,
|
||||
Address,
|
||||
UtilityBill,
|
||||
BankStatement,
|
||||
RentalAgreement,
|
||||
PassportRegistration,
|
||||
TemporaryRegistration,
|
||||
Phone,
|
||||
Email,
|
||||
};
|
||||
std::vector<Type> types;
|
||||
};
|
||||
|
||||
struct ServiceAction {
|
||||
base::optional_variant<> data;
|
||||
base::optional_variant<
|
||||
ActionChatCreate,
|
||||
ActionChatEditTitle,
|
||||
ActionChatEditPhoto,
|
||||
ActionChatDeletePhoto,
|
||||
ActionChatAddUser,
|
||||
ActionChatDeleteUser,
|
||||
ActionChatJoinedByLink,
|
||||
ActionChannelCreate,
|
||||
ActionChatMigrateTo,
|
||||
ActionChannelMigrateFrom,
|
||||
ActionPinMessage,
|
||||
ActionHistoryClear,
|
||||
ActionGameScore,
|
||||
ActionPaymentSent,
|
||||
ActionPhoneCall,
|
||||
ActionScreenshotTaken,
|
||||
ActionCustomAction,
|
||||
ActionBotAllowed,
|
||||
ActionSecureValuesSent> content;
|
||||
};
|
||||
|
||||
ServiceAction ParseServiceAction(
|
||||
|
@ -202,12 +373,16 @@ struct Message {
|
|||
TimeId date = 0;
|
||||
TimeId edited = 0;
|
||||
int32 fromId = 0;
|
||||
PeerId forwardedFromId = 0;
|
||||
Utf8String signature;
|
||||
int32 viaBotId = 0;
|
||||
int32 replyToMsgId = 0;
|
||||
Utf8String text;
|
||||
Media media;
|
||||
ServiceAction action;
|
||||
|
||||
File &file();
|
||||
const File &file() const;
|
||||
};
|
||||
|
||||
Message ParseMessage(const MTPMessage &data, const QString &mediaFolder);
|
||||
|
@ -226,7 +401,7 @@ struct DialogInfo {
|
|||
Type type = Type::Unknown;
|
||||
Utf8String name;
|
||||
|
||||
MTPInputPeer input;
|
||||
MTPInputPeer input = MTP_inputPeerEmpty();
|
||||
int32 topMessageId = 0;
|
||||
TimeId topMessageDate = 0;
|
||||
|
||||
|
@ -259,5 +434,7 @@ Utf8String FormatDateTime(
|
|||
QChar timeSeparator = QChar(':'),
|
||||
QChar separator = QChar(' '));
|
||||
|
||||
Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy);
|
||||
|
||||
} // namespace Data
|
||||
} // namespace Export
|
||||
|
|
|
@ -470,7 +470,7 @@ void ApiWrap::loadNextMessageFile() {
|
|||
if (index >= list.size()) {
|
||||
break;
|
||||
}
|
||||
const auto &file = list[index].media.file();
|
||||
const auto &file = list[index].file();
|
||||
if (WillLoadFile(file)) {
|
||||
loadFile(
|
||||
file,
|
||||
|
@ -479,16 +479,16 @@ void ApiWrap::loadNextMessageFile() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!list.empty()) {
|
||||
process->offsetId = list.back().id + 1;
|
||||
}
|
||||
_dialogsProcess->sliceOne(*base::take(process->slice));
|
||||
|
||||
if (process->lastSlice) {
|
||||
finishMessages();
|
||||
return;
|
||||
} else {
|
||||
requestMessagesSlice();
|
||||
}
|
||||
|
||||
Assert(!list.empty());
|
||||
process->offsetId = list.back().id + 1;
|
||||
requestMessagesSlice();
|
||||
}
|
||||
|
||||
void ApiWrap::loadMessageFileDone(const QString &relativePath) {
|
||||
|
@ -501,7 +501,7 @@ void ApiWrap::loadMessageFileDone(const QString &relativePath) {
|
|||
|
||||
const auto process = _dialogsProcess->single.get();
|
||||
const auto index = process->fileIndex;
|
||||
process->slice->list[index].media.file().relativePath = relativePath;
|
||||
process->slice->list[index].file().relativePath = relativePath;
|
||||
loadNextMessageFile();
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ void Controller::fillExportSteps() {
|
|||
|
||||
void Controller::exportNext() {
|
||||
if (!++_stepIndex) {
|
||||
_writer->start(_settings.path);
|
||||
_writer->start(_settings);
|
||||
}
|
||||
if (_stepIndex >= _steps.size()) {
|
||||
_writer->finish();
|
||||
|
|
|
@ -11,8 +11,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtCore/QDir>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
||||
#include <crl/crl.h>
|
||||
#include <rpl/rpl.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
|
|
@ -50,6 +50,7 @@ struct Settings {
|
|||
friend inline constexpr auto is_flag_type(Type) { return true; };
|
||||
|
||||
QString path;
|
||||
QString internalLinksDomain;
|
||||
Output::Format format = Output::Format();
|
||||
|
||||
Types types = DefaultTypes();
|
||||
|
@ -62,7 +63,8 @@ struct Settings {
|
|||
| Type::Userpics
|
||||
| Type::Contacts
|
||||
| Type::Sessions
|
||||
| Type::PersonalChats;
|
||||
| Type::PersonalChats
|
||||
| Type::PrivateGroups;
|
||||
}
|
||||
|
||||
static inline Types DefaultFullChats() {
|
||||
|
|
|
@ -21,6 +21,8 @@ struct DialogInfo;
|
|||
struct MessagesSlice;
|
||||
} // namespace Data
|
||||
|
||||
struct Settings;
|
||||
|
||||
namespace Output {
|
||||
|
||||
enum class Format {
|
||||
|
@ -31,7 +33,7 @@ enum class Format {
|
|||
|
||||
class AbstractWriter {
|
||||
public:
|
||||
virtual bool start(const QString &folder) = 0;
|
||||
virtual bool start(const Settings &settings) = 0;
|
||||
|
||||
virtual bool writePersonal(const Data::PersonalInfo &data) = 0;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QDir>
|
||||
|
||||
#include <gsl/gsl_util>
|
||||
|
||||
namespace Export {
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "export/output/export_output_text.h"
|
||||
|
||||
#include "export/data/export_data_types.h"
|
||||
#include "core/utils.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
|
||||
|
@ -83,12 +84,312 @@ Data::Utf8String FormatUsername(const Data::Utf8String &username) {
|
|||
return username.isEmpty() ? username : ('@' + username);
|
||||
}
|
||||
|
||||
QByteArray FormatFilePath(const Data::File &file) {
|
||||
return file.relativePath.toUtf8();
|
||||
}
|
||||
|
||||
QByteArray SerializeMessage(
|
||||
const Data::Message &message,
|
||||
const std::map<Data::PeerId, Data::Peer> &peers,
|
||||
const QString &internalLinksDomain) {
|
||||
using namespace Data;
|
||||
|
||||
if (message.media.content.is<UnsupportedMedia>()) {
|
||||
return "Error! This message is not supported "
|
||||
"by this version of Telegram Desktop. "
|
||||
"Please update the application.";
|
||||
}
|
||||
|
||||
const auto peer = [&](PeerId peerId) -> const Peer& {
|
||||
if (const auto i = peers.find(peerId); i != end(peers)) {
|
||||
return i->second;
|
||||
}
|
||||
static auto empty = Peer{ User() };
|
||||
return empty;
|
||||
};
|
||||
const auto user = [&](int32 userId) -> const User& {
|
||||
if (const auto result = peer(UserPeerId(userId)).user()) {
|
||||
return *result;
|
||||
}
|
||||
static auto empty = User();
|
||||
return empty;
|
||||
};
|
||||
const auto chat = [&](int32 chatId) -> const Chat& {
|
||||
if (const auto result = peer(ChatPeerId(chatId)).chat()) {
|
||||
return *result;
|
||||
}
|
||||
static auto empty = Chat();
|
||||
return empty;
|
||||
};
|
||||
|
||||
auto values = std::vector<std::pair<QByteArray, QByteArray>>{
|
||||
{ "ID", NumberToString(message.id) },
|
||||
{ "Date", FormatDateTime(message.date) },
|
||||
{ "Edited", FormatDateTime(message.edited) },
|
||||
};
|
||||
const auto push = [&](const QByteArray &key, const QByteArray &value) {
|
||||
if (!value.isEmpty()) {
|
||||
values.emplace_back(key, value);
|
||||
}
|
||||
};
|
||||
const auto wrapPeerName = [&](PeerId peerId) {
|
||||
const auto result = peer(peerId).name();
|
||||
return result.isEmpty() ? QByteArray("(unknown peer)") : result;
|
||||
};
|
||||
const auto wrapUserName = [&](int32 userId) {
|
||||
const auto result = user(userId).name();
|
||||
return result.isEmpty() ? QByteArray("(unknown user)") : result;
|
||||
};
|
||||
const auto pushFrom = [&](const QByteArray &label = "From") {
|
||||
if (message.fromId) {
|
||||
push(label, wrapUserName(message.fromId));
|
||||
}
|
||||
};
|
||||
const auto pushReplyToMsgId = [&](
|
||||
const QByteArray &label = "Reply to message") {
|
||||
if (message.replyToMsgId) {
|
||||
push(label, "ID-" + NumberToString(message.replyToMsgId));
|
||||
}
|
||||
};
|
||||
const auto pushUserNames = [&](
|
||||
const std::vector<int32> &data,
|
||||
const QByteArray &labelOne = "Member",
|
||||
const QByteArray &labelMany = "Members") {
|
||||
auto list = std::vector<QByteArray>();
|
||||
for (const auto userId : data) {
|
||||
list.push_back(wrapUserName(userId));
|
||||
}
|
||||
if (list.size() == 1) {
|
||||
push(labelOne, list[0]);
|
||||
} else if (!list.empty()) {
|
||||
push(labelMany, JoinList(", ", list));
|
||||
}
|
||||
};
|
||||
const auto pushActor = [&] {
|
||||
pushFrom("Actor");
|
||||
};
|
||||
const auto pushAction = [&](const QByteArray &action) {
|
||||
push("Action", action);
|
||||
};
|
||||
message.action.content.match([&](const ActionChatCreate &data) {
|
||||
pushActor();
|
||||
pushAction("Create group");
|
||||
push("Title", data.title);
|
||||
pushUserNames(data.userIds);
|
||||
}, [&](const ActionChatEditTitle &data) {
|
||||
pushActor();
|
||||
pushAction("Edit group title");
|
||||
push("New title", data.title);
|
||||
}, [&](const ActionChatEditPhoto &data) {
|
||||
pushActor();
|
||||
pushAction("Edit group photo");
|
||||
push("Photo", FormatFilePath(data.photo.image.file));
|
||||
}, [&](const ActionChatDeletePhoto &data) {
|
||||
pushActor();
|
||||
pushAction("Delete group photo");
|
||||
}, [&](const ActionChatAddUser &data) {
|
||||
pushActor();
|
||||
pushAction("Invite members");
|
||||
pushUserNames(data.userIds);
|
||||
}, [&](const ActionChatDeleteUser &data) {
|
||||
pushActor();
|
||||
pushAction("Remove members");
|
||||
push("Member", wrapUserName(data.userId));
|
||||
}, [&](const ActionChatJoinedByLink &data) {
|
||||
pushActor();
|
||||
pushAction("Join group by link");
|
||||
push("Inviter", wrapUserName(data.inviterId));
|
||||
}, [&](const ActionChannelCreate &data) {
|
||||
pushActor();
|
||||
pushAction("Create channel");
|
||||
push("Title", data.title);
|
||||
}, [&](const ActionChatMigrateTo &data) {
|
||||
pushActor();
|
||||
pushAction("Migrate this group to supergroup");
|
||||
}, [&](const ActionChannelMigrateFrom &data) {
|
||||
pushActor();
|
||||
pushAction("Migrate this supergroup from group");
|
||||
push("Title", data.title);
|
||||
}, [&](const ActionPinMessage &data) {
|
||||
pushActor();
|
||||
pushAction("Pin message");
|
||||
pushReplyToMsgId("Message");
|
||||
}, [&](const ActionHistoryClear &data) {
|
||||
pushActor();
|
||||
pushAction("Clear history");
|
||||
}, [&](const ActionGameScore &data) {
|
||||
pushActor();
|
||||
pushAction("Score in a game");
|
||||
pushReplyToMsgId("Game message");
|
||||
push("Score", NumberToString(data.score));
|
||||
}, [&](const ActionPaymentSent &data) {
|
||||
pushAction("Send payment");
|
||||
push(
|
||||
"Amount",
|
||||
Data::FormatMoneyAmount(data.amount, data.currency));
|
||||
pushReplyToMsgId("Invoice message");
|
||||
}, [&](const ActionPhoneCall &data) {
|
||||
pushActor();
|
||||
pushAction("Phone call");
|
||||
if (data.duration) {
|
||||
push("Duration", NumberToString(data.duration) + " sec.");
|
||||
}
|
||||
using Reason = ActionPhoneCall::DiscardReason;
|
||||
push("Discard reason", [&] {
|
||||
switch (data.discardReason) {
|
||||
case Reason::Busy: return "Busy";
|
||||
case Reason::Disconnect: return "Disconnect";
|
||||
case Reason::Hangup: return "Hangup";
|
||||
case Reason::Missed: return "Missed";
|
||||
}
|
||||
return "";
|
||||
}());
|
||||
}, [&](const ActionScreenshotTaken &data) {
|
||||
pushActor();
|
||||
pushAction("Take screenshot");
|
||||
}, [&](const ActionCustomAction &data) {
|
||||
pushActor();
|
||||
push("Information", data.message);
|
||||
}, [&](const ActionBotAllowed &data) {
|
||||
pushAction("Allow sending messages");
|
||||
push("Reason", "Login on \"" + data.domain + "\"");
|
||||
}, [&](const ActionSecureValuesSent &data) {
|
||||
pushAction("Send Telegram Passport values");
|
||||
auto list = std::vector<QByteArray>();
|
||||
for (const auto type : data.types) {
|
||||
list.push_back([&] {
|
||||
using Type = ActionSecureValuesSent::Type;
|
||||
switch (type) {
|
||||
case Type::PersonalDetails: return "Personal details";
|
||||
case Type::Passport: return "Passport";
|
||||
case Type::DriverLicense: return "Driver license";
|
||||
case Type::IdentityCard: return "Identity card";
|
||||
case Type::InternalPassport: return "Internal passport";
|
||||
case Type::Address: return "Address information";
|
||||
case Type::UtilityBill: return "Utility bill";
|
||||
case Type::BankStatement: return "Bank statement";
|
||||
case Type::RentalAgreement: return "Rental agreement";
|
||||
case Type::PassportRegistration:
|
||||
return "Passport registration";
|
||||
case Type::TemporaryRegistration:
|
||||
return "Temporary registration";
|
||||
case Type::Phone: return "Phone number";
|
||||
case Type::Email: return "Email";
|
||||
}
|
||||
return "";
|
||||
}());
|
||||
}
|
||||
if (list.size() == 1) {
|
||||
push("Value", list[0]);
|
||||
} else if (!list.empty()) {
|
||||
push("Values", JoinList(", ", list));
|
||||
}
|
||||
}, [](const base::none_type &) {});
|
||||
|
||||
if (!message.action.content) {
|
||||
pushFrom();
|
||||
push("Author", message.signature);
|
||||
if (message.forwardedFromId) {
|
||||
push("Forwarded from", wrapPeerName(message.forwardedFromId));
|
||||
}
|
||||
pushReplyToMsgId();
|
||||
if (message.viaBotId) {
|
||||
push("Via", user(message.viaBotId).username);
|
||||
}
|
||||
}
|
||||
|
||||
message.media.content.match([&](const Photo &photo) {
|
||||
}, [&](const Document &data) {
|
||||
const auto pushPath = [&](const QByteArray &label) {
|
||||
push(label, FormatFilePath(data.file));
|
||||
};
|
||||
if (!data.stickerEmoji.isEmpty()) {
|
||||
pushPath("Sticker");
|
||||
push("Emoji", data.stickerEmoji);
|
||||
} else if (data.isVideoMessage) {
|
||||
pushPath("Video message");
|
||||
} else if (data.isVoiceMessage) {
|
||||
pushPath("Voice message");
|
||||
} else if (data.isAnimated) {
|
||||
pushPath("Animation");
|
||||
} else if (data.isVideoFile) {
|
||||
pushPath("Video file");
|
||||
} else if (data.isAudioFile) {
|
||||
pushPath("Audio file");
|
||||
push("Performer", data.songPerformer);
|
||||
push("Title", data.songTitle);
|
||||
} else {
|
||||
pushPath("File");
|
||||
}
|
||||
if (data.stickerEmoji.isEmpty()) {
|
||||
push("Mime type", data.mime);
|
||||
}
|
||||
if (data.duration) {
|
||||
push("Duration", NumberToString(data.duration) + " sec.");
|
||||
}
|
||||
if (data.width && data.height) {
|
||||
push("Width", NumberToString(data.width));
|
||||
push("Height", NumberToString(data.height));
|
||||
}
|
||||
}, [&](const ContactInfo &data) {
|
||||
push("Contact information", SerializeKeyValue({
|
||||
{ "First name", data.firstName },
|
||||
{ "Last name", data.lastName },
|
||||
{ "Phone number", FormatPhoneNumber(data.phoneNumber) },
|
||||
}));
|
||||
}, [&](const GeoPoint &data) {
|
||||
push("Location", data.valid ? SerializeKeyValue({
|
||||
{ "Latitude", NumberToString(data.latitude) },
|
||||
{ "Longitude", NumberToString(data.longitude) },
|
||||
}) : QByteArray("(empty value)"));
|
||||
}, [&](const Venue &data) {
|
||||
push("Place name", data.title);
|
||||
push("Address", data.address);
|
||||
if (data.point.valid) {
|
||||
push("Location", SerializeKeyValue({
|
||||
{ "Latitude", NumberToString(data.point.latitude) },
|
||||
{ "Longitude", NumberToString(data.point.longitude) },
|
||||
}));
|
||||
}
|
||||
}, [&](const Game &data) {
|
||||
push("Game", data.title);
|
||||
push("Description", data.description);
|
||||
if (data.botId != 0 && !data.shortName.isEmpty()) {
|
||||
const auto bot = user(data.botId);
|
||||
if (bot.isBot && !bot.username.isEmpty()) {
|
||||
push("Link", internalLinksDomain.toUtf8()
|
||||
+ bot.username
|
||||
+ "?game="
|
||||
+ data.shortName);
|
||||
}
|
||||
}
|
||||
}, [&](const Invoice &data) {
|
||||
push("Invoice", SerializeKeyValue({
|
||||
{ "Title", data.title },
|
||||
{ "Description", data.description },
|
||||
{
|
||||
"Amount",
|
||||
Data::FormatMoneyAmount(data.amount, data.currency)
|
||||
},
|
||||
{ "Receipt message", (data.receiptMsgId
|
||||
? "ID-" + NumberToString(data.receiptMsgId)
|
||||
: QByteArray()) }
|
||||
}));
|
||||
}, [](const UnsupportedMedia &data) {
|
||||
Unexpected("Unsupported message.");
|
||||
}, [](const base::none_type &) {});
|
||||
|
||||
push("Text", message.text);
|
||||
|
||||
return SerializeKeyValue(std::move(values));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool TextWriter::start(const QString &folder) {
|
||||
Expects(folder.endsWith('/'));
|
||||
bool TextWriter::start(const Settings &settings) {
|
||||
Expects(settings.path.endsWith('/'));
|
||||
|
||||
_folder = folder;
|
||||
_settings = base::duplicate(settings);
|
||||
_result = fileWithRelativePath(mainFileRelativePath());
|
||||
return true;
|
||||
}
|
||||
|
@ -96,13 +397,14 @@ bool TextWriter::start(const QString &folder) {
|
|||
bool TextWriter::writePersonal(const Data::PersonalInfo &data) {
|
||||
Expects(_result != nullptr);
|
||||
|
||||
const auto &info = data.user.info;
|
||||
const auto serialized = "Personal information"
|
||||
+ kLineBreak
|
||||
+ kLineBreak
|
||||
+ SerializeKeyValue({
|
||||
{ "First name", data.user.firstName },
|
||||
{ "Last name", data.user.lastName },
|
||||
{ "Phone number", Data::FormatPhoneNumber(data.user.phoneNumber) },
|
||||
{ "First name", info.firstName },
|
||||
{ "Last name", info.lastName },
|
||||
{ "Phone number", Data::FormatPhoneNumber(info.phoneNumber) },
|
||||
{ "Username", FormatUsername(data.user.username) },
|
||||
{ "Bio", data.bio },
|
||||
})
|
||||
|
@ -158,7 +460,7 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) {
|
|||
list.reserve(data.list.size());
|
||||
for (const auto &index : Data::SortedContactsIndices(data)) {
|
||||
const auto &contact = data.list[index];
|
||||
if (!contact.id) {
|
||||
if (!contact.userId) {
|
||||
list.push_back("(user unavailable)" + kLineBreak);
|
||||
} else if (contact.firstName.isEmpty()
|
||||
&& contact.lastName.isEmpty()
|
||||
|
@ -300,11 +602,10 @@ bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) {
|
|||
list.reserve(data.list.size());
|
||||
auto index = 0;
|
||||
for (const auto &message : data.list) {
|
||||
list.push_back(SerializeKeyValue({
|
||||
{ "ID", Data::NumberToString(message.id) },
|
||||
{ "Date", Data::FormatDateTime(message.date) },
|
||||
{ "Text", message.text }
|
||||
}));
|
||||
list.push_back(SerializeMessage(
|
||||
message,
|
||||
data.peers,
|
||||
_settings.internalLinksDomain));
|
||||
}
|
||||
const auto full = _dialog->empty()
|
||||
? JoinList(kLineBreak, list)
|
||||
|
@ -336,12 +637,12 @@ QString TextWriter::mainFileRelativePath() const {
|
|||
}
|
||||
|
||||
QString TextWriter::pathWithRelativePath(const QString &path) const {
|
||||
return _folder + path;
|
||||
return _settings.path + path;
|
||||
}
|
||||
|
||||
std::unique_ptr<File> TextWriter::fileWithRelativePath(
|
||||
const QString &path) const {
|
||||
return std::make_unique<File>(_folder + path);
|
||||
return std::make_unique<File>(pathWithRelativePath(path));
|
||||
}
|
||||
|
||||
} // namespace Output
|
||||
|
|
|
@ -9,13 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "export/output/export_output_abstract.h"
|
||||
#include "export/output/export_output_file.h"
|
||||
#include "export/export_settings.h"
|
||||
|
||||
namespace Export {
|
||||
namespace Output {
|
||||
|
||||
class TextWriter : public AbstractWriter {
|
||||
public:
|
||||
bool start(const QString &folder) override;
|
||||
bool start(const Settings &settings) override;
|
||||
|
||||
bool writePersonal(const Data::PersonalInfo &data) override;
|
||||
|
||||
|
@ -42,7 +43,7 @@ private:
|
|||
QString pathWithRelativePath(const QString &path) const;
|
||||
std::unique_ptr<File> fileWithRelativePath(const QString &path) const;
|
||||
|
||||
QString _folder;
|
||||
Settings _settings;
|
||||
|
||||
std::unique_ptr<File> _result;
|
||||
int _userpicsCount = 0;
|
||||
|
|
|
@ -29,6 +29,8 @@ SettingsWidget::SettingsWidget(QWidget *parent)
|
|||
} else {
|
||||
_data.path = Global::DownloadPath();
|
||||
}
|
||||
_data.internalLinksDomain = Global::InternalLinksDomain();
|
||||
|
||||
setupContent();
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,61 @@ std::unique_ptr<HistoryMedia> CreateAttach(
|
|||
|
||||
} // namespace
|
||||
|
||||
QString FillAmountAndCurrency(uint64 amount, const QString ¤cy) {
|
||||
static const auto ShortCurrencyNames = QMap<QString, QString> {
|
||||
{ qsl("USD"), QString::fromUtf8("\x24") },
|
||||
{ qsl("GBP"), QString::fromUtf8("\xC2\xA3") },
|
||||
{ qsl("EUR"), QString::fromUtf8("\xE2\x82\xAC") },
|
||||
{ qsl("JPY"), QString::fromUtf8("\xC2\xA5") },
|
||||
};
|
||||
static const auto Denominators = QMap<QString, int> {
|
||||
{ qsl("CLF"), 10000 },
|
||||
{ qsl("BHD"), 1000 },
|
||||
{ qsl("IQD"), 1000 },
|
||||
{ qsl("JOD"), 1000 },
|
||||
{ qsl("KWD"), 1000 },
|
||||
{ qsl("LYD"), 1000 },
|
||||
{ qsl("OMR"), 1000 },
|
||||
{ qsl("TND"), 1000 },
|
||||
{ qsl("BIF"), 1 },
|
||||
{ qsl("BYR"), 1 },
|
||||
{ qsl("CLP"), 1 },
|
||||
{ qsl("CVE"), 1 },
|
||||
{ qsl("DJF"), 1 },
|
||||
{ qsl("GNF"), 1 },
|
||||
{ qsl("ISK"), 1 },
|
||||
{ qsl("JPY"), 1 },
|
||||
{ qsl("KMF"), 1 },
|
||||
{ qsl("KRW"), 1 },
|
||||
{ qsl("MGA"), 1 },
|
||||
{ qsl("PYG"), 1 },
|
||||
{ qsl("RWF"), 1 },
|
||||
{ qsl("UGX"), 1 },
|
||||
{ qsl("UYI"), 1 },
|
||||
{ qsl("VND"), 1 },
|
||||
{ qsl("VUV"), 1 },
|
||||
{ qsl("XAF"), 1 },
|
||||
{ qsl("XOF"), 1 },
|
||||
{ qsl("XPF"), 1 },
|
||||
{ qsl("MRO"), 10 },
|
||||
};
|
||||
const auto currencyText = ShortCurrencyNames.value(currency, currency);
|
||||
const auto denominator = Denominators.value(currency, 100);
|
||||
const auto currencyValue = amount / float64(denominator);
|
||||
const auto digits = [&] {
|
||||
auto result = 0;
|
||||
for (auto test = 1; test < denominator; test *= 10) {
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
return QLocale::system().toCurrencyString(currencyValue, currencyText);
|
||||
//auto amountBucks = amount / 100;
|
||||
//auto amountCents = amount % 100;
|
||||
//auto amountText = qsl("%1,%2").arg(amountBucks).arg(amountCents, 2, 10, QChar('0'));
|
||||
//return currencyText + amountText;
|
||||
}
|
||||
|
||||
void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
if (p == _savel || p == _cancell) {
|
||||
if (active && !dataLoaded()) {
|
||||
|
@ -4259,63 +4314,6 @@ HistoryInvoice::HistoryInvoice(
|
|||
fillFromData(invoice);
|
||||
}
|
||||
|
||||
QString HistoryInvoice::fillAmountAndCurrency(
|
||||
uint64 amount,
|
||||
const QString ¤cy) {
|
||||
static const auto ShortCurrencyNames = QMap<QString, QString> {
|
||||
{ qsl("USD"), QString::fromUtf8("\x24") },
|
||||
{ qsl("GBP"), QString::fromUtf8("\xC2\xA3") },
|
||||
{ qsl("EUR"), QString::fromUtf8("\xE2\x82\xAC") },
|
||||
{ qsl("JPY"), QString::fromUtf8("\xC2\xA5") },
|
||||
};
|
||||
static const auto Denominators = QMap<QString, int> {
|
||||
{ qsl("CLF"), 10000 },
|
||||
{ qsl("BHD"), 1000 },
|
||||
{ qsl("IQD"), 1000 },
|
||||
{ qsl("JOD"), 1000 },
|
||||
{ qsl("KWD"), 1000 },
|
||||
{ qsl("LYD"), 1000 },
|
||||
{ qsl("OMR"), 1000 },
|
||||
{ qsl("TND"), 1000 },
|
||||
{ qsl("BIF"), 1 },
|
||||
{ qsl("BYR"), 1 },
|
||||
{ qsl("CLP"), 1 },
|
||||
{ qsl("CVE"), 1 },
|
||||
{ qsl("DJF"), 1 },
|
||||
{ qsl("GNF"), 1 },
|
||||
{ qsl("ISK"), 1 },
|
||||
{ qsl("JPY"), 1 },
|
||||
{ qsl("KMF"), 1 },
|
||||
{ qsl("KRW"), 1 },
|
||||
{ qsl("MGA"), 1 },
|
||||
{ qsl("PYG"), 1 },
|
||||
{ qsl("RWF"), 1 },
|
||||
{ qsl("UGX"), 1 },
|
||||
{ qsl("UYI"), 1 },
|
||||
{ qsl("VND"), 1 },
|
||||
{ qsl("VUV"), 1 },
|
||||
{ qsl("XAF"), 1 },
|
||||
{ qsl("XOF"), 1 },
|
||||
{ qsl("XPF"), 1 },
|
||||
{ qsl("MRO"), 10 },
|
||||
};
|
||||
const auto currencyText = ShortCurrencyNames.value(currency, currency);
|
||||
const auto denominator = Denominators.value(currency, 100);
|
||||
const auto currencyValue = amount / float64(denominator);
|
||||
const auto digits = [&] {
|
||||
auto result = 0;
|
||||
for (auto test = 1; test < denominator; test *= 10) {
|
||||
++result;
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
return QLocale::system().toCurrencyString(currencyValue, currencyText);
|
||||
//auto amountBucks = amount / 100;
|
||||
//auto amountCents = amount % 100;
|
||||
//auto amountText = qsl("%1,%2").arg(amountBucks).arg(amountCents, 2, 10, QChar('0'));
|
||||
//return currencyText + amountText;
|
||||
}
|
||||
|
||||
void HistoryInvoice::fillFromData(not_null<Data::Invoice*> invoice) {
|
||||
// init attach
|
||||
auto labelText = [&] {
|
||||
|
@ -4330,7 +4328,7 @@ void HistoryInvoice::fillFromData(not_null<Data::Invoice*> invoice) {
|
|||
return lang(lng_payments_invoice_label);
|
||||
};
|
||||
auto statusText = TextWithEntities {
|
||||
fillAmountAndCurrency(invoice->amount, invoice->currency),
|
||||
FillAmountAndCurrency(invoice->amount, invoice->currency),
|
||||
EntitiesInText()
|
||||
};
|
||||
statusText.entities.push_back(EntityInText(EntityInTextBold, 0, statusText.text.size()));
|
||||
|
|
|
@ -41,6 +41,8 @@ namespace Ui {
|
|||
class EmptyUserpic;
|
||||
} // namespace Ui
|
||||
|
||||
QString FillAmountAndCurrency(uint64 amount, const QString ¤cy);
|
||||
|
||||
class HistoryFileMedia : public HistoryMedia {
|
||||
public:
|
||||
using HistoryMedia::HistoryMedia;
|
||||
|
@ -850,7 +852,6 @@ public:
|
|||
QString getTitle() const {
|
||||
return _title.originalText();
|
||||
}
|
||||
static QString fillAmountAndCurrency(uint64 amount, const QString ¤cy);
|
||||
|
||||
bool hideMessageText() const override {
|
||||
return false;
|
||||
|
|
|
@ -608,7 +608,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
|||
UpdateComponents(HistoryServicePayment::Bit());
|
||||
auto amount = message.vaction.c_messageActionPaymentSent().vtotal_amount.v;
|
||||
auto currency = qs(message.vaction.c_messageActionPaymentSent().vcurrency);
|
||||
Get<HistoryServicePayment>()->amount = HistoryInvoice::fillAmountAndCurrency(amount, currency);
|
||||
Get<HistoryServicePayment>()->amount = FillAmountAndCurrency(amount, currency);
|
||||
}
|
||||
if (message.has_reply_to_msg_id()) {
|
||||
if (message.vaction.type() == mtpc_messageActionPinMessage) {
|
||||
|
|
Loading…
Add table
Reference in a new issue