migrate from perforce

This commit is contained in:
2026-04-19 01:09:29 +02:00
commit ab61de2c79
55 changed files with 50979 additions and 0 deletions

26
Concilium.sln Normal file
View File

@@ -0,0 +1,26 @@
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Concilium", "Concilium\Concilium.csproj", "{2695100D-D24B-4B41-8035-D1E8ED9A87CF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Debug|x86.ActiveCfg = Debug|x86
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Debug|x86.Build.0 = Debug|x86
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Release|Any CPU.Build.0 = Release|Any CPU
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Release|x86.ActiveCfg = Release|x86
{2695100D-D24B-4B41-8035-D1E8ED9A87CF}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

155
Concilium/App.cs Normal file
View File

@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using System.IO;
using System.Threading;
using log4net.Appender;
using log4net.Config;
using log4net;
using log4net.Layout;
using Concilium.Bot;
using Concilium.Helpers;
using System.Net;
using System.Diagnostics;
namespace Concilium
{
public class App
{
public static void Main(string[] args)
{
Thread.CurrentThread.Name = "Main";
Console.BufferWidth = 120;
Console.BufferHeight = 1000;
Console.WindowWidth = 120;
var directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Concilium");
var allConsoleAppender = new ColoredConsoleAppender();
allConsoleAppender.Layout = new PatternLayout("%message (%logger)%newline");
var debugMapping = new log4net.Appender.ColoredConsoleAppender.LevelColors()
{
Level = log4net.Core.Level.Debug,
ForeColor = ColoredConsoleAppender.Colors.HighIntensity
};
var infoMapping = new log4net.Appender.ColoredConsoleAppender.LevelColors()
{
Level = log4net.Core.Level.Info,
ForeColor = ColoredConsoleAppender.Colors.White
};
var warningMapping = new log4net.Appender.ColoredConsoleAppender.LevelColors()
{
Level = log4net.Core.Level.Warn,
ForeColor = ColoredConsoleAppender.Colors.Yellow
};
var errorMapping = new log4net.Appender.ColoredConsoleAppender.LevelColors()
{
Level = log4net.Core.Level.Error,
ForeColor = ColoredConsoleAppender.Colors.Red
};
var fatalMapping = new log4net.Appender.ColoredConsoleAppender.LevelColors()
{
Level = log4net.Core.Level.Fatal,
BackColor = ColoredConsoleAppender.Colors.Red,
ForeColor = ColoredConsoleAppender.Colors.Yellow
};
allConsoleAppender.AddMapping(debugMapping);
allConsoleAppender.AddMapping(infoMapping);
allConsoleAppender.AddMapping(warningMapping);
allConsoleAppender.AddMapping(errorMapping);
allConsoleAppender.AddMapping(fatalMapping);
var levelRangeFilter = new log4net.Filter.LevelRangeFilter();
levelRangeFilter.LevelMin = log4net.Core.Level.Info;
levelRangeFilter.LevelMax = log4net.Core.Level.Off;
var noMessageTypesFilter = new log4net.Filter.LoggerMatchFilter();
noMessageTypesFilter.LoggerToMatch = "MESSAGETYPES";
noMessageTypesFilter.AcceptOnMatch = false;
noMessageTypesFilter.Next = levelRangeFilter;
var noConciliumFilter = new log4net.Filter.LoggerMatchFilter();
noConciliumFilter.LoggerToMatch = Log.Get().Logger.Name;
noConciliumFilter.AcceptOnMatch = false;
noConciliumFilter.Next = noMessageTypesFilter;
allConsoleAppender.AddFilter(noConciliumFilter);
allConsoleAppender.ActivateOptions();
var conciliumConsoleAppender = new ColoredConsoleAppender();
var conciliumFilter = new log4net.Filter.LoggerMatchFilter();
conciliumFilter.LoggerToMatch = Log.Get().Logger.Name;
conciliumFilter.AcceptOnMatch = true;
conciliumFilter.Next = new log4net.Filter.DenyAllFilter();
var ownMapping = new log4net.Appender.ColoredConsoleAppender.LevelColors()
{
Level = log4net.Core.Level.Info,
ForeColor = ColoredConsoleAppender.Colors.Cyan
};
var ownErrorMapping = new log4net.Appender.ColoredConsoleAppender.LevelColors()
{
Level = log4net.Core.Level.Error,
ForeColor = ColoredConsoleAppender.Colors.Red
};
conciliumConsoleAppender.AddMapping(ownMapping);
conciliumConsoleAppender.AddMapping(errorMapping);
conciliumConsoleAppender.AddMapping(fatalMapping);
conciliumConsoleAppender.AddMapping(warningMapping);
conciliumConsoleAppender.AddFilter(conciliumFilter);
conciliumConsoleAppender.Layout = new PatternLayout("%message%newline");
conciliumConsoleAppender.ActivateOptions();
var fileAppender = new FileAppender();
fileAppender.File = Path.Combine(directory, "Concilium.log");
fileAppender.Layout = new PatternLayout("%timestamp [%thread] %-5level %logger - %message%newline");
fileAppender.ActivateOptions();
BasicConfigurator.Configure(new IAppender[] { conciliumConsoleAppender, allConsoleAppender, fileAppender });
var concilium = new Concilium.Bot.Concilium();
ConsoleHelpers.SetConsoleCtrlHandler(new HandlerRoutine((t) =>
{
var isclosing = false;
switch (t)
{
case CtrlTypes.CTRL_C_EVENT:
isclosing = true;
Console.WriteLine("CTRL+C received!");
break;
case CtrlTypes.CTRL_BREAK_EVENT:
isclosing = true;
Console.WriteLine("CTRL+BREAK received!");
break;
case CtrlTypes.CTRL_CLOSE_EVENT:
isclosing = true;
Console.WriteLine("Program being closed!");
break;
case CtrlTypes.CTRL_LOGOFF_EVENT:
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
isclosing = true;
Console.WriteLine("User is logging off!");
break;
}
if (isclosing && concilium != null)
{
try
{
concilium.Save();
concilium = null;
}
catch (Exception)
{
}
}
return true;
}), true);
concilium.Run();
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class BotFeatureAttribute : Attribute
{
public BotFeatureAttribute(string _sName)
{
Name = _sName;
}
public string Name { get; private set; }
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot
{
public abstract class BotFeatureBase : IBotFeature
{
public virtual IrcDotNet.IrcClient Client
{
get;
set;
}
public virtual void Load()
{
}
public abstract void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip);
}
}

580
Concilium/Bot/Concilium.cs Normal file
View File

@@ -0,0 +1,580 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.Net;
using System.Web;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.IO;
using System.Xml;
using System.ServiceModel.Syndication;
using System.Net.Sockets;
using System.Collections;
using Concilium.Helpers;
using System.Net.NetworkInformation;
using System.Diagnostics;
using System.Drawing;
using IrcDotNet;
using System.Reflection;
namespace Concilium.Bot
{
public class RssItem
{
public string Title;
public string Link;
}
public class RssFeed
{
public string Url;
public List<RssItem> LastItems;
}
public class UserInfo
{
public DateTime? LastWritten;
public DateTime? LastJoined;
public DateTime? LastLeft;
public List<RssFeed> SubscribedFeeds;
}
public class BotData
{
public Dictionary<string, UserInfo> Info;
public Dictionary<string, List<string>> ChannelUsers;
public List<string> LogBlackList;
public Mafia Mafia;
public Resistance Resistance;
public PredicateLogic PredicateLogic;
}
public class Concilium : IrcBot
{
private Timer userMonitorTimer;
private Timer saveTimer;
private Timer rssTimer;
public static BotData Data;
private List<IBotFeature> botFeatures = new List<IBotFeature>();
protected override void InitializeChatCommandProcessors()
{
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
var attributes = type.GetCustomAttributes(typeof(BotFeatureAttribute), true).Cast<BotFeatureAttribute>();
if (attributes.Count() == 0)
continue;
var botFeature = Activator.CreateInstance(type) as IBotFeature;
botFeatures.Add(botFeature);
foreach (var attribute in attributes)
{
var name = attribute.Name;
ChatCommandProcessors.Add(name, (client, source, targets, message, parameters) =>
{
string sIp = null;
if (source is IrcUser)
{
sIp = GetIP((source as IrcUser).HostName);
}
botFeature.Execute(name, source.Name, targets.First().Name, message, parameters, sIp);
});
}
}
ChatCommandProcessors.Add(".random", ProcessChatRandom);
ChatCommandProcessors.Add(".log", ProcessLog);
ChatCommandProcessors.Add(".help", ProcessHelp);
ChatCommandProcessors.Add(".admin", ProcessAdmin);
ChatCommandProcessors.Add(".blacklist", ProcessBlackList);
ChatCommandProcessors.Add(">", ProcessPredicateLogic);
}
protected override void InitializeCommandProcessors()
{
}
public Concilium()
{
Load();
Connect();
}
protected override void OnClientRegistered(IrcClient client)
{
base.OnClientRegistered(client);
Client.Channels.Join("#makegames");
Client.Channels.Join("#sppro");
foreach (var botFeature in botFeatures)
{
botFeature.Client = Client;
botFeature.Load();
}
}
protected override void OnChannelMessageReceived(IrcChannel channel, IrcMessageEventArgs e)
{
UpdateLastWrite(e.Source.Name, e.Text, e.Targets.Select(t => t.Name));
}
protected override void OnLocalUserMessageReceived(IrcLocalUser localUser, IrcMessageEventArgs e)
{
UpdateLastWrite(e.Source.Name, e.Text, e.Targets.Select(t => t.Name));
}
protected void UpdateLastWrite(string _sNickName, string _sMessage, IEnumerable<string> _targets)
{
if (!Concilium.Data.Info.ContainsKey(_sNickName))
Concilium.Data.Info[_sNickName] = new UserInfo();
Concilium.Data.Info[_sNickName].LastWritten = DateTime.Now;
var target = _targets.FirstOrDefault();
string channel;
if (string.IsNullOrEmpty(target))
channel = "#makegames";//return;
else
channel = target;
if (channel[0] == '#')
channel = channel.Substring(1);
var now = DateTime.Now.Year.ToString() + '-' + DateTime.Now.Month.ToString() + "-" + DateTime.Now.Day.ToString();
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Concilium", channel, now + ".log");
Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var stream = new FileStream(path, FileMode.Append))
{
var message = DateTime.Now.Hour + ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second + " " + _sNickName + " " + _sMessage + "\r\n";
var bytes = Encoding.UTF8.GetBytes(message);
stream.Write(bytes, 0, bytes.Length);
}
}
protected override void OnLocalUserJoinedChannel(IrcLocalUser localUser, IrcChannelEventArgs e)
{
if (!Concilium.Data.ChannelUsers.ContainsKey(e.Channel.Name))
{
Concilium.Data.ChannelUsers[e.Channel.Name] = new List<string>();
}
}
protected override void OnLocalUserLeftChannel(IrcLocalUser localUser, IrcChannelEventArgs e)
{
if (Concilium.Data.ChannelUsers.ContainsKey(e.Channel.Name))
{
Concilium.Data.ChannelUsers.Remove(e.Channel.Name);
}
}
public void Load()
{
if (Concilium.Data != null)
return;
saveTimer = new Timer((object botImpl) =>
{
this.Save();
}, this, 0, 60000);
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Concilium", "data.json");
if (File.Exists(path))
{
Log.Get().Info(string.Format("Loading Concilium.Data from: {0}", path));
var dataJson = File.ReadAllText(path);
Concilium.Data = JsonConvert.DeserializeObject<BotData>(dataJson);
}
if (Concilium.Data == null)
Concilium.Data = new BotData();
if (Concilium.Data.Info == null)
Concilium.Data.Info = new Dictionary<string, UserInfo>();
if (Concilium.Data.ChannelUsers == null)
Concilium.Data.ChannelUsers = new Dictionary<string, List<string>>();
if (Concilium.Data.LogBlackList == null)
Concilium.Data.LogBlackList = new List<string>();
if (Concilium.Data.PredicateLogic == null)
Concilium.Data.PredicateLogic = new PredicateLogic();
if (Concilium.Data.Mafia == null)
Concilium.Data.Mafia = new Mafia();
if (Concilium.Data.Resistance == null)
Concilium.Data.Resistance = new Resistance();
foreach (var info in Concilium.Data.Info)
{
if (info.Value.SubscribedFeeds == null)
info.Value.SubscribedFeeds = new List<RssFeed>();
foreach (var feed in info.Value.SubscribedFeeds)
{
if (feed.LastItems == null)
feed.LastItems = new List<RssItem>();
}
}
}
public void Save()
{
if (Concilium.Data == null)
return;
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Concilium", "data.json");
Log.Get().Info(string.Format("Saving data to: {0}", path));
var dataJson = JsonConvert.SerializeObject(Concilium.Data, Newtonsoft.Json.Formatting.Indented);
while (true)
{
try
{
File.WriteAllText(path, dataJson);
break;
}
catch (Exception)
{
Thread.Sleep(1000);
}
}
}
private List<string> GetParameters(string message)
{
return CmdLineToArgvW.SplitArgs(message).ToList();
}
public static Color ColorFromHSV(double hue, double saturation, double value)
{
int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
double f = hue / 60 - Math.Floor(hue / 60);
value = value * 255;
int v = Convert.ToInt32(value);
int p = Convert.ToInt32(value * (1 - saturation));
int q = Convert.ToInt32(value * (1 - f * saturation));
int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
if (hi == 0)
return Color.FromArgb(255, v, t, p);
else if (hi == 1)
return Color.FromArgb(255, q, v, p);
else if (hi == 2)
return Color.FromArgb(255, p, v, t);
else if (hi == 3)
return Color.FromArgb(255, p, q, v);
else if (hi == 4)
return Color.FromArgb(255, t, p, v);
else
return Color.FromArgb(255, v, p, q);
}
private void ProcessHelp(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string message, IList<string> parameters)
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "I understand following commands: " + string.Join(" ", ChatCommandProcessors.Keys));
}
private void ProcessBlackList(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string message, IList<string> parameters)
{
if (!IsOp(source))
return;
if (parameters.Count < 1)
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Usage: .blacklist {show|add nickname|del nickname}.");
return;
}
if (parameters[0] == "show")
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Blacklisted members: " + string.Join(", ", Concilium.Data.LogBlackList));
}
else if (parameters[0] == "add")
{
if (parameters.Count > 1 && !string.IsNullOrEmpty(parameters[1]))
{
Concilium.Data.LogBlackList.Add(parameters[1]);
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Added " + parameters[1] + " to the black list.");
}
else
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Can't add to black list. Nickname was not specified.");
}
}
else if (parameters[0] == "del")
{
if (parameters.Count > 1 && !string.IsNullOrEmpty(parameters[1]))
{
if (Concilium.Data.LogBlackList.Remove(parameters[1]))
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Removed " + parameters[1] + " from the black list.");
}
else
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), parameters[1] + " was not in the black list.");
}
}
else
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Can't add to black list. Nickname was not specified.");
}
}
else
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Usage: .blacklist {show|add nickname|del nickname}.");
return;
}
}
private void ProcessPredicateLogic(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string message, IList<string> parameters)
{
var input = string.Join(" ", parameters);
var output = Concilium.Data.PredicateLogic.Parse(input);
if (!string.IsNullOrEmpty(output))
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), output);
}
private void ProcessLog(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string message, IList<string> parameters)
{
if (parameters.Count < 2)
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Usage: .log number unit (highlight). Unit is either s(econd), m(inute), h(our), d(ay) or w(eek).");
return;
}
int number = 0;
if (!int.TryParse(parameters[0], out number))
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "First parameter must be an integer.");
return;
}
string unit = parameters[1].ToLower();
TimeSpan logDuration = new TimeSpan();
if (unit == "s")
logDuration = TimeSpan.FromSeconds(number);
else if (unit == "m")
logDuration = TimeSpan.FromMinutes(number);
else if (unit == "h")
logDuration = TimeSpan.FromHours(number);
else if (unit == "d")
logDuration = TimeSpan.FromDays(number);
else if (unit == "w")
logDuration = TimeSpan.FromDays(7 * number);
else
{
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Second parameter must be either s(econd), m(inute), h(our), d(ay) or w(eek).");
return;
}
if (logDuration > TimeSpan.FromHours(2) && !IsOp(source))
logDuration = TimeSpan.FromHours(2);
DateTime startDate = DateTime.Now - logDuration;
DateTime endDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day + 1);
endDate.AddMilliseconds(-1);
var target = targets.FirstOrDefault();
string channel;
if (target == null || string.IsNullOrEmpty(target.Name))
channel = "#makegames";//return;
else
channel = target.Name;
if (channel[0] == '#')
channel = channel.Substring(1);
var userColors = new Dictionary<string, System.Drawing.Color>();
var log = new List<dynamic>();
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{
var now = date.Year.ToString() + '-' + date.Month.ToString() + "-" + date.Day.ToString();
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Concilium", channel, now + ".log");
if (File.Exists(path))
{
using (var sr = new StreamReader(path))
{
log.Add(date.ToLongDateString());
string sMessage = sr.ReadLine();
while (sMessage != null)
{
var tokens = sMessage.ReverseFormat("{0}:{1}:{2} {3} {4}");
var logDay = new DateTime(date.Year, date.Month, date.Day, int.Parse(tokens[0]), int.Parse(tokens[1]), int.Parse(tokens[2]));
if (logDay > startDate)
{
var nickName = tokens[3];
var messageContent = tokens[4];
log.Add(new
{
time = logDay.ToLongTimeString(),
nick = nickName,
message = messageContent
});
userColors[nickName] = new Color();
}
sMessage = sr.ReadLine();
}
}
}
}
string result = "<html><head><style type=\"text/css\">";
result += "body, document, table { font-family: Verdana, sans-serif; background: #222; color: white; margin: 0; padding: 0; width: 100%; } ";
result += "p { margin: 0px } ";
result += "a { color: #80A0FF } ";
result += "a:visited { color: #A080FF } ";
result += ".date { font-size: 18px; margin: 5px; } ";
result += ".time { font-size: 10px; margin: 2px; width: 80px; } ";
result += ".nick { font-weight: bold; margin: 2px; text-align: right; width: 1px; } ";
result += ".highlight { background: #FFE0A0; color: #102040; } ";
result += "</style></head><body><table>";
double angle = 0.0;
var users = userColors.Keys.ToList();
foreach (var user in users)
{
userColors[user] = ColorFromHSV(angle, 0.75, 0.75);
angle += 360.0 / users.Count;
}
foreach (var logEntry in log)
{
if (logEntry is string)
{
result += "<tr><td colspan=\"3\" class=\"date\">" + (logEntry as string) + "</td></tr>";
}
else
{
var colorString = ColorTranslator.ToHtml(userColors[logEntry.nick]);
result += "<tr>";
result += "<td class=\"time\">" + logEntry.time + "</td>";
result += "<td class=\"nick\" style=\"color:" + colorString + "\">&lt;" + logEntry.nick + "&gt;</td>";
var sMessage = Regex.Replace(logEntry.message, "http\\://[a-zA-Z0-9\\-\\.]+(/\\S*)?", "<a href=\"$&\">$&</a>");
sMessage = Regex.Replace(sMessage, "https\\://[a-zA-Z0-9\\-\\.]+(/\\S*)?", "<a href=\"$&\">$&</a>");
result += "<td class=\"msg\">" + HttpUtility.HtmlEncode(sMessage) + "</td>";
result += "</tr>";
}
}
result += "</html></body>";
if (parameters.Count > 2)
result = Regex.Replace(result, parameters[2], "<span class=\"highlight\">$&</span>");
using (var wc = new WebClient())
{
var reqparm = new System.Collections.Specialized.NameValueCollection();
reqparm.Add("file", result);
byte[] responsebytes = wc.UploadValues("http://xTr1m.com/conciliumlog.php", "POST", reqparm);
string responsebody = Encoding.UTF8.GetString(responsebytes);
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), "Log updated: http://www.xTr1m.com/temp/concilium.html");
}
}
private bool IsOp(IIrcMessageSource source)
{
if (source.Name == "xTr1m")
return true;
return source.Name.StartsWith("!") || source.Name.StartsWith("@");
}
private void ProcessAdmin(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string message, IList<string> parameters)
{
if (source.Name != "xTr1m" && source.Name != "Concilium")
return;
if (parameters[0] == "say")
{
Client.LocalUser.SendMessage(parameters[1], string.Join(" ", parameters.Skip(2)));
}
else if (parameters[0] == "restart")
{
Disconnect();
Connect();
}
else if (parameters[0] == "quit")
{
this.Save();
Disconnect();
System.Environment.Exit(0);
}
else if (parameters[0] == "topic")
{
client.GetChannel(parameters[1]).SetTopic(string.Join(" ", parameters.Skip(2)));
}
}
private void ProcessChatRandom(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string message, IList<string> parameters)
{
if (parameters.Count != 2)
throw new InvalidCommandParametersException(".random", 2);
Log.Get().Info(string.Format("{0} requested a random number", source.Name));
var min = double.Parse(parameters[0]);
var max = double.Parse(parameters[1]);
var len = max - min;
var rand = new Random();
Client.LocalUser.SendMessage(GetDefaultReplyTargets(client, source, targets), (Math.Floor((rand.NextDouble() * len + min) * 100.0) / 100.0).ToString());
}
public static string GetIP(string hostName)
{
try
{
var host = Dns.GetHostEntry(hostName);
foreach (IPAddress ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
}
catch (Exception)
{
try
{
var nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (var nic in nics)
{
var ipProps = nic.GetIPProperties();
// We're only interested in IPv4 addresses for this example.
var ipv4Addrs = ipProps.UnicastAddresses
.Where(addr => addr.Address.AddressFamily == AddressFamily.InterNetwork);
foreach (var addr in ipv4Addrs)
{
if (addr.IPv4Mask == null)
continue;
return addr.Address.ToString();
}
}
}
catch (Exception)
{
}
}
return null;
}
public static WebClient CreateWebClient()
{
var wc = new WebClient();
wc.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11");
wc.Headers.Add(HttpRequestHeader.AcceptLanguage, "de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4");
wc.Headers.Add(HttpRequestHeader.Referer, "91.194.90.37");
return wc;
}
}
}

