mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 07:33:52 +02:00
Support "Device Storage" web app feature.
This commit is contained in:
parent
4c2ec15f70
commit
59abfcbd6d
9 changed files with 441 additions and 5 deletions
|
@ -1083,6 +1083,8 @@ PRIVATE
|
|||
inline_bots/inline_bot_result.h
|
||||
inline_bots/inline_bot_send_data.cpp
|
||||
inline_bots/inline_bot_send_data.h
|
||||
inline_bots/inline_bot_storage.cpp
|
||||
inline_bots/inline_bot_storage.h
|
||||
inline_bots/inline_results_inner.cpp
|
||||
inline_bots/inline_results_inner.h
|
||||
inline_bots/inline_results_widget.cpp
|
||||
|
|
|
@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "inline_bots/inline_bot_confirm_prepared.h"
|
||||
#include "inline_bots/inline_bot_downloads.h"
|
||||
#include "inline_bots/inline_bot_storage.h"
|
||||
#include "iv/iv_instance.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
|
@ -1502,7 +1503,7 @@ void WebViewInstance::botHandleInvoice(QString slug) {
|
|||
}
|
||||
};
|
||||
Payments::CheckoutProcess::Start(
|
||||
&_bot->session(),
|
||||
_session,
|
||||
slug,
|
||||
reactivate,
|
||||
nonPanelPaymentFormFactory(reactivate));
|
||||
|
@ -1698,6 +1699,20 @@ void WebViewInstance::botAllowWriteAccess(Fn<void(bool allowed)> callback) {
|
|||
}).send();
|
||||
}
|
||||
|
||||
bool WebViewInstance::botStorageWrite(
|
||||
QString key,
|
||||
std::optional<QString> value) {
|
||||
return _session->attachWebView().storage().write(_bot->id, key, value);
|
||||
}
|
||||
|
||||
std::optional<QString> WebViewInstance::botStorageRead(QString key) {
|
||||
return _session->attachWebView().storage().read(_bot->id, key);
|
||||
}
|
||||
|
||||
void WebViewInstance::botStorageClear() {
|
||||
_session->attachWebView().storage().clear(_bot->id);
|
||||
}
|
||||
|
||||
void WebViewInstance::botRequestEmojiStatusAccess(
|
||||
Fn<void(bool allowed)> callback) {
|
||||
if (_bot->botInfo->canManageEmojiStatus) {
|
||||
|
@ -1955,20 +1970,20 @@ void WebViewInstance::botDownloadFile(
|
|||
callback(false);
|
||||
return;
|
||||
}
|
||||
_bot->session().attachWebView().downloads().start({
|
||||
_session->attachWebView().downloads().start({
|
||||
.bot = _bot,
|
||||
.url = request.url,
|
||||
.path = path,
|
||||
});
|
||||
callback(true);
|
||||
};
|
||||
_bot->session().api().request(MTPbots_CheckDownloadFileParams(
|
||||
_session->api().request(MTPbots_CheckDownloadFileParams(
|
||||
_bot->inputUser,
|
||||
MTP_string(request.name),
|
||||
MTP_string(request.url)
|
||||
)).done([=] {
|
||||
_panel->showBox(Box(DownloadFileBox, DownloadBoxArgs{
|
||||
.session = &_bot->session(),
|
||||
.session = _session,
|
||||
.bot = _bot->name(),
|
||||
.name = base::FileNameFromUserString(request.name),
|
||||
.url = request.url,
|
||||
|
@ -2018,7 +2033,7 @@ void WebViewInstance::botOpenPrivacyPolicy() {
|
|||
};
|
||||
const auto openUrl = [=](const QString &url) {
|
||||
Core::App().iv().openWithIvPreferred(
|
||||
&_bot->session(),
|
||||
_session,
|
||||
url,
|
||||
makeOtherContext(false));
|
||||
};
|
||||
|
@ -2092,6 +2107,7 @@ std::shared_ptr<Main::SessionShow> WebViewInstance::uiShow() {
|
|||
AttachWebView::AttachWebView(not_null<Main::Session*> session)
|
||||
: _session(session)
|
||||
, _downloads(std::make_unique<Downloads>(session))
|
||||
, _storage(std::make_unique<Storage>(session))
|
||||
, _refreshTimer([=] { requestBots(); }) {
|
||||
_refreshTimer.callEach(kRefreshBotsTimeout);
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace InlineBots {
|
|||
|
||||
class WebViewInstance;
|
||||
class Downloads;
|
||||
class Storage;
|
||||
|
||||
enum class PeerType : uint8 {
|
||||
SameBot = 0x01,
|
||||
|
@ -276,6 +277,9 @@ private:
|
|||
QString query) override;
|
||||
void botCheckWriteAccess(Fn<void(bool allowed)> callback) override;
|
||||
void botAllowWriteAccess(Fn<void(bool allowed)> callback) override;
|
||||
bool botStorageWrite(QString key, std::optional<QString> value) override;
|
||||
std::optional<QString> botStorageRead(QString key) override;
|
||||
void botStorageClear() override;
|
||||
void botRequestEmojiStatusAccess(
|
||||
Fn<void(bool allowed)> callback) override;
|
||||
void botSharePhone(Fn<void(bool shared)> callback) override;
|
||||
|
@ -322,6 +326,9 @@ public:
|
|||
[[nodiscard]] Downloads &downloads() const {
|
||||
return *_downloads;
|
||||
}
|
||||
[[nodiscard]] Storage &storage() const {
|
||||
return *_storage;
|
||||
}
|
||||
|
||||
void open(WebViewDescriptor &&descriptor);
|
||||
void openByUsername(
|
||||
|
@ -394,6 +401,7 @@ private:
|
|||
|
||||
const not_null<Main::Session*> _session;
|
||||
const std::unique_ptr<Downloads> _downloads;
|
||||
const std::unique_ptr<Storage> _storage;
|
||||
|
||||
base::Timer _refreshTimer;
|
||||
|
||||
|
|
181
Telegram/SourceFiles/inline_bots/inline_bot_storage.cpp
Normal file
181
Telegram/SourceFiles/inline_bots/inline_bot_storage.cpp
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
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 "inline_bots/inline_bot_storage.h"
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "storage/storage_account.h"
|
||||
|
||||
#include <xxhash.h>
|
||||
|
||||
namespace InlineBots {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxStorageSize = (5 << 20);
|
||||
|
||||
[[nodiscard]] uint64 KeyHash(const QString &key) {
|
||||
return XXH64(key.data(), key.size(), 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Storage::Storage(not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
bool Storage::write(
|
||||
PeerId botId,
|
||||
const QString &key,
|
||||
const std::optional<QString> &value) {
|
||||
if (value && value->size() > kMaxStorageSize) {
|
||||
return false;
|
||||
}
|
||||
readFromDisk(botId);
|
||||
auto i = _lists.find(botId);
|
||||
if (i == end(_lists)) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
i = _lists.emplace(botId).first;
|
||||
}
|
||||
auto &list = i->second;
|
||||
const auto hash = KeyHash(key);
|
||||
auto j = list.data.find(hash);
|
||||
if (j == end(list.data)) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
j = list.data.emplace(hash).first;
|
||||
}
|
||||
auto &bykey = j->second;
|
||||
const auto k = ranges::find(bykey, key, &Entry::key);
|
||||
if (k == end(bykey) && !value) {
|
||||
return true;
|
||||
}
|
||||
const auto size = list.totalSize
|
||||
- (k != end(bykey) ? (key.size() + k->value.size()) : 0)
|
||||
+ (value ? (key.size() + value->size()) : 0);
|
||||
if (size > kMaxStorageSize) {
|
||||
return false;
|
||||
}
|
||||
if (k == end(bykey)) {
|
||||
bykey.emplace_back(key, *value);
|
||||
++list.keysCount;
|
||||
} else if (value) {
|
||||
k->value = *value;
|
||||
} else {
|
||||
bykey.erase(k);
|
||||
--list.keysCount;
|
||||
}
|
||||
if (bykey.empty()) {
|
||||
list.data.erase(j);
|
||||
if (list.data.empty()) {
|
||||
Assert(size == 0);
|
||||
_lists.erase(i);
|
||||
} else {
|
||||
list.totalSize = size;
|
||||
}
|
||||
} else {
|
||||
list.totalSize = size;
|
||||
}
|
||||
saveToDisk(botId);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<QString> Storage::read(PeerId botId, const QString &key) {
|
||||
readFromDisk(botId);
|
||||
const auto i = _lists.find(botId);
|
||||
if (i == end(_lists)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto &list = i->second;
|
||||
const auto j = list.data.find(KeyHash(key));
|
||||
if (j == end(list.data)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto &bykey = j->second;
|
||||
const auto k = ranges::find(bykey, key, &Entry::key);
|
||||
if (k == end(bykey)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return k->value;
|
||||
}
|
||||
|
||||
void Storage::clear(PeerId botId) {
|
||||
if (_lists.remove(botId)) {
|
||||
saveToDisk(botId);
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::saveToDisk(PeerId botId) {
|
||||
const auto i = _lists.find(botId);
|
||||
if (i != end(_lists)) {
|
||||
_session->local().writeBotStorage(botId, Serialize(i->second));
|
||||
} else {
|
||||
_session->local().writeBotStorage(botId, QByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::readFromDisk(PeerId botId) {
|
||||
const auto serialized = _session->local().readBotStorage(botId);
|
||||
if (!serialized.isEmpty()) {
|
||||
_lists[botId] = Deserialize(serialized);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Storage::Serialize(const List &list) {
|
||||
auto result = QByteArray();
|
||||
const auto size = sizeof(quint32)
|
||||
+ (list.keysCount * sizeof(quint32))
|
||||
+ (list.totalSize * sizeof(ushort));
|
||||
result.reserve(size);
|
||||
{
|
||||
QDataStream stream(&result, QIODevice::WriteOnly);
|
||||
auto count = 0;
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
stream << quint32(list.keysCount);
|
||||
for (const auto &[hash, bykey] : list.data) {
|
||||
for (const auto &entry : bykey) {
|
||||
stream << entry.key << entry.value;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
Assert(count == list.keysCount);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Storage::List Storage::Deserialize(const QByteArray &serialized) {
|
||||
QDataStream stream(serialized);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
|
||||
auto count = quint32();
|
||||
auto result = List();
|
||||
stream >> count;
|
||||
if (count > kMaxStorageSize) {
|
||||
return {};
|
||||
}
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
auto entry = Entry();
|
||||
stream >> entry.key >> entry.value;
|
||||
const auto hash = KeyHash(entry.key);
|
||||
auto j = result.data.find(hash);
|
||||
if (j == end(result.data)) {
|
||||
j = result.data.emplace(hash).first;
|
||||
}
|
||||
auto &bykey = j->second;
|
||||
const auto k = ranges::find(bykey, entry.key, &Entry::key);
|
||||
if (k == end(bykey)) {
|
||||
bykey.push_back(entry);
|
||||
result.totalSize += entry.key.size() + entry.value.size();
|
||||
++result.keysCount;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace InlineBots
|
52
Telegram/SourceFiles/inline_bots/inline_bot_storage.h
Normal file
52
Telegram/SourceFiles/inline_bots/inline_bot_storage.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/chat/attach/attach_bot_webview.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace InlineBots {
|
||||
|
||||
class Storage final {
|
||||
public:
|
||||
explicit Storage(not_null<Main::Session*> session);
|
||||
|
||||
bool write(
|
||||
PeerId botId,
|
||||
const QString &key,
|
||||
const std::optional<QString> &value);
|
||||
std::optional<QString> read(PeerId botId, const QString &key);
|
||||
void clear(PeerId botId);
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
QString key;
|
||||
QString value;
|
||||
};
|
||||
struct List {
|
||||
base::flat_map<uint64, std::vector<Entry>> data;
|
||||
int keysCount = 0;
|
||||
int totalSize = 0;
|
||||
};
|
||||
|
||||
void saveToDisk(PeerId botId);
|
||||
void readFromDisk(PeerId botId);
|
||||
|
||||
[[nodiscard]] static QByteArray Serialize(const List &list);
|
||||
[[nodiscard]] static List Deserialize(const QByteArray &serialized);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
base::flat_map<PeerId, List> _lists;
|
||||
|
||||
};
|
||||
|
||||
} // namespace InlineBots
|
|
@ -98,6 +98,7 @@ enum { // Local Storage Keys
|
|||
lskRoundPlaceholder = 0x1a, // no data
|
||||
lskInlineBotsDownloads = 0x1b, // no data
|
||||
lskMediaLastPlaybackPositions = 0x1c, // no data
|
||||
lskBotStorages = 0x1d, // data: PeerId botId
|
||||
};
|
||||
|
||||
auto EmptyMessageDraftSources()
|
||||
|
@ -255,6 +256,9 @@ base::flat_set<QString> Account::collectGoodNames() const {
|
|||
for (const auto &[key, value] : _draftCursorsMap) {
|
||||
push(value);
|
||||
}
|
||||
for (const auto &[key, value] : _botStoragesMap) {
|
||||
push(value);
|
||||
}
|
||||
for (const auto &value : keys) {
|
||||
push(value);
|
||||
}
|
||||
|
@ -308,6 +312,8 @@ Account::ReadMapResult Account::readMapWith(
|
|||
base::flat_map<PeerId, FileKey> draftsMap;
|
||||
base::flat_map<PeerId, FileKey> draftCursorsMap;
|
||||
base::flat_map<PeerId, bool> draftsNotReadMap;
|
||||
base::flat_map<PeerId, FileKey> botStoragesMap;
|
||||
base::flat_map<PeerId, bool> botStoragesNotReadMap;
|
||||
quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedPeersKey = 0;
|
||||
quint64 recentStickersKeyOld = 0;
|
||||
quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0;
|
||||
|
@ -443,6 +449,18 @@ Account::ReadMapResult Account::readMapWith(
|
|||
>> webviewStorageTokenBots
|
||||
>> webviewStorageTokenOther;
|
||||
} break;
|
||||
case lskBotStorages: {
|
||||
quint32 count = 0;
|
||||
map.stream >> count;
|
||||
for (quint32 i = 0; i < count; ++i) {
|
||||
FileKey key;
|
||||
quint64 peerIdSerialized;
|
||||
map.stream >> key >> peerIdSerialized;
|
||||
const auto peerId = DeserializePeerId(peerIdSerialized);
|
||||
botStoragesMap.emplace(peerId, key);
|
||||
botStoragesNotReadMap.emplace(peerId, true);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
|
||||
return ReadMapResult::Failed;
|
||||
|
@ -457,6 +475,8 @@ Account::ReadMapResult Account::readMapWith(
|
|||
_draftsMap = draftsMap;
|
||||
_draftCursorsMap = draftCursorsMap;
|
||||
_draftsNotReadMap = draftsNotReadMap;
|
||||
_botStoragesMap = botStoragesMap;
|
||||
_botStoragesNotReadMap = botStoragesNotReadMap;
|
||||
|
||||
_locationsKey = locationsKey;
|
||||
_trustedPeersKey = trustedPeersKey;
|
||||
|
@ -599,6 +619,7 @@ void Account::writeMap() {
|
|||
if (_roundPlaceholderKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_inlineBotsDownloadsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_mediaLastPlaybackPositionsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (!_botStoragesMap.empty()) mapSize += sizeof(quint32) * 2 + _botStoragesMap.size() * sizeof(quint64) * 2;
|
||||
|
||||
EncryptedDescriptor mapData(mapSize);
|
||||
if (!self.isEmpty()) {
|
||||
|
@ -681,6 +702,12 @@ void Account::writeMap() {
|
|||
mapData.stream << quint32(lskMediaLastPlaybackPositions);
|
||||
mapData.stream << quint64(_mediaLastPlaybackPositionsKey);
|
||||
}
|
||||
if (!_botStoragesMap.empty()) {
|
||||
mapData.stream << quint32(lskBotStorages) << quint32(_botStoragesMap.size());
|
||||
for (const auto &[key, value] : _botStoragesMap) {
|
||||
mapData.stream << quint64(value) << SerializePeerId(key);
|
||||
}
|
||||
}
|
||||
map.writeEncrypted(mapData, _localKey);
|
||||
|
||||
_mapChanged = false;
|
||||
|
@ -693,6 +720,8 @@ void Account::reset() {
|
|||
_draftsMap.clear();
|
||||
_draftCursorsMap.clear();
|
||||
_draftsNotReadMap.clear();
|
||||
_botStoragesMap.clear();
|
||||
_botStoragesNotReadMap.clear();
|
||||
_locationsKey = _trustedPeersKey = 0;
|
||||
_recentStickersKeyOld = 0;
|
||||
_installedStickersKey = 0;
|
||||
|
@ -3473,6 +3502,63 @@ void Account::writeInlineBotsDownloads(const QByteArray &bytes) {
|
|||
file.writeEncrypted(data, _localKey);
|
||||
}
|
||||
|
||||
void Account::writeBotStorage(PeerId botId, const QByteArray &serialized) {
|
||||
if (serialized.isEmpty()) {
|
||||
auto i = _botStoragesMap.find(botId);
|
||||
if (i != _botStoragesMap.cend()) {
|
||||
ClearKey(i->second, _basePath);
|
||||
_botStoragesMap.erase(i);
|
||||
writeMapDelayed();
|
||||
}
|
||||
_botStoragesNotReadMap.remove(botId);
|
||||
return;
|
||||
}
|
||||
|
||||
auto i = _botStoragesMap.find(botId);
|
||||
if (i == _botStoragesMap.cend()) {
|
||||
i = _botStoragesMap.emplace(botId, GenerateKey(_basePath)).first;
|
||||
writeMapQueued();
|
||||
}
|
||||
|
||||
auto size = Serialize::bytearraySize(serialized);
|
||||
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << serialized;
|
||||
|
||||
FileWriteDescriptor file(i->second, _basePath);
|
||||
file.writeEncrypted(data, _localKey);
|
||||
|
||||
_botStoragesNotReadMap.remove(botId);
|
||||
}
|
||||
|
||||
QByteArray Account::readBotStorage(PeerId botId) {
|
||||
if (!_botStoragesNotReadMap.remove(botId)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto j = _botStoragesMap.find(botId);
|
||||
if (j == _botStoragesMap.cend()) {
|
||||
return {};
|
||||
}
|
||||
FileReadDescriptor storage;
|
||||
if (!ReadEncryptedFile(storage, j->second, _basePath, _localKey)) {
|
||||
ClearKey(j->second, _basePath);
|
||||
_botStoragesMap.erase(j);
|
||||
writeMapDelayed();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = QByteArray();
|
||||
storage.stream >> result;
|
||||
if (storage.stream.status() != QDataStream::Ok) {
|
||||
ClearKey(j->second, _basePath);
|
||||
_botStoragesMap.erase(j);
|
||||
writeMapDelayed();
|
||||
return {};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Account::encrypt(
|
||||
const void *src,
|
||||
void *dst,
|
||||
|
|
|
@ -191,6 +191,9 @@ public:
|
|||
[[nodiscard]] QByteArray readInlineBotsDownloads();
|
||||
void writeInlineBotsDownloads(const QByteArray &bytes);
|
||||
|
||||
void writeBotStorage(PeerId botId, const QByteArray &serialized);
|
||||
[[nodiscard]] QByteArray readBotStorage(PeerId botId);
|
||||
|
||||
[[nodiscard]] bool encrypt(
|
||||
const void *src,
|
||||
void *dst,
|
||||
|
@ -293,6 +296,8 @@ private:
|
|||
base::flat_map<
|
||||
not_null<History*>,
|
||||
base::flat_map<Data::DraftKey, MessageDraftSource>> _draftSources;
|
||||
base::flat_map<PeerId, FileKey> _botStoragesMap;
|
||||
base::flat_map<PeerId, bool> _botStoragesNotReadMap;
|
||||
|
||||
QMultiMap<MediaKey, Core::FileLocation> _fileLocations;
|
||||
QMap<QString, QPair<MediaKey, Core::FileLocation>> _fileLocationPairs;
|
||||
|
|
|
@ -997,6 +997,20 @@ bool Panel::createWebview(const Webview::ThemeParams ¶ms) {
|
|||
processEmojiStatusRequest(arguments);
|
||||
} else if (command == "web_app_request_emoji_status_access") {
|
||||
processEmojiStatusAccessRequest();
|
||||
} else if (command == "web_app_device_storage_save_key") {
|
||||
processStorageSaveKey(arguments);
|
||||
} else if (command == "web_app_device_storage_get_key") {
|
||||
processStorageGetKey(arguments);
|
||||
} else if (command == "web_app_device_storage_clear") {
|
||||
processStorageClear(arguments);
|
||||
} else if (command == "web_app_secure_storage_save_key") {
|
||||
secureStorageFailed(arguments);
|
||||
} else if (command == "web_app_secure_storage_get_key") {
|
||||
secureStorageFailed(arguments);
|
||||
} else if (command == "web_app_secure_storage_restore_key") {
|
||||
secureStorageFailed(arguments);
|
||||
} else if (command == "web_app_secure_storage_clear") {
|
||||
secureStorageFailed(arguments);
|
||||
} else if (command == "share_score") {
|
||||
_delegate->botHandleMenuButton(MenuButton::ShareGame);
|
||||
}
|
||||
|
@ -1201,6 +1215,63 @@ void Panel::processEmojiStatusAccessRequest() {
|
|||
_delegate->botRequestEmojiStatusAccess(std::move(callback));
|
||||
}
|
||||
|
||||
void Panel::processStorageSaveKey(const QJsonObject &args) {
|
||||
const auto keyObject = args["key"];
|
||||
const auto valueObject = args["value"];
|
||||
const auto key = keyObject.toString();
|
||||
if (!keyObject.isString()) {
|
||||
deviceStorageFailed(args, u"KEY_INVALID"_q);
|
||||
} else if (valueObject.isNull()) {
|
||||
_delegate->botStorageWrite(key, std::nullopt);
|
||||
replyDeviceStorage(args, u"device_storage_key_saved"_q, {});
|
||||
} else if (!valueObject.isString()) {
|
||||
deviceStorageFailed(args, u"VALUE_INVALID"_q);
|
||||
} else if (_delegate->botStorageWrite(key, valueObject.toString())) {
|
||||
replyDeviceStorage(args, u"device_storage_key_saved"_q, {});
|
||||
} else {
|
||||
deviceStorageFailed(args, u"QUOTA_EXCEEDED"_q);
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::processStorageGetKey(const QJsonObject &args) {
|
||||
const auto keyObject = args["key"];
|
||||
const auto key = keyObject.toString();
|
||||
if (!keyObject.isString()) {
|
||||
deviceStorageFailed(args, u"KEY_INVALID"_q);
|
||||
} else {
|
||||
const auto value = _delegate->botStorageRead(key);
|
||||
replyDeviceStorage(args, u"device_storage_key_received"_q, {
|
||||
{ u"value"_q, value ? QJsonValue(*value) : QJsonValue::Null },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::processStorageClear(const QJsonObject &args) {
|
||||
_delegate->botStorageClear();
|
||||
replyDeviceStorage(args, u"device_storage_cleared"_q, {});
|
||||
}
|
||||
|
||||
void Panel::replyDeviceStorage(
|
||||
const QJsonObject &args,
|
||||
const QString &event,
|
||||
QJsonObject response) {
|
||||
response[u"req_id"_q] = args[u"req_id"_q];
|
||||
postEvent(event, response);
|
||||
}
|
||||
|
||||
void Panel::deviceStorageFailed(const QJsonObject &args, QString error) {
|
||||
replyDeviceStorage(args, u"device_storage_failed"_q, {
|
||||
{ u"error"_q, error },
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::secureStorageFailed(const QJsonObject &args) {
|
||||
postEvent(u"secure_storage_failed"_q, QJsonObject{
|
||||
{ u"req_id"_q, args["req_id"] },
|
||||
{ u"error"_q, u"UNSUPPORTED"_q },
|
||||
});
|
||||
}
|
||||
|
||||
void Panel::openTgLink(const QJsonObject &args) {
|
||||
if (args.isEmpty()) {
|
||||
LOG(("BotWebView Error: Bad arguments in 'web_app_open_tg_link'."));
|
||||
|
|
|
@ -91,6 +91,12 @@ public:
|
|||
QString query) = 0;
|
||||
virtual void botCheckWriteAccess(Fn<void(bool allowed)> callback) = 0;
|
||||
virtual void botAllowWriteAccess(Fn<void(bool allowed)> callback) = 0;
|
||||
virtual bool botStorageWrite(
|
||||
QString key,
|
||||
std::optional<QString> value) = 0;
|
||||
[[nodiscard]] virtual std::optional<QString> botStorageRead(
|
||||
QString key) = 0;
|
||||
virtual void botStorageClear() = 0;
|
||||
virtual void botRequestEmojiStatusAccess(
|
||||
Fn<void(bool allowed)> callback) = 0;
|
||||
virtual void botSharePhone(Fn<void(bool shared)> callback) = 0;
|
||||
|
@ -165,6 +171,9 @@ private:
|
|||
void processSendMessageRequest(const QJsonObject &args);
|
||||
void processEmojiStatusRequest(const QJsonObject &args);
|
||||
void processEmojiStatusAccessRequest();
|
||||
void processStorageSaveKey(const QJsonObject &args);
|
||||
void processStorageGetKey(const QJsonObject &args);
|
||||
void processStorageClear(const QJsonObject &args);
|
||||
void processButtonMessage(
|
||||
std::unique_ptr<Button> &button,
|
||||
const QJsonObject &args);
|
||||
|
@ -188,6 +197,12 @@ private:
|
|||
void replyCustomMethod(QJsonValue requestId, QJsonObject response);
|
||||
void requestClipboardText(const QJsonObject &args);
|
||||
void setupClosingBehaviour(const QJsonObject &args);
|
||||
void replyDeviceStorage(
|
||||
const QJsonObject &args,
|
||||
const QString &event,
|
||||
QJsonObject response);
|
||||
void deviceStorageFailed(const QJsonObject &args, QString error);
|
||||
void secureStorageFailed(const QJsonObject &args);
|
||||
void createButton(std::unique_ptr<Button> &button);
|
||||
void scheduleCloseWithConfirmation();
|
||||
void closeWithConfirmation();
|
||||
|
|
Loading…
Add table
Reference in a new issue