categorized emtotepopup

This commit is contained in:
fourtf 2018-08-11 14:20:53 +02:00
parent 09b8a9d821
commit c719bb6b74
55 changed files with 420 additions and 604 deletions

View file

@ -233,7 +233,6 @@ SOURCES += \
src/providers/twitch/PubsubClient.cpp \
src/providers/twitch/TwitchApi.cpp \
src/messages/Emote.cpp \
src/messages/EmoteMap.cpp \
src/messages/ImageSet.cpp \
src/providers/bttv/BttvEmotes.cpp \
src/providers/ffz/FfzEmotes.cpp \
@ -250,7 +249,8 @@ SOURCES += \
src/util/FunctionEventFilter.cpp \
src/widgets/helper/EffectLabel.cpp \
src/widgets/helper/Button.cpp \
src/messages/MessageContainer.cpp
src/messages/MessageContainer.cpp \
src/debug/Benchmark.cpp
HEADERS += \
src/Application.hpp \
@ -258,7 +258,7 @@ HEADERS += \
src/common/Common.hpp \
src/common/CompletionModel.hpp \
src/common/FlagsEnum.hpp \
src/common/MutexValue.hpp \
src/common/Atomic.hpp \
src/common/NetworkCommon.hpp \
src/common/NetworkData.hpp \
src/common/NetworkManager.hpp \
@ -305,7 +305,6 @@ HEADERS += \
src/messages/MessageBuilder.hpp \
src/messages/MessageColor.hpp \
src/messages/MessageElement.hpp \
src/messages/MessageParseArgs.hpp \
src/messages/Selection.hpp \
src/PrecompiledHeader.hpp \
src/providers/emoji/Emojis.hpp \
@ -414,7 +413,6 @@ HEADERS += \
src/singletons/Updates.hpp \
src/singletons/NativeMessaging.hpp \
src/singletons/Theme.hpp \
src/common/SimpleSignalVector.hpp \
src/common/SignalVector.hpp \
src/widgets/dialogs/LogsPopup.hpp \
src/common/Singleton.hpp \
@ -427,7 +425,6 @@ HEADERS += \
src/providers/twitch/PubsubClient.hpp \
src/providers/twitch/TwitchApi.hpp \
src/messages/Emote.hpp \
src/messages/EmoteMap.hpp \
src/messages/EmoteCache.hpp \
src/messages/ImageSet.hpp \
src/common/Outcome.hpp \

View file