View File

@@ -0,0 +1,25 @@
using Concilium.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot.Features
{
[BotFeature(".fortune")]
public class FortuneFeature : BotFeatureBase
{
public override void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip)
{
if (_parameters.Count != 0)
throw new InvalidCommandParametersException(".fortune", 0);
var wc = Concilium.CreateWebClient();
var result = Encoding.UTF8.GetString(wc.DownloadData("http://www.fortunefortoday.com/getfortuneonly.php"));
result = result.Replace("\n", "");
Log.Get().Info(string.Format("{0} requested a fortune cookie", _sSource));
Client.LocalUser.SendMessage(Concilium.GetDefaultReplyTarget(_sSource, _sTarget), string.Format("{0}: {1}", _sSource, result));
}
}
}

View File

@@ -0,0 +1,127 @@
using Concilium.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
namespace Concilium.Bot.Features
{
[BotFeature(".googlefight")]
[BotFeature(".google")]
[BotFeature(".googleimage")]
public class GoogleFeature : BotFeatureBase
{
public override void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip)
{
string sReply = null;
if (_sCommand == ".googlefight")
sReply = ProcessChatGoogleFight(_parameters, _ip);
else if (_sCommand == ".google")
sReply = ProcessChatGoogle(_parameters, _ip);
else if (_sCommand == ".googleimage")
sReply = ProcessChatGoogleImage(_parameters, _ip);
else
throw new NotImplementedException();
Client.LocalUser.SendMessage(Concilium.GetDefaultReplyTarget(_sSource, _sTarget), sReply);
}
private dynamic SearchGoolgeFor(string _sQuery, string _sType, string _ip)
{
var wc = Concilium.CreateWebClient();
_ip = _ip == null ? "" : "&userip=" + _ip;
var result = wc.DownloadData("http://ajax.googleapis.com/ajax/services/search/" + _sType + "?v=1.0&q=" + HttpUtility.UrlEncode(_sQuery) + _ip);
return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(result));
}
private string ProcessChatGoogle(IList<string> _parameters, string _ip)
{
var query = string.Join(" ", _parameters);
dynamic json = SearchGoolgeFor(query, "web", _ip);
if (json.responseData == null)
{
Log.Get().Info(string.Format("Google search failed: {0}", json.responseDetails.Value as string));
return string.Format("Google search failed: {0}", json.responseDetails.Value as string);
}
else
{
if (json.responseData.results == null || (json.responseData.results is JArray && (json.responseData.results as JArray).Count == 0))
{
return "No results found";
}
else
{
string url = json.responseData.results[0].url.Value;
string title = json.responseData.results[0].title.Value;
title = HttpUtility.HtmlDecode(title);
return string.Format("{0} {1}", title, url);
}
}
}
private string ProcessChatGoogleImage(IList<string> _parameters, string _ip)
{
var query = string.Join(" ", _parameters);
var json = SearchGoolgeFor(query, "images", _ip);
if (json.responseData == null)
{
Log.Get().Info(string.Format("Google search failed: {0}", json.responseDetails.Value as string));
return string.Format("Google search failed: {0}", json.responseDetails.Value as string);
}
else
{
if (json.responseData.results == null || (json.responseData.results is JArray && (json.responseData.results as JArray).Count == 0))
{
return "No results found";
}
else
{
string url = json.responseData.results[0].url.Value;
string title = json.responseData.results[0].title.Value;
title = HttpUtility.HtmlDecode(title);
return string.Format("{0} {1}", title, url);
}
}
}
private string ProcessChatGoogleFight(IList<string> _parameters, string _ip)
{
var joinedParams = string.Join(" ", _parameters.ToArray());
if (_parameters.Count() != 2)
throw new InvalidCommandParametersException(".googlefight", 2);
var json1 = SearchGoolgeFor(_parameters[0], "web", _ip);
var json2 = SearchGoolgeFor(_parameters[1], "web", _ip);
if (json1.responseData == null || json2.responseData == null)
{
Log.Get().Info(string.Format("Googlefighting failed: {0} {1}", json1.responseDetails.Value as string, json2.responseDetails.Value as string));
return string.Format("Googlefighting failed: {0} {1}", json1.responseDetails.Value as string, json2.responseDetails.Value as string);
}
else
{
//Count and display the hits
var hits1 = int.Parse(json1.responseData.cursor.estimatedResultCount == null ? "0" : json1.responseData.cursor.estimatedResultCount.Value as string);
var hits2 = int.Parse(json2.responseData.cursor.estimatedResultCount == null ? "0" : json2.responseData.cursor.estimatedResultCount.Value as string);
var total = hits1 + hits2;
float percent1 = (float)Math.Floor(10000.0f * (float)hits1 / (float)total) / 100.0f;
float percent2 = (float)Math.Floor(10000.0f * (float)hits2 / (float)total) / 100.0f;
var hits1s = json1.responseData.cursor.resultCount == null ? "0" : json1.responseData.cursor.resultCount.Value as string;
var hits2s = json2.responseData.cursor.resultCount == null ? "0" : json2.responseData.cursor.resultCount.Value as string;
Log.Get().Info(string.Format("Googlefighting {0} and {1}", _parameters[0], _parameters[1]));
return string.Format("\"{0}\": {1} hits ({2}%), \"{3}\": {4} hits ({5}%)", _parameters[0], hits1s, percent1, _parameters[1], hits2s, percent2);
}
}
}
}

View File

@@ -0,0 +1,61 @@
using Concilium.Helpers;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
namespace Concilium.Bot.Features
{
[BotFeature(".joke")]
public class JokeFeature : BotFeatureBase
{
public override void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip)
{
string target = "";
if (_parameters.Count == 1)
target = _parameters[0];
else if (_parameters.Count > 1)
throw new InvalidCommandParametersException(".joke", 1);
else
{
var minDate = DateTime.Now;
foreach (var entry in Concilium.Data.Info)
{
if (entry.Value.LastWritten.HasValue && entry.Value.LastWritten.Value < minDate)
{
minDate = entry.Value.LastWritten.Value;
target = entry.Key;
}
}
if (target == "")
target = "Concilium";
}
var wc = Concilium.CreateWebClient();
var result = wc.DownloadData("http://api.icndb.com/jokes/random?lastName=&firstName=" + target);
dynamic json = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(result));
if (json.type == null || json.type.Value != "success")
{
Log.Get().Info(string.Format("Joke failed: {0}", _sSource));
Client.LocalUser.SendMessage(Concilium.GetDefaultReplyTarget(_sSource, _sTarget), "Joke failed");
}
else
{
var joke = json.value.joke.Value as string;
joke = joke.Replace(" ", " ");
joke = joke.Replace(" .", ".");
joke = joke.Replace(" ,", ",");
joke = joke.Replace(" 's", "'s");
joke = HttpUtility.HtmlDecode(joke);
Log.Get().Info(string.Format("{0} wants a joke about {1}", _sSource, target));
Client.LocalUser.SendMessage(Concilium.GetDefaultReplyTarget(_sSource, _sTarget), joke);
}
}
}
}

