--- 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;
+ }
+ });
+ }
+}