mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05: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 IEmbedBuilderService _eb;
 | 
			
		||||
    private readonly SearchesConfigService _config;
 | 
			
		||||
 | 
			
		||||
    public TypedKey<List<StreamData>> StreamsOnlineKey { get; }
 | 
			
		||||
    public TypedKey<List<StreamData>> StreamsOfflineKey { get; }
 | 
			
		||||
@@ -49,14 +50,16 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
        IHttpClientFactory httpFactory,
 | 
			
		||||
        Bot bot,
 | 
			
		||||
        IPubSub pubSub,
 | 
			
		||||
        IEmbedBuilderService eb)
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        SearchesConfigService config)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _strings = strings;
 | 
			
		||||
        _pubSub = pubSub;
 | 
			
		||||
        _eb = eb;
 | 
			
		||||
        
 | 
			
		||||
        _config = config;
 | 
			
		||||
 | 
			
		||||
        _streamTracker = new(httpFactory, creds);
 | 
			
		||||
 | 
			
		||||
        StreamsOnlineKey = new("streams.online");
 | 
			
		||||
@@ -69,34 +72,34 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
        {
 | 
			
		||||
            var ids = client.GetGuildIds();
 | 
			
		||||
            var guildConfigs = uow.Set<GuildConfig>()
 | 
			
		||||
                                  .AsQueryable()
 | 
			
		||||
                                  .Include(x => x.FollowedStreams)
 | 
			
		||||
                                  .Where(x => ids.Contains(x.GuildId))
 | 
			
		||||
                                  .ToList();
 | 
			
		||||
                .AsQueryable()
 | 
			
		||||
                .Include(x => x.FollowedStreams)
 | 
			
		||||
                .Where(x => ids.Contains(x.GuildId))
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
            _offlineNotificationServers = new(guildConfigs
 | 
			
		||||
                                              .Where(gc => gc.NotifyStreamOffline)
 | 
			
		||||
                                              .Select(x => x.GuildId)
 | 
			
		||||
                                              .ToList());
 | 
			
		||||
            
 | 
			
		||||
                .Where(gc => gc.NotifyStreamOffline)
 | 
			
		||||
                .Select(x => x.GuildId)
 | 
			
		||||
                .ToList());
 | 
			
		||||
 | 
			
		||||
            _deleteOnOfflineServers = new(guildConfigs
 | 
			
		||||
                                              .Where(gc => gc.DeleteStreamOnlineMessage)
 | 
			
		||||
                                              .Select(x => x.GuildId)
 | 
			
		||||
                                              .ToList());
 | 
			
		||||
                .Where(gc => gc.DeleteStreamOnlineMessage)
 | 
			
		||||
                .Select(x => x.GuildId)
 | 
			
		||||
                .ToList());
 | 
			
		||||
 | 
			
		||||
            var followedStreams = guildConfigs.SelectMany(x => x.FollowedStreams).ToList();
 | 
			
		||||
 | 
			
		||||
            _shardTrackedStreams = followedStreams.GroupBy(x => new
 | 
			
		||||
                                                  {
 | 
			
		||||
                                                      x.Type,
 | 
			
		||||
                                                      Name = x.Username.ToLower()
 | 
			
		||||
                                                  })
 | 
			
		||||
                                                  .ToList()
 | 
			
		||||
                                                  .ToDictionary(
 | 
			
		||||
                                                      x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
 | 
			
		||||
                                                      x => x.GroupBy(y => y.GuildId)
 | 
			
		||||
                                                            .ToDictionary(y => y.Key,
 | 
			
		||||
                                                                y => y.AsEnumerable().ToHashSet()));
 | 
			
		||||
                {
 | 
			
		||||
                    x.Type,
 | 
			
		||||
                    Name = x.Username.ToLower()
 | 
			
		||||
                })
 | 
			
		||||
                .ToList()
 | 
			
		||||
                .ToDictionary(
 | 
			
		||||
                    x => new StreamDataKey(x.Key.Type, x.Key.Name.ToLower()),
 | 
			
		||||
                    x => x.GroupBy(y => y.GuildId)
 | 
			
		||||
                        .ToDictionary(y => y.Key,
 | 
			
		||||
                            y => y.AsEnumerable().ToHashSet()));
 | 
			
		||||
 | 
			
		||||
            // shard 0 will keep track of when there are no more guilds which track a stream
 | 
			
		||||
            if (client.ShardId == 0)
 | 
			
		||||
