some user input support

This commit is contained in:
Evert Prants 2016-09-24 19:09:03 +03:00
parent 8b8a09ac97
commit d118c08aee
5 changed files with 357 additions and 18 deletions

View File

@ -204,6 +204,11 @@ body {
display: none; display: none;
overflow: hidden; overflow: hidden;
} }
.ircclient #chat .ircwrapper .chatarea .topicbar:hover {
height: auto;
overflow: visible;
word-wrap: break-word;
}
.ircclient #chat .ircwrapper .chatarea .letterbox { .ircclient #chat .ircwrapper .chatarea .letterbox {
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -141,6 +141,48 @@ Date.prototype.format = function (format, utc){
return format; return format;
}; };
irc.whoisMessage = function(whoisData, buffer) {
let messages = [];
for(let key in whoisData) {
switch(key) {
case "hostmask":
messages.push("<span class='hostmask'>"+whoisData[key]+"</span>: "+whoisData['realname']);
break;
case "idleSeconds":
let msgs = "is idle for "+whoisData[key]+" seconds";
if(whoisData['signonTime'])
msgs += ", signed on at "+new Date(parseInt(whoisData['signonTime'])*1000);
messages.push(msgs);
break;
case "loggedIn":
case "registered":
case "title":
messages.push(whoisData[key]);
break;
case "channels":
messages.push(whoisData[key].join(" "));
break;
case "server":
let adfd = "is on <span class='server nick'>"+whoisData[key]+"</span>";
if(whoisData['server_name'])
adfd += "&nbsp;<span class='hostmask'>"+whoisData['server_name']+"</span>";
messages.push(adfd);
break;
case "secure":
messages.push("is using a secure connection.");
break;
case "bot":
messages.push("is a bot on "+irc.serverData[buffer.server].network);
break;
}
}
for(let i in messages) {
let mesg = "[<span class='nick'>"+whoisData.nickname+"</span>]&nbsp;"+messages[i];
buffer.addMessage(mesg, null, "whois");
}
}
function rand(min, max) { function rand(min, max) {
return parseInt(Math.random() * (max-min+1), 10) + min; return parseInt(Math.random() * (max-min+1), 10) + min;
} }
@ -235,16 +277,17 @@ let composer = {
break; break;
} }
if(sender){ if(sender) {
let sndr1 = element.querySelector('.sender'); let sndr1 = element.querySelector('.sender');
let sndr2 = element.querySelectorAll('.nick');
if(sndr1) if(sndr1)
sndr1.style.color = colorizer.get_random_color(sndr1.innerHTML); sndr1.style.color = colorizer.get_random_color(sndr1.innerHTML);
else if(sndr2.length > 0) }
let sndr2 = element.querySelectorAll('.nick');
if(sndr2.length > 0)
for(let a in sndr2) for(let a in sndr2)
if(sndr2[a] && sndr2[a]['style']) if(sndr2[a] && sndr2[a]['style'])
sndr2[a].style.color = colorizer.get_random_color(sndr2[a].innerHTML); sndr2[a].style.color = colorizer.get_random_color(sndr2[a].innerHTML);
}
return element; return element;
} }
@ -479,6 +522,12 @@ class Tab {
} }
} }
setTitle(title) {
let titleEl = this.element.querySelector('#title');
if(!titleEl)
titleEl.innerHTML = title;
}
close() { close() {
this.closeRequested = true; this.closeRequested = true;
this.buffer.closeBuffer(); this.buffer.closeBuffer();
@ -499,6 +548,7 @@ class Buffer {
this.title = tabname; this.title = tabname;
this.type = type; this.type = type;
this.active = false; this.active = false;
this.alive = true;
this.tab = new Tab(this); this.tab = new Tab(this);
this.tab.renderTab(clientdom.tabby); this.tab.renderTab(clientdom.tabby);
@ -584,6 +634,14 @@ class Buffer {
this.active = false; this.active = false;
} }
setAliveStatus(status) {
this.alive = status;
if(this.alive)
this.tab.setTitle(this.title);
else
this.tab.setTitle('<i>('+this.title+')</i>');
}
closeBuffer() { closeBuffer() {
irc.chat.closeBuffer(this); irc.chat.closeBuffer(this);
} }
@ -700,15 +758,92 @@ class InputHandler {
if(!buf) return; if(!buf) return;
if(inp.trim() == "") return; if(inp.trim() == "") return;
let listargs = inp.split(' '); let listargs = inp.split(' ');
if(listargs[0].indexOf('/') == 0) if(listargs[0].indexOf('/') == 0) {
return; let cmd = listargs[0].substring(1).toLowerCase();
switch(cmd) {
case "join":
if (!listargs[1]) {
if(!buf.alive) {
irc.socket.emit("userinput", {command: "join", server: buf.server, message: "", arguments: [buf.name]});
} else {
this.commandError(buf, listargs[0].toUpperCase()+': Missing parameters!');
}
} else {
irc.socket.emit("userinput", {command: "join", server: buf.server, message: "", arguments: [listargs[1]]});
}
break;
case "part":
if (!listargs[1] && buf.type == "channel") {
inpcommand = "part";
listargs = [buf.name];
} else if(buf.type != "channel") {
this.commandError(buf, listargs[0].toUpperCase()+': Buffer is not a channel.');
} else if(listargs[1]) {
if(listargs[1].indexOf('#')) {
let msg = "";
if(listargs[2])
msg = listargs.slice(2).join(" ");
irc.socket.emit("userinput", {command: "part", server: buf.server, message: msg, arguments: [buf.name]});
} else {
if(buf.type == "channel") {
irc.socket.emit("userinput", {command: "part", server: buf.server, message: listargs.slice(1).join(" "), arguments: [buf.name]});
} else {
this.commandError(buf, listargs[0].toUpperCase()+': Buffer is not a channel.');
}
}
}
break;
case "msg":
case "privmsg":
case "say":
if(!listargs[1] || !listargs[2])
return this.commandError(buf, listargs[0].toUpperCase()+': Missing parameters!');
if(listargs[1] == '*')
listargs[1] = buf.name;
irc.socket.emit("userinput", {command: "privmsg", server: buf.server, message: listargs.slice(2).join(" "), arguments: [listargs[1]]});
break;
case "notice":
if(!listargs[1] || !listargs[2])
return this.commandError(buf, listargs[0].toUpperCase()+': Missing parameters!');
if(listargs[1] == '*')
listargs[1] = buf.name;
irc.socket.emit("userinput", {command: "notice", server: buf.server, message: listargs.slice(2).join(" "), arguments: [listargs[1]]});
break;
case "me":
case "action":
irc.socket.emit("userinput", {command: "privmsg", server: buf.server, message: "\x01ACTION "+inp.substring(cmd.length+2)+"\x01", arguments: [buf.name]});
break;
case "list":
irc.socket.emit("userinput", {command: cmd, server: buf.server, message: "", arguments: listargs});
break;
case "quote":
case "raw":
irc.socket.emit("userinput", {command: listargs[1], server: buf.server, message: listargs.slice(2).join(" "), arguments: listargs.splice(2)});
break;
case "whois":
if(!listargs[1])
return this.commandError(buf, listargs[0].toUpperCase()+': Missing parameters!');
irc.socket.emit("userinput", {command: "whois", server: buf.server, message: "", arguments: [listargs[1]]});
break;
default:
this.commandError(buf, listargs[0].toUpperCase()+': Unknown command!');
}
} else {
irc.socket.emit("userinput", {command: "privmsg", server: buf.server, message: inp, arguments: [buf.name]});
}
irc.socket.emit("userinput", {target: buf.name, targetType: buf.type, server: buf.server, message: inp, splitup: inp.split(" ")});
this.history.push(inp); this.history.push(inp);
clientdom.input.value = ""; clientdom.input.value = "";
} }
commandError(buffer, message) {
buffer.addMessage(message, null, "error");
return true;
}
} }
class IRCChatWindow { class IRCChatWindow {
@ -798,7 +933,8 @@ class IRCChatWindow {
modeTranslation: serverinfo.supportedModes, modeTranslation: serverinfo.supportedModes,
supportedPrefixes: prefixes, supportedPrefixes: prefixes,
network: serverinfo.network, network: serverinfo.network,
my_nick: serverinfo.nickname my_nick: serverinfo.nickname,
max_channel_length: serverinfo.max_channel_length
} }
let newServer = new Buffer(serverinfo.address, serverinfo.address, serverinfo.network, "server"); let newServer = new Buffer(serverinfo.address, serverinfo.address, serverinfo.network, "server");
@ -835,7 +971,9 @@ class IRCChatWindow {
closeBuffer(buffer) { closeBuffer(buffer) {
if(buffer.type == "server") return; // Don't close server buffers, lol if(buffer.type == "server") return; // Don't close server buffers, lol
if(buffer.type == "channel") console.log("TODO: PART"); if(buffer.type == "channel" && buffer.alive)
irc.socket.emit("userinput", {command: "part", server: buffer.server, message: "Tab closed", arguments: [buffer.name]});
let bufIndex = this.buffers.indexOf(buffer); let bufIndex = this.buffers.indexOf(buffer);
if(buffer.active) { if(buffer.active) {
@ -859,6 +997,11 @@ class IRCChatWindow {
if(buf == null) if(buf == null)
buf = this.createBuffer(server, name, "message", false); buf = this.createBuffer(server, name, "message", false);
if(message.type == "privmsg" && message.message.indexOf('\x01ACTION') == 0) {
message.message = message.message.substring(8);
message.type = "action";
}
buf.addMessage(message.message, message.from, message.type); buf.addMessage(message.message, message.from, message.type);
} }
@ -942,6 +1085,9 @@ class IRCChatWindow {
if(!buffer) return; if(!buffer) return;
if(user.nickname == irc.serverData[server].my_nick)
buffer.setAliveStatus(true);
buffer.addMessage("<span class='hostmask'>"+user.username+"@"+user.hostname+"</span> has joined "+channel, user.nickname, "join"); buffer.addMessage("<span class='hostmask'>"+user.username+"@"+user.hostname+"</span> has joined "+channel, user.nickname, "join");
buffer.nicklist.nickAdd(user.nickname); buffer.nicklist.nickAdd(user.nickname);
} }
@ -951,6 +1097,14 @@ class IRCChatWindow {
if(!buffer) return; if(!buffer) return;
if(user['nickname']) {
if(user.nickname == irc.serverData[server].my_nick)
buffer.setAliveStatus(false);
} else {
if(user == irc.serverData[server].my_nick)
buffer.setAliveStatus(false);
}
if(kicker) if(kicker)
buffer.addMessage("has kicked <span class='nick'>"+user+"</span> <span class='reason'>"+reason+"</span>", kicker.nickname, "part"); buffer.addMessage("has kicked <span class='nick'>"+user+"</span> <span class='reason'>"+reason+"</span>", kicker.nickname, "part");
else else
@ -986,6 +1140,32 @@ class IRCChatWindow {
} }
} }
joinChannels(server, channel) {
if (channel.indexOf(",") !== -1) {
channel = channel.trim().split(",");
for (let t in channel) {
let chan = channel[t];
channel[t] = chan.trim();
if (chan.indexOf("#") != 0) {
channel[t] = "#"+chan;
}
}
} else if(channel != "") {
channel = channel.trim();
if (channel.indexOf("#") != 0) {
channel = "#"+channel;
}
channel = [channel];
} else {
channel = [];
}
irc.socket.emit("userinput", {command: "join", server: server, message: "", arguments: channel});
}
render(buffer) { render(buffer) {
let activeNow = this.getActiveBuffer(); let activeNow = this.getActiveBuffer();
@ -1098,6 +1278,14 @@ window.onload = function() {
case "connect_message": case "connect_message":
irc.auther.authMessage(data.message, data.error); irc.auther.authMessage(data.message, data.error);
break; break;
case "whoisResponse":
irc.whoisMessage(data.whois, irc.chat.getActiveBuffer());
break;
case "listedchan":
irc.chat.messageBuffer(data.server, data.server, {message: "<span class='channel'>"+data.channel+"</span>"+
"&nbsp<span class='usercount'>"+data.users+"</span>&nbsp;<span class='topic'>"+data.topic+"</span>",
type: "listentry", from: data.from});
break;
} }
}); });
} }