View File

@@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot.Features
{
[BotFeature(".mafia")]
public class MafiaFeature : BotFeatureBase
{
public static string MafiaChannel = "#sppro.mafia";
public override void Load()
{
var channel = Client.GetChannel(MafiaChannel);
if (channel == null)
{
Client.Channels.Join(MafiaChannel);
channel = Client.GetChannel(MafiaChannel);
}
Concilium.Data.Mafia.DayBegins += () =>
{
foreach (var user in channel.Users)
user.Voice();
};
Concilium.Data.Mafia.NightBegins += () =>
{
foreach (var user in channel.Users)
user.DeVoice();
};
Concilium.Data.Mafia.OnUserJoined += (nickName) =>
{
channel.Users.First(u => u.User.NickName == nickName).Voice();
};
Concilium.Data.Mafia.OnUserDied += (nickName) =>
{
channel.Users.First(u => u.User.NickName == nickName).DeVoice();
};
Concilium.Data.Mafia.GameOver += () =>
{
foreach (var user in channel.Users)
user.DeVoice();
Concilium.Data.Mafia = null;
channel.SetModes("-m");
};
Concilium.Data.Mafia.OnLobbyNotice += (notice) =>
{
Client.LocalUser.SendMessage(MafiaChannel, notice);
};
Concilium.Data.Mafia.OnUsersNotice += (users, notice) =>
{
foreach (var user in users)
Client.LocalUser.SendMessage(user, notice);
};
Concilium.Data.Mafia.CheckState();
}
public override void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip)
{
if (_parameters.Count == 0)
{
Client.LocalUser.SendMessage(_sSource, "Mafia is played at the " + MafiaChannel + " channel.");
Client.GetChannel(MafiaChannel).Invite(_sSource);
return;
}
if (_sTarget != null && _sTarget != MafiaChannel)
{
Client.LocalUser.SendMessage(_sSource, "Mafia is played at the " + MafiaChannel + " channel.");
Client.GetChannel(MafiaChannel).Invite(_sSource);
return;
}
var action = _parameters[0];
if (action == "help")
{
if (_parameters.Count == 1)
Client.LocalUser.SendMessage(_sSource, "Mafia commands: help, create, join, vote, unvote, detect, save, status, kill. Use \".mafia help %command%\" to learn more.");
else
{
var helpAction = _parameters[1];
if (helpAction == "help")
{
Client.LocalUser.SendMessage(_sSource, string.Format("Are you dumb, {0}?", _sSource));
}
else if (helpAction == "create")
{
Client.LocalUser.SendMessage(_sSource, "Creates a new mafia game. Expects no _parameters.");
}
else if (helpAction == "start")
{
Client.LocalUser.SendMessage(_sSource, "Starts a mafia game when at least 5 players have joined. Expects no parameter.");
}
else if (helpAction == "join")
{
Client.LocalUser.SendMessage(_sSource, "Join the current mafia game. Expects no _parameters.");
}
else if (helpAction == "vote")
{
Client.LocalUser.SendMessage(_sSource, "Places a vote. May be used several times. Parameter: The nick name of the player you're voting for.");
}
else if (helpAction == "unvote")
{
Client.LocalUser.SendMessage(_sSource, "Removes your vote if it exists.");
}
else if (helpAction == "detect")
{
Client.LocalUser.SendMessage(_sSource, "Detectives can discover if one player is a mafioso once each night. Parameter: The nick name of the player you're detecting.");
}
else if (helpAction == "save")
{
Client.LocalUser.SendMessage(_sSource, "Doctors can rescue one player from a possible mafioso assault. Parameter: The nick name of the player you're detecting.");
}
else if (helpAction == "kill")
{
Client.LocalUser.SendMessage(_sSource, "Vigilantes can kill one player at will during the night. Parameter: The nick name of the player to be killed.");
}
else if (helpAction == "status")
{
Client.LocalUser.SendMessage(_sSource, "Announces the current game status.");
}
else
{
Client.LocalUser.SendMessage(_sSource, "There's no such command available in mafia.");
}
}
}
else if (action == "reset")
{
if (Concilium.Data.Mafia != null && _sTarget == "Concilium" && _sSource == Concilium.Data.Mafia.GameCreator)
{
Client.LocalUser.SendMessage(MafiaChannel, "The mafia game was shut down. (reset command used by " + Concilium.Data.Mafia.GameCreator + ")");
Concilium.Data.Mafia = null;
}
}
else if (action == "create")
{
if (Concilium.Data.Mafia != null)
return;
Concilium.Data.Mafia = new Mafia();
Load();
Concilium.Data.Mafia.Init(_sSource);
foreach (var user in Client.GetChannel(MafiaChannel).Users)
user.DeVoice();
foreach (var channel in Client.Channels)
{
if (channel.Name != MafiaChannel)
Client.LocalUser.SendMessage(channel, "A mafia game was just created! If you'd like to play, join " + MafiaChannel + " now!");
}
}
else if (action == "start")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
Concilium.Data.Mafia.StartGame(_sSource);
Client.GetChannel(MafiaChannel).SetModes("+m");
}
else if (action == "join")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
Concilium.Data.Mafia.JoinPlayer(_sSource);
}
else if (action == "kill")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
if (_sTarget == "Concilium")
{
Client.LocalUser.SendMessage(_sSource, "Kill only via query!");
}
else
{
if (_parameters.Count != 2)
Client.LocalUser.SendMessage(_sSource, "Vigilantes can kill one player at will during the night. Parameter: The nick name of the player to be killed.");
else
{
Concilium.Data.Mafia.VoteVigilanteKill(_sSource, _parameters[1]);
}
}
}
else if (action == "vote")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
if (_sTarget == "Concilium")
{
Client.LocalUser.SendMessage(_sSource, "Place a vote only via query!");
}
else
{
if (_parameters.Count != 2)
Client.LocalUser.SendMessage(_sSource, "Places a vote. May be used several times. Parameter: The nick name of the player you're voting for.");
else
{
Concilium.Data.Mafia.PlayerVote(_sSource, _parameters[1]);
}
}
}
else if (action == "unvote")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
Concilium.Data.Mafia.Unvote(_sSource);
}
else if (action == "detect")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
if (_sTarget == "Concilium")
{
Client.LocalUser.SendMessage(_sSource, "Detective work should only be done via query!");
}
else
{
if (_parameters.Count != 2)
Client.LocalUser.SendMessage(_sSource, "Detectives can discover if one player is a mafioso once each night. Parameter: The nick name of the player you're detecting.");
else
{
Concilium.Data.Mafia.DetectMafioso(_sSource, _parameters[1]);
}
}
}
else if (action == "save")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
if (_sTarget == "Concilium")
{
Client.LocalUser.SendMessage(_sSource, "Doctor work should only be done via query!");
}
else
{
if (_parameters.Count != 2)
Client.LocalUser.SendMessage(_sSource, "Doctors can rescue one player from a possible mafioso assault. Parameter: The nick name of the player you're detecting.");
else
{
Concilium.Data.Mafia.SaveCivilist(_sSource, _parameters[1]);
}
}
}
else if (action == "status")
{
if (Concilium.Data.Mafia == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running mafia game.");
return;
}
Client.LocalUser.SendMessage(Concilium.GetDefaultReplyTarget(_sSource, _sTarget), Concilium.Data.Mafia.GetStatus());
}
}
}
}

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Text;
using System.Threading;
using System.Xml;
namespace Concilium.Bot.Features
{
[BotFeature(".rssadd")]
[BotFeature(".rssdel")]
[BotFeature(".rsslist")]
public class RssFeature : BotFeatureBase
{
public override void Load()
{
m_Timer = new Timer(RssTimerStart, this, 0, 60000);
}
private void RssTimerStart(object state)
{
if (Concilium.Data == null)
return;
foreach (var nickName in Concilium.Data.Info.Keys.ToList())
{
if (Concilium.Data.Info[nickName].SubscribedFeeds != null)
{
foreach (var feed in Concilium.Data.Info[nickName].SubscribedFeeds.ToList())
{
try
{
var reader = XmlReader.Create(feed.Url);
var synFeed = SyndicationFeed.Load(reader);
var newList = new List<RssItem>();
foreach (SyndicationItem i in synFeed.Items)
{
newList.Add(new RssItem() { Link = i.Links[0].Uri.ToString(), Title = i.Title.Text });
}
foreach (var item in newList)
{
if (feed.LastItems != null && feed.LastItems.Any(li => li.Link == item.Link))
continue;
Client.LocalUser.SendMessage(nickName, string.Format("{0} {1}", item.Title, item.Link));
}
feed.LastItems = newList;
}
catch (Exception e)
{
Client.LocalUser.SendNotice(nickName, string.Format("Error while checking {0}: {1}", feed.Url, e.Message));
}
}
}
}
}
public override void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip)
{
if (_sCommand == ".rssadd")
ProcessChatRssAdd(_sSource, _sMessage, _parameters);
else if (_sCommand == ".rssdel")
ProcessChatRssAdd(_sSource, _sMessage, _parameters);
else if (_sCommand == ".rsslist")
ProcessChatRssAdd(_sSource, _sMessage, _parameters);
else
throw new NotImplementedException();
}
private void ProcessChatRssAdd(string _sSource, string _sMessage, IList<string> _parameters)
{
if (_parameters.Count != 1)
throw new InvalidCommandParametersException(".rssadd", 1);
try
{
XmlReader reader = XmlReader.Create(_parameters[0], new XmlReaderSettings() { DtdProcessing = DtdProcessing.Parse });
SyndicationFeed synFeed = SyndicationFeed.Load(reader);
if (Concilium.Data.Info[_sSource].SubscribedFeeds == null)
Concilium.Data.Info[_sSource].SubscribedFeeds = new List<RssFeed>();
if (!Concilium.Data.Info[_sSource].SubscribedFeeds.Any(f => f.Url == _parameters[0]))
{
Concilium.Data.Info[_sSource].SubscribedFeeds.Add(new RssFeed() { Url = _parameters[0], LastItems = new List<RssItem>() });
Client.LocalUser.SendMessage(_sSource, "RSS feed subscribed successfully");
}
else
{
Client.LocalUser.SendMessage(_sSource, "RSS feed already subscribed");
}
}
catch (Exception)
{
Client.LocalUser.SendMessage(_sSource, "Error: RSS feed not recognized");
}
}
private void ProcessChatRssDel(string _sSource, string _sMessage, IList<string> _parameters)
{
if (_parameters.Count != 1)
throw new InvalidCommandParametersException(".rssdel", 1);
if (Concilium.Data.Info[_sSource].SubscribedFeeds == null)
{
Client.LocalUser.SendMessage(_sSource, "RSS feed doesn't exist");
}
else
{
var rssFeed = Concilium.Data.Info[_sSource].SubscribedFeeds.FirstOrDefault(f => f.Url == _parameters[0]);
if (rssFeed != null)
{
Concilium.Data.Info[_sSource].SubscribedFeeds.Remove(rssFeed);
Client.LocalUser.SendMessage(_sSource, "RSS feed removed successfully");
}
else
{
Client.LocalUser.SendMessage(_sSource, "RSS feed doesn't exist");
}
}
}
private void ProcessChatRssList(string _sSource, string _sMessage, IList<string> _parameters)
{
if (_parameters.Count != 0)
throw new InvalidCommandParametersException(".rsslist", 0);
Client.LocalUser.SendMessage(_sSource, "Currently subscribed RSS feeds:");
if (Concilium.Data.Info[_sSource].SubscribedFeeds == null || Concilium.Data.Info[_sSource].SubscribedFeeds.Count == 0)
Client.LocalUser.SendMessage(_sSource, "(none)");
else foreach (var rssFeed in Concilium.Data.Info[_sSource].SubscribedFeeds)
{
Client.LocalUser.SendMessage(_sSource, rssFeed.Url);
}
}
private Timer m_Timer;
}
}

View File

