comparison mod_ircd/mod_ircd.in.lua @ 470:4f9224369e69

mod_ircd: merged in various changes including -- code to propagate aff/role changes as modes, topic hooks/command, scarecrow motd banner, default port.
author Marco Cirillo <maranda@lightwitch.org>
date Tue, 01 Nov 2011 23:41:02 +0000
parents ff03a325aa41
children fffac0eef024
comparison
equal deleted inserted replaced
469:ff03a325aa41 470:4f9224369e69
127 end 127 end
128 local function muc2irc(room) 128 local function muc2irc(room)
129 local channel, _, nick = jid.split(room); 129 local channel, _, nick = jid.split(room);
130 return "#"..channel, nick; 130 return "#"..channel, nick;
131 end 131 end
132 local rolemap = { 132 local role_map = {
133 moderator = "@", 133 moderator = "@",
134 participant = "+", 134 participant = "",
135 visitor = "",
136 none = ""
135 } 137 }
136 local modemap = { 138 local aff_map = {
137 moderator = "o", 139 owner = "~",
138 participant = "v", 140 administrator = "&",
141 member = "+",
142 none = ""
139 } 143 }
140 144 local role_modemap = {
141 local irc_listener = { default_port = 6667, default_mode = "*l" }; 145 moderator = "o",
146 participant = "",
147 visitor = "",
148 none = ""
149 }
150 local aff_modemap = {
151 owner = "q",
152 administrator = "a",
153 member = "v",
154 none = ""
155 }
156
157 local irc_listener = { default_port = 7000, default_mode = "*l" };
142 158
143 local sessions = {}; 159 local sessions = {};
144 local jids = {}; 160 local jids = {};
145 local commands = {}; 161 local commands = {};
146 162
229 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; 245 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"};
230 return; 246 return;
231 end 247 end
232 local full_jid = jid.join(nick, component_jid, "ircd"); 248 local full_jid = jid.join(nick, component_jid, "ircd");
233 jids[full_jid] = session; 249 jids[full_jid] = session;
250 jids[full_jid]["ar_last"] = {};
234 nicks[nick] = session; 251 nicks[nick] = session;
235 session.nick = nick; 252 session.nick = nick;
236 session.full_jid = full_jid; 253 session.full_jid = full_jid;
237 session.type = "c2s"; 254 session.type = "c2s";
238 session.send{from = muc_server, "001", nick, "Welcome to IRC gateway to XMPP!"}; 255
239 session.send{from = muc_server, "002", nick, module.host.." running Prosody "..prosody.version}; 256 session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick};
257 session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version};
240 session.send{from = muc_server, "003", nick, os.date(nil, prosody.start_time)} 258 session.send{from = muc_server, "003", nick, os.date(nil, prosody.start_time)}
241 session.send{from = muc_server, "004", table.concat({muc_server, "alpha", "i", "ov"}, " ")}; 259 session.send{from = muc_server, "004", table.concat({muc_server, "alpha", "i", "aoqv"}, " ")};
242 session.send{from = nick, "MODE", nick, "+i"}; -- why 260 session.send{from = muc_server, "375", nick, "- "..muc_server.." Message of the day -"};
261 session.send{from = muc_server, "372", nick, "-"};
262 session.send{from = muc_server, "372", nick, "- Please be warned that this is only a partial irc implementation,"};
263 session.send{from = muc_server, "372", nick, "- it's made to facilitate users transiting away from irc to XMPP."};
264 session.send{from = muc_server, "372", nick, "-"};
265 session.send{from = muc_server, "372", nick, "- Prosody is _NOT_ an IRC Server and it never will."};
266 session.send{from = muc_server, "372", nick, "- We also would like to remind you that this plugin is provided as is,"};
267 session.send{from = muc_server, "372", nick, "- it's still an Alpha and it's still a work in progress, use it at your sole"};
268 session.send{from = muc_server, "372", nick, "- risk as there's a not so little chance something will break."};
269
270 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting,
271 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set")
243 end 272 end
244 273
245 function commands.USER(session, params) 274 function commands.USER(session, params)
246 -- FIXME 275 -- FIXME
247 -- Empty command for now 276 -- Empty command for now
277 end
278
279 local function mode_map(am, rm, nicks)
280 local rnick;
281 local c_modes;
282 c_modes = aff_modemap[am]..role_modemap[rm]
283 rnick = string.rep(nicks.." ", c_modes:len())
284 if c_modes == "" then return nil, nil end
285 return c_modes, rnick
248 end 286 end
249 287
250 function commands.JOIN(session, args) 288 function commands.JOIN(session, args)
251 local channel = args[1]; 289 local channel = args[1];
252 if not channel then return end 290 if not channel then return end
253 local room_jid = irc2muc(channel); 291 local room_jid = irc2muc(channel);
254 print(session.full_jid); 292 print(session.full_jid);
293 if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end
255 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } ); 294 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } );
256 if not room then 295 if not room then
257 return ":"..session.host.." ERR :Could not join room: "..err 296 return ":"..muc_server.." ERR :Could not join room: "..err
258 end 297 end
259 session.rooms[channel] = room; 298 session.rooms[channel] = room;
260 room.channel = channel; 299 room.channel = channel;
261 room.session = session; 300 room.session = session;
262 session.send{from=session.nick, "JOIN", channel}; 301 session.send{from=session.nick, "JOIN", channel};
263 session.send{from=muc_server, 332, session.nick, channel ,"Connection in progress..."}; 302 if room.subject then
264 303 session.send{from=muc_server, 332, session.nick, channel ,room.subject};
304 end
305 commands.NAMES(session, channel);
306
307 room:hook("subject-changed", function(changed)
308 session.send((":%s TOPIC %s :%s"):format(changed.by, channel, changed.to or ""));
309 end);
310
265 room:hook("message", function(event) 311 room:hook("message", function(event)
266 if not event.body then return end 312 if not event.body then return end
267 local nick, body = event.nick, event.body; 313 local nick, body = event.nick, event.body;
268 if nick ~= session.nick then 314 if nick ~= session.nick then
269 if body:sub(1,4) == "/me " then 315 if body:sub(1,4) == "/me " then
272 local type = event.stanza.attr.type; 318 local type = event.stanza.attr.type;
273 session.send{from=nick, "PRIVMSG", type == "groupchat" and channel or nick, body}; 319 session.send{from=nick, "PRIVMSG", type == "groupchat" and channel or nick, body};
274 --FIXME PM's probably won't work 320 --FIXME PM's probably won't work
275 end 321 end
276 end); 322 end);
323
324 room:hook("presence", function(ar)
325 local c_modes;
326 local rnick;
327 if ar.nick and not jids[session.full_jid].ar_last[ar.room_jid][ar.nick] then jids[session.full_jid].ar_last[ar.room_jid][ar.nick] = {} end
328 local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user")
329 if x_ar then
330 local xar_item = x_ar:get_child("item")
331 if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then
332 if xar_item.attr.affiliation and xar_item.attr.role then
333 if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and
334 not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] then
335 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation
336 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role
337 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick);
338 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end
339 else
340 c_modes, rnick = mode_map(jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"], jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"], ar.nick);
341 if c_modes and rnick then session.send((":%s MODE %s -%s"):format(muc_server, channel, c_modes.." "..rnick)); end
342 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation
343 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role
344 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick);
345 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end
346 end
347 end
348 end
349 end
350 end, -1);
277 end 351 end
278 352
279 c:hook("groupchat/joined", function(room) 353 c:hook("groupchat/joined", function(room)
280 local session = room.session or jids[room.opts.source]; 354 local session = room.session or jids[room.opts.source];
281 local channel = room.channel; 355 local channel = "#"..room.jid:match("^(.*)@");
282 session.send{from=session.nick.."!"..session.nick, "JOIN", channel}; 356 session.send{from=session.nick.."!"..session.nick, "JOIN", channel};
283 session.send((":%s!%s JOIN %s :"):format(session.nick, session.nick, channel));
284 if room.topic then 357 if room.topic then
285 session.send{from=muc_server, 332, room.topic}; 358 session.send{from=muc_server, 332, room.topic};
286 end 359 end
287 commands.NAMES(session, channel) 360 commands.NAMES(session, channel)
288 if session.nick.role then
289 session.send{from=muc_server, "MODE", channel, session.nick, modemap[session.nick.role], session.nick}
290 end
291 room:hook("occupant-joined", function(nick) 361 room:hook("occupant-joined", function(nick)
292 session.send{from=nick.nick.."!"..nick.nick, "JOIN", channel}; 362 session.send{from=nick.nick.."!"..nick.nick, "JOIN", channel};
293 if nick.role and modemap[nick.role] then
294 session.send{from=nick.nick.."!"..nick.nick, "MODE", channel, modemap[nick.role], nick.nick};
295 end
296 end); 363 end);
297 room:hook("occupant-left", function(nick) 364 room:hook("occupant-left", function(nick)
298 session.send{from=nick.nick.."!"..nick.nick, "PART", room.channel}; 365 jids[session.full_jid].ar_last[nick.room_jid][nick.nick] = nil;
366 session.send{from=nick.nick.."!"..nick.nick, "PART", channel};
299 end); 367 end);
300 end); 368 end);
301 369
302 function commands.NAMES(session, channel) 370 function commands.NAMES(session, channel)
303 local nicks = { }; 371 local nicks = { };
304 local room = session.rooms[channel]; 372 local room = session.rooms[channel];
373 local symbols_map = {
374 owner = "~",
375 administrator = "&",
376 moderator = "@",
377 member = "+"
378 }
379
305 if not room then return end 380 if not room then return end
306 -- TODO Break this out into commands.NAMES 381 -- TODO Break this out into commands.NAMES
307 for nick, n in pairs(room.occupants) do 382 for nick, n in pairs(room.occupants) do
308 if n.role and rolemap[n.role] then 383 if n.affiliation == "owner" and n.role == "moderator" then
309 nick = rolemap[n.role] .. nick; 384 nick = symbols_map[n.affiliation]..nick;
385 elseif n.affiliation == "administrator" and n.role == "moderator" then
386 nick = symbols_map[n.affiliation]..nick;
387 elseif n.affiliation == "member" and n.role == "moderator" then
388 nick = symbols_map[n.role]..nick;
389 elseif n.affiliation == "member" and n.role == "partecipant" then
390 nick = symbols_map[n.affiliation]..nick;
391 elseif n.affiliation == "none" and n.role == "moderator" then
392 nick = symbols_map[n.role]..nick;
310 end 393 end
311 table.insert(nicks, nick); 394 table.insert(nicks, nick);
312 end 395 end
313 nicks = table.concat(nicks, " "); 396 nicks = table.concat(nicks, " ");
314 session.send((":%s 353 %s = %s :%s"):format(session.host, session.nick, channel, nicks)); 397 session.send((":%s 353 %s = %s :%s"):format(muc_server, session.nick, channel, nicks));
315 session.send((":%s 366 %s %s :End of /NAMES list."):format(session.host, session.nick, channel)); 398 session.send((":%s 366 %s %s :End of /NAMES list."):format(muc_server, session.nick, channel));
316 session.send(":"..session.host.." 353 "..session.nick.." = "..channel.." :"..nicks); 399 session.send(":"..muc_server.." 353 "..session.nick.." = "..channel.." :"..nicks);
317 end 400 end
318 401
319 function commands.PART(session, args) 402 function commands.PART(session, args)
320 local channel, part_message = unpack(args); 403 local channel, part_message = unpack(args);
404 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil;
405 if not room then return end
321 channel = channel:match("^([%S]*)"); 406 channel = channel:match("^([%S]*)");
322 session.rooms[channel]:leave(part_message); 407 session.rooms[channel]:leave(part_message);
408 jids[session.full_jid].ar_last[room.."@"..muc_server] = nil;
323 session.send(":"..session.nick.." PART :"..channel); 409 session.send(":"..session.nick.." PART :"..channel);
324 end 410 end
325 411
326 function commands.PRIVMSG(session, args) 412 function commands.PRIVMSG(session, args)
327 local channel, message = unpack(args); 413 local channel, message = unpack(args);
357 443
358 function commands.PING(session, args) 444 function commands.PING(session, args)
359 session.send{from=muc_server, "PONG", args[1]}; 445 session.send{from=muc_server, "PONG", args[1]};
360 end 446 end
361 447
448 function commands.TOPIC(session, message)
449 if not message then return end
450 local channel, topic = message:match("^(%S+) :(.*)$");
451 if not channel then
452 channel = message:match("^(%S+)");
453 end
454 if not channel then return end
455 local room = session.rooms[channel];
456 if topic then
457 room:set_subject(topic)
458 session.send((":%s TOPIC %s :%s"):format(session.nick, channel, room.subject or ""));
459 else
460 session.send((":%s TOPIC %s :%s"):format(session.nick, channel, room.subject or ""));
461 end
462 end
463
362 function commands.WHO(session, args) 464 function commands.WHO(session, args)
363 local channel = args[1]; 465 local channel = args[1];
364 if session.rooms[channel] then 466 if session.rooms[channel] then
365 local room = session.rooms[channel] 467 local room = session.rooms[channel]
366 for nick in pairs(room.occupants) do 468 for nick in pairs(room.occupants) do
410 module:hook("module-unloaded", desetup) 512 module:hook("module-unloaded", desetup)
411 513
412 514
413 --print("Starting loop...") 515 --print("Starting loop...")
414 --verse.loop() 516 --verse.loop()
415
416