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;
}
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);
if (file!=null) file.flush();
//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;
}
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 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(" "," "));
}
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(" "," ");
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(" "," ");
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(" "," ");
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(" "," ");
var nickp = Markup.escape_text(prev).replace(" "," ");
room.write(Time.local(time_t()), "nickchange", "*", nickp+" is now known as "+nick);
}
});
}
public override string name() {
return "muc_log";
}
}