コード例 #1
0
        /// <summary>
        /// DI Constructor
        /// </summary>
        public DiscordBot(IDiscordSocketClientWrapper discordSocketClient, ICommandServiceWrapper commandService,
                          IServiceProvider serviceProvider)
        {
            _discordSocketClient = discordSocketClient;
            _commandService      = commandService;
            _serviceProvider     = serviceProvider;

            _discordSocketClient.Log             += DiscordSocketClientOnLog;
            _discordSocketClient.MessageReceived += DiscordSocketClientOnMessageReceived;
            _logger = Logging.StaticLogger.GetLogger <DiscordBot>();
        }
コード例 #2
0
        /// <inheritdoc />
        public SocketCommandContext GetCommandContextForUserMessage(IDiscordSocketClientWrapper discordSocketClient, out int argPos)
        {
            argPos = 0;
            if (!(SocketMessage is SocketUserMessage msg))
            {
                return(null);
            }

            if ((_botConfig.AcceptedCharPrefix != '\0' && !msg.HasCharPrefix(_botConfig.AcceptedCharPrefix, ref argPos)) ||
                (!string.IsNullOrEmpty(_botConfig.AcceptedStringPrefix) && !msg.HasStringPrefix(_botConfig.AcceptedStringPrefix, ref argPos)) ||
                msg.HasMentionPrefix(discordSocketClient.CurrentUser, ref argPos))
            {
                _logger.LogDebug("Skipping {message}", msg.Content);
                return(null);
            }

            return(new SocketCommandContext(discordSocketClient.DiscordSocketClient, msg));
        }
