refactored irc

This commit is contained in:
fourtf 2018-02-05 15:11:50 +01:00
parent 12b30eb2ed
commit b351c40d29
56 changed files with 1397 additions and 1154 deletions

View file

@ -90,196 +90,204 @@ SOURCES += \
src/application.cpp \
src/channel.cpp \
src/channeldata.cpp \
src/singletons/ircmanager.cpp \
src/messages/link.cpp \
src/messages/message.cpp \
src/widgets/notebook.cpp \
src/widgets/helper/notebookbutton.cpp \
src/widgets/helper/notebooktab.cpp \
src/widgets/scrollbar.cpp \
src/widgets/helper/scrollbarhighlight.cpp \
src/widgets/settingsdialog.cpp \
src/widgets/helper/settingsdialogtab.cpp \
src/widgets/textinputdialog.cpp \
src/singletons/loggingmanager.cpp \
src/singletons/helper/loggingchannel.cpp \
src/singletons/windowmanager.cpp \
src/singletons/channelmanager.cpp \
src/singletons/fontmanager.cpp \
src/singletons/settingsmanager.cpp \
src/singletons/emotemanager.cpp \
src/messages/messagebuilder.cpp \
src/twitch/twitchmessagebuilder.cpp \
src/widgets/titlebar.cpp \
src/singletons/accountmanager.cpp \
src/twitch/twitchuser.cpp \
src/ircaccount.cpp \
src/widgets/accountpopup.cpp \
src/widgets/basewidget.cpp \
src/widgets/helper/resizingtextedit.cpp \
src/singletons/completionmanager.cpp \
src/widgets/logindialog.cpp \
src/widgets/qualitypopup.cpp \
src/widgets/emotepopup.cpp \
src/widgets/helper/channelview.cpp \
src/twitch/twitchchannel.cpp \
src/widgets/helper/rippleeffectlabel.cpp \
src/widgets/helper/rippleeffectbutton.cpp \
src/messages/messagecolor.cpp \
src/util/networkmanager.cpp \
src/singletons/commandmanager.cpp \
src/widgets/split.cpp \
src/widgets/helper/splitinput.cpp \
src/widgets/helper/splitheader.cpp \
src/widgets/splitcontainer.cpp \
src/widgets/helper/droppreview.cpp \
src/widgets/window.cpp \
src/widgets/helper/splitcolumn.cpp \
src/widgets/accountswitchwidget.cpp \
src/widgets/accountswitchpopupwidget.cpp \
src/widgets/tooltipwidget.cpp \
src/singletons/thememanager.cpp \
src/twitch/twitchaccountmanager.cpp \
src/singletons/helper/completionmodel.cpp \
src/singletons/resourcemanager.cpp \
src/singletons/helper/ircmessagehandler.cpp \
src/singletons/pathmanager.cpp \
src/widgets/helper/searchpopup.cpp \
src/messages/messageelement.cpp \
src/messages/image.cpp \
src/messages/layouts/messagelayout.cpp \
src/messages/layouts/messagelayoutelement.cpp \
src/messages/layouts/messagelayoutcontainer.cpp \
src/messages/layouts/messagelayoutelement.cpp \
src/messages/link.cpp \
src/messages/message.cpp \
src/messages/messagebuilder.cpp \
src/messages/messagecolor.cpp \
src/messages/messageelement.cpp \
src/providers/irc/abstractircserver.cpp \
src/providers/twitch/ircmessagehandler.cpp \
src/providers/twitch/twitchaccount.cpp \
src/providers/twitch/twitchaccountmanager.cpp \
src/providers/twitch/twitchchannel.cpp \
src/providers/twitch/twitchmessagebuilder.cpp \
src/providers/twitch/twitchserver.cpp \
src/singletons/accountmanager.cpp \
src/singletons/channelmanager.cpp \
src/singletons/commandmanager.cpp \
src/singletons/completionmanager.cpp \
src/singletons/emotemanager.cpp \
src/singletons/fontmanager.cpp \
src/singletons/helper/completionmodel.cpp \
src/singletons/helper/loggingchannel.cpp \
src/singletons/helper/moderationaction.cpp \
src/singletons/loggingmanager.cpp \
src/singletons/pathmanager.cpp \
src/singletons/resourcemanager.cpp \
src/singletons/settingsmanager.cpp \
src/singletons/thememanager.cpp \
src/singletons/windowmanager.cpp \
src/util/networkmanager.cpp \
src/util/networkrequest.cpp \
src/widgets/accountpopup.cpp \
src/widgets/accountswitchpopupwidget.cpp \
src/widgets/accountswitchwidget.cpp \
src/widgets/basewidget.cpp \
src/widgets/basewindow.cpp \
src/widgets/emotepopup.cpp \
src/widgets/helper/channelview.cpp \
src/widgets/helper/droppreview.cpp \
src/widgets/helper/label.cpp \
src/widgets/helper/notebookbutton.cpp \
src/widgets/helper/notebooktab.cpp \
src/widgets/helper/resizingtextedit.cpp \
src/widgets/helper/rippleeffectbutton.cpp \
src/widgets/helper/rippleeffectlabel.cpp \
src/widgets/helper/scrollbarhighlight.cpp \
src/widgets/helper/searchpopup.cpp \
src/widgets/helper/settingsdialogtab.cpp \
src/widgets/helper/splitcolumn.cpp \
src/widgets/helper/splitheader.cpp \
src/widgets/helper/splitinput.cpp \
src/widgets/helper/titlebarbutton.cpp \
src/widgets/logindialog.cpp \
src/widgets/notebook.cpp \
src/widgets/qualitypopup.cpp \
src/widgets/scrollbar.cpp \
src/widgets/settingsdialog.cpp \
src/widgets/settingspages/aboutpage.cpp \
src/widgets/settingspages/accountspage.cpp \
src/widgets/settingspages/appearancepage.cpp \
src/widgets/settingspages/settingspage.cpp \
src/widgets/settingspages/behaviourpage.cpp \
src/widgets/settingspages/commandpage.cpp \
src/widgets/settingspages/emotespage.cpp \
src/widgets/settingspages/highlightingpage.cpp \
src/widgets/settingspages/accountspage.cpp \
src/widgets/settingspages/aboutpage.cpp \
src/widgets/settingspages/moderationpage.cpp \
src/widgets/settingspages/logspage.cpp \
src/widgets/basewindow.cpp \
src/singletons/helper/moderationaction.cpp \
src/widgets/streamview.cpp \
src/util/networkrequest.cpp \
src/widgets/settingspages/ignoreuserspage.cpp \
src/widgets/settingspages/ignoremessagespage.cpp \
src/widgets/settingspages/specialchannelspage.cpp \
src/widgets/settingspages/ignoreuserspage.cpp \
src/widgets/settingspages/keyboardsettingspage.cpp \
src/widgets/helper/titlebarbutton.cpp \
src/widgets/helper/label.cpp
src/widgets/settingspages/logspage.cpp \
src/widgets/settingspages/moderationpage.cpp \
src/widgets/settingspages/settingspage.cpp \
src/widgets/settingspages/specialchannelspage.cpp \
src/widgets/split.cpp \
src/widgets/splitcontainer.cpp \
src/widgets/streamview.cpp \
src/widgets/textinputdialog.cpp \
src/widgets/titlebar.cpp \
src/widgets/tooltipwidget.cpp \
src/widgets/window.cpp \
src/providers/irc/_ircaccount.cpp \
src/providers/irc/_ircserver.cpp \
src/providers/irc/_ircchannel.cpp
HEADERS += \
src/precompiled_header.hpp \
src/util/posttothread.hpp \
src/channel.hpp \
src/util/concurrentmap.hpp \
src/emojis.hpp \
src/singletons/ircmanager.hpp \
src/messages/link.hpp \
src/messages/message.hpp \
src/twitch/emotevalue.hpp \
src/widgets/notebook.hpp \
src/widgets/helper/notebookbutton.hpp \
src/widgets/helper/notebooktab.hpp \
src/widgets/scrollbar.hpp \
src/widgets/helper/scrollbarhighlight.hpp \
src/widgets/settingsdialog.hpp \
src/widgets/helper/settingsdialogtab.hpp \
src/widgets/helper/signallabel.hpp \
src/widgets/textinputdialog.hpp \
src/widgets/helper/resizingtextedit.hpp \
src/messages/limitedqueue.hpp \
src/messages/limitedqueuesnapshot.hpp \
src/singletons/loggingmanager.hpp \
src/singletons/helper/loggingchannel.hpp \
src/singletons/channelmanager.hpp \
src/singletons/windowmanager.hpp \
src/singletons/settingsmanager.hpp \
src/singletons/fontmanager.hpp \
src/singletons/emotemanager.hpp \
src/util/urlfetch.hpp \
src/messages/messageparseargs.hpp \
src/messages/messagebuilder.hpp \
src/twitch/twitchmessagebuilder.hpp \
src/widgets/titlebar.hpp \
src/singletons/accountmanager.hpp \
src/twitch/twitchuser.hpp \
src/ircaccount.hpp \
src/widgets/accountpopup.hpp \
src/util/distancebetweenpoints.hpp \
src/widgets/basewidget.hpp \
src/singletons/completionmanager.hpp \
src/widgets/helper/channelview.hpp \
src/twitch/twitchchannel.hpp \
src/widgets/helper/rippleeffectbutton.hpp \
src/widgets/helper/rippleeffectlabel.hpp \
src/widgets/qualitypopup.hpp \
src/widgets/emotepopup.hpp \
src/messages/messagecolor.hpp \
src/util/nativeeventhelper.hpp \
src/debug/log.hpp \
src/util/benchmark.hpp \
src/util/networkmanager.hpp \
src/singletons/commandmanager.hpp \
src/widgets/split.hpp \
src/widgets/helper/splitheader.hpp \
src/widgets/helper/splitinput.hpp \
src/widgets/window.hpp \
src/widgets/splitcontainer.hpp \
src/widgets/helper/droppreview.hpp \
src/widgets/helper/splitcolumn.hpp \
src/util/irchelpers.hpp \
src/util/helpers.hpp \
src/widgets/accountswitchwidget.hpp \
src/widgets/accountswitchpopupwidget.hpp \
src/const.hpp \
src/widgets/tooltipwidget.hpp \
src/singletons/thememanager.hpp \
src/twitch/twitchaccountmanager.hpp \
src/singletons/helper/completionmodel.hpp \
src/singletons/helper/chatterinosetting.hpp \
src/singletons/resourcemanager.hpp \
src/util/emotemap.hpp \
src/singletons/helper/ircmessagehandler.hpp \
src/util/serialize-custom.hpp \
src/debug/log.hpp \
src/emojis.hpp \
src/messages/highlightphrase.hpp \
src/messages/selection.hpp \
src/singletons/pathmanager.hpp \
src/widgets/helper/searchpopup.hpp \
src/widgets/helper/shortcut.hpp \
src/messages/messageelement.hpp \
src/messages/image.hpp \
src/messages/layouts/messagelayout.hpp \
src/messages/layouts/messagelayoutelement.hpp \
src/messages/layouts/messagelayoutcontainer.hpp \
src/util/property.hpp \
src/widgets/settingspages/appearancepage.hpp \
src/widgets/settingspages/settingspage.hpp \
src/messages/layouts/messagelayoutelement.hpp \
src/messages/limitedqueue.hpp \
src/messages/limitedqueuesnapshot.hpp \
src/messages/link.hpp \
src/messages/message.hpp \
src/messages/messagebuilder.hpp \
src/messages/messagecolor.hpp \
src/messages/messageelement.hpp \
src/messages/messageparseargs.hpp \
src/messages/selection.hpp \
src/providers/twitch/emotevalue.hpp \
src/providers/twitch/ircmessagehandler.hpp \
src/providers/twitch/twitchaccount.hpp \
src/providers/twitch/twitchaccountmanager.hpp \
src/providers/twitch/twitchchannel.hpp \
src/providers/twitch/twitchmessagebuilder.hpp \
src/providers/twitch/twitchserver.hpp \
src/singletons/accountmanager.hpp \
src/singletons/channelmanager.hpp \
src/singletons/commandmanager.hpp \
src/singletons/completionmanager.hpp \
src/singletons/emotemanager.hpp \
src/singletons/fontmanager.hpp \
src/singletons/helper/chatterinosetting.hpp \
src/singletons/helper/completionmodel.hpp \
src/singletons/helper/loggingchannel.hpp \
src/singletons/helper/moderationaction.hpp \
src/singletons/loggingmanager.hpp \
src/singletons/pathmanager.hpp \
src/singletons/providermanager.hp \
src/singletons/resourcemanager.hpp \
src/singletons/settingsmanager.hpp \
src/singletons/thememanager.hpp \
src/singletons/windowmanager.hpp \
src/util/benchmark.hpp \
src/util/concurrentmap.hpp \
src/util/distancebetweenpoints.hpp \
src/util/emotemap.hpp \
src/util/flagsenum.hpp \
src/util/helpers.hpp \
src/util/irchelpers.hpp \
src/util/layoutcreator.hpp \
src/util/nativeeventhelper.hpp \
src/util/networkmanager.hpp \
src/util/networkrequest.hpp \
src/util/networkrequester.hpp \
src/util/networkworker.hpp \
src/util/posttothread.hpp \
src/util/property.hpp \
src/util/serialize-custom.hpp \
src/util/urlfetch.hpp \
src/widgets/accountpopup.hpp \
src/widgets/accountswitchpopupwidget.hpp \
src/widgets/accountswitchwidget.hpp \
src/widgets/basewidget.hpp \
src/widgets/basewindow.hpp \
src/widgets/emotepopup.hpp \
src/widgets/helper/channelview.hpp \
src/widgets/helper/droppreview.hpp \
src/widgets/helper/label.hpp \
src/widgets/helper/notebookbutton.hpp \
src/widgets/helper/notebooktab.hpp \
src/widgets/helper/resizingtextedit.hpp \
src/widgets/helper/rippleeffectbutton.hpp \
src/widgets/helper/rippleeffectlabel.hpp \
src/widgets/helper/scrollbarhighlight.hpp \
src/widgets/helper/searchpopup.hpp \
src/widgets/helper/settingsdialogtab.hpp \
src/widgets/helper/shortcut.hpp \
src/widgets/helper/signallabel.hpp \
src/widgets/helper/splitcolumn.hpp \
src/widgets/helper/splitheader.hpp \
src/widgets/helper/splitinput.hpp \
src/widgets/helper/titlebarbutton.hpp \
src/widgets/notebook.hpp \
src/widgets/qualitypopup.hpp \
src/widgets/scrollbar.hpp \
src/widgets/settingsdialog.hpp \
src/widgets/settingspages/aboutpage.hpp \
src/widgets/settingspages/accountspage.hpp \
src/widgets/settingspages/appearancepage.hpp \
src/widgets/settingspages/behaviourpage.hpp \
src/widgets/settingspages/commandpage.hpp \
src/widgets/settingspages/emotespage.hpp \
src/widgets/settingspages/highlightingpage.hpp \
src/widgets/settingspages/accountspage.hpp \
src/widgets/settingspages/aboutpage.hpp \
src/widgets/settingspages/moderationpage.hpp \
src/widgets/settingspages/logspage.hpp \
src/widgets/basewindow.hpp \
src/singletons/helper/moderationaction.hpp \
src/widgets/streamview.hpp \
src/util/networkrequest.hpp \
src/util/networkworker.hpp \
src/util/networkrequester.hpp \
src/widgets/settingspages/ignoreuserspage.hpp \
src/widgets/settingspages/ignoremessagespage.hpp \
src/widgets/settingspages/specialchannelspage.hpp \
src/widgets/settingspages/ignoreuserspage.hpp \
src/widgets/settingspages/keyboardsettingspage.hpp \
src/widgets/helper/titlebarbutton.hpp \
src/widgets/helper/label.hpp \
src/util/flagsenum.hpp
src/widgets/settingspages/logspage.hpp \
src/widgets/settingspages/moderationpage.hpp \
src/widgets/settingspages/settingspage.hpp \
src/widgets/settingspages/specialchannelspage.hpp \
src/widgets/split.hpp \
src/widgets/splitcontainer.hpp \
src/widgets/streamview.hpp \
src/widgets/textinputdialog.hpp \
src/widgets/titlebar.hpp \
src/widgets/tooltipwidget.hpp \
src/widgets/window.hpp \
src/common.hpp \
src/providers/irc/_ircchannel.hpp \
src/providers/irc/_ircaccount.hpp \
src/providers/irc/_ircserver.hpp \
src/providers/irc/abstractircserver.hpp
RESOURCES += \
resources/resources.qrc

