Somehow compiles and even joins a conference
authorStiletto <blasux@blasux.ru>
Fri, 19 Oct 2012 00:38:54 +0400
changeset 1 76caf6a3f413
parent 0 e27ed261417d
child 2 4e050075fab9
Somehow compiles and even joins a conference
Makefile
config.vala
iswydt.vala
muc.vala
--- 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 $@ $^
 
--- /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<string,string> hash;
+    protected string rkey(string section, string key) {
+        return section+"\n"+key;
+    }
+    public Config.from_file(string filename) {
+        base();
+        hash = new Gee.HashMap<string,string>();
+        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));
+    }
+}
+
--- 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<string,string> {
-    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("<message to='stiletto@stiletto.name'><body>ТХБ</body></message>");
+                /*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 <config file>\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();
--- /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<string,Occupant> 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<string,Occupant>();
+        }
+        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<int>();
+
+                            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<string,Conference> rooms;
+
+    public ModuleMuc(Config cfg, Connection conn) {
+        base(cfg,conn);
+        this.rooms = new Gee.HashMap<string,Conference>();
+        
+        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;
+            }
+        });
+    }
+}