@@ -35,6 +35,7 @@ namespace Combot | |||
private int PreNickChoice; | |||
private int RetryCount; | |||
private bool RetryAllowed; | |||
private Dictionary<string, SpamSession> SpamSessions; | |||
public Bot(ServerConfig serverConfig) | |||
{ | |||
@@ -46,6 +47,7 @@ namespace Combot | |||
ServerConfig = serverConfig; | |||
LoadTime = DateTime.Now; | |||
ConnectionTime = DateTime.Now; | |||
SpamSessions = new Dictionary<string, SpamSession>(); | |||
IRC = new IRC(serverConfig.MaxMessageLength, serverConfig.MessageSendDelay); | |||
IRC.ConnectEvent += HandleConnectEvent; | |||
@@ -419,6 +421,65 @@ namespace Combot | |||
return isCommand; | |||
} | |||
public bool SpamCheck(Channel channel, Nick nick, Module module, Command command) | |||
{ | |||
bool allowed = true; | |||
// Always allow the owner to skip the spam check | |||
if (ServerConfig.Owners.Contains(nick.Nickname)) | |||
return true; | |||
string source = string.Empty; | |||
// Generate the source based on the configured source type | |||
switch (ServerConfig.SpamSourceType) | |||
{ | |||
case SpamSourceType.Server: | |||
source = ServerConfig.Name; | |||
break; | |||
case SpamSourceType.Channel: | |||
source = channel.Name; | |||
break; | |||
case SpamSourceType.Nick: | |||
source = nick.Nickname; | |||
break; | |||
case SpamSourceType.Module: | |||
source = module.Name; | |||
break; | |||
case SpamSourceType.Command: | |||
source = command.Name; | |||
break; | |||
} | |||
if (SpamSessions.ContainsKey(source)) | |||
{ | |||
SpamSession session = SpamSessions[source]; | |||
// Check the current delay for the module | |||
DateTime curTime = DateTime.Now; | |||
DateTime lastTime = session.LastInstance; | |||
session.LastInstance = curTime; | |||
session.CurrentCount++; | |||
if (curTime.Subtract(lastTime) < ServerConfig.SpamSessionTime) | |||
{ | |||
// The time since the last command is within the spacing time setting | |||
// We need to check to see if we are over the max command count for this session | |||
if (session.CurrentCount > ServerConfig.SpamCountMax) | |||
{ | |||
allowed = false; | |||
} | |||
} | |||
else | |||
{ | |||
SpamSessions.Remove(source); | |||
SpamSessions.Add(source, new SpamSession()); | |||
} | |||
} | |||
else | |||
{ | |||
SpamSessions.Add(source, new SpamSession()); | |||
} | |||
return allowed; | |||
} | |||
private void HandleJoinEvent(object sender, JoinChannelInfo info) | |||
{ | |||
if (info.Nick.Nickname == IRC.Nickname) |
@@ -35,6 +35,9 @@ | |||
"CommandPrefix": ".", | |||
"JoinDelay": 100, | |||
"MaxMessageLength": 400, | |||
"MessageSendDelay": 100 | |||
"MessageSendDelay": 100, | |||
"SpamSourceType": 2, | |||
"SpamCountMax": 5, | |||
"SpamSessionTime": "00:00:01" | |||
} | |||
] |
@@ -78,6 +78,8 @@ | |||
<Compile Include="Modules\CommandArgument.cs" /> | |||
<Compile Include="Modules\CommandMessage.cs" /> | |||
<Compile Include="Modules\Option.cs" /> | |||
<Compile Include="SpamSession.cs" /> | |||
<Compile Include="SpamSourceType.cs" /> | |||
<Compile Include="Types.cs" /> | |||
<Compile Include="Modules\Module.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> |
@@ -27,6 +27,9 @@ namespace Combot.Configurations | |||
public int JoinDelay { get; set; } | |||
public int MaxMessageLength { get; set; } | |||
public int MessageSendDelay { get; set; } | |||
public SpamSourceType SpamSourceType { get; set; } | |||
public int SpamCountMax { get; set; } | |||
public TimeSpan SpamSessionTime { get; set; } | |||
public ServerConfig() | |||
{ | |||
@@ -47,6 +50,9 @@ namespace Combot.Configurations | |||
JoinDelay = 0; | |||
MaxMessageLength = 400; | |||
MessageSendDelay = 0; | |||
SpamSourceType = SpamSourceType.Nick; | |||
SpamCountMax = 5; | |||
SpamSessionTime = new TimeSpan(0, 0, 1); | |||
ModuleLocation = string.Empty; | |||
Owners = new List<string>(); | |||
ChannelBlacklist = new List<string>(); | |||
@@ -74,6 +80,9 @@ namespace Combot.Configurations | |||
JoinDelay = config.JoinDelay; | |||
MaxMessageLength = config.MaxMessageLength; | |||
MessageSendDelay = config.MessageSendDelay; | |||
SpamSourceType = config.SpamSourceType; | |||
SpamCountMax = config.SpamCountMax; | |||
SpamSessionTime = config.SpamSessionTime; | |||
ModuleLocation = config.ModuleLocation; | |||
Owners = new List<string>(); | |||
for (int i = 0; i < config.Owners.Count; i++) |
@@ -83,12 +83,16 @@ namespace Combot.Modules | |||
// If they have the correct access for the command, send it | |||
if (cmd.AllowedAccess.Exists(access => nickAccessTypes.Contains(access))) | |||
{ | |||
ParseCommand(command); | |||
// Make sure that the command isn't being spammed | |||
if (Bot.SpamCheck(Bot.IRC.Channels.Find(chan => chan.Name == command.Location), command.Nick, this, cmd)) | |||
{ | |||
ParseCommand(command); | |||
} | |||
} | |||
else | |||
{ | |||
string noAccessMessage = string.Format("You do not have access to use \u0002{0}\u000F.", command.Command); | |||
SendResponse(command.MessageType, command.Location, command.Nick.Nickname, noAccessMessage); | |||
SendResponse(command.MessageType, command.Location, command.Nick.Nickname, noAccessMessage, true); | |||
} | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Combot | |||
{ | |||
public class SpamSession | |||
{ | |||
public int CurrentCount { get; set; } | |||
public DateTime LastInstance { get; set; } | |||
public SpamSession() | |||
{ | |||
SetDefaults(); | |||
} | |||
private void SetDefaults() | |||
{ | |||
CurrentCount = 1; | |||
LastInstance = DateTime.Now; | |||
} | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Combot | |||
{ | |||
public enum SpamSourceType | |||
{ | |||
Server, | |||
Channel, | |||
Nick, | |||
Module, | |||
Command | |||
} | |||
} |
@@ -17,13 +17,17 @@ namespace Combot.Modules.Plugins | |||
{ | |||
if (!Bot.ServerConfig.ChannelBlacklist.Contains(inviteInfo.Channel) && !ChannelBlacklist.Contains(inviteInfo.Channel)) | |||
{ | |||
Bot.IRC.Command.SendJoin(inviteInfo.Channel); | |||
string helpMessage = string.Empty; | |||
if (Bot.Modules.Exists(module => module.Commands.Exists(cmd => cmd.Triggers.Contains("help") && cmd.Enabled))) | |||
// Check to see if it's being spammed | |||
if (Bot.SpamCheck(Bot.IRC.Channels.Find(chan => chan.Name == inviteInfo.Channel), inviteInfo.Requester, this, new Command() { Name = string.Format("{0} Commands", Name) })) | |||
{ | |||
helpMessage = string.Format(" For more information on what I can do, just type: {0}help", Bot.ServerConfig.CommandPrefix); | |||
Bot.IRC.Command.SendJoin(inviteInfo.Channel); | |||
string helpMessage = string.Empty; | |||
if (Bot.Modules.Exists(module => module.Commands.Exists(cmd => cmd.Triggers.Contains("help") && cmd.Enabled))) | |||
{ | |||
helpMessage = string.Format(" For more information on what I can do, just type: {0}help", Bot.ServerConfig.CommandPrefix); | |||
} | |||
Bot.IRC.Command.SendPrivateMessage(inviteInfo.Channel, string.Format("{0} has invited me to this channel. If you would like me to leave, just kick me.{1}", inviteInfo.Requester.Nickname, helpMessage)); | |||
} | |||
Bot.IRC.Command.SendPrivateMessage(inviteInfo.Channel, string.Format("{0} has invited me to this channel. If you would like me to leave, just kick me.{1}", inviteInfo.Requester.Nickname, helpMessage)); | |||
} | |||
else | |||
{ |
@@ -32,73 +32,77 @@ namespace Combot.Modules.Plugins | |||
{ | |||
if (urlRegex.IsMatch(message.Message)) | |||
{ | |||
MatchCollection urlMatches = urlRegex.Matches(message.Message); | |||
for (int i = 0; i < urlMatches.Count; i++) | |||
// Check to see if it's being spammed | |||
if (Bot.SpamCheck(Bot.IRC.Channels.Find(chan => chan.Name == message.Channel), message.Sender, this, new Command() { Name = string.Format( "{0} Commands", Name) })) | |||
{ | |||
Match urlMatch = urlMatches[i]; | |||
Uri url = new Uri(urlMatch.Value); | |||
HttpWebRequest webRequest = (HttpWebRequest) WebRequest.Create(url); | |||
webRequest.Method = "HEAD"; | |||
webRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"; | |||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | |||
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; | |||
try | |||
MatchCollection urlMatches = urlRegex.Matches(message.Message); | |||
for (int i = 0; i < urlMatches.Count; i++) | |||
{ | |||
using (HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse()) | |||
Match urlMatch = urlMatches[i]; | |||
Uri url = new Uri(urlMatch.Value); | |||
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url); | |||
webRequest.Method = "HEAD"; | |||
webRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"; | |||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | |||
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; | |||
try | |||
{ | |||
int code = (int) webResponse.StatusCode; | |||
if (code == 200) | |||
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse()) | |||
{ | |||
string contentType = webResponse.ContentType.Split('/')[0]; | |||
long contentLength = webResponse.ContentLength; | |||
switch (contentType) | |||
int code = (int)webResponse.StatusCode; | |||
if (code == 200) | |||
{ | |||
case "image": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[{0}] Size: {1}", webResponse.ContentType, ToFileSize(contentLength))); | |||
break; | |||
case "video": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[Video] Type: {0} | Size: {1}", webResponse.ContentType.Split('/')[1], ToFileSize(contentLength))); | |||
break; | |||
case "application": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[Application] Type: {0} | Size: {1}", webResponse.ContentType.Split('/')[1], ToFileSize(contentLength))); | |||
break; | |||
case "audio": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[Audio] Type: {0} | Size: {1}", webResponse.ContentType.Split('/')[1], ToFileSize(contentLength))); | |||
break; | |||
default: | |||
Regex ytRegex = new Regex("(((youtube.*(v=|/v/))|(youtu\\.be/))(?<ID>[-_a-zA-Z0-9]+))"); | |||
if (ytRegex.IsMatch(urlMatch.ToString())) | |||
{ | |||
Match ytMatch = ytRegex.Match(urlMatch.ToString()); | |||
string youtubeMessage = GetYoutubeDescription(ytMatch.Groups["ID"].Value); | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, youtubeMessage); | |||
} | |||
else | |||
{ | |||
ParseTitle(message, urlMatch.ToString()); | |||
} | |||
break; | |||
string contentType = webResponse.ContentType.Split('/')[0]; | |||
long contentLength = webResponse.ContentLength; | |||
switch (contentType) | |||
{ | |||
case "image": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[{0}] Size: {1}", webResponse.ContentType, ToFileSize(contentLength))); | |||
break; | |||
case "video": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[Video] Type: {0} | Size: {1}", webResponse.ContentType.Split('/')[1], ToFileSize(contentLength))); | |||
break; | |||
case "application": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[Application] Type: {0} | Size: {1}", webResponse.ContentType.Split('/')[1], ToFileSize(contentLength))); | |||
break; | |||
case "audio": | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[Audio] Type: {0} | Size: {1}", webResponse.ContentType.Split('/')[1], ToFileSize(contentLength))); | |||
break; | |||
default: | |||
Regex ytRegex = new Regex("(((youtube.*(v=|/v/))|(youtu\\.be/))(?<ID>[-_a-zA-Z0-9]+))"); | |||
if (ytRegex.IsMatch(urlMatch.ToString())) | |||
{ | |||
Match ytMatch = ytRegex.Match(urlMatch.ToString()); | |||
string youtubeMessage = GetYoutubeDescription(ytMatch.Groups["ID"].Value); | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, youtubeMessage); | |||
} | |||
else | |||
{ | |||
ParseTitle(message, urlMatch.ToString()); | |||
} | |||
break; | |||
} | |||
} | |||
else | |||
{ | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[URL] Returned Status Code \u0002{0}\u0002 ({1})", code, url.Host)); | |||
} | |||
} | |||
else | |||
} | |||
catch (WebException ex) | |||
{ | |||
if (ex.Response != null) | |||
{ | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[URL] Returned Status Code \u0002{0}\u0002 ({1})", code, url.Host)); | |||
int code = (int)((HttpWebResponse)ex.Response).StatusCode; | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[URL] Response Code: \u0002{0}\u0002 ({1})", code, url.Host)); | |||
} | |||
} | |||
} | |||
catch (WebException ex) | |||
{ | |||
if (ex.Response != null) | |||
catch (OutOfMemoryException ex) | |||
{ | |||
int code = (int) ((HttpWebResponse) ex.Response).StatusCode; | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[URL] Response Code: \u0002{0}\u0002 ({1})", code, url.Host)); | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[URL] \u0002Site content was too large\u0002 ({0})", url.Host)); | |||
} | |||
} | |||
catch (OutOfMemoryException ex) | |||
{ | |||
Bot.IRC.Command.SendPrivateMessage(message.Channel, string.Format("[URL] \u0002Site content was too large\u0002 ({0})", url.Host)); | |||
} | |||
} | |||
} | |||
} |