mirror of https://github.com/procxx/kepka.git
				
				
				
			Export sessions list.
This commit is contained in:
		
							parent
							
								
									cec8114b99
								
							
						
					
					
						commit
						d3fdf433cd
					
				|  | @ -100,19 +100,18 @@ Photo ParsePhoto(const MTPPhoto &data, const QString &suggestedPath) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Utf8String FormatDateTime( | Utf8String FormatDateTime( | ||||||
| 		const int32 date, | 		const QDateTime &date, | ||||||
| 		QChar dateSeparator, | 		QChar dateSeparator, | ||||||
| 		QChar timeSeparator, | 		QChar timeSeparator, | ||||||
| 		QChar separator) { | 		QChar separator) { | ||||||
| 	const auto value = QDateTime::fromTime_t(date); |  | ||||||
| 	return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3" | 	return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3" | ||||||
| 		+ separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6" | 		+ separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6" | ||||||
| 	).arg(value.date().year() | 	).arg(date.date().year() | ||||||
| 	).arg(value.date().month(), 2, 10, QChar('0') | 	).arg(date.date().month(), 2, 10, QChar('0') | ||||||
| 	).arg(value.date().day(), 2, 10, QChar('0') | 	).arg(date.date().day(), 2, 10, QChar('0') | ||||||
| 	).arg(value.time().hour(), 2, 10, QChar('0') | 	).arg(date.time().hour(), 2, 10, QChar('0') | ||||||
| 	).arg(value.time().minute(), 2, 10, QChar('0') | 	).arg(date.time().minute(), 2, 10, QChar('0') | ||||||
| 	).arg(value.time().second(), 2, 10, QChar('0') | 	).arg(date.time().second(), 2, 10, QChar('0') | ||||||
| 	).toUtf8(); | 	).toUtf8(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -200,6 +199,52 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) { | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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) | ||||||
|  | 			+ ' ' | ||||||
|  | 			+ QString::fromUtf8(user.lastName)).toLower(); | ||||||
|  | 	}) | ranges::to_vector; | ||||||
|  | 
 | ||||||
