diff --git a/src/core/common/helpers/findMediaLinks.spec.ts b/src/core/common/helpers/findMediaLinks.spec.ts new file mode 100644 index 000000000..c6aa68fea --- /dev/null +++ b/src/core/common/helpers/findMediaLinks.spec.ts @@ -0,0 +1,45 @@ +import { findMediaLinks } from "./findMediaLinks"; + +it("can detect links", () => { + const bodies = [ + // YouTube + "http://www.youtube.com/watch?v=o3LJPaYBqgU", + "http://youtube.com/watch?v=o3LJPaYBqgU", + "http://youtu.be/o3LJPaYBqgU", + "https://www.youtube.com/watch?v=o3LJPaYBqgU", + "https://youtube.com/watch?v=o3LJPaYBqgU", + "https://youtu.be/o3LJPaYBqgU", + // Twitter + "http://twitter.com/coralproject/status/1280903734478987265", + "http://www.twitter.com/coralproject/status/1280903734478987265", + "http://mobile.twitter.com/coralproject/status/1280903734478987265", + "https://twitter.com/coralproject/status/1280903734478987265", + "https://www.twitter.com/coralproject/status/1280903734478987265", + "https://mobile.twitter.com/coralproject/status/1280903734478987265", + ]; + + for (const body of bodies) { + const links = findMediaLinks(body); + expect(links).toHaveLength(1); + expect(links[0].url).toEqual(body); + } +}); + +it("will force https on links without a scheme", () => { + const bodies = [ + // YouTube + "www.youtube.com/watch?v=o3LJPaYBqgU", + "twitter.com/coralproject/status/1280903734478987265", + "youtu.be/o3LJPaYBqgU", + // Twitter + "www.twitter.com/coralproject/status/1280903734478987265", + "mobile.twitter.com/coralproject/status/1280903734478987265", + "youtube.com/watch?v=o3LJPaYBqgU", + ]; + + for (const body of bodies) { + const links = findMediaLinks(body); + expect(links).toHaveLength(1); + expect(links[0].url).toEqual(`https://${body}`); + } +}); diff --git a/src/core/common/helpers/findMediaLinks.ts b/src/core/common/helpers/findMediaLinks.ts index 51a99dd7a..70400bc92 100644 --- a/src/core/common/helpers/findMediaLinks.ts +++ b/src/core/common/helpers/findMediaLinks.ts @@ -19,24 +19,39 @@ export function isMediaLink( return false; } -function formatLink(type: MediaType, link: string): MediaLink { +function formatLink(type: MediaType, url: string): MediaLink { return { - url: link, + url: url.startsWith("http") ? url : `https://${url}`, type, }; } -const youtubeRegex = /(https?:\/\/)?(www\.)?(youtube\.com\/watch\?v=[-_a-zA-z0-9]{1,12}|youtu\.be\/[-_a-zA-z0-9]{1,12})/g; -const twitterRegex = /(https?:\/\/)?(www\.)?(twitter\.com\/[a-zA-z0-9]+\/status\/[0-9]+)/g; +const patterns: Array<{ type: MediaType; pattern: RegExp }> = [ + { + type: "youtube", + pattern: /(https?:\/\/)?(www\.)?(youtube\.com\/watch\?v=[-_a-zA-z0-9]{1,12}|youtu\.be\/[-_a-zA-z0-9]{1,12})/g, + }, + { + type: "twitter", + pattern: /(https?:\/\/)?(www\.|mobile\.)?(twitter\.com\/[a-zA-z0-9]+\/status\/[0-9]+)/g, + }, +]; export function findMediaLinks(body: string): MediaLink[] { - const foundYouTubeLinks = new Set(body.match(youtubeRegex) || []); - const foundTwitterLinks = new Set(body.match(twitterRegex) || []); + const media: MediaLink[] = []; - const media = [ - ...[...foundYouTubeLinks].map((l) => formatLink("youtube", l)), - ...[...foundTwitterLinks].map((l) => formatLink("twitter", l)), - ]; + // For each of the patterns available... + for (const { type, pattern } of patterns) { + // Find all urls in the body with the pattern and put it into a set to + // deduplicate it. + const urls = new Set(body.match(pattern) || []); + + // For each of the unique urls, format the url to a media link and push it + // in. + Array.from(urls).forEach((url) => { + media.push(formatLink(type, url)); + }); + } return media; }