Skip to content

Commit

Permalink
test: more stuff + badges fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerixyz committed Sep 19, 2024
1 parent 3d91aff commit a9dee36
Show file tree
Hide file tree
Showing 27 changed files with 3,339 additions and 91 deletions.
16 changes: 15 additions & 1 deletion mocks/include/mocks/ChatterinoBadges.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,30 @@

#include "providers/chatterino/ChatterinoBadges.hpp"

#include <unordered_map>

namespace chatterino::mock {

class ChatterinoBadges : public IChatterinoBadges
{
public:
std::optional<EmotePtr> getBadge(const UserId &id) override
{
(void)id;
auto it = this->users.find(id);
if (it != this->users.end())
{
return it->second;
}
return std::nullopt;
}

void setBadge(UserId id, EmotePtr emote)
{
this->users.emplace(std::move(id), std::move(emote));
}

private:
std::unordered_map<UserId, EmotePtr> users;
};

} // namespace chatterino::mock
22 changes: 22 additions & 0 deletions src/providers/ffz/FfzBadges.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,26 @@ void FfzBadges::load()
.execute();
}

void FfzBadges::registerBadge(int badgeID, Badge badge)
{
std::unique_lock lock(this->mutex_);

this->badges.emplace(badgeID, std::move(badge));
}

void FfzBadges::assignBadgeToUser(const UserId &userID, int badgeID)
{
std::unique_lock lock(this->mutex_);

auto it = this->userBadges.find(userID.string);
if (it != this->userBadges.end())
{
it->second.emplace(badgeID);
}
else
{
this->userBadges.emplace(userID.string, std::set{badgeID});
}
}

} // namespace chatterino
3 changes: 3 additions & 0 deletions src/providers/ffz/FfzBadges.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class FfzBadges
std::vector<Badge> getUserBadges(const UserId &id);
std::optional<Badge> getBadge(int badgeID) const;

void registerBadge(int badgeID, Badge badge);
void assignBadgeToUser(const UserId &userID, int badgeID);

void load();

private:
Expand Down
5 changes: 5 additions & 0 deletions src/providers/twitch/IrcMessageHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,11 @@ std::vector<MessagePtr> IrcMessageHandler::parseMessageWithReply(
QString content = privMsg->content();
int messageOffset = stripLeadingReplyMention(privMsg->tags(), content);
MessageParseArgs args;
auto tags = privMsg->tags();
if (const auto it = tags.find("custom-reward-id"); it != tags.end())
{
args.channelPointRewardId = it.value().toString();
}
MessageBuilder builder(channel, message, args, content,
privMsg->isAction());
builder.setMessageOffset(messageOffset);
Expand Down
52 changes: 29 additions & 23 deletions src/providers/twitch/TwitchBadges.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,38 +79,44 @@ void TwitchBadges::loadTwitchBadges()
break;
}
qCWarning(chatterinoTwitch) << errorMessage;
QFile file(":/twitch-badges.json");
if (!file.open(QFile::ReadOnly))
{
// Despite erroring out, we still want to reach the same point
// Loaded should still be set to true to not build up an endless queue, and the quuee should still be flushed.
qCWarning(chatterinoTwitch)
<< "Error loading Twitch Badges from the local backup file";
this->loaded();
return;
}
auto bytes = file.readAll();
auto doc = QJsonDocument::fromJson(bytes);
this->loadLocalBadges();
});
}

void TwitchBadges::loadLocalBadges()
{
QFile file(":/twitch-badges.json");
if (!file.open(QFile::ReadOnly))
{
// Despite erroring out, we still want to reach the same point
// Loaded should still be set to true to not build up an endless queue, and the quuee should still be flushed.
qCWarning(chatterinoTwitch)
<< "Error loading Twitch Badges from the local backup file";
this->loaded();
return;
}
auto bytes = file.readAll();
auto doc = QJsonDocument::fromJson(bytes);

this->parseTwitchBadges(doc.object());
this->parseTwitchBadges(doc.object());

this->loaded();
});
this->loaded();
}

