mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-12 10:18:27 -04:00
Renamings for consistency
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
using NadekoBot.Db.Models;
|
||||
using System.Text.RegularExpressions;
|
||||
using TwitchLib.Api;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace NadekoBot.Modules.Searches.Common.StreamNotifications.Providers;
|
||||
|
||||
public sealed class TwitchHelixProvider : Provider
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
|
||||
private static Regex Regex { get; } = new(@"twitch.tv/(?<name>[\w\d\-_]+)/?",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public override FollowedStream.FType Platform
|
||||
=> FollowedStream.FType.Twitch;
|
||||
|
||||
private readonly Lazy<TwitchAPI> _api;
|
||||
private readonly string _clientId;
|
||||
|
||||
public TwitchHelixProvider(IHttpClientFactory httpClientFactory, IBotCredsProvider credsProvider)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
|
||||
var creds = credsProvider.GetCreds();
|
||||
_clientId = creds.TwitchClientId;
|
||||
var clientSecret = creds.TwitchClientSecret;
|
||||
_api = new(() => new()
|
||||
{
|
||||
Helix =
|
||||
{
|
||||
Settings =
|
||||
{
|
||||
ClientId = _clientId,
|
||||
Secret = clientSecret
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<string?> EnsureTokenValidAsync()
|
||||
=> await _api.Value.Auth.GetAccessTokenAsync();
|
||||
|
||||
public override Task<bool> IsValidUrl(string url)
|
||||
{
|
||||
var match = Regex.Match(url);
|
||||
if (!match.Success)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public override Task<StreamData?> GetStreamDataByUrlAsync(string url)
|
||||
{
|
||||
var match = Regex.Match(url);
|
||||
if (match.Success)
|
||||
{
|
||||
var name = match.Groups["name"].Value;
|
||||
return GetStreamDataAsync(name);
|
||||
}
|
||||
|
||||
return Task.FromResult<StreamData?>(null);
|
||||
}
|
||||
|
||||
public override async Task<StreamData?> GetStreamDataAsync(string login)
|
||||
{
|
||||
var data = await GetStreamDataAsync(new List<string>
|
||||
{
|
||||
login
|
||||
});
|
||||
|
||||
return data.FirstOrDefault();
|
||||
}
|
||||
|
||||
public override async Task<IReadOnlyCollection<StreamData>> GetStreamDataAsync(List<string> logins)
|
||||
{
|
||||
if (logins.Count == 0)
|
||||
{
|
||||
return Array.Empty<StreamData>();
|
||||
}
|
||||
|
||||
var token = await EnsureTokenValidAsync();
|
||||
|
||||
if (token is null)
|
||||
{
|
||||
Log.Warning("Twitch client id and client secret key are not added to creds.yml or incorrect");
|
||||
return Array.Empty<StreamData>();
|
||||
}
|
||||
|
||||
using var http = _httpClientFactory.CreateClient();
|
||||
http.DefaultRequestHeaders.Clear();
|
||||
http.DefaultRequestHeaders.Add("Client-Id", _clientId);
|
||||
http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
|
||||
|
||||
var loginsSet = logins.Select(x => x.ToLowerInvariant())
|
||||
.Distinct()
|
||||
.ToHashSet();
|
||||
|
||||
var dataDict = new Dictionary<string, StreamData>();
|
||||
|
||||
foreach (var chunk in logins.Chunk(100))
|
||||
{
|
||||
try
|
||||
{
|
||||
var str = await http.GetStringAsync($"https://api.twitch.tv/helix/users"
|
||||
+ $"?{chunk.Select(x => $"login={x}").Join('&')}"
|
||||
+ $"&first=100");
|
||||
|
||||
var resObj = JsonSerializer.Deserialize<HelixUsersResponse>(str);
|
||||
|
||||
if (resObj?.Data is null || resObj.Data.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (var user in resObj.Data)
|
||||
{
|
||||
var lowerLogin = user.Login.ToLowerInvariant();
|
||||
if (loginsSet.Remove(lowerLogin))
|
||||
{
|
||||
dataDict[lowerLogin] = UserToStreamData(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Something went wrong retreiving {StreamPlatform} streams", Platform);
|
||||
return new List<StreamData>();
|
||||
}
|
||||
}
|
||||
|
||||
// any item left over loginsSet is an invalid username
|
||||
foreach (var login in loginsSet)
|
||||
{
|
||||
_failingStreams.TryAdd(login, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
// only get streams for users which exist
|
||||
foreach (var chunk in dataDict.Keys.Chunk(100))
|
||||
{
|
||||
try
|
||||
{
|
||||
var str = await http.GetStringAsync($"https://api.twitch.tv/helix/streams"
|
||||
+ $"?{chunk.Select(x => $"user_login={x}").Join('&')}"
|
||||
+ "&first=100");
|
||||
|
||||
var res = JsonSerializer.Deserialize<HelixStreamsResponse>(str);
|
||||
|
||||
if (res?.Data is null || res.Data.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var helixStreamData in res.Data)
|
||||
{
|
||||
var login = helixStreamData.UserLogin.ToLowerInvariant();
|
||||
if (dataDict.TryGetValue(login, out var old))
|
||||
{
|
||||
dataDict[login] = FillStreamData(old, helixStreamData);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Something went wrong retreiving {StreamPlatform} streams", Platform);
|
||||
return new List<StreamData>();
|
||||
}
|
||||
}
|
||||
|
||||
return dataDict.Values;
|
||||
}
|
||||
|
||||
private StreamData UserToStreamData(HelixUsersResponse.User user)
|
||||
=> new()
|
||||
{
|
||||
UniqueName = user.Login,
|
||||
Name = user.DisplayName,
|
||||
AvatarUrl = user.ProfileImageUrl,
|
||||
IsLive = false,
|
||||
StreamUrl = $"https://twitch.tv/{user.Login}",
|
||||
StreamType = FollowedStream.FType.Twitch,
|
||||
Preview = user.OfflineImageUrl
|
||||
};
|
||||
|
||||
private StreamData FillStreamData(StreamData partial, HelixStreamsResponse.StreamData apiData)
|
||||
=> partial with
|
||||
{
|
||||
StreamType = FollowedStream.FType.Twitch,
|
||||
Viewers = apiData.ViewerCount,
|
||||
Title = apiData.Title,
|
||||
IsLive = apiData.Type == "live",
|
||||
Preview = apiData.ThumbnailUrl
|
||||
.Replace("{width}", "640")
|
||||
.Replace("{height}", "480"),
|
||||
Game = apiData.GameName,
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user