mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
remembering last used inline bots, showing them in mentions dropdown, 9015006 beta
This commit is contained in:
parent
85f46cef8c
commit
cb2df51af6
15 changed files with 308 additions and 198 deletions
|
@ -705,10 +705,11 @@ void Application::checkMapVersion() {
|
|||
if (Local::oldMapVersion() < AppVersion) {
|
||||
if (Local::oldMapVersion()) {
|
||||
QString versionFeatures;
|
||||
if (cDevVersion() && Local::oldMapVersion() < 9014) {
|
||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Sticker management: manually rearrange your sticker packs, pack order is now synced across all your devices\n\xe2\x80\x94 Click and hold on a sticker to preview it before sending\n\xe2\x80\x94 New context menu for chats in chats list\n\xe2\x80\x94 Support for all existing emoji");// .replace('@', qsl("@") + QChar(0x200D));
|
||||
if (cDevVersion() && Local::oldMapVersion() < 9016) {
|
||||
// versionFeatures = QString::fromUtf8("\xe2\x80\x94 Sticker management: manually rearrange your sticker packs, pack order is now synced across all your devices\n\xe2\x80\x94 Click and hold on a sticker to preview it before sending\n\xe2\x80\x94 New context menu for chats in chats list\n\xe2\x80\x94 Support for all existing emoji");// .replace('@', qsl("@") + QChar(0x200D));
|
||||
versionFeatures = lng_new_version_text(lt_gifs_link, qsl("https://telegram.org/blog/gif-revolution"), lt_bots_link, qsl("https://telegram.org/blog/inline-bots")).trimmed();
|
||||
} else if (Local::oldMapVersion() < 9015) {
|
||||
// versionFeatures = lang(lng_new_version_text).trimmed();
|
||||
versionFeatures = lng_new_version_text(lt_gifs_link, qsl("https://telegram.org/blog/gif-revolution"), lt_bots_link, qsl("https://telegram.org/blog/inline-bots")).trimmed();
|
||||
} else {
|
||||
versionFeatures = lang(lng_new_version_minor).trimmed();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org
|
|||
static const int32 AppVersion = 9015;
|
||||
static const wchar_t *AppVersionStr = L"0.9.15";
|
||||
static const bool DevVersion = false;
|
||||
#define BETA_VERSION (9015005ULL) // just comment this line to build public version
|
||||
#define BETA_VERSION (9015006ULL) // just comment this line to build public version
|
||||
|
||||
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
||||
static const wchar_t *AppName = L"Telegram Desktop";
|
||||
|
@ -87,6 +87,7 @@ enum {
|
|||
AverageGifSize = 320 * 240,
|
||||
WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it
|
||||
InlineBotRequestDelay = 400, // wait 400ms before context bot realtime request
|
||||
RecentInlineBotsLimit = 10,
|
||||
|
||||
AVBlockSize = 4096, // 4Kb for ffmpeg blocksize
|
||||
|
||||
|
|
|
@ -872,7 +872,7 @@ void DialogsInner::onHashtagFilterUpdate(QStringRef newFilter) {
|
|||
}
|
||||
_hashtagFilter = newFilter.toString();
|
||||
if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) {
|
||||
Local::readRecentHashtags();
|
||||
Local::readRecentHashtagsAndBots();
|
||||
}
|
||||
const RecentHashtagPack &recent(cRecentSearchHashtags());
|
||||
_hashtagResults.clear();
|
||||
|
@ -1434,7 +1434,7 @@ bool DialogsInner::choosePeer() {
|
|||
}
|
||||
}
|
||||
cSetRecentSearchHashtags(recent);
|
||||
Local::writeRecentHashtags();
|
||||
Local::writeRecentHashtagsAndBots();
|
||||
emit refreshHashtags();
|
||||
|
||||
selByMouse = true;
|
||||
|
@ -1487,7 +1487,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
|
|||
}
|
||||
}
|
||||
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
|
||||
Local::readRecentHashtags();
|
||||
Local::readRecentHashtagsAndBots();
|
||||
recent = cRecentSearchHashtags();
|
||||
}
|
||||
found = true;
|
||||
|
@ -1495,7 +1495,7 @@ void DialogsInner::saveRecentHashtags(const QString &text) {
|
|||
}
|
||||
if (found) {
|
||||
cSetRecentSearchHashtags(recent);
|
||||
Local::writeRecentHashtags();
|
||||
Local::writeRecentHashtagsAndBots();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3766,7 +3766,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) {
|
|||
if (selected) {
|
||||
p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver->b);
|
||||
int skip = (st::mentionHeight - st::notifyClose.icon.pxHeight()) / 2;
|
||||
if (!_hrows->isEmpty()) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
|
||||
if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) p.drawPixmap(QPoint(width() - st::notifyClose.icon.pxWidth() - skip, i * st::mentionHeight + skip), App::sprite(), st::notifyClose.icon);
|
||||
}
|
||||
p.setPen(st::black->p);
|
||||
if (!_mrows->isEmpty()) {
|
||||
|
@ -3902,6 +3902,10 @@ bool MentionsInner::select() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void MentionsInner::setRecentInlineBotsInRows(int32 bots) {
|
||||
_recentInlineBotsInRows = bots;
|
||||
}
|
||||
|
||||
QString MentionsInner::getSelected() const {
|
||||
int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size());
|
||||
if (_sel >= 0 && _sel < maxSel) {
|
||||
|
@ -3930,20 +3934,32 @@ void MentionsInner::mousePressEvent(QMouseEvent *e) {
|
|||
_mouseSel = true;
|
||||
onUpdateSelected(true);
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
if (_overDelete && _sel >= 0 && _sel < _hrows->size()) {
|
||||
if (_overDelete && _sel >= 0 && _sel < (_mrows->isEmpty() ? _hrows->size() : _recentInlineBotsInRows)) {
|
||||
_mousePos = mapToGlobal(e->pos());
|
||||
|
||||
bool removed = false;
|
||||
if (_mrows->isEmpty()) {
|
||||
QString toRemove = _hrows->at(_sel);
|
||||
RecentHashtagPack recent(cRecentWriteHashtags());
|
||||
RecentHashtagPack &recent(cRefRecentWriteHashtags());
|
||||
for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) {
|
||||
if (i->first == toRemove) {
|
||||
i = recent.erase(i);
|
||||
removed = true;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
cSetRecentWriteHashtags(recent);
|
||||
Local::writeRecentHashtags();
|
||||
} else {
|
||||
UserData *toRemove = _mrows->at(_sel);
|
||||
RecentInlineBots &recent(cRefRecentInlineBots());
|
||||
int32 index = recent.indexOf(toRemove);
|
||||
if (index >= 0) {
|
||||
recent.remove(index);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
Local::writeRecentHashtagsAndBots();
|
||||
}
|
||||
_parent->updateFiltered();
|
||||
|
||||
_mouseSel = true;
|
||||
|
@ -3980,8 +3996,8 @@ void MentionsInner::onUpdateSelected(bool force) {
|
|||
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
|
||||
|
||||
int w = width(), mouseY = mouse.y();
|
||||
_overDelete = _mrows->isEmpty() && (mouse.x() >= w - st::mentionHeight);
|
||||
int32 sel = mouseY / int32(st::mentionHeight), maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size();
|
||||
_overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= w - st::mentionHeight) : false;
|
||||
if (sel < 0 || sel >= maxSel) {
|
||||
sel = -1;
|
||||
}
|
||||
|
@ -4043,15 +4059,23 @@ void MentionsDropdown::paintEvent(QPaintEvent *e) {
|
|||
|
||||
}
|
||||
|
||||
void MentionsDropdown::showFiltered(PeerData *peer, QString start) {
|
||||
void MentionsDropdown::showFiltered(PeerData *peer, QString query, bool start) {
|
||||
if (query.isEmpty() || (peer->isUser() && query.at(0) == '@' && (!start || cRecentInlineBots().isEmpty()))) {
|
||||
if (!isHidden()) {
|
||||
hideStart();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_chat = peer->asChat();
|
||||
_user = peer->asUser();
|
||||
_channel = peer->asChannel();
|
||||
start = start.toLower();
|
||||
bool toDown = (_filter != start);
|
||||
query = query.toLower();
|
||||
bool toDown = (_filter != query);
|
||||
if (toDown) {
|
||||
_filter = start;
|
||||
_filter = query;
|
||||
}
|
||||
_addInlineBots = start;
|
||||
|
||||
updateFiltered(toDown);
|
||||
}
|
||||
|
@ -4063,13 +4087,34 @@ bool MentionsDropdown::clearFilteredBotCommands() {
|
|||
}
|
||||
|
||||
void MentionsDropdown::updateFiltered(bool toDown) {
|
||||
int32 now = unixtime();
|
||||
MentionRows rows;
|
||||
int32 now = unixtime(), recentInlineBots = 0;
|
||||
MentionRows mrows;
|
||||
HashtagRows hrows;
|
||||
BotCommandRows brows;
|
||||
if (_filter.at(0) == '@') {
|
||||
if (_chat) {
|
||||
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
|
||||
} else if (_channel && _channel->isMegagroup()) {
|
||||
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
|
||||
} else {
|
||||
mrows.reserve((_addInlineBots ? cRecentInlineBots().size() : 0) + _channel->mgInfo->lastParticipants.size());
|
||||
}
|
||||
} else if (_addInlineBots) {
|
||||
mrows.reserve(cRecentInlineBots().size());
|
||||
}
|
||||
if (_addInlineBots) {
|
||||
for (RecentInlineBots::const_iterator i = cRecentInlineBots().cbegin(), e = cRecentInlineBots().cend(); i != e; ++i) {
|
||||
UserData *user = *i;
|
||||
if (user->username.isEmpty()) continue;
|
||||
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
|
||||
mrows.push_back(user);
|
||||
++recentInlineBots;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_filter.at(0) == '@' && _chat) {
|
||||
QMultiMap<int32, UserData*> ordered;
|
||||
rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
|
||||
mrows.reserve(mrows.size() + (_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size()));
|
||||
if (_chat->noParticipantInfo()) {
|
||||
if (App::api()) App::api()->requestFullPeer(_chat);
|
||||
} else if (!_chat->participants.isEmpty()) {
|
||||
|
@ -4084,7 +4129,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
|
|||
UserData *user = *i;
|
||||
if (user->username.isEmpty()) continue;
|
||||
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
|
||||
rows.push_back(user);
|
||||
mrows.push_back(user);
|
||||
if (!ordered.isEmpty()) {
|
||||
ordered.remove(App::onlineForSort(user, now), user);
|
||||
}
|
||||
|
@ -4092,7 +4137,7 @@ void MentionsDropdown::updateFiltered(bool toDown) {
|
|||
if (!ordered.isEmpty()) {
|
||||
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
|
||||
--i;
|
||||
rows.push_back(i.value());
|
||||
mrows.push_back(i.value());
|
||||
}
|
||||
}
|
||||
} else if (_filter.at(0) == '@' && _channel && _channel->isMegagroup()) {
|
||||
|
@ -4100,12 +4145,12 @@ void MentionsDropdown::updateFiltered(bool toDown) {
|
|||
if (_channel->mgInfo->lastParticipants.isEmpty() || _channel->lastParticipantsCountOutdated()) {
|
||||
if (App::api()) App::api()->requestLastParticipants(_channel);
|
||||
} else {
|
||||
rows.reserve(_channel->mgInfo->lastParticipants.size());
|
||||
mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size());
|
||||
for (MegagroupInfo::LastParticipants::const_iterator i = _channel->mgInfo->lastParticipants.cbegin(), e = _channel->mgInfo->lastParticipants.cend(); i != e; ++i) {
|
||||
UserData *user = *i;
|
||||
if (user->username.isEmpty()) continue;
|
||||
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
|
||||
rows.push_back(user);
|
||||
mrows.push_back(user);
|
||||
}
|
||||
}
|
||||
} else if (_filter.at(0) == '#') {
|
||||
|
@ -4184,7 +4229,8 @@ void MentionsDropdown::updateFiltered(bool toDown) {
|
|||
}
|
||||
}
|
||||
}
|
||||
rowsUpdated(rows, hrows, brows, toDown);
|
||||
rowsUpdated(mrows, hrows, brows, toDown);
|
||||
_inner.setRecentInlineBotsInRows(recentInlineBots);
|
||||
}
|
||||
|
||||
void MentionsDropdown::rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown) {
|
||||
|
|
|
@ -736,6 +736,8 @@ public:
|
|||
bool moveSel(int direction);
|
||||
bool select();
|
||||
|
||||
void setRecentInlineBotsInRows(int32 bots);
|
||||
|
||||
QString getSelected() const;
|
||||
|
||||
signals:
|
||||
|
@ -756,6 +758,7 @@ private:
|
|||
MentionRows *_mrows;
|
||||
HashtagRows *_hrows;
|
||||
BotCommandRows *_brows;
|
||||
int32 _recentInlineBotsInRows;
|
||||
int32 _sel;
|
||||
bool _mouseSel;
|
||||
QPoint _mousePos;
|
||||
|
@ -775,7 +778,7 @@ public:
|
|||
void fastHide();
|
||||
|
||||
bool clearFilteredBotCommands();
|
||||
void showFiltered(PeerData *peer, QString start);
|
||||
void showFiltered(PeerData *peer, QString query, bool start);
|
||||
void updateFiltered(bool toDown = false);
|
||||
void setBoundings(QRect boundings);
|
||||
|
||||
|
@ -820,7 +823,7 @@ private:
|
|||
HashtagRows _hrows;
|
||||
BotCommandRows _brows;
|
||||
|
||||
void rowsUpdated(const MentionRows &rows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown);
|
||||
void rowsUpdated(const MentionRows &mrows, const HashtagRows &hrows, const BotCommandRows &brows, bool toDown);
|
||||
|
||||
ScrollArea _scroll;
|
||||
MentionsInner _inner;
|
||||
|
@ -830,6 +833,7 @@ private:
|
|||
ChannelData *_channel;
|
||||
QString _filter;
|
||||
QRect _boundings;
|
||||
bool _addInlineBots;
|
||||
|
||||
int32 _width, _height;
|
||||
bool _hiding;
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace Ui {
|
|||
|
||||
void showPeerHistoryAsync(const PeerId &peer, MsgId msgId) {
|
||||
if (MainWidget *m = App::main()) {
|
||||
QMetaObject::invokeMethod(m, SLOT(ui_showPeerHistoryAsync(quint64,qint32)), Qt::QueuedConnection, Q_ARG(quint64, peer), Q_ARG(qint32, msgId));
|
||||
QMetaObject::invokeMethod(m, "ui_showPeerHistoryAsync", Qt::QueuedConnection, Q_ARG(quint64, peer), Q_ARG(qint32, msgId));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -270,7 +270,9 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&inlineBot, QString &inlineBotUsername) const {
|
||||
QString FlatTextarea::getMentionHashtagBotCommandPart(bool &start, UserData *&inlineBot, QString &inlineBotUsername) const {
|
||||
start = false;
|
||||
|
||||
// check inline bot query
|
||||
const QString &text(getLastText());
|
||||
int32 inlineUsernameStart = 1, inlineUsernameLength = 0, size = text.size();
|
||||
|
@ -301,13 +303,12 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i
|
|||
inlineBot = InlineBotLookingUpData;
|
||||
}
|
||||
}
|
||||
if (inlineBot == InlineBotLookingUpData) return;
|
||||
if (inlineBot == InlineBotLookingUpData) return QString();
|
||||
|
||||
if (inlineBot && (!inlineBot->botInfo || inlineBot->botInfo->inlinePlaceholder.isEmpty())) {
|
||||
inlineBot = 0;
|
||||
} else {
|
||||
start = text.mid(inlineUsernameStart + inlineUsernameLength + 1);
|
||||
return;
|
||||
return text.mid(inlineUsernameStart + inlineUsernameLength + 1);
|
||||
}
|
||||
} else {
|
||||
inlineUsernameLength = 0;
|
||||
|
@ -319,7 +320,7 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i
|
|||
}
|
||||
|
||||
int32 pos = textCursor().position();
|
||||
if (textCursor().anchor() != pos) return;
|
||||
if (textCursor().anchor() != pos) return QString();
|
||||
|
||||
// check mention / hashtag / bot command
|
||||
QTextDocument *doc(document());
|
||||
|
@ -339,29 +340,33 @@ void FlatTextarea::getMentionHashtagBotCommandStart(QString &start, UserData *&i
|
|||
for (int i = pos - p; i > 0; --i) {
|
||||
if (t.at(i - 1) == '@') {
|
||||
if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
|
||||
start = t.mid(i - 1, pos - p - i + 1);
|
||||
start = (i == 1) && (p == 0);
|
||||
return t.mid(i - 1, pos - p - i + 1);
|
||||
} else if ((pos - p - i < 1 || t.at(i).isLetter()) && i > 2 && (t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_') && !mentionInCommand) {
|
||||
mentionInCommand = true;
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
return QString();
|
||||
} else if (t.at(i - 1) == '#') {
|
||||
if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) {
|
||||
start = t.mid(i - 1, pos - p - i + 1);
|
||||
start = (i == 1) && (p == 0);
|
||||
return t.mid(i - 1, pos - p - i + 1);
|
||||
}
|
||||
return;
|
||||
return QString();
|
||||
} else if (t.at(i - 1) == '/') {
|
||||
if (i < 2) {
|
||||
start = t.mid(i - 1, pos - p - i + 1);
|
||||
start = (i == 1) && (p == 0);
|
||||
return t.mid(i - 1, pos - p - i + 1);
|
||||
}
|
||||
return;
|
||||
return QString();
|
||||
}
|
||||
if (pos - p - i > 127 || (!mentionInCommand && (pos - p - i > 63))) break;
|
||||
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void FlatTextarea::onMentionHashtagOrBotCommandInsert(QString str) {
|
||||
|
|
|
@ -64,7 +64,7 @@ public:
|
|||
QSize minimumSizeHint() const;
|
||||
|
||||
EmojiPtr getSingleEmoji() const;
|
||||
void getMentionHashtagBotCommandStart(QString &start, UserData *&contextBot, QString &contextBotUsername) const;
|
||||
QString getMentionHashtagBotCommandPart(bool &start, UserData *&contextBot, QString &contextBotUsername) const;
|
||||
void removeSingleEmoji();
|
||||
bool hasText() const;
|
||||
|
||||
|
|
|
@ -5338,8 +5338,9 @@ void HistoryWidget::onCheckMentionDropdown() {
|
|||
if (!_history || _a_show.animating()) return;
|
||||
|
||||
UserData *bot = _inlineBot;
|
||||
QString start, inlineBotUsername(_inlineBotUsername);
|
||||
_field.getMentionHashtagBotCommandStart(start, _inlineBot, _inlineBotUsername);
|
||||
bool start = false;
|
||||
QString inlineBotUsername(_inlineBotUsername);
|
||||
QString query = _field.getMentionHashtagBotCommandPart(start, _inlineBot, _inlineBotUsername);
|
||||
if (inlineBotUsername != _inlineBotUsername) {
|
||||
if (_inlineBotResolveRequestId) {
|
||||
Notify::inlineBotRequesting(false);
|
||||
|
@ -5359,10 +5360,10 @@ void HistoryWidget::onCheckMentionDropdown() {
|
|||
if (_inlineBot != bot) {
|
||||
updateFieldPlaceholder();
|
||||
}
|
||||
if (_inlineBot->username == (cTestMode() ? qstr("contextbot") : qstr("gif")) && start.isEmpty()) {
|
||||
if (_inlineBot->username == (cTestMode() ? qstr("contextbot") : qstr("gif")) && query.isEmpty()) {
|
||||
_emojiPan.clearInlineBot();
|
||||
} else {
|
||||
_emojiPan.queryInlineBot(_inlineBot, start);
|
||||
_emojiPan.queryInlineBot(_inlineBot, query);
|
||||
}
|
||||
if (!_attachMention.isHidden()) {
|
||||
_attachMention.hideStart();
|
||||
|
@ -5373,16 +5374,12 @@ void HistoryWidget::onCheckMentionDropdown() {
|
|||
_field.finishPlaceholder();
|
||||
}
|
||||
_emojiPan.clearInlineBot();
|
||||
if (!start.isEmpty()) {
|
||||
if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags();
|
||||
if (start.at(0) == '@' && _peer->isUser()) return;
|
||||
if (start.at(0) == '/' && _peer->isUser() && !_peer->asUser()->botInfo) return;
|
||||
_attachMention.showFiltered(_peer, start);
|
||||
} else {
|
||||
if (!_attachMention.isHidden()) {
|
||||
_attachMention.hideStart();
|
||||
}
|
||||
if (!query.isEmpty()) {
|
||||
if (query.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtagsAndBots();
|
||||
if (query.at(0) == '@' && cRecentInlineBots().isEmpty()) Local::readRecentHashtagsAndBots();
|
||||
if (query.at(0) == '/' && _peer->isUser() && !_peer->asUser()->botInfo) return;
|
||||
}
|
||||
_attachMention.showFiltered(_peer, query, start);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6428,6 +6425,18 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
|
|||
_saveDraftStart = getms();
|
||||
onDraftSave();
|
||||
|
||||
RecentInlineBots &bots(cRefRecentInlineBots());
|
||||
int32 index = bots.indexOf(bot);
|
||||
if (index) {
|
||||
if (index > 0) {
|
||||
bots.removeAt(index);
|
||||
} else if (bots.size() >= RecentInlineBotsLimit) {
|
||||
bots.resize(RecentInlineBotsLimit - 1);
|
||||
}
|
||||
bots.push_front(bot);
|
||||
Local::writeRecentHashtagsAndBots();
|
||||
}
|
||||
|
||||
onCheckMentionDropdown();
|
||||
if (!_attachType.isHidden()) _attachType.hideStart();
|
||||
|
||||
|
|
|
@ -548,12 +548,12 @@ namespace {
|
|||
lskRecentStickersOld = 0x07, // no data
|
||||
lskBackground = 0x08, // no data
|
||||
lskUserSettings = 0x09, // no data
|
||||
lskRecentHashtags = 0x0a, // no data
|
||||
lskRecentHashtagsAndBots = 0x0a, // no data
|
||||
lskStickers = 0x0b, // no data
|
||||
lskSavedPeers = 0x0c, // no data
|
||||
lskReportSpamStatuses = 0x0d, // no data
|
||||
lskSavedGifsOld = 0x0e,
|
||||
lskSavedGifs = 0x0f,
|
||||
lskSavedGifsOld = 0x0e, // no data
|
||||
lskSavedGifs = 0x0f, // no data
|
||||
};
|
||||
|
||||
typedef QMap<PeerId, FileKey> DraftsMap;
|
||||
|
@ -581,8 +581,8 @@ namespace {
|
|||
bool _backgroundWasRead = false;
|
||||
|
||||
FileKey _userSettingsKey = 0;
|
||||
FileKey _recentHashtagsKey = 0;
|
||||
bool _recentHashtagsWereRead = false;
|
||||
FileKey _recentHashtagsAndBotsKey = 0;
|
||||
bool _recentHashtagsAndBotsWereRead = false;
|
||||
|
||||
FileKey _savedPeersKey = 0;
|
||||
|
||||
|
@ -726,6 +726,7 @@ namespace {
|
|||
_fileLocationAliases.insert(MediaKey(kfirst, ksecond), MediaKey(vfirst, vsecond));
|
||||
}
|
||||
|
||||
if (!locations.stream.atEnd()) {
|
||||
_storageWebFilesSize = 0;
|
||||
_webFilesMap.clear();
|
||||
|
||||
|
@ -741,6 +742,7 @@ namespace {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _writeReportSpamStatuses() {
|
||||
if (!_working()) return;
|
||||
|
@ -1667,7 +1669,7 @@ namespace {
|
|||
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
|
||||
quint64 locationsKey = 0, reportSpamStatusesKey = 0;
|
||||
quint64 recentStickersKeyOld = 0, stickersKey = 0, savedGifsKey = 0;
|
||||
quint64 backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0, savedPeersKey = 0;
|
||||
quint64 backgroundKey = 0, userSettingsKey = 0, recentHashtagsAndBotsKey = 0, savedPeersKey = 0;
|
||||
while (!map.stream.atEnd()) {
|
||||
quint32 keyType;
|
||||
map.stream >> keyType;
|
||||
|
@ -1744,8 +1746,8 @@ namespace {
|
|||
case lskUserSettings: {
|
||||
map.stream >> userSettingsKey;
|
||||
} break;
|
||||
case lskRecentHashtags: {
|
||||
map.stream >> recentHashtagsKey;
|
||||
case lskRecentHashtagsAndBots: {
|
||||
map.stream >> recentHashtagsAndBotsKey;
|
||||
} break;
|
||||
case lskStickers: {
|
||||
map.stream >> stickersKey;
|
||||
|
@ -1788,7 +1790,7 @@ namespace {
|
|||
_savedPeersKey = savedPeersKey;
|
||||
_backgroundKey = backgroundKey;
|
||||
_userSettingsKey = userSettingsKey;
|
||||
_recentHashtagsKey = recentHashtagsKey;
|
||||
_recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
|
||||
_oldMapVersion = mapData.version;
|
||||
if (_oldMapVersion < AppVersion) {
|
||||
_mapChanged = true;
|
||||
|
@ -1861,7 +1863,7 @@ namespace {
|
|||
if (_savedPeersKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentHashtagsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||
EncryptedDescriptor mapData(mapSize);
|
||||
if (!_draftsMap.isEmpty()) {
|
||||
mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
|
||||
|
@ -1917,8 +1919,8 @@ namespace {
|
|||
if (_userSettingsKey) {
|
||||
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
|
||||
}
|
||||
if (_recentHashtagsKey) {
|
||||
mapData.stream << quint32(lskRecentHashtags) << quint64(_recentHashtagsKey);
|
||||
if (_recentHashtagsAndBotsKey) {
|
||||
mapData.stream << quint32(lskRecentHashtagsAndBots) << quint64(_recentHashtagsAndBotsKey);
|
||||
}
|
||||
map.writeEncrypted(mapData);
|
||||
|
||||
|
@ -2187,7 +2189,7 @@ namespace Local {
|
|||
_storageWebFilesSize = 0;
|
||||
_locationsKey = _reportSpamStatusesKey = 0;
|
||||
_recentStickersKeyOld = _stickersKey = _savedGifsKey = 0;
|
||||
_backgroundKey = _userSettingsKey = _recentHashtagsKey = _savedPeersKey = 0;
|
||||
_backgroundKey = _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = 0;
|
||||
_oldMapVersion = _oldSettingsVersion = 0;
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapNow);
|
||||
|
@ -3258,89 +3260,6 @@ namespace Local {
|
|||
return false;
|
||||
}
|
||||
|
||||
void writeRecentHashtags() {
|
||||
if (!_working()) return;
|
||||
|
||||
const RecentHashtagPack &write(cRecentWriteHashtags()), &search(cRecentSearchHashtags());
|
||||
if (write.isEmpty() && search.isEmpty()) readRecentHashtags();
|
||||
if (write.isEmpty() && search.isEmpty()) {
|
||||
if (_recentHashtagsKey) {
|
||||
clearKey(_recentHashtagsKey);
|
||||
_recentHashtagsKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
_writeMap();
|
||||
} else {
|
||||
if (!_recentHashtagsKey) {
|
||||
_recentHashtagsKey = genKey();
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapFast);
|
||||
}
|
||||
quint32 size = sizeof(quint32) * 2, writeCnt = 0, searchCnt = 0;
|
||||
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
|
||||
if (!i->first.isEmpty()) {
|
||||
size += _stringSize(i->first) + sizeof(quint16);
|
||||
++writeCnt;
|
||||
}
|
||||
}
|
||||
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
|
||||
if (!i->first.isEmpty()) {
|
||||
size += _stringSize(i->first) + sizeof(quint16);
|
||||
++searchCnt;
|
||||
}
|
||||
}
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << quint32(writeCnt) << quint32(searchCnt);
|
||||
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
|
||||
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
|
||||
}
|
||||
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
|
||||
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
|
||||
}
|
||||
FileWriteDescriptor file(_recentHashtagsKey);
|
||||
file.writeEncrypted(data);
|
||||
}
|
||||
}
|
||||
|
||||
void readRecentHashtags() {
|
||||
if (_recentHashtagsWereRead) return;
|
||||
_recentHashtagsWereRead = true;
|
||||
|
||||
if (!_recentHashtagsKey) return;
|
||||
|
||||
FileReadDescriptor hashtags;
|
||||
if (!readEncryptedFile(hashtags, _recentHashtagsKey)) {
|
||||
clearKey(_recentHashtagsKey);
|
||||
_recentHashtagsKey = 0;
|
||||
_writeMap();
|
||||
return;
|
||||
}
|
||||
|
||||
quint32 writeCount = 0, searchCount = 0;
|
||||
hashtags.stream >> writeCount >> searchCount;
|
||||
|
||||
QString tag;
|
||||
quint16 count;
|
||||
|
||||
RecentHashtagPack write, search;
|
||||
if (writeCount) {
|
||||
write.reserve(writeCount);
|
||||
for (uint32 i = 0; i < writeCount; ++i) {
|
||||
hashtags.stream >> tag >> count;
|
||||
write.push_back(qMakePair(tag.trimmed(), count));
|
||||
}
|
||||
}
|
||||
if (searchCount) {
|
||||
search.reserve(searchCount);
|
||||
for (uint32 i = 0; i < searchCount; ++i) {
|
||||
hashtags.stream >> tag >> count;
|
||||
search.push_back(qMakePair(tag.trimmed(), count));
|
||||
}
|
||||
}
|
||||
cSetRecentWriteHashtags(write);
|
||||
cSetRecentSearchHashtags(search);
|
||||
}
|
||||
|
||||
uint32 _peerSize(PeerData *peer) {
|
||||
uint32 result = sizeof(quint64) + sizeof(quint64) + _storageImageLocationSize();
|
||||
if (peer->isUser()) {
|
||||
|
@ -3370,7 +3289,7 @@ namespace Local {
|
|||
return result;
|
||||
}
|
||||
|
||||
void _writePeer(QDataStream &stream, PeerData *peer) {
|
||||
void _writePeer(QDataStream &stream, PeerData *peer, int32 fileVersion = AppVersion) {
|
||||
stream << quint64(peer->id) << quint64(peer->photoId);
|
||||
_writeStorageImageLocation(stream, peer->photoLoc);
|
||||
if (peer->isUser()) {
|
||||
|
@ -3380,6 +3299,9 @@ namespace Local {
|
|||
if (AppVersion >= 9012) {
|
||||
stream << qint32(user->flags);
|
||||
}
|
||||
if (AppVersion >= 9016 || fileVersion >= 9016) {
|
||||
stream << (user->botInfo ? user->botInfo->inlinePlaceholder : QString());
|
||||
}
|
||||
stream << qint32(user->onlineTill) << qint32(user->contact) << qint32(user->botInfo ? user->botInfo->version : -1);
|
||||
} else if (peer->isChat()) {
|
||||
ChatData *chat = peer->asChat();
|
||||
|
@ -3396,25 +3318,31 @@ namespace Local {
|
|||
}
|
||||
}
|
||||
|
||||
PeerData *_readPeer(FileReadDescriptor &from) {
|
||||
PeerData *_readPeer(FileReadDescriptor &from, int32 fileVersion = 0) {
|
||||
PeerData *result = 0;
|
||||
quint64 peerId = 0, photoId = 0;
|
||||
from.stream >> peerId >> photoId;
|
||||
|
||||
StorageImageLocation photoLoc(_readStorageImageLocation(from));
|
||||
|
||||
result = App::peerLoaded(peerId);
|
||||
if (result && result->loaded) return result;
|
||||
|
||||
result = App::peer(peerId);
|
||||
result->loaded = true;
|
||||
if (result->isUser()) {
|
||||
UserData *user = result->asUser();
|
||||
|
||||
QString first, last, phone, username;
|
||||
QString first, last, phone, username, inlinePlaceholder;
|
||||
quint64 access;
|
||||
qint32 flags = 0, onlineTill, contact, botInfoVersion;
|
||||
from.stream >> first >> last >> phone >> username >> access;
|
||||
if (from.version >= 9012) {
|
||||
from.stream >> flags;
|
||||
}
|
||||
if (from.version >= 9016 || fileVersion >= 9016) {
|
||||
from.stream >> inlinePlaceholder;
|
||||
}
|
||||
from.stream >> onlineTill >> contact >> botInfoVersion;
|
||||
|
||||
bool showPhone = !isServiceUser(user->id) && (peerToUser(user->id) != MTP::authedId()) && (contact <= 0);
|
||||
|
@ -3427,6 +3355,9 @@ namespace Local {
|
|||
user->onlineTill = onlineTill;
|
||||
user->contact = contact;
|
||||
user->setBotInfoVersion(botInfoVersion);
|
||||
if (!inlinePlaceholder.isEmpty() && user->botInfo) {
|
||||
user->botInfo->inlinePlaceholder = inlinePlaceholder;
|
||||
}
|
||||
|
||||
if (peerToUser(user->id) == MTP::authedId()) {
|
||||
user->input = MTP_inputPeerSelf();
|
||||
|
@ -3489,6 +3420,113 @@ namespace Local {
|
|||
return result;
|
||||
}
|
||||
|
||||
void writeRecentHashtagsAndBots() {
|
||||
if (!_working()) return;
|
||||
|
||||
const RecentHashtagPack &write(cRecentWriteHashtags()), &search(cRecentSearchHashtags());
|
||||
const RecentInlineBots &bots(cRecentInlineBots());
|
||||
if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) readRecentHashtagsAndBots();
|
||||
if (write.isEmpty() && search.isEmpty() && bots.isEmpty()) {
|
||||
if (_recentHashtagsAndBotsKey) {
|
||||
clearKey(_recentHashtagsAndBotsKey);
|
||||
_recentHashtagsAndBotsKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
_writeMap();
|
||||
} else {
|
||||
if (!_recentHashtagsAndBotsKey) {
|
||||
_recentHashtagsAndBotsKey = genKey();
|
||||
_mapChanged = true;
|
||||
_writeMap(WriteMapFast);
|
||||
}
|
||||
quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = cRecentInlineBots().size();
|
||||
for (RecentHashtagPack::const_iterator i = write.cbegin(), e = write.cend(); i != e; ++i) {
|
||||
if (!i->first.isEmpty()) {
|
||||
size += _stringSize(i->first) + sizeof(quint16);
|
||||
++writeCnt;
|
||||
}
|
||||
}
|
||||
for (RecentHashtagPack::const_iterator i = search.cbegin(), e = search.cend(); i != e; ++i) {
|
||||
if (!i->first.isEmpty()) {
|
||||
size += _stringSize(i->first) + sizeof(quint16);
|
||||
++searchCnt;
|
||||
}
|
||||
}
|
||||
for (RecentInlineBots::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
|
||||
size += _peerSize(*i);
|
||||
}
|
||||
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << quint32(writeCnt) << quint32(searchCnt);
|
||||
for (RecentHashtagPack::const_iterator i = write.cbegin(), e = write.cend(); i != e; ++i) {
|
||||
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
|
||||
}
|
||||
for (RecentHashtagPack::const_iterator i = search.cbegin(), e = search.cend(); i != e; ++i) {
|
||||
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
|
||||
}
|
||||
data.stream << quint32(botsCnt);
|
||||
for (RecentInlineBots::const_iterator i = bots.cbegin(), e = bots.cend(); i != e; ++i) {
|
||||
_writePeer(data.stream, *i, 9016);
|
||||
}
|
||||
FileWriteDescriptor file(_recentHashtagsAndBotsKey);
|
||||
file.writeEncrypted(data);
|
||||
}
|
||||
}
|
||||
|
||||
void readRecentHashtagsAndBots() {
|
||||
if (_recentHashtagsAndBotsWereRead) return;
|
||||
_recentHashtagsAndBotsWereRead = true;
|
||||
|
||||
if (!_recentHashtagsAndBotsKey) return;
|
||||
|
||||
FileReadDescriptor hashtags;
|
||||
if (!readEncryptedFile(hashtags, _recentHashtagsAndBotsKey)) {
|
||||
clearKey(_recentHashtagsAndBotsKey);
|
||||
_recentHashtagsAndBotsKey = 0;
|
||||
_writeMap();
|
||||
return;
|
||||
}
|
||||
|
||||
quint32 writeCount = 0, searchCount = 0, botsCount = 0;
|
||||
hashtags.stream >> writeCount >> searchCount;
|
||||
|
||||
QString tag;
|
||||
quint16 count;
|
||||
|
||||
RecentHashtagPack write, search;
|
||||
RecentInlineBots bots;
|
||||
if (writeCount) {
|
||||
write.reserve(writeCount);
|
||||
for (uint32 i = 0; i < writeCount; ++i) {
|
||||
hashtags.stream >> tag >> count;
|
||||
write.push_back(qMakePair(tag.trimmed(), count));
|
||||
}
|
||||
}
|
||||
if (searchCount) {
|
||||
search.reserve(searchCount);
|
||||
for (uint32 i = 0; i < searchCount; ++i) {
|
||||
hashtags.stream >> tag >> count;
|
||||
search.push_back(qMakePair(tag.trimmed(), count));
|
||||
}
|
||||
}
|
||||
cSetRecentWriteHashtags(write);
|
||||
cSetRecentSearchHashtags(search);
|
||||
|
||||
if (!hashtags.stream.atEnd()) {
|
||||
hashtags.stream >> botsCount;
|
||||
if (botsCount) {
|
||||
bots.reserve(botsCount);
|
||||
for (uint32 i = 0; i < botsCount; ++i) {
|
||||
PeerData *peer = _readPeer(hashtags, 9016);
|
||||
if (peer && peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->inlinePlaceholder.isEmpty() && !peer->asUser()->username.isEmpty()) {
|
||||
bots.push_back(peer->asUser());
|
||||
}
|
||||
}
|
||||
}
|
||||
cSetRecentInlineBots(bots);
|
||||
}
|
||||
}
|
||||
|
||||
void writeSavedPeers() {
|
||||
if (!_working()) return;
|
||||
|
||||
|
@ -3651,8 +3689,8 @@ namespace Local {
|
|||
_stickersKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_recentHashtagsKey) {
|
||||
_recentHashtagsKey = 0;
|
||||
if (_recentHashtagsAndBotsKey) {
|
||||
_recentHashtagsAndBotsKey = 0;
|
||||
_mapChanged = true;
|
||||
}
|
||||
if (_savedPeersKey) {
|
||||
|
|
|
@ -157,8 +157,8 @@ namespace Local {
|
|||
void writeBackground(int32 id, const QImage &img);
|
||||
bool readBackground();
|
||||
|
||||
void writeRecentHashtags();
|
||||
void readRecentHashtags();
|
||||
void writeRecentHashtagsAndBots();
|
||||
void readRecentHashtagsAndBots();
|
||||
|
||||
void addSavedPeer(PeerData *peer, const QDateTime &position);
|
||||
void removeSavedPeer(PeerData *peer);
|
||||
|
|
|
@ -1349,7 +1349,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
|
|||
}
|
||||
}
|
||||
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
|
||||
Local::readRecentHashtags();
|
||||
Local::readRecentHashtagsAndBots();
|
||||
recent = cRecentWriteHashtags();
|
||||
}
|
||||
found = true;
|
||||
|
@ -1357,7 +1357,7 @@ void MainWidget::saveRecentHashtags(const QString &text) {
|
|||
}
|
||||
if (found) {
|
||||
cSetRecentWriteHashtags(recent);
|
||||
Local::writeRecentHashtags();
|
||||
Local::writeRecentHashtagsAndBots();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,6 +121,8 @@ int32 gSavedGifsLimit = 100;
|
|||
|
||||
RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags;
|
||||
|
||||
RecentInlineBots gRecentInlineBots;
|
||||
|
||||
bool gPasswordRecovered = false;
|
||||
int32 gPasscodeBadTries = 0;
|
||||
uint64 gPasscodeLastTry = 0;
|
||||
|
|
|
@ -230,9 +230,13 @@ DeclareSetting(bool, ShowingSavedGifs);
|
|||
DeclareSetting(int32, SavedGifsLimit);
|
||||
|
||||
typedef QList<QPair<QString, ushort> > RecentHashtagPack;
|
||||
DeclareSetting(RecentHashtagPack, RecentWriteHashtags);
|
||||
DeclareRefSetting(RecentHashtagPack, RecentWriteHashtags);
|
||||
DeclareSetting(RecentHashtagPack, RecentSearchHashtags);
|
||||
|
||||
class UserData;
|
||||
typedef QVector<UserData*> RecentInlineBots;
|
||||
DeclareRefSetting(RecentInlineBots, RecentInlineBots);
|
||||
|
||||
DeclareSetting(bool, PasswordRecovered);
|
||||
|
||||
DeclareSetting(int32, PasscodeBadTries);
|
||||
|
|
|
@ -34,8 +34,8 @@ IDI_ICON1 ICON "SourceFiles\\art\\icon256.ico"
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,9,15,5
|
||||
PRODUCTVERSION 0,9,15,5
|
||||
FILEVERSION 0,9,15,6
|
||||
PRODUCTVERSION 0,9,15,6
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -51,10 +51,10 @@ BEGIN
|
|||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileVersion", "0.9.15.5"
|
||||
VALUE "FileVersion", "0.9.15.6"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2013"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "0.9.15.5"
|
||||
VALUE "ProductVersion", "0.9.15.6"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
Loading…
Add table
Reference in a new issue