Tweetを取り込むPopup menu
APIのpathの customize
をこのプロジェクトの名前 motoso
に置換しています
Font Awesomeを入れていないので文字に変更
フォーマットを改変
必要な情報をこのあたりで入れている
script.jsimport {convertWholeText} from '/api/code/motoso/Tweetを取り込むPopup menu/convert.js';
// https://scrapbox.io/customize/scrapbox-insert-text
export function insertText({text}) {
const cursor = document.getElementById('text-input');
cursor.focus();
const start = cursor.selectionStart; // in this case maybe 0
cursor.setRangeText(text);
cursor.selectionStart = cursor.selectionEnd = start + text.length;
const uiEvent = document.createEvent('UIEvent');
uiEvent.initEvent('input', true, false);
cursor.dispatchEvent(uiEvent);
}
scrapbox.PopupMenu.addButton({
title: 'Embed a tweet',
onClick: text => {
if (!/https:\/\/twitter\.com\S+\/status\/\d+/.test(text)) return; // URLがなければ何もしない
const cursor = document.getElementById('text-input')
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({text: lines.join('\n'), cursor: cursor}));
// 入力しやすいよう選択範囲を先に消しておく
return '';
},
});
tweet情報を整形して返す
convert.jsimport {getTweetInfo} from '/api/code/motoso/Tweetを取り込むPopup menu/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.jsfunction convert({tweetInfo, indent}) {
return [
...tweetInfo.content.map(text => `${indent}> [${tweetInfo.author.screenName} ${tweetInfo.date.href}]: ${text}`)
].join('\n');
}
こうなる
> @motoso: Twitterのようなバイラルを求めつつ規制を受けないというのは多分無理なので棲み分けがめちゃくちゃ重要だと思うんだよね
convert.js.orgfunction convert({tweetInfo, indent}) {
return [
...tweetInfo.content.map(text => `${indent}> ${text}`),
`${indent}> \t${tweetInfo.signature}[${tweetInfo.date.href} ${tweetInfo.date.text}]`
].join('\n');
}
こうなる
> Twitterのようなバイラルを求めつつ規制を受けないというのは多分無理なので棲み分けがめちゃくちゃ重要だと思うんだよね
tweet取得処理
getTweetInfo.jsexport async function getTweetInfo({tweetUrl} = {}) {
if (!window.getTweetInfo) {
alert('Please install "getTweetInfo" from https://scrapbox.io/api/code/motoso/Tweetを取り込むPopup menu/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`);
// @screenNameを取得
const regexp = /twitter.com\/(\S+)/g;
const screenName = '@' + [...tweet.author_url.matchAll(regexp)][0][1];
// 各種情報を詰め込んで返す
return {
author: {
name: tweet.author_name,
screenName: screenName,
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.js// ==UserScript==
// @name twitter-info-proxy
// @namespace https://scrapbox.io
// @version 0.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==
;(function () {
"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",
})
})
}
})()