예제 #1
0
        private void Migrate2()
        {
            // load new images
            var urls = JsonConvert.DeserializeObject <ImageUrls>(
                File.ReadAllText(Path.Combine(_basePath, "images.json")));

            if (urls.Version >= 2)
            {
                return;
            }
            _log.Info("Migrating images v1 to images v2.");
            urls.Version = 2;

            var prefix = $"{_creds.RedisKey()}_localimg_";

            _db.KeyDelete(new[] {
                prefix + "heads",
                prefix + "tails",
                prefix + "dice",
                prefix + "slot_background",
                prefix + "slotnumbers",
                prefix + "slotemojis",
                prefix + "wife_matrix",
                prefix + "rategirl_dot",
                prefix + "xp_card",
                prefix + "rip",
                prefix + "rip_overlay"
            }
                          .Select(x => (RedisKey)x).ToArray());

            File.WriteAllText(Path.Combine(_basePath, "images.json"), JsonConvert.SerializeObject(urls, Formatting.Indented));
        }
예제 #2
0
        public async Task <bool> AllKeysExist()
        {
            try
            {
                var prefix  = $"{_creds.RedisKey()}_localimg_";
                var results = await Task.WhenAll(_db.KeyExistsAsync(prefix + "heads"),
                                                 _db.KeyExistsAsync(prefix + "tails"),
                                                 _db.KeyExistsAsync(prefix + "dice"),
                                                 _db.KeyExistsAsync(prefix + "slot_background"),
                                                 _db.KeyExistsAsync(prefix + "slotnumbers"),
                                                 _db.KeyExistsAsync(prefix + "slotemojis"),
                                                 _db.KeyExistsAsync(prefix + "wife_matrix"),
                                                 _db.KeyExistsAsync(prefix + "rategirl_dot"),
                                                 _db.KeyExistsAsync(prefix + "xp_card"),
                                                 _db.KeyExistsAsync(prefix + "rip"),
                                                 _db.KeyExistsAsync(prefix + "rip_overlay"));

                return(results.All(x => x));
            }
            catch (Exception ex)
            {
                _log.Warn(ex);
                return(false);
            }
        }
예제 #3
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;

            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);
                }
            });
        }
예제 #4
0
        public SelfService(DiscordSocketClient client, CommandHandler cmdHandler, DbService db,
                           IBotStrings strings, IBotCredentials creds, IDataCache cache, IHttpClientFactory factory,
                           BotConfigService bss)
        {
            _redis       = cache.Redis;
            _cmdHandler  = cmdHandler;
            _db          = db;
            _strings     = strings;
            _client      = client;
            _creds       = creds;
            _cache       = cache;
            _imgs        = cache.LocalImages;
            _httpFactory = factory;
            _bss         = bss;

            var sub = _redis.GetSubscriber();

            if (_client.ShardId == 0)
            {
                sub.Subscribe(_creds.RedisKey() + "_reload_images",
                              delegate { _imgs.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.Information($"Left server {server.Name} [{server.Id}]");
                    }
                    else
                    {
                        await server.DeleteAsync().ConfigureAwait(false);
                        Log.Information($"Deleted server {server.Name} [{server.Id}]");
                    }
                }
                catch
                {
                }
            }, CommandFlags.FireAndForget);
        }
예제 #5
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;

            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);

                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).ConfigureAwait(false);

                if (client.ShardId == 0)
                {
                    await LoadOwnerChannels().ConfigureAwait(false);
                }
            });
        }
예제 #6
0
        public PatreonRewardsService(IBotCredentials creds, DbService db,
                                     CurrencyService currency,
                                     DiscordSocketClient client, IDataCache cache)
        {
            _log      = LogManager.GetCurrentClassLogger();
            _creds    = creds;
            _db       = db;
            _currency = currency;
            _cache    = cache;
            _key      = _creds.RedisKey() + "_patreon_rewards";

            _pledges = new FactoryCache <PatreonUserAndReward[]>(() =>
            {
                var r    = _cache.Redis.GetDatabase();
                var data = r.StringGet(_key);
                if (data.IsNullOrEmpty)
                {
                    return(null);
                }
                else
                {
                    return(JsonConvert.DeserializeObject <PatreonUserAndReward[]>(data));
                }
            }, TimeSpan.FromSeconds(20));

            if (client.ShardId == 0)
            {
                Updater = new Timer(async _ => await RefreshPledges(),
                                    null, TimeSpan.Zero, Interval);
            }
        }
