muc_log.vala
author Stiletto <blasux@blasux.ru>
Mon, 05 Nov 2012 23:54:44 +0400
changeset 12 d3e36b368fc5
parent 9 d3260253f0bd
permissions -rw-r--r--
iq, commands, makefile up

using Gee;

class ModuleMucLog : Module {
    protected weak ModuleMuc muc;
    protected HashMap<string,RoomLog> rooms;
    class RoomLog : Object {
        public string jid;
        public string logpath;
        public string filename;
        protected weak ModuleMucLog muclog;
        protected FileStream file;
        public string getid(Time time) {
            return time.format("%s");
        }
        protected int lastday;
        protected int lastyear;
        protected int lastmonth;

        public RoomLog(ModuleMucLog muclog, string jid) {
            this.jid = jid;
            this.muclog = muclog;
            logpath = muclog.getconf(jid, "log-path", null);
            filename = null;
            write(Time.local(time_t()), "logstart", "", "Log started");
        }
        ~RoomLog() {
            write(Time.local(time_t()), "logstop", "", "Log stopped");
        }

        public static uint nick_hash(string s) {
            uint sum = 0;
            const uint k = 102, m = 54, l = 140;
            for( var j = 0, iTop = s.length; j < iTop; j++ ) {
                sum += k ^ s[j];
            }
            return ((sum ^ m)+l)%10;
        }

        protected void real_write(Time time, string _class, string nick, string str) {
            var id = getid(time);
            var times = time.format("%H:%M:%S");
            var nicklass = (_class=="message") ? (" nick%u".printf(nick_hash(nick))) : "";

            file.printf("<tr id='l_%s' class='%s'><td class='time'><a id='i_%s' href='#i_%s'>%s</a></td>", id, _class, id, id, times);
            file.printf("<td class='nick%s'>%s</td><td class='text'>%s</td></tr>\n", nicklass, nick, str);
            file.flush();
        }

        public void write(Time time, string _class, string nick, string str) {
            if ((lastday != time.day)||(lastmonth != time.month)||(lastyear!=time.year)) {
                var fname = time.format(logpath).replace("<muc>",jid);
                log("muc_log", LogLevelFlags.LEVEL_INFO, "Switching log file '%s' to '%s'", filename, fname);
                bool wasntnull = false;
                if (file!=null) {
                    wasntnull = true;
                    real_write(time, "logstop", "", "Log stopped. See you in the next episode.");
                }
                //try {
                    file = FileStream.open(fname,"a");
                if (file==null) {
                    int start = 0;
                    int length = fname.length;
                    while (true) {
                        var pos = fname.index_of("/", start);
                        if (pos==-1)
                            break;
                        stderr.printf("--------------------- %s\n", fname[0:pos]);
                        Posix.mkdir(fname[0:pos],0777);
                        start = pos+1;
                    } 

                    file = FileStream.open(fname,"a");
                    if (file == null) {
                        log("muc_log", LogLevelFlags.LEVEL_WARNING, "Failed to create log file '%s'", fname);
                        return;
                    }
                }

                if (file.tell()==0) {
                    var loghead = muclog.getconf(jid, "log-head",null);
                    if (loghead!="null") {
                        try {
                            var headfile = new IOChannel.file(loghead,"r");
                            string head;
                            size_t length;
                            headfile.read_to_end(out head, out length);
                            headfile.shutdown(false);
                            file.printf("%s\n", head.replace("<muc>",jid).replace("<date>",time.format("%d.%m.%Y")));
                        } catch (FileError fe) {
                            log("muc_log", LogLevelFlags.LEVEL_WARNING, "Failed to read head");
                        }
                    }
                }
                filename = fname;
                lastyear = time.year; lastmonth = time.month; lastday = time.day;
                if (wasntnull)
                    real_write(time, "logstart", "", "Log started. This soap opera will never end.");
            }

            real_write(time, _class, nick, str);
        }
            
    }
    public string getconf(string jid, string key, string? def) {
        var res = cfg["muc "+jid, key];
        if (res==null) res = cfg["muc", key];
        if (res==null) res = def;
        return res;
    }
    public ModuleMucLog(Config cfg, Connection conn) {
        base(cfg, conn);
        rooms = new HashMap<string,RoomLog>();
        muc = null;
        foreach (var module in conn.modules) {
            if (module.name()=="muc")
                muc = (ModuleMuc)module;
        }
        if (muc==null)
            log("muc_log", LogLevelFlags.LEVEL_ERROR, "Module 'muc' is not loaded");
        muc.state_changed.connect( (conf, olds, news, desc) => {
            switch (news) {
                case ModuleMuc.State.CONNECTED:
                    if (getconf(conf.jid, "log", "no")=="yes") {
                        var room = new RoomLog(this, conf.jid); 
                        rooms[conf.jid] = room;
                        var sb = new StringBuilder();
                        var notfirst = false;
                        sb.append("Participants: ");
                        foreach (var occupant in conf.occupants.values) {
                            if (notfirst)
                                sb.append(", ");
                            else
                                notfirst=true;
                            sb.append(Markup.escape_text(occupant.nick).replace(" ","&nbsp;"));
                        }
                        room.write(Time.local(time_t()), "userlist", "", sb.str);
                    }
                    break;
                case ModuleMuc.State.DISCONNECTED:
                    rooms.unset(conf.jid);
                    break;
            }
        });
        muc.on_message.connect( (conf, user, message, body) => {
            if ((user!=null)&&(body!=null)&&(message.get_child("delay")==null)) {
                var room = rooms[conf.jid];
                if (room!=null) {
                    var nick = Markup.escape_text(user.nick).replace(" ","&nbsp;");
                    if (body.has_prefix("/me ")) {
                        room.write(Time.local(time_t()), "action", "*", nick+Markup.escape_text(body[3:body.length]).replace("\n","<br/>"));
                    } else
                        room.write(Time.local(time_t()), "message", nick, Markup.escape_text(body).replace("\n","<br/>"));
                }
            }
        });
        muc.on_join.connect( (conf, user) => {
            var room = rooms[conf.jid];
            if (room!=null) {
                var nick = Markup.escape_text(user.nick).replace(" ","&nbsp;");
                room.write(Time.local(time_t()), "join", "*", nick+" has joined the room");
            }
        });
        muc.on_part.connect( (conf, user, status) => {
            var room = rooms[conf.jid];
            if (room!=null) {
                var nick = Markup.escape_text(user.nick).replace(" ","&nbsp;");
                room.write(Time.local(time_t()), "part", "*",
                    nick+" has left the room"+((status!=null) ? (": "+Markup.escape_text(status)) : ""));
            }
        });
        muc.on_nick.connect( (conf, user, prev) => {
            var room = rooms[conf.jid];
            if (room!=null) {
                var nick = Markup.escape_text(user.nick).replace(" ","&nbsp;");
                var nickp = Markup.escape_text(prev).replace(" ","&nbsp;"); 
                room.write(Time.local(time_t()), "nickchange", "*", nickp+" is now known as "+nick);
            }
        });
    }
    public override string name() {
        return "muc_log";
    }
}