// 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(); } }