From b469c2415462c598233c846f56c5ce2349342d0e Mon Sep 17 00:00:00 2001 From: Rasmus Karlsson Date: Sat, 3 Nov 2018 14:52:38 +0100 Subject: [PATCH] Support commands with spaces --- .../commands/CommandController.cpp | 653 +++++++++--------- .../commands/CommandController.hpp | 1 + 2 files changed, 334 insertions(+), 320 deletions(-) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 5989d962a..f792938d6 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -47,6 +47,19 @@ void CommandController::initialize(Settings &, Paths &paths) break; } } + + int maxSpaces = 0; + + for (const Command &cmd : this->items_.getVector()) + { + auto localMaxSpaces = cmd.name.count(' '); + if (localMaxSpaces > maxSpaces) + { + maxSpaces = localMaxSpaces; + } + } + + this->maxSpaces_ = maxSpaces; }; this->items_.itemInserted.connect(addFirstMatchToMap); this->items_.itemRemoved.connect(addFirstMatchToMap); @@ -96,330 +109,330 @@ QString CommandController::execCommand(const QString &textNoEmoji, { QString text = getApp()->emotes->emojis.replaceShortCodes(textNoEmoji); QStringList words = text.split(' ', QString::SkipEmptyParts); - Command command; + std::lock_guard lock(this->mutex_); + + if (words.length() == 0) { - std::lock_guard lock(this->mutex_); - - if (words.length() == 0) - { - return text; - } - - QString commandName = words[0]; - - // works in a valid twitch channel and /whispers, etc... - if (!dryRun && channel->isTwitchChannel()) - { - if (commandName == "/w") - { - if (words.length() <= 2) - { - return ""; - } - - auto app = getApp(); - - MessageBuilder b; - - b.emplace(); - b.emplace( - app->accounts->twitch.getCurrent()->getUserName(), - MessageElementFlag::Text, MessageColor::Text, - FontStyle::ChatMediumBold); - b.emplace("->", MessageElementFlag::Text); - b.emplace(words[1] + ":", MessageElementFlag::Text, - MessageColor::Text, - FontStyle::ChatMediumBold); - - const auto &acc = app->accounts->twitch.getCurrent(); - const auto &accemotes = *acc->accessEmotes(); - const auto &bttvemotes = app->twitch.server->getBttvEmotes(); - const auto &ffzemotes = app->twitch.server->getFfzEmotes(); - auto flags = MessageElementFlags(); - auto emote = boost::optional{}; - for (int i = 2; i < words.length(); i++) - { - { // twitch emote - auto it = accemotes.emotes.find({words[i]}); - if (it != accemotes.emotes.end()) - { - b.emplace( - it->second, MessageElementFlag::TwitchEmote); - continue; - } - } // twitch emote - - { // bttv/ffz emote - if ((emote = bttvemotes.emote({words[i]}))) - { - flags = MessageElementFlag::BttvEmote; - } - else if ((emote = ffzemotes.emote({words[i]}))) - { - flags = MessageElementFlag::FfzEmote; - } - if (emote) - { - b.emplace(emote.get(), flags); - continue; - } - } // bttv/ffz emote - { // emoji/text - for (auto &variant : - app->emotes->emojis.parse(words[i])) - { - constexpr const static struct { - void operator()(EmotePtr emote, - MessageBuilder &b) const - { - b.emplace( - emote, MessageElementFlag::EmojiAll); - } - void operator()(const QString &string, - MessageBuilder &b) const - { - b.emplace( - string, MessageElementFlag::Text); - } - } visitor; - boost::apply_visitor( - [&b](auto &&arg) { visitor(arg, b); }, variant); - } // emoji/text - } - } - - b->flags.set(MessageFlag::DoNotTriggerNotification); - auto messagexD = b.release(); - - app->twitch.server->whispersChannel->addMessage(messagexD); - - app->twitch.server->sendMessage("jtv", text); - - if (getSettings()->inlineWhispers) - { - app->twitch.server->forEachChannel( - [&messagexD](ChannelPtr _channel) { - _channel->addMessage(messagexD); - }); - } - - return ""; - } - } - - // check if default command exists - auto *twitchChannel = dynamic_cast(channel.get()); - - // works only in a valid twitch channel - if (!dryRun && twitchChannel != nullptr) - { - if (commandName == "/debug-args") - { - QString msg = QApplication::instance()->arguments().join(' '); - - channel->addMessage(makeSystemMessage(msg)); - - return ""; - } - else if (commandName == "/uptime") - { - const auto &streamStatus = twitchChannel->accessStreamStatus(); - - QString messageText = streamStatus->live - ? streamStatus->uptime - : "Channel is not live."; - - channel->addMessage(makeSystemMessage(messageText)); - - return ""; - } - else if (commandName == "/ignore") - { - if (words.size() < 2) - { - channel->addMessage( - makeSystemMessage("Usage: /ignore [user]")); - return ""; - } - auto app = getApp(); - - auto user = app->accounts->twitch.getCurrent(); - auto target = words.at(1); - - if (user->isAnon()) - { - channel->addMessage(makeSystemMessage( - "You must be logged in to ignore someone")); - return ""; - } - - user->ignore( - target, [channel](auto resultCode, const QString &message) { - channel->addMessage(makeSystemMessage(message)); - }); - - return ""; - } - else if (commandName == "/unignore") - { - if (words.size() < 2) - { - channel->addMessage( - makeSystemMessage("Usage: /unignore [user]")); - return ""; - } - auto app = getApp(); - - auto user = app->accounts->twitch.getCurrent(); - auto target = words.at(1); - - if (user->isAnon()) - { - channel->addMessage(makeSystemMessage( - "You must be logged in to ignore someone")); - return ""; - } - - user->unignore( - target, [channel](auto resultCode, const QString &message) { - channel->addMessage(makeSystemMessage(message)); - }); - - return ""; - } - else if (commandName == "/follow") - { - if (words.size() < 2) - { - channel->addMessage( - makeSystemMessage("Usage: /follow [user]")); - return ""; - } - auto app = getApp(); - - auto user = app->accounts->twitch.getCurrent(); - auto target = words.at(1); - - if (user->isAnon()) - { - channel->addMessage(makeSystemMessage( - "You must be logged in to follow someone")); - return ""; - } - - TwitchApi::findUserId( - target, [user, channel, target](QString userId) { - if (userId.isEmpty()) - { - channel->addMessage(makeSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->followUser(userId, [channel, target]() { - channel->addMessage(makeSystemMessage( - "You successfully followed " + target)); - }); - }); - - return ""; - } - else if (commandName == "/unfollow") - { - if (words.size() < 2) - { - channel->addMessage( - makeSystemMessage("Usage: /unfollow [user]")); - return ""; - } - auto app = getApp(); - - auto user = app->accounts->twitch.getCurrent(); - auto target = words.at(1); - - if (user->isAnon()) - { - channel->addMessage(makeSystemMessage( - "You must be logged in to follow someone")); - return ""; - } - - TwitchApi::findUserId( - target, [user, channel, target](QString userId) { - if (userId.isEmpty()) - { - channel->addMessage(makeSystemMessage( - "User " + target + " could not be followed!")); - return; - } - user->unfollowUser(userId, [channel, target]() { - channel->addMessage(makeSystemMessage( - "You successfully unfollowed " + target)); - }); - }); - - return ""; - } - else if (commandName == "/logs") - { - if (words.size() < 2) - { - channel->addMessage( - makeSystemMessage("Usage: /logs [user] (channel)")); - return ""; - } - auto app = getApp(); - - auto logs = new LogsPopup(); - QString target; - - if (words.at(1).at(0) == "@") - { - target = words.at(1).mid(1); - } - else - { - target = words.at(1); - } - - if (words.size() == 3) - { - QString channelName = words.at(2); - if (words.at(2).at(0) == "#") - { - channelName = words.at(2).mid(1); - } - auto logsChannel = - app->twitch.server->getChannelOrEmpty(channelName); - if (logsChannel == nullptr) - { - } - else - { - logs->setInfo(logsChannel, target); - } - } - else - { - logs->setInfo(channel, target); - } - logs->setAttribute(Qt::WA_DeleteOnClose); - logs->show(); - return ""; - } - } - - // check if custom command exists - auto it = this->commandsMap_.find(commandName); - if (it == this->commandsMap_.end()) - { - return text; - } - - command = it.value(); + return text; } - return this->execCustomCommand(words, command); + QString commandName = words[0]; + + // works in a valid twitch channel and /whispers, etc... + if (!dryRun && channel->isTwitchChannel()) + { + if (commandName == "/w") + { + if (words.length() <= 2) + { + return ""; + } + + auto app = getApp(); + + MessageBuilder b; + + b.emplace(); + b.emplace( + app->accounts->twitch.getCurrent()->getUserName(), + MessageElementFlag::Text, MessageColor::Text, + FontStyle::ChatMediumBold); + b.emplace("->", MessageElementFlag::Text); + b.emplace(words[1] + ":", MessageElementFlag::Text, + MessageColor::Text, + FontStyle::ChatMediumBold); + + const auto &acc = app->accounts->twitch.getCurrent(); + const auto &accemotes = *acc->accessEmotes(); + const auto &bttvemotes = app->twitch.server->getBttvEmotes(); + const auto &ffzemotes = app->twitch.server->getFfzEmotes(); + auto flags = MessageElementFlags(); + auto emote = boost::optional{}; + for (int i = 2; i < words.length(); i++) + { + { // twitch emote + auto it = accemotes.emotes.find({words[i]}); + if (it != accemotes.emotes.end()) + { + b.emplace( + it->second, MessageElementFlag::TwitchEmote); + continue; + } + } // twitch emote + + { // bttv/ffz emote + if ((emote = bttvemotes.emote({words[i]}))) + { + flags = MessageElementFlag::BttvEmote; + } + else if ((emote = ffzemotes.emote({words[i]}))) + { + flags = MessageElementFlag::FfzEmote; + } + if (emote) + { + b.emplace(emote.get(), flags); + continue; + } + } // bttv/ffz emote + { // emoji/text + for (auto &variant : app->emotes->emojis.parse(words[i])) + { + constexpr const static struct { + void operator()(EmotePtr emote, + MessageBuilder &b) const + { + b.emplace( + emote, MessageElementFlag::EmojiAll); + } + void operator()(const QString &string, + MessageBuilder &b) const + { + b.emplace( + string, MessageElementFlag::Text); + } + } visitor; + boost::apply_visitor( + [&b](auto &&arg) { visitor(arg, b); }, variant); + } // emoji/text + } + } + + b->flags.set(MessageFlag::DoNotTriggerNotification); + auto messagexD = b.release(); + + app->twitch.server->whispersChannel->addMessage(messagexD); + + app->twitch.server->sendMessage("jtv", text); + + if (getSettings()->inlineWhispers) + { + app->twitch.server->forEachChannel( + [&messagexD](ChannelPtr _channel) { + _channel->addMessage(messagexD); + }); + } + + return ""; + } + } + + // check if default command exists + auto *twitchChannel = dynamic_cast(channel.get()); + + // works only in a valid twitch channel + if (!dryRun && twitchChannel != nullptr) + { + if (commandName == "/debug-args") + { + QString msg = QApplication::instance()->arguments().join(' '); + + channel->addMessage(makeSystemMessage(msg)); + + return ""; + } + else if (commandName == "/uptime") + { + const auto &streamStatus = twitchChannel->accessStreamStatus(); + + QString messageText = streamStatus->live ? streamStatus->uptime + : "Channel is not live."; + + channel->addMessage(makeSystemMessage(messageText)); + + return ""; + } + else if (commandName == "/ignore") + { + if (words.size() < 2) + { + channel->addMessage(makeSystemMessage("Usage: /ignore [user]")); + return ""; + } + auto app = getApp(); + + auto user = app->accounts->twitch.getCurrent(); + auto target = words.at(1); + + if (user->isAnon()) + { + channel->addMessage(makeSystemMessage( + "You must be logged in to ignore someone")); + return ""; + } + + user->ignore(target, + [channel](auto resultCode, const QString &message) { + channel->addMessage(makeSystemMessage(message)); + }); + + return ""; + } + else if (commandName == "/unignore") + { + if (words.size() < 2) + { + channel->addMessage( + makeSystemMessage("Usage: /unignore [user]")); + return ""; + } + auto app = getApp(); + + auto user = app->accounts->twitch.getCurrent(); + auto target = words.at(1); + + if (user->isAnon()) + { + channel->addMessage(makeSystemMessage( + "You must be logged in to ignore someone")); + return ""; + } + + user->unignore(target, + [channel](auto resultCode, const QString &message) { + channel->addMessage(makeSystemMessage(message)); + }); + + return ""; + } + else if (commandName == "/follow") + { + if (words.size() < 2) + { + channel->addMessage(makeSystemMessage("Usage: /follow [user]")); + return ""; + } + auto app = getApp(); + + auto user = app->accounts->twitch.getCurrent(); + auto target = words.at(1); + + if (user->isAnon()) + { + channel->addMessage(makeSystemMessage( + "You must be logged in to follow someone")); + return ""; + } + + TwitchApi::findUserId( + target, [user, channel, target](QString userId) { + if (userId.isEmpty()) + { + channel->addMessage(makeSystemMessage( + "User " + target + " could not be followed!")); + return; + } + user->followUser(userId, [channel, target]() { + channel->addMessage(makeSystemMessage( + "You successfully followed " + target)); + }); + }); + + return ""; + } + else if (commandName == "/unfollow") + { + if (words.size() < 2) + { + channel->addMessage( + makeSystemMessage("Usage: /unfollow [user]")); + return ""; + } + auto app = getApp(); + + auto user = app->accounts->twitch.getCurrent(); + auto target = words.at(1); + + if (user->isAnon()) + { + channel->addMessage(makeSystemMessage( + "You must be logged in to follow someone")); + return ""; + } + + TwitchApi::findUserId( + target, [user, channel, target](QString userId) { + if (userId.isEmpty()) + { + channel->addMessage(makeSystemMessage( + "User " + target + " could not be followed!")); + return; + } + user->unfollowUser(userId, [channel, target]() { + channel->addMessage(makeSystemMessage( + "You successfully unfollowed " + target)); + }); + }); + + return ""; + } + else if (commandName == "/logs") + { + if (words.size() < 2) + { + channel->addMessage( + makeSystemMessage("Usage: /logs [user] (channel)")); + return ""; + } + auto app = getApp(); + + auto logs = new LogsPopup(); + QString target; + + if (words.at(1).at(0) == "@") + { + target = words.at(1).mid(1); + } + else + { + target = words.at(1); + } + + if (words.size() == 3) + { + QString channelName = words.at(2); + if (words.at(2).at(0) == "#") + { + channelName = words.at(2).mid(1); + } + auto logsChannel = + app->twitch.server->getChannelOrEmpty(channelName); + if (logsChannel != nullptr) + { + logs->setInfo(logsChannel, target); + } + } + else + { + logs->setInfo(channel, target); + } + logs->setAttribute(Qt::WA_DeleteOnClose); + logs->show(); + return ""; + } + } + + // check if custom command exists + auto it = this->commandsMap_.find(commandName); + if (it != this->commandsMap_.end()) + { + return this->execCustomCommand(words, it.value()); + } + + auto maxSpaces = std::min(this->maxSpaces_, words.length() - 1); + for (int i = 0; i < maxSpaces; ++i) + { + commandName += ' ' + words[i + 1]; + + auto it = this->commandsMap_.find(commandName); + if (it != this->commandsMap_.end()) + { + return this->execCustomCommand(words, it.value()); + } + } + + return text; } QString CommandController::execCustomCommand(const QStringList &words, diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 45be8be32..fb6f9d89d 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -37,6 +37,7 @@ private: void load(Paths &paths); QMap commandsMap_; + int maxSpaces_ = 0; std::mutex mutex_;