changed .clang-format

This commit is contained in:
fourtf
2018-08-15 22:46:20 +02:00
parent 44c16f1b3a
commit af7b742a23
46 changed files with 1636 additions and 1611 deletions
+62 -59
View File
@@ -13,73 +13,76 @@
namespace chatterino {
namespace {
Url getEmoteLink(QString urlTemplate, const EmoteId &id,
const QString &emoteScale)
{
urlTemplate.detach();
Url getEmoteLink(QString urlTemplate, const EmoteId &id,
const QString &emoteScale)
{
urlTemplate.detach();
return {urlTemplate.replace("{{id}}", id.string)
.replace("{{image}}", emoteScale)};
}
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
const EmoteMap &currentEmotes)
{
auto emotes = EmoteMap();
auto jsonEmotes = jsonRoot.value("emotes").toArray();
auto urlTemplate = qS("https:") + jsonRoot.value("urlTemplate").toString();
for (auto jsonEmote : jsonEmotes) {
auto id = EmoteId{jsonEmote.toObject().value("id").toString()};
auto name = EmoteName{jsonEmote.toObject().value("code").toString()};
auto emote = Emote(
{name,
ImageSet{
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
Tooltip{name.string + "<br />Global Bttv Emote"},
Url{"https://manage.betterttv.net/emotes/" + id.string}});
emotes[name] = cachedOrMakeEmotePtr(std::move(emote), currentEmotes);
return {urlTemplate.replace("{{id}}", id.string)
.replace("{{image}}", emoteScale)};
}
std::pair<Outcome, EmoteMap> parseGlobalEmotes(
const QJsonObject &jsonRoot, const EmoteMap &currentEmotes)
{
auto emotes = EmoteMap();
auto jsonEmotes = jsonRoot.value("emotes").toArray();
auto urlTemplate =
qS("https:") + jsonRoot.value("urlTemplate").toString();
return {Success, std::move(emotes)};
}
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
{
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
static std::mutex mutex;
for (auto jsonEmote : jsonEmotes) {
auto id = EmoteId{jsonEmote.toObject().value("id").toString()};
auto name =
EmoteName{jsonEmote.toObject().value("code").toString()};
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
}
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
{
auto emotes = EmoteMap();
auto jsonEmotes = jsonRoot.value("emotes").toArray();
auto urlTemplate = "https:" + jsonRoot.value("urlTemplate").toString();
auto emote = Emote(
{name,
ImageSet{
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
Tooltip{name.string + "<br />Global Bttv Emote"},
Url{"https://manage.betterttv.net/emotes/" + id.string}});
for (auto jsonEmote_ : jsonEmotes) {
auto jsonEmote = jsonEmote_.toObject();
emotes[name] =
cachedOrMakeEmotePtr(std::move(emote), currentEmotes);
}
auto id = EmoteId{jsonEmote.value("id").toString()};
auto name = EmoteName{jsonEmote.value("code").toString()};
// emoteObject.value("imageType").toString();
auto emote = Emote(
{name,
ImageSet{
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
Tooltip{name.string + "<br />Channel Bttv Emote"},
Url{"https://manage.betterttv.net/emotes/" + id.string}});
emotes[name] = cachedOrMake(std::move(emote), id);
return {Success, std::move(emotes)};
}
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
{
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
static std::mutex mutex;
return {Success, std::move(emotes)};
}
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
}
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
{
auto emotes = EmoteMap();
auto jsonEmotes = jsonRoot.value("emotes").toArray();
auto urlTemplate = "https:" + jsonRoot.value("urlTemplate").toString();
for (auto jsonEmote_ : jsonEmotes) {
auto jsonEmote = jsonEmote_.toObject();
auto id = EmoteId{jsonEmote.value("id").toString()};
auto name = EmoteName{jsonEmote.value("code").toString()};
// emoteObject.value("imageType").toString();
auto emote = Emote(
{name,
ImageSet{
Image::fromUrl(getEmoteLink(urlTemplate, id, "1x"), 1),
Image::fromUrl(getEmoteLink(urlTemplate, id, "2x"), 0.5),
Image::fromUrl(getEmoteLink(urlTemplate, id, "3x"), 0.25)},
Tooltip{name.string + "<br />Channel Bttv Emote"},
Url{"https://manage.betterttv.net/emotes/" + id.string}});
emotes[name] = cachedOrMake(std::move(emote), id);
}
return {Success, std::move(emotes)};
}
} // namespace
//
+66 -67
View File
@@ -13,82 +13,81 @@
#include <memory>
namespace chatterino {
namespace {
void parseEmoji(const std::shared_ptr<EmojiData> &emojiData,
const rapidjson::Value &unparsedEmoji,
QString shortCode = QString())
{
static uint unicodeBytes[4];
void parseEmoji(const std::shared_ptr<EmojiData> &emojiData,
const rapidjson::Value &unparsedEmoji,
QString shortCode = QString())
{
static uint unicodeBytes[4];
struct {
bool apple;
bool google;
bool twitter;
bool emojione;
bool facebook;
bool messenger;
} capabilities;
struct {
bool apple;
bool google;
bool twitter;
bool emojione;
bool facebook;
bool messenger;
} capabilities;
if (!shortCode.isEmpty()) {
emojiData->shortCodes.push_back(shortCode);
} else {
const auto &shortCodes = unparsedEmoji["short_names"];
for (const auto &shortCode : shortCodes.GetArray()) {
emojiData->shortCodes.emplace_back(shortCode.GetString());
if (!shortCode.isEmpty()) {
emojiData->shortCodes.push_back(shortCode);
} else {
const auto &shortCodes = unparsedEmoji["short_names"];
for (const auto &shortCode : shortCodes.GetArray()) {
emojiData->shortCodes.emplace_back(shortCode.GetString());
}
}
}
rj::getSafe(unparsedEmoji, "non_qualified", emojiData->nonQualifiedCode);
rj::getSafe(unparsedEmoji, "unified", emojiData->unifiedCode);
rj::getSafe(unparsedEmoji, "non_qualified",
emojiData->nonQualifiedCode);
rj::getSafe(unparsedEmoji, "unified", emojiData->unifiedCode);
rj::getSafe(unparsedEmoji, "has_img_apple", capabilities.apple);
rj::getSafe(unparsedEmoji, "has_img_google", capabilities.google);
rj::getSafe(unparsedEmoji, "has_img_twitter", capabilities.twitter);
rj::getSafe(unparsedEmoji, "has_img_emojione", capabilities.emojione);
rj::getSafe(unparsedEmoji, "has_img_facebook", capabilities.facebook);
rj::getSafe(unparsedEmoji, "has_img_messenger", capabilities.messenger);
rj::getSafe(unparsedEmoji, "has_img_apple", capabilities.apple);
rj::getSafe(unparsedEmoji, "has_img_google", capabilities.google);
rj::getSafe(unparsedEmoji, "has_img_twitter", capabilities.twitter);
rj::getSafe(unparsedEmoji, "has_img_emojione", capabilities.emojione);
rj::getSafe(unparsedEmoji, "has_img_facebook", capabilities.facebook);
rj::getSafe(unparsedEmoji, "has_img_messenger", capabilities.messenger);
if (capabilities.apple) {
emojiData->capabilities.insert("Apple");
}
if (capabilities.google) {
emojiData->capabilities.insert("Google");
}
if (capabilities.twitter) {
emojiData->capabilities.insert("Twitter");
}
if (capabilities.emojione) {
emojiData->capabilities.insert("EmojiOne 3");
}
if (capabilities.facebook) {
emojiData->capabilities.insert("Facebook");
}
if (capabilities.messenger) {
emojiData->capabilities.insert("Messenger");
}
if (capabilities.apple) {
emojiData->capabilities.insert("Apple");
}
if (capabilities.google) {
emojiData->capabilities.insert("Google");
}
if (capabilities.twitter) {
emojiData->capabilities.insert("Twitter");
}
if (capabilities.emojione) {
emojiData->capabilities.insert("EmojiOne 3");
}
if (capabilities.facebook) {
emojiData->capabilities.insert("Facebook");
}
if (capabilities.messenger) {
emojiData->capabilities.insert("Messenger");
}
QStringList unicodeCharacters;
if (!emojiData->nonQualifiedCode.isEmpty()) {
unicodeCharacters = emojiData->nonQualifiedCode.toLower().split('-');
} else {
unicodeCharacters = emojiData->unifiedCode.toLower().split('-');
QStringList unicodeCharacters;
if (!emojiData->nonQualifiedCode.isEmpty()) {
unicodeCharacters =
emojiData->nonQualifiedCode.toLower().split('-');
} else {
unicodeCharacters = emojiData->unifiedCode.toLower().split('-');
}
if (unicodeCharacters.length() < 1) {
return;
}
int numUnicodeBytes = 0;
for (const QString &unicodeCharacter : unicodeCharacters) {
unicodeBytes[numUnicodeBytes++] =
QString(unicodeCharacter).toUInt(nullptr, 16);
}
emojiData->value = QString::fromUcs4(unicodeBytes, numUnicodeBytes);
}
if (unicodeCharacters.length() < 1) {
return;
}
int numUnicodeBytes = 0;
for (const QString &unicodeCharacter : unicodeCharacters) {
unicodeBytes[numUnicodeBytes++] =
QString(unicodeCharacter).toUInt(nullptr, 16);
}
emojiData->value = QString::fromUcs4(unicodeBytes, numUnicodeBytes);
}
} // namespace
void Emojis::load()
+84 -83
View File
@@ -10,99 +10,100 @@
namespace chatterino {
namespace {
Url getEmoteLink(const QJsonObject &urls, const QString &emoteScale)
{
auto emote = urls.value(emoteScale);
if (emote.isUndefined()) {
return {""};
}
assert(emote.isString());
return {"https:" + emote.toString()};
}
void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
const QString &tooltip, Emote &emoteData)
{
auto url1x = getEmoteLink(urls, "1");
auto url2x = getEmoteLink(urls, "2");
auto url3x = getEmoteLink(urls, "4");
//, code, tooltip
emoteData.name = name;
emoteData.images =
ImageSet{Image::fromUrl(url1x, 1), Image::fromUrl(url2x, 0.5),
Image::fromUrl(url3x, 0.25)};
emoteData.tooltip = {tooltip};
}
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
{
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
static std::mutex mutex;
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
}
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
const EmoteMap &currentEmotes)
{
auto jsonSets = jsonRoot.value("sets").toObject();
auto emotes = EmoteMap();
for (auto jsonSet : jsonSets) {
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
for (auto jsonEmoteValue : jsonEmotes) {
auto jsonEmote = jsonEmoteValue.toObject();
auto name = EmoteName{jsonEmote.value("name").toString()};
auto id = EmoteId{jsonEmote.value("id").toString()};
auto urls = jsonEmote.value("urls").toObject();
auto emote = Emote();
fillInEmoteData(urls, name, name.string + "<br/>Global FFZ Emote",
emote);
emote.homePage =
Url{QString("https://www.frankerfacez.com/emoticon/%1-%2")
.arg(id.string)
.arg(name.string)};
emotes[name] =
cachedOrMakeEmotePtr(std::move(emote), currentEmotes);
Url getEmoteLink(const QJsonObject &urls, const QString &emoteScale)
{
auto emote = urls.value(emoteScale);
if (emote.isUndefined()) {
return {""};
}
assert(emote.isString());
return {"https:" + emote.toString()};
}
void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
const QString &tooltip, Emote &emoteData)
{
auto url1x = getEmoteLink(urls, "1");
auto url2x = getEmoteLink(urls, "2");
auto url3x = getEmoteLink(urls, "4");
return {Success, std::move(emotes)};
}
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
{
auto jsonSets = jsonRoot.value("sets").toObject();
auto emotes = EmoteMap();
//, code, tooltip
emoteData.name = name;
emoteData.images =
ImageSet{Image::fromUrl(url1x, 1), Image::fromUrl(url2x, 0.5),
Image::fromUrl(url3x, 0.25)};
emoteData.tooltip = {tooltip};
}
EmotePtr cachedOrMake(Emote &&emote, const EmoteId &id)
{
static std::unordered_map<EmoteId, std::weak_ptr<const Emote>> cache;
static std::mutex mutex;
for (auto jsonSet : jsonSets) {
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
return cachedOrMakeEmotePtr(std::move(emote), cache, mutex, id);
}
std::pair<Outcome, EmoteMap> parseGlobalEmotes(
const QJsonObject &jsonRoot, const EmoteMap &currentEmotes)
{
auto jsonSets = jsonRoot.value("sets").toObject();
auto emotes = EmoteMap();
for (auto _jsonEmote : jsonEmotes) {
auto jsonEmote = _jsonEmote.toObject();
for (auto jsonSet : jsonSets) {
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
// margins
auto id = EmoteId{QString::number(jsonEmote.value("id").toInt())};
auto name = EmoteName{jsonEmote.value("name").toString()};
auto urls = jsonEmote.value("urls").toObject();
for (auto jsonEmoteValue : jsonEmotes) {
auto jsonEmote = jsonEmoteValue.toObject();
Emote emote;
fillInEmoteData(urls, name, name.string + "<br/>Channel FFZ Emote",
emote);
emote.homePage =
Url{QString("https://www.frankerfacez.com/emoticon/%1-%2")
.arg(id.string)
.arg(name.string)};
auto name = EmoteName{jsonEmote.value("name").toString()};
auto id = EmoteId{jsonEmote.value("id").toString()};
auto urls = jsonEmote.value("urls").toObject();
emotes[name] = cachedOrMake(std::move(emote), id);
auto emote = Emote();
fillInEmoteData(urls, name,
name.string + "<br/>Global FFZ Emote", emote);
emote.homePage =
Url{QString("https://www.frankerfacez.com/emoticon/%1-%2")
.arg(id.string)
.arg(name.string)};
emotes[name] =
cachedOrMakeEmotePtr(std::move(emote), currentEmotes);
}
}
}
return {Success, std::move(emotes)};
}
return {Success, std::move(emotes)};
}
std::pair<Outcome, EmoteMap> parseChannelEmotes(const QJsonObject &jsonRoot)
{
auto jsonSets = jsonRoot.value("sets").toObject();
auto emotes = EmoteMap();
for (auto jsonSet : jsonSets) {
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
for (auto _jsonEmote : jsonEmotes) {
auto jsonEmote = _jsonEmote.toObject();
// margins
auto id =
EmoteId{QString::number(jsonEmote.value("id").toInt())};
auto name = EmoteName{jsonEmote.value("name").toString()};
auto urls = jsonEmote.value("urls").toObject();
Emote emote;
fillInEmoteData(urls, name,
name.string + "<br/>Channel FFZ Emote", emote);
emote.homePage =
Url{QString("https://www.frankerfacez.com/emoticon/%1-%2")
.arg(id.string)
.arg(name.string)};
emotes[name] = cachedOrMake(std::move(emote), id);
}
}
return {Success, std::move(emotes)};
}
} // namespace
FfzEmotes::FfzEmotes()
+128 -126
View File
@@ -24,157 +24,159 @@ static std::map<QString, std::string> sentMessages;
namespace detail {
PubSubClient::PubSubClient(WebsocketClient &websocketClient,
WebsocketHandle handle)
: websocketClient_(websocketClient)
, handle_(handle)
{
}
void PubSubClient::start()
{
assert(!this->started_);
this->started_ = true;
this->ping();
}
void PubSubClient::stop()
{
assert(this->started_);
this->started_ = false;
}
bool PubSubClient::listen(rapidjson::Document &message)
{
int numRequestedListens = message["data"]["topics"].Size();
if (this->numListens_ + numRequestedListens > MAX_PUBSUB_LISTENS) {
// This PubSubClient is already at its peak listens
return false;
PubSubClient::PubSubClient(WebsocketClient &websocketClient,
WebsocketHandle handle)
: websocketClient_(websocketClient)
, handle_(handle)
{
}
this->numListens_ += numRequestedListens;
void PubSubClient::start()
{
assert(!this->started_);
for (const auto &topic : message["data"]["topics"].GetArray()) {
this->listeners_.emplace_back(
Listener{topic.GetString(), false, false, false});
this->started_ = true;
this->ping();
}
auto uuid = CreateUUID();
void PubSubClient::stop()
{
assert(this->started_);
rj::set(message, "nonce", uuid);
this->started_ = false;
}
std::string payload = rj::stringify(message);
sentMessages[uuid] = payload;
bool PubSubClient::listen(rapidjson::Document &message)
{
int numRequestedListens = message["data"]["topics"].Size();
this->send(payload.c_str());
return true;
}
void PubSubClient::unlistenPrefix(const std::string &prefix)
{
std::vector<std::string> topics;
for (auto it = this->listeners_.begin(); it != this->listeners_.end();) {
const auto &listener = *it;
if (listener.topic.find(prefix) == 0) {
topics.push_back(listener.topic);
it = this->listeners_.erase(it);
} else {
++it;
if (this->numListens_ + numRequestedListens > MAX_PUBSUB_LISTENS) {
// This PubSubClient is already at its peak listens
return false;
}
}
if (topics.empty()) {
return;
}
this->numListens_ += numRequestedListens;
auto message = createUnlistenMessage(topics);
auto uuid = CreateUUID();
rj::set(message, "nonce", CreateUUID());
std::string payload = rj::stringify(message);
sentMessages[uuid] = payload;
this->send(payload.c_str());
}
void PubSubClient::handlePong()
{
assert(this->awaitingPong_);
log("Got pong!");
this->awaitingPong_ = false;
}
bool PubSubClient::isListeningToTopic(const std::string &payload)
{
for (const auto &listener : this->listeners_) {
if (listener.topic == payload) {
return true;
for (const auto &topic : message["data"]["topics"].GetArray()) {
this->listeners_.emplace_back(
Listener{topic.GetString(), false, false, false});
}
auto uuid = CreateUUID();
rj::set(message, "nonce", uuid);
std::string payload = rj::stringify(message);
sentMessages[uuid] = payload;
this->send(payload.c_str());
return true;
}
return false;
}
void PubSubClient::unlistenPrefix(const std::string &prefix)
{
std::vector<std::string> topics;
void PubSubClient::ping()
{
assert(this->started_);
for (auto it = this->listeners_.begin();
it != this->listeners_.end();) {
const auto &listener = *it;
if (listener.topic.find(prefix) == 0) {
topics.push_back(listener.topic);
it = this->listeners_.erase(it);
} else {
++it;
}
}
if (!this->send(pingPayload)) {
return;
if (topics.empty()) {
return;
}
auto message = createUnlistenMessage(topics);
auto uuid = CreateUUID();
rj::set(message, "nonce", CreateUUID());
std::string payload = rj::stringify(message);
sentMessages[uuid] = payload;
this->send(payload.c_str());
}
this->awaitingPong_ = true;
void PubSubClient::handlePong()
{
assert(this->awaitingPong_);
auto self = this->shared_from_this();
log("Got pong!");
runAfter(this->websocketClient_.get_io_service(), std::chrono::seconds(15),
[self](auto timer) {
if (!self->started_) {
return;
}
this->awaitingPong_ = false;
}
if (self->awaitingPong_) {
log("No pong respnose, disconnect!");
// TODO(pajlada): Label this connection as "disconnect me"
}
});
runAfter(this->websocketClient_.get_io_service(), std::chrono::minutes(5),
[self](auto timer) {
if (!self->started_) {
return;
}
self->ping(); //
});
}
bool PubSubClient::send(const char *payload)
{
WebsocketErrorCode ec;
this->websocketClient_.send(this->handle_, payload,
websocketpp::frame::opcode::text, ec);
if (ec) {
log("Error sending message {}: {}", payload, ec.message());
// TODO(pajlada): Check which error code happened and maybe gracefully
// handle it
bool PubSubClient::isListeningToTopic(const std::string &payload)
{
for (const auto &listener : this->listeners_) {
if (listener.topic == payload) {
return true;
}
}
return false;
}
return true;
}
void PubSubClient::ping()
{
assert(this->started_);
if (!this->send(pingPayload)) {
return;
}
this->awaitingPong_ = true;
auto self = this->shared_from_this();
runAfter(this->websocketClient_.get_io_service(),
std::chrono::seconds(15), [self](auto timer) {
if (!self->started_) {
return;
}
if (self->awaitingPong_) {
log("No pong respnose, disconnect!");
// TODO(pajlada): Label this connection as "disconnect
// me"
}
});
runAfter(this->websocketClient_.get_io_service(),
std::chrono::minutes(5), [self](auto timer) {
if (!self->started_) {
return;
}
self->ping(); //
});
}
bool PubSubClient::send(const char *payload)
{
WebsocketErrorCode ec;
this->websocketClient_.send(this->handle_, payload,
websocketpp::frame::opcode::text, ec);
if (ec) {
log("Error sending message {}: {}", payload, ec.message());
// TODO(pajlada): Check which error code happened and maybe
// gracefully handle it
return false;
}
return true;
}
} // namespace detail
+27 -26
View File
@@ -32,41 +32,42 @@ using WebsocketErrorCode = websocketpp::lib::error_code;
namespace detail {
struct Listener {
std::string topic;
bool authed;
bool persistent;
bool confirmed = false;
};
struct Listener {
std::string topic;
bool authed;
bool persistent;
bool confirmed = false;
};
class PubSubClient : public std::enable_shared_from_this<PubSubClient>
{
public:
PubSubClient(WebsocketClient &_websocketClient, WebsocketHandle _handle);
class PubSubClient : public std::enable_shared_from_this<PubSubClient>
{
public:
PubSubClient(WebsocketClient &_websocketClient,
WebsocketHandle _handle);
void start();
void stop();
void start();
void stop();
bool listen(rapidjson::Document &message);
void unlistenPrefix(const std::string &prefix);
bool listen(rapidjson::Document &message);
void unlistenPrefix(const std::string &prefix);
void handlePong();
void handlePong();
bool isListeningToTopic(const std::string &topic);
bool isListeningToTopic(const std::string &topic);
private:
void ping();
bool send(const char *payload);
private:
void ping();
bool send(const char *payload);
WebsocketClient &websocketClient_;
WebsocketHandle handle_;
uint16_t numListens_ = 0;
WebsocketClient &websocketClient_;
WebsocketHandle handle_;
uint16_t numListens_ = 0;
std::vector<Listener> listeners_;
std::vector<Listener> listeners_;
std::atomic<bool> awaitingPong_{false};
std::atomic<bool> started_{false};
};
std::atomic<bool> awaitingPong_{false};
std::atomic<bool> started_{false};
};
} // namespace detail
+2 -3
View File
@@ -1,11 +1,10 @@
#pragma once
#include "debug/Log.hpp"
#include "util/RapidjsonHelpers.hpp"
#include "debug/Log.hpp"
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <memory>
#include "debug/Log.hpp"
#include "util/RapidjsonHelpers.hpp"
namespace chatterino {
+23 -23
View File
@@ -15,32 +15,32 @@ namespace chatterino {
namespace {
EmoteName cleanUpCode(const EmoteName &dirtyEmoteCode)
{
auto cleanCode = dirtyEmoteCode.string;
cleanCode.detach();
EmoteName cleanUpCode(const EmoteName &dirtyEmoteCode)
{
auto cleanCode = dirtyEmoteCode.string;
cleanCode.detach();
static QMap<QString, QString> emoteNameReplacements{
{"[oO](_|\\.)[oO]", "O_o"}, {"\\&gt\\;\\(", "&gt;("},
{"\\&lt\\;3", "&lt;3"}, {"\\:-?(o|O)", ":O"},
{"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"},
{"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("},
{"\\:-?\\)", ":)"}, {"\\:-?D", ":D"},
{"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"},
{"R-?\\)", "R)"}, {"B-?\\)", "B)"},
};
static QMap<QString, QString> emoteNameReplacements{
{"[oO](_|\\.)[oO]", "O_o"}, {"\\&gt\\;\\(", "&gt;("},
{"\\&lt\\;3", "&lt;3"}, {"\\:-?(o|O)", ":O"},
{"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"},
{"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("},
{"\\:-?\\)", ":)"}, {"\\:-?D", ":D"},
{"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"},
{"R-?\\)", "R)"}, {"B-?\\)", "B)"},
};
auto it = emoteNameReplacements.find(dirtyEmoteCode.string);
if (it != emoteNameReplacements.end()) {
cleanCode = it.value();
auto it = emoteNameReplacements.find(dirtyEmoteCode.string);
if (it != emoteNameReplacements.end()) {
cleanCode = it.value();
}
cleanCode.replace("&lt;", "<");
cleanCode.replace("&gt;", ">");
return {cleanCode};
}
cleanCode.replace("&lt;", "<");
cleanCode.replace("&gt;", ">");
return {cleanCode};
}
} // namespace
TwitchAccount::TwitchAccount(const QString &username, const QString &oauthToken,
@@ -417,7 +417,7 @@ void TwitchAccount::loadEmotes()
}
AccessGuard<const TwitchAccount::TwitchAccountEmoteData>
TwitchAccount::accessEmotes() const
TwitchAccount::accessEmotes() const
{
return this->emotes_.accessConst();
}
+34 -33
View File
@@ -25,48 +25,49 @@
namespace chatterino {
namespace {
auto parseRecentMessages(const QJsonObject &jsonRoot, TwitchChannel &channel)
{
QJsonArray jsonMessages = jsonRoot.value("messages").toArray();
std::vector<MessagePtr> messages;
auto parseRecentMessages(const QJsonObject &jsonRoot,
TwitchChannel &channel)
{
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) {
auto content = jsonMessage.toString().toUtf8();
// passing nullptr as the channel makes the message invalid but we don't
// check for that anyways
auto message = Communi::IrcMessage::fromData(content, nullptr);
auto privMsg = dynamic_cast<Communi::IrcPrivateMessage *>(message);
assert(privMsg);
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
auto message = Communi::IrcMessage::fromData(content, nullptr);
auto privMsg = dynamic_cast<Communi::IrcPrivateMessage *>(message);
assert(privMsg);
MessageParseArgs args;
TwitchMessageBuilder builder(&channel, privMsg, args);
if (!builder.isIgnored()) {
messages.push_back(builder.build());
MessageParseArgs args;
TwitchMessageBuilder builder(&channel, privMsg, args);
if (!builder.isIgnored()) {
messages.push_back(builder.build());
}
}
return messages;
}
std::pair<Outcome, UsernameSet> parseChatters(const QJsonObject &jsonRoot)
{
static QStringList categories = {"moderators", "staff", "admins",
"global_mods", "viewers"};
return messages;
}
std::pair<Outcome, UsernameSet> parseChatters(const QJsonObject &jsonRoot)
{
static QStringList categories = {"moderators", "staff", "admins",
"global_mods", "viewers"};
auto usernames = UsernameSet();
auto usernames = UsernameSet();
// parse json
QJsonObject jsonCategories = jsonRoot.value("chatters").toObject();
// parse json
QJsonObject jsonCategories = jsonRoot.value("chatters").toObject();
for (const auto &category : categories) {
for (auto jsonCategory : jsonCategories.value(category).toArray()) {
usernames.insert(jsonCategory.toString());
for (const auto &category : categories) {
for (auto jsonCategory : jsonCategories.value(category).toArray()) {
usernames.insert(jsonCategory.toString());
}
}
}
return {Success, std::move(usernames)};
}
return {Success, std::move(usernames)};
}
} // namespace
TwitchChannel::TwitchChannel(const QString &name,
@@ -309,7 +310,7 @@ bool TwitchChannel::isLive() const
}
AccessGuard<const TwitchChannel::StreamStatus>
TwitchChannel::accessStreamStatus() const
TwitchChannel::accessStreamStatus() const
{
return this->streamStatus_.accessConst();
}
+177 -177
View File
@@ -8,235 +8,235 @@ namespace chatterino {
namespace {
template <typename Type>
inline bool ReadValue(const rapidjson::Value &object, const char *key,
Type &out)
{
if (!object.HasMember(key)) {
return false;
}
const auto &value = object[key];
if (!value.Is<Type>()) {
return false;
}
out = value.Get<Type>();
return true;
}
template <>
inline bool ReadValue<QString>(const rapidjson::Value &object, const char *key,
QString &out)
{
if (!object.HasMember(key)) {
return false;
}
const auto &value = object[key];
if (!value.IsString()) {
return false;
}
out = value.GetString();
return true;
}
template <>
inline bool ReadValue<std::vector<QString>>(const rapidjson::Value &object,
const char *key,
std::vector<QString> &out)
{
if (!object.HasMember(key)) {
return false;
}
const auto &value = object[key];
if (!value.IsArray()) {
return false;
}
for (const rapidjson::Value &innerValue : value.GetArray()) {
if (!innerValue.IsString()) {
template <typename Type>
inline bool ReadValue(const rapidjson::Value &object, const char *key,
Type &out)
{
if (!object.HasMember(key)) {
return false;
}
out.emplace_back(innerValue.GetString());
}
const auto &value = object[key];
return true;
}
// Parse a single cheermote set (or "action") from the twitch api
inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set,
const rapidjson::Value &action)
{
if (!action.IsObject()) {
return false;
}
if (!ReadValue(action, "prefix", set.prefix)) {
return false;
}
if (!ReadValue(action, "scales", set.scales)) {
return false;
}
if (!ReadValue(action, "backgrounds", set.backgrounds)) {
return false;
}
if (!ReadValue(action, "states", set.states)) {
return false;
}
if (!ReadValue(action, "type", set.type)) {
return false;
}
if (!ReadValue(action, "updated_at", set.updatedAt)) {
return false;
}
if (!ReadValue(action, "priority", set.priority)) {
return false;
}
// Tiers
if (!action.HasMember("tiers")) {
return false;
}
const auto &tiersValue = action["tiers"];
if (!tiersValue.IsArray()) {
return false;
}
for (const rapidjson::Value &tierValue : tiersValue.GetArray()) {
JSONCheermoteSet::CheermoteTier tier;
if (!tierValue.IsObject()) {
if (!value.Is<Type>()) {
return false;
}
if (!ReadValue(tierValue, "min_bits", tier.minBits)) {
out = value.Get<Type>();
return true;
}
template <>
inline bool ReadValue<QString>(const rapidjson::Value &object,
const char *key, QString &out)
{
if (!object.HasMember(key)) {
return false;
}
if (!ReadValue(tierValue, "id", tier.id)) {
const auto &value = object[key];
if (!value.IsString()) {
return false;
}
if (!ReadValue(tierValue, "color", tier.color)) {
out = value.GetString();
return true;
}
template <>
inline bool ReadValue<std::vector<QString>>(const rapidjson::Value &object,
const char *key,
std::vector<QString> &out)
{
if (!object.HasMember(key)) {
return false;
}
// Images
if (!tierValue.HasMember("images")) {
const auto &value = object[key];
if (!value.IsArray()) {
return false;
}
const auto &imagesValue = tierValue["images"];
if (!imagesValue.IsObject()) {
return false;
}
// Read images object
for (const auto &imageBackgroundValue : imagesValue.GetObject()) {
QString background = imageBackgroundValue.name.GetString();
bool backgroundExists = false;
for (const auto &bg : set.backgrounds) {
if (background == bg) {
backgroundExists = true;
break;
}
for (const rapidjson::Value &innerValue : value.GetArray()) {
if (!innerValue.IsString()) {
return false;
}
if (!backgroundExists) {
continue;
out.emplace_back(innerValue.GetString());
}
return true;
}
// Parse a single cheermote set (or "action") from the twitch api
inline bool ParseSingleCheermoteSet(JSONCheermoteSet &set,
const rapidjson::Value &action)
{
if (!action.IsObject()) {
return false;
}
if (!ReadValue(action, "prefix", set.prefix)) {
return false;
}
if (!ReadValue(action, "scales", set.scales)) {
return false;
}
if (!ReadValue(action, "backgrounds", set.backgrounds)) {
return false;
}
if (!ReadValue(action, "states", set.states)) {
return false;
}
if (!ReadValue(action, "type", set.type)) {
return false;
}
if (!ReadValue(action, "updated_at", set.updatedAt)) {
return false;
}
if (!ReadValue(action, "priority", set.priority)) {
return false;
}
// Tiers
if (!action.HasMember("tiers")) {
return false;
}
const auto &tiersValue = action["tiers"];
if (!tiersValue.IsArray()) {
return false;
}
for (const rapidjson::Value &tierValue : tiersValue.GetArray()) {
JSONCheermoteSet::CheermoteTier tier;
if (!tierValue.IsObject()) {
return false;
}
const rapidjson::Value &imageBackgroundStates =
imageBackgroundValue.value;
if (!imageBackgroundStates.IsObject()) {
continue;
if (!ReadValue(tierValue, "min_bits", tier.minBits)) {
return false;
}
// Read each key which represents a background
for (const auto &imageBackgroundState :
imageBackgroundStates.GetObject()) {
QString state = imageBackgroundState.name.GetString();
bool stateExists = false;
for (const auto &_state : set.states) {
if (state == _state) {
stateExists = true;
if (!ReadValue(tierValue, "id", tier.id)) {
return false;
}
if (!ReadValue(tierValue, "color", tier.color)) {
return false;
}
// Images
if (!tierValue.HasMember("images")) {
return false;
}
const auto &imagesValue = tierValue["images"];
if (!imagesValue.IsObject()) {
return false;
}
// Read images object
for (const auto &imageBackgroundValue : imagesValue.GetObject()) {
QString background = imageBackgroundValue.name.GetString();
bool backgroundExists = false;
for (const auto &bg : set.backgrounds) {
if (background == bg) {
backgroundExists = true;
break;
}
}
if (!stateExists) {
if (!backgroundExists) {
continue;
}
const rapidjson::Value &imageScalesValue =
imageBackgroundState.value;
if (!imageScalesValue.IsObject()) {
const rapidjson::Value &imageBackgroundStates =
imageBackgroundValue.value;
if (!imageBackgroundStates.IsObject()) {
continue;
}
// Read each key which represents a scale
for (const auto &imageScaleValue :
imageScalesValue.GetObject()) {
QString scale = imageScaleValue.name.GetString();
bool scaleExists = false;
for (const auto &_scale : set.scales) {
if (scale == _scale) {
scaleExists = true;
// Read each key which represents a background
for (const auto &imageBackgroundState :
imageBackgroundStates.GetObject()) {
QString state = imageBackgroundState.name.GetString();
bool stateExists = false;
for (const auto &_state : set.states) {
if (state == _state) {
stateExists = true;
break;
}
}
if (!scaleExists) {
if (!stateExists) {
continue;
}
const rapidjson::Value &imageScaleURLValue =
imageScaleValue.value;
if (!imageScaleURLValue.IsString()) {
const rapidjson::Value &imageScalesValue =
imageBackgroundState.value;
if (!imageScalesValue.IsObject()) {
continue;
}
QString url = imageScaleURLValue.GetString();
// Read each key which represents a scale
for (const auto &imageScaleValue :
imageScalesValue.GetObject()) {
QString scale = imageScaleValue.name.GetString();
bool scaleExists = false;
for (const auto &_scale : set.scales) {
if (scale == _scale) {
scaleExists = true;
break;
}
}
bool ok = false;
qreal scaleNumber = scale.toFloat(&ok);
if (!ok) {
continue;
if (!scaleExists) {
continue;
}
const rapidjson::Value &imageScaleURLValue =
imageScaleValue.value;
if (!imageScaleURLValue.IsString()) {
continue;
}
QString url = imageScaleURLValue.GetString();
bool ok = false;
qreal scaleNumber = scale.toFloat(&ok);
if (!ok) {
continue;
}
qreal chatterinoScale = 1 / scaleNumber;
auto image = Image::fromUrl({url}, chatterinoScale);
// TODO(pajlada): Fill in name and tooltip
tier.images[background][state][scale] = image;
}
qreal chatterinoScale = 1 / scaleNumber;
auto image = Image::fromUrl({url}, chatterinoScale);
// TODO(pajlada): Fill in name and tooltip
tier.images[background][state][scale] = image;
}
}
set.tiers.emplace_back(tier);
}
set.tiers.emplace_back(tier);
return true;
}
return true;
}
} // namespace
// Look through the results of
+33 -33
View File
@@ -34,43 +34,43 @@ struct TwitchUser {
namespace pajlada {
namespace Settings {
template <>
struct Deserialize<chatterino::TwitchUser> {
static chatterino::TwitchUser get(const rapidjson::Value &value,
bool *error = nullptr)
{
using namespace chatterino;
template <>
struct Deserialize<chatterino::TwitchUser> {
static chatterino::TwitchUser get(const rapidjson::Value &value,
bool *error = nullptr)
{
using namespace chatterino;
TwitchUser user;
TwitchUser user;
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)) {
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION("Missing ID key");
return user;
}
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)) {
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION("Missing display name key");
return user;
}
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)) {
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION("Missing ID key");
return user;
}
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)) {
PAJLADA_REPORT_ERROR(error)
PAJLADA_THROW_EXCEPTION("Missing display name key");
return user;
}
return user;
}
};
};
} // namespace Settings
} // namespace pajlada