Esempio n. 1
0
        public CustomReactionsService(PermissionService perms, NadekoStrings strings, DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow)
        {
            _log     = LogManager.GetCurrentClassLogger();
            _client  = client;
            _perms   = perms;
            _cmd     = cmd;
            _bc      = bc;
            _strings = strings;

            var items = uow.CustomReactions.GetAll().ToList();

            GuildReactions  = new ConcurrentDictionary <ulong, CustomReaction[]>(items.Where(cr => cr.GuildId != null && cr.GuildId != 0).GroupBy(cr => cr.GuildId.Value).ToDictionary(cr => cr.Key, cr => cr.ToArray()));
            GlobalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
        }
Esempio n. 2
0
        public SelfService(DiscordSocketClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
                           IBotConfigProvider bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds,
                           IDataCache cache, IImagesService imgs)
        {
            _redis        = cache.Redis;
            _bot          = bot;
            _cmdHandler   = cmdHandler;
            _db           = db;
            _log          = LogManager.GetCurrentClassLogger();
            _localization = localization;
            _strings      = strings;
            _client       = client;
            _creds        = creds;
            _bc           = bc;
            _imgs         = imgs;

            var sub = _redis.GetSubscriber();

            sub.Subscribe(_creds.RedisKey() + "_reload_images",
                          delegate { _imgs.Reload(); }, CommandFlags.FireAndForget);

            Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                foreach (var cmd in bc.BotConfig.StartupCommands)
                {
                    var prefix = _cmdHandler.GetPrefix(cmd.GuildId);
                    //if someone already has .die as their startup command, ignore it
                    if (cmd.CommandText.StartsWith(prefix + "die"))
                    {
                        continue;
                    }
                    await cmdHandler.ExecuteExternal(cmd.GuildId, cmd.ChannelId, cmd.CommandText);
                    await Task.Delay(400).ConfigureAwait(false);
                }
            });

            Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                await Task.Delay(5000);

                if (client.ShardId == 0)
                {
                    LoadOwnerChannels();
                }
            });
        }
        public ChatterBotService(DiscordSocketClient client, PermissionService perms, IEnumerable <GuildConfig> gcs,
                                 CommandHandler cmd, NadekoStrings strings, IBotCredentials creds)
        {
            _client  = client;
            _log     = LogManager.GetCurrentClassLogger();
            _perms   = perms;
            _cmd     = cmd;
            _strings = strings;
            _creds   = creds;

            ChatterBotGuilds = new ConcurrentDictionary <ulong, Lazy <IChatterBotSession> >(
                gcs.Where(gc => gc.CleverbotEnabled)
                .ToDictionary(gc => gc.GuildId, gc => new Lazy <IChatterBotSession>(() => CreateSession(), true)));
        }
