This commit is contained in:
2017-12-17 00:07:03 +01:00
commit 269a5500d8
22 changed files with 594 additions and 380 deletions

View file

@ -169,7 +169,8 @@ HEADERS += \
src/widgets/window.hpp \ src/widgets/window.hpp \
src/widgets/splitcontainer.hpp \ src/widgets/splitcontainer.hpp \
src/widgets/helper/droppreview.hpp \ src/widgets/helper/droppreview.hpp \
src/widgets/helper/splitcolumn.hpp src/widgets/helper/splitcolumn.hpp \
src/util/irchelpers.hpp
PRECOMPILED_HEADER = PRECOMPILED_HEADER =

View file

@ -1,7 +1,6 @@
#include "accountmanager.hpp" #include "accountmanager.hpp"
#include "common.hpp" #include "common.hpp"
#include "debug/log.hpp"
#include <pajlada/settings/setting.hpp>
namespace chatterino { namespace chatterino {
@ -19,18 +18,84 @@ inline QString getEnvString(const char *target)
} // namespace } // namespace
AccountManager::AccountManager() std::shared_ptr<twitch::TwitchUser> TwitchAccountManager::getCurrent()
: currentUser("/accounts/current", "")
, twitchAnonymousUser("justinfan64537", "", "")
{ {
if (!this->currentUser) {
return this->anonymousUser;
}
return this->currentUser;
}
std::vector<QString> TwitchAccountManager::getUsernames() const
{
std::vector<QString> userNames;
std::lock_guard<std::mutex> lock(this->mutex);
for (const auto &user : this->users) {
userNames.push_back(user->getUserName());
}
return userNames;
}
std::shared_ptr<twitch::TwitchUser> TwitchAccountManager::findUserByUsername(
const QString &username) const
{
std::lock_guard<std::mutex> lock(this->mutex);
for (const auto &user : this->users) {
if (username.compare(user->getUserName(), Qt::CaseInsensitive) == 0) {
return user;
}
}
return nullptr;
}
bool TwitchAccountManager::userExists(const QString &username) const
{
return this->findUserByUsername(username) != nullptr;
}
bool TwitchAccountManager::addUser(std::shared_ptr<twitch::TwitchUser> user)
{
if (this->userExists(user->getNickName())) {
// User already exists in user list
return false;
}
std::lock_guard<std::mutex> lock(this->mutex);
this->users.push_back(user);
return true;
}
AccountManager::AccountManager()
{
this->Twitch.anonymousUser.reset(new twitch::TwitchUser("justinfan64537", "", ""));
this->Twitch.currentUsername.getValueChangedSignal().connect([this](const auto &newValue) {
QString newUsername(QString::fromStdString(newValue));
auto user = this->Twitch.findUserByUsername(newUsername);
if (user) {
debug::Log("[AccountManager:currentUsernameChanged] User successfully updated to {}",
newUsername);
// XXX: Should we set the user regardless if the username is found or not?
// I can see the logic in setting it to nullptr if the `currentUsername` value has been
// set to "" or an invalid username
this->Twitch.currentUser = user;
this->Twitch.userChanged.invoke();
}
});
} }
void AccountManager::load() void AccountManager::load()
{ {
auto keys = pajlada::Settings::SettingManager::getObjectKeys("/accounts"); auto keys = pajlada::Settings::SettingManager::getObjectKeys("/accounts");
bool first = true;
for (const auto &uid : keys) { for (const auto &uid : keys) {
if (uid == "current") { if (uid == "current") {
continue; continue;
@ -49,74 +114,20 @@ void AccountManager::load()
continue; continue;
} }
if (first) { auto user =
this->setCurrentTwitchUser(qS(username)); std::make_shared<twitch::TwitchUser>(qS(username), qS(oauthToken), qS(clientID));
first = false;
}
twitch::TwitchUser user(qS(username), qS(oauthToken), qS(clientID)); this->Twitch.addUser(user);
this->addTwitchUser(user);
printf("Adding user %s(%s)\n", username.c_str(), userID.c_str()); printf("Adding user %s(%s)\n", username.c_str(), userID.c_str());
} }
}
twitch::TwitchUser &AccountManager::getTwitchAnon() auto currentUser = this->Twitch.findUserByUsername(
{ QString::fromStdString(this->Twitch.currentUsername.getValue()));
return this->twitchAnonymousUser; if (currentUser) {
} this->Twitch.currentUser = currentUser;
this->Twitch.userChanged.invoke();
twitch::TwitchUser &AccountManager::getTwitchUser()
{
std::lock_guard<std::mutex> lock(this->twitchUsersMutex);
if (this->twitchUsers.size() == 0) {
return this->getTwitchAnon();
} }
QString currentUsername = QString::fromStdString(this->currentUser);
for (auto &user : this->twitchUsers) {
if (user.getUserName() == currentUsername) {
return user;
}
}
return this->twitchUsers.front();
}
void AccountManager::setCurrentTwitchUser(const QString &username)
{
this->currentUser.setValue(username.toStdString());
}
std::vector<twitch::TwitchUser> AccountManager::getTwitchUsers()
{
std::lock_guard<std::mutex> lock(this->twitchUsersMutex);
return std::vector<twitch::TwitchUser>(this->twitchUsers);
}
bool AccountManager::removeTwitchUser(const QString &userName)
{
std::lock_guard<std::mutex> lock(this->twitchUsersMutex);
for (auto it = this->twitchUsers.begin(); it != this->twitchUsers.end(); it++) {
if ((*it).getUserName() == userName) {
this->twitchUsers.erase(it);
return true;
}
}
return false;
}
void AccountManager::addTwitchUser(const twitch::TwitchUser &user)
{
std::lock_guard<std::mutex> lock(this->twitchUsersMutex);
this->twitchUsers.push_back(user);
} }
} // namespace chatterino } // namespace chatterino

View file

