modularization
This commit is contained in:
parent
fd04f4024f
commit
ed1d500c23
7 changed files with 1233 additions and 76 deletions
|
@ -4,12 +4,15 @@
|
|||
"author": "Jörn-Michael Miehe <joern-michael.miehe@lenaisten.de>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"build": "webpack --mode=production",
|
||||
"lint-ext": "web-ext lint --source-dir=dist",
|
||||
"build-ext": "web-ext build --source-dir=dist --artifacts-dir=dist --overwrite-dest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/preset-env": "^7.23.7",
|
||||
"@types/chrome": "^0.0.254",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"prettier": "^3.1.1",
|
||||
"ts-loader": "^9.5.1",
|
||||
|
|
28
src/bttv_api.ts
Normal file
28
src/bttv_api.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
type BTTV_Emote = {
|
||||
id: string;
|
||||
code: string;
|
||||
imageType: string;
|
||||
animated: boolean;
|
||||
user: any;
|
||||
};
|
||||
|
||||
export async function bttv_get_url(
|
||||
code: string,
|
||||
): Promise<[string, string] | null> {
|
||||
const res = await fetch(
|
||||
`https://api.betterttv.net/3/emotes/shared/search?query=${code}&offset=0&limit=50`,
|
||||
);
|
||||
const content = await res.json();
|
||||
|
||||
if (!Array.isArray(content)) return null;
|
||||
const matches = (content as BTTV_Emote[]).filter(
|
||||
(be) => be.code.toLowerCase() == code.toLowerCase(),
|
||||
);
|
||||
|
||||
if (matches.length <= 0) return null;
|
||||
|
||||
return [
|
||||
code,
|
||||
`//cdn.betterttv.net/emote/${matches[0].id}/1x.${matches[0].imageType}`,
|
||||
];
|
||||
}
|
12
src/helpers.ts
Normal file
12
src/helpers.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
type MapFunction<In, Out> = (input: In) => Promise<[In, Out] | null>;
|
||||
|
||||
export async function map_parallel<In, Out>(
|
||||
inputs: Iterable<In>,
|
||||
map_fn: MapFunction<In, Out>,
|
||||
): Promise<Map<In, Out>> {
|
||||
const queue = [...inputs].map((input) => map_fn(input));
|
||||
|
||||
return new Map<In, Out>(
|
||||
(await Promise.all(queue)).filter((job): job is [In, Out] => job !== null),
|
||||
);
|
||||
}
|
82
src/main.ts
82
src/main.ts
|
@ -1,73 +1,33 @@
|
|||
import { bttv_get_url } from "./bttv_api";
|
||||
import { map_parallel } from "./helpers";
|
||||
|
||||
const emote_regex = /bttv:([^\s]+)/gi;
|
||||
|
||||
type Emote = {
|
||||
code: string;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
type BTTV_Emote = {
|
||||
id: string;
|
||||
code: string;
|
||||
imageType: string;
|
||||
animated: boolean;
|
||||
user: any;
|
||||
};
|
||||
|
||||
async function bttv_get_url(code: string): Promise<Emote> {
|
||||
const res = await fetch(
|
||||
`https://api.betterttv.net/3/emotes/shared/search?query=${code}&offset=0&limit=50`,
|
||||
);
|
||||
const content = await res.json();
|
||||
|
||||
if (!Array.isArray(content)) return { code: code };
|
||||
const matches = (content as BTTV_Emote[]).filter(
|
||||
(be) => be.code.toLowerCase() == code.toLowerCase(),
|
||||
);
|
||||
|
||||
if (matches.length <= 0) return { code: code };
|
||||
|
||||
return {
|
||||
code: code,
|
||||
url: `//cdn.betterttv.net/emote/${matches[0].id}/1x.${matches[0].imageType}`,
|
||||
};
|
||||
}
|
||||
|
||||
type EmoteMap = { [code: string]: string };
|
||||
|
||||
async function get_emotes(codes: Iterable<string>): Promise<EmoteMap> {
|
||||
const codes_set = new Set(codes);
|
||||
|
||||
// queue emote jobs
|
||||
const queue = [...codes_set].map((code) => bttv_get_url(code));
|
||||
console.log("[lmlfc-bttv] queued", queue.length, "jobs");
|
||||
|
||||
// run
|
||||
const result: EmoteMap = {};
|
||||
for (const job of await Promise.all(queue)) {
|
||||
if (job.url) result[job.code] = job.url;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function process_text(text: string): Promise<string> {
|
||||
const matches = [...text.matchAll(emote_regex)];
|
||||
const emotes = await get_emotes(matches.map(([_, code]) => code));
|
||||
const codes = new Set(matches.map(([_, code]) => code));
|
||||
console.log("[lmlfc-bttv]", `searching ${[...codes].length} emotes`);
|
||||
const emotes = await map_parallel(codes, bttv_get_url);
|
||||
console.debug("matches", matches, "codes", codes, "emotes", emotes);
|
||||
|
||||
for (const emote of matches.reverse()) {
|
||||
const [match, code] = emote;
|
||||
const index = emote.index ?? 0;
|
||||
if (!(code in emotes)) continue;
|
||||
console.debug("match", match, "code", code);
|
||||
|
||||
if (emote.index == undefined) continue;
|
||||
const index = emote.index;
|
||||
|
||||
if (!emotes.has(code)) continue;
|
||||
const url = emotes.get(code);
|
||||
console.debug("index", index, "url", url);
|
||||
|
||||
text =
|
||||
text.substring(0, index) +
|
||||
`[not]${emotes[code]}[/not]` +
|
||||
`[not]${url}[/not]` +
|
||||
text.substring(index + match.length);
|
||||
}
|
||||
|
||||
text = text.replace(/\[\/not\]\s+\[not\]/g, "[/not][not]");
|
||||
|
||||
return text;
|
||||
return text.replace(/\[\/not\]\s+\[not\]/g, "[/not][not]");
|
||||
}
|
||||
|
||||
(() => {
|
||||
|
@ -79,6 +39,9 @@ async function process_text(text: string): Promise<string> {
|
|||
);
|
||||
if (!(cb_send instanceof HTMLInputElement)) return;
|
||||
|
||||
const cb_input = document.querySelector("#mgc_cb_evo_input");
|
||||
if (!(cb_input instanceof HTMLInputElement)) return;
|
||||
|
||||
const bttv_btn = (() => {
|
||||
const btn = document.createElement("a");
|
||||
btn.style.setProperty("cursor", "pointer");
|
||||
|
@ -91,9 +54,6 @@ async function process_text(text: string): Promise<string> {
|
|||
img.style.setProperty("vertical-align", "middle");
|
||||
|
||||
btn.addEventListener("click", async () => {
|
||||
const cb_input = document.querySelector("#mgc_cb_evo_input");
|
||||
if (!(cb_input instanceof HTMLInputElement)) return;
|
||||
|
||||
cb_input.value = await process_text(cb_input.value);
|
||||
cb_input.focus();
|
||||
cb_send.click();
|
||||
|
@ -105,5 +65,5 @@ async function process_text(text: string): Promise<string> {
|
|||
|
||||
cb_form.insertBefore(bttv_btn, cb_send);
|
||||
|
||||
console.log("done.");
|
||||
console.log("[lmlfc-bttv]", `loaded`);
|
||||
})();
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"module": "CommonJS",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true,
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "dist/js",
|
||||
// flags
|
||||
"alwaysStrict": true,
|
||||
"strictNullChecks": true,
|
||||
"noEmitOnError": true,
|
||||
"typeRoots": ["node_modules/@types"]
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ const path = require("path");
|
|||
const CopyPlugin = require("copy-webpack-plugin");
|
||||
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
entry: {
|
||||
main: path.join(__dirname, "src", "main.ts"),
|
||||
},
|
||||
|
@ -16,9 +15,19 @@ module.exports = {
|
|||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
test: /\.[jt]s$/,
|
||||
exclude: /(node_modules)/,
|
||||
use: [
|
||||
{
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["@babel/preset-env"],
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: "ts-loader",
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue