Reduce log spam in case of invalid token
Some checks failed
debian-build / build (push) Has been cancelled
Check locale / ubuntu64 (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled

The caching mechanism for the token validity checks did not have the
desired effect, and the Twitch channel to user id mapping function was
attempting to resolve names even with an invalid token.
This commit is contained in:
WarmUpTill 2025-05-05 21:55:38 +02:00 committed by WarmUpTill
parent 13dba6527d
commit f13beaead2
4 changed files with 65 additions and 29 deletions

View File

@ -31,6 +31,11 @@ void TwitchChannel::Save(obs_data_t *obj) const
std::string TwitchChannel::GetUserID(const TwitchToken &token) const
{
static std::map<std::string, std::string> userIDCache;
if (!token.IsValid()) {
vblog(LOG_INFO, "%s() failed: token invalid", __func__);
return "invalid";
}
auto it = userIDCache.find(std::string(_name));
if (it != userIDCache.end()) {
return it->second;
@ -47,7 +52,7 @@ std::string TwitchChannel::GetUserID(const TwitchToken &token) const
}
if (res.status != 200) {
blog(LOG_INFO, "failed to get Twitch user id from token! (%d)",
blog(LOG_INFO, "failed to get Twitch user id for channel! (%d)",
res.status);
return "invalid";
}
@ -90,7 +95,7 @@ getStringArrayHelper(const OBSDataAutoRelease &data, const std::string &value)
}
try {
auto json = nlohmann::json::parse(jsonStr);
auto array = json[value];
auto &array = json[value];
if (!array.is_array()) {
return result;
}
@ -104,7 +109,7 @@ getStringArrayHelper(const OBSDataAutoRelease &data, const std::string &value)
}
std::optional<ChannelLiveInfo>
TwitchChannel::GetLiveInfo(const TwitchToken &token)
TwitchChannel::GetLiveInfo(const TwitchToken &token) const
{
auto id = GetUserID(token);
if (!IsValid(id)) {
@ -146,7 +151,8 @@ TwitchChannel::GetLiveInfo(const TwitchToken &token)
return info;
}
std::optional<ChannelInfo> TwitchChannel::GetInfo(const TwitchToken &token)
std::optional<ChannelInfo>
TwitchChannel::GetInfo(const TwitchToken &token) const
{
auto id = GetUserID(token);
if (!IsValid(id)) {

View File

@ -51,8 +51,8 @@ struct TwitchChannel {
StringVariable GetName() const { return _name; }
std::string GetUserID(const TwitchToken &token) const;
bool IsValid(const std::string &id) const;
std::optional<ChannelLiveInfo> GetLiveInfo(const TwitchToken &);
std::optional<ChannelInfo> GetInfo(const TwitchToken &);
std::optional<ChannelLiveInfo> GetLiveInfo(const TwitchToken &) const;
std::optional<ChannelInfo> GetInfo(const TwitchToken &) const;
void ResolveVariables();
private:

View File

@ -81,7 +81,7 @@ const std::unordered_map<std::string, std::string> TokenOption::_apiIdToLocale{
static void saveConnections(obs_data_t *obj);
static void loadConnections(obs_data_t *obj);
bool setupTwitchTokenSupport()
static bool setupTwitchTokenSupport()
{
AddSaveStep(saveConnections);
AddLoadStep(loadConnections);
@ -153,6 +153,29 @@ bool TokenOption::operator<(const TokenOption &other) const
return apiId < other.apiId;
}
TwitchToken::TwitchToken(const TwitchToken &other)
: Item(other._name),
_token(other._token),
_lastValidityCheckTime(),
_lastValidityCheckValue(),
_lastValidityCheckResult(false),
_userID(other._userID),
_tokenOptions(other._tokenOptions),
_eventSub(),
_validateEventSubTimestamps(other._validateEventSubTimestamps)
{
}
TwitchToken &TwitchToken::operator=(const TwitchToken &other)
{
_name = other._name;
_token = other._token;
_userID = other._userID;
_tokenOptions = other._tokenOptions;
_validateEventSubTimestamps = other._validateEventSubTimestamps;
return *this;
}
void TwitchToken::Load(obs_data_t *obj)
{
Item::Load(obj);
@ -273,39 +296,38 @@ std::shared_ptr<EventSub> TwitchToken::GetEventSub()
bool TwitchToken::IsValid(bool forceUpdate) const
{
static std::chrono::system_clock::time_point queryTime;
static std::string lastQueryToken;
static httplib::Result response;
static httplib::Client cli("https://id.twitch.tv");
httplib::Headers headers{{"Authorization", "OAuth " + _token}};
std::scoped_lock<std::mutex> lock(_cacheMutex);
auto currentTime = std::chrono::system_clock::now();
auto diff = currentTime - queryTime;
auto diff = currentTime - _lastValidityCheckTime;
const bool cacheIsTooOld = diff >= std::chrono::hours(1);
const bool tokenChanged = lastQueryToken != _token;
if (tokenChanged) {
response =
const auto checkToken = [&]() -> bool {
const auto response =
cli.Get("/oauth2/validate", httplib::Params{}, headers);
queryTime = std::chrono::system_clock::now();
lastQueryToken = _token;
return response && response->status == 200;
_lastValidityCheckTime = std::chrono::system_clock::now();
_lastValidityCheckValue = _token;
_lastValidityCheckResult = response && response->status == 200;
if (!_lastValidityCheckResult) {
blog(LOG_INFO, "Twitch token %s is not valid!",
_name.c_str());
}
return _lastValidityCheckResult;
};
const bool tokenChanged = _lastValidityCheckValue != _token;
if (tokenChanged) {
return checkToken();
}
// No point in checking again as token will not become valid again
if (!forceUpdate && response && response->status != 200) {
blog(LOG_INFO, "Twitch token %s is not valid!", _name.c_str());
return false;
if (!forceUpdate && !cacheIsTooOld) {
return _lastValidityCheckResult;
}
if (!forceUpdate && !cacheIsTooOld && response) {
return response->status == 200;
}
response = cli.Get("/oauth2/validate", httplib::Params{}, headers);
queryTime = std::chrono::system_clock::now();
lastQueryToken = _token;
return response && response->status == 200;
return checkToken();
}
TwitchToken *GetTwitchTokenByName(const QString &name)

View File

@ -33,6 +33,10 @@ private:
class TwitchToken : public Item {
public:
TwitchToken() = default;
TwitchToken(const TwitchToken &);
TwitchToken &operator=(const TwitchToken &);
static std::shared_ptr<Item> Create()
{
return std::make_shared<TwitchToken>();
@ -55,6 +59,10 @@ public:
private:
std::string _token;
mutable std::mutex _cacheMutex;
mutable std::string _lastValidityCheckValue;
mutable bool _lastValidityCheckResult = false;
mutable std::chrono::system_clock::time_point _lastValidityCheckTime;
std::string _userID;
std::set<TokenOption> _tokenOptions = TokenOption::GetAllTokenOptions();
std::shared_ptr<EventSub> _eventSub;