View File

@ -184,6 +184,10 @@ body
padding: 12px; padding: 12px;
display: none; display: none;
overflow: hidden; overflow: hidden;
&:hover
height: auto
overflow: visible
word-wrap: break-word
.letterbox .letterbox
position: absolute; position: absolute;
top: 0; top: 0;

View File

@ -18,13 +18,55 @@ class IRCConnectionHandler {
} }
handleUserLine(data) { handleUserLine(data) {
console.log(data);
switch(data.command) {
case "kick":
case "part":
this.conn.write('{0} {1} :{2}'.format(data.command.toUpperCase(), data.arguments[0], data.message));
break;
case "nick":
case "whois":
case "who":
case "join":
this.conn.write('{0} {1}'.format(data.command.toUpperCase(), data.arguments[0]));
break;
case "quit":
this.conn.write('{0} :{1}'.format(data.command.toUpperCase(), data.message));
break;
case "privmsg":
this.conn.write('PRIVMSG {0} :{1}'.format(data.arguments[0], data.message));
this.conn.emit('pass_to_client', {type: "message", messageType: "privmsg", to: data.arguments[0],
user: {nickname: this.conn.config.nickname}, message: data.message, server: data.server});
break;
case "notice":
this.conn.write('NOTICE {0} :{1}'.format(data.arguments[0], data.message));
this.conn.emit('pass_to_client', {type: "message", messageType: "notice", to: data.arguments[0],
user: {nickname: this.conn.config.nickname}, message: data.message, server: data.server});
break;
case "list":
this.conn.write(data.command.toUpperCase());
break;
default:
this.conn.write(data.command.toUpperCase()+' '+data.message);
}
if(data.targetType == "channel" || data.targetType == "message") { if(data.targetType == "channel" || data.targetType == "message") {
this.conn.socket.write('PRIVMSG {0} :{1}\r\n'.format(data.target, data.message)); this.conn.write('PRIVMSG {0} :{1}'.format(data.target, data.message));
this.conn.emit('pass_to_client', {type: "message", messageType: "privmsg", to: data.target, this.conn.emit('pass_to_client', {type: "message", messageType: "privmsg", to: data.target,
user: {nickname: this.conn.config.nickname}, message: data.message, server: data.server}); user: {nickname: this.conn.config.nickname}, message: data.message, server: data.server});
} }
} }
whoisManage(whom, list) {
if(!this.conn.queue.whois)
this.conn.queue.whois = {};
if(!this.conn.queue.whois[whom])
this.conn.queue.whois[whom] = list;
else
for(let a in list)
this.conn.queue.whois[whom][a] = list[a];
}
handleServerLine(line) { handleServerLine(line) {
console.log(line); console.log(line);
if(this.conn.queue["supportsmsg"] && line.command != "005") { if(this.conn.queue["supportsmsg"] && line.command != "005") {
@ -33,7 +75,7 @@ class IRCConnectionHandler {
if(this.conn.config.autojoin.length > 0) if(this.conn.config.autojoin.length > 0)
for(let t in this.conn.config.autojoin) for(let t in this.conn.config.autojoin)
this.conn.socket.write('JOIN {0}\r\n'.format(this.conn.config.autojoin[t])); this.conn.write('JOIN '+this.conn.config.autojoin[t]);
this.conn.emit('authenticated', {}); this.conn.emit('authenticated', {});
} }
@ -41,6 +83,7 @@ class IRCConnectionHandler {
let serverName = this.conn.config.server; let serverName = this.conn.config.server;
let realServerName = this.conn.data.actualServer; let realServerName = this.conn.data.actualServer;
let list = null;
switch(line.command) { switch(line.command) {
case "error": case "error":
this.conn.emit("connerror", {type: "irc_error", raw: line.raw}); this.conn.emit("connerror", {type: "irc_error", raw: line.raw});
@ -67,6 +110,8 @@ class IRCConnectionHandler {
this.conn.data.supportedModes[r[b]] = aa[b]; this.conn.data.supportedModes[r[b]] = aa[b];
} else if(t[0] === 'NETWORK') { } else if(t[0] === 'NETWORK') {
this.conn.data.network = t[1]; this.conn.data.network = t[1];
} else if(t[0] === 'CHANNELLEN') {
this.conn.data.max_channel_length = parseInt(t[1]);
} }
this.conn.data.serverSupports[t[0]] = t[1]; this.conn.data.serverSupports[t[0]] = t[1];
@ -113,9 +158,7 @@ class IRCConnectionHandler {
let type = "privmsg"; let type = "privmsg";
if(line.trailing.indexOf('\x01ACTION') == 0) { if(line.trailing.indexOf('\x01ACTION') == 0) {
line.trailing = line.trailing.substring(8); // TODO: remove once proper CTCP handling is done;
line.trailing.substring(0, line.trailing.length-1);
type = "action";
} else if(line.trailing.indexOf('\x01') == 0) { } else if(line.trailing.indexOf('\x01') == 0) {
// TODO: handle CTCPs // TODO: handle CTCPs
return; return;
@ -135,6 +178,9 @@ class IRCConnectionHandler {
this.conn.emit('pass_to_client', {type: "server_message", messageType: "notice", message: line.trailing, server: serverName, from: realServerName}); this.conn.emit('pass_to_client', {type: "server_message", messageType: "notice", message: line.trailing, server: serverName, from: realServerName});
break; break;
case "NICK": case "NICK":
if(line.user.nickname == this.conn.config.nickname)
this.conn.config.nickname = line.arguments[0];
this.conn.emit('pass_to_client', {type: "nick_change", nick: line.user.nickname, newNick: line.arguments[0], server: serverName}); this.conn.emit('pass_to_client', {type: "nick_change", nick: line.user.nickname, newNick: line.arguments[0], server: serverName});
break; break;
case "KICK": case "KICK":
@ -155,6 +201,8 @@ class IRCConnectionHandler {
this.conn.emit('pass_to_client', {type: "server_message", messageType: "motd", message: line.trailing, server: serverName, from: realServerName}); this.conn.emit('pass_to_client', {type: "server_message", messageType: "motd", message: line.trailing, server: serverName, from: realServerName});
break; break;
case "251": case "251":
case "290":
case "292":
case "255": case "255":
this.conn.emit('pass_to_client', {type: "server_message", messageType: "regular", message: line.trailing, server: serverName, from: realServerName}); this.conn.emit('pass_to_client', {type: "server_message", messageType: "regular", message: line.trailing, server: serverName, from: realServerName});
break; break;
@ -191,6 +239,91 @@ class IRCConnectionHandler {
this.conn.emit('pass_to_client', {type: "mode", target: line.arguments[0], message: line.arguments.slice(1).join(" "), this.conn.emit('pass_to_client', {type: "mode", target: line.arguments[0], message: line.arguments.slice(1).join(" "),
server: serverName, user: line.user}); server: serverName, user: line.user});
break; break;
case "433":
let newNick = this.conn.config.nickname + "_";
this.conn.write('NICK '+newNick);
this.conn.config.nickname = newNick;
break;
case "401":
case "402":
this.conn.emit('pass_to_client', {type: "message", to: line.arguments[1], message: line.trailing,
server: serverName, user: {nickname: realServerName}, messageType: "error"});
break;
case "311":
// start whois queue
list = {
nickname: line.arguments[1],
hostmask: "{0}!{1}@{2}".format(line.arguments[1], line.arguments[2], line.arguments[3]),
realname: line.trailing || ""
};
this.whoisManage(line.arguments[1], list);
break;
case "319":
// whois: channels
list = {
channels: line.trailing.split(" "),
};
this.whoisManage(line.arguments[1], list);
break;
case "312":
list = {
server: line.arguments[2],
server_name: line.trailing || ""
}
this.whoisManage(line.arguments[1], list);
break;
case "313":
list = {
title: line.trailing
}
this.whoisManage(line.arguments[1], list);
break;
case "330":
list = {
loggedIn: line.trailing+' '+line.arguments[2]
}
this.whoisManage(line.arguments[1], list);
break;
case "335":
list = {
bot: true
}
this.whoisManage(line.arguments[1], list);
break;
case "307":
list = {
registered: line.trailing
}
this.whoisManage(line.arguments[1], list);
break;
case "671":
list = {
secure: true
}
this.whoisManage(line.arguments[1], list);
break;
case "317":
list = {
signonTime: line.arguments[3],
idleSeconds: line.arguments[2]
}
this.whoisManage(line.arguments[1], list);
break;
case "318":
if(!this.conn.queue.whois || !this.conn.queue.whois[line.arguments[1]])
return;
this.conn.emit('pass_to_client', {type: "whoisResponse", whois: this.conn.queue.whois[line.arguments[1]],
server: serverName, from: realServerName});
delete this.conn.queue.whois[line.arguments[1]];
break;
case "321":
this.conn.emit('pass_to_client', {type: "listedchan", channel: "Channel", users: "Users", topic: "Topic",
server: serverName, from: realServerName});
break;
case "322":
this.conn.emit('pass_to_client', {type: "listedchan", channel: line.arguments[1], users: line.arguments[2], topic: line.trailing,
server: serverName, from: realServerName});
break;
} }
} }
} }
@ -225,6 +358,7 @@ class IRCConnection extends EventEmitter {
serverSupports: {}, serverSupports: {},
network: this.config.server, network: this.config.server,
actualServer: this.config.server, actualServer: this.config.server,
max_channel_length: 64,
supportedModes: {} supportedModes: {}
}; };
this.queue = {}; this.queue = {};
@ -297,7 +431,7 @@ class IRCConnection extends EventEmitter {
disconnect(message) { disconnect(message) {
if(!this.connected) { if(!this.connected) {
this.emit('error', {type: "sock_closed", message: "Connection already closed."}); this.emit('connerror', {type: "sock_closed", message: "Connection already closed."});
return; return;
} }
@ -305,6 +439,13 @@ class IRCConnection extends EventEmitter {
this.socket.write('QUIT :{0}\r\n'.format(message != null ? message : configuration.client.default_quit_msg)); this.socket.write('QUIT :{0}\r\n'.format(message != null ? message : configuration.client.default_quit_msg));
} }
write(message) {
if(!this.connected) {
this.emit('connerror', {type: "sock_closed", message: "Connection is closed."});
return;
}
this.socket.write(message+'\r\n');
}
} }

View File

@ -71,7 +71,8 @@ io.sockets.on('connection', function (socket) {
newConnection.on('authenticated', () => { newConnection.on('authenticated', () => {
socket.emit('act_client', {type: "event_connect", address: connectiondata.server, network: newConnection.data.network, socket.emit('act_client', {type: "event_connect", address: connectiondata.server, network: newConnection.data.network,
supportedModes: newConnection.data.supportedModes, nickname: newConnection.config.nickname}); supportedModes: newConnection.data.supportedModes, nickname: newConnection.config.nickname,
max_channel_length: newConnection.data.max_channel_length});
}); });
newConnection.on('connerror', (data) => { newConnection.on('connerror', (data) => {