예제 #1
0
        protected override Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken)
        {
            var match = Regex.Match(request.Text);

            if (match.Success)
            {
                var command = match.Groups[1].Value;
                var details = match.Groups[2].Value;

                if (command.Equals("quit", StringComparison.InvariantCultureIgnoreCase))
                {
                    return(MessageBus.PublishAsync(new IrcQuitMessage(details)));
                }

                if (command.Equals("join", StringComparison.InvariantCultureIgnoreCase))
                {
                    return(MessageBus.PublishAsync(new IrcJoinChannelMessage(details)));
                }

                if (command.Equals("part", StringComparison.InvariantCultureIgnoreCase))
                {
                    var partMatch = PartRegex.Match(details);
                    if (partMatch.Success)
                    {
                        return(MessageBus.PublishAsync(new IrcPartChannelMessage(partMatch.Groups[1].Value, partMatch.Groups[2].Value)));
                    }
                }
            }

            return(Task.CompletedTask);
        }
예제 #2
0
        private async Task ListUsers(IrcPrivMessage request)
        {
            var users = string.Join(", ",
                                    _userManager.Users.Select(user => user.Email));

            await SendResponse(request, $"Users: [{users}]");
        }
예제 #3
0
        protected override async Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken)
        {
            var match = KarmaRegex.Match(request.Text);

            if (match.Success)
            {
                var nick = match.Groups[1].Value;
                var op   = match.Groups[2].Value;

                var user = _context.Karma
                           .SingleOrDefault(k => k.Name.ToLower() == nick.ToLower()) ?? new KarmaItem {
                    Name = nick, Karma = 0
                };

                // Ignore anyone tweaking their own karma.
                if (string.IsNullOrEmpty(op) || !nick.Equals(request.UserName, StringComparison.InvariantCultureIgnoreCase))
                {
                    if (op.Equals("--"))
                    {
                        user.Karma--;
                    }
                    else if (op.Equals("++"))
                    {
                        user.Karma++;
                    }

                    await SaveOrUpdate(user, cancellationToken);
                    await SendResponse(request, $"{user.Name} has a karma of {user.Karma}");
                }
            }
        }
예제 #4
0
        protected override async Task HandleMessageAsyncInternal(IrcPrivMessage request,
                                                                 CancellationToken cancellationToken)
        {
            var token = HttpUtility.UrlEncode(await _userManager.GenerateUserTokenAsync(request.IdentityUser,
                                                                                        "PasswordlessLoginTokenProvider",
                                                                                        "passwordless-auth"));

            await SendMessageToNick(request.UserName,
                                    $"{Configuration.WebAccessHost}/Authentication/TokenCallback?email={request.IdentityUser.Email}&token={token}");
        }
예제 #5
0
        private async Task RemoveUser(IrcPrivMessage request, string email)
        {
            var user = await _userManager.FindByEmailAsync(email);

            if (null != user)
            {
                await _userManager.DeleteAsync(user);
                await SendResponse(request, $"Successfully deleted user {email}.");

                Log.Information("{IrcUserName} (identity: {IdentityUserName}) has just deleted user {AffectedUserName} ", request.UserName,
                                request.IdentityUser.UserName, email);
            }
            else
            {
                await SendResponse(request, $"User {email} does not exist.");
            }
        }
예제 #6
0
        private async Task CreateUser(IrcPrivMessage request, string nickname, string email, string ircHostname,
                                      string matchType, string role, CancellationToken cancellationToken)
        {
            // If the user already exists, bail.
            if (null != await _userManager.FindByEmailAsync(email))
            {
                await SendResponse(request, $"{email} already exists, bailing...");
            }
            else
            {
                bool.TryParse(matchType, out var prefixMatch);

                // Create our user. Apparently the pre-built identity login methods prefer the username and email to be equal.
                var user = new SpikeCoreUser {
                    UserName = email, Email = email
                };
                await _userManager.CreateAsync(user);

                // Associate the proper roles with the user.
                var persistedUser = await _userManager.FindByEmailAsync(email);

                var roles = role.Equals("op", StringComparison.InvariantCultureIgnoreCase) ? OpRoles : RegularRoles;
                await _userManager.AddToRolesAsync(persistedUser, roles);

                // Associate a login with the user, so they can use the bot.
                await _userManager.AddLoginAsync(persistedUser,
                                                 new UserLoginInfo(IrcLoginProvider, ircHostname, nickname));

                // Prefix match is a custom field, so we'll update it out of band via EF directly.
                if (prefixMatch)
                {
                    var login = _context.UserLogins.Single(record =>
                                                           record.LoginProvider == IrcLoginProvider && record.ProviderKey == ircHostname);

                    login.MatchType = "StartsWith";

                    _context.UserLogins.Update(login);
                    await _context.SaveChangesAsync(cancellationToken);
                }

                Log.Information("{IrcUserName} (identity: {IdentityUserName}) has just created user {AffectedUserName}", request.UserName,
                                request.IdentityUser.UserName, email);
                await SendResponse(request,
                                   $"successfully created user {nickname} (email: {email}), with roles [{string.Join(", ", roles)}] (match type: {(prefixMatch ? "StartsWith" : "Literal")})");
            }
        }