@@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot.Features
{
[BotFeature(".resist")]
public class TheResistanceFeature : BotFeatureBase
{
public static string ResistanceChannel = "#sppro.mafia";
public override void Load()
{
var channel = Client.GetChannel(ResistanceChannel);
if (channel == null)
{
Client.Channels.Join(ResistanceChannel);
channel = Client.GetChannel(ResistanceChannel);
}
Concilium.Data.Resistance.GameOver += () =>
{
Concilium.Data.Resistance = null;
};
Concilium.Data.Resistance.OnLobbyNotice += (notice) =>
{
Client.LocalUser.SendMessage(ResistanceChannel, notice);
};
Concilium.Data.Resistance.OnUsersNotice += (users, notice) =>
{
foreach (var user in users)
Client.LocalUser.SendMessage(user, notice);
};
Concilium.Data.Resistance.CheckState();
}
public override void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip)
{
if (_parameters.Count == 0)
{
Client.GetChannel(ResistanceChannel).Invite(_sSource);
return;
}
var action = _parameters[0];
if (action == "admin" && (_sSource == "Concilium" || _sSource == "xTr1m"))
{
action = _parameters[0];
_sSource = _parameters[1];
_parameters = _parameters.Skip(2).ToList();
}
if (_sTarget != null && _sTarget != ResistanceChannel)
{
Client.LocalUser.SendMessage(_sSource, "The Resistance is played at the " + ResistanceChannel + "channel.");
Client.GetChannel(ResistanceChannel).Invite(_sSource);
return;
}
if (action == "help")
{
if (_parameters.Count == 1)
Client.LocalUser.SendMessage(_sSource, "Resistance commands: help, create, reset, join, agree, disagree, team, execute, sabotage, status, missions. Use \".resist help %command%\" to learn more.");
else
{
var helpAction = _parameters[1];
if (helpAction == "help")
{
Client.LocalUser.SendMessage(_sSource, string.Format("Are you dumb, {0}?", _sSource));
}
else if (helpAction == "create")
{
Client.LocalUser.SendMessage(_sSource, "Creates a new The Resistance game. Expects no _parameters.");
}
else if (helpAction == "reset")
{
Client.LocalUser.SendMessage(_sSource, "The creator of a The Resistance game can shut it down.");
}
else if (helpAction == "start")
{
Client.LocalUser.SendMessage(_sSource, "Starts a The Resistance game when at least 5 players have joined. Expects no parameter.");
}
else if (helpAction == "join")
{
Client.LocalUser.SendMessage(_sSource, "Join the current The Resistance game. Expects no _parameters.");
}
else if (helpAction == "status")
{
Client.LocalUser.SendMessage(_sSource, "Announces the current game status.");
}
else if (helpAction == "agree")
{
Client.LocalUser.SendMessage(_sSource, "Only via query. The player hereby agrees with the team proposed by the leader.");
}
else if (helpAction == "team")
{
Client.LocalUser.SendMessage(_sSource, "Only via query. The leader hereby declares his or her team members.");
}
else if (helpAction == "disagree")
{
Client.LocalUser.SendMessage(_sSource, "Only via query. The player hereby disagrees with the team proposed by the leader.");
}
else if (helpAction == "execute")
{
Client.LocalUser.SendMessage(_sSource, "Only via query. The player hereby helps to execute the mission.");
}
else if (helpAction == "sabotage")
{
Client.LocalUser.SendMessage(_sSource, "Only via query. The player hereby sabotages the mission. Can only be done by spies.");
}
else if (helpAction == "missions")
{
Client.LocalUser.SendMessage(_sSource, "Posts the mission Concilium.Data defined in the rules for this game (amount of team members and sabotages per mission).");
}
else
{
Client.LocalUser.SendMessage(_sSource, "There's no such command available in The Resistance.");
}
}
}
else if (action == "reset")
{
if (Concilium.Data.Resistance != null && _sTarget == null && (_sSource == Concilium.Data.Resistance.GameCreator || _sSource == "xTr1m"))
{
Client.LocalUser.SendMessage(ResistanceChannel, "This The Resistance game was shut down. (reset command used by " + Concilium.Data.Resistance.GameCreator + ")");
Concilium.Data.Resistance = null;
}
}
else if (action == "create")
{
if (Concilium.Data.Resistance != null)
return;
Concilium.Data.Resistance = new Resistance();
Load();
Concilium.Data.Resistance.Init(_sSource);
foreach (var channel in Client.Channels)
{
if (channel.Name != ResistanceChannel)
Client.LocalUser.SendMessage(channel, "A The Resistance game was just created! If you'd like to play, join " + ResistanceChannel + " now!");
}
}
else if (action == "start")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
Concilium.Data.Resistance.StartGame(_sSource);
}
else if (action == "join")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
Concilium.Data.Resistance.JoinPlayer(_sSource);
}
else if (action == "status")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
Client.LocalUser.SendMessage(Concilium.GetDefaultReplyTarget(_sSource, _sTarget), Concilium.Data.Resistance.GetStatus());
}
else if (action == "agree")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
if (_sTarget == "Concilium")
Concilium.Data.Resistance.AgreeTeam(_sSource);
else
Client.LocalUser.SendMessage(ResistanceChannel, "Agree only via PM. " + _sSource + "'s command was " + (char)2 + "not" + (char)2 + "executed");
}
else if (action == "team")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
if (_sTarget == "Concilium")
Concilium.Data.Resistance.SetTeamMembers(_sSource, _parameters.Skip(1));
else
Client.LocalUser.SendMessage(ResistanceChannel, "Set up a team only via PM. " + _sSource + "'s command was " + (char)2 + "not" + (char)2 + "executed");
}
else if (action == "disagree")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
if (_sTarget == "Concilium")
Concilium.Data.Resistance.DisagreeTeam(_sSource);
else
Client.LocalUser.SendMessage(ResistanceChannel, "Disagree only via PM. " + _sSource + "'s command was " + (char)2 + "not" + (char)2 + "executed");
}
else if (action == "execute")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
if (_sTarget == "Concilium")
Concilium.Data.Resistance.ExecuteMission(_sSource);
else
Client.LocalUser.SendMessage(ResistanceChannel, "Execute mission only via PM. " + _sSource + "'s command was " + (char)2 + "not" + (char)2 + "executed");
}
else if (action == "sabotage")
{
if (Concilium.Data.Resistance == null)
{
Client.LocalUser.SendMessage(_sSource, "There's currently no running The Resistance game.");
return;
}
if (_sTarget == "Concilium")
Concilium.Data.Resistance.SabotageMission(_sSource);
else
Client.LocalUser.SendMessage(ResistanceChannel, "Sabotage mission only via PM. " + _sSource + "'s command was " + (char)2 + "not" + (char)2 + "executed");
}
}
}
}

View File

@@ -0,0 +1,182 @@
using Concilium.Helpers;
using IrcDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Concilium.Bot.Features
{
[BotFeature(".lastjoin")]
[BotFeature(".lastpart")]
[BotFeature(".lastwrite")]
public class UserMonitorFeature : BotFeatureBase
{
public override void Load()
{
m_Timer = new Timer(UserMonitorTimerStart, this, 0, 1000);
}
private void UserMonitorTimerStart(object state)
{
foreach (var channelUser in Concilium.Data.ChannelUsers)
{
var channelName = channelUser.Key;
var users = channelUser.Value.ToList();
IrcChannel channel;
if ((channel = Client.Channels.FirstOrDefault(c => c.Name == channelName)) != null)
{
foreach (var nickName in users)
{
if (!channel.Users.Any(u => u.User.NickName == nickName))
{
if (!Concilium.Data.Info.ContainsKey(nickName))
Concilium.Data.Info[nickName] = new UserInfo();
Concilium.Data.Info[nickName].LastLeft = DateTime.Now;
Log.Get().Info(string.Format("{0} just left {1}", nickName, channelName));
channelUser.Value.Remove(nickName);
}
}
foreach (var userNick in channel.Users)
{
if (!users.Any(u => u == userNick.User.NickName))
{
if (!Concilium.Data.Info.ContainsKey(userNick.User.NickName))
Concilium.Data.Info[userNick.User.NickName] = new UserInfo();
Concilium.Data.Info[userNick.User.NickName].LastJoined = DateTime.Now;
Log.Get().Info(string.Format("{0} just joined {1}", userNick, channelName));
channelUser.Value.Add(userNick.User.NickName);
userNick.User.PropertyChanged += (s, e) =>
{
if (e.PropertyName == "HostName")
{
OnJoin(userNick);
}
};
userNick.User.WhoIs();
}
}
}
}
}
private void OnJoin(IrcChannelUser userNick)
{
var whitelist = new string[]
{
"mirlix",
"xTr1m",
"BlueCobold",
"Daeva",
"dcu",
"eXpl0it3r",
"Lightkey",
"MasterQ32",
"Melan",
"Paprikachu",
"Socke",
"Sofa",
"Schorsch",
"Nimelrian",
"ompf",
"Yatekii",
"Marrrk",
"Marrrrk"
};
foreach (var nick in whitelist)
{
if (userNick.User.NickName.StartsWith(nick, StringComparison.InvariantCultureIgnoreCase) || userNick.User.NickName.EndsWith(nick, StringComparison.InvariantCultureIgnoreCase))
{
if (!userNick.Modes.Contains('v'))
{
userNick.Voice();
}
return;
}
}
// Auto-kick:
//////////////////////////////////////////////////////////////////////
bool bBlackList = false;
// Kitty
bBlackList |= (userNick.User.NickName.ToLowerInvariant().Contains("kitty") || userNick.User.HostName.Contains("dyn.telefonica.de"));
if (bBlackList)
{
userNick.Kick("black listed");
}
}
public override void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip)
{
if (_parameters.Count != 1)
throw new InvalidCommandParametersException(_sCommand, 1);
string sParameter = _parameters.First();
string sReply = null;
if (!Concilium.Data.Info.ContainsKey(sParameter))
{
sReply = string.Format("I have no information about {0} :(", sParameter);
}
else
{
if (_sCommand == ".lastjoin")
sReply = ProcessChatLastJoin(_sSource, _sMessage, sParameter);
else if (_sCommand == ".lastpart")
sReply = ProcessChatLastPart(_sSource, _sMessage, sParameter);
else if (_sCommand == ".lastwrite")
sReply = ProcessChatLastWrite(_sSource, _sMessage, sParameter);
else
throw new NotImplementedException();
}
Client.LocalUser.SendMessage(Concilium.GetDefaultReplyTarget(_sSource, _sTarget), sReply);
}
private string ProcessChatLastJoin(string _sSource, string _sMessage, string _sParameter)
{
if (!Concilium.Data.Info[_sParameter].LastJoined.HasValue)
{
return string.Format("{0} joined before I did", _sParameter);
}
else
{
return string.Format("{0}'s last join date was: {1}", _sParameter, Concilium.Data.Info[_sParameter].LastJoined.Value);
}
}
private string ProcessChatLastPart(string _sSource, string _sMessage, string _sParameter)
{
if (!Concilium.Data.Info[_sParameter].LastLeft.HasValue)
{
return string.Format("I didn't witness {0} leaving", _sParameter);
}
else
{
return string.Format("{0} left: {1}", _sParameter, Concilium.Data.Info[_sParameter].LastLeft.Value);
}
}
private string ProcessChatLastWrite(string _sSource, string _sMessage, string _sParameter)
{
if (!Concilium.Data.Info[_sParameter].LastWritten.HasValue)
{
return string.Format("I've not seen any text from {0}", _sParameter);
}
else
{
return string.Format("{0}'s last message was on: {1}", _sParameter, Concilium.Data.Info[_sParameter].LastWritten.Value);
}
}
private Timer m_Timer;
}
}

View File

