muc.vala
changeset 1 76caf6a3f413
child 2 4e050075fab9
equal deleted inserted replaced
0:e27ed261417d 1:76caf6a3f413
       
     1 abstract class Module : Object {
       
     2     protected weak Config cfg;
       
     3     protected weak Connection conn;
       
     4 
       
     5     public Module(Config cfg, Connection conn) {
       
     6         this.cfg = cfg;
       
     7         this.conn = conn;
       
     8     }
       
     9     public abstract string name();
       
    10 }
       
    11 
       
    12 class ModuleMuc : Module {
       
    13     public enum Affiliation {
       
    14         NONE,
       
    15         OUTCAST,
       
    16         MEMBER,
       
    17         ADMIN,
       
    18         OWNER
       
    19     }
       
    20 
       
    21     public enum Role {
       
    22         NONE,
       
    23         VISITOR,
       
    24         PARTICIPANT,
       
    25         MODERATOR
       
    26     }
       
    27 
       
    28     static Role role_from_string(string role) {
       
    29         switch (role) {
       
    30             case "moderator": return Role.MODERATOR; break;
       
    31             case "participant": return Role.PARTICIPANT; break;
       
    32             case "visitor": return Role.VISITOR; break;
       
    33         }
       
    34         return Role.NONE;
       
    35     }
       
    36     static Affiliation affil_from_string(string affil) {
       
    37         switch (affil) {
       
    38             case "owner": return Affiliation.OWNER; break;
       
    39             case "admin": return Affiliation.ADMIN; break;
       
    40             case "member": return Affiliation.MEMBER; break;
       
    41             case "outcast": return Affiliation.OUTCAST; break;
       
    42         }
       
    43         return Affiliation.NONE;
       
    44     }
       
    45 
       
    46     public class Occupant : Object {
       
    47         public weak Conference conference;
       
    48         public string nick;
       
    49         public string real_jid;
       
    50         public Affiliation affil;
       
    51         public Role role;
       
    52         public bool isme;
       
    53     }
       
    54 
       
    55     public enum State {
       
    56         CONNECTED,
       
    57         DISCOVERING,
       
    58         CONNECTING,
       
    59         DISCONNECTING,
       
    60         DISCONNECTED
       
    61     }
       
    62 
       
    63     public signal void state_changed(Conference conf, State old_state, State new_state, string description);
       
    64     public signal void on_join(Conference conf, Occupant occupant);
       
    65     public signal void on_part(Conference conf, Occupant occupant);
       
    66     public signal void on_role(Conference conf, Occupant occupant, Role prev);
       
    67     public signal void on_affil(Conference conf, Occupant occupant, Affiliation prev);
       
    68     public signal void on_nick(Conference conf, Occupant occupant, string prev);
       
    69     public signal void on_subject(Conference conf);
       
    70 
       
    71     public class Conference : Object {
       
    72         public string jid;
       
    73         public string nick;
       
    74         public string desired_nick;
       
    75         public string subject;
       
    76 
       
    77         protected State _state;
       
    78         public Time time;
       
    79         public State state { get { return _state; } }
       
    80         public Gee.HashMap<string,Occupant> occupants;
       
    81         protected string presence_join_id;
       
    82         protected weak ModuleMuc module;
       
    83 
       
    84         protected void _change_state(State new_state, string description) {
       
    85             var old_state = this._state;
       
    86             if (old_state != new_state) {
       
    87                 this._state = new_state;
       
    88                 stderr.printf("MUC State changed %s -> %s : %s\n",old_state.to_string(), new_state.to_string(), description);
       
    89                 module.state_changed(this, old_state, new_state, description);
       
    90             } else
       
    91                 stderr.printf("MUC State not changed %s : %s\n",old_state.to_string(), description);
       
    92         }
       
    93 
       
    94         public Conference(ModuleMuc module, string jid) {
       
    95             this.module = module;
       
    96             var section = "muc "+jid;
       
    97             this.desired_nick = module.cfg[section,"nick"];
       
    98             this.nick = this.desired_nick;
       
    99             this.jid = jid;
       
   100             this._state = State.DISCONNECTED;
       
   101             this.occupants = new Gee.HashMap<string,Occupant>();
       
   102         }
       
   103         public void join(string desc) {
       
   104             if (_state == State.DISCONNECTED) {
       
   105                 var prs = new Lm.Message(jid+"/"+desired_nick, Lm.MessageType.PRESENCE);
       
   106                 presence_join_id = jid+"_"+Random.next_int().to_string();
       
   107                 prs.node.set_attribute("id",presence_join_id);
       
   108                 prs.node.add_child("x",null).set_attribute("xmlns","http://jabber.org/protocol/muc");
       
   109                 module.conn.cn.send(prs);
       
   110                 nick = desired_nick;
       
   111                 _change_state(State.CONNECTING, "requested to join");
       
   112             }
       
   113         }
       
   114         
       
   115         public Lm.HandlerResult muc_handler(Lm.MessageHandler handler, Lm.Connection connection, Lm.Message message) {
       
   116             var node = message.node;
       
   117             var from = node.get_attribute("from").split("/",2);
       
   118             stdout.printf("MUC<%s>: %s\n",this.jid,node.to_string());
       
   119             var type = node.get_attribute("type");
       
   120             if (message.get_type()==Lm.MessageType.PRESENCE) {
       
   121                 switch (type) {
       
   122                     case null:
       
   123                     case "unavailable":
       
   124                         if (from.length==2) {
       
   125                             var statuses = new Gee.HashSet<int>();
       
   126 
       
   127                             var x = node.get_child("x");
       
   128                             var child = x!=null ? x.children : null;
       
   129                             while (child != null) {
       
   130                                 stdout.printf("Child %s %s\n", child.name, child.get_attribute("code"));
       
   131                                 if (child.name == "status") {
       
   132                                     var code = child.get_attribute("code");
       
   133                                     if (code!=null)
       
   134                                         statuses.add(code.to_int());
       
   135                                 }
       
   136                                 child = child.next;
       
   137                             }
       
   138 
       
   139                             if (statuses.contains(110) && (_state == State.CONNECTED))
       
   140                                 log("Muc", LogLevelFlags.LEVEL_INFO, "Joined a room which I was already in: %s", from[0]);
       
   141 
       
   142                             var occupant = occupants[from[1]];
       
   143                             var item = x != null ? x.get_child("item") : null;
       
   144                             var affil = Affiliation.NONE;
       
   145                             var role = Role.NONE;
       
   146                             string nick = null;
       
   147                             if (item != null) {
       
   148                                 affil = affil_from_string(item.get_attribute("affiliation"));
       
   149                                 role = role_from_string(item.get_attribute("role")); 
       
   150                                 nick = item.get_attribute("nick");
       
   151                             } else
       
   152                                 log("Muc", LogLevelFlags.LEVEL_ERROR, "Your MUC server is shit. No role and affiliation info in presences: %s", node.to_string());
       
   153                             if (occupant != null) {
       
   154                                 if (role == Role.NONE) {
       
   155                                     occupants.unset(from[1]);
       
   156                                     log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has parted.", this.jid, from[1]);
       
   157                                     module.on_part(this, occupant);
       
   158                                     if (from[1]==this.nick) {
       
   159                                         _change_state(State.DISCONNECTED, "we became unavailable");
       
   160                                         occupants.clear();
       
   161                                     }
       
   162                                 } else {
       
   163                                     if (affil != occupant.affil) {
       
   164                                         var prev = occupant.affil;
       
   165                                         occupant.affil = affil;
       
   166                                         module.on_affil(this, occupant, prev);
       
   167                                     }
       
   168                                     if (role != occupant.role) {
       
   169                                         var prev = occupant.role;
       
   170                                         occupant.role = role;
       
   171                                         module.on_role(this, occupant, prev);
       
   172                                     }
       
   173                                     if (statuses.contains(303) && (nick!=null)) {
       
   174                                         occupants[nick] = occupant;
       
   175                                         occupants.unset(from[1]);
       
   176                                         occupant.nick = nick;
       
   177                                         module.on_nick(this, occupant, from[1]);
       
   178                                     }
       
   179                                 }
       
   180                             } else {
       
   181                                 assert( role!= Role.NONE);
       
   182                                 occupant = new Occupant();
       
   183                                 occupant.role = role;
       
   184                                 occupant.affil = affil;
       
   185                                 occupant.nick = from[1];
       
   186                                 occupants[from[1]] = occupant;
       
   187                                 occupant.isme = statuses.contains(110);
       
   188                                 log("Muc", LogLevelFlags.LEVEL_INFO, "MUC<%s> %s has joined as %s/%s.", this.jid, from[1], affil.to_string(), role.to_string());
       
   189                                 if (statuses.contains(110))
       
   190                                     _change_state(State.CONNECTED, "got own presence. we are "+this.nick);
       
   191                                 module.on_join(this, occupant);
       
   192                             }
       
   193                             
       
   194                             stdout.printf("User list: ");
       
   195                             foreach (var a in occupants.keys)
       
   196                                 stdout.printf("%s <%s,%s>, ", a, occupants[a].affil.to_string(), occupants[a].role.to_string());
       
   197                             stdout.printf("\n");
       
   198                         }
       
   199 
       
   200                         break;
       
   201                     case "error":
       
   202                         if ((from.length==1)||(node.get_attribute("id")==presence_join_id)) {
       
   203                             _change_state(State.DISCONNECTED, node.get_child("error").to_string());
       
   204                             return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
       
   205                         } else if (from.length==2) {
       
   206                             //if (from[1]==this.nick) 
       
   207                         }
       
   208                         break;
       
   209                 }
       
   210             } else if ((message.get_type()==Lm.MessageType.MESSAGE)&&(type=="groupchat")) {
       
   211                 var subj = node.get_attribute("subject");
       
   212                 if (subj != null) {
       
   213                     if (subj != this.subject) {
       
   214                         this.subject = subj;
       
   215                         module.on_subject(this);
       
   216                     }
       
   217                 }
       
   218             }
       
   219             return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
       
   220         }
       
   221         public void part(string desc) {
       
   222         }
       
   223     }
       
   224 
       
   225     public override string name() { return "muc"; }
       
   226 
       
   227     public Gee.HashMap<string,Conference> rooms;
       
   228 
       
   229     public ModuleMuc(Config cfg, Connection conn) {
       
   230         base(cfg,conn);
       
   231         this.rooms = new Gee.HashMap<string,Conference>();
       
   232         
       
   233         var muc_handler = new Lm.MessageHandler((handler, connection, message) => {
       
   234             var node = message.node;
       
   235             var from = node.get_attribute("from");
       
   236             if (from != null) {
       
   237                 var froms = from.split("/",2);
       
   238                 if (rooms.has_key(froms[0])) {
       
   239                     return rooms[froms[0]].muc_handler(handler, connection, message);
       
   240                 }
       
   241             }
       
   242             return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
       
   243         }, null);
       
   244         conn.cn.register_message_handler(muc_handler, Lm.MessageType.MESSAGE, Lm.HandlerPriority.NORMAL);
       
   245         conn.cn.register_message_handler(muc_handler, Lm.MessageType.PRESENCE, Lm.HandlerPriority.NORMAL);
       
   246         conn.cn.register_message_handler(muc_handler, Lm.MessageType.IQ, Lm.HandlerPriority.NORMAL);
       
   247         conn.state_changed.connect( (olds, news, desc) => {
       
   248             if (news == Connection.State.CONNECTED) {
       
   249                 var room = new Conference(this, "говнохост@conference.blasux.ru"); //, "Ζαλυπα");
       
   250                 room.join("Oh hai");
       
   251                 this.rooms[room.jid] = room;
       
   252             }
       
   253         });
       
   254     }
       
   255 }