@ -9,6 +9,34 @@
namespace chatterino { namespace chatterino {
class AccountManager;
class TwitchAccountManager
{
public:
// Returns the current twitchUsers, or the anonymous user if we're not currently logged in
std::shared_ptr<twitch::TwitchUser> getCurrent();
std::vector<QString> getUsernames() const;
std::shared_ptr<twitch::TwitchUser> findUserByUsername(const QString &username) const;
bool userExists(const QString &username) const;
pajlada::Settings::Setting<std::string> currentUsername = {"/accounts/current", ""};
pajlada::Signals::NoArgSignal userChanged;
private:
bool addUser(std::shared_ptr<twitch::TwitchUser> user);
std::shared_ptr<twitch::TwitchUser> currentUser;
std::shared_ptr<twitch::TwitchUser> anonymousUser;
std::vector<std::shared_ptr<twitch::TwitchUser>> users;
mutable std::mutex mutex;
friend class AccountManager;
};
class AccountManager class AccountManager
{ {
public: public:
@ -20,30 +48,10 @@ public:
void load(); void load();
twitch::TwitchUser &getTwitchAnon(); TwitchAccountManager Twitch;
// Returns first user from twitchUsers, or twitchAnonymousUser if twitchUsers is empty
twitch::TwitchUser &getTwitchUser();
// Return a copy of the current available twitch users
std::vector<twitch::TwitchUser> getTwitchUsers();
// Remove twitch user with the given username
bool removeTwitchUser(const QString &userName);
void setCurrentTwitchUser(const QString &username);
// Add twitch user to the list of available twitch users
void addTwitchUser(const twitch::TwitchUser &user);
private: private:
AccountManager(); AccountManager();
pajlada::Settings::Setting<std::string> currentUser;
twitch::TwitchUser twitchAnonymousUser;
std::vector<twitch::TwitchUser> twitchUsers;
std::mutex twitchUsersMutex;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -18,7 +18,6 @@ Application::Application()
, channelManager(this->windowManager, this->emoteManager, this->ircManager) , channelManager(this->windowManager, this->emoteManager, this->ircManager)
, ircManager(this->channelManager, this->resources, this->emoteManager, this->windowManager) , ircManager(this->channelManager, this->resources, this->emoteManager, this->windowManager)
{ {
// TODO(pajlada): Get rid of all singletons
logging::init(); logging::init();
SettingsManager::getInstance().load(); SettingsManager::getInstance().load();
@ -29,8 +28,6 @@ Application::Application()
AccountManager::getInstance().load(); AccountManager::getInstance().load();
this->ircManager.setUser(AccountManager::getInstance().getTwitchUser());
// XXX // XXX
SettingsManager::getInstance().updateWordTypeMask(); SettingsManager::getInstance().updateWordTypeMask();

View file

@ -25,3 +25,30 @@ public:
private: private:
std::function<void()> action; std::function<void()> action;
}; };
// Taken from
// https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style
// Qt 5/4 - preferred, has least allocations
template <typename F>
static void postToThread(F &&fun, QObject *obj = qApp)
{
struct Event : public QEvent {
using Fun = typename std::decay<F>::type;
Fun fun;
Event(Fun &&fun)
: QEvent(QEvent::None)
, fun(std::move(fun))
{
}
Event(const Fun &fun)
: QEvent(QEvent::None)
, fun(fun)
{
}
~Event()
{
fun();
}
};
QCoreApplication::postEvent(obj, new Event(std::forward<F>(fun)));
}

View file

@ -136,4 +136,14 @@ WindowManager &ChannelManager::getWindowManager()
return this->windowManager; return this->windowManager;
} }
void ChannelManager::doOnAll(std::function<void(std::shared_ptr<TwitchChannel>)> func)
{
for (const auto &channel : this->twitchChannels) {
func(std::get<0>(channel));
}
func(this->whispersChannel);
func(this->mentionsChannel);
}
} // namespace chatterino } // namespace chatterino

View file

@ -32,6 +32,8 @@ public:
EmoteManager &getEmoteManager(); EmoteManager &getEmoteManager();
WindowManager &getWindowManager(); WindowManager &getWindowManager();
void doOnAll(std::function<void(std::shared_ptr<twitch::TwitchChannel>)> func);
// Special channels // Special channels
const std::shared_ptr<twitch::TwitchChannel> whispersChannel; const std::shared_ptr<twitch::TwitchChannel> whispersChannel;
const std::shared_ptr<twitch::TwitchChannel> mentionsChannel; const std::shared_ptr<twitch::TwitchChannel> mentionsChannel;

View file

@ -505,7 +505,6 @@ void EmoteManager::loadFFZEmotes()
EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteName) EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteName)
{ {
return _twitchEmoteFromCache.getOrAdd(id, [this, &emoteName, &id] { return _twitchEmoteFromCache.getOrAdd(id, [this, &emoteName, &id] {
qDebug() << "added twitch emote: " << id;
qreal scale; qreal scale;
QString url = getTwitchEmoteLink(id, scale); QString url = getTwitchEmoteLink(id, scale);
return new LazyLoadedImage(*this, this->windowManager, url, scale, emoteName, return new LazyLoadedImage(*this, this->windowManager, url, scale, emoteName,

View file

@ -3,6 +3,7 @@
#include "asyncexec.hpp" #include "asyncexec.hpp"
#include "channel.hpp" #include "channel.hpp"
#include "channelmanager.hpp" #include "channelmanager.hpp"
#include "debug/log.hpp"
#include "emotemanager.hpp" #include "emotemanager.hpp"
#include "messages/messageparseargs.hpp" #include "messages/messageparseargs.hpp"
#include "twitch/twitchmessagebuilder.hpp" #include "twitch/twitchmessagebuilder.hpp"
@ -12,7 +13,6 @@
#include "windowmanager.hpp" #include "windowmanager.hpp"
#include <irccommand.h> #include <irccommand.h>
#include <ircconnection.h>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@ -31,46 +31,65 @@ IrcManager::IrcManager(ChannelManager &_channelManager, Resources &_resources,
, resources(_resources) , resources(_resources)
, emoteManager(_emoteManager) , emoteManager(_emoteManager)
, windowManager(_windowManager) , windowManager(_windowManager)
, account(AccountManager::getInstance().getTwitchAnon())
, currentUser("/accounts/current")
{ {
this->currentUser.getValueChangedSignal().connect([](const auto &newUsername) { AccountManager::getInstance().Twitch.userChanged.connect([this]() {
// TODO: Implement this->setUser(AccountManager::getInstance().Twitch.getCurrent());
qDebug() << "Current user changed, fetch new credentials and reconnect";
debug::Log("[IrcManager] Reconnecting to Twitch IRC as new user {}",
this->account->getUserName());
postToThread([this] { this->connect(); });
}); });
// Initialize the connections
this->writeConnection.reset(new Communi::IrcConnection);
this->writeConnection->moveToThread(QCoreApplication::instance()->thread());
QObject::connect(this->writeConnection.get(), &Communi::IrcConnection::messageReceived, this,
&IrcManager::writeConnectionMessageReceived);
this->readConnection.reset(new Communi::IrcConnection);
this->readConnection->moveToThread(QCoreApplication::instance()->thread());
// Listen to read connection message signals
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::messageReceived, this,
&IrcManager::messageReceived);
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::privateMessageReceived,
this, &IrcManager::privateMessageReceived);
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::connected, this,
&IrcManager::onConnected);
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::disconnected, this,
&IrcManager::onDisconnected);
} }
const twitch::TwitchUser &IrcManager::getUser() const void IrcManager::setUser(std::shared_ptr<twitch::TwitchUser> newAccount)
{ {
return this->account; this->account = newAccount;
}
void IrcManager::setUser(const twitch::TwitchUser &account)
{
this->account = account;
} }
void IrcManager::connect() void IrcManager::connect()
{ {
disconnect(); this->disconnect();
async_exec([this] { beginConnecting(); }); this->initializeConnection(this->writeConnection, false);
this->initializeConnection(this->readConnection, true);
// XXX(pajlada): Disabled the async_exec for now, because if we happen to run the
// `beginConnecting` function in a different thread than last time, we won't be able to connect
// because we can't clean up the previous connection properly
// async_exec([this] { beginConnecting(); });
this->beginConnecting();
} }
Communi::IrcConnection *IrcManager::createConnection(bool doRead) void IrcManager::initializeConnection(const std::unique_ptr<Communi::IrcConnection> &connection,
bool isReadConnection)
{ {
Communi::IrcConnection *connection = new Communi::IrcConnection; assert(this->account);
if (doRead) { QString username = this->account->getUserName();
QObject::connect(connection, &Communi::IrcConnection::messageReceived, this, QString oauthClient = this->account->getOAuthClient();
&IrcManager::messageReceived); QString oauthToken = this->account->getOAuthToken();
QObject::connect(connection, &Communi::IrcConnection::privateMessageReceived, this,
&IrcManager::privateMessageReceived);
}
QString username = this->account.getUserName();
QString oauthClient = this->account.getOAuthClient();
QString oauthToken = this->account.getOAuthToken();
if (!oauthToken.startsWith("oauth:")) { if (!oauthToken.startsWith("oauth:")) {
oauthToken.prepend("oauth:"); oauthToken.prepend("oauth:");
} }
@ -79,23 +98,27 @@ Communi::IrcConnection *IrcManager::createConnection(bool doRead)
connection->setNickName(username); connection->setNickName(username);
connection->setRealName(username); connection->setRealName(username);
if (!this->account.isAnon()) { if (!this->account->isAnon()) {
connection->setPassword(oauthToken); connection->setPassword(oauthToken);
this->refreshIgnoredUsers(username, oauthClient, oauthToken); this->refreshIgnoredUsers(username, oauthClient, oauthToken);
} }
if (doRead) { if (isReadConnection) {
connection->sendCommand( connection->sendCommand(
Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership")); Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership"));
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands")); connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags")); connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags"));
} else {
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags"));
connection->sendCommand(
Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership"));
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
} }
connection->setHost("irc.chat.twitch.tv"); connection->setHost("irc.chat.twitch.tv");
connection->setPort(6667); connection->setPort(6667);
return connection;
} }
void IrcManager::refreshIgnoredUsers(const QString &username, const QString &oauthClient, void IrcManager::refreshIgnoredUsers(const QString &username, const QString &oauthClient,
@ -136,43 +159,23 @@ void IrcManager::refreshIgnoredUsers(const QString &username, const QString &oau
void IrcManager::beginConnecting() void IrcManager::beginConnecting()
{ {
uint32_t generation = ++this->connectionGeneration;
Communi::IrcConnection *_writeConnection = this->createConnection(false);
Communi::IrcConnection *_readConnection = this->createConnection(true);
std::lock_guard<std::mutex> locker(this->connectionMutex); std::lock_guard<std::mutex> locker(this->connectionMutex);
if (generation == this->connectionGeneration) {
this->writeConnection = std::shared_ptr<Communi::IrcConnection>(_writeConnection);
this->readConnection = std::shared_ptr<Communi::IrcConnection>(_readConnection);
this->writeConnection->moveToThread(QCoreApplication::instance()->thread());
this->readConnection->moveToThread(QCoreApplication::instance()->thread());
for (auto &channel : this->channelManager.getItems()) { for (auto &channel : this->channelManager.getItems()) {
this->writeConnection->sendRaw("JOIN #" + channel->name); this->writeConnection->sendRaw("JOIN #" + channel->name);
this->readConnection->sendRaw("JOIN #" + channel->name); this->readConnection->sendRaw("JOIN #" + channel->name);
} }
this->writeConnection->open(); this->writeConnection->open();
this->readConnection->open(); this->readConnection->open();
} else {
delete _writeConnection;
delete _readConnection;
}
} }
void IrcManager::disconnect() void IrcManager::disconnect()
{ {
this->connectionMutex.lock(); std::lock_guard<std::mutex> locker(this->connectionMutex);
auto _readConnection = this->readConnection; this->readConnection->close();
auto _writeConnection = this->writeConnection; this->writeConnection->close();
this->readConnection.reset();
this->writeConnection.reset();
this->connectionMutex.unlock();
} }
void IrcManager::sendMessage(const QString &channelName, const QString &message) void IrcManager::sendMessage(const QString &channelName, const QString &message)
@ -184,29 +187,6 @@ void IrcManager::sendMessage(const QString &channelName, const QString &message)
} }
this->connectionMutex.unlock(); this->connectionMutex.unlock();
// DEBUGGING
/*
Communi::IrcPrivateMessage msg(this->readConnection.get());
QStringList params{"#pajlada", message};
qDebug() << params;
if (message == "COMIC SANS LOL") {
FontManager::getInstance().currentFontFamily = "Comic Sans MS";
} else if (message == "ARIAL LOL") {
FontManager::getInstance().currentFontFamily = "Arial";
} else if (message == "WINGDINGS LOL") {
FontManager::getInstance().currentFontFamily = "Wingdings";
}
msg.setParameters(params);
msg.setPrefix("pajlada!pajlada@pajlada");
this->privateMessageReceived(&msg);
*/
} }
void IrcManager::joinChannel(const QString &channelName) void IrcManager::joinChannel(const QString &channelName)
@ -271,6 +251,18 @@ void IrcManager::messageReceived(Communi::IrcMessage *message)
this->handleUserNoticeMessage(message); this->handleUserNoticeMessage(message);
} else if (command == "MODE") { } else if (command == "MODE") {
this->handleModeMessage(message); this->handleModeMessage(message);
} else if (command == "NOTICE") {
this->handleNoticeMessage(static_cast<Communi::IrcNoticeMessage *>(message));
}
}
void IrcManager::writeConnectionMessageReceived(Communi::IrcMessage *message)
{
switch (message->type()) {
case Communi::IrcMessage::Type::Notice: {
this->handleWriteConnectionNoticeMessage(
static_cast<Communi::IrcNoticeMessage *>(message));
} break;
} }
} }
@ -292,17 +284,59 @@ void IrcManager::handleRoomStateMessage(Communi::IrcMessage *message)
void IrcManager::handleClearChatMessage(Communi::IrcMessage *message) void IrcManager::handleClearChatMessage(Communi::IrcMessage *message)
{ {
// do nothing assert(message->parameters().length() >= 1);
auto rawChannelName = message->parameter(0);
assert(rawChannelName.length() >= 2);
auto trimmedChannelName = rawChannelName.mid(1);
auto c = this->channelManager.getTwitchChannel(trimmedChannelName);
if (!c) {
debug::Log("[IrcManager:handleClearChatMessage] Channel {} not found in channel manager",
trimmedChannelName);
return;
}
if (message->parameters().length() == 1) {
std::shared_ptr<Message> msg(
Message::createSystemMessage("Chat has been cleared by a moderator."));
c->addMessage(msg);
return;
}
assert(message->parameters().length() >= 2);
QString username = message->parameter(1);
QString durationInSeconds, reason;
QVariant v = message->tag("ban-duration");
if (v.isValid()) {
durationInSeconds = v.toString();
}
v = message->tag("ban-reason");
if (v.isValid()) {
reason = v.toString();
}
std::shared_ptr<Message> msg(
Message::createTimeoutMessage(username, durationInSeconds, reason));
c->addMessage(msg);
} }
void IrcManager::handleUserStateMessage(Communi::IrcMessage *message) void IrcManager::handleUserStateMessage(Communi::IrcMessage *message)
{ {
// do nothing // TODO: Implement
} }
void IrcManager::handleWhisperMessage(Communi::IrcMessage *message) void IrcManager::handleWhisperMessage(Communi::IrcMessage *message)
{ {
// do nothing // TODO: Implement
} }
void IrcManager::handleUserNoticeMessage(Communi::IrcMessage *message) void IrcManager::handleUserNoticeMessage(Communi::IrcMessage *message)
@ -312,16 +346,15 @@ void IrcManager::handleUserNoticeMessage(Communi::IrcMessage *message)
void IrcManager::handleModeMessage(Communi::IrcMessage *message) void IrcManager::handleModeMessage(Communi::IrcMessage *message)
{ {
auto channel = channelManager.getTwitchChannel(message->parameter(0).remove(0,1)); auto channel = channelManager.getTwitchChannel(message->parameter(0).remove(0, 1));
if(message->parameter(1) == "+o") if (message->parameter(1) == "+o") {
{
channel->modList.append(message->parameter(2)); channel->modList.append(message->parameter(2));
} else if(message->parameter(1) == "-o") } else if (message->parameter(1) == "-o") {
{
channel->modList.append(message->parameter(2)); channel->modList.append(message->parameter(2));
} }
} }
// XXX: This does not fit in IrcManager
bool IrcManager::isTwitchBlockedUser(QString const &username) bool IrcManager::isTwitchBlockedUser(QString const &username)
{ {
QMutexLocker locker(&this->twitchBlockedUsersMutex); QMutexLocker locker(&this->twitchBlockedUsersMutex);
@ -331,11 +364,14 @@ bool IrcManager::isTwitchBlockedUser(QString const &username)
return iterator != this->twitchBlockedUsers.end(); return iterator != this->twitchBlockedUsers.end();
} }
// XXX: This does not fit in IrcManager
bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage) bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
{ {
QUrl url("https://api.twitch.tv/kraken/users/" + this->account.getUserName() + "/blocks/" + assert(this->account);
username + "?oauth_token=" + this->account.getOAuthToken() +
"&client_id=" + this->account.getOAuthClient()); QUrl url("https://api.twitch.tv/kraken/users/" + this->account->getUserName() + "/blocks/" +
username + "?oauth_token=" + this->account->getOAuthToken() +
"&client_id=" + this->account->getOAuthClient());
QNetworkRequest request(url); QNetworkRequest request(url);
auto reply = this->networkAccessManager.put(request, QByteArray()); auto reply = this->networkAccessManager.put(request, QByteArray());
@ -352,9 +388,11 @@ bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessag
reply->deleteLater(); reply->deleteLater();
errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString(); errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString();
return false; return false;
} }
// XXX: This does not fit in IrcManager
void IrcManager::addIgnoredUser(QString const &username) void IrcManager::addIgnoredUser(QString const &username)
{ {
QString errorMessage; QString errorMessage;
@ -363,11 +401,14 @@ void IrcManager::addIgnoredUser(QString const &username)
} }
} }
// XXX: This does not fit in IrcManager
bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage) bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
{ {
QUrl url("https://api.twitch.tv/kraken/users/" + this->account.getUserName() + "/blocks/" + assert(this->account);
username + "?oauth_token=" + this->account.getOAuthToken() +
"&client_id=" + this->account.getOAuthClient()); QUrl url("https://api.twitch.tv/kraken/users/" + this->account->getUserName() + "/blocks/" +
username + "?oauth_token=" + this->account->getOAuthToken() +
"&client_id=" + this->account->getOAuthClient());
QNetworkRequest request(url); QNetworkRequest request(url);
auto reply = this->networkAccessManager.deleteResource(request); auto reply = this->networkAccessManager.deleteResource(request);
@ -384,9 +425,11 @@ bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMes
reply->deleteLater(); reply->deleteLater();
errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString(); errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString();
return false; return false;
} }
// XXX: This does not fit in IrcManager
void IrcManager::removeIgnoredUser(QString const &username) void IrcManager::removeIgnoredUser(QString const &username)
{ {
QString errorMessage; QString errorMessage;
@ -395,4 +438,79 @@ void IrcManager::removeIgnoredUser(QString const &username)
} }
} }
void IrcManager::handleNoticeMessage(Communi::IrcNoticeMessage *message)
{
auto rawChannelName = message->target();
assert(rawChannelName.length() >= 2);
auto trimmedChannelName = rawChannelName.mid(1);
auto c = this->channelManager.getTwitchChannel(trimmedChannelName);
if (!c) {
debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager",
trimmedChannelName);
return;
}
std::shared_ptr<Message> msg(Message::createSystemMessage(message->content()));
c->addMessage(msg);
}
void IrcManager::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message)
{
auto rawChannelName = message->target();
assert(rawChannelName.length() >= 2);
auto trimmedChannelName = rawChannelName.mid(1);
auto c = this->channelManager.getTwitchChannel(trimmedChannelName);
if (!c) {
debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager",
trimmedChannelName);
return;
}
QVariant v = message->tag("msg-id");
if (!v.isValid()) {
return;
}
QString msg_id = v.toString();
static QList<QString> idsToSkip = {"timeout_success", "ban_success"};
if (idsToSkip.contains(msg_id)) {
// Already handled in the read-connection
return;
}
std::shared_ptr<Message> msg(Message::createSystemMessage(message->content()));
c->addMessage(msg);
}
void IrcManager::onConnected()
{
std::shared_ptr<Message> msg(Message::createSystemMessage("connected to chat"));
this->channelManager.doOnAll([msg](std::shared_ptr<twitch::TwitchChannel> channel) {
assert(channel);
channel->addMessage(msg);
});
}
void IrcManager::onDisconnected()
{
std::shared_ptr<Message> msg(Message::createSystemMessage("disconnected from chat"));
this->channelManager.doOnAll([msg](std::shared_ptr<twitch::TwitchChannel> channel) {
assert(channel);
channel->addMessage(msg);
});
}
} // namespace chatterino } // namespace chatterino

