mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
Compare commits
No commits in common. "5334b6046128c9d00e95c368b07de66eaab0fedc" and "c66b8853a1e5b717e37d53813ef9c30370a93119" have entirely different histories.
5334b60461
...
c66b8853a1
|
@ -7,7 +7,7 @@
|
||||||
- Major: Improve high-DPI support on Windows. (#4868, #5391, #5664, #5666)
|
- 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, #5661)
|
- 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: 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,13 @@
|
||||||
- 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, #5671)
|
- Dev: Added more tests for message building. (#5598, #5654, #5656)
|
||||||
- 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, #5668)
|
- Dev: Decoupled reply parsing from `MessageBuilder`. (#5660)
|
||||||
- Dev: Refactored IRC message building. (#5663)
|
- Dev: Refactored IRC message building. (#5663)
|
||||||
|
|
||||||
## 2.5.1
|
## 2.5.1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4a0a1e599377cdcdc91b0fbbefc312936b48730c
|
Subproject commit c58874c1aa5d0619df2c975bcb87433941b46920
|
|
@ -3,7 +3,6 @@
|
||||||
#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"
|
||||||
|
@ -56,11 +55,6 @@ public:
|
||||||
return &this->fonts;
|
return &this->fonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
ITwitchUsers *getTwitchUsers() override
|
|
||||||
{
|
|
||||||
return &this->twitchUsers;
|
|
||||||
}
|
|
||||||
|
|
||||||
BttvLiveUpdates *getBttvLiveUpdates() override
|
BttvLiveUpdates *getBttvLiveUpdates() override
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -77,7 +71,6 @@ public:
|
||||||
DisabledStreamerMode streamerMode;
|
DisabledStreamerMode streamerMode;
|
||||||
Theme theme;
|
Theme theme;
|
||||||
Fonts fonts;
|
Fonts fonts;
|
||||||
TwitchUsers twitchUsers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino::mock
|
} // namespace chatterino::mock
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
#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
|
|
Binary file not shown.
Before Width: | Height: | Size: 1 KiB |
|
@ -32,7 +32,6 @@
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "providers/twitch/TwitchIrc.hpp"
|
#include "providers/twitch/TwitchIrc.hpp"
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
#include "providers/twitch/TwitchUsers.hpp"
|
|
||||||
#include "singletons/Emotes.hpp"
|
#include "singletons/Emotes.hpp"
|
||||||
#include "singletons/Resources.hpp"
|
#include "singletons/Resources.hpp"
|
||||||
#include "singletons/Settings.hpp"
|
#include "singletons/Settings.hpp"
|
||||||
|
@ -381,18 +380,6 @@ EmotePtr makeAutoModBadge()
|
||||||
Url{"https://dashboard.twitch.tv/settings/moderation/automod"}});
|
Url{"https://dashboard.twitch.tv/settings/moderation/automod"}});
|
||||||
}
|
}
|
||||||
|
|
||||||
EmotePtr makeSharedChatBadge(const QString &sourceName)
|
|
||||||
{
|
|
||||||
return std::make_shared<Emote>(Emote{
|
|
||||||
.name = EmoteName{},
|
|
||||||
.images = ImageSet{Image::fromResourcePixmap(
|
|
||||||
getResources().twitch.sharedChat, 0.25)},
|
|
||||||
.tooltip = Tooltip{"Shared Message" +
|
|
||||||
(sourceName.isEmpty() ? "" : " from " + sourceName)},
|
|
||||||
.homePage = Url{"https://link.twitch.tv/SharedChatViewer"},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<std::optional<EmotePtr>, MessageElementFlags, bool> parseEmote(
|
std::tuple<std::optional<EmotePtr>, MessageElementFlags, bool> parseEmote(
|
||||||
TwitchChannel *twitchChannel, const EmoteName &name)
|
TwitchChannel *twitchChannel, const EmoteName &name)
|
||||||
{
|
{
|
||||||
|
@ -2395,11 +2382,6 @@ void MessageBuilder::parseThread(const QString &messageContent,
|
||||||
this->message().replyParent = parent;
|
this->message().replyParent = parent;
|
||||||
thread->addToThread(std::weak_ptr{this->message_});
|
thread->addToThread(std::weak_ptr{this->message_});
|
||||||
|
|
||||||
if (thread->subscribed())
|
|
||||||
{
|
|
||||||
this->message().flags.set(MessageFlag::SubscribedThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable reply flag
|
// enable reply flag
|
||||||
this->message().flags.set(MessageFlag::ReplyMessage);
|
this->message().flags.set(MessageFlag::ReplyMessage);
|
||||||
|
|
||||||
|
@ -2764,28 +2746,6 @@ void MessageBuilder::appendTwitchBadges(const QVariantMap &tags,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->message().flags.has(MessageFlag::SharedMessage))
|
|
||||||
{
|
|
||||||
const QString sourceId = tags["source-room-id"].toString();
|
|
||||||
QString sourceName;
|
|
||||||
if (sourceId.isEmpty())
|
|
||||||
{
|
|
||||||
sourceName = "";
|
|
||||||
}
|
|
||||||
else if (twitchChannel->roomId() == sourceId)
|
|
||||||
{
|
|
||||||
sourceName = twitchChannel->getName();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sourceName =
|
|
||||||
getApp()->getTwitchUsers()->resolveID({sourceId})->displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->emplace<BadgeElement>(makeSharedChatBadge(sourceName),
|
|
||||||
MessageElementFlag::BadgeSharedChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto badgeInfos = parseBadgeInfoTag(tags);
|
auto badgeInfos = parseBadgeInfoTag(tags);
|
||||||
auto badges = parseBadgeTag(tags);
|
auto badges = parseBadgeTag(tags);
|
||||||
appendBadges(this, badges, badgeInfos, twitchChannel);
|
appendBadges(this, badges, badgeInfos, twitchChannel);
|
||||||
|
|
|
@ -66,10 +66,6 @@ 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
|
||||||
|
@ -123,7 +119,7 @@ enum class MessageElementFlag : int64_t {
|
||||||
|
|
||||||
Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
|
Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
|
||||||
BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV |
|
BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV |
|
||||||
BadgeFfz | BadgeSharedChannel,
|
BadgeFfz,
|
||||||
|
|
||||||
ChannelName = (1LL << 20),
|
ChannelName = (1LL << 20),
|
||||||
|
|
||||||
|
|
|
@ -123,34 +123,47 @@ int stripLeadingReplyMention(const QVariantMap &tags, QString &content)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkThreadSubscription(const QVariantMap &tags,
|
[[nodiscard]] bool shouldHighlightReplyThread(
|
||||||
const QString &senderLogin,
|
const QVariantMap &tags, const QString &senderLogin,
|
||||||
std::shared_ptr<MessageThread> &thread)
|
std::shared_ptr<MessageThread> &thread, bool isNew)
|
||||||
{
|
{
|
||||||
if (thread->subscribed() || thread->unsubscribed())
|
const auto ¤tLogin =
|
||||||
|
getApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
||||||
|
|
||||||
|
if (thread->subscribed())
|
||||||
{
|
{
|
||||||
return;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread->unsubscribed())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getSettings()->autoSubToParticipatedThreads)
|
if (getSettings()->autoSubToParticipatedThreads)
|
||||||
{
|
{
|
||||||
const auto ¤tLogin =
|
if (isNew)
|
||||||
getApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
|
||||||
|
|
||||||
if (senderLogin == currentLogin)
|
|
||||||
{
|
{
|
||||||
thread->markSubscribed();
|
if (const auto it = tags.find("reply-parent-user-login");
|
||||||
}
|
|
||||||
else if (const auto it = tags.find("reply-parent-user-login");
|
|
||||||
it != tags.end())
|
it != tags.end())
|
||||||
{
|
{
|
||||||
auto name = it.value().toString();
|
auto name = it.value().toString();
|
||||||
if (name == currentLogin)
|
if (name == currentLogin)
|
||||||
{
|
{
|
||||||
thread->markSubscribed();
|
thread->markSubscribed();
|
||||||
|
return true; // already marked as participated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (senderLogin == currentLogin)
|
||||||
|
{
|
||||||
|
thread->markSubscribed();
|
||||||
|
// don't set the highlight here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelPtr channelOrEmptyByTarget(const QString &target,
|
ChannelPtr channelOrEmptyByTarget(const QString &target,
|
||||||
|
@ -230,6 +243,7 @@ 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(
|
||||||
|
@ -251,7 +265,8 @@ struct ReplyContext {
|
||||||
if (owned)
|
if (owned)
|
||||||
{
|
{
|
||||||
// Thread already exists (has a reply)
|
// Thread already exists (has a reply)
|
||||||
checkThreadSubscription(tags, message->nick(), owned);
|
ctx.highlight = shouldHighlightReplyThread(
|
||||||
|
tags, message->nick(), owned, false);
|
||||||
ctx.thread = owned;
|
ctx.thread = owned;
|
||||||
rootThread = owned;
|
rootThread = owned;
|
||||||
}
|
}
|
||||||
|
@ -286,7 +301,8 @@ struct ReplyContext {
|
||||||
{
|
{
|
||||||
std::shared_ptr<MessageThread> newThread =
|
std::shared_ptr<MessageThread> newThread =
|
||||||
std::make_shared<MessageThread>(foundMessage);
|
std::make_shared<MessageThread>(foundMessage);
|
||||||
checkThreadSubscription(tags, message->nick(), newThread);
|
ctx.highlight = shouldHighlightReplyThread(
|
||||||
|
tags, message->nick(), newThread, true);
|
||||||
|
|
||||||
ctx.thread = newThread;
|
ctx.thread = newThread;
|
||||||
rootThread = newThread;
|
rootThread = newThread;
|
||||||
|
@ -708,6 +724,10 @@ std::vector<MessagePtr> IrcMessageHandler::parseMessageWithReply(
|
||||||
|
|
||||||
if (built)
|
if (built)
|
||||||
{
|
{
|
||||||
|
if (replyCtx.highlight)
|
||||||
|
{
|
||||||
|
built->flags.set(MessageFlag::SubscribedThread);
|
||||||
|
}
|
||||||
builtMessages.emplace_back(built);
|
builtMessages.emplace_back(built);
|
||||||
MessageBuilder::triggerHighlights(channel, alert);
|
MessageBuilder::triggerHighlights(channel, alert);
|
||||||
}
|
}
|
||||||
|
@ -1532,7 +1552,8 @@ 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();
|
||||||
checkThreadSubscription(tags, message->nick(), thread);
|
replyCtx.highlight = shouldHighlightReplyThread(
|
||||||
|
tags, message->nick(), thread, false);
|
||||||
replyCtx.thread = thread;
|
replyCtx.thread = thread;
|
||||||
rootThread = thread;
|
rootThread = thread;
|
||||||
}
|
}
|
||||||
|
@ -1544,7 +1565,8 @@ 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);
|
||||||
checkThreadSubscription(tags, message->nick(), newThread);
|
replyCtx.highlight = shouldHighlightReplyThread(
|
||||||
|
tags, message->nick(), newThread, true);
|
||||||
|
|
||||||
replyCtx.thread = newThread;
|
replyCtx.thread = newThread;
|
||||||
rootThread = newThread;
|
rootThread = newThread;
|
||||||
|
@ -1599,6 +1621,10 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
msg->flags.set(MessageFlag::Subscription);
|
msg->flags.set(MessageFlag::Subscription);
|
||||||
msg->flags.unset(MessageFlag::Highlighted);
|
msg->flags.unset(MessageFlag::Highlighted);
|
||||||
}
|
}
|
||||||
|
if (replyCtx.highlight)
|
||||||
|
{
|
||||||
|
msg->flags.set(MessageFlag::SubscribedThread);
|
||||||
|
}
|
||||||
|
|
||||||
IrcMessageHandler::setSimilarityFlags(msg, chan);
|
IrcMessageHandler::setSimilarityFlags(msg, chan);
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,6 @@ 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
|
||||||
|
|
|
@ -2408,11 +2408,6 @@ 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;
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
{
|
|
||||||
"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,24 +64,6 @@
|
||||||
"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,24 +64,6 @@
|
||||||
"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,24 +64,6 @@
|
||||||
"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",
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"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,24 +64,6 @@
|
||||||
"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",
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in a new issue