# HG changeset patch # User Stiletto # Date 1350592734 -14400 # Node ID 76caf6a3f413a42611546a17a781a24f9556de33 # Parent e27ed261417d714e8425db5fe72398fbcc48e3b5 Somehow compiles and even joins a conference diff -r e27ed261417d -r 76caf6a3f413 Makefile --- a/Makefile Fri Oct 05 15:41:19 2012 +0400 +++ b/Makefile Fri Oct 19 00:38:54 2012 +0400 @@ -5,6 +5,6 @@ VALALIBS := $(patsubst %, --pkg %, $(LIBS)) VFLAGS = -iswydt-bot: iswydt.vala +iswydt-bot: iswydt.vala config.vala muc.vala $(VALAC) $(VFLAGS) $(VALALIBS) -o $@ $^ diff -r e27ed261417d -r 76caf6a3f413 config.vala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.vala Fri Oct 19 00:38:54 2012 +0400 @@ -0,0 +1,49 @@ +class Config : Object { + protected Gee.HashMap hash; + protected string rkey(string section, string key) { + return section+"\n"+key; + } + public Config.from_file(string filename) { + base(); + hash = new Gee.HashMap(); + IOChannel cfg; + /* try { */ + cfg = new IOChannel.file(filename,"r"); + /*} catch (FileError e) { + File.new_for_path(filename);*/ + string section = "default"; + do { + string str; + size_t length, termpos; + IOStatus stat = cfg.read_line(out str,out length,out termpos); + if (stat == IOStatus.NORMAL) { + str = str[0:(long)termpos]; + if ( str.has_prefix("[") && str.has_suffix("]") ) { + section = str[1:-1]; + stdout.printf("Section '%s'\n",section); + } else { + string[] parts = str.split("=",2); + if (parts.length==2) { + var nkey = rkey(section,parts[0]); + hash[nkey] = parts[1]; + stdout.printf("LOL: key: %s value: %s\n",nkey,parts[1]); + } + } + } else + break; + } while (true); + } + public string get(string section, string key) { + return hash[rkey(section,key)]; + } + public string get_default(string section, string key, string def) { + var nkey = rkey(section,key); + if (hash.has_key(nkey)) + return hash[nkey]; + return def; + } + public bool has_key(string section, string key) { + return hash.has_key(rkey(section,key)); + } +} + diff -r e27ed261417d -r 76caf6a3f413 iswydt.vala --- a/iswydt.vala Fri Oct 05 15:41:19 2012 +0400 +++ b/iswydt.vala Fri Oct 19 00:38:54 2012 +0400 @@ -1,38 +1,12 @@ -class Config : Gee.HashMap { - public Config.from_file(string filename) { - base(); - IOChannel cfg; - /* try { */ - cfg = new IOChannel.file(filename,"r"); - /*} catch (FileError e) { - File.new_for_path(filename);*/ - do { - string str; - size_t length, termpos; - IOStatus stat = cfg.read_line(out str,out length,out termpos); - if (stat == IOStatus.NORMAL) { - string[] parts = str[0:(long)termpos].split("=",2); - if (parts.length==2) { - this[parts[0]] = parts[1]; - stdout.printf("LOL: key: %s value: %s\n",parts[0],parts[1]); - } - } else - break; - } while (true); - - - } -} - -class Account : Object { - protected Lm.Connection cn; - protected string jid; - protected string password; - protected string resource; - protected string server; - protected int port; - protected bool ssl; +class Connection : Object { + public Lm.Connection cn; + public string jid; + public string password; + public string resource; + public string server; + public int port; + public bool ssl; public enum State { DISCONNECTED, @@ -40,13 +14,15 @@ AUTHENTICATING, CONNECTED } - + protected State _state; public State state { get { return _state; } } + public Module[] modules; + public signal void state_changed(State old_state, State new_state, string description); protected void _change_state(State new_state, string description) { var old_state = this._state; @@ -58,36 +34,62 @@ stderr.printf("State not changed %s : %s\n",old_state.to_string(), description); } - public Account(Config cfg) { - this.jid = cfg["jid"]; - this.password = cfg["password"]; + public Connection(Config cfg) { + this.modules = new Module[10]; + this.jid = cfg["server","jid"]; + this.password = cfg["server","password"]; - if (cfg.has_key("server")) - this.server = cfg["server"]; - else - this.server = cfg["jid"].split("@",2)[1]; + this.server = cfg.get_default("server", "server", this.jid.split("@",2)[1]); - if (cfg.has_key("port")) - this.port = cfg["port"].to_int(); + if (cfg.has_key("server", "port")) + this.port = cfg["server", "port"].to_int(); else this.port = Lm.Connection.DEFAULT_PORT; - if (cfg.has_key("ssl")) - this.ssl = (cfg["ssl"]=="yes"); - else - this.ssl = false; + this.ssl = (cfg.get_default("server", "ssl", "yes")=="yes"); - if (cfg.has_key("resource")) - this.resource = cfg["resource"]; - else - this.resource = "iwydt"; + this.resource = cfg.get_default("server", "resource", "iswydt"); cn = new Lm.Connection(this.server); cn.set_disconnect_function((connection, reason) => { stderr.printf("Disconnected: %s\n",reason.to_string()); _change_state(State.DISCONNECTED,reason.to_string()); }, null); - //cn.register_message_handler( + var msg_handler = new Lm.MessageHandler((handler, connection, message) => { + stdout.printf("Got msg\n"); + var node = message.get_node(); + var body = node.find_child("body"); + if (body!=null) { + stdout.printf("MSG %s to %s: %s\n", node.get_attribute("from"), node.get_attribute("to"), body.get_value()); + } + return Lm.HandlerResult.ALLOW_MORE_HANDLERS; + }, null); + cn.register_message_handler(msg_handler, Lm.MessageType.MESSAGE, Lm.HandlerPriority.NORMAL); + /*var muc_handler = new Lm.MessageHandler((handler, connection, message) => { + var node = message.node; + var from = node.get_attribute("from"); + if (from != null) { + var froms = from.split("/",2); + //stdout.printf("From: %s %s\n",node.to_string(),message.get_type().to_string()); + if (rooms.has_key(froms[0])) { + return rooms[froms[0]].muc_handler(handler, connection, message); + } + } + return Lm.HandlerResult.ALLOW_MORE_HANDLERS; + }, null); + cn.register_message_handler(muc_handler, Lm.MessageType.MESSAGE, Lm.HandlerPriority.NORMAL); + cn.register_message_handler(muc_handler, Lm.MessageType.PRESENCE, Lm.HandlerPriority.NORMAL); + cn.register_message_handler(muc_handler, Lm.MessageType.IQ, Lm.HandlerPriority.NORMAL);*/ + var module = new ModuleMuc(cfg, this); + modules = {module}; + state_changed.connect( (olds, news, desc) => { + if (news == Connection.State.CONNECTED) { + cn.send_raw("ТХБ"); + /*var room = new Conference(this, "говнохост@conference.blasux.ru", "Ζαλυπα"); + room.join("Oh hai"); + this.rooms[room.jid] = room;*/ + } + }); } public void open() { @@ -128,13 +130,13 @@ stderr.printf("Usage: %s \n",args[0]); return 1; } - /*Log.set_handler("LM", (LogLevelFlags)65535, (domain, levels, message) => { + Log.set_handler("Muc", (LogLevelFlags)65535, (domain, levels, message) => { stderr.printf("--- %s\n", message); - });*/ + }); log("LM", LogLevelFlags.LEVEL_DEBUG, "HATE HATE"); var cfg = new Config.from_file(args[1]); var loop = new MainLoop(); - var account = new Account(cfg); + var account = new Connection(cfg); account.open(); stdout.printf("Fuck yeah\n"); loop.run(); diff -r e27ed261417d -r 76caf6a3f413 muc.vala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/muc.vala Fri Oct 19 00:38:54 2012 +0400 @@ -0,0 +1,255 @@ +abstract class Module : Object { + protected weak Config cfg; + protected weak Connection conn; + + public Module(Config cfg, Connection conn) { + this.cfg = cfg; + this.conn = conn; + } + public abstract string name(); +} + +class ModuleMuc : Module { + public enum Affiliation { + NONE, + OUTCAST, + MEMBER, + ADMIN, + OWNER + } + + public enum Role { + NONE, + VISITOR, + PARTICIPANT, + MODERATOR + } + + static Role role_from_string(string role) { + switch (role) { + case "moderator": return Role.MODERATOR; break; + case "participant": return Role.PARTICIPANT; break; + case "visitor": return Role.VISITOR; break; + } + return Role.NONE; + } + static Affiliation affil_from_string(string affil) { + switch (affil) { + case "owner": return Affiliation.OWNER; break; + case "admin": return Affiliation.ADMIN; break; + case "member": return Affiliation.MEMBER; break; + case "outcast": return Affiliation.OUTCAST; break; + } + return Affiliation.NONE; + } + + public class Occupant : Object { + public weak Conference conference; + public string nick; + public string real_jid; + public Affiliation affil; + public Role role; + public bool isme; + } + + public enum State { + CONNECTED, + DISCOVERING, + CONNECTING, + DISCONNECTING, + DISCONNECTED + } + + public signal void state_changed(Conference conf, State old_state, State new_state, string description); + public signal void on_join(Conference conf, Occupant occupant); + public signal void on_part(Conference conf, Occupant occupant); + public signal void on_role(Conference conf, Occupant occupant, Role prev); + public signal void on_affil(Conference conf, Occupant occupant, Affiliation prev); + public signal void on_nick(Conference conf, Occupant occupant, string prev); + public signal void on_subject(Conference conf); + + public class Conference : Object { + public string jid; + public string nick; + public string desired_nick; + public string subject; + + protected State _state; + public Time time; + public State state { get { return _state; } } + public Gee.HashMap occupants; + protected string presence_join_id; + protected weak ModuleMuc module; + + protected void _change_state(State new_state, string description) { + var old_state = this._state; + if (old_state != new_state) { + this._state = new_state; + stderr.printf("MUC State changed %s -> %s : %s\n",old_state.to_string(), new_state.to_string(), description); + module.state_changed(this, old_state, new_state, description); + } else + stderr.printf("MUC State not changed %s : %s\n",old_state.to_string(), description); + } + + public Conference(ModuleMuc module, string jid) { + this.module = module; + var section = "muc "+jid; + this.desired_nick = module.cfg[section,"nick"]; + this.nick = this.desired_nick; + this.jid = jid; + this._state = State.DISCONNECTED; + this.occupants = new Gee.HashMap(); + } + public void join(string desc) { + if (_state == State.DISCONNECTED) { + var prs = new Lm.Message(jid+"/"+desired_nick, Lm.MessageType.PRESENCE); + presence_join_id = jid+"_"+Random.next_int().to_string(); + prs.node.set_attribute("id",presence_join_id); + prs.node.add_child("x",null).set_attribute("xmlns","http://jabber.org/protocol/muc"); + module.conn.cn.send(prs); + nick = desired_nick; + _change_state(State.CONNECTING, "requested to join"); + } + } + + public Lm.HandlerResult muc_handler(Lm.MessageHandler handler, Lm.Connection connection, Lm.Message message) { + var node = message.node; + var from = node.get_attribute("from").split("/",2); + stdout.printf("MUC<%s>: %s\n",this.jid,node.to_string()); + var type = node.get_attribute("type"); + if (message.get_type()==Lm.MessageType.PRESENCE) { + switch (type) { + case null: + case "unavailable": + if (from.length==2) { + var statuses = new Gee.HashSet(); + + var x = node.get_child("x"); + var child = x!=null ? x.children : null; + while (child != null) { + stdout.printf("Child %s %s\n", child.name, child.get_attribute("code")); + if (child.name == "status") { + var code = child.get_attribute("code"); + if (code!=null) + statuses.add(code.to_int()); + } + child = child.next; + } + + if (statuses.contains(110) && (_state == State.CONNECTED)) + log("Muc", LogLevelFlags.LEVEL_INFO, "Joined a room which I was already in: %s", from[0]); + + var occupant = occupants[from[1]]; + var item = x != null ? x.get_child("item") : null; + var affil = Affiliation.NONE; + var role = Role.NONE; + string nick = null; + if (item != null) { + affil = affil_from_string(item.get_attribute("affiliation")); + role = role_from_string(item.get_attribute("role")); + nick = item.get_attribute("nick"); + } else + log("Muc", LogLevelFlags.LEVEL_ERROR, "Your MUC server is shit. No role and affiliation info in presences: %s", node.to_string()); + if (occupant != null) { + if (role == Role.NONE) { + occupants.unset(from[1]); + log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has parted.", this.jid, from[1]); + module.on_part(this, occupant); + if (from[1]==this.nick) { + _change_state(State.DISCONNECTED, "we became unavailable"); + occupants.clear(); + } + } else { + if (affil != occupant.affil) { + var prev = occupant.affil; + occupant.affil = affil; + module.on_affil(this, occupant, prev); + } + if (role != occupant.role) { + var prev = occupant.role; + occupant.role = role; + module.on_role(this, occupant, prev); + } + if (statuses.contains(303) && (nick!=null)) { + occupants[nick] = occupant; + occupants.unset(from[1]); + occupant.nick = nick; + module.on_nick(this, occupant, from[1]); + } + } + } else { + assert( role!= Role.NONE); + occupant = new Occupant(); + occupant.role = role; + occupant.affil = affil; + occupant.nick = from[1]; + occupants[from[1]] = occupant; + occupant.isme = statuses.contains(110); + log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has joined as %s/%s.", this.jid, from[1], affil.to_string(), role.to_string()); + if (statuses.contains(110)) + _change_state(State.CONNECTED, "got own presence. we are "+this.nick); + module.on_join(this, occupant); + } + + stdout.printf("User list: "); + foreach (var a in occupants.keys) + stdout.printf("%s <%s,%s>, ", a, occupants[a].affil.to_string(), occupants[a].role.to_string()); + stdout.printf("\n"); + } + + break; + case "error": + if ((from.length==1)||(node.get_attribute("id")==presence_join_id)) { + _change_state(State.DISCONNECTED, node.get_child("error").to_string()); + return Lm.HandlerResult.ALLOW_MORE_HANDLERS; + } else if (from.length==2) { + //if (from[1]==this.nick) + } + break; + } + } else if ((message.get_type()==Lm.MessageType.MESSAGE)&&(type=="groupchat")) { + var subj = node.get_attribute("subject"); + if (subj != null) { + if (subj != this.subject) { + this.subject = subj; + module.on_subject(this); + } + } + } + return Lm.HandlerResult.ALLOW_MORE_HANDLERS; + } + public void part(string desc) { + } + } + + public override string name() { return "muc"; } + + public Gee.HashMap rooms; + + public ModuleMuc(Config cfg, Connection conn) { + base(cfg,conn); + this.rooms = new Gee.HashMap(); + + var muc_handler = new Lm.MessageHandler((handler, connection, message) => { + var node = message.node; + var from = node.get_attribute("from"); + if (from != null) { + var froms = from.split("/",2); + if (rooms.has_key(froms[0])) { + return rooms[froms[0]].muc_handler(handler, connection, message); + } + } + return Lm.HandlerResult.ALLOW_MORE_HANDLERS; + }, null); + conn.cn.register_message_handler(muc_handler, Lm.MessageType.MESSAGE, Lm.HandlerPriority.NORMAL); + conn.cn.register_message_handler(muc_handler, Lm.MessageType.PRESENCE, Lm.HandlerPriority.NORMAL); + conn.cn.register_message_handler(muc_handler, Lm.MessageType.IQ, Lm.HandlerPriority.NORMAL); + conn.state_changed.connect( (olds, news, desc) => { + if (news == Connection.State.CONNECTED) { + var room = new Conference(this, "говнохост@conference.blasux.ru"); //, "Ζαλυπα"); + room.join("Oh hai"); + this.rooms[room.jid] = room; + } + }); + } +}