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); }
private async Task ListUsers(IrcPrivMessage request) { var users = string.Join(", ", _userManager.Users.Select(user => user.Email)); await SendResponse(request, $"Users: [{users}]"); }
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}"); } } }
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}"); }
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."); } }
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")})"); } }
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.")); }
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); }
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)}]"); } }
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); } } }
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."); } } }
private Task GetModules(IrcPrivMessage request) { return(SendMessageToNick(request.UserName, "Modules list: " + string.Join(", ", _modules.Value.Select(module => module.Name).ToList()))); }
protected override Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken) { var splitMessage = request.Text.Split(" "); return(splitMessage.Length <= 1 ? GetModules(request) : GetHelpForModule(request, splitMessage[1])); }
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}"); } } } }
protected override async Task HandleMessageAsyncInternal(IrcPrivMessage request, CancellationToken cancellationToken) { await SendResponse(request, "SpikeCore: " + _taglines[_random.Next(0, _taglines.Length)]); }