Esempio n. 4
0
        public CustomReactionsService(PermissionService perms, DbService db, NadekoStrings strings,
                                      DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc,
                                      IDataCache cache, GlobalPermissionService gperm, NadekoBot bot)
        {
            _log     = LogManager.GetCurrentClassLogger();
            _db      = db;
            _client  = client;
            _perms   = perms;
            _cmd     = cmd;
            _bc      = bc;
            _strings = strings;
            _cache   = cache;
            _gperm   = gperm;

            var sub = _cache.Redis.GetSubscriber();

            sub.Subscribe(_client.CurrentUser.Id + "_crs.reload", (ch, msg) =>
            {
                ReloadInternal(bot.GetCurrentGuildConfigs());
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_gcr.added", (ch, msg) =>
            {
                var cr = JsonConvert.DeserializeObject <CustomReaction>(msg);
                _globalReactions.TryAdd(cr.Id, cr);
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_gcr.deleted", (ch, msg) =>
            {
                var id = int.Parse(msg);
                _globalReactions.TryRemove(id, out _);
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_gcr.edited", (ch, msg) =>
            {
                var obj = new { Id = 0, Res = "", Ad = false, Dm = false, Ca = false, Re = "" };
                obj     = JsonConvert.DeserializeAnonymousType(msg, obj);
                if (_globalReactions.TryGetValue(obj.Id, out var gcr))
                {
                    gcr.Response          = obj.Res;
                    gcr.AutoDeleteTrigger = obj.Ad;
                    gcr.DmResponse        = obj.Dm;
                    gcr.ContainsAnywhere  = obj.Ca;
                    gcr.Reactions         = obj.Re;
                }
            }, StackExchange.Redis.CommandFlags.FireAndForget);

            ReloadInternal(bot.AllGuildConfigs);

            bot.JoinedGuild   += Bot_JoinedGuild;
            _client.LeftGuild += _client_LeftGuild;
        }
Esempio n. 5
0
            public TicTacToe(NadekoStrings strings, DiscordSocketClient client, ITextChannel channel, IGuildUser firstUser)
            {
                _channel = channel;
                _strings = strings;
                _client  = client;

                _users = new[] { firstUser, null };
                _state = new int?[, ] {
                    { null, null, null },
                    { null, null, null },
                    { null, null, null },
                };

                _phase    = Phase.Starting;
                _moveLock = new SemaphoreSlim(1, 1);
            }
Esempio n. 6
0
        public TriviaGame(NadekoStrings strings, DiscordSocketClient client, IBotConfigProvider bc,
                          IDataCache cache, ICurrencyService cs, IGuild guild, ITextChannel channel,
                          TriviaOptions options)
        {
            _log          = LogManager.GetCurrentClassLogger();
            _cache        = cache;
            _questionPool = new TriviaQuestionPool(_cache);
            _strings      = strings;
            _client       = client;
            _bc           = bc;
            _cs           = cs;
            _options      = options;

            Guild   = guild;
            Channel = channel;
        }
        public TriviaGame(NadekoStrings strings, DiscordSocketClient client, IBotConfigProvider bc,
                          CurrencyService cs, IGuild guild, ITextChannel channel,
                          bool showHints, int winReq, bool isPokemon)
        {
            _log     = LogManager.GetCurrentClassLogger();
            _strings = strings;
            _client  = client;
            _bc      = bc;
            _cs      = cs;

            ShowHints      = showHints;
            Guild          = guild;
            Channel        = channel;
            WinRequirement = winReq;
            IsPokemon      = isPokemon;
        }
        public PermissionService(DiscordSocketClient client, DbService db, CommandHandler cmd, NadekoStrings strings)
        {
            _db      = db;
            _cmd     = cmd;
            _strings = strings;

            using (var uow = _db.UnitOfWork)
            {
                foreach (var x in uow.GuildConfigs.Permissionsv2ForAll(client.Guilds.ToArray().Select(x => (long)x.Id).ToList()))
                {
                    Cache.TryAdd(x.GuildId, new PermissionCache {
                        Verbose     = x.VerbosePermissions,
                        PermRole    = x.PermissionRole,
                        Permissions = new PermissionsCollection <Permissionv2>(x.Permissions)
                    });
                }
            }
        }
Esempio n. 9
0
        public MusicService(DiscordSocketClient client, IGoogleApiService google, NadekoStrings strings, ILocalization localization, DbService db, SoundCloudApiService sc, IEnumerable <GuildConfig> gcs)
        {
            _client       = client;
            _google       = google;
            _strings      = strings;
            _localization = localization;
            _db           = db;
            _sc           = sc;
            _log          = LogManager.GetCurrentClassLogger();

            try { Directory.Delete(MusicDataPath, true); } catch { }

            _defaultVolumes = new ConcurrentDictionary <ulong, float>(gcs.ToDictionary(x => x.GuildId, x => x.DefaultMusicVolume));

            Directory.CreateDirectory(MusicDataPath);

            //_t = new Timer(_ => _log.Info(MusicPlayers.Count(x => x.Value.Current.Current != null)), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
        }
Esempio n. 10
0
        public PollService(DiscordSocketClient client, NadekoStrings strings, DbService db,
                           NadekoStrings strs, IUnitOfWork uow)
        {
            _log     = LogManager.GetCurrentClassLogger();
            _client  = client;
            _strings = strings;
            _db      = db;
            _strs    = strs;

            ActivePolls = uow.Polls.GetAllPolls()
                          .ToDictionary(x => x.GuildId, x =>
            {
                var pr      = new PollRunner(db, x);
                pr.OnVoted += Pr_OnVoted;
                return(pr);
            })
                          .ToConcurrent();
        }
Esempio n. 11
0
        public PlantPickService(DbService db, CommandHandler cmd, NadekoBot bot, NadekoStrings strings,
                                IDataCache cache, FontProvider fonts, IBotConfigProvider bc, ICurrencyService cs,
                                CommandHandler cmdHandler, DiscordSocketClient client)
        {
            _db         = db;
            _strings    = strings;
            _images     = cache.LocalImages;
            _fonts      = fonts;
            _bc         = bc;
            _log        = LogManager.GetCurrentClassLogger();
            _cs         = cs;
            _cmdHandler = cmdHandler;
            _rng        = new NadekoRandom();
            _client     = client;

            cmd.OnMessageNoTrigger += PotentialFlowerGeneration;

            _generationChannels = new ConcurrentHashSet <ulong>(bot
                                                                .AllGuildConfigs
                                                                .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
        }
Esempio n. 12
0
        public SelfService(DiscordSocketClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
                           IBotConfigProvider bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds)
        {
            _bot          = bot;
            _cmdHandler   = cmdHandler;
            _db           = db;
            _log          = LogManager.GetCurrentClassLogger();
            _localization = localization;
            _strings      = strings;
            _client       = client;
            _creds        = creds;
            _bc           = bc;

            var _ = Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                foreach (var cmd in bc.BotConfig.StartupCommands)
                {
                    await cmdHandler.ExecuteExternal(cmd.GuildId, cmd.ChannelId, cmd.CommandText);
                    await Task.Delay(400).ConfigureAwait(false);
                }
            });

            var ___ = Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                await Task.Delay(5000);

                _client.Guilds.SelectMany(g => g.Users);

                if (client.ShardId == 0)
                {
                    LoadOwnerChannels();
                }
            });
        }
Esempio n. 13
0
        public GamesService(CommandHandler cmd, IBotConfigProvider bc, NadekoBot bot,
                            NadekoStrings strings, IDataCache data, CommandHandler cmdHandler,
                            CurrencyService cs)
        {
            _bc         = bc;
            _cmd        = cmd;
            _strings    = strings;
            _images     = data.LocalImages;
            _cmdHandler = cmdHandler;
            _log        = LogManager.GetCurrentClassLogger();
            _rng        = new NadekoRandom();
            _cs         = cs;

            //8ball
            EightBallResponses = _bc.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray();

            //girl ratings
            _t = new Timer((_) =>
            {
                GirlRatings.Clear();
            }, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));

            //plantpick
            _cmd.OnMessageNoTrigger += PotentialFlowerGeneration;
            GenerationChannels       = new ConcurrentHashSet <ulong>(bot
                                                                     .AllGuildConfigs
                                                                     .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));

            try
            {
                TypingArticles = JsonConvert.DeserializeObject <List <TypingArticle> >(File.ReadAllText(TypingArticlesPath));
            }
            catch (Exception ex)
            {
                _log.Warn("Error while loading typing articles {0}", ex.ToString());
                TypingArticles = new List <TypingArticle>();
            }
        }
