Master.cs
using KNLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace Billard_Simulator
{
public class Master : KNClient
{
private RemoteEndpoint endpoint;
private String nickname, password;
private String channel;
private Dictionary<String, String> tables;
private int count;
private Object countLock;

public Master(String chatsystem, String nickname, String password)
{
switch (chatsystem)
{
case "Mainfranken-Chat":
endpoint = RemoteEndpoint.MFC;
break;
case "Knuddels.ch":
endpoint = RemoteEndpoint.CH;
break;
case "Knuddels.at":
endpoint = RemoteEndpoint.AT;
break;
default:
endpoint = RemoteEndpoint.DE;
break;
}

this.nickname = nickname;
this.password = password;
channel = "9-Ball";
}

public String getNickname()
{
return nickname;
}

public void Connect(String proxy)
{
try
{
if (proxy.Equals(String.Empty))
{
Connect(endpoint);
}
else
{
String[] socks5 = proxy.Split(':');
Connect(endpoint, socks5[0], Int32.Parse(socks5[1]));
}

Simulator.Get().Log("Master", "Verbunden.");
}
catch (SocketException)
{
Simulator.Get().Log("Master", "Verbinden fehlgeschlagen.");
}
}

private void SendDelayed(String token)
{
lock (countLock)
{
Send(token);
count++;

if (count == 2)
{
count = 0;
Thread.Sleep(4500);
}
}
}

public override void OnReceive(String token)
{
String[] tokens = token.Split('\0');

switch (tokens[0])
{
case "=":
switch (tokens[2])
{
case "a":
SendDelayed(String.Format("b\0{0}\0a", tables[tokens[1]]));
break;
case "b":
new Thread(new ThreadStart(delegate
{
Thread.Sleep(30 * 1000);
SendDelayed(String.Format("b\0{0}\0b\09\01351.0885235293317\0-281.4885614620557\0-133.32351868748881", tables[tokens[1]]));
SendDelayed(String.Format("b\0{0}\0a\032\01\00\09\01\00\02\01\02\04\01\00\01\01\04\06\01\06\07\01\01\03\01\00\09\01\03\05\01\05\06\01\02\08\01\00\01\01\01\08\02\04\01\02\07\016\02\04\016\01\04\08\01\04\05\02\07\013\01\01\04\02\05\016\02\03\013\01\04\05\02\03\016\02\09\01\03\05\028\02\04\013\03\05\032\02\08\08\04\08\03\02\01\013\04\07\02\0173.87448089616535\0180.1360081643251\0301.94548868562856\0319.8297799721764\0142.0796241619943\0128.91093717632114\080.59166953172453\0261.3320826816378\0209.82758851912536\0270.86570125659506\066.8262917573852\0298.82576774953236\0119.72123774812964\0145.03626723480312\0-1\0-1\0-1\0-1\0333.6948428583014\097.18263518727728", tables[tokens[1]]));
})).Start();

break;
case "h":
SendDelayed(String.Format("b\0{0}\0g\09\0481.0\0327.0", tables[tokens[1]]));
break;
case "i":
tables.Add(tokens[1], tokens[3]);
SendDelayed(String.Format("b\0{0}\0h", tokens[3]));
break;
default:
break;
}

break;
case "0":
switch (tokens[1])
{
case "l":
String nick = tokens[3].Split('\x0A')[0];

foreach (Player player in Simulator.Get().GetPlayers())
{
if (player.getNickname().Equals(nick))
{
SendDelayed(String.Format("e\0{0}\0/billard j:{1}", channel, nick));
break;
}
}

break;
}

break;
case "a":
if (tokens[1].Equals(channel))
{
nickname = tokens[2];
Simulator.Get().Log("Master", "Eingeloggt.");
tables = new Dictionary<String, String>();
countLock = new Object();

foreach (Player player in Simulator.Get().GetPlayers())
{
player.ConnectAndLogin(endpoint, channel);
}
}

break;
case "b":
int id = 1, size = 0;
int count = 1;
bool isChannel = true;
bool isBillard = false;

for (int i = 1; i < tokens.Length; i++)
{
if (isChannel)
{
String[] ch = tokens[i].Split('\x0A');

if (ch[0].Equals(channel))
{
isBillard = true;
size = Int32.Parse(ch[1]);
}
else if (ch[0].Equals(""") && isBillard)
{
count++;

if (Int32.Parse(ch[1]) < size)
{
id = count;
size = Int32.Parse(ch[1]);
}
}
else
{
break;
}

isChannel = false;
}
else if (tokens[i].Equals("-"))
{
isChannel = true;
}
}

if (id > 1)
{
channel = String.Format("{0} {1}", channel, id);
}

Simulator.Get().Log("Master", String.Format("Channel festgelegt: {0}.", channel));
Login(nickname, password, channel);
break;
case "k":
token = token.Substring(2);
tokens = token.Split('\xF5');

switch (tokens[0])
{
case "Problem":
case "Nick Gesperrt":
Simulator.Get().Log("Master", "Login fehlgeschlagen.");
break;
case "Billard - Frage":
SendDelayed(String.Format("{0}\0{1}\0 Ja \0", tokens[1].Substring(1), tokens[2]));
break;
default:
if (tokens[0].Contains("Spiel von"))
{
SendDelayed(String.Format("{0}\0{1}\0Spiel Beitreten\0", tokens[1].Substring(1), tokens[2]));
}

break;
}

break;
}
}

public override void OnDisconnect()
{
Simulator.Get().Log("Master", "Verbindung unterbrochen, Spieler werden ausgeloggt.");
Player[] players = new Player[Simulator.Get().GetPlayers().Count];
Simulator.Get().GetPlayers().CopyTo(players);

foreach (Player player in players)
{
player.Disconnect();
}
}
}
}
Player.cs
using KNLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace Billard_Simulator
{
public class Player : KNClient
{
private String nickname, password, proxy;
private String channel;
private String request;
private String game;

public Player(String nickname, String password, String proxy)
{
this.nickname = nickname;
this.password = password;
this.proxy = proxy;
}

public String getNickname()
{
return nickname;
}

public void ConnectAndLogin(RemoteEndpoint endpoint, String channel)
{
new Thread(new ThreadStart(delegate
{
try
{
if (proxy.Equals(String.Empty))
{
Connect(endpoint);
}
else
{
String[] socks5 = proxy.Split(':');
Connect(endpoint, socks5[0], Int32.Parse(socks5[1]));
}
}
catch (SocketException)
{
Simulator.Get().RemovePlayer(this);
Simulator.Get().Log(nickname, "Verbinden fehlgeschlagen.");
return;
}

Simulator.Get().Log(nickname, "Verbunden.");
this.channel = channel;
Login(nickname, password, channel);
})).Start();
}

public void Accept()
{
Send(String.Format("iv\0{0}\0 Ja \0", request));
Simulator.Get().Log(nickname, "Spielbeitritt von Master zugestimmt.");
}

public override void OnReceive(String token)
{
String[] tokens = token.Split('\0');

switch (tokens[0])
{
case "=":
switch (tokens[2])
{
case "a":
Send(String.Format("b\0{0}\0a", game));
break;
case "b":
Send(String.Format("b\0{0}\0b\09\0567.3807731341585\0133.10171691997812\0-239.42272261062402", game));
Send(String.Format("b\0{0}\0a\01\04\09\02\0192.0015672698858\0192.00240592208564\0175.53794826824122\0201.5062959814314\0175.53549018501585\0182.49211938935622\0159.0709393495886\0211.0119896680176\0159.07322016127216\0172.98822495379144\0142.6049895903074\0201.50452661423168\0142.60917857505893\0182.4931737036524\0126.14118061088443\0191.99892199919526\0159.07218539067836\0191.99740058522752\0-1\0-1", game));
break;
case "h":
Send(String.Format("b\0{0}\0g\09\0481.0\0327.0", game));
break;
case "i":
game = tokens[3];
Send(String.Format("b\0{0}\0h", game));
break;
}

break;
case "a":
if (tokens[1].Equals(channel))
{
nickname = tokens[2];
Simulator.Get().Log(nickname, "Eingeloggt.");
SendMessage("/billard j:+Spiel eröffnen", channel);
}

break;
case "k":
token = token.Substring(2);
tokens = token.Split('\xF5');

switch (tokens[0])
{
case "Problem":
Simulator.Get().RemovePlayer(this);
Simulator.Get().Log(nickname, "Login fehlgeschlagen.");
break;
case "Neues Spiel - 9-Ball":
Send(String.Format("b\0{0}\0 Anlegen \0Standard\01\01\01\0lang\00\00\01\01\00\00\0", tokens[2]));
Simulator.Get().Log(nickname, String.Format("Spiel eröffnet: {0}.", tokens[2].Split('|')[0]));
break;
case "Billard - Frage":
if (token.Contains("mitspielen.##Einverstanden?"))
{
String nick = Regex.Match(token, "°>_h([^\\|]+)").Groups[1].Value.Replace("\", String.Empty);

if (nick.Equals(Simulator.Get().GetMaster().getNickname()))
{
request = tokens[2];
Simulator.Get().Log(nickname, "Master hat um Spielerlaubnis gebeten.");
Simulator.Get().IncreaseRequests();
}
else
{
Simulator.Get().Log(nickname, String.Format("Anfrage um Spielerlaubnis von {0} ignoriert.", nick));
}
}
else
{
Send(String.Format("{0}\0{1}\0 Ja \0", tokens[1].Substring(1), tokens[2]));
Simulator.Get().Log(nickname, "Spiel gestartet.");
}

break;
}

break;
case "t":
if (tokens.Length > 3)
{
Match match = Regex.Match(tokens[3], "°>_h(.+)\\|/serverpp "\\|/w "<° erhält (\\d+) Billard-Punkte");
String points = match.Groups[2].Value;

if (!points.Equals(String.Empty))
{
String nick = match.Groups[1].Value.Replace("\", String.Empty).Replace(Simulator.Get().GetMaster().getNickname(), "Master");
Simulator.Get().Log(nickname, String.Format("{0} hat {1} Punkte abgesahnt.", nick, points));
}
}

break;
}
}

public override void OnDisconnect()
{
if (Simulator.Get().GetPlayers().Contains(this))
{
Simulator.Get().RemovePlayer(this);
Simulator.Get().Log(nickname, "Verbindung unterbrochen.");
}
}
}
}
Es gibt noch eine weitere Datei, Simulator.cs, die stell ich euch aber genau wie die komplette Projektmappe nicht zur Verfügung, da sie keine für den Bot relevanten Informationene beinhaltet. Ich will ja nicht, dass ihr den Bot kopiert und euch dann ein Keks freut wenn ihr meinen Namen durch euren ersetzt, ihr sollt ja auch was dabei lernen.

Edit: Extra für die Admins zur Verfügung gestellt, können die sich mal anschauen wie der Channel Bug funktioniert und was dagegen tun.