mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
categorized emtotepopup
This commit is contained in:
parent
09b8a9d821
commit
c719bb6b74
55 changed files with 420 additions and 604 deletions
|
@ -233,7 +233,6 @@ SOURCES += \
|
||||||
src/providers/twitch/PubsubClient.cpp \
|
src/providers/twitch/PubsubClient.cpp \
|
||||||
src/providers/twitch/TwitchApi.cpp \
|
src/providers/twitch/TwitchApi.cpp \
|
||||||
src/messages/Emote.cpp \
|
src/messages/Emote.cpp \
|
||||||
src/messages/EmoteMap.cpp \
|
|
||||||
src/messages/ImageSet.cpp \
|
src/messages/ImageSet.cpp \
|
||||||
src/providers/bttv/BttvEmotes.cpp \
|
src/providers/bttv/BttvEmotes.cpp \
|
||||||
src/providers/ffz/FfzEmotes.cpp \
|
src/providers/ffz/FfzEmotes.cpp \
|
||||||
|
@ -250,7 +249,8 @@ SOURCES += \
|
||||||
src/util/FunctionEventFilter.cpp \
|
src/util/FunctionEventFilter.cpp \
|
||||||
src/widgets/helper/EffectLabel.cpp \
|
src/widgets/helper/EffectLabel.cpp \
|
||||||
src/widgets/helper/Button.cpp \
|
src/widgets/helper/Button.cpp \
|
||||||
src/messages/MessageContainer.cpp
|
src/messages/MessageContainer.cpp \
|
||||||
|
src/debug/Benchmark.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
src/Application.hpp \
|
src/Application.hpp \
|
||||||
|
@ -258,7 +258,7 @@ HEADERS += \
|
||||||
src/common/Common.hpp \
|
src/common/Common.hpp \
|
||||||
src/common/CompletionModel.hpp \
|
src/common/CompletionModel.hpp \
|
||||||
src/common/FlagsEnum.hpp \
|
src/common/FlagsEnum.hpp \
|
||||||
src/common/MutexValue.hpp \
|
src/common/Atomic.hpp \
|
||||||
src/common/NetworkCommon.hpp \
|
src/common/NetworkCommon.hpp \
|
||||||
src/common/NetworkData.hpp \
|
src/common/NetworkData.hpp \
|
||||||
src/common/NetworkManager.hpp \
|
src/common/NetworkManager.hpp \
|
||||||
|
@ -305,7 +305,6 @@ HEADERS += \
|
||||||
src/messages/MessageBuilder.hpp \
|
src/messages/MessageBuilder.hpp \
|
||||||
src/messages/MessageColor.hpp \
|
src/messages/MessageColor.hpp \
|
||||||
src/messages/MessageElement.hpp \
|
src/messages/MessageElement.hpp \
|
||||||
src/messages/MessageParseArgs.hpp \
|
|
||||||
src/messages/Selection.hpp \
|
src/messages/Selection.hpp \
|
||||||
src/PrecompiledHeader.hpp \
|
src/PrecompiledHeader.hpp \
|
||||||
src/providers/emoji/Emojis.hpp \
|
src/providers/emoji/Emojis.hpp \
|
||||||
|
@ -414,7 +413,6 @@ HEADERS += \
|
||||||
src/singletons/Updates.hpp \
|
src/singletons/Updates.hpp \
|
||||||
src/singletons/NativeMessaging.hpp \
|
src/singletons/NativeMessaging.hpp \
|
||||||
src/singletons/Theme.hpp \
|
src/singletons/Theme.hpp \
|
||||||
src/common/SimpleSignalVector.hpp \
|
|
||||||
src/common/SignalVector.hpp \
|
src/common/SignalVector.hpp \
|
||||||
src/widgets/dialogs/LogsPopup.hpp \
|
src/widgets/dialogs/LogsPopup.hpp \
|
||||||
src/common/Singleton.hpp \
|
src/common/Singleton.hpp \
|
||||||
|
@ -427,7 +425,6 @@ HEADERS += \
|
||||||
src/providers/twitch/PubsubClient.hpp \
|
src/providers/twitch/PubsubClient.hpp \
|
||||||
src/providers/twitch/TwitchApi.hpp \
|
src/providers/twitch/TwitchApi.hpp \
|
||||||
src/messages/Emote.hpp \
|
src/messages/Emote.hpp \
|
||||||
src/messages/EmoteMap.hpp \
|
|
||||||
src/messages/EmoteCache.hpp \
|
src/messages/EmoteCache.hpp \
|
||||||
src/messages/ImageSet.hpp \
|
src/messages/ImageSet.hpp \
|
||||||
src/common/Outcome.hpp \
|
src/common/Outcome.hpp \
|
||||||
|
|
|
@ -113,11 +113,11 @@ void Application::initNm()
|
||||||
void Application::initPubsub()
|
void Application::initPubsub()
|
||||||
{
|
{
|
||||||
this->twitch.pubsub->signals_.whisper.sent.connect([](const auto &msg) {
|
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) {
|
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(
|
this->twitch.pubsub->signals_.moderation.chatCleared.connect(
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class MutexValue : boost::noncopyable
|
class Atomic : boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MutexValue()
|
Atomic()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MutexValue(T &&val)
|
Atomic(T &&val)
|
||||||
: value_(val)
|
: value_(val)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,13 @@ public:
|
||||||
this->value_ = val;
|
this->value_ = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set(T &&val)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(this->mutex_);
|
||||||
|
|
||||||
|
this->value_ = std::move(val);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable std::mutex mutex_;
|
mutable std::mutex mutex_;
|
||||||
T value_;
|
T value_;
|
|
@ -104,7 +104,7 @@ int CompletionModel::rowCount(const QModelIndex &) const
|
||||||
|
|
||||||
void CompletionModel::refresh()
|
void CompletionModel::refresh()
|
||||||
{
|
{
|
||||||
Log("[CompletionModel:{}] Refreshing...]", this->channelName_);
|
log("[CompletionModel:{}] Refreshing...]", this->channelName_);
|
||||||
|
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ void NetworkRequest::execute()
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
Log("[Execute] Unhandled request type");
|
log("[Execute] Unhandled request type");
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,10 +190,11 @@ void NetworkRequest::doRequest()
|
||||||
|
|
||||||
case NetworkRequestType::Put:
|
case NetworkRequestType::Put:
|
||||||
return NetworkManager::accessManager.put(data->request_,
|
return NetworkManager::accessManager.put(data->request_,
|
||||||
data->payload_);
|
data->payload_);
|
||||||
|
|
||||||
case NetworkRequestType::Delete:
|
case NetworkRequestType::Delete:
|
||||||
return NetworkManager::accessManager.deleteResource(data->request_);
|
return NetworkManager::accessManager.deleteResource(
|
||||||
|
data->request_);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -201,13 +202,13 @@ void NetworkRequest::doRequest()
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (reply == nullptr) {
|
if (reply == nullptr) {
|
||||||
Log("Unhandled request type");
|
log("Unhandled request type");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timer->isStarted()) {
|
if (timer->isStarted()) {
|
||||||
timer->onTimeout(worker, [reply, data]() {
|
timer->onTimeout(worker, [reply, data]() {
|
||||||
Log("Aborted!");
|
log("Aborted!");
|
||||||
reply->abort();
|
reply->abort();
|
||||||
if (data->onError_) {
|
if (data->onError_) {
|
||||||
data->onError_(-2);
|
data->onError_(-2);
|
||||||
|
@ -234,7 +235,7 @@ void NetworkRequest::doRequest()
|
||||||
NetworkResult result(bytes);
|
NetworkResult result(bytes);
|
||||||
|
|
||||||
DebugCount::increase("http request success");
|
DebugCount::increase("http request success");
|
||||||
Log("starting {}", data->request_.url().toString());
|
// log("starting {}", data->request_.url().toString());
|
||||||
if (data->onSuccess_) {
|
if (data->onSuccess_) {
|
||||||
if (data->executeConcurrently)
|
if (data->executeConcurrently)
|
||||||
QtConcurrent::run(
|
QtConcurrent::run(
|
||||||
|
@ -243,7 +244,7 @@ void NetworkRequest::doRequest()
|
||||||
else
|
else
|
||||||
data->onSuccess_(result);
|
data->onSuccess_(result);
|
||||||
}
|
}
|
||||||
Log("finished {}", data->request_.url().toString());
|
// log("finished {}", data->request_.url().toString());
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ rapidjson::Document NetworkResult::parseRapidJson() const
|
||||||
ret.Parse(this->data_.data(), this->data_.length());
|
ret.Parse(this->data_.data(), this->data_.length());
|
||||||
|
|
||||||
if (result.Code() != rapidjson::kParseErrorNone) {
|
if (result.Code() != rapidjson::kParseErrorNone) {
|
||||||
Log("JSON parse error: {} ({})",
|
log("JSON parse error: {} ({})",
|
||||||
rapidjson::GetParseError_En(result.Code()), result.Offset());
|
rapidjson::GetParseError_En(result.Code()), result.Offset());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -46,16 +46,15 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T *element_;
|
T *element_{};
|
||||||
std::mutex *mutex_;
|
std::mutex *mutex_{};
|
||||||
bool isValid_ = true;
|
bool isValid_{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class UniqueAccess
|
class UniqueAccess
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// template <typename X = decltype(T())>
|
|
||||||
UniqueAccess()
|
UniqueAccess()
|
||||||
: element_(T())
|
: element_(T())
|
||||||
{
|
{
|
||||||
|
|
|
@ -78,7 +78,7 @@ void CommandController::save()
|
||||||
{
|
{
|
||||||
QFile textFile(this->filePath_);
|
QFile textFile(this->filePath_);
|
||||||
if (!textFile.open(QIODevice::WriteOnly)) {
|
if (!textFile.open(QIODevice::WriteOnly)) {
|
||||||
Log("[CommandController::saveCommands] Unable to open {} for writing",
|
log("[CommandController::saveCommands] Unable to open {} for writing",
|
||||||
this->filePath_);
|
this->filePath_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
21
src/debug/Benchmark.cpp
Normal file
21
src/debug/Benchmark.cpp
Normal 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
|
|
@ -3,40 +3,20 @@
|
||||||
#include "debug/Log.hpp"
|
#include "debug/Log.hpp"
|
||||||
|
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <boost/current_function.hpp>
|
|
||||||
#include <boost/noncopyable.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 {
|
namespace chatterino {
|
||||||
|
|
||||||
class BenchmarkGuard : boost::noncopyable
|
class BenchmarkGuard : boost::noncopyable
|
||||||
{
|
{
|
||||||
QElapsedTimer timer;
|
|
||||||
QString name;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BenchmarkGuard(const QString &_name)
|
BenchmarkGuard(const QString &_name);
|
||||||
: name(_name)
|
~BenchmarkGuard();
|
||||||
{
|
qreal getElapsedMs();
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
~BenchmarkGuard()
|
private:
|
||||||
{
|
QElapsedTimer timer_;
|
||||||
Log("{} {} ms", this->name, float(timer.nsecsElapsed()) / 1000000.0f);
|
QString name_;
|
||||||
}
|
|
||||||
|
|
||||||
qreal getElapsedMs()
|
|
||||||
{
|
|
||||||
return qreal(timer.nsecsElapsed()) / 1000000.0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -8,17 +8,22 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
template <typename... Args>
|
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")
|
qDebug().noquote() << QTime::currentTime().toString("hh:mm:ss.zzz")
|
||||||
<< fS(formatString, std::forward<Args>(args)...).c_str();
|
<< fS(formatString, std::forward<Args>(args)...).c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
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")
|
log(std::string(formatString), std::forward<Args>(args)...);
|
||||||
<< fS(formatString, std::forward<Args>(args)...).c_str();
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void log(const QString &formatString, Args &&... args)
|
||||||
|
{
|
||||||
|
log(formatString.toStdString(), std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -5,13 +5,15 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <memory>
|
||||||
#include <messages/Image.hpp>
|
|
||||||
|
|
||||||
using namespace chatterino;
|
using namespace chatterino;
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
auto shared = std::make_shared<QString>();
|
||||||
|
log(std::atomic_is_lock_free(&shared));
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
// convert char** to QStringList
|
// convert char** to QStringList
|
||||||
|
|
|
@ -15,61 +15,13 @@ bool operator!=(const Emote &a, const Emote &b)
|
||||||
return !(a == 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)
|
return std::make_shared<Emote>(std::move(emote));
|
||||||
//{
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
//}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -37,4 +37,6 @@ using EmoteIdMap = std::unordered_map<EmoteId, EmotePtr>;
|
||||||
using WeakEmoteMap = std::unordered_map<EmoteName, std::weak_ptr<const Emote>>;
|
using WeakEmoteMap = std::unordered_map<EmoteName, std::weak_ptr<const Emote>>;
|
||||||
using WeakEmoteIdMap = std::unordered_map<EmoteId, 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
|
} // namespace chatterino
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -78,7 +78,7 @@ QVector<Frame<QImage>> readFrames(QImageReader &reader, const Url &url)
|
||||||
QVector<Frame<QImage>> frames;
|
QVector<Frame<QImage>> frames;
|
||||||
|
|
||||||
if (reader.imageCount() == 0) {
|
if (reader.imageCount() == 0) {
|
||||||
Log("Error while reading image {}: '{}'", url.string,
|
log("Error while reading image {}: '{}'", url.string,
|
||||||
reader.errorString());
|
reader.errorString());
|
||||||
return frames;
|
return frames;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ QVector<Frame<QImage>> readFrames(QImageReader &reader, const Url &url)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frames.size() == 0) {
|
if (frames.size() == 0) {
|
||||||
Log("Error while reading image {}: '{}'", url.string,
|
log("Error while reading image {}: '{}'", url.string,
|
||||||
reader.errorString());
|
reader.errorString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ Image::Image(const Url &url, qreal scale)
|
||||||
|
|
||||||
Image::Image(const QPixmap &pixmap, qreal scale)
|
Image::Image(const QPixmap &pixmap, qreal scale)
|
||||||
: scale_(scale)
|
: scale_(scale)
|
||||||
, frames_(QVector<Frame<QPixmap>>{Frame<QPixmap>{pixmap}})
|
, frames_(QVector<Frame<QPixmap>>{Frame<QPixmap>{pixmap, 1}})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,14 @@ const TimeoutMessageTag timeoutMessage{};
|
||||||
|
|
||||||
MessagePtr makeSystemMessage(const QString &text);
|
MessagePtr makeSystemMessage(const QString &text);
|
||||||
|
|
||||||
|
struct MessageParseArgs {
|
||||||
|
bool disablePingSounds = false;
|
||||||
|
bool isReceivedWhisper = false;
|
||||||
|
bool isSentWhisper = false;
|
||||||
|
bool trimSubscriberUsername = false;
|
||||||
|
bool isStaffOrBroadcaster = false;
|
||||||
|
};
|
||||||
|
|
||||||
class MessageBuilder
|
class MessageBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -2,12 +2,5 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
struct MessageParseArgs {
|
|
||||||
bool disablePingSounds = false;
|
|
||||||
bool isReceivedWhisper = false;
|
|
||||||
bool isSentWhisper = false;
|
|
||||||
bool trimSubscriberUsername = false;
|
|
||||||
bool isStaffOrBroadcaster = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -35,7 +35,7 @@ struct SelectionItem {
|
||||||
|
|
||||||
bool operator>(const SelectionItem &b) const
|
bool operator>(const SelectionItem &b) const
|
||||||
{
|
{
|
||||||
return b.operator<(*this);
|
return this->operator!=(b) && b.operator<(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const SelectionItem &b) const
|
bool operator==(const SelectionItem &b) const
|
||||||
|
|
|
@ -31,13 +31,10 @@ public:
|
||||||
|
|
||||||
const Message *getMessage();
|
const Message *getMessage();
|
||||||
|
|
||||||
// Height
|
|
||||||
int getHeight() const;
|
int getHeight() const;
|
||||||
|
|
||||||
// Flags
|
|
||||||
MessageLayoutFlags flags;
|
MessageLayoutFlags flags;
|
||||||
|
|
||||||
// Layout
|
|
||||||
bool layout(int width, float scale_, MessageElementFlags flags);
|
bool layout(int width, float scale_, MessageElementFlags flags);
|
||||||
|
|
||||||
// Painting
|
// Painting
|
||||||
|
|
|
@ -126,9 +126,10 @@ void MessageLayoutContainer::breakLine()
|
||||||
int xOffset = 0;
|
int xOffset = 0;
|
||||||
|
|
||||||
if (this->flags_.has(MessageFlag::Centered) && this->elements_.size() > 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() -
|
||||||
->getRect()
|
this->elements_.at(this->elements_.size() - 1)
|
||||||
.right()) /
|
->getRect()
|
||||||
|
.right()) /
|
||||||
2;
|
2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,7 @@
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Url getEmoteLink(QString urlTemplate, const EmoteId &id,
|
Url getEmoteLink(QString urlTemplate, const EmoteId &id,
|
||||||
const QString &emoteScale)
|
const QString &emoteScale)
|
||||||
{
|
{
|
||||||
|
@ -21,72 +19,14 @@ Url getEmoteLink(QString urlTemplate, const EmoteId &id,
|
||||||
return {urlTemplate.replace("{{id}}", id.string)
|
return {urlTemplate.replace("{{id}}", id.string)
|
||||||
.replace("{{image}}", emoteScale)};
|
.replace("{{image}}", emoteScale)};
|
||||||
}
|
}
|
||||||
|
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
|
||||||
} // namespace
|
const EmoteMap ¤tEmotes)
|
||||||
|
|
||||||
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 ¤tEmotes)
|
|
||||||
{
|
{
|
||||||
auto emotes = EmoteMap();
|
auto emotes = EmoteMap();
|
||||||
auto jsonEmotes = jsonRoot.value("emotes").toArray();
|
auto jsonEmotes = jsonRoot.value("emotes").toArray();
|
||||||
auto urlTemplate =
|
auto urlTemplate = qS("https:") + jsonRoot.value("urlTemplate").toString();
|
||||||
QString("https:" + jsonRoot.value("urlTemplate").toString());
|
|
||||||
|
|
||||||
for (const QJsonValue &jsonEmote : jsonEmotes) {
|
for (auto jsonEmote : jsonEmotes) {
|
||||||
auto id = EmoteId{jsonEmote.toObject().value("id").toString()};
|
auto id = EmoteId{jsonEmote.toObject().value("id").toString()};
|
||||||
auto name = EmoteName{jsonEmote.toObject().value("code").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"},
|
Tooltip{name.string + "<br />Global Bttv Emote"},
|
||||||
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
Url{"https://manage.betterttv.net/emotes/" + id.string}});
|
||||||
|
|
||||||
auto it = currentEmotes.find(name);
|
emotes[name] = cachedOrMakeEmotePtr(std::move(emote), currentEmotes);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {Success, std::move(emotes)};
|
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
|
} // namespace chatterino
|
||||||
|
|
|
@ -1,33 +1,25 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "common/Atomic.hpp"
|
||||||
#include "common/UniqueAccess.hpp"
|
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "messages/EmoteCache.hpp"
|
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class BttvEmotes final : std::enable_shared_from_this<BttvEmotes>
|
class BttvEmotes final
|
||||||
{
|
{
|
||||||
static constexpr const char *globalEmoteApiUrl =
|
static constexpr const char *globalEmoteApiUrl =
|
||||||
"https://api.betterttv.net/2/emotes";
|
"https://api.betterttv.net/2/emotes";
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// BttvEmotes();
|
BttvEmotes();
|
||||||
|
|
||||||
AccessGuard<const EmoteMap> accessGlobalEmotes() const;
|
std::shared_ptr<const EmoteMap> global() const;
|
||||||
boost::optional<EmotePtr> getGlobalEmote(const EmoteName &name);
|
boost::optional<EmotePtr> global(const EmoteName &name) const;
|
||||||
boost::optional<EmotePtr> getEmote(const EmoteId &id);
|
void loadGlobal();
|
||||||
|
|
||||||
void loadGlobalEmotes();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pair<Outcome, EmoteMap> parseGlobalEmotes(
|
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||||
const QJsonObject &jsonRoot, const EmoteMap ¤tEmotes);
|
|
||||||
|
|
||||||
UniqueAccess<EmoteMap> globalEmotes_;
|
|
||||||
// UniqueAccess<WeakEmoteIdMap> channelEmoteCache_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -118,7 +118,7 @@ void Emojis::loadEmojis()
|
||||||
rapidjson::ParseResult result = root.Parse(data.toUtf8(), data.length());
|
rapidjson::ParseResult result = root.Parse(data.toUtf8(), data.length());
|
||||||
|
|
||||||
if (result.Code() != rapidjson::kParseErrorNone) {
|
if (result.Code() != rapidjson::kParseErrorNone) {
|
||||||
Log("JSON parse error: {} ({})",
|
log("JSON parse error: {} ({})",
|
||||||
rapidjson::GetParseError_En(result.Code()), result.Offset());
|
rapidjson::GetParseError_En(result.Code()), result.Offset());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ void Emojis::loadEmojis()
|
||||||
|
|
||||||
auto toneNameIt = toneNames.find(tone);
|
auto toneNameIt = toneNames.find(tone);
|
||||||
if (toneNameIt == toneNames.end()) {
|
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);
|
tone);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ void Emojis::loadEmojiSet()
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
app->settings->emojiSet.connect([=](const auto &emojiSet, auto) {
|
app->settings->emojiSet.connect([=](const auto &emojiSet, auto) {
|
||||||
Log("Using emoji set {}", emojiSet);
|
log("Using emoji set {}", emojiSet);
|
||||||
this->emojis.each([=](const auto &name,
|
this->emojis.each([=](const auto &name,
|
||||||
std::shared_ptr<EmojiData> &emoji) {
|
std::shared_ptr<EmojiData> &emoji) {
|
||||||
QString emojiSetToUse = emojiSet;
|
QString emojiSetToUse = emojiSet;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/SimpleSignalVector.hpp"
|
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "util/ConcurrentMap.hpp"
|
#include "util/ConcurrentMap.hpp"
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ Url getEmoteLink(const QJsonObject &urls, const QString &emoteScale)
|
||||||
|
|
||||||
return {"https:" + emote.toString()};
|
return {"https:" + emote.toString()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
|
void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
|
||||||
const QString &tooltip, Emote &emoteData)
|
const QString &tooltip, Emote &emoteData)
|
||||||
{
|
{
|
||||||
|
@ -34,52 +33,11 @@ void fillInEmoteData(const QJsonObject &urls, const EmoteName &name,
|
||||||
Image::fromUrl(url3x, 0.25)};
|
Image::fromUrl(url3x, 0.25)};
|
||||||
emoteData.tooltip = {tooltip};
|
emoteData.tooltip = {tooltip};
|
||||||
}
|
}
|
||||||
} // namespace
|
std::pair<Outcome, EmoteMap> parseGlobalEmotes(const QJsonObject &jsonRoot,
|
||||||
|
const EmoteMap ¤tEmotes)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
auto jsonSets = jsonRoot.value("sets").toObject();
|
auto jsonSets = jsonRoot.value("sets").toObject();
|
||||||
auto emotes = this->globalEmotes_.access();
|
auto emotes = EmoteMap();
|
||||||
auto replacement = emotes->makeReplacment();
|
|
||||||
|
|
||||||
for (auto jsonSet : jsonSets) {
|
for (auto jsonSet : jsonSets) {
|
||||||
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
|
auto jsonEmotes = jsonSet.toObject().value("emoticons").toArray();
|
||||||
|
@ -99,15 +57,55 @@ Outcome FfzEmotes::parseGlobalEmotes(const QJsonObject &jsonRoot)
|
||||||
.arg(id.string)
|
.arg(id.string)
|
||||||
.arg(name.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
|
||||||
std::function<void(EmoteMap &&)> callback)
|
{
|
||||||
|
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",
|
// printf("[FFZEmotes] Reload FFZ Channel Emotes for channel %s\n",
|
||||||
// qPrintable(channelName));
|
// qPrintable(channelName));
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "common/UniqueAccess.hpp"
|
#include "common/Atomic.hpp"
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "messages/EmoteCache.hpp"
|
#include "messages/EmoteCache.hpp"
|
||||||
|
|
||||||
|
@ -16,24 +16,17 @@ class FfzEmotes final : std::enable_shared_from_this<FfzEmotes>
|
||||||
"https://api.betterttv.net/2/channels/";
|
"https://api.betterttv.net/2/channels/";
|
||||||
|
|
||||||
public:
|
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;
|
void loadGlobal();
|
||||||
boost::optional<EmotePtr> getGlobalEmote(const EmoteName &name);
|
void loadChannel(const QString &channelName,
|
||||||
boost::optional<EmotePtr> getEmote(const EmoteId &id);
|
std::function<void(EmoteMap &&)> callback);
|
||||||
|
|
||||||
void loadGlobalEmotes();
|
private:
|
||||||
void loadChannelEmotes(const QString &channelName,
|
Atomic<std::shared_ptr<const EmoteMap>> global_;
|
||||||
std::function<void(EmoteMap &&)> callback);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Outcome parseGlobalEmotes(const QJsonObject &jsonRoot);
|
|
||||||
Outcome parseChannelEmotes(const QJsonObject &jsonRoot);
|
|
||||||
|
|
||||||
UniqueAccess<EmoteCache<EmoteName>> globalEmotes_;
|
|
||||||
UniqueAccess<WeakEmoteIdMap> channelEmoteCache_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -131,7 +131,7 @@ std::shared_ptr<Channel> AbstractIrcServer::getOrAddChannel(
|
||||||
chan->destroyed.connect([this, clojuresInCppAreShit] {
|
chan->destroyed.connect([this, clojuresInCppAreShit] {
|
||||||
// fourtf: issues when the server itself is destroyed
|
// fourtf: issues when the server itself is destroyed
|
||||||
|
|
||||||
Log("[AbstractIrcServer::addChannel] {} was destroyed",
|
log("[AbstractIrcServer::addChannel] {} was destroyed",
|
||||||
clojuresInCppAreShit);
|
clojuresInCppAreShit);
|
||||||
this->channels.remove(clojuresInCppAreShit);
|
this->channels.remove(clojuresInCppAreShit);
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
|
||||||
auto chan = app->twitch.server->getChannelOrEmpty(chanName);
|
auto chan = app->twitch.server->getChannelOrEmpty(chanName);
|
||||||
|
|
||||||
if (chan->isEmpty()) {
|
if (chan->isEmpty()) {
|
||||||
Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not "
|
log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not "
|
||||||
"found",
|
"found",
|
||||||
chanName);
|
chanName);
|
||||||
return;
|
return;
|
||||||
|
@ -209,7 +209,7 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
|
||||||
void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
|
void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *message)
|
||||||
{
|
{
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
Log("Received whisper!");
|
log("Received whisper!");
|
||||||
MessageParseArgs args;
|
MessageParseArgs args;
|
||||||
|
|
||||||
args.isReceivedWhisper = true;
|
args.isReceivedWhisper = true;
|
||||||
|
@ -326,7 +326,7 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
|
||||||
auto channel = app->twitch.server->getChannelOrEmpty(channelName);
|
auto channel = app->twitch.server->getChannelOrEmpty(channelName);
|
||||||
|
|
||||||
if (channel->isEmpty()) {
|
if (channel->isEmpty()) {
|
||||||
Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel "
|
log("[IrcManager:handleNoticeMessage] Channel {} not found in channel "
|
||||||
"manager ",
|
"manager ",
|
||||||
channelName);
|
channelName);
|
||||||
return;
|
return;
|
||||||
|
@ -366,7 +366,7 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("Showing notice message from write connection with message id '{}'",
|
log("Showing notice message from write connection with message id '{}'",
|
||||||
msgID);
|
msgID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,23 +42,23 @@ void PartialTwitchUser::getId(std::function<void(QString)> successCallback,
|
||||||
request.onSuccess([successCallback](auto result) -> Outcome {
|
request.onSuccess([successCallback](auto result) -> Outcome {
|
||||||
auto root = result.parseJson();
|
auto root = result.parseJson();
|
||||||
if (!root.value("users").isArray()) {
|
if (!root.value("users").isArray()) {
|
||||||
Log("API Error while getting user id, users is not an array");
|
log("API Error while getting user id, users is not an array");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto users = root.value("users").toArray();
|
auto users = root.value("users").toArray();
|
||||||
if (users.size() != 1) {
|
if (users.size() != 1) {
|
||||||
Log("API Error while getting user id, users array size is not 1");
|
log("API Error while getting user id, users array size is not 1");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
if (!users[0].isObject()) {
|
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;
|
return Failure;
|
||||||
}
|
}
|
||||||
auto firstUser = users[0].toObject();
|
auto firstUser = users[0].toObject();
|
||||||
auto id = firstUser.value("_id");
|
auto id = firstUser.value("_id");
|
||||||
if (!id.isString()) {
|
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 "
|
"is not a "
|
||||||
"string");
|
"string");
|
||||||
return Failure;
|
return Failure;
|
||||||
|
|
|
@ -109,7 +109,7 @@ void PubSubClient::handlePong()
|
||||||
{
|
{
|
||||||
assert(this->awaitingPong_);
|
assert(this->awaitingPong_);
|
||||||
|
|
||||||
Log("Got pong!");
|
log("Got pong!");
|
||||||
|
|
||||||
this->awaitingPong_ = false;
|
this->awaitingPong_ = false;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ void PubSubClient::ping()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->awaitingPong_) {
|
if (self->awaitingPong_) {
|
||||||
Log("No pong respnose, disconnect!");
|
log("No pong respnose, disconnect!");
|
||||||
// TODO(pajlada): Label this connection as "disconnect me"
|
// TODO(pajlada): Label this connection as "disconnect me"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -166,7 +166,7 @@ bool PubSubClient::send(const char *payload)
|
||||||
websocketpp::frame::opcode::text, ec);
|
websocketpp::frame::opcode::text, ec);
|
||||||
|
|
||||||
if (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
|
// TODO(pajlada): Check which error code happened and maybe gracefully
|
||||||
// handle it
|
// handle it
|
||||||
|
|
||||||
|
@ -207,26 +207,26 @@ PubSub::PubSub()
|
||||||
action.state = ModeChangedAction::State::On;
|
action.state = ModeChangedAction::State::On;
|
||||||
|
|
||||||
if (!data.HasMember("args")) {
|
if (!data.HasMember("args")) {
|
||||||
Log("Missing required args member");
|
log("Missing required args member");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &args = data["args"];
|
const auto &args = data["args"];
|
||||||
|
|
||||||
if (!args.IsArray()) {
|
if (!args.IsArray()) {
|
||||||
Log("args member must be an array");
|
log("args member must be an array");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.Size() == 0) {
|
if (args.Size() == 0) {
|
||||||
Log("Missing duration argument in slowmode on");
|
log("Missing duration argument in slowmode on");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &durationArg = args[0];
|
const auto &durationArg = args[0];
|
||||||
|
|
||||||
if (!durationArg.IsString()) {
|
if (!durationArg.IsString()) {
|
||||||
Log("Duration arg must be a string");
|
log("Duration arg must be a string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ PubSub::PubSub()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (const std::runtime_error &ex) {
|
} catch (const std::runtime_error &ex) {
|
||||||
Log("Error parsing moderation action: {}", ex.what());
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
action.modded = false;
|
action.modded = false;
|
||||||
|
@ -339,7 +339,7 @@ PubSub::PubSub()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (const std::runtime_error &ex) {
|
} catch (const std::runtime_error &ex) {
|
||||||
Log("Error parsing moderation action: {}", ex.what());
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
action.modded = true;
|
action.modded = true;
|
||||||
|
@ -380,7 +380,7 @@ PubSub::PubSub()
|
||||||
|
|
||||||
this->signals_.moderation.userBanned.invoke(action);
|
this->signals_.moderation.userBanned.invoke(action);
|
||||||
} catch (const std::runtime_error &ex) {
|
} catch (const std::runtime_error &ex) {
|
||||||
Log("Error parsing moderation action: {}", ex.what());
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ PubSub::PubSub()
|
||||||
|
|
||||||
this->signals_.moderation.userBanned.invoke(action);
|
this->signals_.moderation.userBanned.invoke(action);
|
||||||
} catch (const std::runtime_error &ex) {
|
} catch (const std::runtime_error &ex) {
|
||||||
Log("Error parsing moderation action: {}", ex.what());
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -436,7 +436,7 @@ PubSub::PubSub()
|
||||||
|
|
||||||
this->signals_.moderation.userUnbanned.invoke(action);
|
this->signals_.moderation.userUnbanned.invoke(action);
|
||||||
} catch (const std::runtime_error &ex) {
|
} catch (const std::runtime_error &ex) {
|
||||||
Log("Error parsing moderation action: {}", ex.what());
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ PubSub::PubSub()
|
||||||
|
|
||||||
this->signals_.moderation.userUnbanned.invoke(action);
|
this->signals_.moderation.userUnbanned.invoke(action);
|
||||||
} catch (const std::runtime_error &ex) {
|
} catch (const std::runtime_error &ex) {
|
||||||
Log("Error parsing moderation action: {}", ex.what());
|
log("Error parsing moderation action: {}", ex.what());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -493,7 +493,7 @@ void PubSub::addClient()
|
||||||
auto con = this->websocketClient.get_connection(TWITCH_PUBSUB_URL, ec);
|
auto con = this->websocketClient.get_connection(TWITCH_PUBSUB_URL, ec);
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
Log("Unable to establish connection: {}", ec.message());
|
log("Unable to establish connection: {}", ec.message());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +512,7 @@ void PubSub::listenToWhispers(std::shared_ptr<TwitchAccount> account)
|
||||||
|
|
||||||
std::string userID = account->getUserId().toStdString();
|
std::string userID = account->getUserId().toStdString();
|
||||||
|
|
||||||
Log("Connection open!");
|
log("Connection open!");
|
||||||
websocketpp::lib::error_code ec;
|
websocketpp::lib::error_code ec;
|
||||||
|
|
||||||
std::vector<std::string> topics({"whispers." + userID});
|
std::vector<std::string> topics({"whispers." + userID});
|
||||||
|
@ -520,7 +520,7 @@ void PubSub::listenToWhispers(std::shared_ptr<TwitchAccount> account)
|
||||||
this->listen(createListenMessage(topics, account));
|
this->listen(createListenMessage(topics, account));
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
Log("Unable to send message to websocket server: {}", ec.message());
|
log("Unable to send message to websocket server: {}", ec.message());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -544,11 +544,11 @@ void PubSub::listenToChannelModerationActions(
|
||||||
std::string topic(fS("chat_moderator_actions.{}.{}", userID, channelID));
|
std::string topic(fS("chat_moderator_actions.{}.{}", userID, channelID));
|
||||||
|
|
||||||
if (this->isListeningToTopic(topic)) {
|
if (this->isListeningToTopic(topic)) {
|
||||||
Log("We are already listening to topic {}", topic);
|
log("We are already listening to topic {}", topic);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("Listen to topic {}", topic);
|
log("Listen to topic {}", topic);
|
||||||
|
|
||||||
this->listenToTopic(topic, account);
|
this->listenToTopic(topic, account);
|
||||||
}
|
}
|
||||||
|
@ -564,18 +564,18 @@ void PubSub::listenToTopic(const std::string &topic,
|
||||||
void PubSub::listen(rapidjson::Document &&msg)
|
void PubSub::listen(rapidjson::Document &&msg)
|
||||||
{
|
{
|
||||||
if (this->tryListen(msg)) {
|
if (this->tryListen(msg)) {
|
||||||
Log("Successfully listened!");
|
log("Successfully listened!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("Added to the back of the queue");
|
log("Added to the back of the queue");
|
||||||
this->requests.emplace_back(
|
this->requests.emplace_back(
|
||||||
std::make_unique<rapidjson::Document>(std::move(msg)));
|
std::make_unique<rapidjson::Document>(std::move(msg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PubSub::tryListen(rapidjson::Document &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) {
|
for (const auto &p : this->clients) {
|
||||||
const auto &client = p.second;
|
const auto &client = p.second;
|
||||||
if (client->listen(msg)) {
|
if (client->listen(msg)) {
|
||||||
|
@ -608,13 +608,13 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
|
||||||
rapidjson::ParseResult res = msg.Parse(payload.c_str());
|
rapidjson::ParseResult res = msg.Parse(payload.c_str());
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
Log("Error parsing message '{}' from PubSub: {}", payload,
|
log("Error parsing message '{}' from PubSub: {}", payload,
|
||||||
rapidjson::GetParseError_En(res.Code()));
|
rapidjson::GetParseError_En(res.Code()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!msg.IsObject()) {
|
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",
|
"object",
|
||||||
payload);
|
payload);
|
||||||
return;
|
return;
|
||||||
|
@ -623,7 +623,7 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
|
||||||
std::string type;
|
std::string type;
|
||||||
|
|
||||||
if (!rj::getSafe(msg, "type", 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,14 +631,14 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
|
||||||
this->handleListenResponse(msg);
|
this->handleListenResponse(msg);
|
||||||
} else if (type == "MESSAGE") {
|
} else if (type == "MESSAGE") {
|
||||||
if (!msg.HasMember("data")) {
|
if (!msg.HasMember("data")) {
|
||||||
Log("Missing required object member `data` in message root");
|
log("Missing required object member `data` in message root");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &data = msg["data"];
|
const auto &data = msg["data"];
|
||||||
|
|
||||||
if (!data.IsObject()) {
|
if (!data.IsObject()) {
|
||||||
Log("Member `data` must be an object");
|
log("Member `data` must be an object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,7 +654,7 @@ void PubSub::onMessage(websocketpp::connection_hdl hdl,
|
||||||
|
|
||||||
client.second->handlePong();
|
client.second->handlePong();
|
||||||
} else {
|
} 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::no_sslv2 |
|
||||||
boost::asio::ssl::context::single_dh_use);
|
boost::asio::ssl::context::single_dh_use);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
Log("Exception caught in OnTLSInit: {}", e.what());
|
log("Exception caught in OnTLSInit: {}", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
|
@ -714,12 +714,12 @@ void PubSub::handleListenResponse(const rapidjson::Document &msg)
|
||||||
rj::getSafe(msg, "nonce", nonce);
|
rj::getSafe(msg, "nonce", nonce);
|
||||||
|
|
||||||
if (error.empty()) {
|
if (error.empty()) {
|
||||||
Log("Successfully listened to nonce {}", nonce);
|
log("Successfully listened to nonce {}", nonce);
|
||||||
// Nothing went wrong
|
// Nothing went wrong
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("PubSub error: {} on nonce {}", error, nonce);
|
log("PubSub error: {} on nonce {}", error, nonce);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -729,14 +729,14 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
|
||||||
QString topic;
|
QString topic;
|
||||||
|
|
||||||
if (!rj::getSafe(outerData, "topic", topic)) {
|
if (!rj::getSafe(outerData, "topic", topic)) {
|
||||||
Log("Missing required string member `topic` in outerData");
|
log("Missing required string member `topic` in outerData");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string payload;
|
std::string payload;
|
||||||
|
|
||||||
if (!rj::getSafe(outerData, "message", payload)) {
|
if (!rj::getSafe(outerData, "message", payload)) {
|
||||||
Log("Expected string message in outerData");
|
log("Expected string message in outerData");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,7 +745,7 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
|
||||||
rapidjson::ParseResult res = msg.Parse(payload.c_str());
|
rapidjson::ParseResult res = msg.Parse(payload.c_str());
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
Log("Error parsing message '{}' from PubSub: {}", payload,
|
log("Error parsing message '{}' from PubSub: {}", payload,
|
||||||
rapidjson::GetParseError_En(res.Code()));
|
rapidjson::GetParseError_En(res.Code()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -754,7 +754,7 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
|
||||||
std::string whisperType;
|
std::string whisperType;
|
||||||
|
|
||||||
if (!rj::getSafe(msg, "type", whisperType)) {
|
if (!rj::getSafe(msg, "type", whisperType)) {
|
||||||
Log("Bad whisper data");
|
log("Bad whisper data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,7 +765,7 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
|
||||||
} else if (whisperType == "thread") {
|
} else if (whisperType == "thread") {
|
||||||
// Handle thread?
|
// Handle thread?
|
||||||
} else {
|
} else {
|
||||||
Log("Invalid whisper type: {}", whisperType);
|
log("Invalid whisper type: {}", whisperType);
|
||||||
assert(false);
|
assert(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -777,30 +777,30 @@ void PubSub::handleMessageResponse(const rapidjson::Value &outerData)
|
||||||
std::string moderationAction;
|
std::string moderationAction;
|
||||||
|
|
||||||
if (!rj::getSafe(data, "moderation_action", moderationAction)) {
|
if (!rj::getSafe(data, "moderation_action", moderationAction)) {
|
||||||
Log("Missing moderation action in data: {}", rj::stringify(data));
|
log("Missing moderation action in data: {}", rj::stringify(data));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto handlerIt = this->moderationActionHandlers.find(moderationAction);
|
auto handlerIt = this->moderationActionHandlers.find(moderationAction);
|
||||||
|
|
||||||
if (handlerIt == this->moderationActionHandlers.end()) {
|
if (handlerIt == this->moderationActionHandlers.end()) {
|
||||||
Log("No handler found for moderation action {}", moderationAction);
|
log("No handler found for moderation action {}", moderationAction);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke handler function
|
// Invoke handler function
|
||||||
handlerIt->second(data, topicParts[2]);
|
handlerIt->second(data, topicParts[2]);
|
||||||
} else {
|
} else {
|
||||||
Log("Unknown topic: {}", topic);
|
log("Unknown topic: {}", topic);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PubSub::runThread()
|
void PubSub::runThread()
|
||||||
{
|
{
|
||||||
Log("Start pubsub manager thread");
|
log("Start pubsub manager thread");
|
||||||
this->websocketClient.run();
|
this->websocketClient.run();
|
||||||
Log("Done with pubsub manager thread");
|
log("Done with pubsub manager thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -35,7 +35,7 @@ void runAfter(boost::asio::io_service &ioService, Duration duration,
|
||||||
|
|
||||||
timer->async_wait([timer, cb](const boost::system::error_code &ec) {
|
timer->async_wait([timer, cb](const boost::system::error_code &ec) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
Log("Error in runAfter: {}", ec.message());
|
log("Error in runAfter: {}", ec.message());
|
||||||
return;
|
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) {
|
timer->async_wait([timer, cb](const boost::system::error_code &ec) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
Log("Error in runAfter: {}", ec.message());
|
log("Error in runAfter: {}", ec.message());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ void TwitchAccount::loadIgnores()
|
||||||
}
|
}
|
||||||
TwitchUser ignoredUser;
|
TwitchUser ignoredUser;
|
||||||
if (!rj::getSafe(userIt->value, ignoredUser)) {
|
if (!rj::getSafe(userIt->value, ignoredUser)) {
|
||||||
Log("Error parsing twitch user JSON {}",
|
log("Error parsing twitch user JSON {}",
|
||||||
rj::stringify(userIt->value));
|
rj::stringify(userIt->value));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -368,13 +368,13 @@ std::set<TwitchUser> TwitchAccount::getIgnores() const
|
||||||
|
|
||||||
void TwitchAccount::loadEmotes()
|
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 &clientID = this->getOAuthClient();
|
||||||
const auto &oauthToken = this->getOAuthToken();
|
const auto &oauthToken = this->getOAuthToken();
|
||||||
|
|
||||||
if (clientID.isEmpty() || oauthToken.isEmpty()) {
|
if (clientID.isEmpty() || oauthToken.isEmpty()) {
|
||||||
Log("Missing Client ID or OAuth token");
|
log("Missing Client ID or OAuth token");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,7 +386,7 @@ void TwitchAccount::loadEmotes()
|
||||||
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
|
req.makeAuthorizedV5(this->getOAuthClient(), this->getOAuthToken());
|
||||||
|
|
||||||
req.onError([=](int errorCode) {
|
req.onError([=](int errorCode) {
|
||||||
Log("[TwitchAccount::loadEmotes] Error {}", errorCode);
|
log("[TwitchAccount::loadEmotes] Error {}", errorCode);
|
||||||
if (errorCode == 203) {
|
if (errorCode == 203) {
|
||||||
// onFinished(FollowResult_NotFollowing);
|
// onFinished(FollowResult_NotFollowing);
|
||||||
} else {
|
} else {
|
||||||
|
@ -420,7 +420,7 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
|
||||||
|
|
||||||
auto emoticonSets = root.FindMember("emoticon_sets");
|
auto emoticonSets = root.FindMember("emoticon_sets");
|
||||||
if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject()) {
|
if (emoticonSets == root.MemberEnd() || !emoticonSets->value.IsObject()) {
|
||||||
Log("No emoticon_sets in load emotes response");
|
log("No emoticon_sets in load emotes response");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,19 +434,19 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
|
||||||
for (const rapidjson::Value &emoteJSON :
|
for (const rapidjson::Value &emoteJSON :
|
||||||
emoteSetJSON.value.GetArray()) {
|
emoteSetJSON.value.GetArray()) {
|
||||||
if (!emoteJSON.IsObject()) {
|
if (!emoteJSON.IsObject()) {
|
||||||
Log("Emote value was invalid");
|
log("Emote value was invalid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t idNumber;
|
uint64_t idNumber;
|
||||||
if (!rj::getSafe(emoteJSON, "id", idNumber)) {
|
if (!rj::getSafe(emoteJSON, "id", idNumber)) {
|
||||||
Log("No ID key found in Emote value");
|
log("No ID key found in Emote value");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString _code;
|
QString _code;
|
||||||
if (!rj::getSafe(emoteJSON, "code", _code)) {
|
if (!rj::getSafe(emoteJSON, "code", _code)) {
|
||||||
Log("No code key found in Emote value");
|
log("No code key found in Emote value");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ void TwitchAccount::parseEmotes(const rapidjson::Document &root)
|
||||||
void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
|
void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
|
||||||
{
|
{
|
||||||
if (!emoteSet) {
|
if (!emoteSet) {
|
||||||
Log("null emote set sent");
|
log("null emote set sent");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,7 +486,7 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
|
||||||
req.setUseQuickLoadCache(true);
|
req.setUseQuickLoadCache(true);
|
||||||
|
|
||||||
req.onError([](int errorCode) -> bool {
|
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;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -507,16 +507,15 @@ void TwitchAccount::loadEmoteSetData(std::shared_ptr<EmoteSet> emoteSet)
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("Loaded twitch emote set data for {}!", emoteSet->key);
|
log("Loaded twitch emote set data for {}!", emoteSet->key);
|
||||||
|
|
||||||
if (type == "sub") {
|
auto name = channelName;
|
||||||
emoteSet->text =
|
name.detach();
|
||||||
QString("Twitch Subscriber Emote (%1)").arg(channelName);
|
name[0] = name[0].toUpper();
|
||||||
} else {
|
|
||||||
emoteSet->text =
|
|
||||||
QString("Twitch Account Emote (%1)").arg(channelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
emoteSet->text = name;
|
||||||
|
|
||||||
|
emoteSet->type = type;
|
||||||
emoteSet->channelName = channelName;
|
emoteSet->channelName = channelName;
|
||||||
|
|
||||||
return Success;
|
return Success;
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
QString key;
|
QString key;
|
||||||
QString channelName;
|
QString channelName;
|
||||||
QString text;
|
QString text;
|
||||||
|
QString type;
|
||||||
std::vector<TwitchEmote> emotes;
|
std::vector<TwitchEmote> emotes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -93,20 +93,20 @@ void TwitchAccountManager::reloadUsers()
|
||||||
|
|
||||||
switch (this->addUser(userData)) {
|
switch (this->addUser(userData)) {
|
||||||
case AddUserResponse::UserAlreadyExists: {
|
case AddUserResponse::UserAlreadyExists: {
|
||||||
Log("User {} already exists", userData.username);
|
log("User {} already exists", userData.username);
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} break;
|
} break;
|
||||||
case AddUserResponse::UserValuesUpdated: {
|
case AddUserResponse::UserValuesUpdated: {
|
||||||
Log("User {} already exists, and values updated!",
|
log("User {} already exists, and values updated!",
|
||||||
userData.username);
|
userData.username);
|
||||||
if (userData.username == this->getCurrent()->getUserName()) {
|
if (userData.username == this->getCurrent()->getUserName()) {
|
||||||
Log("It was the current user, so we need to reconnect "
|
log("It was the current user, so we need to reconnect "
|
||||||
"stuff!");
|
"stuff!");
|
||||||
this->currentUserChanged.invoke();
|
this->currentUserChanged.invoke();
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case AddUserResponse::UserAdded: {
|
case AddUserResponse::UserAdded: {
|
||||||
Log("Added user {}", userData.username);
|
log("Added user {}", userData.username);
|
||||||
listUpdated = true;
|
listUpdated = true;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
@ -125,12 +125,12 @@ void TwitchAccountManager::load()
|
||||||
QString newUsername(QString::fromStdString(newValue));
|
QString newUsername(QString::fromStdString(newValue));
|
||||||
auto user = this->findUserByUsername(newUsername);
|
auto user = this->findUserByUsername(newUsername);
|
||||||
if (user) {
|
if (user) {
|
||||||
Log("[AccountManager:currentUsernameChanged] User successfully "
|
log("[AccountManager:currentUsernameChanged] User successfully "
|
||||||
"updated to {}",
|
"updated to {}",
|
||||||
newUsername);
|
newUsername);
|
||||||
this->currentUser_ = user;
|
this->currentUser_ = user;
|
||||||
} else {
|
} else {
|
||||||
Log("[AccountManager:currentUsernameChanged] User successfully "
|
log("[AccountManager:currentUsernameChanged] User successfully "
|
||||||
"updated to anonymous");
|
"updated to anonymous");
|
||||||
this->currentUser_ = this->anonymousUser_;
|
this->currentUser_ = this->anonymousUser_;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,25 +20,25 @@ void TwitchApi::findUserId(const QString user,
|
||||||
request.onSuccess([successCallback](auto result) mutable -> Outcome {
|
request.onSuccess([successCallback](auto result) mutable -> Outcome {
|
||||||
auto root = result.parseJson();
|
auto root = result.parseJson();
|
||||||
if (!root.value("users").isArray()) {
|
if (!root.value("users").isArray()) {
|
||||||
Log("API Error while getting user id, users is not an array");
|
log("API Error while getting user id, users is not an array");
|
||||||
successCallback("");
|
successCallback("");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
auto users = root.value("users").toArray();
|
auto users = root.value("users").toArray();
|
||||||
if (users.size() != 1) {
|
if (users.size() != 1) {
|
||||||
Log("API Error while getting user id, users array size is not 1");
|
log("API Error while getting user id, users array size is not 1");
|
||||||
successCallback("");
|
successCallback("");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
if (!users[0].isObject()) {
|
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("");
|
successCallback("");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
auto firstUser = users[0].toObject();
|
auto firstUser = users[0].toObject();
|
||||||
auto id = firstUser.value("_id");
|
auto id = firstUser.value("_id");
|
||||||
if (!id.isString()) {
|
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 "
|
"is not a "
|
||||||
"string");
|
"string");
|
||||||
successCallback("");
|
successCallback("");
|
||||||
|
|
|
@ -56,7 +56,7 @@ TwitchChannel::TwitchChannel(const QString &name)
|
||||||
, popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name)
|
, popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name)
|
||||||
, mod_(false)
|
, mod_(false)
|
||||||
{
|
{
|
||||||
Log("[TwitchChannel:{}] Opened", name);
|
log("[TwitchChannel:{}] Opened", name);
|
||||||
|
|
||||||
// this->refreshChannelEmotes();
|
// this->refreshChannelEmotes();
|
||||||
// this->refreshViewerList();
|
// this->refreshViewerList();
|
||||||
|
@ -116,7 +116,7 @@ void TwitchChannel::refreshChannelEmotes()
|
||||||
if (auto shared = weak.lock()) //
|
if (auto shared = weak.lock()) //
|
||||||
*this->bttvEmotes_.access() = emoteMap;
|
*this->bttvEmotes_.access() = emoteMap;
|
||||||
});
|
});
|
||||||
getApp()->emotes->ffz.loadChannelEmotes(
|
getApp()->emotes->ffz.loadChannel(
|
||||||
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
this->getName(), [this, weak = weakOf<Channel>(this)](auto &&emoteMap) {
|
||||||
if (auto shared = weak.lock())
|
if (auto shared = weak.lock())
|
||||||
*this->ffzEmotes_.access() = emoteMap;
|
*this->ffzEmotes_.access() = emoteMap;
|
||||||
|
@ -136,7 +136,7 @@ void TwitchChannel::sendMessage(const QString &message)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("[TwitchChannel:{}] Send message: {}", this->getName(), message);
|
log("[TwitchChannel:{}] Send message: {}", this->getName(), message);
|
||||||
|
|
||||||
// Do last message processing
|
// Do last message processing
|
||||||
QString parsedMessage = app->emotes->emojis.replaceShortCodes(message);
|
QString parsedMessage = app->emotes->emojis.replaceShortCodes(message);
|
||||||
|
@ -350,13 +350,13 @@ void TwitchChannel::refreshLiveStatus()
|
||||||
auto roomID = this->getRoomId();
|
auto roomID = this->getRoomId();
|
||||||
|
|
||||||
if (roomID.isEmpty()) {
|
if (roomID.isEmpty()) {
|
||||||
Log("[TwitchChannel:{}] Refreshing live status (Missing ID)",
|
log("[TwitchChannel:{}] Refreshing live status (Missing ID)",
|
||||||
this->getName());
|
this->getName());
|
||||||
this->setLive(false);
|
this->setLive(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("[TwitchChannel:{}] Refreshing live status", this->getName());
|
log("[TwitchChannel:{}] Refreshing live status", this->getName());
|
||||||
|
|
||||||
QString url("https://api.twitch.tv/kraken/streams/" + roomID);
|
QString url("https://api.twitch.tv/kraken/streams/" + roomID);
|
||||||
|
|
||||||
|
@ -381,12 +381,12 @@ void TwitchChannel::refreshLiveStatus()
|
||||||
Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
|
Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
|
||||||
{
|
{
|
||||||
if (!document.IsObject()) {
|
if (!document.IsObject()) {
|
||||||
Log("[TwitchChannel:refreshLiveStatus] root is not an object");
|
log("[TwitchChannel:refreshLiveStatus] root is not an object");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!document.HasMember("stream")) {
|
if (!document.HasMember("stream")) {
|
||||||
Log("[TwitchChannel:refreshLiveStatus] Missing stream in root");
|
log("[TwitchChannel:refreshLiveStatus] Missing stream in root");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +400,7 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
|
||||||
|
|
||||||
if (!stream.HasMember("viewers") || !stream.HasMember("game") ||
|
if (!stream.HasMember("viewers") || !stream.HasMember("game") ||
|
||||||
!stream.HasMember("channel") || !stream.HasMember("created_at")) {
|
!stream.HasMember("channel") || !stream.HasMember("created_at")) {
|
||||||
Log("[TwitchChannel:refreshLiveStatus] Missing members in stream");
|
log("[TwitchChannel:refreshLiveStatus] Missing members in stream");
|
||||||
this->setLive(false);
|
this->setLive(false);
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
@ -408,7 +408,7 @@ Outcome TwitchChannel::parseLiveStatus(const rapidjson::Document &document)
|
||||||
const rapidjson::Value &streamChannel = stream["channel"];
|
const rapidjson::Value &streamChannel = stream["channel"];
|
||||||
|
|
||||||
if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) {
|
if (!streamChannel.IsObject() || !streamChannel.HasMember("status")) {
|
||||||
Log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in "
|
log("[TwitchChannel:refreshLiveStatus] Missing member \"status\" in "
|
||||||
"channel");
|
"channel");
|
||||||
return Failure;
|
return Failure;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#include "common/Channel.hpp"
|
#include "common/Channel.hpp"
|
||||||
#include "common/Common.hpp"
|
#include "common/Common.hpp"
|
||||||
#include "common/MutexValue.hpp"
|
#include "common/Atomic.hpp"
|
||||||
#include "common/UniqueAccess.hpp"
|
#include "common/UniqueAccess.hpp"
|
||||||
#include "messages/Emote.hpp"
|
#include "messages/Emote.hpp"
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace chatterino {
|
||||||
bool trimChannelName(const QString &channelName, QString &outChannelName)
|
bool trimChannelName(const QString &channelName, QString &outChannelName)
|
||||||
{
|
{
|
||||||
if (channelName.length() < 3) {
|
if (channelName.length() < 3) {
|
||||||
Log("channel name length below 3");
|
log("channel name length below 3");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ bool TwitchMessageBuilder::isIgnored() const
|
||||||
// TODO(pajlada): Do we need to check if the phrase is valid first?
|
// TODO(pajlada): Do we need to check if the phrase is valid first?
|
||||||
for (const auto &phrase : app->ignores->phrases.getVector()) {
|
for (const auto &phrase : app->ignores->phrases.getVector()) {
|
||||||
if (phrase.isMatch(this->originalMessage_)) {
|
if (phrase.isMatch(this->originalMessage_)) {
|
||||||
Log("Blocking message because it contains ignored phrase {}",
|
log("Blocking message because it contains ignored phrase {}",
|
||||||
phrase.getPattern());
|
phrase.getPattern());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ bool TwitchMessageBuilder::isIgnored() const
|
||||||
for (const auto &user :
|
for (const auto &user :
|
||||||
app->accounts->twitch.getCurrent()->getIgnores()) {
|
app->accounts->twitch.getCurrent()->getIgnores()) {
|
||||||
if (sourceUserID == user.id) {
|
if (sourceUserID == user.id) {
|
||||||
Log("Blocking message because it's from blocked user {}",
|
log("Blocking message because it's from blocked user {}",
|
||||||
user.name);
|
user.name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -533,7 +533,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
||||||
if (!app->highlights->blacklistContains(this->ircMessage->nick())) {
|
if (!app->highlights->blacklistContains(this->ircMessage->nick())) {
|
||||||
for (const HighlightPhrase &highlight : activeHighlights) {
|
for (const HighlightPhrase &highlight : activeHighlights) {
|
||||||
if (highlight.isMatch(this->originalMessage_)) {
|
if (highlight.isMatch(this->originalMessage_)) {
|
||||||
Log("Highlight because {} matches {}", this->originalMessage_,
|
log("Highlight because {} matches {}", this->originalMessage_,
|
||||||
highlight.getPattern());
|
highlight.getPattern());
|
||||||
doHighlight = true;
|
doHighlight = true;
|
||||||
|
|
||||||
|
@ -555,7 +555,7 @@ void TwitchMessageBuilder::parseHighlights(bool isPastMsg)
|
||||||
}
|
}
|
||||||
for (const HighlightPhrase &userHighlight : userHighlights) {
|
for (const HighlightPhrase &userHighlight : userHighlights) {
|
||||||
if (userHighlight.isMatch(this->ircMessage->nick())) {
|
if (userHighlight.isMatch(this->ircMessage->nick())) {
|
||||||
Log("Highlight because user {} sent a message",
|
log("Highlight because user {} sent a message",
|
||||||
this->ircMessage->nick());
|
this->ircMessage->nick());
|
||||||
doHighlight = true;
|
doHighlight = true;
|
||||||
|
|
||||||
|
@ -638,12 +638,12 @@ Outcome TwitchMessageBuilder::tryAppendEmote(const EmoteName &name)
|
||||||
auto flags = MessageElementFlags();
|
auto flags = MessageElementFlags();
|
||||||
auto emote = boost::optional<EmotePtr>{};
|
auto emote = boost::optional<EmotePtr>{};
|
||||||
|
|
||||||
if ((emote = getApp()->emotes->bttv.getGlobalEmote(name))) {
|
if ((emote = getApp()->emotes->bttv.global(name))) {
|
||||||
flags = MessageElementFlag::BttvEmote;
|
flags = MessageElementFlag::BttvEmote;
|
||||||
} else if (twitchChannel &&
|
} else if (twitchChannel &&
|
||||||
(emote = this->twitchChannel->getBttvEmote(name))) {
|
(emote = this->twitchChannel->getBttvEmote(name))) {
|
||||||
flags = MessageElementFlag::BttvEmote;
|
flags = MessageElementFlag::BttvEmote;
|
||||||
} else if ((emote = getApp()->emotes->ffz.getGlobalEmote(name))) {
|
} else if ((emote = getApp()->emotes->ffz.global(name))) {
|
||||||
flags = MessageElementFlag::FfzEmote;
|
flags = MessageElementFlag::FfzEmote;
|
||||||
} else if (twitchChannel &&
|
} else if (twitchChannel &&
|
||||||
(emote = this->twitchChannel->getFfzEmote(name))) {
|
(emote = this->twitchChannel->getFfzEmote(name))) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
#include "messages/MessageParseArgs.hpp"
|
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
|
|
||||||
#include <IrcMessage>
|
#include <IrcMessage>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/MutexValue.hpp"
|
#include "common/Atomic.hpp"
|
||||||
#include "common/Singleton.hpp"
|
#include "common/Singleton.hpp"
|
||||||
#include "providers/irc/AbstractIrcServer.hpp"
|
#include "providers/irc/AbstractIrcServer.hpp"
|
||||||
#include "providers/twitch/TwitchAccount.hpp"
|
#include "providers/twitch/TwitchAccount.hpp"
|
||||||
|
@ -29,7 +29,7 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<Channel> getChannelOrEmptyByID(const QString &channelID);
|
std::shared_ptr<Channel> getChannelOrEmptyByID(const QString &channelID);
|
||||||
|
|
||||||
MutexValue<QString> lastUserThatWhisperedMe;
|
Atomic<QString> lastUserThatWhisperedMe;
|
||||||
|
|
||||||
const ChannelPtr whispersChannel;
|
const ChannelPtr whispersChannel;
|
||||||
const ChannelPtr mentionsChannel;
|
const ChannelPtr mentionsChannel;
|
||||||
|
|
|
@ -15,8 +15,8 @@ void Emotes::initialize(Settings &settings, Paths &paths)
|
||||||
[] { getApp()->accounts->twitch.getCurrent()->loadEmotes(); });
|
[] { getApp()->accounts->twitch.getCurrent()->loadEmotes(); });
|
||||||
|
|
||||||
this->emojis.load();
|
this->emojis.load();
|
||||||
this->bttv.loadGlobalEmotes();
|
this->bttv.loadGlobal();
|
||||||
this->ffz.loadGlobalEmotes();
|
this->ffz.loadGlobal();
|
||||||
|
|
||||||
this->gifTimer.initialize();
|
this->gifTimer.initialize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ void Settings::saveSnapshot()
|
||||||
|
|
||||||
this->snapshot_.reset(d);
|
this->snapshot_.reset(d);
|
||||||
|
|
||||||
Log("hehe: {}", pajlada::Settings::SettingManager::stringify(*d));
|
log("hehe: {}", pajlada::Settings::SettingManager::stringify(*d));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::restoreSnapshot()
|
void Settings::restoreSnapshot()
|
||||||
|
@ -64,14 +64,14 @@ void Settings::restoreSnapshot()
|
||||||
for (const auto &weakSetting : _settings) {
|
for (const auto &weakSetting : _settings) {
|
||||||
auto setting = weakSetting.lock();
|
auto setting = weakSetting.lock();
|
||||||
if (!setting) {
|
if (!setting) {
|
||||||
Log("Error stage 1 of loading");
|
log("Error stage 1 of loading");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *path = setting->getPath().c_str();
|
const char *path = setting->getPath().c_str();
|
||||||
|
|
||||||
if (!snapshotObject.HasMember(path)) {
|
if (!snapshotObject.HasMember(path)) {
|
||||||
Log("Error stage 2 of loading");
|
log("Error stage 2 of loading");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ Window *WindowManager::windowAt(int index)
|
||||||
if (index < 0 || (size_t)index >= this->windows_.size()) {
|
if (index < 0 || (size_t)index >= this->windows_.size()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
Log("getting window at bad index {}", index);
|
log("getting window at bad index {}", index);
|
||||||
|
|
||||||
return this->windows_.at(index);
|
return this->windows_.at(index);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,13 +65,13 @@ void LoggingChannel::openLogFile()
|
||||||
this->baseDirectory + QDir::separator() + this->subDirectory;
|
this->baseDirectory + QDir::separator() + this->subDirectory;
|
||||||
|
|
||||||
if (!QDir().mkpath(directory)) {
|
if (!QDir().mkpath(directory)) {
|
||||||
Log("Unable to create logging path");
|
log("Unable to create logging path");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open file handle to log file of current date
|
// Open file handle to log file of current date
|
||||||
QString fileName = directory + QDir::separator() + baseFileName;
|
QString fileName = directory + QDir::separator() + baseFileName;
|
||||||
Log("Logging to {}", fileName);
|
log("Logging to {}", fileName);
|
||||||
this->fileHandle.setFileName(fileName);
|
this->fileHandle.setFileName(fileName);
|
||||||
|
|
||||||
this->fileHandle.open(QIODevice::Append);
|
this->fileHandle.open(QIODevice::Append);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
template <typename... Args>
|
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)...);
|
return fmt::format(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
#include "controllers/accounts/AccountController.hpp"
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
|
#include "debug/Benchmark.hpp"
|
||||||
#include "messages/MessageBuilder.hpp"
|
#include "messages/MessageBuilder.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "widgets/Notebook.hpp"
|
#include "widgets/Notebook.hpp"
|
||||||
|
@ -11,36 +12,88 @@
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
|
|
||||||
namespace chatterino {
|
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()
|
EmotePopup::EmotePopup()
|
||||||
: BaseWindow(nullptr, BaseWindow::EnableCustomFrame)
|
: BaseWindow(nullptr, BaseWindow::EnableCustomFrame)
|
||||||
{
|
{
|
||||||
this->viewEmotes_ = new ChannelView();
|
auto layout = new QVBoxLayout(this);
|
||||||
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);
|
|
||||||
this->getLayoutContainer()->setLayout(layout);
|
this->getLayoutContainer()->setLayout(layout);
|
||||||
|
|
||||||
Notebook *notebook = new Notebook(this);
|
auto notebook = new Notebook(this);
|
||||||
layout->addWidget(notebook);
|
layout->addWidget(notebook);
|
||||||
layout->setMargin(0);
|
layout->setMargin(0);
|
||||||
|
|
||||||
notebook->addPage(this->viewEmotes_, "Emotes");
|
auto makeView = [&](QString tabTitle) {
|
||||||
notebook->addPage(this->viewEmojis_, "Emojis");
|
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->loadEmojis();
|
||||||
|
|
||||||
this->viewEmotes_->linkClicked.connect(
|
this->globalEmotesView_->linkClicked.connect(
|
||||||
[this](const Link &link) { this->linkClicked.invoke(link); });
|
[this](const Link &link) { this->linkClicked.invoke(link); });
|
||||||
this->viewEmojis_->linkClicked.connect(
|
this->viewEmojis_->linkClicked.connect(
|
||||||
[this](const Link &link) { this->linkClicked.invoke(link); });
|
[this](const Link &link) { this->linkClicked.invoke(link); });
|
||||||
|
@ -48,93 +101,41 @@ EmotePopup::EmotePopup()
|
||||||
|
|
||||||
void EmotePopup::loadChannel(ChannelPtr _channel)
|
void EmotePopup::loadChannel(ChannelPtr _channel)
|
||||||
{
|
{
|
||||||
|
BenchmarkGuard guard("loadChannel");
|
||||||
|
|
||||||
this->setWindowTitle("Emotes from " + _channel->getName());
|
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) {
|
auto addEmotes = [&](Channel &channel, const EmoteMap &map,
|
||||||
return;
|
const QString &title) {
|
||||||
}
|
channel.addMessage(makeTitleMessage(title));
|
||||||
|
channel.addMessage(makeEmoteMessage(map));
|
||||||
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 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
|
// twitch
|
||||||
// point in trying to fix this pile of garbage
|
addEmoteSets(
|
||||||
for (const auto &set :
|
getApp()->accounts->twitch.getCurrent()->accessEmotes()->emoteSets,
|
||||||
app->accounts->twitch.getCurrent()->accessEmotes()->emoteSets) {
|
*globalChannel, *subChannel);
|
||||||
// TITLE
|
|
||||||
MessageBuilder builder1;
|
|
||||||
|
|
||||||
QString setText;
|
// global
|
||||||
if (set->text.isEmpty()) {
|
addEmotes(*globalChannel, *getApp()->emotes->bttv.global(), "BetterTTV");
|
||||||
if (set->channelName.isEmpty()) {
|
addEmotes(*globalChannel, *getApp()->emotes->ffz.global(), "FrankerFaceZ");
|
||||||
setText = "Twitch Account Emotes";
|
|
||||||
} else {
|
|
||||||
setText = "Twitch Account Emotes (" + set->channelName + ")";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setText = set->text;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
this->globalEmotesView_->setChannel(globalChannel);
|
||||||
emoteChannel->addMessage(builder1.release());
|
this->subEmotesView_->setChannel(subChannel);
|
||||||
|
this->channelEmotesView_->setChannel(channelChannel);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmotePopup::loadEmojis()
|
void EmotePopup::loadEmojis()
|
||||||
|
@ -143,13 +144,6 @@ void EmotePopup::loadEmojis()
|
||||||
|
|
||||||
ChannelPtr emojiChannel(new Channel("", Channel::Type::None));
|
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
|
// emojis
|
||||||
MessageBuilder builder;
|
MessageBuilder builder;
|
||||||
builder->flags.set(MessageFlag::Centered);
|
builder->flags.set(MessageFlag::Centered);
|
||||||
|
|
|
@ -19,8 +19,10 @@ public:
|
||||||
pajlada::Signals::Signal<Link> linkClicked;
|
pajlada::Signals::Signal<Link> linkClicked;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ChannelView *viewEmotes_;
|
ChannelView *globalEmotesView_{};
|
||||||
ChannelView *viewEmojis_;
|
ChannelView *channelEmotesView_{};
|
||||||
|
ChannelView *subEmotesView_{};
|
||||||
|
ChannelView *viewEmojis_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -47,7 +47,7 @@ void QualityPopup::okButtonClicked()
|
||||||
try {
|
try {
|
||||||
openStreamlink(channelURL, this->ui_.selector.currentText());
|
openStreamlink(channelURL, this->ui_.selector.currentText());
|
||||||
} catch (const Exception &ex) {
|
} catch (const Exception &ex) {
|
||||||
Log("Exception caught trying to open streamlink: {}", ex.what());
|
log("Exception caught trying to open streamlink: {}", ex.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
this->close();
|
this->close();
|
||||||
|
|
|
@ -492,7 +492,7 @@ void ChannelView::setChannel(ChannelPtr newChannel)
|
||||||
MessageLayoutPtr newItem(new MessageLayout(replacement));
|
MessageLayoutPtr newItem(new MessageLayout(replacement));
|
||||||
auto snapshot = this->messages.getSnapshot();
|
auto snapshot = this->messages.getSnapshot();
|
||||||
if (index >= snapshot.getLength()) {
|
if (index >= snapshot.getLength()) {
|
||||||
Log("Tried to replace out of bounds message. Index: {}. "
|
log("Tried to replace out of bounds message. Index: {}. "
|
||||||
"Length: {}",
|
"Length: {}",
|
||||||
index, snapshot.getLength());
|
index, snapshot.getLength());
|
||||||
return;
|
return;
|
||||||
|
@ -1169,19 +1169,18 @@ void ChannelView::handleLinkClick(QMouseEvent *event, const Link &link,
|
||||||
userPopup->show();
|
userPopup->show();
|
||||||
|
|
||||||
qDebug() << "Clicked " << user << "s message";
|
qDebug() << "Clicked " << user << "s message";
|
||||||
break;
|
|
||||||
}
|
} break;
|
||||||
|
|
||||||
case Link::Url: {
|
case Link::Url: {
|
||||||
QDesktopServices::openUrl(QUrl(link.value));
|
QDesktopServices::openUrl(QUrl(link.value));
|
||||||
break;
|
} break;
|
||||||
}
|
|
||||||
|
|
||||||
case Link::UserAction: {
|
case Link::UserAction: {
|
||||||
QString value = link.value;
|
QString value = link.value;
|
||||||
value.replace("{user}", layout->getMessage()->loginName);
|
value.replace("{user}", layout->getMessage()->loginName);
|
||||||
this->channel_->sendMessage(value);
|
this->channel_->sendMessage(value);
|
||||||
}
|
} break;
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ AboutPage::AboutPage()
|
||||||
QStringList contributorParts = line.split("|");
|
QStringList contributorParts = line.split("|");
|
||||||
|
|
||||||
if (contributorParts.size() != 4) {
|
if (contributorParts.size() != 4) {
|
||||||
Log("Missing parts in line '{}'", line);
|
log("Missing parts in line '{}'", line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -429,7 +429,7 @@ void Split::openInStreamlink()
|
||||||
try {
|
try {
|
||||||
openStreamlinkForChannel(this->getChannel()->getName());
|
openStreamlinkForChannel(this->getChannel()->getName());
|
||||||
} catch (const Exception &ex) {
|
} catch (const Exception &ex) {
|
||||||
Log("Error in doOpenStreamlink: {}", ex.what());
|
log("Error in doOpenStreamlink: {}", ex.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue