2018-06-26 14:09:39 +02:00
|
|
|
#include "CommandController.hpp"
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "Application.hpp"
|
2018-06-28 20:25:37 +02:00
|
|
|
#include "common/SignalVector.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "controllers/accounts/AccountController.hpp"
|
|
|
|
#include "controllers/commands/Command.hpp"
|
|
|
|
#include "controllers/commands/CommandModel.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "debug/Log.hpp"
|
|
|
|
#include "messages/Message.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "messages/MessageBuilder.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "messages/MessageElement.hpp"
|
2018-07-12 10:58:29 +02:00
|
|
|
#include "providers/twitch/TwitchApi.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "providers/twitch/TwitchChannel.hpp"
|
|
|
|
#include "providers/twitch/TwitchServer.hpp"
|
2018-10-26 17:38:17 +02:00
|
|
|
#include "singletons/Emotes.hpp"
|
2018-06-28 19:46:45 +02:00
|
|
|
#include "singletons/Paths.hpp"
|
|
|
|
#include "singletons/Settings.hpp"
|
2019-05-05 16:05:29 +02:00
|
|
|
#include "singletons/Theme.hpp"
|
2018-08-02 14:23:27 +02:00
|
|
|
#include "util/CombinePath.hpp"
|
2018-07-07 10:25:12 +02:00
|
|
|
#include "widgets/dialogs/LogsPopup.hpp"
|
2018-04-30 23:30:05 +02:00
|
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
#define TWITCH_DEFAULT_COMMANDS \
|
|
|
|
{ \
|
|
|
|
"/help", "/w", "/me", "/disconnect", "/mods", "/color", "/ban", \
|
|
|
|
"/unban", "/timeout", "/untimeout", "/slow", "/slowoff", \
|
|
|
|
"/r9kbeta", "/r9kbetaoff", "/emoteonly", "/emoteonlyoff", \
|
|
|
|
"/clear", "/subscribers", "/subscribersoff", "/followers", \
|
2019-06-03 17:56:54 +02:00
|
|
|
"/followersoff", "/user" \
|
2018-06-24 13:56:56 +02:00
|
|
|
}
|
|
|
|
|
2019-05-05 16:05:29 +02:00
|
|
|
namespace {
|
|
|
|
using namespace chatterino;
|
|
|
|
|
2019-05-07 20:21:08 +02:00
|
|
|
static const QStringList whisperCommands{"/w", ".w"};
|
|
|
|
|
2019-05-05 16:05:29 +02:00
|
|
|
void sendWhisperMessage(const QString &text)
|
|
|
|
{
|
2019-05-07 20:21:08 +02:00
|
|
|
// (hemirt) pajlada: "we should not be sending whispers through jtv, but
|
|
|
|
// rather to your own username"
|
2019-05-05 16:05:29 +02:00
|
|
|
auto app = getApp();
|
2019-05-05 16:24:14 +02:00
|
|
|
app->twitch.server->sendMessage("jtv", text.simplified());
|
2019-05-05 16:05:29 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 20:21:08 +02:00
|
|
|
bool appendWhisperMessageWordsLocally(const QStringList &words)
|
2019-05-05 16:05:29 +02:00
|
|
|
{
|
|
|
|
auto app = getApp();
|
|
|
|
|
|
|
|
MessageBuilder b;
|
|
|
|
|
|
|
|
b.emplace<TimestampElement>();
|
|
|
|
b.emplace<TextElement>(app->accounts->twitch.getCurrent()->getUserName(),
|
|
|
|
MessageElementFlag::Text, MessageColor::Text,
|
|
|
|
FontStyle::ChatMediumBold);
|
|
|
|
b.emplace<TextElement>("->", MessageElementFlag::Text,
|
|
|
|
getApp()->themes->messages.textColors.system);
|
|
|
|
b.emplace<TextElement>(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<EmotePtr>{};
|
|
|
|
for (int i = 2; i < words.length(); i++)
|
|
|
|
{
|
|
|
|
{ // twitch emote
|
|
|
|
auto it = accemotes.emotes.find({words[i]});
|
|
|
|
if (it != accemotes.emotes.end())
|
|
|
|
{
|
|
|
|
b.emplace<EmoteElement>(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<EmoteElement>(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<EmoteElement>(emote,
|
|
|
|
MessageElementFlag::EmojiAll);
|
|
|
|
}
|
|
|
|
void operator()(const QString &string,
|
|
|
|
MessageBuilder &b) const
|
|
|
|
{
|
|
|
|
auto linkString = b.matchLink(string);
|
|
|
|
if (linkString.isEmpty())
|
|
|
|
{
|
|
|
|
b.emplace<TextElement>(string,
|
|
|
|
MessageElementFlag::Text);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
b.addLink(string, linkString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} visitor;
|
|
|
|
boost::apply_visitor([&b](auto &&arg) { visitor(arg, b); },
|
|
|
|
variant);
|
|
|
|
} // emoji/text
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
b->flags.set(MessageFlag::DoNotTriggerNotification);
|
|
|
|
b->flags.set(MessageFlag::Whisper);
|
|
|
|
auto messagexD = b.release();
|
|
|
|
|
|
|
|
app->twitch.server->whispersChannel->addMessage(messagexD);
|
|
|
|
|
|
|
|
auto overrideFlags = boost::optional<MessageFlags>(messagexD->flags);
|
|
|
|
overrideFlags->set(MessageFlag::DoNotLog);
|
|
|
|
|
|
|
|
if (getSettings()->inlineWhispers)
|
|
|
|
{
|
|
|
|
app->twitch.server->forEachChannel(
|
|
|
|
[&messagexD, overrideFlags](ChannelPtr _channel) {
|
|
|
|
_channel->addMessage(messagexD, overrideFlags);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-07 20:21:08 +02:00
|
|
|
bool appendWhisperMessageStringLocally(const QString &textNoEmoji)
|
2019-05-05 16:05:29 +02:00
|
|
|
{
|
|
|
|
QString text = getApp()->emotes->emojis.replaceShortCodes(textNoEmoji);
|
|
|
|
QStringList words = text.split(' ', QString::SkipEmptyParts);
|
|
|
|
|
|
|
|
if (words.length() == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString commandName = words[0];
|
|
|
|
|
2019-05-07 20:21:08 +02:00
|
|
|
if (whisperCommands.contains(commandName, Qt::CaseInsensitive))
|
2019-05-05 16:05:29 +02:00
|
|
|
{
|
|
|
|
if (words.length() > 2)
|
|
|
|
{
|
2019-05-07 20:21:08 +02:00
|
|
|
return appendWhisperMessageWordsLocally(words);
|
2019-05-05 16:05:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2018-04-30 23:30:05 +02:00
|
|
|
namespace chatterino {
|
|
|
|
|
2018-11-03 13:37:09 +01:00
|
|
|
void CommandController::initialize(Settings &, Paths &paths)
|
2018-04-30 23:30:05 +02:00
|
|
|
{
|
2018-11-03 13:37:09 +01:00
|
|
|
// Update commands map when the vector of commands has been updated
|
2018-04-30 23:30:05 +02:00
|
|
|
auto addFirstMatchToMap = [this](auto args) {
|
2018-07-06 18:10:21 +02:00
|
|
|
this->commandsMap_.remove(args.item.name);
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2019-07-31 22:29:07 +02:00
|
|
|
for (const Command &cmd : this->items_)
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
|
|
|
if (cmd.name == args.item.name)
|
|
|
|
{
|
2018-07-06 18:10:21 +02:00
|
|
|
this->commandsMap_[cmd.name] = cmd;
|
2018-04-30 23:30:05 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
|
|
|
|
int maxSpaces = 0;
|
|
|
|
|
2019-07-31 22:29:07 +02:00
|
|
|
for (const Command &cmd : this->items_)
|
2018-11-03 14:52:38 +01:00
|
|
|
{
|
|
|
|
auto localMaxSpaces = cmd.name.count(' ');
|
|
|
|
if (localMaxSpaces > maxSpaces)
|
|
|
|
{
|
|
|
|
maxSpaces = localMaxSpaces;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this->maxSpaces_ = maxSpaces;
|
2018-04-30 23:30:05 +02:00
|
|
|
};
|
2018-11-03 13:37:09 +01:00
|
|
|
this->items_.itemInserted.connect(addFirstMatchToMap);
|
|
|
|
this->items_.itemRemoved.connect(addFirstMatchToMap);
|
|
|
|
|
|
|
|
// Initialize setting manager for commands.json
|
|
|
|
auto path = combinePath(paths.settingsDirectory, "commands.json");
|
|
|
|
this->sm_ = std::make_shared<pajlada::Settings::SettingManager>();
|
|
|
|
this->sm_->setPath(path.toStdString());
|
|
|
|
|
|
|
|
// Delayed initialization of the setting storing all commands
|
|
|
|
this->commandsSetting_.reset(
|
|
|
|
new pajlada::Settings::Setting<std::vector<Command>>("/commands",
|
|
|
|
this->sm_));
|
|
|
|
|
|
|
|
// Update the setting when the vector of commands has been updated (most
|
|
|
|
// likely from the settings dialog)
|
|
|
|
this->items_.delayedItemsChanged.connect([this] { //
|
|
|
|
this->commandsSetting_->setValue(this->items_.getVector());
|
|
|
|
});
|
|
|
|
|
|
|
|
// Load commands from commands.json
|
|
|
|
this->sm_->load();
|
|
|
|
|
|
|
|
// Add loaded commands to our vector of commands (which will update the map
|
|
|
|
// of commands)
|
|
|
|
for (const auto &command : this->commandsSetting_->getValue())
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2018-11-03 13:37:09 +01:00
|
|
|
this->items_.appendItem(command);
|
2018-04-30 23:30:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandController::save()
|
|
|
|
{
|
2018-11-03 13:37:09 +01:00
|
|
|
this->sm_->save();
|
2018-04-30 23:30:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CommandModel *CommandController::createModel(QObject *parent)
|
|
|
|
{
|
|
|
|
CommandModel *model = new CommandModel(parent);
|
2018-11-03 13:37:09 +01:00
|
|
|
model->init(&this->items_);
|
2018-04-30 23:30:05 +02:00
|
|
|
|
|
|
|
return model;
|
|
|
|
}
|
|
|
|
|
2018-11-03 13:37:09 +01:00
|
|
|
QString CommandController::execCommand(const QString &textNoEmoji,
|
|
|
|
ChannelPtr channel, bool dryRun)
|
2018-04-30 23:30:05 +02:00
|
|
|
{
|
2018-10-27 00:13:55 +02:00
|
|
|
QString text = getApp()->emotes->emojis.replaceShortCodes(textNoEmoji);
|
2018-04-30 23:30:05 +02:00
|
|
|
QStringList words = text.split(' ', QString::SkipEmptyParts);
|
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
std::lock_guard<std::mutex> lock(this->mutex_);
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
if (words.length() == 0)
|
|
|
|
{
|
|
|
|
return text;
|
|
|
|
}
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
QString commandName = words[0];
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
// works in a valid twitch channel and /whispers, etc...
|
|
|
|
if (!dryRun && channel->isTwitchChannel())
|
|
|
|
{
|
2019-05-07 20:21:08 +02:00
|
|
|
if (whisperCommands.contains(commandName, Qt::CaseInsensitive))
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2019-05-05 16:05:29 +02:00
|
|
|
if (words.length() > 2)
|
2018-11-03 14:52:38 +01:00
|
|
|
{
|
2019-05-07 20:21:08 +02:00
|
|
|
appendWhisperMessageWordsLocally(words);
|
2019-05-05 16:05:29 +02:00
|
|
|
sendWhisperMessage(text);
|
2018-06-05 18:51:14 +02:00
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
|
|
|
|
return "";
|
2018-06-05 18:51:14 +02:00
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
}
|
2018-06-05 18:51:14 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
// check if default command exists
|
|
|
|
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
// works only in a valid twitch channel
|
|
|
|
if (!dryRun && twitchChannel != nullptr)
|
|
|
|
{
|
|
|
|
if (commandName == "/debug-args")
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2018-11-03 14:52:38 +01:00
|
|
|
QString msg = QApplication::instance()->arguments().join(' ');
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
channel->addMessage(makeSystemMessage(msg));
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
else if (commandName == "/uptime")
|
|
|
|
{
|
|
|
|
const auto &streamStatus = twitchChannel->accessStreamStatus();
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
QString messageText = streamStatus->live ? streamStatus->uptime
|
|
|
|
: "Channel is not live.";
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
channel->addMessage(makeSystemMessage(messageText));
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
else if (commandName == "/ignore")
|
|
|
|
{
|
|
|
|
if (words.size() < 2)
|
|
|
|
{
|
|
|
|
channel->addMessage(makeSystemMessage("Usage: /ignore [user]"));
|
2018-04-30 23:30:05 +02:00
|
|
|
return "";
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
auto app = getApp();
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
auto user = app->accounts->twitch.getCurrent();
|
|
|
|
auto target = words.at(1);
|
2018-05-12 20:34:13 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
if (user->isAnon())
|
|
|
|
{
|
|
|
|
channel->addMessage(makeSystemMessage(
|
|
|
|
"You must be logged in to ignore someone"));
|
|
|
|
return "";
|
|
|
|
}
|
2018-05-12 20:34:13 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
user->ignore(target,
|
|
|
|
[channel](auto resultCode, const QString &message) {
|
|
|
|
channel->addMessage(makeSystemMessage(message));
|
|
|
|
});
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
else if (commandName == "/unignore")
|
|
|
|
{
|
|
|
|
if (words.size() < 2)
|
|
|
|
{
|
|
|
|
channel->addMessage(
|
|
|
|
makeSystemMessage("Usage: /unignore [user]"));
|
2018-04-30 23:30:05 +02:00
|
|
|
return "";
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
auto app = getApp();
|
2018-05-12 20:34:13 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
auto user = app->accounts->twitch.getCurrent();
|
|
|
|
auto target = words.at(1);
|
2018-05-12 20:34:13 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
if (user->isAnon())
|
|
|
|
{
|
|
|
|
channel->addMessage(makeSystemMessage(
|
|
|
|
"You must be logged in to ignore someone"));
|
|
|
|
return "";
|
|
|
|
}
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
user->unignore(target,
|
|
|
|
[channel](auto resultCode, const QString &message) {
|
|
|
|
channel->addMessage(makeSystemMessage(message));
|
|
|
|
});
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
else if (commandName == "/follow")
|
|
|
|
{
|
|
|
|
if (words.size() < 2)
|
|
|
|
{
|
|
|
|
channel->addMessage(makeSystemMessage("Usage: /follow [user]"));
|
2018-07-12 03:47:37 +02:00
|
|
|
return "";
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
auto app = getApp();
|
2018-07-12 03:47:37 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
auto user = app->accounts->twitch.getCurrent();
|
|
|
|
auto target = words.at(1);
|
2018-07-12 10:52:18 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
if (user->isAnon())
|
|
|
|
{
|
|
|
|
channel->addMessage(makeSystemMessage(
|
|
|
|
"You must be logged in to follow someone"));
|
|
|
|
return "";
|
|
|
|
}
|
2018-07-12 10:52:18 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
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));
|
2018-07-12 03:47:37 +02:00
|
|
|
});
|
2018-11-03 14:52:38 +01:00
|
|
|
});
|
2018-07-12 03:47:37 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
else if (commandName == "/unfollow")
|
|
|
|
{
|
|
|
|
if (words.size() < 2)
|
|
|
|
{
|
|
|
|
channel->addMessage(
|
|
|
|
makeSystemMessage("Usage: /unfollow [user]"));
|
2018-07-12 03:47:37 +02:00
|
|
|
return "";
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
auto app = getApp();
|
2018-07-12 03:47:37 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
auto user = app->accounts->twitch.getCurrent();
|
|
|
|
auto target = words.at(1);
|
2018-07-12 10:52:18 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
if (user->isAnon())
|
|
|
|
{
|
|
|
|
channel->addMessage(makeSystemMessage(
|
|
|
|
"You must be logged in to follow someone"));
|
|
|
|
return "";
|
|
|
|
}
|
2018-07-12 03:47:37 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
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));
|
2018-07-12 03:47:37 +02:00
|
|
|
});
|
2018-11-03 14:52:38 +01:00
|
|
|
});
|
2018-07-12 03:47:37 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
else if (commandName == "/logs")
|
|
|
|
{
|
|
|
|
if (words.size() < 2)
|
|
|
|
{
|
|
|
|
channel->addMessage(
|
|
|
|
makeSystemMessage("Usage: /logs [user] (channel)"));
|
2018-07-07 10:25:12 +02:00
|
|
|
return "";
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
auto app = getApp();
|
2018-07-07 10:25:12 +02:00
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
auto logs = new LogsPopup();
|
2018-11-03 15:37:56 +01:00
|
|
|
QString target = words.at(1);
|
2018-07-07 10:25:12 +02:00
|
|
|
|
2018-11-03 15:37:56 +01:00
|
|
|
if (target.at(0) == '@')
|
2018-11-03 14:52:38 +01:00
|
|
|
{
|
2018-11-03 15:37:56 +01:00
|
|
|
target = target.mid(1);
|
2018-11-03 14:52:38 +01:00
|
|
|
}
|
2018-07-07 10:25:12 +02:00
|
|
|
|
2018-11-03 15:37:56 +01:00
|
|
|
logs->setTargetUserName(target);
|
|
|
|
|
|
|
|
std::shared_ptr<Channel> logsChannel = channel;
|
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
if (words.size() == 3)
|
|
|
|
{
|
|
|
|
QString channelName = words.at(2);
|
2018-11-03 15:37:56 +01:00
|
|
|
if (words.at(2).at(0) == '#')
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2018-11-03 15:37:56 +01:00
|
|
|
channelName = channelName.mid(1);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
2018-11-03 15:37:56 +01:00
|
|
|
|
|
|
|
logs->setChannelName(channelName);
|
|
|
|
|
|
|
|
logsChannel =
|
2018-11-03 14:52:38 +01:00
|
|
|
app->twitch.server->getChannelOrEmpty(channelName);
|
|
|
|
}
|
2018-11-03 15:37:56 +01:00
|
|
|
|
|
|
|
logs->setChannel(logsChannel);
|
|
|
|
|
|
|
|
logs->getLogs();
|
2018-11-03 14:52:38 +01:00
|
|
|
logs->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
logs->show();
|
|
|
|
return "";
|
2018-04-30 23:30:05 +02:00
|
|
|
}
|
2019-06-03 17:56:54 +02:00
|
|
|
else if (commandName == "/user")
|
|
|
|
{
|
|
|
|
if (words.size() < 2)
|
|
|
|
{
|
|
|
|
channel->addMessage(
|
|
|
|
makeSystemMessage("Usage /user [user] (channel)"));
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
QString channelName = channel->getName();
|
|
|
|
if (words.size() > 2)
|
|
|
|
{
|
|
|
|
channelName = words[2];
|
|
|
|
if (channelName[0] == '#')
|
|
|
|
{
|
|
|
|
channelName.remove(0, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
QDesktopServices::openUrl("https://www.twitch.tv/popout/" +
|
|
|
|
channelName + "/viewercard/" + words[1]);
|
|
|
|
return "";
|
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2019-05-08 08:51:14 +02:00
|
|
|
// check if custom command exists
|
|
|
|
const auto it = this->commandsMap_.find(commandName);
|
|
|
|
if (it != this->commandsMap_.end())
|
|
|
|
{
|
|
|
|
return this->execCustomCommand(words, it.value(), dryRun);
|
|
|
|
}
|
2018-11-03 14:52:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
auto maxSpaces = std::min(this->maxSpaces_, words.length() - 1);
|
|
|
|
for (int i = 0; i < maxSpaces; ++i)
|
|
|
|
{
|
|
|
|
commandName += ' ' + words[i + 1];
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2019-05-08 08:51:14 +02:00
|
|
|
const auto it = this->commandsMap_.find(commandName);
|
2018-11-03 14:52:38 +01:00
|
|
|
if (it != this->commandsMap_.end())
|
2018-10-21 13:43:02 +02:00
|
|
|
{
|
2019-05-05 16:05:29 +02:00
|
|
|
return this->execCustomCommand(words, it.value(), dryRun);
|
2018-04-30 23:30:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-03 14:52:38 +01:00
|
|
|
return text;
|
2018-04-30 23:30:05 +02:00
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
QString CommandController::execCustomCommand(const QStringList &words,
|
2019-05-05 16:05:29 +02:00
|
|
|
const Command &command,
|
|
|
|
bool dryRun)
|
2018-04-30 23:30:05 +02:00
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
|
|
|
|
static QRegularExpression parseCommand("(^|[^{])({{)*{(\\d+\\+?)}");
|
|
|
|
|
|
|
|
int lastCaptureEnd = 0;
|
|
|
|
|
|
|
|
auto globalMatch = parseCommand.globalMatch(command.func);
|
|
|
|
int matchOffset = 0;
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
while (true)
|
|
|
|
{
|
2018-08-06 21:17:03 +02:00
|
|
|
QRegularExpressionMatch match =
|
|
|
|
parseCommand.match(command.func, matchOffset);
|
2018-04-30 23:30:05 +02:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (!match.hasMatch())
|
|
|
|
{
|
2018-04-30 23:30:05 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
result += command.func.mid(lastCaptureEnd,
|
|
|
|
match.capturedStart() - lastCaptureEnd + 1);
|
2018-04-30 23:30:05 +02:00
|
|
|
|
|
|
|
lastCaptureEnd = match.capturedEnd();
|
|
|
|
matchOffset = lastCaptureEnd - 1;
|
|
|
|
|
|
|
|
QString wordIndexMatch = match.captured(3);
|
|
|
|
|
|
|
|
bool plus = wordIndexMatch.at(wordIndexMatch.size() - 1) == '+';
|
|
|
|
wordIndexMatch = wordIndexMatch.replace("+", "");
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
int wordIndex = wordIndexMatch.replace("=", "").toInt(&ok);
|
2018-10-21 13:43:02 +02:00
|
|
|
if (!ok || wordIndex == 0)
|
|
|
|
{
|
2018-04-30 23:30:05 +02:00
|
|
|
result += "{" + match.captured(3) + "}";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (words.length() <= wordIndex)
|
|
|
|
{
|
2018-04-30 23:30:05 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (plus)
|
|
|
|
{
|
2018-04-30 23:30:05 +02:00
|
|
|
bool first = true;
|
2018-10-21 13:43:02 +02:00
|
|
|
for (int i = wordIndex; i < words.length(); i++)
|
|
|
|
{
|
|
|
|
if (!first)
|
|
|
|
{
|
2018-04-30 23:30:05 +02:00
|
|
|
result += " ";
|
|
|
|
}
|
|
|
|
result += words[i];
|
|
|
|
first = false;
|
|
|
|
}
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-30 23:30:05 +02:00
|
|
|
result += words[wordIndex];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result += command.func.mid(lastCaptureEnd);
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (result.size() > 0 && result.at(0) == '{')
|
|
|
|
{
|
2018-04-30 23:30:05 +02:00
|
|
|
result = result.mid(1);
|
|
|
|
}
|
|
|
|
|
2019-05-05 16:05:29 +02:00
|
|
|
auto res = result.replace("{{", "{");
|
|
|
|
|
2019-05-07 20:21:08 +02:00
|
|
|
if (dryRun || !appendWhisperMessageStringLocally(res))
|
2019-05-05 16:05:29 +02:00
|
|
|
{
|
|
|
|
return res;
|
|
|
|
}
|
2019-05-07 20:21:08 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
sendWhisperMessage(res);
|
|
|
|
return "";
|
|
|
|
}
|
2018-04-30 23:30:05 +02:00
|
|
|
}
|
|
|
|
|
2018-06-24 13:56:56 +02:00
|
|
|
QStringList CommandController::getDefaultTwitchCommandList()
|
|
|
|
{
|
|
|
|
QStringList l = TWITCH_DEFAULT_COMMANDS;
|
|
|
|
l += "/uptime";
|
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2018-04-30 23:30:05 +02:00
|
|
|
} // namespace chatterino
|