|  | 	auto indices = ranges::view::ints(0, int(data.list.size())) | ||||||
|  | 		| ranges::to_vector; | ||||||
|  | 	ranges::sort(indices, [&](int i, int j) { | ||||||
|  | 		return names[i] < names[j]; | ||||||
|  | 	}); | ||||||
|  | 	return indices; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Session ParseSession(const MTPAuthorization &data) { | ||||||
|  | 	Expects(data.type() == mtpc_authorization); | ||||||
|  | 
 | ||||||
|  | 	const auto &fields = data.c_authorization(); | ||||||
|  | 	auto result = Session(); | ||||||
|  | 	result.platform = ParseString(fields.vplatform); | ||||||
|  | 	result.deviceModel = ParseString(fields.vdevice_model); | ||||||
|  | 	result.systemVersion = ParseString(fields.vsystem_version); | ||||||
|  | 	result.applicationName = ParseString(fields.vapp_name); | ||||||
|  | 	result.applicationVersion = ParseString(fields.vapp_version); | ||||||
|  | 	result.created = QDateTime::fromTime_t(fields.vdate_created.v); | ||||||
|  | 	result.lastActive = QDateTime::fromTime_t(fields.vdate_active.v); | ||||||
|  | 	result.ip = ParseString(fields.vip); | ||||||
|  | 	result.country = ParseString(fields.vcountry); | ||||||
|  | 	result.region = ParseString(fields.vregion); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SessionsList ParseSessionsList(const MTPaccount_Authorizations &data) { | ||||||
|  | 	Expects(data.type() == mtpc_account_authorizations); | ||||||
|  | 
 | ||||||
|  | 	auto result = SessionsList(); | ||||||
|  | 	const auto &list = data.c_account_authorizations().vauthorizations.v; | ||||||
|  | 	for (const auto &session : list) { | ||||||
|  | 		result.list.push_back(ParseSession(session)); | ||||||
|  | 	} | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Utf8String FormatPhoneNumber(const Utf8String &phoneNumber) { | Utf8String FormatPhoneNumber(const Utf8String &phoneNumber) { | ||||||
| 	return phoneNumber.isEmpty() | 	return phoneNumber.isEmpty() | ||||||
| 		? Utf8String() | 		? Utf8String() | ||||||
|  |  | ||||||
|  | @ -86,6 +86,7 @@ struct ContactsList { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| ContactsList ParseContactsList(const MTPcontacts_Contacts &data); | ContactsList ParseContactsList(const MTPcontacts_Contacts &data); | ||||||
|  | std::vector<int> SortedContactsIndices(const ContactsList &data); | ||||||
| 
 | 
 | ||||||
| struct Session { | struct Session { | ||||||
| 	Utf8String platform; | 	Utf8String platform; | ||||||
|  | @ -104,6 +105,8 @@ struct SessionsList { | ||||||
| 	std::vector<Session> list; | 	std::vector<Session> list; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | SessionsList ParseSessionsList(const MTPaccount_Authorizations &data); | ||||||
|  | 
 | ||||||
| struct ChatsInfo { | struct ChatsInfo { | ||||||
| 	int count = 0; | 	int count = 0; | ||||||
| }; | }; | ||||||
|  | @ -127,11 +130,24 @@ struct MessagesSlice { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Utf8String FormatPhoneNumber(const Utf8String &phoneNumber); | Utf8String FormatPhoneNumber(const Utf8String &phoneNumber); | ||||||
|  | 
 | ||||||
| Utf8String FormatDateTime( | Utf8String FormatDateTime( | ||||||
| 	const int32 date, | 	const QDateTime &date, | ||||||
| 	QChar dateSeparator = QChar('.'), | 	QChar dateSeparator = QChar('.'), | ||||||
| 	QChar timeSeparator = QChar(':'), | 	QChar timeSeparator = QChar(':'), | ||||||
| 	QChar separator = QChar(' ')); | 	QChar separator = QChar(' ')); | ||||||
| 
 | 
 | ||||||
|  | inline Utf8String FormatDateTime( | ||||||
|  | 		int32 date, | ||||||
|  | 		QChar dateSeparator = QChar('.'), | ||||||
|  | 		QChar timeSeparator = QChar(':'), | ||||||
|  | 		QChar separator = QChar(' ')) { | ||||||
|  | 	return FormatDateTime( | ||||||
|  | 		QDateTime::fromTime_t(date), | ||||||
|  | 		dateSeparator, | ||||||
|  | 		timeSeparator, | ||||||
|  | 		separator); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Data
 | } // namespace Data
 | ||||||
| } // namespace Export
 | } // namespace Export
 | ||||||
|  |  | ||||||
|  | @ -243,6 +243,14 @@ void ApiWrap::requestContacts(FnMut<void(Data::ContactsList&&)> done) { | ||||||
| 	}).send(); | 	}).send(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ApiWrap::requestSessions(FnMut<void(Data::SessionsList&&)> done) { | ||||||
|  | 	mainRequest(MTPaccount_GetAuthorizations( | ||||||
|  | 	)).done([=, done = std::move(done)]( | ||||||
|  | 			const MTPaccount_Authorizations &result) mutable { | ||||||
|  | 		done(Data::ParseSessionsList(result)); | ||||||
|  | 	}).send(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) { | void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) { | ||||||
| 	Expects(_fileProcess == nullptr); | 	Expects(_fileProcess == nullptr); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ struct PersonalInfo; | ||||||
| struct UserpicsInfo; | struct UserpicsInfo; | ||||||
| struct UserpicsSlice; | struct UserpicsSlice; | ||||||
| struct ContactsList; | struct ContactsList; | ||||||
|  | struct SessionsList; | ||||||
| } // namespace Data
 | } // namespace Data
 | ||||||
| 
 | 
 | ||||||
| class ApiWrap { | class ApiWrap { | ||||||
|  | @ -36,6 +37,8 @@ public: | ||||||
| 
 | 
 | ||||||
| 	void requestContacts(FnMut<void(Data::ContactsList&&)> done); | 	void requestContacts(FnMut<void(Data::ContactsList&&)> done); | ||||||
| 
 | 
 | ||||||
|  | 	void requestSessions(FnMut<void(Data::SessionsList&&)> done); | ||||||
|  | 
 | ||||||
| 	~ApiWrap(); | 	~ApiWrap(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |  | ||||||
|  | @ -261,7 +261,10 @@ void Controller::exportContacts() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Controller::exportSessions() { | void Controller::exportSessions() { | ||||||
| 	exportNext(); | 	_api.requestSessions([=](Data::SessionsList &&result) { | ||||||
|  | 		_writer->writeSessionsList(result); | ||||||
|  | 		exportNext(); | ||||||
|  | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Controller::exportChats() { | void Controller::exportChats() { | ||||||
|  |  | ||||||
|  | @ -130,8 +130,7 @@ bool TextWriter::writeUserpicsSlice(const Data::UserpicsSlice &data) { | ||||||
| 		if (!userpic.date.isValid()) { | 		if (!userpic.date.isValid()) { | ||||||
| 			lines.append("(empty photo)"); | 			lines.append("(empty photo)"); | ||||||
| 		} else { | 		} else { | ||||||
| 			lines.append(Data::FormatDateTime(userpic.date.toTime_t())); | 			lines.append(Data::FormatDateTime(userpic.date)).append(" - "); | ||||||
| 			lines.append(" - "); |  | ||||||
| 			if (userpic.image.relativePath.isEmpty()) { | 			if (userpic.image.relativePath.isEmpty()) { | ||||||
| 				lines.append("(file unavailable)"); | 				lines.append("(file unavailable)"); | ||||||
| 			} else { | 			} else { | ||||||
|  | @ -154,28 +153,13 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) { | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Get sorted by name indices.
 |  | ||||||
| 	const auto names = ranges::view::all( |  | ||||||
| 		data.list |  | ||||||
| 	) | ranges::view::transform([](const Data::User &user) { |  | ||||||
| 		return (QString::fromUtf8(user.firstName) |  | ||||||
| 			+ ' ' |  | ||||||
| 			+ QString::fromUtf8(user.lastName)).toLower(); |  | ||||||
| 	}) | ranges::to_vector; |  | ||||||
| 
 |  | ||||||
| 	auto indices = ranges::view::ints(0, int(data.list.size())) |  | ||||||
| 		| ranges::to_vector; |  | ||||||
| 	ranges::sort(indices, [&](int i, int j) { |  | ||||||
| 		return names[i] < names[j]; |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	const auto header = "Contacts " | 	const auto header = "Contacts " | ||||||
| 		"(" + Data::NumberToString(data.list.size()) + ")" | 		"(" + Data::NumberToString(data.list.size()) + ")" | ||||||
| 		+ kLineBreak | 		+ kLineBreak | ||||||
| 		+ kLineBreak; | 		+ kLineBreak; | ||||||
| 	auto list = std::vector<QByteArray>(); | 	auto list = std::vector<QByteArray>(); | ||||||
| 	list.reserve(data.list.size()); | 	list.reserve(data.list.size()); | ||||||
| 	for (const auto &index : indices) { | 	for (const auto &index : Data::SortedContactsIndices(data)) { | ||||||
| 		const auto &contact = data.list[index]; | 		const auto &contact = data.list[index]; | ||||||
| 		if (!contact.id) { | 		if (!contact.id) { | ||||||
| 			list.push_back("(user unavailable)"); | 			list.push_back("(user unavailable)"); | ||||||
|  | @ -199,7 +183,33 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TextWriter::writeSessionsList(const Data::SessionsList &data) { | bool TextWriter::writeSessionsList(const Data::SessionsList &data) { | ||||||
| 	return true; | 	const auto header = "Sessions " | ||||||
|  | 		"(" + Data::NumberToString(data.list.size()) + ")" | ||||||
|  | 		+ kLineBreak | ||||||
|  | 		+ kLineBreak; | ||||||
|  | 	auto list = std::vector<QByteArray>(); | ||||||
|  | 	list.reserve(data.list.size()); | ||||||
|  | 	for (const auto &session : data.list) { | ||||||
|  | 		list.push_back(SerializeKeyValue({ | ||||||
|  | 			{ "Last active", Data::FormatDateTime(session.lastActive) }, | ||||||
|  | 			{ "Last IP address", session.ip }, | ||||||
|  | 			{ "Last country", session.country }, | ||||||
|  | 			{ "Last region", session.region }, | ||||||
|  | 			{ | ||||||
|  | 				"Application name", | ||||||
|  | 				(session.applicationName.isEmpty() | ||||||
|  | 					? Data::Utf8String("(unknown)") | ||||||
|  | 					: session.applicationName) | ||||||
|  | 			}, | ||||||
|  | 			{ "Application version", session.applicationVersion }, | ||||||
|  | 			{ "Device model", session.deviceModel }, | ||||||
|  | 			{ "Platform", session.platform }, | ||||||
|  | 			{ "System version", session.systemVersion }, | ||||||
|  | 			{ "Created", Data::FormatDateTime(session.created) }, | ||||||
|  | 		})); | ||||||
|  | 	} | ||||||
|  | 	const auto full = header + JoinList(kLineBreak, list) + kLineBreak; | ||||||
|  | 	return _result->writeBlock(full) == File::Result::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool TextWriter::writeChatsStart(const Data::ChatsInfo &data) { | bool TextWriter::writeChatsStart(const Data::ChatsInfo &data) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue