mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-04-30 03:22:04 -04:00
YouTube PubSub
This commit is contained in:
41
KfChatDotNetBot/Models/YouTubeApiModels.cs
Normal file
41
KfChatDotNetBot/Models/YouTubeApiModels.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace KfChatDotNetBot.Models;
|
||||
|
||||
public class YouTubeApiModels
|
||||
{
|
||||
public class ContentDetailsRoot
|
||||
{
|
||||
[JsonPropertyName("kind")]
|
||||
public required string Kind { get; set; }
|
||||
[JsonPropertyName("items")]
|
||||
public required List<ItemModel> Items { get; set; }
|
||||
}
|
||||
|
||||
public class SnippetModel
|
||||
{
|
||||
[JsonPropertyName("publishedAt")]
|
||||
public required DateTime PublishedAt { get; set; }
|
||||
[JsonPropertyName("channelId")]
|
||||
public required string ChannelId { get; set; }
|
||||
[JsonPropertyName("title")]
|
||||
public required string Title { get; set; }
|
||||
[JsonPropertyName("description")]
|
||||
public required string Description { get; set; }
|
||||
[JsonPropertyName("channelTitle")]
|
||||
public required string ChannelTitle { get; set; }
|
||||
// "none", "live", "upcoming"
|
||||
[JsonPropertyName("liveBroadcastContent")]
|
||||
public required string LiveBroadcastContent { get; set; }
|
||||
}
|
||||
|
||||
public class ItemModel
|
||||
{
|
||||
[JsonPropertyName("kind")]
|
||||
public required string Kind { get; set; }
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
[JsonPropertyName("snippet")]
|
||||
public required SnippetModel Snippet { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ public class BotServices
|
||||
private PeerTube? _peerTubeStatusCheck;
|
||||
private Owncast? _owncastStatusCheck;
|
||||
private ShuffleDotUs? _shuffleDotUs;
|
||||
private YouTubePubSub? _youTubePubSub;
|
||||
public OpenRouter? OpenRouter;
|
||||
|
||||
private Task? _websocketWatchdog;
|
||||
@@ -130,6 +131,20 @@ public class BotServices
|
||||
_shuffleDotUs.OnLatestBetUpdated += ShuffleOnLatestBetUpdated;
|
||||
await _shuffleDotUs.StartWsClient();
|
||||
}
|
||||
|
||||
private async Task BuildYouTubePubSub()
|
||||
{
|
||||
var settings = await SettingsProvider.GetMultipleValuesAsync([BuiltIn.Keys.YouTubePubSubEnabled]);
|
||||
if (!settings[BuiltIn.Keys.YouTubePubSubEnabled].ToBoolean())
|
||||
{
|
||||
_logger.Debug("YouTube PubSub is disabled");
|
||||
return;
|
||||
}
|
||||
_youTubePubSub = new YouTubePubSub(_cancellationToken);
|
||||
_youTubePubSub.OnNewVideo += YouTubePubSubOnNewVideo;
|
||||
await _youTubePubSub.Connect();
|
||||
_logger.Info("Built YouTube PubSub");
|
||||
}
|
||||
|
||||
private async Task BuildDiscord()
|
||||
{
|
||||
@@ -393,7 +408,8 @@ public class BotServices
|
||||
var settings = await SettingsProvider.GetMultipleValuesAsync([
|
||||
BuiltIn.Keys.KickEnabled, BuiltIn.Keys.HowlggEnabled, BuiltIn.Keys.ChipsggEnabled,
|
||||
BuiltIn.Keys.ClashggEnabled, BuiltIn.Keys.BetBoltEnabled, BuiltIn.Keys.YeetEnabled,
|
||||
BuiltIn.Keys.RainbetEnabled, BuiltIn.Keys.PartiEnabled, BuiltIn.Keys.JackpotEnabled
|
||||
BuiltIn.Keys.RainbetEnabled, BuiltIn.Keys.PartiEnabled, BuiltIn.Keys.JackpotEnabled,
|
||||
BuiltIn.Keys.YouTubePubSubEnabled
|
||||
]);
|
||||
try
|
||||
{
|
||||
@@ -508,6 +524,15 @@ public class BotServices
|
||||
_shuffleDotUs = null!;
|
||||
await BuildShuffleDotUs();
|
||||
}
|
||||
|
||||
if (settings[BuiltIn.Keys.YouTubePubSubEnabled].ToBoolean() && _youTubePubSub != null &&
|
||||
!_youTubePubSub.IsConnected())
|
||||
{
|
||||
_logger.Error("YouTube PubSub died, recreating it");
|
||||
_youTubePubSub.Dispose();
|
||||
_youTubePubSub = null;
|
||||
await BuildYouTubePubSub();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1253,4 +1278,31 @@ public class BotServices
|
||||
};
|
||||
await SettingsProvider.SetValueAsJsonObjectAsync(BuiltIn.Keys.BossmanLastSighting, sighting);
|
||||
}
|
||||
|
||||
private void YouTubePubSubOnNewVideo(object sender, YouTubePubSubNotificationModel data)
|
||||
{
|
||||
var video = YouTubeApi.GetVideoDetails(data.Id).Result;
|
||||
if (video == null)
|
||||
{
|
||||
_logger.Error($"Caught a null when getting video details for {data.Id} which we were just notified about");
|
||||
return;
|
||||
}
|
||||
|
||||
if (video.Snippet.LiveBroadcastContent == "live")
|
||||
{
|
||||
_chatBot.SendChatMessageAsync($"@{video.Snippet.ChannelTitle} has gone live! {data.Title} {data.Url}", true).Wait(_cancellationToken);
|
||||
}
|
||||
else if (video.Snippet.LiveBroadcastContent == "upcoming")
|
||||
{
|
||||
_chatBot.SendChatMessageAsync($"@{video.Snippet.ChannelTitle} has scheduled a live stream! {data.Title} {data.Url}", true).Wait(_cancellationToken);
|
||||
}
|
||||
else if (video.Snippet.LiveBroadcastContent == "none")
|
||||
{
|
||||
_chatBot.SendChatMessageAsync($"@{video.Snippet.ChannelTitle} has uploaded a new video! {data.Title} {data.Url}", true).Wait(_cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Error($"YouTube live broadcast content '{video.Snippet.LiveBroadcastContent}' was unhandled for {data.Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,42 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Settings;
|
||||
|
||||
namespace KfChatDotNetBot.Services;
|
||||
|
||||
public class YouTubeApi
|
||||
public static class YouTubeApi
|
||||
{
|
||||
|
||||
private static async Task<HttpClient> GetHttpClient()
|
||||
{
|
||||
var settings =
|
||||
await SettingsProvider.GetMultipleValuesAsync([BuiltIn.Keys.YouTubeApiKey, BuiltIn.Keys.Proxy]);
|
||||
if (settings[BuiltIn.Keys.YouTubeApiKey].Value == null)
|
||||
throw new InvalidOperationException("YouTube API key has not been configured");
|
||||
var handler = new HttpClientHandler();
|
||||
if (settings[BuiltIn.Keys.Proxy].Value != null)
|
||||
{
|
||||
handler.Proxy = new WebProxy(settings[BuiltIn.Keys.Proxy].Value);
|
||||
handler.UseProxy = true;
|
||||
}
|
||||
handler.AutomaticDecompression = DecompressionMethods.All;
|
||||
var client = new HttpClient(handler);
|
||||
client.DefaultRequestHeaders.UserAgent.Clear();
|
||||
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("KenoGPT", "1.0"));
|
||||
client.DefaultRequestHeaders.Add("X-Goog-Api-Key", settings[BuiltIn.Keys.YouTubeApiKey].Value);
|
||||
client.BaseAddress = new Uri("https://www.googleapis.com/youtube/v3/");
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
return client;
|
||||
}
|
||||
|
||||
public static async Task<YouTubeApiModels.ItemModel?> GetVideoDetails(string id)
|
||||
{
|
||||
var client = await GetHttpClient();
|
||||
var response =
|
||||
await client.GetFromJsonAsync<YouTubeApiModels.ContentDetailsRoot>($"videos?part=snippet&id={id}");
|
||||
if (response?.Items.Count == 0) return null;
|
||||
return response?.Items[0];
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ public class YouTubePubSub : IDisposable
|
||||
_logger.Error(e);
|
||||
_logger.Error("--- Payload ---");
|
||||
_logger.Error(message.ToString());
|
||||
_logger.Error("---------------");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,14 +80,4 @@ public class YouTubePubSubNotificationModel
|
||||
public required string Title { get; set; }
|
||||
[JsonPropertyName("url")]
|
||||
public required string Url { get; set; }
|
||||
[JsonPropertyName("channel")]
|
||||
public required YouTubePubSubNotificationChannelModel Channel { get; set; }
|
||||
}
|
||||
|
||||
public class YouTubePubSubNotificationChannelModel
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; set; }
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
}
|
||||
@@ -453,6 +453,10 @@ public static class BuiltIn
|
||||
public static string KasinoDiceCleanupDelay = "Kasino.Dice.CleanupDelay";
|
||||
[BuiltInSetting("Delay in milliseconds before cleaning up wheel", SettingValueType.Text, "30000", WholeNumberRegex)]
|
||||
public static string KasinoWheelCleanupDelay = "Kasino.Wheel.CleanupDelay";
|
||||
[BuiltInSetting("Whether the YouTube PubSub Redis client is enabled", SettingValueType.Boolean, "true", BooleanRegex)]
|
||||
public static string YouTubePubSubEnabled = "YouTube.PubSub.Enabled";
|
||||
[BuiltInSetting("YouTube API Key", SettingValueType.Text, isSecret: true)]
|
||||
public static string YouTubeApiKey = "YouTube.ApiKey";
|
||||
[BuiltInSetting("Openrouter API key for hostess command", SettingValueType.Text, isSecret: true)]
|
||||
public static string OpenrouterApiKey = "Openrouter.ApiKey";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user