URLを外部リンク記法に変換するUserScript (TamperMonkeyなし)
from
mod.tsimport { useStatusBar } from "../scrapbox-userscript-std/dom.ts";
import { getWebPageTitle, getTweetInfo } from "../scrapbox-userscript-std/rest.ts";
import type { TweetInfo } from "../scrapbox-jp%2Ftypes/rest.ts";
const checkRegExp = /(https?:\/\/\S+)/;
export const hasURL = (text: string): boolean => checkRegExp.test(text);
export const convert = async (text: string): Promise<string> => {
if (!hasURL(text)) return text;
const { render, dispose } = useStatusBar();
render(
{ type: "spinner" },
{ type: "text", text: "convert URLs..."},
);
let count = 0;
try {
const result = await Promise.all(
text.split(checkRegExp).map(async (fragment) => {
if (!/^https?:\/\/\S+/.test(fragment)) return fragment;
let url: URL;
try {
url = new URL(fragment);
} catch(e: unknown) {
if (e instanceof TypeError) return fragment;
throw e;
}
if (isTwitterURL(url)) {
const result = await getTweetInfo(url);
if (!result.ok) return fragment;
count++;
return formatTweet(url, result.value);
}
const result = await getWebPageTitle(url);
if (!result.ok) return fragment;
count++;
const title = result.value
.replace(/\s/g, " ") // スペースと改行を全て半角スペースにする
.replaceAll("[", "[")
.replaceAll("]", "]");
return `[${url} ${title}]`;
})
);
render(
{ type: "check-circle" },
{ type: "text", text: `Converted ${count} URLs.`},
);
return result.join("");
} catch(e: unknown) {
render(
{ type: "exclamation-triangle" },
{ type: "text", text: e instanceof Error ?
`${e.name} ${e.message}` :
`Unknown error! (see developper console)`,
},
);
console.error(e);
throw e;
} finally {
setTimeout(() => dispose(), 1000);
}
};
Tweetのformat
mod.tsconst isTwitterURL = (url: string | URL) =>
/^https:\/\/(?:www\.|mobile\.|m\.|)twitter\.com\/([A-Za-z0-9_]*)\/(?:status|statuses)\/\d+/.test(url.toString());
const escapeForEmbed = (text: string) => text
.replace(/\b/gm, "")
.replace(/[\s\r\n\u2028\u2029]+/gm, " ")
.replace(/\s*[[\]`]\s*/g, " ")
.trim();
const formatTweet = (url: URL, tweet: TweetInfo) => [
`[${escapeForEmbed(tweet.screenName)}(@${escapeForEmbed(tweet.userName)}) ${url.origin}${url.pathname}]`,
...tweet.description.split("\n").map((line) => `> ${escapeForEmbed(line)}`),
...(tweet.images.length > 0 ? [`> ${tweet.images.map((image) => `[${image}]`)}`] : []),
].join("\n");