void TwitchBadges::parseTwitchBadges(QJsonObject root)
void TwitchBadges::parseTwitchBadges(const QJsonObject &root)
{
auto badgeSets = this->badgeSets_.access();

auto jsonSets = root.value("badge_sets").toObject();
for (auto sIt = jsonSets.begin(); sIt != jsonSets.end(); ++sIt)
auto jsonSets = root.value("data").toArray();
for (auto setValue : jsonSets)
{
auto key = sIt.key();
auto versions = sIt.value().toObject().value("versions").toObject();
const auto set = setValue.toObject();
auto key = set["set_id"].toString();

for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt)
for (auto versionValue : set["versions"].toArray())
{
auto versionObj = vIt.value().toObject();
const auto versionObj = versionValue.toObject();
auto id = versionObj["id"].toString();
auto emote = Emote{
.name = {""},
.images =
Expand All @@ -129,7 +135,7 @@ void TwitchBadges::parseTwitchBadges(QJsonObject root)
.homePage = Url{versionObj.value("click_url").toString()},
};

(*badgeSets)[key][vIt.key()] = std::make_shared<Emote>(emote);
(*badgeSets)[key][id] = std::make_shared<Emote>(emote);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/providers/twitch/TwitchBadges.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ class TwitchBadges
BadgeIconCallback callback);

void loadTwitchBadges();
void loadLocalBadges();

private:
void parseTwitchBadges(QJsonObject root);
void parseTwitchBadges(const QJsonObject &root);
void loaded();
void loadEmoteImage(const QString &name, const ImagePtr &image,
BadgeIconCallback &&callback);
Expand Down
129 changes: 68 additions & 61 deletions src/providers/twitch/TwitchChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1641,72 +1641,73 @@ void TwitchChannel::refreshCheerEmotes()
return;
}

std::vector<CheerEmoteSet> emoteSets;
this->setCheerEmoteSets(cheermoteSets);
},
[] {
// Failure
});
}

for (const auto &set : cheermoteSets)
{
auto cheerEmoteSet = CheerEmoteSet();
cheerEmoteSet.regex = QRegularExpression(
"^" + set.prefix + "([1-9][0-9]*)$",
QRegularExpression::CaseInsensitiveOption);
void TwitchChannel::setCheerEmoteSets(
const std::vector<HelixCheermoteSet> &cheermoteSets)
{
std::vector<CheerEmoteSet> emoteSets;

for (const auto &tier : set.tiers)
{
CheerEmote cheerEmote;

cheerEmote.color = QColor(tier.color);
cheerEmote.minBits = tier.minBits;
cheerEmote.regex = cheerEmoteSet.regex;

// TODO(pajlada): We currently hardcode dark here :|
// We will continue to do so for now since we haven't had to
// solve that anywhere else

// Combine the prefix (e.g. BibleThump) with the tier (1, 100 etc.)
auto emoteTooltip =
set.prefix + tier.id + "<br>Twitch Cheer Emote";
auto makeImageSet = [](const HelixCheermoteImage &image) {
return ImageSet{
Image::fromUrl(image.imageURL1x, 1.0,
BASE_BADGE_SIZE),
Image::fromUrl(image.imageURL2x, 0.5,
BASE_BADGE_SIZE * 2),
Image::fromUrl(image.imageURL4x, 0.25,
BASE_BADGE_SIZE * 4),
};
};
cheerEmote.animatedEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{"cheer emote"},
.images = makeImageSet(tier.darkAnimated),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});
cheerEmote.staticEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{"cheer emote"},
.images = makeImageSet(tier.darkStatic),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});

cheerEmoteSet.cheerEmotes.emplace_back(
std::move(cheerEmote));
}
for (const auto &set : cheermoteSets)
{
auto cheerEmoteSet = CheerEmoteSet();
cheerEmoteSet.regex =
QRegularExpression("^" + set.prefix + "([1-9][0-9]*)$",
QRegularExpression::CaseInsensitiveOption);

for (const auto &tier : set.tiers)
{
CheerEmote cheerEmote;

cheerEmote.color = QColor(tier.color);
cheerEmote.minBits = tier.minBits;
cheerEmote.regex = cheerEmoteSet.regex;

// TODO(pajlada): We currently hardcode dark here :|
// We will continue to do so for now since we haven't had to
// solve that anywhere else

// Combine the prefix (e.g. BibleThump) with the tier (1, 100 etc.)
auto emoteTooltip = set.prefix + tier.id + "<br>Twitch Cheer Emote";
auto makeImageSet = [](const HelixCheermoteImage &image) {
return ImageSet{
Image::fromUrl(image.imageURL1x, 1.0, BASE_BADGE_SIZE),
Image::fromUrl(image.imageURL2x, 0.5, BASE_BADGE_SIZE * 2),
Image::fromUrl(image.imageURL4x, 0.25, BASE_BADGE_SIZE * 4),
};
};
cheerEmote.animatedEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{"cheer emote"},
.images = makeImageSet(tier.darkAnimated),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});
cheerEmote.staticEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{"cheer emote"},
.images = makeImageSet(tier.darkStatic),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});