예제 #7
0
 public RedisCache(IBotCredentials creds)
 {
     Redis = ConnectionMultiplexer.Connect("127.0.0.1");
     Redis.PreserveAsyncOrder = false;
     _db       = Redis.GetDatabase();
     _redisKey = creds.RedisKey();
 }
예제 #8
0
 public RedisCache(IBotCredentials creds)
 {
     _log  = LogManager.GetCurrentClassLogger();
     Redis = ConnectionMultiplexer.Connect("127.0.0.1");
     Redis.PreserveAsyncOrder = false;
     LocalImages = new RedisImagesCache(Redis, creds);
     LocalData   = new RedisLocalDataCache(Redis, creds);
     _redisKey   = creds.RedisKey();
 }
예제 #9
0
 public RedisCache(IBotCredentials creds)
 {
     Redis = ConnectionMultiplexer.Connect("127.0.0.1");
     Redis.PreserveAsyncOrder = false;
     LocalImages = new RedisImagesCache(Redis, creds);
     LocalData   = new RedisLocalDataCache(Redis, creds);
     _db         = Redis.GetDatabase();
     _redisKey   = creds.RedisKey();
 }
예제 #10
0
            public async Task ShardStats(int page = 1)
            {
                if (--page < 0)
                {
                    return;
                }
                var db       = _cache.Redis.GetDatabase();
                var statuses = db.ListRange(_creds.RedisKey() + "_shardstats")
                               .Select(x => JsonConvert.DeserializeObject <ShardComMessage>(x));

                var status = string.Join(", ", statuses
                                         .GroupBy(x => x.ConnectionState)
                                         .Select(x => $"{x.Count()} {x.Key}")
                                         .ToArray());

                var allShardStrings = statuses
                                      .Select(x =>
                {
                    var timeDiff = DateTime.UtcNow - x.Time;
                    if (timeDiff > TimeSpan.FromSeconds(20))
                    {
                        return($"Shard #{Format.Bold(x.ShardId.ToString())} **UNRESPONSIVE** for {timeDiff.ToString(@"hh\:mm\:ss")}");
                    }
                    return(GetText("shard_stats_txt", x.ShardId.ToString(),
                                   Format.Bold(x.ConnectionState.ToString()), Format.Bold(x.Guilds.ToString()), timeDiff.ToString(@"hh\:mm\:ss")));
                })
                                      .ToArray();

                await Context.Channel.SendPaginatedConfirmAsync(_client, page, (curPage) =>
                {
                    var str = string.Join("\n", allShardStrings.Skip(25 * curPage).Take(25));

                    if (string.IsNullOrWhiteSpace(str))
                    {
                        str = GetText("no_shards_on_page");
                    }

                    return(new EmbedBuilder()
                           .WithAuthor(a => a.WithName(GetText("shard_stats")))
                           .WithTitle(status)
                           .WithOkColor()
                           .WithDescription(str));
                }, allShardStrings.Length, 25);
            }
예제 #11
0
        private bool SetUserRewarded(ulong userId)
        {
            var r   = _cache.Redis.GetDatabase();
            var key = $"{_creds.RedisKey()}_user_xp_gain_{userId}";

            return(r.StringSet(key,
                               true,
                               TimeSpan.FromMinutes(_bc.BotConfig.XpMinutesTimeout),
                               StackExchange.Redis.When.NotExists));
        }
예제 #12
0
        public RedisCache(IBotCredentials creds, int shardId)
        {
            var conf = ConfigurationOptions.Parse(creds.RedisOptions);

            Redis                    = ConnectionMultiplexer.Connect(conf);
            _redisEndpoint           = Redis.GetEndPoints().First();
            Redis.PreserveAsyncOrder = false;
            LocalImages              = new RedisImagesCache(Redis, creds);
            LocalData                = new RedisLocalDataCache(Redis, creds, shardId);
            _redisKey                = creds.RedisKey();
        }