コード例 #3
0
        public async Task <MoveResult> MovePlayersToCorrectChannelsAsync(
            IRCONWrapper rcon,
            IDiscordSocketClientWrapper client,
            ISocketGuildWrapper guild,
            CancellationToken cancellationToken)
        {
            if (rcon is null)
            {
                throw new ArgumentNullException(nameof(rcon));
            }
            if (client is null)
            {
                throw new ArgumentNullException(nameof(client));
            }
            if (guild is null)
            {
                throw new ArgumentNullException(nameof(guild));
            }

            var guildSettings = _settings.DiscordSettings.GuildSettings.FirstOrDefault(g => g.Id == guild.Id);

            if (guildSettings == null)
            {
                throw new Exception($"Unable to find guild setting with ID {guild.Id} in the configuration.");
            }

            var primaryVoiceChannel = guild.GetVoiceChannel(guildSettings.Channels.Primary.Id);

            if (primaryVoiceChannel == null)
            {
                throw new Exception("Bad primary channel ID in config.");
            }
            var secondaryVoiceChannel = guild.GetVoiceChannel(guildSettings.Channels.Secondary.Id);

            if (secondaryVoiceChannel == null)
            {
                throw new Exception("Bad secondary channel ID in config.");
            }

            var result = new MoveResult();

            var printInfo = await rcon.SendCommandAsync <PrintInfo>("sm_printinfo");

            var currentPlayersOnServer = printInfo.Players
                                         .Where(p => !"BOT".Equals(p.SteamId, StringComparison.CurrentCultureIgnoreCase))
                                         .ToList();

            _logger.LogDebug("Getting current voice channel users.");

            var getPrimaryChannelTask = guild.GetVoiceChannelAsync(primaryVoiceChannel.Id,
                                                                   options: new RequestOptions {
                CancelToken = cancellationToken
            });
            var getSecondaryChannelTask = guild.GetVoiceChannelAsync(secondaryVoiceChannel.Id,
                                                                     options: new RequestOptions {
                CancelToken = cancellationToken
            });

            await Task.WhenAll(getPrimaryChannelTask, getSecondaryChannelTask);

            var priamryChannel   = getPrimaryChannelTask.Result;
            var secondaryChannel = getSecondaryChannelTask.Result;

            var getPrimaryChannelUsersTask = priamryChannel.GetUsersAsync(
                options: new RequestOptions {
                CancelToken = cancellationToken
            })
                                             .FlattenAsync();
            var getSecondaryChannelUsersTask = secondaryChannel.GetUsersAsync(
                options: new RequestOptions {
                CancelToken = cancellationToken
            })
                                               .FlattenAsync();

            await Task.WhenAll(getPrimaryChannelTask, getSecondaryChannelUsersTask);

            var usersInPrimaryChannel   = getPrimaryChannelUsersTask.Result.ToList();
            var usersInSecondaryChannel = getSecondaryChannelUsersTask.Result.ToList();

            _logger.LogDebug("Current players per PrintInfo results ({playerCount}):", currentPlayersOnServer.Count);
            for (var i = 0; i < currentPlayersOnServer.Count; i++)
            {
                _logger.LogDebug("  {index}: {steamId} - {name}", i, currentPlayersOnServer[i].SteamId, currentPlayersOnServer[i].Name);
            }

            var discordUsersToMove = new List <(ISocketGuildUserWrapper user, ISocketVoiceChannelWrapper intendedChannel)>();

            var currentlyPlayingSteamIds = currentPlayersOnServer
                                           .Select(p => p.SteamId)
                                           .ToList();

            var currentPlayerMappings = _settings.UserMappings
                                        .Where(d => currentlyPlayingSteamIds.Intersect(d.SteamIds, StringComparer.CurrentCultureIgnoreCase).Any())
                                        .ToList();

            _logger.LogDebug("Current players found in mapping data ({playerCount}):", currentPlayerMappings.Count);
            for (var i = 0; i < currentPlayerMappings.Count; i++)
            {
                _logger.LogDebug("  {index}: {steamIds} - {discordId} - {name}",
                                 i, string.Join(",", currentPlayerMappings[i].SteamIds), currentPlayerMappings[i].DiscordId, currentPlayerMappings[i].Name);
            }

            var allSteamIdsFromUserMappings = _settings.UserMappings.SelectMany(um => um.SteamIds).ToList();
            var missingSteamMappings        = currentPlayersOnServer
                                              .Where(p => !allSteamIdsFromUserMappings.Contains(p.SteamId, StringComparer.CurrentCultureIgnoreCase))
                                              .ToList();

            var currentPlayerDiscordSnowflakes = currentPlayerMappings
                                                 .Select(p => p.DiscordId)
                                                 .ToList();

            var discordAccountsForCurrentPlayers = (guild.Users ?? Array.Empty <ISocketGuildUserWrapper>())
                                                   .Where(u => currentPlayerDiscordSnowflakes.Contains(u.Id))
                                                   .ToList();

            _logger.LogDebug("Discord accounts found from mappings ({matchCount}):", discordAccountsForCurrentPlayers.Count);
            for (var i = 0; i < discordAccountsForCurrentPlayers.Count; i++)
            {
                _logger.LogDebug("  {index}: {discordId} - {username}",
                                 i, discordAccountsForCurrentPlayers[i].Id, discordAccountsForCurrentPlayers[i].Username);
            }

            var discordSnowflakesInVoice = usersInPrimaryChannel.Select(u => u.Id)
                                           .Concat(usersInSecondaryChannel.Select(u => u.Id))
                                           .ToList();

            var missingDiscordMappings = discordAccountsForCurrentPlayers.Where(d =>
                                                                                !discordSnowflakesInVoice.Contains(d.Id))
                                         .ToList();

            if (missingSteamMappings.Any())
            {
                _logger.LogDebug("Current Steam users MISSING from mapping ({missingUserCount}):", missingSteamMappings.Count);
                for (var i = 0; i < missingSteamMappings.Count; i++)
                {
                    result.UnmappedSteamUsers.Add(new UnmappedSteamUser(missingSteamMappings[i].Name, missingSteamMappings[i].SteamId));
                    _logger.LogDebug("  {index}: {steamId} - {name}", i, missingSteamMappings[i].SteamId, missingSteamMappings[i].Name);
                }
            }

            if (missingDiscordMappings.Any())
            {
                _logger.LogDebug("Current Discord users MISSING from mapping ({missingUserCount}):", missingDiscordMappings.Count);
                for (var i = 0; i < missingDiscordMappings.Count; i++)
                {
                    _logger.LogDebug("  {index}: {id} - \"{nickname}\" (\"{username}\")",
                                     i,
                                     missingDiscordMappings[i].Id,
                                     missingDiscordMappings[i].Nickname,
                                     missingDiscordMappings[i].Username);
                }
            }

            foreach (var discordAccount in discordAccountsForCurrentPlayers)
            {
                ISocketVoiceChannelWrapper currentVoiceChannel;

                if (usersInPrimaryChannel != null && usersInPrimaryChannel.Any(u => u.Id == discordAccount.Id))
                {
                    currentVoiceChannel = primaryVoiceChannel;

                    _logger.LogDebug("{username} ({id}) found in primary channel.", discordAccount.Username, discordAccount.Id);
                }
                else if (usersInSecondaryChannel != null && usersInSecondaryChannel.Any(u => u.Id == discordAccount.Id))
                {
                    currentVoiceChannel = secondaryVoiceChannel;

                    _logger.LogDebug("{username} ({id}) found in secondary channel.", discordAccount.Username, discordAccount.Id);
                }
                else
                {
                    _logger.LogDebug("Skipping {username} ({id}): not in voice chat.",
                                     discordAccount.Username, discordAccount.Id);
                    continue;
                }

                if (primaryVoiceChannel.Id != currentVoiceChannel.Id && secondaryVoiceChannel.Id != currentVoiceChannel.Id)
                {
                    _logger.LogDebug("Skipping {username} ({id}): not in a designated voice channel.",
                                     discordAccount.Username, discordAccount.Id);
                    continue;
                }

                var userMappingsFromDiscordId = _settings.UserMappings.Where(um => um.DiscordId == discordAccount.Id).ToList();
                if (!userMappingsFromDiscordId.Any())
                {
                    _logger.LogDebug("Skipping {username} ({id}): Couldn't find user mapping.",
                                     discordAccount.Username, discordAccount.Id);
                    continue;
                }

                var allSteamIdsFromDiscordId = userMappingsFromDiscordId.SelectMany(s => s.SteamIds);
                var usersPrintInfo           = printInfo.Players.FirstOrDefault(pi =>
                                                                                allSteamIdsFromDiscordId.Any(sid => sid.Equals(pi.SteamId, StringComparison.CurrentCultureIgnoreCase)));
                if (usersPrintInfo == null)
                {
                    _logger.LogDebug("Skipping {username} ({id}): Couldn't find user's Steam ID ({userMappingSteamIds}) in PrintInfo results.",
                                     discordAccount.Username, discordAccount.Id, string.Join(", ", allSteamIdsFromDiscordId));
                    continue;
                }

                ISocketVoiceChannelWrapper intendedChannel;
                if (usersPrintInfo.TeamIndex == PrintInfo.TeamIndexSurvivor)
                {
                    intendedChannel = primaryVoiceChannel;
                }
                else if (usersPrintInfo.TeamIndex == PrintInfo.TeamIndexInfected)
                {
                    intendedChannel = secondaryVoiceChannel;
                }
                else
                {
                    continue;
                }

                discordUsersToMove.Add((discordAccount, intendedChannel));
            }

            foreach (var(user, intendedChannel) in discordUsersToMove)
            {
                ISocketVoiceChannelWrapper currentVoiceChannel;

                if (usersInPrimaryChannel.Any(u => u.Id == user.Id))
                {
                    currentVoiceChannel = primaryVoiceChannel;
                }
                else if (usersInSecondaryChannel.Any(u => u.Id == user.Id))
                {
                    currentVoiceChannel = secondaryVoiceChannel;
                }
                else
                {
                    // Not in voice chat
                    continue;
                }

                if (currentVoiceChannel.Id != intendedChannel.Id)
                {
                    _logger.LogDebug("Moving {username} ({id}) to other voice channel (from {fromChannelName} to {toChannelName}).",
                                     user.Username, user.Id,
                                     currentVoiceChannel.Name, intendedChannel.Name);

                    await user.ModifyAsync(p => p.ChannelId = intendedChannel.Id);

                    await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);

                    result.MoveCount++;
                }
            }

            return(result);
        }