// Sort cheermotes by cost
std::sort(cheerEmoteSet.cheerEmotes.begin(),
cheerEmoteSet.cheerEmotes.end(),
[](const auto &lhs, const auto &rhs) {
return lhs.minBits > rhs.minBits;
});
cheerEmoteSet.cheerEmotes.emplace_back(std::move(cheerEmote));
}

emoteSets.emplace_back(std::move(cheerEmoteSet));
}
// Sort cheermotes by cost
std::sort(cheerEmoteSet.cheerEmotes.begin(),
cheerEmoteSet.cheerEmotes.end(),
[](const auto &lhs, const auto &rhs) {
return lhs.minBits > rhs.minBits;
});

*this->cheerEmoteSets_.access() = std::move(emoteSets);
},
[] {
// Failure
});
emoteSets.emplace_back(std::move(cheerEmoteSet));
}

*this->cheerEmoteSets_.access() = std::move(emoteSets);
}

void TwitchChannel::createClip()
Expand Down Expand Up @@ -1865,6 +1866,12 @@ std::vector<FfzBadges::Badge> TwitchChannel::ffzChannelBadges(
return badges;
}

void TwitchChannel::setFfzChannelBadges(FfzChannelBadgeMap map)
{
this->tgFfzChannelBadges_.guard();
this->ffzChannelBadges_ = std::move(map);
}

std::optional<EmotePtr> TwitchChannel::ffzCustomModBadge() const
{
return this->ffzCustomModBadge_.get();
Expand Down
3 changes: 3 additions & 0 deletions src/providers/twitch/TwitchChannel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct ChannelPointReward;
class MessageThread;
struct CheerEmoteSet;
struct HelixStream;
struct HelixCheermoteSet;

class TwitchIrcServer;

Expand Down Expand Up @@ -213,9 +214,11 @@ class TwitchChannel final : public Channel, public ChannelChatters
* Returns a list of channel-specific FrankerFaceZ badges for the given user
*/
std::vector<FfzBadges::Badge> ffzChannelBadges(const QString &userID) const;
void setFfzChannelBadges(FfzChannelBadgeMap map);

// Cheers
std::optional<CheerEmote> cheerEmote(const QString &string);
void setCheerEmoteSets(const std::vector<HelixCheermoteSet> &cheermoteSets);

// Replies
/**
Expand Down
39 changes: 39 additions & 0 deletions tests/fixtures/MessageBuilder/IRC/action.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,45 @@
"trailingSpace": true,
"type": "TimestampElement"
},
{
"emote": {
"images": {
"1x": "https://static-cdn.jtvnw.net/badges/v1/5527c58c-fb7d-422d-b71b-f309dcb85cc1/1",
"2x": "https://static-cdn.jtvnw.net/badges/v1/5527c58c-fb7d-422d-b71b-f309dcb85cc1/2",
"3x": "https://static-cdn.jtvnw.net/badges/v1/5527c58c-fb7d-422d-b71b-f309dcb85cc1/3"
},
"name": "",
"tooltip": "Broadcaster"
},
"flags": "BadgeChannelAuthority",
"link": {
"type": "None",
"value": ""
},
"tooltip": "Broadcaster",
"trailingSpace": true,
"type": "BadgeElement"
},
{
"emote": {
"homePage": "https://blog.twitch.tv/2017/04/24/the-verified-badge-is-here-13381bc05735",
"images": {
"1x": "https://static-cdn.jtvnw.net/badges/v1/d12a2e27-16f6-41d0-ab77-b780518f00a3/1",
"2x": "https://static-cdn.jtvnw.net/badges/v1/d12a2e27-16f6-41d0-ab77-b780518f00a3/2",
"3x": "https://static-cdn.jtvnw.net/badges/v1/d12a2e27-16f6-41d0-ab77-b780518f00a3/3"
},
"name": "",
"tooltip": "Verified"
},
"flags": "BadgeVanity",
"link": {
"type": "None",
"value": ""
},
"tooltip": "Verified",
"trailingSpace": true,
"type": "BadgeElement"
},
{
"color": "#ffcc44ff",
"flags": "Username",
Expand Down
Loading

0 comments on commit a9dee36

Please sign in to comment.