File indexing completed on 2024-04-28 08:51:47

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"