View file

@ -5,6 +5,7 @@
#include "messages/message.hpp" #include "messages/message.hpp"
#include "twitch/twitchuser.hpp" #include "twitch/twitchuser.hpp"
#include <ircconnection.h>
#include <IrcMessage> #include <IrcMessage>
#include <QMap> #include <QMap>
#include <QMutex> #include <QMutex>
@ -44,8 +45,7 @@ public:
void joinChannel(const QString &channelName); void joinChannel(const QString &channelName);
void partChannel(const QString &channelName); void partChannel(const QString &channelName);
const twitch::TwitchUser &getUser() const; void setUser(std::shared_ptr<twitch::TwitchUser> newAccount);
void setUser(const twitch::TwitchUser &account);
pajlada::Signals::Signal<Communi::IrcPrivateMessage *> onPrivateMessage; pajlada::Signals::Signal<Communi::IrcPrivateMessage *> onPrivateMessage;
@ -56,26 +56,20 @@ public:
private: private:
// variables // variables
twitch::TwitchUser account; std::shared_ptr<twitch::TwitchUser> account = nullptr;
pajlada::Settings::Setting<std::string> currentUser; std::unique_ptr<Communi::IrcConnection> writeConnection = nullptr;
std::unique_ptr<Communi::IrcConnection> readConnection = nullptr;
std::shared_ptr<Communi::IrcConnection> writeConnection = nullptr;
public:
std::shared_ptr<Communi::IrcConnection> readConnection = nullptr;
private:
std::mutex connectionMutex; std::mutex connectionMutex;
uint32_t connectionGeneration = 0;
QMap<QString, bool> twitchBlockedUsers; QMap<QString, bool> twitchBlockedUsers;
QMutex twitchBlockedUsersMutex; QMutex twitchBlockedUsersMutex;
QNetworkAccessManager networkAccessManager; QNetworkAccessManager networkAccessManager;
// methods void initializeConnection(const std::unique_ptr<Communi::IrcConnection> &connection,
Communi::IrcConnection *createConnection(bool doRead); bool isReadConnection);
void refreshIgnoredUsers(const QString &username, const QString &oauthClient, void refreshIgnoredUsers(const QString &username, const QString &oauthClient,
const QString &oauthToken); const QString &oauthToken);
@ -85,12 +79,19 @@ private:
void privateMessageReceived(Communi::IrcPrivateMessage *message); void privateMessageReceived(Communi::IrcPrivateMessage *message);
void messageReceived(Communi::IrcMessage *message); void messageReceived(Communi::IrcMessage *message);
void writeConnectionMessageReceived(Communi::IrcMessage *message);
void handleRoomStateMessage(Communi::IrcMessage *message); void handleRoomStateMessage(Communi::IrcMessage *message);
void handleClearChatMessage(Communi::IrcMessage *message); void handleClearChatMessage(Communi::IrcMessage *message);
void handleUserStateMessage(Communi::IrcMessage *message); void handleUserStateMessage(Communi::IrcMessage *message);
void handleWhisperMessage(Communi::IrcMessage *message); void handleWhisperMessage(Communi::IrcMessage *message);
void handleUserNoticeMessage(Communi::IrcMessage *message); void handleUserNoticeMessage(Communi::IrcMessage *message);
void handleModeMessage(Communi::IrcMessage *message); void handleModeMessage(Communi::IrcMessage *message);
void handleNoticeMessage(Communi::IrcNoticeMessage *message);
void handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message);
void onConnected();
void onDisconnected();
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -7,6 +7,7 @@
#include "ircmanager.hpp" #include "ircmanager.hpp"
#include "messages/link.hpp" #include "messages/link.hpp"
#include "resources.hpp" #include "resources.hpp"
#include "util/irchelpers.hpp"
#include <ctime> #include <ctime>
#include <list> #include <list>
@ -70,5 +71,83 @@ const QString &Message::getId() const
return this->id; return this->id;
} }
namespace {
void AddCurrentTimestamp(Message *message)
{
std::time_t t;
time(&t);
char timeStampBuffer[69];
// Add word for timestamp with no seconds
strftime(timeStampBuffer, 69, "%H:%M", localtime(&t));
QString timestampNoSeconds(timeStampBuffer);
message->getWords().push_back(Word(timestampNoSeconds, Word::TimestampNoSeconds,
MessageColor(MessageColor::System), QString(), QString()));
// Add word for timestamp with seconds
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&t));
QString timestampWithSeconds(timeStampBuffer);
message->getWords().push_back(Word(timestampWithSeconds, Word::TimestampWithSeconds,
MessageColor(MessageColor::System), QString(), QString()));
}
} // namespace
/// Static
Message *Message::createSystemMessage(const QString &text)
{
Message *message = new Message;
AddCurrentTimestamp(message);
Word word(text, Word::Type::Default, MessageColor(MessageColor::Type::System), text, text);
message->getWords().push_back(word);
return message;
}
Message *Message::createTimeoutMessage(const QString &username, const QString &durationInSeconds,
const QString &reason)
{
Message *message = new Message;
AddCurrentTimestamp(message);
QString text;
text.append(username);
if (!durationInSeconds.isEmpty()) {
text.append(" has been timed out");
// TODO: Implement who timed the user out
text.append(" for ");
text.append(durationInSeconds);
bool ok = true;
int timeoutDuration = durationInSeconds.toInt(&ok);
text.append(" second");
if (ok && timeoutDuration > 1) {
text.append("s");
}
} else {
text.append(" has been permanently banned");
}
if (reason.length() > 0) {
text.append(": \"");
text.append(ParseTagString(reason));
text.append("\"");
}
text.append(".");
Word word(text, Word::Type::Default, MessageColor(MessageColor::Type::System), text, text);
message->getWords().push_back(word);
return message;
}
} // namespace messages } // namespace messages
} // namespace chatterino } // namespace chatterino