Esempio n. 14
0
        public MusicService(DiscordSocketClient client, IGoogleApiService google,
                            NadekoStrings strings, ILocalization localization, DbService db,
                            SoundCloudApiService sc, IBotCredentials creds, NadekoBot bot)
        {
            _client       = client;
            _google       = google;
            _strings      = strings;
            _localization = localization;
            _db           = db;
            _sc           = sc;
            _creds        = creds;
            _log          = LogManager.GetCurrentClassLogger();

            _client.LeftGuild += _client_LeftGuild;

            try { Directory.Delete(MusicDataPath, true); } catch { }

            _defaultVolumes = new ConcurrentDictionary <ulong, float>(
                bot.AllGuildConfigs
                .ToDictionary(x => x.GuildId, x => x.DefaultMusicVolume));

            Directory.CreateDirectory(MusicDataPath);
        }
Esempio n. 15
0
        public GamesService(CommandHandler cmd, IBotConfigProvider bc, NadekoBot bot,
                            NadekoStrings strings, IDataCache data, CommandHandler cmdHandler,
                            ICurrencyService cs, FontProvider fonts, IHttpClientFactory httpFactory)
        {
            _bc          = bc;
            _cmd         = cmd;
            _strings     = strings;
            _images      = data.LocalImages;
            _cmdHandler  = cmdHandler;
            _log         = LogManager.GetCurrentClassLogger();
            _rng         = new NadekoRandom();
            _cs          = cs;
            _fonts       = fonts;
            _httpFactory = httpFactory;

            Ratings = new AsyncLazy <RatingTexts>(GetRatingTexts);

            //8ball
            EightBallResponses = _bc.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray();

            //girl ratings
            _t = new Timer((_) =>
            {
                GirlRatings.Clear();
            }, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));

            try
            {
                TypingArticles = JsonConvert.DeserializeObject <List <TypingArticle> >(File.ReadAllText(TypingArticlesPath));
            }
            catch (Exception ex)
            {
                _log.Warn("Error while loading typing articles {0}", ex.ToString());
                TypingArticles = new List <TypingArticle>();
            }
        }
 public HelpService(IBotConfigProvider bc, CommandHandler ch, NadekoStrings strings)
 {
     _bc      = bc;
     _ch      = ch;
     _strings = strings;
 }