@@ -107,12 +110,12 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
                    _streamTracker.AddLastData(fs.CreateKey(), null, false);
 | 
			
		||||
 | 
			
		||||
                _trackCounter = allFollowedStreams.GroupBy(x => new
 | 
			
		||||
                                                  {
 | 
			
		||||
                                                      x.Type,
 | 
			
		||||
                                                      Name = x.Username.ToLower()
 | 
			
		||||
                                                  })
 | 
			
		||||
                                                  .ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
 | 
			
		||||
                                                      x => x.Select(fs => fs.GuildId).ToHashSet());
 | 
			
		||||
                    {
 | 
			
		||||
                        x.Type,
 | 
			
		||||
                        Name = x.Username.ToLower()
 | 
			
		||||
                    })
 | 
			
		||||
                    .ToDictionary(x => new StreamDataKey(x.Key.Type, x.Key.Name),
 | 
			
		||||
                        x => x.Select(fs => fs.GuildId).ToHashSet());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -152,7 +155,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                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();
 | 
			
		||||
                foreach (var kvp in deleteGroups)
 | 
			
		||||
@@ -165,9 +168,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
                        string.Join(", ", kvp.Value));
 | 
			
		||||
 | 
			
		||||
                    var toDelete = uow.Set<FollowedStream>()
 | 
			
		||||
                                      .AsQueryable()
 | 
			
		||||
                                      .Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
 | 
			
		||||
                                      .ToList();
 | 
			
		||||
                        .AsQueryable()
 | 
			
		||||
                        .Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
 | 
			
		||||
                        .ToList();
 | 
			
		||||
 | 
			
		||||
                    uow.RemoveRange(toDelete);
 | 
			
		||||
                    await uow.SaveChangesAsync();
 | 
			
		||||
