Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add channel for messages caught by AutoMod #4986

Merged
merged 8 commits into from
Dec 3, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 8 additions & 2 deletions src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
});
Expand Down
6 changes: 4 additions & 2 deletions src/common/Channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions src/common/Channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class Channel : public std::enable_shared_from_this<Channel>
TwitchWatching,
TwitchMentions,
TwitchLive,
TwitchAutomod,
TwitchEnd,
Irc,
Misc
Expand Down
2 changes: 1 addition & 1 deletion src/common/WindowDescriptors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion src/messages/MessageBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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});
iProdigy marked this conversation as resolved.
Show resolved Hide resolved
builder2.emplace<TimestampElement>();
builder2.emplace<TwitchModerationElement>();
builder2.message().loginName = action.target.login;
Expand Down
2 changes: 1 addition & 1 deletion src/messages/MessageBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions src/providers/twitch/TwitchIrcServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions src/providers/twitch/TwitchIrcServer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class TwitchIrcServer final : public AbstractIrcServer,
const ChannelPtr whispersChannel;
const ChannelPtr mentionsChannel;
const ChannelPtr liveChannel;
const ChannelPtr automodChannel;
IndirectChannel watchingChannel;

PubSub *pubsub;
Expand Down
8 changes: 8 additions & 0 deletions src/singletons/WindowManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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_,
Expand Down
7 changes: 6 additions & 1 deletion src/singletons/helper/LoggingChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ LoggingChannel::LoggingChannel(const QString &_channelName,
{
this->subDirectory = "Live";
}
else if (channelName.startsWith("/automod"))
{
this->subDirectory = "AutoMod";
}
else
{
this->subDirectory =
Expand Down Expand Up @@ -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 + " ");
}
Expand Down
5 changes: 3 additions & 2 deletions src/widgets/Notebook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
35 changes: 31 additions & 4 deletions src/widgets/dialogs/SelectChannelDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,35 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent)
live_lbl->setVisible(enabled);
});

// automod_btn
auto automod_btn = vbox.emplace<QRadioButton>("AutoMod").assign(
iProdigy marked this conversation as resolved.
Show resolved Hide resolved
&this->ui_.twitch.automod);
auto automod_lbl =
iProdigy marked this conversation as resolved.
Show resolved Hide resolved
vbox.emplace<QLabel>("Shows when AutoMod catches a message in any "
"channel you moderate.")
.hidden();
iProdigy marked this conversation as resolved.
Show resolved Hide resolved

automod_lbl->setWordWrap(true);
iProdigy marked this conversation as resolved.
Show resolved Hide resolved
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(),
mentions_btn.getElement());
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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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;
}
iProdigy marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions src/widgets/dialogs/SelectChannelDialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class SelectChannelDialog final : public BaseWindow
QRadioButton *mentions;
QRadioButton *watching;
QRadioButton *live;
QRadioButton *automod;
} twitch;
struct {
QLineEdit *channel;
Expand Down
23 changes: 15 additions & 8 deletions src/widgets/helper/ChannelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 =
Expand All @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/widgets/helper/SearchPopup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -166,6 +168,10 @@ void SearchPopup::updateWindowTitle()
{
historyName = "multiple channels'";
}
else if (this->channelName_ == "/automod")
{
historyName = "automod";
}
else if (this->channelName_ == "/mentions")
{
historyName = "mentions";
Expand Down
5 changes: 4 additions & 1 deletion src/widgets/splits/Split.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1454,7 +1454,10 @@ void Split::showSearch(bool singleChannel)
auto container = dynamic_cast<SplitContainer *>(notebook.getPageAt(i));
for (auto split : container->getSplits())
{
popup->addChannel(split->getChannelView());
if (split->channel_.getType() != Channel::Type::TwitchAutomod)
{
popup->addChannel(split->getChannelView());
}
}
}

Expand Down
Loading