From 77e9a40bb40b99b1074e7dff553633e9231c2123 Mon Sep 17 00:00:00 2001 From: Daniel Sage <24928223+dnsge@users.noreply.github.com> Date: Sun, 22 May 2022 16:59:05 -0400 Subject: [PATCH 01/18] Automatically place newlines at end of generated files (#3751) Co-authored-by: Kasia --- resources/_generate_resources.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/_generate_resources.py b/resources/_generate_resources.py index 602306dfb..7f1ee7a53 100644 --- a/resources/_generate_resources.py +++ b/resources/_generate_resources.py @@ -4,7 +4,7 @@ resources_header = \ resources_footer = \ ''' -''' +\n''' header_header = \ '''#include @@ -22,7 +22,7 @@ public: header_footer = \ '''}; -} // namespace chatterino''' +} // namespace chatterino\n''' source_header = \ '''#include "ResourcesAutogen.hpp" @@ -36,4 +36,4 @@ Resources2::Resources2() source_footer = \ '''} -} // namespace chatterino''' +} // namespace chatterino\n''' From e11677c62b1e320ec98df7c11caadf19482fda83 Mon Sep 17 00:00:00 2001 From: Kasia Date: Mon, 23 May 2022 00:42:52 +0200 Subject: [PATCH 02/18] Added /copy command (#3763) Copies the given arguments to clipboard --- CHANGELOG.md | 1 + src/controllers/commands/CommandController.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03756a104..24e0fb4fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Minor: Fixed automod caught message notice appearing twice for mods. (#3717) - Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746) - Minor: Added ability to execute commands on chat messages using the message context menu. (#3738) +- Minor: Added `/copy` command. Usage: `/copy `. Copies provided text to clipboard - can be useful with custom commands. (#3763) - Bugfix: Fixed live notifications for usernames containing uppercase characters. (#3646) - Bugfix: Fixed live notifications not getting updated for closed streams going offline. (#3678) - Bugfix: Fixed certain settings dialogs appearing behind the main window, when `Always on top` was used. (#3679) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 99525f6f0..843b953a4 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -17,6 +17,7 @@ #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "singletons/WindowManager.hpp" +#include "util/Clipboard.hpp" #include "util/CombinePath.hpp" #include "util/FormatTime.hpp" #include "util/Helpers.hpp" @@ -825,6 +826,7 @@ void CommandController::initialize(Settings &, Paths &paths) } return ""; }); + this->registerCommand("/setgame", [](const QStringList &words, const ChannelPtr channel) { if (words.size() < 2) @@ -923,6 +925,7 @@ void CommandController::initialize(Settings &, Paths &paths) return ""; }); + this->registerCommand( "/delete", [](const QStringList &words, ChannelPtr channel) -> QString { // This is a wrapper over the standard Twitch /delete command @@ -965,6 +968,7 @@ void CommandController::initialize(Settings &, Paths &paths) getApp()->twitch->sendRawMessage(words.mid(1).join(" ")); return ""; }); + #ifndef NDEBUG this->registerCommand( "/fakemsg", @@ -981,6 +985,19 @@ void CommandController::initialize(Settings &, Paths &paths) return ""; }); #endif + + this->registerCommand( + "/copy", [](const QStringList &words, ChannelPtr channel) -> QString { + if (words.size() < 2) + { + channel->addMessage( + makeSystemMessage("Usage: /copy - copies provided " + "text to clipboard.")); + return ""; + } + crossPlatformCopy(words.mid(1).join(" ")); + return ""; + }); } void CommandController::save() From 57783c7478a955f5224e5025653c0f8549df12fc Mon Sep 17 00:00:00 2001 From: James Upjohn Date: Mon, 23 May 2022 12:47:16 +1200 Subject: [PATCH 03/18] feat: add global channel search support (#3694) Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/controllers/hotkeys/ActionNames.hpp | 3 +- src/controllers/hotkeys/HotkeyController.cpp | 3 + src/widgets/helper/ChannelView.hpp | 3 +- src/widgets/helper/SearchPopup.cpp | 116 +++++++++++++++---- src/widgets/helper/SearchPopup.hpp | 10 +- src/widgets/splits/Split.cpp | 33 +++++- src/widgets/splits/Split.hpp | 2 +- 8 files changed, 135 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24e0fb4fb..720014b34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Major: Added multi-channel searching to search dialog via keyboard shortcut. [Ctrl+Shift+F by default] (#3694) - Minor: Added `is:first-msg` search option. (#3700) - Minor: Added quotation marks in the permitted/blocked Automod messages for clarity. (#3654) - Minor: Adjust large stream thumbnail to 16:9 (#3655) diff --git a/src/controllers/hotkeys/ActionNames.hpp b/src/controllers/hotkeys/ActionNames.hpp index 5470d7dc4..f326c24c8 100644 --- a/src/controllers/hotkeys/ActionNames.hpp +++ b/src/controllers/hotkeys/ActionNames.hpp @@ -103,7 +103,8 @@ inline const std::map actionNames{ 0, 1, }}, - {"showSearch", ActionDefinition{"Search"}}, + {"showSearch", ActionDefinition{"Search current channel"}}, + {"showGlobalSearch", ActionDefinition{"Search all channels"}}, {"startWatching", ActionDefinition{"Start watching"}}, {"debug", ActionDefinition{"Show debug popup"}}, }}, diff --git a/src/controllers/hotkeys/HotkeyController.cpp b/src/controllers/hotkeys/HotkeyController.cpp index 6e6d32f9c..7557fa67a 100644 --- a/src/controllers/hotkeys/HotkeyController.cpp +++ b/src/controllers/hotkeys/HotkeyController.cpp @@ -339,6 +339,9 @@ void HotkeyController::addDefaults(std::set &addedHotkeys) this->tryAddDefault(addedHotkeys, HotkeyCategory::Split, QKeySequence("Ctrl+F"), "showSearch", std::vector(), "show search"); + this->tryAddDefault(addedHotkeys, HotkeyCategory::Split, + QKeySequence("Ctrl+Shift+F"), "showGlobalSearch", + std::vector(), "show global search"); this->tryAddDefault(addedHotkeys, HotkeyCategory::Split, QKeySequence("Ctrl+F5"), "reconnect", std::vector(), "reconnect"); diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 994f1e762..6588fc6cc 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -81,6 +81,8 @@ public: void pause(PauseReason reason, boost::optional msecs = boost::none); void unpause(PauseReason reason); + MessageElementFlags getFlags() const; + ChannelPtr channel(); void setChannel(ChannelPtr channel_); @@ -158,7 +160,6 @@ private: void drawMessages(QPainter &painter); void setSelection(const SelectionItem &start, const SelectionItem &end); - MessageElementFlags getFlags() const; void selectWholeMessage(MessageLayout *layout, int &messageIndex); void getWordBounds(MessageLayout *layout, const MessageLayoutElement *element, diff --git a/src/widgets/helper/SearchPopup.cpp b/src/widgets/helper/SearchPopup.cpp index 731ebe0ce..b5e80e616 100644 --- a/src/widgets/helper/SearchPopup.cpp +++ b/src/widgets/helper/SearchPopup.cpp @@ -3,11 +3,9 @@ #include #include #include -#include #include "common/Channel.hpp" #include "controllers/hotkeys/HotkeyController.hpp" -#include "messages/Message.hpp" #include "messages/search/AuthorPredicate.hpp" #include "messages/search/ChannelPredicate.hpp" #include "messages/search/LinkPredicate.hpp" @@ -19,8 +17,7 @@ namespace chatterino { ChannelPtr SearchPopup::filter(const QString &text, const QString &channelName, - const LimitedQueueSnapshot &snapshot, - FilterSetPtr filterSet) + const LimitedQueueSnapshot &snapshot) { ChannelPtr channel(new Channel(channelName, Channel::Type::None)); @@ -44,9 +41,6 @@ ChannelPtr SearchPopup::filter(const QString &text, const QString &channelName, } } - if (accept && filterSet) - accept = filterSet->filter(message, channel); - // If all predicates match, add the message to the channel if (accept) channel->addMessage(message); @@ -67,13 +61,13 @@ void SearchPopup::addShortcuts() { HotkeyController::HotkeyMap actions{ {"search", - [this](std::vector) -> QString { + [this](const std::vector &) -> QString { this->searchInput_->setFocus(); this->searchInput_->selectAll(); return ""; }}, {"delete", - [this](std::vector) -> QString { + [this](const std::vector &) -> QString { this->close(); return ""; }}, @@ -88,17 +82,25 @@ void SearchPopup::addShortcuts() HotkeyCategory::PopupWindow, actions, this); } -void SearchPopup::setChannelFilters(FilterSetPtr filters) +void SearchPopup::addChannel(ChannelView &channel) { - this->channelFilters_ = std::move(filters); -} + if (this->searchChannels_.empty()) + { + this->channelView_->setSourceChannel(channel.channel()); + this->channelName_ = channel.channel()->getName(); + } + else if (this->searchChannels_.size() == 1) + { + this->channelView_->setSourceChannel( + std::make_shared("multichannel", Channel::Type::None)); -void SearchPopup::setChannel(const ChannelPtr &channel) -{ - this->channelView_->setSourceChannel(channel); - this->channelName_ = channel->getName(); - this->snapshot_ = channel->getMessageSnapshot(); - this->search(); + auto flags = this->channelView_->getFlags(); + flags.set(MessageElementFlag::ChannelName); + flags.unset(MessageElementFlag::ModeratorTools); + this->channelView_->setOverrideFlags(flags); + } + + this->searchChannels_.append(std::ref(channel)); this->updateWindowTitle(); } @@ -115,6 +117,10 @@ void SearchPopup::updateWindowTitle() { historyName = "mentions"; } + else if (this->searchChannels_.size() > 1) + { + historyName = "multiple channels'"; + } else if (this->channelName_.isEmpty()) { historyName = "'s"; @@ -126,24 +132,90 @@ void SearchPopup::updateWindowTitle() this->setWindowTitle("Searching in " + historyName + " history"); } +void SearchPopup::showEvent(QShowEvent *) +{ + this->search(); +} + void SearchPopup::search() { + if (this->snapshot_.size() == 0) + { + this->snapshot_ = this->buildSnapshot(); + } + this->channelView_->setChannel(filter(this->searchInput_->text(), - this->channelName_, this->snapshot_, - this->channelFilters_)); + this->channelName_, this->snapshot_)); +} + +LimitedQueueSnapshot SearchPopup::buildSnapshot() +{ + // no point in filtering/sorting if it's a single channel search + if (this->searchChannels_.length() == 1) + { + const auto channelPtr = this->searchChannels_.at(0); + return channelPtr.get().channel()->getMessageSnapshot(); + } + + auto combinedSnapshot = std::vector>{}; + for (auto &channel : this->searchChannels_) + { + ChannelView &sharedView = channel.get(); + + const FilterSetPtr filterSet = sharedView.getFilterSet(); + const LimitedQueueSnapshot &snapshot = + sharedView.channel()->getMessageSnapshot(); + + // TODO: implement iterator on LimitedQueueSnapshot? + for (auto i = 0; i < snapshot.size(); ++i) + { + const MessagePtr &message = snapshot[i]; + if (filterSet && !filterSet->filter(message, sharedView.channel())) + { + continue; + } + + combinedSnapshot.push_back(message); + } + } + + // remove any duplicate messages from splits containing the same channel + std::sort(combinedSnapshot.begin(), combinedSnapshot.end(), + [](MessagePtr &a, MessagePtr &b) { + return a->id > b->id; + }); + + auto uniqueIterator = + std::unique(combinedSnapshot.begin(), combinedSnapshot.end(), + [](MessagePtr &a, MessagePtr &b) { + return a->id == b->id; + }); + + combinedSnapshot.erase(uniqueIterator, combinedSnapshot.end()); + + // resort by time for presentation + std::sort(combinedSnapshot.begin(), combinedSnapshot.end(), + [](MessagePtr &a, MessagePtr &b) { + return a->serverReceivedTime < b->serverReceivedTime; + }); + + auto queue = LimitedQueue(combinedSnapshot.size()); + queue.pushFront(combinedSnapshot); + + return queue.getSnapshot(); } void SearchPopup::initLayout() { // VBOX { - QVBoxLayout *layout1 = new QVBoxLayout(this); + auto *layout1 = new QVBoxLayout(this); layout1->setMargin(0); layout1->setSpacing(0); // HBOX { - QHBoxLayout *layout2 = new QHBoxLayout(this); + auto *layout2 = new QHBoxLayout(this); layout2->setMargin(8); layout2->setSpacing(8); diff --git a/src/widgets/helper/SearchPopup.hpp b/src/widgets/helper/SearchPopup.hpp index cf9499ec2..61c0f9719 100644 --- a/src/widgets/helper/SearchPopup.hpp +++ b/src/widgets/helper/SearchPopup.hpp @@ -17,16 +17,17 @@ class SearchPopup : public BasePopup public: SearchPopup(QWidget *parent); - virtual void setChannel(const ChannelPtr &channel); - virtual void setChannelFilters(FilterSetPtr filters); + virtual void addChannel(ChannelView &channel); protected: virtual void updateWindowTitle(); + void showEvent(QShowEvent *event) override; private: void initLayout(); void search(); void addShortcuts() override; + LimitedQueueSnapshot buildSnapshot(); /** * @brief Only retains those message from a list of messages that satisfy a @@ -41,8 +42,7 @@ private: * "snapshot" */ static ChannelPtr filter(const QString &text, const QString &channelName, - const LimitedQueueSnapshot &snapshot, - FilterSetPtr filterSet); + const LimitedQueueSnapshot &snapshot); /** * @brief Checks the input for tags and registers their corresponding @@ -58,7 +58,7 @@ private: QLineEdit *searchInput_{}; ChannelView *channelView_{}; QString channelName_{}; - FilterSetPtr channelFilters_; + QList> searchChannels_; }; } // namespace chatterino diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 8ee377f41..ac9923a15 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -273,7 +273,12 @@ void Split::addShortcuts() }}, {"showSearch", [this](std::vector) -> QString { - this->showSearch(); + this->showSearch(true); + return ""; + }}, + {"showGlobalSearch", + [this](std::vector) -> QString { + this->showSearch(false); return ""; }}, {"reconnect", @@ -1155,13 +1160,29 @@ const QList Split::getFilters() const return this->view_->getFilterIds(); } -void Split::showSearch() +void Split::showSearch(bool singleChannel) { - SearchPopup *popup = new SearchPopup(this); - - popup->setChannelFilters(this->view_->getFilterSet()); + auto *popup = new SearchPopup(this); popup->setAttribute(Qt::WA_DeleteOnClose); - popup->setChannel(this->getChannel()); + + if (singleChannel) + { + popup->addChannel(this->getChannelView()); + popup->show(); + return; + } + + // Pass every ChannelView for every Split across the app to the search popup + auto ¬ebook = getApp()->windows->getMainWindow().getNotebook(); + for (int i = 0; i < notebook.getPageCount(); ++i) + { + auto container = dynamic_cast(notebook.getPageAt(i)); + for (auto split : container->getSplits()) + { + popup->addChannel(split->getChannelView()); + } + } + popup->show(); } diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index 3f49d26fa..7a1eb350e 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -171,7 +171,7 @@ public slots: void copyToClipboard(); void startWatching(); void setFiltersDialog(); - void showSearch(); + void showSearch(bool singleChannel); void showViewerList(); void openSubPage(); void reloadChannelAndSubscriberEmotes(); From 6ef3ecc952be5e1b07bddf6a751a2999139e8a34 Mon Sep 17 00:00:00 2001 From: xHeaveny Date: Mon, 23 May 2022 23:40:44 +0200 Subject: [PATCH 04/18] Use Visual Studio 2022 in Windows build instructions (#3390) Co-authored-by: Kasia --- BUILDING_ON_WINDOWS.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/BUILDING_ON_WINDOWS.md b/BUILDING_ON_WINDOWS.md index 6832581f2..c6db04c39 100644 --- a/BUILDING_ON_WINDOWS.md +++ b/BUILDING_ON_WINDOWS.md @@ -4,9 +4,9 @@ This guide assumes you are on a 64-bit system. You might need to manually search out alternate download links should you desire to build Chatterino on a 32-bit system. -## Visual Studio 2019 +## Visual Studio 2022 -Download and install [Visual Studio 2019 Community](https://visualstudio.microsoft.com/downloads/). In the installer, select "Desktop development with C++" and "Universal Windows Platform development". +Download and install [Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/). In the installer, select "Desktop development with C++" and "Universal Windows Platform development". Notes: @@ -20,14 +20,14 @@ Notes: - Visit the downloads list on [SourceForge](https://sourceforge.net/projects/boost/files/boost-binaries/). - Select the latest version from the list. - Download the `.exe` file appropriate to your Visual Studio installation version and system bitness (choose `-64` for 64-bit systems). - Visual Studio versions map as follows: `14.2` in the filename corresponds to MSVC 2019, `14.1` to 2017, `14.0` to 2015. _Anything prior to Visual Studio 2015 is unsupported. Please upgrade should you have an older installation._ + Visual Studio versions map as follows: `14.3` in the filename corresponds to MSVC 2022,`14.2` to 2019, `14.1` to 2017, `14.0` to 2015. _Anything prior to Visual Studio 2015 is unsupported. Please upgrade should you have an older installation._ - **Convenience link for Visual Studio 2019: [boost_1_76_0-msvc-14.2-64.exe](https://sourceforge.net/projects/boost/files/boost-binaries/1.76.0/boost_1_76_0-msvc-14.2-64.exe/download)** + **Convenience link for Visual Studio 2022: [boost_1_79_0_b1-msvc-14.3-64.exe](https://sourceforge.net/projects/boost/files/boost-binaries/1.79.0_b1/boost_1_79_0_b1-msvc-14.3-64.exe/download)** 2. When prompted where to install Boost, set the location to `C:\local\boost`. -3. After the installation finishes, rename the `C:\local\boost\lib64-msvc-14.2` (or similar) directory to simply `lib` (`C:\local\boost\lib`). +3. After the installation finishes, rename the `C:\local\boost\boost_1_79_0_b1_rc1` (or similar) directory to simply `lib` (`C:\local\boost\lib`). -Note: This installation will take about 1.5 GB of disk space. +Note: This installation will take about 2.1 GB of disk space. ## OpenSSL @@ -84,7 +84,7 @@ Compiling with Breakpad support enables crash reports that can be of use for dev 1. Open the `chatterino.pro` file by double-clicking it, or by opening it via Qt Creator. 2. You will be presented with a screen that is titled "Configure Project". In this screen, you should have at least one option present ready to be configured, like this: - ![Qt Create Configure Project screenshot](https://user-images.githubusercontent.com/41973452/159462759-470e5371-671e-478e-85ca-33452ca9bea3.png) + ![Qt Create Configure Project screenshot](https://user-images.githubusercontent.com/69117321/169887645-2ae0871a-fe8a-4eb9-98db-7b996dea3a54.png) 3. Select the profile(s) you want to build with and click "Configure Project". ### How to run and produce builds From 7d0023cf73f92995b9067772c1e83069840e625c Mon Sep 17 00:00:00 2001 From: Kasia Date: Sat, 28 May 2022 11:55:48 +0200 Subject: [PATCH 05/18] Corrected the way we parse comma-separated "list tags" in PRIVMSGs (#3771) tl;dr: we now split by slash only its first occurrence instead of every occurrence. --- src/controllers/highlights/HighlightBadge.cpp | 20 +-- src/messages/Message.hpp | 3 +- src/messages/SharedMessageBuilder.cpp | 70 ++++++---- src/messages/SharedMessageBuilder.hpp | 6 + src/providers/twitch/TwitchBadge.cpp | 5 + src/providers/twitch/TwitchBadge.hpp | 10 +- src/providers/twitch/TwitchMessageBuilder.cpp | 81 ++++------- src/providers/twitch/TwitchMessageBuilder.hpp | 4 + tests/CMakeLists.txt | 1 + tests/src/TwitchMessageBuilder.cpp | 130 ++++++++++++++++++ 10 files changed, 228 insertions(+), 102 deletions(-) create mode 100644 tests/src/TwitchMessageBuilder.cpp diff --git a/src/controllers/highlights/HighlightBadge.cpp b/src/controllers/highlights/HighlightBadge.cpp index d00feacc7..8195cbd36 100644 --- a/src/controllers/highlights/HighlightBadge.cpp +++ b/src/controllers/highlights/HighlightBadge.cpp @@ -1,5 +1,6 @@ #include "HighlightBadge.hpp" +#include "messages/SharedMessageBuilder.hpp" #include "singletons/Resources.hpp" namespace chatterino { @@ -86,21 +87,12 @@ bool HighlightBadge::compare(const QString &id, const Badge &badge) const { if (this->hasVersions_) { - auto parts = id.split("/"); - if (parts.size() == 2) - { - return parts.at(0).compare(badge.key_, Qt::CaseInsensitive) == 0 && - parts.at(1).compare(badge.value_, Qt::CaseInsensitive) == 0; - } - else - { - return parts.at(0).compare(badge.key_, Qt::CaseInsensitive) == 0; - } - } - else - { - return id.compare(badge.key_, Qt::CaseInsensitive) == 0; + auto parts = SharedMessageBuilder::slashKeyValue(id); + return parts.first.compare(badge.key_, Qt::CaseInsensitive) == 0 && + parts.second.compare(badge.value_, Qt::CaseInsensitive) == 0; } + + return id.compare(badge.key_, Qt::CaseInsensitive) == 0; } bool HighlightBadge::hasCustomSound() const diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp index d3e258323..a5969daaa 100644 --- a/src/messages/Message.hpp +++ b/src/messages/Message.hpp @@ -2,6 +2,7 @@ #include "common/FlagsEnum.hpp" #include "providers/twitch/TwitchBadge.hpp" +#include "util/QStringHash.hpp" #include "widgets/helper/ScrollbarHighlight.hpp" #include @@ -65,7 +66,7 @@ struct Message : boost::noncopyable { QColor usernameColor; QDateTime serverReceivedTime; std::vector badges; - std::map badgeInfos; + std::unordered_map badgeInfos; std::shared_ptr highlightColor; uint32_t count = 1; std::vector> elements; diff --git a/src/messages/SharedMessageBuilder.cpp b/src/messages/SharedMessageBuilder.cpp index 6b0948491..88f140953 100644 --- a/src/messages/SharedMessageBuilder.cpp +++ b/src/messages/SharedMessageBuilder.cpp @@ -4,7 +4,6 @@ #include "common/QLogging.hpp" #include "controllers/ignores/IgnoreController.hpp" #include "controllers/ignores/IgnorePhrase.hpp" -#include "messages/Message.hpp" #include "messages/MessageElement.hpp" #include "singletons/Settings.hpp" #include "singletons/WindowManager.hpp" @@ -36,33 +35,6 @@ namespace { } } - QStringList parseTagList(const QVariantMap &tags, const QString &key) - { - auto iterator = tags.find(key); - if (iterator == tags.end()) - return QStringList{}; - - return iterator.value().toString().split(',', Qt::SkipEmptyParts); - } - - std::vector parseBadges(const QVariantMap &tags) - { - std::vector badges; - - for (QString badge : parseTagList(tags, "badges")) - { - QStringList parts = badge.split('/'); - if (parts.size() != 2) - { - continue; - } - - badges.emplace_back(parts[0], parts[1]); - } - - return badges; - } - } // namespace SharedMessageBuilder::SharedMessageBuilder( @@ -103,6 +75,46 @@ void SharedMessageBuilder::parse() this->message().flags.set(MessageFlag::Collapsed); } +// "foo/bar/baz,tri/hard" can be a valid badge-info tag +// In that case, valid map content should be 'split by slash' only once: +// {"foo": "bar/baz", "tri": "hard"} +std::pair SharedMessageBuilder::slashKeyValue( + const QString &kvStr) +{ + return { + // part before first slash (index 0 of section) + kvStr.section('/', 0, 0), + // part after first slash (index 1 of section) + kvStr.section('/', 1, -1), + }; +} + +std::vector SharedMessageBuilder::parseBadgeTag(const QVariantMap &tags) +{ + std::vector b; + + auto badgesIt = tags.constFind("badges"); + if (badgesIt == tags.end()) + { + return b; + } + + auto badges = badgesIt.value().toString().split(',', Qt::SkipEmptyParts); + + for (const QString &badge : badges) + { + if (!badge.contains('/')) + { + continue; + } + + auto pair = SharedMessageBuilder::slashKeyValue(badge); + b.emplace_back(Badge{pair.first, pair.second}); + } + + return b; +} + bool SharedMessageBuilder::isIgnored() const { return isIgnoredMessage({ @@ -332,7 +344,7 @@ void SharedMessageBuilder::parseHighlights() } // Highlight because of badge - auto badges = parseBadges(this->tags); + auto badges = this->parseBadgeTag(this->tags); auto badgeHighlights = getCSettings().highlightedBadges.readOnly(); bool badgeHighlightSet = false; for (const HighlightBadge &highlight : *badgeHighlights) diff --git a/src/messages/SharedMessageBuilder.hpp b/src/messages/SharedMessageBuilder.hpp index 2bee11fcd..62adb45df 100644 --- a/src/messages/SharedMessageBuilder.hpp +++ b/src/messages/SharedMessageBuilder.hpp @@ -3,6 +3,7 @@ #include "common/Aliases.hpp" #include "common/Outcome.hpp" #include "messages/MessageColor.hpp" +#include "providers/twitch/TwitchBadge.hpp" #include #include @@ -32,6 +33,11 @@ public: virtual void triggerHighlights(); virtual MessagePtr build() = 0; + static std::pair slashKeyValue(const QString &kvStr); + + // Parses "badges" tag which contains a comma separated list of key-value elements + static std::vector parseBadgeTag(const QVariantMap &tags); + protected: virtual void parse(); diff --git a/src/providers/twitch/TwitchBadge.cpp b/src/providers/twitch/TwitchBadge.cpp index b48d3cb7b..0a8799927 100644 --- a/src/providers/twitch/TwitchBadge.cpp +++ b/src/providers/twitch/TwitchBadge.cpp @@ -33,4 +33,9 @@ Badge::Badge(QString key, QString value) } } +bool Badge::operator==(const Badge &other) const +{ + return this->key_ == other.key_ && this->value_ == other.value_; +} + } // namespace chatterino diff --git a/src/providers/twitch/TwitchBadge.hpp b/src/providers/twitch/TwitchBadge.hpp index 764be2e71..f3267687e 100644 --- a/src/providers/twitch/TwitchBadge.hpp +++ b/src/providers/twitch/TwitchBadge.hpp @@ -11,9 +11,13 @@ class Badge public: Badge(QString key, QString value); - QString key_; // e.g. bits - QString value_; // e.g. 100 - QString extraValue_{}; // e.g. 5 (the number of months subscribed) + bool operator==(const Badge &other) const; + + // Class members are fetched from both "badges" and "badge-info" tags + // E.g.: "badges": "subscriber/18", "badge-info": "subscriber/22" + QString key_; // subscriber + QString value_; // 18 + //QString info_; // 22 (should be parsed separetly into an std::unordered_map) MessageElementFlag flag_{ MessageElementFlag::BadgeVanity}; // badge slot it takes up }; diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 4d831252e..aadaa4119 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -7,6 +7,7 @@ #include "messages/Message.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/ffz/FfzBadges.hpp" +#include "providers/twitch/TwitchBadge.hpp" #include "providers/twitch/TwitchBadges.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchIrcServer.hpp" @@ -47,55 +48,6 @@ const QSet zeroWidthEmotes{ namespace chatterino { -namespace { - - QStringList parseTagList(const QVariantMap &tags, const QString &key) - { - auto iterator = tags.find(key); - if (iterator == tags.end()) - return QStringList{}; - - return iterator.value().toString().split(',', Qt::SkipEmptyParts); - } - - std::map parseBadgeInfos(const QVariantMap &tags) - { - std::map badgeInfos; - - for (QString badgeInfo : parseTagList(tags, "badge-info")) - { - QStringList parts = badgeInfo.split('/'); - if (parts.size() != 2) - { - continue; - } - - badgeInfos.emplace(parts[0], parts[1]); - } - - return badgeInfos; - } - - std::vector parseBadges(const QVariantMap &tags) - { - std::vector badges; - - for (QString badge : parseTagList(tags, "badges")) - { - QStringList parts = badge.split('/'); - if (parts.size() != 2) - { - continue; - } - - badges.emplace_back(parts[0], parts[1]); - } - - return badges; - } - -} // namespace - TwitchMessageBuilder::TwitchMessageBuilder( Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage, const MessageParseArgs &_args) @@ -1033,6 +985,25 @@ boost::optional TwitchMessageBuilder::getTwitchBadge( return boost::none; } +std::unordered_map TwitchMessageBuilder::parseBadgeInfoTag( + const QVariantMap &tags) +{ + std::unordered_map infoMap; + + auto infoIt = tags.constFind("badge-info"); + if (infoIt == tags.end()) + return infoMap; + + auto info = infoIt.value().toString().split(',', Qt::SkipEmptyParts); + + for (const QString &badge : info) + { + infoMap.emplace(SharedMessageBuilder::slashKeyValue(badge)); + } + + return infoMap; +} + void TwitchMessageBuilder::appendTwitchBadges() { if (this->twitchChannel == nullptr) @@ -1040,8 +1011,8 @@ void TwitchMessageBuilder::appendTwitchBadges() return; } - auto badgeInfos = parseBadgeInfos(this->tags); - auto badges = parseBadges(this->tags); + auto badgeInfos = TwitchMessageBuilder::parseBadgeInfoTag(this->tags); + auto badges = this->parseBadgeTag(this->tags); for (const auto &badge : badges) { @@ -1091,7 +1062,7 @@ void TwitchMessageBuilder::appendTwitchBadges() // (tier + amount of months with leading zero if less than 100) // e.g. 3054 - tier 3 4,5-year sub. 2108 - tier 2 9-year sub const auto &subTier = - badge.value_.length() > 3 ? badge.value_.front() : '1'; + badge.value_.length() > 3 ? badge.value_.at(0) : '1'; const auto &subMonths = badgeInfoIt->second; tooltip += QString(" (%1%2 months)") @@ -1107,9 +1078,9 @@ void TwitchMessageBuilder::appendTwitchBadges() { auto predictionText = badgeInfoIt->second - .replace("\\s", " ") // standard IRC escapes - .replace("\\:", ";") - .replace("\\\\", "\\") + .replace(R"(\s)", " ") // standard IRC escapes + .replace(R"(\:)", ";") + .replace(R"(\\)", R"(\)") .replace("⸝", ","); // twitch's comma escape // Careful, the first character is RIGHT LOW PARAPHRASE BRACKET or U+2E1D, which just looks like a comma diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 0d4091d50..c96712d61 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -68,6 +68,10 @@ public: Channel *channel, MessageBuilder *builder); + // Shares some common logic from SharedMessageBuilder::parseBadgeTag + static std::unordered_map parseBadgeInfoTag( + const QVariantMap &tags); + private: void parseUsernameColor() override; void parseUsername() override; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 61e583f42..58db955ec 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,6 +17,7 @@ set(test_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/UtilTwitch.cpp ${CMAKE_CURRENT_LIST_DIR}/src/IrcHelpers.cpp ${CMAKE_CURRENT_LIST_DIR}/src/TwitchPubSubClient.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/TwitchMessageBuilder.cpp # Add your new file above this line! ) diff --git a/tests/src/TwitchMessageBuilder.cpp b/tests/src/TwitchMessageBuilder.cpp new file mode 100644 index 000000000..24eb42641 --- /dev/null +++ b/tests/src/TwitchMessageBuilder.cpp @@ -0,0 +1,130 @@ +#include "providers/twitch/TwitchMessageBuilder.hpp" + +#include "common/Channel.hpp" +#include "messages/MessageBuilder.hpp" +#include "providers/twitch/TwitchBadge.hpp" + +#include "ircconnection.h" + +#include +#include +#include +#include +#include + +using namespace chatterino; + +TEST(TwitchMessageBuilder, CommaSeparatedListTagParsing) +{ + struct TestCase { + QString input; + std::pair expectedOutput; + }; + + std::vector testCases{ + { + "broadcaster/1", + {"broadcaster", "1"}, + }, + { + "predictions/foo/bar/baz", + {"predictions", "foo/bar/baz"}, + }, + { + "test/", + {"test", ""}, + }, + { + "/", + {"", ""}, + }, + { + "/value", + {"", "value"}, + }, + { + "", + {"", ""}, + }, + }; + + for (const auto &test : testCases) + { + auto output = TwitchMessageBuilder::slashKeyValue(test.input); + + EXPECT_EQ(output, test.expectedOutput) + << "Input " << test.input.toStdString() << " failed"; + } +} + +TEST(TwitchMessageBuilder, BadgeInfoParsing) +{ + struct TestCase { + QByteArray input; + std::unordered_map expectedBadgeInfo; + std::vector expectedBadges; + }; + + std::vector testCases{ + { + R"(@badge-info=predictions/<<<<<<\sHEAD[15A⸝asdf/test;badges=predictions/pink-2;client-nonce=9dbb88e516edf4efb055c011f91ea0cf;color=#FF4500;display-name=もっと頑張って;emotes=;first-msg=0;flags=;id=feb00b12-4ec5-4f77-9160-667de463dab1;mod=0;room-id=99631238;subscriber=0;tmi-sent-ts=1653494874297;turbo=0;user-id=648946956;user-type= :zniksbot!zniksbot@zniksbot.tmi.twitch.tv PRIVMSG #zneix :-tags")", + { + {"predictions", R"(<<<<<<\sHEAD[15A⸝asdf/test)"}, + }, + { + Badge{"predictions", "pink-2"}, + }, + }, + { + R"(@badge-info=predictions/<<<<<<\sHEAD[15A⸝asdf/test,founder/17;badges=predictions/pink-2,vip/1,founder/0,bits/1;client-nonce=9b836e232170a9df213aefdcb458b67e;color=#696969;display-name=NotKarar;emotes=;first-msg=0;flags=;id=e00881bd-5f21-4993-8bbd-1736cd13d42e;mod=0;room-id=99631238;subscriber=1;tmi-sent-ts=1653494879409;turbo=0;user-id=89954186;user-type= :notkarar!notkarar@notkarar.tmi.twitch.tv PRIVMSG #zneix :-tags)", + { + {"predictions", R"(<<<<<<\sHEAD[15A⸝asdf/test)"}, + {"founder", "17"}, + }, + { + Badge{"predictions", "pink-2"}, + Badge{"vip", "1"}, + Badge{"founder", "0"}, + Badge{"bits", "1"}, + }, + }, + { + R"(@badge-info=predictions/foo/bar/baz;badges=predictions/blue-1,moderator/1,glhf-pledge/1;client-nonce=f73f16228e6e32f8e92b47ab8283b7e1;color=#1E90FF;display-name=zneixbot;emotes=30259:6-12;first-msg=0;flags=;id=9682a5f1-a0b0-45e2-be9f-8074b58c5f8f;mod=1;room-id=99631238;subscriber=0;tmi-sent-ts=1653573594035;turbo=0;user-id=463521670;user-type=mod :zneixbot!zneixbot@zneixbot.tmi.twitch.tv PRIVMSG #zneix :-tags HeyGuys)", + { + {"predictions", "foo/bar/baz"}, + }, + { + Badge{"predictions", "blue-1"}, + Badge{"moderator", "1"}, + Badge{"glhf-pledge", "1"}, + }, + }, + { + R"(@badge-info=subscriber/22;badges=broadcaster/1,subscriber/18,glhf-pledge/1;color=#F97304;display-name=zneix;emotes=;first-msg=0;flags=;id=1d99f67f-a566-4416-a4e2-e85d7fce9223;mod=0;room-id=99631238;subscriber=1;tmi-sent-ts=1653612232758;turbo=0;user-id=99631238;user-type= :zneix!zneix@zneix.tmi.twitch.tv PRIVMSG #zneix :-tags)", + { + {"subscriber", "22"}, + }, + { + Badge{"broadcaster", "1"}, + Badge{"subscriber", "18"}, + Badge{"glhf-pledge", "1"}, + }, + }, + }; + + for (const auto &test : testCases) + { + auto privmsg = + Communi::IrcPrivateMessage::fromData(test.input, nullptr); + + auto outputBadgeInfo = + TwitchMessageBuilder::parseBadgeInfoTag(privmsg->tags()); + EXPECT_EQ(outputBadgeInfo, test.expectedBadgeInfo) + << "Input for badgeInfo " << test.input.toStdString() << " failed"; + + auto outputBadges = + SharedMessageBuilder::parseBadgeTag(privmsg->tags()); + EXPECT_EQ(outputBadges, test.expectedBadges) + << "Input for badges " << test.input.toStdString() << " failed"; + } +} From 3e1e30e4c2c354c527ae52667ece134f1d433ba4 Mon Sep 17 00:00:00 2001 From: xel86 Date: Sat, 28 May 2022 06:25:58 -0400 Subject: [PATCH 06/18] Add scrollbar to `Select filters` dialog (#3737) Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/widgets/dialogs/SelectChannelFiltersDialog.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 720014b34..6ab30cecf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Minor: Fixed tag parsing for consecutive escaped characters. (#3711) - Minor: Prevent user from entering incorrect characters in Live Notifications channels list. (#3715, #3730) - Minor: Fixed automod caught message notice appearing twice for mods. (#3717) +- Minor: Add scrollbar to `Select filters` dialog. (#3737) - Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746) - Minor: Added ability to execute commands on chat messages using the message context menu. (#3738) - Minor: Added `/copy` command. Usage: `/copy `. Copies provided text to clipboard - can be useful with custom commands. (#3763) diff --git a/src/widgets/dialogs/SelectChannelFiltersDialog.cpp b/src/widgets/dialogs/SelectChannelFiltersDialog.cpp index 2421d8bd8..1cf7d75ef 100644 --- a/src/widgets/dialogs/SelectChannelFiltersDialog.cpp +++ b/src/widgets/dialogs/SelectChannelFiltersDialog.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace chatterino { @@ -19,7 +20,15 @@ SelectChannelFiltersDialog::SelectChannelFiltersDialog( auto okButton = new QPushButton("Ok"); auto cancelButton = new QPushButton("Cancel"); - vbox->addLayout(itemVbox); + auto scrollAreaContent = new QWidget; + scrollAreaContent->setLayout(itemVbox); + + auto scrollArea = new QScrollArea; + scrollArea->setWidgetResizable(true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setWidget(scrollAreaContent); + + vbox->addWidget(scrollArea); vbox->addLayout(buttonBox); buttonBox->addStretch(1); From 135f914b387c1358409dc007608f88bfe13d69ba Mon Sep 17 00:00:00 2001 From: badoge <18620902+badoge@users.noreply.github.com> Date: Sat, 28 May 2022 15:11:51 +0400 Subject: [PATCH 07/18] Hide category labels when searching in Viewer list (#3719) This fixed a bug introduced in #3683 Co-authored-by: pajlada --- CHANGELOG.md | 2 +- src/widgets/splits/Split.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab30cecf..c71505055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - Minor: Fixed being unable to load Twitch Usercards from the `/mentions` tab. (#3623) - Minor: Add information about the user's operating system in the About page. (#3663) - Bugfix: Connection to Twitch PubSub now recovers more reliably. (#3643, #3716) -- Minor: Added chatter count for each category in viewer list. (#3683) +- Minor: Added chatter count for each category in viewer list. (#3683, #3719) - Minor: Sorted usernames in /vips message to be case-insensitive. (#3696) - Minor: Added option to open a user's chat in a new tab from the usercard profile picture context menu. (#3625) - Minor: Fixed tag parsing for consecutive escaped characters. (#3711) diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index ac9923a15..8f9b7370d 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -1037,7 +1037,7 @@ void Split::showViewerList() resultList->clear(); for (auto &item : results) { - if (!labels.contains(item->text())) + if (!item->text().contains("(")) { resultList->addItem(formatListItemText(item->text())); } From efcfb1918766cf4b0c78949e1be47d3c05707cf8 Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sat, 28 May 2022 11:48:31 +0000 Subject: [PATCH 08/18] Add safe checks around use of QImageReader (#3736) Co-authored-by: pajlada --- src/messages/Image.cpp | 32 +++++++++++++++++++-------- src/messages/MessageBuilder.cpp | 1 - src/providers/twitch/TwitchBadges.cpp | 14 ++++++++++-- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index 89e382fc3..fda0637f6 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -138,14 +138,6 @@ namespace detail { { QVector> frames; - if (reader.imageCount() == 0) - { - qCDebug(chatterinoImage) - << "Error while reading image" << url.string << ": '" - << reader.errorString() << "'"; - return frames; - } - QImage image; for (int index = 0; index < reader.imageCount(); ++index) { @@ -413,8 +405,30 @@ void Image::actuallyLoad() buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer); + if (!reader.canRead()) + { + qCDebug(chatterinoImage) + << "Error: image cant be read " << shared->url().string; + return Failure; + } + + const auto size = reader.size(); + if (size.isEmpty()) + { + return Failure; + } + + // returns 1 for non-animated formats + if (reader.imageCount() <= 0) + { + qCDebug(chatterinoImage) + << "Error: image has less than 1 frame " + << shared->url().string << ": " << reader.errorString(); + return Failure; + } + // use "double" to prevent int overflows - if (double(reader.size().width()) * double(reader.size().height()) * + if (double(size.width()) * double(size.height()) * double(reader.imageCount()) * 4.0 > double(Image::maxBytesRam)) { diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 376d1fa48..c34fd3b85 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -14,7 +14,6 @@ #include "util/FormatTime.hpp" #include -#include namespace chatterino { diff --git a/src/providers/twitch/TwitchBadges.cpp b/src/providers/twitch/TwitchBadges.cpp index 4394da9e6..11fe07af5 100644 --- a/src/providers/twitch/TwitchBadges.cpp +++ b/src/providers/twitch/TwitchBadges.cpp @@ -204,8 +204,18 @@ void TwitchBadges::loadEmoteImage(const QString &name, ImagePtr image, buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer); - QImage image; - if (reader.imageCount() == 0 || !reader.read(&image)) + if (!reader.canRead() || reader.size().isEmpty()) + { + return Failure; + } + + QImage image = reader.read(); + if (image.isNull()) + { + return Failure; + } + + if (reader.imageCount() <= 0) { return Failure; } From 57f92f5eaa6376adb7b6c17f85fa34f57d13d4c3 Mon Sep 17 00:00:00 2001 From: badoge <18620902+badoge@users.noreply.github.com> Date: Sat, 28 May 2022 16:17:42 +0400 Subject: [PATCH 09/18] Added more streaming software to streamer mode binaries list (#3740) Streamer mode now automatically detects if XSplit, PRISM Live Studio, Twitch Studio, or vMix are running Co-authored-by: Kasia Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/singletons/Settings.hpp | 2 +- src/util/StreamerMode.cpp | 8 +++++--- src/util/StreamerMode.hpp | 6 +++++- src/widgets/Window.cpp | 2 +- src/widgets/settingspages/GeneralPage.cpp | 10 +++++----- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c71505055..824c93548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Minor: Fixed tag parsing for consecutive escaped characters. (#3711) - Minor: Prevent user from entering incorrect characters in Live Notifications channels list. (#3715, #3730) - Minor: Fixed automod caught message notice appearing twice for mods. (#3717) +- Minor: Streamer mode now automatically detects if XSplit, PRISM Live Studio, Twitch Studio, or vMix are running. (#3740) - Minor: Add scrollbar to `Select filters` dialog. (#3737) - Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746) - Minor: Added ability to execute commands on chat messages using the message context menu. (#3738) diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 14ade6c93..53b076a09 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -208,7 +208,7 @@ public: /// Streamer Mode EnumSetting enableStreamerMode = { - "/streamerMode/enabled", StreamerModeSetting::DetectObs}; + "/streamerMode/enabled", StreamerModeSetting::DetectStreamingSoftware}; BoolSetting streamerModeHideUsercardAvatars = { "/streamerMode/hideUsercardAvatars", true}; BoolSetting streamerModeHideLinkThumbnails = { diff --git a/src/util/StreamerMode.cpp b/src/util/StreamerMode.cpp index dfe8d85b6..2724ed73f 100644 --- a/src/util/StreamerMode.cpp +++ b/src/util/StreamerMode.cpp @@ -30,9 +30,11 @@ bool shouldShowWarning = true; const QStringList &broadcastingBinaries() { #ifdef USEWINSDK - static QStringList bins = {"obs.exe", "obs64.exe"}; + static QStringList bins = { + "obs.exe", "obs64.exe", "PRISMLiveStudio.exe", + "XSplit.Core.exe", "TwitchStudio.exe", "vMix64.exe"}; #else - static QStringList bins = {"obs"}; + static QStringList bins = {"obs", "Twitch Studio", "Streamlabs Desktop"}; #endif return bins; } @@ -45,7 +47,7 @@ bool isInStreamerMode() return true; case StreamerModeSetting::Disabled: return false; - case StreamerModeSetting::DetectObs: + case StreamerModeSetting::DetectStreamingSoftware: #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) diff --git a/src/util/StreamerMode.hpp b/src/util/StreamerMode.hpp index 0c8daaf6a..d161b9067 100644 --- a/src/util/StreamerMode.hpp +++ b/src/util/StreamerMode.hpp @@ -4,7 +4,11 @@ namespace chatterino { -enum StreamerModeSetting { Disabled = 0, Enabled = 1, DetectObs = 2 }; +enum StreamerModeSetting { + Disabled = 0, + Enabled = 1, + DetectStreamingSoftware = 2, +}; const QStringList &broadcastingBinaries(); bool isInStreamerMode(); diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index ee4f2c9f0..bc271103c 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -620,7 +620,7 @@ void Window::addShortcuts() else if (mode == 3) { getSettings()->enableStreamerMode.setValue( - StreamerModeSetting::DetectObs); + StreamerModeSetting::DetectStreamingSoftware); } return ""; }}, diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 8895ccd24..85b13a179 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -336,14 +336,14 @@ void GeneralPage::initLayout(GeneralPageView &layout) layout.addTitle("Streamer Mode"); layout.addDescription( - "Chatterino can automatically change behavior if it detects that \"OBS " - "Studio\" is running.\nSelect which things you want to change while " - "streaming"); + "Chatterino can automatically change behavior if it detects that any " + "streaming software is running.\nSelect which things you want to " + "change while streaming"); ComboBox *dankDropdown = layout.addDropdown::type>( "Enable Streamer Mode", - {"Disabled", "Enabled", "Automatic (Detect OBS)"}, + {"Disabled", "Enabled", "Automatic (Detect streaming software)"}, s.enableStreamerMode, [](int value) { return value; @@ -352,7 +352,7 @@ void GeneralPage::initLayout(GeneralPageView &layout) return static_cast(args.index); }, false); - dankDropdown->setMinimumWidth(dankDropdown->minimumSizeHint().width() + 10); + dankDropdown->setMinimumWidth(dankDropdown->minimumSizeHint().width() + 30); layout.addCheckbox("Hide usercard avatars", s.streamerModeHideUsercardAvatars); From 74ec310228698ccfa1d897beff8a58b337ed3f73 Mon Sep 17 00:00:00 2001 From: Kasia Date: Sat, 28 May 2022 20:10:10 +0200 Subject: [PATCH 10/18] Fixed channels not being set as offline (#3767) --- CHANGELOG.md | 2 +- .../notifications/NotificationController.cpp | 3 + src/providers/twitch/TwitchChannel.cpp | 3 + src/providers/twitch/TwitchIrcServer.cpp | 104 +++++++++++------- src/providers/twitch/api/Helix.cpp | 15 ++- src/providers/twitch/api/Helix.hpp | 18 ++- 6 files changed, 92 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 824c93548..66c731ccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ - Bugfix: Fixed viewer list not closing after pressing escape key. (#3734) - Bugfix: Fixed links with no thumbnail having previous link's thumbnail. (#3720) - Dev: Use Game Name returned by Get Streams instead of querying it from the Get Games API. (#3662) -- Dev: Batch checking live status for all channels after startup. (#3757, #3762) +- Dev: Batch checking live status for all channels after startup. (#3757, #3762, #3767) ## 2.3.5 diff --git a/src/controllers/notifications/NotificationController.cpp b/src/controllers/notifications/NotificationController.cpp index a2f2c5ae2..0d20ce6e1 100644 --- a/src/controllers/notifications/NotificationController.cpp +++ b/src/controllers/notifications/NotificationController.cpp @@ -191,6 +191,9 @@ void NotificationController::fetchFakeChannels() // we done fucked up. qCWarning(chatterinoNotification) << "Failed to fetch live status for " << batch; + }, + []() { + // finally }); } } diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 0f6feba57..26e30a976 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -708,6 +708,9 @@ void TwitchChannel::refreshLiveStatus() }, [] { // failure + }, + [] { + // finally }); } diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 08d68ddaa..d803f45cc 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -26,6 +26,35 @@ using namespace std::chrono_literals; namespace chatterino { +namespace { + // TODO: combine this with getEmoteSetBatches in TwitchAccount.cpp, maybe some templated thing + template + std::vector getChannelsInBatches(T channels) + { + constexpr int batchSize = 100; + + int batchCount = (channels.size() / batchSize) + 1; + + std::vector batches; + batches.reserve(batchCount); + + for (int i = 0; i < batchCount; i++) + { + T batch; + + // I hate you, msvc + int last = (std::min)(batchSize, channels.size() - batchSize * i); + for (int j = 0; j < last; j++) + { + batch.push_back(channels.at(j + (batchSize * i))); + } + batches.emplace_back(batch); + } + + return batches; + } +} // namespace + TwitchIrcServer::TwitchIrcServer() : whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers)) , mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions)) @@ -302,60 +331,55 @@ std::shared_ptr TwitchIrcServer::getChannelOrEmptyByID( return Channel::getEmpty(); } -namespace { - // TODO: combine this with getEmoteSetBatches in TwitchAccount.cpp, maybe some templated thing - std::vector getChannelsInBatches(QStringList channels) - { - constexpr int batchSize = 100; - - int batchCount = (channels.size() / batchSize) + 1; - - std::vector batches; - batches.reserve(batchCount); - - for (int i = 0; i < batchCount; i++) - { - QStringList batch; - - // I hate you, msvc - int last = (std::min)(batchSize, channels.size() - batchSize * i); - for (int j = 0; j < last; j++) - { - batch.push_back(channels.at(j + (batchSize * i))); - } - batches.emplace_back(batch); - } - - return batches; - } -} // namespace - void TwitchIrcServer::bulkRefreshLiveStatus() { - QStringList userIDs; - this->forEachChannel([&userIDs](ChannelPtr chan) { - auto twitchChan = dynamic_cast(chan.get()); - if (!twitchChan->roomId().isEmpty()) - userIDs.push_back(twitchChan->roomId()); + auto twitchChans = std::make_shared>(); + + this->forEachChannel([twitchChans](ChannelPtr chan) { + auto tc = dynamic_cast(chan.get()); + if (tc && !tc->roomId().isEmpty()) + { + twitchChans->insert(tc->roomId(), tc); + } }); - for (const auto &batch : getChannelsInBatches(userIDs)) + // iterate over batches of channel IDs + for (const auto &batch : getChannelsInBatches(twitchChans->keys())) { getHelix()->fetchStreams( - batch, QStringList(), - [this](std::vector streams) { + batch, {}, + [twitchChans](std::vector streams) { for (const auto &stream : streams) { - auto chan = this->getChannelOrEmpty(stream.userLogin); - if (chan->getType() != Channel::Type::Twitch) + // remaining channels will be used later to set their stream status as offline + // so we use take(id) to remove it + auto tc = twitchChans->take(stream.userId); + if (tc == nullptr) + { continue; + } - auto twitchChan = dynamic_cast(chan.get()); - twitchChan->parseLiveStatus(true, stream); + tc->parseLiveStatus(true, stream); } }, []() { // failure + }, + [batch, twitchChans] { + // All the channels that were not present in fetchStreams response should be assumed to be offline + // It is necessary to update their stream status in case they've gone live -> offline + // Otherwise some of them will be marked as live forever + for (const auto &chID : batch) + { + auto tc = twitchChans->value(chID); + // early out in case channel does not exist anymore + if (tc == nullptr) + { + continue; + } + + tc->parseLiveStatus(false, {}); + } }); } } diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp index ee47489a0..953d7d989 100644 --- a/src/providers/twitch/api/Helix.cpp +++ b/src/providers/twitch/api/Helix.cpp @@ -145,7 +145,7 @@ void Helix::getUserFollowers( void Helix::fetchStreams( QStringList userIds, QStringList userLogins, ResultCallback> successCallback, - HelixFailureCallback failureCallback) + HelixFailureCallback failureCallback, std::function finallyCallback) { QUrlQuery urlQuery; @@ -186,19 +186,21 @@ void Helix::fetchStreams( // TODO: make better xd failureCallback(); }) + .finally(finallyCallback) .execute(); } void Helix::getStreamById(QString userId, ResultCallback successCallback, - HelixFailureCallback failureCallback) + HelixFailureCallback failureCallback, + std::function finallyCallback) { QStringList userIds{std::move(userId)}; QStringList userLogins; this->fetchStreams( userIds, userLogins, - [successCallback, failureCallback](const auto &streams) { + [successCallback](const auto &streams) { if (streams.empty()) { successCallback(false, HelixStream()); @@ -206,12 +208,13 @@ void Helix::getStreamById(QString userId, } successCallback(true, streams[0]); }, - failureCallback); + failureCallback, finallyCallback); } void Helix::getStreamByName(QString userName, ResultCallback successCallback, - HelixFailureCallback failureCallback) + HelixFailureCallback failureCallback, + std::function finallyCallback) { QStringList userIds; QStringList userLogins{std::move(userName)}; @@ -226,7 +229,7 @@ void Helix::getStreamByName(QString userName, } successCallback(true, streams[0]); }, - failureCallback); + failureCallback, finallyCallback); } /// diff --git a/src/providers/twitch/api/Helix.hpp b/src/providers/twitch/api/Helix.hpp index 3e0c3e54d..878c40eb0 100644 --- a/src/providers/twitch/api/Helix.hpp +++ b/src/providers/twitch/api/Helix.hpp @@ -352,15 +352,18 @@ public: virtual void fetchStreams( QStringList userIds, QStringList userLogins, ResultCallback> successCallback, - HelixFailureCallback failureCallback) = 0; + HelixFailureCallback failureCallback, + std::function finallyCallback) = 0; virtual void getStreamById( QString userId, ResultCallback successCallback, - HelixFailureCallback failureCallback) = 0; + HelixFailureCallback failureCallback, + std::function finallyCallback) = 0; virtual void getStreamByName( QString userName, ResultCallback successCallback, - HelixFailureCallback failureCallback) = 0; + HelixFailureCallback failureCallback, + std::function finallyCallback) = 0; // https://dev.twitch.tv/docs/api/reference#get-games virtual void fetchGames( @@ -469,15 +472,18 @@ public: // https://dev.twitch.tv/docs/api/reference#get-streams void fetchStreams(QStringList userIds, QStringList userLogins, ResultCallback> successCallback, - HelixFailureCallback failureCallback) final; + HelixFailureCallback failureCallback, + std::function finallyCallback) final; void getStreamById(QString userId, ResultCallback successCallback, - HelixFailureCallback failureCallback) final; + HelixFailureCallback failureCallback, + std::function finallyCallback) final; void getStreamByName(QString userName, ResultCallback successCallback, - HelixFailureCallback failureCallback) final; + HelixFailureCallback failureCallback, + std::function finallyCallback) final; // https://dev.twitch.tv/docs/api/reference#get-games void fetchGames(QStringList gameIds, QStringList gameNames, From 8b98f0e142c633812fc96d057f7ab5f4e93bb8ab Mon Sep 17 00:00:00 2001 From: Kasia Date: Sun, 29 May 2022 12:19:26 +0200 Subject: [PATCH 11/18] Removed unused files IsBigEndian.hpp and rangealgorithm.hpp (#3776) util/IsBigEndian.hpp and util/rangealgorithm.hpp have been unused for a long time, removing them from the codebase because git keeps the history forever anyway! --- chatterino.pro | 2 -- src/Application.cpp | 1 - src/BrowserExtension.cpp | 13 ------------- src/util/IsBigEndian.hpp | 13 ------------- src/util/rangealgorithm.hpp | 22 ---------------------- 5 files changed, 51 deletions(-) delete mode 100644 src/util/IsBigEndian.hpp delete mode 100644 src/util/rangealgorithm.hpp diff --git a/chatterino.pro b/chatterino.pro index 8380ef9b5..4f7573c29 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -527,7 +527,6 @@ HEADERS += \ src/util/IncognitoBrowser.hpp \ src/util/InitUpdateButton.hpp \ src/util/IrcHelpers.hpp \ - src/util/IsBigEndian.hpp \ src/util/LayoutCreator.hpp \ src/util/LayoutHelper.hpp \ src/util/NuulsUploader.hpp \ @@ -536,7 +535,6 @@ HEADERS += \ src/util/PostToThread.hpp \ src/util/QObjectRef.hpp \ src/util/QStringHash.hpp \ - src/util/rangealgorithm.hpp \ src/util/RapidjsonHelpers.hpp \ src/util/RapidJsonSerializeQString.hpp \ src/util/RatelimitBucket.hpp \ diff --git a/src/Application.cpp b/src/Application.cpp index 234d3d7c2..47d56f3c4 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -32,7 +32,6 @@ #include "singletons/Updates.hpp" #include "singletons/WindowManager.hpp" #include "util/Helpers.hpp" -#include "util/IsBigEndian.hpp" #include "util/PostToThread.hpp" #include "util/RapidjsonHelpers.hpp" #include "widgets/Notebook.hpp" diff --git a/src/BrowserExtension.cpp b/src/BrowserExtension.cpp index f004b659a..dad0ac2af 100644 --- a/src/BrowserExtension.cpp +++ b/src/BrowserExtension.cpp @@ -58,19 +58,6 @@ namespace { auto size = *reinterpret_cast(size_c); -#if 0 - bool bigEndian = isBigEndian(); - // To avoid breaking strict-aliasing rules and potentially inducing undefined behaviour, the following code can be run instead - uint32_t size = 0; - if (bigEndian) { - size = size_c[3] | static_cast(size_c[2]) << 8 | - static_cast(size_c[1]) << 16 | static_cast(size_c[0]) << 24; - } else { - size = size_c[0] | static_cast(size_c[1]) << 8 | - static_cast(size_c[2]) << 16 | static_cast(size_c[3]) << 24; - } -#endif - std::unique_ptr buffer(new char[size + 1]); std::cin.read(buffer.get(), size); *(buffer.get() + size) = '\0'; diff --git a/src/util/IsBigEndian.hpp b/src/util/IsBigEndian.hpp deleted file mode 100644 index 20e516b73..000000000 --- a/src/util/IsBigEndian.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -namespace chatterino { - -bool isBigEndian() -{ - int test = 1; - char *p = reinterpret_cast(&test); - - return p[0] == 0; -} - -} // namespace chatterino diff --git a/src/util/rangealgorithm.hpp b/src/util/rangealgorithm.hpp deleted file mode 100644 index 03c7e19ff..000000000 --- a/src/util/rangealgorithm.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -namespace chatterino { -namespace util { - - template - typename Container::iterator find_if(Container &container, - UnaryPredicate pred) - { - return std::find_if(container.begin(), container.end(), pred); - } - - template - bool any_of(Container &container, UnaryPredicate pred) - { - return std::any_of(container.begin(), container.end(), pred); - } - -} // namespace util -} // namespace chatterino From 143f4ef2ec376c8929a16f2035b39ac142a09b98 Mon Sep 17 00:00:00 2001 From: kornes <28986062+kornes@users.noreply.github.com> Date: Sun, 29 May 2022 11:06:01 +0000 Subject: [PATCH 12/18] Fix viewers list search when used before loading finishes (#3774) Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/widgets/splits/Split.cpp | 70 +++++++++++++++++++----------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66c731ccc..b320fed61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746) - Minor: Added ability to execute commands on chat messages using the message context menu. (#3738) - Minor: Added `/copy` command. Usage: `/copy `. Copies provided text to clipboard - can be useful with custom commands. (#3763) +- Bugfix: Fixed viewers list search not working when used before loading finishes. (#3774) - Bugfix: Fixed live notifications for usernames containing uppercase characters. (#3646) - Bugfix: Fixed live notifications not getting updated for closed streams going offline. (#3678) - Bugfix: Fixed certain settings dialogs appearing behind the main window, when `Always on top` was used. (#3679) diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index 8f9b7370d..a805b822e 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -990,6 +990,33 @@ void Split::showViewerList() "viewers"}; auto loadingLabel = new QLabel("Loading..."); + searchBar->setPlaceholderText("Search User..."); + + auto performListSearch = [=]() { + auto query = searchBar->text(); + if (query.isEmpty()) + { + resultList->hide(); + chattersList->show(); + return; + } + + auto results = chattersList->findItems(query, Qt::MatchContains); + chattersList->hide(); + resultList->clear(); + for (auto &item : results) + { + if (!item->text().contains("(")) + { + resultList->addItem(formatListItemText(item->text())); + } + } + resultList->show(); + }; + + QObject::connect(searchBar, &QLineEdit::textEdited, this, + performListSearch); + NetworkRequest::twitchRequest("https://tmi.twitch.tv/group/user/" + this->getChannel()->getName() + "/chatters") .caller(this) @@ -1023,54 +1050,31 @@ void Split::showViewerList() chattersList->addItem(new QListWidgetItem()); } + performListSearch(); return Success; }) .execute(); - searchBar->setPlaceholderText("Search User..."); - QObject::connect(searchBar, &QLineEdit::textEdited, this, [=]() { - auto query = searchBar->text(); - if (!query.isEmpty()) - { - auto results = chattersList->findItems(query, Qt::MatchContains); - chattersList->hide(); - resultList->clear(); - for (auto &item : results) - { - if (!item->text().contains("(")) - { - resultList->addItem(formatListItemText(item->text())); - } - } - resultList->show(); - } - else - { - resultList->hide(); - chattersList->show(); - } - }); - QObject::connect(viewerDock, &QDockWidget::topLevelChanged, this, [=]() { viewerDock->setMinimumWidth(300); }); - auto listDoubleClick = [=](QString userName) { + auto listDoubleClick = [this](const QModelIndex &index) { + const auto itemText = index.data().toString(); + // if the list item contains a parentheses it means that // it's a category label so don't show a usercard - if (!userName.contains("(") && !userName.isEmpty()) + if (!itemText.contains("(") && !itemText.isEmpty()) { - this->view_->showUserInfoPopup(userName); + this->view_->showUserInfoPopup(itemText); } }; - QObject::connect(chattersList, &QListWidget::doubleClicked, this, [=]() { - listDoubleClick(chattersList->currentItem()->text()); - }); + QObject::connect(chattersList, &QListWidget::doubleClicked, this, + listDoubleClick); - QObject::connect(resultList, &QListWidget::doubleClicked, this, [=]() { - listDoubleClick(resultList->currentItem()->text()); - }); + QObject::connect(resultList, &QListWidget::doubleClicked, this, + listDoubleClick); HotkeyController::HotkeyMap actions{ {"delete", From 0ad66c0af46f5f5163845068de3756177800f354 Mon Sep 17 00:00:00 2001 From: Kasia Date: Sun, 29 May 2022 13:54:42 +0200 Subject: [PATCH 13/18] Optimize formatTime utility (#3777) Adds benchmarks and unit tests for the function Co-authored-by: Rasmus Karlsson --- benchmarks/CMakeLists.txt | 1 + benchmarks/src/FormatTime.cpp | 38 ++++++++++ src/util/FormatTime.cpp | 25 +++---- tests/CMakeLists.txt | 1 + tests/src/FormatTime.cpp | 133 ++++++++++++++++++++++++++++++++++ 5 files changed, 183 insertions(+), 15 deletions(-) create mode 100644 benchmarks/src/FormatTime.cpp create mode 100644 tests/src/FormatTime.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 6435f1398..b9fd4a9f9 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -3,6 +3,7 @@ project(chatterino-benchmark) set(benchmark_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/main.cpp ${CMAKE_CURRENT_LIST_DIR}/src/Emojis.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/FormatTime.cpp # Add your new file above this line! ) diff --git a/benchmarks/src/FormatTime.cpp b/benchmarks/src/FormatTime.cpp new file mode 100644 index 000000000..cf63e4cad --- /dev/null +++ b/benchmarks/src/FormatTime.cpp @@ -0,0 +1,38 @@ +#include "util/FormatTime.hpp" + +#include + +using namespace chatterino; + +template +void BM_TimeFormatting(benchmark::State &state, Args &&...args) +{ + auto args_tuple = std::make_tuple(std::move(args)...); + for (auto _ : state) + { + formatTime(std::get<0>(args_tuple)); + } +} + +BENCHMARK_CAPTURE(BM_TimeFormatting, 0, 0); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs0, "0"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 1337, 1337); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs1337, "1337"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 623452, 623452); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs623452, "623452"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 8345, 8345); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs8345, "8345"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 314034, 314034); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs314034, "314034"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 27, 27); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs27, "27"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 34589, 34589); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs34589, "34589"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 3659, 3659); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs3659, "3659"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 1045345, 1045345); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs1045345, "1045345"); +BENCHMARK_CAPTURE(BM_TimeFormatting, 86432, 86432); +BENCHMARK_CAPTURE(BM_TimeFormatting, qs86432, "86432"); +BENCHMARK_CAPTURE(BM_TimeFormatting, qsempty, ""); +BENCHMARK_CAPTURE(BM_TimeFormatting, qsinvalid, "asd"); diff --git a/src/util/FormatTime.cpp b/src/util/FormatTime.cpp index ba66031ba..6d6f2e525 100644 --- a/src/util/FormatTime.cpp +++ b/src/util/FormatTime.cpp @@ -1,12 +1,19 @@ #include "FormatTime.hpp" namespace chatterino { + namespace { - void appendDuration(int count, QChar &&order, QString &outString) + + void appendDuration(int count, QChar &&suffix, QString &out) { - outString.append(QString::number(count)); - outString.append(order); + if (!out.isEmpty()) + { + out.append(' '); + } + out.append(QString::number(count)); + out.append(suffix); } + } // namespace QString formatTime(int totalSeconds) @@ -25,26 +32,14 @@ QString formatTime(int totalSeconds) } if (hours > 0) { - if (!res.isEmpty()) - { - res.append(" "); - } appendDuration(hours, 'h', res); } if (minutes > 0) { - if (!res.isEmpty()) - { - res.append(" "); - } appendDuration(minutes, 'm', res); } if (seconds > 0) { - if (!res.isEmpty()) - { - res.append(" "); - } appendDuration(seconds, 's', res); } return res; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 58db955ec..e065dd3af 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,7 @@ set(test_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/IrcHelpers.cpp ${CMAKE_CURRENT_LIST_DIR}/src/TwitchPubSubClient.cpp ${CMAKE_CURRENT_LIST_DIR}/src/TwitchMessageBuilder.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/FormatTime.cpp # Add your new file above this line! ) diff --git a/tests/src/FormatTime.cpp b/tests/src/FormatTime.cpp new file mode 100644 index 000000000..3c4b5e8c9 --- /dev/null +++ b/tests/src/FormatTime.cpp @@ -0,0 +1,133 @@ +#include "util/FormatTime.hpp" + +#include + +using namespace chatterino; + +TEST(FormatTime, Int) +{ + struct TestCase { + int input; + QString expectedOutput; + }; + + std::vector tests{ + { + 0, + "", + }, + { + 1337, + "22m 17s", + }, + { + 623452, + "7d 5h 10m 52s", + }, + { + 8345, + "2h 19m 5s", + }, + { + 314034, + "3d 15h 13m 54s", + }, + { + 27, + "27s", + }, + { + 34589, + "9h 36m 29s", + }, + { + 3659, + "1h 59s", + }, + { + 1045345, + "12d 2h 22m 25s", + }, + { + 86432, + "1d 32s", + }, + }; + + for (const auto &[input, expected] : tests) + { + const auto actual = formatTime(input); + + EXPECT_EQ(actual, expected) + << qUtf8Printable(actual) << " (" << input + << ") did not match expected value " << qUtf8Printable(expected); + } +} + +TEST(FormatTime, QString) +{ + struct TestCase { + QString input; + QString expectedOutput; + }; + + std::vector tests{ + { + "0", + "", + }, + { + "1337", + "22m 17s", + }, + { + "623452", + "7d 5h 10m 52s", + }, + { + "8345", + "2h 19m 5s", + }, + { + "314034", + "3d 15h 13m 54s", + }, + { + "27", + "27s", + }, + { + "34589", + "9h 36m 29s", + }, + { + "3659", + "1h 59s", + }, + { + "1045345", + "12d 2h 22m 25s", + }, + { + "86432", + "1d 32s", + }, + { + "", + "n/a", + }, + { + "asd", + "n/a", + }, + }; + + for (const auto &[input, expected] : tests) + { + const auto actual = formatTime(input); + + EXPECT_EQ(actual, expected) + << qUtf8Printable(actual) << " (" << qUtf8Printable(input) + << ") did not match expected value " << qUtf8Printable(expected); + } +} From c8f5d35042dd7cbbb7e2c4888e9c518e0bb3fe07 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 29 May 2022 12:23:29 +0000 Subject: [PATCH 14/18] Added mod button-like placeholders in right click commands (#3765) Implemented input.text to return altText when it makes no sense to contain data Co-authored-by: Kasia --- CHANGELOG.md | 2 +- .../commands/CommandController.cpp | 22 +++++++++++----- src/widgets/helper/ChannelView.cpp | 26 ++++++++++++++++--- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b320fed61..137d231f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ - Minor: Streamer mode now automatically detects if XSplit, PRISM Live Studio, Twitch Studio, or vMix are running. (#3740) - Minor: Add scrollbar to `Select filters` dialog. (#3737) - Minor: Added `/requests` command. Usage: `/requests [channel]`. Opens the channel points requests queue for the provided channel or the current channel if no input is provided. (#3746) -- Minor: Added ability to execute commands on chat messages using the message context menu. (#3738) +- Minor: Added ability to execute commands on chat messages using the message context menu. (#3738, #3765) - Minor: Added `/copy` command. Usage: `/copy `. Copies provided text to clipboard - can be useful with custom commands. (#3763) - Bugfix: Fixed viewers list search not working when used before loading finishes. (#3774) - Bugfix: Fixed live notifications for usernames containing uppercase characters. (#3646) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 843b953a4..7aa82c7d4 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -177,6 +177,11 @@ bool appendWhisperMessageStringLocally(const QString &textNoEmoji) return false; } +const std::function + noOpPlaceholder = [](const auto &altText, const auto &channel) { + return altText; + }; + const std::map> COMMAND_VARS{ @@ -240,6 +245,11 @@ const std::mapsecond(altText, channel); + result += var->second.isEmpty() ? altText : var->second; } else { - auto it = context.find(varName); - if (it != context.end()) + auto it = COMMAND_VARS.find(varName); + if (it != COMMAND_VARS.end()) { - result += it->second.isEmpty() ? altText : it->second; + result += it->second(altText, channel); } else { diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index c2fb1d2cc..1b9c37f0b 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -2119,7 +2119,7 @@ void ChannelView::addCommandExecutionContextMenuItems( inputText.push_front(cmd.name + " "); - cmdMenu->addAction(cmd.name, [this, inputText] { + cmdMenu->addAction(cmd.name, [this, layout, cmd, inputText] { ChannelPtr channel; /* Search popups and user message history's underlyingChannels aren't of type TwitchChannel, but @@ -2132,9 +2132,29 @@ void ChannelView::addCommandExecutionContextMenuItems( { channel = this->underlyingChannel_; } + auto split = dynamic_cast(this->parentWidget()); + QString userText; + if (split) + { + userText = split->getInput().getInputText(); + } + QString value = getApp()->commands->execCustomCommand( + inputText.split(' '), cmd, true, channel, + { + {"user.name", layout->getMessage()->loginName}, + {"msg.id", layout->getMessage()->id}, + {"msg.text", layout->getMessage()->messageText}, + {"input.text", userText}, - QString value = - getApp()->commands->execCommand(inputText, channel, false); + // old placeholders + {"user", layout->getMessage()->loginName}, + {"msg-id", layout->getMessage()->id}, + {"message", layout->getMessage()->messageText}, + + {"channel", this->channel()->getName()}, + }); + + value = getApp()->commands->execCommand(value, channel, false); channel->sendMessage(value); }); From 765a17d374e2948d8bad00ff25273fd02537760a Mon Sep 17 00:00:00 2001 From: Kasia Date: Sun, 29 May 2022 14:53:22 +0200 Subject: [PATCH 15/18] Simplify code for fake debug messages (#3775) --- chatterino.pro | 4 +- src/CMakeLists.txt | 2 + src/util/DebugCount.hpp | 2 +- src/util/SampleCheerMessages.hpp | 64 ------ src/util/SampleData.cpp | 328 +++++++++++++++++++++++++++++++ src/util/SampleData.hpp | 24 +++ src/util/SampleLinks.hpp | 174 ---------------- src/widgets/Window.cpp | 90 ++------- 8 files changed, 369 insertions(+), 319 deletions(-) delete mode 100644 src/util/SampleCheerMessages.hpp create mode 100644 src/util/SampleData.cpp create mode 100644 src/util/SampleData.hpp delete mode 100644 src/util/SampleLinks.hpp diff --git a/chatterino.pro b/chatterino.pro index 4f7573c29..8461c4ad3 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -263,6 +263,7 @@ SOURCES += \ src/util/NuulsUploader.cpp \ src/util/RapidjsonHelpers.cpp \ src/util/RatelimitBucket.cpp \ + src/util/SampleData.cpp \ src/util/SplitCommand.cpp \ src/util/StreamerMode.cpp \ src/util/StreamLink.cpp \ @@ -539,8 +540,7 @@ HEADERS += \ src/util/RapidJsonSerializeQString.hpp \ src/util/RatelimitBucket.hpp \ src/util/RemoveScrollAreaBackground.hpp \ - src/util/SampleCheerMessages.hpp \ - src/util/SampleLinks.hpp \ + src/util/SampleData.hpp \ src/util/SharedPtrElementLess.hpp \ src/util/SplitCommand.hpp \ src/util/StandardItemHelper.hpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a66f319a4..df836ed3d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -320,6 +320,8 @@ set(SOURCE_FILES util/RapidjsonHelpers.hpp util/RatelimitBucket.cpp util/RatelimitBucket.hpp + util/SampleData.cpp + util/SampleData.hpp util/SplitCommand.cpp util/SplitCommand.hpp util/StreamLink.cpp diff --git a/src/util/DebugCount.hpp b/src/util/DebugCount.hpp index ef53e6814..99f70217e 100644 --- a/src/util/DebugCount.hpp +++ b/src/util/DebugCount.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "common/UniqueAccess.hpp" #include #include diff --git a/src/util/SampleCheerMessages.hpp b/src/util/SampleCheerMessages.hpp deleted file mode 100644 index 7e3471e53..000000000 --- a/src/util/SampleCheerMessages.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -namespace chatterino { - -std::vector getSampleCheerMessage() -{ - // clang-format off -std::vector cheerMessageVector; -cheerMessageVector.push_back(R"(@badge-info=subscriber/4;badges=moderator/1,subscriber/3,sub-gifter/5;bits=2;color=#FF0000;display-name=69_faith_420;emotes=;flags=;id=c5fd49c7-ecbc-46dd-a790-c9f10fdaaa67;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567282184553;turbo=0;user-id=125608098;user-type=mod :69_faith_420!69_faith_420@69_faith_420.tmi.twitch.tv PRIVMSG #pajlada :cheer2 Stop what? I'm not doing anything.)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/4;badges=moderator/1,subscriber/3,sub-gifter/5;bits=2;color=#FF0000;display-name=69_faith_420;emotes=;flags=;id=397f4d2e-cac8-4689-922a-32709b9e8b4f;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567282159076;turbo=0;user-id=125608098;user-type=mod :69_faith_420!69_faith_420@69_faith_420.tmi.twitch.tv PRIVMSG #pajlada :cheer2 Who keeps getting their bits out now?)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/1;badges=subscriber/0,bits/1;bits=2;color=#FF0000;display-name=FlameGodFlann;emotes=;flags=;id=664ddc92-649d-4889-9641-208a6e62ef1e;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567282066199;turbo=0;user-id=56442185;user-type= :flamegodflann!flamegodflann@flamegodflann.tmi.twitch.tv PRIVMSG #pajlada :Cheer2 I'm saving my only can of Stella for your upcoming win, lets go!)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/3;badges=moderator/1,subscriber/3,bits/100;bits=10;color=#008000;display-name=k4izn;emotes=;flags=;id=3919af0b-93e0-412c-b238-d152f92ffea7;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567811485257;turbo=0;user-id=207114672;user-type=mod :k4izn!k4izn@k4izn.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Kleiner Cheer(s) !)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/12;badges=subscriber/12,bits/1000;bits=20;color=#00CCFF;display-name=YaBoiBurnsy;emotes=;flags=;id=5b53975d-b339-484f-a2a0-3ffbedde0df2;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567529634584;turbo=0;user-id=45258137;user-type= :yaboiburnsy!yaboiburnsy@yaboiburnsy.tmi.twitch.tv PRIVMSG #pajlada :ShowLove20)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/1;badges=moderator/1,subscriber/0,bits-leader/2;bits=1;color=;display-name=jdfellie;emotes=;flags=18-22:A.3/P.5;id=28c8f4b7-b1e3-4404-b0f8-5cfe46411ef9;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567668177856;turbo=0;user-id=137619637;user-type=mod :jdfellie!jdfellie@jdfellie.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 take a bit bitch)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/2;bits=30;color=#EC3B83;display-name=Sammay;emotes=;flags=;id=ccf058a6-c1f1-45de-a764-fc8f96f21449;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566719874294;turbo=0;user-id=58283830;user-type= :sammay!sammay@sammay.tmi.twitch.tv PRIVMSG #pajlada :ShowLove30 @Emperor_Zhang)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/2;bits=6;color=#97E7FF;display-name=Emperor_Zhang;emotes=;flags=;id=53bab01b-9f6c-4123-a852-9916ab371cf9;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566719803345;turbo=0;user-id=105292882;user-type= :emperor_zhang!emperor_zhang@emperor_zhang.tmi.twitch.tv PRIVMSG #pajlada :uni6)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=5;color=#97E7FF;display-name=Emperor_Zhang;emotes=;flags=;id=545caec6-8b5f-460a-8b4b-3e407e179689;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566704926380;turbo=0;user-id=105292882;user-type= :emperor_zhang!emperor_zhang@emperor_zhang.tmi.twitch.tv PRIVMSG #pajlada :VoHiYo5)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/100;bits=50;color=;display-name=Schmiddi55;emotes=;flags=;id=777f1018-941d-48aa-bf4e-ed8053d556c8;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567708393343;turbo=0;user-id=101444120;user-type= :schmiddi55!schmiddi55@schmiddi55.tmi.twitch.tv PRIVMSG #pajlada :cheer50 sere ihr radlertrinker)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/3;badges=subscriber/3,sub-gifter/10;bits=100;color=#0000FF;display-name=MLPTheChad;emotes=;flags=87-91:P.5;id=ed7db31e-884b-4761-9c88-b1676caa8814;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567681752733;turbo=0;user-id=63179867;user-type= :mlpthechad!mlpthechad@mlpthechad.tmi.twitch.tv PRIVMSG #pajlada :Subway100 bonus10 Statistically speaking, 10 out of 10 constipated people don't give a shit.)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/3;badges=subscriber/3,sub-gifter/10;bits=100;color=#0000FF;display-name=MLPTheChad;emotes=;flags=;id=506b482a-515a-4914-a694-2c69d2add23a;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567681618814;turbo=0;user-id=63179867;user-type= :mlpthechad!mlpthechad@mlpthechad.tmi.twitch.tv PRIVMSG #pajlada :Subway100 bonus10 That's some SUB par gameplay, Dabier.)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=premium/1;bits=100;color=;display-name=AkiraKurusu__;emotes=;flags=;id=6e343f5d-0e0e-47f7-bf6d-d5d7bf18b95a;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567765732657;turbo=0;user-id=151679027;user-type= :akirakurusu__!akirakurusu__@akirakurusu__.tmi.twitch.tv PRIVMSG #pajlada :TriHard100)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=premium/1;bits=1;color=;display-name=AkiraKurusu__;emotes=;flags=;id=dfdf6c2f-abee-4a4b-99fe-0d0b221f07de;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567765295301;turbo=0;user-id=151679027;user-type= :akirakurusu__!akirakurusu__@akirakurusu__.tmi.twitch.tv PRIVMSG #pajlada :TriHard1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/100;bits=500;color=#0000FF;display-name=Stabbr;emotes=;flags=;id=e28b384e-fb6a-4da5-9a36-1b6153c6089d;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567648284623;turbo=0;user-id=183081176;user-type= :stabbr!stabbr@stabbr.tmi.twitch.tv PRIVMSG #pajlada :cheer500 Gotta be on top)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/1;badges=subscriber/0,bits-leader/1;bits=100;color=;display-name=dbf_sub;emotes=;flags=;id=7cf317b8-6e28-4615-a0ba-e0bbaa0d4b29;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567646349560;turbo=0;user-id=450101746;user-type= :dbf_sub!dbf_sub@dbf_sub.tmi.twitch.tv PRIVMSG #pajlada :EleGiggle100)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/1;badges=subscriber/0,bits/1;bits=1;color=;display-name=dbf_sub;emotes=;flags=;id=43b5fc97-e7cc-4ac1-8d7e-7504c435c3f1;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567643510222;turbo=0;user-id=450101746;user-type= :dbf_sub!dbf_sub@dbf_sub.tmi.twitch.tv PRIVMSG #pajlada :SeemsGood1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/2;bits=100;color=;display-name=RobertsonRobotics;emotes=;flags=;id=598dfa14-23e9-4e45-a2fe-7a0263828817;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567873463820;turbo=0;user-id=117177721;user-type= :robertsonrobotics!robertsonrobotics@robertsonrobotics.tmi.twitch.tv PRIVMSG #pajlada :firstCheer100 This is so cool! Can’t wait for the competition!)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/100;bits=18;color=#1E90FF;display-name=Vipacman11;emotes=;flags=;id=07f59664-0c75-459e-b137-26c8d03e44be;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567873210379;turbo=0;user-id=89634839;user-type= :vipacman11!vipacman11@vipacman11.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=sub-gifter/5;bits=100;color=#FF7F50;display-name=darkside_sinner;emotes=;flags=;id=090102b3-369d-4ce4-ad1f-283849b10de0;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567822075293;turbo=0;user-id=104942909;user-type= :darkside_sinner!darkside_sinner@darkside_sinner.tmi.twitch.tv PRIVMSG #pajlada :Subway100 bonus10)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=sub-gifter/5;bits=200;color=#FF7F50;display-name=darkside_sinner;emotes=;flags=;id=2bdf7846-5ffa-4798-a397-997e7209a6d0;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567821695287;turbo=0;user-id=104942909;user-type= :darkside_sinner!darkside_sinner@darkside_sinner.tmi.twitch.tv PRIVMSG #pajlada :Subway200 bonus20)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=50;color=#0000FF;display-name=SincereBC;emotes=;flags=;id=b8c9236b-aeb9-4c72-a191-593e33c6c3f1;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567818308913;turbo=0;user-id=146097597;user-type= :sincerebc!sincerebc@sincerebc.tmi.twitch.tv PRIVMSG #pajlada :cheer50)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=1;color=#FF0000;display-name=AngryCh33s3puff;emotes=;flags=;id=6ab62185-ac1b-4ee5-bd93-165009917078;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567474810480;turbo=0;user-id=55399500;user-type= :angrych33s3puff!angrych33s3puff@angrych33s3puff.tmi.twitch.tv PRIVMSG #pajlada :cheer1 for the chair!)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/3;badges=moderator/1,subscriber/0,bits/1000;bits=1500;color=#5F9EA0;display-name=LaurenJW28;emotes=;flags=;id=2403678c-6109-43ac-b3b5-1f5230f91729;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567746107991;turbo=0;user-id=244354979;user-type=mod :laurenjw28!laurenjw28@laurenjw28.tmi.twitch.tv PRIVMSG #pajlada :Cheer1000 Cheer100 Cheer100 Cheer100 Cheer100 Cheer100)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=5;color=#5F9EA0;display-name=drkwings;emotes=;flags=;id=ad45dae5-b985-4526-9b9e-0bdba2d23289;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567742106689;turbo=0;user-id=440230526;user-type= :drkwings!drkwings@drkwings.tmi.twitch.tv PRIVMSG #pajlada :SeemsGood1 SeemsGood1 SeemsGood1 SeemsGood1 SeemsGood1)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/16;badges=subscriber/12,bits/1000;bits=1;color=;display-name=mustangbugatti;emotes=;flags=;id=ee987ee9-46a4-4c06-bf66-2cafff5d4cdd;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567883658780;turbo=0;user-id=115948494;user-type= :mustangbugatti!mustangbugatti@mustangbugatti.tmi.twitch.tv PRIVMSG #pajlada :(In clarkson accent) Some say...the only number in his contacts is himself..... And...that he is the international butt-dial champion... All we know is.... HES CALLED THE STIG Cheer1)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/2;badges=subscriber/0,bits/1000;bits=1;color=;display-name=derpysaurus1;emotes=;flags=;id=c41c3d8b-c591-4db0-87e7-a78c5536de82;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567883655116;turbo=0;user-id=419221818;user-type= :derpysaurus1!derpysaurus1@derpysaurus1.tmi.twitch.tv PRIVMSG #pajlada :cheer1 OMG ur back yaaaaaaaaaaaaaaaaaaaaayyyyyyyyy)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/5;badges=subscriber/0,premium/1;bits=1;color=#8A2BE2;display-name=sirlordstallion;emotes=;flags=;id=61a87aeb-88b1-42f9-90f5-74429d8bf387;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567882978939;turbo=0;user-id=92145441;user-type= :sirlordstallion!sirlordstallion@sirlordstallion.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Alex is definetly not putting his eggs in Narreths basket)"); -cheerMessageVector.push_back(R"(@badge-info=subscriber/1;badges=subscriber/0,bits/1;bits=1;color=;display-name=xplosivegingerx;emotes=;flags=;id=f8aac1e0-050a-44bf-abcc-c0cf12cbedfc;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567882249072;turbo=0;user-id=151265906;user-type= :xplosivegingerx!xplosivegingerx@xplosivegingerx.tmi.twitch.tv PRIVMSG #pajlada :Cheer1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/100;bits=500;color=;display-name=AlexJohanning;emotes=;flags=;id=4e4229a3-e7f2-4082-8c55-47d42db3b09c;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567881969862;turbo=0;user-id=190390930;user-type= :alexjohanning!alexjohanning@alexjohanning.tmi.twitch.tv PRIVMSG #pajlada :cheer500)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=245;color=;display-name=undonebunion6;emotes=;flags=;id=331ec583-0a80-4299-9206-0efd9e33d934;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567881553759;turbo=0;user-id=452974274;user-type= :undonebunion6!undonebunion6@undonebunion6.tmi.twitch.tv PRIVMSG #pajlada :cheer245 can I join?)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/100;bits=100;color=;display-name=therealruffnix;emotes=;flags=61-67:S.6;id=25f567ad-ac95-45ab-b12e-4d647f6a2345;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567524218162;turbo=0;user-id=55059620;user-type= :therealruffnix!therealruffnix@therealruffnix.tmi.twitch.tv PRIVMSG #pajlada :cheer100 This is the kind of ASMR I'm missing on YouTube and PornHub)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=1;color=;display-name=BeamMeUpSnotty;emotes=;flags=;id=8022f41f-dcb8-42f2-b46a-04d4a99180bd;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567270037926;turbo=0;user-id=261679182;user-type= :beammeupsnotty!beammeupsnotty@beammeupsnotty.tmi.twitch.tv PRIVMSG #pajlada :SeemsGood1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=10;color=#00FF7F;display-name=EXDE_HUN;emotes=;flags=;id=60d8835b-23fa-418c-96ca-5874e5d5e8ba;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566654664248;turbo=0;user-id=129793695;user-type= :exde_hun!exde_hun@exde_hun.tmi.twitch.tv PRIVMSG #pajlada :PogChamp10)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/3;bits=5;color=;display-name=slyckity;emotes=;flags=;id=fd6c5507-3a4e-4d24-8f6e-fadf07f520d3;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824273752;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/3;bits=5;color=;display-name=slyckity;emotes=;flags=;id=7003f119-b9a6-4319-a1e8-8e99f96ab01a;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824186437;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/3;bits=10;color=;display-name=slyckity;emotes=;flags=;id=3f7de686-77f6-46d2-919e-404312c6676f;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824128736;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/3;bits=10;color=;display-name=slyckity;emotes=;flags=;id=9e830ed3-8735-4ccb-9a8b-80466598ca19;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824118921;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=377;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=262f4d54-9b21-4f13-aac3-6d3b1051282f;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440897074;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :NotLikeThis377)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=144;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=3556e0ad-b5f8-4190-9c4c-e39c1940d191;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440861545;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :bday144)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=89;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=96e380a5-786d-44b8-819a-529b6adb06ac;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440848361;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :SwiftRage89)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=34;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=76239011-65fa-4f6a-a6d6-dc5d5dcbd674;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440816630;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :MrDestructoid34)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=21;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=4c05c97c-7b6c-4ae9-bc91-04e98240c1d5;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440806389;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :TriHard21)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=8;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=3b2ecce7-842e-429e-b6c8-9456c4646362;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440774009;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :EleGiggle8)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=5;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=3b8736d1-832d-4152-832a-50c526714fd1;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440762580;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :uni5)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=3;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=c13a1540-2a03-4c7d-af50-cb20ed88cefd;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440750103;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :Party3)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/1;bits=2;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=5d889eeb-b6b9-4a4e-91ff-0aecdf297edd;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440738337;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :ShowLove2)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=1;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=da47f91a-40d3-4209-ba1c-0219d8b8ecaf;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440720363;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :Scoops1)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits/1;bits=10;color=#8A2BE2;display-name=EkimSky;emotes=;flags=;id=8adea5b4-7430-44ea-a666-5ebaceb69441;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567833047623;turbo=0;user-id=42132818;user-type= :ekimsky!ekimsky@ekimsky.tmi.twitch.tv PRIVMSG #pajlada :Hi Cheer10)"); -cheerMessageVector.push_back(R"(@badge-info=;badges=bits-leader/2;bits=500;color=;display-name=godkiller76;emotes=;flags=;id=80e86bcc-d048-44f3-8073-9a1014568e0c;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567753685704;turbo=0;user-id=258838478;user-type= :godkiller76!godkiller76@godkiller76.tmi.twitch.tv PRIVMSG #pajlada :Party100 Party100 Party100 Party100 Party100)"); - -return cheerMessageVector; - // clang-format on -}; - -} // namespace chatterino diff --git a/src/util/SampleData.cpp b/src/util/SampleData.cpp new file mode 100644 index 000000000..2a9af80f7 --- /dev/null +++ b/src/util/SampleData.cpp @@ -0,0 +1,328 @@ +#include "SampleData.hpp" + +namespace chatterino { + +/// Sample messages coming from IRC + +const QStringList &getSampleCheerMessages() +{ + static QStringList list{ + R"(@badge-info=subscriber/4;badges=moderator/1,subscriber/3,sub-gifter/5;bits=2;color=#FF0000;display-name=69_faith_420;emotes=;flags=;id=c5fd49c7-ecbc-46dd-a790-c9f10fdaaa67;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567282184553;turbo=0;user-id=125608098;user-type=mod :69_faith_420!69_faith_420@69_faith_420.tmi.twitch.tv PRIVMSG #pajlada :cheer2 Stop what? I'm not doing anything.)", + R"(@badge-info=subscriber/4;badges=moderator/1,subscriber/3,sub-gifter/5;bits=2;color=#FF0000;display-name=69_faith_420;emotes=;flags=;id=397f4d2e-cac8-4689-922a-32709b9e8b4f;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567282159076;turbo=0;user-id=125608098;user-type=mod :69_faith_420!69_faith_420@69_faith_420.tmi.twitch.tv PRIVMSG #pajlada :cheer2 Who keeps getting their bits out now?)", + R"(@badge-info=subscriber/1;badges=subscriber/0,bits/1;bits=2;color=#FF0000;display-name=FlameGodFlann;emotes=;flags=;id=664ddc92-649d-4889-9641-208a6e62ef1e;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567282066199;turbo=0;user-id=56442185;user-type= :flamegodflann!flamegodflann@flamegodflann.tmi.twitch.tv PRIVMSG #pajlada :Cheer2 I'm saving my only can of Stella for your upcoming win, lets go!)", + R"(@badge-info=subscriber/3;badges=moderator/1,subscriber/3,bits/100;bits=10;color=#008000;display-name=k4izn;emotes=;flags=;id=3919af0b-93e0-412c-b238-d152f92ffea7;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567811485257;turbo=0;user-id=207114672;user-type=mod :k4izn!k4izn@k4izn.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Kleiner Cheer(s) !)", + R"(@badge-info=subscriber/12;badges=subscriber/12,bits/1000;bits=20;color=#00CCFF;display-name=YaBoiBurnsy;emotes=;flags=;id=5b53975d-b339-484f-a2a0-3ffbedde0df2;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567529634584;turbo=0;user-id=45258137;user-type= :yaboiburnsy!yaboiburnsy@yaboiburnsy.tmi.twitch.tv PRIVMSG #pajlada :ShowLove20)", + R"(@badge-info=subscriber/1;badges=moderator/1,subscriber/0,bits-leader/2;bits=1;color=;display-name=jdfellie;emotes=;flags=18-22:A.3/P.5;id=28c8f4b7-b1e3-4404-b0f8-5cfe46411ef9;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567668177856;turbo=0;user-id=137619637;user-type=mod :jdfellie!jdfellie@jdfellie.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 take a bit bitch)", + R"(@badge-info=;badges=bits-leader/2;bits=30;color=#EC3B83;display-name=Sammay;emotes=;flags=;id=ccf058a6-c1f1-45de-a764-fc8f96f21449;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566719874294;turbo=0;user-id=58283830;user-type= :sammay!sammay@sammay.tmi.twitch.tv PRIVMSG #pajlada :ShowLove30 @Emperor_Zhang)", + R"(@badge-info=;badges=bits-leader/2;bits=6;color=#97E7FF;display-name=Emperor_Zhang;emotes=;flags=;id=53bab01b-9f6c-4123-a852-9916ab371cf9;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566719803345;turbo=0;user-id=105292882;user-type= :emperor_zhang!emperor_zhang@emperor_zhang.tmi.twitch.tv PRIVMSG #pajlada :uni6)", + R"(@badge-info=;badges=bits/1;bits=5;color=#97E7FF;display-name=Emperor_Zhang;emotes=;flags=;id=545caec6-8b5f-460a-8b4b-3e407e179689;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566704926380;turbo=0;user-id=105292882;user-type= :emperor_zhang!emperor_zhang@emperor_zhang.tmi.twitch.tv PRIVMSG #pajlada :VoHiYo5)", + R"(@badge-info=;badges=bits/100;bits=50;color=;display-name=Schmiddi55;emotes=;flags=;id=777f1018-941d-48aa-bf4e-ed8053d556c8;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567708393343;turbo=0;user-id=101444120;user-type= :schmiddi55!schmiddi55@schmiddi55.tmi.twitch.tv PRIVMSG #pajlada :cheer50 sere ihr radlertrinker)", + R"(@badge-info=subscriber/3;badges=subscriber/3,sub-gifter/10;bits=100;color=#0000FF;display-name=MLPTheChad;emotes=;flags=87-91:P.5;id=ed7db31e-884b-4761-9c88-b1676caa8814;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567681752733;turbo=0;user-id=63179867;user-type= :mlpthechad!mlpthechad@mlpthechad.tmi.twitch.tv PRIVMSG #pajlada :Subway100 bonus10 Statistically speaking, 10 out of 10 constipated people don't give a shit.)", + R"(@badge-info=subscriber/3;badges=subscriber/3,sub-gifter/10;bits=100;color=#0000FF;display-name=MLPTheChad;emotes=;flags=;id=506b482a-515a-4914-a694-2c69d2add23a;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567681618814;turbo=0;user-id=63179867;user-type= :mlpthechad!mlpthechad@mlpthechad.tmi.twitch.tv PRIVMSG #pajlada :Subway100 bonus10 That's some SUB par gameplay, Dabier.)", + R"(@badge-info=;badges=premium/1;bits=100;color=;display-name=AkiraKurusu__;emotes=;flags=;id=6e343f5d-0e0e-47f7-bf6d-d5d7bf18b95a;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567765732657;turbo=0;user-id=151679027;user-type= :akirakurusu__!akirakurusu__@akirakurusu__.tmi.twitch.tv PRIVMSG #pajlada :TriHard100)", + R"(@badge-info=;badges=premium/1;bits=1;color=;display-name=AkiraKurusu__;emotes=;flags=;id=dfdf6c2f-abee-4a4b-99fe-0d0b221f07de;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567765295301;turbo=0;user-id=151679027;user-type= :akirakurusu__!akirakurusu__@akirakurusu__.tmi.twitch.tv PRIVMSG #pajlada :TriHard1)", + R"(@badge-info=;badges=bits/100;bits=500;color=#0000FF;display-name=Stabbr;emotes=;flags=;id=e28b384e-fb6a-4da5-9a36-1b6153c6089d;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567648284623;turbo=0;user-id=183081176;user-type= :stabbr!stabbr@stabbr.tmi.twitch.tv PRIVMSG #pajlada :cheer500 Gotta be on top)", + R"(@badge-info=subscriber/1;badges=subscriber/0,bits-leader/1;bits=100;color=;display-name=dbf_sub;emotes=;flags=;id=7cf317b8-6e28-4615-a0ba-e0bbaa0d4b29;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567646349560;turbo=0;user-id=450101746;user-type= :dbf_sub!dbf_sub@dbf_sub.tmi.twitch.tv PRIVMSG #pajlada :EleGiggle100)", + R"(@badge-info=subscriber/1;badges=subscriber/0,bits/1;bits=1;color=;display-name=dbf_sub;emotes=;flags=;id=43b5fc97-e7cc-4ac1-8d7e-7504c435c3f1;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567643510222;turbo=0;user-id=450101746;user-type= :dbf_sub!dbf_sub@dbf_sub.tmi.twitch.tv PRIVMSG #pajlada :SeemsGood1)", + R"(@badge-info=;badges=bits-leader/2;bits=100;color=;display-name=RobertsonRobotics;emotes=;flags=;id=598dfa14-23e9-4e45-a2fe-7a0263828817;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567873463820;turbo=0;user-id=117177721;user-type= :robertsonrobotics!robertsonrobotics@robertsonrobotics.tmi.twitch.tv PRIVMSG #pajlada :firstCheer100 This is so cool! Can’t wait for the competition!)", + R"(@badge-info=;badges=bits/100;bits=18;color=#1E90FF;display-name=Vipacman11;emotes=;flags=;id=07f59664-0c75-459e-b137-26c8d03e44be;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567873210379;turbo=0;user-id=89634839;user-type= :vipacman11!vipacman11@vipacman11.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)", + R"(@badge-info=;badges=sub-gifter/5;bits=100;color=#FF7F50;display-name=darkside_sinner;emotes=;flags=;id=090102b3-369d-4ce4-ad1f-283849b10de0;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567822075293;turbo=0;user-id=104942909;user-type= :darkside_sinner!darkside_sinner@darkside_sinner.tmi.twitch.tv PRIVMSG #pajlada :Subway100 bonus10)", + R"(@badge-info=;badges=sub-gifter/5;bits=200;color=#FF7F50;display-name=darkside_sinner;emotes=;flags=;id=2bdf7846-5ffa-4798-a397-997e7209a6d0;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567821695287;turbo=0;user-id=104942909;user-type= :darkside_sinner!darkside_sinner@darkside_sinner.tmi.twitch.tv PRIVMSG #pajlada :Subway200 bonus20)", + R"(@badge-info=;badges=bits/1;bits=50;color=#0000FF;display-name=SincereBC;emotes=;flags=;id=b8c9236b-aeb9-4c72-a191-593e33c6c3f1;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567818308913;turbo=0;user-id=146097597;user-type= :sincerebc!sincerebc@sincerebc.tmi.twitch.tv PRIVMSG #pajlada :cheer50)", + R"(@badge-info=;badges=bits/1;bits=1;color=#FF0000;display-name=AngryCh33s3puff;emotes=;flags=;id=6ab62185-ac1b-4ee5-bd93-165009917078;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567474810480;turbo=0;user-id=55399500;user-type= :angrych33s3puff!angrych33s3puff@angrych33s3puff.tmi.twitch.tv PRIVMSG #pajlada :cheer1 for the chair!)", + R"(@badge-info=subscriber/3;badges=moderator/1,subscriber/0,bits/1000;bits=1500;color=#5F9EA0;display-name=LaurenJW28;emotes=;flags=;id=2403678c-6109-43ac-b3b5-1f5230f91729;mod=1;room-id=111448817;subscriber=1;tmi-sent-ts=1567746107991;turbo=0;user-id=244354979;user-type=mod :laurenjw28!laurenjw28@laurenjw28.tmi.twitch.tv PRIVMSG #pajlada :Cheer1000 Cheer100 Cheer100 Cheer100 Cheer100 Cheer100)", + R"(@badge-info=;badges=bits/1;bits=5;color=#5F9EA0;display-name=drkwings;emotes=;flags=;id=ad45dae5-b985-4526-9b9e-0bdba2d23289;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567742106689;turbo=0;user-id=440230526;user-type= :drkwings!drkwings@drkwings.tmi.twitch.tv PRIVMSG #pajlada :SeemsGood1 SeemsGood1 SeemsGood1 SeemsGood1 SeemsGood1)", + R"(@badge-info=subscriber/16;badges=subscriber/12,bits/1000;bits=1;color=;display-name=mustangbugatti;emotes=;flags=;id=ee987ee9-46a4-4c06-bf66-2cafff5d4cdd;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567883658780;turbo=0;user-id=115948494;user-type= :mustangbugatti!mustangbugatti@mustangbugatti.tmi.twitch.tv PRIVMSG #pajlada :(In clarkson accent) Some say...the only number in his contacts is himself..... And...that he is the international butt-dial champion... All we know is.... HES CALLED THE STIG Cheer1)", + R"(@badge-info=subscriber/2;badges=subscriber/0,bits/1000;bits=1;color=;display-name=derpysaurus1;emotes=;flags=;id=c41c3d8b-c591-4db0-87e7-a78c5536de82;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567883655116;turbo=0;user-id=419221818;user-type= :derpysaurus1!derpysaurus1@derpysaurus1.tmi.twitch.tv PRIVMSG #pajlada :cheer1 OMG ur back yaaaaaaaaaaaaaaaaaaaaayyyyyyyyy)", + R"(@badge-info=subscriber/5;badges=subscriber/0,premium/1;bits=1;color=#8A2BE2;display-name=sirlordstallion;emotes=;flags=;id=61a87aeb-88b1-42f9-90f5-74429d8bf387;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567882978939;turbo=0;user-id=92145441;user-type= :sirlordstallion!sirlordstallion@sirlordstallion.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Alex is definetly not putting his eggs in Narreths basket)", + R"(@badge-info=subscriber/1;badges=subscriber/0,bits/1;bits=1;color=;display-name=xplosivegingerx;emotes=;flags=;id=f8aac1e0-050a-44bf-abcc-c0cf12cbedfc;mod=0;room-id=111448817;subscriber=1;tmi-sent-ts=1567882249072;turbo=0;user-id=151265906;user-type= :xplosivegingerx!xplosivegingerx@xplosivegingerx.tmi.twitch.tv PRIVMSG #pajlada :Cheer1)", + R"(@badge-info=;badges=bits/100;bits=500;color=;display-name=AlexJohanning;emotes=;flags=;id=4e4229a3-e7f2-4082-8c55-47d42db3b09c;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567881969862;turbo=0;user-id=190390930;user-type= :alexjohanning!alexjohanning@alexjohanning.tmi.twitch.tv PRIVMSG #pajlada :cheer500)", + R"(@badge-info=;badges=bits-leader/1;bits=245;color=;display-name=undonebunion6;emotes=;flags=;id=331ec583-0a80-4299-9206-0efd9e33d934;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567881553759;turbo=0;user-id=452974274;user-type= :undonebunion6!undonebunion6@undonebunion6.tmi.twitch.tv PRIVMSG #pajlada :cheer245 can I join?)", + R"(@badge-info=;badges=bits/100;bits=100;color=;display-name=therealruffnix;emotes=;flags=61-67:S.6;id=25f567ad-ac95-45ab-b12e-4d647f6a2345;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567524218162;turbo=0;user-id=55059620;user-type= :therealruffnix!therealruffnix@therealruffnix.tmi.twitch.tv PRIVMSG #pajlada :cheer100 This is the kind of ASMR I'm missing on YouTube and PornHub)", + R"(@badge-info=;badges=bits/1;bits=1;color=;display-name=BeamMeUpSnotty;emotes=;flags=;id=8022f41f-dcb8-42f2-b46a-04d4a99180bd;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567270037926;turbo=0;user-id=261679182;user-type= :beammeupsnotty!beammeupsnotty@beammeupsnotty.tmi.twitch.tv PRIVMSG #pajlada :SeemsGood1)", + R"(@badge-info=;badges=bits/1;bits=10;color=#00FF7F;display-name=EXDE_HUN;emotes=;flags=;id=60d8835b-23fa-418c-96ca-5874e5d5e8ba;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1566654664248;turbo=0;user-id=129793695;user-type= :exde_hun!exde_hun@exde_hun.tmi.twitch.tv PRIVMSG #pajlada :PogChamp10)", + R"(@badge-info=;badges=bits-leader/3;bits=5;color=;display-name=slyckity;emotes=;flags=;id=fd6c5507-3a4e-4d24-8f6e-fadf07f520d3;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824273752;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)", + R"(@badge-info=;badges=bits-leader/3;bits=5;color=;display-name=slyckity;emotes=;flags=;id=7003f119-b9a6-4319-a1e8-8e99f96ab01a;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824186437;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)", + R"(@badge-info=;badges=bits-leader/3;bits=10;color=;display-name=slyckity;emotes=;flags=;id=3f7de686-77f6-46d2-919e-404312c6676f;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824128736;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)", + R"(@badge-info=;badges=bits-leader/3;bits=10;color=;display-name=slyckity;emotes=;flags=;id=9e830ed3-8735-4ccb-9a8b-80466598ca19;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567824118921;turbo=0;user-id=143114011;user-type= :slyckity!slyckity@slyckity.tmi.twitch.tv PRIVMSG #pajlada :Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1 Cheer1)", + R"(@badge-info=;badges=bits-leader/1;bits=377;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=262f4d54-9b21-4f13-aac3-6d3b1051282f;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440897074;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :NotLikeThis377)", + R"(@badge-info=;badges=bits-leader/1;bits=144;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=3556e0ad-b5f8-4190-9c4c-e39c1940d191;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440861545;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :bday144)", + R"(@badge-info=;badges=bits-leader/1;bits=89;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=96e380a5-786d-44b8-819a-529b6adb06ac;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440848361;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :SwiftRage89)", + R"(@badge-info=;badges=bits-leader/1;bits=34;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=76239011-65fa-4f6a-a6d6-dc5d5dcbd674;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440816630;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :MrDestructoid34)", + R"(@badge-info=;badges=bits-leader/1;bits=21;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=4c05c97c-7b6c-4ae9-bc91-04e98240c1d5;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440806389;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :TriHard21)", + R"(@badge-info=;badges=bits-leader/1;bits=8;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=3b2ecce7-842e-429e-b6c8-9456c4646362;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440774009;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :EleGiggle8)", + R"(@badge-info=;badges=bits-leader/1;bits=5;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=3b8736d1-832d-4152-832a-50c526714fd1;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440762580;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :uni5)", + R"(@badge-info=;badges=bits-leader/1;bits=3;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=c13a1540-2a03-4c7d-af50-cb20ed88cefd;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440750103;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :Party3)", + R"(@badge-info=;badges=bits-leader/1;bits=2;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=5d889eeb-b6b9-4a4e-91ff-0aecdf297edd;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440738337;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :ShowLove2)", + R"(@badge-info=;badges=bits/1;bits=1;color=#00FF7F;display-name=Baekjoon;emotes=;flags=;id=da47f91a-40d3-4209-ba1c-0219d8b8ecaf;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567440720363;turbo=0;user-id=73587716;user-type= :baekjoon!baekjoon@baekjoon.tmi.twitch.tv PRIVMSG #pajlada :Scoops1)", + R"(@badge-info=;badges=bits/1;bits=10;color=#8A2BE2;display-name=EkimSky;emotes=;flags=;id=8adea5b4-7430-44ea-a666-5ebaceb69441;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567833047623;turbo=0;user-id=42132818;user-type= :ekimsky!ekimsky@ekimsky.tmi.twitch.tv PRIVMSG #pajlada :Hi Cheer10)", + R"(@badge-info=;badges=bits-leader/2;bits=500;color=;display-name=godkiller76;emotes=;flags=;id=80e86bcc-d048-44f3-8073-9a1014568e0c;mod=0;room-id=111448817;subscriber=0;tmi-sent-ts=1567753685704;turbo=0;user-id=258838478;user-type= :godkiller76!godkiller76@godkiller76.tmi.twitch.tv PRIVMSG #pajlada :Party100 Party100 Party100 Party100 Party100)", + }; + return list; +} + +const QStringList &getSampleSubMessages() +{ + static QStringList list{ + R"(@badges=staff/1,broadcaster/1,turbo/1;color=#008000;display-name=ronni;emotes=;id=db25007f-7a18-43eb-9379-80131e44d633;login=ronni;mod=0;msg-id=resub;msg-param-months=6;msg-param-sub-plan=Prime;msg-param-sub-plan-name=Prime;room-id=1337;subscriber=1;system-msg=ronni\shas\ssubscribed\sfor\s6\smonths!;tmi-sent-ts=1507246572675;turbo=1;user-id=1337;user-type=staff :tmi.twitch.tv USERNOTICE #pajlada :Great stream -- keep it up!)", + R"(@badges=staff/1,premium/1;color=#0000FF;display-name=TWW2;emotes=;id=e9176cd8-5e22-4684-ad40-ce53c2561c5e;login=tww2;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=Mr_Woodchuck;msg-param-recipient-id=89614178;msg-param-recipient-name=mr_woodchuck;msg-param-sub-plan-name=House\sof\sNyoro~n;msg-param-sub-plan=1000;room-id=19571752;subscriber=0;system-msg=TWW2\sgifted\sa\sTier\s1\ssub\sto\sMr_Woodchuck!;tmi-sent-ts=1521159445153;turbo=0;user-id=13405587;user-type=staff :tmi.twitch.tv USERNOTICE #pajlada)", + + // hyperbolicxd gifted a sub to quote_if_nam + R"(@badges=subscriber/0,premium/1;color=#00FF7F;display-name=hyperbolicxd;emotes=;id=b20ef4fe-cba8-41d0-a371-6327651dc9cc;login=hyperbolicxd;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=quote_if_nam;msg-param-recipient-id=217259245;msg-param-recipient-user-name=quote_if_nam;msg-param-sender-count=1;msg-param-sub-plan-name=Channel\sSubscription\s(nymn_hs);msg-param-sub-plan=1000;room-id=62300805;subscriber=1;system-msg=hyperbolicxd\sgifted\sa\sTier\s1\ssub\sto\squote_if_nam!\sThis\sis\stheir\sfirst\sGift\sSub\sin\sthe\schannel!;tmi-sent-ts=1528190938558;turbo=0;user-id=111534250;user-type= :tmi.twitch.tv USERNOTICE #pajlada)", + + // first time sub + R"(@badges=subscriber/0,premium/1;color=#0000FF;display-name=byebyeheart;emotes=;id=fe390424-ab89-4c33-bb5a-53c6e5214b9f;login=byebyeheart;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=byebyeheart\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190963670;turbo=0;user-id=131956000;user-type= :tmi.twitch.tv USERNOTICE #pajlada)", + + // first time sub + R"(@badges=subscriber/0,premium/1;color=;display-name=vJoeyzz;emotes=;id=b2476df5-fffe-4338-837b-380c5dd90051;login=vjoeyzz;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=vJoeyzz\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190995089;turbo=0;user-id=78945903;user-type= :tmi.twitch.tv USERNOTICE #pajlada)", + + // first time sub + R"(@badges=subscriber/0,premium/1;color=;display-name=Lennydog3;emotes=;id=44feb1eb-df60-45f6-904b-7bf0d5375a41;login=lennydog3;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=Lennydog3\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528191098733;turbo=0;user-id=175759335;user-type= :tmi.twitch.tv USERNOTICE #pajlada)", + + // resub with message + R"(@badges=subscriber/0,premium/1;color=#1E90FF;display-name=OscarLord;emotes=;id=376529fd-31a8-4da9-9c0d-92a9470da2cd;login=oscarlord;mod=0;msg-id=resub;msg-param-months=2;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=1000;room-id=39298218;subscriber=1;system-msg=OscarLord\sjust\ssubscribed\swith\sa\sTier\s1\ssub.\sOscarLord\ssubscribed\sfor\s2\smonths\sin\sa\srow!;tmi-sent-ts=1528191154801;turbo=0;user-id=162607810;user-type= :tmi.twitch.tv USERNOTICE #pajlada :Hey dk love to watch your streams keep up the good work)", + + // resub with message + R"(@badges=subscriber/0,premium/1;color=;display-name=samewl;emotes=9:22-23;id=599fda87-ca1e-41f2-9af7-6a28208daf1c;login=samewl;mod=0;msg-id=resub;msg-param-months=5;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol);msg-param-sub-plan=Prime;room-id=22484632;subscriber=1;system-msg=samewl\sjust\ssubscribed\swith\sTwitch\sPrime.\ssamewl\ssubscribed\sfor\s5\smonths\sin\sa\srow!;tmi-sent-ts=1528191317948;turbo=0;user-id=70273207;user-type= :tmi.twitch.tv USERNOTICE #pajlada :lot of love sebastian <3)", + + // resub without message + R"(@badges=subscriber/12;color=#CC00C2;display-name=cspice;emotes=;id=6fc4c3e0-ca61-454a-84b8-5669dee69fc9;login=cspice;mod=0;msg-id=resub;msg-param-months=12;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol):\s$9.99\sSub;msg-param-sub-plan=2000;room-id=22484632;subscriber=1;system-msg=cspice\sjust\ssubscribed\swith\sa\sTier\s2\ssub.\scspice\ssubscribed\sfor\s12\smonths\sin\sa\srow!;tmi-sent-ts=1528192510808;turbo=0;user-id=47894662;user-type= :tmi.twitch.tv USERNOTICE #pajlada)", + }; + return list; +} + +const QStringList &getSampleMiscMessages() +{ + static QStringList list{ + // display name renders strangely + R"(@badges=;color=#00AD2B;display-name=Iamme420\s;emotes=;id=d47a1e4b-a3c6-4b9e-9bf1-51b8f3dbc76e;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1529670347537;turbo=0;user-id=56422869;user-type= :iamme420!iamme420@iamme420.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)", + R"(@badge-info=founder/47;badges=moderator/1,founder/0,premium/1;color=#00FF80;display-name=gempir;emotes=;flags=;id=d4514490-202e-43cb-b429-ef01a9d9c2fe;mod=1;room-id=11148817;subscriber=0;tmi-sent-ts=1575198233854;turbo=0;user-id=77829817;user-type=mod :gempir!gempir@gempir.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)", + + // "first time chat" message + R"(@badge-info=;badges=glhf-pledge/1;client-nonce=5d2627b0cbe56fa05faf5420def4807d;color=#1E90FF;display-name=oldcoeur;emote-only=1;emotes=84608:0-7;first-msg=1;flags=;id=7412fea4-8683-4cc9-a506-4228127a5c2d;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1623429859222;turbo=0;user-id=139147886;user-type= :oldcoeur!oldcoeur@oldcoeur.tmi.twitch.tv PRIVMSG #pajlada :cmonBruh)", + + // Message with founder badge + R"(@badge-info=founder/72;badges=founder/0,bits/5000;color=#FF0000;display-name=TranRed;emotes=;first-msg=0;flags=;id=7482163f-493d-41d9-b36f-fba50e0701b7;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1641123773885;turbo=0;user-id=57019243;user-type= :tranred!tranred@tranred.tmi.twitch.tv PRIVMSG #pajlada :GFMP pajaE)", + + // mod announcement + R"(@badge-info=subscriber/47;badges=broadcaster/1,subscriber/3012,twitchconAmsterdam2020/1;color=#FF0000;display-name=Supinic;emotes=;flags=;id=8c26e1ab-b50c-4d9d-bc11-3fd57a941d90;login=supinic;mod=0;msg-id=announcement;msg-param-color=PRIMARY;room-id=31400525;subscriber=1;system-msg=;tmi-sent-ts=1648762219962;user-id=31400525;user-type= :tmi.twitch.tv USERNOTICE #supinic :mm test lol)", + }; + return list; +} + +const QStringList &getSampleEmoteTestMessages() +{ + static QStringList list{ + R"(@badge-info=subscriber/3;badges=subscriber/3;color=#0000FF;display-name=Linkoping;emotes=25:40-44;flags=17-26:S.6;id=744f9c58-b180-4f46-bd9e-b515b5ef75c1;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1566335866017;turbo=0;user-id=91673457;user-type= :linkoping!linkoping@linkoping.tmi.twitch.tv PRIVMSG #pajlada :Då kan du begära skadestånd och förtal Kappa)", + R"(@badge-info=subscriber/1;badges=subscriber/0;color=;display-name=jhoelsc;emotes=301683486:46-58,60-72,74-86/301683544:88-100;flags=0-4:S.6;id=1f1afcdd-d94c-4699-b35f-d214deb1e11a;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1588640587462;turbo=0;user-id=505763008;user-type= :jhoelsc!jhoelsc@jhoelsc.tmi.twitch.tv PRIVMSG #pajlada :pensé que no habría directo que bueno que si staryuukiLove staryuukiLove staryuukiLove staryuukiBits)", + R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,16-23;flags=;id=97c28382-e8d2-45a0-bb5d-2305fc4ef139;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922036771;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm, Kreygasm)", + R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=25:24-28/41:6-13,15-22;flags=;id=5a36536b-a952-43f7-9c41-88c829371b7a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922039721;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm Kappa (no space then space))", + R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=25:6-10/1902:12-16/88:18-25;flags=;id=aed9e67e-f8cd-493e-aa6b-da054edd7292;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922042881;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kappa.Keepo.PogChamp.extratext (3 emotes with extra text))", + R"(@badge-info=;badges=moderator/1,partner/1;color=#5B99FF;display-name=StreamElements;emotes=86:30-39/822112:73-79;flags=22-27:S.5;id=03c3eec9-afd1-4858-a2e0-fccbf6ad8d1a;mod=1;room-id=11148817;subscriber=0;tmi-sent-ts=1588638345928;turbo=0;user-id=100135110;user-type=mod :streamelements!streamelements@streamelements.tmi.twitch.tv PRIVMSG #pajlada :╔ACTION A LOJA AINDA NÃO ESTÁ PRONTA BibleThump , AGUARDE... NOVIDADES EM BREVE FortOne╔)", + R"(@badge-info=subscriber/20;badges=moderator/1,subscriber/12;color=#19E6E6;display-name=randers;emotes=25:39-43;flags=;id=3ea97f01-abb2-4acf-bdb8-f52e79cd0324;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1588837097115;turbo=0;user-id=40286300;user-type=mod :randers!randers@randers.tmi.twitch.tv PRIVMSG #pajlada :Då kan du begära skadestånd och förtal Kappa)", + R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,15-22;flags=;id=a3196c7e-be4c-4b49-9c5a-8b8302b50c2a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922213730;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm (no space))", + }; + return list; +} + +/// Channel point reward tests + +const QString &getSampleChannelRewardMessage() +{ + static QString str{ + R"({ "type": "MESSAGE", "data": { "topic": "community-points-channel-v1.11148817", "message": { "type": "reward-redeemed", "data": { "timestamp": "2020-07-13T20:19:31.430785354Z", "redemption": { "id": "b9628798-1b4e-4122-b2a6-031658df6755", "user": { "id": "91800084", "login": "cranken1337", "display_name": "cranken1337" }, "channel_id": "11148817", "redeemed_at": "2020-07-13T20:19:31.345237005Z", "reward": { "id": "313969fe-cc9f-4a0a-83c6-172acbd96957", "channel_id": "11148817", "title": "annoying reward pogchamp", "prompt": "", "cost": 3000, "is_user_input_required": true, "is_sub_only": false, "image": null, "default_image": { "url_1x": "https://static-cdn.jtvnw.net/custom-reward-images/default-1.png", "url_2x": "https://static-cdn.jtvnw.net/custom-reward-images/default-2.png", "url_4x": "https://static-cdn.jtvnw.net/custom-reward-images/default-4.png" }, "background_color": "#52ACEC", "is_enabled": true, "is_paused": false, "is_in_stock": true, "max_per_stream": { "is_enabled": false, "max_per_stream": 0 }, "should_redemptions_skip_request_queue": false, "template_id": null, "updated_for_indicator_at": "2020-01-20T04:33:33.624956679Z" }, "user_input": "wow, amazing reward", "status": "UNFULFILLED", "cursor": "Yjk2Mjg3OTgtMWI0ZS00MTIyLWIyYTYtMDMxNjU4ZGY2NzU1X18yMDIwLTA3LTEzVDIwOjE5OjMxLjM0NTIzNzAwNVo=" } } } } })", + }; + return str; +} + +const QString &getSampleChannelRewardMessage2() +{ + static QString str{ + R"({ "type": "MESSAGE", "data": { "topic": "community-points-channel-v1.11148817", "message": { "type": "reward-redeemed", "data": { "timestamp": "2020-07-13T20:19:31.430785354Z", "redemption": { "id": "b9628798-1b4e-4122-b2a6-031658df6755", "user": { "id": "91800084", "login": "cranken1337", "display_name": "cranken1337" }, "channel_id": "11148817", "redeemed_at": "2020-07-13T20:19:31.345237005Z", "reward": { "id": "313969fe-cc9f-4a0a-83c6-172acbd96957", "channel_id": "11148817", "title": "annoying reward pogchamp", "prompt": "", "cost": 3000, "is_user_input_required": false, "is_sub_only": false, "image": null, "default_image": { "url_1x": "https://static-cdn.jtvnw.net/custom-reward-images/default-1.png", "url_2x": "https://static-cdn.jtvnw.net/custom-reward-images/default-2.png", "url_4x": "https://static-cdn.jtvnw.net/custom-reward-images/default-4.png" }, "background_color": "#52ACEC", "is_enabled": true, "is_paused": false, "is_in_stock": true, "max_per_stream": { "is_enabled": false, "max_per_stream": 0 }, "should_redemptions_skip_request_queue": false, "template_id": null, "updated_for_indicator_at": "2020-01-20T04:33:33.624956679Z" }, "status": "UNFULFILLED", "cursor": "Yjk2Mjg3OTgtMWI0ZS00MTIyLWIyYTYtMDMxNjU4ZGY2NzU1X18yMDIwLTA3LTEzVDIwOjE5OjMxLjM0NTIzNzAwNVo=" } } } } })", + }; + return str; +} + +const QString &getSampleChannelRewardIRCMessage() +{ + static QString str{ + R"(@badge-info=subscriber/43;badges=subscriber/42;color=#1E90FF;custom-reward-id=313969fe-cc9f-4a0a-83c6-172acbd96957;display-name=Cranken1337;emotes=;flags=;id=3cee3f27-a1d0-44d1-a606-722cebdad08b;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1594756484132;turbo=0;user-id=91800084;user-type= :cranken1337!cranken1337@cranken1337.tmi.twitch.tv PRIVMSG #pajlada :wow, amazing reward)", + }; + return str; +}; + +/// Links + +const QStringList &getSampleLinkMessages() +{ + static QStringList validLinks{ + R"(http://github.com/)", + R"(https://github.com/)", + R"(http://username@github.com/)", + R"(https://username@github.com/)", + R"(http://pajlada.github.io)", + R"(https://pajlada.github.io)", + R"(http://pajlada.github.io/)", + R"(https://pajlada.github.io/)", + R"(http://github.com/some/random/path)", + R"(https://github.com/some/random/path)", + R"(http://github.com?query=value)", + R"(https://github.com?query=value)", + R"(http://github.com?query=value&abc=123)", + R"(https://github.com?query=value&abc=123)", + R"(http://github.com/?query=value&abc=123&yhf=abc_def)", + R"(http://github.com/?query=value&abc=123&yhf)", + R"(http://github.com?query=value&abc=)", + R"(http://github.com?query=value&abc=)", + R"(http://github.com/#block)", + R"(https://github.com/#anchor)", + R"(http://github.com/path/?qs=true#block)", + R"(https://github.com/path/?qs=true#anchor)", + R"(github.com/)", + R"(username@github.com/)", + R"(pajlada.github.io)", + R"(pajlada.github.io/)", + R"(github.com/some/random/path)", + R"(github.com?query=value)", + R"(github.com?query=value&abc=123)", + R"(github.com/?query=value&abc=123&yhf=abc_def)", + R"(github.com/?query=value&abc=123&yhf)", + R"(github.com?query=value&abc=)", + R"(github.com?query=value&abc=)", + R"(github.com/#block)", + R"(github.com/path/?qs=true#block)", + R"(HTTP://GITHUB.COM/)", + R"(HTTPS://GITHUB.COM/)", + R"(HTTP://USERNAME@GITHUB.COM/)", + R"(HTTPS://USERNAME@GITHUB.COM/)", + R"(HTTP://PAJLADA.GITHUB.IO)", + R"(HTTPS://PAJLADA.GITHUB.IO)", + R"(HTTP://PAJLADA.GITHUB.IO/)", + R"(HTTPS://PAJLADA.GITHUB.IO/)", + R"(HTTP://GITHUB.COM/SOME/RANDOM/PATH)", + R"(HTTPS://GITHUB.COM/SOME/RANDOM/PATH)", + R"(HTTP://GITHUB.COM?QUERY=VALUE)", + R"(HTTPS://GITHUB.COM?QUERY=VALUE)", + R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=123)", + R"(HTTPS://GITHUB.COM?QUERY=VALUE&ABC=123)", + R"(HTTP://GITHUB.COM/?QUERY=VALUE&ABC=123&YHF=ABC_DEF)", + R"(HTTP://GITHUB.COM/?QUERY=VALUE&ABC=123&YHF)", + R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=)", + R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=)", + R"(HTTP://GITHUB.COM/#BLOCK)", + R"(HTTPS://GITHUB.COM/#ANCHOR)", + R"(HTTP://GITHUB.COM/PATH/?QS=TRUE#BLOCK)", + R"(HTTPS://GITHUB.COM/PATH/?QS=TRUE#ANCHOR)", + R"(GITHUB.COM/)", + R"(USERNAME@GITHUB.COM/)", + R"(PAJLADA.GITHUB.IO)", + R"(PAJLADA.GITHUB.IO/)", + R"(GITHUB.COM/SOME/RANDOM/PATH)", + R"(GITHUB.COM?QUERY=VALUE)", + R"(GITHUB.COM?QUERY=VALUE&ABC=123)", + R"(GITHUB.COM/?QUERY=VALUE&ABC=123&YHF=ABC_DEF)", + R"(GITHUB.COM/?QUERY=VALUE&ABC=123&YHF)", + R"(GITHUB.COM?QUERY=VALUE&ABC=)", + R"(GITHUB.COM?QUERY=VALUE&ABC=)", + R"(GITHUB.COM/#BLOCK)", + R"(GITHUB.COM/PATH/?QS=TRUE#BLOCK)", + R"(http://foo.com/blah_blah)", + R"(http://foo.com/blah_blah/)", + R"(http://foo.com/blah_blah_(wikipedia))", + R"(http://foo.com/blah_blah_(wikipedia)_(again))", + R"(http://www.example.com/wpstyle/?p=364)", + R"(https://www.example.com/foo/?bar=baz&inga=42&quux)", + R"(http://✪df.ws/123)", + R"(http://userid@example.com)", + R"(http://userid@example.com/)", + R"(http://userid@example.com:8080)", + R"(http://userid@example.com:8080/)", + R"(http://142.42.1.1/)", + R"(http://142.42.1.1:8080/)", + R"(http://➡.ws/䨹)", + R"(http://⌘.ws)", + R"(http://⌘.ws/)", + R"(http://foo.com/blah_(wikipedia)#cite-1)", + R"(http://foo.com/blah_(wikipedia)_blah#cite-1)", + R"(http://foo.com/unicode_(✪)_in_parens)", + R"(http://foo.com/(something)?after=parens)", + R"(http://☺.damowmow.com/)", + R"(http://code.google.com/events/#&product=browser)", + R"(http://j.mp)", + R"(ftp://foo.bar/baz)", + R"(http://foo.bar/?q=Test%20URL-encoded%20stuff)", + R"(http://مثال.إختبار)", + R"(http://例子.测试)", + R"(http://उदाहरण.परीक्षा)", + R"(http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com)", + R"(http://1337.net)", + R"(http://a.b-c.de)", + R"(http://223.255.255.254)", + }; + + static QStringList validButIgnoredLinks{ + R"(http://username:password@github.com/)", + R"(https://username:password@github.com/)", + R"(http://userid:password@example.com)", + R"(http://userid:password@example.com/)", + R"(http://userid:password@example.com:8080)", + R"(http://userid:password@example.com:8080/)", + }; + + static QStringList invalidLinks{ + R"(1.40)", + R"(test..)", + R"(test.)", + R"(http://)", + R"(http://.)", + R"(http://..)", + R"(http://../)", + R"(http://?)", + R"(http://??)", + R"(http://??/)", + R"(http://#)", + R"(http://##)", + R"(http://##/)", + R"(http://foo.bar?q=Spaces should be encoded)", + R"(//)", + R"(//a)", + R"(///a)", + R"(///)", + R"(http:///a)", + R"(foo.com)", + R"(rdar://1234)", + R"(h://test)", + R"(http:// shouldfail.com)", + R"(:// should fail)", + R"(http://foo.bar/foo(bar)baz quux)", + R"(ftps://foo.bar/)", + R"(http://-error-.invalid/)", + R"(http://a.b--c.de/)", + R"(http://-a.b.co)", + R"(http://a.b-.co)", + R"(http://0.0.0.0)", + R"(http://10.1.1.0)", + R"(http://10.1.1.255)", + R"(http://224.1.1.1)", + R"(http://1.1.1.1.1)", + R"(http://123.123.123)", + R"(http://3628126748)", + R"(http://.www.foo.bar/)", + R"(http://www.foo.bar./)", + R"(http://.www.foo.bar./)", + R"(http://10.1.1.1)", + }; + + static QStringList linkList{ + R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should pass: )" + + validLinks.join(' '), + R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should NOT pass: )" + + validButIgnoredLinks.join(' '), + R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should technically pass but we choose not to parse them: )" + + invalidLinks.join(' '), + }; + + return linkList; +}; + +} // namespace chatterino diff --git a/src/util/SampleData.hpp b/src/util/SampleData.hpp new file mode 100644 index 000000000..53d22f1ea --- /dev/null +++ b/src/util/SampleData.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace chatterino { + +/// Sample messages coming from IRC + +const QStringList &getSampleCheerMessages(); +const QStringList &getSampleSubMessages(); +const QStringList &getSampleMiscMessages(); +const QStringList &getSampleEmoteTestMessages(); + +/// Channel point reward tests + +const QString &getSampleChannelRewardMessage(); +const QString &getSampleChannelRewardMessage2(); +const QString &getSampleChannelRewardIRCMessage(); + +/// Links + +const QStringList &getSampleLinkMessages(); + +} // namespace chatterino diff --git a/src/util/SampleLinks.hpp b/src/util/SampleLinks.hpp deleted file mode 100644 index 4ddc9b413..000000000 --- a/src/util/SampleLinks.hpp +++ /dev/null @@ -1,174 +0,0 @@ -#pragma once - -#include - -namespace chatterino { - -QStringList getValidLinks() -{ - return { - R"(http://github.com/)", - R"(https://github.com/)", - R"(http://username@github.com/)", - R"(https://username@github.com/)", - R"(http://pajlada.github.io)", - R"(https://pajlada.github.io)", - R"(http://pajlada.github.io/)", - R"(https://pajlada.github.io/)", - R"(http://github.com/some/random/path)", - R"(https://github.com/some/random/path)", - R"(http://github.com?query=value)", - R"(https://github.com?query=value)", - R"(http://github.com?query=value&abc=123)", - R"(https://github.com?query=value&abc=123)", - R"(http://github.com/?query=value&abc=123&yhf=abc_def)", - R"(http://github.com/?query=value&abc=123&yhf)", - R"(http://github.com?query=value&abc=)", - R"(http://github.com?query=value&abc=)", - R"(http://github.com/#block)", - R"(https://github.com/#anchor)", - R"(http://github.com/path/?qs=true#block)", - R"(https://github.com/path/?qs=true#anchor)", - R"(github.com/)", - R"(username@github.com/)", - R"(pajlada.github.io)", - R"(pajlada.github.io/)", - R"(github.com/some/random/path)", - R"(github.com?query=value)", - R"(github.com?query=value&abc=123)", - R"(github.com/?query=value&abc=123&yhf=abc_def)", - R"(github.com/?query=value&abc=123&yhf)", - R"(github.com?query=value&abc=)", - R"(github.com?query=value&abc=)", - R"(github.com/#block)", - R"(github.com/path/?qs=true#block)", - R"(HTTP://GITHUB.COM/)", - R"(HTTPS://GITHUB.COM/)", - R"(HTTP://USERNAME@GITHUB.COM/)", - R"(HTTPS://USERNAME@GITHUB.COM/)", - R"(HTTP://PAJLADA.GITHUB.IO)", - R"(HTTPS://PAJLADA.GITHUB.IO)", - R"(HTTP://PAJLADA.GITHUB.IO/)", - R"(HTTPS://PAJLADA.GITHUB.IO/)", - R"(HTTP://GITHUB.COM/SOME/RANDOM/PATH)", - R"(HTTPS://GITHUB.COM/SOME/RANDOM/PATH)", - R"(HTTP://GITHUB.COM?QUERY=VALUE)", - R"(HTTPS://GITHUB.COM?QUERY=VALUE)", - R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=123)", - R"(HTTPS://GITHUB.COM?QUERY=VALUE&ABC=123)", - R"(HTTP://GITHUB.COM/?QUERY=VALUE&ABC=123&YHF=ABC_DEF)", - R"(HTTP://GITHUB.COM/?QUERY=VALUE&ABC=123&YHF)", - R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=)", - R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=)", - R"(HTTP://GITHUB.COM/#BLOCK)", - R"(HTTPS://GITHUB.COM/#ANCHOR)", - R"(HTTP://GITHUB.COM/PATH/?QS=TRUE#BLOCK)", - R"(HTTPS://GITHUB.COM/PATH/?QS=TRUE#ANCHOR)", - R"(GITHUB.COM/)", - R"(USERNAME@GITHUB.COM/)", - R"(PAJLADA.GITHUB.IO)", - R"(PAJLADA.GITHUB.IO/)", - R"(GITHUB.COM/SOME/RANDOM/PATH)", - R"(GITHUB.COM?QUERY=VALUE)", - R"(GITHUB.COM?QUERY=VALUE&ABC=123)", - R"(GITHUB.COM/?QUERY=VALUE&ABC=123&YHF=ABC_DEF)", - R"(GITHUB.COM/?QUERY=VALUE&ABC=123&YHF)", - R"(GITHUB.COM?QUERY=VALUE&ABC=)", - R"(GITHUB.COM?QUERY=VALUE&ABC=)", - R"(GITHUB.COM/#BLOCK)", - R"(GITHUB.COM/PATH/?QS=TRUE#BLOCK)", - R"(http://foo.com/blah_blah)", - R"(http://foo.com/blah_blah/)", - R"(http://foo.com/blah_blah_(wikipedia))", - R"(http://foo.com/blah_blah_(wikipedia)_(again))", - R"(http://www.example.com/wpstyle/?p=364)", - R"(https://www.example.com/foo/?bar=baz&inga=42&quux)", - R"(http://✪df.ws/123)", - R"(http://userid@example.com)", - R"(http://userid@example.com/)", - R"(http://userid@example.com:8080)", - R"(http://userid@example.com:8080/)", - R"(http://142.42.1.1/)", - R"(http://142.42.1.1:8080/)", - R"(http://➡.ws/䨹)", - R"(http://⌘.ws)", - R"(http://⌘.ws/)", - R"(http://foo.com/blah_(wikipedia)#cite-1)", - R"(http://foo.com/blah_(wikipedia)_blah#cite-1)", - R"(http://foo.com/unicode_(✪)_in_parens)", - R"(http://foo.com/(something)?after=parens)", - R"(http://☺.damowmow.com/)", - R"(http://code.google.com/events/#&product=browser)", - R"(http://j.mp)", - R"(ftp://foo.bar/baz)", - R"(http://foo.bar/?q=Test%20URL-encoded%20stuff)", - R"(http://مثال.إختبار)", - R"(http://例子.测试)", - R"(http://उदाहरण.परीक्षा)", - R"(http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com)", - R"(http://1337.net)", - R"(http://a.b-c.de)", - R"(http://223.255.255.254)", - }; -} - -QStringList getValidButIgnoredLinks() -{ - return { - R"(http://username:password@github.com/)", - R"(https://username:password@github.com/)", - R"(http://userid:password@example.com)", - R"(http://userid:password@example.com/)", - R"(http://userid:password@example.com:8080)", - R"(http://userid:password@example.com:8080/)", - }; -} - -QStringList getInvalidLinks() -{ - return { - R"(1.40)", - R"(test..)", - R"(test.)", - R"(http://)", - R"(http://.)", - R"(http://..)", - R"(http://../)", - R"(http://?)", - R"(http://??)", - R"(http://??/)", - R"(http://#)", - R"(http://##)", - R"(http://##/)", - R"(http://foo.bar?q=Spaces should be encoded)", - R"(//)", - R"(//a)", - R"(///a)", - R"(///)", - R"(http:///a)", - R"(foo.com)", - R"(rdar://1234)", - R"(h://test)", - R"(http:// shouldfail.com)", - R"(:// should fail)", - R"(http://foo.bar/foo(bar)baz quux)", - R"(ftps://foo.bar/)", - R"(http://-error-.invalid/)", - R"(http://a.b--c.de/)", - R"(http://-a.b.co)", - R"(http://a.b-.co)", - R"(http://0.0.0.0)", - R"(http://10.1.1.0)", - R"(http://10.1.1.255)", - R"(http://224.1.1.1)", - R"(http://1.1.1.1.1)", - R"(http://123.123.123)", - R"(http://3628126748)", - R"(http://.www.foo.bar/)", - R"(http://www.foo.bar./)", - R"(http://.www.foo.bar./)", - R"(http://10.1.1.1)", - }; -} - -} // namespace chatterino diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index bc271103c..5b6d38e18 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -30,8 +30,7 @@ # include # include "providers/twitch/PubSubManager.hpp" # include "providers/twitch/PubSubMessages.hpp" -# include "util/SampleCheerMessages.hpp" -# include "util/SampleLinks.hpp" +# include "util/SampleData.hpp" #endif #include @@ -188,82 +187,16 @@ void Window::addCustomTitlebarButtons() void Window::addDebugStuff(HotkeyController::HotkeyMap &actions) { #ifndef NDEBUG - std::vector cheerMessages, subMessages, miscMessages, linkMessages, - emoteTestMessages; - - cheerMessages = getSampleCheerMessage(); - auto validLinks = getValidLinks(); - auto invalidLinks = getInvalidLinks(); - // clang-format off - - subMessages.emplace_back(R"(@badges=staff/1,broadcaster/1,turbo/1;color=#008000;display-name=ronni;emotes=;id=db25007f-7a18-43eb-9379-80131e44d633;login=ronni;mod=0;msg-id=resub;msg-param-months=6;msg-param-sub-plan=Prime;msg-param-sub-plan-name=Prime;room-id=1337;subscriber=1;system-msg=ronni\shas\ssubscribed\sfor\s6\smonths!;tmi-sent-ts=1507246572675;turbo=1;user-id=1337;user-type=staff :tmi.twitch.tv USERNOTICE #pajlada :Great stream -- keep it up!)"); - subMessages.emplace_back(R"(@badges=staff/1,premium/1;color=#0000FF;display-name=TWW2;emotes=;id=e9176cd8-5e22-4684-ad40-ce53c2561c5e;login=tww2;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=Mr_Woodchuck;msg-param-recipient-id=89614178;msg-param-recipient-name=mr_woodchuck;msg-param-sub-plan-name=House\sof\sNyoro~n;msg-param-sub-plan=1000;room-id=19571752;subscriber=0;system-msg=TWW2\sgifted\sa\sTier\s1\ssub\sto\sMr_Woodchuck!;tmi-sent-ts=1521159445153;turbo=0;user-id=13405587;user-type=staff :tmi.twitch.tv USERNOTICE #pajlada)"); - - // hyperbolicxd gifted a sub to quote_if_nam - subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#00FF7F;display-name=hyperbolicxd;emotes=;id=b20ef4fe-cba8-41d0-a371-6327651dc9cc;login=hyperbolicxd;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=quote_if_nam;msg-param-recipient-id=217259245;msg-param-recipient-user-name=quote_if_nam;msg-param-sender-count=1;msg-param-sub-plan-name=Channel\sSubscription\s(nymn_hs);msg-param-sub-plan=1000;room-id=62300805;subscriber=1;system-msg=hyperbolicxd\sgifted\sa\sTier\s1\ssub\sto\squote_if_nam!\sThis\sis\stheir\sfirst\sGift\sSub\sin\sthe\schannel!;tmi-sent-ts=1528190938558;turbo=0;user-id=111534250;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); - - // first time sub - subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#0000FF;display-name=byebyeheart;emotes=;id=fe390424-ab89-4c33-bb5a-53c6e5214b9f;login=byebyeheart;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=byebyeheart\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190963670;turbo=0;user-id=131956000;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); - - // first time sub - subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=vJoeyzz;emotes=;id=b2476df5-fffe-4338-837b-380c5dd90051;login=vjoeyzz;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=vJoeyzz\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528190995089;turbo=0;user-id=78945903;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); - - // first time sub - subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=Lennydog3;emotes=;id=44feb1eb-df60-45f6-904b-7bf0d5375a41;login=lennydog3;mod=0;msg-id=sub;msg-param-months=0;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=Prime;room-id=39298218;subscriber=0;system-msg=Lennydog3\sjust\ssubscribed\swith\sTwitch\sPrime!;tmi-sent-ts=1528191098733;turbo=0;user-id=175759335;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); - - // resub with message - subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=#1E90FF;display-name=OscarLord;emotes=;id=376529fd-31a8-4da9-9c0d-92a9470da2cd;login=oscarlord;mod=0;msg-id=resub;msg-param-months=2;msg-param-sub-plan-name=Dakotaz;msg-param-sub-plan=1000;room-id=39298218;subscriber=1;system-msg=OscarLord\sjust\ssubscribed\swith\sa\sTier\s1\ssub.\sOscarLord\ssubscribed\sfor\s2\smonths\sin\sa\srow!;tmi-sent-ts=1528191154801;turbo=0;user-id=162607810;user-type= :tmi.twitch.tv USERNOTICE #pajlada :Hey dk love to watch your streams keep up the good work)"); - - // resub with message - subMessages.emplace_back(R"(@badges=subscriber/0,premium/1;color=;display-name=samewl;emotes=9:22-23;id=599fda87-ca1e-41f2-9af7-6a28208daf1c;login=samewl;mod=0;msg-id=resub;msg-param-months=5;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol);msg-param-sub-plan=Prime;room-id=22484632;subscriber=1;system-msg=samewl\sjust\ssubscribed\swith\sTwitch\sPrime.\ssamewl\ssubscribed\sfor\s5\smonths\sin\sa\srow!;tmi-sent-ts=1528191317948;turbo=0;user-id=70273207;user-type= :tmi.twitch.tv USERNOTICE #pajlada :lot of love sebastian <3)"); - - // resub without message - subMessages.emplace_back(R"(@badges=subscriber/12;color=#CC00C2;display-name=cspice;emotes=;id=6fc4c3e0-ca61-454a-84b8-5669dee69fc9;login=cspice;mod=0;msg-id=resub;msg-param-months=12;msg-param-sub-plan-name=Channel\sSubscription\s(forsenlol):\s$9.99\sSub;msg-param-sub-plan=2000;room-id=22484632;subscriber=1;system-msg=cspice\sjust\ssubscribed\swith\sa\sTier\s2\ssub.\scspice\ssubscribed\sfor\s12\smonths\sin\sa\srow!;tmi-sent-ts=1528192510808;turbo=0;user-id=47894662;user-type= :tmi.twitch.tv USERNOTICE #pajlada)"); - - // display name renders strangely - miscMessages.emplace_back(R"(@badges=;color=#00AD2B;display-name=Iamme420\s;emotes=;id=d47a1e4b-a3c6-4b9e-9bf1-51b8f3dbc76e;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1529670347537;turbo=0;user-id=56422869;user-type= :iamme420!iamme420@iamme420.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)"); - miscMessages.emplace_back(R"(@badge-info=founder/47;badges=moderator/1,founder/0,premium/1;color=#00FF80;display-name=gempir;emotes=;flags=;id=d4514490-202e-43cb-b429-ef01a9d9c2fe;mod=1;room-id=11148817;subscriber=0;tmi-sent-ts=1575198233854;turbo=0;user-id=77829817;user-type=mod :gempir!gempir@gempir.tmi.twitch.tv PRIVMSG #pajlada :offline chat gachiBASS)"); - - // "first time chat" message - miscMessages.emplace_back(R"(@badge-info=;badges=glhf-pledge/1;client-nonce=5d2627b0cbe56fa05faf5420def4807d;color=#1E90FF;display-name=oldcoeur;emote-only=1;emotes=84608:0-7;first-msg=1;flags=;id=7412fea4-8683-4cc9-a506-4228127a5c2d;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1623429859222;turbo=0;user-id=139147886;user-type= :oldcoeur!oldcoeur@oldcoeur.tmi.twitch.tv PRIVMSG #pajlada :cmonBruh)"); - - // Message with founder badge - miscMessages.emplace_back(R"(@badge-info=founder/72;badges=founder/0,bits/5000;color=#FF0000;display-name=TranRed;emotes=;first-msg=0;flags=;id=7482163f-493d-41d9-b36f-fba50e0701b7;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1641123773885;turbo=0;user-id=57019243;user-type= :tranred!tranred@tranred.tmi.twitch.tv PRIVMSG #pajlada :GFMP pajaE)"); - - // mod announcement - miscMessages.emplace_back(R"(@badge-info=subscriber/47;badges=broadcaster/1,subscriber/3012,twitchconAmsterdam2020/1;color=#FF0000;display-name=Supinic;emotes=;flags=;id=8c26e1ab-b50c-4d9d-bc11-3fd57a941d90;login=supinic;mod=0;msg-id=announcement;msg-param-color=PRIMARY;room-id=31400525;subscriber=1;system-msg=;tmi-sent-ts=1648762219962;user-id=31400525;user-type= :tmi.twitch.tv USERNOTICE #supinic :mm test lol)"); - - // various link tests - linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should pass: )" + getValidLinks().join(' ')); - linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should NOT pass: )" + getInvalidLinks().join(' ')); - linkMessages.emplace_back(R"(@badge-info=subscriber/48;badges=broadcaster/1,subscriber/36,partner/1;color=#CC44FF;display-name=pajlada;emotes=;flags=;id=3c23cf3c-0864-4699-a76b-089350141147;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1577628844607;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #pajlada : Links that should technically pass but we choose not to parse them: )" + getValidButIgnoredLinks().join(' ')); - - // channel point reward test - const char *channelRewardMessage = "{ \"type\": \"MESSAGE\", \"data\": { \"topic\": \"community-points-channel-v1.11148817\", \"message\": { \"type\": \"reward-redeemed\", \"data\": { \"timestamp\": \"2020-07-13T20:19:31.430785354Z\", \"redemption\": { \"id\": \"b9628798-1b4e-4122-b2a6-031658df6755\", \"user\": { \"id\": \"91800084\", \"login\": \"cranken1337\", \"display_name\": \"cranken1337\" }, \"channel_id\": \"11148817\", \"redeemed_at\": \"2020-07-13T20:19:31.345237005Z\", \"reward\": { \"id\": \"313969fe-cc9f-4a0a-83c6-172acbd96957\", \"channel_id\": \"11148817\", \"title\": \"annoying reward pogchamp\", \"prompt\": \"\", \"cost\": 3000, \"is_user_input_required\": true, \"is_sub_only\": false, \"image\": null, \"default_image\": { \"url_1x\": \"https://static-cdn.jtvnw.net/custom-reward-images/default-1.png\", \"url_2x\": \"https://static-cdn.jtvnw.net/custom-reward-images/default-2.png\", \"url_4x\": \"https://static-cdn.jtvnw.net/custom-reward-images/default-4.png\" }, \"background_color\": \"#52ACEC\", \"is_enabled\": true, \"is_paused\": false, \"is_in_stock\": true, \"max_per_stream\": { \"is_enabled\": false, \"max_per_stream\": 0 }, \"should_redemptions_skip_request_queue\": false, \"template_id\": null, \"updated_for_indicator_at\": \"2020-01-20T04:33:33.624956679Z\" }, \"user_input\": \"wow, amazing reward\", \"status\": \"UNFULFILLED\", \"cursor\": \"Yjk2Mjg3OTgtMWI0ZS00MTIyLWIyYTYtMDMxNjU4ZGY2NzU1X18yMDIwLTA3LTEzVDIwOjE5OjMxLjM0NTIzNzAwNVo=\" } } } } }"; - const char *channelRewardMessage2 = "{ \"type\": \"MESSAGE\", \"data\": { \"topic\": \"community-points-channel-v1.11148817\", \"message\": { \"type\": \"reward-redeemed\", \"data\": { \"timestamp\": \"2020-07-13T20:19:31.430785354Z\", \"redemption\": { \"id\": \"b9628798-1b4e-4122-b2a6-031658df6755\", \"user\": { \"id\": \"91800084\", \"login\": \"cranken1337\", \"display_name\": \"cranken1337\" }, \"channel_id\": \"11148817\", \"redeemed_at\": \"2020-07-13T20:19:31.345237005Z\", \"reward\": { \"id\": \"313969fe-cc9f-4a0a-83c6-172acbd96957\", \"channel_id\": \"11148817\", \"title\": \"annoying reward pogchamp\", \"prompt\": \"\", \"cost\": 3000, \"is_user_input_required\": false, \"is_sub_only\": false, \"image\": null, \"default_image\": { \"url_1x\": \"https://static-cdn.jtvnw.net/custom-reward-images/default-1.png\", \"url_2x\": \"https://static-cdn.jtvnw.net/custom-reward-images/default-2.png\", \"url_4x\": \"https://static-cdn.jtvnw.net/custom-reward-images/default-4.png\" }, \"background_color\": \"#52ACEC\", \"is_enabled\": true, \"is_paused\": false, \"is_in_stock\": true, \"max_per_stream\": { \"is_enabled\": false, \"max_per_stream\": 0 }, \"should_redemptions_skip_request_queue\": false, \"template_id\": null, \"updated_for_indicator_at\": \"2020-01-20T04:33:33.624956679Z\" }, \"status\": \"UNFULFILLED\", \"cursor\": \"Yjk2Mjg3OTgtMWI0ZS00MTIyLWIyYTYtMDMxNjU4ZGY2NzU1X18yMDIwLTA3LTEzVDIwOjE5OjMxLjM0NTIzNzAwNVo=\" } } } } }"; - const char *channelRewardIRCMessage(R"(@badge-info=subscriber/43;badges=subscriber/42;color=#1E90FF;custom-reward-id=313969fe-cc9f-4a0a-83c6-172acbd96957;display-name=Cranken1337;emotes=;flags=;id=3cee3f27-a1d0-44d1-a606-722cebdad08b;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1594756484132;turbo=0;user-id=91800084;user-type= :cranken1337!cranken1337@cranken1337.tmi.twitch.tv PRIVMSG #pajlada :wow, amazing reward)"); - - emoteTestMessages.emplace_back(R"(@badge-info=subscriber/3;badges=subscriber/3;color=#0000FF;display-name=Linkoping;emotes=25:40-44;flags=17-26:S.6;id=744f9c58-b180-4f46-bd9e-b515b5ef75c1;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1566335866017;turbo=0;user-id=91673457;user-type= :linkoping!linkoping@linkoping.tmi.twitch.tv PRIVMSG #pajlada :Då kan du begära skadestånd och förtal Kappa)"); - emoteTestMessages.emplace_back(R"(@badge-info=subscriber/1;badges=subscriber/0;color=;display-name=jhoelsc;emotes=301683486:46-58,60-72,74-86/301683544:88-100;flags=0-4:S.6;id=1f1afcdd-d94c-4699-b35f-d214deb1e11a;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1588640587462;turbo=0;user-id=505763008;user-type= :jhoelsc!jhoelsc@jhoelsc.tmi.twitch.tv PRIVMSG #pajlada :pensé que no habría directo que bueno que si staryuukiLove staryuukiLove staryuukiLove staryuukiBits)"); - emoteTestMessages.emplace_back(R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,16-23;flags=;id=97c28382-e8d2-45a0-bb5d-2305fc4ef139;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922036771;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm, Kreygasm)"); - emoteTestMessages.emplace_back(R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=25:24-28/41:6-13,15-22;flags=;id=5a36536b-a952-43f7-9c41-88c829371b7a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922039721;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm Kappa (no space then space))"); - emoteTestMessages.emplace_back(R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=25:6-10/1902:12-16/88:18-25;flags=;id=aed9e67e-f8cd-493e-aa6b-da054edd7292;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922042881;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kappa.Keepo.PogChamp.extratext (3 emotes with extra text))"); - emoteTestMessages.emplace_back(R"(@badge-info=;badges=moderator/1,partner/1;color=#5B99FF;display-name=StreamElements;emotes=86:30-39/822112:73-79;flags=22-27:S.5;id=03c3eec9-afd1-4858-a2e0-fccbf6ad8d1a;mod=1;room-id=11148817;subscriber=0;tmi-sent-ts=1588638345928;turbo=0;user-id=100135110;user-type=mod :streamelements!streamelements@streamelements.tmi.twitch.tv PRIVMSG #pajlada :╔ACTION A LOJA AINDA NÃO ESTÁ PRONTA BibleThump , AGUARDE... NOVIDADES EM BREVE FortOne╔)"); - emoteTestMessages.emplace_back(R"(@badge-info=subscriber/20;badges=moderator/1,subscriber/12;color=#19E6E6;display-name=randers;emotes=25:39-43;flags=;id=3ea97f01-abb2-4acf-bdb8-f52e79cd0324;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1588837097115;turbo=0;user-id=40286300;user-type=mod :randers!randers@randers.tmi.twitch.tv PRIVMSG #pajlada :Då kan du begära skadestånd och förtal Kappa)"); - emoteTestMessages.emplace_back(R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,15-22;flags=;id=a3196c7e-be4c-4b49-9c5a-8b8302b50c2a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922213730;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm (no space))"); - // clang-format on - actions.emplace("addMiscMessage", [=](std::vector) -> QString { - const auto &messages = miscMessages; + const auto &messages = getSampleMiscMessages(); static int index = 0; - auto app = getApp(); const auto &msg = messages[index++ % messages.size()]; - app->twitch->addFakeMessage(msg); + getApp()->twitch->addFakeMessage(msg); return ""; }); actions.emplace("addCheerMessage", [=](std::vector) -> QString { - const auto &messages = cheerMessages; + const auto &messages = getSampleCheerMessages(); static int index = 0; const auto &msg = messages[index++ % messages.size()]; getApp()->twitch->addFakeMessage(msg); @@ -271,11 +204,10 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions) }); actions.emplace("addLinkMessage", [=](std::vector) -> QString { - const auto &messages = linkMessages; + const auto &messages = getSampleLinkMessages(); static int index = 0; - auto app = getApp(); const auto &msg = messages[index++ % messages.size()]; - app->twitch->addFakeMessage(msg); + getApp()->twitch->addFakeMessage(msg); return ""; }); @@ -285,19 +217,21 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions) static bool alt = true; if (alt) { - auto oMessage = parsePubSubBaseMessage(channelRewardMessage); + auto oMessage = + parsePubSubBaseMessage(getSampleChannelRewardMessage()); auto oInnerMessage = oMessage->toInner() ->toInner(); - app->twitch->addFakeMessage(channelRewardIRCMessage); + app->twitch->addFakeMessage(getSampleChannelRewardIRCMessage()); app->twitch->pubsub->signals_.pointReward.redeemed.invoke( oInnerMessage->data.value("redemption").toObject()); alt = !alt; } else { - auto oMessage = parsePubSubBaseMessage(channelRewardMessage2); + auto oMessage = + parsePubSubBaseMessage(getSampleChannelRewardMessage2()); auto oInnerMessage = oMessage->toInner() ->toInner(); @@ -309,7 +243,7 @@ void Window::addDebugStuff(HotkeyController::HotkeyMap &actions) }); actions.emplace("addEmoteMessage", [=](std::vector) -> QString { - const auto &messages = emoteTestMessages; + const auto &messages = getSampleEmoteTestMessages(); static int index = 0; const auto &msg = messages[index++ % messages.size()]; getApp()->twitch->addFakeMessage(msg); From b198438f2568f124971053c7f804fd986dff68ba Mon Sep 17 00:00:00 2001 From: pajlada Date: Mon, 30 May 2022 01:43:13 +0200 Subject: [PATCH 16/18] Run httpbin docker image locally in CI (#3778) --- .github/workflows/test.yml | 2 ++ tests/README.md | 8 ++++++++ tests/src/NetworkRequest.cpp | 18 +++++++++++++----- 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 tests/README.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fe1e5ceda..8ba250038 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -83,8 +83,10 @@ jobs: - name: Test (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | + docker pull kennethreitz/httpbin docker pull ${{ env.TWITCH_PUBSUB_SERVER_IMAGE }} docker run --network=host --detach ${{ env.TWITCH_PUBSUB_SERVER_IMAGE }} + docker run -p 9051:80 --detach kennethreitz/httpbin ./bin/chatterino-test --platform minimal working-directory: build-test shell: bash diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..449e59013 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,8 @@ +To run tests all tests you will need to run the `kennethreitz/httpbin` and `ghcr.io/chatterino/twitch-pubsub-server-test:latest` docker images + +For example: + +```bash +docker run --network=host --detach ghcr.io/chatterino/twitch-pubsub-server-test:latest +docker run -p 9051:80 --detach kennethreitz/httpbin +``` diff --git a/tests/src/NetworkRequest.cpp b/tests/src/NetworkRequest.cpp index 8af91514f..96d62ca4b 100644 --- a/tests/src/NetworkRequest.cpp +++ b/tests/src/NetworkRequest.cpp @@ -13,9 +13,17 @@ using namespace chatterino; namespace { -static QString getStatusURL(int code) +// Change to http://httpbin.org if you don't want to run the docker image yourself to test this +const char *const HTTPBIN_BASE_URL = "http://127.0.0.1:9051"; + +QString getStatusURL(int code) { - return QString("http://httpbin.org/status/%1").arg(code); + return QString("%1/status/%2").arg(HTTPBIN_BASE_URL).arg(code); +} + +QString getDelayURL(int delay) +{ + return QString("%1/delay/%2").arg(HTTPBIN_BASE_URL).arg(delay); } } // namespace @@ -201,7 +209,7 @@ TEST(NetworkRequest, TimeoutTimingOut) { EXPECT_TRUE(NetworkManager::workerThread.isRunning()); - auto url = "http://httpbin.org/delay/5"; + auto url = getDelayURL(5); std::mutex mut; bool requestDone = false; @@ -248,7 +256,7 @@ TEST(NetworkRequest, TimeoutNotTimingOut) { EXPECT_TRUE(NetworkManager::workerThread.isRunning()); - auto url = "http://httpbin.org/delay/1"; + auto url = getDelayURL(1); std::mutex mut; bool requestDone = false; @@ -293,7 +301,7 @@ TEST(NetworkRequest, FinallyCallbackOnTimeout) { EXPECT_TRUE(NetworkManager::workerThread.isRunning()); - auto url = "http://httpbin.org/delay/5"; + auto url = getDelayURL(5); std::mutex mut; bool requestDone = false; From 64da34342c11f2c67046041857ac6cce8f34390a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 May 2022 03:05:39 +0200 Subject: [PATCH 17/18] Bump lib/magic_enum from `87190b8` to `6956c56` (#3783) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- lib/magic_enum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/magic_enum b/lib/magic_enum index 87190b811..6956c5603 160000 --- a/lib/magic_enum +++ b/lib/magic_enum @@ -1 +1 @@ -Subproject commit 87190b811c5d6bb90f970902bcda28740f6c07d3 +Subproject commit 6956c560330e21c7673ba8d5e43d53b71a7fce48 From e9e3e5a25a3d54c6d90b8e9bab350188bcbff32c Mon Sep 17 00:00:00 2001 From: pajlada Date: Mon, 30 May 2022 13:09:38 +0200 Subject: [PATCH 18/18] Use `setPlainText` when finishing a completion (#3785) --- src/widgets/splits/SplitInput.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index a0e73430f..d89041ab7 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -651,7 +651,8 @@ void SplitInput::insertCompletionText(const QString &input_) if (done) { auto cursor = edit.textCursor(); - edit.setText(text.remove(i, position - i + 1).insert(i, input)); + edit.setPlainText( + text.remove(i, position - i + 1).insert(i, input)); cursor.setPosition(i + input.size()); edit.setTextCursor(cursor);