URLを外部リンク記法に変換するUserScript (TamperMonkeyなし)
対応している機能
Tweetを展開する
限界
UTF-8以外の文字コードは文字化けする
Nota側で実装を直してもらう以外解決方法がない
2022-03-03 12:23:32 ひらめいた。2重でproxy serverを経由するのはどうだ?
URLをparameterにつけてアクセスすると、そのページのタイトルを <title>
にしてHTMLを返すserver
2022-06-19 17:24:30 でもこんなことするくらいなら、直接Scrapboxのコードに手を加えたほうが早いよなあ
2022-06-19
18:23:34
URLが見つからないときは即座に処理を終了する
hasURL
を追加した
$ deno check --remote -r=https://scrapbox.io "https://scrapbox.io/api/code/takker/URLを外部リンク記法に変換するUserScript_(TamperMonkeyなし)/mod.ts"
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");