--- a/.hgignore Mon Nov 05 23:53:52 2012 +0400
+++ b/.hgignore Mon Nov 05 23:54:44 2012 +0400
@@ -1,4 +1,7 @@
^config.ini$
^iswydt-bot$
-.swp$
+.sw[op]$
^cfiles/
+^logs/.*.html$
+^test/
+^version.vala$
--- a/Makefile Mon Nov 05 23:53:52 2012 +0400
+++ b/Makefile Mon Nov 05 23:54:44 2012 +0400
@@ -5,11 +5,14 @@
VALAONLYLIBS := posix
VALALIBS := $(patsubst %, --pkg %, $(LIBS) $(VALAONLYLIBS))
VFLAGS = -g
+SOURCES = iswydt.vala config.vala simple_iq.vala muc.vala muc_log.vala muc_commands.vala version.vala
-iswydt-bot: iswydt.vala config.vala muc.vala muc_log.vala
+version.vala: .
+ echo "static const string IswydtVersion = \"$$(hg id -bnit)\";" > version.vala
+iswydt-bot: $(SOURCES)
$(VALAC) $(VFLAGS) $(VALALIBS) -o $@ $^
-cfiles: iswydt.vala config.vala muc.vala muc_log.vala
+cfiles: $(SOURCES)
$(VALAC) $(VFLAGS) $(VALALIBS) -C $^
mkdir -p cfiles
mv *.c cfiles/
--- a/iswydt.vala Mon Nov 05 23:53:52 2012 +0400
+++ b/iswydt.vala Mon Nov 05 23:54:44 2012 +0400
@@ -167,7 +167,9 @@
});
checktimer.attach(loop.get_context());
+ account.add_module(new ModuleIq(cfg, account));
account.add_module(new ModuleMuc(cfg, account));
+ account.add_module(new ModuleMucCommands(cfg, account));
account.add_module(new ModuleMucLog(cfg, account));
Posix.sigaction_t action = Posix.sigaction_t();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/muc_commands.vala Mon Nov 05 23:54:44 2012 +0400
@@ -0,0 +1,94 @@
+using Gee;
+
+class ModuleMucCommands : Module {
+ protected weak ModuleMuc muc;
+ public delegate void CommandHandler(ModuleMuc.Conference conf, ModuleMuc.Occupant user, string command, string? arguments);
+ public class Command : Object {
+ public weak Module mod;
+ public CommandHandler hnd;
+ }
+ protected HashMap<string,Command> commands;
+
+ 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 bool register(string command, Module mod, CommandHandler handler) {
+ var cmd = new Command();
+ cmd.mod = mod; /* this will cause "copying delegates is discouraged" warning. that's okay, this delegate's data needs no destroy notify */
+ cmd.hnd = handler;
+ if (commands.has_key(command))
+ log("muc_commands", LogLevelFlags.LEVEL_WARNING, "Command '%s' defined by '%s' was redefined by '%s'.",
+ command, commands[command].mod.name(), mod.name());
+ commands[command] = cmd;
+ return true;
+ }
+ public bool unregister(string command) {
+ return commands.unset(command);
+ }
+
+ protected void hnd_modules(ModuleMuc.Conference conf, ModuleMuc.Occupant user, string command, string? arguments) {
+ var sb = new StringBuilder();
+ sb.append("Loaded modules:");
+ muc.name();
+ foreach (var mod in conn.modules)
+ sb.append(" "+mod.name());
+ user.public_message(sb.str);
+ }
+
+ protected void hnd_status(ModuleMuc.Conference conf, ModuleMuc.Occupant user, string command, string? arguments) {
+ var sb = new StringBuilder();
+ sb.append("Status:");
+ foreach (var room in muc.rooms.values) {
+ sb.append(" ");
+ sb.append(room.jid.split("@",2)[0]);
+ sb.append("[");
+ sb.append(room.enabled ? "EN":"DIS");
+ sb.append(":");
+ switch (room.state) {
+ case ModuleMuc.State.CONNECTED: sb.append("C"); break;
+ case ModuleMuc.State.DISCOVERING: sb.append("DS"); break;
+ case ModuleMuc.State.CONNECTING: sb.append(">C"); break;
+ case ModuleMuc.State.DISCONNECTING: sb.append(">D"); break;
+ case ModuleMuc.State.DISCONNECTED: sb.append("D"); break;
+ }
+ sb.append("]");
+ }
+ user.public_message(sb.str);
+ }
+
+ public ModuleMucCommands(Config cfg, Connection conn) {
+ base(cfg, conn);
+ commands = new HashMap<string,Command>();
+ 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.on_message.connect( (conf, user, message, body) => {
+ if ((user!=null)&&(body!=null)&&(message.get_child("delay")==null)) {
+ if (getconf(conf.jid, "commands", "no")!="yes")
+ return;
+ if (body.has_prefix(conf.nick)) {
+ var txt = body[conf.nick.length:body.length];
+ if (txt.has_prefix(": ")||txt.has_prefix(", ")) {
+ var command_n_args = txt[2:txt.length].split(" ",2);
+ var command = commands[command_n_args[0]];
+ if (command!=null)
+ command.hnd(conf, user, command_n_args[0], (command_n_args.length > 1) ? command_n_args[1] : null);
+ }
+ }
+ }
+ });
+
+ register("modules", this, hnd_modules);
+ register("status", this, hnd_status);
+ }
+ public override string name() {
+ return "muc_commands";
+ }
+}
--- a/muc_log.vala Mon Nov 05 23:53:52 2012 +0400
+++ b/muc_log.vala Mon Nov 05 23:54:44 2012 +0400
@@ -35,12 +35,26 @@
}
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);
- if (file!=null) file.flush();
+ 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) {
@@ -79,15 +93,11 @@
}
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.");
}
-
- 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();
+ real_write(time, _class, nick, str);
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/simple_iq.vala Mon Nov 05 23:54:44 2012 +0400
@@ -0,0 +1,41 @@
+using Gee, Lm;
+
+class ModuleIq : Module {
+ const string NS_IQ_VERSION = "jabber:iq:version";
+ public static delegate void Callback(string id, MessageNode request, MessageNode response);
+ protected HashMap<string, Callback> callbacks;
+ public ModuleIq(Config cfg, Connection conn) {
+ base(cfg, conn);
+ var iq_handler = new Lm.MessageHandler((handler, connection, message) => {
+ var node = message.node;
+
+ var query = node.get_child("query");
+ if (query == null) return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
+
+ var from = node.get_attribute("from");
+ var id = node.get_attribute("id");
+
+ var response = new Lm.Message(from, Lm.MessageType.IQ);
+ response.node.set_attribute("type","result");
+ if (id!=null)
+ response.node.set_attribute("id",id);
+ var rquery = response.node.add_child("query",null);
+
+ switch (query.get_attribute("xmlns")) {
+ case NS_IQ_VERSION:
+ rquery.set_attribute("xmlns", NS_IQ_VERSION);
+ rquery.add_child("name", "iswydt");
+ rquery.add_child("version", IswydtVersion);
+ stderr.printf("RQ: %s\n",rquery.to_string());
+ stderr.printf("RS: %s\n",response.node.to_string());
+ this.conn.cn.send(response);
+ return Lm.HandlerResult.REMOVE_MESSAGE;
+ }
+ return Lm.HandlerResult.ALLOW_MORE_HANDLERS;
+ }, null);
+ conn.cn.register_message_handler(iq_handler, Lm.MessageType.IQ, Lm.HandlerPriority.NORMAL);
+ }
+ public override string name() {
+ return "iq";
+ }
+}