Twitter Detail Page Save to Notion

Extract images, tweets from Twitter detail page

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Twitter Detail Page Save to Notion
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Extract images, tweets from Twitter detail page
// @author       CherryLover
// @match        https://twitter.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @license MIT
// ==/UserScript==

(function() {
	'use strict';

	function extractImages() {
		// const className = "css-175oi2r r-1pi2tsx r-13qz1uu r-eqz5dr";
		const className = 'css-175oi2r r-9aw3ui r-1s2bzr4';
		let images = [];
		let elements = document.getElementsByClassName(className);
		if (elements.length <= 0) {
			return images;
		}

		for (let i = 0; i < elements.length; i++) {
			const target = elements[i];
			// 查找所有属性:data-testid 为 "tweetPhoto" 的 div 元素
			let divElements = target.querySelectorAll('div[data-testid=\'tweetPhoto\']');
			console.log('find ' + divElements.length + ' divs');

			for (let j = 0; j < divElements.length; j++) {
				const innerDiv = divElements[j];
				let imgElements = innerDiv.querySelectorAll('img');
				console.log('find ' + imgElements.length + ' imgs');
				imgElements.forEach((img) => {
					let imgData = {
						src: img.src,
						alt: img.alt
					};
					let imgExist = images.find(element => element.src === imgData.src);
					if (!imgExist) {
						images.push(imgData);
					}
				});
			}

		}
		return images;
	}

	function getTwitterContent() {
		const className = 'css-175oi2r r-1s2bzr4';
		// 获取所有指定类的div元素
		let elements = document.getElementsByClassName(className);
		let text = '';

		// 遍历所有获取到的元素
		if (elements.length <= 0) {
			return text;
		}
		console.log('all element size ', elements.length);
		for (let i = 0; i < elements.length; i++) {
			// console.log("element ", i, " ", elements[i].innerText);
			let children = elements[i].childNodes;

			for (let j = 0; j < children.length; j++) {
				const attrDataTestId = children[j].getAttribute('data-testid');
				if (attrDataTestId === 'tweetText') {
					const tweetText = parseUrlAndText(children[j].childNodes);
					// const tweetText = children[j].innerText;
					console.log('tweet text', tweetText);
					text += tweetText;
				}
			}
		}
		return text;
	}

	function parseUrlAndText(elements) {
		let text = '';
		for (let i = 0; i < elements.length; i++) {
			const element = elements[i];
			if (element.nodeName === 'A') {
				text += element.href + '\n';
			} else {
				text += element.textContent;
			}
		}
		return text;
	}

	function showResults(token, databaseId) {
		let currentUrl = window.location.href;
		console.log('当前页面的URL是: ' + currentUrl);
		// 使用方法
		let allText = '';
		allText = getTwitterContent();
		const imgs = extractImages();

		const header = {
			'Content-Type': 'application/json',
			'Authorization': `Bearer ${token}`,
			'Notion-Version': '2022-02-22'
		};
		let finalText = allText + '\n\n';
		// for (var i = 0; i < imgs.length; i++) {
		// 	finalText += 'img: ' + imgs[i].src + '\n';
		// }
		console.log('token: ', token);
		console.log('databaseId: ', databaseId);

		// alert("Data has been sent to server");

		// title 为 finalText 的前 10 个字符
		let title = '';
		if (finalText.length > 10) {
			title = finalText.substring(0, 10);
		} else {
			title = finalText.replaceAll('\n', '');
			if (title.length === 0) {
				title = 'No title';
			}
		}

		const requestUrl = `https://api.notion.com/v1/pages/`;
		const body = {
			'parent': {
				'database_id': databaseId
			},
			'properties': {
				'Name': {
					'title': [
						{
							'text': {
								'content': title
							}
						}
					]
				},
				'original': {
					'url': currentUrl
				}
			},
			'children': [
				{
					'object': 'block',
					'type': 'paragraph',
					'paragraph': {
						'rich_text': [
							{
								'type': 'text',
								'text': {
									'content': finalText
								}
							}
						]
					}
				}
			]
		};

		imgs.forEach((img) => {
			// 向正文中添加图片 及 alt
			// body.properties.Content.text.push({
			// 	'image': {
			// 		'type': 'external',
			// 		'external': {
			// 			'url': img.src
			// 		}
			// 	}
			// });
			body.children.push({
				'object': 'block',
				'type': 'embed',
				'embed': {
					'caption': [
						{
							'type': 'text',
							'text': {
								'content': img.alt
							}
						}
					],
					'url': img.src
				}
			});
		});

		console.log('request body ' + JSON.stringify(body));
		GM_xmlhttpRequest({
			method: 'POST',
			url: requestUrl,
			headers: header,
			data: JSON.stringify(body),
			onload: function(response) {
				console.log('Response: ', response);
				alert('Data has been sent to server');
			},
			onerror: function(response) {
				console.log('Error: ', response);
				alert('Error: ' + response);
			}
		});

		// GM_xmlhttpRequest({
		//     method: "POST",
		//     url: `https://${domain}/api/v1/memo`,
		//     headers: header,
		//     data: JSON.stringify({
		//         content: finalText,
		//     }),
		//     onload: function(response) {
		//         console.log("Response: ", response);
		//         alert("Data has been sent to server");
		//     },
		//     onerror: function(response) {
		//         console.log("Error: ", response);
		//         alert("Error: " + response);
		//     }
		// });
	}

	// Create input for domain
	let databaseIdInput = document.createElement('input');
	databaseIdInput.placeholder = 'Database Id';
	databaseIdInput.style.position = 'fixed';
	databaseIdInput.style.bottom = '50px';
	databaseIdInput.style.left = '10px';
	databaseIdInput.style.zIndex = '9999';

// Create input for accessToken
	let tokenInput = document.createElement('input');
	tokenInput.placeholder = 'Integrations Secret';
	tokenInput.style.position = 'fixed';
	tokenInput.style.bottom = '30px';
	tokenInput.style.left = '10px';
	tokenInput.style.zIndex = '9999';

// Append inputs to body
	document.body.appendChild(databaseIdInput);
	document.body.appendChild(tokenInput);

	// Create button
	let button = document.createElement('button');
	button.textContent = '保存到 Notion';
	button.style.position = 'fixed';
	button.style.bottom = '10px';
	button.style.left = '10px';
	button.style.zIndex = '9999';
	button.onclick = function() {
		const currentUrl = window.location.href;
		// 判断当前页面是否是推特详情页
		if (!currentUrl.includes('status')) {
			alert('请进入具体推文详情页面后再点击');
			return;
		}
		let dbId = '';
		let token = '';
		if (databaseIdInput.style.display === 'block') {
			dbId = databaseIdInput.value;
		} else {
			dbId = GM_getValue('databaseId', '');
		}
		if (tokenInput.style.display === 'block') {
			token = tokenInput.value;
		} else {
			token = GM_getValue('accessToken', '');
		}
		if (!dbId || !token) {
			alert('请配置 Notion 数据库 ID 和 Integrations Secret');
			databaseIdInput.style.display = 'block';
			tokenInput.style.display = 'block';
			return;
		}
		GM_setValue('databaseId', dbId);
		GM_setValue('accessToken', token);
		databaseIdInput.style.display = 'none';
		tokenInput.style.display = 'none';
		showResults(token, dbId);
	};

	// Append button to body
	document.body.appendChild(button);

// Initially hide the inputs
	databaseIdInput.style.display = 'none';
	tokenInput.style.display = 'none';
	var settingShow = false;

	function toggleSettings() {
		settingShow = !settingShow;
		if (settingShow) {
			databaseIdInput.style.display = 'block';
			tokenInput.style.display = 'block';
			const cDatabaseId = GM_getValue('databaseId', '');
			const cToken = GM_getValue('accessToken', '');

			databaseIdInput.value = cDatabaseId;
			tokenInput.value = cToken;
			databaseIdInput.focus();
		} else {
			GM_setValue('databaseId', databaseIdInput.value);
			GM_setValue('accessToken', tokenInput.value);
			databaseIdInput.style.display = 'none';
			tokenInput.style.display = 'none';
		}
	}

	window.addEventListener('keydown', function(event) {
		if (event.key === 'F9') {
			toggleSettings();
		}
	});

})();