feat: add shared chat badge

This commit is contained in:
iProdigy 2024-10-19 03:40:06 -07:00
parent dab97b3235
commit f4036b269b
8 changed files with 90 additions and 3 deletions

View file

@ -71,6 +71,17 @@ public:
}
}
void remove(const key_t &key)
{
auto it = _cache_items_map.find(key);
if (it == _cache_items_map.end())
{
throw std::range_error("There is no such key in cache");
}
_cache_items_list.erase(it->second);
_cache_items_map.erase(it);
}
const value_t &get(const key_t &key)
{
auto it = _cache_items_map.find(key);

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -384,6 +384,17 @@ EmotePtr makeAutoModBadge()
Url{"https://dashboard.twitch.tv/settings/moderation/automod"}});
}
EmotePtr makeSharedChatBadge(const QString &sourceName)
{
return std::make_shared<Emote>(
Emote{"SharedChat_" + sourceName,
ImageSet{Image::fromResourcePixmap(
getResources().twitch.sharedChat, 0.25)},
Tooltip{"Shared Message" +
(sourceName.isEmpty() ? "" : " from " + sourceName)},
Url{"https://link.twitch.tv/SharedChatViewer"}});
}
} // namespace
namespace chatterino {
@ -1156,6 +1167,14 @@ MessagePtr MessageBuilder::build()
this->emplace<TwitchModerationElement>();
}
if (this->sourceName.has_value())
{
this->emplace<BadgeElement>(
makeSharedChatBadge(this->sourceName.value()),
MessageElementFlag::BadgeSharedChannel)
->setLink({Link::UserInfo, sourceName.value()});
}
this->appendTwitchBadges();
this->appendChatterinoBadges();
@ -2242,16 +2261,23 @@ void MessageBuilder::parseRoomID()
{
this->message().flags.set(MessageFlag::SharedMessage);
auto sourceChan =
getApp()->getTwitch()->getChannelOrEmptyByID(sourceRoom);
auto *twitch = getApp()->getTwitch();
auto sourceChan = twitch->getChannelOrEmptyByID(sourceRoom);
if (sourceChan && !sourceChan->isEmpty())
{
this->sourceName = sourceChan->getName();
this->sourceChannel =
dynamic_cast<TwitchChannel *>(sourceChan.get());
// avoid duplicate pings
this->message().flags.set(
MessageFlag::DoNotTriggerNotification);
}
else
{
this->sourceName =
twitch->getOrPopulateChannelCache(sourceRoom);
}
}
}
}

View file

@ -157,6 +157,7 @@ public:
TwitchChannel *twitchChannel = nullptr;
/// The Twitch Channel the message was sent in, according to the Shared Chat feature
TwitchChannel *sourceChannel = nullptr;
std::optional<QString> sourceName = std::nullopt;
Message *operator->();
Message &message();

View file

@ -66,6 +66,10 @@ enum class MessageElementFlag : int64_t {
BitsStatic = (1LL << 11),
BitsAnimated = (1LL << 12),
// Slot 0: Twitch
// - Shared Channel indicator badge
BadgeSharedChannel = (1LL << 37),
// Slot 1: Twitch
// - Staff badge
// - Admin badge
@ -119,7 +123,7 @@ enum class MessageElementFlag : int64_t {
Badges = BadgeGlobalAuthority | BadgePredictions | BadgeChannelAuthority |
BadgeSubscription | BadgeVanity | BadgeChatterino | BadgeSevenTV |
BadgeFfz,
BadgeFfz | BadgeSharedChannel,
ChannelName = (1LL << 20),

View file

@ -153,6 +153,7 @@ TwitchIrcServer::TwitchIrcServer()
, liveChannel(new Channel("/live", Channel::Type::TwitchLive))
, automodChannel(new Channel("/automod", Channel::Type::TwitchAutomod))
, watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
, channelNamesById_(512)
{
// Initialize the connections
// XXX: don't create write connection if there is no separate write connection.
@ -1128,6 +1129,38 @@ std::shared_ptr<Channel> TwitchIrcServer::getChannelOrEmptyByID(
return Channel::getEmpty();
}
std::optional<QString> TwitchIrcServer::getOrPopulateChannelCache(
const QString &channelId)
{
{
const auto cache = this->channelNamesById_.access();
if (cache->exists(channelId))
{
return cache->get(channelId);
}
// prevent multiple helix requests for single user
cache->put(channelId, "");
}
getHelix()->getUserById(
channelId,
[this](const HelixUser &user) {
const auto cache = this->channelNamesById_.access();
cache->put(user.id, user.login);
},
[this, &channelId] {
const auto cache = this->channelNamesById_.access();
if (cache->exists(channelId) && cache->get(channelId).isEmpty())
{
// invalidate cache so another helix request can be attempted
cache->remove(channelId);
}
});
return {};
}
QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName)
{
if (dirtyChannelName.startsWith('#'))

View file

@ -3,10 +3,12 @@
#include "common/Atomic.hpp"
#include "common/Channel.hpp"
#include "common/Common.hpp"
#include "common/UniqueAccess.hpp"
#include "providers/irc/IrcConnection2.hpp"
#include "util/RatelimitBucket.hpp"
#include <IrcMessage>
#include <lrucache/lrucache.hpp>
#include <pajlada/signals/signal.hpp>
#include <pajlada/signals/signalholder.hpp>
@ -43,6 +45,9 @@ public:
virtual ChannelPtr getOrAddChannel(const QString &dirtyChannelName) = 0;
virtual ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) = 0;
virtual std::optional<QString> getOrPopulateChannelCache(
const QString &channelId) = 0;
virtual void addFakeMessage(const QString &data) = 0;
virtual void addGlobalSystemMessage(const QString &messageText) = 0;
@ -95,6 +100,9 @@ public:
std::shared_ptr<Channel> getChannelOrEmptyByID(
const QString &channelID) override;
std::optional<QString> getOrPopulateChannelCache(
const QString &channelId) override;
void reloadAllBTTVChannelEmotes();
void reloadAllFFZChannelEmotes();
void reloadAllSevenTVChannelEmotes();
@ -190,6 +198,9 @@ private:
// https://dev.twitch.tv/docs/irc/guide#rate-limits
QObjectPtr<RatelimitBucket> joinBucket_;
// cached channel id => name for resolving Shared Chat members
UniqueAccess<cache::lru_cache<QString, QString>> channelNamesById_;
QTimer reconnectTimer_;
int falloffCounter_ = 1;

View file

@ -195,6 +195,7 @@ void WindowManager::updateWordTypeMask()
flags.set(settings->animateEmotes ? MEF::BitsAnimated : MEF::BitsStatic);
// badges
flags.set(MEF::BadgeSharedChannel);
flags.set(settings->showBadgesGlobalAuthority ? MEF::BadgeGlobalAuthority
: MEF::None);
flags.set(settings->showBadgesPredictions ? MEF::BadgePredictions