コード例 #4
0
        public async Task <ReuniteResult> RenuitePlayersAsync(IDiscordSocketClientWrapper client, ISocketGuildWrapper guild,
                                                              CancellationToken cancellationToken)
        {
            if (client is null)
            {
                throw new ArgumentNullException(nameof(client));
            }
            if (guild is null)
            {
                throw new ArgumentNullException(nameof(guild));
            }

            var guildSettings = _settings.DiscordSettings.GuildSettings.FirstOrDefault(g => g.Id == guild.Id);

            if (guildSettings == null)
            {
                throw new Exception($"Unable to find guild setting with ID {guild.Id} in the configuration.");
            }

            var primaryVoiceChannel = guild.GetVoiceChannel(guildSettings.Channels.Primary.Id);

            if (primaryVoiceChannel == null)
            {
                throw new Exception("Bad primary channel ID in config.");
            }
            var secondaryVoiceChannel = guild.GetVoiceChannel(guildSettings.Channels.Secondary.Id);

            if (secondaryVoiceChannel == null)
            {
                throw new Exception("Bad secondary channel ID in config.");
            }

            var result = new ReuniteResult();

            _logger.LogDebug("Getting current voice channel users.");

            var getPrimaryChannelTask = guild.GetVoiceChannelAsync(primaryVoiceChannel.Id,
                                                                   options: new RequestOptions {
                CancelToken = cancellationToken
            });
            var getSecondaryChannelTask = guild.GetVoiceChannelAsync(secondaryVoiceChannel.Id,
                                                                     options: new RequestOptions {
                CancelToken = cancellationToken
            });

            await Task.WhenAll(getPrimaryChannelTask, getSecondaryChannelTask);

            var priamryChannel   = getPrimaryChannelTask.Result;
            var secondaryChannel = getSecondaryChannelTask.Result;

            var getPrimaryChannelUsersTask = priamryChannel.GetUsersAsync(
                options: new RequestOptions {
                CancelToken = cancellationToken
            })
                                             .FlattenAsync();
            var getSecondaryChannelUsersTask = secondaryChannel.GetUsersAsync(
                options: new RequestOptions {
                CancelToken = cancellationToken
            })
                                               .FlattenAsync();

            await Task.WhenAll(getPrimaryChannelTask, getSecondaryChannelUsersTask);

            var usersInPrimaryChannel   = getPrimaryChannelUsersTask.Result.ToList();
            var usersInSecondaryChannel = getSecondaryChannelUsersTask.Result.ToList();

            if (!usersInPrimaryChannel.Any() || !usersInSecondaryChannel.Any())
            {
                return(result);
            }

            foreach (var user in usersInSecondaryChannel)
            {
                await user.ModifyAsync(p => p.ChannelId = primaryVoiceChannel.Id);

                await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);

                result.MoveCount++;
            }

            return(result);
        }