@@ -0,0 +1,15 @@
using IrcDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot
{
public interface IBotFeature
{
void Load();
void Execute(string _sCommand, string _sSource, string _sTarget, string _sMessage, IList<string> _parameters, string _ip);
IrcClient Client { get; set; }
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using log4net;
using Concilium.Helpers;
using System.Net;
using System.Diagnostics;
namespace Concilium.Bot
{
public class InvalidCommandParametersException : Exception
{
public InvalidCommandParametersException(string _sCommand, int minParameters, int? maxParameters = null)
: base()
{
m_sCommand = _sCommand;
Debug.Assert(minParameters >= 0,
"minParameters must be at least zero.");
Debug.Assert(maxParameters == null || maxParameters >= minParameters,
"maxParameters must be at least minParameters.");
this.MinParameters = minParameters;
this.MaxParameters = maxParameters ?? minParameters;
}
public int MinParameters
{
get;
private set;
}
public int MaxParameters
{
get;
private set;
}
public override string Message
{
get
{
if (this.MinParameters == 0 && this.MaxParameters == 0)
return string.Format("Command {0} takes no parameters", m_sCommand);
else if (this.MinParameters == this.MaxParameters)
return string.Format("Command {0} takes {1} parameters", m_sCommand, this.MinParameters);
else
return string.Format("Command {0} takes {1} to {2} parameters", m_sCommand, this.MinParameters, this.MaxParameters);
}
}
private string m_sCommand;
}
}

393
Concilium/Bot/IrcBot.cs Normal file
View File

@@ -0,0 +1,393 @@
using Concilium.Helpers;
using IrcDotNet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace Concilium.Bot
{
// Provides core functionality for an IRC bot that operates via multiple clients.
public abstract class IrcBot : IDisposable
{
private const int clientQuitTimeout = 1000;
// Dictionary of all chat command processors, keyed by name.
private IDictionary<string, ChatCommandProcessor> chatCommandProcessors;
// Dictionary of all command processors, keyed by name.
private IDictionary<string, CommandProcessor> commandProcessors;
// True if the read loop is currently active, false if ready to terminate.
private bool isRunning;
private bool isDisposed = false;
public IrcBot()
{
this.isRunning = false;
this.commandProcessors = new Dictionary<string, CommandProcessor>(StringComparer.InvariantCultureIgnoreCase);
InitializeCommandProcessors();
this.chatCommandProcessors = new Dictionary<string, ChatCommandProcessor>(StringComparer.InvariantCultureIgnoreCase);
InitializeChatCommandProcessors();
}
~IrcBot()
{
Dispose(false);
}
public virtual string QuitMessage
{
get { return "Big brother isn't watching"; }
}
protected IDictionary<string, ChatCommandProcessor> ChatCommandProcessors
{
get { return this.chatCommandProcessors; }
}
public StandardIrcClient Client
{
get;
private set;
}
protected IDictionary<string, CommandProcessor> CommandProcessors
{
get { return this.commandProcessors; }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!this.isDisposed)
{
if (disposing)
{
// Disconnect each client gracefully.
if (Client != null)
{
Client.Quit(clientQuitTimeout, this.QuitMessage);
Client.Dispose();
}
}
}
this.isDisposed = true;
}
public void Run()
{
// Read commands from stdin until bot terminates.
this.isRunning = true;
while (this.isRunning)
{
Console.Write("> ");
var line = Console.ReadLine();
if (line == null)
break;
if (line.Length == 0)
continue;
var parts = line.Split(' ');
var command = parts[0].ToLower();
var parameters = parts.Skip(1).ToArray();
ReadCommand(command, parameters);
}
}
public void Stop()
{
this.isRunning = false;
}
protected abstract void InitializeCommandProcessors();
private void ReadCommand(string command, IList<string> parameters)
{
CommandProcessor processor;
if (this.commandProcessors.TryGetValue(command, out processor))
{
try
{
processor(command, parameters);
}
catch (Exception ex)
{
Log.Get().Error(string.Format("Error: {0}", ex.Message));
}
}
else
{
Log.Get().Error(string.Format("Command '{0}' not recognized.", command));
}
}
protected void Connect()
{
var registrationInfo = new IrcUserRegistrationInfo()
{
UserName = "Concilium",
NickName = "Concilium",
Password = "K3kst0ru5",
RealName = "#sppro knowledge bot"
};
// Create new IRC client and connect to given server.
Client = new StandardIrcClient();
Client.FloodPreventer = new IrcStandardFloodPreventer(4, 2000);
Client.Connected += IrcClient_Connected;
Client.Disconnected += IrcClient_Disconnected;
Client.Registered += IrcClient_Registered;
Client.RawMessageReceived += IrcClient_RawMessageReceived;
// Wait until connection has succeeded or timed out.
using (var connectedEvent = new ManualResetEventSlim(false))
{
Client.Connected += (sender2, e2) => connectedEvent.Set();
Client.Connect("irc.euirc.net", 6665, false, registrationInfo);
if (!connectedEvent.Wait(10000))
{
Client.Dispose();
Log.Get().Error("Connection timed out");
return;
}
}
Log.Get().Info("Now connected");
}
public void Disconnect()
{
// Disconnect IRC client that is connected to given server.
Client.Quit(clientQuitTimeout, this.QuitMessage);
Client.Dispose();
// Remove client from connection.
Client = null;
Log.Get().Info("Disconnected");
}
protected abstract void InitializeChatCommandProcessors();
private bool ReadChatCommand(IrcClient client, IrcMessageEventArgs eventArgs)
{
// Check if given message represents chat command.
var line = eventArgs.Text;
if (line.Length > 1 && line.StartsWith(".") || line.StartsWith(">"))
{
// Process command.
var parts = line.Split(' ');
var command = parts.First();
var parameters = parts.Skip(1).ToArray();
ReadChatCommand(client, eventArgs.Source, eventArgs.Targets, command, parameters);
return true;
}
return false;
}
private void ReadChatCommand(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string command, string[] parameters)
{
var defaultReplyTargets = GetDefaultReplyTargets(client, source, targets);
ChatCommandProcessor processor;
if (this.chatCommandProcessors.TryGetValue(command, out processor))
{
try
{
processor(client, source, targets, command, parameters);
}
catch (InvalidCommandParametersException exInvalidCommandParameters)
{
client.LocalUser.SendNotice(defaultReplyTargets, exInvalidCommandParameters.Message);
}
catch (Exception ex)
{
if (source is IIrcMessageTarget)
{
client.LocalUser.SendNotice(defaultReplyTargets, string.Format("Error processing '{0}' command: {1}", command, ex.Message));
}
}
}
else
{
if (source is IIrcMessageTarget)
{
client.LocalUser.SendNotice(defaultReplyTargets, string.Format("Command '{0}' not recognized.", command));
}
}
}
protected virtual void OnRawMessageReceived(IrcClient client, IrcRawMessageEventArgs e) { }
protected virtual void OnClientConnect(IrcClient client) { }
protected virtual void OnClientDisconnect(IrcClient client) { }
protected virtual void OnClientRegistered(IrcClient client) { }
protected virtual void OnLocalUserJoinedChannel(IrcLocalUser localUser, IrcChannelEventArgs e) { }
protected virtual void OnLocalUserLeftChannel(IrcLocalUser localUser, IrcChannelEventArgs e) { }
protected virtual void OnLocalUserNoticeReceived(IrcLocalUser localUser, IrcMessageEventArgs e) { }
protected virtual void OnLocalUserMessageReceived(IrcLocalUser localUser, IrcMessageEventArgs e) { }
protected virtual void OnChannelUserJoined(IrcChannel channel, IrcChannelUserEventArgs e) { }
protected virtual void OnChannelUserLeft(IrcChannel channel, IrcChannelUserEventArgs e) { }
protected virtual void OnChannelNoticeReceived(IrcChannel channel, IrcMessageEventArgs e) { }
protected virtual void OnChannelMessageReceived(IrcChannel channel, IrcMessageEventArgs e) { }
#region IRC Client Event Handlers
private void IrcClient_Connected(object sender, EventArgs e)
{
var client = (IrcClient)sender;
OnClientConnect(client);
}
private void IrcClient_Disconnected(object sender, EventArgs e)
{
var client = (IrcClient)sender;
OnClientDisconnect(client);
}
private void IrcClient_Registered(object sender, EventArgs e)
{
var client = (IrcClient)sender;
client.LocalUser.NoticeReceived += IrcClient_LocalUser_NoticeReceived;
client.LocalUser.MessageReceived += IrcClient_LocalUser_MessageReceived;
client.LocalUser.JoinedChannel += IrcClient_LocalUser_JoinedChannel;
client.LocalUser.LeftChannel += IrcClient_LocalUser_LeftChannel;
OnClientRegistered(client);
}
private void IrcClient_LocalUser_NoticeReceived(object sender, IrcMessageEventArgs e)
{
var localUser = (IrcLocalUser)sender;
OnLocalUserNoticeReceived(localUser, e);
}
private void IrcClient_LocalUser_MessageReceived(object sender, IrcMessageEventArgs e)
{
var localUser = (IrcLocalUser)sender;
if (e.Source is IrcUser)
{
// Read message and process if it is chat command.
if (ReadChatCommand(localUser.Client, e))
return;
}
OnLocalUserMessageReceived(localUser, e);
}
private void IrcClient_LocalUser_JoinedChannel(object sender, IrcChannelEventArgs e)
{
var localUser = (IrcLocalUser)sender;
e.Channel.UserJoined += IrcClient_Channel_UserJoined;
e.Channel.UserLeft += IrcClient_Channel_UserLeft;
e.Channel.MessageReceived += IrcClient_Channel_MessageReceived;
e.Channel.NoticeReceived += IrcClient_Channel_NoticeReceived;
OnLocalUserJoinedChannel(localUser, e);
}
private void IrcClient_LocalUser_LeftChannel(object sender, IrcChannelEventArgs e)
{
var localUser = (IrcLocalUser)sender;
e.Channel.UserJoined -= IrcClient_Channel_UserJoined;
e.Channel.UserLeft -= IrcClient_Channel_UserLeft;
e.Channel.MessageReceived -= IrcClient_Channel_MessageReceived;
e.Channel.NoticeReceived -= IrcClient_Channel_NoticeReceived;
OnLocalUserJoinedChannel(localUser, e);
}
private void IrcClient_RawMessageReceived(object sender, IrcRawMessageEventArgs e)
{
var client = (IrcClient)sender;
OnRawMessageReceived(client, e);
}
private void IrcClient_Channel_UserLeft(object sender, IrcChannelUserEventArgs e)
{
var channel = (IrcChannel)sender;
OnChannelUserJoined(channel, e);
}
private void IrcClient_Channel_UserJoined(object sender, IrcChannelUserEventArgs e)
{
var channel = (IrcChannel)sender;
OnChannelUserLeft(channel, e);
}
private void IrcClient_Channel_NoticeReceived(object sender, IrcMessageEventArgs e)
{
var channel = (IrcChannel)sender;
OnChannelNoticeReceived(channel, e);
}
private void IrcClient_Channel_MessageReceived(object sender, IrcMessageEventArgs e)
{
var channel = (IrcChannel)sender;
if (e.Source is IrcUser)
{
// Read message and process if it is chat command.
if (ReadChatCommand(channel.Client, e))
return;
}
OnChannelMessageReceived(channel, e);
}
#endregion
public static IList<IIrcMessageTarget> GetDefaultReplyTargets(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets)
{
if (targets.Contains(client.LocalUser) && source is IIrcMessageTarget)
return new[] { (IIrcMessageTarget)source };
else
return targets;
}
public static string GetDefaultReplyTarget(string _sSource, string _sTarget)
{
if (_sTarget == "Concilium")
return _sSource;
else
return _sTarget;
}
protected delegate void ChatCommandProcessor(IrcClient client, IIrcMessageSource source, IList<IIrcMessageTarget> targets, string command, IList<string> parameters);
protected delegate void CommandProcessor(string command, IList<string> parameters);
}
}

View File

@@ -0,0 +1,26 @@
using IrcDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot
{
public static class IrcClientExtensions
{
public static IrcChannel GetChannel(this IrcClient _client, string _sChannelName)
{
return _client.Channels.FirstOrDefault(c => c.Name == _sChannelName);
}
public static IrcUser GetUser(this IrcClient _client, string _sNickName)
{
return _client.Users.FirstOrDefault(u => u.NickName == _sNickName);
}
public static IrcChannelUser GetUser(this IrcChannel _channel, string _sNickName)
{
return _channel.Users.FirstOrDefault(u => u.User.NickName == _sNickName);
}
}
}

631
Concilium/Bot/Mafia.cs Normal file
View File

@@ -0,0 +1,631 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Concilium.Helpers;
namespace Concilium.Bot
{
public class Mafia
{
private static class Strings
{
public static string Welcome = "A new mafia game was started by {0}!";
public static string TooFewPlayers = "There are still too few players. At least 5 players are required.";
public static string PlayingMafioso = "Both you and {0} are playing as a mafiosos. Your job is to take over the city by ensuring that in the end the mafiosos outnumber the civilists. Kill one civilist every night.";
public static string PlayingCivilist = "You're playing as a civilist. Your job is to try to discover the mafiosos infiltrating your city. Do a public voting each day on who should be killed. Kill all mafiosos and you win.";
public static string PlayingDoctor = "You're playing as a doctor. Your job is to guard a civilist each night to prevent his/her death, should he become a target from the mafiosos infiltrating your city. You also count as a civilist, do a public voting each day on who should be killed. Kill all mafiosos and you win.";
public static string PlayingDetective = "You're playing as a detective. Your job is to try each night to discover one of the mafiosos infiltrating your city. You also count as a civilist, do a public voting each day on who should be killed. Kill all mafiosos and you win.";
public static string PlayingVigilante = "You're playing as a vigilante. Your job is to try to discover the mafiosos infiltrating your city. Do a public voting each day on who should be killed. At night you can kill any player at will. Kill all mafiosos and you win.";
public static string HelpNightMafioso = "You're a mafioso and it's night. Discuss with the mafia and agree on one target. Vote for that target by querying " + (char)2 + ".mafia vote %nickName%" + (char)2 + " now.";
public static string HelpNightCivilist = "You're civilist and it's night. Go to bed and relax.";
public static string HelpNightDoctor = "You're a doctor and it's night. Your job is to guard and save a civilist now, do that by querying " + (char)2 + ".mafia save %nickName%" + (char)2 + " now.";
public static string HelpNightDetective = "You're a detective and it's night. Your job is to detect whether a player is a mafioso. Do that by querying " + (char)2 + ".mafia detect %nickName%" + (char)2 + " now.";
public static string HelpNightVigilante = "You're a vigilante and it's night. Your job is to kill all mafia players. Kill your suspect by querying " + (char)2 + ".mafia kill %nickName%" + (char)2 + " now.";
public static string UserJoined = "{0} joined the mafia game!";
public static string UserCantJoin = "I'm sorry, {0}. You can't join the current mafia game, since it already started.";
public static string UserDuplicate = "You already joined the mafia game!";
public static string GameBegins = "All necessary players joined! Let the game begin!";
public static string DayBegins = "The sun comes out, it's day. Everybody wakes up.";
public static string[] Deaths = new string[]
{
"This morning, the police found the body of {0} floating in the river. The chest had wounds from bullet impacts.",
"After the neighbours alarmed the police, {0} was found dead at home. Experts believe that he was poisoned while consuming breakfast.",
"The neighbourhood was scared this morning, when the car of {0} suddenly exploded. When the firemen arrived, the corpse was already carbonized.",
"A citizen called the police this morning, indicating that he found the head of {0}, cut off, in his trash bin. There is no trace from the rest of the body."
};
public static string VoteStatus = "The citizens currently vote for killing: ";
public static string DeathRole = "Upon closer inspection of the crime scene, the police could discover from the surrounding belongings that the victim was a " + (char)2 + "{0}" + (char)2 + ".";
public static string LynchRole = "Upon closer inspection of the dead body, the citizens discover from the belongings that the victim was a " + (char)2 + "{0}" + (char)2 + ".";
public static string NoDeathsOvernight = "This night was calm, that's a relief for the city. No player died.";
public static string DayPreparation = "Each player must now vote who they think is a mafioso and should be killed. Just write via query " + (char)2 + ".mafia vote %nickName%" + (char)2 + " to vote.";
public static string CantVote = "You can't vote at this time.";
public static string CantUnvote = "You can't unvote at this time.";
public static string InvalidVote = "I don't know who you mean. Please choose one from: ";
public static string SelfVote = "You can't vote for youself!";
public static string FirstVote = "{0} thinks that {1} should be killed.";
public static string Unvote = "{0} is not so sure anymore and removes his/her vote.";
public static string NextVote = "{0} changed his/her mind and proposes {1} to be killed.";
public static string DayEnds = "As the day reaches its end and the sun sets, all players assemble in the execution place.";
public static string LynchPlayer = "Agreed upon by public voting, {0} is pushed forward, and a rope is tied around the neck. Finally what remains is the lifeless look of {0}'s half-opened eyes.";
public static string NightBegins = "The stars begin to shine, the city is quiet. It is now nighttime. All mafiosos will now meet in secret and agree on their victim.";
public static string DetectiveIntro = "The detective might now be able to catch a mafioso on its way to their meeting place. Just write in a query " + (char)2 + ".mafia detect %nickName%" + (char)2 + " to check.";
public static string DetectiveNotNight = "It's not night yet. You can only operate when the streets are dark.";
public static string DetectiveAlreadyDone = "You've already done your job this night! You found out that {0} is{1} a mafioso.";
public static string DetectiveFinished = "During your inspection this night you conclude that {0} is{1} a mafioso.";
public static string DoctorIntro = "The doctor can now decide which player should be cured, for the case that this player will be a target of a mafioso attack. Just write in a query " + (char)2 + ".mafia save %nickName%" + (char)2 + " to save that player from a possible death.";
public static string DoctorNotNight = "It's not night yet. The mafia only operates at night, and then it's where you should choose whom to guard.";
public static string DoctorFinished = "OK, {0} will definitely survive this night.";
public static string VigilanteNotNight = "It's not night yet. You should only do your killing at night.";
public static string VigilanteFinished = "OK, you're about to kill {0} tonight.";
public static string MafiosoIntro = "Each mafioso must now vote who they want to kill. All votes must target the same player. Just write in a query " + (char)2 + ".mafia vote %nickName%" + (char)2 + " to vote.";
public static string NightEnds = "The stars start to fade. Slowly the sky turns into shades of blue.";
public static string CivilistsWin = "All mafiosos were killed! Civilists win this game!";
public static string MafiososWin = "The mafiosos managed to take over the city! They outnumber the citizens and therefore win this game!";
public static string HallOfFame = "Following players participated: ";
}
public enum State
{
Lobby,
Day,
Night
};
public enum Role
{
Unassigned,
Civilist,
Mafioso,
Detective,
Doctor,
Avenger,
Vigilante
};
public delegate void DummyHandler();
public event DummyHandler GameOver;
private void SendGameOver()
{
if (GameOver != null)
GameOver();
}
public event DummyHandler DayBegins;
private void SendDayBegins()
{
if (DayBegins != null)
DayBegins();
}
public event DummyHandler NightBegins;
private void SendNightBegins()
{
if (NightBegins != null)
NightBegins();
}
public delegate void LobbyNoticeHandler(string notice);
public event LobbyNoticeHandler OnLobbyNotice;
private void SendLobbyNotice(string notice)
{
if (OnLobbyNotice != null)
OnLobbyNotice(notice);
}
public delegate void UsersNoticeHandler(IEnumerable<string> users, string notice);
public event UsersNoticeHandler OnUsersNotice;
private void SendUsersNotice(IEnumerable<string> users, string notice)
{
if (OnUsersNotice != null && users.Count() > 0)
OnUsersNotice(users, notice);
}
public delegate void UserEventHandler(string nickName);
public event UserEventHandler OnUserJoined;
private void SendUserJoined(string nickName)
{
if (OnUserJoined != null)
OnUserJoined(nickName);
}
public event UserEventHandler OnUserDied;
private void SendUserDied(string nickName)
{
if (OnUserDied != null)
OnUserDied(nickName);
}
public Mafia()
{
this.CurrentState = State.Lobby;
this.FirstDay = true;
this.NumPlayers = -1;
}
public void Init(string nickName)
{
this.GameCreator = nickName;
SendLobbyNotice(string.Format(Strings.Welcome, this.GameCreator));
}
public bool StartGame(string nickName)
{
if (CurrentState != State.Lobby)
{
return false;
}
if (GameCreator != nickName)
{
return false;
}
if (allPlayers.Count < 5)
{
SendLobbyNotice(string.Format(Strings.TooFewPlayers, nickName));
return false;
}
this.NumPlayers = allPlayers.Count;
CheckState();
return true;
}
public bool JoinPlayer(string nickName)
{
if (CurrentState != State.Lobby)
{
SendUsersNotice(new List<string> { nickName }, string.Format(Strings.UserCantJoin, nickName));
return false;
}
if (allPlayers.ContainsKey(nickName))
{
SendUsersNotice(new List<string> { nickName }, Strings.UserDuplicate);
return false;
}
allPlayers[nickName] = Role.Unassigned;
SendLobbyNotice(string.Format(Strings.UserJoined, nickName));
OnUserJoined(nickName);
CheckState();
return true;
}
public void CheckState()
{
if (string.IsNullOrEmpty(GameCreator))
return;
switch (CurrentState)
{
case State.Lobby:
if (allPlayers.Count == NumPlayers)
{
SendLobbyNotice(Strings.GameBegins);
var specialRoles = new List<Role>() { Role.Doctor, Role.Detective, Role.Vigilante };
specialRoles.Shuffle();
// Assign roles
var players = new List<string>(allPlayers.Keys);
players.Shuffle();
allPlayers[players[0]] = Role.Mafioso;
SendUsersNotice(new List<string>() { players[0] }, string.Format(Strings.PlayingMafioso, players[1]));
allPlayers[players[1]] = Role.Mafioso;
SendUsersNotice(new List<string>() { players[1] }, string.Format(Strings.PlayingMafioso, players[0]));
allPlayers[players[2]] = specialRoles.First(); specialRoles.RemoveAt(0);
allPlayers[players[3]] = specialRoles.First(); specialRoles.RemoveAt(0);
foreach (var remainingPlayer in allPlayers.Where(p => p.Value == Role.Unassigned).ToList())
allPlayers[remainingPlayer.Key] = Role.Civilist;
SendUsersNotice(allPlayers.Where(p => p.Value == Role.Civilist).Select(p => p.Key), Strings.PlayingCivilist);
foreach (var playerEntry in allPlayers)
{
if (playerEntry.Value == Role.Detective)
SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.PlayingDetective);
else if (playerEntry.Value == Role.Doctor)
SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.PlayingDoctor);
else if (playerEntry.Value == Role.Vigilante)
SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.PlayingVigilante);
}
GenerateHallOfFame();
BeginDay();
}
break;
case State.Day:
if (PlayerVotes.Count == allPlayers.Count)
{
SendLobbyNotice(Strings.DayEnds);
var dict = new Dictionary<string, int>();
foreach (var vote in PlayerVotes)
{
if (!dict.ContainsKey(vote.Value))
dict[vote.Value] = 0;
dict[vote.Value] = dict[vote.Value] + 1;
}
var maxVotes = dict.Max(d => d.Value);
var candidates = dict.Where(d => d.Value == maxVotes).Select(d => d.Key).ToArray();
var rand = new Random();
var candidate = candidates[rand.Next(candidates.Length)];
var role = allPlayers[candidate];
allPlayers.Remove(candidate);
OnUserDied(candidate);
SendLobbyNotice(string.Format(Strings.LynchPlayer, candidate));
SendLobbyNotice(string.Format(Strings.LynchRole, role.ToString()));
BeginNight();
}
break;
case State.Night:
if (PlayerVotes.Count == GetMafiosos().Count() && PlayerVotes.All(v => v.Value == PlayerVotes.First().Value) && (GetDoctor() == null || DoctorSave != null) && (GetVigilante() == null || VigilanteKill != null) && (GetDetective() == null || DetectiveTarget != null))
{
MafiosoKill = PlayerVotes.First().Value;
SendLobbyNotice(Strings.NightEnds);
BeginDay();
}
break;
default:
throw new NotImplementedException("BUG! Implementation incomplete, an internal state was not handled");
}
}
private void GenerateHallOfFame()
{
hallOfFame = Strings.HallOfFame + string.Join(", ", allPlayers.Select(p => p.Key + " (" + p.Value.ToString() + ")"));
}
private void BeginDay()
{
if (CurrentState == State.Day)
throw new InvalidOperationException("BUG! Day can't begin when it's already day!");
CurrentState = State.Day;
SendLobbyNotice(Strings.DayBegins);
PlayerVotes.Clear();
if (!FirstDay)
{
var deaths = Strings.Deaths.ToList();
deaths.Shuffle();
var deathCount = 0;
if (MafiosoKill != null && MafiosoKill != DoctorSave)
{
var role = allPlayers[MafiosoKill];
allPlayers.Remove(MafiosoKill);
OnUserDied(MafiosoKill);
var death = deaths[deathCount++];
SendLobbyNotice(string.Format(death, MafiosoKill));
SendLobbyNotice(string.Format(Strings.DeathRole, role.ToString()));
}
if (VigilanteKill != null && VigilanteKill != DoctorSave)
{
var role = allPlayers[VigilanteKill];
allPlayers.Remove(VigilanteKill);
OnUserDied(VigilanteKill);
var death = deaths[deathCount++];
SendLobbyNotice(string.Format(death, VigilanteKill));
SendLobbyNotice(string.Format(Strings.DeathRole, role.ToString()));
}
if (deathCount == 0)
{
SendLobbyNotice(Strings.NoDeathsOvernight);
}
}
else
{
FirstDay = false;
}
if (GetMafiosos().Count() == 0)
{
SendLobbyNotice(Strings.CivilistsWin);
SendLobbyNotice(hallOfFame);
SendGameOver();
}
else if (GetMafiosos().Count() > GetAllPlayers().Count() - GetMafiosos().Count())
{
SendLobbyNotice(Strings.MafiososWin);
SendLobbyNotice(hallOfFame);
SendGameOver();
}
else
{
SendLobbyNotice(Strings.DayPreparation);
SendDayBegins();
}
}
private void BeginNight()
{
if (CurrentState == State.Night)
throw new InvalidOperationException("BUG! Night can't begin when it's already night!");
foreach (var playerEntry in allPlayers)
{
switch (playerEntry.Value)
{
case Role.Civilist: SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.HelpNightCivilist); break;
case Role.Mafioso: SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.HelpNightMafioso); break;
case Role.Doctor: SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.HelpNightDoctor); break;
case Role.Detective: SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.HelpNightDetective); break;
case Role.Vigilante: SendUsersNotice(new List<string>() { playerEntry.Key }, Strings.HelpNightVigilante); break;
default: break;
}
}
if (GetMafiosos().Count() == 0)
{
SendLobbyNotice(Strings.CivilistsWin);
SendLobbyNotice(hallOfFame);
SendGameOver();
}
else if (GetMafiosos().Count() > GetAllPlayers().Count() - GetMafiosos().Count())
{
SendLobbyNotice(Strings.MafiososWin);
SendLobbyNotice(hallOfFame);
SendGameOver();
}
else
{
SendLobbyNotice(Strings.NightBegins);
CurrentState = State.Night;
PlayerVotes.Clear();
DetectiveTarget = null;
SendNightBegins();
}
}
public bool Unvote(string nickName)
{
if (!allPlayers.ContainsKey(nickName))
{
return false;
}
if (CurrentState == State.Day)
{
if (PlayerVotes.ContainsKey(nickName))
{
SendLobbyNotice(string.Format(Strings.Unvote, nickName));
PlayerVotes.Remove(nickName);
CheckState();
return true;
}
else
{
return false;
}
}
else if (CurrentState == State.Night)
{
if (!GetMafiosos().Contains(nickName))
return false;
var recipients = GetMafiosos().Where(p => p != nickName);
if (PlayerVotes.ContainsKey(nickName))
{
SendUsersNotice(recipients, string.Format(Strings.Unvote, nickName));
PlayerVotes.Remove(nickName);
CheckState();
return true;
}
else
{
return false;
}
}
else
{
SendUsersNotice(new List<string> { nickName }, Strings.CantUnvote);
return false;
}
}
public bool PlayerVote(string nickName, string vote)
{
if (!allPlayers.ContainsKey(vote))
{
var message = Strings.InvalidVote + string.Join(", ", GetAllPlayers().Where(p => p != nickName));
SendUsersNotice(new List<string> { nickName }, message);
return false;
}
if (!allPlayers.ContainsKey(nickName))
{
return false;
}
if (nickName == vote)
{
SendUsersNotice(new List<string> { nickName }, Strings.SelfVote);
return false;
}
if (CurrentState == State.Day)
{
if (PlayerVotes.ContainsKey(nickName) && PlayerVotes[nickName] != vote)
{
SendLobbyNotice(string.Format(Strings.NextVote, nickName, vote));
}
else
{
SendLobbyNotice(string.Format(Strings.FirstVote, nickName, vote));
}
PlayerVotes[nickName] = vote;
SendLobbyNotice(GetVoteStatus());
CheckState();
return true;
}
else if (CurrentState == State.Night)
{
if (!GetMafiosos().Contains(nickName))
return false;
var recipients = GetMafiosos().Where(p => p != nickName);
if (PlayerVotes.ContainsKey(nickName) && PlayerVotes[nickName] != vote)
{
SendUsersNotice(recipients, string.Format(Strings.NextVote, nickName, vote));
}
else
{
SendUsersNotice(recipients, string.Format(Strings.FirstVote, nickName, vote));
}
PlayerVotes[nickName] = vote;
CheckState();
return true;
}
else
{
SendUsersNotice(new List<string> { nickName }, Strings.CantVote);
return false;
}
}
public string GetVoteStatus()
{
if (CurrentState == State.Day)
{
var message = Strings.VoteStatus;
message += string.Join(", ", PlayerVotes.Select(v => string.Format("{0} => {1}", v.Key, v.Value)));
if (PlayerVotes.Count == 0)
message += "(none)";
return message;
}
return "";
}
public bool DetectMafioso(string nickName, string query)
{
if (GetDetective() != nickName)
{
return false;
}
if (CurrentState != State.Night)
{
SendUsersNotice(new List<string> { nickName }, Strings.DetectiveNotNight);
return false;
}
if (!allPlayers.ContainsKey(query))
{
var message = Strings.InvalidVote + string.Join(", ", GetAllPlayers().Where(p => p != nickName));
SendUsersNotice(new List<string> { nickName }, message);
return false;
}
var not = GetMafiosos().Any(s => s == query) ? "" : " not";
if (DetectiveTarget != null)
{
SendUsersNotice(new List<string> { nickName }, string.Format(Strings.DetectiveAlreadyDone, DetectiveTarget, not));
return false;
}
DetectiveTarget = query;
SendUsersNotice(new List<string> { nickName }, string.Format(Strings.DetectiveFinished, DetectiveTarget, not));
CheckState();
return true;
}
public bool SaveCivilist(string nickName, string query)
{
if (GetDoctor() != nickName)
{
return false;
}
if (CurrentState != State.Night)
{
SendUsersNotice(new List<string> { nickName }, Strings.DoctorNotNight);
return false;
}
if (!allPlayers.ContainsKey(query))
{
var message = Strings.InvalidVote + string.Join(", ", GetAllPlayers().Where(p => p != nickName));
SendUsersNotice(new List<string> { nickName }, message);
return false;
}
DoctorSave = query;
SendUsersNotice(new List<string> { nickName }, string.Format(Strings.DoctorFinished, DoctorSave));
CheckState();
return true;
}
public bool VoteVigilanteKill(string nickName, string query)
{
if (GetVigilante() != nickName)
{
return false;
}
if (CurrentState != State.Night)
{
SendUsersNotice(new List<string> { nickName }, Strings.VigilanteNotNight);
return false;
}
if (!allPlayers.ContainsKey(query))
{
var message = Strings.InvalidVote + string.Join(", ", GetAllPlayers().Where(p => p != nickName));
SendUsersNotice(new List<string> { nickName }, message);
return false;
}
VigilanteKill = query;
SendUsersNotice(new List<string> { nickName }, string.Format(Strings.VigilanteFinished, VigilanteKill));
CheckState();
return true;
}
public string GetStatus()
{
if (CurrentState == State.Lobby)
{
var message = "A game was recently created by " + GameCreator + ". Waiting for players to join. Following players already joined: ";
var existing = string.Join(", ", GetAllPlayers());
return message + existing;
}
else if (CurrentState == State.Day)
{
var message = "It's day. Waiting for all players to place their vote. Following players haven't voted: ";
var missing = string.Join(", ", GetAllPlayers().Where(p => !PlayerVotes.Keys.Contains(p)));
return message + missing + ". " + GetVoteStatus();
}
else if (CurrentState == State.Night)
{
var message = "It's currently night. Everybody is sleeping. (yeah right)";
return message;
}
return "";
}
public string MafiosoKill;
public string DoctorSave;
public string VigilanteKill;
public string DetectiveTarget;
public string GameCreator;
public bool FirstDay;
public int NumPlayers;
public State CurrentState;
public Dictionary<string, Role> allPlayers = new Dictionary<string, Role>();
public string hallOfFame;
public IEnumerable<string> GetAllPlayers() { return allPlayers.Keys; }
public string GetDoctor() { return allPlayers.FirstOrDefault(p => p.Value == Role.Doctor).Key; }
public string GetVigilante() { return allPlayers.FirstOrDefault(p => p.Value == Role.Vigilante).Key; }
public string GetDetective() { return allPlayers.FirstOrDefault(p => p.Value == Role.Detective).Key; }
public IEnumerable<string> GetMafiosos() { return allPlayers.Where(p => p.Value == Role.Mafioso).Select(p => p.Key); }
public IEnumerable<string> GetCivilists() { return allPlayers.Where(p => p.Value == Role.Civilist).Select(p => p.Key); }
public Dictionary<string, string> PlayerVotes = new Dictionary<string, string>();
}
}

