added brace wrapping after if and for

This commit is contained in:
fourtf
2018-10-21 13:43:02 +02:00
parent c6e1ec3c71
commit e259b9e39f
138 changed files with 4738 additions and 2237 deletions
+7 -3
View File
@@ -23,12 +23,16 @@ void LinkResolver::getLinkInfo(
auto statusCode = root.value("status").toInt();
QString response = QString();
QString linkString = url;
if (statusCode == 200) {
if (statusCode == 200)
{
response = root.value("tooltip").toString();
if (getSettings()->enableUnshortLinks) {
if (getSettings()->enableUnshortLinks)
{
linkString = root.value("link").toString();
}
} else {
}
else
{
response = root.value("message").toString();
}
successCallback(QUrl::fromPercentEncoding(response.toUtf8()),
+1 -1
View File
@@ -11,7 +11,7 @@ class LinkResolver
{
public:
static void getLinkInfo(const QString url,
std::function<void(QString, Link)> callback);
std::function<void(QString, Link)> callback);
private:
};
+8 -4
View File
@@ -29,7 +29,8 @@ namespace {
auto urlTemplate =
qS("https:") + jsonRoot.value("urlTemplate").toString();
for (auto jsonEmote : jsonEmotes) {
for (auto jsonEmote : jsonEmotes)
{
auto id = EmoteId{jsonEmote.toObject().value("id").toString()};
auto name =
EmoteName{jsonEmote.toObject().value("code").toString()};
@@ -62,7 +63,8 @@ namespace {
auto jsonEmotes = jsonRoot.value("emotes").toArray();
auto urlTemplate = "https:" + jsonRoot.value("urlTemplate").toString();
for (auto jsonEmote_ : jsonEmotes) {
for (auto jsonEmote_ : jsonEmotes)
{
auto jsonEmote = jsonEmote_.toObject();
auto id = EmoteId{jsonEmote.value("id").toString()};
@@ -103,7 +105,8 @@ boost::optional<EmotePtr> BttvEmotes::emote(const EmoteName &name) const
auto emotes = this->global_.get();
auto it = emotes->find(name);
if (it == emotes->end()) return boost::none;
if (it == emotes->end())
return boost::none;
return it->second;
}
@@ -137,7 +140,8 @@ void BttvEmotes::loadChannel(const QString &channelName,
request.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
auto pair = parseChannelEmotes(result.parseJson());
if (pair.first) callback(std::move(pair.second));
if (pair.first)
callback(std::move(pair.second));
return pair.first;
});
@@ -25,7 +25,8 @@ ChatterinoBadges::ChatterinoBadges()
boost::optional<EmotePtr> ChatterinoBadges::getBadge(const UserName &username)
{
auto it = badgeMap.find(username.string);
if (it != badgeMap.end()) {
if (it != badgeMap.end())
{
return emotes[it->second];
}
return boost::none;
@@ -41,7 +42,8 @@ void ChatterinoBadges::loadChatterinoBadges()
req.onSuccess([this](auto result) -> Outcome {
auto jsonRoot = result.parseJson();
int index = 0;
for (const auto &jsonBadge_ : jsonRoot.value("badges").toArray()) {
for (const auto &jsonBadge_ : jsonRoot.value("badges").toArray())
{
auto jsonBadge = jsonBadge_.toObject();
auto emote = Emote{
EmoteName{}, ImageSet{Url{jsonBadge.value("image").toString()}},
@@ -49,7 +51,8 @@ void ChatterinoBadges::loadChatterinoBadges()
emotes.push_back(std::make_shared<const Emote>(std::move(emote)));
for (const auto &user : jsonBadge.value("users").toArray()) {
for (const auto &user : jsonBadge.value("users").toArray())
{
badgeMap[user.toString()] = index;
}
++index;
+84 -41
View File
@@ -29,11 +29,15 @@ namespace {
bool messenger;
} capabilities;
if (!shortCode.isEmpty()) {
if (!shortCode.isEmpty())
{
emojiData->shortCodes.push_back(shortCode);
} else {
}
else
{
const auto &shortCodes = unparsedEmoji["short_names"];
for (const auto &shortCode : shortCodes.GetArray()) {
for (const auto &shortCode : shortCodes.GetArray())
{
emojiData->shortCodes.emplace_back(shortCode.GetString());
}
}
@@ -49,39 +53,50 @@ namespace {
rj::getSafe(unparsedEmoji, "has_img_facebook", capabilities.facebook);
rj::getSafe(unparsedEmoji, "has_img_messenger", capabilities.messenger);
if (capabilities.apple) {
if (capabilities.apple)
{
emojiData->capabilities.insert("Apple");
}
if (capabilities.google) {
if (capabilities.google)
{
emojiData->capabilities.insert("Google");
}
if (capabilities.twitter) {
if (capabilities.twitter)
{
emojiData->capabilities.insert("Twitter");
}
if (capabilities.emojione) {
if (capabilities.emojione)
{
emojiData->capabilities.insert("EmojiOne 3");
}
if (capabilities.facebook) {
if (capabilities.facebook)
{
emojiData->capabilities.insert("Facebook");
}
if (capabilities.messenger) {
if (capabilities.messenger)
{
emojiData->capabilities.insert("Messenger");
}
QStringList unicodeCharacters;
if (!emojiData->nonQualifiedCode.isEmpty()) {
if (!emojiData->nonQualifiedCode.isEmpty())
{
unicodeCharacters =
emojiData->nonQualifiedCode.toLower().split('-');
} else {
}
else
{
unicodeCharacters = emojiData->unifiedCode.toLower().split('-');
}
if (unicodeCharacters.length() < 1) {
if (unicodeCharacters.length() < 1)
{
return;
}
int numUnicodeBytes = 0;
for (const QString &unicodeCharacter : unicodeCharacters) {
for (const QString &unicodeCharacter : unicodeCharacters)
{
unicodeBytes[numUnicodeBytes++] =
QString(unicodeCharacter).toUInt(nullptr, 16);
}
@@ -115,17 +130,20 @@ void Emojis::loadEmojis()
rapidjson::Document root;
rapidjson::ParseResult result = root.Parse(data.toUtf8(), data.length());
if (result.Code() != rapidjson::kParseErrorNone) {
if (result.Code() != rapidjson::kParseErrorNone)
{
log("JSON parse error: {} ({})",
rapidjson::GetParseError_En(result.Code()), result.Offset());
return;
}
for (const auto &unparsedEmoji : root.GetArray()) {
for (const auto &unparsedEmoji : root.GetArray())
{
auto emojiData = std::make_shared<EmojiData>();
parseEmoji(emojiData, unparsedEmoji);
for (const auto &shortCode : emojiData->shortCodes) {
for (const auto &shortCode : emojiData->shortCodes)
{
this->emojiShortCodeToEmoji_.insert(shortCode, emojiData);
this->shortCodes.emplace_back(shortCode);
}
@@ -134,16 +152,19 @@ void Emojis::loadEmojis()
this->emojis.insert(emojiData->unifiedCode, emojiData);
if (unparsedEmoji.HasMember("skin_variations")) {
if (unparsedEmoji.HasMember("skin_variations"))
{
for (const auto &skinVariation :
unparsedEmoji["skin_variations"].GetObject()) {
unparsedEmoji["skin_variations"].GetObject())
{
std::string tone = skinVariation.name.GetString();
const auto &variation = skinVariation.value;
auto variationEmojiData = std::make_shared<EmojiData>();
auto toneNameIt = toneNames.find(tone);
if (toneNameIt == toneNames.end()) {
if (toneNameIt == toneNames.end())
{
log("Tone with key {} does not exist in tone names map",
tone);
continue;
@@ -172,24 +193,28 @@ void Emojis::loadEmojiOne2Capabilities()
file.open(QFile::ReadOnly);
QTextStream in(&file);
while (!in.atEnd()) {
while (!in.atEnd())
{
// Line example: sunglasses 1f60e
QString line = in.readLine();
if (line.at(0) == '#') {
if (line.at(0) == '#')
{
// Ignore lines starting with # (comments)
continue;
}
QStringList parts = line.split(' ');
if (parts.length() < 2) {
if (parts.length() < 2)
{
continue;
}
QString shortCode = parts[0];
auto emojiIt = this->emojiShortCodeToEmoji_.find(shortCode);
if (emojiIt != this->emojiShortCodeToEmoji_.end()) {
if (emojiIt != this->emojiShortCodeToEmoji_.end())
{
std::shared_ptr<EmojiData> emoji = *emojiIt;
emoji->capabilities.insert("EmojiOne 2");
continue;
@@ -199,7 +224,8 @@ void Emojis::loadEmojiOne2Capabilities()
void Emojis::sortEmojis()
{
for (auto &p : this->emojiFirstByte_) {
for (auto &p : this->emojiFirstByte_)
{
std::stable_sort(p.begin(), p.end(),
[](const auto &lhs, const auto &rhs) {
return lhs->value.length() > rhs->value.length();
@@ -239,13 +265,16 @@ void Emojis::loadEmojiSet()
};
// clang-format on
if (emoji->capabilities.count(emojiSetToUse) == 0) {
if (emoji->capabilities.count(emojiSetToUse) == 0)
{
emojiSetToUse = "EmojiOne 3";
}
QString code = emoji->unifiedCode;
if (emojiSetToUse == "EmojiOne 2") {
if (!emoji->nonQualifiedCode.isEmpty()) {
if (emojiSetToUse == "EmojiOne 2")
{
if (!emoji->nonQualifiedCode.isEmpty())
{
code = emoji->nonQualifiedCode;
}
}
@@ -253,7 +282,8 @@ void Emojis::loadEmojiSet()
QString urlPrefix = "https://cdnjs.cloudflare.com/ajax/libs/"
"emojione/2.2.6/assets/png/";
auto it = emojiSets.find(emojiSetToUse);
if (it != emojiSets.end()) {
if (it != emojiSets.end())
{
urlPrefix = it->second;
}
QString url = urlPrefix + code + ".png";
@@ -270,15 +300,18 @@ std::vector<boost::variant<EmotePtr, QString>> Emojis::parse(
auto result = std::vector<boost::variant<EmotePtr, QString>>();
int lastParsedEmojiEndIndex = 0;
for (auto i = 0; i < text.length(); ++i) {
for (auto i = 0; i < text.length(); ++i)
{
const QChar character = text.at(i);
if (character.isLowSurrogate()) {
if (character.isLowSurrogate())
{
continue;
}
auto it = this->emojiFirstByte_.find(character);
if (it == this->emojiFirstByte_.end()) {
if (it == this->emojiFirstByte_.end())
{
// No emoji starts with this character
continue;
}
@@ -291,24 +324,29 @@ std::vector<boost::variant<EmotePtr, QString>> Emojis::parse(
int matchedEmojiLength = 0;
for (const std::shared_ptr<EmojiData> &emoji : possibleEmojis) {
for (const std::shared_ptr<EmojiData> &emoji : possibleEmojis)
{
int emojiExtraCharacters = emoji->value.length() - 1;
if (emojiExtraCharacters > remainingCharacters) {
if (emojiExtraCharacters > remainingCharacters)
{
// It cannot be this emoji, there's not enough space for it
continue;
}
bool match = true;
for (int j = 1; j < emoji->value.length(); ++j) {
if (text.at(i + j) != emoji->value.at(j)) {
for (int j = 1; j < emoji->value.length(); ++j)
{
if (text.at(i + j) != emoji->value.at(j))
{
match = false;
break;
}
}
if (match) {
if (match)
{
matchedEmoji = emoji;
matchedEmojiLength = emoji->value.length();
@@ -316,7 +354,8 @@ std::vector<boost::variant<EmotePtr, QString>> Emojis::parse(
}
}
if (matchedEmojiLength == 0) {
if (matchedEmojiLength == 0)
{
continue;
}
@@ -326,7 +365,8 @@ std::vector<boost::variant<EmotePtr, QString>> Emojis::parse(
int charactersFromLastParsedEmoji =
currentParsedEmojiFirstIndex - lastParsedEmojiEndIndex;
if (charactersFromLastParsedEmoji > 0) {
if (charactersFromLastParsedEmoji > 0)
{
// Add characters inbetween emojis
result.emplace_back(text.mid(lastParsedEmojiEndIndex,
charactersFromLastParsedEmoji));
@@ -340,7 +380,8 @@ std::vector<boost::variant<EmotePtr, QString>> Emojis::parse(
i += matchedEmojiLength - 1;
}
if (lastParsedEmojiEndIndex < text.length()) {
if (lastParsedEmojiEndIndex < text.length())
{
// Add remaining characters
result.emplace_back(text.mid(lastParsedEmojiEndIndex));
}
@@ -355,7 +396,8 @@ QString Emojis::replaceShortCodes(const QString &text)
int32_t offset = 0;
while (it.hasNext()) {
while (it.hasNext())
{
auto match = it.next();
auto capturedString = match.captured();
@@ -365,7 +407,8 @@ QString Emojis::replaceShortCodes(const QString &text)
auto emojiIt = this->emojiShortCodeToEmoji_.constFind(matchString);
if (emojiIt == this->emojiShortCodeToEmoji_.constEnd()) {
if (emojiIt == this->emojiShortCodeToEmoji_.constEnd())
{
continue;
}
+18 -9
View File
@@ -13,7 +13,8 @@ namespace {
Url getEmoteLink(const QJsonObject &urls, const QString &emoteScale)
{
auto emote = urls.value(emoteScale);
if (emote.isUndefined()) {
if (emote.isUndefined())
{
return {""};
}
@@ -48,10 +49,12 @@ namespace {
auto jsonSets = jsonRoot.value("sets").toObject();
auto emotes = EmoteMap();
for (auto jsonSet : jsonSets) {
for (auto jsonSet : jsonSets)
{
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
for (auto jsonEmoteValue : jsonEmotes) {
for (auto jsonEmoteValue : jsonEmotes)
{
auto jsonEmote = jsonEmoteValue.toObject();
auto name = EmoteName{jsonEmote.value("name").toString()};
@@ -78,10 +81,12 @@ namespace {
auto jsonSets = jsonRoot.value("sets").toObject();
auto emotes = EmoteMap();
for (auto jsonSet : jsonSets) {
for (auto jsonSet : jsonSets)
{
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
for (auto _jsonEmote : jsonEmotes) {
for (auto _jsonEmote : jsonEmotes)
{
auto jsonEmote = _jsonEmote.toObject();
// margins
@@ -120,7 +125,8 @@ boost::optional<EmotePtr> FfzEmotes::emote(const EmoteName &name) const
{
auto emotes = this->global_.get();
auto it = emotes->find(name);
if (it != emotes->end()) return it->second;
if (it != emotes->end())
return it->second;
return boost::none;
}
@@ -156,17 +162,20 @@ void FfzEmotes::loadChannel(const QString &channelName,
request.onSuccess([callback = std::move(callback)](auto result) -> Outcome {
auto pair = parseChannelEmotes(result.parseJson());
if (pair.first) callback(std::move(pair.second));
if (pair.first)
callback(std::move(pair.second));
return pair.first;
});
request.onError([channelName](int result) {
if (result == 203) {
if (result == 203)
{
// User does not have any FFZ emotes
return true;
}
if (result == -2) {
if (result == -2)
{
// TODO: Auto retry in case of a timeout, with a delay
log("Fetching FFZ emotes for channel {} failed due to timeout",
channelName);
+53 -25
View File
@@ -60,7 +60,8 @@ AbstractIrcServer::AbstractIrcServer()
this->falloffCounter_ =
std::min(MAX_FALLOFF_COUNTER, this->falloffCounter_ + 1);
if (!this->readConnection_->isConnected()) {
if (!this->readConnection_->isConnected())
{
log("Trying to reconnect... {}", this->falloffCounter_);
this->connect();
}
@@ -73,10 +74,13 @@ void AbstractIrcServer::connect()
bool separateWriteConnection = this->hasSeparateWriteConnection();
if (separateWriteConnection) {
if (separateWriteConnection)
{
this->initializeConnection(this->writeConnection_.get(), false, true);
this->initializeConnection(this->readConnection_.get(), true, false);
} else {
}
else
{
this->initializeConnection(this->readConnection_.get(), true, true);
}
@@ -85,8 +89,10 @@ void AbstractIrcServer::connect()
std::lock_guard<std::mutex> lock1(this->connectionMutex_);
std::lock_guard<std::mutex> lock2(this->channelMutex);
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
if (auto channel = std::shared_ptr<Channel>(weak.lock())) {
for (std::weak_ptr<Channel> &weak : this->channels.values())
{
if (auto channel = std::shared_ptr<Channel>(weak.lock()))
{
this->readConnection_->sendRaw("JOIN #" + channel->getName());
}
}
@@ -117,9 +123,12 @@ void AbstractIrcServer::sendRawMessage(const QString &rawMessage)
{
std::lock_guard<std::mutex> locker(this->connectionMutex_);
if (this->hasSeparateWriteConnection()) {
if (this->hasSeparateWriteConnection())
{
this->writeConnection_->sendRaw(rawMessage);
} else {
}
else
{
this->readConnection_->sendRaw(rawMessage);
}
}
@@ -136,7 +145,8 @@ std::shared_ptr<Channel> AbstractIrcServer::getOrAddChannel(
// try get channel
ChannelPtr chan = this->getChannelOrEmpty(channelName);
if (chan != Channel::getEmpty()) {
if (chan != Channel::getEmpty())
{
return chan;
}
@@ -144,7 +154,8 @@ std::shared_ptr<Channel> AbstractIrcServer::getOrAddChannel(
// value doesn't exist
chan = this->createChannel(channelName);
if (!chan) {
if (!chan)
{
return Channel::getEmpty();
}
@@ -158,11 +169,13 @@ std::shared_ptr<Channel> AbstractIrcServer::getOrAddChannel(
clojuresInCppAreShit);
this->channels.remove(clojuresInCppAreShit);
if (this->readConnection_) {
if (this->readConnection_)
{
this->readConnection_->sendRaw("PART #" + clojuresInCppAreShit);
}
if (this->writeConnection_) {
if (this->writeConnection_)
{
this->writeConnection_->sendRaw("PART #" + clojuresInCppAreShit);
}
});
@@ -171,11 +184,13 @@ std::shared_ptr<Channel> AbstractIrcServer::getOrAddChannel(
{
std::lock_guard<std::mutex> lock2(this->connectionMutex_);
if (this->readConnection_) {
if (this->readConnection_)
{
this->readConnection_->sendRaw("JOIN #" + channelName);
}
if (this->writeConnection_) {
if (this->writeConnection_)
{
this->writeConnection_->sendRaw("JOIN #" + channelName);
}
}
@@ -192,16 +207,19 @@ std::shared_ptr<Channel> AbstractIrcServer::getChannelOrEmpty(
// try get special channel
ChannelPtr chan = this->getCustomChannel(channelName);
if (chan) {
if (chan)
{
return chan;
}
// value exists
auto it = this->channels.find(channelName);
if (it != this->channels.end()) {
if (it != this->channels.end())
{
chan = it.value().lock();
if (chan) {
if (chan)
{
return chan;
}
}
@@ -216,9 +234,11 @@ void AbstractIrcServer::onConnected()
auto connected = makeSystemMessage("connected to chat");
auto reconnected = makeSystemMessage("reconnected to chat");
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
for (std::weak_ptr<Channel> &weak : this->channels.values())
{
std::shared_ptr<Channel> chan = weak.lock();
if (!chan) {
if (!chan)
{
continue;
}
@@ -228,7 +248,8 @@ void AbstractIrcServer::onConnected()
snapshot[snapshot.getLength() - 1]->flags.has(
MessageFlag::DisconnectedMessage);
if (replaceMessage) {
if (replaceMessage)
{
chan->replaceMessage(snapshot[snapshot.getLength() - 1],
reconnected);
continue;
@@ -248,9 +269,11 @@ void AbstractIrcServer::onDisconnected()
b->flags.set(MessageFlag::DisconnectedMessage);
auto disconnected = b.release();
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
for (std::weak_ptr<Channel> &weak : this->channels.values())
{
std::shared_ptr<Channel> chan = weak.lock();
if (!chan) {
if (!chan)
{
continue;
}
@@ -279,10 +302,13 @@ void AbstractIrcServer::addFakeMessage(const QString &data)
auto fakeMessage = Communi::IrcMessage::fromData(
data.toUtf8(), this->readConnection_.get());
if (fakeMessage->command() == "PRIVMSG") {
if (fakeMessage->command() == "PRIVMSG")
{
this->privateMessageReceived(
static_cast<Communi::IrcPrivateMessage *>(fakeMessage));
} else {
}
else
{
this->messageReceived(fakeMessage);
}
}
@@ -300,9 +326,11 @@ void AbstractIrcServer::forEachChannel(std::function<void(ChannelPtr)> func)
{
std::lock_guard<std::mutex> lock(this->channelMutex);
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
for (std::weak_ptr<Channel> &weak : this->channels.values())
{
std::shared_ptr<Channel> chan = weak.lock();
if (!chan) {
if (!chan)
{
continue;
}
+8 -4
View File
@@ -9,8 +9,10 @@ IrcConnection::IrcConnection(QObject *parent)
this->pingTimer_.setInterval(5000);
this->pingTimer_.start();
QObject::connect(&this->pingTimer_, &QTimer::timeout, [this] {
if (this->isConnected()) {
if (!this->recentlyReceivedMessage_.load()) {
if (this->isConnected())
{
if (!this->recentlyReceivedMessage_.load())
{
this->sendRaw("PING");
this->reconnectTimer_.start();
}
@@ -22,7 +24,8 @@ IrcConnection::IrcConnection(QObject *parent)
this->reconnectTimer_.setInterval(5000);
this->reconnectTimer_.setSingleShot(true);
QObject::connect(&this->reconnectTimer_, &QTimer::timeout, [this] {
if (this->isConnected()) {
if (this->isConnected())
{
reconnectRequested.invoke();
}
});
@@ -31,7 +34,8 @@ IrcConnection::IrcConnection(QObject *parent)
[this](Communi::IrcMessage *) {
this->recentlyReceivedMessage_ = true;
if (this->reconnectTimer_.isActive()) {
if (this->reconnectTimer_.isActive())
{
this->reconnectTimer_.stop();
}
});
+91 -45
View File
@@ -39,29 +39,35 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
bool isSub, bool isAction)
{
QString channelName;
if (!trimChannelName(target, channelName)) {
if (!trimChannelName(target, channelName))
{
return;
}
auto chan = server.getChannelOrEmpty(channelName);
if (chan->isEmpty()) {
if (chan->isEmpty())
{
return;
}
MessageParseArgs args;
if (isSub) {
if (isSub)
{
args.trimSubscriberUsername = true;
}
if (chan->isBroadcaster()) {
if (chan->isBroadcaster())
{
args.isStaffOrBroadcaster = true;
}
TwitchMessageBuilder builder(chan.get(), _message, args, content, isAction);
if (isSub || !builder.isIgnored()) {
if (isSub) {
if (isSub || !builder.isIgnored())
{
if (isSub)
{
builder->flags.set(MessageFlag::Subscription);
builder->flags.unset(MessageFlag::Highlighted);
}
@@ -69,8 +75,10 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *_message,
auto msg = builder.build();
auto highlighted = msg->flags.has(MessageFlag::Highlighted);
if (!isSub) {
if (highlighted) {
if (!isSub)
{
if (highlighted)
{
server.mentionsChannel->addMessage(msg);
getApp()->highlights->addHighlight(msg);
}
@@ -87,16 +95,19 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
// get twitch channel
QString chanName;
if (!trimChannelName(message->parameter(0), chanName)) {
if (!trimChannelName(message->parameter(0), chanName))
{
return;
}
auto chan = app->twitch.server->getChannelOrEmpty(chanName);
if (auto *twitchChannel = dynamic_cast<TwitchChannel *>(chan.get())) {
if (auto *twitchChannel = dynamic_cast<TwitchChannel *>(chan.get()))
{
// room-id
decltype(tags.find("xD")) it;
if ((it = tags.find("room-id")) != tags.end()) {
if ((it = tags.find("room-id")) != tags.end())
{
auto roomId = it.value().toString();
twitchChannel->setRoomId(roomId);
@@ -106,19 +117,24 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
{
auto roomModes = *twitchChannel->accessRoomModes();
if ((it = tags.find("emote-only")) != tags.end()) {
if ((it = tags.find("emote-only")) != tags.end())
{
roomModes.emoteOnly = it.value() == "1";
}
if ((it = tags.find("subs-only")) != tags.end()) {
if ((it = tags.find("subs-only")) != tags.end())
{
roomModes.submode = it.value() == "1";
}
if ((it = tags.find("slow")) != tags.end()) {
if ((it = tags.find("slow")) != tags.end())
{
roomModes.slowMode = it.value().toInt();
}
if ((it = tags.find("r9k")) != tags.end()) {
if ((it = tags.find("r9k")) != tags.end())
{
roomModes.r9k = it.value() == "1";
}
if ((it = tags.find("broadcaster-lang")) != tags.end()) {
if ((it = tags.find("broadcaster-lang")) != tags.end())
{
roomModes.broadcasterLang = it.value().toString();
}
twitchChannel->setRoomModes(roomModes);
@@ -131,12 +147,14 @@ void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
{
// check parameter count
if (message->parameters().length() < 1) {
if (message->parameters().length() < 1)
{
return;
}
QString chanName;
if (!trimChannelName(message->parameter(0), chanName)) {
if (!trimChannelName(message->parameter(0), chanName))
{
return;
}
@@ -145,7 +163,8 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
// get channel
auto chan = app->twitch.server->getChannelOrEmpty(chanName);
if (chan->isEmpty()) {
if (chan->isEmpty())
{
log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not "
"found",
chanName);
@@ -153,7 +172,8 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
}
// check if the chat has been cleared by a moderator
if (message->parameters().length() == 1) {
if (message->parameters().length() == 1)
{
chan->disableAllMessages();
chan->addMessage(
makeSystemMessage("Chat has been cleared by a moderator."));
@@ -165,12 +185,14 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
QString username = message->parameter(1);
QString durationInSeconds, reason;
QVariant v = message->tag("ban-duration");
if (v.isValid()) {
if (v.isValid())
{
durationInSeconds = v.toString();
}
v = message->tag("ban-reason");
if (v.isValid()) {
if (v.isValid())
{
reason = v.toString();
}
@@ -187,21 +209,25 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
{
QVariant _mod = message->tag("mod");
if (_mod.isValid()) {
if (_mod.isValid())
{
auto app = getApp();
QString channelName;
if (!trimChannelName(message->parameter(0), channelName)) {
if (!trimChannelName(message->parameter(0), channelName))
{
return;
}
auto c = app->twitch.server->getChannelOrEmpty(channelName);
if (c->isEmpty()) {
if (c->isEmpty())
{
return;
}
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(c.get());
if (tc != nullptr) {
if (tc != nullptr)
{
tc->setMod(_mod == "1");
}
}
@@ -220,12 +246,14 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
TwitchMessageBuilder builder(c, message, args, message->parameter(1),
false);
if (!builder.isIgnored()) {
if (!builder.isIgnored())
{
MessagePtr _message = builder.build();
app->twitch.server->lastUserThatWhisperedMe.set(builder.userName);
if (_message->flags.has(MessageFlag::Highlighted)) {
if (_message->flags.has(MessageFlag::Highlighted))
{
app->twitch.server->mentionsChannel->addMessage(_message);
}
@@ -234,7 +262,8 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
auto overrideFlags = boost::optional<MessageFlags>(_message->flags);
overrideFlags->set(MessageFlag::DoNotTriggerNotification);
if (getSettings()->inlineWhispers) {
if (getSettings()->inlineWhispers)
{
app->twitch.server->forEachChannel(
[_message, overrideFlags](ChannelPtr channel) {
channel->addMessage(_message, overrideFlags); //
@@ -254,21 +283,25 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
auto target = parameters[0];
QString msgType = tags.value("msg-id", "").toString();
QString content;
if (parameters.size() >= 2) {
if (parameters.size() >= 2)
{
content = parameters[1];
}
if (msgType == "sub" || msgType == "resub" || msgType == "subgift") {
if (msgType == "sub" || msgType == "resub" || msgType == "subgift")
{
// Sub-specific message. I think it's only allowed for "resub" messages
// atm
if (!content.isEmpty()) {
if (!content.isEmpty())
{
this->addMessage(message, target, content, server, true, false);
}
}
auto it = tags.find("system-msg");
if (it != tags.end()) {
if (it != tags.end())
{
auto b = MessageBuilder(systemMessage,
parseTagString(it.value().toString()));
@@ -277,17 +310,20 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
QString channelName;
if (message->parameters().size() < 1) {
if (message->parameters().size() < 1)
{
return;
}
if (!trimChannelName(message->parameter(0), channelName)) {
if (!trimChannelName(message->parameter(0), channelName))
{
return;
}
auto chan = server.getChannelOrEmpty(channelName);
if (!chan->isEmpty()) {
if (!chan->isEmpty())
{
chan->addMessage(newMessage);
}
}
@@ -300,13 +336,17 @@ void IrcMessageHandler::handleModeMessage(Communi::IrcMessage *message)
auto channel = app->twitch.server->getChannelOrEmpty(
message->parameter(0).remove(0, 1));
if (channel->isEmpty()) {
if (channel->isEmpty())
{
return;
}
if (message->parameter(1) == "+o") {
if (message->parameter(1) == "+o")
{
channel->modList.append(message->parameter(2));
} else if (message->parameter(1) == "-o") {
}
else if (message->parameter(1) == "-o")
{
channel->modList.append(message->parameter(2));
}
}
@@ -317,7 +357,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
MessagePtr msg = makeSystemMessage(message->content());
QString channelName;
if (!trimChannelName(message->target(), channelName)) {
if (!trimChannelName(message->target(), channelName))
{
// Notice wasn't targeted at a single channel, send to all twitch
// channels
app->twitch.server->forEachChannelAndSpecialChannels(
@@ -330,7 +371,8 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
auto channel = app->twitch.server->getChannelOrEmpty(channelName);
if (channel->isEmpty()) {
if (channel->isEmpty())
{
log("[IrcManager:handleNoticeMessage] Channel {} not found in channel "
"manager ",
channelName);
@@ -367,10 +409,12 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(
};
QVariant v = message->tag("msg-id");
if (v.isValid()) {
if (v.isValid())
{
std::string msgID = v.toString().toStdString();
if (readConnectionOnlyIDs.find(msgID) != readConnectionOnlyIDs.end()) {
if (readConnectionOnlyIDs.find(msgID) != readConnectionOnlyIDs.end())
{
return;
}
@@ -388,7 +432,8 @@ void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message)
message->parameter(0).remove(0, 1));
if (TwitchChannel *twitchChannel =
dynamic_cast<TwitchChannel *>(channel.get())) {
dynamic_cast<TwitchChannel *>(channel.get()))
{
twitchChannel->addJoinedUser(message->nick());
}
}
@@ -400,7 +445,8 @@ void IrcMessageHandler::handlePartMessage(Communi::IrcMessage *message)
message->parameter(0).remove(0, 1));
if (TwitchChannel *twitchChannel =
dynamic_cast<TwitchChannel *>(channel.get())) {
dynamic_cast<TwitchChannel *>(channel.get()))
{
twitchChannel->addPartedUser(message->nick());
}
}
+10 -5
View File
@@ -31,7 +31,8 @@ void PartialTwitchUser::getId(std::function<void(QString)> successCallback,
{
assert(!this->username_.isEmpty());
if (caller == nullptr) {
if (caller == nullptr)
{
caller = QThread::currentThread();
}
@@ -42,23 +43,27 @@ void PartialTwitchUser::getId(std::function<void(QString)> successCallback,
request.onSuccess([successCallback](auto result) -> Outcome {
auto root = result.parseJson();
if (!root.value("users").isArray()) {
if (!root.value("users").isArray())
{
log("API Error while getting user id, users is not an array");
return Failure;
}
auto users = root.value("users").toArray();
if (users.size() != 1) {
if (users.size() != 1)
{
log("API Error while getting user id, users array size is not 1");
return Failure;
}
if (!users[0].isObject()) {
if (!users[0].isObject())
{
log("API Error while getting user id, first user is not an object");
return Failure;
}
auto firstUser = users[0].toObject();
auto id = firstUser.value("_id");
if (!id.isString()) {
if (!id.isString())
{
log("API Error: while getting user id, first user object `_id` key "
"is not a "
"string");
+2 -1
View File
@@ -44,7 +44,8 @@ struct ModeChangedAction : PubSubAction {
const char *getModeName() const
{
switch (this->mode) {
switch (this->mode)
{
case Mode::Slow:
return "slow";
case Mode::R9K:
+180 -83
View File
@@ -51,14 +51,16 @@ namespace detail {
{
int numRequestedListens = message["data"]["topics"].Size();
if (this->numListens_ + numRequestedListens > MAX_PUBSUB_LISTENS) {
if (this->numListens_ + numRequestedListens > MAX_PUBSUB_LISTENS)
{
// This PubSubClient is already at its peak listens
return false;
}
this->numListens_ += numRequestedListens;
for (const auto &topic : message["data"]["topics"].GetArray()) {
for (const auto &topic : message["data"]["topics"].GetArray())
{
this->listeners_.emplace_back(
Listener{topic.GetString(), false, false, false});
}
@@ -79,18 +81,22 @@ namespace detail {
{
std::vector<std::string> topics;
for (auto it = this->listeners_.begin();
it != this->listeners_.end();) {
for (auto it = this->listeners_.begin(); it != this->listeners_.end();)
{
const auto &listener = *it;
if (listener.topic.find(prefix) == 0) {
if (listener.topic.find(prefix) == 0)
{
topics.push_back(listener.topic);
it = this->listeners_.erase(it);
} else {
}
else
{
++it;
}
}
if (topics.empty()) {
if (topics.empty())
{
return;
}
@@ -117,8 +123,10 @@ namespace detail {
bool PubSubClient::isListeningToTopic(const std::string &payload)
{
for (const auto &listener : this->listeners_) {
if (listener.topic == payload) {
for (const auto &listener : this->listeners_)
{
if (listener.topic == payload)
{
return true;
}
}
@@ -130,7 +138,8 @@ namespace detail {
{
assert(this->started_);
if (!this->send(pingPayload)) {
if (!this->send(pingPayload))
{
return;
}
@@ -140,11 +149,13 @@ namespace detail {
runAfter(this->websocketClient_.get_io_service(),
std::chrono::seconds(15), [self](auto timer) {
if (!self->started_) {
if (!self->started_)
{
return;
}
if (self->awaitingPong_) {
if (self->awaitingPong_)
{
log("No pong respnose, disconnect!");
// TODO(pajlada): Label this connection as "disconnect
// me"
@@ -153,7 +164,8 @@ namespace detail {
runAfter(this->websocketClient_.get_io_service(),
std::chrono::minutes(5), [self](auto timer) {
if (!self->started_) {
if (!self->started_)
{
return;
}
@@ -167,7 +179,8 @@ namespace detail {
this->websocketClient_.send(this->handle_, payload,
websocketpp::frame::opcode::text, ec);
if (ec) {
if (ec)
{
log("Error sending message {}: {}", payload, ec.message());
// TODO(pajlada): Check which error code happened and maybe
// gracefully handle it
@@ -208,26 +221,30 @@ PubSub::PubSub()
action.mode = ModeChangedAction::Mode::Slow;
action.state = ModeChangedAction::State::On;
if (!data.HasMember("args")) {
if (!data.HasMember("args"))
{
log("Missing required args member");
return;
}
const auto &args = data["args"];
if (!args.IsArray()) {
if (!args.IsArray())
{
log("args member must be an array");
return;
}
if (args.Size() == 0) {
if (args.Size() == 0)
{
log("Missing duration argument in slowmode on");
return;
}
const auto &durationArg = args[0];
if (!durationArg.IsString()) {
if (!durationArg.IsString())
{
log("Duration arg must be a string");
return;
}
@@ -305,17 +322,22 @@ PubSub::PubSub()
getTargetUser(data, action.target);
try {
try
{
const auto &args = getArgs(data);
if (args.Size() < 1) {
if (args.Size() < 1)
{
return;
}
if (!rj::getSafe(args[0], action.target.name)) {
if (!rj::getSafe(args[0], action.target.name))
{
return;
}
} catch (const std::runtime_error &ex) {
}
catch (const std::runtime_error &ex)
{
log("Error parsing moderation action: {}", ex.what());
}
@@ -330,17 +352,22 @@ PubSub::PubSub()
getTargetUser(data, action.target);
try {
try
{
const auto &args = getArgs(data);
if (args.Size() < 1) {
if (args.Size() < 1)
{
return;
}
if (!rj::getSafe(args[0], action.target.name)) {
if (!rj::getSafe(args[0], action.target.name))
{
return;
}
} catch (const std::runtime_error &ex) {
}
catch (const std::runtime_error &ex)
{
log("Error parsing moderation action: {}", ex.what());
}
@@ -356,32 +383,40 @@ PubSub::PubSub()
getCreatedByUser(data, action.source);
getTargetUser(data, action.target);
try {
try
{
const auto &args = getArgs(data);
if (args.Size() < 2) {
if (args.Size() < 2)
{
return;
}
if (!rj::getSafe(args[0], action.target.name)) {
if (!rj::getSafe(args[0], action.target.name))
{
return;
}
QString durationString;
if (!rj::getSafe(args[1], durationString)) {
if (!rj::getSafe(args[1], durationString))
{
return;
}
bool ok;
action.duration = durationString.toUInt(&ok, 10);
if (args.Size() >= 3) {
if (!rj::getSafe(args[2], action.reason)) {
if (args.Size() >= 3)
{
if (!rj::getSafe(args[2], action.reason))
{
return;
}
}
this->signals_.moderation.userBanned.invoke(action);
} catch (const std::runtime_error &ex) {
}
catch (const std::runtime_error &ex)
{
log("Error parsing moderation action: {}", ex.what());
}
};
@@ -393,25 +428,32 @@ PubSub::PubSub()
getCreatedByUser(data, action.source);
getTargetUser(data, action.target);
try {
try
{
const auto &args = getArgs(data);
if (args.Size() < 1) {
if (args.Size() < 1)
{
return;
}
if (!rj::getSafe(args[0], action.target.name)) {
if (!rj::getSafe(args[0], action.target.name))
{
return;
}
if (args.Size() >= 2) {
if (!rj::getSafe(args[1], action.reason)) {
if (args.Size() >= 2)
{
if (!rj::getSafe(args[1], action.reason))
{
return;
}
}
this->signals_.moderation.userBanned.invoke(action);
} catch (const std::runtime_error &ex) {
}
catch (const std::runtime_error &ex)
{
log("Error parsing moderation action: {}", ex.what());
}
};
@@ -425,19 +467,24 @@ PubSub::PubSub()
action.previousState = UnbanAction::Banned;
try {
try
{
const auto &args = getArgs(data);
if (args.Size() < 1) {
if (args.Size() < 1)
{
return;
}
if (!rj::getSafe(args[0], action.target.name)) {
if (!rj::getSafe(args[0], action.target.name))
{
return;
}
this->signals_.moderation.userUnbanned.invoke(action);
} catch (const std::runtime_error &ex) {
}
catch (const std::runtime_error &ex)
{
log("Error parsing moderation action: {}", ex.what());
}
};
@@ -451,19 +498,24 @@ PubSub::PubSub()
action.previousState = UnbanAction::TimedOut;
try {
try
{
const auto &args = getArgs(data);
if (args.Size() < 1) {
if (args.Size() < 1)
{
return;
}
if (!rj::getSafe(args[0], action.target.name)) {
if (!rj::getSafe(args[0], action.target.name))
{
return;
}
this->signals_.moderation.userUnbanned.invoke(action);
} catch (const std::runtime_error &ex) {
}
catch (const std::runtime_error &ex)
{
log("Error parsing moderation action: {}", ex.what());
}
};
@@ -494,7 +546,8 @@ void PubSub::addClient()
websocketpp::lib::error_code ec;
auto con = this->websocketClient.get_connection(TWITCH_PUBSUB_URL, ec);
if (ec) {
if (ec)
{
log("Unable to establish connection: {}", ec.message());
return;
}
@@ -521,7 +574,8 @@ void PubSub::listenToWhispers(std::shared_ptr<TwitchAccount> account)
this->listen(createListenMessage(topics, account));
if (ec) {
if (ec)
{
log("Unable to send message to websocket server: {}", ec.message());
return;
}
@@ -529,7 +583,8 @@ void PubSub::listenToWhispers(std::shared_ptr<TwitchAccount> account)
void PubSub::unlistenAllModerationActions()
{
for (const auto &p : this->clients) {
for (const auto &p : this->clients)
{
const auto &client = p.second;
client->unlistenPrefix("chat_moderator_actions.");
}
@@ -541,11 +596,13 @@ void PubSub::listenToChannelModerationActions(
assert(!channelID.isEmpty());
assert(account != nullptr);
QString userID = account->getUserId();
if (userID.isEmpty()) return;
if (userID.isEmpty())
return;
std::string topic(fS("chat_moderator_actions.{}.{}", userID, channelID));
if (this->isListeningToTopic(topic)) {
if (this->isListeningToTopic(topic))
{
log("We are already listening to topic {}", topic);
return;
}
@@ -565,7 +622,8 @@ void PubSub::listenToTopic(const std::string &topic,
void PubSub::listen(rapidjson::Document &&msg)
{
if (this->tryListen(msg)) {
if (this->tryListen(msg))
{
log("Successfully listened!");
return;
}
@@ -578,9 +636,11 @@ void PubSub::listen(rapidjson::Document &&msg)
bool PubSub::tryListen(rapidjson::Document &msg)
{
log("tryListen with {} clients", this->clients.size());
for (const auto &p : this->clients) {
for (const auto &p : this->clients)
{
const auto &client = p.second;
if (client->listen(msg)) {
if (client->listen(msg))
{
return true;
}
}
@@ -590,9 +650,11 @@ bool PubSub::tryListen(rapidjson::Document &msg)
bool PubSub::isListeningToTopic(const std::string &topic)
{
for (const auto &p : this->clients) {
for (const auto &p : this->clients)
{
const auto &client = p.second;
if (client->isListeningToTopic(topic)) {
if (client->isListeningToTopic(topic))
{
return true;
}
}
@@ -609,13 +671,15 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
rapidjson::ParseResult res = msg.Parse(payload.c_str());
if (!res) {
if (!res)
{
log("Error parsing message '{}' from PubSub: {}", payload,
rapidjson::GetParseError_En(res.Code()));
return;
}
if (!msg.IsObject()) {
if (!msg.IsObject())
{
log("Error parsing message '{}' from PubSub. Root object is not an "
"object",
payload);
@@ -624,28 +688,36 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
std::string type;
if (!rj::getSafe(msg, "type", type)) {
if (!rj::getSafe(msg, "type", type))
{
log("Missing required string member `type` in message root");
return;
}
if (type == "RESPONSE") {
if (type == "RESPONSE")
{
this->handleListenResponse(msg);
} else if (type == "MESSAGE") {
if (!msg.HasMember("data")) {
}
else if (type == "MESSAGE")
{
if (!msg.HasMember("data"))
{
log("Missing required object member `data` in message root");
return;
}
const auto &data = msg["data"];
if (!data.IsObject()) {
if (!data.IsObject())
{
log("Member `data` must be an object");
return;
}
this->handleMessageResponse(data);
} else if (type == "PONG") {
}
else if (type == "PONG")
{
auto clientIt = this->clients.find(hdl);
// If this assert goes off, there's something wrong with the connection
@@ -655,7 +727,9 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
auto &client = *clientIt;
client.second->handlePong();
} else {
}
else
{
log("Unknown message type: {}", type);
}
}
@@ -696,11 +770,14 @@ PubSub::WebsocketContextPtr PubSub::onTLSInit(websocketpp::connection_hdl hdl)
WebsocketContextPtr ctx(
new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1));
try {
try
{
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::single_dh_use);
} catch (const std::exception &e) {
}
catch (const std::exception &e)
{
log("Exception caught in OnTLSInit: {}", e.what());
}
@@ -711,11 +788,13 @@ void PubSub::handleListenResponse(const rapidjson::Document &msg)
{
std::string error;
if (rj::getSafe(msg, "error", error)) {
if (rj::getSafe(msg, "error", error))
{
std::string nonce;
rj::getSafe(msg, "nonce", nonce);
if (error.empty()) {
if (error.empty())
{
log("Successfully listened to nonce {}", nonce);
// Nothing went wrong
return;
@@ -730,14 +809,16 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
{
QString topic;
if (!rj::getSafe(outerData, "topic", topic)) {
if (!rj::getSafe(outerData, "topic", topic))
{
log("Missing required string member `topic` in outerData");
return;
}
std::string payload;
if (!rj::getSafe(outerData, "message", payload)) {
if (!rj::getSafe(outerData, "message", payload))
{
log("Expected string message in outerData");
return;
}
@@ -746,53 +827,69 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
rapidjson::ParseResult res = msg.Parse(payload.c_str());
if (!res) {
if (!res)
{
log("Error parsing message '{}' from PubSub: {}", payload,
rapidjson::GetParseError_En(res.Code()));
return;
}
if (topic.startsWith("whispers.")) {
if (topic.startsWith("whispers."))
{
std::string whisperType;
if (!rj::getSafe(msg, "type", whisperType)) {
if (!rj::getSafe(msg, "type", whisperType))
{
log("Bad whisper data");
return;
}
if (whisperType == "whisper_received") {
if (whisperType == "whisper_received")
{
this->signals_.whisper.received.invoke(msg);
} else if (whisperType == "whisper_sent") {
}
else if (whisperType == "whisper_sent")
{
this->signals_.whisper.sent.invoke(msg);
} else if (whisperType == "thread") {
}
else if (whisperType == "thread")
{
// Handle thread?
} else {
}
else
{
log("Invalid whisper type: {}", whisperType);
assert(false);
return;
}
} else if (topic.startsWith("chat_moderator_actions.")) {
}
else if (topic.startsWith("chat_moderator_actions."))
{
auto topicParts = topic.split(".");
assert(topicParts.length() == 3);
const auto &data = msg["data"];
std::string moderationAction;
if (!rj::getSafe(data, "moderation_action", moderationAction)) {
if (!rj::getSafe(data, "moderation_action", moderationAction))
{
log("Missing moderation action in data: {}", rj::stringify(data));
return;
}
auto handlerIt = this->moderationActionHandlers.find(moderationAction);
if (handlerIt == this->moderationActionHandlers.end()) {
if (handlerIt == this->moderationActionHandlers.end())
{
log("No handler found for moderation action {}", moderationAction);
return;
}
// Invoke handler function
handlerIt->second(data, topicParts[2]);
} else {
}
else
{
log("Unknown topic: {}", topic);
return;
}
+10 -5
View File
@@ -8,13 +8,15 @@ namespace chatterino {
const rapidjson::Value &getArgs(const rapidjson::Value &data)
{
if (!data.HasMember("args")) {
if (!data.HasMember("args"))
{
throw std::runtime_error("Missing member args");
}
const auto &args = data["args"];
if (!args.IsArray()) {
if (!args.IsArray())
{
throw std::runtime_error("args must be an array");
}
@@ -43,12 +45,14 @@ rapidjson::Document createListenMessage(
rapidjson::Value data(rapidjson::kObjectType);
if (account) {
if (account)
{
rj::set(data, "auth_token", account->getOAuthToken(), a);
}
rapidjson::Value topics(rapidjson::kArrayType);
for (const auto &topic : topicsVec) {
for (const auto &topic : topicsVec)
{
rj::add(topics, topic, a);
}
@@ -70,7 +74,8 @@ rapidjson::Document createUnlistenMessage(
rapidjson::Value data(rapidjson::kObjectType);
rapidjson::Value topics(rapidjson::kArrayType);
for (const auto &topic : topicsVec) {
for (const auto &topic : topicsVec)
{
rj::add(topics, topic, a);
}
+4 -2
View File
@@ -32,7 +32,8 @@ void runAfter(boost::asio::io_service &ioService, Duration duration,
timer->expires_from_now(duration);
timer->async_wait([timer, cb](const boost::system::error_code &ec) {
if (ec) {
if (ec)
{
log("Error in runAfter: {}", ec.message());
return;
}
@@ -49,7 +50,8 @@ void runAfter(std::shared_ptr<boost::asio::steady_timer> timer,
timer->expires_from_now(duration);
timer->async_wait([timer, cb](const boost::system::error_code &ec) {
if (ec) {
if (ec)
{
log("Error in runAfter: {}", ec.message());
return;
}
+64 -32
View File
@@ -31,7 +31,8 @@ namespace {
};
auto it = emoteNameReplacements.find(dirtyEmoteCode.string);
if (it != emoteNameReplacements.end()) {
if (it != emoteNameReplacements.end())
{
cleanCode = it.value();
}
@@ -91,7 +92,8 @@ void TwitchAccount::setColor(QColor color)
bool TwitchAccount::setOAuthClient(const QString &newClientID)
{
if (this->oauthClient_.compare(newClientID) == 0) {
if (this->oauthClient_.compare(newClientID) == 0)
{
return false;
}
@@ -102,7 +104,8 @@ bool TwitchAccount::setOAuthClient(const QString &newClientID)
bool TwitchAccount::setOAuthToken(const QString &newOAuthToken)
{
if (this->oauthToken_.compare(newOAuthToken) == 0) {
if (this->oauthToken_.compare(newOAuthToken) == 0)
{
return false;
}
@@ -126,17 +129,20 @@ void TwitchAccount::loadIgnores()
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
req.onSuccess([=](auto result) -> Outcome {
auto document = result.parseRapidJson();
if (!document.IsObject()) {
if (!document.IsObject())
{
return Failure;
}
auto blocksIt = document.FindMember("blocks");
if (blocksIt == document.MemberEnd()) {
if (blocksIt == document.MemberEnd())
{
return Failure;
}
const auto &blocks = blocksIt->value;
if (!blocks.IsArray()) {
if (!blocks.IsArray())
{
return Failure;
}
@@ -144,16 +150,20 @@ void TwitchAccount::loadIgnores()
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
this->ignores_.clear();
for (const auto &block : blocks.GetArray()) {
if (!block.IsObject()) {
for (const auto &block : blocks.GetArray())
{
if (!block.IsObject())
{
continue;
}
auto userIt = block.FindMember("user");
if (userIt == block.MemberEnd()) {
if (userIt == block.MemberEnd())
{
continue;
}
TwitchUser ignoredUser;
if (!rj::getSafe(userIt->value, ignoredUser)) {
if (!rj::getSafe(userIt->value, ignoredUser))
{
log("Error parsing twitch user JSON {}",
rj::stringify(userIt->value));
continue;
@@ -201,14 +211,16 @@ void TwitchAccount::ignoreByID(
req.onSuccess([=](auto result) -> Outcome {
auto document = result.parseRapidJson();
if (!document.IsObject()) {
if (!document.IsObject())
{
onFinished(IgnoreResult_Failed,
"Bad JSON data while ignoring user " + targetName);
return Failure;
}
auto userIt = document.FindMember("user");
if (userIt == document.MemberEnd()) {
if (userIt == document.MemberEnd())
{
onFinished(IgnoreResult_Failed,
"Bad JSON data while ignoring user (missing user) " +
targetName);
@@ -216,7 +228,8 @@ void TwitchAccount::ignoreByID(
}
TwitchUser ignoredUser;
if (!rj::getSafe(userIt->value, ignoredUser)) {
if (!rj::getSafe(userIt->value, ignoredUser))
{
onFinished(IgnoreResult_Failed,
"Bad JSON data while ignoring user (invalid user) " +
targetName);
@@ -226,7 +239,8 @@ void TwitchAccount::ignoreByID(
std::lock_guard<std::mutex> lock(this->ignoresMutex_);
auto res = this->ignores_.insert(ignoredUser);
if (!res.second) {
if (!res.second)
{
const TwitchUser &existingUser = *(res.first);
existingUser.update(ignoredUser);
onFinished(IgnoreResult_AlreadyIgnored,
@@ -303,9 +317,12 @@ void TwitchAccount::checkFollow(const QString targetUserID,
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
req.onError([=](int errorCode) {
if (errorCode == 203) {
if (errorCode == 203)
{
onFinished(FollowResult_NotFollowing);
} else {
}
else
{
onFinished(FollowResult_Failed);
}
@@ -354,7 +371,8 @@ void TwitchAccount::unfollowUser(const QString userID,
request.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
request.onError([successCallback](int code) {
if (code >= 200 && code <= 299) {
if (code >= 200 && code <= 299)
{
successCallback();
}
@@ -384,7 +402,8 @@ void TwitchAccount::loadEmotes()
const auto &clientID = this->getOAuthClient();
const auto &oauthToken = this->getOAuthToken();
if (clientID.isEmpty() || oauthToken.isEmpty()) {
if (clientID.isEmpty() || oauthToken.isEmpty())
{
log("Missing Client ID or OAuth token");
return;
}
@@ -398,9 +417,12 @@ void TwitchAccount::loadEmotes()
req.onError([=](int errorCode) {
log("[TwitchAccount::loadEmotes] Error {}", errorCode);
if (errorCode == 203) {
if (errorCode == 203)
{
// onFinished(FollowResult_NotFollowing);
} else {
}
else
{
// onFinished(FollowResult_Failed);
}
@@ -430,33 +452,38 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
emoteData->allEmoteNames.clear();
auto emoticonSets = root.FindMember("emoticon_sets");
if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject()) {
if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject())
{
log("No emoticon_sets in load emotes response");
return;
}
for (const auto &emoteSetJSON : emoticonSets->value.GetObject()) {
for (const auto &emoteSetJSON : emoticonSets->value.GetObject())
{
auto emoteSet = std::make_shared<EmoteSet>();
emoteSet->key = emoteSetJSON.name.GetString();
this->loadEmoteSetData(emoteSet);
for (const rapidjson::Value &emoteJSON :
emoteSetJSON.value.GetArray()) {
if (!emoteJSON.IsObject()) {
for (const rapidjson::Value &emoteJSON : emoteSetJSON.value.GetArray())
{
if (!emoteJSON.IsObject())
{
log("Emote value was invalid");
return;
}
uint64_t idNumber;
if (!rj::getSafe(emoteJSON, "id", idNumber)) {
if (!rj::getSafe(emoteJSON, "id", idNumber))
{
log("No ID key found in Emote value");
return;
}
QString _code;
if (!rj::getSafe(emoteJSON, "code", _code)) {
if (!rj::getSafe(emoteJSON, "code", _code))
{
log("No code key found in Emote value");
return;
}
@@ -478,13 +505,15 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
{
if (!emoteSet) {
if (!emoteSet)
{
log("null emote set sent");
return;
}
auto staticSetIt = this->staticEmoteSets.find(emoteSet->key);
if (staticSetIt != this->staticEmoteSets.end()) {
if (staticSetIt != this->staticEmoteSets.end())
{
const auto &staticSet = staticSetIt->second;
emoteSet->channelName = staticSet.channelName;
emoteSet->text = staticSet.text;
@@ -503,18 +532,21 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
req.onSuccess([emoteSet](auto result) -> Outcome {
auto root = result.parseRapidJson();
if (!root.IsObject()) {
if (!root.IsObject())
{
return Failure;
}
std::string emoteSetID;
QString channelName;
QString type;
if (!rj::getSafe(root, "channel_name", channelName)) {
if (!rj::getSafe(root, "channel_name", channelName))
{
return Failure;
}
if (!rj::getSafe(root, "type", type)) {
if (!rj::getSafe(root, "type", type))
{
return Failure;
}
+54 -26
View File
@@ -21,7 +21,8 @@ TwitchAccountManager::TwitchAccountManager()
std::shared_ptr<TwitchAccount> TwitchAccountManager::getCurrent()
{
if (!this->currentUser_) {
if (!this->currentUser_)
{
return this->anonymousUser_;
}
@@ -34,7 +35,8 @@ std::vector<QString> TwitchAccountManager::getUsernames() const
std::lock_guard<std::mutex> lock(this->mutex_);
for (const auto &user : this->accounts.getVector()) {
for (const auto &user : this->accounts.getVector())
{
userNames.push_back(user->getUserName());
}
@@ -46,8 +48,10 @@ std::shared_ptr<TwitchAccount> TwitchAccountManager::findUserByUsername(
{
std::lock_guard<std::mutex> lock(this->mutex_);
for (const auto &user : this->accounts.getVector()) {
if (username.compare(user->getUserName(), Qt::CaseInsensitive) == 0) {
for (const auto &user : this->accounts.getVector())
{
if (username.compare(user->getUserName(), Qt::CaseInsensitive) == 0)
{
return user;
}
}
@@ -68,8 +72,10 @@ void TwitchAccountManager::reloadUsers()
bool listUpdated = false;
for (const auto &uid : keys) {
if (uid == "current") {
for (const auto &uid : keys)
{
if (uid == "current")
{
continue;
}
@@ -83,7 +89,8 @@ void TwitchAccountManager::reloadUsers()
"/accounts/" + uid + "/oauthToken");
if (username.empty() || userID.empty() || clientID.empty() ||
oauthToken.empty()) {
oauthToken.empty())
{
continue;
}
@@ -92,28 +99,37 @@ void TwitchAccountManager::reloadUsers()
userData.clientID = qS(clientID).trimmed();
userData.oauthToken = qS(oauthToken).trimmed();
switch (this->addUser(userData)) {
case AddUserResponse::UserAlreadyExists: {
switch (this->addUser(userData))
{
case AddUserResponse::UserAlreadyExists:
{
log("User {} already exists", userData.username);
// Do nothing
} break;
case AddUserResponse::UserValuesUpdated: {
}
break;
case AddUserResponse::UserValuesUpdated:
{
log("User {} already exists, and values updated!",
userData.username);
if (userData.username == this->getCurrent()->getUserName()) {
if (userData.username == this->getCurrent()->getUserName())
{
log("It was the current user, so we need to reconnect "
"stuff!");
this->currentUserChanged.invoke();
}
} break;
case AddUserResponse::UserAdded: {
}
break;
case AddUserResponse::UserAdded:
{
log("Added user {}", userData.username);
listUpdated = true;
} break;
}
break;
}
}
if (listUpdated) {
if (listUpdated)
{
this->userListUpdated.invoke();
}
}
@@ -125,12 +141,15 @@ void TwitchAccountManager::load()
this->currentUsername.connect([this](const auto &newValue, auto) {
QString newUsername(QString::fromStdString(newValue));
auto user = this->findUserByUsername(newUsername);
if (user) {
if (user)
{
log("[AccountManager:currentUsernameChanged] User successfully "
"updated to {}",
newUsername);
this->currentUser_ = user;
} else {
}
else
{
log("[AccountManager:currentUsernameChanged] User successfully "
"updated to anonymous");
this->currentUser_ = this->anonymousUser_;
@@ -142,7 +161,8 @@ void TwitchAccountManager::load()
bool TwitchAccountManager::isLoggedIn() const
{
if (!this->currentUser_) {
if (!this->currentUser_)
{
return false;
}
@@ -156,12 +176,14 @@ bool TwitchAccountManager::removeUser(TwitchAccount *account)
const auto &accs = this->accounts.getVector();
std::string userID(account->getUserId().toStdString());
if (!userID.empty()) {
if (!userID.empty())
{
pajlada::Settings::SettingManager::removeSetting("/accounts/uid" +
userID);
}
if (account->getUserName() == qS(this->currentUsername.getValue())) {
if (account->getUserName() == qS(this->currentUsername.getValue()))
{
// The user that was removed is the current user, log into the anonymous
// user
this->currentUsername = "";
@@ -176,20 +198,26 @@ TwitchAccountManager::AddUserResponse TwitchAccountManager::addUser(
const TwitchAccountManager::UserData &userData)
{
auto previousUser = this->findUserByUsername(userData.username);
if (previousUser) {
if (previousUser)
{
bool userUpdated = false;
if (previousUser->setOAuthClient(userData.clientID)) {
if (previousUser->setOAuthClient(userData.clientID))
{
userUpdated = true;
}
if (previousUser->setOAuthToken(userData.oauthToken)) {
if (previousUser->setOAuthToken(userData.oauthToken))
{
userUpdated = true;
}
if (userUpdated) {
if (userUpdated)
{
return AddUserResponse::UserValuesUpdated;
} else {
}
else
{
return AddUserResponse::UserAlreadyExists;
}
}
+8 -4
View File
@@ -21,25 +21,29 @@ void TwitchApi::findUserId(const QString user,
request.setTimeout(30000);
request.onSuccess([successCallback](auto result) mutable -> Outcome {
auto root = result.parseJson();
if (!root.value("users").isArray()) {
if (!root.value("users").isArray())
{
log("API Error while getting user id, users is not an array");
successCallback("");
return Failure;
}
auto users = root.value("users").toArray();
if (users.size() != 1) {
if (users.size() != 1)
{
log("API Error while getting user id, users array size is not 1");
successCallback("");
return Failure;
}
if (!users[0].isObject()) {
if (!users[0].isObject())
{
log("API Error while getting user id, first user is not an object");
successCallback("");
return Failure;
}
auto firstUser = users[0].toObject();
auto id = firstUser.value("_id");
if (!id.isString()) {
if (!id.isString())
{
log("API Error: while getting user id, first user object `_id` key "
"is not a "
"string");
+8 -4
View File
@@ -24,11 +24,13 @@ void TwitchBadges::loadTwitchBadges()
auto badgeSets = this->badgeSets_.access();
auto jsonSets = root.value("badge_sets").toObject();
for (auto sIt = jsonSets.begin(); sIt != jsonSets.end(); ++sIt) {
for (auto sIt = jsonSets.begin(); sIt != jsonSets.end(); ++sIt)
{
auto key = sIt.key();
auto versions = sIt.value().toObject().value("versions").toObject();
for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt) {
for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt)
{
auto versionObj = vIt.value().toObject();
auto emote = Emote{
@@ -61,9 +63,11 @@ boost::optional<EmotePtr> TwitchBadges::badge(const QString &set,
{
auto badgeSets = this->badgeSets_.access();
auto it = badgeSets->find(set);
if (it != badgeSets->end()) {
if (it != badgeSets->end())
{
auto it2 = it->second.find(version);
if (it2 != it->second.end()) {
if (it2 != it->second.end())
{
return it2->second;
}
}
+106 -52
View File
@@ -34,9 +34,11 @@ namespace {
QJsonArray jsonMessages = jsonRoot.value("messages").toArray();
std::vector<MessagePtr> messages;
if (jsonMessages.empty()) return messages;
if (jsonMessages.empty())
return messages;
for (const auto jsonMessage : jsonMessages) {
for (const auto jsonMessage : jsonMessages)
{
auto content = jsonMessage.toString().toUtf8();
// passing nullptr as the channel makes the message invalid but we
// don't check for that anyways
@@ -46,7 +48,8 @@ namespace {
MessageParseArgs args;
TwitchMessageBuilder builder(channel.get(), privMsg, args);
if (!builder.isIgnored()) {
if (!builder.isIgnored())
{
messages.push_back(builder.build());
}
}
@@ -63,8 +66,10 @@ namespace {
// parse json
QJsonObject jsonCategories = jsonRoot.value("chatters").toObject();
for (const auto &category : categories) {
for (auto jsonCategory : jsonCategories.value(category).toArray()) {
for (const auto &category : categories)
{
for (auto jsonCategory : jsonCategories.value(category).toArray())
{
usernames.insert(jsonCategory.toString());
}
}
@@ -90,7 +95,8 @@ TwitchChannel::TwitchChannel(const QString &name,
log("[TwitchChannel:{}] Opened", name);
this->liveStatusChanged.connect([this]() {
if (this->isLive() == 1) {
if (this->isLive() == 1)
{
}
});
@@ -168,7 +174,8 @@ void TwitchChannel::sendMessage(const QString &message)
{
auto app = getApp();
if (!app->accounts->twitch.isLoggedIn()) {
if (!app->accounts->twitch.isLoggedIn())
{
// XXX: It would be nice if we could add a link here somehow that opened
// the "account manager" dialog
this->addMessage(
@@ -184,13 +191,17 @@ void TwitchChannel::sendMessage(const QString &message)
parsedMessage = parsedMessage.trimmed();
if (parsedMessage.isEmpty()) {
if (parsedMessage.isEmpty())
{
return;
}
if (!this->hasModRights()) {
if (getSettings()->allowDuplicateMessages) {
if (parsedMessage == this->lastSentMessage_) {
if (!this->hasModRights())
{
if (getSettings()->allowDuplicateMessages)
{
if (parsedMessage == this->lastSentMessage_)
{
parsedMessage.append(this->messageSuffix_);
}
}
@@ -199,7 +210,8 @@ void TwitchChannel::sendMessage(const QString &message)
bool messageSent = false;
this->sendMessageSignal.invoke(this->getName(), parsedMessage, messageSent);
if (messageSent) {
if (messageSent)
{
qDebug() << "sent";
this->lastSentMessage_ = parsedMessage;
}
@@ -212,7 +224,8 @@ bool TwitchChannel::isMod() const
void TwitchChannel::setMod(bool value)
{
if (this->mod_ != value) {
if (this->mod_ != value)
{
this->mod_ = value;
this->userStateChanged.invoke();
@@ -235,14 +248,16 @@ void TwitchChannel::addJoinedUser(const QString &user)
{
auto app = getApp();
if (user == app->accounts->twitch.getCurrent()->getUserName() ||
!getSettings()->showJoins.getValue()) {
!getSettings()->showJoins.getValue())
{
return;
}
auto joinedUsers = this->joinedUsers_.access();
joinedUsers->append(user);
if (!this->joinedUsersMergeQueued_) {
if (!this->joinedUsersMergeQueued_)
{
this->joinedUsersMergeQueued_ = true;
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
@@ -263,14 +278,16 @@ void TwitchChannel::addPartedUser(const QString &user)
auto app = getApp();
if (user == app->accounts->twitch.getCurrent()->getUserName() ||
!getSettings()->showJoins.getValue()) {
!getSettings()->showJoins.getValue())
{
return;
}
auto partedUsers = this->partedUsers_.access();
partedUsers->append(user);
if (!this->partedUsersMergeQueued_) {
if (!this->partedUsersMergeQueued_)
{
this->partedUsersMergeQueued_ = true;
QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
@@ -294,7 +311,8 @@ QString TwitchChannel::roomId() const
void TwitchChannel::setRoomId(const QString &id)
{
if (*this->roomID_.accessConst() != id) {
if (*this->roomID_.accessConst() != id)
{
*this->roomID_.access() = id;
this->roomIdChanged.invoke();
this->loadRecentMessages();
@@ -350,7 +368,8 @@ boost::optional<EmotePtr> TwitchChannel::bttvEmote(const EmoteName &name) const
auto emotes = this->bttvEmotes_.get();
auto it = emotes->find(name);
if (it == emotes->end()) return boost::none;
if (it == emotes->end())
return boost::none;
return it->second;
}
@@ -359,7 +378,8 @@ boost::optional<EmotePtr> TwitchChannel::ffzEmote(const EmoteName &name) const
auto emotes = this->ffzEmotes_.get();
auto it = emotes->find(name);
if (it == emotes->end()) return boost::none;
if (it == emotes->end())
return boost::none;
return it->second;
}
@@ -393,25 +413,33 @@ void TwitchChannel::setLive(bool newLiveStatus)
bool gotNewLiveStatus = false;
{
auto guard = this->streamStatus_.access();
if (guard->live != newLiveStatus) {
if (guard->live != newLiveStatus)
{
gotNewLiveStatus = true;
if (newLiveStatus) {
if (newLiveStatus)
{
if (getApp()->notifications->isChannelNotified(
this->getName(), Platform::Twitch)) {
if (Toasts::isEnabled()) {
this->getName(), Platform::Twitch))
{
if (Toasts::isEnabled())
{
getApp()->toasts->sendChannelNotification(
this->getName(), Platform::Twitch);
}
if (getSettings()->notificationPlaySound) {
if (getSettings()->notificationPlaySound)
{
getApp()->notifications->playSound();
}
if (getSettings()->notificationFlashTaskbar) {
if (getSettings()->notificationFlashTaskbar)
{
getApp()->windows->sendAlert();
}
}
auto live = makeSystemMessage(this->getName() + " is live");
this->addMessage(live);
} else {
}
else
{
auto offline =
makeSystemMessage(this->getName() + " is offline");
this->addMessage(offline);
@@ -420,7 +448,8 @@ void TwitchChannel::setLive(bool newLiveStatus)
}
}
if (gotNewLiveStatus) {
if (gotNewLiveStatus)
{
this->liveStatusChanged.invoke();
}
}
@@ -429,7 +458,8 @@ void TwitchChannel::refreshLiveStatus()
{
auto roomID = this->roomId();
if (roomID.isEmpty()) {
if (roomID.isEmpty())
{
log("[TwitchChannel:{}] Refreshing live status (Missing ID)",
this->getName());
this->setLive(false);
@@ -450,7 +480,8 @@ void TwitchChannel::refreshLiveStatus()
request.onSuccess(
[this, weak = weakOf<Channel>(this)](auto result) -> Outcome {
ChannelPtr shared = weak.lock();
if (!shared) return Failure;
if (!shared)
return Failure;
return this->parseLiveStatus(result.parseRapidJson());
});
@@ -460,26 +491,30 @@ void TwitchChannel::refreshLiveStatus()
Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
{
if (!document.IsObject()) {
if (!document.IsObject())
{
log("[TwitchChannel:refreshLiveStatus] root is not an object");
return Failure;
}
if (!document.HasMember("stream")) {
if (!document.HasMember("stream"))
{
log("[TwitchChannel:refreshLiveStatus] Missing stream in root");
return Failure;
}
const auto &stream = document["stream"];
if (!stream.IsObject()) {
if (!stream.IsObject())
{
// Stream is offline (stream is most likely null)
this->setLive(false);
return Failure;
}
if (!stream.HasMember("viewers") || !stream.HasMember("game") ||
!stream.HasMember("channel") || !stream.HasMember("created_at")) {
!stream.HasMember("channel") || !stream.HasMember("created_at"))
{
log("[TwitchChannel:refreshLiveStatus] Missing members in stream");
this->setLive(false);
return Failure;
@@ -487,7 +522,8 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
const rapidjson::Value &streamChannel = stream["channel"];
if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) {
if (!streamChannel.IsObject() || !streamChannel.HasMember("status"))
{
log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in "
"channel");
return Failure;
@@ -507,19 +543,25 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
QString::number(diff % 3600 / 60) + "m";
status->rerun = false;
if (stream.HasMember("stream_type")) {
if (stream.HasMember("stream_type"))
{
status->streamType = stream["stream_type"].GetString();
} else {
}
else
{
status->streamType = QString();
}
if (stream.HasMember("broadcast_platform")) {
if (stream.HasMember("broadcast_platform"))
{
const auto &broadcastPlatformValue = stream["broadcast_platform"];
if (broadcastPlatformValue.IsString()) {
if (broadcastPlatformValue.IsString())
{
const char *broadcastPlatform =
stream["broadcast_platform"].GetString();
if (strcmp(broadcastPlatform, "rerun") == 0) {
if (strcmp(broadcastPlatform, "rerun") == 0)
{
status->rerun = true;
}
}
@@ -546,7 +588,8 @@ void TwitchChannel::loadRecentMessages()
request.onSuccess([weak = weakOf<Channel>(this)](auto result) -> Outcome {
auto shared = weak.lock();
if (!shared) return Failure;
if (!shared)
return Failure;
auto messages = parseRecentMessages(result.parseJson(), shared);
@@ -561,9 +604,11 @@ void TwitchChannel::loadRecentMessages()
void TwitchChannel::refreshPubsub()
{
// listen to moderation actions
if (!this->hasModRights()) return;
if (!this->hasModRights())
return;
auto roomId = this->roomId();
if (roomId.isEmpty()) return;
if (roomId.isEmpty())
return;
auto account = getApp()->accounts->twitch.getCurrent();
getApp()->twitch2->pubsub->listenToChannelModerationActions(roomId,
@@ -575,9 +620,11 @@ void TwitchChannel::refreshChatters()
// setting?
const auto streamStatus = this->accessStreamStatus();
if (getSettings()->onlyFetchChattersForSmallerStreamers) {
if (getSettings()->onlyFetchChattersForSmallerStreamers)
{
if (streamStatus->live &&
streamStatus->viewerCount > getSettings()->smallStreamerLimit) {
streamStatus->viewerCount > getSettings()->smallStreamerLimit)
{
return;
}
}
@@ -591,10 +638,12 @@ void TwitchChannel::refreshChatters()
[this, weak = weakOf<Channel>(this)](auto result) -> Outcome {
// channel still exists?
auto shared = weak.lock();
if (!shared) return Failure;
if (!shared)
return Failure;
auto pair = parseChatters(result.parseJson());
if (pair.first) {
if (pair.first)
{
*this->chatters_.access() = std::move(pair.second);
}
@@ -613,7 +662,8 @@ void TwitchChannel::refreshBadges()
req.onSuccess([this, weak = weakOf<Channel>(this)](auto result) -> Outcome {
auto shared = weak.lock();
if (!shared) return Failure;
if (!shared)
return Failure;
auto badgeSets = this->badgeSets_.access();
@@ -621,12 +671,14 @@ void TwitchChannel::refreshBadges()
auto _ = jsonRoot["badge_sets"].toObject();
for (auto jsonBadgeSet = _.begin(); jsonBadgeSet != _.end();
jsonBadgeSet++) {
jsonBadgeSet++)
{
auto &versions = (*badgeSets)[jsonBadgeSet.key()];
auto _ = jsonBadgeSet->toObject()["versions"].toObject();
for (auto jsonVersion_ = _.begin(); jsonVersion_ != _.end();
jsonVersion_++) {
jsonVersion_++)
{
auto jsonVersion = jsonVersion_->toObject();
auto emote = std::make_shared<Emote>(Emote{
EmoteName{},
@@ -719,9 +771,11 @@ boost::optional<EmotePtr> TwitchChannel::twitchBadge(
{
auto badgeSets = this->badgeSets_.access();
auto it = badgeSets->find(set);
if (it != badgeSets->end()) {
if (it != badgeSets->end())
{
auto it2 = it->second.find(version);
if (it2 != it->second.end()) {
if (it2 != it->second.end())
{
return it2->second;
}
}
+4 -2
View File
@@ -37,7 +37,8 @@ EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id,
// replace regexes
auto it = replacements.find(name);
if (it != replacements.end()) {
if (it != replacements.end())
{
name = it.value();
}
@@ -45,7 +46,8 @@ EmotePtr TwitchEmotes::getOrCreateEmote(const EmoteId &id,
auto cache = this->twitchEmotesCache_.access();
auto shared = (*cache)[id].lock();
if (!shared) {
if (!shared)
{
(*cache)[id] = shared = std::make_shared<Emote>(
Emote{EmoteName{name},
ImageSet{
+2 -1
View File
@@ -5,7 +5,8 @@ namespace chatterino {
bool trimChannelName(const QString &channelName, QString &outChannelName)
{
if (channelName.length() < 2) {
if (channelName.length() < 2)
{
log("channel name length below 2");
return false;
}
+332 -154
View File
@@ -59,8 +59,10 @@ bool TwitchMessageBuilder::isIgnored() const
auto app = getApp();
// TODO(pajlada): Do we need to check if the phrase is valid first?
for (const auto &phrase : app->ignores->phrases.getVector()) {
if (phrase.isBlock() && phrase.isMatch(this->originalMessage_)) {
for (const auto &phrase : app->ignores->phrases.getVector())
{
if (phrase.isBlock() && phrase.isMatch(this->originalMessage_))
{
log("Blocking message because it contains ignored phrase {}",
phrase.getPattern());
return true;
@@ -68,12 +70,15 @@ bool TwitchMessageBuilder::isIgnored() const
}
if (getSettings()->enableTwitchIgnoredUsers &&
this->tags.contains("user-id")) {
this->tags.contains("user-id"))
{
auto sourceUserID = this->tags.value("user-id").toString();
for (const auto &user :
app->accounts->twitch.getCurrent()->getIgnores()) {
if (sourceUserID == user.id) {
app->accounts->twitch.getCurrent()->getIgnores())
{
if (sourceUserID == user.id)
{
log("Blocking message because it's from blocked user {}",
user.name);
return true;
@@ -89,7 +94,8 @@ MessagePtr TwitchMessageBuilder::build()
// PARSING
this->parseUsername();
if (this->userName == this->channel->getName()) {
if (this->userName == this->channel->getName())
{
this->senderIsBroadcaster = true;
}
@@ -104,32 +110,42 @@ MessagePtr TwitchMessageBuilder::build()
// timestamp
bool isPastMsg = this->tags.contains("historical");
if (isPastMsg) {
if (isPastMsg)
{
// This may be architecture dependent(datatype)
qint64 ts = this->tags.value("tmi-sent-ts").toLongLong();
QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(ts);
this->emplace<TimestampElement>(dateTime.time());
} else {
}
else
{
this->emplace<TimestampElement>();
}
bool addModerationElement = true;
if (this->senderIsBroadcaster) {
if (this->senderIsBroadcaster)
{
addModerationElement = false;
} else {
}
else
{
bool hasUserType = this->tags.contains("user-type");
if (hasUserType) {
if (hasUserType)
{
QString userType = this->tags.value("user-type").toString();
if (userType == "mod") {
if (!args.isStaffOrBroadcaster) {
if (userType == "mod")
{
if (!args.isStaffOrBroadcaster)
{
addModerationElement = false;
}
}
}
}
if (addModerationElement) {
if (addModerationElement)
{
this->emplace<TwitchModerationElement>();
}
@@ -144,7 +160,8 @@ MessagePtr TwitchMessageBuilder::build()
// QString bits;
auto iterator = this->tags.find("bits");
if (iterator != this->tags.end()) {
if (iterator != this->tags.end())
{
this->hasBits_ = true;
// bits = iterator.value().toString();
}
@@ -153,10 +170,12 @@ MessagePtr TwitchMessageBuilder::build()
std::vector<std::tuple<int, EmotePtr, EmoteName>> twitchEmotes;
iterator = this->tags.find("emotes");
if (iterator != this->tags.end()) {
if (iterator != this->tags.end())
{
QStringList emoteString = iterator.value().toString().split('/');
for (QString emote : emoteString) {
for (QString emote : emoteString)
{
this->appendTwitchEmote(emote, twitchEmotes);
}
}
@@ -172,8 +191,10 @@ MessagePtr TwitchMessageBuilder::build()
return !((std::get<0>(item) >= pos) &&
std::get<0>(item) < (pos + len));
});
for (auto copy = it; copy != twitchEmotes.end(); ++copy) {
if (std::get<1>(*copy) == nullptr) {
for (auto copy = it; copy != twitchEmotes.end(); ++copy)
{
if (std::get<1>(*copy) == nullptr)
{
log("remem nullptr {}", std::get<2>(*copy).string);
}
}
@@ -184,9 +205,11 @@ MessagePtr TwitchMessageBuilder::build()
};
auto shiftIndicesAfter = [&twitchEmotes](int pos, int by) mutable {
for (auto &item : twitchEmotes) {
for (auto &item : twitchEmotes)
{
auto &index = std::get<0>(item);
if (index >= pos) {
if (index >= pos)
{
index += by;
}
}
@@ -195,16 +218,21 @@ MessagePtr TwitchMessageBuilder::build()
auto addReplEmotes = [&twitchEmotes](const IgnorePhrase &phrase,
const QStringRef &midrepl,
int startIndex) mutable {
if (!phrase.containsEmote()) {
if (!phrase.containsEmote())
{
return;
}
QVector<QStringRef> words = midrepl.split(' ');
int pos = 0;
for (const auto &word : words) {
for (const auto &emote : phrase.getEmotes()) {
if (word == emote.first.string) {
if (emote.second == nullptr) {
for (const auto &word : words)
{
for (const auto &emote : phrase.getEmotes())
{
if (word == emote.first.string)
{
if (emote.second == nullptr)
{
log("emote null {}", emote.first.string);
}
twitchEmotes.push_back(std::tuple<int, EmotePtr, EmoteName>{
@@ -215,19 +243,24 @@ MessagePtr TwitchMessageBuilder::build()
}
};
for (const auto &phrase : phrases) {
if (phrase.isBlock()) {
for (const auto &phrase : phrases)
{
if (phrase.isBlock())
{
continue;
}
if (phrase.isRegex()) {
if (phrase.isRegex())
{
const auto &regex = phrase.getRegex();
if (!regex.isValid()) {
if (!regex.isValid())
{
continue;
}
QRegularExpressionMatch match;
int from = 0;
while ((from = this->originalMessage_.indexOf(regex, from,
&match)) != -1) {
&match)) != -1)
{
int len = match.capturedLength();
auto vret = removeEmotesInRange(from, len, twitchEmotes);
auto mid = this->originalMessage_.mid(from, len);
@@ -236,15 +269,19 @@ MessagePtr TwitchMessageBuilder::build()
int midsize = mid.size();
this->originalMessage_.replace(from, len, mid);
int pos1 = from;
while (pos1 > 0) {
if (this->originalMessage_[pos1 - 1] == ' ') {
while (pos1 > 0)
{
if (this->originalMessage_[pos1 - 1] == ' ')
{
break;
}
--pos1;
}
int pos2 = from + midsize;
while (pos2 < this->originalMessage_.length()) {
if (this->originalMessage_[pos2] == ' ') {
while (pos2 < this->originalMessage_.length())
{
if (this->originalMessage_[pos2] == ' ')
{
break;
}
++pos2;
@@ -255,8 +292,10 @@ MessagePtr TwitchMessageBuilder::build()
auto midExtendedRef =
this->originalMessage_.midRef(pos1, pos2 - pos1);
for (auto &tup : vret) {
if (std::get<1>(tup) == nullptr) {
for (auto &tup : vret)
{
if (std::get<1>(tup) == nullptr)
{
log("v nullptr {}", std::get<2>(tup).string);
continue;
}
@@ -264,9 +303,11 @@ MessagePtr TwitchMessageBuilder::build()
"\\b" + std::get<2>(tup).string + "\\b",
QRegularExpression::UseUnicodePropertiesOption);
auto match = emoteregex.match(midExtendedRef);
if (match.hasMatch()) {
if (match.hasMatch())
{
int last = match.lastCapturedIndex();
for (int i = 0; i <= last; ++i) {
for (int i = 0; i <= last; ++i)
{
std::get<0>(tup) = from + match.capturedStart();
twitchEmotes.push_back(std::move(tup));
}
@@ -277,14 +318,18 @@ MessagePtr TwitchMessageBuilder::build()
from += midsize;
}
} else {
}
else
{
const auto &pattern = phrase.getPattern();
if (pattern.isEmpty()) {
if (pattern.isEmpty())
{
continue;
}
int from = 0;
while ((from = this->originalMessage_.indexOf(
pattern, from, phrase.caseSensitivity())) != -1) {
pattern, from, phrase.caseSensitivity())) != -1)
{
int len = pattern.size();
auto vret = removeEmotesInRange(from, len, twitchEmotes);
auto replace = phrase.getReplace();
@@ -293,15 +338,19 @@ MessagePtr TwitchMessageBuilder::build()
this->originalMessage_.replace(from, len, replace);
int pos1 = from;
while (pos1 > 0) {
if (this->originalMessage_[pos1 - 1] == ' ') {
while (pos1 > 0)
{
if (this->originalMessage_[pos1 - 1] == ' ')
{
break;
}
--pos1;
}
int pos2 = from + replacesize;
while (pos2 < this->originalMessage_.length()) {
if (this->originalMessage_[pos2] == ' ') {
while (pos2 < this->originalMessage_.length())
{
if (this->originalMessage_[pos2] == ' ')
{
break;
}
++pos2;
@@ -312,8 +361,10 @@ MessagePtr TwitchMessageBuilder::build()
auto midExtendedRef =
this->originalMessage_.midRef(pos1, pos2 - pos1);
for (auto &tup : vret) {
if (std::get<1>(tup) == nullptr) {
for (auto &tup : vret)
{
if (std::get<1>(tup) == nullptr)
{
log("v nullptr {}", std::get<2>(tup).string);
continue;
}
@@ -321,9 +372,11 @@ MessagePtr TwitchMessageBuilder::build()
"\\b" + std::get<2>(tup).string + "\\b",
QRegularExpression::UseUnicodePropertiesOption);
auto match = emoteregex.match(midExtendedRef);
if (match.hasMatch()) {
if (match.hasMatch())
{
int last = match.lastCapturedIndex();
for (int i = 0; i <= last; ++i) {
for (int i = 0; i <= last; ++i)
{
std::get<0>(tup) = from + match.capturedStart();
twitchEmotes.push_back(std::move(tup));
}
@@ -365,16 +418,20 @@ void TwitchMessageBuilder::addWords(
auto i = int();
auto currentTwitchEmote = twitchEmotes.begin();
for (const auto &word : words) {
for (const auto &word : words)
{
// check if it's a twitch emote twitch emote
while (currentTwitchEmote != twitchEmotes.end() &&
std::get<0>(*currentTwitchEmote) < i) {
std::get<0>(*currentTwitchEmote) < i)
{
++currentTwitchEmote;
}
if (currentTwitchEmote != twitchEmotes.end() &&
std::get<0>(*currentTwitchEmote) == i) {
std::get<0>(*currentTwitchEmote) == i)
{
auto emoteImage = std::get<1>(*currentTwitchEmote);
if (emoteImage == nullptr) {
if (emoteImage == nullptr)
{
log("emoteImage nullptr {}",
std::get<2>(*currentTwitchEmote).string);
}
@@ -388,15 +445,18 @@ void TwitchMessageBuilder::addWords(
}
// split words
for (auto &variant : getApp()->emotes->emojis.parse(word)) {
for (auto &variant : getApp()->emotes->emojis.parse(word))
{
boost::apply_visitor([&](auto &&arg) { this->addTextOrEmoji(arg); },
variant);
}
for (int j = 0; j < word.size(); j++) {
for (int j = 0; j < word.size(); j++)
{
i++;
if (word.at(j).isHighSurrogate()) {
if (word.at(j).isHighSurrogate())
{
j++;
}
}
@@ -414,7 +474,8 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
{
auto string = QString(string_);
if (this->hasBits_ && this->tryParseCheermote(string)) {
if (this->hasBits_ && this->tryParseCheermote(string))
{
// This string was parsed as a cheermote
return;
}
@@ -424,7 +485,8 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
// Emote name: "forsenPuke" - if string in ignoredEmotes
// Will match emote regardless of source (i.e. bttv, ffz)
// Emote source + name: "bttv:nyanPls"
if (this->tryAppendEmote({string})) {
if (this->tryAppendEmote({string}))
{
// Successfully appended an emote
return;
}
@@ -435,28 +497,37 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
auto textColor = this->action_ ? MessageColor(this->usernameColor_)
: MessageColor(MessageColor::Text);
if (linkString.isEmpty()) {
if (string.startsWith('@')) {
if (linkString.isEmpty())
{
if (string.startsWith('@'))
{
this->emplace<TextElement>(string, MessageElementFlag::BoldUsername,
textColor, FontStyle::ChatMediumBold);
this->emplace<TextElement>(
string, MessageElementFlag::NonBoldUsername, textColor);
} else {
}
else
{
this->emplace<TextElement>(string, MessageElementFlag::Text,
textColor);
}
} else {
}
else
{
static QRegularExpression domainRegex(
R"(^(?:(?:ftp|http)s?:\/\/)?([^\/]+)(?:\/.*)?$)",
QRegularExpression::CaseInsensitiveOption);
QString lowercaseLinkString;
auto match = domainRegex.match(string);
if (match.isValid()) {
if (match.isValid())
{
lowercaseLinkString = string.mid(0, match.capturedStart(1)) +
match.captured(1).toLower() +
string.mid(match.capturedEnd(1));
} else {
}
else
{
lowercaseLinkString = string;
}
link = Link(Link::Url, linkString);
@@ -475,12 +546,14 @@ void TwitchMessageBuilder::addTextOrEmoji(const QString &string_)
LinkResolver::getLinkInfo(
linkString, [linkMELowercase, linkMEOriginal, linkString](
QString tooltipText, Link originalLink) {
if (!tooltipText.isEmpty()) {
if (!tooltipText.isEmpty())
{
linkMELowercase->setTooltip(tooltipText);
linkMEOriginal->setTooltip(tooltipText);
}
if (originalLink.value != linkString &&
!originalLink.value.isEmpty()) {
!originalLink.value.isEmpty())
{
linkMELowercase->setLink(originalLink)->updateLink();
linkMEOriginal->setLink(originalLink)->updateLink();
}
@@ -526,23 +599,27 @@ void TwitchMessageBuilder::parseMessageID()
{
auto iterator = this->tags.find("id");
if (iterator != this->tags.end()) {
if (iterator != this->tags.end())
{
this->messageID = iterator.value().toString();
}
}
void TwitchMessageBuilder::parseRoomID()
{
if (this->twitchChannel == nullptr) {
if (this->twitchChannel == nullptr)
{
return;
}
auto iterator = this->tags.find("room-id");
if (iterator != std::end(this->tags)) {
if (iterator != std::end(this->tags))
{
this->roomID_ = iterator.value().toString();
if (this->twitchChannel->roomId().isEmpty()) {
if (this->twitchChannel->roomId().isEmpty())
{
this->twitchChannel->setRoomId(this->roomID_);
}
}
@@ -561,14 +638,16 @@ void TwitchMessageBuilder::appendChannelName()
void TwitchMessageBuilder::parseUsername()
{
auto iterator = this->tags.find("color");
if (iterator != this->tags.end()) {
if (iterator != this->tags.end())
{
this->usernameColor_ = QColor(iterator.value().toString());
}
// username
this->userName = this->ircMessage->nick();
if (this->userName.isEmpty() || this->args.trimSubscriberUsername) {
if (this->userName.isEmpty() || this->args.trimSubscriberUsername)
{
this->userName = this->tags.value(QLatin1String("login")).toString();
}
@@ -591,16 +670,20 @@ void TwitchMessageBuilder::appendUsername()
QString localizedName;
auto iterator = this->tags.find("display-name");
if (iterator != this->tags.end()) {
if (iterator != this->tags.end())
{
QString displayName =
parseTagString(iterator.value().toString()).trimmed();
if (QString::compare(displayName, this->userName,
Qt::CaseInsensitive) == 0) {
Qt::CaseInsensitive) == 0)
{
username = displayName;
this->message().displayName = displayName;
} else {
}
else
{
localizedName = displayName;
this->message().displayName = username;
@@ -617,34 +700,50 @@ void TwitchMessageBuilder::appendUsername()
"/appearance/messages/usernameDisplayMode",
UsernameDisplayMode::UsernameAndLocalizedName);
switch (usernameDisplayMode.getValue()) {
case UsernameDisplayMode::Username: {
switch (usernameDisplayMode.getValue())
{
case UsernameDisplayMode::Username:
{
usernameText = username;
} break;
}
break;
case UsernameDisplayMode::LocalizedName: {
if (hasLocalizedName) {
case UsernameDisplayMode::LocalizedName:
{
if (hasLocalizedName)
{
usernameText = localizedName;
} else {
}
else
{
usernameText = username;
}
} break;
}
break;
default:
case UsernameDisplayMode::UsernameAndLocalizedName: {
if (hasLocalizedName) {
case UsernameDisplayMode::UsernameAndLocalizedName:
{
if (hasLocalizedName)
{
usernameText = username + "(" + localizedName + ")";
} else {
}
else
{
usernameText = username;
}
} break;
}
break;
}
if (this->args.isSentWhisper) {
if (this->args.isSentWhisper)
{
// TODO(pajlada): Re-implement
// userDisplayString +=
// IrcManager::getInstance().getUser().getUserName();
} else if (this->args.isReceivedWhisper) {
}
else if (this->args.isReceivedWhisper)
{
// Sender username
this->emplace<TextElement>(usernameText, MessageElementFlag::Username,
this->usernameColor_,
@@ -659,7 +758,8 @@ void TwitchMessageBuilder::appendUsername()
FontStyle::ChatMedium);
QColor selfColor = currentUser->color();
if (!selfColor.isValid()) {
if (!selfColor.isValid())
{
selfColor = app->themes->messages.textColors.system;
}
@@ -667,8 +767,11 @@ void TwitchMessageBuilder::appendUsername()
this->emplace<TextElement>(currentUser->getUserName() + ":",
MessageElementFlag::Username, selfColor,
FontStyle::ChatMediumBold);
} else {
if (!this->action_) {
}
else
{
if (!this->action_)
{
usernameText += ":";
}
@@ -690,7 +793,8 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
QString currentUsername = currentUser->getUserName();
if (this->ircMessage->nick() == currentUsername) {
if (this->ircMessage->nick() == currentUsername)
{
currentUser->setColor(this->usernameColor_);
// Do nothing. Highlights cannot be triggered by yourself
return;
@@ -698,14 +802,18 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
// update the media player url if necessary
QUrl highlightSoundUrl;
if (getSettings()->customHighlightSound) {
if (getSettings()->customHighlightSound)
{
highlightSoundUrl =
QUrl::fromLocalFile(getSettings()->pathHighlightSound.getValue());
} else {
}
else
{
highlightSoundUrl = QUrl("qrc:/sounds/ping2.wav");
}
if (currentPlayerUrl != highlightSoundUrl) {
if (currentPlayerUrl != highlightSoundUrl)
{
player->setMedia(highlightSoundUrl);
currentPlayerUrl = highlightSoundUrl;
@@ -718,7 +826,8 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
std::vector<HighlightPhrase> userHighlights =
app->highlights->highlightedUsers.getVector();
if (getSettings()->enableSelfHighlight && currentUsername.size() > 0) {
if (getSettings()->enableSelfHighlight && currentUsername.size() > 0)
{
HighlightPhrase selfHighlight(
currentUsername, getSettings()->enableSelfHighlightTaskbar,
getSettings()->enableSelfHighlightSound, false);
@@ -731,22 +840,28 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
bool hasFocus = (QApplication::focusWidget() != nullptr);
if (!app->highlights->blacklistContains(this->ircMessage->nick())) {
for (const HighlightPhrase &highlight : activeHighlights) {
if (highlight.isMatch(this->originalMessage_)) {
if (!app->highlights->blacklistContains(this->ircMessage->nick()))
{
for (const HighlightPhrase &highlight : activeHighlights)
{
if (highlight.isMatch(this->originalMessage_))
{
log("Highlight because {} matches {}", this->originalMessage_,
highlight.getPattern());
doHighlight = true;
if (highlight.getAlert()) {
if (highlight.getAlert())
{
doAlert = true;
}
if (highlight.getSound()) {
if (highlight.getSound())
{
playSound = true;
}
if (playSound && doAlert) {
if (playSound && doAlert)
{
// Break if no further action can be taken from other
// highlights This might change if highlights can have
// custom colors/sounds/actions
@@ -754,21 +869,26 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
}
}
}
for (const HighlightPhrase &userHighlight : userHighlights) {
if (userHighlight.isMatch(this->ircMessage->nick())) {
for (const HighlightPhrase &userHighlight : userHighlights)
{
if (userHighlight.isMatch(this->ircMessage->nick()))
{
log("Highlight because user {} sent a message",
this->ircMessage->nick());
doHighlight = true;
if (userHighlight.getAlert()) {
if (userHighlight.getAlert())
{
doAlert = true;
}
if (userHighlight.getSound()) {
if (userHighlight.getSound())
{
playSound = true;
}
if (playSound && doAlert) {
if (playSound && doAlert)
{
// Break if no further action can be taken from other
// usernames Mostly used for regex stuff
break;
@@ -776,24 +896,30 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
}
}
if (this->args.isReceivedWhisper &&
getSettings()->enableWhisperHighlight) {
if (getSettings()->enableWhisperHighlightTaskbar) {
getSettings()->enableWhisperHighlight)
{
if (getSettings()->enableWhisperHighlightTaskbar)
{
doAlert = true;
}
if (getSettings()->enableWhisperHighlightSound) {
if (getSettings()->enableWhisperHighlightSound)
{
playSound = true;
}
}
this->message().flags.set(MessageFlag::Highlighted, doHighlight);
if (!isPastMsg) {
if (!isPastMsg)
{
if (playSound &&
(!hasFocus || getSettings()->highlightAlwaysPlaySound)) {
(!hasFocus || getSettings()->highlightAlwaysPlaySound))
{
player->play();
}
if (doAlert) {
if (doAlert)
{
getApp()->windows->sendAlert();
}
}
@@ -805,13 +931,15 @@ void TwitchMessageBuilder::appendTwitchEmote(
std::vector<std::tuple<int, EmotePtr, EmoteName>> &vec)
{
auto app = getApp();
if (!emote.contains(':')) {
if (!emote.contains(':'))
{
return;
}
auto parameters = emote.split(':');
if (parameters.length() < 2) {
if (parameters.length() < 2)
{
return;
}
@@ -819,18 +947,20 @@ void TwitchMessageBuilder::appendTwitchEmote(
auto occurences = parameters.at(1).split(',');
for (QString occurence : occurences) {
for (QString occurence : occurences)
{
auto coords = occurence.split('-');
if (coords.length() < 2) {
if (coords.length() < 2)
{
return;
}
auto start = coords.at(0).toInt();
auto end = coords.at(1).toInt();
if (start >= end || start < 0 ||
end > this->originalMessage_.length()) {
if (start >= end || start < 0 || end > this->originalMessage_.length())
{
return;
}
@@ -838,7 +968,8 @@ void TwitchMessageBuilder::appendTwitchEmote(
EmoteName{this->originalMessage_.mid(start, end - start + 1)};
auto tup = std::tuple<int, EmotePtr, EmoteName>{
start, app->emotes->twitch.getOrCreateEmote(id, name), name};
if (std::get<1>(tup) == nullptr) {
if (std::get<1>(tup) == nullptr)
{
log("nullptr {}", std::get<2>(tup).string);
}
vec.push_back(std::move(tup));
@@ -849,24 +980,33 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
{
// Special channels, like /whispers and /channels return here
// This means they will not render any BTTV or FFZ emotes
if (this->twitchChannel == nullptr) {
if (this->twitchChannel == nullptr)
{
return Failure;
}
auto flags = MessageElementFlags();
auto emote = boost::optional<EmotePtr>{};
if ((emote = this->twitchChannel->globalBttv().emote(name))) {
if ((emote = this->twitchChannel->globalBttv().emote(name)))
{
flags = MessageElementFlag::BttvEmote;
} else if ((emote = this->twitchChannel->bttvEmote(name))) {
}
else if ((emote = this->twitchChannel->bttvEmote(name)))
{
flags = MessageElementFlag::BttvEmote;
} else if ((emote = this->twitchChannel->globalFfz().emote(name))) {
}
else if ((emote = this->twitchChannel->globalFfz().emote(name)))
{
flags = MessageElementFlag::FfzEmote;
} else if ((emote = this->twitchChannel->ffzEmote(name))) {
}
else if ((emote = this->twitchChannel->ffzEmote(name)))
{
flags = MessageElementFlag::FfzEmote;
}
if (emote) {
if (emote)
{
this->emplace<EmoteElement>(emote.get(), flags);
return Success;
}
@@ -877,96 +1017,129 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
// fourtf: this is ugly
void TwitchMessageBuilder::appendTwitchBadges()
{
if (this->twitchChannel == nullptr) {
if (this->twitchChannel == nullptr)
{
return;
}
auto app = getApp();
auto iterator = this->tags.find("badges");
if (iterator == this->tags.end()) return;
if (iterator == this->tags.end())
return;
for (QString badge : iterator.value().toString().split(',')) {
if (badge.startsWith("bits/")) {
for (QString badge : iterator.value().toString().split(','))
{
if (badge.startsWith("bits/"))
{
QString cheerAmount = badge.mid(5);
QString tooltip = QString("Twitch cheer ") + cheerAmount;
// Try to fetch channel-specific bit badge
try {
try
{
if (twitchChannel)
if (const auto &badge = this->twitchChannel->twitchBadge(
"bits", cheerAmount)) {
"bits", cheerAmount))
{
this->emplace<EmoteElement>(
badge.get(), MessageElementFlag::BadgeVanity)
->setTooltip(tooltip);
continue;
}
} catch (const std::out_of_range &) {
}
catch (const std::out_of_range &)
{
// Channel does not contain a special bit badge for this version
}
// Use default bit badge
if (auto badge = this->twitchChannel->globalTwitchBadges().badge(
"bits", cheerAmount)) {
"bits", cheerAmount))
{
this->emplace<EmoteElement>(badge.get(),
MessageElementFlag::BadgeVanity)
->setTooltip(tooltip);
}
} else if (badge == "staff/1") {
}
else if (badge == "staff/1")
{
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.staff),
MessageElementFlag::BadgeGlobalAuthority)
->setTooltip("Twitch Staff");
} else if (badge == "admin/1") {
}
else if (badge == "admin/1")
{
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.admin),
MessageElementFlag::BadgeGlobalAuthority)
->setTooltip("Twitch Admin");
} else if (badge == "global_mod/1") {
}
else if (badge == "global_mod/1")
{
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.globalmod),
MessageElementFlag::BadgeGlobalAuthority)
->setTooltip("Twitch Global Moderator");
} else if (badge == "moderator/1") {
}
else if (badge == "moderator/1")
{
// TODO: Implement custom FFZ moderator badge
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.moderator),
MessageElementFlag::BadgeChannelAuthority)
->setTooltip("Twitch Channel Moderator");
} else if (badge == "turbo/1") {
}
else if (badge == "turbo/1")
{
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.turbo),
MessageElementFlag::BadgeGlobalAuthority)
->setTooltip("Twitch Turbo Subscriber");
} else if (badge == "broadcaster/1") {
}
else if (badge == "broadcaster/1")
{
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.broadcaster),
MessageElementFlag::BadgeChannelAuthority)
->setTooltip("Twitch Broadcaster");
} else if (badge == "premium/1") {
}
else if (badge == "premium/1")
{
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.prime),
MessageElementFlag::BadgeVanity)
->setTooltip("Twitch Prime Subscriber");
} else if (badge.startsWith("partner/")) {
}
else if (badge.startsWith("partner/"))
{
int index = badge.midRef(8).toInt();
switch (index) {
case 1: {
switch (index)
{
case 1:
{
this->emplace<ImageElement>(
Image::fromPixmap(app->resources->twitch.verified,
0.25),
MessageElementFlag::BadgeVanity)
->setTooltip("Twitch Verified");
} break;
default: {
}
break;
default:
{
printf("[TwitchMessageBuilder] Unhandled partner badge "
"index: %d\n",
index);
} break;
}
break;
}
} else if (badge.startsWith("subscriber/")) {
}
else if (badge.startsWith("subscriber/"))
{
if (auto badgeEmote = this->twitchChannel->twitchBadge(
"subscriber", badge.mid(11))) {
"subscriber", badge.mid(11)))
{
this->emplace<EmoteElement>(
badgeEmote.get(), MessageElementFlag::BadgeSubscription)
->setTooltip((*badgeEmote)->tooltip.string);
@@ -978,12 +1151,16 @@ void TwitchMessageBuilder::appendTwitchBadges()
Image::fromPixmap(app->resources->twitch.subscriber, 0.25),
MessageElementFlag::BadgeSubscription)
->setTooltip("Twitch Subscriber");
} else {
}
else
{
auto splits = badge.split('/');
if (splits.size() != 2) continue;
if (splits.size() != 2)
continue;
if (auto badgeEmote =
this->twitchChannel->twitchBadge(splits[0], splits[1])) {
this->twitchChannel->twitchBadge(splits[0], splits[1]))
{
this->emplace<EmoteElement>(badgeEmote.get(),
MessageElementFlag::BadgeVanity)
->setTooltip((*badgeEmote)->tooltip.string);
@@ -997,7 +1174,8 @@ void TwitchMessageBuilder::appendChatterinoBadges()
{
auto chatterinoBadgePtr =
getApp()->chatterinoBadges->getBadge({this->userName});
if (chatterinoBadgePtr) {
if (chatterinoBadgePtr)
{
this->emplace<EmoteElement>(*chatterinoBadgePtr,
MessageElementFlag::BadgeChatterino);
}
+92 -46
View File
@@ -12,13 +12,15 @@ namespace {
inline bool ReadValue(const rapidjson::Value &object, const char *key,
Type &out)
{
if (!object.HasMember(key)) {
if (!object.HasMember(key))
{
return false;
}
const auto &value = object[key];
if (!value.Is<Type>()) {
if (!value.Is<Type>())
{
return false;
}
@@ -31,13 +33,15 @@ namespace {
inline bool ReadValue<QString>(const rapidjson::Value &object,
const char *key, QString &out)
{
if (!object.HasMember(key)) {
if (!object.HasMember(key))
{
return false;
}
const auto &value = object[key];
if (!value.IsString()) {
if (!value.IsString())
{
return false;
}
@@ -51,18 +55,22 @@ namespace {
const char *key,
std::vector<QString> &out)
{
if (!object.HasMember(key)) {
if (!object.HasMember(key))
{
return false;
}
const auto &value = object[key];
if (!value.IsArray()) {
if (!value.IsArray())
{
return false;
}
for (const rapidjson::Value &innerValue : value.GetArray()) {
if (!innerValue.IsString()) {
for (const rapidjson::Value &innerValue : value.GetArray())
{
if (!innerValue.IsString())
{
return false;
}
@@ -76,141 +84,173 @@ namespace {
inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set,
const rapidjson::Value &action)
{
if (!action.IsObject()) {
if (!action.IsObject())
{
return false;
}
if (!ReadValue(action, "prefix", set.prefix)) {
if (!ReadValue(action, "prefix", set.prefix))
{
return false;
}
if (!ReadValue(action, "scales", set.scales)) {
if (!ReadValue(action, "scales", set.scales))
{
return false;
}
if (!ReadValue(action, "backgrounds", set.backgrounds)) {
if (!ReadValue(action, "backgrounds", set.backgrounds))
{
return false;
}
if (!ReadValue(action, "states", set.states)) {
if (!ReadValue(action, "states", set.states))
{
return false;
}
if (!ReadValue(action, "type", set.type)) {
if (!ReadValue(action, "type", set.type))
{
return false;
}
if (!ReadValue(action, "updated_at", set.updatedAt)) {
if (!ReadValue(action, "updated_at", set.updatedAt))
{
return false;
}
if (!ReadValue(action, "priority", set.priority)) {
if (!ReadValue(action, "priority", set.priority))
{
return false;
}
// Tiers
if (!action.HasMember("tiers")) {
if (!action.HasMember("tiers"))
{
return false;
}
const auto &tiersValue = action["tiers"];
if (!tiersValue.IsArray()) {
if (!tiersValue.IsArray())
{
return false;
}
for (const rapidjson::Value &tierValue : tiersValue.GetArray()) {
for (const rapidjson::Value &tierValue : tiersValue.GetArray())
{
JSONCheermoteSet::CheermoteTier tier;
if (!tierValue.IsObject()) {
if (!tierValue.IsObject())
{
return false;
}
if (!ReadValue(tierValue, "min_bits", tier.minBits)) {
if (!ReadValue(tierValue, "min_bits", tier.minBits))
{
return false;
}
if (!ReadValue(tierValue, "id", tier.id)) {
if (!ReadValue(tierValue, "id", tier.id))
{
return false;
}
if (!ReadValue(tierValue, "color", tier.color)) {
if (!ReadValue(tierValue, "color", tier.color))
{
return false;
}
// Images
if (!tierValue.HasMember("images")) {
if (!tierValue.HasMember("images"))
{
return false;
}
const auto &imagesValue = tierValue["images"];
if (!imagesValue.IsObject()) {
if (!imagesValue.IsObject())
{
return false;
}
// Read images object
for (const auto &imageBackgroundValue : imagesValue.GetObject()) {
for (const auto &imageBackgroundValue : imagesValue.GetObject())
{
QString background = imageBackgroundValue.name.GetString();
bool backgroundExists = false;
for (const auto &bg : set.backgrounds) {
if (background == bg) {
for (const auto &bg : set.backgrounds)
{
if (background == bg)
{
backgroundExists = true;
break;
}
}
if (!backgroundExists) {
if (!backgroundExists)
{
continue;
}
const rapidjson::Value &imageBackgroundStates =
imageBackgroundValue.value;
if (!imageBackgroundStates.IsObject()) {
if (!imageBackgroundStates.IsObject())
{
continue;
}
// Read each key which represents a background
for (const auto &imageBackgroundState :
imageBackgroundStates.GetObject()) {
imageBackgroundStates.GetObject())
{
QString state = imageBackgroundState.name.GetString();
bool stateExists = false;
for (const auto &_state : set.states) {
if (state == _state) {
for (const auto &_state : set.states)
{
if (state == _state)
{
stateExists = true;
break;
}
}
if (!stateExists) {
if (!stateExists)
{
continue;
}
const rapidjson::Value &imageScalesValue =
imageBackgroundState.value;
if (!imageScalesValue.IsObject()) {
if (!imageScalesValue.IsObject())
{
continue;
}
// Read each key which represents a scale
for (const auto &imageScaleValue :
imageScalesValue.GetObject()) {
imageScalesValue.GetObject())
{
QString scale = imageScaleValue.name.GetString();
bool scaleExists = false;
for (const auto &_scale : set.scales) {
if (scale == _scale) {
for (const auto &_scale : set.scales)
{
if (scale == _scale)
{
scaleExists = true;
break;
}
}
if (!scaleExists) {
if (!scaleExists)
{
continue;
}
const rapidjson::Value &imageScaleURLValue =
imageScaleValue.value;
if (!imageScaleURLValue.IsString()) {
if (!imageScaleURLValue.IsString())
{
continue;
}
@@ -218,7 +258,8 @@ namespace {
bool ok = false;
qreal scaleNumber = scale.toFloat(&ok);
if (!ok) {
if (!ok)
{
continue;
}
@@ -246,25 +287,30 @@ std::vector<JSONCheermoteSet> ParseCheermoteSets(const rapidjson::Document &d)
{
std::vector<JSONCheermoteSet> sets;
if (!d.IsObject()) {
if (!d.IsObject())
{
return sets;
}
if (!d.HasMember("actions")) {
if (!d.HasMember("actions"))
{
return sets;
}
const auto &actionsValue = d["actions"];
if (!actionsValue.IsArray()) {
if (!actionsValue.IsArray())
{
return sets;
}
for (const auto &action : actionsValue.GetArray()) {
for (const auto &action : actionsValue.GetArray())
{
JSONCheermoteSet set;
bool res = ParseSingleCheermoteSet(set, action);
if (res) {
if (res)
{
sets.emplace_back(set);
}
}
+60 -27
View File
@@ -58,7 +58,8 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
QString username = account->getUserName();
QString oauthToken = account->getOAuthToken();
if (!oauthToken.startsWith("oauth:")) {
if (!oauthToken.startsWith("oauth:"))
{
oauthToken.prepend("oauth:");
}
@@ -66,7 +67,8 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
connection->setNickName(username);
connection->setRealName(username);
if (!account->isAnon()) {
if (!account->isAnon())
{
connection->setPassword(oauthToken);
}
@@ -103,7 +105,8 @@ void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
void TwitchServer::messageReceived(Communi::IrcMessage *message)
{
// this->readConnection
if (message->type() == Communi::IrcMessage::Type::Private) {
if (message->type() == Communi::IrcMessage::Type::Private)
{
// We already have a handler for private messages
return;
}
@@ -112,35 +115,55 @@ void TwitchServer::messageReceived(Communi::IrcMessage *message)
auto &handler = IrcMessageHandler::getInstance();
if (command == "ROOMSTATE") {
if (command == "ROOMSTATE")
{
handler.handleRoomStateMessage(message);
} else if (command == "CLEARCHAT") {
}
else if (command == "CLEARCHAT")
{
handler.handleClearChatMessage(message);
} else if (command == "USERSTATE") {
}
else if (command == "USERSTATE")
{
handler.handleUserStateMessage(message);
} else if (command == "WHISPER") {
}
else if (command == "WHISPER")
{
handler.handleWhisperMessage(message);
} else if (command == "USERNOTICE") {
}
else if (command == "USERNOTICE")
{
handler.handleUserNoticeMessage(message, *this);
} else if (command == "MODE") {
}
else if (command == "MODE")
{
handler.handleModeMessage(message);
} else if (command == "NOTICE") {
}
else if (command == "NOTICE")
{
handler.handleNoticeMessage(
static_cast<Communi::IrcNoticeMessage *>(message));
} else if (command == "JOIN") {
}
else if (command == "JOIN")
{
handler.handleJoinMessage(message);
} else if (command == "PART") {
}
else if (command == "PART")
{
handler.handlePartMessage(message);
}
}
void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message)
{
switch (message->type()) {
case Communi::IrcMessage::Type::Notice: {
switch (message->type())
{
case Communi::IrcMessage::Type::Notice:
{
IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage(
static_cast<Communi::IrcNoticeMessage *>(message));
} break;
}
break;
default:;
}
@@ -149,11 +172,13 @@ void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message)
std::shared_ptr<Channel> TwitchServer::getCustomChannel(
const QString &channelName)
{
if (channelName == "/whispers") {
if (channelName == "/whispers")
{
return this->whispersChannel;
}
if (channelName == "/mentions") {
if (channelName == "/mentions")
{
return this->mentionsChannel;
}
@@ -174,14 +199,18 @@ std::shared_ptr<Channel> TwitchServer::getChannelOrEmptyByID(
{
std::lock_guard<std::mutex> lock(this->channelMutex);
for (const auto &weakChannel : this->channels) {
for (const auto &weakChannel : this->channels)
{
auto channel = weakChannel.lock();
if (!channel) continue;
if (!channel)
continue;
auto twitchChannel = std::dynamic_pointer_cast<TwitchChannel>(channel);
if (!twitchChannel) continue;
if (!twitchChannel)
continue;
if (twitchChannel->roomId() == channelId) {
if (twitchChannel->roomId() == channelId)
{
return twitchChannel;
}
}
@@ -217,9 +246,10 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel,
auto now = std::chrono::steady_clock::now();
// check if you are sending messages too fast
if (!lastMessage.empty() &&
lastMessage.back() + minMessageOffset > now) {
if (this->lastErrorTimeSpeed_ + 30s < now) {
if (!lastMessage.empty() && lastMessage.back() + minMessageOffset > now)
{
if (this->lastErrorTimeSpeed_ + 30s < now)
{
auto errorMessage =
makeSystemMessage("sending messages too fast");
@@ -231,13 +261,16 @@ void TwitchServer::onMessageSendRequested(TwitchChannel *channel,
}
// remove messages older than 30 seconds
while (!lastMessage.empty() && lastMessage.front() + 32s < now) {
while (!lastMessage.empty() && lastMessage.front() + 32s < now)
{
lastMessage.pop();
}
// check if you are sending too many messages
if (lastMessage.size() >= maxMessageCount) {
if (this->lastErrorTimeAmount_ + 30s < now) {
if (lastMessage.size() >= maxMessageCount)
{
if (this->lastErrorTimeAmount_ + 30s < now)
{
auto errorMessage =
makeSystemMessage("sending too many messages");
+8 -4
View File
@@ -43,26 +43,30 @@ namespace Settings {
TwitchUser user;
if (!value.IsObject()) {
if (!value.IsObject())
{
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION(
"Deserialized rapidjson::Value is wrong type");
return user;
}
if (!rj::getSafe(value, "_id", user.id)) {
if (!rj::getSafe(value, "_id", user.id))
{
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION("Missing ID key");
return user;
}
if (!rj::getSafe(value, "name", user.name)) {
if (!rj::getSafe(value, "name", user.name))
{
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION("Missing name key");
return user;
}
if (!rj::getSafe(value, "display_name", user.displayName)) {
if (!rj::getSafe(value, "display_name", user.displayName))
{
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION("Missing display name key");
return user;