From 7a7b714e789ac371566d1f5cf0f4c199c91263d3 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 4 Jan 2017 15:12:31 +0100 Subject: [PATCH] sdf --- account.cpp | 10 +++ account.h | 39 ++++++++++ asyncexec.h | 14 ++++ chatterino.pro | 14 +++- concurrentmap.cpp | 7 ++ concurrentmap.h | 60 +++++++++++++++ emotes.cpp | 32 ++++++++ emotes.h | 35 +++++++++ ircmanager.cpp | 173 +++++++++++++++++++++++++++++++++++++++++--- ircmanager.h | 21 +++++- lambdaqrunnable.cpp | 2 +- lambdaqrunnable.h | 4 +- lazyloadedimage.cpp | 6 ++ lazyloadedimage.h | 11 +++ twitchemotevalue.h | 27 +++++++ 15 files changed, 437 insertions(+), 18 deletions(-) create mode 100644 account.cpp create mode 100644 account.h create mode 100644 asyncexec.h create mode 100644 concurrentmap.cpp create mode 100644 concurrentmap.h create mode 100644 emotes.cpp create mode 100644 emotes.h create mode 100644 lazyloadedimage.cpp create mode 100644 lazyloadedimage.h create mode 100644 twitchemotevalue.h diff --git a/account.cpp b/account.cpp new file mode 100644 index 000000000..f414a282c --- /dev/null +++ b/account.cpp @@ -0,0 +1,10 @@ +#include "account.h" + +const Account* Account::m_anon = new Account("justinfan123", "", ""); + +Account::Account(QString username, QString oauthToken, QString oauthClient) +{ + m_oauthClient = oauthClient; + m_oauthToken = oauthToken; + m_username = username; +} diff --git a/account.h b/account.h new file mode 100644 index 000000000..7bca5c648 --- /dev/null +++ b/account.h @@ -0,0 +1,39 @@ +#ifndef ACCOUNT_H +#define ACCOUNT_H + +#include "QString" + +class Account +{ +public: + Account(QString username, QString oauthToken, QString oauthClient); + + static const Account* anon() { + return m_anon; + } + + const QString& username() { + return m_username; + } + + const QString& oauthToken() { + return m_oauthToken; + } + + const QString& oauthClient() { + return m_oauthClient; + } + + bool isAnon() { + return m_username.startsWith("justinfan"); + } + +private: + const static Account* m_anon; + + QString m_username; + QString m_oauthClient; + QString m_oauthToken; +}; + +#endif // ACCOUNT_H diff --git a/asyncexec.h b/asyncexec.h new file mode 100644 index 000000000..81142b4cc --- /dev/null +++ b/asyncexec.h @@ -0,0 +1,14 @@ +#ifndef ASYNCEXEC_H +#define ASYNCEXEC_H + +#include "QThreadPool" +#include "QRunnable" +#include "lambdaqrunnable.h" +#include "qcoreapplication.h" + +#define async_start QThreadPool::globalInstance()->start(new LambdaQRunnable( +#define async_end )); +#define async_exec(a) QThreadPool::globalInstance()->start(new LambdaQRunnable([]{ a; })); + + +#endif // ASYNCEXEC_H diff --git a/chatterino.pro b/chatterino.pro index d3701e6b4..e93d6a1da 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -46,7 +46,11 @@ SOURCES += main.cpp\ scrollbar.cpp \ scrollbarhighlight.cpp \ ircmanager.cpp \ - lambdaqrunnable.cpp + lambdaqrunnable.cpp \ + account.cpp \ + emotes.cpp \ + lazyloadedimage.cpp \ + concurrentmap.cpp HEADERS += mainwindow.h \ chatwidget.h \ @@ -66,7 +70,13 @@ HEADERS += mainwindow.h \ scrollbar.h \ scrollbarhighlight.h \ ircmanager.h \ - lambdaqrunnable.h + lambdaqrunnable.h \ + asyncexec.h \ + account.h \ + emotes.h \ + lazyloadedimage.h \ + twitchemotevalue.h \ + concurrentmap.h FORMS += \ dialog.ui diff --git a/concurrentmap.cpp b/concurrentmap.cpp new file mode 100644 index 000000000..fa2365bf6 --- /dev/null +++ b/concurrentmap.cpp @@ -0,0 +1,7 @@ +#include "concurrentmap.h" + +//template +//ConcurrentMap::ConcurrentMap() +//{ + +//} diff --git a/concurrentmap.h b/concurrentmap.h new file mode 100644 index 000000000..613872baf --- /dev/null +++ b/concurrentmap.h @@ -0,0 +1,60 @@ +#ifndef CONCURRENTMAP_H +#define CONCURRENTMAP_H + +#include "QMutex" +#include "QMap" +#include "functional" + +template +class ConcurrentMap +{ +public: + ConcurrentMap() { + mutex = new QMutex(); + map = new QMap(); + } + + bool tryGet(const TKey &name, TValue& value) { + mutex->lock(); + auto a = map->find(name); + if (a == map->end()) { + mutex->unlock(); + value = NULL; + return false; + } + mutex->unlock(); + value = a.value(); + return true; + } + + TValue getOrAdd(const TKey &name, function addLambda) { + mutex->lock(); + auto a = map->find(name); + if (a == map->end()) { + TValue value = addLambda(); + map->insert(name, value); + mutex->unlock(); + return value; + } + mutex->unlock(); + return a.value(); + } + + void clear() { + mutex->lock(); + map->clear(); + mutex->unlock(); + } + + void insert(const TKey &name, const TValue &value) { + mutex->lock(); + map->insert(name, value); + mutex->unlock(); + } + +private: + QMutex* mutex; + QMap* map; +}; + +#endif // CONCURRENTMAP_H diff --git a/emotes.cpp b/emotes.cpp new file mode 100644 index 000000000..b2b84d5d0 --- /dev/null +++ b/emotes.cpp @@ -0,0 +1,32 @@ +#include "emotes.h" + +ConcurrentMap* Emotes::m_twitchEmotes = new ConcurrentMap(); +ConcurrentMap* Emotes::m_bttvEmotes = new ConcurrentMap(); +ConcurrentMap* Emotes::m_ffzEmotes = new ConcurrentMap(); +ConcurrentMap* Emotes::m_chatterinoEmotes = new ConcurrentMap(); +ConcurrentMap* Emotes::m_bttvChannelEmoteFromCaches = new ConcurrentMap(); +ConcurrentMap* Emotes::m_fFzChannelEmoteFromCaches = new ConcurrentMap(); +ConcurrentMap* Emotes::m_twitchEmoteFromCache = new ConcurrentMap(); +ConcurrentMap* Emotes::m_miscImageFromCache = new ConcurrentMap(); + +//QMutex* Emotes::mutexBttvEmote = new QMutex(); +//QMap* Emotes::mapBttvEmote = new QMap(); + +//LazyLoadedImage* Emotes::getBttvEmote(const QString &name) { +// mutexBttvEmote->lock(); +// auto a = mapBttvEmote->find(name); +// if (a == mapBttvEmote->end()) { +// mutexBttvEmote->unlock(); +// return NULL; +// } +// mutexBttvEmote->unlock(); +// return a.value(); +//} + +//void + + +Emotes::Emotes() +{ + +} diff --git a/emotes.h b/emotes.h new file mode 100644 index 000000000..4efe7c2a4 --- /dev/null +++ b/emotes.h @@ -0,0 +1,35 @@ +#ifndef EMOTES_H +#define EMOTES_H + +#include "twitchemotevalue.h" +#include "lazyloadedimage.h" +#include "QMutex" +#include "QMap" +#include "concurrentmap.h" + +class Emotes +{ +public: + static ConcurrentMap& twitchEmotes() { return *m_twitchEmotes ; } + static ConcurrentMap& bttvEmotes() { return *m_bttvEmotes ; } + static ConcurrentMap& ffzEmotes() { return *m_ffzEmotes ; } + static ConcurrentMap& chatterinoEmotes() { return *m_chatterinoEmotes ; } + static ConcurrentMap& bttvChannelEmoteFromCaches() { return *m_bttvChannelEmoteFromCaches; } + static ConcurrentMap& fFzChannelEmoteFromCaches() { return *m_fFzChannelEmoteFromCaches ; } + static ConcurrentMap& twitchEmoteFromCache() { return *m_twitchEmoteFromCache ; } + static ConcurrentMap& miscImageFromCache() { return *m_miscImageFromCache ; } + +private: + Emotes(); + + static ConcurrentMap* m_twitchEmotes; + static ConcurrentMap* m_bttvEmotes; + static ConcurrentMap* m_ffzEmotes; + static ConcurrentMap* m_chatterinoEmotes; + static ConcurrentMap* m_bttvChannelEmoteFromCaches; + static ConcurrentMap* m_fFzChannelEmoteFromCaches; + static ConcurrentMap* m_twitchEmoteFromCache; + static ConcurrentMap* m_miscImageFromCache; +}; + +#endif // EMOTES_H diff --git a/ircmanager.cpp b/ircmanager.cpp index 3d30672d9..a74eaa33b 100644 --- a/ircmanager.cpp +++ b/ircmanager.cpp @@ -2,27 +2,33 @@ #include "ircconnection.h" #include "irccommand.h" #include "future" -#include "QThreadPool" -#include "QRunnable" -#include "lambdaqrunnable.h" -#include "qcoreapplication.h" +#include "QNetworkReply" +#include "asyncexec.h" +#include "qnetworkrequest.h" +#include "QJsonDocument" +#include "QJsonObject" +#include "QJsonArray" -IrcConnection* IrcManager::connection = NULL; -QMutex* IrcManager::connectionMutex = new QMutex(); -long IrcManager::connectionIteration = 0; +Account* IrcManager::account = NULL; +IrcConnection* IrcManager::connection = NULL; +QMutex* IrcManager::connectionMutex = new QMutex(); +long IrcManager::connectionIteration = 0; +const QString IrcManager::defaultClientId = "7ue61iz46fz11y3cugd0l3tawb4taal"; +QNetworkAccessManager* IrcManager::accessManager = new QNetworkAccessManager(); -QObject* IrcManager::parent = new QObject(); +QMap* IrcManager::twitchBlockedUsers = new QMap; +QMutex* IrcManager::twitchBlockedUsersMutex = new QMutex(); IrcManager::IrcManager() { - +// account = Account::anon(); } void IrcManager::connect() { disconnect(); - QThreadPool::globalInstance()->start(new LambdaQRunnable([]{ beginConnecting(); return false; })); + async_exec(beginConnecting()); } void IrcManager::beginConnecting() @@ -38,6 +44,68 @@ void IrcManager::beginConnecting() &IrcConnection::privateMessageReceived, &privateMessageReceived); + if (account->isAnon()) { + // fetch ignored users + QString username = account->username(); + QString oauthClient = account->oauthClient(); + QString oauthToken = account->oauthToken(); + + { + QString nextLink = "https://api.twitch.tv/kraken/users/" + username + + "/blocks?limit=" + 100 + + "&client_id=" + oauthClient; + + QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken)); + QNetworkReply *reply = accessManager->get(req); + + QObject::connect(reply, &QNetworkReply::finished, [=]{ + twitchBlockedUsersMutex->lock(); + twitchBlockedUsers->clear(); + twitchBlockedUsersMutex->unlock(); + + QByteArray data = reply->readAll(); + QJsonDocument jsonDoc(QJsonDocument::fromJson(data)); + QJsonObject root = jsonDoc.object(); + + //nextLink = root.value("_links").toObject().value("next").toString(); + + auto blocks = root.value("blocks").toArray(); + + twitchBlockedUsersMutex->lock(); + for (QJsonValue block : blocks) { + QJsonObject user = block.toObject().value("user").toObject(); + // display_name + twitchBlockedUsers->insert(user.value("name").toString().toLower(), true); + } + twitchBlockedUsersMutex->unlock(); + }); + } + + // fetch available twitch emtoes + { + QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + username + "/emotes?oauth_token=" + oauthToken + "&client_id=" + oauthClient)); + QNetworkReply *reply = accessManager->get(req); + + QObject::connect(reply, &QNetworkReply::finished, [=]{ + QByteArray data = reply->readAll(); + QJsonDocument jsonDoc(QJsonDocument::fromJson(data)); + QJsonObject root = jsonDoc.object(); + + //nextLink = root.value("_links").toObject().value("next").toString(); + + auto blocks = root.value("blocks").toArray(); + + twitchBlockedUsersMutex->lock(); + for (QJsonValue block : blocks) { + QJsonObject user = block.toObject().value("user").toObject(); + // display_name + twitchBlockedUsers->insert(user.value("name").toString().toLower(), true); + } + twitchBlockedUsersMutex->unlock(); + }); + } + } + c->setHost("irc.chat.twitch.tv"); c->setPort(6667); @@ -77,10 +145,93 @@ void IrcManager::disconnect() void IrcManager::messageReceived(IrcMessage *message) { -// qInfo(message->()); + qInfo(message->command().toStdString().c_str()); + +// if (message->command() == "") } void IrcManager::privateMessageReceived(IrcPrivateMessage *message) { qInfo(message->content().toStdString().c_str()); } + +bool IrcManager::isTwitchBlockedUser(QString const &username) +{ + twitchBlockedUsersMutex->lock(); + + auto iterator = twitchBlockedUsers->find(username); + + if (iterator == twitchBlockedUsers->end()) { + twitchBlockedUsersMutex->unlock(); + return false; + } + + twitchBlockedUsersMutex->unlock(); + return true; +} + +bool IrcManager::tryAddIgnoredUser(QString const &username, QString& errorMessage) +{ + QUrl url("https://api.twitch.tv/kraken/users/" + account->username() + + "/blocks/" + username + + "?oauth_token=" + account->oauthToken() + + "&client_id=" + account->oauthClient()); + + QNetworkRequest request(url); + auto reply = accessManager->put(request, QByteArray()); + reply->waitForReadyRead(10000); + + if (reply->error() == QNetworkReply::NoError) + { + twitchBlockedUsersMutex->lock(); + twitchBlockedUsers->insert(username, true); + twitchBlockedUsersMutex->unlock(); + + delete reply; + return true; + } + + errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString(); + return false; +} + +void IrcManager::addIgnoredUser(QString const &username) +{ + QString errorMessage; + if (tryAddIgnoredUser(username, errorMessage)) { +#warning "xD" + } +} + +bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString& errorMessage) +{ + QUrl url("https://api.twitch.tv/kraken/users/" + account->username() + + "/blocks/" + username + + "?oauth_token=" + account->oauthToken() + + "&client_id=" + account->oauthClient()); + + QNetworkRequest request(url); + auto reply = accessManager->deleteResource(request); + reply->waitForReadyRead(10000); + + if (reply->error() == QNetworkReply::NoError) + { + twitchBlockedUsersMutex->lock(); + twitchBlockedUsers->remove(username); + twitchBlockedUsersMutex->unlock(); + + delete reply; + return true; + } + + errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString(); + return false; +} + +void IrcManager::removeIgnoredUser(QString const &username) +{ + QString errorMessage; + if (tryRemoveIgnoredUser(username, errorMessage)) { +#warning "xD" + } +} diff --git a/ircmanager.h b/ircmanager.h index f64baf339..ec9e525d0 100644 --- a/ircmanager.h +++ b/ircmanager.h @@ -5,6 +5,10 @@ #include "IrcMessage" #include "QMutex" +#include "QString" +#include "QMap" +#include "account.h" +#include "qnetworkaccessmanager.h" class IrcManager { @@ -12,12 +16,25 @@ public: static void connect(); static void disconnect(); + static const QString defaultClientId; + + bool isTwitchBlockedUser(QString const &username); + bool tryAddIgnoredUser(QString const &username, QString& errorMessage); + void addIgnoredUser(QString const &username); + bool tryRemoveIgnoredUser(QString const &username, QString& errorMessage); + void removeIgnoredUser(QString const &username); + + static Account* account; + private: IrcManager(); - static void beginConnecting(); + static QMap* twitchBlockedUsers; + static QMutex* twitchBlockedUsersMutex; - static QObject* parent; + static QNetworkAccessManager* accessManager; + + static void beginConnecting(); static IrcConnection* connection; static QMutex* connectionMutex; diff --git a/lambdaqrunnable.cpp b/lambdaqrunnable.cpp index 4051b4afd..9a7ccc22e 100644 --- a/lambdaqrunnable.cpp +++ b/lambdaqrunnable.cpp @@ -1,6 +1,6 @@ #include "lambdaqrunnable.h" -LambdaQRunnable::LambdaQRunnable(std::function action) +LambdaQRunnable::LambdaQRunnable(std::function action) { this->action = action; } diff --git a/lambdaqrunnable.h b/lambdaqrunnable.h index 289097c0f..d4af93e88 100644 --- a/lambdaqrunnable.h +++ b/lambdaqrunnable.h @@ -7,12 +7,12 @@ class LambdaQRunnable : public QRunnable { public: - LambdaQRunnable(std::function action); + LambdaQRunnable(std::function action); void run(); private: - std::function action; + std::function action; }; #endif // LAMBDAQRUNNABLE_H diff --git a/lazyloadedimage.cpp b/lazyloadedimage.cpp new file mode 100644 index 000000000..0b8092a0c --- /dev/null +++ b/lazyloadedimage.cpp @@ -0,0 +1,6 @@ +#include "lazyloadedimage.h" + +LazyLoadedImage::LazyLoadedImage() +{ + +} diff --git a/lazyloadedimage.h b/lazyloadedimage.h new file mode 100644 index 000000000..cb4e5e993 --- /dev/null +++ b/lazyloadedimage.h @@ -0,0 +1,11 @@ +#ifndef LAZYLOADEDIMAGE_H +#define LAZYLOADEDIMAGE_H + + +class LazyLoadedImage +{ +public: + LazyLoadedImage(); +}; + +#endif // LAZYLOADEDIMAGE_H \ No newline at end of file diff --git a/twitchemotevalue.h b/twitchemotevalue.h new file mode 100644 index 000000000..9c0027183 --- /dev/null +++ b/twitchemotevalue.h @@ -0,0 +1,27 @@ +#ifndef TWITCHEMOTEVALUE_H +#define TWITCHEMOTEVALUE_H + +#include "QString" + +struct TwitchEmoteValue +{ +public: + int set() { + return m_set; + } + + int id() { + return m_id; + } + + QString channelName() { + return m_channelName; + } + +private: + int m_set; + int m_id; + QString m_channelName; +}; + +#endif // TWITCHEMOTEVALUE_H