Twitter Detail Page Save to Notion

Extract images, tweets from Twitter detail page

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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();
		}
	});

})();