Esempio n. 17
0
        public XpService(DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc,
                         NadekoBot bot, DbService db, NadekoStrings strings, IDataCache cache,
                         FontProvider fonts, IBotCredentials creds, ICurrencyService cs, IHttpClientFactory http)
        {
            _db          = db;
            _cmd         = cmd;
            _bc          = bc;
            _images      = cache.LocalImages;
            _log         = LogManager.GetCurrentClassLogger();
            _strings     = strings;
            _cache       = cache;
            _fonts       = fonts;
            _creds       = creds;
            _cs          = cs;
            _httpFactory = http;
            InternalReloadXpTemplate();

            if (client.ShardId == 0)
            {
                var sub = _cache.Redis.GetSubscriber();
                sub.Subscribe(_creds.RedisKey() + "_reload_xp_template",
                              (ch, val) => InternalReloadXpTemplate());
            }
            //load settings
            var allGuildConfigs = bot.AllGuildConfigs.Where(x => x.XpSettings != null);

            _excludedChannels = allGuildConfigs
                                .ToDictionary(
                x => x.GuildId,
                x => new ConcurrentHashSet <ulong>(x.XpSettings
                                                   .ExclusionList
                                                   .Where(ex => ex.ItemType == ExcludedItemType.Channel)
                                                   .Select(ex => ex.ItemId)
                                                   .Distinct()))
                                .ToConcurrent();

            _excludedRoles = allGuildConfigs
                             .ToDictionary(
                x => x.GuildId,
                x => new ConcurrentHashSet <ulong>(x.XpSettings
                                                   .ExclusionList
                                                   .Where(ex => ex.ItemType == ExcludedItemType.Role)
                                                   .Select(ex => ex.ItemId)
                                                   .Distinct()))
                             .ToConcurrent();

            _excludedServers = new ConcurrentHashSet <ulong>(
                allGuildConfigs.Where(x => x.XpSettings.ServerExcluded)
                .Select(x => x.GuildId));

            _cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger;

            updateXpTask = Task.Run(async() =>
            {
                while (true)
                {
                    await Task.Delay(TimeSpan.FromSeconds(5));
                    try
                    {
                        var toNotify    = new List <(IMessageChannel MessageChannel, IUser User, int Level, XpNotificationType NotifyType, NotifOf NotifOf)>();
                        var roleRewards = new Dictionary <ulong, List <XpRoleReward> >();
                        var curRewards  = new Dictionary <ulong, List <XpCurrencyReward> >();

                        var toAddTo = new List <UserCacheItem>();
                        while (_addMessageXp.TryDequeue(out var usr))
                        {
                            toAddTo.Add(usr);
                        }

                        var group = toAddTo.GroupBy(x => (GuildId: x.Guild.Id, x.User));
                        if (toAddTo.Count == 0)
                        {
                            continue;
                        }

                        using (var uow = _db.UnitOfWork)
                        {
                            foreach (var item in group)
                            {
                                var xp = item.Select(x => bc.BotConfig.XpPerMessage).Sum();

                                //1. Mass query discord users and userxpstats and get them from local dict
                                //2. (better but much harder) Move everything to the database, and get old and new xp
                                // amounts for every user (in order to give rewards)

                                var usr = uow.Xp.GetOrCreateUser(item.Key.GuildId, item.Key.User.Id);
                                var du  = uow.DiscordUsers.GetOrCreate(item.Key.User);

                                var globalXp           = du.TotalXp;
                                var oldGlobalLevelData = new LevelStats(globalXp);
                                var newGlobalLevelData = new LevelStats(globalXp + xp);

                                var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
                                usr.Xp     += xp;
                                du.TotalXp += xp;
                                if (du.Club != null)
                                {
                                    du.Club.Xp += xp;
                                }
                                var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);

                                if (oldGlobalLevelData.Level < newGlobalLevelData.Level)
                                {
                                    du.LastLevelUp = DateTime.UtcNow;
                                    var first      = item.First();
                                    if (du.NotifyOnLevelUp != XpNotificationType.None)
                                    {
                                        toNotify.Add((first.Channel, first.User, newGlobalLevelData.Level, du.NotifyOnLevelUp, NotifOf.Global));
                                    }
                                }

                                if (oldGuildLevelData.Level < newGuildLevelData.Level)
                                {
                                    usr.LastLevelUp = DateTime.UtcNow;
                                    //send level up notification
                                    var first = item.First();
                                    if (usr.NotifyOnLevelUp != XpNotificationType.None)
                                    {
                                        toNotify.Add((first.Channel, first.User, newGuildLevelData.Level, usr.NotifyOnLevelUp, NotifOf.Server));
                                    }

                                    //give role
                                    if (!roleRewards.TryGetValue(usr.GuildId, out var rrews))
                                    {
                                        rrews = uow.GuildConfigs.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
                                        roleRewards.Add(usr.GuildId, rrews);
                                    }

                                    if (!curRewards.TryGetValue(usr.GuildId, out var crews))
                                    {
                                        crews = uow.GuildConfigs.XpSettingsFor(usr.GuildId).CurrencyRewards.ToList();
                                        curRewards.Add(usr.GuildId, crews);
                                    }

                                    var rrew = rrews.FirstOrDefault(x => x.Level == newGuildLevelData.Level);
                                    if (rrew != null)
                                    {
                                        var role = first.User.Guild.GetRole(rrew.RoleId);
                                        if (role != null)
                                        {
                                            var __ = first.User.AddRoleAsync(role);
                                        }
                                    }
                                    //get currency reward for this level
                                    var crew = crews.FirstOrDefault(x => x.Level == newGuildLevelData.Level);
                                    if (crew != null)
                                    {
                                        //give the user the reward if it exists
                                        await _cs.AddAsync(item.Key.User.Id, "Level-up Reward", crew.Amount);
                                    }
                                }
                            }

                            uow.Complete();
                        }

                        await Task.WhenAll(toNotify.Select(async x =>
                        {
                            if (x.NotifOf == NotifOf.Server)
                            {
                                if (x.NotifyType == XpNotificationType.Dm)
                                {
                                    var chan = await x.User.GetOrCreateDMChannelAsync();
                                    if (chan != null)
                                    {
                                        await chan.SendConfirmAsync(_strings.GetText("level_up_dm",
                                                                                     (x.MessageChannel as ITextChannel)?.GuildId,
                                                                                     "xp",
                                                                                     x.User.Mention, Format.Bold(x.Level.ToString()),
                                                                                     Format.Bold((x.MessageChannel as ITextChannel)?.Guild.ToString() ?? "-")));
                                    }
                                }
                                else // channel
                                {
                                    await x.MessageChannel.SendConfirmAsync(_strings.GetText("level_up_channel",
                                                                                             (x.MessageChannel as ITextChannel)?.GuildId,
                                                                                             "xp",
                                                                                             x.User.Mention, Format.Bold(x.Level.ToString())));
                                }
                            }
                            else
                            {
                                IMessageChannel chan;
                                if (x.NotifyType == XpNotificationType.Dm)
                                {
                                    chan = await x.User.GetOrCreateDMChannelAsync();
                                }
                                else // channel
                                {
                                    chan = x.MessageChannel;
                                }
                                await chan.SendConfirmAsync(_strings.GetText("level_up_global",
                                                                             (x.MessageChannel as ITextChannel)?.GuildId,
                                                                             "xp",
                                                                             x.User.Mention, Format.Bold(x.Level.ToString())));
                            }
                        }));
                    }
                    catch (Exception ex)
                    {
                        _log.Warn(ex);
                    }
                }
            });
        }
        public VplusTService(DiscordSocketClient client, IEnumerable <GuildConfig> gcs, NadekoStrings strings,
                             DbService db)
        {
            _client  = client;
            _strings = strings;
            _db      = db;
            _log     = LogManager.GetCurrentClassLogger();

            VoicePlusTextCache             = new ConcurrentHashSet <ulong>(gcs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
            _client.UserVoiceStateUpdated += UserUpdatedEventHandler;
        }
Esempio n. 19
0
        public XpService(DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc,
                         NadekoBot bot, DbService db, NadekoStrings strings, IDataCache cache,
                         FontProvider fonts, IBotCredentials creds, ICurrencyService cs, IHttpClientFactory http)
        {
            _db          = db;
            _cmd         = cmd;
            _bc          = bc;
            _images      = cache.LocalImages;
            _log         = LogManager.GetCurrentClassLogger();
            _strings     = strings;
            _cache       = cache;
            _fonts       = fonts;
            _creds       = creds;
            _cs          = cs;
            _httpFactory = http;
            _client      = client;

            InternalReloadXpTemplate();

            if (client.ShardId == 0)
            {
                var sub = _cache.Redis.GetSubscriber();
                sub.Subscribe(_creds.RedisKey() + "_reload_xp_template",
                              (ch, val) => InternalReloadXpTemplate());
            }

            //load settings
            var allGuildConfigs = bot.AllGuildConfigs
                                  .Where(x => x.XpSettings != null)
                                  .ToList();

            _excludedChannels = allGuildConfigs
                                .ToDictionary(
                x => x.GuildId,
                x => new ConcurrentHashSet <ulong>(x.XpSettings
                                                   .ExclusionList
                                                   .Where(ex => ex.ItemType == ExcludedItemType.Channel)
                                                   .Select(ex => ex.ItemId)
                                                   .Distinct()))
                                .ToConcurrent();

            _excludedRoles = allGuildConfigs
                             .ToDictionary(
                x => x.GuildId,
                x => new ConcurrentHashSet <ulong>(x.XpSettings
                                                   .ExclusionList
                                                   .Where(ex => ex.ItemType == ExcludedItemType.Role)
                                                   .Select(ex => ex.ItemId)
                                                   .Distinct()))
                             .ToConcurrent();

            _excludedServers = new ConcurrentHashSet <ulong>(
                allGuildConfigs.Where(x => x.XpSettings.ServerExcluded)
                .Select(x => x.GuildId));

            _cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger;

#if !GLOBAL_NADEKO
            _client.UserVoiceStateUpdated += _client_OnUserVoiceStateUpdated;

            // Scan guilds on startup.
            _client.GuildAvailable += _client_OnGuildAvailable;
            foreach (var guild in _client.Guilds)
            {
                _client_OnGuildAvailable(guild);
            }
#endif
            updateXpTask = Task.Run(UpdateLoop);
        }
Esempio n. 20
0
        public SelfService(DiscordSocketClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
                           IBotConfigProvider bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds,
                           IDataCache cache)
        {
            _redis        = cache.Redis;
            _bot          = bot;
            _cmdHandler   = cmdHandler;
            _db           = db;
            _log          = LogManager.GetCurrentClassLogger();
            _localization = localization;
            _strings      = strings;
            _client       = client;
            _creds        = creds;
            _bc           = bc;
            _cache        = cache;
            _imgs         = cache.LocalImages;
            if (_client.ShardId == 0)
            {
                _updateTimer = new Timer(async _ =>
                {
                    try
                    {
                        var ch = ownerChannels?.Values.FirstOrDefault();

                        if (ch == null) // no owner channels
                        {
                            return;
                        }

                        var cfo = _bc.BotConfig.CheckForUpdates;
                        if (cfo == UpdateCheckType.None)
                        {
                            return;
                        }

                        string data;
                        if ((cfo == UpdateCheckType.Commit && (data = await GetNewCommit()) != null) ||
                            (cfo == UpdateCheckType.Release && (data = await GetNewRelease()) != null))
                        {
                            await ch.SendConfirmAsync("New Bot Update", data).ConfigureAwait(false);
                        }
                    }
                    catch (Exception ex)
                    {
                        _log.Warn(ex);
                    }
                }, null, TimeSpan.FromHours(8), TimeSpan.FromHours(8));
            }

            var sub = _redis.GetSubscriber();

            sub.Subscribe(_creds.RedisKey() + "_reload_images",
                          delegate { _imgs.Reload(); }, CommandFlags.FireAndForget);
            sub.Subscribe(_creds.RedisKey() + "_reload_bot_config",
                          delegate { _bc.Reload(); }, CommandFlags.FireAndForget);

            Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                _autoCommands = bc.BotConfig
                                .StartupCommands
                                .Where(x => x.Interval >= 5)
                                .GroupBy(x => x.GuildId)
                                .ToDictionary(
                    x => x.Key,
                    y => y.ToDictionary(x => x.Id,
                                        x => TimerFromStartupCommand((StartupCommand)x))
                    .ToConcurrent())
                                .ToConcurrent();

                foreach (var cmd in bc.BotConfig.StartupCommands.Where(x => x.Interval <= 0))
                {
                    try { await ExecuteCommand(cmd); } catch { }
                }
            });

            Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                await Task.Delay(5000).ConfigureAwait(false);

                if (client.ShardId == 0)
                {
                    await LoadOwnerChannels().ConfigureAwait(false);
                }
            });
        }
