mirror of
https://github.com/thelounge/thelounge
synced 2024-11-14 00:07:17 +00:00
add highlight exceptions
This commit is contained in:
parent
c29ae50392
commit
b97b145df1
9 changed files with 150 additions and 56 deletions
|
@ -351,8 +351,15 @@ This may break orientation if your browser does not support that."
|
|||
|
||||
<div v-if="!$store.state.serverConfiguration.public && $store.state.settings.advanced">
|
||||
<label class="opt">
|
||||
<label for="highlights" class="sr-only">
|
||||
Custom highlights (comma-separated keywords)
|
||||
<label for="highlights" class="opt">
|
||||
Custom highlights
|
||||
<span
|
||||
class="tooltipped tooltipped-n tooltipped-no-delay"
|
||||
aria-label="If a message contains any of these comma-separated
|
||||
expressions, it will trigger a highlight."
|
||||
>
|
||||
<button class="extra-help" />
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
id="highlights"
|
||||
|
@ -360,7 +367,31 @@ This may break orientation if your browser does not support that."
|
|||
type="text"
|
||||
name="highlights"
|
||||
class="input"
|
||||
placeholder="Custom highlights (comma-separated keywords)"
|
||||
placeholder="Comma-separated, e.g.: word, some more words, anotherword"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="!$store.state.serverConfiguration.public && $store.state.settings.advanced">
|
||||
<label class="opt">
|
||||
<label for="highlightExceptions" class="opt">
|
||||
Highlight exceptions
|
||||
<span
|
||||
class="tooltipped tooltipped-n tooltipped-no-delay"
|
||||
aria-label="If a message contains any of these comma-separated
|
||||
expressions, it will not trigger a highlight even if it contains
|
||||
your nickname or expressions defined in custom highlights."
|
||||
>
|
||||
<button class="extra-help" />
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
id="highlightExceptions"
|
||||
:value="$store.state.settings.highlightExceptions"
|
||||
type="text"
|
||||
name="highlightExceptions"
|
||||
class="input"
|
||||
placeholder="Comma-separated, e.g.: word, some more words, anotherword"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -46,6 +46,10 @@ export const config = normalizeConfig({
|
|||
default: "",
|
||||
sync: "always",
|
||||
},
|
||||
highlightExceptions: {
|
||||
default: "",
|
||||
sync: "always",
|
||||
},
|
||||
awayMessage: {
|
||||
default: "",
|
||||
sync: "always",
|
||||
|
|
|
@ -62,6 +62,7 @@ function Client(manager, name, config = {}) {
|
|||
manager: manager,
|
||||
messageStorage: [],
|
||||
highlightRegex: null,
|
||||
highlightExceptionRegex: null,
|
||||
});
|
||||
|
||||
const client = this;
|
||||
|
@ -424,30 +425,32 @@ Client.prototype.inputLine = function (data) {
|
|||
};
|
||||
|
||||
Client.prototype.compileCustomHighlights = function () {
|
||||
const client = this;
|
||||
this.highlightRegex = compileHighlightRegex(this.config.clientSettings.highlights);
|
||||
this.highlightExceptionRegex = compileHighlightRegex(
|
||||
this.config.clientSettings.highlightExceptions
|
||||
);
|
||||
};
|
||||
|
||||
if (typeof client.config.clientSettings.highlights !== "string") {
|
||||
client.highlightRegex = null;
|
||||
return;
|
||||
function compileHighlightRegex(customHighlightString) {
|
||||
if (typeof customHighlightString !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure we don't have empty string in the list of highlights
|
||||
// otherwise, users get notifications for everything
|
||||
const highlightsTokens = client.config.clientSettings.highlights
|
||||
// Ensure we don't have empty strings in the list of highlights
|
||||
const highlightsTokens = customHighlightString
|
||||
.split(",")
|
||||
.map((highlight) => escapeRegExp(highlight.trim()))
|
||||
.filter((highlight) => highlight.length > 0);
|
||||
|
||||
if (highlightsTokens.length === 0) {
|
||||
client.highlightRegex = null;
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
client.highlightRegex = new RegExp(
|
||||
return new RegExp(
|
||||
`(?:^|[ .,+!?|/:<>(){}'"@&~-])(?:${highlightsTokens.join("|")})(?:$|[ .,+!?|/:<>(){}'"-])`,
|
||||
"i"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Client.prototype.more = function (data) {
|
||||
const client = this;
|
||||
|
|
|
@ -5,21 +5,17 @@ const got = require("got");
|
|||
const URL = require("url").URL;
|
||||
const mime = require("mime-types");
|
||||
const Helper = require("../../helper");
|
||||
const cleanIrcMessage = require("../../../client/js/helpers/ircmessageparser/cleanIrcMessage");
|
||||
const {findLinksWithSchema} = require("../../../client/js/helpers/ircmessageparser/findLinks");
|
||||
const storage = require("../storage");
|
||||
const currentFetchPromises = new Map();
|
||||
const imageTypeRegex = /^image\/.+/;
|
||||
const mediaTypeRegex = /^(audio|video)\/.+/;
|
||||
|
||||
module.exports = function (client, chan, msg) {
|
||||
module.exports = function (client, chan, msg, cleanText) {
|
||||
if (!Helper.config.prefetch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove all IRC formatting characters before searching for links
|
||||
const cleanText = cleanIrcMessage(msg.text);
|
||||
|
||||
msg.previews = findLinksWithSchema(cleanText).reduce((cleanLinks, link) => {
|
||||
const url = normalizeURL(link.link);
|
||||
|
||||
|
|
|
@ -115,6 +115,9 @@ module.exports = function (irc, network) {
|
|||
msg.showInActive = true;
|
||||
}
|
||||
|
||||
// remove IRC formatting for custom highlight testing
|
||||
const cleanMessage = cleanIrcMessage(data.message);
|
||||
|
||||
// Self messages in channels are never highlighted
|
||||
// Non-self messages are highlighted as soon as the nick is detected
|
||||
if (!msg.highlight && !msg.self) {
|
||||
|
@ -122,10 +125,15 @@ module.exports = function (irc, network) {
|
|||
|
||||
// If we still don't have a highlight, test against custom highlights if there's any
|
||||
if (!msg.highlight && client.highlightRegex) {
|
||||
msg.highlight = client.highlightRegex.test(data.message);
|
||||
msg.highlight = client.highlightRegex.test(cleanMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// if highlight exceptions match, do not highlight at all
|
||||
if (msg.highlight && client.highlightExceptionRegex) {
|
||||
msg.highlight = !client.highlightExceptionRegex.test(cleanMessage);
|
||||
}
|
||||
|
||||
if (data.group) {
|
||||
msg.statusmsgGroup = data.group;
|
||||
}
|
||||
|
@ -140,7 +148,7 @@ module.exports = function (irc, network) {
|
|||
|
||||
// No prefetch URLs unless are simple MESSAGE or ACTION types
|
||||
if ([Msg.Type.MESSAGE, Msg.Type.ACTION].includes(data.type)) {
|
||||
LinkPrefetch(client, chan, msg);
|
||||
LinkPrefetch(client, chan, msg, cleanMessage);
|
||||
}
|
||||
|
||||
chan.pushMessage(client, msg, !msg.self);
|
||||
|
@ -148,7 +156,7 @@ module.exports = function (irc, network) {
|
|||
// Do not send notifications for messages older than 15 minutes (znc buffer for example)
|
||||
if (msg.highlight && (!data.time || data.time > Date.now() - 900000)) {
|
||||
let title = chan.name;
|
||||
let body = cleanIrcMessage(data.message);
|
||||
let body = cleanMessage;
|
||||
|
||||
if (msg.type === Msg.Type.ACTION) {
|
||||
// For actions, do not include colon in the message
|
||||
|
|
|
@ -621,7 +621,7 @@ function initializeClient(socket, client, token, lastMessage, openChannel) {
|
|||
|
||||
client.save();
|
||||
|
||||
if (newSetting.name === "highlights") {
|
||||
if (newSetting.name === "highlights" || newSetting.name === "highlightExceptions") {
|
||||
client.compileCustomHighlights();
|
||||
} else if (newSetting.name === "awayMessage") {
|
||||
if (typeof newSetting.value !== "string") {
|
||||
|
|
|
@ -49,7 +49,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: url,
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
expect(message.previews).to.deep.equal([
|
||||
{
|
||||
|
@ -86,7 +86,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: url,
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
expect(message.previews).to.deep.equal([
|
||||
{
|
||||
|
@ -122,7 +122,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: url,
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/truncate", function (req, res) {
|
||||
res.send(
|
||||
|
@ -146,7 +146,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/basic-og",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/basic-og", function (req, res) {
|
||||
res.send("<title>test</title><meta property='og:title' content='opengraph test'>");
|
||||
|
@ -163,7 +163,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/duplicate-tags",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/duplicate-tags", function (req, res) {
|
||||
res.send(
|
||||
|
@ -183,7 +183,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/description-og",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/description-og", function (req, res) {
|
||||
res.send(
|
||||
|
@ -203,7 +203,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/thumb",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/thumb", function (req, res) {
|
||||
res.send(
|
||||
|
@ -249,7 +249,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + port + "/thumb",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
this.irc.once("msg:preview", function (data) {
|
||||
expect(data.preview.head).to.equal("Google");
|
||||
|
@ -276,7 +276,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + port + "/thumb",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
this.irc.once("msg:preview", function (data) {
|
||||
expect(data.preview.head).to.equal("Google");
|
||||
|
@ -292,7 +292,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/thumb-image-src",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/thumb-image-src", function (req, res) {
|
||||
res.send(
|
||||
|
@ -314,7 +314,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/thumb-image-src",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/thumb-image-src", function (req, res) {
|
||||
res.send("<link rel='image_src' href='//localhost:" + port + "/real-test-image.png'>");
|
||||
|
@ -334,7 +334,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/relative-thumb",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/relative-thumb", function (req, res) {
|
||||
res.send(
|
||||
|
@ -358,7 +358,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/thumb-no-title",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/thumb-no-title", function (req, res) {
|
||||
res.send(
|
||||
|
@ -384,7 +384,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/body-no-title",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/body-no-title", function (req, res) {
|
||||
res.send("<meta name='description' content='hello world'>");
|
||||
|
@ -405,7 +405,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/thumb-404",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/thumb-404", function (req, res) {
|
||||
res.send(
|
||||
|
@ -429,7 +429,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + port + "/real-test-image.png",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
this.irc.once("msg:preview", function (data) {
|
||||
expect(data.preview.type).to.equal("image");
|
||||
|
@ -448,7 +448,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + port + "/one http://localhost:" + this.port + "/two",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
expect(message.previews).to.eql([
|
||||
{
|
||||
|
@ -511,7 +511,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/language-check",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
});
|
||||
|
||||
it("should send accept text/html for initial request", function (done) {
|
||||
|
@ -527,7 +527,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + this.port + "/accept-header-html",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
});
|
||||
|
||||
it("should send accept */* for meta image", function (done) {
|
||||
|
@ -551,7 +551,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + port + "/accept-header-thumb",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
});
|
||||
|
||||
it("should not add slash to url", function (done) {
|
||||
|
@ -560,7 +560,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: "http://localhost:" + port + "",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
this.irc.once("msg:preview", function (data) {
|
||||
expect(data.preview.link).to.equal("http://localhost:" + port + "");
|
||||
|
@ -591,7 +591,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
"/unicodeq/?q=🙈-emoji-test",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
app.get("/unicode/:q", function (req, res) {
|
||||
res.send(`<title>${req.params.q}</title>`);
|
||||
|
@ -629,7 +629,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
text: `//localhost:${port} localhost:${port} //localhost:${port}/test localhost:${port}/test`,
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
expect(message.previews).to.be.empty;
|
||||
});
|
||||
|
@ -647,7 +647,7 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
"",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
expect(message.previews).to.deep.equal([
|
||||
{
|
||||
|
@ -695,9 +695,9 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
|
||||
this.irc.config.browser.language = "very nice language";
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
process.nextTick(() => link(this.irc, this.network.channels[0], message));
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
process.nextTick(() => link(this.irc, this.network.channels[0], message, message.text));
|
||||
|
||||
app.get("/basic-og-once", function (req, res) {
|
||||
requests++;
|
||||
|
@ -734,11 +734,11 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
|
|||
let responses = 0;
|
||||
|
||||
this.irc.config.browser.language = "first language";
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
setTimeout(() => {
|
||||
this.irc.config.browser.language = "second language";
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
}, 100);
|
||||
|
||||
app.get("/basic-og-once-lang", function (req, res) {
|
||||
|
|
|
@ -76,7 +76,7 @@ describe("Image storage", function () {
|
|||
text: "http://localhost:" + port + "/thumb",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
this.app.get("/thumb", function (req, res) {
|
||||
res.send(
|
||||
|
@ -100,7 +100,7 @@ describe("Image storage", function () {
|
|||
text: "http://localhost:" + port + "/real-test-image.png",
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
this.irc.once("msg:preview", function (data) {
|
||||
expect(data.preview.type).to.equal("image");
|
||||
|
@ -124,7 +124,7 @@ describe("Image storage", function () {
|
|||
);
|
||||
});
|
||||
|
||||
link(this.irc, this.network.channels[0], message);
|
||||
link(this.irc, this.network.channels[0], message, message.text);
|
||||
|
||||
this.irc.once("msg:preview", function (data) {
|
||||
expect(data.preview.type).to.equal("link");
|
||||
|
|
|
@ -22,7 +22,10 @@ describe("Custom highlights", function () {
|
|||
},
|
||||
"test",
|
||||
{
|
||||
clientSettings: {highlights: "foo, @all, sp ace , 고"},
|
||||
clientSettings: {
|
||||
highlights: "foo, @all, sp ace , 고",
|
||||
highlightExceptions: "foo bar, bar @all, test sp ace test",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -96,4 +99,53 @@ describe("Custom highlights", function () {
|
|||
client.compileCustomHighlights();
|
||||
expect(client.highlightRegex).to.be.null;
|
||||
});
|
||||
|
||||
// tests for highlight exceptions
|
||||
it("should NOT highlight due to highlight exceptions", function () {
|
||||
const teststrings = [
|
||||
"foo bar baz",
|
||||
"test foo bar",
|
||||
"foo bar @all test",
|
||||
"with a test sp ace test",
|
||||
];
|
||||
|
||||
for (const teststring of teststrings) {
|
||||
expect(teststring).to.match(client.highlightExceptionRegex);
|
||||
}
|
||||
});
|
||||
|
||||
it("should highlight regardless of highlight exceptions", function () {
|
||||
const teststrings = [
|
||||
"Hey foo hello",
|
||||
"hey Foo: hi",
|
||||
"hey Foo, hi",
|
||||
"<foo> testing",
|
||||
"foo",
|
||||
"hey @all test",
|
||||
"testing foo's stuff",
|
||||
'"foo"',
|
||||
'"@all"',
|
||||
"foo!",
|
||||
"www.foo.bar",
|
||||
"www.bar.foo/page",
|
||||
"고",
|
||||
"test 고",
|
||||
"고!",
|
||||
"www.고.com",
|
||||
"hey @Foo",
|
||||
"hey ~Foo",
|
||||
"hey +Foo",
|
||||
"hello &foo",
|
||||
"@all",
|
||||
"@all wtf",
|
||||
"wtfbar @all",
|
||||
"@@all",
|
||||
"@고",
|
||||
"f00 sp ace: bar",
|
||||
];
|
||||
|
||||
for (const teststring of teststrings) {
|
||||
expect(teststring).to.not.match(client.highlightExceptionRegex);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue