File indexing completed on 2024-05-12 16:25:03

0001 // SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "actionsmodel.h"
0005 
0006 #include "controller.h"
0007 #include "neochatroom.h"
0008 #include "roommanager.h"
0009 #include <Quotient/events/roommemberevent.h>
0010 #include <Quotient/events/roompowerlevelsevent.h>
0011 
0012 #include <KLocalizedString>
0013 
0014 using Action = ActionsModel::Action;
0015 using namespace Quotient;
0016 
0017 QStringList rainbowColors{"#ff2b00", "#ff5500", "#ff8000", "#ffaa00", "#ffd500", "#ffff00", "#d4ff00", "#aaff00", "#80ff00", "#55ff00", "#2bff00", "#00ff00",
0018                           "#00ff2b", "#00ff55", "#00ff80", "#00ffaa", "#00ffd5", "#00ffff", "#00d4ff", "#00aaff", "#007fff", "#0055ff", "#002bff", "#0000ff",
0019                           "#2a00ff", "#5500ff", "#7f00ff", "#aa00ff", "#d400ff", "#ff00ff", "#ff00d4", "#ff00aa", "#ff0080", "#ff0055", "#ff002b", "#ff0000"};
0020 
0021 auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room) {
0022     if (text.isEmpty()) {
0023         Q_EMIT room->showMessage(NeoChatRoom::Info, i18n("Leaving this room."));
0024         room->connection()->leaveRoom(room);
0025     } else {
0026         QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
0027         auto regexMatch = roomRegex.match(text);
0028         if (!regexMatch.hasMatch()) {
0029             Q_EMIT room->showMessage(NeoChatRoom::Error,
0030                                      i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
0031             return QString();
0032         }
0033         auto leaving = room->connection()->room(text);
0034         if (!leaving) {
0035             leaving = room->connection()->roomByAlias(text);
0036         }
0037         if (leaving) {
0038             Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Leaving room <roomname>.", "Leaving room %1.", text));
0039             room->connection()->leaveRoom(leaving);
0040         } else {
0041             Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Room <roomname> not found", "Room %1 not found.", text));
0042         }
0043     }
0044     return QString();
0045 };
0046 
0047 auto roomNickLambda = [](const QString &text, NeoChatRoom *room) {
0048     if (text.isEmpty()) {
0049         Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen."));
0050     } else {
0051         room->connection()->user()->rename(text, room);
0052     }
0053     return QString();
0054 };
0055 
0056 QVector<ActionsModel::Action> actions{
0057     Action{
0058         QStringLiteral("shrug"),
0059         [](const QString &message, NeoChatRoom *) {
0060             return QStringLiteral("¯\\\\_(ツ)_/¯ %1").arg(message);
0061         },
0062         true,
0063         std::nullopt,
0064         kli18n("<message>"),
0065         kli18n("Prepends ¯\\_(ツ)_/¯ to a plain-text message"),
0066     },
0067     Action{
0068         QStringLiteral("lenny"),
0069         [](const QString &message, NeoChatRoom *) {
0070             return QStringLiteral("( ͡° ͜ʖ ͡°) %1").arg(message);
0071         },
0072         true,
0073         std::nullopt,
0074         kli18n("<message>"),
0075         kli18n("Prepends ( ͡° ͜ʖ ͡°) to a plain-text message"),
0076     },
0077     Action{
0078         QStringLiteral("tableflip"),
0079         [](const QString &message, NeoChatRoom *) {
0080             return QStringLiteral("(╯°□°)╯︵ ┻━┻ %1").arg(message);
0081         },
0082         true,
0083         std::nullopt,
0084         kli18n("<message>"),
0085         kli18n("Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message"),
0086     },
0087     Action{
0088         QStringLiteral("unflip"),
0089         [](const QString &message, NeoChatRoom *) {
0090             return QStringLiteral("┬──┬ ノ( ゜-゜ノ) %1").arg(message);
0091         },
0092         true,
0093         std::nullopt,
0094         kli18n("<message>"),
0095         kli18n("Prepends ┬──┬ ノ( ゜-゜ノ) to a plain-text message"),
0096     },
0097     Action{
0098         QStringLiteral("rainbow"),
0099         [](const QString &text, NeoChatRoom *room) {
0100             QString rainbowText;
0101             for (int i = 0; i < text.length(); i++) {
0102                 rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i));
0103             }
0104             // Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
0105             room->postMessage(QStringLiteral("/rainbow %1").arg(text),
0106                               rainbowText,
0107                               RoomMessageEvent::MsgType::Text,
0108                               room->chatBoxReplyId(),
0109                               room->chatBoxEditId());
0110             return QString();
0111         },
0112         false,
0113         std::nullopt,
0114         kli18n("<message>"),
0115         kli18n("Sends the given message colored as a rainbow"),
0116     },
0117     Action{
0118         QStringLiteral("rainbowme"),
0119         [](const QString &text, NeoChatRoom *room) {
0120             QString rainbowText;
0121             for (int i = 0; i < text.length(); i++) {
0122                 rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i));
0123             }
0124             // Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
0125             room->postMessage(QStringLiteral("/rainbow %1").arg(text),
0126                               rainbowText,
0127                               RoomMessageEvent::MsgType::Emote,
0128                               room->chatBoxReplyId(),
0129                               room->chatBoxEditId());
0130             return QString();
0131         },
0132         false,
0133         std::nullopt,
0134         kli18n("<message>"),
0135         kli18n("Sends the given emote colored as a rainbow"),
0136     },
0137     Action{
0138         QStringLiteral("plain"),
0139         [](const QString &text, NeoChatRoom *room) {
0140             room->postMessage(text, text.toHtmlEscaped(), RoomMessageEvent::MsgType::Text, {}, {});
0141             return QString();
0142         },
0143         false,
0144         std::nullopt,
0145         kli18n("<message>"),
0146         kli18n("Sends the given message as plain text"),
0147     },
0148     Action{
0149         QStringLiteral("spoiler"),
0150         [](const QString &text, NeoChatRoom *room) {
0151             // Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
0152             room->postMessage(QStringLiteral("/spoiler %1").arg(text),
0153                               QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text),
0154                               RoomMessageEvent::MsgType::Text,
0155                               room->chatBoxReplyId(),
0156                               room->chatBoxEditId());
0157             return QString();
0158         },
0159         false,
0160         std::nullopt,
0161         kli18n("<message>"),
0162         kli18n("Sends the given message as a spoiler"),
0163     },
0164     Action{
0165         QStringLiteral("me"),
0166         [](const QString &text, NeoChatRoom *) {
0167             return text;
0168         },
0169         true,
0170         RoomMessageEvent::MsgType::Emote,
0171         kli18n("<message>"),
0172         kli18n("Sends the given emote"),
0173     },
0174     Action{
0175         QStringLiteral("notice"),
0176         [](const QString &text, NeoChatRoom *) {
0177             return text;
0178         },
0179         true,
0180         RoomMessageEvent::MsgType::Notice,
0181         kli18n("<message>"),
0182         kli18n("Sends the given message as a notice"),
0183     },
0184     Action{
0185         QStringLiteral("invite"),
0186         [](const QString &text, NeoChatRoom *room) {
0187             static const QRegularExpression mxidRegex(
0188                 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
0189             auto regexMatch = mxidRegex.match(text);
0190             if (!regexMatch.hasMatch()) {
0191                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
0192                 return QString();
0193             }
0194             const RoomMemberEvent *roomMemberEvent = room->currentState().get<RoomMemberEvent>(text);
0195             if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) {
0196                 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text));
0197                 return QString();
0198             }
0199             if (roomMemberEvent && roomMemberEvent->membership() == Membership::Ban) {
0200                 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text));
0201                 return QString();
0202             }
0203             if (room->localUser()->id() == text) {
0204                 Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room."));
0205                 return QString();
0206             }
0207             if (room->users().contains(room->user(text))) {
0208                 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already in this room.", "%1 is already in this room.", text));
0209                 return QString();
0210             }
0211             room->inviteToRoom(text);
0212             Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was invited into this room", "%1 was invited into this room", text));
0213             return QString();
0214         },
0215         false,
0216         std::nullopt,
0217         kli18n("<user id>"),
0218         kli18n("Invites the user to this room"),
0219     },
0220     Action{
0221         QStringLiteral("join"),
0222         [](const QString &text, NeoChatRoom *room) {
0223             QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
0224             auto regexMatch = roomRegex.match(text);
0225             if (!regexMatch.hasMatch()) {
0226                 Q_EMIT room->showMessage(NeoChatRoom::Error,
0227                                          i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
0228                 return QString();
0229             }
0230             auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
0231             if (targetRoom) {
0232                 RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom));
0233                 return QString();
0234             }
0235             Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Joining room <roomname>.", "Joining room %1.", text));
0236             Controller::instance().joinRoom(text);
0237             return QString();
0238         },
0239         false,
0240         std::nullopt,
0241         kli18n("<room alias or id>"),
0242         kli18n("Joins the given room"),
0243     },
0244     Action{
0245         QStringLiteral("knock"),
0246         [](const QString &text, NeoChatRoom *room) {
0247             auto parts = text.split(QLatin1String(" "));
0248             QString roomName = parts[0];
0249             QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
0250             auto regexMatch = roomRegex.match(roomName);
0251             if (!regexMatch.hasMatch()) {
0252                 Q_EMIT room->showMessage(NeoChatRoom::Error,
0253                                          i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
0254                 return QString();
0255             }
0256             auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
0257             if (targetRoom) {
0258                 RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom));
0259                 return QString();
0260             }
0261             Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
0262             auto connection = Controller::instance().activeConnection();
0263             const auto knownServer = roomName.mid(roomName.indexOf(":") + 1);
0264             if (parts.length() >= 2) {
0265                 RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
0266             } else {
0267                 RoomManager::instance().knockRoom(connection, roomName, QString(), QStringList{knownServer});
0268             }
0269             return QString();
0270         },
0271         false,
0272         std::nullopt,
0273         kli18n("<room alias or id> [<reason>]"),
0274         kli18n("Requests to join the given room"),
0275     },
0276     Action{
0277         QStringLiteral("j"),
0278         [](const QString &text, NeoChatRoom *room) {
0279             QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
0280             auto regexMatch = roomRegex.match(text);
0281             if (!regexMatch.hasMatch()) {
0282                 Q_EMIT room->showMessage(NeoChatRoom::Error,
0283                                          i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
0284                 return QString();
0285             }
0286             if (Controller::instance().activeConnection()->room(text) || Controller::instance().activeConnection()->roomByAlias(text)) {
0287                 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("You are already in room <roomname>.", "You are already in room %1.", text));
0288                 return QString();
0289             }
0290             Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Joining room <roomname>.", "Joining room %1.", text));
0291             Controller::instance().joinRoom(text);
0292             return QString();
0293         },
0294         false,
0295         std::nullopt,
0296         kli18n("<room alias or id>"),
0297         kli18n("Joins the given room"),
0298     },
0299     Action{
0300         QStringLiteral("part"),
0301         leaveRoomLambda,
0302         false,
0303         std::nullopt,
0304         kli18n("[<room alias or id>]"),
0305         kli18n("Leaves the given room or this room, if there is none given"),
0306     },
0307     Action{
0308         QStringLiteral("leave"),
0309         leaveRoomLambda,
0310         false,
0311         std::nullopt,
0312         kli18n("[<room alias or id>]"),
0313         kli18n("Leaves the given room or this room, if there is none given"),
0314     },
0315     Action{
0316         QStringLiteral("nick"),
0317         [](const QString &text, NeoChatRoom *room) {
0318             if (text.isEmpty()) {
0319                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen."));
0320             } else {
0321                 room->connection()->user()->rename(text);
0322             }
0323             return QString();
0324         },
0325         false,
0326         std::nullopt,
0327         kli18n("<display name>"),
0328         kli18n("Changes your global display name"),
0329     },
0330     Action{
0331         QStringLiteral("roomnick"),
0332         roomNickLambda,
0333         false,
0334         std::nullopt,
0335         kli18n("<display name>"),
0336         kli18n("Changes your display name in this room"),
0337     },
0338     Action{
0339         QStringLiteral("myroomnick"),
0340         roomNickLambda,
0341         false,
0342         std::nullopt,
0343         kli18n("<display name>"),
0344         kli18n("Changes your display name in this room"),
0345     },
0346     Action{
0347         QStringLiteral("ignore"),
0348         [](const QString &text, NeoChatRoom *room) {
0349             static const QRegularExpression mxidRegex(
0350                 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
0351             auto regexMatch = mxidRegex.match(text);
0352             if (!regexMatch.hasMatch()) {
0353                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
0354                 return QString();
0355             }
0356             auto user = room->connection()->users()[text];
0357             if (room->connection()->ignoredUsers().contains(user->id())) {
0358                 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<username> is already ignored.", "%1 is already ignored.", text));
0359                 return QString();
0360             }
0361             if (user) {
0362                 room->connection()->addToIgnoredUsers(user);
0363                 Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> is now ignored", "%1 is now ignored.", text));
0364             } else {
0365                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not a known user", "%1 is not a known user.", text));
0366             }
0367             return QString();
0368         },
0369         false,
0370         std::nullopt,
0371         kli18n("<user id>"),
0372         kli18n("Ignores the given user"),
0373     },
0374     Action{
0375         QStringLiteral("unignore"),
0376         [](const QString &text, NeoChatRoom *room) {
0377             static const QRegularExpression mxidRegex(
0378                 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
0379             auto regexMatch = mxidRegex.match(text);
0380             if (!regexMatch.hasMatch()) {
0381                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
0382                 return QString();
0383             }
0384             auto user = room->connection()->users()[text];
0385             if (user) {
0386                 if (!room->connection()->ignoredUsers().contains(user->id())) {
0387                     Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<username> is not ignored.", "%1 is not ignored.", text));
0388                     return QString();
0389                 }
0390                 room->connection()->removeFromIgnoredUsers(user);
0391                 Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> is no longer ignored.", "%1 is no longer ignored.", text));
0392             } else {
0393                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not a known user", "%1 is not a known user.", text));
0394             }
0395             return QString();
0396         },
0397         false,
0398         std::nullopt,
0399         kli18n("<user id>"),
0400         kli18n("Unignores the given user"),
0401     },
0402     Action{
0403         QStringLiteral("react"),
0404         [](const QString &text, NeoChatRoom *room) {
0405             QString replyEventId = room->chatBoxReplyId();
0406             if (replyEventId.isEmpty()) {
0407                 for (auto it = room->messageEvents().crbegin(); it != room->messageEvents().crend(); it++) {
0408                     const auto &evt = **it;
0409                     if (const auto event = eventCast<const RoomMessageEvent>(&evt)) {
0410                         room->toggleReaction(event->id(), text);
0411                         return QString();
0412                     }
0413                 }
0414             }
0415             room->toggleReaction(replyEventId, text);
0416             return QString();
0417         },
0418         false,
0419         std::nullopt,
0420         kli18n("<reaction text>"),
0421         kli18n("React to the message with the given text"),
0422     },
0423     Action{
0424         QStringLiteral("ban"),
0425         [](const QString &text, NeoChatRoom *room) {
0426             auto parts = text.split(QLatin1String(" "));
0427             static const QRegularExpression mxidRegex(
0428                 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
0429             auto regexMatch = mxidRegex.match(parts[0]);
0430             if (!regexMatch.hasMatch()) {
0431                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
0432                 return QString();
0433             }
0434             auto state = room->currentState().get<RoomMemberEvent>(parts[0]);
0435             if (state && state->membership() == Membership::Ban) {
0436                 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already banned from this room.", "%1 is already banned from this room.", text));
0437                 return QString();
0438             }
0439             auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
0440             if (!plEvent) {
0441                 return QString();
0442             }
0443             if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
0444                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to ban users from this room."));
0445                 return QString();
0446             }
0447             if (plEvent->powerLevelForUser(room->localUser()->id()) <= plEvent->powerLevelForUser(parts[0])) {
0448                 Q_EMIT room->showMessage(
0449                     NeoChatRoom::Error,
0450                     i18nc("You are not allowed to ban <username> from this room.", "You are not allowed to ban %1 from this room.", parts[0]));
0451                 return QString();
0452             }
0453             room->ban(parts[0], parts.size() > 1 ? parts.mid(1).join(" ") : QString());
0454             Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was banned from this room.", "%1 was banned from this room.", parts[0]));
0455             return QString();
0456         },
0457         false,
0458         std::nullopt,
0459         kli18n("<user id> [<reason>]"),
0460         kli18n("Bans the given user"),
0461     },
0462     Action{
0463         QStringLiteral("unban"),
0464         [](const QString &text, NeoChatRoom *room) {
0465             static const QRegularExpression mxidRegex(
0466                 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
0467             auto regexMatch = mxidRegex.match(text);
0468             if (!regexMatch.hasMatch()) {
0469                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text));
0470                 return QString();
0471             }
0472             auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
0473             if (!plEvent) {
0474                 return QString();
0475             }
0476             if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
0477                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to unban users from this room."));
0478                 return QString();
0479             }
0480             auto state = room->currentState().get<RoomMemberEvent>(text);
0481             if (state && state->membership() != Membership::Ban) {
0482                 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is not banned from this room.", "%1 is not banned from this room.", text));
0483                 return QString();
0484             }
0485             room->unban(text);
0486             Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was unbanned from this room.", "%1 was unbanned from this room.", text));
0487 
0488             return QString();
0489         },
0490         false,
0491         std::nullopt,
0492         kli18n("<user id>"),
0493         kli18n("Removes the ban of the given user"),
0494     },
0495     Action{
0496         QStringLiteral("kick"),
0497         [](const QString &text, NeoChatRoom *room) {
0498             auto parts = text.split(QLatin1String(" "));
0499             static const QRegularExpression mxidRegex(
0500                 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
0501             auto regexMatch = mxidRegex.match(parts[0]);
0502             if (!regexMatch.hasMatch()) {
0503                 Q_EMIT room->showMessage(NeoChatRoom::Error,
0504                                          i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", parts[0]));
0505                 return QString();
0506             }
0507             if (parts[0] == room->localUser()->id()) {
0508                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You cannot kick yourself from the room."));
0509                 return QString();
0510             }
0511             if (!room->isMember(parts[0])) {
0512                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not in this room", "%1 is not in this room.", parts[0]));
0513                 return QString();
0514             }
0515             auto plEvent = room->currentState().get<RoomPowerLevelsEvent>();
0516             if (!plEvent) {
0517                 return QString();
0518             }
0519             auto kick = plEvent->kick();
0520             if (plEvent->powerLevelForUser(room->localUser()->id()) < kick) {
0521                 Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to kick users from this room."));
0522                 return QString();
0523             }
0524             if (plEvent->powerLevelForUser(room->localUser()->id()) <= plEvent->powerLevelForUser(parts[0])) {
0525                 Q_EMIT room->showMessage(
0526                     NeoChatRoom::Error,
0527                     i18nc("You are not allowed to kick <username> from this room", "You are not allowed to kick %1 from this room.", parts[0]));
0528                 return QString();
0529             }
0530             room->kickMember(parts[0], parts.size() > 1 ? parts.mid(1).join(" ") : QString());
0531             Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was kicked from this room.", "%1 was kicked from this room.", parts[0]));
0532             return QString();
0533         },
0534         false,
0535         std::nullopt,
0536         kli18n("<user id> [<reason>]"),
0537         kli18n("Removes the user from the room"),
0538     },
0539 };
0540 
0541 int ActionsModel::rowCount(const QModelIndex &parent) const
0542 {
0543     Q_UNUSED(parent);
0544     return actions.size();
0545 }
0546 
0547 QVariant ActionsModel::data(const QModelIndex &index, int role) const
0548 {
0549     if (index.row() < 0 || index.row() >= actions.size()) {
0550         return {};
0551     }
0552     if (role == Prefix) {
0553         return actions[index.row()].prefix;
0554     }
0555     if (role == Description) {
0556         return actions[index.row()].description.toString();
0557     }
0558     if (role == CompletionType) {
0559         return QStringLiteral("action");
0560     }
0561     if (role == Parameters) {
0562         return actions[index.row()].parameters.toString();
0563     }
0564     return {};
0565 }
0566 
0567 QHash<int, QByteArray> ActionsModel::roleNames() const
0568 {
0569     return {
0570         {Prefix, "prefix"},
0571         {Description, "description"},
0572         {CompletionType, "completionType"},
0573     };
0574 }
0575 
0576 QVector<Action> &ActionsModel::allActions() const
0577 {
0578     return actions;
0579 }