/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "support/support_helper.h" #include "dialogs/dialogs_key.h" #include "data/data_drafts.h" #include "history/history.h" #include "window/window_controller.h" #include "auth_session.h" #include "observer_peer.h" #include "apiwrap.h" namespace Support { namespace { constexpr auto kOccupyFor = TimeId(60); constexpr auto kReoccupyEach = 30 * TimeMs(1000); uint32 OccupationTag() { return uint32(Sandbox::UserTag() & 0xFFFFFFFFU); } Data::Draft OccupiedDraft() { const auto now = unixtime(), till = now + kOccupyFor; return { TextWithTags{ "t:" + QString::number(till) + ";u:" + QString::number(OccupationTag()) }, MsgId(0), MessageCursor(), false }; } uint32 ParseOccupationTag(History *history) { if (!history) { return 0; } const auto draft = history->cloudDraft(); if (!draft) { return 0; } const auto &text = draft->textWithTags.text; #ifndef OS_MAC_OLD const auto parts = text.splitRef(';'); #else // OS_MAC_OLD const auto parts = text.split(';'); #endif // OS_MAC_OLD auto valid = false; auto result = uint32(); for (const auto &part : parts) { if (part.startsWith(qstr("t:"))) { if (part.mid(2).toInt() >= unixtime()) { valid = true; } else { return 0; } } else if (part.startsWith(qstr("u:"))) { result = part.mid(2).toUInt(); } } return valid ? result : 0; } TimeId OccupiedBySomeoneTill(History *history) { if (!history) { return 0; } const auto draft = history->cloudDraft(); if (!draft) { return 0; } const auto &text = draft->textWithTags.text; #ifndef OS_MAC_OLD const auto parts = text.splitRef(';'); #else // OS_MAC_OLD const auto parts = text.split(';'); #endif // OS_MAC_OLD auto valid = false; auto result = TimeId(); for (const auto &part : parts) { if (part.startsWith(qstr("t:"))) { if (part.mid(2).toInt() >= unixtime()) { result = part.mid(2).toInt(); } else { return 0; } } else if (part.startsWith(qstr("u:"))) { if (part.mid(2).toUInt() != OccupationTag()) { valid = true; } else { return 0; } } } return valid ? result : 0; } } // namespace Helper::Helper(not_null session) : _session(session) , _templates(_session) , _reoccupyTimer([=] { reoccupy(); }) , _checkOccupiedTimer([=] { checkOccupiedChats(); }) { } void Helper::registerWindow(not_null controller) { controller->activeChatValue( ) | rpl::map([](Dialogs::Key key) { return key.history(); }) | rpl::distinct_until_changed( ) | rpl::start_with_next([=](History *history) { updateOccupiedHistory(controller, history); }, controller->lifetime()); } void Helper::cloudDraftChanged(not_null history) { chatOccupiedUpdated(history); if (history != _occupiedHistory) { return; } if (!IsOccupiedByMe(_occupiedHistory)) { occupyInDraft(); } } void Helper::chatOccupiedUpdated(not_null history) { if (const auto till = OccupiedBySomeoneTill(history)) { _occupiedChats[history] = till + 2; Notify::peerUpdatedDelayed( history->peer, Notify::PeerUpdate::Flag::OccupiedChanged); checkOccupiedChats(); } else if (_occupiedChats.take(history)) { Notify::peerUpdatedDelayed( history->peer, Notify::PeerUpdate::Flag::OccupiedChanged); } } void Helper::checkOccupiedChats() { const auto now = unixtime(); while (!_occupiedChats.empty()) { const auto nearest = ranges::min_element( _occupiedChats, std::less<>(), [](const auto &pair) { return pair.second; }); if (nearest->second <= now) { const auto history = nearest->first; _occupiedChats.erase(nearest); Notify::peerUpdatedDelayed( history->peer, Notify::PeerUpdate::Flag::OccupiedChanged); } else { _checkOccupiedTimer.callOnce( (nearest->second - now) * TimeMs(1000)); return; } } _checkOccupiedTimer.cancel(); } void Helper::updateOccupiedHistory( not_null controller, History *history) { if (IsOccupiedByMe(_occupiedHistory)) { _occupiedHistory->clearCloudDraft(); _session->api().saveDraftToCloudDelayed(_occupiedHistory); } _occupiedHistory = history; occupyInDraft(); } void Helper::occupyInDraft() { if (_occupiedHistory && !IsOccupiedBySomeone(_occupiedHistory)) { const auto draft = OccupiedDraft(); _occupiedHistory->createCloudDraft(&draft); _session->api().saveDraftToCloudDelayed(_occupiedHistory); _reoccupyTimer.callEach(kReoccupyEach); } } void Helper::reoccupy() { if (IsOccupiedByMe(_occupiedHistory)) { const auto draft = OccupiedDraft(); _occupiedHistory->createCloudDraft(&draft); _session->api().saveDraftToCloudDelayed(_occupiedHistory); } } Templates &Helper::templates() { return _templates; } bool IsOccupiedByMe(History *history) { if (const auto tag = ParseOccupationTag(history)) { return (tag == OccupationTag()); } return false; } bool IsOccupiedBySomeone(History *history) { if (const auto tag = ParseOccupationTag(history)) { return (tag != OccupationTag()); } return false; } QString ChatOccupiedString() { return QString::fromUtf8("\xe2\x9c\x8b\xef\xb8\x8f chat taken"); } } // namespace Support