@@ -246,17 +249,17 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
            if (_shardTrackedStreams.TryGetValue(key, out var fss))
 | 
			
		||||
            {
 | 
			
		||||
                await fss
 | 
			
		||||
                      // send offline stream notifications only to guilds which enable it with .stoff
 | 
			
		||||
                      .SelectMany(x => x.Value)
 | 
			
		||||
                      .Where(x => _offlineNotificationServers.Contains(x.GuildId))
 | 
			
		||||
                      .Select(fs => _client.GetGuild(fs.GuildId)
 | 
			
		||||
                                           ?.GetTextChannel(fs.ChannelId)
 | 
			
		||||
                                           ?.EmbedAsync(GetEmbed(fs.GuildId, stream)))
 | 
			
		||||
                      .WhenAll();
 | 
			
		||||
                    // send offline stream notifications only to guilds which enable it with .stoff
 | 
			
		||||
                    .SelectMany(x => x.Value)
 | 
			
		||||
                    .Where(x => _offlineNotificationServers.Contains(x.GuildId))
 | 
			
		||||
                    .Select(fs => _client.GetGuild(fs.GuildId)
 | 
			
		||||
                        ?.GetTextChannel(fs.ChannelId)
 | 
			
		||||
                        ?.EmbedAsync(GetEmbed(fs.GuildId, stream)))
 | 
			
		||||
                    .WhenAll();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private async ValueTask HandleStreamsOnline(List<StreamData> onlineStreams)
 | 
			
		||||
    {
 | 
			
		||||
@@ -266,30 +269,30 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
            if (_shardTrackedStreams.TryGetValue(key, out var fss))
 | 
			
		||||
            {
 | 
			
		||||
                var messages = await fss.SelectMany(x => x.Value)
 | 
			
		||||
                         .Select(async fs =>
 | 
			
		||||
                         {
 | 
			
		||||
                             var textChannel = _client.GetGuild(fs.GuildId)?.GetTextChannel(fs.ChannelId);
 | 
			
		||||
                    .Select(async fs =>
 | 
			
		||||
                    {
 | 
			
		||||
                        var textChannel = _client.GetGuild(fs.GuildId)?.GetTextChannel(fs.ChannelId);
 | 
			
		||||
 | 
			
		||||
                             if (textChannel is null)
 | 
			
		||||
                                 return default;
 | 
			
		||||
                        if (textChannel is null)
 | 
			
		||||
                            return default;
 | 
			
		||||
 | 
			
		||||
                             var rep = new ReplacementBuilder().WithOverride("%user%", () => fs.Username)
 | 
			
		||||
                                                               .WithOverride("%platform%", () => fs.Type.ToString())
 | 
			
		||||
                                                               .Build();
 | 
			
		||||
                        var rep = new ReplacementBuilder().WithOverride("%user%", () => fs.Username)
 | 
			
		||||
                            .WithOverride("%platform%", () => fs.Type.ToString())
 | 
			
		||||
                            .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
 | 
			
		||||
                // when streams go offline, any server which
 | 
			
		||||
                // has the online stream message deletion feature
 | 
			
		||||
@@ -297,16 +300,15 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var pairs = messages
 | 
			
		||||
                                .Where(x => x != default)
 | 
			
		||||
                                .Select(x => (x.Item1, x.Item2))
 | 
			
		||||
                                .ToList();
 | 
			
		||||
                        .Where(x => x != default)
 | 
			
		||||
                        .Select(x => (x.Item1, x.Item2))
 | 
			
		||||
                        .ToList();
 | 
			
		||||
 | 
			
		||||
                    if (pairs.Count > 0)
 | 
			
		||||
                        await OnlineMessagesSent(key.Type, key.Name, pairs);
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -384,10 +386,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
        await using (var uow = _db.GetDbContext())
 | 
			
		||||
        {
 | 
			
		||||
            var fss = uow.Set<FollowedStream>()
 | 
			
		||||
                         .AsQueryable()
 | 
			
		||||
                         .Where(x => x.GuildId == guildId)
 | 
			
		||||
                         .OrderBy(x => x.Id)
 | 
			
		||||
                         .ToList();
 | 
			
		||||
                .AsQueryable()
 | 
			
		||||
                .Where(x => x.GuildId == guildId)
 | 
			
		||||
                .OrderBy(x => x.Id)
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
            // out of range
 | 
			
		||||
            if (fss.Count <= index)
 | 
			
		||||
@@ -450,7 +452,9 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
                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;
 | 
			
		||||
 | 
			
		||||
            gc.FollowedStreams.Add(fs);
 | 
			
		||||
@@ -475,10 +479,10 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
    public IEmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
 | 
			
		||||
    {
 | 
			
		||||
        var embed = _eb.Create()
 | 
			
		||||
                       .WithTitle(status.Name)
 | 
			
		||||
                       .WithUrl(status.StreamUrl)
 | 
			
		||||
                       .WithDescription(status.StreamUrl)
 | 
			
		||||
                       .AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true);
 | 
			
		||||
            .WithTitle(status.Name)
 | 
			
		||||
            .WithUrl(status.StreamUrl)
 | 
			
		||||
            .WithDescription(status.StreamUrl)
 | 
			
		||||
            .AddField(GetText(guildId, strs.status), status.IsLive ? "🟢 Online" : "🔴 Offline", true);
 | 
			
		||||
 | 
			
		||||
        if (showViewers)
 | 
			
		||||
        {
 | 
			
		||||
@@ -527,7 +531,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
 | 
			
		||||
 | 
			
		||||
        return newValue;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    public bool ToggleStreamOnlineDelete(ulong guildId)
 | 
			
		||||
    {
 | 
			
		||||
        using var uow = _db.GetDbContext();
 | 
			
		||||
 
 | 
			
		||||
@@ -8,18 +8,18 @@ public partial class SearchesConfig : ICloneable<SearchesConfig>
 | 
			
		||||
{
 | 
			
		||||
    [Comment("DO NOT CHANGE")]
 | 
			
		||||
    public int Version { get; set; } = 0;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    [Comment(@"Which engine should .search command
 | 
			
		||||
'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
 | 
			
		||||
'searx' - requires at least one searx instance specified in the 'searxInstances' property below")]
 | 
			
		||||
    public WebSearchEngine WebSearchEngine { get; set; } = WebSearchEngine.Google_Scrape;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    [Comment(@"Which engine should .image command use
 | 
			
		||||
'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")]
 | 
			
		||||
    public ImgSearchEngine ImgSearchEngine { get; set; } = ImgSearchEngine.Google;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    [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.
 | 
			
		||||
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>();
 | 
			
		||||
 | 
			
		||||
    [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
 | 
			
		||||
 
 | 
			
		||||
@@ -17,17 +17,22 @@ public class SearchesConfigService : ConfigServiceBase<SearchesConfig>
 | 
			
		||||
            sc => sc.WebSearchEngine,
 | 
			
		||||
            ConfigParsers.InsensitiveEnum,
 | 
			
		||||
            ConfigPrinters.ToString);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        AddParsedProp("imgEngine",
 | 
			
		||||
            sc => sc.ImgSearchEngine,
 | 
			
		||||
            ConfigParsers.InsensitiveEnum,
 | 
			
		||||
            ConfigPrinters.ToString);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        AddParsedProp("ytProvider",
 | 
			
		||||
            sc => sc.YtProvider,
 | 
			
		||||
            ConfigParsers.InsensitiveEnum,
 | 
			
		||||
            ConfigPrinters.ToString);
 | 
			
		||||
 | 
			
		||||
        AddParsedProp("followedStreams.maxCount",
 | 
			
		||||
            sc => sc.FollowedStreams.MaxCount,
 | 
			
		||||
            ConfigParsers.InsensitiveEnum,
 | 
			
		||||
            ConfigPrinters.ToString);
 | 
			
		||||
 | 
			
		||||
        Migrate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -41,5 +46,13 @@ public class SearchesConfigService : ConfigServiceBase<SearchesConfig>
 | 
			
		||||
                c.WebSearchEngine = WebSearchEngine.Google_Scrape;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (data.Version < 2)
 | 
			
		||||
        {
 | 
			
		||||
            ModifyConfig(c =>
 | 
			
		||||
            {
 | 
			
		||||
                c.Version = 2;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user