diff --git a/chatterino.pro b/chatterino.pro index 5513c6d6c..9e15defa4 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -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 diff --git a/src/application.cpp b/src/application.cpp index 4d282bb88..5f5f33c62 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -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(); diff --git a/src/channel.cpp b/src/channel.cpp index 03658b0b4..58a65d9a3 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -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 Channel::getMessageSnapshot() @@ -103,4 +108,13 @@ void Channel::sendMessage(const QString &message) { } +std::shared_ptr Channel::getEmpty() +{ + static std::shared_ptr channel(new Channel("")); + return channel; +} + +void Channel::onConnected() +{ +} } // namespace chatterino diff --git a/src/channel.hpp b/src/channel.hpp index 7724560f2..1b06f4bf2 100644 --- a/src/channel.hpp +++ b/src/channel.hpp @@ -23,11 +23,15 @@ class Channel : public std::enable_shared_from_this { public: explicit Channel(const QString &_name); + virtual ~Channel(); + + pajlada::Signals::Signal sendMessageSignal; boost::signals2::signal messageRemovedFromStart; boost::signals2::signal messageAppended; boost::signals2::signal &)> messagesAddedAtStart; boost::signals2::signal messageReplaced; + pajlada::Signals::NoArgSignal destroyed; virtual bool isEmpty() const; messages::LimitedQueueSnapshot getMessageSnapshot(); @@ -58,6 +62,11 @@ public: return false; } + static std::shared_ptr getEmpty(); + +protected: + virtual void onConnected(); + private: messages::LimitedQueue messages; }; diff --git a/src/common.hpp b/src/common.hpp index 62303fc31..80ca6608f 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -1,20 +1,22 @@ -#pragma once - -#include - -#include - -namespace chatterino { - -enum class HighlightState { - None, - Highlighted, - NewMessage, -}; - -inline QString qS(const std::string &string) -{ - return QString::fromStdString(string); -} - -} // namespace chatterino +#pragma once + +#include +#include +#include + +#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 diff --git a/src/const.hpp b/src/const.hpp index a1c7a2734..925e0b5a0 100644 --- a/src/const.hpp +++ b/src/const.hpp @@ -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 diff --git a/src/ircaccount.cpp b/src/ircaccount.cpp deleted file mode 100644 index 6d226f64e..000000000 --- a/src/ircaccount.cpp +++ /dev/null @@ -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 diff --git a/src/ircaccount.hpp b/src/ircaccount.hpp deleted file mode 100644 index 5228fbc39..000000000 --- a/src/ircaccount.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -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 diff --git a/src/messages/image.cpp b/src/messages/image.cpp index ac6a68a7b..0792d8c35 100644 --- a/src/messages/image.cpp +++ b/src/messages/image.cpp @@ -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([=]() { diff --git a/src/messages/layouts/messagelayoutelement.hpp b/src/messages/layouts/messagelayoutelement.hpp index 38b981722..16b8db0ed 100644 --- a/src/messages/layouts/messagelayoutelement.hpp +++ b/src/messages/layouts/messagelayoutelement.hpp @@ -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; }; diff --git a/src/providers/irc/_ircaccount.cpp b/src/providers/irc/_ircaccount.cpp new file mode 100644 index 000000000..dae4b38db --- /dev/null +++ b/src/providers/irc/_ircaccount.cpp @@ -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 diff --git a/src/providers/irc/_ircaccount.hpp b/src/providers/irc/_ircaccount.hpp new file mode 100644 index 000000000..351387cd6 --- /dev/null +++ b/src/providers/irc/_ircaccount.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +// 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 diff --git a/src/providers/irc/_ircchannel.cpp b/src/providers/irc/_ircchannel.cpp new file mode 100644 index 000000000..2faf773dc --- /dev/null +++ b/src/providers/irc/_ircchannel.cpp @@ -0,0 +1,11 @@ +#include "_ircchannel.hpp" + +namespace chatterino { +namespace providers { +namespace irc { +// IrcChannel::IrcChannel() +//{ +//} +} // namespace irc +} // namespace providers +} // namespace chatterino diff --git a/src/providers/irc/_ircchannel.hpp b/src/providers/irc/_ircchannel.hpp new file mode 100644 index 000000000..d8fe790dd --- /dev/null +++ b/src/providers/irc/_ircchannel.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace chatterino { +namespace providers { +namespace irc { +// class IrcChannel +//{ +// public: +// IrcChannel(); +//}; +} // namespace irc +} // namespace providers +} // namespace chatterino diff --git a/src/providers/irc/_ircserver.cpp b/src/providers/irc/_ircserver.cpp new file mode 100644 index 000000000..b5b31762a --- /dev/null +++ b/src/providers/irc/_ircserver.cpp @@ -0,0 +1,14 @@ +#include "_ircserver.hpp" + +#include + +namespace chatterino { +namespace providers { +namespace irc { +// IrcServer::IrcServer(const QString &hostname, int port) +//{ +// this->initConnection(); +//} +} // namespace irc +} // namespace providers +} // namespace chatterino diff --git a/src/providers/irc/_ircserver.hpp b/src/providers/irc/_ircserver.hpp new file mode 100644 index 000000000..cb915256a --- /dev/null +++ b/src/providers/irc/_ircserver.hpp @@ -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 newAccount); +// std::shared_ptr 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 diff --git a/src/providers/irc/abstractircserver.cpp b/src/providers/irc/abstractircserver.cpp new file mode 100644 index 000000000..198cdc632 --- /dev/null +++ b/src/providers/irc/abstractircserver.cpp @@ -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 lock1(this->connectionMutex); + std::lock_guard lock2(this->channelMutex); + + for (std::weak_ptr &weak : this->channels.values()) { + std::shared_ptr 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 locker(this->connectionMutex); + + this->readConnection->close(); + this->writeConnection->close(); +} + +void AbstractIrcServer::sendMessage(const QString &channelName, const QString &message) +{ + std::lock_guard 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 AbstractIrcServer::addChannel(const QString &channelName) +{ + std::lock_guard lock(this->channelMutex); + + // value exists + auto it = this->channels.find(channelName); + if (it != this->channels.end()) { + std::shared_ptr chan = it.value().lock(); + + if (chan) { + return chan; + } + } + + // value doesn't exist + std::shared_ptr 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 lock2(this->connectionMutex); + if (this->readConnection) { + this->readConnection->sendRaw("JOIN #" + channelName); + } + + if (this->writeConnection) { + this->writeConnection->sendRaw("JOIN #" + channelName); + } + } + + return chan; +} + +std::shared_ptr AbstractIrcServer::getChannel(const QString &channelName) +{ + std::lock_guard lock(this->channelMutex); + + // value exists + auto it = this->channels.find(channelName); + if (it != this->channels.end()) { + std::shared_ptr chan = it.value().lock(); + + if (chan) { + return chan; + } + } + + return Channel::getEmpty(); +} + +void AbstractIrcServer::onConnected() +{ + std::lock_guard lock(this->channelMutex); + + MessagePtr connMsg = Message::createSystemMessage("connected to chat"); + MessagePtr reconnMsg = Message::createSystemMessage("reconnected to chat"); + + for (std::weak_ptr &weak : this->channels.values()) { + std::shared_ptr chan = weak.lock(); + if (!chan) { + continue; + } + + LimitedQueueSnapshot 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 lock(this->channelMutex); + + MessagePtr msg = Message::createSystemMessage("disconnected from chat"); + msg->flags &= Message::DisconnectedMessage; + + for (std::weak_ptr &weak : this->channels.values()) { + std::shared_ptr chan = weak.lock(); + if (!chan) { + continue; + } + + chan->addMessage(msg); + } +} + +std::shared_ptr 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(fakeMessage)); +} + +void AbstractIrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message) +{ +} + +void AbstractIrcServer::messageReceived(Communi::IrcMessage *message) +{ +} +} // namespace irc +} // namespace providers +} // namespace chatterino diff --git a/src/providers/irc/abstractircserver.hpp b/src/providers/irc/abstractircserver.hpp new file mode 100644 index 000000000..3662066e7 --- /dev/null +++ b/src/providers/irc/abstractircserver.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +#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 addChannel(const QString &channelName); + std::shared_ptr getChannel(const QString &channelName); + + // signals + pajlada::Signals::NoArgSignal connected; + pajlada::Signals::NoArgSignal disconnected; + pajlada::Signals::Signal onPrivateMessage; + + void addFakeMessage(const QString &data); + +protected: + AbstractIrcServer(); + + virtual void initializeConnection(Communi::IrcConnection *connection, bool isRead, + bool isWrite) = 0; + virtual std::shared_ptr 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 getCustomChannel(const QString &channelName); + +private: + void initConnection(); + + QMap> channels; + + std::unique_ptr writeConnection = nullptr; + std::unique_ptr readConnection = nullptr; + + std::mutex connectionMutex; + std::mutex channelMutex; +}; +} // namespace irc +} // namespace providers +} // namespace chatterino diff --git a/src/twitch/emotevalue.hpp b/src/providers/twitch/emotevalue.hpp similarity index 89% rename from src/twitch/emotevalue.hpp rename to src/providers/twitch/emotevalue.hpp index 7d4763823..18f4e542e 100644 --- a/src/twitch/emotevalue.hpp +++ b/src/providers/twitch/emotevalue.hpp @@ -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 diff --git a/src/singletons/helper/ircmessagehandler.cpp b/src/providers/twitch/ircmessagehandler.cpp similarity index 61% rename from src/singletons/helper/ircmessagehandler.cpp rename to src/providers/twitch/ircmessagehandler.cpp index a74e9fda3..3bb115c87 100644 --- a/src/singletons/helper/ircmessagehandler.cpp +++ b/src/providers/twitch/ircmessagehandler.cpp @@ -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(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(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 snapshot = c->getMessageSnapshot(); + LimitedQueueSnapshot 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(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 diff --git a/src/singletons/helper/ircmessagehandler.hpp b/src/providers/twitch/ircmessagehandler.hpp similarity index 71% rename from src/singletons/helper/ircmessagehandler.hpp rename to src/providers/twitch/ircmessagehandler.hpp index 81fec9b1e..a23ad2547 100644 --- a/src/singletons/helper/ircmessagehandler.hpp +++ b/src/providers/twitch/ircmessagehandler.hpp @@ -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 diff --git a/src/providers/twitch/twitchaccount.cpp b/src/providers/twitch/twitchaccount.cpp new file mode 100644 index 000000000..5e11adbb3 --- /dev/null +++ b/src/providers/twitch/twitchaccount.cpp @@ -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 diff --git a/src/twitch/twitchuser.hpp b/src/providers/twitch/twitchaccount.hpp similarity index 70% rename from src/twitch/twitchuser.hpp rename to src/providers/twitch/twitchaccount.hpp index c7562b200..f0c85844e 100644 --- a/src/twitch/twitchuser.hpp +++ b/src/providers/twitch/twitchaccount.hpp @@ -1,19 +1,19 @@ #pragma once -#include "ircaccount.hpp" - #include 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 diff --git a/src/twitch/twitchaccountmanager.cpp b/src/providers/twitch/twitchaccountmanager.cpp similarity index 88% rename from src/twitch/twitchaccountmanager.cpp rename to src/providers/twitch/twitchaccountmanager.cpp index f5bb57ddc..7c4135f36 100644 --- a/src/twitch/twitchaccountmanager.cpp +++ b/src/providers/twitch/twitchaccountmanager.cpp @@ -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 TwitchAccountManager::getCurrent() +std::shared_ptr TwitchAccountManager::getCurrent() { if (!this->currentUser) { return this->anonymousUser; @@ -50,7 +51,7 @@ std::vector TwitchAccountManager::getUsernames() const return userNames; } -std::shared_ptr TwitchAccountManager::findUserByUsername( +std::shared_ptr TwitchAccountManager::findUserByUsername( const QString &username) const { std::lock_guard 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(userData.username, userData.oauthToken, - userData.clientID); + auto newUser = + std::make_shared(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 diff --git a/src/twitch/twitchaccountmanager.hpp b/src/providers/twitch/twitchaccountmanager.hpp similarity index 74% rename from src/twitch/twitchaccountmanager.hpp rename to src/providers/twitch/twitchaccountmanager.hpp index 535f3e1c2..4ca070f64 100644 --- a/src/twitch/twitchaccountmanager.hpp +++ b/src/providers/twitch/twitchaccountmanager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "twitch/twitchuser.hpp" +#include "providers/twitch/twitchaccount.hpp" #include @@ -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 getCurrent(); + std::shared_ptr getCurrent(); std::vector getUsernames() const; - std::shared_ptr findUserByUsername(const QString &username) const; + std::shared_ptr 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 currentUser; + std::shared_ptr currentUser; - std::shared_ptr anonymousUser; - std::vector> users; + std::shared_ptr anonymousUser; + std::vector> users; mutable std::mutex mutex; friend class chatterino::singletons::AccountManager; }; - } // namespace twitch +} // namespace providers } // namespace chatterino diff --git a/src/twitch/twitchchannel.cpp b/src/providers/twitch/twitchchannel.cpp similarity index 93% rename from src/twitch/twitchchannel.cpp rename to src/providers/twitch/twitchchannel.cpp index e308a1e22..3a8aaf083 100644 --- a/src/twitch/twitchchannel.cpp +++ b/src/providers/twitch/twitchchannel.cpp @@ -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 #include #include 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(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 diff --git a/src/twitch/twitchchannel.hpp b/src/providers/twitch/twitchchannel.hpp similarity index 81% rename from src/twitch/twitchchannel.hpp rename to src/providers/twitch/twitchchannel.hpp index 18f1e0652..90d077a70 100644 --- a/src/twitch/twitchchannel.hpp +++ b/src/providers/twitch/twitchchannel.hpp @@ -1,19 +1,22 @@ #pragma once +#include + #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 diff --git a/src/twitch/twitchmessagebuilder.cpp b/src/providers/twitch/twitchmessagebuilder.cpp similarity index 99% rename from src/twitch/twitchmessagebuilder.cpp rename to src/providers/twitch/twitchmessagebuilder.cpp index 2c67b2963..1417f6d03 100644 --- a/src/twitch/twitchmessagebuilder.cpp +++ b/src/providers/twitch/twitchmessagebuilder.cpp @@ -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 #include @@ -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 diff --git a/src/twitch/twitchmessagebuilder.hpp b/src/providers/twitch/twitchmessagebuilder.hpp similarity index 97% rename from src/twitch/twitchmessagebuilder.hpp rename to src/providers/twitch/twitchmessagebuilder.hpp index 16035a714..31f263d84 100644 --- a/src/twitch/twitchmessagebuilder.hpp +++ b/src/providers/twitch/twitchmessagebuilder.hpp @@ -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 diff --git a/src/providers/twitch/twitchserver.cpp b/src/providers/twitch/twitchserver.cpp new file mode 100644 index 000000000..60e5b6676 --- /dev/null +++ b/src/providers/twitch/twitchserver.cpp @@ -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 + +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 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 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); +} + +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(message)); + } +} + +void TwitchServer::writeConnectionMessageReceived(IrcMessage *message) +{ + switch (message->type()) { + case IrcMessage::Type::Notice: { + IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage( + static_cast(message)); + } break; + } +} + +std::shared_ptr TwitchServer::getCustomChannel(const QString &channelName) +{ + if (channelName == "/whispers") { + return whispersChannel; + } + + if (channelName == "/mentions") { + return mentionsChannel; + } + + return nullptr; +} +} // namespace twitch +} // namespace providers +} // namespace chatterino diff --git a/src/providers/twitch/twitchserver.hpp b/src/providers/twitch/twitchserver.hpp new file mode 100644 index 000000000..63d4b0ed9 --- /dev/null +++ b/src/providers/twitch/twitchserver.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#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 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 getCustomChannel(const QString &channelname) override; +}; +} // namespace twitch +} // namespace providers +} // namespace chatterino diff --git a/src/singletons/accountmanager.cpp b/src/singletons/accountmanager.cpp index d88efbbc4..9677a25c1 100644 --- a/src/singletons/accountmanager.cpp +++ b/src/singletons/accountmanager.cpp @@ -43,5 +43,5 @@ void AccountManager::load() this->Twitch.userChanged.invoke(); } +} // namespace singletons } // namespace chatterino -} diff --git a/src/singletons/accountmanager.hpp b/src/singletons/accountmanager.hpp index d72424295..774ebb6fc 100644 --- a/src/singletons/accountmanager.hpp +++ b/src/singletons/accountmanager.hpp @@ -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 diff --git a/src/singletons/channelmanager.cpp b/src/singletons/channelmanager.cpp index b18a56ff7..d688430f9 100644 --- a/src/singletons/channelmanager.cpp +++ b/src/singletons/channelmanager.cpp @@ -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 ChannelManager::getItems() +//{ +// QMutexLocker locker(&this->channelsMutex); -const std::vector ChannelManager::getItems() -{ - QMutexLocker locker(&this->channelsMutex); +// std::vector items; - std::vector 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(channelName); - if (it == this->twitchChannels.end()) { - auto channel = std::make_shared(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 func) +//{ +// for (const auto &channel : this->twitchChannels) { +// func(std::get<0>(channel)); +// } -void ChannelManager::doOnAll(std::function 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 diff --git a/src/singletons/channelmanager.hpp b/src/singletons/channelmanager.hpp index 89e3d257e..8277cdb1c 100644 --- a/src/singletons/channelmanager.hpp +++ b/src/singletons/channelmanager.hpp @@ -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 +//#include -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 getItems(); +// const std::vector 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 func); - void doOnAll(std::function 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 usernameToID; +// std::map channelDatas; -private: - std::map usernameToID; - std::map channelDatas; +// QMutex channelsMutex; +// QMap, int>> twitchChannels; - QMutex channelsMutex; - QMap, int>> twitchChannels; +// pajlada::Signals::Signal ircJoin; +// pajlada::Signals::Signal ircPart; - pajlada::Signals::Signal ircJoin; - pajlada::Signals::Signal ircPart; - - friend class singletons::IrcManager; -}; - -} // namespace chatterino -} +// friend class singletons::IrcManager; +//}; +//} // namespace singletons +//} // namespace chatterino diff --git a/src/singletons/commandmanager.cpp b/src/singletons/commandmanager.cpp index a19060f3b..4d11e65c7 100644 --- a/src/singletons/commandmanager.cpp +++ b/src/singletons/commandmanager.cpp @@ -6,7 +6,9 @@ #include #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(channel.get()); + auto *twitchChannel = dynamic_cast(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 ""; } } diff --git a/src/singletons/completionmanager.cpp b/src/singletons/completionmanager.cpp index b316bc501..bb7326eae 100644 --- a/src/singletons/completionmanager.cpp +++ b/src/singletons/completionmanager.cpp @@ -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 -} diff --git a/src/singletons/completionmanager.hpp b/src/singletons/completionmanager.hpp index 174d08354..3efd7b828 100644 --- a/src/singletons/completionmanager.hpp +++ b/src/singletons/completionmanager.hpp @@ -16,11 +16,11 @@ class CompletionManager public: static CompletionManager &getInstance(); - CompletionModel *createModel(const std::string &channelName); + CompletionModel *createModel(const QString &channelName); private: - std::map models; + std::map models; }; +} // namespace singletons } // namespace chatterino -} diff --git a/src/singletons/emotemanager.cpp b/src/singletons/emotemanager.cpp index b1cadf44b..e48db006d 100644 --- a/src/singletons/emotemanager.cpp +++ b/src/singletons/emotemanager.cpp @@ -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 &EmoteManager::getTwitchEmotes() +util::ConcurrentMap &EmoteManager::getTwitchEmotes() { return _twitchEmotes; } @@ -420,9 +421,9 @@ QString EmoteManager::replaceShortCodes(const QString &text) return ret; } -void EmoteManager::refreshTwitchEmotes(const std::shared_ptr &user) +void EmoteManager::refreshTwitchEmotes(const std::shared_ptr &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 } emoteData.filled = true; - } - - ); + }); } void EmoteManager::loadBTTVEmotes() diff --git a/src/singletons/emotemanager.hpp b/src/singletons/emotemanager.hpp index 5fe9e8b9a..75ef6744a 100644 --- a/src/singletons/emotemanager.hpp +++ b/src/singletons/emotemanager.hpp @@ -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 channelEmoteMap); - util::ConcurrentMap &getTwitchEmotes(); + util::ConcurrentMap &getTwitchEmotes(); util::EmoteMap &getFFZEmotes(); util::EmoteMap &getChatterinoEmotes(); util::EmoteMap &getBTTVChannelEmoteFromCaches(); @@ -92,7 +92,7 @@ public: std::vector emojiShortCodes; /// Twitch emotes - void refreshTwitchEmotes(const std::shared_ptr &user); + void refreshTwitchEmotes(const std::shared_ptr &user); struct TwitchAccountEmoteData { struct TwitchEmote { @@ -112,7 +112,7 @@ public: private: // emote code - util::ConcurrentMap _twitchEmotes; + util::ConcurrentMap _twitchEmotes; // emote id util::ConcurrentMap _twitchEmoteFromCache; diff --git a/src/singletons/helper/completionmodel.cpp b/src/singletons/helper/completionmodel.cpp index 94ecdb9b8..afe9b2a83 100644 --- a/src/singletons/helper/completionmodel.cpp +++ b/src/singletons/helper/completionmodel.cpp @@ -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 diff --git a/src/singletons/ircmanager.cpp b/src/singletons/ircmanager.cpp index e6599b123..13e9100f8 100644 --- a/src/singletons/ircmanager.cpp +++ b/src/singletons/ircmanager.cpp @@ -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 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 &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 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 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(message)); - } -} - -void IrcManager::writeConnectionMessageReceived(Communi::IrcMessage *message) -{ - switch (message->type()) { - case Communi::IrcMessage::Type::Notice: { - helper::IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage( - static_cast(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 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(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 +// } +//} diff --git a/src/singletons/ircmanager.hpp b/src/singletons/ircmanager.hpp index da85f2d30..e148cc6ae 100644 --- a/src/singletons/ircmanager.hpp +++ b/src/singletons/ircmanager.hpp @@ -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 twitchBlockedUsers; +// QMutex twitchBlockedUsersMutex; -#include "messages/message.hpp" -#include "twitch/twitchuser.hpp" +// QNetworkAccessManager networkAccessManager; -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -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 newAccount); - - pajlada::Signals::Signal onPrivateMessage; - void privateMessageReceived(Communi::IrcPrivateMessage *message); - boost::signals2::signal connected; - - Communi::IrcConnection *getReadConnection(); - - /// Debug function - void addFakeMessage(const QString &data); - -private: - ChannelManager &channelManager; - ResourceManager &resources; - AccountManager &accountManager; - - // variables - std::shared_ptr account = nullptr; - - std::unique_ptr writeConnection = nullptr; - std::unique_ptr readConnection = nullptr; - - std::mutex connectionMutex; - - QMap twitchBlockedUsers; - QMutex twitchBlockedUsersMutex; - - QNetworkAccessManager networkAccessManager; - - void initializeConnection(const std::unique_ptr &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); diff --git a/src/twitch/twitchuser.cpp b/src/twitch/twitchuser.cpp deleted file mode 100644 index 5962096be..000000000 --- a/src/twitch/twitchuser.cpp +++ /dev/null @@ -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 diff --git a/src/util/posttothread.hpp b/src/util/posttothread.hpp index 7899676c7..9868a3d35 100644 --- a/src/util/posttothread.hpp +++ b/src/util/posttothread.hpp @@ -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(fun))); } +} // namespace util +} // namespace chatterino diff --git a/src/widgets/accountpopup.hpp b/src/widgets/accountpopup.hpp index b2ec8b522..14d819658 100644 --- a/src/widgets/accountpopup.hpp +++ b/src/widgets/accountpopup.hpp @@ -1,7 +1,7 @@ #pragma once #include "basewindow.hpp" -#include "twitch/twitchchannel.hpp" +#include "providers/twitch/twitchchannel.hpp" #include "util/concurrentmap.hpp" #include diff --git a/src/widgets/emotepopup.cpp b/src/widgets/emotepopup.cpp index eac36dec1..5cd092d5d 100644 --- a/src/widgets/emotepopup.cpp +++ b/src/widgets/emotepopup.cpp @@ -4,9 +4,9 @@ #include #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()); diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 4b99d4883..1e974dbf9 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -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()) + , userPopupWidget(std::shared_ptr()) { #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); } } diff --git a/src/widgets/helper/resizingtextedit.cpp b/src/widgets/helper/resizingtextedit.cpp index 0b97e9651..4f0755905 100644 --- a/src/widgets/helper/resizingtextedit.cpp +++ b/src/widgets/helper/resizingtextedit.cpp @@ -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(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) diff --git a/src/widgets/helper/splitheader.cpp b/src/widgets/helper/splitheader.cpp index b6f9b885f..45b7f70ee 100644 --- a/src/widgets/helper/splitheader.cpp +++ b/src/widgets/helper/splitheader.cpp @@ -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(_channel.get()); + TwitchChannel *tc = dynamic_cast(_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(channel.get()); + TwitchChannel *twitchChannel = dynamic_cast(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(""); } else { auto channel = this->split->getChannel(); - twitch::TwitchChannel *twitchChannel = dynamic_cast(channel.get()); + TwitchChannel *twitchChannel = dynamic_cast(channel.get()); if (twitchChannel != nullptr && twitchChannel->isLive) { this->isLive = true; @@ -169,10 +172,10 @@ void SplitHeader::updateChannelText() twitchChannel->streamViewerCount + " viewers" "

