feat: add countdown for slow mode and timeouts (#6782)
This commit is contained in:
@@ -222,7 +222,9 @@ std::optional<ClearChatMessage> parseClearChatMessage(
|
||||
calculateMessageTime(message))
|
||||
.release();
|
||||
|
||||
return ClearChatMessage{.message = timeoutMsg, .disableAllMessages = false};
|
||||
return ClearChatMessage{.message = timeoutMsg,
|
||||
.disableAllMessages = false,
|
||||
.username = username};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,6 +405,22 @@ void IrcMessageHandler::parsePrivMessageInto(
|
||||
channel->setVIP(parsedBadges.contains("vip"));
|
||||
channel->setStaff(parsedBadges.contains("staff"));
|
||||
}
|
||||
|
||||
if (!channel->isLoadingRecentMessages())
|
||||
{
|
||||
// Clear the send wait timer when we are able to send a message
|
||||
channel->setSendWait(0);
|
||||
|
||||
// Update send wait timer with slow mode timeout if this user is not a mod or vip.
|
||||
if (!channel->hasHighRateLimit())
|
||||
{
|
||||
auto roomModes = *channel->accessRoomModes();
|
||||
if (roomModes.slowMode > 0)
|
||||
{
|
||||
channel->setSendWait(roomModes.slowMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IrcMessageHandler::addMessage(
|
||||
@@ -510,6 +528,25 @@ void IrcMessageHandler::handleClearChatMessage(Communi::IrcMessage *message)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set send wait timer when the user is timed out
|
||||
const auto currentUsername =
|
||||
getApp()->getAccounts()->twitch.getCurrent()->getUserName();
|
||||
if (currentUsername == clearChat.username)
|
||||
{
|
||||
bool ok = false;
|
||||
int remainingTime =
|
||||
message->tags().value("ban-duration").toInt(&ok);
|
||||
if (ok)
|
||||
{
|
||||
auto *tc = dynamic_cast<TwitchChannel *>(chan.get());
|
||||
assert(tc != nullptr);
|
||||
if (tc != nullptr)
|
||||
{
|
||||
tc->setSendWait(remainingTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chan->addOrReplaceTimeout(std::move(clearChat.message), time);
|
||||
}
|
||||
|
||||
@@ -617,6 +654,13 @@ void IrcMessageHandler::handleUserStateMessage(Communi::IrcMessage *message)
|
||||
tc->setMod(modTag == "1");
|
||||
}
|
||||
}
|
||||
|
||||
// When a user is updated to mod or vip status, any previous
|
||||
// slow mode or timeout timers are no longer in effect.
|
||||
if (tc->hasHighRateLimit())
|
||||
{
|
||||
tc->setSendWait(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -979,6 +1023,33 @@ void IrcMessageHandler::handleNoticeMessage(Communi::IrcNoticeMessage *message)
|
||||
{
|
||||
channel->addMessage(msg, MessageContext::Original);
|
||||
}
|
||||
|
||||
auto handleSendWait = [&channel](const QString &remaining) {
|
||||
bool ok = false;
|
||||
int seconds = remaining.toInt(&ok);
|
||||
if (ok)
|
||||
{
|
||||
auto *tc = dynamic_cast<TwitchChannel *>(channel.get());
|
||||
assert(tc != nullptr);
|
||||
if (tc != nullptr)
|
||||
{
|
||||
tc->setSendWait(seconds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (tags == "msg_slowmode")
|
||||
{
|
||||
// Notice received when the user sends a message too quickly during slow mode.
|
||||
// @msg-id=msg_slowmode :tmi.twitch.tv NOTICE #channel :This room is in slow mode and you are sending messages too quickly. You will be able to talk again in 10 seconds.
|
||||
handleSendWait(message->content().split(u' ').value(21));
|
||||
}
|
||||
else if (tags == "msg_timedout")
|
||||
{
|
||||
// Notice received when the user sends a message while timed out.
|
||||
// @msg-id=msg_timedout :tmi.twitch.tv NOTICE #twitch :You are timed out for 3600 more seconds.
|
||||
handleSendWait(message->content().split(u' ').value(5));
|
||||
}
|
||||
}
|
||||
|
||||
void IrcMessageHandler::handleJoinMessage(Communi::IrcMessage *message)
|
||||
|
||||
@@ -23,6 +23,7 @@ class MessageSink;
|
||||
struct ClearChatMessage {
|
||||
MessagePtr message;
|
||||
bool disableAllMessages;
|
||||
std::optional<QString> username;
|
||||
};
|
||||
|
||||
class IrcMessageHandler
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "singletons/StreamerMode.hpp"
|
||||
#include "singletons/Toasts.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/FormatTime.hpp"
|
||||
#include "util/Helpers.hpp"
|
||||
#include "util/PostToThread.hpp"
|
||||
#include "util/QStringHash.hpp"
|
||||
@@ -65,6 +66,7 @@
|
||||
namespace chatterino {
|
||||
|
||||
using namespace literals;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -212,6 +214,11 @@ TwitchChannel::TwitchChannel(const QString &name)
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(&this->sendWaitTimer_, &QTimer::timeout,
|
||||
&this->lifetimeGuard_, [this] {
|
||||
this->syncSendWaitTimer();
|
||||
});
|
||||
|
||||
// debugging
|
||||
#if 0
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
@@ -997,6 +1004,12 @@ void TwitchChannel::setRoomModes(const RoomModes &newRoomModes)
|
||||
{
|
||||
this->roomModes = newRoomModes;
|
||||
|
||||
// Clear send wait timer when slow mode is disabled
|
||||
if (newRoomModes.slowMode == 0)
|
||||
{
|
||||
this->setSendWait(newRoomModes.slowMode);
|
||||
}
|
||||
|
||||
this->roomModesChanged.invoke();
|
||||
}
|
||||
|
||||
@@ -2342,4 +2355,48 @@ void TwitchChannel::listenSevenTVCosmetics() const
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchChannel::syncSendWaitTimer()
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
const auto remaining =
|
||||
this->sendWaitEnd_.has_value()
|
||||
? std::chrono::duration_cast<std::chrono::seconds>(
|
||||
this->sendWaitEnd_.value() - now)
|
||||
: 0s;
|
||||
if (remaining <= 0s)
|
||||
{
|
||||
this->sendWaitTimer_.stop();
|
||||
this->sendWaitUpdate.invoke("");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->sendWaitUpdate.invoke(formatTime(remaining, 2));
|
||||
}
|
||||
}
|
||||
|
||||
void TwitchChannel::setSendWait(int seconds)
|
||||
{
|
||||
if (seconds <= 0)
|
||||
{
|
||||
if (this->sendWaitEnd_.has_value())
|
||||
{
|
||||
this->sendWaitEnd_ = std::nullopt;
|
||||
this->syncSendWaitTimer();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this->sendWaitEnd_ =
|
||||
std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
|
||||
if (!this->sendWaitTimer_.isActive())
|
||||
{
|
||||
this->sendWaitTimer_.start(1s);
|
||||
this->syncSendWaitTimer();
|
||||
}
|
||||
}
|
||||
|
||||
bool TwitchChannel::isLoadingRecentMessages() const
|
||||
{
|
||||
return this->loadingRecentMessages_.test();
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -338,6 +338,8 @@ public:
|
||||
|
||||
pajlada::Signals::NoArgSignal destroyed;
|
||||
|
||||
pajlada::Signals::Signal<const QString &> sendWaitUpdate;
|
||||
|
||||
// Channel point rewards
|
||||
void addQueuedRedemption(const QString &rewardId,
|
||||
const QString &originalContent,
|
||||
@@ -368,6 +370,21 @@ public:
|
||||
const QString &getDisplayName() const override;
|
||||
void updateDisplayName(const QString &displayName);
|
||||
|
||||
/**
|
||||
* Sync the text of the send wait timer to the actual time remaining.
|
||||
*/
|
||||
void syncSendWaitTimer();
|
||||
/**
|
||||
* Set the send wait timer to given number of seconds
|
||||
*
|
||||
* When a channel is in slow mode or the user is timed out, calling
|
||||
* this function sets the timer to show how long the user will need
|
||||
* to wait before they can send again.
|
||||
*/
|
||||
void setSendWait(int seconds);
|
||||
|
||||
bool isLoadingRecentMessages() const;
|
||||
|
||||
private:
|
||||
struct NameOptions {
|
||||
// displayName is the non-CJK-display name for this user
|
||||
@@ -572,6 +589,10 @@ private:
|
||||
friend class Commands_E2E_Test;
|
||||
friend class ::TestIrcMessageHandlerP;
|
||||
friend class ::TestEventSubMessagesP;
|
||||
|
||||
QTimer sendWaitTimer_;
|
||||
// Timepoint at which the user can send messages again
|
||||
std::optional<std::chrono::steady_clock::time_point> sendWaitEnd_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
Reference in New Issue
Block a user