Esempio n. 21
0
        public XpService(CommandHandler cmd, IBotConfigProvider bc,
            NadekoBot bot, DbService db, NadekoStrings strings, IDataCache cache,
            FontProvider fonts)
        {
            _db = db;
            _cmd = cmd;
            _bc = bc;
            _images = cache.LocalImages;
            _log = LogManager.GetCurrentClassLogger();
            _strings = strings;
            _cache = cache;
            _fonts = fonts;

            //load settings
            var allGuildConfigs = bot.AllGuildConfigs.Where(x => x.XpSettings != null);
            _excludedChannels = allGuildConfigs
                .ToDictionary(
                    x => x.GuildId,
                    x => new ConcurrentHashSet<ulong>(x.XpSettings
                            .ExclusionList
                            .Where(ex => ex.ItemType == ExcludedItemType.Channel)
                            .Select(ex => ex.ItemId)
                            .Distinct()))
                .ToConcurrent();

            _excludedRoles = allGuildConfigs
                .ToDictionary(
                    x => x.GuildId,
                    x => new ConcurrentHashSet<ulong>(x.XpSettings
                            .ExclusionList
                            .Where(ex => ex.ItemType == ExcludedItemType.Role)
                            .Select(ex => ex.ItemId)
                            .Distinct()))
                .ToConcurrent();

            _excludedServers = new ConcurrentHashSet<ulong>(
                allGuildConfigs.Where(x => x.XpSettings.ServerExcluded)
                               .Select(x => x.GuildId));

            _cmd.OnMessageNoTrigger += _cmd_OnMessageNoTrigger;

            _updateXpTimer = new Timer(async _ =>
            {
                try
                {
                    var toNotify = new List<(IMessageChannel MessageChannel, IUser User, int Level, XpNotificationType NotifyType, NotifOf NotifOf)>();
                    var roleRewards = new Dictionary<ulong, List<XpRoleReward>>();

                    var toAddTo = new List<UserCacheItem>();
                    while (_addMessageXp.TryDequeue(out var usr))
                        toAddTo.Add(usr);

                    var group = toAddTo.GroupBy(x => (GuildId: x.Guild.Id, User: x.User));
                    if (toAddTo.Count == 0)
                        return;

                    using (var uow = _db.UnitOfWork)
                    {
                        foreach (var item in group)
                        {
                            var xp = item.Select(x => bc.BotConfig.XpPerMessage).Sum();

                            var usr = uow.Xp.GetOrCreateUser(item.Key.GuildId, item.Key.User.Id);
                            var du = uow.DiscordUsers.GetOrCreate(item.Key.User);

                            if (du.LastXpGain + TimeSpan.FromMinutes(_bc.BotConfig.XpMinutesTimeout) > DateTime.UtcNow)
                                continue;

                            du.LastXpGain = DateTime.UtcNow;

                            var globalXp = du.TotalXp;
                            var oldGlobalLevelData = new LevelStats(globalXp);
                            var newGlobalLevelData = new LevelStats(globalXp + xp);

                            var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
                            usr.Xp += xp;
                            du.TotalXp += xp;
                            if (du.Club != null)
                                du.Club.Xp += xp;
                            var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);

                            if (oldGlobalLevelData.Level < newGlobalLevelData.Level)
                            {
                                du.LastLevelUp = DateTime.UtcNow;
                                var first = item.First();
                                if (du.NotifyOnLevelUp != XpNotificationType.None)
                                    toNotify.Add((first.Channel, first.User, newGlobalLevelData.Level, du.NotifyOnLevelUp, NotifOf.Global));
                            }

                            if (oldGuildLevelData.Level < newGuildLevelData.Level)
                            {
                                usr.LastLevelUp = DateTime.UtcNow;
                                //send level up notification
                                var first = item.First();
                                if (usr.NotifyOnLevelUp != XpNotificationType.None)
                                    toNotify.Add((first.Channel, first.User, newGuildLevelData.Level, usr.NotifyOnLevelUp, NotifOf.Server));

                                //give role
                                if (!roleRewards.TryGetValue(usr.GuildId, out var rewards))
                                {
                                    rewards = uow.GuildConfigs.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
                                    roleRewards.Add(usr.GuildId, rewards);
                                }

                                var rew = rewards.FirstOrDefault(x => x.Level == newGuildLevelData.Level);
                                if (rew != null)
                                {
                                    var role = first.User.Guild.GetRole(rew.RoleId);
                                    if (role != null)
                                    {
                                        var __ = first.User.AddRoleAsync(role);
                                    }
                                }
                            }
                        }

                        uow.Complete();
                    }

                    await Task.WhenAll(toNotify.Select(async x =>
                    {
                        if (x.NotifOf == NotifOf.Server)
                        {
                            if (x.NotifyType == XpNotificationType.Dm)
                            {
                                var chan = await x.User.GetOrCreateDMChannelAsync().ConfigureAwait(false);
                                if (chan != null)
                                    await chan.SendConfirmAsync(_strings.GetText("level_up_dm",
                                        (x.MessageChannel as ITextChannel)?.GuildId,
                                        "xp",
                                        x.User.Mention, Format.Bold(x.Level.ToString()),
                                        Format.Bold((x.MessageChannel as ITextChannel)?.Guild.ToString() ?? "-")))
                                        .ConfigureAwait(false);
                            }
                            else // channel
                            {
                                await x.MessageChannel.SendConfirmAsync(_strings.GetText("level_up_channel",
                                          (x.MessageChannel as ITextChannel)?.GuildId,
                                          "xp",
                                          x.User.Mention, Format.Bold(x.Level.ToString())))
                                          .ConfigureAwait(false);
                            }
                        }
                        else
                        {
                            IMessageChannel chan;
                            if (x.NotifyType == XpNotificationType.Dm)
                            {
                                chan = await x.User.GetOrCreateDMChannelAsync().ConfigureAwait(false);
                            }
                            else // channel
                            {
                                chan = x.MessageChannel;
                            }
                            await chan.SendConfirmAsync(_strings.GetText("level_up_global",
                                          (x.MessageChannel as ITextChannel)?.GuildId,
                                          "xp",
                                          x.User.Mention, Format.Bold(x.Level.ToString())))
                                            .ConfigureAwait(false);
                        }
                    }));
                }
                catch (Exception ex)
                {
                    _log.Warn(ex);
                }
            }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
            
            _clearRewardTimerTokenSource = new CancellationTokenSource();
            var token = _clearRewardTimerTokenSource.Token;
            //just a first line, in order to prevent queries. But since other shards can try to do this too,
            //i'll check in the db too.
            _clearRewardTimer = Task.Run(async () =>
            {
                while (!token.IsCancellationRequested)
                {
                    _rewardedUsers.Clear();
                    
                    await Task.Delay(TimeSpan.FromMinutes(_bc.BotConfig.XpMinutesTimeout));
                }
            }, token);
        }
        public StreamNotificationService(DbService db, DiscordSocketClient client, NadekoStrings strings)
        {
            _db      = db;
            _client  = client;
            _strings = strings;
            _http    = new HttpClient();
#if !GLOBAL_NADEKO
            var _ = Task.Run(async() =>
            {
                while (true)
                {
                    await Task.Delay(60000);
                    var oldCachedStatuses = new ConcurrentDictionary <string, IStreamResponse>(_cachedStatuses);
                    _cachedStatuses.Clear();
                    IEnumerable <FollowedStream> streams;
                    using (var uow = _db.UnitOfWork)
                    {
                        streams = uow.GuildConfigs.GetAllFollowedStreams(client.Guilds.Select(x => (long)x.Id).ToList());
                    }

                    await Task.WhenAll(streams.Select(async fs =>
                    {
                        try
                        {
                            var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
                            if (firstStreamNotifPass)
                            {
                                return;
                            }

                            IStreamResponse oldResponse;
                            if (oldCachedStatuses.TryGetValue(newStatus.Url, out oldResponse) &&
                                oldResponse.Live != newStatus.Live)
                            {
                                var server  = _client.GetGuild(fs.GuildId);
                                var channel = server?.GetTextChannel(fs.ChannelId);
                                if (channel == null)
                                {
                                    return;
                                }
                                try
                                {
                                    await channel.EmbedAsync(GetEmbed(fs, newStatus, channel.Guild.Id)).ConfigureAwait(false);
                                }
                                catch
                                {
                                    // ignored
                                }
                            }
                        }
                        catch
                        {
                            // ignored
                        }
                    }));

                    firstStreamNotifPass = false;
                }
            });
#endif
        }