@ -113,11 +113,11 @@ void Application::initNm()
void Application::initPubsub()
{
this->twitch.pubsub->signals_.whisper.sent.connect([](const auto &msg) {
Log("WHISPER SENT LOL"); //
log("WHISPER SENT LOL"); //
});
this->twitch.pubsub->signals_.whisper.received.connect([](const auto &msg) {
Log("WHISPER RECEIVED LOL"); //
log("WHISPER RECEIVED LOL"); //
});
this->twitch.pubsub->signals_.moderation.chatCleared.connect(

View file

@ -6,14 +6,14 @@
namespace chatterino {
template <typename T>
class MutexValue : boost::noncopyable
class Atomic : boost::noncopyable
{
public:
MutexValue()
Atomic()
{
}
MutexValue(T &&val)
Atomic(T &&val)
: value_(val)
{
}
@ -32,6 +32,13 @@ public:
this->value_ = val;
}
void set(T &&val)
{
std::lock_guard<std::mutex> guard(this->mutex_);
this->value_ = std::move(val);
}
private:
mutable std::mutex mutex_;
T value_;

View file

@ -104,7 +104,7 @@ int CompletionModel::rowCount(const QModelIndex &) const
void CompletionModel::refresh()
{
Log("[CompletionModel:{}] Refreshing...]", this->channelName_);
log("[CompletionModel:{}] Refreshing...]", this->channelName_);
auto app = getApp();

View file

@ -136,7 +136,7 @@ void NetworkRequest::execute()
} break;
default: {
Log("[Execute] Unhandled request type");
log("[Execute] Unhandled request type");
} break;
}
}
@ -193,7 +193,8 @@ void NetworkRequest::doRequest()
data->payload_);
case NetworkRequestType::Delete:
return NetworkManager::accessManager.deleteResource(data->request_);
return NetworkManager::accessManager.deleteResource(
data->request_);
default:
return nullptr;
@ -201,13 +202,13 @@ void NetworkRequest::doRequest()
}();
if (reply == nullptr) {
Log("Unhandled request type");
log("Unhandled request type");
return;
}
if (timer->isStarted()) {
timer->onTimeout(worker, [reply, data]() {
Log("Aborted!");
log("Aborted!");
reply->abort();
if (data->onError_) {
data->onError_(-2);
@ -234,7 +235,7 @@ void NetworkRequest::doRequest()
NetworkResult result(bytes);
DebugCount::increase("http request success");
Log("starting {}", data->request_.url().toString());
// log("starting {}", data->request_.url().toString());
if (data->onSuccess_) {
if (data->executeConcurrently)
QtConcurrent::run(
@ -243,7 +244,7 @@ void NetworkRequest::doRequest()
else
data->onSuccess_(result);
}
Log("finished {}", data->request_.url().toString());
// log("finished {}", data->request_.url().toString());
reply->deleteLater();
};

View file

@ -31,7 +31,7 @@ rapidjson::Document NetworkResult::parseRapidJson() const
ret.Parse(this->data_.data(), this->data_.length());
if (result.Code() != rapidjson::kParseErrorNone) {
Log("JSON parse error: {} ({})",
log("JSON parse error: {} ({})",
rapidjson::GetParseError_En(result.Code()), result.Offset());
return ret;
}

View file

@ -1,34 +0,0 @@
#pragma once
#include <pajlada/signals/signal.hpp>
#include <mutex>
#include <vector>
namespace chatterino {
template <typename TValue>
class SimpleSignalVector
{
public:
SimpleSignalVector &operator=(std::vector<TValue> &other)
{
this->data_ = other;
this->updated.invoke();
return *this;
}
operator std::vector<TValue> &()
{
return this->data_;
}
pajlada::Signals::NoArgSignal updated;
private:
std::vector<TValue> data_;
};
} // namespace chatterino

View file

@ -46,16 +46,15 @@ public:
}
private:
T *element_;
std::mutex *mutex_;
bool isValid_ = true;
T *element_{};
std::mutex *mutex_{};
bool isValid_{true};
};
template <typename T>
class UniqueAccess
{
public:
// template <typename X = decltype(T())>
UniqueAccess()
: element_(T())
{

View file

@ -78,7 +78,7 @@ void CommandController::save()
{
QFile textFile(this->filePath_);
if (!textFile.open(QIODevice::WriteOnly)) {
Log("[CommandController::saveCommands] Unable to open {} for writing",
log("[CommandController::saveCommands] Unable to open {} for writing",
this->filePath_);
return;
}

21
src/debug/Benchmark.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "Benchmark.hpp"
namespace chatterino {
BenchmarkGuard::BenchmarkGuard(const QString &_name)
: name_(_name)
{
timer_.start();
}
BenchmarkGuard::~BenchmarkGuard()
{
log("{} {} ms", this->name_, float(timer_.nsecsElapsed()) / 1000000.0f);
}
qreal BenchmarkGuard::getElapsedMs()
{
return qreal(timer_.nsecsElapsed()) / 1000000.0;
}
} // namespace chatterino

View file

@ -3,40 +3,20 @@
#include "debug/Log.hpp"
#include <QElapsedTimer>
#include <boost/current_function.hpp>
#include <boost/noncopyable.hpp>
#define BENCH(x) \
QElapsedTimer x; \
x.start();
#define MARK(x) \
qDebug() << BOOST_CURRENT_FUNCTION << __LINE__ \
<< static_cast<float>(x.nsecsElapsed()) / 1000000.0 << "ms";
namespace chatterino {
class BenchmarkGuard : boost::noncopyable
{
QElapsedTimer timer;
QString name;
public:
BenchmarkGuard(const QString &_name)
: name(_name)
{
timer.start();
}
BenchmarkGuard(const QString &_name);
~BenchmarkGuard();
qreal getElapsedMs();
~BenchmarkGuard()
{
Log("{} {} ms", this->name, float(timer.nsecsElapsed()) / 1000000.0f);
}
qreal getElapsedMs()
{
return qreal(timer.nsecsElapsed()) / 1000000.0;
}
private:
QElapsedTimer timer_;
QString name_;
};
} // namespace chatterino

View file

@ -8,17 +8,22 @@
namespace chatterino {
template <typename... Args>
inline void Log(const std::string &formatString, Args &&... args)
inline void log(const std::string &formatString, Args &&... args)
{
qDebug().noquote() << QTime::currentTime().toString("hh:mm:ss.zzz")
<< fS(formatString, std::forward<Args>(args)...).c_str();
}
template <typename... Args>
inline void Warn(const std::string &formatString, Args &&... args)
inline void log(const char *formatString, Args &&... args)
{
qWarning() << QTime::currentTime().toString("hh:mm:ss.zzz")
<< fS(formatString, std::forward<Args>(args)...).c_str();
log(std::string(formatString), std::forward<Args>(args)...);
}
template <typename... Args>
inline void log(const QString &formatString, Args &&... args)
{
log(formatString.toStdString(), std::forward<Args>(args)...);
}
} // namespace chatterino

View file

@ -5,13 +5,15 @@
#include <QApplication>
#include <QStringList>
#include <messages/Image.hpp>
#include <memory>
using namespace chatterino;
int main(int argc, char **argv)
{
auto shared = std::make_shared<QString>();
log(std::atomic_is_lock_free(&shared));
QApplication a(argc, argv);
// convert char** to QStringList

View file

@ -15,61 +15,13 @@ bool operator!=(const Emote &a, const Emote &b)
return !(a == b);
}
// EmotePtr Emote::create(const EmoteData2 &data)
//{
//}
EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache)
{
// reuse old shared_ptr if nothing changed
auto it = cache.find(emote.name);
if (it != cache.end() && *it->second == emote) return it->second;
// EmotePtr Emote::create(EmoteData2 &&data)
//{
//}
// Emote::Emote(EmoteData2 &&data)
// : data_(data)
//{
//}
//
// Emote::Emote(const EmoteData2 &data)
// : data_(data)
//{
//}
//
// const Url &Emote::getHomePage() const
//{
// return this->data_.homePage;
//}
//
// const EmoteName &Emote::getName() const
//{
// return this->data_.name;
//}
//
// const Tooltip &Emote::getTooltip() const
//{
// return this->data_.tooltip;
//}
//
// const ImageSet &Emote::getImages() const
//{
// return this->data_.images;
//}
//
// const QString &Emote::getCopyString() const
//{
// return this->data_.name.string;
//}
//
// bool Emote::operator==(const Emote &other) const
//{
// auto &a = this->data_;
// auto &b = other.data_;
//
// return std::tie(a.homePage, a.name, a.tooltip, a.images) ==
// std::tie(b.homePage, b.name, b.tooltip, b.images);
//}
//
// bool Emote::operator!=(const Emote &other) const
//{
// return !this->operator==(other);
//}
return std::make_shared<Emote>(std::move(emote));
}
} // namespace chatterino

View file

@ -37,4 +37,6 @@ using EmoteIdMap = std::unordered_map<EmoteId, EmotePtr>;
using WeakEmoteMap = std::unordered_map<EmoteName, std::weak_ptr<const Emote>>;
using WeakEmoteIdMap = std::unordered_map<EmoteId, std::weak_ptr<const Emote>>;
EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache);
} // namespace chatterino

View file

@ -1,44 +0,0 @@
#include "EmoteMap.hpp"
#include "Application.hpp"
#include "singletons/Settings.hpp"
namespace chatterino {
// EmoteData::EmoteData(Image *image)
// : image1x(image)
//{
//}
//// Emotes must have a 1x image to be valid
// bool EmoteData::isValid() const
//{
// return this->image1x != nullptr;
//}
// Image *EmoteData::getImage(float scale) const
//{
// int quality = getApp()->settings->preferredEmoteQuality;
// if (quality == 0) {
// scale *= getApp()->settings->emoteScale.getValue();
// quality = [&] {
// if (scale <= 1) return 1;
// if (scale <= 2) return 2;
// return 3;
// }();
// }
// Image *_image;
// if (quality == 3 && this->image3x != nullptr) {
// _image = this->image3x;
// } else if (quality >= 2 && this->image2x != nullptr) {
// _image = this->image2x;
// } else {
// _image = this->image1x;
// }
// return _image;
//}
} // namespace chatterino

View file

@ -1,20 +0,0 @@
#pragma once
#include "boost/optional.hpp"
#include "messages/Emote.hpp"
namespace chatterino {
// class EmoteMap
//{
// public:
// void add(Emote emote);
// void remove(const Emote &emote);
// void remove(const QString &name);
// private:
//};
// using EmoteMap = ConcurrentMap<QString, EmoteData>;
} // namespace chatterino

View file

@ -78,7 +78,7 @@ QVector<Frame<QImage>> readFrames(QImageReader &reader, const Url &url)
QVector<Frame<QImage>> frames;
if (reader.imageCount() == 0) {
Log("Error while reading image {}: '{}'", url.string,
log("Error while reading image {}: '{}'", url.string,
reader.errorString());
return frames;
}
@ -94,7 +94,7 @@ QVector<Frame<QImage>> readFrames(QImageReader &reader, const Url &url)
}
if (frames.size() == 0) {
Log("Error while reading image {}: '{}'", url.string,
log("Error while reading image {}: '{}'", url.string,
reader.errorString());
}
@ -200,7 +200,7 @@ Image::Image(const Url &url, qreal scale)
Image::Image(const QPixmap &pixmap, qreal scale)
: scale_(scale)
, frames_(QVector<Frame<QPixmap>>{Frame<QPixmap>{pixmap}})
, frames_(QVector<Frame<QPixmap>>{Frame<QPixmap>{pixmap, 1}})
{
}

View file

@ -16,6 +16,14 @@ const TimeoutMessageTag timeoutMessage{};
MessagePtr makeSystemMessage(const QString &text);
struct MessageParseArgs {
bool disablePingSounds = false;
bool isReceivedWhisper = false;
bool isSentWhisper = false;
bool trimSubscriberUsername = false;
bool isStaffOrBroadcaster = false;
};
class MessageBuilder
{
public:

View file

@ -2,12 +2,5 @@
namespace chatterino {
struct MessageParseArgs {
bool disablePingSounds = false;
bool isReceivedWhisper = false;
bool isSentWhisper = false;
bool trimSubscriberUsername = false;
bool isStaffOrBroadcaster = false;
};
} // namespace chatterino

View file

@ -35,7 +35,7 @@ struct SelectionItem {
bool operator>(const SelectionItem &b) const
{
return b.operator<(*this);
return this->operator!=(b) && b.operator<(*this);
}
bool operator==(const SelectionItem &b) const

View file

@ -31,13 +31,10 @@ public:
const Message *getMessage();
// Height
int getHeight() const;
// Flags
MessageLayoutFlags flags;
// Layout
bool layout(int width, float scale_, MessageElementFlags flags);
// Painting

View file

@ -126,7 +126,8 @@ void MessageLayoutContainer::breakLine()
int xOffset = 0;
if (this->flags_.has(MessageFlag::Centered) && this->elements_.size() > 0) {
xOffset = (width_ - this->elements_.at(this->elements_.size() - 1)
xOffset = (width_ - this->elements_.at(0)->getRect().left() -
this->elements_.at(this->elements_.size() - 1)
->getRect()
.right()) /
2;

View file

@ -10,9 +10,7 @@
#include <QThread>
namespace chatterino {
namespace {
Url getEmoteLink(QString urlTemplate, const EmoteId &id,
const QString &emoteScale)
{
@ -21,72 +19,14 @@ Url getEmoteLink(QString urlTemplate, const EmoteId &id,
return {urlTemplate.replace("{{id}}", id.string)
.replace("{{image}}", emoteScale)};
}
} // namespace
AccessGuard<const EmoteMap> BttvEmotes::accessGlobalEmotes() const
{
return this->globalEmotes_.accessConst();
}
boost::optional<EmotePtr> BttvEmotes::getGlobalEmote(const EmoteName &name)
{
auto emotes = this->globalEmotes_.access();
auto it = emotes->find(name);
if (it == emotes->end()) return boost::none;
return it->second;
}
// FOURTF: never returns anything
// boost::optional<EmotePtr> BttvEmotes::getEmote(const EmoteId &id)
//{
// auto cache = this->channelEmoteCache_.access();
// auto it = cache->find(id);
//
// if (it != cache->end()) {
// auto shared = it->second.lock();
// if (shared) {
// return shared;
// }
// }
//
// return boost::none;
//}
void BttvEmotes::loadGlobalEmotes()
{
auto request = NetworkRequest(QString(globalEmoteApiUrl));
request.setCaller(QThread::currentThread());
request.setTimeout(30000);
request.onSuccess([this](auto result) -> Outcome {
// if (auto shared = weak.lock()) {
auto currentEmotes = this->globalEmotes_.access();
auto pair = this->parseGlobalEmotes(result.parseJson(), *currentEmotes);
if (pair.first) {
*currentEmotes = std::move(pair.second);
}
return pair.first;
// }
return Failure;
});
request.execute();
}
std::pair<Outcome, EmoteMap> BttvEmotes::parseGlobalEmotes(
const QJsonObject &jsonRoot, const EmoteMap &currentEmotes)
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
const EmoteMap &currentEmotes)
{
auto emotes = EmoteMap();
auto jsonEmotes = jsonRoot.value("emotes").toArray();
auto urlTemplate =
QString("https:" + jsonRoot.value("urlTemplate").toString());
auto urlTemplate = qS("https:") + jsonRoot.value("urlTemplate").toString();
for (const QJsonValue &jsonEmote : jsonEmotes) {
for (auto jsonEmote : jsonEmotes) {
auto id = EmoteId{jsonEmote.toObject().value("id").toString()};
auto name = EmoteName{jsonEmote.toObject().value("code").toString()};
@ -99,16 +39,49 @@ std::pair<Outcome, EmoteMap> BttvEmotes::parseGlobalEmotes(
Tooltip{name.string + "<br />Global Bttv Emote"},
Url{"https://manage.betterttv.net/emotes/" + id.string}});
auto it = currentEmotes.find(name);
if (it != currentEmotes.end() && *it->second == emote) {
// reuse old shared_ptr if nothing changed
emotes[name] = it->second;
} else {
emotes[name] = std::make_shared<Emote>(std::move(emote));
}
emotes[name] = cachedOrMakeEmotePtr(std::move(emote), currentEmotes);
}
return {Success, std::move(emotes)};
}
} // namespace
BttvEmotes::BttvEmotes()
: global_(std::make_shared<EmoteMap>())
{
}
std::shared_ptr<const EmoteMap> BttvEmotes::global() const
{
return this->global_.get();
}
boost::optional<EmotePtr> BttvEmotes::global(const EmoteName &name) const
{
auto emotes = this->global_.get();
auto it = emotes->find(name);
if (it == emotes->end()) return boost::none;
return it->second;
}
void BttvEmotes::loadGlobal()
{
auto request = NetworkRequest(QString(globalEmoteApiUrl));
request.setCaller(QThread::currentThread());
request.setTimeout(30000);
request.onSuccess([this](auto result) -> Outcome {
auto emotes = this->global_.get();
auto pair = parseGlobalEmotes(result.parseJson(), *emotes);
if (pair.first)
this->global_.set(
std::make_shared<EmoteMap>(std::move(pair.second)));
return pair.first;
});
request.execute();
}
} // namespace chatterino

View file

@ -1,33 +1,25 @@
#pragma once
#include <memory>
#include "common/UniqueAccess.hpp"
#include "common/Atomic.hpp"
#include "messages/Emote.hpp"
#include "messages/EmoteCache.hpp"
namespace chatterino {
class BttvEmotes final : std::enable_shared_from_this<BttvEmotes>
class BttvEmotes final
{
static constexpr const char *globalEmoteApiUrl =
"https://api.betterttv.net/2/emotes";
public:
// BttvEmotes();
BttvEmotes();
AccessGuard<const EmoteMap> accessGlobalEmotes() const;
boost::optional<EmotePtr> getGlobalEmote(const EmoteName &name);
boost::optional<EmotePtr> getEmote(const EmoteId &id);
void loadGlobalEmotes();
std::shared_ptr<const EmoteMap> global() const;
boost::optional<EmotePtr> global(const EmoteName &name) const;
void loadGlobal();
private:
std::pair<Outcome, EmoteMap> parseGlobalEmotes(
const QJsonObject &jsonRoot, const EmoteMap &currentEmotes);
UniqueAccess<EmoteMap> globalEmotes_;
// UniqueAccess<WeakEmoteIdMap> channelEmoteCache_;
Atomic<std::shared_ptr<const EmoteMap>> global_;
};
} // namespace chatterino

View file

@ -118,7 +118,7 @@ void Emojis::loadEmojis()
rapidjson::ParseResult result = root.Parse(data.toUtf8(), data.length());
if (result.Code() != rapidjson::kParseErrorNone) {
Log("JSON parse error: {} ({})",
log("JSON parse error: {} ({})",
rapidjson::GetParseError_En(result.Code()), result.Offset());
return;
}
@ -146,7 +146,7 @@ void Emojis::loadEmojis()
auto toneNameIt = toneNames.find(tone);
if (toneNameIt == toneNames.end()) {
Log("Tone with key {} does not exist in tone names map",
log("Tone with key {} does not exist in tone names map",
tone);
continue;
}
@ -219,7 +219,7 @@ void Emojis::loadEmojiSet()
auto app = getApp();
app->settings->emojiSet.connect([=](const auto &emojiSet, auto) {
Log("Using emoji set {}", emojiSet);
log("Using emoji set {}", emojiSet);
this->emojis.each([=](const auto &name,
std::shared_ptr<EmojiData> &emoji) {
QString emojiSetToUse = emojiSet;

View file

@ -1,6 +1,5 @@
#pragma once
#include "common/SimpleSignalVector.hpp"
#include "messages/Emote.hpp"
#include "util/ConcurrentMap.hpp"

View file

@ -19,7 +19,6 @@ Url getEmoteLink(const QJsonObject &urls, const QString &emoteScale)
return {"https:" + emote.toString()};
}
void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
const QString &tooltip, Emote &emoteData)
{
@ -34,52 +33,11 @@ void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
Image::fromUrl(url3x, 0.25)};
emoteData.tooltip = {tooltip};
}
} // namespace
AccessGuard<const EmoteCache<EmoteName>> FfzEmotes::accessGlobalEmotes() const
{
return this->globalEmotes_.accessConst();
}
boost::optional<EmotePtr> FfzEmotes::getEmote(const EmoteId &id)
{
auto cache = this->channelEmoteCache_.access();
auto it = cache->find(id);
if (it != cache->end()) {
auto shared = it->second.lock();
if (shared) {
return shared;
}
}
return boost::none;
}
boost::optional<EmotePtr> FfzEmotes::getGlobalEmote(const EmoteName &name)
{
return this->globalEmotes_.access()->get(name);
}
void FfzEmotes::loadGlobalEmotes()
{
QString url("https://api.frankerfacez.com/v1/set/global");
NetworkRequest request(url);
request.setCaller(QThread::currentThread());
request.setTimeout(30000);
request.onSuccess([this](auto result) -> Outcome {
return this->parseGlobalEmotes(result.parseJson());
});
request.execute();
}
Outcome FfzEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot)
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
const EmoteMap &currentEmotes)
{
auto jsonSets = jsonRoot.value("sets").toObject();
auto emotes = this->globalEmotes_.access();
auto replacement = emotes->makeReplacment();
auto emotes = EmoteMap();
for (auto jsonSet : jsonSets) {
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
@ -99,14 +57,54 @@ Outcome FfzEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot)
.arg(id.string)
.arg(name.string)};
replacement.add(name, emote);
emotes[name] =
cachedOrMakeEmotePtr(std::move(emote), currentEmotes);
}
}
return Success;
return {Success, std::move(emotes)};
}
} // namespace
FfzEmotes::FfzEmotes()
: global_(std::make_shared<EmoteMap>())
{
}
void FfzEmotes::loadChannelEmotes(const QString &channelName,
std::shared_ptr<const EmoteMap> FfzEmotes::global() const
{
return this->global_.get();
}
boost::optional<EmotePtr> FfzEmotes::global(const EmoteName &name) const
{
auto emotes = this->global_.get();
auto it = emotes->find(name);
if (it != emotes->end()) return it->second;
return boost::none;
}
void FfzEmotes::loadGlobal()
{
QString url("https://api.frankerfacez.com/v1/set/global");
NetworkRequest request(url);
request.setCaller(QThread::currentThread());
request.setTimeout(30000);
request.onSuccess([this](auto result) -> Outcome {
auto emotes = this->global();
auto pair = parseGlobalEmotes(result.parseJson(), *emotes);
if (pair.first)
this->global_.set(
std::make_shared<EmoteMap>(std::move(pair.second)));
return pair.first;
});
request.execute();
}
void FfzEmotes::loadChannel(const QString &channelName,
std::function<void(EmoteMap &&)> callback)
{
// printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n",

View file

@ -2,7 +2,7 @@
#include <memory>
#include "common/UniqueAccess.hpp"
#include "common/Atomic.hpp"
#include "messages/Emote.hpp"
#include "messages/EmoteCache.hpp"
@ -16,24 +16,17 @@ class FfzEmotes final : std::enable_shared_from_this<FfzEmotes>
"https://api.betterttv.net/2/channels/";
public:
// FfzEmotes();
FfzEmotes();
static std::shared_ptr<FfzEmotes> create();
std::shared_ptr<const EmoteMap> global() const;
boost::optional<EmotePtr> global(const EmoteName &name) const;
AccessGuard<const EmoteCache<EmoteName>> accessGlobalEmotes() const;
boost::optional<EmotePtr> getGlobalEmote(const EmoteName &name);
boost::optional<EmotePtr> getEmote(const EmoteId &id);
void loadGlobalEmotes();
void loadChannelEmotes(const QString &channelName,
void loadGlobal();
void loadChannel(const QString &channelName,
std::function<void(EmoteMap &&)> callback);
protected:
Outcome parseGlobalEmotes(const QJsonObject &jsonRoot);
Outcome parseChannelEmotes(const QJsonObject &jsonRoot);
UniqueAccess<EmoteCache<EmoteName>> globalEmotes_;
UniqueAccess<WeakEmoteIdMap> channelEmoteCache_;
private:
Atomic<std::shared_ptr<const EmoteMap>> global_;
};
} // namespace chatterino

View file

@ -131,7 +131,7 @@ std::shared_ptr<Channel> AbstractIrcServer::getOrAddChannel(
chan->destroyed.connect([this, clojuresInCppAreShit] {
// fourtf: issues when the server itself is destroyed
Log("[AbstractIrcServer::addChannel] {} was destroyed",
log("[AbstractIrcServer::addChannel] {} was destroyed",
clojuresInCppAreShit);
this->channels.remove(clojuresInCppAreShit);

View file

@ -145,7 +145,7 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
auto chan = app->twitch.server->getChannelOrEmpty(chanName);
if (chan->isEmpty()) {
Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not "
log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not "
"found",
chanName);
return;
@ -209,7 +209,7 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
{
auto app = getApp();
Log("Received whisper!");
log("Received whisper!");
MessageParseArgs args;
args.isReceivedWhisper = true;
@ -326,7 +326,7 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
auto channel = app->twitch.server->getChannelOrEmpty(channelName);
if (channel->isEmpty()) {
Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel "
log("[IrcManager:handleNoticeMessage] Channel {} not found in channel "
"manager ",
channelName);
return;
@ -366,7 +366,7 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(
return;
}
Log("Showing notice message from write connection with message id '{}'",
log("Showing notice message from write connection with message id '{}'",
msgID);
}

View file

@ -42,23 +42,23 @@ void PartialTwitchUser::getId(std::function<void(QString)> successCallback,
request.onSuccess([successCallback](auto result) -> Outcome {
auto root = result.parseJson();
if (!root.value("users").isArray()) {
Log("API Error while getting user id, users is not an array");
log("API Error while getting user id, users is not an array");
return Failure;
}
auto users = root.value("users").toArray();
if (users.size() != 1) {
Log("API Error while getting user id, users array size is not 1");
log("API Error while getting user id, users array size is not 1");
return Failure;
}
if (!users[0].isObject()) {
Log("API Error while getting user id, first user is not an object");
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()) {
Log("API Error: while getting user id, first user object `_id` key "
log("API Error: while getting user id, first user object `_id` key "
"is not a "
"string");
return Failure;

View file

@ -109,7 +109,7 @@ void PubSubClient::handlePong()
{
assert(this->awaitingPong_);
Log("Got pong!");
log("Got pong!");
this->awaitingPong_ = false;
}
@ -144,7 +144,7 @@ void PubSubClient::ping()
}
if (self->awaitingPong_) {
Log("No pong respnose, disconnect!");
log("No pong respnose, disconnect!");
// TODO(pajlada): Label this connection as "disconnect me"
}
});
@ -166,7 +166,7 @@ bool PubSubClient::send(const char *payload)
websocketpp::frame::opcode::text, ec);
if (ec) {
Log("Error sending message {}: {}", payload, ec.message());
log("Error sending message {}: {}", payload, ec.message());
// TODO(pajlada): Check which error code happened and maybe gracefully
// handle it
@ -207,26 +207,26 @@ PubSub::PubSub()
action.state = ModeChangedAction::State::On;
if (!data.HasMember("args")) {
Log("Missing required args member");
log("Missing required args member");
return;
}
const auto &args = data["args"];
if (!args.IsArray()) {
Log("args member must be an array");
log("args member must be an array");
return;
}
if (args.Size() == 0) {
Log("Missing duration argument in slowmode on");
log("Missing duration argument in slowmode on");
return;
}
const auto &durationArg = args[0];
if (!durationArg.IsString()) {
Log("Duration arg must be a string");
log("Duration arg must be a string");
return;
}
@ -314,7 +314,7 @@ PubSub::PubSub()
return;
}
} catch (const std::runtime_error &ex) {
Log("Error parsing moderation action: {}", ex.what());
log("Error parsing moderation action: {}", ex.what());
}
action.modded = false;
@ -339,7 +339,7 @@ PubSub::PubSub()
return;
}
} catch (const std::runtime_error &ex) {
Log("Error parsing moderation action: {}", ex.what());
log("Error parsing moderation action: {}", ex.what());
}
action.modded = true;
@ -380,7 +380,7 @@ PubSub::PubSub()
this->signals_.moderation.userBanned.invoke(action);
} catch (const std::runtime_error &ex) {
Log("Error parsing moderation action: {}", ex.what());
log("Error parsing moderation action: {}", ex.what());
}
};
@ -410,7 +410,7 @@ PubSub::PubSub()
this->signals_.moderation.userBanned.invoke(action);
} catch (const std::runtime_error &ex) {
Log("Error parsing moderation action: {}", ex.what());
log("Error parsing moderation action: {}", ex.what());
}
};
@ -436,7 +436,7 @@ PubSub::PubSub()
this->signals_.moderation.userUnbanned.invoke(action);
} catch (const std::runtime_error &ex) {
Log("Error parsing moderation action: {}", ex.what());
log("Error parsing moderation action: {}", ex.what());
}
};
@ -462,7 +462,7 @@ PubSub::PubSub()
this->signals_.moderation.userUnbanned.invoke(action);
} catch (const std::runtime_error &ex) {
Log("Error parsing moderation action: {}", ex.what());
log("Error parsing moderation action: {}", ex.what());
}
};
@ -493,7 +493,7 @@ void PubSub::addClient()
auto con = this->websocketClient.get_connection(TWITCH_PUBSUB_URL, ec);
if (ec) {
Log("Unable to establish connection: {}", ec.message());
log("Unable to establish connection: {}", ec.message());
return;
}
@ -512,7 +512,7 @@ void PubSub::listenToWhispers(std::shared_ptr<TwitchAccount> account)
std::string userID = account->getUserId().toStdString();
Log("Connection open!");
log("Connection open!");
websocketpp::lib::error_code ec;
std::vector<std::string> topics({"whispers." + userID});
@ -520,7 +520,7 @@ void PubSub::listenToWhispers(std::shared_ptr<TwitchAccount> account)
this->listen(createListenMessage(topics, account));
if (ec) {
Log("Unable to send message to websocket server: {}", ec.message());
log("Unable to send message to websocket server: {}", ec.message());
return;
}
}
@ -544,11 +544,11 @@ void PubSub::listenToChannelModerationActions(
std::string topic(fS("chat_moderator_actions.{}.{}", userID, channelID));
if (this->isListeningToTopic(topic)) {
Log("We are already listening to topic {}", topic);
log("We are already listening to topic {}", topic);
return;
}
Log("Listen to topic {}", topic);
log("Listen to topic {}", topic);
this->listenToTopic(topic, account);
}
@ -564,18 +564,18 @@ void PubSub::listenToTopic(const std::string &topic,
void PubSub::listen(rapidjson::Document &&msg)
{
if (this->tryListen(msg)) {
Log("Successfully listened!");
log("Successfully listened!");
return;
}
Log("Added to the back of the queue");
log("Added to the back of the queue");
this->requests.emplace_back(
std::make_unique<rapidjson::Document>(std::move(msg)));
}
bool PubSub::tryListen(rapidjson::Document &msg)
{
Log("tryListen with {} clients", this->clients.size());
log("tryListen with {} clients", this->clients.size());
for (const auto &p : this->clients) {
const auto &client = p.second;
if (client->listen(msg)) {
@ -608,13 +608,13 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
rapidjson::ParseResult res = msg.Parse(payload.c_str());
if (!res) {
Log("Error parsing message '{}' from PubSub: {}", payload,
log("Error parsing message '{}' from PubSub: {}", payload,
rapidjson::GetParseError_En(res.Code()));
return;
}
if (!msg.IsObject()) {
Log("Error parsing message '{}' from PubSub. Root object is not an "
log("Error parsing message '{}' from PubSub. Root object is not an "
"object",
payload);
return;
@ -623,7 +623,7 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
std::string type;
if (!rj::getSafe(msg, "type", type)) {
Log("Missing required string member `type` in message root");
log("Missing required string member `type` in message root");
return;
}
@ -631,14 +631,14 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
this->handleListenResponse(msg);
} else if (type == "MESSAGE") {
if (!msg.HasMember("data")) {
Log("Missing required object member `data` in message root");
log("Missing required object member `data` in message root");
return;
}
const auto &data = msg["data"];
if (!data.IsObject()) {
Log("Member `data` must be an object");
log("Member `data` must be an object");
return;
}
@ -654,7 +654,7 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
client.second->handlePong();
} else {
Log("Unknown message type: {}", type);
log("Unknown message type: {}", type);
}
}
@ -699,7 +699,7 @@ PubSub::WebsocketContextPtr PubSub::onTLSInit(websocketpp::connection_hdl hdl)
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::single_dh_use);
} catch (const std::exception &e) {
Log("Exception caught in OnTLSInit: {}", e.what());
log("Exception caught in OnTLSInit: {}", e.what());
}
return ctx;
@ -714,12 +714,12 @@ void PubSub::handleListenResponse(const rapidjson::Document &msg)
rj::getSafe(msg, "nonce", nonce);
if (error.empty()) {
Log("Successfully listened to nonce {}", nonce);
log("Successfully listened to nonce {}", nonce);
// Nothing went wrong
return;
}
Log("PubSub error: {} on nonce {}", error, nonce);
log("PubSub error: {} on nonce {}", error, nonce);
return;
}
}
@ -729,14 +729,14 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
QString topic;
if (!rj::getSafe(outerData, "topic", topic)) {
Log("Missing required string member `topic` in outerData");
log("Missing required string member `topic` in outerData");
return;
}
std::string payload;
if (!rj::getSafe(outerData, "message", payload)) {
Log("Expected string message in outerData");
log("Expected string message in outerData");
return;
}
@ -745,7 +745,7 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
rapidjson::ParseResult res = msg.Parse(payload.c_str());
if (!res) {
Log("Error parsing message '{}' from PubSub: {}", payload,
log("Error parsing message '{}' from PubSub: {}", payload,
rapidjson::GetParseError_En(res.Code()));
return;
}
@ -754,7 +754,7 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
std::string whisperType;
if (!rj::getSafe(msg, "type", whisperType)) {
Log("Bad whisper data");
log("Bad whisper data");
return;
}
@ -765,7 +765,7 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
} else if (whisperType == "thread") {
// Handle thread?
} else {
Log("Invalid whisper type: {}", whisperType);
log("Invalid whisper type: {}", whisperType);
assert(false);
return;
}
@ -777,30 +777,30 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
std::string moderationAction;
if (!rj::getSafe(data, "moderation_action", moderationAction)) {
Log("Missing moderation action in data: {}", rj::stringify(data));
log("Missing moderation action in data: {}", rj::stringify(data));
return;
}
auto handlerIt = this->moderationActionHandlers.find(moderationAction);
if (handlerIt == this->moderationActionHandlers.end()) {
Log("No handler found for moderation action {}", moderationAction);
log("No handler found for moderation action {}", moderationAction);
return;
}
// Invoke handler function
handlerIt->second(data, topicParts[2]);
} else {
Log("Unknown topic: {}", topic);
log("Unknown topic: {}", topic);
return;
}
}
void PubSub::runThread()
{
Log("Start pubsub manager thread");
log("Start pubsub manager thread");
this->websocketClient.run();
Log("Done with pubsub manager thread");
log("Done with pubsub manager thread");
}
} // namespace chatterino

