mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Compare commits
7 commits
dabaa186cd
...
5eb669c0a9
Author | SHA1 | Date | |
---|---|---|---|
5eb669c0a9 | |||
55a12fa008 | |||
18c4815ad7 | |||
2ec8fa8723 | |||
45d2c292d0 | |||
867e3f3ab0 | |||
e35fabfabe |
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
- Major: Add option to show pronouns in user card. (#5442, #5583)
|
- Major: Add option to show pronouns in user card. (#5442, #5583)
|
||||||
- Major: Release plugins alpha. (#5288)
|
- Major: Release plugins alpha. (#5288)
|
||||||
- Major: Improve high-DPI support on Windows. (#4868, #5391, #5664)
|
- Major: Improve high-DPI support on Windows. (#4868, #5391, #5664, #5666)
|
||||||
- Major: Added transparent overlay window (default keybind: <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>N</kbd>). (#4746, #5643, #5659)
|
- Major: Added transparent overlay window (default keybind: <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>N</kbd>). (#4746, #5643, #5659)
|
||||||
- Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530)
|
- Minor: Removed the Ctrl+Shift+L hotkey for toggling the "live only" tab visibility state. (#5530)
|
||||||
- Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625)
|
- Minor: Add support for Shared Chat messages. Shared chat messages can be filtered with the `flags.shared` filter variable, or with search using `is:shared`. Some messages like subscriptions are filtered on purpose to avoid confusion for the broadcaster. If you have both channels participating in Shared Chat open, only one of the message triggering your highlight will trigger. (#5606, #5625, #5661)
|
||||||
- Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530)
|
- Minor: Moved tab visibility control to a submenu, without any toggle actions. (#5530)
|
||||||
- Minor: Add option to customise Moderation buttons with images. (#5369)
|
- Minor: Add option to customise Moderation buttons with images. (#5369)
|
||||||
- Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300)
|
- Minor: Colored usernames now update on the fly when changing the "Color @usernames" setting. (#5300)
|
||||||
|
@ -104,13 +104,14 @@
|
||||||
- Dev: Added more tests for input completion. (#5604)
|
- Dev: Added more tests for input completion. (#5604)
|
||||||
- Dev: Refactored legacy Unicode zero-width-joiner replacement. (#5594)
|
- Dev: Refactored legacy Unicode zero-width-joiner replacement. (#5594)
|
||||||
- Dev: The JSON output when copying a message (<kbd>SHIFT</kbd> + right-click) is now more extensive. (#5600)
|
- Dev: The JSON output when copying a message (<kbd>SHIFT</kbd> + right-click) is now more extensive. (#5600)
|
||||||
- Dev: Added more tests for message building. (#5598, #5654, #5656)
|
- Dev: Added more tests for message building. (#5598, #5654, #5656, #5671)
|
||||||
- Dev: Twitch messages are now sent using Twitch's Helix API instead of IRC by default. (#5607)
|
- Dev: Twitch messages are now sent using Twitch's Helix API instead of IRC by default. (#5607)
|
||||||
- Dev: `GIFTimer` is no longer initialized in tests. (#5608)
|
- Dev: `GIFTimer` is no longer initialized in tests. (#5608)
|
||||||
- Dev: Emojis now use flags instead of a set of strings for capabilities. (#5616)
|
- Dev: Emojis now use flags instead of a set of strings for capabilities. (#5616)
|
||||||
- Dev: Move plugins to Sol2. (#5622)
|
- Dev: Move plugins to Sol2. (#5622)
|
||||||
- Dev: Refactored static `MessageBuilder` helpers to standalone functions. (#5652)
|
- Dev: Refactored static `MessageBuilder` helpers to standalone functions. (#5652)
|
||||||
- Dev: Decoupled reply parsing from `MessageBuilder`. (#5660)
|
- Dev: Decoupled reply parsing from `MessageBuilder`. (#5660, #5668)
|
||||||
|
- Dev: Refactored IRC message building. (#5663)
|
||||||
|
|
||||||
## 2.5.1
|
## 2.5.1
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ set(benchmark_SOURCES
|
||||||
resources/bench.qrc
|
resources/bench.qrc
|
||||||
|
|
||||||
src/Emojis.cpp
|
src/Emojis.cpp
|
||||||
src/Highlights.cpp
|
|
||||||
src/FormatTime.cpp
|
src/FormatTime.cpp
|
||||||
src/Helpers.cpp
|
src/Helpers.cpp
|
||||||
src/LimitedQueue.cpp
|
src/LimitedQueue.cpp
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
#include "Application.hpp"
|
|
||||||
#include "common/Channel.hpp"
|
|
||||||
#include "controllers/accounts/AccountController.hpp"
|
|
||||||
#include "controllers/highlights/HighlightController.hpp"
|
|
||||||
#include "controllers/highlights/HighlightPhrase.hpp"
|
|
||||||
#include "messages/Message.hpp"
|
|
||||||
#include "messages/MessageBuilder.hpp"
|
|
||||||
#include "mocks/BaseApplication.hpp"
|
|
||||||
#include "mocks/UserData.hpp"
|
|
||||||
#include "util/Helpers.hpp"
|
|
||||||
|
|
||||||
#include <benchmark/benchmark.h>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTemporaryDir>
|
|
||||||
|
|
||||||
using namespace chatterino;
|
|
||||||
|
|
||||||
class BenchmarkMessageBuilder : public MessageBuilder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit BenchmarkMessageBuilder(
|
|
||||||
Channel *_channel, const Communi::IrcPrivateMessage *_ircMessage,
|
|
||||||
const MessageParseArgs &_args)
|
|
||||||
: MessageBuilder(_channel, _ircMessage, _args)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual MessagePtr build()
|
|
||||||
{
|
|
||||||
// PARSE
|
|
||||||
this->parse();
|
|
||||||
this->usernameColor_ = getRandomColor(this->ircMessage->nick());
|
|
||||||
|
|
||||||
// words
|
|
||||||
// this->addWords(this->originalMessage_.split(' '));
|
|
||||||
|
|
||||||
this->message().messageText = this->originalMessage_;
|
|
||||||
this->message().searchText = this->message().localizedName + " " +
|
|
||||||
this->userName + ": " +
|
|
||||||
this->originalMessage_;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench()
|
|
||||||
{
|
|
||||||
this->parseHighlights();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MockApplication : public mock::BaseApplication
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MockApplication()
|
|
||||||
: highlights(this->settings, &this->accounts)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountController *getAccounts() override
|
|
||||||
{
|
|
||||||
return &this->accounts;
|
|
||||||
}
|
|
||||||
HighlightController *getHighlights() override
|
|
||||||
{
|
|
||||||
return &this->highlights;
|
|
||||||
}
|
|
||||||
|
|
||||||
IUserDataController *getUserData() override
|
|
||||||
{
|
|
||||||
return &this->userData;
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountController accounts;
|
|
||||||
HighlightController highlights;
|
|
||||||
mock::UserDataController userData;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void BM_HighlightTest(benchmark::State &state)
|
|
||||||
{
|
|
||||||
MockApplication mockApplication;
|
|
||||||
|
|
||||||
std::string message =
|
|
||||||
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))";
|
|
||||||
auto ircMessage = Communi::IrcMessage::fromData(message.c_str(), nullptr);
|
|
||||||
auto privMsg = dynamic_cast<Communi::IrcPrivateMessage *>(ircMessage);
|
|
||||||
assert(privMsg != nullptr);
|
|
||||||
MessageParseArgs args;
|
|
||||||
auto emptyChannel = Channel::getEmpty();
|
|
||||||
|
|
||||||
for (auto _ : state)
|
|
||||||
{
|
|
||||||
state.PauseTiming();
|
|
||||||
BenchmarkMessageBuilder b(emptyChannel.get(), privMsg, args);
|
|
||||||
|
|
||||||
b.build();
|
|
||||||
state.ResumeTiming();
|
|
||||||
|
|
||||||
b.bench();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BENCHMARK(BM_HighlightTest);
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "common/Args.hpp"
|
#include "common/Args.hpp"
|
||||||
#include "mocks/DisabledStreamerMode.hpp"
|
#include "mocks/DisabledStreamerMode.hpp"
|
||||||
#include "mocks/EmptyApplication.hpp"
|
#include "mocks/EmptyApplication.hpp"
|
||||||
|
#include "mocks/TwitchUsers.hpp"
|
||||||
#include "providers/bttv/BttvLiveUpdates.hpp"
|
#include "providers/bttv/BttvLiveUpdates.hpp"
|
||||||
#include "singletons/Fonts.hpp"
|
#include "singletons/Fonts.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
|
@ -55,6 +56,11 @@ public:
|
||||||
return &this->fonts;
|
return &this->fonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ITwitchUsers *getTwitchUsers() override
|
||||||
|
{
|
||||||
|
return &this->twitchUsers;
|
||||||
|
}
|
||||||
|
|
||||||
BttvLiveUpdates *getBttvLiveUpdates() override
|
BttvLiveUpdates *getBttvLiveUpdates() override
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -71,6 +77,7 @@ public:
|
||||||
DisabledStreamerMode streamerMode;
|
DisabledStreamerMode streamerMode;
|
||||||
Theme theme;
|
Theme theme;
|
||||||
Fonts fonts;
|
Fonts fonts;
|
||||||
|
TwitchUsers twitchUsers;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino::mock
|
} // namespace chatterino::mock
|
||||||
|
|
24
mocks/include/mocks/TwitchUsers.hpp
Normal file
24
mocks/include/mocks/TwitchUsers.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "providers/twitch/TwitchUser.hpp"
|
||||||
|
#include "providers/twitch/TwitchUsers.hpp"
|
||||||
|
|
||||||
|
namespace chatterino::mock {
|
||||||
|
|
||||||
|
class TwitchUsers : public ITwitchUsers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TwitchUsers() = default;
|
||||||
|
|
||||||
|
std::shared_ptr<TwitchUser> resolveID(const UserId &id)
|
||||||
|
{
|
||||||
|
TwitchUser u = {
|
||||||
|
.id = id.string,
|
||||||
|
.name = {},
|
||||||
|
.displayName = {},
|
||||||
|
};
|
||||||
|
return std::make_shared<TwitchUser>(u);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino::mock
|
BIN
resources/twitch/sharedChat.png
Normal file
BIN
resources/twitch/sharedChat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
|
@ -22,6 +22,7 @@ class ScrollbarHighlight;
|
||||||
|
|
||||||
struct Message;
|
struct Message;
|
||||||
using MessagePtr = std::shared_ptr<const Message>;
|
using MessagePtr = std::shared_ptr<const Message>;
|
||||||
|
using MessagePtrMut = std::shared_ptr<Message>;
|
||||||
struct Message {
|
struct Message {
|
||||||
Message();
|
Message();
|
||||||
~Message();
|
~Message();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
#include <tuple>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -31,6 +29,7 @@ struct AutomodUserAction;
|
||||||
struct AutomodInfoAction;
|
struct AutomodInfoAction;
|
||||||
struct Message;
|
struct Message;
|
||||||
using MessagePtr = std::shared_ptr<const Message>;
|
using MessagePtr = std::shared_ptr<const Message>;
|
||||||
|
using MessagePtrMut = std::shared_ptr<Message>;
|
||||||
|
|
||||||
class MessageElement;
|
class MessageElement;
|
||||||
class TextElement;
|
class TextElement;
|
||||||
|
@ -68,6 +67,7 @@ struct LiveUpdatesUpdateEmoteSetMessageTag {
|
||||||
struct ImageUploaderResultTag {
|
struct ImageUploaderResultTag {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
const SystemMessageTag systemMessage{};
|
const SystemMessageTag systemMessage{};
|
||||||
const RaidEntryMessageTag raidEntryMessage{};
|
const RaidEntryMessageTag raidEntryMessage{};
|
||||||
const TimeoutMessageTag timeoutMessage{};
|
const TimeoutMessageTag timeoutMessage{};
|
||||||
|
@ -79,6 +79,7 @@ const LiveUpdatesUpdateEmoteSetMessageTag liveUpdatesUpdateEmoteSetMessage{};
|
||||||
// This signifies that you want to construct a message containing the result of
|
// This signifies that you want to construct a message containing the result of
|
||||||
// a successful image upload.
|
// a successful image upload.
|
||||||
const ImageUploaderResultTag imageUploaderResultMessage{};
|
const ImageUploaderResultTag imageUploaderResultMessage{};
|
||||||
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
|
||||||
MessagePtr makeSystemMessage(const QString &text);
|
MessagePtr makeSystemMessage(const QString &text);
|
||||||
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
|
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
|
||||||
|
@ -90,26 +91,22 @@ struct MessageParseArgs {
|
||||||
bool trimSubscriberUsername = false;
|
bool trimSubscriberUsername = false;
|
||||||
bool isStaffOrBroadcaster = false;
|
bool isStaffOrBroadcaster = false;
|
||||||
bool isSubscriptionMessage = false;
|
bool isSubscriptionMessage = false;
|
||||||
|
bool allowIgnore = true;
|
||||||
|
bool isAction = false;
|
||||||
QString channelPointRewardId = "";
|
QString channelPointRewardId = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HighlightAlert {
|
||||||
|
QUrl customSound;
|
||||||
|
bool playSound = false;
|
||||||
|
bool windowAlert = false;
|
||||||
|
};
|
||||||
class MessageBuilder
|
class MessageBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Build a message without a base IRC message.
|
/// Build a message without a base IRC message.
|
||||||
MessageBuilder();
|
MessageBuilder();
|
||||||
|
|
||||||
/// Build a message based on an incoming IRC PRIVMSG
|
|
||||||
explicit MessageBuilder(Channel *_channel,
|
|
||||||
const Communi::IrcPrivateMessage *_ircMessage,
|
|
||||||
const MessageParseArgs &_args);
|
|
||||||
|
|
||||||
/// Build a message based on an incoming IRC message (e.g. notice)
|
|
||||||
explicit MessageBuilder(Channel *_channel,
|
|
||||||
const Communi::IrcMessage *_ircMessage,
|
|
||||||
const MessageParseArgs &_args, QString content,
|
|
||||||
bool isAction);
|
|
||||||
|
|
||||||
MessageBuilder(SystemMessageTag, const QString &text,
|
MessageBuilder(SystemMessageTag, const QString &text,
|
||||||
const QTime &time = QTime::currentTime());
|
const QTime &time = QTime::currentTime());
|
||||||
MessageBuilder(RaidEntryMessageTag, const QString &text,
|
MessageBuilder(RaidEntryMessageTag, const QString &text,
|
||||||
|
@ -157,17 +154,10 @@ public:
|
||||||
|
|
||||||
~MessageBuilder() = default;
|
~MessageBuilder() = default;
|
||||||
|
|
||||||
QString userName;
|
|
||||||
|
|
||||||
/// The Twitch Channel the message was received in
|
|
||||||
TwitchChannel *twitchChannel = nullptr;
|
|
||||||
/// The Twitch Channel the message was sent in, according to the Shared Chat feature
|
|
||||||
TwitchChannel *sourceChannel = nullptr;
|
|
||||||
|
|
||||||
Message *operator->();
|
Message *operator->();
|
||||||
Message &message();
|
Message &message();
|
||||||
MessagePtr release();
|
MessagePtrMut release();
|
||||||
std::weak_ptr<Message> weakOf();
|
std::weak_ptr<const Message> weakOf();
|
||||||
|
|
||||||
void append(std::unique_ptr<MessageElement> element);
|
void append(std::unique_ptr<MessageElement> element);
|
||||||
void addLink(const linkparser::Parsed &parsedLink, const QString &source);
|
void addLink(const linkparser::Parsed &parsedLink, const QString &source);
|
||||||
|
@ -184,14 +174,8 @@ public:
|
||||||
return pointer;
|
return pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool isIgnored() const;
|
static void triggerHighlights(const Channel *channel,
|
||||||
bool isIgnoredReply() const;
|
const HighlightAlert &alert);
|
||||||
void triggerHighlights();
|
|
||||||
MessagePtr build();
|
|
||||||
|
|
||||||
void setThread(std::shared_ptr<MessageThread> thread);
|
|
||||||
void setParent(MessagePtr parent);
|
|
||||||
void setMessageOffset(int offset);
|
|
||||||
|
|
||||||
void appendChannelPointRewardMessage(const ChannelPointReward &reward,
|
void appendChannelPointRewardMessage(const ChannelPointReward &reward,
|
||||||
bool isMod, bool isBroadcaster);
|
bool isMod, bool isBroadcaster);
|
||||||
|
@ -231,96 +215,124 @@ public:
|
||||||
static MessagePtr makeLowTrustUpdateMessage(
|
static MessagePtr makeLowTrustUpdateMessage(
|
||||||
const PubSubLowTrustUsersMessage &action);
|
const PubSubLowTrustUsersMessage &action);
|
||||||
|
|
||||||
protected:
|
/// @brief Builds a message out of an `ircMessage`.
|
||||||
void addTextOrEmoji(EmotePtr emote);
|
///
|
||||||
void addTextOrEmoji(const QString &string_);
|
/// Building a message won't cause highlights to be triggered. They will
|
||||||
|
/// only be parsed. To trigger highlights (play sound etc.), use
|
||||||
|
/// triggerHighlights().
|
||||||
|
///
|
||||||
|
/// @param channel The channel this message was sent to. Must not be
|
||||||
|
/// `nullptr`.
|
||||||
|
/// @param ircMessage The original message. This can be any message
|
||||||
|
/// (PRIVMSG, USERNOTICE, etc.). Its content is not
|
||||||
|
/// accessed through this parameter but through `content`,
|
||||||
|
/// as the content might be inside a tag (e.g. gifts in a
|
||||||
|
/// USERNOTICE).
|
||||||
|
/// @param args Arguments from parsing a chat message.
|
||||||
|
/// @param content The message text. This isn't always the entire text. In
|
||||||
|
/// replies, the leading mention can be cut off.
|
||||||
|
/// See `messageOffset`.
|
||||||
|
/// @param messageOffset Starting offset to be used on index-based
|
||||||
|
/// operations on `content` such as parsing emotes.
|
||||||
|
/// For example:
|
||||||
|
/// ircMessage = "@hi there"
|
||||||
|
/// content = "there"
|
||||||
|
/// messageOffset_ = 4
|
||||||
|
/// The index 6 would resolve to 6 - 4 = 2 => 'e'
|
||||||
|
/// @param thread The reply thread this message is part of. If there's no
|
||||||
|
/// thread, this is an empty `shared_ptr`.
|
||||||
|
/// @param parent The direct parent this message is replying to. This does
|
||||||
|
/// not need to be the `thread`s root. If this message isn't
|
||||||
|
/// replying to anything, this is an empty `shared_ptr`.
|
||||||
|
///
|
||||||
|
/// @returns The built message and a highlight result. If the message is
|
||||||
|
/// ignored (e.g. from a blocked user), then the returned pointer
|
||||||
|
/// will be en empty `shared_ptr`.
|
||||||
|
static std::pair<MessagePtrMut, HighlightAlert> makeIrcMessage(
|
||||||
|
Channel *channel, const Communi::IrcMessage *ircMessage,
|
||||||
|
const MessageParseArgs &args, QString content,
|
||||||
|
QString::size_type messageOffset,
|
||||||
|
const std::shared_ptr<MessageThread> &thread = {},
|
||||||
|
const MessagePtr &parent = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct TextState {
|
||||||
|
TwitchChannel *twitchChannel = nullptr;
|
||||||
|
bool hasBits = false;
|
||||||
|
bool bitsStacked = false;
|
||||||
|
int bitsLeft = 0;
|
||||||
|
};
|
||||||
|
void addEmoji(const EmotePtr &emote);
|
||||||
|
void addTextOrEmote(TextState &state, QString string);
|
||||||
|
|
||||||
|
Outcome tryAppendCheermote(TextState &state, const QString &string);
|
||||||
|
Outcome tryAppendEmote(TwitchChannel *twitchChannel, const EmoteName &name);
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
MessageElement &back();
|
MessageElement &back();
|
||||||
std::unique_ptr<MessageElement> releaseBack();
|
std::unique_ptr<MessageElement> releaseBack();
|
||||||
|
|
||||||
MessageColor textColor_ = MessageColor::Text;
|
|
||||||
|
|
||||||
// Helper method that emplaces some text stylized as system text
|
// Helper method that emplaces some text stylized as system text
|
||||||
// and then appends that text to the QString parameter "toUpdate".
|
// and then appends that text to the QString parameter "toUpdate".
|
||||||
// Returns the TextElement that was emplaced.
|
// Returns the TextElement that was emplaced.
|
||||||
TextElement *emplaceSystemTextAndUpdate(const QString &text,
|
TextElement *emplaceSystemTextAndUpdate(const QString &text,
|
||||||
QString &toUpdate);
|
QString &toUpdate);
|
||||||
|
|
||||||
std::shared_ptr<Message> message_;
|
|
||||||
|
|
||||||
void parse();
|
void parse();
|
||||||
void parseUsernameColor();
|
void parseUsernameColor(const QVariantMap &tags, const QString &userID);
|
||||||
void parseUsername();
|
void parseUsername(const Communi::IrcMessage *ircMessage,
|
||||||
void parseMessageID();
|
TwitchChannel *twitchChannel,
|
||||||
void parseRoomID();
|
bool trimSubscriberUsername);
|
||||||
|
void parseMessageID(const QVariantMap &tags);
|
||||||
|
|
||||||
|
/// Parses the room-ID this message was received in
|
||||||
|
///
|
||||||
|
/// @returns The room-ID
|
||||||
|
static QString parseRoomID(const QVariantMap &tags,
|
||||||
|
TwitchChannel *twitchChannel);
|
||||||
|
|
||||||
|
/// Parses the shared-chat information from this message.
|
||||||
|
///
|
||||||
|
/// @param tags The tags of the received message
|
||||||
|
/// @param twitchChannel The channel this message was received in
|
||||||
|
/// @returns The source channel - the channel this message originated from.
|
||||||
|
/// If there's no channel currently open, @a twitchChannel is
|
||||||
|
/// returned.
|
||||||
|
TwitchChannel *parseSharedChatInfo(const QVariantMap &tags,
|
||||||
|
TwitchChannel *twitchChannel);
|
||||||
|
|
||||||
// Parse & build thread information into the message
|
// Parse & build thread information into the message
|
||||||
// Will read information from thread_ or from IRC tags
|
// Will read information from thread_ or from IRC tags
|
||||||
void parseThread();
|
void parseThread(const QString &messageContent, const QVariantMap &tags,
|
||||||
|
const Channel *channel,
|
||||||
|
const std::shared_ptr<MessageThread> &thread,
|
||||||
|
const MessagePtr &parent);
|
||||||
// parseHighlights only updates the visual state of the message, but leaves the playing of alerts and sounds to the triggerHighlights function
|
// parseHighlights only updates the visual state of the message, but leaves the playing of alerts and sounds to the triggerHighlights function
|
||||||
void parseHighlights();
|
HighlightAlert parseHighlights(const QVariantMap &tags,
|
||||||
void appendChannelName();
|
const QString &originalMessage,
|
||||||
void appendUsername();
|
const MessageParseArgs &args);
|
||||||
|
|
||||||
/// Return the Twitch Channel this message originated from
|
void appendChannelName(const Channel *channel);
|
||||||
///
|
void appendUsername(const QVariantMap &tags, const MessageParseArgs &args);
|
||||||
/// Useful to handle messages from the "Shared Chat" feature
|
|
||||||
///
|
|
||||||
/// Can return nullptr
|
|
||||||
const TwitchChannel *getSourceChannel() const;
|
|
||||||
|
|
||||||
std::tuple<std::optional<EmotePtr>, MessageElementFlags, bool> parseEmote(
|
|
||||||
const EmoteName &name) const;
|
|
||||||
Outcome tryAppendEmote(const EmoteName &name);
|
|
||||||
|
|
||||||
void addWords(const QStringList &words,
|
void addWords(const QStringList &words,
|
||||||
const std::vector<TwitchEmoteOccurrence> &twitchEmotes);
|
const std::vector<TwitchEmoteOccurrence> &twitchEmotes,
|
||||||
|
TextState &state);
|
||||||
|
|
||||||
void appendTwitchBadges();
|
void appendTwitchBadges(const QVariantMap &tags,
|
||||||
void appendChatterinoBadges();
|
TwitchChannel *twitchChannel);
|
||||||
void appendFfzBadges();
|
void appendChatterinoBadges(const QString &userID);
|
||||||
void appendSeventvBadges();
|
void appendFfzBadges(TwitchChannel *twitchChannel, const QString &userID);
|
||||||
Outcome tryParseCheermote(const QString &string);
|
void appendSeventvBadges(const QString &userID);
|
||||||
|
|
||||||
bool shouldAddModerationElements() const;
|
[[nodiscard]] static bool isIgnored(const QString &originalMessage,
|
||||||
|
const QString &userID,
|
||||||
|
const Channel *channel);
|
||||||
|
|
||||||
QString roomID_;
|
std::shared_ptr<Message> message_;
|
||||||
bool hasBits_ = false;
|
MessageColor textColor_ = MessageColor::Text;
|
||||||
QString bits;
|
|
||||||
int bitsLeft{};
|
|
||||||
bool bitsStacked = false;
|
|
||||||
bool historicalMessage_ = false;
|
|
||||||
std::shared_ptr<MessageThread> thread_;
|
|
||||||
MessagePtr parent_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starting offset to be used on index-based operations on `originalMessage_`.
|
|
||||||
*
|
|
||||||
* For example:
|
|
||||||
* originalMessage_ = "there"
|
|
||||||
* messageOffset_ = 4
|
|
||||||
* (the irc message is "hey there")
|
|
||||||
*
|
|
||||||
* then the index 6 would resolve to 6 - 4 = 2 => 'e'
|
|
||||||
*/
|
|
||||||
int messageOffset_ = 0;
|
|
||||||
|
|
||||||
QString userId_;
|
|
||||||
bool senderIsBroadcaster{};
|
|
||||||
|
|
||||||
Channel *channel = nullptr;
|
|
||||||
const Communi::IrcMessage *ircMessage;
|
|
||||||
MessageParseArgs args;
|
|
||||||
const QVariantMap tags;
|
|
||||||
QString originalMessage_;
|
|
||||||
|
|
||||||
const bool action_{};
|
|
||||||
|
|
||||||
QColor usernameColor_ = {153, 153, 153};
|
QColor usernameColor_ = {153, 153, 153};
|
||||||
|
|
||||||
bool highlightAlert_ = false;
|
|
||||||
bool highlightSound_ = false;
|
|
||||||
std::optional<QUrl> highlightSoundCustomUrl_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -66,6 +66,10 @@ enum class MessageElementFlag : int64_t {
|
||||||
BitsStatic = (1LL << 11),
|
BitsStatic = (1LL << 11),
|
||||||
BitsAnimated = (1LL << 12),
|
BitsAnimated = (1LL << 12),
|
||||||
|
|
||||||
|
// Slot 0: Twitch
|
||||||
|
// - Shared Channel indicator badge
|
||||||
|
BadgeSharedChannel = (1LL << 37),
|
||||||
|
|
||||||
// Slot 1: Twitch
|
// Slot 1: Twitch
|
||||||
// - Staff badge
|
// - Staff badge
|
||||||
// - Admin badge
|
// - Admin badge
|
||||||
|
@ -119,7 +123,7 @@ enum class MessageElementFlag : int64_t {
|
||||||
|
|
||||||
Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
|
Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
|
||||||
BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV |
|
BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV |
|
||||||
BadgeFfz,
|
BadgeFfz | BadgeSharedChannel,
|
||||||
|
|
||||||
ChannelName = (1LL << 20),
|
ChannelName = (1LL << 20),
|
||||||
|
|
||||||
|
|
|
@ -123,47 +123,34 @@ int stripLeadingReplyMention(const QVariantMap &tags, QString &content)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool shouldHighlightReplyThread(
|
void checkThreadSubscription(const QVariantMap &tags,
|
||||||
const QVariantMap &tags, const QString &senderLogin,
|
const QString &senderLogin,
|
||||||
std::shared_ptr<MessageThread> &thread, bool isNew)
|
std::shared_ptr<MessageThread> &thread)
|
||||||
{
|
{
|
||||||
const auto ¤tLogin =
|
if (thread->subscribed() || thread->unsubscribed())
|
||||||
getApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
|
||||||
|
|
||||||
if (thread->subscribed())
|
|
||||||
{
|
{
|
||||||
return true;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (thread->unsubscribed())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getSettings()->autoSubToParticipatedThreads)
|
if (getSettings()->autoSubToParticipatedThreads)
|
||||||
{
|
{
|
||||||
if (isNew)
|
const auto ¤tLogin =
|
||||||
{
|
getApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
||||||
if (const auto it = tags.find("reply-parent-user-login");
|
|
||||||
it != tags.end())
|
|
||||||
{
|
|
||||||
auto name = it.value().toString();
|
|
||||||
if (name == currentLogin)
|
|
||||||
{
|
|
||||||
thread->markSubscribed();
|
|
||||||
return true; // already marked as participated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (senderLogin == currentLogin)
|
if (senderLogin == currentLogin)
|
||||||
{
|
{
|
||||||
thread->markSubscribed();
|
thread->markSubscribed();
|
||||||
// don't set the highlight here
|
}
|
||||||
|
else if (const auto it = tags.find("reply-parent-user-login");
|
||||||
|
it != tags.end())
|
||||||
|
{
|
||||||
|
auto name = it.value().toString();
|
||||||
|
if (name == currentLogin)
|
||||||
|
{
|
||||||
|
thread->markSubscribed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelPtr channelOrEmptyByTarget(const QString &target,
|
ChannelPtr channelOrEmptyByTarget(const QString &target,
|
||||||
|
@ -243,7 +230,6 @@ QMap<QString, QString> parseBadges(const QString &badgesString)
|
||||||
struct ReplyContext {
|
struct ReplyContext {
|
||||||
std::shared_ptr<MessageThread> thread;
|
std::shared_ptr<MessageThread> thread;
|
||||||
MessagePtr parent;
|
MessagePtr parent;
|
||||||
bool highlight = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] ReplyContext getReplyContext(
|
[[nodiscard]] ReplyContext getReplyContext(
|
||||||
|
@ -265,8 +251,7 @@ struct ReplyContext {
|
||||||
if (owned)
|
if (owned)
|
||||||
{
|
{
|
||||||
// Thread already exists (has a reply)
|
// Thread already exists (has a reply)
|
||||||
ctx.highlight = shouldHighlightReplyThread(
|
checkThreadSubscription(tags, message->nick(), owned);
|
||||||
tags, message->nick(), owned, false);
|
|
||||||
ctx.thread = owned;
|
ctx.thread = owned;
|
||||||
rootThread = owned;
|
rootThread = owned;
|
||||||
}
|
}
|
||||||
|
@ -301,8 +286,7 @@ struct ReplyContext {
|
||||||
{
|
{
|
||||||
std::shared_ptr<MessageThread> newThread =
|
std::shared_ptr<MessageThread> newThread =
|
||||||
std::make_shared<MessageThread>(foundMessage);
|
std::make_shared<MessageThread>(foundMessage);
|
||||||
ctx.highlight = shouldHighlightReplyThread(
|
checkThreadSubscription(tags, message->nick(), newThread);
|
||||||
tags, message->nick(), newThread, true);
|
|
||||||
|
|
||||||
ctx.thread = newThread;
|
ctx.thread = newThread;
|
||||||
rootThread = newThread;
|
rootThread = newThread;
|
||||||
|
@ -508,15 +492,20 @@ std::vector<MessagePtr> parseUserNoticeMessage(Channel *channel,
|
||||||
{
|
{
|
||||||
MessageParseArgs args;
|
MessageParseArgs args;
|
||||||
args.trimSubscriberUsername = true;
|
args.trimSubscriberUsername = true;
|
||||||
|
args.allowIgnore = false;
|
||||||
|
|
||||||
MessageBuilder builder(channel, message, args, content, false);
|
auto [built, highlight] = MessageBuilder::makeIrcMessage(
|
||||||
builder->flags.set(MessageFlag::Subscription);
|
channel, message, args, content, 0);
|
||||||
builder->flags.unset(MessageFlag::Highlighted);
|
if (built)
|
||||||
if (mirrored)
|
|
||||||
{
|
{
|
||||||
builder->flags.set(MessageFlag::SharedMessage);
|
built->flags.set(MessageFlag::Subscription);
|
||||||
|
built->flags.unset(MessageFlag::Highlighted);
|
||||||
|
if (mirrored)
|
||||||
|
{
|
||||||
|
built->flags.set(MessageFlag::SharedMessage);
|
||||||
|
}
|
||||||
|
builtMessages.emplace_back(std::move(built));
|
||||||
}
|
}
|
||||||
builtMessages.emplace_back(builder.build());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -661,12 +650,13 @@ std::vector<MessagePtr> parsePrivMessage(Channel *channel,
|
||||||
|
|
||||||
std::vector<MessagePtr> builtMessages;
|
std::vector<MessagePtr> builtMessages;
|
||||||
MessageParseArgs args;
|
MessageParseArgs args;
|
||||||
MessageBuilder builder(channel, message, args, message->content(),
|
args.isAction = message->isAction();
|
||||||
message->isAction());
|
auto [built, alert] = MessageBuilder::makeIrcMessage(channel, message, args,
|
||||||
if (!builder.isIgnored())
|
message->content(), 0);
|
||||||
|
if (built)
|
||||||
{
|
{
|
||||||
builtMessages.emplace_back(builder.build());
|
builtMessages.emplace_back(std::move(built));
|
||||||
builder.triggerHighlights();
|
MessageBuilder::triggerHighlights(channel, alert);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builtMessages;
|
return builtMessages;
|
||||||
|
@ -709,22 +699,17 @@ std::vector<MessagePtr> IrcMessageHandler::parseMessageWithReply(
|
||||||
{
|
{
|
||||||
args.channelPointRewardId = it.value().toString();
|
args.channelPointRewardId = it.value().toString();
|
||||||
}
|
}
|
||||||
MessageBuilder builder(channel, message, args, content,
|
args.isAction = privMsg->isAction();
|
||||||
privMsg->isAction());
|
|
||||||
builder.setMessageOffset(messageOffset);
|
|
||||||
|
|
||||||
auto replyCtx = getReplyContext(tc, message, otherLoaded);
|
auto replyCtx = getReplyContext(tc, message, otherLoaded);
|
||||||
builder.setThread(std::move(replyCtx.thread));
|
auto [built, alert] = MessageBuilder::makeIrcMessage(
|
||||||
builder.setParent(std::move(replyCtx.parent));
|
channel, message, args, content, messageOffset, replyCtx.thread,
|
||||||
if (replyCtx.highlight)
|
replyCtx.parent);
|
||||||
{
|
|
||||||
builder.message().flags.set(MessageFlag::SubscribedThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!builder.isIgnored())
|
if (built)
|
||||||
{
|
{
|
||||||
builtMessages.emplace_back(builder.build());
|
builtMessages.emplace_back(built);
|
||||||
builder.triggerHighlights();
|
MessageBuilder::triggerHighlights(channel, alert);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message->tags().contains(u"pinned-chat-paid-amount"_s))
|
if (message->tags().contains(u"pinned-chat-paid-amount"_s))
|
||||||
|
@ -1016,20 +1001,18 @@ void IrcMessageHandler::handleWhisperMessage(Communi::IrcMessage *ircMessage)
|
||||||
|
|
||||||
auto *c = getApp()->getTwitch()->getWhispersChannel().get();
|
auto *c = getApp()->getTwitch()->getWhispersChannel().get();
|
||||||
|
|
||||||
MessageBuilder builder(c, ircMessage, args,
|
auto [message, alert] = MessageBuilder::makeIrcMessage(
|
||||||
unescapeZeroWidthJoiner(ircMessage->parameter(1)),
|
c, ircMessage, args, unescapeZeroWidthJoiner(ircMessage->parameter(1)),
|
||||||
false);
|
0);
|
||||||
|
if (!message)
|
||||||
if (builder.isIgnored())
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder->flags.set(MessageFlag::Whisper);
|
message->flags.set(MessageFlag::Whisper);
|
||||||
MessagePtr message = builder.build();
|
MessageBuilder::triggerHighlights(c, alert);
|
||||||
builder.triggerHighlights();
|
|
||||||
|
|
||||||
getApp()->getTwitch()->setLastUserThatWhisperedMe(builder.userName);
|
getApp()->getTwitch()->setLastUserThatWhisperedMe(message->loginName);
|
||||||
|
|
||||||
if (message->flags.has(MessageFlag::ShowInMentions))
|
if (message->flags.has(MessageFlag::ShowInMentions))
|
||||||
{
|
{
|
||||||
|
@ -1504,6 +1487,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
{
|
{
|
||||||
args.isStaffOrBroadcaster = true;
|
args.isStaffOrBroadcaster = true;
|
||||||
}
|
}
|
||||||
|
args.isAction = isAction;
|
||||||
|
|
||||||
auto *channel = dynamic_cast<TwitchChannel *>(chan.get());
|
auto *channel = dynamic_cast<TwitchChannel *>(chan.get());
|
||||||
|
|
||||||
|
@ -1548,8 +1532,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
{
|
{
|
||||||
// Thread already exists (has a reply)
|
// Thread already exists (has a reply)
|
||||||
auto thread = threadIt->second.lock();
|
auto thread = threadIt->second.lock();
|
||||||
replyCtx.highlight = shouldHighlightReplyThread(
|
checkThreadSubscription(tags, message->nick(), thread);
|
||||||
tags, message->nick(), thread, false);
|
|
||||||
replyCtx.thread = thread;
|
replyCtx.thread = thread;
|
||||||
rootThread = thread;
|
rootThread = thread;
|
||||||
}
|
}
|
||||||
|
@ -1561,8 +1544,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
{
|
{
|
||||||
// Found root reply message
|
// Found root reply message
|
||||||
auto newThread = std::make_shared<MessageThread>(root);
|
auto newThread = std::make_shared<MessageThread>(root);
|
||||||
replyCtx.highlight = shouldHighlightReplyThread(
|
checkThreadSubscription(tags, message->nick(), newThread);
|
||||||
tags, message->nick(), newThread, true);
|
|
||||||
|
|
||||||
replyCtx.thread = newThread;
|
replyCtx.thread = newThread;
|
||||||
rootThread = newThread;
|
rootThread = newThread;
|
||||||
|
@ -1605,24 +1587,18 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBuilder builder(channel, message, args, content, isAction);
|
args.allowIgnore = !isSub;
|
||||||
builder.setMessageOffset(messageOffset);
|
auto [msg, alert] = MessageBuilder::makeIrcMessage(
|
||||||
|
channel, message, args, content, messageOffset, replyCtx.thread,
|
||||||
|
replyCtx.parent);
|
||||||
|
|
||||||
builder.setThread(std::move(replyCtx.thread));
|
if (msg)
|
||||||
builder.setParent(std::move(replyCtx.parent));
|
|
||||||
if (replyCtx.highlight)
|
|
||||||
{
|
|
||||||
builder.message().flags.set(MessageFlag::SubscribedThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSub || !builder.isIgnored())
|
|
||||||
{
|
{
|
||||||
if (isSub)
|
if (isSub)
|
||||||
{
|
{
|
||||||
builder->flags.set(MessageFlag::Subscription);
|
msg->flags.set(MessageFlag::Subscription);
|
||||||
builder->flags.unset(MessageFlag::Highlighted);
|
msg->flags.unset(MessageFlag::Highlighted);
|
||||||
}
|
}
|
||||||
auto msg = builder.build();
|
|
||||||
|
|
||||||
IrcMessageHandler::setSimilarityFlags(msg, chan);
|
IrcMessageHandler::setSimilarityFlags(msg, chan);
|
||||||
|
|
||||||
|
@ -1630,7 +1606,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
(!getSettings()->hideSimilar &&
|
(!getSettings()->hideSimilar &&
|
||||||
getSettings()->shownSimilarTriggerHighlights))
|
getSettings()->shownSimilarTriggerHighlights))
|
||||||
{
|
{
|
||||||
builder.triggerHighlights();
|
MessageBuilder::triggerHighlights(channel, alert);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto highlighted = msg->flags.has(MessageFlag::Highlighted);
|
const auto highlighted = msg->flags.has(MessageFlag::Highlighted);
|
||||||
|
|
|
@ -195,6 +195,7 @@ void WindowManager::updateWordTypeMask()
|
||||||
flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic);
|
flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic);
|
||||||
|
|
||||||
// badges
|
// badges
|
||||||
|
flags.set(MEF::BadgeSharedChannel);
|
||||||
flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority
|
flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority
|
||||||
: MEF::None);
|
: MEF::None);
|
||||||
flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions
|
flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions
|
||||||
|
|
|
@ -877,10 +877,13 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_DPICHANGED: {
|
case WM_DPICHANGED: {
|
||||||
// wait for Qt to process this message
|
if (this->flags_.has(ClearBuffersOnDpiChange))
|
||||||
postToThread([] {
|
{
|
||||||
getApp()->getWindows()->invalidateChannelViewBuffers();
|
// wait for Qt to process this message
|
||||||
});
|
postToThread([] {
|
||||||
|
getApp()->getWindows()->invalidateChannelViewBuffers();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
Dialog = 1 << 6,
|
Dialog = 1 << 6,
|
||||||
DisableLayoutSave = 1 << 7,
|
DisableLayoutSave = 1 << 7,
|
||||||
BoundsCheckOnShow = 1 << 8,
|
BoundsCheckOnShow = 1 << 8,
|
||||||
|
ClearBuffersOnDpiChange = 1 << 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ActionOnFocusLoss { Nothing, Delete, Close, Hide };
|
enum ActionOnFocusLoss { Nothing, Delete, Close, Hide };
|
||||||
|
|
|
@ -36,9 +36,9 @@ namespace {
|
||||||
|
|
||||||
DraggablePopup::DraggablePopup(bool closeAutomatically, QWidget *parent)
|
DraggablePopup::DraggablePopup(bool closeAutomatically, QWidget *parent)
|
||||||
: BaseWindow(
|
: BaseWindow(
|
||||||
closeAutomatically
|
(closeAutomatically ? popupFlagsCloseAutomatically : popupFlags) |
|
||||||
? popupFlagsCloseAutomatically | BaseWindow::DisableLayoutSave
|
BaseWindow::DisableLayoutSave |
|
||||||
: popupFlags | BaseWindow::DisableLayoutSave,
|
BaseWindow::ClearBuffersOnDpiChange,
|
||||||
parent)
|
parent)
|
||||||
, lifetimeHack_(std::make_shared<bool>(false))
|
, lifetimeHack_(std::make_shared<bool>(false))
|
||||||
, dragTimer_(this)
|
, dragTimer_(this)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
#include "singletons/WindowManager.hpp"
|
#include "singletons/WindowManager.hpp"
|
||||||
|
#include "util/PostToThread.hpp"
|
||||||
#include "widgets/BaseWidget.hpp"
|
#include "widgets/BaseWidget.hpp"
|
||||||
#include "widgets/helper/ChannelView.hpp"
|
#include "widgets/helper/ChannelView.hpp"
|
||||||
#include "widgets/helper/InvisibleSizeGrip.hpp"
|
#include "widgets/helper/InvisibleSizeGrip.hpp"
|
||||||
|
@ -312,6 +313,13 @@ bool OverlayWindow::nativeEvent(const QByteArray &eventType, void *message,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
# endif
|
# endif
|
||||||
|
case WM_DPICHANGED: {
|
||||||
|
// wait for Qt to process this message, same as in BaseWindow
|
||||||
|
postToThread([] {
|
||||||
|
getApp()->getWindows()->invalidateChannelViewBuffers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QWidget::nativeEvent(eventType, message, result);
|
return QWidget::nativeEvent(eventType, message, result);
|
||||||
|
|
|
@ -52,7 +52,9 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
Window::Window(WindowType type, QWidget *parent)
|
Window::Window(WindowType type, QWidget *parent)
|
||||||
: BaseWindow(BaseWindow::EnableCustomFrame, parent)
|
: BaseWindow(
|
||||||
|
{BaseWindow::EnableCustomFrame, BaseWindow::ClearBuffersOnDpiChange},
|
||||||
|
parent)
|
||||||
, type_(type)
|
, type_(type)
|
||||||
, notebook_(new SplitNotebook(this))
|
, notebook_(new SplitNotebook(this))
|
||||||
{
|
{
|
||||||
|
|
|
@ -2408,6 +2408,11 @@ void ChannelView::handleMouseClick(QMouseEvent *event,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link.value.startsWith("id:"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Insert @username into split input
|
// Insert @username into split input
|
||||||
const bool commaMention =
|
const bool commaMention =
|
||||||
getSettings()->mentionUsersWithComma;
|
getSettings()->mentionUsersWithComma;
|
||||||
|
|
|
@ -153,7 +153,7 @@
|
||||||
"searchText": "mm2pl mm2pl: Kappa ",
|
"searchText": "mm2pl mm2pl: Kappa ",
|
||||||
"serverReceivedTime": "2022-09-03T10:31:42Z",
|
"serverReceivedTime": "2022-09-03T10:31:42Z",
|
||||||
"timeoutUser": "",
|
"timeoutUser": "",
|
||||||
"usernameColor": "#ff000000"
|
"usernameColor": "#ffdaa521"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@
|
||||||
"searchText": "mm2pl mm2pl: Kappa ",
|
"searchText": "mm2pl mm2pl: Kappa ",
|
||||||
"serverReceivedTime": "2022-09-03T10:31:42Z",
|
"serverReceivedTime": "2022-09-03T10:31:42Z",
|
||||||
"timeoutUser": "",
|
"timeoutUser": "",
|
||||||
"usernameColor": "#ff000000"
|
"usernameColor": "#ffdaa521"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
157
tests/snapshots/IrcMessageHandler/clearchat.json
Normal file
157
tests/snapshots/IrcMessageHandler/clearchat.json
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
{
|
||||||
|
"input": "@room-id=11148817;rm-received-ts=1729627607652;tmi-sent-ts=1729627607545;historical=1 :tmi.twitch.tv CLEARCHAT #pajlada",
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"20:06"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "20:06:47",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"Chat"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"has"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"been"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"cleared"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"by"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"a"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"moderator."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|DoNotTriggerNotification",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "",
|
||||||
|
"messageText": "Chat has been cleared by a moderator.",
|
||||||
|
"searchText": "Chat has been cleared by a moderator.",
|
||||||
|
"serverReceivedTime": "",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -153,7 +153,7 @@
|
||||||
"searchText": "mm2pl mm2pl: Keepo ",
|
"searchText": "mm2pl mm2pl: Keepo ",
|
||||||
"serverReceivedTime": "2022-09-03T10:31:35Z",
|
"serverReceivedTime": "2022-09-03T10:31:35Z",
|
||||||
"timeoutUser": "",
|
"timeoutUser": "",
|
||||||
"usernameColor": "#ff000000"
|
"usernameColor": "#ffdaa521"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
157
tests/snapshots/IrcMessageHandler/emoteonly-on.json
Normal file
157
tests/snapshots/IrcMessageHandler/emoteonly-on.json
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
{
|
||||||
|
"input": "@historical=1;rm-received-ts=1729627965650;msg-id=emote_only_on :tmi.twitch.tv NOTICE #pajlada :This room is now in emote-only mode.",
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"20:12"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "20:12:45",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"This"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"room"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"is"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"now"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"in"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"emote-only"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"mode."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|DoNotTriggerNotification",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "",
|
||||||
|
"messageText": "This room is now in emote-only mode.",
|
||||||
|
"searchText": "This room is now in emote-only mode.",
|
||||||
|
"serverReceivedTime": "",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -221,7 +221,7 @@
|
||||||
"searchText": "mm2pl mm2pl: Kappa Keepo PogChamp ",
|
"searchText": "mm2pl mm2pl: Kappa Keepo PogChamp ",
|
||||||
"serverReceivedTime": "2022-09-03T10:31:42Z",
|
"serverReceivedTime": "2022-09-03T10:31:42Z",
|
||||||
"timeoutUser": "",
|
"timeoutUser": "",
|
||||||
"usernameColor": "#ff000000"
|
"usernameColor": "#ffdaa521"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
145
tests/snapshots/IrcMessageHandler/raid.json
Normal file
145
tests/snapshots/IrcMessageHandler/raid.json
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
{
|
||||||
|
"input": "@badges=subscriber/24;login=nerixyz;msg-param-displayName=nerixyz;user-type=;tmi-sent-ts=1729626466361;system-msg=2\\sraiders\\sfrom\\snerixyz\\shave\\sjoined!;room-id=11148817;user-id=129546453;display-name=nerixyz;subscriber=1;historical=1;rm-received-ts=1729626466492;msg-id=raid;vip=0;id=7299b7bc-61ce-423c-85ce-8d651b56cce4;msg-param-login=nerixyz;color=#FF0000;mod=0;msg-param-viewerCount=2;flags=;msg-param-profileImageURL=https://static-cdn.jtvnw.net/jtv_user_pictures/e065218b-49df-459d-afd3-c6557870f551-profile_image-%s.png;emotes=;badge-info=subscriber/28 :tmi.twitch.tv USERNOTICE #pajlada",
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"19:47"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "19:47:46",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"raiders"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"from"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"fallbackColor": "System",
|
||||||
|
"flags": "Text|Mention",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "MentionElement",
|
||||||
|
"userColor": "#ffff0000",
|
||||||
|
"userLoginName": "nerixyz",
|
||||||
|
"words": [
|
||||||
|
"nerixyz"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"have"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"joined!"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|DoNotTriggerNotification|Subscription",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "",
|
||||||
|
"messageText": "2 raiders from nerixyz have joined!",
|
||||||
|
"searchText": "2 raiders from nerixyz have joined!",
|
||||||
|
"serverReceivedTime": "",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -64,6 +64,24 @@
|
||||||
"trailingSpace": true,
|
"trailingSpace": true,
|
||||||
"type": "TwitchModerationElement"
|
"type": "TwitchModerationElement"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"emote": {
|
||||||
|
"homePage": "https://link.twitch.tv/SharedChatViewer",
|
||||||
|
"images": {
|
||||||
|
"1x": ""
|
||||||
|
},
|
||||||
|
"name": "",
|
||||||
|
"tooltip": "Shared Message"
|
||||||
|
},
|
||||||
|
"flags": "BadgeSharedChannel",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"tooltip": "Shared Message",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "BadgeElement"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"emote": {
|
"emote": {
|
||||||
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge",
|
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge",
|
||||||
|
|
|
@ -64,6 +64,24 @@
|
||||||
"trailingSpace": true,
|
"trailingSpace": true,
|
||||||
"type": "TwitchModerationElement"
|
"type": "TwitchModerationElement"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"emote": {
|
||||||
|
"homePage": "https://link.twitch.tv/SharedChatViewer",
|
||||||
|
"images": {
|
||||||
|
"1x": ""
|
||||||
|
},
|
||||||
|
"name": "",
|
||||||
|
"tooltip": "Shared Message from twitchdev"
|
||||||
|
},
|
||||||
|
"flags": "BadgeSharedChannel",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"tooltip": "Shared Message from twitchdev",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "BadgeElement"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"color": "#ffff0000",
|
"color": "#ffff0000",
|
||||||
"flags": "Username",
|
"flags": "Username",
|
||||||
|
|
|
@ -64,6 +64,24 @@
|
||||||
"trailingSpace": true,
|
"trailingSpace": true,
|
||||||
"type": "TwitchModerationElement"
|
"type": "TwitchModerationElement"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"emote": {
|
||||||
|
"homePage": "https://link.twitch.tv/SharedChatViewer",
|
||||||
|
"images": {
|
||||||
|
"1x": ""
|
||||||
|
},
|
||||||
|
"name": "",
|
||||||
|
"tooltip": "Shared Message from twitchdev"
|
||||||
|
},
|
||||||
|
"flags": "BadgeSharedChannel",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"tooltip": "Shared Message from twitchdev",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "BadgeElement"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"emote": {
|
"emote": {
|
||||||
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge",
|
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge",
|
||||||
|
|
5
tests/snapshots/IrcMessageHandler/shared-chat-raid.json
Normal file
5
tests/snapshots/IrcMessageHandler/shared-chat-raid.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"input": "@color=#FF0000;emotes=;subscriber=0;msg-id=sharedchatnotice;historical=1;msg-param-profileImageURL=https://static-cdn.jtvnw.net/jtv_user_pictures/e065218b-49df-459d-afd3-c6557870f551-profile_image-%s.png;tmi-sent-ts=1729627237027;rm-received-ts=1729627237138;msg-param-displayName=nerixyz;id=c585cb3e-cb4f-4a48-a251-b568d217587e;display-name=nerixyz;badges=;user-id=129546453;source-id=d86cdfb2-e138-48e2-985f-5b8efb765ba4;source-room-id=955766119;room-id=11148817;user-type=;msg-param-login=nerixyz;flags=;source-badge-info=;mod=0;vip=0;system-msg=2\\sraiders\\sfrom\\snerixyz\\shave\\sjoined!;login=nerixyz;msg-param-viewerCount=2;source-badges=;source-msg-id=raid;badge-info= :tmi.twitch.tv USERNOTICE #pajlada",
|
||||||
|
"output": [
|
||||||
|
]
|
||||||
|
}
|
|
@ -64,6 +64,24 @@
|
||||||
"trailingSpace": true,
|
"trailingSpace": true,
|
||||||
"type": "TwitchModerationElement"
|
"type": "TwitchModerationElement"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"emote": {
|
||||||
|
"homePage": "https://link.twitch.tv/SharedChatViewer",
|
||||||
|
"images": {
|
||||||
|
"1x": ""
|
||||||
|
},
|
||||||
|
"name": "",
|
||||||
|
"tooltip": "Shared Message"
|
||||||
|
},
|
||||||
|
"flags": "BadgeSharedChannel",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"tooltip": "Shared Message",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "BadgeElement"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"emote": {
|
"emote": {
|
||||||
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge",
|
"homePage": "https://www.twitch.tv/jobs?ref=chat_badge",
|
||||||
|
|
87
tests/snapshots/IrcMessageHandler/timeout.json
Normal file
87
tests/snapshots/IrcMessageHandler/timeout.json
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
{
|
||||||
|
"input": "@tmi-sent-ts=1729628658012;rm-received-ts=1729628658106;historical=1;ban-duration=1;room-id=11148817;target-user-id=129546453 :tmi.twitch.tv CLEARCHAT #pajlada nerixyz",
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"20:24"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "20:24:18",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "UserInfo",
|
||||||
|
"value": "nerixyz"
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"nerixyz"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"has",
|
||||||
|
"been",
|
||||||
|
"timed",
|
||||||
|
"out",
|
||||||
|
"for",
|
||||||
|
"1s."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|Timeout|DoNotTriggerNotification",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "",
|
||||||
|
"messageText": "nerixyz has been timed out for 1s. ",
|
||||||
|
"searchText": "nerixyz has been timed out for 1s. ",
|
||||||
|
"serverReceivedTime": "",
|
||||||
|
"timeoutUser": "nerixyz",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -285,9 +285,9 @@ TEST_F(FiltersF, TypingContextChecks)
|
||||||
|
|
||||||
QString originalMessage = privmsg->content();
|
QString originalMessage = privmsg->content();
|
||||||
|
|
||||||
MessageBuilder builder(&channel, privmsg, MessageParseArgs{});
|
auto [msg, alert] = MessageBuilder::makeIrcMessage(
|
||||||
|
&channel, privmsg, MessageParseArgs{}, originalMessage, 0);
|
||||||
|
|
||||||
auto msg = builder.build();
|
|
||||||
EXPECT_NE(msg.get(), nullptr);
|
EXPECT_NE(msg.get(), nullptr);
|
||||||
|
|
||||||
auto contextMap = buildContextMap(msg, &channel);
|
auto contextMap = buildContextMap(msg, &channel);
|
||||||
|
|
Loading…
Reference in a new issue