View file

@ -1,8 +1,9 @@
#include "application.hpp"
#include "singletons/loggingmanager.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/accountmanager.hpp"
#include "singletons/commandmanager.hpp"
#include "singletons/emotemanager.hpp"
#include "singletons/loggingmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "singletons/windowmanager.hpp"
@ -42,7 +43,7 @@ Application::~Application()
int Application::run(QApplication &qtApp)
{
// Start connecting to the IRC Servers (Twitch only for now)
singletons::IrcManager::getInstance().connect();
providers::twitch::TwitchServer::getInstance().connect();
// Show main window
singletons::WindowManager::getInstance().getMainWindow().show();

View file

@ -23,9 +23,14 @@ Channel::Channel(const QString &_name)
{
}
Channel::~Channel()
{
this->destroyed.invoke();
}
bool Channel::isEmpty() const
{
return false;
return this->name.isEmpty();
}
messages::LimitedQueueSnapshot<messages::MessagePtr> Channel::getMessageSnapshot()
@ -103,4 +108,13 @@ void Channel::sendMessage(const QString &message)
{
}
std::shared_ptr<Channel> Channel::getEmpty()
{
static std::shared_ptr<Channel> channel(new Channel(""));
return channel;
}
void Channel::onConnected()
{
}
} // namespace chatterino

View file

@ -23,11 +23,15 @@ class Channel : public std::enable_shared_from_this<Channel>
{
public:
explicit Channel(const QString &_name);
virtual ~Channel();
pajlada::Signals::Signal<const QString &, const QString &> sendMessageSignal;
boost::signals2::signal<void(messages::MessagePtr &)> messageRemovedFromStart;
boost::signals2::signal<void(messages::MessagePtr &)> messageAppended;
boost::signals2::signal<void(std::vector<messages::MessagePtr> &)> messagesAddedAtStart;
boost::signals2::signal<void(size_t index, messages::MessagePtr &)> messageReplaced;
pajlada::Signals::NoArgSignal destroyed;
virtual bool isEmpty() const;
messages::LimitedQueueSnapshot<messages::MessagePtr> getMessageSnapshot();
@ -58,6 +62,11 @@ public:
return false;
}
static std::shared_ptr<Channel> getEmpty();
protected:
virtual void onConnected();
private:
messages::LimitedQueue<messages::MessagePtr> messages;
};

View file

@ -1,20 +1,22 @@
#pragma once
#include <QString>
#include <string>
namespace chatterino {
enum class HighlightState {
None,
Highlighted,
NewMessage,
};
inline QString qS(const std::string &string)
{
return QString::fromStdString(string);
}
} // namespace chatterino
#pragma once
#include <QString>
#include <boost/preprocessor.hpp>
#include <string>
#include "debug/log.hpp"
namespace chatterino {
enum class HighlightState {
None,
Highlighted,
NewMessage,
};
inline QString qS(const std::string &string)
{
return QString::fromStdString(string);
}
} // namespace chatterino

View file

@ -6,10 +6,11 @@ namespace chatterino {
static const QString ANONYMOUS_USERNAME_LABEL(" - anonymous - ");
namespace providers {
namespace twitch {
static const QString ANONYMOUS_USERNAME("justinfan64537");
} // namespace twitch
}
} // namespace providers
} // namespace chatterino

View file

@ -1,34 +0,0 @@
#include "ircaccount.hpp"
namespace chatterino {
IrcUser2::IrcUser2(const QString &_userName, const QString &_nickName, const QString &_realName,
const QString &_password)
: userName(_userName)
, nickName(_nickName)
, realName(_realName)
, password(_password)
{
}
const QString &IrcUser2::getUserName() const
{
return this->userName;
}
const QString &IrcUser2::getNickName() const
{
return this->nickName;
}
const QString &IrcUser2::getRealName() const
{
return this->realName;
}
const QString &IrcUser2::getPassword() const
{
return this->password;
}
} // namespace chatterino

View file

@ -1,25 +0,0 @@
#pragma once
#include <QString>
namespace chatterino {
class IrcUser2
{
public:
IrcUser2(const QString &userName, const QString &nickName, const QString &realName,
const QString &password);
const QString &getUserName() const;
const QString &getNickName() const;
const QString &getRealName() const;
const QString &getPassword() const;
private:
QString userName;
QString nickName;
QString realName;
QString password;
};
} // namespace chatterino

View file

