From 1ee8d134bcec5914a921be4ca49331cf79599cab Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Sat, 18 Nov 2023 15:45:22 +0200 Subject: [PATCH] Adapt to changes in TdLib (#524) * Adapt setTdlibParameters for TdLib > 1.8.5 For some reason tdlibParameters were inlined between 1.8.5 and 1.8.6 See https://github.com/tdlib/td/commit/f6a2ecd * sponsoredMessage => sponsoredMessages in TdLib 1.8.8 See https://github.com/tdlib/td/commit/ec1310a * Support another variant of messageReaction The reaction field has changed from string to ReactionType somewhere between 1.8.5 and 1.8.6 See https://github.com/tdlib/td/commit/b14708f * Add support for new message reactions API It has changed between 1.8.5 and 1.8.6 https://github.com/tdlib/td/commit/b14708f (ReactionType) https://github.com/tdlib/td/commit/0b8e143 (ChatAvailableReactions) https://github.com/tdlib/td/commit/6b2f6b4 (addMessageReaction) https://github.com/tdlib/td/commit/d29d367 (updateActiveEmojiReactions) etc. * Highlight chosen reaction * Support username in the new format username attribute has been replaced with usernames in 1.8.8 and now looks like this: "usernames": { "@type": "usernames", "active_usernames": [ "whatever" ], "disabled_usernames": [ ], "editable_username": "whatever" } See https://github.com/tdlib/td/commit/897032e * Support new reply_to message attribute Since 1.8.15 it replaces reply_to_message_id and reply_in_chat_id. Looks like this: "reply_to": { "@type": "messageReplyToMessage", "chat_id": -1001234567890, "is_quote_manual": false, "message_id": 234567890, "origin_send_date": 0 }, See https://github.com/tdlib/td/commit/6116573 * Added support for MessageOrigin values All of a sudden MessageForwardOrigin has been renamed into MessageOrigin in TdLib 1.8.20 just because why not: https://github.com/tdlib/td/commit/10c9e40 --- qml/components/MessageListViewItem.qml | 26 ++- qml/components/MessageOverlayFlickable.qml | 2 + .../ChatInformationTabItemMembersGroups.qml | 5 +- qml/pages/ChatPage.qml | 4 +- src/tdlibreceiver.cpp | 69 ++++++- src/tdlibreceiver.h | 3 + src/tdlibwrapper.cpp | 179 +++++++++++++----- src/tdlibwrapper.h | 8 +- 8 files changed, 235 insertions(+), 61 deletions(-) diff --git a/qml/components/MessageListViewItem.qml b/qml/components/MessageListViewItem.qml index 88ab301..b01a39a 100644 --- a/qml/components/MessageListViewItem.qml +++ b/qml/components/MessageListViewItem.qml @@ -64,6 +64,7 @@ ListItem { readonly property bool showForwardMessageMenuItem: (baseContextMenuItemCount + 2) <= maxContextMenuItemCount // And don't count "More Options..." for "Delete Message" if "Delete Message" is the only extra option readonly property bool haveSpaceForDeleteMessageMenuItem: (baseContextMenuItemCount + 3 - (deleteMessageIsOnlyExtraOption ? 1 : 0)) <= maxContextMenuItemCount + property var chatReactions property var messageReactions highlighted: (down || isSelected || additionalOptionsOpened) && !menuOpen @@ -94,15 +95,21 @@ ListItem { } } - function getInteractionText(viewCount, reactions) { + function getInteractionText(viewCount, reactions, size, highlightColor) { var interactionText = ""; if (viewCount > 0) { - interactionText = Emoji.emojify("👁️", Theme.fontSizeTiny) + Functions.getShortenedCount(viewCount); + interactionText = Emoji.emojify("👁️ ", size) + Functions.getShortenedCount(viewCount); } for (var i = 0; i < reactions.length; i++) { - interactionText += ( " " + Emoji.emojify(reactions[i].reaction, Theme.fontSizeTiny) ); - if (!chatPage.isPrivateChat) { - interactionText += ( " " + Functions.getShortenedCount(reactions[i].total_count) ); + var reaction = reactions[i] + var reactionText = reaction.reaction ? reaction.reaction : (reaction.type && reaction.type.emoji) ? reaction.type.emoji : "" + if (reactionText) { + interactionText += ( " " + Emoji.emojify(reactionText, size) ); + if (!chatPage.isPrivateChat) { + var count = Functions.getShortenedCount(reaction.total_count) + interactionText += " " + interactionText += (reaction.is_chosen ? ( "" + count + "" ) : count) + } } } return interactionText; @@ -125,6 +132,8 @@ ListItem { if (messageListItem.messageReactions) { messageListItem.messageReactions = null; + } else if (messageListItem.chatReactions) { + messageListItem.messageReactions = chatReactions } else { tdLibWrapper.getMessageAvailableReactions(messageListItem.chatId, messageListItem.messageId); } @@ -467,11 +476,12 @@ ListItem { width: parent.width Component.onCompleted: { - if (myMessage.forward_info.origin["@type"] === "messageForwardOriginChannel") { + var originType = myMessage.forward_info.origin["@type"] + if (originType === "messageOriginChannel" || originType === "messageForwardOriginChannel") { var otherChatInformation = tdLibWrapper.getChat(myMessage.forward_info.origin.chat_id); forwardedThumbnail.photoData = (typeof otherChatInformation.photo !== "undefined") ? otherChatInformation.photo.small : {}; forwardedChannelText.text = Emoji.emojify(otherChatInformation.title, Theme.fontSizeExtraSmall); - } else if (myMessage.forward_info.origin["@type"] === "messageForwardOriginUser") { + } else if (originType === "messageOriginUser" || originType === "messageForwardOriginUser") { var otherUserInformation = tdLibWrapper.getUserInformation(myMessage.forward_info.origin.sender_user_id); forwardedThumbnail.photoData = (typeof otherUserInformation.profile_photo !== "undefined") ? otherUserInformation.profile_photo.small : {}; forwardedChannelText.text = Emoji.emojify(Functions.getUserName(otherUserInformation), Theme.fontSizeExtraSmall); @@ -625,7 +635,7 @@ ListItem { height: ( ( chatPage.isChannel && messageViewCount > 0 ) || reactions.length > 0 ) ? ( Theme.fontSizeExtraSmall + Theme.paddingSmall ) : 0 sourceComponent: Component { Label { - text: getInteractionText(messageViewCount, reactions) + text: getInteractionText(messageViewCount, reactions, font.pixelSize, Theme.highlightColor) width: parent.width font.pixelSize: Theme.fontSizeTiny color: messageListItem.isOwnMessage ? Theme.secondaryHighlightColor : Theme.secondaryColor diff --git a/qml/components/MessageOverlayFlickable.qml b/qml/components/MessageOverlayFlickable.qml index 274a284..b5d8915 100644 --- a/qml/components/MessageOverlayFlickable.qml +++ b/qml/components/MessageOverlayFlickable.qml @@ -40,9 +40,11 @@ Flickable { function getOriginalAuthor(forwardInformation, fontSize) { switch (forwardInformation.origin["@type"]) { + case "messageOriginChannel": case "messageForwardOriginChannel": var otherChatInformation = tdLibWrapper.getChat(forwardInformation.origin.chat_id); return Emoji.emojify(otherChatInformation.title, fontSize); + case "messageOriginUser": case "messageForwardOriginUser": var otherUserInformation = tdLibWrapper.getUserInformation(forwardInformation.origin.sender_id.user_id); return Emoji.emojify(Functions.getUserName(otherUserInformation), fontSize); diff --git a/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml b/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml index b4280b5..2b12c6f 100644 --- a/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml +++ b/qml/components/chatInformationPage/ChatInformationTabItemMembersGroups.qml @@ -79,7 +79,7 @@ ChatInformationTabItemBase { // chat title primaryText.text: Emoji.emojify(Functions.getUserName(user), primaryText.font.pixelSize) // last user - prologSecondaryText.text: "@"+(user.username !== "" ? user.username : member_id.user_id) + (member_id.user_id === chatInformationPage.myUserId ? " " + qsTr("You") : "") + prologSecondaryText.text: "@"+(user.username ? user.username : member_id.user_id) + (member_id.user_id === chatInformationPage.myUserId ? " " + qsTr("You") : "") secondaryText { horizontalAlignment: Text.AlignRight property string statusText: Functions.getChatMemberStatusText(model.status["@type"]) @@ -180,6 +180,9 @@ ChatInformationTabItemBase { for(var memberIndex in members) { var memberData = members[memberIndex]; var userInfo = tdLibWrapper.getUserInformation(memberData.member_id.user_id) || {user:{}, bot_info:{}}; + if (!userInfo.username && userInfo.usernames && userInfo.usernames.active_usernames) { + userInfo.username = userInfo.usernames.active_usernames[0] + } memberData.user = userInfo; memberData.bot_info = memberData.bot_info || {}; pageContent.membersList.append(memberData); diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml index acfec79..c4bb440 100644 --- a/qml/pages/ChatPage.qml +++ b/qml/pages/ChatPage.qml @@ -66,6 +66,7 @@ Page { readonly property bool canSendMessages: hasSendPrivilege("can_send_messages") property bool doSendBotStartMessage property string sendBotStartMessageParameter + property var availableReactions states: [ State { @@ -184,7 +185,7 @@ Page { } tdLibWrapper.getChatPinnedMessage(chatInformation.id); tdLibWrapper.toggleChatIsMarkedAsUnread(chatInformation.id, false); - + availableReactions = tdLibWrapper.getChatReactions(chatInformation.id); } function getMessageStatusText(message, listItemIndex, lastReadSentIndex, useElapsed) { @@ -1348,6 +1349,7 @@ Page { messageId: model.message_id messageViewCount: model.view_count reactions: model.reactions + chatReactions: availableReactions messageIndex: model.index hasContentComponent: !!myMessage.content && chatView.delegateMessagesContent.indexOf(model.content_type) > -1 canReplyToMessage: chatPage.canSendMessages diff --git a/src/tdlibreceiver.cpp b/src/tdlibreceiver.cpp index 985e7b7..ba035e9 100644 --- a/src/tdlibreceiver.cpp +++ b/src/tdlibreceiver.cpp @@ -60,6 +60,10 @@ namespace { const QString CONTENT("content"); const QString NEW_CONTENT("new_content"); const QString SETS("sets"); + const QString EMOJIS("emojis"); + const QString REPLY_TO("reply_to"); + const QString REPLY_IN_CHAT_ID("reply_in_chat_id"); + const QString REPLY_TO_MESSAGE_ID("reply_to_message_id"); const QString _TYPE("@type"); const QString _EXTRA("@extra"); @@ -70,6 +74,7 @@ namespace { const QString TYPE_MESSAGE("message"); const QString TYPE_STICKER("sticker"); const QString TYPE_MESSAGE_STICKER("messageSticker"); + const QString TYPE_MESSAGE_REPLY_TO_MESSAGE("messageReplyToMessage"); const QString TYPE_MESSAGE_ANIMATED_EMOJI("messageAnimatedEmoji"); const QString TYPE_ANIMATED_EMOJI("animatedEmoji"); } @@ -119,7 +124,8 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren handlers.insert("updateSupergroup", &TDLibReceiver::processUpdateSuperGroup); handlers.insert("updateChatOnlineMemberCount", &TDLibReceiver::processChatOnlineMemberCountUpdated); handlers.insert("messages", &TDLibReceiver::processMessages); - handlers.insert("sponsoredMessage", &TDLibReceiver::processSponsoredMessage); + handlers.insert("sponsoredMessage", &TDLibReceiver::processSponsoredMessage); // TdLib <= 1.8.7 + handlers.insert("sponsoredMessages", &TDLibReceiver::processSponsoredMessages); // TdLib >= 1.8.8 handlers.insert("updateNewMessage", &TDLibReceiver::processUpdateNewMessage); handlers.insert("message", &TDLibReceiver::processMessage); handlers.insert("messageLinkInfo", &TDLibReceiver::processMessageLinkInfo); @@ -167,6 +173,7 @@ TDLibReceiver::TDLibReceiver(void *tdLibClient, QObject *parent) : QThread(paren handlers.insert("availableReactions", &TDLibReceiver::processAvailableReactions); handlers.insert("updateChatUnreadMentionCount", &TDLibReceiver::processUpdateChatUnreadMentionCount); handlers.insert("updateChatUnreadReactionCount", &TDLibReceiver::processUpdateChatUnreadReactionCount); + handlers.insert("updateActiveEmojiReactions", &TDLibReceiver::processUpdateActiveEmojiReactions); } void TDLibReceiver::setActive(bool active) @@ -382,11 +389,24 @@ void TDLibReceiver::processMessages(const QVariantMap &receivedInformation) void TDLibReceiver::processSponsoredMessage(const QVariantMap &receivedInformation) { + // TdLib <= 1.8.7 const qlonglong chatId = receivedInformation.value(_EXTRA).toLongLong(); // See TDLibWrapper::getChatSponsoredMessage LOG("Received sponsored message for chat" << chatId); emit sponsoredMessageReceived(chatId, receivedInformation); } +void TDLibReceiver::processSponsoredMessages(const QVariantMap &receivedInformation) +{ + // TdLib >= 1.8.8 + const qlonglong chatId = receivedInformation.value(_EXTRA).toLongLong(); // See TDLibWrapper::getChatSponsoredMessage + const QVariantList messages(receivedInformation.value(MESSAGES).toList()); + LOG("Received" << messages.count() << "sponsored messages for chat" << chatId); + QListIterator it(messages); + while (it.hasNext()) { + emit sponsoredMessageReceived(chatId, it.next().toMap()); + } +} + void TDLibReceiver::processUpdateNewMessage(const QVariantMap &receivedInformation) { const QVariantMap message = receivedInformation.value(MESSAGE).toMap(); @@ -400,7 +420,7 @@ void TDLibReceiver::processMessage(const QVariantMap &receivedInformation) const qlonglong chatId = receivedInformation.value(CHAT_ID).toLongLong(); const qlonglong messageId = receivedInformation.value(ID).toLongLong(); LOG("Received message " << chatId << messageId); - emit messageInformation(chatId, messageId, receivedInformation); + emit messageInformation(chatId, messageId, cleanupMap(receivedInformation)); } void TDLibReceiver::processMessageLinkInfo(const QVariantMap &receivedInformation) @@ -718,6 +738,13 @@ void TDLibReceiver::processUpdateChatUnreadReactionCount(const QVariantMap &rece emit chatUnreadReactionCountUpdated(chatId, unreadReactionCount); } +void TDLibReceiver::processUpdateActiveEmojiReactions(const QVariantMap &receivedInformation) +{ + // updateActiveEmojiReactions was introduced between 1.8.5 and 1.8.6 + // See https://github.com/tdlib/td/commit/d29d367 + emit activeEmojiReactionsUpdated(receivedInformation.value(EMOJIS).toStringList()); +} + // Recursively removes (some) unused entries from QVariantMaps to reduce // memory usage. QStrings allocated by QVariantMaps are the top consumers // of memory. The biggest saving is achieved by removing "outline" from @@ -747,12 +774,42 @@ const QVariantMap TDLibReceiver::cleanupMap(const QVariantMap& map, bool *update return animated_emoji; } } else if (type == TYPE_MESSAGE) { - bool cleaned = false; - const QVariantMap content(cleanupMap(map.value(CONTENT).toMap(), &cleaned)); - if (cleaned) { - QVariantMap message(map); + QVariantMap message(map); + bool messageChanged = false; + const QVariantMap content(cleanupMap(map.value(CONTENT).toMap(), &messageChanged)); + if (messageChanged) { message.remove(CONTENT); message.insert(CONTENT, content); + } + if (map.contains(REPLY_TO)) { + // In TdLib 1.8.15 reply_to_message_id and reply_in_chat_id attributes + // had been replaced with reply_to structure, e.g: + // + // "reply_to": { + // "@type": "messageReplyToMessage", + // "chat_id": -1001234567890, + // "is_quote_manual": false, + // "message_id": 234567890, + // "origin_send_date": 0 + // } + // + QVariantMap reply_to(message.value(REPLY_TO).toMap()); + if (reply_to.value(_TYPE).toString() == TYPE_MESSAGE_REPLY_TO_MESSAGE) { + if (reply_to.contains(MESSAGE_ID) && + !message.contains(REPLY_TO_MESSAGE_ID)) { + message.insert(REPLY_TO_MESSAGE_ID, reply_to.value(MESSAGE_ID)); + } + if (reply_to.contains(CHAT_ID) && + !message.contains(REPLY_IN_CHAT_ID)) { + message.insert(REPLY_IN_CHAT_ID, reply_to.value(CHAT_ID)); + } + reply_to.remove(_TYPE); + reply_to.insert(_TYPE, TYPE_MESSAGE_REPLY_TO_MESSAGE); + message.insert(REPLY_TO, reply_to); + messageChanged = true; + } + } + if (messageChanged) { message.remove(_TYPE); message.insert(_TYPE, TYPE_MESSAGE); // Replace with a shared value if (updated) *updated = true; diff --git a/src/tdlibreceiver.h b/src/tdlibreceiver.h index b033d9d..a13ae04 100644 --- a/src/tdlibreceiver.h +++ b/src/tdlibreceiver.h @@ -105,6 +105,7 @@ signals: void availableReactionsReceived(qlonglong messageId, const QStringList &reactions); void chatUnreadMentionCountUpdated(qlonglong chatId, int unreadMentionCount); void chatUnreadReactionCountUpdated(qlonglong chatId, int unreadReactionCount); + void activeEmojiReactionsUpdated(const QStringList& emojis); private: typedef void (TDLibReceiver::*Handler)(const QVariantMap &); @@ -139,6 +140,7 @@ private: void processChatOnlineMemberCountUpdated(const QVariantMap &receivedInformation); void processMessages(const QVariantMap &receivedInformation); void processSponsoredMessage(const QVariantMap &receivedInformation); + void processSponsoredMessages(const QVariantMap &receivedInformation); void processUpdateNewMessage(const QVariantMap &receivedInformation); void processMessage(const QVariantMap &receivedInformation); void processMessageLinkInfo(const QVariantMap &receivedInformation); @@ -186,6 +188,7 @@ private: void processAvailableReactions(const QVariantMap &receivedInformation); void processUpdateChatUnreadMentionCount(const QVariantMap &receivedInformation); void processUpdateChatUnreadReactionCount(const QVariantMap &receivedInformation); + void processUpdateActiveEmojiReactions(const QVariantMap &receivedInformation); }; #endif // TDLIBRECEIVER_H diff --git a/src/tdlibwrapper.cpp b/src/tdlibwrapper.cpp index 53be644..6cedc4d 100644 --- a/src/tdlibwrapper.cpp +++ b/src/tdlibwrapper.cpp @@ -36,6 +36,9 @@ #define DEBUG_MODULE TDLibWrapper #include "debuglog.h" +#define VERSION_NUMBER(x,y,z) \ + ((((x) & 0x3ff) << 20) | (((y) & 0x3ff) << 10) | ((z) & 0x3ff)) + namespace { const QString STATUS("status"); const QString ID("id"); @@ -51,20 +54,28 @@ namespace { const QString _TYPE("@type"); const QString _EXTRA("@extra"); const QString CHAT_LIST_MAIN("chatListMain"); + const QString CHAT_AVAILABLE_REACTIONS("available_reactions"); + const QString CHAT_AVAILABLE_REACTIONS_ALL("chatAvailableReactionsAll"); + const QString CHAT_AVAILABLE_REACTIONS_SOME("chatAvailableReactionsSome"); + const QString REACTIONS("reactions"); + const QString REACTION_TYPE("reaction_type"); + const QString REACTION_TYPE_EMOJI("reactionTypeEmoji"); + const QString EMOJI("emoji"); } -TDLibWrapper::TDLibWrapper(AppSettings *appSettings, MceInterface *mceInterface, QObject *parent) +TDLibWrapper::TDLibWrapper(AppSettings *settings, MceInterface *mce, QObject *parent) : QObject(parent) + , tdLibClient(td_json_client_create()) , manager(new QNetworkAccessManager(this)) , networkConfigurationManager(new QNetworkConfigurationManager(this)) + , appSettings(settings) + , mceInterface(mce) + , authorizationState(AuthorizationState::Closed) + , versionNumber(0) , joinChatRequested(false) + , isLoggingOut(false) { LOG("Initializing TD Lib..."); - this->appSettings = appSettings; - this->mceInterface = mceInterface; - this->tdLibClient = td_json_client_create(); - this->authorizationState = AuthorizationState::Closed; - this->isLoggingOut = false; initializeTDLibReceiver(); @@ -174,6 +185,7 @@ void TDLibWrapper::initializeTDLibReceiver() { connect(this->tdLibReceiver, SIGNAL(availableReactionsReceived(qlonglong, QStringList)), this, SIGNAL(availableReactionsReceived(qlonglong, QStringList))); connect(this->tdLibReceiver, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int)), this, SIGNAL(chatUnreadMentionCountUpdated(qlonglong, int))); connect(this->tdLibReceiver, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int)), this, SIGNAL(chatUnreadReactionCountUpdated(qlonglong, int))); + connect(this->tdLibReceiver, SIGNAL(activeEmojiReactionsUpdated(QStringList)), this, SLOT(handleActiveEmojiReactionsUpdated(QStringList))); this->tdLibReceiver->start(); } @@ -192,7 +204,7 @@ void TDLibWrapper::sendRequest(const QVariantMap &requestObject) QString TDLibWrapper::getVersion() { - return this->version; + return this->versionString; } TDLibWrapper::AuthorizationState TDLibWrapper::getAuthorizationState() @@ -255,7 +267,7 @@ void TDLibWrapper::logout() { LOG("Logging out"); QVariantMap requestObject; - requestObject.insert("@type", "logOut"); + requestObject.insert(_TYPE, "logOut"); this->sendRequest(requestObject); this->isLoggingOut = true; @@ -436,7 +448,7 @@ void TDLibWrapper::sendTextMessage(const QString &chatId, const QString &message QVariantMap entityType; entityType.insert(_TYPE, "textEntityTypeMentionName"); entityType.insert("user_id", nextReplacement.value("userId").toString()); - entity.insert("type", entityType); + entity.insert(TYPE, entityType); entities.append(entity); offsetCorrection += replacementLength - replacementPlainText.length(); } @@ -586,7 +598,7 @@ void TDLibWrapper::sendStickerMessage(const QString &chatId, const QString &file QVariantMap stickerInputFile; stickerInputFile.insert(_TYPE, "inputFileRemote"); - stickerInputFile.insert("id", fileId); + stickerInputFile.insert(ID, fileId); inputMessageContent.insert("sticker", stickerInputFile); @@ -620,7 +632,7 @@ void TDLibWrapper::sendPollMessage(const QString &chatId, const QString &questio pollType.insert("allow_multiple_answers", multiple); } - inputMessageContent.insert("type", pollType); + inputMessageContent.insert(TYPE, pollType); inputMessageContent.insert("question", question); inputMessageContent.insert("options", options); inputMessageContent.insert("is_anonymous", anonymous); @@ -709,7 +721,11 @@ void TDLibWrapper::getChatSponsoredMessage(qlonglong chatId) { LOG("Retrieving sponsored message" << chatId); QVariantMap requestObject; - requestObject.insert(_TYPE, "getChatSponsoredMessage"); + // getChatSponsoredMessage has been replaced with getChatSponsoredMessages + // between 1.8.7 and 1.8.8 + // See https://github.com/tdlib/td/commit/ec1310a + requestObject.insert(_TYPE, QString((versionNumber > VERSION_NUMBER(1,8,7)) ? + "getChatSponsoredMessages" : "getChatSponsoredMessage")); requestObject.insert(CHAT_ID, chatId); requestObject.insert(_EXTRA, chatId); // see TDLibReceiver::processSponsoredMessage this->sendRequest(requestObject); @@ -1433,8 +1449,8 @@ void TDLibWrapper::getMessageAvailableReactions(qlonglong chatId, qlonglong mess QVariantMap requestObject; requestObject.insert(_TYPE, "getMessageAvailableReactions"); requestObject.insert(_EXTRA, QString::number(messageId)); - requestObject.insert("chat_id", chatId); - requestObject.insert("message_id", messageId); + requestObject.insert(CHAT_ID, chatId); + requestObject.insert(MESSAGE_ID, messageId); this->sendRequest(requestObject); } @@ -1457,11 +1473,23 @@ void TDLibWrapper::setMessageReaction(qlonglong chatId, qlonglong messageId, con { LOG("Set message reaction" << chatId << messageId << reaction); QVariantMap requestObject; - requestObject.insert(_TYPE, "setMessageReaction"); - requestObject.insert("chat_id", chatId); - requestObject.insert("message_id", messageId); - requestObject.insert("reaction", reaction); + requestObject.insert(CHAT_ID, chatId); + requestObject.insert(MESSAGE_ID, messageId); requestObject.insert("is_big", false); + if (versionNumber > VERSION_NUMBER(1,8,5)) { + // "reaction_type": { + // "@type": "reactionTypeEmoji", + // "emoji": "..." + // } + QVariantMap reactionType; + reactionType.insert(_TYPE, REACTION_TYPE_EMOJI); + reactionType.insert(EMOJI, reaction); + requestObject.insert(REACTION_TYPE, reactionType); + requestObject.insert(_TYPE, "addMessageReaction"); + } else { + requestObject.insert("reaction", reaction); + requestObject.insert(_TYPE, "setMessageReaction"); + } this->sendRequest(requestObject); } @@ -1494,7 +1522,7 @@ void TDLibWrapper::setNetworkType(NetworkType networkType) break; } - requestObject.insert("type", networkTypeObject); + requestObject.insert(TYPE, networkTypeObject); this->sendRequest(requestObject); } @@ -1575,6 +1603,43 @@ QVariantMap TDLibWrapper::getChat(const QString &chatId) return this->chats.value(chatId).toMap(); } +QStringList TDLibWrapper::getChatReactions(const QString &chatId) +{ + const QVariant available_reactions(chats.value(chatId).toMap().value(CHAT_AVAILABLE_REACTIONS)); + const QVariantMap map(available_reactions.toMap()); + const QString reactions_type(map.value(_TYPE).toString()); + if (reactions_type == CHAT_AVAILABLE_REACTIONS_ALL) { + return activeEmojiReactions; + } else if (reactions_type == CHAT_AVAILABLE_REACTIONS_SOME) { + const QVariantList reactions(map.value(REACTIONS).toList()); + const int n = reactions.count(); + QStringList emojis; + + // "available_reactions": { + // "@type": "chatAvailableReactionsSome", + // "reactions": [ + // { + // "@type": "reactionTypeEmoji", + // "emoji": "..." + // }, + emojis.reserve(n); + for (int i = 0; i < n; i++) { + const QVariantMap reaction(reactions.at(i).toMap()); + if (reaction.value(_TYPE).toString() == REACTION_TYPE_EMOJI) { + const QString emoji(reaction.value(EMOJI).toString()); + if (!emoji.isEmpty()) { + emojis.append(emoji); + } + } + } + return emojis; + } else if (reactions_type.isEmpty()) { + return available_reactions.toStringList(); + } else { + return QStringList(); + } +} + QVariantMap TDLibWrapper::getSecretChatFromCache(qlonglong secretChatId) { return this->secretChats.value(secretChatId); @@ -1645,7 +1710,16 @@ DBusAdaptor *TDLibWrapper::getDBusAdaptor() void TDLibWrapper::handleVersionDetected(const QString &version) { - this->version = version; + this->versionString = version; + const QStringList parts(version.split('.')); + uint major, minor, release; + bool ok; + if (parts.count() >= 3 && + (major = parts.at(0).toInt(&ok), ok) && + (minor = parts.at(1).toInt(&ok), ok) && + (release = parts.at(2).toInt(&ok), ok)) { + versionNumber = VERSION_NUMBER(major, minor, release); + } emit versionDetected(version); } @@ -1758,7 +1832,7 @@ void TDLibWrapper::handleConnectionStateChanged(const QString &connectionState) void TDLibWrapper::handleUserUpdated(const QVariantMap &userInformation) { - QString updatedUserId = userInformation.value("id").toString(); + QString updatedUserId = userInformation.value(ID).toString(); if (updatedUserId == this->options.value("my_id").toString()) { LOG("Own user information updated :)"); this->userInformation = userInformation; @@ -1774,11 +1848,11 @@ void TDLibWrapper::handleUserStatusUpdated(const QString &userId, const QVariant { if (userId == this->options.value("my_id").toString()) { LOG("Own user status information updated :)"); - this->userInformation.insert("status", userStatusInformation); + this->userInformation.insert(STATUS, userStatusInformation); } LOG("User status information updated:" << userId << userStatusInformation.value(_TYPE).toString()); QVariantMap updatedUserInformation = this->allUsers.value(userId).toMap(); - updatedUserInformation.insert("status", userStatusInformation); + updatedUserInformation.insert(STATUS, userStatusInformation); this->allUsers.insert(userId, updatedUserInformation); this->allUserNames.insert(userInformation.value(USERNAME).toString(), userInformation); emit userUpdated(userId, updatedUserInformation); @@ -1786,12 +1860,12 @@ void TDLibWrapper::handleUserStatusUpdated(const QString &userId, const QVariant void TDLibWrapper::handleFileUpdated(const QVariantMap &fileInformation) { - emit fileUpdated(fileInformation.value("id").toInt(), fileInformation); + emit fileUpdated(fileInformation.value(ID).toInt(), fileInformation); } void TDLibWrapper::handleNewChatDiscovered(const QVariantMap &chatInformation) { - QString chatId = chatInformation.value("id").toString(); + QString chatId = chatInformation.value(ID).toString(); this->chats.insert(chatId, chatInformation); emit newChatDiscovered(chatId, chatInformation); } @@ -1856,7 +1930,7 @@ void TDLibWrapper::handleStickerSets(const QVariantList &stickerSets) QListIterator stickerSetIterator(stickerSets); while (stickerSetIterator.hasNext()) { QVariantMap stickerSet = stickerSetIterator.next().toMap(); - this->getStickerSet(stickerSet.value("id").toString()); + this->getStickerSet(stickerSet.value(ID).toString()); } emit this->stickerSetsReceived(stickerSets); } @@ -1988,6 +2062,13 @@ void TDLibWrapper::handleSponsoredMessage(qlonglong chatId, const QVariantMap &m } } +void TDLibWrapper::handleActiveEmojiReactionsUpdated(const QStringList& emojis) +{ + if (activeEmojiReactions != emojis) { + activeEmojiReactions = emojis; + LOG(emojis.count() << "reaction(s) available"); + } +} void TDLibWrapper::handleNetworkConfigurationChanged(const QNetworkConfiguration &config) { @@ -2077,28 +2158,40 @@ void TDLibWrapper::handleGetPageSourceFinished() } } +QVariantMap& TDLibWrapper::fillTdlibParameters(QVariantMap& parameters) +{ + parameters.insert("api_id", TDLIB_API_ID); + parameters.insert("api_hash", TDLIB_API_HASH); + parameters.insert("database_directory", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib"); + bool onlineOnlyMode = this->appSettings->onlineOnlyMode(); + parameters.insert("use_file_database", !onlineOnlyMode); + parameters.insert("use_chat_info_database", !onlineOnlyMode); + parameters.insert("use_message_database", !onlineOnlyMode); + parameters.insert("use_secret_chats", true); + parameters.insert("system_language_code", QLocale::system().name()); + QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat); + parameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString()); + parameters.insert("system_version", QSysInfo::prettyProductName()); + parameters.insert("application_version", "0.17"); + parameters.insert("enable_storage_optimizer", appSettings->storageOptimizer()); + // parameters.insert("use_test_dc", true); + return parameters; +} + void TDLibWrapper::setInitialParameters() { LOG("Sending initial parameters to TD Lib"); QVariantMap requestObject; requestObject.insert(_TYPE, "setTdlibParameters"); - QVariantMap initialParameters; - initialParameters.insert("api_id", TDLIB_API_ID); - initialParameters.insert("api_hash", TDLIB_API_HASH); - initialParameters.insert("database_directory", QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tdlib"); - bool onlineOnlyMode = this->appSettings->onlineOnlyMode(); - initialParameters.insert("use_file_database", !onlineOnlyMode); - initialParameters.insert("use_chat_info_database", !onlineOnlyMode); - initialParameters.insert("use_message_database", !onlineOnlyMode); - initialParameters.insert("use_secret_chats", true); - initialParameters.insert("system_language_code", QLocale::system().name()); - QSettings hardwareSettings("/etc/hw-release", QSettings::NativeFormat); - initialParameters.insert("device_model", hardwareSettings.value("NAME", "Unknown Mobile Device").toString()); - initialParameters.insert("system_version", QSysInfo::prettyProductName()); - initialParameters.insert("application_version", "0.17"); - initialParameters.insert("enable_storage_optimizer", appSettings->storageOptimizer()); - // initialParameters.insert("use_test_dc", true); - requestObject.insert("parameters", initialParameters); + // tdlibParameters were inlined between 1.8.5 and 1.8.6 + // See https://github.com/tdlib/td/commit/f6a2ecd + if (versionNumber > VERSION_NUMBER(1,8,5)) { + fillTdlibParameters(requestObject); + } else { + QVariantMap initialParameters; + fillTdlibParameters(initialParameters); + requestObject.insert("parameters", initialParameters); + } this->sendRequest(requestObject); } diff --git a/src/tdlibwrapper.h b/src/tdlibwrapper.h index 1d1ec6d..4caff11 100644 --- a/src/tdlibwrapper.h +++ b/src/tdlibwrapper.h @@ -148,6 +148,7 @@ public: Q_INVOKABLE QVariantMap getSuperGroup(qlonglong groupId) const; Q_INVOKABLE QVariantMap getChat(const QString &chatId); Q_INVOKABLE QVariantMap getSecretChatFromCache(qlonglong secretChatId); + Q_INVOKABLE QStringList getChatReactions(const QString &chatId); Q_INVOKABLE QString getOptionString(const QString &optionName); Q_INVOKABLE void copyFileToDownloads(const QString &filePath, bool openAfterCopy = false); Q_INVOKABLE void openFileOnDevice(const QString &filePath); @@ -364,7 +365,7 @@ public slots: void handleUpdatedUserPrivacySettingRules(const QVariantMap &updatedRules); void handleSponsoredMessage(qlonglong chatId, const QVariantMap &message); void handleNetworkConfigurationChanged(const QNetworkConfiguration &config); - + void handleActiveEmojiReactionsUpdated(const QStringList& emojis); void handleGetPageSourceFinished(); private: @@ -372,6 +373,7 @@ private: void setInitialParameters(); void setEncryptionKey(); void setLogVerbosityLevel(); + QVariantMap &fillTdlibParameters(QVariantMap ¶meters); const Group *updateGroup(qlonglong groupId, const QVariantMap &groupInfo, QHash *groups); void initializeTDLibReceiver(); @@ -383,7 +385,7 @@ private: MceInterface *mceInterface; TDLibReceiver *tdLibReceiver; DBusInterface *dbusInterface; - QString version; + QString versionString; TDLibWrapper::AuthorizationState authorizationState; QVariantMap authorizationStateData; TDLibWrapper::ConnectionState connectionState; @@ -399,7 +401,9 @@ private: QHash basicGroups; QHash superGroups; EmojiSearchWorker emojiSearchWorker; + QStringList activeEmojiReactions; + int versionNumber; QString activeChatSearchName; bool joinChatRequested; bool isLoggingOut;