generated at
scrapbox-embed-tweet
twitterのtweet URLから直接scrapbox用の引用テキストを作るPopupMenu

install方法
import.js
import '/api/code/takker/scrapbox-embed-tweet/script.js';
他人のprojectからimportするのは安全上問題があるので、codeをコピペすることをおすすめします
その際ファイルパスを適宜変更して下さい
それからこのUserscriptTamperMonkeyにinstallして下さい

使い方
tweet URLを含んだ文を選択して、PopupMenuのボタンを押すと変換される
URLが複数入っていたときは、その全てを変換する
変換形式はscrapbox-embed-tweet#5fceff1b1280f00000ca7c8fをいじると変えられる

既知の問題
done末尾についている ?s=20 などのURL parameterを除去できない
?s=\d+ にだけ即席で対応しておいた
他のURL parameterは無理

類似UserScript
参考にしたscript
TamperMonkeyが不要な代わりに、埋め込み用HTMLを毎回手動で取得しなくてはならない
テキストではなくtwitterのscreenshotを貼り付けるscript
本UserScriptと同様、TemperMonkeyを使って外部APIを叩きにいっている

2021-06-12
12:34:35
2021-06-08
02:46:58 取得した埋め込み用HTMLに <a> <br> が見つからないときとエラーになってた
2021-05-26
15:02:24
libraryのupdate
tweet URLが無い時はボタンを非表示にする

UserScript
script.js
import {insertText} from '../scrapbox-insert-text-2/script.js'; import {convertWholeText} from '../scrapbox-embed-tweet/convert.js'; scrapbox.PopupMenu.addButton({ title: text => /https:\/\/twitter\.com\S+\/status\/\d+/.test(text) ? '\uf081' : // Font Awesomeでないと文字化けします '', onClick: text => { if (!/https:\/\/twitter\.com\S+\/status\/\d+/.test(text)) return; // URLがなければ何もしない Promise.all(text.split('\n').map(line => { const matches = line.match(/^\s+|.*/g); const indent = /^\s+$/.test(matches[0])? matches[0] : ''; const content = /^\s+$/.test(matches[0])? matches[1] : matches[0]; console.log([indent,content]); return convertWholeText(content, indent) })).then(lines => insertText(lines.join('\n'))); // 入力しやすいよう選択範囲を先に消しておく return ''; }, });

tweet情報を整形して返す
convert.js
import {getTweetInfo} from '/api/code/takker/scrapbox-embed-tweet/getTweetInfo.js'; // 複数のURLを含んだテキストをまとめて変換する export async function convertWholeText(text,indent) { const tweetRegExp = /https:\/\/twitter\.com\S+\/status\/\d+(?:\?s=\d+)?/g; const urls = text.match(tweetRegExp) ?? []; if (urls.length === 0) return undefined; const tweets = (await Promise.all(urls.map(url => getTweetInfo({tweetUrl: url})))) .map(tweetInfo => convert({ tweetInfo, indent, })); console.log(tweets); let map = {}; for (const originalUrl of urls) { const i = urls.indexOf(originalUrl); if (!tweets[i]) break; map[originalUrl]= tweets[i]; } //console.log(map); const result = text.replace(tweetRegExp, match => map[match] ?? match); //console.log(result); return result; }

引用の形を決めるところ
この関数をいじってお好みのformatにしてください 
convert.js
function convert({tweetInfo, indent}) { return [...tweetInfo.content.map(text => `${indent}> ${text}`), `${indent}> \t${tweetInfo.signature}[${tweetInfo.date.href} ${tweetInfo.date.text}]`].join('\n'); }

tweet取得処理
getTweetInfo.js
export async function getTweetInfo({tweetUrl} = {}) { if (!window.getTweetInfo) { alert('Please install "getTweetInfo" from https://scrapbox.io/api/code/takker/scrapbox-embed-tweet/temperMonkey.js'); return; } try { const tweet = await window.getTweetInfo(`https://publish.twitter.com/oembed?url=${tweetUrl}`) .then(req => req.response); // 埋め込み用HTMLを解析する const dummy = document.createElement('div'); dummy.insertAdjacentHTML('beforeend', tweet.html); console.log(tweet.html); // <blockquote>を取得 const blockquote =dummy.getElementsByTagName('blockquote')?.[0]; // 要素に分解 const [contentDOM, signatureDOM, dateDOM] = [...blockquote?.childNodes]; // tweet本文をscrapbox記法に変換 [...contentDOM?.getElementsByTagName('a')] ?.forEach?.(link => link.innerHTML = `[${link.href} ${link.textContent}]`); [...contentDOM?.getElementsByTagName('br')] ?.forEach?.(br => br.innerHTML =`\n`); // 各種情報を詰め込んで返す return { author: { name: tweet.author_name, url: tweet.author_url, }, content: contentDOM?.textContent.split('\n'), signature: signatureDOM?.textContent, date : { href: dateDOM?.href, text: dateDOM?.textContent, }, }; } catch(e) { console.error(e); } }


TemperMonkeyに貼り付けるコード
url-info-proxyをコピペして少しいじった
temperMonkey.js
// ==UserScript== // @name twitter-info-proxy // @namespace https://scrapbox.io // @version 0.1.1 // @description fetch the information of a tweet // @author takker // @match https://scrapbox.io/* // @connect publish.twitter.com // @grant GM_xmlhttpRequest // @license MIT // @copyright Copyright (c) 2020 takker // ==/UserScript== "use strict" unsafeWindow.getTweetInfo = (url) => { const u = new URL(url) if (!['publish.twitter.com'].includes(u.hostname)) { throw Error("unexpected url!") } return new Promise((r) => { GM_xmlhttpRequest({ method: "GET", url, onload: (res) => r(res), withCredentials: true, responseType: "json", }) }) };


#2021-06-12 12:34:29
#2021-06-08 02:46:43
#2021-05-26 15:03:42
#2021-01-17 10:35:42
#2020-11-14 15:25:27