View File

@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Bot
{
public class PredicateLogic
{
private static string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
public Dictionary<string, List<string>> Predicates;
public string Parse(string input)
{
if (Predicates == null)
Predicates = new Dictionary<string, List<string>>();
var lastQuestionMark = input.Last() == '?';
var firstQuestionMark = input.First() == '?';
var rand = new Random((int)DateTime.Now.TimeOfDay.TotalMilliseconds);
if (lastQuestionMark && firstQuestionMark)
{
bool randomPredicate = rand.Next(2) == 0;
if (randomPredicate)
{
var predicates = Predicates.Where(p => p.Value.Count > 0).ToList();
var index = rand.Next(predicates.Count);
var kvp = predicates.ElementAt(index);
return DisplayPredicate(kvp.Key);
}
else // randomUser
{
var users = Predicates.SelectMany(kvp => kvp.Value).Distinct().ToList();
var index = rand.Next(users.Count);
return DisplayUser(users[index]);
}
}
else if (lastQuestionMark)
{
var predicate = input.Substring(0, input.Length - 1).Trim();
if (!predicate.All(c => allowedChars.IndexOf(c) != -1))
return "Syntax error: unexpected character in predicate";
if (!Predicates.ContainsKey(predicate) || Predicates[predicate].Count == 0)
return "No one is " + predicate;
return DisplayPredicate(predicate);
}
else if (firstQuestionMark)
{
input = input.Substring(1);
var leftBracket = input.IndexOf('(');
var rightBracket = input.IndexOf(')');
if (leftBracket == -1)
return "Syntax error: expecting '('";
if (rightBracket == -1)
return "Syntax error: expecting ')'";
if (rightBracket < leftBracket)
return "Syntax error: unexpected ')', was expecting '('";
var parameter = input.Substring(leftBracket + 1, rightBracket - leftBracket - 1).Trim();
if (!parameter.Any(c => allowedChars.IndexOf(c) != -1))
return "Syntax error: unexpected character in parameter";
return DisplayUser(parameter);
}
else
{
var leftBracket = input.IndexOf('(');
var rightBracket = input.IndexOf(')');
if (leftBracket == -1)
return "Syntax error: expecting '('";
if (rightBracket == -1)
return "Syntax error: expecting ')'";
if (rightBracket < leftBracket)
return "Syntax error: unexpected ')', was expecting '('";
var predicate = input.Substring(0, leftBracket).Trim();
var parameter = input.Substring(leftBracket + 1, rightBracket - leftBracket - 1).Trim();
var inverse = predicate[0] == '!';
if (inverse)
predicate = predicate.Substring(1);
if (!predicate.All(c => allowedChars.IndexOf(c) != -1))
return "Syntax error: unexpected character in predicate";
if (!parameter.Any(c => allowedChars.IndexOf(c) != -1))
return "Syntax error: unexpected character in parameter";
if (!Predicates.ContainsKey(predicate))
Predicates[predicate] = new List<string>();
if (inverse)
{
if (Predicates[predicate].Contains(parameter))
{
Predicates[predicate].Remove(parameter);
return parameter + " is no longer " + predicate;
}
else
{
return parameter + " is not currently " + predicate;
}
}
else
{
if (Predicates[predicate].Contains(parameter))
{
return parameter + " is already " + predicate;
}
else
{
Predicates[predicate].Add(parameter);
return parameter + " is now " + predicate;
}
}
}
}
private string DisplayUser(string user)
{
var predicates = Predicates.Where(kvp => kvp.Value.Contains(user)).Select(kvp => kvp.Key);
if (predicates.Count() == 0)
return "I don't know nothing about " + user;
var list = string.Join(", ", predicates);
var lastComma = list.LastIndexOf(',');
if (lastComma != -1)
list = list.Substring(0, lastComma) + " and" + list.Substring(lastComma + 1, list.Length - lastComma - 1);
return user + " is " + list;
}
private string DisplayPredicate(string predicate)
{
var list = string.Join(", ", Predicates[predicate]);
var lastComma = list.LastIndexOf(',');
if (lastComma != -1)
list = list.Substring(0, lastComma) + " and" + list.Substring(lastComma + 1, list.Length - lastComma - 1);
return list + (Predicates[predicate].Count > 1 ? " are " : " is ") + predicate;
}
}
}

