From 5cf07fc50f311989e459c7151ca89e067619121f Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 24 Oct 2021 21:10:45 +0900
Subject: [PATCH] enhance: Provide Twemoji SVGs from Misskey server (#2)
 (#7897)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Selfhosting Twemoji

* ちっ

* うざっ

* あ

* add test

Co-authored-by: mei23 <m@m544.net>
---
 package.json             |  1 +
 src/misc/twemoji-base.ts |  2 +-
 src/server/web/index.ts  | 16 ++++++++++++++++
 test/fetch-resource.ts   | 12 ++++++++++++
 yarn.lock                | 37 ++++++++++++++++++++++++++++++++++++-
 5 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index b290cf3b08..ff62742513 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
 		"lodash": "^4.17.21"
 	},
 	"dependencies": {
+		"@discordapp/twemoji": "13.1.0",
 		"@elastic/elasticsearch": "7.11.0",
 		"@koa/cors": "3.1.0",
 		"@koa/multer": "3.0.0",
diff --git a/src/misc/twemoji-base.ts b/src/misc/twemoji-base.ts
index e08556bd49..cd50311b15 100644
--- a/src/misc/twemoji-base.ts
+++ b/src/misc/twemoji-base.ts
@@ -1 +1 @@
-export const twemojiSvgBase = 'https://twemoji.maxcdn.com/v/latest/svg';
+export const twemojiSvgBase = '/twemoji';
diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index 8f9b6add6e..35337868d9 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -101,6 +101,22 @@ router.get('/apple-touch-icon.png', async ctx => {
 	});
 });
 
+router.get('/twemoji/(.*)', async ctx => {
+	const path = ctx.path.replace('/twemoji/', '');
+
+	if (!path.match(/^[0-9a-f-]+\.svg$/)) {
+		ctx.status = 404;
+		return;
+	}
+
+	ctx.set('Content-Security-Policy', `default-src 'none'; style-src 'unsafe-inline'`);
+
+	await send(ctx as any, path, {
+		root: `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/`,
+		maxage: ms('30 days'),
+	});
+});
+
 // ServiceWorker
 router.get('/sw.js', async ctx => {
 	await send(ctx as any, `/sw.${config.version}.js`, {
diff --git a/test/fetch-resource.ts b/test/fetch-resource.ts
index 6efe76d8b8..c403f4d395 100644
--- a/test/fetch-resource.ts
+++ b/test/fetch-resource.ts
@@ -91,6 +91,18 @@ describe('Fetch resource', () => {
 			assert.strictEqual(res.status, 200);
 			assert.strictEqual(res.type, 'image/png');
 		}));
+
+		it('GET twemoji svg', async(async () => {
+			const res = await simpleGet('/twemoji/2764.svg');
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(res.type, 'image/svg+xml');
+		}));
+
+		it('GET twemoji svg with hyphen', async(async () => {
+			const res = await simpleGet('/twemoji/2764-fe0f-200d-1f525.svg');
+			assert.strictEqual(res.status, 200);
+			assert.strictEqual(res.type, 'image/svg+xml');
+		}));
 	});
 
 	describe('/@:username', () => {
diff --git a/yarn.lock b/yarn.lock
index b096bfc91e..d2ac3b0e24 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -108,6 +108,16 @@
     ky "^0.25.1"
     ky-universal "^0.8.2"
 
+"@discordapp/twemoji@13.1.0":
+  version "13.1.0"
+  resolved "https://registry.yarnpkg.com/@discordapp/twemoji/-/twemoji-13.1.0.tgz#6b25f3958fa8fd68692248c87776bc737fd009a9"
+  integrity sha512-KEw/te+ylD2MHutzigafyptv0kdTU05Dbgxr9Y5J9IAQw8PbFz16nKtlPnJtA23BLp9fZQeNXzUmegkRi7fpDA==
+  dependencies:
+    fs-extra "^8.0.1"
+    jsonfile "^5.0.0"
+    twemoji-parser "13.1.0"
+    universalify "^0.1.2"
+
 "@discoveryjs/json-ext@^0.5.0":
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752"
@@ -4904,6 +4914,15 @@ fs-constants@^1.0.0:
   resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
   integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
 
+fs-extra@^8.0.1:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
 fs-extra@^9.1.0:
   version "9.1.0"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
@@ -6399,6 +6418,22 @@ json5@^2.1.2, json5@^2.1.3:
   dependencies:
     minimist "^1.2.5"
 
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+jsonfile@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-5.0.0.tgz#e6b718f73da420d612823996fdf14a03f6ff6922"
+  integrity sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==
+  dependencies:
+    universalify "^0.1.2"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
 jsonfile@^6.0.1:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@@ -11211,7 +11246,7 @@ unique-stream@^2.0.2:
     json-stable-stringify-without-jsonify "^1.0.1"
     through2-filter "^3.0.0"
 
-universalify@^0.1.2:
+universalify@^0.1.0, universalify@^0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
   integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==