@ -87,7 +87,8 @@ void Image::loadImage()
singletons::EmoteManager::getInstance().incGeneration();
postToThread([] { singletons::WindowManager::getInstance().layoutVisibleChatWidgets(); });
util::postToThread(
[] { singletons::WindowManager::getInstance().layoutVisibleChatWidgets(); });
});
singletons::EmoteManager::getInstance().getGifUpdateSignal().connect([=]() {

View file

@ -20,8 +20,7 @@ class Image;
namespace layouts {
struct MessageLayoutElement : boost::noncopyable
{
struct MessageLayoutElement : boost::noncopyable {
public:
MessageLayoutElement(MessageElement &creator, const QSize &size);
@ -47,7 +46,6 @@ protected:
private:
QRect rect;
Link link;
// bool isInNewLine;
MessageElement &creator;
};

View file

@ -0,0 +1,37 @@
#include "_ircaccount.hpp"
// namespace chatterino {
// namespace providers {
// namespace irc {
// IrcAccount::IrcAccount(const QString &_userName, const QString &_nickName, const QString
// &_realName,
// const QString &_password)
// : userName(_userName)
// , nickName(_nickName)
// , realName(_realName)
// , password(_password)
//{
//}
// const QString &IrcAccount::getUserName() const
//{
// return this->userName;
//}
// const QString &IrcAccount::getNickName() const
//{
// return this->nickName;
//}
// const QString &IrcAccount::getRealName() const
//{
// return this->realName;
//}
// const QString &IrcAccount::getPassword() const
//{
// return this->password;
//}
//} // namespace irc
//} // namespace providers
//} // namespace chatterino

View file

@ -0,0 +1,27 @@
#pragma once
#include <QString>
// namespace chatterino {
// namespace providers {
// namespace irc {
// class IrcAccount
//{
// public:
// IrcAccount(const QString &userName, const QString &nickName, const QString &realName,
// const QString &password);
// const QString &getUserName() const;
// const QString &getNickName() const;
// const QString &getRealName() const;
// const QString &getPassword() const;
// private:
// QString userName;
// QString nickName;
// QString realName;
// QString password;
//};
//} // namespace irc
//} // namespace providers
//} // namespace chatterino

View file

@ -0,0 +1,11 @@
#include "_ircchannel.hpp"
namespace chatterino {
namespace providers {
namespace irc {
// IrcChannel::IrcChannel()
//{
//}
} // namespace irc
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,13 @@
#pragma once
namespace chatterino {
namespace providers {
namespace irc {
// class IrcChannel
//{
// public:
// IrcChannel();
//};
} // namespace irc
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,14 @@
#include "_ircserver.hpp"
#include <cassert>
namespace chatterino {
namespace providers {
namespace irc {
// IrcServer::IrcServer(const QString &hostname, int port)
//{
// this->initConnection();
//}
} // namespace irc
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,25 @@
#pragma once
#include "providers/irc/_ircaccount.hpp"
#include "providers/irc/abstractircserver.hpp"
namespace chatterino {
namespace providers {
namespace irc {
// class IrcServer
//{
// public:
// IrcServer(const QString &hostname, int port);
// void setAccount(std::shared_ptr<IrcAccount> newAccount);
// std::shared_ptr<IrcAccount> getAccount() const;
// protected:
// virtual void initializeConnection(Communi::IrcConnection *connection, bool isReadConnection);
// virtual void privateMessageReceived(Communi::IrcPrivateMessage *message);
// virtual void messageReceived(Communi::IrcMessage *message);
//};
} // namespace irc
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,226 @@
#include "abstractircserver.hpp"
#include "common.hpp"
#include "messages/limitedqueuesnapshot.hpp"
#include "messages/message.hpp"
using namespace chatterino::messages;
namespace chatterino {
namespace providers {
namespace irc {
AbstractIrcServer::AbstractIrcServer()
{
// Initialize the connections
this->writeConnection.reset(new Communi::IrcConnection);
this->writeConnection->moveToThread(QCoreApplication::instance()->thread());
QObject::connect(this->writeConnection.get(), &Communi::IrcConnection::messageReceived,
[this](auto msg) { this->writeConnectionMessageReceived(msg); });
// Listen to read connection message signals
this->readConnection.reset(new Communi::IrcConnection);
this->readConnection->moveToThread(QCoreApplication::instance()->thread());
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::messageReceived,
[this](auto msg) { this->messageReceived(msg); });
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::privateMessageReceived,
[this](auto msg) { this->privateMessageReceived(msg); });
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::connected,
[this] { this->onConnected(); });
QObject::connect(this->readConnection.get(), &Communi::IrcConnection::disconnected,
[this] { this->onDisconnected(); });
}
Communi::IrcConnection *AbstractIrcServer::getReadConnection() const
{
return this->readConnection.get();
}
void AbstractIrcServer::connect()
{
this->disconnect();
// if (this->hasSeperateWriteConnection()) {
this->initializeConnection(this->writeConnection.get(), false, true);
this->initializeConnection(this->readConnection.get(), true, false);
// } else {
// this->initializeConnection(this->readConnection.get(), true, true);
// }
// fourtf: this should be asynchronous
{
std::lock_guard<std::mutex> lock1(this->connectionMutex);
std::lock_guard<std::mutex> lock2(this->channelMutex);
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
std::shared_ptr<Channel> chan = weak.lock();
if (!chan) {
continue;
}
this->writeConnection->sendRaw("JOIN #" + chan->name);
this->readConnection->sendRaw("JOIN #" + chan->name);
}
this->writeConnection->open();
this->readConnection->open();
}
this->onConnected();
this->connected.invoke();
}
void AbstractIrcServer::disconnect()
{
std::lock_guard<std::mutex> locker(this->connectionMutex);
this->readConnection->close();
this->writeConnection->close();
}
void AbstractIrcServer::sendMessage(const QString &channelName, const QString &message)
{
std::lock_guard<std::mutex> locker(this->connectionMutex);
// fourtf: trim the message if it's sent from twitch chat
if (this->writeConnection) {
this->writeConnection->sendRaw("PRIVMSG #" + channelName + " :" + message);
}
}
void AbstractIrcServer::writeConnectionMessageReceived(Communi::IrcMessage *message)
{
}
std::shared_ptr<Channel> AbstractIrcServer::addChannel(const QString &channelName)
{
std::lock_guard<std::mutex> lock(this->channelMutex);
// value exists
auto it = this->channels.find(channelName);
if (it != this->channels.end()) {
std::shared_ptr<Channel> chan = it.value().lock();
if (chan) {
return chan;
}
}
// value doesn't exist
std::shared_ptr<Channel> chan = this->createChannel(channelName);
if (!chan) {
return Channel::getEmpty();
}
QString clojuresInCppAreShit = channelName;
this->channels.insert(channelName, chan);
chan->destroyed.connect([this, clojuresInCppAreShit] {
// fourtf: issues when the server itself in destroyed
debug::Log("[AbstractIrcServer::addChannel] {} was destroyed", clojuresInCppAreShit);
this->channels.remove(clojuresInCppAreShit);
});
// join irc channel
{
std::lock_guard<std::mutex> lock2(this->connectionMutex);
if (this->readConnection) {
this->readConnection->sendRaw("JOIN #" + channelName);
}
if (this->writeConnection) {
this->writeConnection->sendRaw("JOIN #" + channelName);
}
}
return chan;
}
std::shared_ptr<Channel> AbstractIrcServer::getChannel(const QString &channelName)
{
std::lock_guard<std::mutex> lock(this->channelMutex);
// value exists
auto it = this->channels.find(channelName);
if (it != this->channels.end()) {
std::shared_ptr<Channel> chan = it.value().lock();
if (chan) {
return chan;
}
}
return Channel::getEmpty();
}
void AbstractIrcServer::onConnected()
{
std::lock_guard<std::mutex> lock(this->channelMutex);
MessagePtr connMsg = Message::createSystemMessage("connected to chat");
MessagePtr reconnMsg = Message::createSystemMessage("reconnected to chat");
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
std::shared_ptr<Channel> chan = weak.lock();
if (!chan) {
continue;
}
LimitedQueueSnapshot<MessagePtr> snapshot = chan->getMessageSnapshot();
bool replaceMessage =
snapshot.getLength() > 0 &&
snapshot[snapshot.getLength() - 1]->flags & Message::DisconnectedMessage;
if (replaceMessage) {
chan->replaceMessage(snapshot[snapshot.getLength() - 1], reconnMsg);
continue;
}
chan->addMessage(connMsg);
}
}
void AbstractIrcServer::onDisconnected()
{
std::lock_guard<std::mutex> lock(this->channelMutex);
MessagePtr msg = Message::createSystemMessage("disconnected from chat");
msg->flags &= Message::DisconnectedMessage;
for (std::weak_ptr<Channel> &weak : this->channels.values()) {
std::shared_ptr<Channel> chan = weak.lock();
if (!chan) {
continue;
}
chan->addMessage(msg);
}
}
std::shared_ptr<Channel> AbstractIrcServer::getCustomChannel(const QString &channelName)
{
return nullptr;
}
void AbstractIrcServer::addFakeMessage(const QString &data)
{
auto fakeMessage = Communi::IrcMessage::fromData(data.toUtf8(), this->readConnection.get());
this->privateMessageReceived(qobject_cast<Communi::IrcPrivateMessage *>(fakeMessage));
}
void AbstractIrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
{
}
void AbstractIrcServer::messageReceived(Communi::IrcMessage *message)
{
}
} // namespace irc
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,63 @@
#pragma once
#include <IrcMessage>
#include <mutex>
#include <pajlada/signals/signal.hpp>
#include "channel.hpp"
namespace chatterino {
namespace providers {
namespace irc {
class AbstractIrcServer
{
public:
// connection
Communi::IrcConnection *getReadConnection() const;
void connect();
void disconnect();
void sendMessage(const QString &channelName, const QString &message);
// channels
std::shared_ptr<Channel> addChannel(const QString &channelName);
std::shared_ptr<Channel> getChannel(const QString &channelName);
// signals
pajlada::Signals::NoArgSignal connected;
pajlada::Signals::NoArgSignal disconnected;
pajlada::Signals::Signal<Communi::IrcPrivateMessage *> onPrivateMessage;
void addFakeMessage(const QString &data);
protected:
AbstractIrcServer();
virtual void initializeConnection(Communi::IrcConnection *connection, bool isRead,
bool isWrite) = 0;
virtual std::shared_ptr<Channel> createChannel(const QString &channelName) = 0;
virtual void privateMessageReceived(Communi::IrcPrivateMessage *message);
virtual void messageReceived(Communi::IrcMessage *message);
virtual void writeConnectionMessageReceived(Communi::IrcMessage *message);
virtual void onConnected();
virtual void onDisconnected();
virtual std::shared_ptr<Channel> getCustomChannel(const QString &channelName);
private:
void initConnection();
QMap<QString, std::weak_ptr<Channel>> channels;
std::unique_ptr<Communi::IrcConnection> writeConnection = nullptr;
std::unique_ptr<Communi::IrcConnection> readConnection = nullptr;
std::mutex connectionMutex;
std::mutex channelMutex;
};
} // namespace irc
} // namespace providers
} // namespace chatterino

View file

@ -3,6 +3,7 @@
#include "QString"
namespace chatterino {
namespace providers {
namespace twitch {
struct EmoteValue {
@ -29,4 +30,5 @@ private:
};
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -5,73 +5,83 @@
#include "debug/log.hpp"
#include "messages/limitedqueue.hpp"
#include "messages/message.hpp"
#include "singletons/channelmanager.hpp"
#include "providers/twitch/twitchchannel.hpp"
//#include "singletons/channelmanager.hpp"
#include "singletons/resourcemanager.hpp"
#include "singletons/windowmanager.hpp"
#include "twitch/twitchchannel.hpp"
#include "twitchserver.hpp"
using namespace chatterino::singletons;
using namespace chatterino::messages;
namespace chatterino {
namespace singletons {
namespace helper {
namespace providers {
namespace twitch {
IrcMessageHandler::IrcMessageHandler(ChannelManager &_channelManager,
ResourceManager &_resourceManager)
: channelManager(_channelManager)
, resourceManager(_resourceManager)
IrcMessageHandler::IrcMessageHandler(singletons::ResourceManager &_resourceManager)
: resourceManager(_resourceManager)
{
}
IrcMessageHandler &IrcMessageHandler::getInstance()
{
static IrcMessageHandler instance(ChannelManager::getInstance(),
ResourceManager::getInstance());
static IrcMessageHandler instance(singletons::ResourceManager::getInstance());
return instance;
}
void IrcMessageHandler::handleRoomStateMessage(Communi::IrcMessage *message)
{
const auto &tags = message->tags();
auto iterator = tags.find("room-id");
if (iterator != tags.end()) {
auto roomID = iterator.value().toString();
auto channel =
this->channelManager.getTwitchChannel(QString(message->toData()).split("#").at(1));
auto twitchChannel = dynamic_cast<twitch::TwitchChannel *>(channel.get());
if (twitchChannel != nullptr) {
QStringList words = QString(message->toData()).split("#");
// ensure the format is valid
if (words.length() < 2)
return;
QString channelName = words.at(1);
auto channel = TwitchServer::getInstance().getChannel(channelName);
if (auto twitchChannel = dynamic_cast<twitch::TwitchChannel *>(channel.get())) {
// set the room id of the channel
twitchChannel->setRoomID(roomID);
}
this->resourceManager.loadChannelData(roomID);
ResourceManager::getInstance().loadChannelData(roomID);
}
}
void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
{
assert(message->parameters().length() >= 1);
// check parameter count
if (message->parameters().length() < 1)
return;
auto rawChannelName = message->parameter(0);
QString chanName = message->parameter(0);
assert(rawChannelName.length() >= 2);
// check channel name length
if (chanName.length() >= 2)
return;
auto trimmedChannelName = rawChannelName.mid(1);
chanName = chanName.mid(1);
auto c = this->channelManager.getTwitchChannel(trimmedChannelName);
// get channel
auto chan = TwitchServer::getInstance().getChannel(chanName);
if (!c) {
debug::Log(
"[IrcMessageHandler:handleClearChatMessage] Channel {} not found in channel manager",
trimmedChannelName);
if (!chan) {
debug::Log("[IrcMessageHandler:handleClearChatMessage] Twitch channel {} not found",
chanName);
return;
}
// check if the chat has been cleared by a moderator
if (message->parameters().length() == 1) {
c->addMessage(Message::createSystemMessage("Chat has been cleared by a moderator."));
chan->addMessage(Message::createSystemMessage("Chat has been cleared by a moderator."));
return;
}
@ -92,7 +102,7 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
}
// add the notice that the user has been timed out
LimitedQueueSnapshot<MessagePtr> snapshot = c->getMessageSnapshot();
LimitedQueueSnapshot<MessagePtr> snapshot = chan->getMessageSnapshot();
bool addMessage = true;
int snapshotLength = snapshot.getLength();
@ -100,14 +110,14 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
if (snapshot[i]->flags & Message::Timeout && snapshot[i]->loginName == username) {
MessagePtr replacement(
Message::createTimeoutMessage(username, durationInSeconds, reason, true));
c->replaceMessage(snapshot[i], replacement);
chan->replaceMessage(snapshot[i], replacement);
addMessage = false;
break;
}
}
if (addMessage) {
c->addMessage(Message::createTimeoutMessage(username, durationInSeconds, reason, false));
chan->addMessage(Message::createTimeoutMessage(username, durationInSeconds, reason, false));
}
// disable the messages from the user
@ -118,7 +128,7 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
}
// refresh all
WindowManager::getInstance().repaintVisibleChatWidgets(c.get());
WindowManager::getInstance().repaintVisibleChatWidgets(chan.get());
}
void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
@ -129,7 +139,7 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
auto rawChannelName = message->parameters().at(0);
auto trimmedChannelName = rawChannelName.mid(1);
auto c = this->channelManager.getTwitchChannel(trimmedChannelName);
auto c = TwitchServer::getInstance().getChannel(trimmedChannelName);
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(c.get());
if (tc != nullptr) {
tc->setMod(_mod == "1");
@ -149,7 +159,8 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message)
void IrcMessageHandler::handleModeMessage(Communi::IrcMessage *message)
{
auto channel = channelManager.getTwitchChannel(message->parameter(0).remove(0, 1));
auto channel = TwitchServer::getInstance().getChannel(message->parameter(0).remove(0, 1));
if (message->parameter(1) == "+o") {
channel->modList.append(message->parameter(2));
} else if (message->parameter(1) == "-o") {
@ -165,24 +176,25 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
MessagePtr msg = Message::createSystemMessage(message->content());
if (broadcast) {
this->channelManager.doOnAll([msg](const auto &c) {
c->addMessage(msg); //
});
// fourtf: send to all twitch channels
// this->channelManager.doOnAll([msg](const auto &c) {
// c->addMessage(msg); //
// });
return;
}
auto trimmedChannelName = rawChannelName.mid(1);
auto c = this->channelManager.getTwitchChannel(trimmedChannelName);
auto channel = TwitchServer::getInstance().getChannel(trimmedChannelName);
if (!c) {
if (!channel) {
debug::Log("[IrcManager:handleNoticeMessage] Channel {} not found in channel manager",
trimmedChannelName);
return;
}
c->addMessage(msg);
channel->addMessage(msg);
}
void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message)
@ -202,6 +214,6 @@ void IrcMessageHandler::handleWriteConnectionNoticeMessage(Communi::IrcNoticeMes
this->handleNoticeMessage(message);
}
} // namespace helper
} // namespace singletons
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -6,14 +6,15 @@ namespace chatterino {
namespace singletons {
class ChannelManager;
class ResourceManager;
} // namespace singletons
namespace helper {
namespace providers {
namespace twitch {
class IrcMessageHandler
{
IrcMessageHandler(ChannelManager &channelManager, ResourceManager &resourceManager);
IrcMessageHandler(singletons::ResourceManager &resourceManager);
ChannelManager &channelManager;
ResourceManager &resourceManager;
singletons::ResourceManager &resourceManager;
public:
static IrcMessageHandler &getInstance();
@ -27,6 +28,6 @@ public:
void handleNoticeMessage(Communi::IrcNoticeMessage *message);
void handleWriteConnectionNoticeMessage(Communi::IrcNoticeMessage *message);
};
}
}
}
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,70 @@
#include "providers/twitch/twitchaccount.hpp"
#include "const.hpp"
#include "util/urlfetch.hpp"
namespace chatterino {
namespace providers {
namespace twitch {
TwitchAccount::TwitchAccount(const QString &_username, const QString &_oauthToken,
const QString &_oauthClient)
: userName(_username)
, oauthClient(_oauthClient)
, oauthToken(_oauthToken)
, _isAnon(_username == ANONYMOUS_USERNAME)
{
}
const QString &TwitchAccount::getUserName() const
{
return this->userName;
}
const QString &TwitchAccount::getOAuthClient() const
{
return this->oauthClient;
}
const QString &TwitchAccount::getOAuthToken() const
{
return this->oauthToken;
}
const QString &TwitchAccount::getUserId() const
{
return this->userId;
}
void TwitchAccount::setUserId(const QString &id)
{
this->userId = id;
}
bool TwitchAccount::setOAuthClient(const QString &newClientID)
{
if (this->oauthClient.compare(newClientID) == 0) {
return false;
}
this->oauthClient = newClientID;
return true;
}
bool TwitchAccount::setOAuthToken(const QString &newOAuthToken)
{
if (this->oauthToken.compare(newOAuthToken) == 0) {
return false;
}
this->oauthToken = newOAuthToken;
return true;
}
bool TwitchAccount::isAnon() const
{
return this->_isAnon;
}
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -1,19 +1,19 @@
#pragma once
#include "ircaccount.hpp"
#include <QString>
namespace chatterino {
namespace providers {
namespace twitch {
class TwitchUser : public IrcUser2
class TwitchAccount
{
public:
TwitchUser(const QString &username, const QString &oauthToken, const QString &oauthClient);
TwitchAccount(const QString &username, const QString &oauthToken, const QString &oauthClient);
const QString &getUserName() const;
const QString &getOAuthToken() const;
const QString &getOAuthClient() const;
const QString &getUserId() const;
void setUserId(const QString &id);
@ -28,11 +28,12 @@ public:
bool isAnon() const;
private:
QString _oauthClient;
QString _oauthToken;
QString _userId;
QString oauthClient;
QString oauthToken;
QString userId;
QString userName;
const bool _isAnon;
};
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -1,15 +1,16 @@
#include "twitchaccountmanager.hpp"
#include "providers/twitch/twitchaccountmanager.hpp"
#include "common.hpp"
#include "const.hpp"
#include "debug/log.hpp"
namespace chatterino {
namespace providers {
namespace twitch {
TwitchAccountManager::TwitchAccountManager()
{
this->anonymousUser.reset(new twitch::TwitchUser(twitch::ANONYMOUS_USERNAME, "", ""));
this->anonymousUser.reset(new TwitchAccount(ANONYMOUS_USERNAME, "", ""));
this->currentUsername.connect([this](const auto &newValue, auto) {
QString newUsername(QString::fromStdString(newValue));
@ -28,7 +29,7 @@ TwitchAccountManager::TwitchAccountManager()
});
}
std::shared_ptr<twitch::TwitchUser> TwitchAccountManager::getCurrent()
std::shared_ptr<TwitchAccount> TwitchAccountManager::getCurrent()
{
if (!this->currentUser) {
return this->anonymousUser;
@ -50,7 +51,7 @@ std::vector<QString> TwitchAccountManager::getUsernames() const
return userNames;
}
std::shared_ptr<twitch::TwitchUser> TwitchAccountManager::findUserByUsername(
std::shared_ptr<TwitchAccount> TwitchAccountManager::findUserByUsername(
const QString &username) const
{
std::lock_guard<std::mutex> lock(this->mutex);
@ -107,7 +108,7 @@ void TwitchAccountManager::reloadUsers()
} break;
case AddUserResponse::UserValuesUpdated: {
debug::Log("User {} already exists, and values updated!", userData.username);
if (userData.username == this->getCurrent()->getNickName()) {
if (userData.username == this->getCurrent()->getUserName()) {
debug::Log("It was the current user, so we need to reconnect stuff!");
this->userChanged.invoke();
}
@ -132,7 +133,7 @@ bool TwitchAccountManager::removeUser(const QString &username)
this->mutex.lock();
this->users.erase(std::remove_if(this->users.begin(), this->users.end(), [username](auto user) {
if (user->getNickName() == username) {
if (user->getUserName() == username) {
std::string userID(user->getUserId().toStdString());
assert(!userID.empty());
pajlada::Settings::SettingManager::removeSetting("/accounts/uid" + userID);
@ -174,8 +175,8 @@ TwitchAccountManager::AddUserResponse TwitchAccountManager::addUser(
}
}
auto newUser = std::make_shared<twitch::TwitchUser>(userData.username, userData.oauthToken,
userData.clientID);
auto newUser =
std::make_shared<TwitchAccount>(userData.username, userData.oauthToken, userData.clientID);
// Set users User ID without the uid prefix
newUser->setUserId(userData.userID);
@ -188,4 +189,5 @@ TwitchAccountManager::AddUserResponse TwitchAccountManager::addUser(
}
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -1,6 +1,6 @@
#pragma once
#include "twitch/twitchuser.hpp"
#include "providers/twitch/twitchaccount.hpp"
#include <pajlada/settings/setting.hpp>
@ -16,9 +16,8 @@ namespace chatterino {
namespace singletons {
class AccountManager;
}
namespace providers {
namespace twitch {
class TwitchAccountManager
{
TwitchAccountManager();
@ -32,11 +31,11 @@ public:
};
// Returns the current twitchUsers, or the anonymous user if we're not currently logged in
std::shared_ptr<twitch::TwitchUser> getCurrent();
std::shared_ptr<TwitchAccount> getCurrent();
std::vector<QString> getUsernames() const;
std::shared_ptr<twitch::TwitchUser> findUserByUsername(const QString &username) const;
std::shared_ptr<TwitchAccount> findUserByUsername(const QString &username) const;
bool userExists(const QString &username) const;
void reloadUsers();
@ -55,14 +54,14 @@ private:
};
AddUserResponse addUser(const UserData &data);
std::shared_ptr<twitch::TwitchUser> currentUser;
std::shared_ptr<TwitchAccount> currentUser;
std::shared_ptr<twitch::TwitchUser> anonymousUser;
std::vector<std::shared_ptr<twitch::TwitchUser>> users;
std::shared_ptr<TwitchAccount> anonymousUser;
std::vector<std::shared_ptr<TwitchAccount>> users;
mutable std::mutex mutex;
friend class chatterino::singletons::AccountManager;
};
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -1,20 +1,22 @@
#include "twitchchannel.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "common.hpp"
#include "debug/log.hpp"
#include "messages/message.hpp"
#include "providers/twitch/twitchmessagebuilder.hpp"
#include "singletons/channelmanager.hpp"
#include "singletons/emotemanager.hpp"
#include "singletons/ircmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "twitch/twitchmessagebuilder.hpp"
#include "util/urlfetch.hpp"
#include <IrcConnection>
#include <QThread>
#include <QTimer>
namespace chatterino {
namespace providers {
namespace twitch {
TwitchChannel::TwitchChannel(const QString &channelName)
TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection *_readConnection)
: Channel(channelName)
, bttvChannelEmotes(new util::EmoteMap)
, ffzChannelEmotes(new util::EmoteMap)
@ -23,6 +25,7 @@ TwitchChannel::TwitchChannel(const QString &channelName)
, popoutPlayerURL("https://player.twitch.tv/?channel=" + name)
, isLive(false)
, mod(false)
, readConnecetion(_readConnection)
{
debug::Log("[TwitchChannel:{}] Opened", this->name);
@ -42,9 +45,6 @@ TwitchChannel::TwitchChannel(const QString &channelName)
this->fetchRecentMessages(); //
});
this->connectedConnection = singletons::IrcManager::getInstance().connected.connect(
[this] { this->userStateChanged(); });
this->messageSuffix.append(' ');
this->messageSuffix.append(QChar(0x206D));
}
@ -95,8 +95,9 @@ void TwitchChannel::sendMessage(const QString &message)
parsedMessage = parsedMessage.trimmed();
if (parsedMessage.isEmpty())
if (parsedMessage.isEmpty()) {
return;
}
if (singletons::SettingManager::getInstance().allowDuplicateMessages) {
if (parsedMessage == this->lastSentMessage) {
@ -106,7 +107,7 @@ void TwitchChannel::sendMessage(const QString &message)
}
}
singletons::IrcManager::getInstance().sendMessage(this->name, parsedMessage);
this->sendMessageSignal.invoke(this->name, parsedMessage);
}
bool TwitchChannel::isMod() const
@ -227,7 +228,7 @@ void TwitchChannel::fetchRecentMessages()
}
TwitchChannel *channel = dynamic_cast<TwitchChannel *>(shared.get());
static auto readConnection = singletons::IrcManager::getInstance().getReadConnection();
static auto readConnection = channel->readConnecetion;
auto msgArray = obj.value("messages").toArray();
if (msgArray.size() > 0) {
@ -249,4 +250,5 @@ void TwitchChannel::fetchRecentMessages()
});
}
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -1,19 +1,22 @@
#pragma once
#include <IrcConnection>
#include "channel.hpp"
#include "common.hpp"
#include "singletons/emotemanager.hpp"
#include "singletons/ircmanager.hpp"
#include "util/concurrentmap.hpp"
namespace chatterino {
namespace providers {
namespace twitch {
class TwitchChannel : public Channel
class TwitchServer;
class TwitchChannel final : public Channel
{
QTimer *liveStatusTimer;
public:
explicit TwitchChannel(const QString &channelName);
~TwitchChannel();
void reloadChannelEmotes();
@ -49,6 +52,8 @@ public:
QString streamUptime;
private:
explicit TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection);
void setLive(bool newLiveStatus);
void refreshLiveStatus();
@ -59,7 +64,11 @@ private:
bool mod;
QByteArray messageSuffix;
QString lastSentMessage;
};
Communi::IrcConnection *readConnecetion;
friend class TwitchServer;
};
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -1,12 +1,12 @@
#include "twitchmessagebuilder.hpp"
#include "providers/twitch/twitchmessagebuilder.hpp"
#include "debug/log.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "singletons/emotemanager.hpp"
#include "singletons/ircmanager.hpp"
#include "singletons/resourcemanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "singletons/windowmanager.hpp"
#include "twitch/twitchchannel.hpp"
#include <QApplication>
#include <QDebug>
@ -15,6 +15,7 @@
using namespace chatterino::messages;
namespace chatterino {
namespace providers {
namespace twitch {
TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel,
@ -707,4 +708,5 @@ bool TwitchMessageBuilder::tryParseCheermote(const QString &string)
return false;
}
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -12,6 +12,7 @@
namespace chatterino {
class Channel;
namespace providers {
namespace twitch {
class TwitchChannel;
@ -64,4 +65,5 @@ private:
};
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,148 @@
#include "twitchserver.hpp"
#include "providers/twitch/ircmessagehandler.hpp"
#include "providers/twitch/twitchaccount.hpp"
#include "providers/twitch/twitchmessagebuilder.hpp"
#include "singletons/accountmanager.hpp"
#include "util/posttothread.hpp"
#include <cassert>
using namespace Communi;
using namespace chatterino::singletons;
namespace chatterino {
namespace providers {
namespace twitch {
TwitchServer::TwitchServer()
: whispersChannel(new Channel("/mentions"))
, mentionsChannel(new Channel("/mentions"))
{
AccountManager::getInstance().Twitch.userChanged.connect([this]() { //
util::postToThread([this] { this->connect(); });
});
}
TwitchServer &TwitchServer::getInstance()
{
static TwitchServer s;
return s;
}
void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, bool isWrite)
{
std::shared_ptr<TwitchAccount> account = AccountManager::getInstance().Twitch.getCurrent();
QString username = account->getUserName();
// QString oauthClient = account->getOAuthClient();
QString oauthToken = account->getOAuthToken();
if (!oauthToken.startsWith("oauth:")) {
oauthToken.prepend("oauth:");
}
connection->setUserName(username);
connection->setNickName(username);
connection->setRealName(username);
if (!account->isAnon()) {
connection->setPassword(oauthToken);
// fourtf: ignored users
// this->refreshIgnoredUsers(username, oauthClient, oauthToken);
}
connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/membership"));
connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands"));
connection->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags"));
connection->setHost("irc.chat.twitch.tv");
connection->setPort(6667);
}
std::shared_ptr<Channel> TwitchServer::createChannel(const QString &channelName)
{
TwitchChannel *channel = new TwitchChannel(channelName, this->getReadConnection());
channel->sendMessageSignal.connect(
[this](auto chan, auto msg) { this->sendMessage(chan, msg); });
return std::shared_ptr<Channel>(channel);
}
void TwitchServer::privateMessageReceived(IrcPrivateMessage *message)
{
this->onPrivateMessage.invoke(message);
auto chan = TwitchServer::getInstance().getChannel(message->target().mid(1));
if (!chan) {
return;
}
messages::MessageParseArgs args;
TwitchMessageBuilder builder(chan.get(), message, args);
if (!builder.isIgnored()) {
messages::MessagePtr _message = builder.build();
if (_message->flags & messages::Message::Highlighted) {
TwitchServer::getInstance().mentionsChannel->addMessage(_message);
}
chan->addMessage(_message);
}
}
void TwitchServer::messageReceived(IrcMessage *message)
{
// this->readConnection
if (message->type() == IrcMessage::Type::Private) {
// We already have a handler for private messages
return;
}
const QString &command = message->command();
if (command == "ROOMSTATE") {
IrcMessageHandler::getInstance().handleRoomStateMessage(message);
} else if (command == "CLEARCHAT") {
IrcMessageHandler::getInstance().handleClearChatMessage(message);
} else if (command == "USERSTATE") {
IrcMessageHandler::getInstance().handleUserStateMessage(message);
} else if (command == "WHISPER") {
IrcMessageHandler::getInstance().handleWhisperMessage(message);
} else if (command == "USERNOTICE") {
IrcMessageHandler::getInstance().handleUserNoticeMessage(message);
} else if (command == "MODE") {
IrcMessageHandler::getInstance().handleModeMessage(message);
} else if (command == "NOTICE") {
IrcMessageHandler::getInstance().handleNoticeMessage(
static_cast<IrcNoticeMessage *>(message));
}
}
void TwitchServer::writeConnectionMessageReceived(IrcMessage *message)
{
switch (message->type()) {
case IrcMessage::Type::Notice: {
IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage(
static_cast<IrcNoticeMessage *>(message));
} break;
}
}
std::shared_ptr<Channel> TwitchServer::getCustomChannel(const QString &channelName)
{
if (channelName == "/whispers") {
return whispersChannel;
}
if (channelName == "/mentions") {
return mentionsChannel;
}
return nullptr;
}
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -0,0 +1,35 @@
#pragma once
#include <memory>
#include "providers/irc/abstractircserver.hpp"
#include "providers/twitch/twitchaccount.hpp"
#include "providers/twitch/twitchchannel.hpp"
namespace chatterino {
namespace providers {
namespace twitch {
class TwitchServer final : public irc::AbstractIrcServer
{
TwitchServer();
public:
static TwitchServer &getInstance();
const ChannelPtr whispersChannel;
const ChannelPtr mentionsChannel;
protected:
virtual void initializeConnection(Communi::IrcConnection *connection, bool isRead,
bool isWrite) override;
virtual std::shared_ptr<Channel> createChannel(const QString &channelName) override;
virtual void privateMessageReceived(Communi::IrcPrivateMessage *message) override;
virtual void messageReceived(Communi::IrcMessage *message) override;
virtual void writeConnectionMessageReceived(Communi::IrcMessage *message) override;
virtual std::shared_ptr<Channel> getCustomChannel(const QString &channelname) override;
};
} // namespace twitch
} // namespace providers
} // namespace chatterino

View file

@ -43,5 +43,5 @@ void AccountManager::load()
this->Twitch.userChanged.invoke();
}
} // namespace singletons
} // namespace chatterino
}

View file

@ -1,6 +1,6 @@
#pragma once
#include "twitch/twitchaccountmanager.hpp"
#include "providers/twitch/twitchaccountmanager.hpp"
namespace chatterino {
namespace singletons {
@ -14,7 +14,7 @@ public:
void load();
twitch::TwitchAccountManager Twitch;
providers::twitch::TwitchAccountManager Twitch;
};
} // namespace singletons

View file

@ -1,142 +1,140 @@
#include "singletons/channelmanager.hpp"
#include "singletons/ircmanager.hpp"
//#include "singletons/channelmanager.hpp"
//#include "singletons/ircmanager.hpp"
using namespace chatterino::twitch;
// namespace chatterino {
// namespace singletons {
namespace chatterino {
namespace singletons {
// ChannelManager &ChannelManager::getInstance()
//{
// static ChannelManager instance;
// return instance;
//}
ChannelManager &ChannelManager::getInstance()
{
static ChannelManager instance;
return instance;
}
// ChannelManager::ChannelManager()
// : whispersChannel(new Channel("/whispers"))
// , mentionsChannel(new Channel("/mentions"))
// , emptyChannel(new Channel(""))
//{
//}
ChannelManager::ChannelManager()
: whispersChannel(new Channel("/whispers"))
, mentionsChannel(new Channel("/mentions"))
, emptyChannel(new Channel(""))
{
}
// const std::vector<ChannelPtr> ChannelManager::getItems()
//{
// QMutexLocker locker(&this->channelsMutex);
const std::vector<ChannelPtr> ChannelManager::getItems()
{
QMutexLocker locker(&this->channelsMutex);
// std::vector<ChannelPtr> items;
std::vector<ChannelPtr> items;
// for (auto &item : this->twitchChannels.values()) {
// items.push_back(std::get<0>(item));
// }
for (auto &item : this->twitchChannels.values()) {
items.push_back(std::get<0>(item));
}
// return items;
//}
return items;
}
// ChannelPtr ChannelManager::addTwitchChannel(const QString &rawChannelName)
//{
// QString channelName = rawChannelName.toLower();
ChannelPtr ChannelManager::addTwitchChannel(const QString &rawChannelName)
{
QString channelName = rawChannelName.toLower();
// if (channelName.length() > 1 && channelName.at(0) == '/') {
// return this->getTwitchChannel(channelName);
// }
if (channelName.length() > 1 && channelName.at(0) == '/') {
return this->getTwitchChannel(channelName);
}
// if (channelName.length() > 0 && channelName.at(0) == '#') {
// channelName = channelName.mid(1);
// }
if (channelName.length() > 0 && channelName.at(0) == '#') {
channelName = channelName.mid(1);
}
// QMutexLocker locker(&this->channelsMutex);
QMutexLocker locker(&this->channelsMutex);
// auto it = this->twitchChannels.find(channelName);
auto it = this->twitchChannels.find(channelName);
// if (it == this->twitchChannels.end()) {
// auto channel = std::make_shared<TwitchChannel>(channelName);
if (it == this->twitchChannels.end()) {
auto channel = std::make_shared<TwitchChannel>(channelName);
// this->twitchChannels.insert(channelName, std::make_tuple(channel, 1));
this->twitchChannels.insert(channelName, std::make_tuple(channel, 1));
// this->ircJoin.invoke(channelName);
this->ircJoin.invoke(channelName);
// return channel;
// }
return channel;
}
// std::get<1>(it.value())++;
std::get<1>(it.value())++;
// return std::get<0>(it.value());
//}
return std::get<0>(it.value());
}
// ChannelPtr ChannelManager::getTwitchChannel(const QString &channel)
//{
// QMutexLocker locker(&this->channelsMutex);
ChannelPtr ChannelManager::getTwitchChannel(const QString &channel)
{
QMutexLocker locker(&this->channelsMutex);
// QString c = channel.toLower();
QString c = channel.toLower();
// if (channel.length() > 1 && channel.at(0) == '/') {
// if (c == "/whispers") {
// return whispersChannel;
// }
if (channel.length() > 1 && channel.at(0) == '/') {
if (c == "/whispers") {
return whispersChannel;
}
// if (c == "/mentions") {
// return mentionsChannel;
// }
if (c == "/mentions") {
return mentionsChannel;
}
// return emptyChannel;
// }
return emptyChannel;
}
// auto a = this->twitchChannels.find(c);
auto a = this->twitchChannels.find(c);
// if (a == this->twitchChannels.end()) {
// return emptyChannel;
// }
if (a == this->twitchChannels.end()) {
return emptyChannel;
}
// return std::get<0>(a.value());
//}
return std::get<0>(a.value());
}
// void ChannelManager::removeTwitchChannel(const QString &channel)
//{
// QMutexLocker locker(&this->channelsMutex);
void ChannelManager::removeTwitchChannel(const QString &channel)
{
QMutexLocker locker(&this->channelsMutex);
// if (channel.length() > 1 && channel.at(0) == '/') {
// return;
// }
if (channel.length() > 1 && channel.at(0) == '/') {
return;
}
// QString c = channel.toLower();
QString c = channel.toLower();
// auto a = this->twitchChannels.find(c);
auto a = this->twitchChannels.find(c);
// if (a == this->twitchChannels.end()) {
// return;
// }
if (a == this->twitchChannels.end()) {
return;
}
// std::get<1>(a.value())--;
std::get<1>(a.value())--;
// if (std::get<1>(a.value()) == 0) {
// this->ircPart.invoke(c);
// this->twitchChannels.remove(c);
// }
//}
if (std::get<1>(a.value()) == 0) {
this->ircPart.invoke(c);
this->twitchChannels.remove(c);
}
}
// const std::string &ChannelManager::getUserID(const std::string &username)
//{
// /* TODO: Implement
// auto it = this->usernameToID.find(username);
const std::string &ChannelManager::getUserID(const std::string &username)
{
/* TODO: Implement
auto it = this->usernameToID.find(username);
// if (it != std::end(this->usernameToID)) {
// return *it;
// }
// */
if (it != std::end(this->usernameToID)) {
return *it;
}
*/
// static std::string temporary = "xd";
// return temporary;
//}
static std::string temporary = "xd";
return temporary;
}
// void ChannelManager::doOnAll(std::function<void(ChannelPtr)> func)
//{
// for (const auto &channel : this->twitchChannels) {
// func(std::get<0>(channel));
// }
void ChannelManager::doOnAll(std::function<void(ChannelPtr)> func)
{
for (const auto &channel : this->twitchChannels) {
func(std::get<0>(channel));
}
// func(this->whispersChannel);
// func(this->mentionsChannel);
//}
func(this->whispersChannel);
func(this->mentionsChannel);
}
} // namespace chatterino
}
//} // namespace singletons
//} // namespace chatterino

View file

@ -1,49 +1,44 @@
#pragma once
//#pragma once
#include "channel.hpp"
#include "channeldata.hpp"
#include "twitch/twitchchannel.hpp"
//#include "channel.hpp"
//#include "channeldata.hpp"
//#include "providers/twitch/twitchchannel.hpp"
#include <map>
//#include <map>
namespace chatterino {
namespace singletons {
class IrcManager;
// namespace chatterino {
// namespace singletons {
// class IrcManager;
class ChannelManager
{
explicit ChannelManager();
// class ChannelManager
//{
// explicit ChannelManager();
public:
static ChannelManager &getInstance();
// public:
// static ChannelManager &getInstance();
const std::vector<ChannelPtr> getItems();
// const std::vector<ChannelPtr> getItems();
ChannelPtr addTwitchChannel(const QString &channel);
ChannelPtr getTwitchChannel(const QString &channel);
void removeTwitchChannel(const QString &channel);
// const std::string &getUserID(const std::string &username);
const std::string &getUserID(const std::string &username);
// void doOnAll(std::function<void(ChannelPtr)> func);
void doOnAll(std::function<void(ChannelPtr)> func);
// // Special channels
// const ChannelPtr whispersChannel;
// const ChannelPtr mentionsChannel;
// const ChannelPtr emptyChannel;
// Special channels
const ChannelPtr whispersChannel;
const ChannelPtr mentionsChannel;
const ChannelPtr emptyChannel;
// private:
// std::map<std::string, std::string> usernameToID;
// std::map<std::string, ChannelData> channelDatas;
private:
std::map<std::string, std::string> usernameToID;
std::map<std::string, ChannelData> channelDatas;
// QMutex channelsMutex;
// QMap<QString, std::tuple<std::shared_ptr<TwitchChannel>, int>> twitchChannels;
QMutex channelsMutex;
QMap<QString, std::tuple<std::shared_ptr<twitch::TwitchChannel>, int>> twitchChannels;
// pajlada::Signals::Signal<const QString &> ircJoin;
// pajlada::Signals::Signal<const QString &> ircPart;
pajlada::Signals::Signal<const QString &> ircJoin;
pajlada::Signals::Signal<const QString &> ircPart;
friend class singletons::IrcManager;
};
} // namespace chatterino
}
// friend class singletons::IrcManager;
//};
//} // namespace singletons
//} // namespace chatterino

View file

@ -6,7 +6,9 @@
#include <QRegularExpression>
#include "channel.hpp"
#include "twitch/twitchchannel.hpp"
#include "providers/twitch/twitchchannel.hpp"
using namespace chatterino::providers::twitch;
namespace chatterino {
namespace singletons {
@ -88,8 +90,7 @@ QStringList CommandManager::getCommands()
return this->commandsStringList;
}
QString CommandManager::execCommand(const QString &text, ChannelPtr channel,
bool dryRun)
QString CommandManager::execCommand(const QString &text, ChannelPtr channel, bool dryRun)
{
QStringList words = text.split(' ', QString::SkipEmptyParts);
Command command;
@ -104,7 +105,7 @@ QString CommandManager::execCommand(const QString &text, ChannelPtr channel,
QString commandName = words[0];
// check if default command exists
auto *twitchChannel = dynamic_cast<twitch::TwitchChannel *>(channel.get());
auto *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (!dryRun && twitchChannel != nullptr) {
if (commandName == "/uptime") {
@ -115,22 +116,26 @@ QString CommandManager::execCommand(const QString &text, ChannelPtr channel,
return "";
} else if (commandName == "/ignore" && words.size() >= 2) {
QString messageText;
// fourtf: ignore user
// QString messageText;
if (IrcManager::getInstance().tryAddIgnoredUser(words.at(1), messageText)) {
messageText = "Ignored user \"" + words.at(1) + "\".";
}
// if (IrcManager::getInstance().tryAddIgnoredUser(words.at(1),
// messageText)) {
// messageText = "Ignored user \"" + words.at(1) + "\".";
// }
channel->addMessage(messages::Message::createSystemMessage(messageText));
// channel->addMessage(messages::Message::createSystemMessage(messageText));
return "";
} else if (commandName == "/unignore") {
QString messageText;
// fourtf: ignore user
// QString messageText;
if (IrcManager::getInstance().tryRemoveIgnoredUser(words.at(1), messageText)) {
messageText = "Ignored user \"" + words.at(1) + "\".";
}
// if (IrcManager::getInstance().tryRemoveIgnoredUser(words.at(1),
// messageText)) {
// messageText = "Ignored user \"" + words.at(1) + "\".";
// }
channel->addMessage(messages::Message::createSystemMessage(messageText));
// channel->addMessage(messages::Message::createSystemMessage(messageText));
return "";
}
}

View file

@ -13,18 +13,18 @@ CompletionManager &CompletionManager::getInstance()
return instance;
}
CompletionModel *CompletionManager::createModel(const std::string &channelName)
CompletionModel *CompletionManager::createModel(const QString &channelName)
{
auto it = this->models.find(channelName);
if (it != this->models.end()) {
return it->second;
}
CompletionModel *ret = new CompletionModel(qS(channelName));
CompletionModel *ret = new CompletionModel(channelName);
this->models[channelName] = ret;
return ret;
}
} // namespace singletons
} // namespace chatterino
}

View file

@ -16,11 +16,11 @@ class CompletionManager
public:
static CompletionManager &getInstance();
CompletionModel *createModel(const std::string &channelName);
CompletionModel *createModel(const QString &channelName);
private:
std::map<std::string, CompletionModel *> models;
std::map<QString, CompletionModel *> models;
};
} // namespace singletons
} // namespace chatterino
}

View file

@ -18,6 +18,7 @@
#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}"
using namespace chatterino::providers::twitch;
using namespace chatterino::messages;
namespace chatterino {
@ -206,7 +207,7 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName,
});
}
util::ConcurrentMap<QString, twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
util::ConcurrentMap<QString, providers::twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
{
return _twitchEmotes;
}
@ -420,9 +421,9 @@ QString EmoteManager::replaceShortCodes(const QString &text)
return ret;
}
void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<twitch::TwitchUser> &user)
void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<TwitchAccount> &user)
{
debug::Log("Loading Twitch emotes for user {}", user->getNickName());
debug::Log("Loading Twitch emotes for user {}", user->getUserName());
const auto &roomID = user->getUserId();
const auto &clientID = user->getOAuthClient();
@ -462,9 +463,7 @@ void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<twitch::TwitchUser>
}
emoteData.filled = true;
}
);
});
}
void EmoteManager::loadBTTVEmotes()

View file

@ -4,9 +4,9 @@
#include "emojis.hpp"
#include "messages/image.hpp"
#include "providers/twitch/emotevalue.hpp"
#include "providers/twitch/twitchaccount.hpp"
#include "signalvector.hpp"
#include "twitch/emotevalue.hpp"
#include "twitch/twitchuser.hpp"
#include "util/concurrentmap.hpp"
#include "util/emotemap.hpp"
@ -38,7 +38,7 @@ public:
void reloadFFZChannelEmotes(const QString &channelName,
std::weak_ptr<util::EmoteMap> channelEmoteMap);
util::ConcurrentMap<QString, twitch::EmoteValue *> &getTwitchEmotes();
util::ConcurrentMap<QString, providers::twitch::EmoteValue *> &getTwitchEmotes();
util::EmoteMap &getFFZEmotes();
util::EmoteMap &getChatterinoEmotes();
util::EmoteMap &getBTTVChannelEmoteFromCaches();
@ -92,7 +92,7 @@ public:
std::vector<std::string> emojiShortCodes;
/// Twitch emotes
void refreshTwitchEmotes(const std::shared_ptr<twitch::TwitchUser> &user);
void refreshTwitchEmotes(const std::shared_ptr<providers::twitch::TwitchAccount> &user);
struct TwitchAccountEmoteData {
struct TwitchEmote {
@ -112,7 +112,7 @@ public:
private:
// emote code
util::ConcurrentMap<QString, twitch::EmoteValue *> _twitchEmotes;
util::ConcurrentMap<QString, providers::twitch::EmoteValue *> _twitchEmotes;
// emote id
util::ConcurrentMap<long, util::EmoteData> _twitchEmoteFromCache;

View file

@ -61,18 +61,19 @@ void CompletionModel::refresh()
}
// Channel-specific: Usernames
auto c = singletons::ChannelManager::getInstance().getTwitchChannel(this->channelName);
auto usernames = c->getUsernamesForCompletions();
for (const auto &name : usernames) {
assert(!name.displayName.isEmpty());
this->addString(name.displayName);
this->addString('@' + name.displayName);
// fourtf: only works with twitch chat
// auto c = singletons::ChannelManager::getInstance().getTwitchChannel(this->channelName);
// auto usernames = c->getUsernamesForCompletions();
// for (const auto &name : usernames) {
// assert(!name.displayName.isEmpty());
// this->addString(name.displayName);
// this->addString('@' + name.displayName);
if (!name.localizedName.isEmpty()) {
this->addString(name.localizedName);
this->addString('@' + name.localizedName);
}
}
// if (!name.localizedName.isEmpty()) {
// this->addString(name.localizedName);
// this->addString('@' + name.localizedName);
// }
// }
}
void CompletionModel::addString(const std::string &str)
@ -86,5 +87,5 @@ void CompletionModel::addString(const QString &str)
// Always add a space at the end of completions
this->emotes.push_back(str + " ");
}
}
}
} // namespace singletons
} // namespace chatterino

View file

@ -2,15 +2,14 @@
#include "channel.hpp"
#include "debug/log.hpp"
#include "messages/messageparseargs.hpp"
#include "providers/twitch/twitchaccount.hpp"
#include "providers/twitch/twitchmessagebuilder.hpp"
#include "singletons/accountmanager.hpp"
#include "singletons/channelmanager.hpp"
#include "singletons/emotemanager.hpp"
#include "singletons/helper/ircmessagehandler.hpp"
#include "singletons/resourcemanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/windowmanager.hpp"
#include "twitch/twitchmessagebuilder.hpp"
#include "twitch/twitchuser.hpp"
#include "util/posttothread.hpp"
#include "util/urlfetch.hpp"
@ -25,412 +24,122 @@
using namespace chatterino::messages;
namespace chatterino {
namespace singletons {
IrcManager::IrcManager(ChannelManager &_channelManager, ResourceManager &_resources,
AccountManager &_accountManager)
: channelManager(_channelManager)
, resources(_resources)
, accountManager(_accountManager)
{
this->account = accountManager.Twitch.getCurrent();
accountManager.Twitch.userChanged.connect([this]() {
this->setUser(accountManager.Twitch.getCurrent());
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);
// join and part chats on event
ChannelManager::getInstance().ircJoin.connect(
[this](const QString &name) { this->joinChannel(name); });
ChannelManager::getInstance().ircPart.connect(
[this](const QString &name) { this->partChannel(name); });
}
IrcManager &IrcManager::getInstance()
{
static IrcManager instance(ChannelManager::getInstance(),
singletons::ResourceManager::getInstance(),
AccountManager::getInstance());
return instance;
}
void IrcManager::setUser(std::shared_ptr<twitch::TwitchUser> newAccount)
{
this->account = newAccount;
}
void IrcManager::connect()
{
this->disconnect();
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();
}
void IrcManager::initializeConnection(const std::unique_ptr<Communi::IrcConnection> &connection,
bool isReadConnection)
{
assert(this->account);
QString username = this->account->getUserName();
QString oauthClient = this->account->getOAuthClient();
QString oauthToken = this->account->getOAuthToken();
if (!oauthToken.startsWith("oauth:")) {
oauthToken.prepend("oauth:");
}
connection->setUserName(username);
connection->setNickName(username);
connection->setRealName(username);
if (!this->account->isAnon()) {
connection->setPassword(oauthToken);
this->refreshIgnoredUsers(username, oauthClient, oauthToken);
}
if (isReadConnection) {
connection->sendCommand(
Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership"));
connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
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->setPort(6667);
}
void IrcManager::refreshIgnoredUsers(const QString &username, const QString &oauthClient,
const QString &oauthToken)
{
QString nextLink = "https://api.twitch.tv/kraken/users/" + username + "/blocks?limit=" + 100 +
"&client_id=" + oauthClient;
QNetworkAccessManager *manager = new QNetworkAccessManager();
QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken));
QNetworkReply *reply = manager->get(req);
QObject::connect(reply, &QNetworkReply::finished, [=] {
this->twitchBlockedUsersMutex.lock();
this->twitchBlockedUsers.clear();
this->twitchBlockedUsersMutex.unlock();
QByteArray data = reply->readAll();
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
QJsonObject root = jsonDoc.object();
// nextLink =
// root.value("this->links").toObject().value("next").toString();
auto blocks = root.value("blocks").toArray();
this->twitchBlockedUsersMutex.lock();
for (QJsonValue block : blocks) {
QJsonObject user = block.toObject().value("user").toObject();
// displaythis->name
this->twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
}
this->twitchBlockedUsersMutex.unlock();
manager->deleteLater();
});
}
void IrcManager::beginConnecting()
{
std::lock_guard<std::mutex> locker(this->connectionMutex);
for (auto &channel : this->channelManager.getItems()) {
this->writeConnection->sendRaw("JOIN #" + channel->name);
this->readConnection->sendRaw("JOIN #" + channel->name);
}
this->writeConnection->open();
this->readConnection->open();
this->connected();
}
void IrcManager::disconnect()
{
std::lock_guard<std::mutex> locker(this->connectionMutex);
this->readConnection->close();
this->writeConnection->close();
}
void IrcManager::sendMessage(const QString &channelName, QString message)
{
QString trimmedMessage = message.trimmed();
if (trimmedMessage.isEmpty()) {
return;
}
this->connectionMutex.lock();
if (this->writeConnection) {
this->writeConnection->sendRaw("PRIVMSG #" + channelName + " :" + trimmedMessage);
}
this->connectionMutex.unlock();
}
void IrcManager::joinChannel(const QString &channelName)
{
this->connectionMutex.lock();
if (this->readConnection && this->writeConnection) {
this->readConnection->sendRaw("JOIN #" + channelName);
this->writeConnection->sendRaw("JOIN #" + channelName);
}
this->connectionMutex.unlock();
}
void IrcManager::partChannel(const QString &channelName)
{
this->connectionMutex.lock();
if (this->readConnection && this->writeConnection) {
this->readConnection->sendRaw("PART #" + channelName);
this->writeConnection->sendRaw("PART #" + channelName);
}
this->connectionMutex.unlock();
}
void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message)
{
this->onPrivateMessage.invoke(message);
auto c = this->channelManager.getTwitchChannel(message->target().mid(1));
if (!c) {
return;
}
// auto xd = message->content();
// auto xd2 = message->toData();
// debug::Log("HEHE: {}", xd2.toStdString());
messages::MessageParseArgs args;
twitch::TwitchMessageBuilder builder(c.get(), message, args);
if (!builder.isIgnored()) {
messages::MessagePtr _message = builder.build();
if (_message->flags & messages::Message::Highlighted) {
singletons::ChannelManager::getInstance().mentionsChannel->addMessage(_message);
}
c->addMessage(_message);
}
}
void IrcManager::messageReceived(Communi::IrcMessage *message)
{
if (message->type() == Communi::IrcMessage::Type::Private) {
// We already have a handler for private messages
return;
}
const QString &command = message->command();
if (command == "ROOMSTATE") {
helper::IrcMessageHandler::getInstance().handleRoomStateMessage(message);
} else if (command == "CLEARCHAT") {
helper::IrcMessageHandler::getInstance().handleClearChatMessage(message);
} else if (command == "USERSTATE") {
helper::IrcMessageHandler::getInstance().handleUserStateMessage(message);
} else if (command == "WHISPER") {
helper::IrcMessageHandler::getInstance().handleWhisperMessage(message);
} else if (command == "USERNOTICE") {
helper::IrcMessageHandler::getInstance().handleUserNoticeMessage(message);
} else if (command == "MODE") {
helper::IrcMessageHandler::getInstance().handleModeMessage(message);
} else if (command == "NOTICE") {
helper::IrcMessageHandler::getInstance().handleNoticeMessage(
static_cast<Communi::IrcNoticeMessage *>(message));
}
}
void IrcManager::writeConnectionMessageReceived(Communi::IrcMessage *message)
{
switch (message->type()) {
case Communi::IrcMessage::Type::Notice: {
helper::IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage(
static_cast<Communi::IrcNoticeMessage *>(message));
} break;
}
}
// XXX: This does not fit in IrcManager
bool IrcManager::isTwitchUserBlocked(QString const &username)
{
QMutexLocker locker(&this->twitchBlockedUsersMutex);
auto iterator = this->twitchBlockedUsers.find(username);
return iterator != this->twitchBlockedUsers.end();
}
// XXX: This does not fit in IrcManager
bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
{
assert(this->account);
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);
auto reply = this->networkAccessManager.put(request, QByteArray());
reply->waitForReadyRead(10000);
if (reply->error() == QNetworkReply::NoError) {
this->twitchBlockedUsersMutex.lock();
this->twitchBlockedUsers.insert(username, true);
this->twitchBlockedUsersMutex.unlock();
return true;
}
reply->deleteLater();
errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString();
return false;
}
// XXX: This does not fit in IrcManager
void IrcManager::addIgnoredUser(QString const &username)
{
QString errorMessage;
if (!tryAddIgnoredUser(username, errorMessage)) {
// TODO: Implement IrcManager::addIgnoredUser
}
}
// XXX: This does not fit in IrcManager
bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
{
assert(this->account);
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);
auto reply = this->networkAccessManager.deleteResource(request);
reply->waitForReadyRead(10000);
if (reply->error() == QNetworkReply::NoError) {
this->twitchBlockedUsersMutex.lock();
this->twitchBlockedUsers.remove(username);
this->twitchBlockedUsersMutex.unlock();
return true;
}
reply->deleteLater();
errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString();
return false;
}
// XXX: This does not fit in IrcManager
void IrcManager::removeIgnoredUser(QString const &username)
{
QString errorMessage;
if (!tryRemoveIgnoredUser(username, errorMessage)) {
// TODO: Implement IrcManager::removeIgnoredUser
}
}
void IrcManager::onConnected()
{
MessagePtr connMsg = Message::createSystemMessage("connected to chat");
MessagePtr reconnMsg = Message::createSystemMessage("reconnected to chat");
this->channelManager.doOnAll([connMsg, reconnMsg](ChannelPtr channel) {
assert(channel);
LimitedQueueSnapshot<MessagePtr> snapshot = channel->getMessageSnapshot();
bool replaceMessage =
snapshot.getLength() > 0 &&
snapshot[snapshot.getLength() - 1]->flags & Message::DisconnectedMessage;
if (replaceMessage) {
channel->replaceMessage(snapshot[snapshot.getLength() - 1], reconnMsg);
return;
}
channel->addMessage(connMsg);
});
}
void IrcManager::onDisconnected()
{
MessagePtr msg = Message::createSystemMessage("disconnected from chat");
msg->flags &= Message::DisconnectedMessage;
this->channelManager.doOnAll([msg](ChannelPtr channel) {
assert(channel);
channel->addMessage(msg);
});
}
Communi::IrcConnection *IrcManager::getReadConnection()
{
return this->readConnection.get();
}
void IrcManager::addFakeMessage(const QString &data)
{
auto fakeMessage = Communi::IrcMessage::fromData(data.toUtf8(), this->readConnection.get());
this->privateMessageReceived(qobject_cast<Communi::IrcPrivateMessage *>(fakeMessage));
}
} // namespace singletons
} // namespace chatterino
// void IrcManager::refreshIgnoredUsers(const QString &username, const QString &oauthClient,
// const QString &oauthToken)
//{
// QString nextLink = "https://api.twitch.tv/kraken/users/" + username + "/blocks?limit=" + 100 +
// "&client_id=" + oauthClient;
//
// QNetworkAccessManager *manager = new QNetworkAccessManager();
// QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken));
// QNetworkReply *reply = manager->get(req);
//
// QObject::connect(reply, &QNetworkReply::finished, [=] {
// this->twitchBlockedUsersMutex.lock();
// this->twitchBlockedUsers.clear();
// this->twitchBlockedUsersMutex.unlock();
//
// QByteArray data = reply->readAll();
// QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
// QJsonObject root = jsonDoc.object();
//
// // nextLink =
// // root.value("this->links").toObject().value("next").toString();
//
// auto blocks = root.value("blocks").toArray();
//
// this->twitchBlockedUsersMutex.lock();
// for (QJsonValue block : blocks) {
// QJsonObject user = block.toObject().value("user").toObject();
// // displaythis->name
// this->twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
// }
// this->twitchBlockedUsersMutex.unlock();
//
// manager->deleteLater();
// });
//}
//
//// XXX: This does not fit in IrcManager
// bool IrcManager::isTwitchUserBlocked(QString const &username)
//{
// QMutexLocker locker(&this->twitchBlockedUsersMutex);
//
// auto iterator = this->twitchBlockedUsers.find(username);
//
// return iterator != this->twitchBlockedUsers.end();
//}
//
//// XXX: This does not fit in IrcManager
// bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
//{
// assert(this->account);
//
// 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);
// auto reply = this->networkAccessManager.put(request, QByteArray());
// reply->waitForReadyRead(10000);
//
// if (reply->error() == QNetworkReply::NoError) {
// this->twitchBlockedUsersMutex.lock();
// this->twitchBlockedUsers.insert(username, true);
// this->twitchBlockedUsersMutex.unlock();
//
// return true;
// }
//
// reply->deleteLater();
//
// errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString();
//
// return false;
//}
//
//// XXX: This does not fit in IrcManager
// void IrcManager::addIgnoredUser(QString const &username)
//{
// QString errorMessage;
// if (!tryAddIgnoredUser(username, errorMessage)) {
// // TODO: Implement IrcManager::addIgnoredUser
// }
//}
//
//// XXX: This does not fit in IrcManager
// bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
//{
// assert(this->account);
//
// 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);
// auto reply = this->networkAccessManager.deleteResource(request);
// reply->waitForReadyRead(10000);
//
// if (reply->error() == QNetworkReply::NoError) {
// this->twitchBlockedUsersMutex.lock();
// this->twitchBlockedUsers.remove(username);
// this->twitchBlockedUsersMutex.unlock();
//
// return true;
// }
//
// reply->deleteLater();
//
// errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString();
//
// return false;
//}
//
//// XXX: This does not fit in IrcManager
// void IrcManager::removeIgnoredUser(QString const &username)
//{
// QString errorMessage;
// if (!tryRemoveIgnoredUser(username, errorMessage)) {
// // TODO: Implement IrcManager::removeIgnoredUser
// }
//}

View file

@ -1,95 +1,13 @@
#pragma once
// bool isTwitchUserBlocked(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);
#define TWITCH_MAX_MESSAGELENGTH 500
// QMap<QString, bool> twitchBlockedUsers;
// QMutex twitchBlockedUsersMutex;
#include "messages/message.hpp"
#include "twitch/twitchuser.hpp"
// QNetworkAccessManager networkAccessManager;
#include <ircconnection.h>
#include <IrcMessage>
#include <QMap>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QString>
#include <pajlada/signals/signal.hpp>
#include <memory>
#include <mutex>
namespace chatterino {
namespace singletons {
class ChannelManager;
class ResourceManager;
class AccountManager;
class WindowManager;
class IrcManager : public QObject
{
IrcManager(ChannelManager &channelManager, ResourceManager &resources,
AccountManager &accountManager);
public:
static IrcManager &getInstance();
void connect();
void disconnect();
bool isTwitchUserBlocked(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);
void sendMessage(const QString &channelName, QString message);
void joinChannel(const QString &channelName);
void partChannel(const QString &channelName);
void setUser(std::shared_ptr<twitch::TwitchUser> newAccount);
pajlada::Signals::Signal<Communi::IrcPrivateMessage *> onPrivateMessage;
void privateMessageReceived(Communi::IrcPrivateMessage *message);
boost::signals2::signal<void()> connected;
Communi::IrcConnection *getReadConnection();
/// Debug function
void addFakeMessage(const QString &data);
private:
ChannelManager &channelManager;
ResourceManager &resources;
AccountManager &accountManager;
// variables
std::shared_ptr<twitch::TwitchUser> account = nullptr;
std::unique_ptr<Communi::IrcConnection> writeConnection = nullptr;
std::unique_ptr<Communi::IrcConnection> readConnection = nullptr;
std::mutex connectionMutex;
QMap<QString, bool> twitchBlockedUsers;
QMutex twitchBlockedUsersMutex;
QNetworkAccessManager networkAccessManager;
void initializeConnection(const std::unique_ptr<Communi::IrcConnection> &connection,
bool isReadConnection);
void refreshIgnoredUsers(const QString &username, const QString &oauthClient,
const QString &oauthToken);
void beginConnecting();
void messageReceived(Communi::IrcMessage *message);
void writeConnectionMessageReceived(Communi::IrcMessage *message);
void onConnected();
void onDisconnected();
};
} // namespace singletons
} // namespace chatterino
// void refreshIgnoredUsers(const QString &username, const QString &oauthClient,
// const QString &oauthToken);

View file

@ -1,65 +0,0 @@
#include "twitchuser.hpp"
#include "const.hpp"
#include "util/urlfetch.hpp"
namespace chatterino {
namespace twitch {
TwitchUser::TwitchUser(const QString &username, const QString &oauthToken,
const QString &oauthClient)
: IrcUser2(username, username, username, "oauth:" + oauthToken)
, _oauthClient(oauthClient)
, _oauthToken(oauthToken)
, _isAnon(username == ANONYMOUS_USERNAME)
{
}
const QString &TwitchUser::getOAuthClient() const
{
return this->_oauthClient;
}
const QString &TwitchUser::getOAuthToken() const
{
return this->_oauthToken;
}
const QString &TwitchUser::getUserId() const
{
return this->_userId;
}
void TwitchUser::setUserId(const QString &id)
{
this->_userId = id;
}
bool TwitchUser::setOAuthClient(const QString &newClientID)
{
if (this->_oauthClient.compare(newClientID) == 0) {
return false;
}
this->_oauthClient = newClientID;
return true;
}
bool TwitchUser::setOAuthToken(const QString &newOAuthToken)
{
if (this->_oauthToken.compare(newOAuthToken) == 0) {
return false;
}
this->_oauthToken = newOAuthToken;
return true;
}
bool TwitchUser::isAnon() const
{
return this->_isAnon;
}
} // namespace twitch
} // namespace chatterino

View file

@ -9,6 +9,8 @@
#define async_exec(a) QThreadPool::globalInstance()->start(new LambdaRunnable(a));
namespace chatterino {
namespace util {
class LambdaRunnable : public QRunnable
{
public:
@ -52,3 +54,5 @@ static void postToThread(F &&fun, QObject *obj = qApp)
};
QCoreApplication::postEvent(obj, new Event(std::forward<F>(fun)));
}
} // namespace util
} // namespace chatterino

View file

@ -1,7 +1,7 @@
#pragma once
#include "basewindow.hpp"
#include "twitch/twitchchannel.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "util/concurrentmap.hpp"
#include <QPushButton>

View file

@ -4,9 +4,9 @@
#include <QTabWidget>
#include "messages/messagebuilder.hpp"
#include "twitch/twitchchannel.hpp"
#include "providers/twitch/twitchchannel.hpp"
using namespace chatterino::twitch;
using namespace chatterino::providers::twitch;
using namespace chatterino::messages;
namespace chatterino {
@ -68,7 +68,7 @@ void EmotePopup::loadChannel(ChannelPtr _channel)
map.each([&](const QString &key, const util::EmoteData &value) {
builder2.append((new EmoteElement(value, MessageElement::Flags::AlwaysShow))
->setLink(Link(Link::InsertText, key)));
->setLink(Link(Link::InsertText, key)));
});
emoteChannel->addMessage(builder2.getMessage());
@ -107,7 +107,7 @@ void EmotePopup::loadEmojis()
emojis.each([this, &builder](const QString &key, const util::EmoteData &value) {
builder.append((new EmoteElement(value, MessageElement::Flags::AlwaysShow))
->setLink(Link(Link::Type::InsertText, key)));
->setLink(Link(Link::Type::InsertText, key)));
});
emojiChannel->addMessage(builder.getMessage());

View file

@ -3,6 +3,7 @@
#include "messages/layouts/messagelayout.hpp"
#include "messages/limitedqueuesnapshot.hpp"
#include "messages/message.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/channelmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
@ -28,6 +29,7 @@
#define LAYOUT_WIDTH (this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getScale())
using namespace chatterino::messages;
using namespace chatterino::providers::twitch;
namespace chatterino {
namespace widgets {
@ -35,7 +37,7 @@ namespace widgets {
ChannelView::ChannelView(BaseWidget *parent)
: BaseWidget(parent)
, scrollBar(this)
, userPopupWidget(std::shared_ptr<twitch::TwitchChannel>())
, userPopupWidget(std::shared_ptr<TwitchChannel>())
{
#ifndef Q_OS_MAC
// this->setAttribute(Qt::WA_OpaquePaintEvent);
@ -476,7 +478,7 @@ messages::MessageElement::Flags ChannelView::getFlags() const
if (split->getModerationMode()) {
flags = (MessageElement::Flags)(flags | MessageElement::ModeratorTools);
}
if (this->channel == singletons::ChannelManager::getInstance().mentionsChannel) {
if (this->channel == TwitchServer::getInstance().mentionsChannel) {
flags = (MessageElement::Flags)(flags | MessageElement::ChannelName);
}
}

View file

@ -1,4 +1,5 @@
#include "widgets/helper/resizingtextedit.hpp"
#include "common.hpp"
#include "singletons/completionmanager.hpp"
ResizingTextEdit::ResizingTextEdit()
@ -79,13 +80,17 @@ void ResizingTextEdit::keyPressEvent(QKeyEvent *event)
return;
}
if (event->key() == Qt::Key_Tab &&
(event->modifiers() & Qt::ControlModifier) == Qt::NoModifier) {
bool doComplete =
event->key() == Qt::Key_Tab && (event->modifiers() & Qt::ControlModifier) == Qt::NoModifier;
if (doComplete) {
// check if there is a completer
return_if_not(this->completer);
QString currentCompletionPrefix = this->textUnderCursor();
if (!currentCompletionPrefix.size()) {
return;
}
// check if there is something to complete
return_if_not(currentCompletionPrefix.size());
auto *completionModel =
static_cast<chatterino::singletons::CompletionModel *>(this->completer->model());
@ -109,6 +114,7 @@ void ResizingTextEdit::keyPressEvent(QKeyEvent *event)
this->completer->complete();
return;
}
// (hemirt)
// this resets the selection in the completion list, it should probably only trigger on actual
// chat input (space, character) and not on every key input (pressing alt for example)

View file

@ -1,7 +1,8 @@
#include "widgets/helper/splitheader.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/resourcemanager.hpp"
#include "singletons/thememanager.hpp"
#include "twitch/twitchchannel.hpp"
#include "util/layoutcreator.hpp"
#include "util/urlfetch.hpp"
#include "widgets/helper/label.hpp"
@ -18,6 +19,8 @@
#include "widgets/streamview.hpp"
#endif
using namespace chatterino::providers::twitch;
namespace chatterino {
namespace widgets {
@ -98,7 +101,7 @@ void SplitHeader::addDropdownItems(RippleEffectButton *label)
#ifdef USEWEBENGINE
this->dropdownMenu.addAction("Start watching", this, [this]{
ChannelPtr _channel = this->split->getChannel();
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_channel.get());
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get());
if (tc != nullptr) {
StreamView *view = new StreamView(_channel, "https://player.twitch.tv/?channel=" + tc->name);
@ -128,7 +131,7 @@ void SplitHeader::initializeChannelSignals()
this->onlineStatusChangedConnection.disconnect();
auto channel = this->split->getChannel();
twitch::TwitchChannel *twitchChannel = dynamic_cast<twitch::TwitchChannel *>(channel.get());
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel) {
twitchChannel->onlineStatusChanged.connect([this]() {
@ -150,13 +153,13 @@ void SplitHeader::scaleChangedEvent(float scale)
void SplitHeader::updateChannelText()
{
const std::string channelName = this->split->channelName;
if (channelName.empty()) {
const QString channelName = this->split->channelName;
if (channelName.isEmpty()) {
this->titleLabel->setText("<no channel>");
} else {
auto channel = this->split->getChannel();
twitch::TwitchChannel *twitchChannel = dynamic_cast<twitch::TwitchChannel *>(channel.get());
TwitchChannel *twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
if (twitchChannel != nullptr && twitchChannel->isLive) {
this->isLive = true;
@ -169,10 +172,10 @@ void SplitHeader::updateChannelText()
twitchChannel->streamViewerCount +
" viewers"
"</p>";
this->titleLabel->setText(QString::fromStdString(channelName) + " (live)");
this->titleLabel->setText(channelName + " (live)");
} else {
this->isLive = false;
this->titleLabel->setText(QString::fromStdString(channelName));
this->titleLabel->setText(channelName);
this->tooltip = "";
}
}
@ -188,7 +191,7 @@ void SplitHeader::updateModerationModeIcon()
bool modButtonVisible = false;
ChannelPtr channel = this->split->getChannel();
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(channel.get());
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(channel.get());
if (tc != nullptr && tc->hasModRights()) {
modButtonVisible = true;
@ -268,7 +271,8 @@ void SplitHeader::menuReloadChannelEmotes()
void SplitHeader::menuManualReconnect()
{
singletons::IrcManager::getInstance().connect();
// fourtf: connection
providers::twitch::TwitchServer::getInstance().connect();
}
void SplitHeader::menuShowChangelog()

View file

@ -24,8 +24,8 @@ namespace widgets {
Notebook::Notebook(Window *parent, bool _showButtons, const std::string &settingPrefix)
: BaseWidget(parent)
, parentWindow(parent)
, settingRoot(fS("{}/notebook", settingPrefix))
, parentWindow(parent)
, addButton(this)
, settingsButton(this)
, userButton(this)

View file

@ -1,10 +1,12 @@
#include "widgets/split.hpp"
#include "providers/twitch/emotevalue.hpp"
#include "providers/twitch/twitchchannel.hpp"
#include "providers/twitch/twitchmessagebuilder.hpp"
#include "providers/twitch/twitchserver.hpp"
#include "singletons/channelmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "singletons/windowmanager.hpp"
#include "twitch/twitchchannel.hpp"
#include "twitch/twitchmessagebuilder.hpp"
#include "util/urlfetch.hpp"
#include "widgets/helper/searchpopup.hpp"
#include "widgets/helper/shortcut.hpp"
@ -34,6 +36,7 @@
#include <functional>
#include <random>
using namespace chatterino::providers::twitch;
using namespace chatterino::messages;
namespace chatterino {
@ -45,7 +48,7 @@ Split::Split(SplitContainer *parent, const std::string &_uuid)
, settingRoot(fS("/splits/{}", this->uuid))
, channelName(fS("{}/channelName", this->settingRoot))
, parentPage(*parent)
, channel(singletons::ChannelManager::getInstance().emptyChannel)
, channel(Channel::getEmpty())
, vbox(this)
, header(this)
, view(this)
@ -122,7 +125,6 @@ Split::Split(SplitContainer *parent, const std::string &_uuid)
Split::~Split()
{
this->channelNameUpdated("");
this->usermodeChangedConnection.disconnect();
this->channelIDChangedConnection.disconnect();
}
@ -150,7 +152,7 @@ void Split::setChannel(ChannelPtr _newChannel)
this->channel = _newChannel;
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_newChannel.get());
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_newChannel.get());
if (tc != nullptr) {
this->usermodeChangedConnection =
@ -198,20 +200,13 @@ bool Split::getModerationMode() const
return this->moderationMode;
}
void Split::channelNameUpdated(const std::string &newChannelName)
void Split::channelNameUpdated(const QString &newChannelName)
{
auto &cman = singletons::ChannelManager::getInstance();
// remove current channel
if (!this->channel->isEmpty()) {
cman.removeTwitchChannel(this->channel->name);
}
// update messages
if (newChannelName.empty()) {
this->setChannel(cman.emptyChannel);
if (newChannelName.isEmpty()) {
this->setChannel(Channel::getEmpty());
} else {
this->setChannel(cman.addTwitchChannel(QString::fromStdString(newChannelName)));
this->setChannel(TwitchServer::getInstance().addChannel(newChannelName));
}
// update header
@ -226,13 +221,13 @@ bool Split::showChangeChannelPopup(const char *dialogTitle, bool empty)
dialog.setWindowTitle(dialogTitle);
if (!empty) {
dialog.setText(QString::fromStdString(this->channelName));
dialog.setText(this->channelName);
}
if (dialog.exec() == QDialog::Accepted) {
QString newChannelName = dialog.getText().trimmed();
this->channelName = newChannelName.toStdString();
this->channelName = newChannelName;
this->parentPage.refreshTitle();
return true;
@ -355,7 +350,7 @@ void Split::doClearChat()
void Split::doOpenChannel()
{
ChannelPtr _channel = this->channel;
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_channel.get());
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get());
if (tc != nullptr) {
QDesktopServices::openUrl("https://twitch.tv/" + tc->name);
@ -365,7 +360,7 @@ void Split::doOpenChannel()
void Split::doOpenPopupPlayer()
{
ChannelPtr _channel = this->channel;
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_channel.get());
TwitchChannel *tc = dynamic_cast<TwitchChannel *>(_channel.get());
if (tc != nullptr) {
QDesktopServices::openUrl("https://player.twitch.tv/?channel=" + tc->name);
@ -379,7 +374,7 @@ void Split::doOpenStreamlink()
preferredQuality = preferredQuality.toLower();
// TODO(Confuseh): Default streamlink paths
QString path = settings.streamlinkPath;
QString channel = QString::fromStdString(this->channelName.getValue());
QString channel = this->channelName.getValue();
QFileInfo fileinfo = QFileInfo(path);
if (path.isEmpty()) {

View file

@ -5,6 +5,7 @@
#include "messages/layouts/messagelayoutelement.hpp"
#include "messages/limitedqueuesnapshot.hpp"
#include "messages/messageelement.hpp"
#include "util/serialize-custom.hpp"
#include "widgets/basewidget.hpp"
#include "widgets/helper/channelview.hpp"
#include "widgets/helper/rippleeffectlabel.hpp"
@ -46,7 +47,7 @@ public:
Split(SplitContainer *parent, const std::string &_uuid);
virtual ~Split();
pajlada::Settings::Setting<std::string> channelName;
pajlada::Settings::Setting<QString> channelName;
boost::signals2::signal<void()> channelChanged;
ChannelView &getChannelView()
@ -99,7 +100,7 @@ private:
void setChannel(ChannelPtr newChannel);
void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user);
void channelNameUpdated(const std::string &newChannelName);
void channelNameUpdated(const QString &newChannelName);
void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers);
public slots:

View file

@ -30,10 +30,9 @@ SplitContainer::SplitContainer(Notebook *parent, NotebookTab *_tab, const std::s
: BaseWidget(parent->themeManager, parent)
, uuid(_uuid)
, settingRoot(fS("/containers/{}", this->uuid))
, chats(fS("{}/chats", this->settingRoot))
, tab(_tab)
, chats(fS("{}/chats", this->settingRoot))
, dropPreview(this)
, splits()
{
this->tab->page = this;
@ -468,8 +467,7 @@ void SplitContainer::refreshTitle()
bool first = true;
for (const auto &chatWidget : this->splits) {
auto channelName = QString::fromStdString(chatWidget->channelName.getValue());
auto channelName = chatWidget->channelName.getValue();
if (channelName.isEmpty()) {
continue;
}

View file

@ -24,7 +24,6 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
, settingRoot(fS("/windows/{}", windowName))
, windowGeometry(this->settingRoot)
, dpi(this->getScale())
, themeManager(_themeManager)
, notebook(this, _isMainWindow, this->settingRoot)
{
singletons::AccountManager::getInstance().Twitch.currentUsername.connect(
@ -98,11 +97,11 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
cheerMessages.emplace_back(R"(@badges=subscriber/3,premium/1;bits=1;color=#FF0000;display-name=kalvarenga;emotes=;id=4744d6f0-de1d-475d-a3ff-38647113265a;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782860740;turbo=0;user-id=108393131;user-type= :kalvarenga!kalvarenga@kalvarenga.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
// clang-format on
CreateWindowShortcut(this, "F5", [cheerMessages] {
auto &ircManager = singletons::IrcManager::getInstance();
static int index = 0;
ircManager.addFakeMessage(cheerMessages[index++ % cheerMessages.size()]);
});
// CreateWindowShortcut(this, "F5", [cheerMessages] {
// auto &ircManager = singletons::IrcManager::getInstance();
// static int index = 0;
// ircManager.addFakeMessage(cheerMessages[index++ % cheerMessages.size()]);
// });
}
void Window::repaintVisibleChatWidgets(Channel *channel)

View file

@ -60,8 +60,6 @@ protected:
virtual bool event(QEvent *event) override;
private:
singletons::ThemeManager &themeManager;
float dpi;
void loadGeometry();