View file

@ -33,6 +33,11 @@ public:
const QString text; const QString text;
bool centered = false; bool centered = false;
static Message *createSystemMessage(const QString &text);
static Message *createTimeoutMessage(const QString &username, const QString &durationInSeconds,
const QString &reason);
private: private:
static LazyLoadedImage *badgeStaff; static LazyLoadedImage *badgeStaff;
static LazyLoadedImage *badgeAdmin; static LazyLoadedImage *badgeAdmin;

View file

@ -2,6 +2,7 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
MessageColor::MessageColor(const QColor &_color) MessageColor::MessageColor(const QColor &_color)
: type(Type::Custom) : type(Type::Custom)
, color(_color) , color(_color)
@ -34,5 +35,6 @@ const QColor &MessageColor::getColor(ColorScheme &colorScheme) const
static QColor _default; static QColor _default;
return _default; return _default;
} }
}
} } // namespace messages
} // namespace chatterino

View file

@ -6,6 +6,7 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
class MessageColor class MessageColor
{ {
public: public:
@ -21,5 +22,6 @@ private:
Type type; Type type;
QColor color; QColor color;
}; };
}
} } // namespace messages
} // namespace chatterino

16
src/util/irchelpers.hpp Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include <QString>
namespace chatterino {
QString ParseTagString(const QString &input)
{
QString output = input;
// code goes here
return output;
}
} // namespace chatterino