예제 #7
0
        private Task GetHelpForModule(IrcPrivMessage request, string moduleName)
        {
            var module = _modules.Value.FirstOrDefault(x => x.Name.Equals(moduleName, StringComparison.InvariantCultureIgnoreCase));

            if (null != module)
            {
                var response = new List <string>
                {
                    "Module name: " + module.Name,
                    "Module Description: " + module.Description,
                    "Module Instructions: " + module.Instructions
                };

                return(SendMessagesToNick(request.UserName, response));
            }

            return(SendMessageToNick(request.UserName, "No such module exists, please try another."));
        }
예제 #8
0
        private void Connect()
        {
            _ircClient.PrivMessageReceived = async(channelMessage) =>
            {
                var message = new IrcPrivMessage()
                {
                    ChannelName  = channelMessage.ChannelName,
                    UserName     = channelMessage.UserName,
                    UserHostName = channelMessage.UserHostName,
                    Text         = channelMessage.Text,
                    IdentityUser = await FindSpikeCoreUser(channelMessage)
                };

                await _messageBus.PublishAsync(message);
            };

            _ircClient.MessageReceived = (receivedMessage) => _messageBus.PublishAsync(new IrcReceiveMessage(receivedMessage));

            _ircClient.Connect(_config.Host, _config.Port, _config.Nickname, _config.Channels, _config.Authenticate, _config.Password);
        }
예제 #9
0
        private async Task ShowUser(IrcPrivMessage request, string email)
        {
            var user = await _userManager.FindByEmailAsync(email);

            if (null == user)
            {
                await SendResponse(request, $"User {email} not found.");
            }
            else
            {
                // Join in our roles
                user.Roles = await _userManager.GetRolesAsync(user);

                // Find all applicable user logins. The bot will only create one, but people with physical DB access can add more.
                var logins = _context.UserLogins.Where(record =>
                                                       record.LoginProvider == IrcLoginProvider && record.UserId == user.Id).Select(record =>
                                                                                                                                    $"[{record.ProviderDisplayName}: {record.ProviderKey}, match type {(record.MatchType.Length > 0 ? record.MatchType : "Literal")}]")
                             .ToList();

                await SendResponse(request,
                                   $"User ID {user.Id}: {user.Email}, roles [{string.Join(", ", user.Roles)}], logins [{string.Join(", ", logins)}]");
            }
        }
예제 #10
0
        protected override async Task HandleMessageAsyncInternal(IrcPrivMessage request,
                                                                 CancellationToken cancellationToken)
        {
            var commandMatch = CommandRegex.Match(request.Text);

            if (commandMatch.Success)
            {
                var command      = commandMatch.Groups[1].Value;
                var details      = commandMatch.Groups[2].Value;
                var splitDetails = details.Length > 0 ? details.Split(" ") : new string[0];

                if (command.Equals("list", StringComparison.InvariantCultureIgnoreCase))
                {
                    await ListUsers(request);
                }

                // Single argument sub-methods.
                if (splitDetails.Length == 1)
                {
                    if (command.Equals("show", StringComparison.InvariantCultureIgnoreCase))
                    {
                        await ShowUser(request, splitDetails[0]);
                    }

                    if (command.Equals("remove", StringComparison.InvariantCultureIgnoreCase))
                    {
                        await RemoveUser(request, splitDetails[0]);
                    }
                }

                if (command.Equals("add", StringComparison.InvariantCultureIgnoreCase) && splitDetails.Length > 4)
                {
                    await CreateUser(request, splitDetails[0], splitDetails[1], splitDetails[2], splitDetails[3],
                                     splitDetails[4], cancellationToken);
                }
            }
        }
