Merge pull request #2197 from RockyTV/rockytv
Add support for /ignore, /unignore and /ignorelist commands
This commit is contained in:
commit
d185a78af7
@ -1248,15 +1248,18 @@ kbd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#chat table.channel-list,
|
#chat table.channel-list,
|
||||||
#chat table.ban-list {
|
#chat table.ban-list,
|
||||||
|
#chat table.ignore-list {
|
||||||
margin: 5px 10px;
|
margin: 5px 10px;
|
||||||
width: calc(100% - 30px);
|
width: calc(100% - 30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat table.channel-list th,
|
#chat table.channel-list th,
|
||||||
#chat table.ban-list th,
|
#chat table.ban-list th,
|
||||||
|
#chat table.ignore-list th,
|
||||||
#chat table.channel-list td,
|
#chat table.channel-list td,
|
||||||
#chat table.ban-list td {
|
#chat table.ban-list td,
|
||||||
|
#chat.table.ignore-list td {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
border-bottom: #eee 1px solid;
|
border-bottom: #eee 1px solid;
|
||||||
@ -1270,7 +1273,9 @@ kbd {
|
|||||||
#chat table.channel-list .topic,
|
#chat table.channel-list .topic,
|
||||||
#chat table.ban-list .hostmask,
|
#chat table.ban-list .hostmask,
|
||||||
#chat table.ban-list .banned_by,
|
#chat table.ban-list .banned_by,
|
||||||
#chat table.ban-list .banned_at {
|
#chat table.ban-list .banned_at,
|
||||||
|
#chat table.ignore-list .hostmask,
|
||||||
|
#chat table.ignore-list .when {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ const commands = [
|
|||||||
"/expand",
|
"/expand",
|
||||||
"/ho",
|
"/ho",
|
||||||
"/hs",
|
"/hs",
|
||||||
|
"/ignore",
|
||||||
|
"/ignorelist",
|
||||||
"/invite",
|
"/invite",
|
||||||
"/join",
|
"/join",
|
||||||
"/kick",
|
"/kick",
|
||||||
@ -64,6 +66,7 @@ const commands = [
|
|||||||
"/slap",
|
"/slap",
|
||||||
"/topic",
|
"/topic",
|
||||||
"/unban",
|
"/unban",
|
||||||
|
"/unignore",
|
||||||
"/voice",
|
"/voice",
|
||||||
"/whois",
|
"/whois",
|
||||||
];
|
];
|
||||||
|
@ -293,6 +293,23 @@ function addJoinItem() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addIgnoreListItem() {
|
||||||
|
function ignorelist(itemData) {
|
||||||
|
socket.emit("input", {
|
||||||
|
target: parseInt(itemData, 10),
|
||||||
|
text: "/ignorelist",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addContextMenuItem({
|
||||||
|
check: (target) => target.hasClass("lobby"),
|
||||||
|
className: "list",
|
||||||
|
displayName: "List ignored users",
|
||||||
|
data: (target) => target.data("id"),
|
||||||
|
callback: ignorelist,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function addDefaultItems() {
|
function addDefaultItems() {
|
||||||
addWhoisItem();
|
addWhoisItem();
|
||||||
addQueryItem();
|
addQueryItem();
|
||||||
@ -306,4 +323,5 @@ function addDefaultItems() {
|
|||||||
addChannelListItem();
|
addChannelListItem();
|
||||||
addBanListItem();
|
addBanListItem();
|
||||||
addJoinItem();
|
addJoinItem();
|
||||||
|
addIgnoreListItem();
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ function processReceivedMessage(data) {
|
|||||||
const container = channel.find(".messages");
|
const container = channel.find(".messages");
|
||||||
const activeChannelId = chat.find(".chan.active").data("id");
|
const activeChannelId = chat.find(".chan.active").data("id");
|
||||||
|
|
||||||
if (data.msg.type === "channel_list" || data.msg.type === "ban_list") {
|
if (data.msg.type === "channel_list" || data.msg.type === "ban_list" || data.msg.type === "ignore_list") {
|
||||||
$(container).empty();
|
$(container).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
client/views/actions/ignore_list.tpl
Normal file
16
client/views/actions/ignore_list.tpl
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<table class="ignore-list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="hostmask">Hostmask</th>
|
||||||
|
<th class="when">Ignored At</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each ignored}}
|
||||||
|
<tr>
|
||||||
|
<td class="hostmask">{{hostmask}}</td>
|
||||||
|
<td class="when">{{{localetime when}}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
@ -360,6 +360,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="help-item">
|
||||||
|
<div class="subject">
|
||||||
|
<code>/ignore nick</code>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Block any messages from the specified user on the current network.
|
||||||
|
This can be a nickname or a hostmask.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="help-item">
|
||||||
|
<div class="subject">
|
||||||
|
<code>/ignorelist</code>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<p>Load the list of ignored users for the current network.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="help-item">
|
<div class="help-item">
|
||||||
<div class="subject">
|
<div class="subject">
|
||||||
<code>/join channel</code>
|
<code>/join channel</code>
|
||||||
@ -537,6 +557,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="help-item">
|
||||||
|
<div class="subject">
|
||||||
|
<code>/unignore nick</code>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Unblock messages from the specified user on the current network.
|
||||||
|
This can be a nickname or a hostmask.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="help-item">
|
<div class="help-item">
|
||||||
<div class="subject">
|
<div class="subject">
|
||||||
<code>/voice nick [...nick]</code>
|
<code>/voice nick [...nick]</code>
|
||||||
|
@ -48,6 +48,7 @@ const inputs = [
|
|||||||
"away",
|
"away",
|
||||||
"connect",
|
"connect",
|
||||||
"disconnect",
|
"disconnect",
|
||||||
|
"ignore",
|
||||||
"invite",
|
"invite",
|
||||||
"kick",
|
"kick",
|
||||||
"mode",
|
"mode",
|
||||||
|
@ -33,6 +33,8 @@ const Helper = {
|
|||||||
ip2hex,
|
ip2hex,
|
||||||
mergeConfig,
|
mergeConfig,
|
||||||
getDefaultNick,
|
getDefaultNick,
|
||||||
|
parseHostmask,
|
||||||
|
compareHostmask,
|
||||||
|
|
||||||
password: {
|
password: {
|
||||||
hash: passwordHash,
|
hash: passwordHash,
|
||||||
@ -226,3 +228,43 @@ function mergeConfig(oldConfig, newConfig) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseHostmask(hostmask) {
|
||||||
|
let nick = "";
|
||||||
|
let ident = "*";
|
||||||
|
let hostname = "*";
|
||||||
|
let parts = [];
|
||||||
|
|
||||||
|
// Parse hostname first, then parse the rest
|
||||||
|
parts = hostmask.split("@");
|
||||||
|
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
hostname = parts[1] || "*";
|
||||||
|
hostmask = parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname = hostname.toLowerCase();
|
||||||
|
|
||||||
|
parts = hostmask.split("!");
|
||||||
|
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
ident = parts[1] || "*";
|
||||||
|
hostmask = parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
ident = ident.toLowerCase();
|
||||||
|
|
||||||
|
nick = hostmask.toLowerCase() || "*";
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
nick: nick,
|
||||||
|
ident: ident,
|
||||||
|
hostname: hostname,
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareHostmask(a, b) {
|
||||||
|
return (a.nick.toLowerCase() === b.nick.toLowerCase() || a.nick === "*") && (a.ident.toLowerCase() === b.ident.toLowerCase() || a.ident === "*") && (a.hostname.toLowerCase() === b.hostname.toLowerCase() || a.hostname === "*");
|
||||||
|
}
|
||||||
|
@ -67,6 +67,7 @@ Msg.Type = {
|
|||||||
TOPIC_SET_BY: "topic_set_by",
|
TOPIC_SET_BY: "topic_set_by",
|
||||||
WHOIS: "whois",
|
WHOIS: "whois",
|
||||||
BANLIST: "ban_list",
|
BANLIST: "ban_list",
|
||||||
|
IGNORELIST: "ignore_list",
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Msg;
|
module.exports = Msg;
|
||||||
|
@ -18,6 +18,7 @@ const filteredFromClient = {
|
|||||||
highlightRegex: true,
|
highlightRegex: true,
|
||||||
irc: true,
|
irc: true,
|
||||||
password: true,
|
password: true,
|
||||||
|
ignoreList: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
function Network(attr) {
|
function Network(attr) {
|
||||||
@ -41,6 +42,7 @@ function Network(attr) {
|
|||||||
NETWORK: "",
|
NETWORK: "",
|
||||||
},
|
},
|
||||||
chanCache: [],
|
chanCache: [],
|
||||||
|
ignoreList: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.uuid) {
|
if (!this.uuid) {
|
||||||
@ -325,6 +327,7 @@ Network.prototype.export = function() {
|
|||||||
"commands",
|
"commands",
|
||||||
"ip",
|
"ip",
|
||||||
"hostname",
|
"hostname",
|
||||||
|
"ignoreList",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
network.channels = this.channels
|
network.channels = this.channels
|
||||||
|
120
src/plugins/inputs/ignore.js
Normal file
120
src/plugins/inputs/ignore.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Chan = require("../../models/chan");
|
||||||
|
const Msg = require("../../models/msg");
|
||||||
|
const Helper = require("../../helper");
|
||||||
|
|
||||||
|
exports.commands = [
|
||||||
|
"ignore",
|
||||||
|
"unignore",
|
||||||
|
"ignorelist",
|
||||||
|
];
|
||||||
|
|
||||||
|
exports.input = function(network, chan, cmd, args) {
|
||||||
|
const client = this;
|
||||||
|
let target;
|
||||||
|
let hostmask;
|
||||||
|
|
||||||
|
if (cmd !== "ignorelist" && (args.length === 0 || args[0].trim().length === 0)) {
|
||||||
|
chan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.ERROR,
|
||||||
|
text: `Usage: /${cmd} <nick>[!ident][@host]`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd !== "ignorelist") {
|
||||||
|
// Trim to remove any spaces from the hostmask
|
||||||
|
target = args[0].trim();
|
||||||
|
hostmask = Helper.parseHostmask(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case "ignore": {
|
||||||
|
// IRC nicks are case insensitive
|
||||||
|
if (hostmask.nick.toLowerCase() === network.nick.toLowerCase()) {
|
||||||
|
chan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.ERROR,
|
||||||
|
text: "You can't ignore yourself",
|
||||||
|
}));
|
||||||
|
} else if (!network.ignoreList.some(function(entry) {
|
||||||
|
return Helper.compareHostmask(entry, hostmask);
|
||||||
|
})) {
|
||||||
|
hostmask.when = Date.now();
|
||||||
|
network.ignoreList.push(hostmask);
|
||||||
|
|
||||||
|
client.save();
|
||||||
|
chan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.ERROR,
|
||||||
|
text: `\u0002${hostmask.nick}!${hostmask.ident}@${hostmask.hostname}\u000f added to ignorelist`,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
chan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.ERROR,
|
||||||
|
text: "The specified user/hostmask is already ignored",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "unignore": {
|
||||||
|
const idx = network.ignoreList.findIndex(function(entry) {
|
||||||
|
return Helper.compareHostmask(entry, hostmask);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if the entry exists before removing it, otherwise
|
||||||
|
// let the user know.
|
||||||
|
if (idx !== -1) {
|
||||||
|
network.ignoreList.splice(idx, 1);
|
||||||
|
client.save();
|
||||||
|
|
||||||
|
chan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.ERROR,
|
||||||
|
text: `Successfully removed \u0002${hostmask.nick}!${hostmask.ident}@${hostmask.hostname}\u000f from ignorelist`,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
chan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.ERROR,
|
||||||
|
text: "The specified user/hostmask is not ignored",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "ignorelist":
|
||||||
|
if (network.ignoreList.length === 0) {
|
||||||
|
chan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.ERROR,
|
||||||
|
text: "Ignorelist is empty",
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
const chanName = "Ignored users";
|
||||||
|
let newChan = network.getChannel(chanName);
|
||||||
|
|
||||||
|
if (typeof newChan === "undefined") {
|
||||||
|
newChan = client.createChannel({
|
||||||
|
type: Chan.Type.SPECIAL,
|
||||||
|
name: chanName,
|
||||||
|
});
|
||||||
|
client.emit("join", {
|
||||||
|
network: network.uuid,
|
||||||
|
chan: newChan.getFilteredClone(true),
|
||||||
|
index: network.addChannel(newChan),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
newChan.pushMessage(client, new Msg({
|
||||||
|
type: Msg.Type.IGNORELIST,
|
||||||
|
ignored: network.ignoreList.map((data) => ({
|
||||||
|
hostmask: `${data.nick}!${data.ident}@${data.hostname}`,
|
||||||
|
when: data.when,
|
||||||
|
})),
|
||||||
|
}), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
@ -4,6 +4,7 @@ const Chan = require("../../models/chan");
|
|||||||
const Msg = require("../../models/msg");
|
const Msg = require("../../models/msg");
|
||||||
const LinkPrefetch = require("./link");
|
const LinkPrefetch = require("./link");
|
||||||
const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage");
|
const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage");
|
||||||
|
const Helper = require("../../helper");
|
||||||
const nickRegExp = /(?:\x03[0-9]{1,2}(?:,[0-9]{1,2})?)?([\w[\]\\`^{|}-]+)/g;
|
const nickRegExp = /(?:\x03[0-9]{1,2}(?:,[0-9]{1,2})?)?([\w[\]\\`^{|}-]+)/g;
|
||||||
|
|
||||||
module.exports = function(irc, network) {
|
module.exports = function(irc, network) {
|
||||||
@ -43,11 +44,20 @@ module.exports = function(irc, network) {
|
|||||||
let showInActive = false;
|
let showInActive = false;
|
||||||
const self = data.nick === irc.user.nick;
|
const self = data.nick === irc.user.nick;
|
||||||
|
|
||||||
|
// Check if the sender is in our ignore list
|
||||||
|
const shouldIgnore = network.ignoreList.some(function(entry) {
|
||||||
|
return Helper.compareHostmask(entry, data);
|
||||||
|
});
|
||||||
|
|
||||||
// Server messages go to server window, no questions asked
|
// Server messages go to server window, no questions asked
|
||||||
if (data.from_server) {
|
if (data.from_server) {
|
||||||
chan = network.channels[0];
|
chan = network.channels[0];
|
||||||
from = chan.getUser(data.nick);
|
from = chan.getUser(data.nick);
|
||||||
} else {
|
} else {
|
||||||
|
if (shouldIgnore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let target = data.target;
|
let target = data.target;
|
||||||
|
|
||||||
// If the message is targeted at us, use sender as target instead
|
// If the message is targeted at us, use sender as target instead
|
||||||
|
@ -47,6 +47,7 @@ describe("Network", function() {
|
|||||||
{name: "&secure", key: "bar"},
|
{name: "&secure", key: "bar"},
|
||||||
{name: "PrivateChat", type: "query"},
|
{name: "PrivateChat", type: "query"},
|
||||||
],
|
],
|
||||||
|
ignoreList: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
62
test/tests/hostmask.js
Normal file
62
test/tests/hostmask.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const expect = require("chai").expect;
|
||||||
|
const Helper = require("../../src/helper");
|
||||||
|
|
||||||
|
describe("Hostmask", function() {
|
||||||
|
it(".parseHostmask", function() {
|
||||||
|
expect(Helper.parseHostmask("nick").nick).to.equal("nick");
|
||||||
|
expect(Helper.parseHostmask("nick").ident).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("nick").hostname).to.equal("*");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("!user").nick).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("!user").ident).to.equal("user");
|
||||||
|
expect(Helper.parseHostmask("!user").hostname).to.equal("*");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("@host").nick).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("@host").ident).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("@host").hostname).to.equal("host");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("!").nick).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("!").ident).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("!").hostname).to.equal("*");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("@").nick).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("@").ident).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("@").hostname).to.equal("*");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("!@").nick).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("!@").ident).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("!@").hostname).to.equal("*");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("nick!user@host").nick).to.equal("nick");
|
||||||
|
expect(Helper.parseHostmask("nick!user@host").ident).to.equal("user");
|
||||||
|
expect(Helper.parseHostmask("nick!user@host").hostname).to.equal("host");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("nick!!!!@thing@@host").nick).to.equal("nick");
|
||||||
|
expect(Helper.parseHostmask("nick!!!!@thing@@host").ident).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("nick!!!!@thing@@host").hostname).to.equal("thing");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("!!!!@thing@@host").nick).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("!!!!@thing@@host").ident).to.equal("*");
|
||||||
|
expect(Helper.parseHostmask("!!!!@thing@@host").hostname).to.equal("thing");
|
||||||
|
|
||||||
|
expect(Helper.parseHostmask("NiCK!uSEr@HOST").nick).to.equal("nick");
|
||||||
|
expect(Helper.parseHostmask("NiCK!uSEr@HOST").ident).to.equal("user");
|
||||||
|
expect(Helper.parseHostmask("NiCK!uSEr@HOST").hostname).to.equal("host");
|
||||||
|
});
|
||||||
|
|
||||||
|
it(".compareHostmask (wildcard)", function() {
|
||||||
|
const a = Helper.parseHostmask("nick!user@host");
|
||||||
|
const b = Helper.parseHostmask("nick!*@*");
|
||||||
|
expect(Helper.compareHostmask(b, a)).to.be.true;
|
||||||
|
expect(Helper.compareHostmask(a, b)).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it(".compareHostmask", function() {
|
||||||
|
const a = Helper.parseHostmask("nick!user@host");
|
||||||
|
const b = Helper.parseHostmask("NiCK!useR@HOST");
|
||||||
|
expect(Helper.compareHostmask(b, a)).to.be.true;
|
||||||
|
expect(Helper.compareHostmask(a, b)).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user