View file

@ -35,7 +35,7 @@ void runAfter(boost::asio::io_service &ioService, Duration duration,
timer->async_wait([timer, cb](const boost::system::error_code &ec) {
if (ec) {
Log("Error in runAfter: {}", ec.message());
log("Error in runAfter: {}", ec.message());
return;
}
@ -52,7 +52,7 @@ void runAfter(std::shared_ptr<boost::asio::steady_timer> timer,
timer->async_wait([timer, cb](const boost::system::error_code &ec) {
if (ec) {
Log("Error in runAfter: {}", ec.message());
log("Error in runAfter: {}", ec.message());
return;
}

View file

@ -143,7 +143,7 @@ void TwitchAccount::loadIgnores()
}
TwitchUser ignoredUser;
if (!rj::getSafe(userIt->value, ignoredUser)) {
Log("Error parsing twitch user JSON {}",
log("Error parsing twitch user JSON {}",
rj::stringify(userIt->value));
continue;
}
@ -368,13 +368,13 @@ std::set<TwitchUser> TwitchAccount::getIgnores() const
void TwitchAccount::loadEmotes()
{
Log("Loading Twitch emotes for user {}", this->getUserName());
log("Loading Twitch emotes for user {}", this->getUserName());
const auto &clientID = this->getOAuthClient();
const auto &oauthToken = this->getOAuthToken();
if (clientID.isEmpty() || oauthToken.isEmpty()) {
Log("Missing Client ID or OAuth token");
log("Missing Client ID or OAuth token");
return;
}
@ -386,7 +386,7 @@ void TwitchAccount::loadEmotes()
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
req.onError([=](int errorCode) {
Log("[TwitchAccount::loadEmotes] Error {}", errorCode);
log("[TwitchAccount::loadEmotes] Error {}", errorCode);
if (errorCode == 203) {
// onFinished(FollowResult_NotFollowing);
} else {
@ -420,7 +420,7 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
auto emoticonSets = root.FindMember("emoticon_sets");
if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject()) {
Log("No emoticon_sets in load emotes response");
log("No emoticon_sets in load emotes response");
return;
}
@ -434,19 +434,19 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
for (const rapidjson::Value &emoteJSON :
emoteSetJSON.value.GetArray()) {
if (!emoteJSON.IsObject()) {
Log("Emote value was invalid");
log("Emote value was invalid");
return;
}
uint64_t idNumber;
if (!rj::getSafe(emoteJSON, "id", idNumber)) {
Log("No ID key found in Emote value");
log("No ID key found in Emote value");
return;
}
QString _code;
if (!rj::getSafe(emoteJSON, "code", _code)) {
Log("No code key found in Emote value");
log("No code key found in Emote value");
return;
}
@ -468,7 +468,7 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
{
if (!emoteSet) {
Log("null emote set sent");
log("null emote set sent");
return;
}
@ -486,7 +486,7 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
req.setUseQuickLoadCache(true);
req.onError([](int errorCode) -> bool {
Log("Error code {} while loading emote set data", errorCode);
log("Error code {} while loading emote set data", errorCode);
return true;
});
@ -507,16 +507,15 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
return Failure;
}
Log("Loaded twitch emote set data for {}!", emoteSet->key);
log("Loaded twitch emote set data for {}!", emoteSet->key);
if (type == "sub") {
emoteSet->text =
QString("Twitch Subscriber Emote (%1)").arg(channelName);
} else {
emoteSet->text =
QString("Twitch Account Emote (%1)").arg(channelName);
}
auto name = channelName;
name.detach();
name[0] = name[0].toUpper();
emoteSet->text = name;
emoteSet->type = type;
emoteSet->channelName = channelName;
return Success;

View file

@ -44,6 +44,7 @@ public:
QString key;
QString channelName;
QString text;
QString type;
std::vector<TwitchEmote> emotes;
};

View file

@ -93,20 +93,20 @@ void TwitchAccountManager::reloadUsers()
switch (this->addUser(userData)) {
case AddUserResponse::UserAlreadyExists: {
Log("User {} already exists", userData.username);
log("User {} already exists", userData.username);
// Do nothing
} break;
case AddUserResponse::UserValuesUpdated: {
Log("User {} already exists, and values updated!",
log("User {} already exists, and values updated!",
userData.username);
if (userData.username == this->getCurrent()->getUserName()) {
Log("It was the current user, so we need to reconnect "
log("It was the current user, so we need to reconnect "
"stuff!");
this->currentUserChanged.invoke();
}
} break;
case AddUserResponse::UserAdded: {
Log("Added user {}", userData.username);
log("Added user {}", userData.username);
listUpdated = true;
} break;
}
@ -125,12 +125,12 @@ void TwitchAccountManager::load()
QString newUsername(QString::fromStdString(newValue));
auto user = this->findUserByUsername(newUsername);
if (user) {
Log("[AccountManager:currentUsernameChanged] User successfully "
log("[AccountManager:currentUsernameChanged] User successfully "
"updated to {}",
newUsername);
this->currentUser_ = user;
} else {
Log("[AccountManager:currentUsernameChanged] User successfully "
log("[AccountManager:currentUsernameChanged] User successfully "
"updated to anonymous");
this->currentUser_ = this->anonymousUser_;
}

View file

@ -20,25 +20,25 @@ void TwitchApi::findUserId(const QString user,
request.onSuccess([successCallback](auto result) mutable -> Outcome {
auto root = result.parseJson();
if (!root.value("users").isArray()) {
Log("API Error while getting user id, users is not an array");
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) {
Log("API Error while getting user id, users array size is not 1");
log("API Error while getting user id, users array size is not 1");
successCallback("");
return Failure;
}
if (!users[0].isObject()) {
Log("API Error while getting user id, first user is not an object");
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()) {
Log("API Error: while getting user id, first user object `_id` key "
log("API Error: while getting user id, first user object `_id` key "
"is not a "
"string");
successCallback("");

View file

@ -56,7 +56,7 @@ TwitchChannel::TwitchChannel(const QString &name)
, popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name)
, mod_(false)
{
Log("[TwitchChannel:{}] Opened", name);
log("[TwitchChannel:{}] Opened", name);
// this->refreshChannelEmotes();
// this->refreshViewerList();
@ -116,7 +116,7 @@ void TwitchChannel::refreshChannelEmotes()
if (auto shared = weak.lock()) //
*this->bttvEmotes_.access() = emoteMap;
});
getApp()->emotes->ffz.loadChannelEmotes(
getApp()->emotes->ffz.loadChannel(
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
if (auto shared = weak.lock())
*this->ffzEmotes_.access() = emoteMap;
@ -136,7 +136,7 @@ void TwitchChannel::sendMessage(const QString &message)
return;
}
Log("[TwitchChannel:{}] Send message: {}", this->getName(), message);
log("[TwitchChannel:{}] Send message: {}", this->getName(), message);
// Do last message processing
QString parsedMessage = app->emotes->emojis.replaceShortCodes(message);
@ -350,13 +350,13 @@ void TwitchChannel::refreshLiveStatus()
auto roomID = this->getRoomId();
if (roomID.isEmpty()) {
Log("[TwitchChannel:{}] Refreshing live status (Missing ID)",
log("[TwitchChannel:{}] Refreshing live status (Missing ID)",
this->getName());
this->setLive(false);
return;
}
Log("[TwitchChannel:{}] Refreshing live status", this->getName());
log("[TwitchChannel:{}] Refreshing live status", this->getName());
QString url("https://api.twitch.tv/kraken/streams/" + roomID);
@ -381,12 +381,12 @@ void TwitchChannel::refreshLiveStatus()
Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
{
if (!document.IsObject()) {
Log("[TwitchChannel:refreshLiveStatus] root is not an object");
log("[TwitchChannel:refreshLiveStatus] root is not an object");
return Failure;
}
if (!document.HasMember("stream")) {
Log("[TwitchChannel:refreshLiveStatus] Missing stream in root");
log("[TwitchChannel:refreshLiveStatus] Missing stream in root");
return Failure;
}
@ -400,7 +400,7 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
if (!stream.HasMember("viewers") || !stream.HasMember("game") ||
!stream.HasMember("channel") || !stream.HasMember("created_at")) {
Log("[TwitchChannel:refreshLiveStatus] Missing members in stream");
log("[TwitchChannel:refreshLiveStatus] Missing members in stream");
this->setLive(false);
return Failure;
}
@ -408,7 +408,7 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
const rapidjson::Value &streamChannel = stream["channel"];
if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) {
Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in "
log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in "
"channel");
return Failure;
}

View file

@ -4,7 +4,7 @@
#include "common/Channel.hpp"
#include "common/Common.hpp"
#include "common/MutexValue.hpp"
#include "common/Atomic.hpp"
#include "common/UniqueAccess.hpp"
#include "messages/Emote.hpp"
#include "singletons/Emotes.hpp"

View file

@ -6,7 +6,7 @@ namespace chatterino {
bool trimChannelName(const QString &channelName, QString &outChannelName)
{
if (channelName.length() < 3) {
Log("channel name length below 3");
log("channel name length below 3");
return false;
}

View file

@ -55,7 +55,7 @@ bool TwitchMessageBuilder::isIgnored() const
// TODO(pajlada): Do we need to check if the phrase is valid first?
for (const auto &phrase : app->ignores->phrases.getVector()) {
if (phrase.isMatch(this->originalMessage_)) {
Log("Blocking message because it contains ignored phrase {}",
log("Blocking message because it contains ignored phrase {}",
phrase.getPattern());
return true;
}
@ -68,7 +68,7 @@ bool TwitchMessageBuilder::isIgnored() const
for (const auto &user :
app->accounts->twitch.getCurrent()->getIgnores()) {
if (sourceUserID == user.id) {
Log("Blocking message because it's from blocked user {}",
log("Blocking message because it's from blocked user {}",
user.name);
return true;
}
@ -533,7 +533,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
if (!app->highlights->blacklistContains(this->ircMessage->nick())) {
for (const HighlightPhrase &highlight : activeHighlights) {
if (highlight.isMatch(this->originalMessage_)) {
Log("Highlight because {} matches {}", this->originalMessage_,
log("Highlight because {} matches {}", this->originalMessage_,
highlight.getPattern());
doHighlight = true;
@ -555,7 +555,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
}
for (const HighlightPhrase &userHighlight : userHighlights) {
if (userHighlight.isMatch(this->ircMessage->nick())) {
Log("Highlight because user {} sent a message",
log("Highlight because user {} sent a message",
this->ircMessage->nick());
doHighlight = true;
@ -638,12 +638,12 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
auto flags = MessageElementFlags();
auto emote = boost::optional<EmotePtr>{};
if ((emote = getApp()->emotes->bttv.getGlobalEmote(name))) {
if ((emote = getApp()->emotes->bttv.global(name))) {
flags = MessageElementFlag::BttvEmote;
} else if (twitchChannel &&
(emote = this->twitchChannel->getBttvEmote(name))) {
flags = MessageElementFlag::BttvEmote;
} else if ((emote = getApp()->emotes->ffz.getGlobalEmote(name))) {
} else if ((emote = getApp()->emotes->ffz.global(name))) {
flags = MessageElementFlag::FfzEmote;
} else if (twitchChannel &&
(emote = this->twitchChannel->getFfzEmote(name))) {

View file

@ -1,7 +1,6 @@
#pragma once
#include "messages/MessageBuilder.hpp"
#include "messages/MessageParseArgs.hpp"
#include "singletons/Emotes.hpp"
#include <IrcMessage>

View file

@ -1,6 +1,6 @@
#pragma once
#include "common/MutexValue.hpp"
#include "common/Atomic.hpp"
#include "common/Singleton.hpp"
#include "providers/irc/AbstractIrcServer.hpp"
#include "providers/twitch/TwitchAccount.hpp"
@ -29,7 +29,7 @@ public:
std::shared_ptr<Channel> getChannelOrEmptyByID(const QString &channelID);
MutexValue<QString> lastUserThatWhisperedMe;
Atomic<QString> lastUserThatWhisperedMe;
const ChannelPtr whispersChannel;
const ChannelPtr mentionsChannel;

View file

@ -15,8 +15,8 @@ void Emotes::initialize(Settings &settings, Paths &paths)
[] { getApp()->accounts->twitch.getCurrent()->loadEmotes(); });
this->emojis.load();
this->bttv.loadGlobalEmotes();
this->ffz.loadGlobalEmotes();
this->bttv.loadGlobal();
this->ffz.loadGlobal();
this->gifTimer.initialize();
}

View file

@ -50,7 +50,7 @@ void Settings::saveSnapshot()
this->snapshot_.reset(d);
Log("hehe: {}", pajlada::Settings::SettingManager::stringify(*d));
log("hehe: {}", pajlada::Settings::SettingManager::stringify(*d));
}
void Settings::restoreSnapshot()
@ -64,14 +64,14 @@ void Settings::restoreSnapshot()
for (const auto &weakSetting : _settings) {
auto setting = weakSetting.lock();
if (!setting) {
Log("Error stage 1 of loading");
log("Error stage 1 of loading");
continue;
}
const char *path = setting->getPath().c_str();
if (!snapshotObject.HasMember(path)) {
Log("Error stage 2 of loading");
log("Error stage 2 of loading");
continue;
}

View file

@ -211,7 +211,7 @@ Window *WindowManager::windowAt(int index)
if (index < 0 || (size_t)index >= this->windows_.size()) {
return nullptr;
}
Log("getting window at bad index {}", index);
log("getting window at bad index {}", index);
return this->windows_.at(index);
}

View file

@ -65,13 +65,13 @@ void LoggingChannel::openLogFile()
this->baseDirectory + QDir::separator() + this->subDirectory;
if (!QDir().mkpath(directory)) {
Log("Unable to create logging path");
log("Unable to create logging path");
return;
}
// Open file handle to log file of current date
QString fileName = directory + QDir::separator() + baseFileName;
Log("Logging to {}", fileName);
log("Logging to {}", fileName);
this->fileHandle.setFileName(fileName);
this->fileHandle.open(QIODevice::Append);

View file

@ -6,7 +6,7 @@
namespace chatterino {
template <typename... Args>
auto fS(Args &&... args) -> decltype(fmt::format(std::forward<Args>(args)...))
auto fS(Args &&... args)
{
return fmt::format(std::forward<Args>(args)...);
}

View file

@ -2,6 +2,7 @@
#include "Application.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "debug/Benchmark.hpp"
#include "messages/MessageBuilder.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "widgets/Notebook.hpp"
@ -11,36 +12,88 @@
#include <QTabWidget>
namespace chatterino {
namespace {
auto makeTitleMessage(const QString &title)
{
MessageBuilder builder;
builder.emplace<TextElement>(title, MessageElementFlag::Text);
builder->flags.set(MessageFlag::Centered);
return builder.release();
}
auto makeEmoteMessage(const EmoteMap &map)
{
MessageBuilder builder;
builder->flags.set(MessageFlag::Centered);
builder->flags.set(MessageFlag::DisableCompactEmotes);
for (const auto &emote : map) {
builder
.emplace<EmoteElement>(emote.second, MessageElementFlag::AlwaysShow)
->setLink(Link(Link::InsertText, emote.first.string));
}
return builder.release();
}
void addEmoteSets(std::vector<std::shared_ptr<TwitchAccount::EmoteSet>> sets,
Channel &globalChannel, Channel &subChannel)
{
for (const auto &set : sets) {
auto &channel = set->key == "0" ? globalChannel : subChannel;
// TITLE
auto text =
set->key == "0" || set->text.isEmpty() ? "Twitch" : set->text;
channel.addMessage(makeTitleMessage(text));
// EMOTES
MessageBuilder builder;
builder->flags.set(MessageFlag::Centered);
builder->flags.set(MessageFlag::DisableCompactEmotes);
for (const auto &emote : set->emotes) {
builder
.emplace<EmoteElement>(
getApp()->emotes->twitch.getOrCreateEmote(emote.id,
emote.name),
MessageElementFlag::AlwaysShow)
->setLink(Link(Link::InsertText, emote.name.string));
}
channel.addMessage(builder.release());
}
}
} // namespace
EmotePopup::EmotePopup()
: BaseWindow(nullptr, BaseWindow::EnableCustomFrame)
{
this->viewEmotes_ = new ChannelView();
this->viewEmojis_ = new ChannelView();
this->viewEmotes_->setOverrideFlags(MessageElementFlags{
MessageElementFlag::Default, MessageElementFlag::AlwaysShow,
MessageElementFlag::EmoteImages});
this->viewEmojis_->setOverrideFlags(MessageElementFlags{
MessageElementFlag::Default, MessageElementFlag::AlwaysShow,
MessageElementFlag::EmoteImages});
this->viewEmotes_->setEnableScrollingToBottom(false);
this->viewEmojis_->setEnableScrollingToBottom(false);
auto *layout = new QVBoxLayout(this);
auto layout = new QVBoxLayout(this);
this->getLayoutContainer()->setLayout(layout);
Notebook *notebook = new Notebook(this);
auto notebook = new Notebook(this);
layout->addWidget(notebook);
layout->setMargin(0);
notebook->addPage(this->viewEmotes_, "Emotes");
notebook->addPage(this->viewEmojis_, "Emojis");
auto makeView = [&](QString tabTitle) {
auto view = new ChannelView();
view->setOverrideFlags(MessageElementFlags{
MessageElementFlag::Default, MessageElementFlag::AlwaysShow,
MessageElementFlag::EmoteImages});
view->setEnableScrollingToBottom(false);
notebook->addPage(view, tabTitle);
return view;
};
this->subEmotesView_ = makeView("Subs");
this->channelEmotesView_ = makeView("Channel");
this->globalEmotesView_ = makeView("Global");
this->viewEmojis_ = makeView("Emojis");
this->loadEmojis();
this->viewEmotes_->linkClicked.connect(
this->globalEmotesView_->linkClicked.connect(
[this](const Link &link) { this->linkClicked.invoke(link); });
this->viewEmojis_->linkClicked.connect(
[this](const Link &link) { this->linkClicked.invoke(link); });
@ -48,93 +101,41 @@ EmotePopup::EmotePopup()
void EmotePopup::loadChannel(ChannelPtr _channel)
{
BenchmarkGuard guard("loadChannel");
this->setWindowTitle("Emotes from " + _channel->getName());
TwitchChannel *channel = dynamic_cast<TwitchChannel *>(_channel.get());
auto twitchChannel = dynamic_cast<TwitchChannel *>(_channel.get());
if (twitchChannel == nullptr) return;
if (channel == nullptr) {
return;
}
ChannelPtr emoteChannel(new Channel("", Channel::Type::None));
auto addEmotes = [&](const EmoteMap &map, const QString &title,
const QString &emoteDesc) {
// TITLE
MessageBuilder builder1;
builder1.emplace<TextElement>(title, MessageElementFlag::Text);
builder1->flags.set(MessageFlag::Centered);
emoteChannel->addMessage(builder1.release());
// EMOTES
MessageBuilder builder2;
builder2->flags.set(MessageFlag::Centered);
builder2->flags.set(MessageFlag::DisableCompactEmotes);
for (auto emote : map) {
builder2
.emplace<EmoteElement>(emote.second,
MessageElementFlag::AlwaysShow)
->setLink(Link(Link::InsertText, emote.first.string));
}
emoteChannel->addMessage(builder2.release());
auto addEmotes = [&](Channel &channel, const EmoteMap &map,
const QString &title) {
channel.addMessage(makeTitleMessage(title));
channel.addMessage(makeEmoteMessage(map));
};
auto app = getApp();
auto subChannel = std::make_shared<Channel>("", Channel::Type::None);
auto globalChannel = std::make_shared<Channel>("", Channel::Type::None);
auto channelChannel = std::make_shared<Channel>("", Channel::Type::None);
// fourtf: the entire emote manager needs to be refactored so there's no
// point in trying to fix this pile of garbage
for (const auto &set :
app->accounts->twitch.getCurrent()->accessEmotes()->emoteSets) {
// TITLE
MessageBuilder builder1;
// twitch
addEmoteSets(
getApp()->accounts->twitch.getCurrent()->accessEmotes()->emoteSets,
*globalChannel, *subChannel);
QString setText;
if (set->text.isEmpty()) {
if (set->channelName.isEmpty()) {
setText = "Twitch Account Emotes";
} else {
setText = "Twitch Account Emotes (" + set->channelName + ")";
}
} else {
setText = set->text;
}
// global
addEmotes(*globalChannel, *getApp()->emotes->bttv.global(), "BetterTTV");
addEmotes(*globalChannel, *getApp()->emotes->ffz.global(), "FrankerFaceZ");
builder1.emplace<TextElement>(setText, MessageElementFlag::Text);
// channel
// addEmotes(*channel->accessBttvEmotes(), "BetterTTV Channel Emotes",
// "BetterTTV Channel Emote");
// addEmotes(*channel->accessFfzEmotes(), "FrankerFaceZ Channel Emotes",
// "FrankerFaceZ Channel Emote");
builder1->flags.set(MessageFlag::Centered);
emoteChannel->addMessage(builder1.release());
// EMOTES
MessageBuilder builder2;
builder2->flags.set(MessageFlag::Centered);
builder2->flags.set(MessageFlag::DisableCompactEmotes);
for (const auto &emote : set->emotes) {
builder2
.emplace<EmoteElement>(
app->emotes->twitch.getOrCreateEmote(emote.id, emote.name),
MessageElementFlag::AlwaysShow)
->setLink(Link(Link::InsertText, emote.name.string));
}
emoteChannel->addMessage(builder2.release());
}
addEmotes(*app->emotes->bttv.accessGlobalEmotes(),
"BetterTTV Global Emotes", "BetterTTV Global Emote");
addEmotes(*channel->accessBttvEmotes(), "BetterTTV Channel Emotes",
"BetterTTV Channel Emote");
// addEmotes(*app->emotes->ffz.accessGlobalEmotes(), "FrankerFaceZ Global
// Emotes",
// "FrankerFaceZ Global Emote");
addEmotes(*channel->accessFfzEmotes(), "FrankerFaceZ Channel Emotes",
"FrankerFaceZ Channel Emote");
this->viewEmotes_->setChannel(emoteChannel);
this->globalEmotesView_->setChannel(globalChannel);
this->subEmotesView_->setChannel(subChannel);
this->channelEmotesView_->setChannel(channelChannel);
}
void EmotePopup::loadEmojis()
@ -143,13 +144,6 @@ void EmotePopup::loadEmojis()
ChannelPtr emojiChannel(new Channel("", Channel::Type::None));
// title
MessageBuilder builder1;
builder1.emplace<TextElement>("emojis", MessageElementFlag::Text);
builder1->flags.set(MessageFlag::Centered);
emojiChannel->addMessage(builder1.release());
// emojis
MessageBuilder builder;
builder->flags.set(MessageFlag::Centered);

View file

@ -19,8 +19,10 @@ public:
pajlada::Signals::Signal<Link> linkClicked;
private:
ChannelView *viewEmotes_;
ChannelView *viewEmojis_;
ChannelView *globalEmotesView_{};
ChannelView *channelEmotesView_{};
ChannelView *subEmotesView_{};
ChannelView *viewEmojis_{};
};
} // namespace chatterino

View file

@ -47,7 +47,7 @@ void QualityPopup::okButtonClicked()
try {
openStreamlink(channelURL, this->ui_.selector.currentText());
} catch (const Exception &ex) {
Log("Exception caught trying to open streamlink: {}", ex.what());
log("Exception caught trying to open streamlink: {}", ex.what());
}
this->close();

View file

@ -492,7 +492,7 @@ void ChannelView::setChannel(ChannelPtr newChannel)
MessageLayoutPtr newItem(new MessageLayout(replacement));
auto snapshot = this->messages.getSnapshot();
if (index >= snapshot.getLength()) {
Log("Tried to replace out of bounds message. Index: {}. "
log("Tried to replace out of bounds message. Index: {}. "
"Length: {}",
index, snapshot.getLength());
return;
@ -1169,19 +1169,18 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
userPopup->show();
qDebug() << "Clicked " << user << "s message";
break;
}
} break;
case Link::Url: {
QDesktopServices::openUrl(QUrl(link.value));
break;
}
} break;
case Link::UserAction: {
QString value = link.value;
value.replace("{user}", layout->getMessage()->loginName);
this->channel_->sendMessage(value);
}
} break;
default:;
}

View file

@ -137,7 +137,7 @@ AboutPage::AboutPage()
QStringList contributorParts = line.split("|");
if (contributorParts.size() != 4) {
Log("Missing parts in line '{}'", line);
log("Missing parts in line '{}'", line);
continue;
}

View file

@ -429,7 +429,7 @@ void Split::openInStreamlink()
try {
openStreamlinkForChannel(this->getChannel()->getName());
} catch (const Exception &ex) {
Log("Error in doOpenStreamlink: {}", ex.what());
log("Error in doOpenStreamlink: {}", ex.what());
}
}