"; - 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(channel.get()); + TwitchChannel *tc = dynamic_cast(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() diff --git a/src/widgets/notebook.cpp b/src/widgets/notebook.cpp index dcca1a8bc..b3082f470 100644 --- a/src/widgets/notebook.cpp +++ b/src/widgets/notebook.cpp @@ -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) diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 22dcdd67d..219dc58d9 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -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 #include +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(_newChannel.get()); + TwitchChannel *tc = dynamic_cast(_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(_channel.get()); + TwitchChannel *tc = dynamic_cast(_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(_channel.get()); + TwitchChannel *tc = dynamic_cast(_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()) { diff --git a/src/widgets/split.hpp b/src/widgets/split.hpp index 42ec4c69b..a3d95808c 100644 --- a/src/widgets/split.hpp +++ b/src/widgets/split.hpp @@ -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 channelName; + pajlada::Settings::Setting channelName; boost::signals2::signal 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: diff --git a/src/widgets/splitcontainer.cpp b/src/widgets/splitcontainer.cpp index ee6ad7d56..be45f987e 100644 --- a/src/widgets/splitcontainer.cpp +++ b/src/widgets/splitcontainer.cpp @@ -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; } diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index 0b5f58bcc..d5202548f 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -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) diff --git a/src/widgets/window.hpp b/src/widgets/window.hpp index 33827dbe3..3cc6f9616 100644 --- a/src/widgets/window.hpp +++ b/src/widgets/window.hpp @@ -60,8 +60,6 @@ protected: virtual bool event(QEvent *event) override; private: - singletons::ThemeManager &themeManager; - float dpi; void loadGeometry();