예제 #13
0
        public RedisCache(IBotCredentials creds, int shardId)
        {
            _log = LogManager.GetCurrentClassLogger();
            var conf = ConfigurationOptions.Parse("127.0.0.1");

            conf.SyncTimeout         = 3000;
            Redis                    = ConnectionMultiplexer.Connect(conf);
            Redis.PreserveAsyncOrder = false;
            LocalImages              = new RedisImagesCache(Redis, creds);
            LocalData                = new RedisLocalDataCache(Redis, creds, shardId);
            _redisKey                = creds.RedisKey();
        }
예제 #14
0
        public void ReloadBotConfig()
        {
            var sub = _cache.Redis.GetSubscriber();

            sub.Publish(_creds.RedisKey() + "_reload_bot_config",
                        "",
                        CommandFlags.FireAndForget);
        }
예제 #15
0
        public RedisCache(IBotCredentials creds, int shardId)
        {
            _log = LogManager.GetCurrentClassLogger();

            ConfigurationOptions conf;

            if (!string.IsNullOrWhiteSpace(creds.RedisOptions))
            {
                conf = ConfigurationOptions.Parse(creds.RedisOptions);
            }
            else
            {
                conf = ConfigurationOptions.Parse("127.0.0.1,syncTimeout=3000");
            }

            Redis = ConnectionMultiplexer.Connect(conf);
            Redis.PreserveAsyncOrder = false;
            LocalImages = new RedisImagesCache(Redis, creds);
            LocalData   = new RedisLocalDataCache(Redis, creds, shardId);
            _redisKey   = creds.RedisKey();
        }
 private T Get <T>(string key) where T : class
 {
     return(JsonConvert.DeserializeObject <T>(_db.StringGet($"{_creds.RedisKey()}_localdata_{key}")));
 }
예제 #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;
            _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);
        }
예제 #18
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);
                }
            });
        }
예제 #19
0
 private byte[] Get(string key)
 {
     return(_db.StringGet($"{_creds.RedisKey()}_localimg_{key}"));
 }
예제 #20
0
        public string GetText(string localeName, string key)
        {
            var value = _redis.GetDatabase().HashGet($"{_creds.RedisKey()}:responses:{localeName}", key);

            return(value);
        }
예제 #21
0
        public StreamNotificationService(DbService db, DiscordSocketClient client,
                                         IBotStrings 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();
                _notifCleanupTimer = new Timer(_ =>
                {
                    try
                    {
                        var errorLimit     = TimeSpan.FromHours(12);
                        var failingStreams = _streamTracker.GetFailingStreams(errorLimit, true)
                                             .ToList();

                        if (!failingStreams.Any())
                        {
                            return;
                        }

                        var deleteGroups = failingStreams.GroupBy(x => x.Type)
                                           .ToDictionary(x => x.Key, x => x.Select(x => x.Name).ToList());

                        using (var uow = _db.GetDbContext())
                        {
                            foreach (var kvp in deleteGroups)
                            {
                                Log.Information($"Deleting {kvp.Value.Count} {kvp.Key} streams because " +
                                                $"they've been erroring for more than {errorLimit}: {string.Join(", ", kvp.Value)}");

                                var toDelete = uow._context.Set <FollowedStream>()
                                               .AsQueryable()
                                               .Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
                                               .ToList();

                                uow._context.RemoveRange(toDelete);
                                uow.SaveChanges();

                                foreach (var loginToDelete in kvp.Value)
                                {
                                    _streamTracker.UntrackStreamByKey(new StreamDataKey(kvp.Key, loginToDelete));
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error("Error cleaning up FollowedStreams");
                        Log.Error(ex.ToString());
                    }
                }, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));

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

            bot.JoinedGuild  += ClientOnJoinedGuild;
            client.LeftGuild += ClientOnLeftGuild;
        }
예제 #22
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;
        }
예제 #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);
                }
            });
        }
예제 #24
0
        public void ReloadXpTemplate()
        {
            var sub = _cache.Redis.GetSubscriber();

            sub.Publish(_creds.RedisKey() + "_reload_xp_template", "");
        }
예제 #25
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);
                    }
                }
            });
        }
예제 #26
0
        public Task LeaveGuild(string guildStr)
        {
            var sub = _cache.Redis.GetSubscriber();

            return(sub.PublishAsync(_creds.RedisKey() + "_leave_guild", guildStr));
        }