File indexing completed on 2024-09-15 04:28:25
0001 // SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org> 0002 // SPDX-License-Identifier: GPL-3.0-or-later 0003 0004 #include "actionshandler.h" 0005 0006 #include <Quotient/csapi/joining.h> 0007 #include <Quotient/events/roommemberevent.h> 0008 0009 #include <cmark.h> 0010 0011 #include <KLocalizedString> 0012 #include <QStringBuilder> 0013 0014 #include "models/actionsmodel.h" 0015 #include "neochatconfig.h" 0016 #include "texthandler.h" 0017 0018 using namespace Quotient; 0019 0020 ActionsHandler::ActionsHandler(QObject *parent) 0021 : QObject(parent) 0022 { 0023 } 0024 0025 NeoChatRoom *ActionsHandler::room() const 0026 { 0027 return m_room; 0028 } 0029 0030 void ActionsHandler::setRoom(NeoChatRoom *room) 0031 { 0032 if (m_room == room) { 0033 return; 0034 } 0035 0036 m_room = room; 0037 Q_EMIT roomChanged(); 0038 } 0039 0040 void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache) 0041 { 0042 if (!m_room || !chatBarCache) { 0043 qWarning() << "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr."; 0044 return; 0045 } 0046 0047 checkEffects(chatBarCache->text()); 0048 if (!chatBarCache->attachmentPath().isEmpty()) { 0049 QUrl url(chatBarCache->attachmentPath()); 0050 auto path = url.isLocalFile() ? url.toLocalFile() : url.toString(); 0051 m_room->uploadFile(QUrl(path), chatBarCache->text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : chatBarCache->text()); 0052 chatBarCache->setAttachmentPath({}); 0053 chatBarCache->setText({}); 0054 return; 0055 } 0056 0057 QString handledText = chatBarCache->text(); 0058 handledText = handleMentions(handledText, chatBarCache->mentions()); 0059 handleMessage(m_room->mainCache()->text(), handledText, chatBarCache); 0060 } 0061 0062 QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *mentions) 0063 { 0064 std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool { 0065 return a.cursor.anchor() > b.cursor.anchor(); 0066 }); 0067 0068 for (const auto &mention : *mentions) { 0069 if (mention.text.isEmpty() || mention.id.isEmpty()) { 0070 continue; 0071 } 0072 handledText = handledText.replace(mention.cursor.anchor(), 0073 mention.cursor.position() - mention.cursor.anchor(), 0074 QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id)); 0075 } 0076 mentions->clear(); 0077 0078 return handledText; 0079 } 0080 0081 void ActionsHandler::handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache) 0082 { 0083 if (NeoChatConfig::allowQuickEdit()) { 0084 QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$")); 0085 auto match = sed.match(text); 0086 if (match.hasMatch()) { 0087 const QString regex = match.captured(1); 0088 const QString replacement = match.captured(2).toHtmlEscaped(); 0089 const QString flags = match.captured(3); 0090 0091 for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) { 0092 if (const auto event = eventCast<const RoomMessageEvent>(&**it)) { 0093 if (event->senderId() == m_room->localUser()->id() && event->hasTextContent()) { 0094 QString originalString; 0095 if (event->content()) { 0096 originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body; 0097 } else { 0098 originalString = event->plainBody(); 0099 } 0100 if (flags == "/g"_ls) { 0101 m_room->postHtmlMessage(handledText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id()); 0102 } else { 0103 m_room->postHtmlMessage(handledText, 0104 originalString.replace(originalString.indexOf(regex), regex.size(), replacement), 0105 event->msgtype(), 0106 {}, 0107 event->id()); 0108 } 0109 return; 0110 } 0111 } 0112 } 0113 } 0114 } 0115 auto messageType = RoomMessageEvent::MsgType::Text; 0116 0117 if (handledText.startsWith(QLatin1Char('/'))) { 0118 for (const auto &action : ActionsModel::instance().allActions()) { 0119 if (handledText.indexOf(action.prefix) == 1 0120 && (handledText.indexOf(" "_ls) == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) { 0121 handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room, chatBarCache); 0122 if (action.messageType.has_value()) { 0123 messageType = *action.messageType; 0124 } 0125 if (action.messageAction) { 0126 break; 0127 } else { 0128 return; 0129 } 0130 } 0131 } 0132 } 0133 0134 TextHandler textHandler; 0135 textHandler.setData(handledText); 0136 handledText = textHandler.handleSendText(); 0137 0138 if (handledText.count("<p>"_ls) == 1 && handledText.count("</p>"_ls) == 1) { 0139 handledText.remove("<p>"_ls); 0140 handledText.remove("</p>"_ls); 0141 } 0142 0143 if (handledText.length() == 0) { 0144 return; 0145 } 0146 0147 m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId()); 0148 } 0149 0150 void ActionsHandler::checkEffects(const QString &text) 0151 { 0152 std::optional<QString> effect = std::nullopt; 0153 if (text.contains(QStringLiteral("\u2744"))) { 0154 effect = QLatin1String("snowflake"); 0155 } else if (text.contains(QStringLiteral("\u1F386"))) { 0156 effect = QLatin1String("fireworks"); 0157 } else if (text.contains(QStringLiteral("\u2F387"))) { 0158 effect = QLatin1String("fireworks"); 0159 } else if (text.contains(QStringLiteral("\u1F389"))) { 0160 effect = QLatin1String("confetti"); 0161 } else if (text.contains(QStringLiteral("\u1F38A"))) { 0162 effect = QLatin1String("confetti"); 0163 } 0164 if (effect.has_value()) { 0165 Q_EMIT showEffect(*effect); 0166 } 0167 } 0168 0169 #include "moc_actionshandler.cpp"