コード例 #5
0
        public async Task StartAsync(IDiscordSocketClientWrapper client, CancellationToken cancellationToken)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            var readyComplete = new TaskCompletionSource <bool>();

#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
            client.Connected += async() => _logger.LogInformation("Discord client event: Connected");

            async Task initalReadyAsync() => await ReadyHandlerWithSignalAsync(readyComplete);

            client.Ready += initalReadyAsync;

            // NOTE: the client configuration will likely have ExclusiveBulkDelete set to true, which means that
            // for messages bulk-deleted with that specific API call, only the MessagesBulkDeleted event will be fired,
            // and not individual MessageDeleted events for the messages that are bulk-deleted.
            // This can be changed in DiscoredSocketConfig when the DiscordSocketClient is created.
            // See https://github.com/discord-net/Discord.Net/releases/tag/2.1.0

            client.Disconnected   += async(ex) => _logger.LogError(ex, "Discord client event: Disconnected");
            client.GuildAvailable += async(guild) =>
            {
                _logger.LogInformation("Discord client event: GuildAvailable ({id}: {name})", guild.Id, guild.Name);

                // NOTE! global commands take about 1 hour to register.
                // Since we're a private bot on only a couple of servers, register to guilds only.
                var registeredCommands = await _interactionService.RegisterCommandsToGuildAsync(guild.Id, true);

                _logger.LogInformation("Registered {count} commands in guild ({id}: {name})", registeredCommands.Count, guild.Id, guild.Name);
            };
            client.GuildMembersDownloaded += async(guild) => _logger.LogInformation("Discord client event: GuildMembersDownloaded");
            client.Log += async(logMessage) =>
            {
                var level = logMessage.Severity.ToLogLevel();

                _logger.Log(
                    level,
                    logMessage.Exception,
                    "Discord client event: Log: (Source: {source}): {message}",
                    logMessage.Source, logMessage.Message);
            };
            client.LoggedIn  += async() => _logger.LogInformation("Discord client event: LoggedIn");
            client.LoggedOut += async() => _logger.LogInformation("Discord client event: LoggedOut");

            await client.LoginAsync(TokenType.Bot, _settings.DiscordSettings.BotToken);

            await client.StartAsync();

            await readyComplete.Task;

            client.Ready -= initalReadyAsync;
            client.Ready += async() =>
            {
                _logger.LogInformation("Discord client event: Ready");
            };
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously

            await client.SetStatusAsync(UserStatus.Online);

            // TODO read commands/prefixes?
            await client.SetGameAsync("/help", type : ActivityType.Listening);
        }