Esempio n. 23
0
        //private readonly Timer _updateTimer;

        public SelfService(DiscordSocketClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
                           IBotConfigProvider bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds,
                           IDataCache cache, IHttpClientFactory factory)
        {
            _redis        = cache.Redis;
            _bot          = bot;
            _cmdHandler   = cmdHandler;
            _db           = db;
            _log          = LogManager.GetCurrentClassLogger();
            _localization = localization;
            _strings      = strings;
            _client       = client;
            _creds        = creds;
            _bc           = bc;
            _cache        = cache;
            _imgs         = cache.LocalImages;
            _httpFactory  = factory;
            var sub = _redis.GetSubscriber();

            if (_client.ShardId == 0)
            {
                sub.Subscribe(_creds.RedisKey() + "_reload_images",
                              delegate { _imgs.Reload(); }, CommandFlags.FireAndForget);

                //_updateTimer = new Timer(async _ =>
                //{
                //    try
                //    {
                //        var ch = ownerChannels?.Values.FirstOrDefault();

                //        if (ch == null) // no owner channels
                //            return;

                //        var cfo = _bc.BotConfig.CheckForUpdates;
                //        if (cfo == UpdateCheckType.None)
                //            return;

                //        string data;
                //        if ((cfo == UpdateCheckType.Commit && (data = await GetNewCommit().ConfigureAwait(false)) != null)
                //            || (cfo == UpdateCheckType.Release && (data = await GetNewRelease().ConfigureAwait(false)) != null))
                //        {
                //            await ch.SendConfirmAsync("New Bot Update", data).ConfigureAwait(false);
                //        }
                //    }
                //    catch (Exception ex)
                //    {
                //        _log.Warn(ex);
                //    }
                //}, null, TimeSpan.FromHours(8), TimeSpan.FromHours(8));
            }
            sub.Subscribe(_creds.RedisKey() + "_reload_bot_config",
                          delegate { _bc.Reload(); }, CommandFlags.FireAndForget);
            sub.Subscribe(_creds.RedisKey() + "_leave_guild", async(ch, v) =>
            {
                try
                {
                    var guildStr = v.ToString()?.Trim().ToUpperInvariant();
                    if (string.IsNullOrWhiteSpace(guildStr))
                    {
                        return;
                    }
                    var server = _client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr) ??
                                 _client.Guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr);

                    if (server == null)
                    {
                        return;
                    }
                    if (server.OwnerId != _client.CurrentUser.Id)
                    {
                        await server.LeaveAsync().ConfigureAwait(false);
                        _log.Info($"Left server {server.Name} [{server.Id}]");
                    }
                    else
                    {
                        await server.DeleteAsync().ConfigureAwait(false);
                        _log.Info($"Deleted server {server.Name} [{server.Id}]");
                    }
                }
                catch { }
            }, CommandFlags.FireAndForget);

            Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                _autoCommands = bc.BotConfig
                                .StartupCommands
                                .Where(x => x.Interval >= 5)
                                .GroupBy(x => x.GuildId)
                                .ToDictionary(
                    x => x.Key,
                    y => y.ToDictionary(x => x.Id,
                                        x => TimerFromStartupCommand((StartupCommand)x))
                    .ToConcurrent())
                                .ToConcurrent();

                foreach (var cmd in bc.BotConfig.StartupCommands.Where(x => x.Interval <= 0))
                {
                    try { await ExecuteCommand(cmd).ConfigureAwait(false); } catch { }
                }
            });

            Task.Run(async() =>
            {
                await bot.Ready.Task.ConfigureAwait(false);

                await Task.Delay(5000).ConfigureAwait(false);

                if (client.ShardId == 0)
                {
                    await LoadOwnerChannels().ConfigureAwait(false);
                }
            });
        }
