fix(emotes): survive BetterTTV's shared-emote endpoint going private
/emotes/shared/top now answers 403 with {"message":"unauthorized"}. Nothing
checked the status code, so that object was handed to a List<Top100>
deserializer, which threw on the very first character on every plugin start.
The throw escaped the whole loader, so the 65 global emotes -- fetched
successfully one call earlier -- were discarded along with it, and State went
back to Unloaded, arming the same failure for the next trigger.
A failed page now stops paging and keeps what the global endpoint returned. The
global call itself still throws when it fails, because without it there is
nothing to keep.
Two smaller hazards on the same path: an empty page would have made Last() throw
rather than end the loop, and a null deserialization result was dereferenced
outright. Both end the loop now.
Logged as a warning, not an error. This is a third-party endpoint changing its
access policy, not a fault in the plugin, and it does not need a stack trace in
the log at every launch.
This commit is contained in:
@@ -125,6 +125,15 @@ public static class EmoteCache
|
||||
try
|
||||
{
|
||||
var global = await Client.GetAsync(GlobalEmotes, ct);
|
||||
if (!global.IsSuccessStatusCode)
|
||||
{
|
||||
// Nothing usable at all -- let the catch below reset to Unloaded
|
||||
// so a later trigger can retry.
|
||||
throw new HttpRequestException(
|
||||
$"BetterTTV global emotes returned {(int)global.StatusCode}."
|
||||
);
|
||||
}
|
||||
|
||||
var globalList = await global.Content.ReadAsStringAsync(ct);
|
||||
|
||||
foreach (var emote in JsonSerializer.Deserialize<Emote[]>(globalList)!)
|
||||
@@ -135,9 +144,27 @@ public static class EmoteCache
|
||||
for (var i = 0; i < 15; i++)
|
||||
{
|
||||
var top = await Client.GetAsync(Top100Emotes.Format(BetterTTV, lastId), ct);
|
||||
|
||||
// The shared-emote endpoint went behind authentication and now
|
||||
// answers 403 with a JSON object. Deserializing that as a list
|
||||
// threw on every single start, and took the global emotes -- which
|
||||
// still work -- down with it. The global set is the useful half
|
||||
// anyway, so a failure here stops paging instead of the load.
|
||||
if (!top.IsSuccessStatusCode)
|
||||
{
|
||||
Plugin.LogProxy.Warning(
|
||||
$"BetterTTV shared emotes unavailable ({(int)top.StatusCode}); "
|
||||
+ "continuing with global emotes only."
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
var topList = await top.Content.ReadAsStringAsync(ct);
|
||||
|
||||
var jsonList = JsonSerializer.Deserialize<List<Top100>>(topList)!;
|
||||
var jsonList = JsonSerializer.Deserialize<List<Top100>>(topList);
|
||||
if (jsonList is not { Count: > 0 })
|
||||
break;
|
||||
|
||||
// BetterTTV occasionally returns entries with a null Code;
|
||||
// skip them so a single bad row doesn't break the whole cache.
|
||||
foreach (var emote in jsonList)
|
||||
@@ -147,7 +174,7 @@ public static class EmoteCache
|
||||
)
|
||||
Cache.TryAdd(emote.Emote.Code, emote.Emote);
|
||||
|
||||
lastId = jsonList.Last().Id;
|
||||
lastId = jsonList[^1].Id;
|
||||
}
|
||||
|
||||
SortedCodeArray = Cache.Keys.Order().ToArray();
|
||||
|
||||
Reference in New Issue
Block a user