// Kelsier project - Bot state and parser code (Bot.cs)
// Written by the Jobbig codeteam.
//
// Copyright 2013 John Runyon.
//
// This file is part of the Kelsier project.
//
// Kelsier is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
using System.Net;
using System.Net.Sockets;
namespace Kelsier.Common {
public class Bot {
public int id { get; private set; }
public string nick { get; private set; }
public string ident { get; private set; }
public string realname { get; private set; }
public string localip { get; private set; }
public string server { get; private set; }
public int serverport { get; private set; }
private IPEndPoint local;
private Socket s;
public bool online { get; private set; }
private Logger log;
public Bot(int id) {
this.id = id;
this.log = new Logger(id.ToString("0000"), Info.log);
MySqlDataReader rdr = Info.db.queryReader("SELECT nick, ident, realname, bindip, server, serverport FROM bots WHERE id = @id", new object[] { "@id", id });
rdr.Read();
this.nick = rdr.GetString("nick");
this.ident = rdr.GetString("ident");
this.realname = rdr.GetString("realname");
if (rdr.IsDBNull(rdr.GetOrdinal("bindip"))) {
this.localip = null;
this.local = new IPEndPoint(IPAddress.Any, 0);
} else {
this.localip = rdr.GetString("bindip");
this.local = new IPEndPoint(IPAddress.Parse(this.localip), 0);
}
this.server = rdr.GetString("server");
this.serverport = rdr.GetInt32("serverport");
rdr.Close();
// TODO move this part into a 001 hook.
MySqlDataReader rdr = Info.db.queryReader("SELECT authname, authpass FROM m_id_quakenet WHERE botid = @id", new object[] { "@id", id });
rdr.Read();
this.authname = rdr.GetString("authname");
this.authpass = rdr.GetString("authpass");
rdr.Close();
}
public string connect() {
if (online) throw new InvalidStateException();
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Bind(local);
s.Connect(server, serverport);
register();
Stator rcvstate = new Stator();
s.BeginReceive(rcvstate.buffer, 0, 1, SocketFlags.None, new AsyncCallback(dataIn), rcvstate);
return "TODO";
}
public void dataIn(IAsyncResult ar) {
Stator rcvstate = (Stator)ar.AsyncState;
s.EndReceive(ar);
if (rcvstate.buffer[0] == '\n') {
string linein = rcvstate.buffersb.ToString();
processData(linein);
rcvstate = new Stator();
} else if (rcvstate.buffer[0] != '\r') {
rcvstate.buffersb.Append(Encoding.ASCII.GetString(rcvstate.buffer, 0, 1));
}
s.BeginReceive(rcvstate.buffer, 0, 1, SocketFlags.None, new AsyncCallback(dataIn), rcvstate);
}
private void send(string data, params object[] args) {
log.info(">>> " + data);
s.Send(Encoding.ASCII.GetBytes(data + "\n"));
}
private void processData(string data) {
log.info("<<< " + data);
string[] parts;
string source = null;
if (data.StartsWith(":")) {
parts = data.Split((char[])null, 2, StringSplitOptions.RemoveEmptyEntries);
source = parts[0].Substring(1);
data = parts[1];
}
parts = data.Split((char[])null, 2, StringSplitOptions.RemoveEmptyEntries);
if (parts[0] == "PRIVMSG") {
processMsg(source, parts[1]);
} else if (parts[0] == "376") {
send("AUTH "+this.authname+" "+this.authpass); // TODO 001 hook
send("MODE "+this.nick+" +x-w");
send("JOIN #jobbig");
} else if (parts[0] == "PING") {
send("PONG "+parts[1]);
}
}
// processMsg("DimeCadmium!dime@jobbig.eu", "#mustis :hi");
private void processMsg(string source, string data) {
string[] dataparts = data.Split((char[])null, 2, StringSplitOptions.RemoveEmptyEntries);
string nick = (source.Split(new char[] { '!' }))[0];
string target = dataparts[0];
string message = dataparts[1];
if (message.StartsWith(":"))
message = message.Substring(1);
string[] msgparts = message.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
// TODO check msgparts[0] first char for trigger
string cmdstr = msgparts[0].Substring(1);
string[] args;
bool chanmsg;
Channel chan;
if (target.StartsWith("#")) {
chanmsg = true;
args = new string[msgparts.Length - 1];
Array.Copy(msgparts, 1, args, 0, msgparts.Length - 1);
chan = new Channel(target);
} else {
chanmsg = false;
args = new string[msgparts.Length - 2];
Array.Copy(msgparts, 2, args, 0, msgparts.Length - 2);
chan = new Channel(msgparts[1]);
}
User user = new User(nick);
Command cmd = new Command(this, cmdstr, args, user, chan, chanmsg);
}
private void register() {
send(String.Format("NICK {0}", nick));
send(String.Format("USER {0} * * :{1}", ident, realname));
online = true;
}
}
public class InvalidStateException : Exception { }
public class Stator {
public byte[] buffer = new byte[1];
public StringBuilder buffersb = new StringBuilder();
}
}