View file

@ -87,11 +87,18 @@ static void put(QUrl url, std::function<void(QJsonObject)> successCallback)
auto manager = new QNetworkAccessManager(); auto manager = new QNetworkAccessManager();
QNetworkRequest request(url); QNetworkRequest request(url);
auto &accountManager = AccountManager::getInstance();
auto currentTwitchUser = accountManager.Twitch.getCurrent();
QByteArray oauthToken;
if (currentTwitchUser) {
oauthToken = currentTwitchUser->getOAuthToken().toUtf8();
} else {
// XXX(pajlada): Bail out?
}
request.setRawHeader("Client-ID", getDefaultClientID()); request.setRawHeader("Client-ID", getDefaultClientID());
request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json"); request.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
request.setRawHeader( request.setRawHeader("Authorization", "OAuth " + oauthToken);
"Authorization",
"OAuth " + AccountManager::getInstance().getTwitchUser().getOAuthToken().toUtf8());
NetworkManager::urlPut(std::move(request), [=](QNetworkReply *reply) { NetworkManager::urlPut(std::move(request), [=](QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NetworkError::NoError) { if (reply->error() == QNetworkReply::NetworkError::NoError) {

View file

@ -1,10 +1,10 @@
#include "widgets/accountpopup.hpp" #include "widgets/accountpopup.hpp"
#include "util/urlfetch.hpp"
#include "accountmanager.hpp" #include "accountmanager.hpp"
#include "channel.hpp" #include "channel.hpp"
#include "credentials.hpp" #include "credentials.hpp"
#include "settingsmanager.hpp" #include "settingsmanager.hpp"
#include "ui_accountpopupform.h" #include "ui_accountpopupform.h"
#include "util/urlfetch.hpp"
#include <QClipboard> #include <QClipboard>
#include <QDesktopServices> #include <QDesktopServices>
@ -32,20 +32,16 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
SettingsManager &settings = SettingsManager::getInstance(); SettingsManager &settings = SettingsManager::getInstance();
permission = permissions::User; permission = permissions::User;
for(auto button : this->_ui->profileLayout->findChildren<QPushButton*>()) for (auto button : this->_ui->profileLayout->findChildren<QPushButton *>()) {
{
button->setFocusProxy(this); button->setFocusProxy(this);
} }
for(auto button: this->_ui->userLayout->findChildren<QPushButton*>()) for (auto button : this->_ui->userLayout->findChildren<QPushButton *>()) {
{
button->setFocusProxy(this); button->setFocusProxy(this);
} }
for(auto button: this->_ui->modLayout->findChildren<QPushButton*>()) for (auto button : this->_ui->modLayout->findChildren<QPushButton *>()) {
{
button->setFocusProxy(this); button->setFocusProxy(this);
} }
for(auto button: this->_ui->ownerLayout->findChildren<QPushButton*>()) for (auto button : this->_ui->ownerLayout->findChildren<QPushButton *>()) {
{
button->setFocusProxy(this); button->setFocusProxy(this);
} }
@ -60,41 +56,42 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
sendCommand(this->_ui->mod, "/mod "); sendCommand(this->_ui->mod, "/mod ");
sendCommand(this->_ui->unMod, "/unmod "); sendCommand(this->_ui->unMod, "/unmod ");
QObject::connect(this->_ui->profile, &QPushButton::clicked, this, [=](){ auto &accountManager = AccountManager::getInstance();
QDesktopServices::openUrl(QUrl("https://twitch.tv/" + QString userId;
this->_ui->lblUsername->text())); QString userNickname;
auto currentTwitchUser = accountManager.Twitch.getCurrent();
if (currentTwitchUser) {
userId = currentTwitchUser->getUserId();
userNickname = currentTwitchUser->getNickName();
}
QObject::connect(this->_ui->profile, &QPushButton::clicked, this, [=]() {
QDesktopServices::openUrl(QUrl("https://twitch.tv/" + this->_ui->lblUsername->text()));
}); });
QObject::connect(this->_ui->sendMessage, &QPushButton::clicked, this, [=](){ QObject::connect(this->_ui->sendMessage, &QPushButton::clicked, this, [=]() {
QDesktopServices::openUrl(QUrl("https://www.twitch.tv/message/compose?to=" + QDesktopServices::openUrl(
this->_ui->lblUsername->text())); QUrl("https://www.twitch.tv/message/compose?to=" + this->_ui->lblUsername->text()));
}); });
QObject::connect(this->_ui->copy, &QPushButton::clicked, this, [=](){ QObject::connect(this->_ui->copy, &QPushButton::clicked, this,
QApplication::clipboard()->setText(this->_ui->lblUsername->text()); [=]() { QApplication::clipboard()->setText(this->_ui->lblUsername->text()); });
QObject::connect(this->_ui->follow, &QPushButton::clicked, this, [=]() {
QUrl requestUrl("https://api.twitch.tv/kraken/users/" + userId + "/follows/channels/" +
this->userID);
util::twitch::put(requestUrl,
[](QJsonObject obj) { qDebug() << "follows channel: " << obj; });
}); });
QObject::connect(this->_ui->follow, &QPushButton::clicked, this, [=](){ QObject::connect(this->_ui->ignore, &QPushButton::clicked, this, [=]() {
QUrl requestUrl("https://api.twitch.tv/kraken/users/" + QUrl requestUrl("https://api.twitch.tv/kraken/users/" + userId + "/blocks/" + this->userID);
AccountManager::getInstance().getTwitchUser().getUserId() +
"/follows/channels/" + this->userID);
util::twitch::put(requestUrl,[](QJsonObject obj){ util::twitch::put(requestUrl, [](QJsonObject obj) { qDebug() << "blocks user: " << obj; });
qDebug() << "follows channel: " << obj;
});
}); });
QObject::connect(this->_ui->ignore, &QPushButton::clicked, this, [=](){ QObject::connect(this->_ui->disableHighlights, &QPushButton::clicked, this, [=, &settings]() {
QUrl requestUrl("https://api.twitch.tv/kraken/users/" +
AccountManager::getInstance().getTwitchUser().getUserId() +
"/blocks/" + this->userID);
util::twitch::put(requestUrl,[](QJsonObject obj){
qDebug() << "blocks user: " << obj;
});
});
QObject::connect(this->_ui->disableHighlights, &QPushButton::clicked, this, [=, &settings](){
QString str = settings.highlightUserBlacklist.getnonConst(); QString str = settings.highlightUserBlacklist.getnonConst();
str.append(this->_ui->lblUsername->text() + "\n"); str.append(this->_ui->lblUsername->text() + "\n");
settings.highlightUserBlacklist.set(str); settings.highlightUserBlacklist.set(str);
@ -102,7 +99,7 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
this->_ui->enableHighlights->show(); this->_ui->enableHighlights->show();
}); });
QObject::connect(this->_ui->enableHighlights, &QPushButton::clicked, this, [=, &settings](){ QObject::connect(this->_ui->enableHighlights, &QPushButton::clicked, this, [=, &settings]() {
QString str = settings.highlightUserBlacklist.getnonConst(); QString str = settings.highlightUserBlacklist.getnonConst();
QStringList list = str.split("\n"); QStringList list = str.split("\n");
list.removeAll(this->_ui->lblUsername->text()); list.removeAll(this->_ui->lblUsername->text());
@ -111,20 +108,17 @@ AccountPopupWidget::AccountPopupWidget(std::shared_ptr<Channel> channel)
this->_ui->disableHighlights->show(); this->_ui->disableHighlights->show();
}); });
updateButtons(this->_ui->userLayout, false);
updateButtons(this->_ui->userLayout,false); updateButtons(this->_ui->modLayout, false);
updateButtons(this->_ui->modLayout,false); updateButtons(this->_ui->ownerLayout, false);
updateButtons(this->_ui->ownerLayout,false);
// Close button // Close button
connect(_ui->btnClose, &QPushButton::clicked, [=]() { connect(_ui->btnClose, &QPushButton::clicked, [=]() {
hide(); // hide(); //
}); });
util::twitch::getUserID(AccountManager::getInstance().getTwitchUser().getNickName(), this, util::twitch::getUserID(userNickname, this,
[=](const QString &id){ [=](const QString &id) { currentTwitchUser->setUserId(id); });
AccountManager::getInstance().getTwitchUser().setUserId(id);
});
} }
void AccountPopupWidget::setName(const QString &name) void AccountPopupWidget::setName(const QString &name)
@ -140,7 +134,7 @@ void AccountPopupWidget::setChannel(std::shared_ptr<Channel> channel)
void AccountPopupWidget::getUserId() void AccountPopupWidget::getUserId()
{ {
util::twitch::getUserID(this->_ui->lblUsername->text(), this, [=](const QString &id){ util::twitch::getUserID(this->_ui->lblUsername->text(), this, [=](const QString &id) {
userID = id; userID = id;
getUserData(); getUserData();
}); });
@ -148,7 +142,8 @@ void AccountPopupWidget::getUserId()
void AccountPopupWidget::getUserData() void AccountPopupWidget::getUserData()
{ {
util::twitch::get("https://api.twitch.tv/kraken/channels/" + userID, this, [=](const QJsonObject &obj){ util::twitch::get(
"https://api.twitch.tv/kraken/channels/" + userID, this, [=](const QJsonObject &obj) {
_ui->lblFollowers->setText(QString::number(obj.value("followers").toInt())); _ui->lblFollowers->setText(QString::number(obj.value("followers").toInt()));
_ui->lblViews->setText(QString::number(obj.value("views").toInt())); _ui->lblViews->setText(QString::number(obj.value("views").toInt()));
_ui->lblAccountAge->setText(obj.value("created_at").toString().section("T", 0, 0)); _ui->lblAccountAge->setText(obj.value("created_at").toString().section("T", 0, 0));
@ -185,34 +180,39 @@ void AccountPopupWidget::loadAvatar(const QUrl &avatarUrl)
void AccountPopupWidget::updatePermissions() void AccountPopupWidget::updatePermissions()
{ {
if(this->_channel.get()->name == AccountManager::getInstance().getTwitchUser().getNickName()) AccountManager &accountManager = AccountManager::getInstance();
{ auto currentTwitchUser = accountManager.Twitch.getCurrent();
permission = permissions::Owner; if (!currentTwitchUser) {
// No twitch user set (should never happen)
return;
} }
else if(this->_channel->modList.contains(AccountManager::getInstance().getTwitchUser().getNickName()))
{ if (this->_channel.get()->name == currentTwitchUser->getNickName()) {
permission = permissions::Owner;
} else if (this->_channel->modList.contains(currentTwitchUser->getNickName())) {
// XXX(pajlada): This might always trigger if user is anonymous (if nickName is empty?)
permission = permissions::Mod; permission = permissions::Mod;
} }
} }
void AccountPopupWidget::updateButtons(QWidget* layout, bool state) void AccountPopupWidget::updateButtons(QWidget *layout, bool state)
{ {
for(auto button : layout->findChildren<QPushButton*>()) for (auto button : layout->findChildren<QPushButton *>()) {
{
button->setVisible(state); button->setVisible(state);
} }
} }
void AccountPopupWidget::timeout(QPushButton *button, int time) void AccountPopupWidget::timeout(QPushButton *button, int time)
{ {
QObject::connect(button, &QPushButton::clicked, this, [=](){ QObject::connect(button, &QPushButton::clicked, this, [=]() {
this->_channel->sendMessage("/timeout " + this->_ui->lblUsername->text() + " " + QString::number(time)); this->_channel->sendMessage("/timeout " + this->_ui->lblUsername->text() + " " +
QString::number(time));
}); });
} }
void AccountPopupWidget::sendCommand(QPushButton *button, QString command) void AccountPopupWidget::sendCommand(QPushButton *button, QString command)
{ {
QObject::connect(button, &QPushButton::clicked, this, [=](){ QObject::connect(button, &QPushButton::clicked, this, [=]() {
this->_channel->sendMessage(command + this->_ui->lblUsername->text()); this->_channel->sendMessage(command + this->_ui->lblUsername->text());
}); });
} }
@ -229,42 +229,39 @@ void AccountPopupWidget::focusOutEvent(QFocusEvent *event)
void AccountPopupWidget::showEvent(QShowEvent *event) void AccountPopupWidget::showEvent(QShowEvent *event)
{ {
if(this->_ui->lblUsername->text() != AccountManager::getInstance().getTwitchUser().getNickName()) AccountManager &accountManager = AccountManager::getInstance();
{ auto currentTwitchUser = accountManager.Twitch.getCurrent();
if (!currentTwitchUser) {
// No twitch user set (should never happen)
return;
}
if (this->_ui->lblUsername->text() != currentTwitchUser->getNickName()) {
updateButtons(this->_ui->userLayout, true); updateButtons(this->_ui->userLayout, true);
if(permission != permissions::User) if (permission != permissions::User) {
{ if (!this->_channel->modList.contains(this->_ui->lblUsername->text())) {
if(!this->_channel->modList.contains(this->_ui->lblUsername->text()))
{
updateButtons(this->_ui->modLayout, true); updateButtons(this->_ui->modLayout, true);
} }
if(permission == permissions::Owner) if (permission == permissions::Owner) {
{
updateButtons(this->_ui->ownerLayout, true); updateButtons(this->_ui->ownerLayout, true);
updateButtons(this->_ui->modLayout, true); updateButtons(this->_ui->modLayout, true);
} }
} }
} } else {
else
{
updateButtons(this->_ui->modLayout, false); updateButtons(this->_ui->modLayout, false);
updateButtons(this->_ui->userLayout, false); updateButtons(this->_ui->userLayout, false);
updateButtons(this->_ui->ownerLayout, false); updateButtons(this->_ui->ownerLayout, false);
} }
QString blacklisted = SettingsManager::getInstance().highlightUserBlacklist.getnonConst(); QString blacklisted = SettingsManager::getInstance().highlightUserBlacklist.getnonConst();
QStringList list = blacklisted.split("\n",QString::SkipEmptyParts); QStringList list = blacklisted.split("\n", QString::SkipEmptyParts);
if(list.contains(this->_ui->lblUsername->text(),Qt::CaseInsensitive)) if (list.contains(this->_ui->lblUsername->text(), Qt::CaseInsensitive)) {
{
this->_ui->disableHighlights->hide(); this->_ui->disableHighlights->hide();
this->_ui->enableHighlights->show(); this->_ui->enableHighlights->show();
} } else {
else
{
this->_ui->disableHighlights->show(); this->_ui->disableHighlights->show();
this->_ui->enableHighlights->hide(); this->_ui->enableHighlights->hide();
} }
} }
} // namespace widgets } // namespace widgets

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "concurrentmap.hpp" #include "concurrentmap.hpp"
#include "twitch/twitchchannel.hpp" #include "twitch/twitchchannel.hpp"
@ -35,9 +36,9 @@ private:
void getUserData(); void getUserData();
void loadAvatar(const QUrl &avatarUrl); void loadAvatar(const QUrl &avatarUrl);
void updateButtons(QWidget* layout, bool state); void updateButtons(QWidget *layout, bool state);
void timeout(QPushButton* button, int time); void timeout(QPushButton *button, int time);
void sendCommand(QPushButton* button, QString command); void sendCommand(QPushButton *button, QString command);
enum class permissions { User, Mod, Owner }; enum class permissions { User, Mod, Owner };
permissions permission; permissions permission;

View file

@ -802,7 +802,7 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
float distance = util::distanceBetweenPoints(this->lastPressPosition, event->screenPos()); float distance = util::distanceBetweenPoints(this->lastPressPosition, event->screenPos());
qDebug() << "Distance: " << distance; // qDebug() << "Distance: " << distance;
if (fabsf(distance) > 15.f) { if (fabsf(distance) > 15.f) {
// It wasn't a proper click, so we don't care about that here // It wasn't a proper click, so we don't care about that here

View file

@ -129,13 +129,14 @@ QVBoxLayout *SettingsDialog::createAccountsTab()
// listview // listview
auto listWidget = new QListWidget(this); auto listWidget = new QListWidget(this);
for (auto &user : AccountManager::getInstance().getTwitchUsers()) { for (const auto &userName : AccountManager::getInstance().Twitch.getUsernames()) {
listWidget->addItem(user.getUserName()); listWidget->addItem(userName);
} }
// Select the currently logged in user
if (listWidget->count() > 0) { if (listWidget->count() > 0) {
const auto &currentUser = AccountManager::getInstance().getTwitchUser(); const QString &currentUsername =
QString currentUsername = currentUser.getUserName(); AccountManager::getInstance().Twitch.getCurrent()->getUserName();
for (int i = 0; i < listWidget->count(); ++i) { for (int i = 0; i < listWidget->count(); ++i) {
QString itemText = listWidget->item(i)->text(); QString itemText = listWidget->item(i)->text();
if (itemText.compare(currentUsername, Qt::CaseInsensitive) == 0) { if (itemText.compare(currentUsername, Qt::CaseInsensitive) == 0) {
@ -147,7 +148,8 @@ QVBoxLayout *SettingsDialog::createAccountsTab()
QObject::connect(listWidget, &QListWidget::clicked, this, [&, listWidget] { QObject::connect(listWidget, &QListWidget::clicked, this, [&, listWidget] {
if (!listWidget->selectedItems().isEmpty()) { if (!listWidget->selectedItems().isEmpty()) {
AccountManager::getInstance().setCurrentTwitchUser(listWidget->currentItem()->text()); QString newUsername = listWidget->currentItem()->text();
AccountManager::getInstance().Twitch.currentUsername = newUsername.toStdString();
} }
}); });

View file

@ -79,15 +79,10 @@ Split::Split(ChannelManager &_channelManager, SplitContainer *parent)
ezShortcut(this, "CTRL+R", &Split::doChangeChannel); ezShortcut(this, "CTRL+R", &Split::doChangeChannel);
// xd // xd
//ezShortcut(this, "ALT+SHIFT+RIGHT", &Split::doIncFlexX); // ezShortcut(this, "ALT+SHIFT+RIGHT", &Split::doIncFlexX);
//ezShortcut(this, "ALT+SHIFT+LEFT", &Split::doDecFlexX); // ezShortcut(this, "ALT+SHIFT+LEFT", &Split::doDecFlexX);
//ezShortcut(this, "ALT+SHIFT+UP", &Split::doIncFlexY); // ezShortcut(this, "ALT+SHIFT+UP", &Split::doIncFlexY);
//ezShortcut(this, "ALT+SHIFT+DOWN", &Split::doDecFlexY); // ezShortcut(this, "ALT+SHIFT+DOWN", &Split::doDecFlexY);
#ifndef NDEBUG
// F12: Toggle message spawning
ezShortcut(this, "ALT+Q", &Split::doToggleMessageSpawning);
#endif
this->channelName.getValueChangedSignal().connect( this->channelName.getValueChangedSignal().connect(
std::bind(&Split::channelNameUpdated, this, std::placeholders::_1)); std::bind(&Split::channelNameUpdated, this, std::placeholders::_1));
@ -102,10 +97,6 @@ Split::Split(ChannelManager &_channelManager, SplitContainer *parent)
this->input.clearSelection(); this->input.clearSelection();
} }
}); });
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &Split::test);
timer->start(1000);
} }
Split::~Split() Split::~Split()
@ -462,30 +453,6 @@ void Split::doCopy()
QApplication::clipboard()->setText(this->view.getSelectedText()); QApplication::clipboard()->setText(this->view.getSelectedText());
} }
static std::vector<std::string> usernameVariants = {
"pajlada", //
"trump", //
"Chancu", //
"pajaWoman", //
"fourtf", //
"weneedmoreautisticbots", //
"fourtfbot", //
"pajbot", //
"snusbot", //
};
static std::vector<std::string> messageVariants = {
"hehe", //
"lol pajlada", //
"hehe BANNEDWORD", //
"someone ordered pizza", //
"for ice poseidon", //
"and delivery guy said it is for enza denino", //
"!gn", //
"for my laptop", //
"should I buy a Herschel backpack?", //
};
template <typename Iter, typename RandomGenerator> template <typename Iter, typename RandomGenerator>
static Iter select_randomly(Iter start, Iter end, RandomGenerator &g) static Iter select_randomly(Iter start, Iter end, RandomGenerator &g)
{ {
@ -502,41 +469,6 @@ static Iter select_randomly(Iter start, Iter end)
return select_randomly(start, end, gen); return select_randomly(start, end, gen);
} }
void Split::test()
{
if (this->testEnabled) {
messages::MessageParseArgs args;
auto message =
new Communi::IrcPrivateMessage(this->channelManager.ircManager.readConnection.get());
std::string text = *(select_randomly(messageVariants.begin(), messageVariants.end()));
std::string username = *(select_randomly(usernameVariants.begin(), usernameVariants.end()));
std::string usernameString = username + "!" + username + "@" + username;
QStringList params{"#pajlada", text.c_str()};
qDebug() << params;
message->setParameters(params);
message->setPrefix(usernameString.c_str());
auto twitchChannel = std::dynamic_pointer_cast<twitch::TwitchChannel>(this->channel);
twitch::TwitchMessageBuilder builder(
twitchChannel.get(), this->channelManager.ircManager.resources,
this->channelManager.emoteManager, this->channelManager.windowManager, message, args);
twitchChannel->addMessage(builder.parse());
}
}
void Split::doToggleMessageSpawning()
{
this->testEnabled = !this->testEnabled;
}
void Split::doIncFlexX() void Split::doIncFlexX()
{ {
this->setFlexSizeX(this->getFlexSizeX() * 1.2); this->setFlexSizeX(this->getFlexSizeX() * 1.2);

View file

@ -52,7 +52,6 @@ public:
CompletionManager &completionManager; CompletionManager &completionManager;
pajlada::Settings::Setting<std::string> channelName; pajlada::Settings::Setting<std::string> channelName;
boost::signals2::signal<void()> channelChanged; boost::signals2::signal<void()> channelChanged;
bool testEnabled = false;
std::shared_ptr<Channel> getChannel() const; std::shared_ptr<Channel> getChannel() const;
std::shared_ptr<Channel> &getChannelRef(); std::shared_ptr<Channel> &getChannelRef();
@ -122,8 +121,6 @@ public slots:
// Open viewer list of the channel // Open viewer list of the channel
void doOpenViewerList(); void doOpenViewerList();
void doToggleMessageSpawning();
void test();
void doIncFlexX(); void doIncFlexX();
void doDecFlexX(); void doDecFlexX();
void doIncFlexY(); void doIncFlexY();