Esempio n. 24
0
        public LogCommandService(DiscordSocketClient client, NadekoStrings strings,
                                 NadekoBot bot, DbService db, MuteService mute, ProtectionService prot, GuildTimezoneService tz)
        {
            _client  = client;
            _log     = LogManager.GetCurrentClassLogger();
            _strings = strings;
            _db      = db;
            _mute    = mute;
            _prot    = prot;
            _tz      = tz;

            GuildLogSettings = bot.AllGuildConfigs
                               .ToDictionary(g => g.GuildId, g => g.LogSetting)
                               .ToConcurrent();

            _timerReference = new Timer(async(state) =>
            {
                try
                {
                    var keys = PresenceUpdates.Keys.ToList();

                    await Task.WhenAll(keys.Select(key =>
                    {
                        if (PresenceUpdates.TryRemove(key, out var msgs))
                        {
                            var title = GetText(key.Guild, "presence_updates");
                            var desc  = string.Join(Environment.NewLine, msgs);
                            return(key.SendConfirmAsync(title, desc.TrimTo(2048)));
                        }
                        return(Task.CompletedTask);
                    }));
                }
                catch (Exception ex)
                {
                    _log.Warn(ex);
                }
            }, null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));

            //_client.MessageReceived += _client_MessageReceived;
            _client.MessageUpdated += _client_MessageUpdated;
            _client.MessageDeleted += _client_MessageDeleted;
            _client.UserBanned     += _client_UserBanned;
            _client.UserUnbanned   += _client_UserUnbanned;
            _client.UserJoined     += _client_UserJoined;
            _client.UserLeft       += _client_UserLeft;
            //_client.UserPresenceUpdated += _client_UserPresenceUpdated;
            _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
            _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
            _client.GuildMemberUpdated    += _client_GuildUserUpdated;
#if !GLOBAL_NADEKO
            _client.UserUpdated += _client_UserUpdated;
#endif
            _client.ChannelCreated   += _client_ChannelCreated;
            _client.ChannelDestroyed += _client_ChannelDestroyed;
            _client.ChannelUpdated   += _client_ChannelUpdated;

            _mute.UserMuted   += MuteCommands_UserMuted;
            _mute.UserUnmuted += MuteCommands_UserUnmuted;

            _prot.OnAntiProtectionTriggered += TriggeredAntiProtection;
        }