559
Concilium/Bot/Resistance.cs Normal file
View File

@@ -0,0 +1,559 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Concilium.Helpers;
namespace Concilium.Bot
{
public class Resistance
{
private static class Strings
{
public static string Welcome = "A new The Resistance game was started by {0}!";
public static string TooFewPlayers = "There are still too few players. At least 5 players are required.";
public static string UserJoined = "{0} joined The Resistance game!";
public static string UserCantJoin = "I'm sorry, {0}. You can't join the current The Resistance game, since it already started.";
public static string UserDuplicate = "You already joined The Resistance game!";
public static string GameBegins = "All necessary players joined! Let the game begin!";
public static string HallOfFame = "Following players participated: ";
public static string AgreeDisagreeNotice = "Every player must now " + (char)2 + ".resist agree" + (char)2 + " or " + (char)2 + ".resist disagree" + (char)2 + " with the team.";
public static string ExecuteSabotageNotice = "You must now " + (char)2 + ".resist execute" + (char)2 + " or " + (char)2 + ".resist sabotage" + (char)2 + " the mission.";
public static string ExecuteNotice = "You must now " + (char)2 + ".resist execute" + (char)2 + " the mission.";
public static string PlayingSpy = "You're playing an imperial spy. Your job is to sabotage as many missions as possible, without revealing your identity to the resistance! The infiltrating spies are: {0}";
public static string PlayingResistance = "You're playing in the resistance. Your job is to accomplish as many missions as possible.";
public static string NewLeader = "A new leader has been determined: ";
public static string LeaderOrder = "The order for determining leaders is as follows: ";
public static string BeginGatheringTeam = "The leader must now choose the team members (attempt #{0}). {1} players must be in the team.";
public static string BeginVotingTeam = "The proposed team consists of: {0}. Please vote now if you agree or disagree with that team.";
public static string BeginMission = "The team will now execute the mission. If there are spies the mission might fail. Required spy sabotages for mission failure: {0}";
public static string SpiesWin = "The spies have managed to sabotage 3 missions and won the game!";
public static string ResistanceWins = "The resistance has managed to execute 3 missions and wins the game!";
public static string ShowVotes = "All players voted, these are the results: {0}";
public static string VoteFailed = "No majority could be found, a new team will be selected from a new leader.";
public static string LastTry = "This is the last attempt for finding a team. The proposal of the leader will be automatically accepted.";
public static string MissionFailed = "The mission was sabotaged and failed, because {0} player(s) sabotaged the mission. Score: Spies({1}), Resistance({2})";
public static string MissionAccomplishedClean = "The mission was accomplished successfully without any sabotage. Score: Spies({0}), Resistance({1})";
public static string MissionAccomplishedBarely = "The mission was accomplished successfully, although {0} player(s) committed sabotage. Score: Spies({1}), Resistance({2})";
public static string MissionData1 = "There are 5 missions, each mission requires a team with a certain amount of people, and requires a certain amount of sabotage acts to fail.";
public static string MissionData2 = "Team size/sabotages: ({0}/{1}), ({2}/{3}), ({4}/{5}), ({6}/{7}), ({8}/{9}).";
}
public enum State
{
Lobby,
GatheringTeam,
VotingTeam,
Mission
};
public enum Role
{
Unassigned,
Spy,
Resistance
};
public delegate void DummyHandler();
public event DummyHandler GameOver;
private void SendGameOver()
{
if (GameOver != null)
GameOver();
}
public event DummyHandler GatheringBegins;
private void SendGatheringBegins()
{
if (GatheringBegins != null)
GatheringBegins();
}
public event DummyHandler MissionBegins;
private void SendMissionBegins()
{
if (MissionBegins != null)
MissionBegins();
}
public delegate void LobbyNoticeHandler(string notice);
public event LobbyNoticeHandler OnLobbyNotice;
private void SendLobbyNotice(string notice)
{
if (OnLobbyNotice != null)
OnLobbyNotice(notice);
}
public delegate void UsersNoticeHandler(IEnumerable<string> users, string notice);
public event UsersNoticeHandler OnUsersNotice;
private void SendUsersNotice(IEnumerable<string> users, string notice)
{
if (OnUsersNotice != null && users.Count() > 0)
OnUsersNotice(users, notice);
}
public Resistance()
{
this.CurrentState = State.Lobby;
}
public void Init(string nickName)
{
this.GameCreator = nickName;
this.Leader = null;
this.NumPlayers = -1;
this.CurrentMission = 0;
this.ScoreSpies = 0;
this.ScoreResistance = 0;
this.TeamTries = 0;
SendLobbyNotice(string.Format(Strings.Welcome, this.GameCreator));
}
public bool StartGame(string nickName)
{
if (CurrentState != State.Lobby)
{
return false;
}
if (GameCreator != nickName)
{
return false;
}
if (allPlayers.Count < 5)
{
SendLobbyNotice(string.Format(Strings.TooFewPlayers, nickName));
return false;
}
this.NumPlayers = allPlayers.Count;
CheckState();
return true;
}
public bool JoinPlayer(string nickName)
{
if (CurrentState != State.Lobby)
{
SendUsersNotice(new List<string> { nickName }, string.Format(Strings.UserCantJoin, nickName));
return false;
}
if (allPlayers.ContainsKey(nickName))
{
SendUsersNotice(new List<string> { nickName }, Strings.UserDuplicate);
return false;
}
allPlayers[nickName] = Role.Unassigned;
SendLobbyNotice(string.Format(Strings.UserJoined, nickName));
CheckState();
return true;
}
private int GetSpyCount()
{
switch (NumPlayers)
{
case 5: return 2;
case 6: return 2;
case 7: return 3;
case 8: return 3;
case 9: return 3;
case 10: return 4;
}
throw new InvalidOperationException("Only 5 to 10 players supported");
}
private int GetTeamSize()
{
switch (NumPlayers)
{
case 5: return (new int[] { 2, 3, 2, 3, 3 })[CurrentMission - 1];
case 6: return (new int[] { 2, 3, 4, 3, 4 })[CurrentMission - 1];
case 7: return (new int[] { 2, 3, 3, 4, 4 })[CurrentMission - 1];
case 8: return (new int[] { 3, 4, 4, 5, 5 })[CurrentMission - 1];
case 9: return (new int[] { 3, 4, 4, 5, 5 })[CurrentMission - 1];
case 10: return (new int[] { 3, 4, 4, 5, 5 })[CurrentMission - 1];
}
throw new InvalidOperationException("Only 5 to 10 players supported");
}
private int GetNumSabotages()
{
if (CurrentMission == 4 && NumPlayers >= 7)
return 2;
return 1;
}
public void CheckState()
{
if (string.IsNullOrEmpty(GameCreator))
return;
switch (CurrentState)
{
case State.Lobby:
if (allPlayers.Count == NumPlayers)
{
SendLobbyNotice(Strings.GameBegins);
// Assign roles
var players = new List<string>(allPlayers.Keys);
players.Shuffle();
var spies = players.Take(GetSpyCount()).ToList();
var resistants = players.Skip(GetSpyCount()).ToList();
foreach (var player in resistants)
{
allPlayers[player] = Role.Resistance;
}
foreach (var player in spies)
{
allPlayers[player] = Role.Spy;
}
players.Shuffle();
playerOrder.AddRange(players);
SendLobbyNotice(Strings.LeaderOrder + string.Join(", ", playerOrder));
SetLeader(playerOrder[0]);
SendUsersNotice(resistants, Strings.PlayingResistance);
SendUsersNotice(spies, string.Format(Strings.PlayingSpy, string.Join(", ", spies)));
GenerateHallOfFame();
SendMissionData();
BeginGatheringTeam();
}
break;
case State.GatheringTeam:
if (TeamMembers.Count == GetTeamSize())
{
if (TeamTries >= 4)
BeginMission();
else
BeginVotingTeam();
}
break;
case State.VotingTeam:
if (playerAgreesWithTeam.Count == NumPlayers)
{
SendLobbyNotice(string.Format(Strings.ShowVotes, string.Join(", ", playerAgreesWithTeam.Select(p => (p.Value ? "+" : "-") + p.Key))));
var agrees = 0;
var disagrees = 0;
foreach (var vote in playerAgreesWithTeam)
{
if (vote.Value)
++agrees;
else
++disagrees;
}
if (agrees > disagrees)
{
TeamTries = 0;
BeginMission();
}
else
{
SendLobbyNotice(Strings.VoteFailed);
RotateLeader();
++TeamTries;
--CurrentMission;
BeginGatheringTeam();
}
}
break;
case State.Mission:
if (playerSabotagesMission.Count == GetTeamSize())
{
var sabotages = 0;
foreach (var vote in playerAgreesWithTeam)
{
if (vote.Value)
++sabotages;
}
var gameOver = false;
if (sabotages >= GetNumSabotages())
{
++ScoreSpies;
SendLobbyNotice(string.Format(Strings.MissionFailed, sabotages, ScoreSpies, ScoreResistance));
if (ScoreSpies >= 3)
{
SendLobbyNotice(Strings.SpiesWin);
SendGameOver();
gameOver = true;
}
}
else
{
++ScoreResistance;
if (sabotages == 0)
SendLobbyNotice(string.Format(Strings.MissionAccomplishedClean, ScoreSpies, ScoreResistance));
else
SendLobbyNotice(string.Format(Strings.MissionAccomplishedBarely, sabotages, ScoreSpies, ScoreResistance));
if (ScoreResistance >= 3)
{
SendLobbyNotice(Strings.ResistanceWins);
SendGameOver();
gameOver = true;
}
}
if (!gameOver)
{
RotateLeader();
BeginGatheringTeam();
}
}
break;
default:
throw new NotImplementedException("BUG! Implementation incomplete, an internal state was not handled");
}
}
public void SendMissionData()
{
SendLobbyNotice(Strings.MissionData1);
SendLobbyNotice(Strings.MissionData2);
}
private void RotateLeader()
{
var currentIndex = playerOrder.IndexOf(Leader);
var newIndex = currentIndex + 1;
if (newIndex >= playerOrder.Count)
newIndex = 0;
SetLeader(playerOrder[newIndex]);
}
private void SetLeader(string player)
{
Leader = player;
SendLobbyNotice(Strings.NewLeader + player);
}
private void BeginGatheringTeam()
{
if (CurrentState == State.GatheringTeam)
throw new InvalidOperationException("Can't begin gathering team right now");
++CurrentMission;
CurrentState = State.GatheringTeam;
TeamMembers.Clear();
SendLobbyNotice(string.Format(Strings.BeginGatheringTeam, TeamTries + 1, GetTeamSize()));
if (TeamTries >= 4)
SendLobbyNotice(string.Format(Strings.LastTry, GetTeamSize()));
}
private void BeginVotingTeam()
{
if (CurrentState != State.GatheringTeam)
throw new InvalidOperationException("Can't begin voting team right now");
CurrentState = State.VotingTeam;
playerAgreesWithTeam.Clear();
SendLobbyNotice(string.Format(Strings.BeginVotingTeam, string.Join(", ", TeamMembers)));
SendLobbyNotice(Strings.AgreeDisagreeNotice);
}
private void BeginMission()
{
if (CurrentState != State.VotingTeam && CurrentState != State.GatheringTeam)
throw new InvalidOperationException("Can't begin the mission right now");
TeamTries = 0;
CurrentState = State.Mission;
playerSabotagesMission.Clear();
SendLobbyNotice(string.Format(Strings.BeginMission, GetNumSabotages()));
SendUsersNotice(ResistancePlayers(), Strings.ExecuteNotice);
SendUsersNotice(SpyPlayers(), Strings.ExecuteSabotageNotice);
}
public void SetTeamMembers(string nick, IEnumerable<string> members)
{
if (nick != Leader)
{
SendUsersNotice(new string[] { nick }, string.Format("You're not the team leader, so you can't choose team members! {0} is the team leader.", Leader));
return;
}
if (CurrentState != State.GatheringTeam)
{
SendUsersNotice(new string[] { nick }, "You cannot gather a team right now.");
return;
}
if (members.Count() != GetTeamSize())
{
SendUsersNotice(new string[] { nick }, string.Format("You need to specify exactly {0} distinct members!", GetTeamSize()));
return;
}
foreach (var member in members)
{
if (!allPlayers.ContainsKey(member))
{
SendUsersNotice(new string[] { nick }, "There's no such player: " + member);
return;
}
}
TeamMembers.Clear();
TeamMembers.AddRange(members);
CheckState();
}
public void AgreeTeam(string nick)
{
if (CurrentState != State.VotingTeam)
{
SendUsersNotice(new string[] { nick }, "You cannot agree for a team right now.");
return;
}
if (playerAgreesWithTeam.ContainsKey(nick))
{
SendUsersNotice(new string[] { nick }, "You have already decided for this team.");
return;
}
SendUsersNotice(new string[] { nick }, "You have agreed with the proposed team. This decision can't be undone.");
playerAgreesWithTeam[nick] = true;
CheckState();
}
public void DisagreeTeam(string nick)
{
if (CurrentState != State.VotingTeam)
{
SendUsersNotice(new string[] { nick }, "You cannot disagree for a team right now.");
return;
}
if (playerAgreesWithTeam.ContainsKey(nick))
{
SendUsersNotice(new string[] { nick }, "You have already decided for this team.");
return;
}
SendUsersNotice(new string[] { nick }, "You have disagreed with the proposed team. This decision can't be undone.");
playerAgreesWithTeam[nick] = false;
CheckState();
}
public void ExecuteMission(string nick)
{
if (CurrentState != State.Mission)
{
SendUsersNotice(new string[] { nick }, "You cannot execute the mission right now.");
return;
}
if (playerSabotagesMission.ContainsKey(nick))
{
SendUsersNotice(new string[] { nick }, "You have already decided for this mission.");
return;
}
if (!TeamMembers.Contains(nick))
{
SendUsersNotice(new string[] { nick }, "You can't decide for this mission since you're not on the team!");
return;
}
playerSabotagesMission[nick] = false;
CheckState();
}
public void SabotageMission(string nick)
{
if (CurrentState != State.Mission)
{
SendUsersNotice(new string[] { nick }, "You cannot sabotage the mission right now.");
return;
}
if (allPlayers[nick] == Role.Resistance)
{
SendUsersNotice(new string[] { nick }, "You cannot sabotage the mission as a resistance member!.");
return;
}
if (playerSabotagesMission.ContainsKey(nick))
{
SendUsersNotice(new string[] { nick }, "You have already decided for this mission.");
return;
}
if (!TeamMembers.Contains(nick))
{
SendUsersNotice(new string[] { nick }, "You can't decide for this mission since you're not on the team!");
return;
}
playerSabotagesMission[nick] = true;
CheckState();
}
private void GenerateHallOfFame()
{
hallOfFame = Strings.HallOfFame + string.Join(", ", allPlayers.Select(p => p.Key + " (" + p.Value.ToString() + ")"));
}
public string GetStatus()
{
if (CurrentState == State.Lobby)
{
var message = "A game was recently created by " + GameCreator + ". Waiting for players to join. Following players already joined: ";
var existing = string.Join(", ", GetAllPlayers());
return message + existing;
}
else if (CurrentState == State.GatheringTeam)
{
var message = "The leader, {0}, is gathering the team. A total of {1} team members are needed for mission #{2}. Score: Spies({3}), Resistance({4})";
return string.Format(message, Leader, GetTeamSize(), CurrentMission, ScoreSpies, ScoreResistance);
}
else if (CurrentState == State.VotingTeam)
{
var message = "The leader is {0}. Waiting for all players to vote for the mission team ({1}). Following players haven't voted yet: {2}. Score: Spies({3}), Resistance({4})";
var missing = string.Join(", ", allPlayers.Where(p => !this.playerAgreesWithTeam.Keys.Contains(p.Key)).Select(p => p.Key));
var team = string.Join(", ", TeamMembers);
return string.Format(message, Leader, team, missing, ScoreSpies, ScoreResistance);
}
else if (CurrentState == State.Mission)
{
var message = "The leader is {0}. The team ({1}) is executing the mission. Each team member has to decide if the mission is executed or sabotaged. Following players haven't decided yet: {2}. Score: Spies({3}), Resistance({4})";
var missing = string.Join(", ", TeamMembers.Where(p => !this.playerSabotagesMission.Keys.Contains(p)));
var team = string.Join(", ", TeamMembers);
return string.Format(message, Leader, team, missing, ScoreSpies, ScoreResistance);
}
return "";
}
public string GameCreator;
public string Leader;
public int CurrentMission;
public int ScoreSpies;
public int ScoreResistance;
public int NumPlayers;
public int TeamTries;
public State CurrentState;
public Dictionary<string, Role> allPlayers = new Dictionary<string, Role>();
public Dictionary<string, bool> playerAgreesWithTeam = new Dictionary<string, bool>();
public Dictionary<string, bool> playerSabotagesMission = new Dictionary<string, bool>();
public List<string> TeamMembers = new List<string>();
public List<string> playerOrder = new List<string>();
public string hallOfFame;
public IEnumerable<string> GetAllPlayers() { return allPlayers.Keys; }
public IEnumerable<string> ResistancePlayers() { return allPlayers.Where(p => p.Value == Role.Resistance).Select(p => p.Key); }
public IEnumerable<string> SpyPlayers() { return allPlayers.Where(p => p.Value == Role.Spy).Select(p => p.Key); }
}
}

