mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
feat: add channel for messages caught by AutoMod (#4986)
Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
812186dc4c
commit
44abe6b487
|
@ -3,6 +3,7 @@
|
|||
## Unversioned
|
||||
|
||||
- Major: Allow use of Twitch follower emotes in other channels if subscribed. (#4922)
|
||||
- Major: Add `/automod` split to track automod caught messages across all open channels the user moderates. (#4986)
|
||||
- Minor: Migrate to the new Get Channel Followers Helix endpoint, fixing follower count not showing up in usercards. (#4809)
|
||||
- Minor: The account switcher is now styled to match your theme. (#4817)
|
||||
- Minor: Add an invisible resize handle to the bottom of frameless user info popups and reply thread popups. (#4795)
|
||||
|
|
|
@ -546,9 +546,15 @@ void Application::initPubSub()
|
|||
msg.senderUserID, msg.senderUserLogin,
|
||||
senderDisplayName, senderColor};
|
||||
postToThread([chan, action] {
|
||||
const auto p = makeAutomodMessage(action);
|
||||
const auto p =
|
||||
makeAutomodMessage(action, chan->getName());
|
||||
chan->addMessage(p.first);
|
||||
chan->addMessage(p.second);
|
||||
|
||||
getApp()->twitch->automodChannel->addMessage(
|
||||
p.first);
|
||||
getApp()->twitch->automodChannel->addMessage(
|
||||
p.second);
|
||||
});
|
||||
}
|
||||
// "ALLOWED" and "DENIED" statuses remain unimplemented
|
||||
|
@ -573,7 +579,7 @@ void Application::initPubSub()
|
|||
}
|
||||
|
||||
postToThread([chan, action] {
|
||||
const auto p = makeAutomodMessage(action);
|
||||
const auto p = makeAutomodMessage(action, chan->getName());
|
||||
chan->addMessage(p.first);
|
||||
chan->addMessage(p.second);
|
||||
});
|
||||
|
|
|
@ -295,7 +295,8 @@ bool Channel::isWritable() const
|
|||
{
|
||||
using Type = Channel::Type;
|
||||
auto type = this->getType();
|
||||
return type != Type::TwitchMentions && type != Type::TwitchLive;
|
||||
return type != Type::TwitchMentions && type != Type::TwitchLive &&
|
||||
type != Type::TwitchAutomod;
|
||||
}
|
||||
|
||||
void Channel::sendMessage(const QString &message)
|
||||
|
@ -330,7 +331,8 @@ bool Channel::isLive() const
|
|||
|
||||
bool Channel::shouldIgnoreHighlights() const
|
||||
{
|
||||
return this->type_ == Type::TwitchMentions ||
|
||||
return this->type_ == Type::TwitchAutomod ||
|
||||
this->type_ == Type::TwitchMentions ||
|
||||
this->type_ == Type::TwitchWhispers;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
TwitchWatching,
|
||||
TwitchMentions,
|
||||
TwitchLive,
|
||||
TwitchAutomod,
|
||||
TwitchEnd,
|
||||
Irc,
|
||||
Misc
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace chatterino {
|
|||
enum class WindowType;
|
||||
|
||||
struct SplitDescriptor {
|
||||
// Twitch or mentions or watching or whispers or IRC
|
||||
// Twitch or mentions or watching or live or automod or whispers or IRC
|
||||
QString type_;
|
||||
|
||||
// Twitch Channel name or IRC channel name
|
||||
|
|
|
@ -141,13 +141,14 @@ MessagePtr makeAutomodInfoMessage(const AutomodInfoAction &action)
|
|||
}
|
||||
|
||||
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
||||
const AutomodAction &action)
|
||||
const AutomodAction &action, const QString &channelName)
|
||||
{
|
||||
MessageBuilder builder, builder2;
|
||||
|
||||
//
|
||||
// Builder for AutoMod message with explanation
|
||||
builder.message().loginName = "automod";
|
||||
builder.message().channelName = channelName;
|
||||
builder.message().flags.set(MessageFlag::PubSub);
|
||||
builder.message().flags.set(MessageFlag::Timeout);
|
||||
builder.message().flags.set(MessageFlag::AutoMod);
|
||||
|
@ -193,6 +194,12 @@ std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
|||
|
||||
//
|
||||
// Builder for offender's message
|
||||
builder2.message().channelName = channelName;
|
||||
builder2
|
||||
.emplace<TextElement>("#" + channelName,
|
||||
MessageElementFlag::ChannelName,
|
||||
MessageColor::System)
|
||||
->setLink({Link::JumpToChannel, channelName});
|
||||
builder2.emplace<TimestampElement>();
|
||||
builder2.emplace<TwitchModerationElement>();
|
||||
builder2.message().loginName = action.target.login;
|
||||
|
|
|
@ -54,7 +54,7 @@ const ImageUploaderResultTag imageUploaderResultMessage{};
|
|||
MessagePtr makeSystemMessage(const QString &text);
|
||||
MessagePtr makeSystemMessage(const QString &text, const QTime &time);
|
||||
std::pair<MessagePtr, MessagePtr> makeAutomodMessage(
|
||||
const AutomodAction &action);
|
||||
const AutomodAction &action, const QString &channelName);
|
||||
MessagePtr makeAutomodInfoMessage(const AutomodInfoAction &action);
|
||||
|
||||
struct MessageParseArgs {
|
||||
|
|
|
@ -43,6 +43,7 @@ TwitchIrcServer::TwitchIrcServer()
|
|||
: whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers))
|
||||
, mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions))
|
||||
, liveChannel(new Channel("/live", Channel::Type::TwitchLive))
|
||||
, automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod))
|
||||
, watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
|
||||
{
|
||||
this->initializeIrc();
|
||||
|
@ -272,6 +273,11 @@ std::shared_ptr<Channel> TwitchIrcServer::getCustomChannel(
|
|||
return this->liveChannel;
|
||||
}
|
||||
|
||||
if (channelName == "/automod")
|
||||
{
|
||||
return this->automodChannel;
|
||||
}
|
||||
|
||||
static auto getTimer = [](ChannelPtr channel, int msBetweenMessages,
|
||||
bool addInitialMessages) {
|
||||
if (addInitialMessages)
|
||||
|
@ -383,6 +389,7 @@ void TwitchIrcServer::forEachChannelAndSpecialChannels(
|
|||
func(this->whispersChannel);
|
||||
func(this->mentionsChannel);
|
||||
func(this->liveChannel);
|
||||
func(this->automodChannel);
|
||||
}
|
||||
|
||||
std::shared_ptr<Channel> TwitchIrcServer::getChannelOrEmptyByID(
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
const ChannelPtr whispersChannel;
|
||||
const ChannelPtr mentionsChannel;
|
||||
const ChannelPtr liveChannel;
|
||||
const ChannelPtr automodChannel;
|
||||
IndirectChannel watchingChannel;
|
||||
|
||||
PubSub *pubsub;
|
||||
|
|
|
@ -603,6 +603,10 @@ void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj)
|
|||
obj.insert("name", channel.get()->getName());
|
||||
}
|
||||
break;
|
||||
case Channel::Type::TwitchAutomod: {
|
||||
obj.insert("type", "automod");
|
||||
}
|
||||
break;
|
||||
case Channel::Type::TwitchMentions: {
|
||||
obj.insert("type", "mentions");
|
||||
}
|
||||
|
@ -676,6 +680,10 @@ IndirectChannel WindowManager::decodeChannel(const SplitDescriptor &descriptor)
|
|||
{
|
||||
return app->twitch->liveChannel;
|
||||
}
|
||||
else if (descriptor.type_ == "automod")
|
||||
{
|
||||
return app->twitch->automodChannel;
|
||||
}
|
||||
else if (descriptor.type_ == "irc")
|
||||
{
|
||||
return Irc::instance().getOrAddChannel(descriptor.server_,
|
||||
|
|
|
@ -29,6 +29,10 @@ LoggingChannel::LoggingChannel(const QString &_channelName,
|
|||
{
|
||||
this->subDirectory = "Live";
|
||||
}
|
||||
else if (channelName.startsWith("/automod"))
|
||||
{
|
||||
this->subDirectory = "AutoMod";
|
||||
}
|
||||
else
|
||||
{
|
||||
this->subDirectory =
|
||||
|
@ -96,7 +100,8 @@ void LoggingChannel::addMessage(MessagePtr message)
|
|||
}
|
||||
|
||||
QString str;
|
||||
if (channelName.startsWith("/mentions"))
|
||||
if (channelName.startsWith("/mentions") ||
|
||||
channelName.startsWith("/automod"))
|
||||
{
|
||||
str.append("#" + message->channelName + " ");
|
||||
}
|
||||
|
|
|
@ -1313,8 +1313,9 @@ SplitNotebook::SplitNotebook(Window *parent)
|
|||
{
|
||||
for (auto *split : sc->getSplits())
|
||||
{
|
||||
if (split->getChannel()->getType() !=
|
||||
Channel::Type::TwitchMentions)
|
||||
auto type = split->getChannel()->getType();
|
||||
if (type != Channel::Type::TwitchMentions &&
|
||||
type != Channel::Type::TwitchAutomod)
|
||||
{
|
||||
if (split->getChannelView().scrollToMessage(
|
||||
message))
|
||||
|
|
|
@ -140,10 +140,27 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
|
|||
live_lbl->setVisible(enabled);
|
||||
});
|
||||
|
||||
// automod_btn
|
||||
auto automod_btn = vbox.emplace<QRadioButton>("AutoMod").assign(
|
||||
&this->ui_.twitch.automod);
|
||||
auto automod_lbl =
|
||||
vbox.emplace<QLabel>("Shows when AutoMod catches a message in any "
|
||||
"channel you moderate.")
|
||||
.hidden();
|
||||
|
||||
automod_lbl->setWordWrap(true);
|
||||
automod_btn->installEventFilter(&this->tabFilter_);
|
||||
|
||||
QObject::connect(automod_btn.getElement(), &QRadioButton::toggled,
|
||||
[=](bool enabled) mutable {
|
||||
automod_lbl->setVisible(enabled);
|
||||
});
|
||||
|
||||
vbox->addStretch(1);
|
||||
|
||||
// tabbing order
|
||||
QWidget::setTabOrder(live_btn.getElement(), channel_btn.getElement());
|
||||
QWidget::setTabOrder(automod_btn.getElement(),
|
||||
channel_btn.getElement());
|
||||
QWidget::setTabOrder(channel_btn.getElement(),
|
||||
whispers_btn.getElement());
|
||||
QWidget::setTabOrder(whispers_btn.getElement(),
|
||||
|
@ -151,6 +168,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
|
|||
QWidget::setTabOrder(mentions_btn.getElement(),
|
||||
watching_btn.getElement());
|
||||
QWidget::setTabOrder(watching_btn.getElement(), live_btn.getElement());
|
||||
QWidget::setTabOrder(live_btn.getElement(), automod_btn.getElement());
|
||||
|
||||
// tab
|
||||
auto tab = notebook->addPage(obj.getElement());
|
||||
|
@ -311,6 +329,11 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel)
|
|||
this->ui_.twitch.live->setFocus();
|
||||
}
|
||||
break;
|
||||
case Channel::Type::TwitchAutomod: {
|
||||
this->ui_.notebook->selectIndex(TAB_TWITCH);
|
||||
this->ui_.twitch.automod->setFocus();
|
||||
}
|
||||
break;
|
||||
case Channel::Type::Irc: {
|
||||
this->ui_.notebook->selectIndex(TAB_IRC);
|
||||
this->ui_.irc.channel->setText(_channel.get()->getName());
|
||||
|
@ -378,6 +401,10 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const
|
|||
{
|
||||
return app->twitch->liveChannel;
|
||||
}
|
||||
else if (this->ui_.twitch.automod->isChecked())
|
||||
{
|
||||
return app->twitch->automodChannel;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TAB_IRC: {
|
||||
|
@ -442,9 +469,9 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched,
|
|||
this->dialog->ui_.twitch.whispers->setFocus();
|
||||
return true;
|
||||
}
|
||||
else if (widget == this->dialog->ui_.twitch.live)
|
||||
else if (widget == this->dialog->ui_.twitch.automod)
|
||||
{
|
||||
// Special case for when current selection is "Live" (the last entry in the list), next wrap is Channel, but we need to select its edit box
|
||||
// Special case for when current selection is "AutoMod" (the last entry in the list), next wrap is Channel, but we need to select its edit box
|
||||
this->dialog->ui_.twitch.channel->setFocus();
|
||||
return true;
|
||||
}
|
||||
|
@ -463,7 +490,7 @@ bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched,
|
|||
if (widget == this->dialog->ui_.twitch.channelName)
|
||||
{
|
||||
// Special case for when current selection is the "Channel" entry's edit box since the Edit box actually has the focus
|
||||
this->dialog->ui_.twitch.live->setFocus();
|
||||
this->dialog->ui_.twitch.automod->setFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ private:
|
|||
QRadioButton *mentions;
|
||||
QRadioButton *watching;
|
||||
QRadioButton *live;
|
||||
QRadioButton *automod;
|
||||
} twitch;
|
||||
struct {
|
||||
QLineEdit *channel;
|
||||
|
|
|
@ -1282,14 +1282,16 @@ MessageElementFlags ChannelView::getFlags() const
|
|||
flags.set(MessageElementFlag::ModeratorTools);
|
||||
}
|
||||
if (this->underlyingChannel_ == app->twitch->mentionsChannel ||
|
||||
this->underlyingChannel_ == app->twitch->liveChannel)
|
||||
this->underlyingChannel_ == app->twitch->liveChannel ||
|
||||
this->underlyingChannel_ == app->twitch->automodChannel)
|
||||
{
|
||||
flags.set(MessageElementFlag::ChannelName);
|
||||
flags.unset(MessageElementFlag::ChannelPointReward);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->sourceChannel_ == app->twitch->mentionsChannel)
|
||||
if (this->sourceChannel_ == app->twitch->mentionsChannel ||
|
||||
this->sourceChannel_ == app->twitch->automodChannel)
|
||||
{
|
||||
flags.set(MessageElementFlag::ChannelName);
|
||||
}
|
||||
|
@ -2347,11 +2349,13 @@ void ChannelView::addMessageContextMenuItems(QMenu *menu,
|
|||
this->split_;
|
||||
bool isMentions =
|
||||
this->channel()->getType() == Channel::Type::TwitchMentions;
|
||||
if (isSearch || isMentions || isReplyOrUserCard)
|
||||
bool isAutomod = this->channel()->getType() == Channel::Type::TwitchAutomod;
|
||||
if (isSearch || isMentions || isReplyOrUserCard || isAutomod)
|
||||
{
|
||||
const auto &messagePtr = layout->getMessagePtr();
|
||||
menu->addAction("&Go to message", [this, &messagePtr, isSearch,
|
||||
isMentions, isReplyOrUserCard] {
|
||||
isMentions, isReplyOrUserCard,
|
||||
isAutomod] {
|
||||
if (isSearch)
|
||||
{
|
||||
if (const auto &search =
|
||||
|
@ -2360,16 +2364,17 @@ void ChannelView::addMessageContextMenuItems(QMenu *menu,
|
|||
search->goToMessage(messagePtr);
|
||||
}
|
||||
}
|
||||
else if (isMentions)
|
||||
else if (isMentions || isAutomod)
|
||||
{
|
||||
getApp()->windows->scrollToMessage(messagePtr);
|
||||
}
|
||||
else if (isReplyOrUserCard)
|
||||
{
|
||||
// If the thread is in the mentions channel,
|
||||
// If the thread is in the mentions or automod channel,
|
||||
// we need to find the original split.
|
||||
if (this->split_->getChannel()->getType() ==
|
||||
Channel::Type::TwitchMentions)
|
||||
const auto type = this->split_->getChannel()->getType();
|
||||
if (type == Channel::Type::TwitchMentions ||
|
||||
type == Channel::Type::TwitchAutomod)
|
||||
{
|
||||
getApp()->windows->scrollToMessage(messagePtr);
|
||||
}
|
||||
|
@ -2606,6 +2611,8 @@ bool ChannelView::mayContainMessage(const MessagePtr &message)
|
|||
return message->flags.has(MessageFlag::Highlighted);
|
||||
case Channel::Type::TwitchLive:
|
||||
return message->flags.has(MessageFlag::System);
|
||||
case Channel::Type::TwitchAutomod:
|
||||
return message->flags.has(MessageFlag::AutoMod);
|
||||
case Channel::Type::TwitchEnd: // TODO: not used?
|
||||
case Channel::Type::None: // Unspecific
|
||||
case Channel::Type::Misc: // Unspecific
|
||||
|
|
|
@ -134,7 +134,9 @@ void SearchPopup::goToMessage(const MessagePtr &message)
|
|||
{
|
||||
for (const auto &view : this->searchChannels_)
|
||||
{
|
||||
if (view.get().channel()->getType() == Channel::Type::TwitchMentions)
|
||||
const auto type = view.get().channel()->getType();
|
||||
if (type == Channel::Type::TwitchMentions ||
|
||||
type == Channel::Type::TwitchAutomod)
|
||||
{
|
||||
getApp()->windows->scrollToMessage(message);
|
||||
return;
|
||||
|
@ -166,6 +168,10 @@ void SearchPopup::updateWindowTitle()
|
|||
{
|
||||
historyName = "multiple channels'";
|
||||
}
|
||||
else if (this->channelName_ == "/automod")
|
||||
{
|
||||
historyName = "automod";
|
||||
}
|
||||
else if (this->channelName_ == "/mentions")
|
||||
{
|
||||
historyName = "mentions";
|
||||
|
|
|
@ -1453,10 +1453,13 @@ void Split::showSearch(bool singleChannel)
|
|||
{
|
||||
auto container = dynamic_cast<SplitContainer *>(notebook.getPageAt(i));
|
||||
for (auto split : container->getSplits())
|
||||
{
|
||||
if (split->channel_.getType() != Channel::Type::TwitchAutomod)
|
||||
{
|
||||
popup->addChannel(split->getChannelView());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
popup->show();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue