mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
Added followedStreams.maxCount to searches configx
This commit is contained in:
@@ -28,6 +28,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
|
|
||||||
private readonly IPubSub _pubSub;
|
private readonly IPubSub _pubSub;
|
||||||
private readonly IEmbedBuilderService _eb;
|
private readonly IEmbedBuilderService _eb;
|
||||||
|
private readonly SearchesConfigService _config;
|
||||||
|
|
||||||
public TypedKey<List<StreamData>> StreamsOnlineKey { get; }
|
public TypedKey<List<StreamData>> StreamsOnlineKey { get; }
|
||||||
public TypedKey<List<StreamData>> StreamsOfflineKey { get; }
|
public TypedKey<List<StreamData>> StreamsOfflineKey { get; }
|
||||||
@@ -49,14 +50,16 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
IHttpClientFactory httpFactory,
|
IHttpClientFactory httpFactory,
|
||||||
Bot bot,
|
Bot bot,
|
||||||
IPubSub pubSub,
|
IPubSub pubSub,
|
||||||
IEmbedBuilderService eb)
|
IEmbedBuilderService eb,
|
||||||
|
SearchesConfigService config)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_client = client;
|
_client = client;
|
||||||
_strings = strings;
|
_strings = strings;
|
||||||
_pubSub = pubSub;
|
_pubSub = pubSub;
|
||||||
_eb = eb;
|
_eb = eb;
|
||||||
|
_config = config;
|
||||||
|
|
||||||
_streamTracker = new(httpFactory, creds);
|
_streamTracker = new(httpFactory, creds);
|
||||||
|
|
||||||
StreamsOnlineKey = new("streams.online");
|
StreamsOnlineKey = new("streams.online");
|
||||||
@@ -69,34 +72,34 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
{
|
{
|
||||||
var ids = client.GetGuildIds();
|
var ids = client.GetGuildIds();
|
||||||
var guildConfigs = uow.Set<GuildConfig>()
|
var guildConfigs = uow.Set<GuildConfig>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Include(x => x.FollowedStreams)
|
.Include(x => x.FollowedStreams)
|
||||||
.Where(x => ids.Contains(x.GuildId))
|
.Where(x => ids.Contains(x.GuildId))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
_offlineNotificationServers = new(guildConfigs
|
_offlineNotificationServers = new(guildConfigs
|
||||||
.Where(gc => gc.NotifyStreamOffline)
|
.Where(gc => gc.NotifyStreamOffline)
|
||||||
.Select(x => x.GuildId)
|
.Select(x => x.GuildId)
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
_deleteOnOfflineServers = new(guildConfigs
|
_deleteOnOfflineServers = new(guildConfigs
|
||||||
.Where(gc => gc.DeleteStreamOnlineMessage)
|
.Where(gc => gc.DeleteStreamOnlineMessage)
|
||||||
.Select(x => x.GuildId)
|
.Select(x => x.GuildId)
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList();
|
var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList();
|
||||||
|
|
||||||
_shardTrackedStreams = followedStreams.GroupBy(x => new
|
_shardTrackedStreams = followedStreams.GroupBy(x => new
|
||||||
{
|
{
|
||||||
x.Type,
|
x.Type,
|
||||||
Name = x.Username.ToLower()
|
Name = x.Username.ToLower()
|
||||||
})
|
})
|
||||||
.ToList()
|
.ToList()
|
||||||
.ToDictionary(
|
.ToDictionary(
|
||||||
x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
|
x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
|
||||||
x => x.GroupBy(y => y.GuildId)
|
x => x.GroupBy(y => y.GuildId)
|
||||||
.ToDictionary(y => y.Key,
|
.ToDictionary(y => y.Key,
|
||||||
y => y.AsEnumerable().ToHashSet()));
|
y => y.AsEnumerable().ToHashSet()));
|
||||||
|
|
||||||
// shard 0 will keep track of when there are no more guilds which track a stream
|
// shard 0 will keep track of when there are no more guilds which track a stream
|
||||||
if (client.ShardId == 0)
|
if (client.ShardId == 0)
|
||||||
@@ -107,12 +110,12 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
_streamTracker.AddLastData(fs.CreateKey(), null, false);
|
_streamTracker.AddLastData(fs.CreateKey(), null, false);
|
||||||
|
|
||||||
_trackCounter = allFollowedStreams.GroupBy(x => new
|
_trackCounter = allFollowedStreams.GroupBy(x => new
|
||||||
{
|
{
|
||||||
x.Type,
|
x.Type,
|
||||||
Name = x.Username.ToLower()
|
Name = x.Username.ToLower()
|
||||||
})
|
})
|
||||||
.ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
|
.ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
|
||||||
x => x.Select(fs => fs.GuildId).ToHashSet());
|
x => x.Select(fs => fs.GuildId).ToHashSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +155,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var deleteGroups = failingStreams.GroupBy(x => x.Type)
|
var deleteGroups = failingStreams.GroupBy(x => x.Type)
|
||||||
.ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList());
|
.ToDictionary(x => x.Key, x => x.Select(y => y.Name).ToList());
|
||||||
|
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
foreach (var kvp in deleteGroups)
|
foreach (var kvp in deleteGroups)
|
||||||
@@ -165,9 +168,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
string.Join(", ", kvp.Value));
|
string.Join(", ", kvp.Value));
|
||||||
|
|
||||||
var toDelete = uow.Set<FollowedStream>()
|
var toDelete = uow.Set<FollowedStream>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
|
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
uow.RemoveRange(toDelete);
|
uow.RemoveRange(toDelete);
|
||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
@@ -246,17 +249,17 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
if (_shardTrackedStreams.TryGetValue(key, out var fss))
|
if (_shardTrackedStreams.TryGetValue(key, out var fss))
|
||||||
{
|
{
|
||||||
await fss
|
await fss
|
||||||
// send offline stream notifications only to guilds which enable it with .stoff
|
// send offline stream notifications only to guilds which enable it with .stoff
|
||||||
.SelectMany(x => x.Value)
|
.SelectMany(x => x.Value)
|
||||||
.Where(x => _offlineNotificationServers.Contains(x.GuildId))
|
.Where(x => _offlineNotificationServers.Contains(x.GuildId))
|
||||||
.Select(fs => _client.GetGuild(fs.GuildId)
|
.Select(fs => _client.GetGuild(fs.GuildId)
|
||||||
?.GetTextChannel(fs.ChannelId)
|
?.GetTextChannel(fs.ChannelId)
|
||||||
?.EmbedAsync(GetEmbed(fs.GuildId, stream)))
|
?.EmbedAsync(GetEmbed(fs.GuildId, stream)))
|
||||||
.WhenAll();
|
.WhenAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async ValueTask HandleStreamsOnline(List<StreamData> onlineStreams)
|
private async ValueTask HandleStreamsOnline(List<StreamData> onlineStreams)
|
||||||
{
|
{
|
||||||
@@ -266,30 +269,30 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
if (_shardTrackedStreams.TryGetValue(key, out var fss))
|
if (_shardTrackedStreams.TryGetValue(key, out var fss))
|
||||||
{
|
{
|
||||||
var messages = await fss.SelectMany(x => x.Value)
|
var messages = await fss.SelectMany(x => x.Value)
|
||||||
.Select(async fs =>
|
.Select(async fs =>
|
||||||
{
|
{
|
||||||
var textChannel = _client.GetGuild(fs.GuildId)?.GetTextChannel(fs.ChannelId);
|
var textChannel = _client.GetGuild(fs.GuildId)?.GetTextChannel(fs.ChannelId);
|
||||||
|
|
||||||
if (textChannel is null)
|
if (textChannel is null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
var rep = new ReplacementBuilder().WithOverride("%user%", () => fs.Username)
|
var rep = new ReplacementBuilder().WithOverride("%user%", () => fs.Username)
|
||||||
.WithOverride("%platform%", () => fs.Type.ToString())
|
.WithOverride("%platform%", () => fs.Type.ToString())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var message = string.IsNullOrWhiteSpace(fs.Message) ? "" : rep.Replace(fs.Message);
|
var message = string.IsNullOrWhiteSpace(fs.Message) ? "" : rep.Replace(fs.Message);
|
||||||
|
|
||||||
var msg = await textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream, false), message);
|
var msg = await textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream, false), message);
|
||||||
|
|
||||||
|
// only cache the ids of channel/message pairs
|
||||||
|
if (_deleteOnOfflineServers.Contains(fs.GuildId))
|
||||||
|
return (textChannel.Id, msg.Id);
|
||||||
|
else
|
||||||
|
return default;
|
||||||
|
})
|
||||||
|
.WhenAll();
|
||||||
|
|
||||||
// only cache the ids of channel/message pairs
|
|
||||||
if(_deleteOnOfflineServers.Contains(fs.GuildId))
|
|
||||||
return (textChannel.Id, msg.Id);
|
|
||||||
else
|
|
||||||
return default;
|
|
||||||
})
|
|
||||||
.WhenAll();
|
|
||||||
|
|
||||||
|
|
||||||
// push online stream messages to redis
|
// push online stream messages to redis
|
||||||
// when streams go offline, any server which
|
// when streams go offline, any server which
|
||||||
// has the online stream message deletion feature
|
// has the online stream message deletion feature
|
||||||
@@ -297,16 +300,15 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var pairs = messages
|
var pairs = messages
|
||||||
.Where(x => x != default)
|
.Where(x => x != default)
|
||||||
.Select(x => (x.Item1, x.Item2))
|
.Select(x => (x.Item1, x.Item2))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (pairs.Count > 0)
|
if (pairs.Count > 0)
|
||||||
await OnlineMessagesSent(key.Type, key.Name, pairs);
|
await OnlineMessagesSent(key.Type, key.Name, pairs);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,10 +386,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
await using (var uow = _db.GetDbContext())
|
await using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
var fss = uow.Set<FollowedStream>()
|
var fss = uow.Set<FollowedStream>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.OrderBy(x => x.Id)
|
.OrderBy(x => x.Id)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// out of range
|
// out of range
|
||||||
if (fss.Count <= index)
|
if (fss.Count <= index)
|
||||||
@@ -450,7 +452,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
GuildId = guildId
|
GuildId = guildId
|
||||||
};
|
};
|
||||||
|
|
||||||
if (gc.FollowedStreams.Count >= 10)
|
var config = _config.Data;
|
||||||
|
if (config.FollowedStreams.MaxCount is not -1
|
||||||
|
&& gc.FollowedStreams.Count >= config.FollowedStreams.MaxCount)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
gc.FollowedStreams.Add(fs);
|
gc.FollowedStreams.Add(fs);
|
||||||
@@ -475,10 +479,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
public IEmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
|
public IEmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
|
||||||
{
|
{
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithTitle(status.Name)
|
.WithTitle(status.Name)
|
||||||
.WithUrl(status.StreamUrl)
|
.WithUrl(status.StreamUrl)
|
||||||
.WithDescription(status.StreamUrl)
|
.WithDescription(status.StreamUrl)
|
||||||
.AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true);
|
.AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true);
|
||||||
|
|
||||||
if (showViewers)
|
if (showViewers)
|
||||||
{
|
{
|
||||||
@@ -527,7 +531,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
|||||||
|
|
||||||
return newValue;
|
return newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ToggleStreamOnlineDelete(ulong guildId)
|
public bool ToggleStreamOnlineDelete(ulong guildId)
|
||||||
{
|
{
|
||||||
using var uow = _db.GetDbContext();
|
using var uow = _db.GetDbContext();
|
||||||
|
@@ -8,18 +8,18 @@ public partial class SearchesConfig : ICloneable<SearchesConfig>
|
|||||||
{
|
{
|
||||||
[Comment("DO NOT CHANGE")]
|
[Comment("DO NOT CHANGE")]
|
||||||
public int Version { get; set; } = 0;
|
public int Version { get; set; } = 0;
|
||||||
|
|
||||||
[Comment(@"Which engine should .search command
|
[Comment(@"Which engine should .search command
|
||||||
'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys.
|
'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys.
|
||||||
'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml
|
'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml
|
||||||
'searx' - requires at least one searx instance specified in the 'searxInstances' property below")]
|
'searx' - requires at least one searx instance specified in the 'searxInstances' property below")]
|
||||||
public WebSearchEngine WebSearchEngine { get; set; } = WebSearchEngine.Google_Scrape;
|
public WebSearchEngine WebSearchEngine { get; set; } = WebSearchEngine.Google_Scrape;
|
||||||
|
|
||||||
[Comment(@"Which engine should .image command use
|
[Comment(@"Which engine should .image command use
|
||||||
'google'- official google api. googleApiKey and google.imageSearchId set in creds.yml
|
'google'- official google api. googleApiKey and google.imageSearchId set in creds.yml
|
||||||
'searx' requires at least one searx instance specified in the 'searxInstances' property below")]
|
'searx' requires at least one searx instance specified in the 'searxInstances' property below")]
|
||||||
public ImgSearchEngine ImgSearchEngine { get; set; } = ImgSearchEngine.Google;
|
public ImgSearchEngine ImgSearchEngine { get; set; } = ImgSearchEngine.Google;
|
||||||
|
|
||||||
|
|
||||||
[Comment(@"Which search provider will be used for the `.youtube` command.
|
[Comment(@"Which search provider will be used for the `.youtube` command.
|
||||||
|
|
||||||
@@ -55,6 +55,15 @@ Use a fully qualified url. Example: https://my-invidious-instance.mydomain.com
|
|||||||
Instances specified must have api available.
|
Instances specified must have api available.
|
||||||
You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending")]
|
You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending")]
|
||||||
public List<string> InvidiousInstances { get; set; } = new List<string>();
|
public List<string> InvidiousInstances { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
[Comment("Maximum number of followed streams per server")]
|
||||||
|
public FollowedStreamConfig FollowedStreams { get; set; } = new FollowedStreamConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class FollowedStreamConfig
|
||||||
|
{
|
||||||
|
[Comment("Maximum number of streams that each server can follow. -1 for infinite")]
|
||||||
|
public int MaxCount { get; set; } = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum YoutubeSearcher
|
public enum YoutubeSearcher
|
||||||
|
@@ -17,17 +17,22 @@ public class SearchesConfigService : ConfigServiceBase<SearchesConfig>
|
|||||||
sc => sc.WebSearchEngine,
|
sc => sc.WebSearchEngine,
|
||||||
ConfigParsers.InsensitiveEnum,
|
ConfigParsers.InsensitiveEnum,
|
||||||
ConfigPrinters.ToString);
|
ConfigPrinters.ToString);
|
||||||
|
|
||||||
AddParsedProp("imgEngine",
|
AddParsedProp("imgEngine",
|
||||||
sc => sc.ImgSearchEngine,
|
sc => sc.ImgSearchEngine,
|
||||||
ConfigParsers.InsensitiveEnum,
|
ConfigParsers.InsensitiveEnum,
|
||||||
ConfigPrinters.ToString);
|
ConfigPrinters.ToString);
|
||||||
|
|
||||||
AddParsedProp("ytProvider",
|
AddParsedProp("ytProvider",
|
||||||
sc => sc.YtProvider,
|
sc => sc.YtProvider,
|
||||||
ConfigParsers.InsensitiveEnum,
|
ConfigParsers.InsensitiveEnum,
|
||||||
ConfigPrinters.ToString);
|
ConfigPrinters.ToString);
|
||||||
|
|
||||||
|
AddParsedProp("followedStreams.maxCount",
|
||||||
|
sc => sc.FollowedStreams.MaxCount,
|
||||||
|
ConfigParsers.InsensitiveEnum,
|
||||||
|
ConfigPrinters.ToString);
|
||||||
|
|
||||||
Migrate();
|
Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,5 +46,13 @@ public class SearchesConfigService : ConfigServiceBase<SearchesConfig>
|
|||||||
c.WebSearchEngine = WebSearchEngine.Google_Scrape;
|
c.WebSearchEngine = WebSearchEngine.Google_Scrape;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.Version < 2)
|
||||||
|
{
|
||||||
|
ModifyConfig(c =>
|
||||||
|
{
|
||||||
|
c.Version = 2;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user