174
Concilium/Concilium.csproj Normal file
View File

@@ -0,0 +1,174 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{2695100D-D24B-4B41-8035-D1E8ED9A87CF}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Concilium</RootNamespace>
<AssemblyName>Concilium</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;LOG4NET</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>Concilium.App</StartupObject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;LOG4NET</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisLogFile>bin\Debug\Concilium.exe.CodeAnalysisLog.xml</CodeAnalysisLogFile>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<CodeAnalysisLogFile>bin\Release\Concilium.exe.CodeAnalysisLog.xml</CodeAnalysisLogFile>
<CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
<CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
<CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
</PropertyGroup>
<ItemGroup>
<Reference Include="IrcDotNet">
<HintPath>..\packages\IrcDotNet.0.5.0\lib\net40\IrcDotNet.dll</HintPath>
</Reference>
<Reference Include="log4net">
<HintPath>.\log4net.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>.\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SmartIrc4Net">
<HintPath>.\SmartIrc4Net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Web" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.Web.Services" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Compile Include="App.cs" />
<Compile Include="Bot\BotFeatureAttribute.cs" />
<Compile Include="Bot\BotFeatureBase.cs" />
<Compile Include="Bot\Features\FortuneFeature.cs" />
<Compile Include="Bot\Features\GoogleFeature.cs" />
<Compile Include="Bot\Features\JokeFeature.cs" />
<Compile Include="Bot\Features\MafiaFeature.cs" />
<Compile Include="Bot\Features\RssFeature.cs" />
<Compile Include="Bot\Features\TheResistanceFeature.cs" />
<Compile Include="Bot\Features\UserMonitorFeature.cs" />
<Compile Include="Bot\IBotFeature.cs" />
<Compile Include="Bot\IrcBot.cs" />
<Compile Include="Bot\IrcClientExtensions.cs" />
<Compile Include="Bot\PredicateLogic.cs" />
<Compile Include="Bot\Resistance.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Helpers\CmdLineToArgvW.cs" />
<Compile Include="Bot\Concilium.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Bot\Mafia.cs" />
<Compile Include="Bot\InvalidCommandParametersException.cs" />
<Compile Include="Helpers\ConsoleHelpers.cs" />
<Compile Include="Helpers\ListExtensions.cs" />
<Compile Include="Helpers\Log.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Helpers\StringExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ProjectFiles</ProjectView>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Concilium.Helpers
{
public static class CmdLineToArgvW
{
// The previous examples on this page used incorrect
// pointer logic and were removed.
public static string[] SplitArgs(string unsplitArgumentLine)
{
int numberOfArgs;
IntPtr ptrToSplitArgs;
string[] splitArgs;
ptrToSplitArgs = CommandLineToArgvW(unsplitArgumentLine, out numberOfArgs);
// CommandLineToArgvW returns NULL upon failure.
if (ptrToSplitArgs == IntPtr.Zero)
throw new ArgumentException("Unable to split argument.", new Win32Exception());
// Make sure the memory ptrToSplitArgs to is freed, even upon failure.
try
{
splitArgs = new string[numberOfArgs];
// ptrToSplitArgs is an array of pointers to null terminated Unicode strings.
// Copy each of these strings into our split argument array.
for (int i = 0; i < numberOfArgs; i++)
splitArgs[i] = Marshal.PtrToStringUni(
Marshal.ReadIntPtr(ptrToSplitArgs, i * IntPtr.Size));
return splitArgs;
}
finally
{
// Free memory obtained by CommandLineToArgW.
LocalFree(ptrToSplitArgs);
}
}
[DllImport("shell32.dll", SetLastError = true)]
static extern IntPtr CommandLineToArgvW(
[MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine,
out int pNumArgs);
[DllImport("kernel32.dll")]
static extern IntPtr LocalFree(IntPtr hMem);
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Concilium.Helpers
{
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
public static class ConsoleHelpers
{
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Concilium.Helpers
{
public static class ListExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
Random rng = new Random();
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}

18
Concilium/Helpers/Log.cs Normal file
View File

@@ -0,0 +1,18 @@
using System.IO;
using System.Collections;
using log4net;
namespace Concilium.Helpers
{
internal class Log
{
private Log()
{
}
public static ILog Get()
{
return LogManager.GetLogger("CONCILIUM");
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Concilium.Helpers
{
public static class StringExtensions
{
public static List<string> ReverseFormat(this string str, string template)
{
string pattern = "^" + Regex.Replace(template, @"\{[0-9]+\}", "(.*?)") + "$";
Regex r = new Regex(pattern);
Match m = r.Match(str);
List<string> ret = new List<string>();
for (int i = 1; i < m.Groups.Count; i++)
{
ret.Add(m.Groups[i].Value);
}
return ret;
}
}
}

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
Concilium/PasteHtml.dll Normal file

Binary file not shown.

BIN
Concilium/PasteHtml.pdb Normal file

Binary file not shown.

View File

@@ -0,0 +1,57 @@
#define LOG4NET
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Concilium")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Concilium")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Concilium.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Concilium.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Concilium.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

BIN
Concilium/SmartIrc4Net.dll Normal file

Binary file not shown.

BIN
Concilium/SmartIrc4Net.pdb Normal file

Binary file not shown.

8
Concilium/app.config Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<configuration>
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing="true" />
</settings>
</system.net>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

BIN
Concilium/log4net.dll Normal file

Binary file not shown.

BIN
Concilium/log4net.pdb Normal file

Binary file not shown.

30192
Concilium/log4net.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="IrcDotNet" version="0.5.0" targetFramework="net40" />
<package id="NetIrc2" version="1.0.0.0" targetFramework="net40" />
</packages>

Binary file not shown.

21
packages/IrcDotNet.0.5.0/LICENSE.md vendored Normal file
View File

@@ -0,0 +1,21 @@
# The MIT License
*Copyright (c) 2011-2015 Alex Regueiro*
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

1467
packages/NetIrc2.1.0.0.0/lib/NetIrc2.XML vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
packages/NetIrc2.1.0.0.0/lib/NetIrc2.chm vendored Normal file

Binary file not shown.

BIN
packages/NetIrc2.1.0.0.0/lib/NetIrc2.dll vendored Normal file

Binary file not shown.

BIN
packages/NetIrc2.1.0.0.0/lib/NetIrc2.pdb vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<repositories>
<repository path="..\Concilium\packages.config" />
</repositories>