Repository: thelounge/thelounge
Pull request #4014 (2020-08-04 17:17:49)

Disable link prefetching for urls with no schema specified
![image](https://user-images.githubusercontent.com/613331/89324068-56042100-d68f-11ea-8562-baa32cf977cb.png)

People find it annoying when things like "readme.md", any python module name, or a .sh script gets a link preview. This patch keeps these links clickable, but will not generate a link preview.

Previews on bare domains are generally not that useful anyway.

*On a side note, I was thinking whether we should skip link previews for links that are wrapped with angle brackets. Discord does that for example.*


Patch:
diff --git a/client/js/helpers/ircmessageparser/findLinks.js b/client/js/helpers/ircmessageparser/findLinks.js
index 9f6ca6c9..be33dc54 100644
--- a/client/js/helpers/ircmessageparser/findLinks.js
+++ b/client/js/helpers/ircmessageparser/findLinks.js
@@ -6,11 +6,13 @@ LinkifyIt.prototype.normalize = function normalize(match) {
 	if (!match.schema) {
 		match.schema = "http:";
 		match.url = "http://" + match.url;
+		match.noschema = true;
 	}
 
 	if (match.schema === "//") {
 		match.schema = "http:";
 		match.url = "http:" + match.url;
+		match.noschema = true;
 	}
 
 	if (match.schema === "mailto:" && !/^mailto:/i.test(match.url)) {
@@ -47,11 +49,28 @@ function findLinks(text) {
 		return [];
 	}
 
-	return matches.map((url) => ({
+	return matches.map(returnUrl);
+}
+
+function findLinksWithSchema(text) {
+	const matches = linkify.match(text);
+
+	if (!matches) {
+		return [];
+	}
+
+	return matches.filter((url) => !url.noschema).map(returnUrl);
+}
+
+function returnUrl(url) {
+	return {
 		start: url.index,
 		end: url.lastIndex,
 		link: url.url,
-	}));
+	};
 }
 
-module.exports = findLinks;
+module.exports = {
+	findLinks,
+	findLinksWithSchema,
+};
diff --git a/client/js/helpers/parse.js b/client/js/helpers/parse.js
index 69420cac..0ea4df2c 100644
--- a/client/js/helpers/parse.js
+++ b/client/js/helpers/parse.js
@@ -2,7 +2,7 @@
 
 import parseStyle from "./ircmessageparser/parseStyle";
 import findChannels from "./ircmessageparser/findChannels";
-import findLinks from "./ircmessageparser/findLinks";
+import {findLinks} from "./ircmessageparser/findLinks";
 import findEmoji from "./ircmessageparser/findEmoji";
 import findNames from "./ircmessageparser/findNames";
 import merge from "./ircmessageparser/merge";
diff --git a/src/plugins/irc-events/link.js b/src/plugins/irc-events/link.js
index f9cec533..4f68f9df 100644
--- a/src/plugins/irc-events/link.js
+++ b/src/plugins/irc-events/link.js
@@ -6,7 +6,7 @@ const URL = require("url").URL;
 const mime = require("mime-types");
 const Helper = require("../../helper");
 const cleanIrcMessage = require("../../../client/js/helpers/ircmessageparser/cleanIrcMessage");
-const findLinks = require("../../../client/js/helpers/ircmessageparser/findLinks");
+const {findLinksWithSchema} = require("../../../client/js/helpers/ircmessageparser/findLinks");
 const storage = require("../storage");
 const currentFetchPromises = new Map();
 const imageTypeRegex = /^image\/.+/;
@@ -20,7 +20,7 @@ module.exports = function (client, chan, msg) {
 	// Remove all IRC formatting characters before searching for links
 	const cleanText = cleanIrcMessage(msg.text);
 
-	msg.previews = findLinks(cleanText).reduce((cleanLinks, link) => {
+	msg.previews = findLinksWithSchema(cleanText).reduce((cleanLinks, link) => {
 		const url = normalizeURL(link.link);
 
 		// If the URL is invalid and cannot be normalized, don't fetch it


Test patch:
diff --git a/test/client/js/helpers/ircmessageparser/findLinks.js b/test/client/js/helpers/ircmessageparser/findLinks.js
index 57930565..9effda56 100644
--- a/test/client/js/helpers/ircmessageparser/findLinks.js
+++ b/test/client/js/helpers/ircmessageparser/findLinks.js
@@ -1,7 +1,10 @@
 "use strict";
 
 const expect = require("chai").expect;
-const findLinks = require("../../../../../client/js/helpers/ircmessageparser/findLinks");
+const {
+	findLinks,
+	findLinksWithSchema,
+} = require("../../../../../client/js/helpers/ircmessageparser/findLinks");
 
 describe("findLinks", () => {
 	it("should find url", () => {
@@ -354,4 +357,24 @@ describe("findLinks", () => {
 
 		expect(actual).to.deep.equal(expected);
 	});
+
+	it("should not return urls with no schema if flag is specified", () => {
+		const input = "https://example.global //example.com http://example.group example.py";
+		const expected = [
+			{
+				link: "https://example.global",
+				start: 0,
+				end: 22,
+			},
+			{
+				end: 57,
+				link: "http://example.group",
+				start: 37,
+			},
+		];
+
+		const actual = findLinksWithSchema(input);
+
+		expect(actual).to.deep.equal(expected);
+	});
 });
diff --git a/test/plugins/link.js b/test/plugins/link.js
index 5b06ca94..b22937a8 100644
--- a/test/plugins/link.js
+++ b/test/plugins/link.js
@@ -623,19 +623,15 @@ Vivamus bibendum vulputate tincidunt. Sed vitae ligula felis.`;
 		});
 	});
 
-	it("should fetch protocol-aware links", function (done) {
+	it("should not fetch links without a schema", function () {
 		const port = this.port;
 		const message = this.irc.createMessage({
-			text: "//localhost:" + port + "",
+			text: `//localhost:${port} localhost:${port} //localhost:${port}/test localhost:${port}/test`,
 		});
 
 		link(this.irc, this.network.channels[0], message);
 
-		this.irc.once("msg:preview", function (data) {
-			expect(data.preview.link).to.equal("http://localhost:" + port + "");
-			expect(data.preview.type).to.equal("error");
-			done();
-		});
+		expect(message.previews).to.be.empty;
 	});
 
 	it("should de-duplicate links", function (done) {