diff --git a/client/js/libs/handlebars/ircmessageparser/findNames.js b/client/js/libs/handlebars/ircmessageparser/findNames.js
new file mode 100644
index 00000000..fd06f5d4
--- /dev/null
+++ b/client/js/libs/handlebars/ircmessageparser/findNames.js
@@ -0,0 +1,17 @@
+"use strict";
+
+function findNames(text, users) {
+ const result = [];
+ let index = 0;
+ users.forEach((nick) => {
+ index = text.indexOf(nick, index);
+ result.push({
+ start: index,
+ end: index + nick.length,
+ nick: nick,
+ });
+ });
+ return result;
+}
+
+module.exports = findNames;
diff --git a/client/js/libs/handlebars/parse.js b/client/js/libs/handlebars/parse.js
index 0bccaa1d..3c75219e 100644
--- a/client/js/libs/handlebars/parse.js
+++ b/client/js/libs/handlebars/parse.js
@@ -6,7 +6,9 @@ const anyIntersection = require("./ircmessageparser/anyIntersection");
const findChannels = require("./ircmessageparser/findChannels");
const findLinks = require("./ircmessageparser/findLinks");
const findEmoji = require("./ircmessageparser/findEmoji");
+const findNames = require("./ircmessageparser/findNames");
const merge = require("./ircmessageparser/merge");
+const colorClass = require("./colorClass");
// Create an HTML `span` with styling information for a given fragment
function createFragment(fragment) {
@@ -47,9 +49,14 @@ function createFragment(fragment) {
return escapedText;
}
-// Transform an IRC message potentially filled with styling control codes, URLs
-// and channels into a string of HTML elements to display on the client.
-module.exports = function parse(text) {
+// Transform an IRC message potentially filled with styling control codes, URLs,
+// nicknames, and channels into a string of HTML elements to display on the client.
+module.exports = function parse(text, users) {
+ // if it's not the users we're expecting, but rather is passed from Handlebars (occurs when users passed to template is null or undefined)
+ if (users && users.hash) {
+ users = [];
+ }
+
// Extract the styling information and get the plain text version from it
const styleFragments = parseStyle(text);
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
@@ -62,11 +69,13 @@ module.exports = function parse(text) {
const channelParts = findChannels(cleanText, channelPrefixes, userModes);
const linkParts = findLinks(cleanText);
const emojiParts = findEmoji(cleanText);
+ const nameParts = findNames(cleanText, (users || []));
// Sort all parts identified based on their position in the original text
const parts = channelParts
.concat(linkParts)
.concat(emojiParts)
+ .concat(nameParts)
.sort((a, b) => a.start - b.start || b.end - a.end)
.reduce((prev, curr) => {
const intersection = prev.some((p) => anyIntersection(p, curr));
@@ -77,7 +86,7 @@ module.exports = function parse(text) {
return prev.concat([curr]);
}, []);
- // Merge the styling information with the channels / URLs / text objects and
+ // Merge the styling information with the channels / URLs / nicks / text objects and
// generate HTML strings with the resulting fragments
return merge(parts, styleFragments).map((textPart) => {
// Create HTML strings with styling information
@@ -92,6 +101,9 @@ module.exports = function parse(text) {
return `${fragments}`;
} else if (textPart.emoji) {
return `${fragments}`;
+ } else if (textPart.nick) {
+ const nick = Handlebars.Utils.escapeExpression(textPart.nick);
+ return `${fragments}`;
}
return fragments;
diff --git a/client/views/msg.tpl b/client/views/msg.tpl
index 4c14619b..64ec97e7 100644
--- a/client/views/msg.tpl
+++ b/client/views/msg.tpl
@@ -8,7 +8,7 @@
{{/if}}
- {{{parse text}}}
+ {{{parse text users}}}
{{#each previews}}
diff --git a/src/plugins/irc-events/message.js b/src/plugins/irc-events/message.js
index 3fed76d0..4b648bc2 100644
--- a/src/plugins/irc-events/message.js
+++ b/src/plugins/irc-events/message.js
@@ -4,6 +4,7 @@ const Chan = require("../../models/chan");
const Msg = require("../../models/msg");
const LinkPrefetch = require("./link");
const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage");
+const nickRegExp = /[\w[\]\\`^{|}-]{4,}/gi;
module.exports = function(irc, network) {
const client = this;
@@ -88,6 +89,14 @@ module.exports = function(irc, network) {
highlight = network.highlightRegex.test(data.message);
}
+ const users = [];
+ let match;
+ while ((match = nickRegExp.exec(data.message))) {
+ if (chan.findUser(match[0])) {
+ users.push(match[0]);
+ }
+ }
+
const msg = new Msg({
type: data.type,
time: data.time,
@@ -95,6 +104,7 @@ module.exports = function(irc, network) {
text: data.message,
self: self,
highlight: highlight,
+ users: users,
});
// No prefetch URLs unless are simple MESSAGE or ACTION types
diff --git a/test/client/js/libs/handlebars/ircmessageparser/findNames.js b/test/client/js/libs/handlebars/ircmessageparser/findNames.js
new file mode 100644
index 00000000..52c937d7
--- /dev/null
+++ b/test/client/js/libs/handlebars/ircmessageparser/findNames.js
@@ -0,0 +1,26 @@
+"use strict";
+
+const expect = require("chai").expect;
+const findNames = require("../../../../../../client/js/libs/handlebars/ircmessageparser/findNames");
+
+describe("findNames", () => {
+ it("should find nicks in text", () => {
+ const input = ": Hello, xPaw, how's it going?";
+ const expected = [
+ {
+ start: 1,
+ end: 10,
+ nick: "MaxLeiter",
+ },
+ {
+ start: 20,
+ end: 24,
+ nick: "xPaw",
+ },
+ ];
+ const nicks = ["MaxLeiter", "xPaw"];
+ const actual = findNames(input, nicks);
+
+ expect(actual).to.deep.equal(expected);
+ });
+});
diff --git a/test/client/js/libs/handlebars/parse.js b/test/client/js/libs/handlebars/parse.js
index 2c08f8f2..4921059b 100644
--- a/test/client/js/libs/handlebars/parse.js
+++ b/test/client/js/libs/handlebars/parse.js
@@ -243,6 +243,49 @@ describe("parse Handlebars helper", () => {
expect(actual).to.deep.equal(expected);
});
+ it("should find nicks", () => {
+ const testCases = [{
+ users: ["MaxLeiter"],
+ input: "test, MaxLeiter",
+ expected:
+ "test, " +
+ "" +
+ "MaxLeiter" +
+ "",
+ }];
+
+ const actual = testCases.map((testCase) => parse(testCase.input, testCase.users));
+ const expected = testCases.map((testCase) => testCase.expected);
+
+ expect(actual).to.deep.equal(expected);
+ });
+
+ it("should not find nicks", () => {
+ const testCases = [{
+ users: ["MaxLeiter, test"],
+ input: "#test-channelMaxLeiter",
+ expected:
+ "" +
+ "#test-channelMaxLeiter" +
+ "",
+ },
+ {
+ users: ["MaxLeiter, test"],
+ input: "https://www.MaxLeiter.com/test",
+ expected:
+ "" +
+ "https://www.MaxLeiter.com/test" +
+ "",
+ },
+
+ ];
+
+ const actual = testCases.map((testCase) => parse(testCase.input));
+ const expected = testCases.map((testCase) => testCase.expected);
+
+ expect(actual).to.deep.equal(expected);
+ });
+
it("should go bonkers like mirc", () => {
const testCases = [{
input: "\x02irc\x0f://\x1dfreenode.net\x0f/\x034,8thelounge",