예제 #11
0
        protected override async Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken)
        {
            var match = Regex.Match(request.Text);

            if (!string.IsNullOrEmpty(Configuration.GeoIpApiKey) && match.Success)
            {
                var ip = match.Groups[1].Value.Trim();
                Task <HttpResponseMessage> ipScoreResponse = null;

                // Only check proxy status if someone has configured a contact email. This is required by the API.
                if (!string.IsNullOrEmpty(Configuration.GeoIpIntelEmailAddress))
                {
                    // This will be slower than our GeoIP lookup, so let's kick it off first.
                    ipScoreResponse = LookUpIpScore(ip, cancellationToken);
                }

                var body = await LookUpGeoIp(ip);

                var parsedGeoIpResponse = JObject.Parse(body);

                // The GeoIP service will give us a 200 even if it failed. The only way we know is to look for a success
                // property that only seems to exist when the call fails.
                if (null == parsedGeoIpResponse["success"] || parsedGeoIpResponse["success"].Value <bool>())
                {
                    var city    = parsedGeoIpResponse["city"].Value <string>() ?? "<unknown city>";
                    var country = parsedGeoIpResponse["country_name"].Value <string>() ?? "<unknown city>";
                    var result  = $"{ip} resolves to: {city}, {country}.";

                    // Assuming our setup has the proxy check enabled, let's give it some time to try and finish. If it
                    // doesn't come back within a reasonable period of time we'll respond without it.
                    if (null != ipScoreResponse &&
                        (await Task.WhenAny(ipScoreResponse, Task.Delay(2000, cancellationToken)) == ipScoreResponse))
                    {
                        var statusCode = ipScoreResponse.Result.StatusCode;

                        // IPIntelligence asks that consumers respect a 429, and try not to call more than 2 queries/sec.
                        if (statusCode == HttpStatusCode.TooManyRequests)
                        {
                            Log.Warning("We've been throttled by IpIntelligence, adding a backoff...");
                            result += " Getipintel.net has throttled this request. Please wait a few seconds before trying another.";
                        }

                        if (ipScoreResponse.Result.IsSuccessStatusCode)
                        {
                            var ipIntelResult = await ipScoreResponse.Result.Content.ReadAsStringAsync();

                            result += $" Getipintel.net scores this at {ipIntelResult} ({ParseIpScoreResponse(ipIntelResult)}).";
                        }
                        else
                        {
                            // The API doesn't document any other response codes, but the 500 series always in play when it comes to HTTP...
                            result += " Getipintel.net lookup has failed, please check the logs.";
                            Log.Warning("Received a status of {StatusCode} from getipintel.net", statusCode);
                        }
                    }

                    await SendResponse(request, result);
                }
                else
                {
                    Log.Warning("Failed to call our GeoIP service. Body: {Body}", body);
                    await SendResponse(request, "Failed calling the GeoIP service, please check the logs.");
                }
            }
        }
예제 #12
0
 private Task GetModules(IrcPrivMessage request)
 {
     return(SendMessageToNick(request.UserName, "Modules list: " + string.Join(", ", _modules.Value.Select(module => module.Name).ToList())));
 }
예제 #13
0
        protected override Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken)
        {
            var splitMessage = request.Text.Split(" ");

            return(splitMessage.Length <= 1 ? GetModules(request) : GetHelpForModule(request, splitMessage[1]));
        }
예제 #14
0
        protected override async Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken)
        {
            var match = FactoidRegex.Match(request.Text);

            if (match.Success)
            {
                var command     = match.Groups[1].Value;
                var name        = match.Groups[2].Value;
                var description = match.Groups[3].Value;

                // We're adding a new factoid.
                if (description.Length > 0)
                {
                    var factoid = new Factoid
                    {
                        Description  = description,
                        Type         = command,
                        CreationDate = DateTime.UtcNow,
                        CreatedBy    = request.UserName,
                        Name         = name
                    };

                    await SaveOrUpdate(factoid, cancellationToken);
                    await SendResponse(request, "Factoid saved.");
                }

                // Otherwise we're looking up factoids by name/type.
                else
                {
                    var query = _context.Factoids
                                .Where(factoid => factoid.Type.ToLower() == command.ToLower() && factoid.Name.ToLower() == name.ToLower());

                    var matchingFactoids = query
                                           .OrderByDescending(factoid => factoid.CreationDate)
                                           .Take(FactDisplayCount)
                                           .ToList();

                    int count = matchingFactoids.Count;
                    if (count > 0)
                    {
                        // If we retrieved the limit, check so we can tell users if there's more facts they can't see.
                        // They'll have to hit the web UI for these.
                        if (count == FactDisplayCount)
                        {
                            count = query.Count();
                        }

                        var pluralization = (count == 1) ? string.Empty : "s";

                        var paginationDisplay = (count > FactDisplayCount)
                            ? $" (showing the first {FactDisplayCount})"
                            : string.Empty;

                        var factoidDisplay = matchingFactoids
                                             .Select(x => $"[{x.Description} at {x.CreationDate:MM/dd/yyyy H:mm:ss UTC} by {x.CreatedBy}]");

                        var response =
                            $"Found {count} factoid{pluralization} of type {command}{paginationDisplay} for {name}: {String.Join(", ", factoidDisplay)}";

                        await SendResponse(request, response);
                    }
                    else
                    {
                        await SendResponse(request, $"No factoids for {name} of type {command}");
                    }
                }
            }
        }
예제 #15
0
 protected override async Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken)
 {
     await SendResponse(request, "SpikeCore: " + _taglines[_random.Next(0, _taglines.Length)]);
 }