Esempio n. 25
0
        public CustomReactionsService(PermissionService perms, DbService db, NadekoStrings strings,
                                      DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow,
                                      IDataCache cache)
        {
            _log     = LogManager.GetCurrentClassLogger();
            _db      = db;
            _client  = client;
            _perms   = perms;
            _cmd     = cmd;
            _bc      = bc;
            _strings = strings;
            _cache   = cache;

            var sub = _cache.Redis.GetSubscriber();

            sub.Subscribe(_client.CurrentUser.Id + "_gcr.added", (ch, msg) =>
            {
                Array.Resize(ref GlobalReactions, GlobalReactions.Length + 1);
                GlobalReactions[GlobalReactions.Length - 1] = JsonConvert.DeserializeObject <CustomReaction>(msg);
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_gcr.deleted", (ch, msg) =>
            {
                var id          = int.Parse(msg);
                GlobalReactions = GlobalReactions.Where(cr => cr?.Id != id).ToArray();
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_gcr.edited", (ch, msg) =>
            {
                var obj = new { Id = 0, Message = "" };
                obj     = JsonConvert.DeserializeAnonymousType(msg, obj);
                var gcr = GlobalReactions.FirstOrDefault(x => x.Id == obj.Id);
                if (gcr != null)
                {
                    gcr.Response = obj.Message;
                }
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_crad.toggle", (ch, msg) =>
            {
                var obj = new { Id = 0, Value = false };
                obj     = JsonConvert.DeserializeAnonymousType(msg, obj);
                var gcr = GlobalReactions.FirstOrDefault(x => x.Id == obj.Id);
                if (gcr != null)
                {
                    gcr.AutoDeleteTrigger = obj.Value;
                }
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_crdm.toggle", (ch, msg) =>
            {
                var obj = new { Id = 0, Value = false };
                obj     = JsonConvert.DeserializeAnonymousType(msg, obj);
                var gcr = GlobalReactions.FirstOrDefault(x => x.Id == obj.Id);
                if (gcr != null)
                {
                    gcr.DmResponse = obj.Value;
                }
            }, StackExchange.Redis.CommandFlags.FireAndForget);
            sub.Subscribe(_client.CurrentUser.Id + "_crca.toggle", (ch, msg) =>
            {
                var obj = new { Id = 0, Value = false };
                obj     = JsonConvert.DeserializeAnonymousType(msg, obj);
                var gcr = GlobalReactions.FirstOrDefault(x => x.Id == obj.Id);
                if (gcr != null)
                {
                    gcr.ContainsAnywhere = obj.Value;
                }
            }, StackExchange.Redis.CommandFlags.FireAndForget);


            var items = uow.CustomReactions.GetAll();

            GuildReactions  = new ConcurrentDictionary <ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray()));
            GlobalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
        }
Esempio n. 26
0
 public PollService(DiscordSocketClient client, NadekoStrings strings)
 {
     _log     = LogManager.GetCurrentClassLogger();
     _client  = client;
     _strings = strings;
 }
Esempio n. 27
0
        public StreamNotificationService(DbService db, DiscordSocketClient client,
                                         NadekoStrings strings, IDataCache cache, IBotCredentials creds, IHttpClientFactory httpFactory,
                                         NadekoBot bot)
        {
            _db            = db;
            _client        = client;
            _strings       = strings;
            _multi         = cache.Redis;
            _creds         = creds;
            _streamTracker = new NotifChecker(httpFactory, cache.Redis, creds.RedisKey(), client.ShardId == 0);

            using (var uow = db.GetDbContext())
            {
                var ids          = client.GetGuildIds();
                var guildConfigs = uow._context.Set <GuildConfig>()
                                   .AsQueryable()
                                   .Include(x => x.FollowedStreams)
                                   .Where(x => ids.Contains(x.GuildId))
                                   .ToList();

                _offlineNotificationServers = new ConcurrentHashSet <ulong>(guildConfigs
                                                                            .Where(gc => gc.NotifyStreamOffline)
                                                                            .Select(x => x.GuildId)
                                                                            .ToList());

                var followedStreams = guildConfigs
                                      .SelectMany(x => x.FollowedStreams)
                                      .ToList();

                _shardTrackedStreams = followedStreams
                                       .GroupBy(x => new { Type = 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)
                {
                    var allFollowedStreams = uow._context.Set <FollowedStream>()
                                             .AsQueryable()
                                             .ToList();

                    foreach (var fs in allFollowedStreams)
                    {
                        _streamTracker.CacheAddData(fs.CreateKey(), null, replace: false);
                    }

                    _trackCounter = allFollowedStreams
                                    .GroupBy(x => new { Type = x.Type, Name = x.Username.ToLower() })
                                    .ToDictionary(
                        x => new StreamDataKey(x.Key.Type, x.Key.Name),
                        x => x.Select(fs => fs.GuildId).ToHashSet());
                }
            }

            var sub = _multi.GetSubscriber();

            sub.Subscribe($"{_creds.RedisKey()}_streams_offline", HandleStreamsOffline);
            sub.Subscribe($"{_creds.RedisKey()}_streams_online", HandleStreamsOnline);

            if (client.ShardId == 0)
            {
                // only shard 0 will run the tracker,
                // and then publish updates with redis to other shards
                _streamTracker.OnStreamsOffline += OnStreamsOffline;
                _streamTracker.OnStreamsOnline  += OnStreamsOnline;
                _ = _streamTracker.RunAsync();

                sub.Subscribe($"{_creds.RedisKey()}_follow_stream", HandleFollowStream);
                sub.Subscribe($"{_creds.RedisKey()}_unfollow_stream", HandleUnfollowStream);
            }

            bot.JoinedGuild  += ClientOnJoinedGuild;
            client.LeftGuild += ClientOnLeftGuild;
        }