File indexing completed on 2024-12-08 07:33:43
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 "chatbarcache.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"_ls, "#ff5500"_ls, "#ff8000"_ls, "#ffaa00"_ls, "#ffd500"_ls, "#ffff00"_ls, "#d4ff00"_ls, "#aaff00"_ls, "#80ff00"_ls, 0018 "#55ff00"_ls, "#2bff00"_ls, "#00ff00"_ls, "#00ff2b"_ls, "#00ff55"_ls, "#00ff80"_ls, "#00ffaa"_ls, "#00ffd5"_ls, "#00ffff"_ls, 0019 "#00d4ff"_ls, "#00aaff"_ls, "#007fff"_ls, "#0055ff"_ls, "#002bff"_ls, "#0000ff"_ls, "#2a00ff"_ls, "#5500ff"_ls, "#7f00ff"_ls, 0020 "#aa00ff"_ls, "#d400ff"_ls, "#ff00ff"_ls, "#ff00d4"_ls, "#ff00aa"_ls, "#ff0080"_ls, "#ff0055"_ls, "#ff002b"_ls, "#ff0000"_ls}; 0021 0022 auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0023 if (text.isEmpty()) { 0024 Q_EMIT room->showMessage(NeoChatRoom::Info, i18n("Leaving this room.")); 0025 room->connection()->leaveRoom(room); 0026 } else { 0027 QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)")); 0028 auto regexMatch = roomRegex.match(text); 0029 if (!regexMatch.hasMatch()) { 0030 Q_EMIT room->showMessage(NeoChatRoom::Error, 0031 i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text)); 0032 return QString(); 0033 } 0034 auto leaving = room->connection()->room(text); 0035 if (!leaving) { 0036 leaving = room->connection()->roomByAlias(text); 0037 } 0038 if (leaving) { 0039 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Leaving room <roomname>.", "Leaving room %1.", text)); 0040 room->connection()->leaveRoom(leaving); 0041 } else { 0042 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Room <roomname> not found", "Room %1 not found.", text)); 0043 } 0044 } 0045 return QString(); 0046 }; 0047 0048 auto roomNickLambda = [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0049 if (text.isEmpty()) { 0050 Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen.")); 0051 } else { 0052 room->connection()->user()->rename(text, room); 0053 } 0054 return QString(); 0055 }; 0056 0057 QList<ActionsModel::Action> actions{ 0058 Action{ 0059 QStringLiteral("shrug"), 0060 [](const QString &message, NeoChatRoom *, ChatBarCache *) { 0061 return QStringLiteral("¯\\\\_(ツ)_/¯ %1").arg(message); 0062 }, 0063 true, 0064 std::nullopt, 0065 kli18n("<message>"), 0066 kli18n("Prepends ¯\\_(ツ)_/¯ to a plain-text message"), 0067 }, 0068 Action{ 0069 QStringLiteral("lenny"), 0070 [](const QString &message, NeoChatRoom *, ChatBarCache *) { 0071 return QStringLiteral("( ͡° ͜ʖ ͡°) %1").arg(message); 0072 }, 0073 true, 0074 std::nullopt, 0075 kli18n("<message>"), 0076 kli18n("Prepends ( ͡° ͜ʖ ͡°) to a plain-text message"), 0077 }, 0078 Action{ 0079 QStringLiteral("tableflip"), 0080 [](const QString &message, NeoChatRoom *, ChatBarCache *) { 0081 return QStringLiteral("(╯°□°)╯︵ ┻━┻ %1").arg(message); 0082 }, 0083 true, 0084 std::nullopt, 0085 kli18n("<message>"), 0086 kli18n("Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message"), 0087 }, 0088 Action{ 0089 QStringLiteral("unflip"), 0090 [](const QString &message, NeoChatRoom *, ChatBarCache *) { 0091 return QStringLiteral("┬──┬ ノ( ゜-゜ノ) %1").arg(message); 0092 }, 0093 true, 0094 std::nullopt, 0095 kli18n("<message>"), 0096 kli18n("Prepends ┬──┬ ノ( ゜-゜ノ) to a plain-text message"), 0097 }, 0098 Action{ 0099 QStringLiteral("rainbow"), 0100 [](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) { 0101 QString rainbowText; 0102 for (int i = 0; i < text.length(); i++) { 0103 rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i)); 0104 } 0105 // Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML. 0106 room->postMessage(QStringLiteral("/rainbow %1").arg(text), 0107 rainbowText, 0108 RoomMessageEvent::MsgType::Text, 0109 chatBarCache->replyId(), 0110 chatBarCache->editId()); 0111 return QString(); 0112 }, 0113 false, 0114 std::nullopt, 0115 kli18n("<message>"), 0116 kli18n("Sends the given message colored as a rainbow"), 0117 }, 0118 Action{ 0119 QStringLiteral("rainbowme"), 0120 [](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) { 0121 QString rainbowText; 0122 for (int i = 0; i < text.length(); i++) { 0123 rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i)); 0124 } 0125 // Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML. 0126 room->postMessage(QStringLiteral("/rainbow %1").arg(text), 0127 rainbowText, 0128 RoomMessageEvent::MsgType::Emote, 0129 chatBarCache->replyId(), 0130 chatBarCache->editId()); 0131 return QString(); 0132 }, 0133 false, 0134 std::nullopt, 0135 kli18n("<message>"), 0136 kli18n("Sends the given emote colored as a rainbow"), 0137 }, 0138 Action{ 0139 QStringLiteral("plain"), 0140 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0141 room->postMessage(text, text.toHtmlEscaped(), RoomMessageEvent::MsgType::Text, {}, {}); 0142 return QString(); 0143 }, 0144 false, 0145 std::nullopt, 0146 kli18n("<message>"), 0147 kli18n("Sends the given message as plain text"), 0148 }, 0149 Action{ 0150 QStringLiteral("spoiler"), 0151 [](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) { 0152 // Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML. 0153 room->postMessage(QStringLiteral("/spoiler %1").arg(text), 0154 QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text), 0155 RoomMessageEvent::MsgType::Text, 0156 chatBarCache->replyId(), 0157 chatBarCache->editId()); 0158 return QString(); 0159 }, 0160 false, 0161 std::nullopt, 0162 kli18n("<message>"), 0163 kli18n("Sends the given message as a spoiler"), 0164 }, 0165 Action{ 0166 QStringLiteral("me"), 0167 [](const QString &text, NeoChatRoom *, ChatBarCache *) { 0168 return text; 0169 }, 0170 true, 0171 RoomMessageEvent::MsgType::Emote, 0172 kli18n("<message>"), 0173 kli18n("Sends the given emote"), 0174 }, 0175 Action{ 0176 QStringLiteral("notice"), 0177 [](const QString &text, NeoChatRoom *, ChatBarCache *) { 0178 return text; 0179 }, 0180 true, 0181 RoomMessageEvent::MsgType::Notice, 0182 kli18n("<message>"), 0183 kli18n("Sends the given message as a notice"), 0184 }, 0185 Action{ 0186 QStringLiteral("invite"), 0187 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0188 static const QRegularExpression mxidRegex( 0189 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))")); 0190 auto regexMatch = mxidRegex.match(text); 0191 if (!regexMatch.hasMatch()) { 0192 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); 0193 return QString(); 0194 } 0195 const RoomMemberEvent *roomMemberEvent = room->currentState().get<RoomMemberEvent>(text); 0196 if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) { 0197 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text)); 0198 return QString(); 0199 } 0200 if (roomMemberEvent && roomMemberEvent->membership() == Membership::Ban) { 0201 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text)); 0202 return QString(); 0203 } 0204 if (room->localUser()->id() == text) { 0205 Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room.")); 0206 return QString(); 0207 } 0208 if (room->users().contains(room->user(text))) { 0209 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already in this room.", "%1 is already in this room.", text)); 0210 return QString(); 0211 } 0212 room->inviteToRoom(text); 0213 Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> was invited into this room", "%1 was invited into this room", text)); 0214 return QString(); 0215 }, 0216 false, 0217 std::nullopt, 0218 kli18n("<user id>"), 0219 kli18n("Invites the user to this room"), 0220 }, 0221 Action{ 0222 QStringLiteral("join"), 0223 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0224 QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)")); 0225 auto regexMatch = roomRegex.match(text); 0226 if (!regexMatch.hasMatch()) { 0227 Q_EMIT room->showMessage(NeoChatRoom::Error, 0228 i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text)); 0229 return QString(); 0230 } 0231 auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text); 0232 if (targetRoom) { 0233 RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom)); 0234 return QString(); 0235 } 0236 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Joining room <roomname>.", "Joining room %1.", text)); 0237 RoomManager::instance().resolveResource(text, "join"_ls); 0238 return QString(); 0239 }, 0240 false, 0241 std::nullopt, 0242 kli18n("<room alias or id>"), 0243 kli18n("Joins the given room"), 0244 }, 0245 Action{ 0246 QStringLiteral("knock"), 0247 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0248 auto parts = text.split(QLatin1String(" ")); 0249 QString roomName = parts[0]; 0250 QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)")); 0251 auto regexMatch = roomRegex.match(roomName); 0252 if (!regexMatch.hasMatch()) { 0253 Q_EMIT room->showMessage(NeoChatRoom::Error, 0254 i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text)); 0255 return QString(); 0256 } 0257 auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text); 0258 if (targetRoom) { 0259 RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom)); 0260 return QString(); 0261 } 0262 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text)); 0263 auto connection = room->connection(); 0264 const auto knownServer = roomName.mid(roomName.indexOf(":"_ls) + 1); 0265 if (parts.length() >= 2) { 0266 RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer}); 0267 } else { 0268 RoomManager::instance().knockRoom(connection, roomName, QString(), QStringList{knownServer}); 0269 } 0270 return QString(); 0271 }, 0272 false, 0273 std::nullopt, 0274 kli18n("<room alias or id> [<reason>]"), 0275 kli18n("Requests to join the given room"), 0276 }, 0277 Action{ 0278 QStringLiteral("j"), 0279 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0280 QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)")); 0281 auto regexMatch = roomRegex.match(text); 0282 if (!regexMatch.hasMatch()) { 0283 Q_EMIT room->showMessage(NeoChatRoom::Error, 0284 i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text)); 0285 return QString(); 0286 } 0287 if (room->connection()->room(text) || room->connection()->roomByAlias(text)) { 0288 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("You are already in room <roomname>.", "You are already in room %1.", text)); 0289 return QString(); 0290 } 0291 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Joining room <roomname>.", "Joining room %1.", text)); 0292 RoomManager::instance().resolveResource(text, "join"_ls); 0293 return QString(); 0294 }, 0295 false, 0296 std::nullopt, 0297 kli18n("<room alias or id>"), 0298 kli18n("Joins the given room"), 0299 }, 0300 Action{ 0301 QStringLiteral("part"), 0302 leaveRoomLambda, 0303 false, 0304 std::nullopt, 0305 kli18n("[<room alias or id>]"), 0306 kli18n("Leaves the given room or this room, if there is none given"), 0307 }, 0308 Action{ 0309 QStringLiteral("leave"), 0310 leaveRoomLambda, 0311 false, 0312 std::nullopt, 0313 kli18n("[<room alias or id>]"), 0314 kli18n("Leaves the given room or this room, if there is none given"), 0315 }, 0316 Action{ 0317 QStringLiteral("nick"), 0318 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0319 if (text.isEmpty()) { 0320 Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen.")); 0321 } else { 0322 room->connection()->user()->rename(text); 0323 } 0324 return QString(); 0325 }, 0326 false, 0327 std::nullopt, 0328 kli18n("<display name>"), 0329 kli18n("Changes your global display name"), 0330 }, 0331 Action{ 0332 QStringLiteral("roomnick"), 0333 roomNickLambda, 0334 false, 0335 std::nullopt, 0336 kli18n("<display name>"), 0337 kli18n("Changes your display name in this room"), 0338 }, 0339 Action{ 0340 QStringLiteral("myroomnick"), 0341 roomNickLambda, 0342 false, 0343 std::nullopt, 0344 kli18n("<display name>"), 0345 kli18n("Changes your display name in this room"), 0346 }, 0347 Action{ 0348 QStringLiteral("ignore"), 0349 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0350 static const QRegularExpression mxidRegex( 0351 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))")); 0352 auto regexMatch = mxidRegex.match(text); 0353 if (!regexMatch.hasMatch()) { 0354 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); 0355 return QString(); 0356 } 0357 auto user = room->connection()->users()[text]; 0358 if (room->connection()->ignoredUsers().contains(user->id())) { 0359 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<username> is already ignored.", "%1 is already ignored.", text)); 0360 return QString(); 0361 } 0362 if (user) { 0363 room->connection()->addToIgnoredUsers(user); 0364 Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> is now ignored", "%1 is now ignored.", text)); 0365 } else { 0366 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not a known user", "%1 is not a known user.", text)); 0367 } 0368 return QString(); 0369 }, 0370 false, 0371 std::nullopt, 0372 kli18n("<user id>"), 0373 kli18n("Ignores the given user"), 0374 }, 0375 Action{ 0376 QStringLiteral("unignore"), 0377 [](const QString &text, NeoChatRoom *room, ChatBarCache *) { 0378 static const QRegularExpression mxidRegex( 0379 QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))")); 0380 auto regexMatch = mxidRegex.match(text); 0381 if (!regexMatch.hasMatch()) { 0382 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", text)); 0383 return QString(); 0384 } 0385 auto user = room->connection()->users()[text]; 0386 if (user) { 0387 if (!room->connection()->ignoredUsers().contains(user->id())) { 0388 Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<username> is not ignored.", "%1 is not ignored.", text)); 0389 return QString(); 0390 } 0391 room->connection()->removeFromIgnoredUsers(user); 0392 Q_EMIT room->showMessage(NeoChatRoom::Positive, i18nc("<username> is no longer ignored.", "%1 is no longer ignored.", text)); 0393 } else { 0394 Q_EMIT room->showMessage(NeoChatRoom::Error, i18nc("<username> is not a known user", "%1 is not a known user.", text)); 0395 } 0396 return QString(); 0397 }, 0398 false, 0399 std::nullopt, 0400 kli18n("<user id>"), 0401 kli18n("Unignores the given user"), 0402 }, 0403 Action{ 0404 QStringLiteral("react"), 0405 [](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) { 0406 if (chatBarCache->replyId().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(chatBarCache->replyId(), 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, ChatBarCache *) { 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(QLatin1Char(' ')) : 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, ChatBarCache *) { 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, ChatBarCache *) { 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(QLatin1Char(' ')) : 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 QList<Action> &ActionsModel::allActions() const 0577 { 0578 return actions; 0579 }