added basic support for MODE, TOPIC

This commit is contained in:
Andris Reinman 2017-09-29 15:23:10 +03:00
parent 05deaab46e
commit e9923ebe12
2 changed files with 207 additions and 10 deletions

View file

@ -369,19 +369,47 @@ indexes:
# Indexes for IRC
- collection: chat
index:
name: ns_nicks
key:
_id: 1
rcpt: 1
- collection: nicks
index:
name: irc_nicks
name: ns_nicks
unique: true
key:
ns: 1
nickview: 1
- collection: nicks
index:
name: user_nick
key:
user: 1
- collection: channels
index:
name: irc_channels
name: ns_channels
unique: true
key:
ns: 1
channelview: 1
- collection: channels
index:
name: channel_members
unique: true
key:
ns: 1
channelview: 1
members: 1
- collection: channels
index:
name: user_channel
unique: true
key:
members: 1

View file

@ -122,6 +122,11 @@ class IRCConnection extends EventEmitter {
break;
}
case 'topic': {
this.send({ time: data.topicTime, source: data.topicAuthor, verb: 'TOPIC', target: data.channel, message: data.topic });
break;
}
}
};
}
@ -228,8 +233,8 @@ class IRCConnection extends EventEmitter {
}
this.lastFetchedItem = message._id;
if (message.channel && message.session.toString() === this.session.id.toString()) {
// ignore messages from self
if (message.channel && message.session.toString() === this.session.id.toString() && !this.capEnabled.has('echo-message')) {
// ignore messages from self unless echo-message
return setImmediate(processNext);
}
@ -334,7 +339,7 @@ class IRCConnection extends EventEmitter {
message = message.concat(payload.params || []);
}
if (payload.message) {
if (payload.message || typeof payload.message === 'string') {
message.push(':' + payload.message);
}
@ -542,7 +547,7 @@ class IRCConnection extends EventEmitter {
.trim()
.split(/\s+/)
.filter(arg => arg);
if (data) {
if (data || typeof data === 'string') {
params.push(data);
}
@ -618,6 +623,12 @@ class IRCConnection extends EventEmitter {
this.send({ source: this.getFormattedName(), verb: 'JOIN', target: false, message: channelData.channel });
if (channelData.topic) {
let topicTime = Math.round((channelData.topicTime || new Date()).getTime() / 1000);
this.send({ verb: 'RPL_TOPIC', params: channelData.channel, message: channelData.topic });
this.send({ verb: 'RPL_TOPICWHOTIME', params: [channelData.channel, channelData.topicAuthor, topicTime] });
}
lines.forEach(line => {
this.send({ verb: 'RPL_NAMREPLY', params: ['=', channelData.channel], message: line.members.join(' ') });
});
@ -700,7 +711,10 @@ class IRCConnection extends EventEmitter {
.project({
_id: true,
channel: true,
members: true
members: true,
topic: true,
topicTime: true,
topicAuthor: true
})
.toArray((err, channels) => {
if (err) {
@ -1124,7 +1138,10 @@ class IRCConnection extends EventEmitter {
}, {
fields: {
_id: true,
channel: true
channel: true,
topic: true,
topicTime: true,
topicAuthor: true
}
}, (err, channelData) => {
if (err) {
@ -1166,6 +1183,7 @@ class IRCConnection extends EventEmitter {
});
}
let time = new Date();
channelData = {
_id: new ObjectID(),
channel,
@ -1173,7 +1191,11 @@ class IRCConnection extends EventEmitter {
ns: this.session.ns,
mode: [],
owner: this.session.auth.id,
members: [this.session.auth.id]
members: [this.session.auth.id],
time,
topic: '',
topicTime: time,
topicAuthor: ''
};
db.database.collection('channels').insertOne(channelData, err => {
@ -1397,7 +1419,7 @@ class IRCConnection extends EventEmitter {
return next();
}
let allowed = ['sasl', 'server-time', 'znc.in/server-time'];
let allowed = ['sasl', 'server-time', 'znc.in/server-time', 'echo-message'];
this.capStarted = true;
let subcommand = params
@ -1587,6 +1609,153 @@ class IRCConnection extends EventEmitter {
return next();
}
command_MODE(tags, prefix, params, next) {
if (!this.session.user || !this.session.nick) {
this.send({ verb: 'ERR_NOTREGISTERED', params: 'JOIN', message: 'You have not registered' });
return next();
}
let channel = params[0].trim();
if (channel.length < 2 || !/^[#&]/.test(channel) || /[#&\s]/.test(channel.substr(1))) {
this.send({ verb: 'ERR_NOSUCHCHANNEL', params: channel, message: 'No such channel' });
return next();
}
if (!this.checkAuth()) {
return next();
}
if (params.length > 1) {
this.send({ verb: 'ERR_CHANOPRIVSNEEDED', params: channel, message: 'You are not channel operator' });
return next();
}
db.database.collection('channels').findOne({
ns: this.session.ns,
channelview: channel.toLowerCase().replace(/\./g, '')
}, {
fields: {
_id: true,
mode: true,
time: true
}
}, (err, channelData) => {
if (err) {
this.send({ verb: 'ERR_FILEERROR', params: channel, message: err.message });
return next();
}
if (!channelData) {
this.send({ verb: 'ERR_NOSUCHCHANNEL', params: channel, message: 'No such channel' });
return next();
}
let channelTime = Math.round((channelData.time || new Date()).getTime() / 1000);
this.send({ verb: 'RPL_CHANNELMODEIS', params: [channel, '+'] });
this.send({ verb: 'RPL_CREATIONTIME', params: [channel, channelTime] });
return next();
});
}
command_TOPIC(tags, prefix, params, next) {
if (!this.session.user || !this.session.nick) {
this.send({ verb: 'ERR_NOTREGISTERED', params: 'JOIN', message: 'You have not registered' });
return next();
}
let channel = params[0].trim();
if (channel.length < 2 || !/^[#&]/.test(channel) || /[#&\s]/.test(channel.substr(1))) {
this.send({ verb: 'ERR_NOSUCHCHANNEL', params: channel, message: 'No such channel' });
return next();
}
if (!this.checkAuth()) {
return next();
}
let newTopic = params
.slice(1)
.join(' ')
.trim();
if (params.length < 2) {
db.database.collection('channels').findOne({
ns: this.session.ns,
channelview: channel.toLowerCase().replace(/\./g, '')
}, {
fields: {
_id: true,
topic: true,
topicTime: true,
topicAuthor: true
}
}, (err, channelData) => {
if (err) {
this.send({ verb: 'ERR_FILEERROR', params: channel, message: err.message });
return next();
}
if (!channelData) {
this.send({ verb: 'ERR_NOSUCHCHANNEL', params: channel, message: 'No such channel' });
return next();
}
if (!channelData.topic) {
this.send({ verb: 'RPL_NOTOPIC', params: channel, message: 'No topic is set' });
return next();
}
let topicTime = Math.round((channelData.topicTime || new Date()).getTime() / 1000);
this.send({ verb: 'RPL_TOPIC', params: channel, message: channelData.topic });
this.send({ verb: 'RPL_TOPICWHOTIME', params: [channel, channelData.topicAuthor, topicTime] });
return next();
});
} else {
let topicTime = new Date();
let topicAuthor = this.getFormattedName();
return db.database.collection('channels').findOneAndUpdate({
ns: this.session.ns,
channelview: channel.toLowerCase().replace(/\./g, '')
}, {
$set: {
topic: newTopic,
topicAuthor,
topicTime
}
}, {
returnOriginal: false
}, (err, result) => {
if (err) {
this.send({ verb: 'ERR_FILEERROR', params: channel, message: err.message });
return next();
}
if (!result || !result.value) {
this.send({ verb: 'ERR_NOSUCHCHANNEL', params: channel, message: 'Could not open channel' });
return next();
}
let channelData = result.value;
this.publish([this.session.ns, '#', channelData._id].join('.'), {
action: 'topic',
channel: channelData.channel,
session: this.session.id.toString(),
channelId: channelData._id.toString(),
topic: newTopic,
topicAuthor,
topicTime
});
next();
});
}
}
}
module.exports = IRCConnection;