[Twitter]ツイートをdiscordへ

ツイートをdiscordにウェブフックでポストします

目前為 2023-08-05 提交的版本,檢視 最新版本

// ==UserScript==
// @name			[Twitter]ツイートをdiscordへ
// @namespace		https://gf.qytechs.cn/ja/users/1023652
// @version			1145141919810.0.0
// @description		ツイートをdiscordにウェブフックでポストします
// @author			ゆにてぃー
// @match			https://twitter.com/*
// @connect			discord.com
// @connect			api.twitter.com
// @connect			api.fanbox.cc
// @connect			skeb.jp
// @connect			fantia.jp
// @connect			booth.pm
// @connect			linktr.ee
// @connect			profcard.info
// @connect			lit.link
// @connect			potofu.me
// @connect			creatorlink.net
// @connect			lab.syncer.jp
// @connect			carrd.co
// @connect			sketch.pixiv.net
// @connect			tumblr.com
// @connect			html.co.jp
// @connect			twpf.jp
// @connect			pbs.twimg.com
// @icon			data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJkSURBVHgB7VZBbtpQEH3zIW0WVYuXVaH4Bs0NSk4AOUFhEarskhMknIDsqkKlcIT0BNAT1D1B3ZJK3dmVuirwp/MhVmzAxiagKBJv9+ePZ97M/JkxsMMODwzChlD84FWQp3MxeCDHAhiumB+MJrr1+8Ryw3p/9+H4DctfIPCq49Xlw8Kv99YlMuB19885gy/i7llziwGfFFWJyR02XzSCuwiBUse7BlFVaz5LS8KQVkRXaXRJsqImfDjKSZBNyzEyFWFKVJ4KFbWLElUao6KbSk8i9TXgTPaorxTskPwOxa7/9baGt4zg8oQbNyfWYJlRU0/KUx9ZwNwYNq1ecFRzl18QpW0bB0Ks//KjV1uwlbuLJA3GxEdh5wb5yGEPl3qMd2xecYQHKnlFlVLX95kxYCFKGg5IlU2a0uLpCM68LEJA+sJ/Dm6Jy3aMjQIRakRUm+UuvfOp/X34iQSejeFo0Hdx4optG5uFH/R+GHNvANcm3VtwLs+Lvy2TRwhIOnrYHhysIuDKcCDwGbYAjglOzQt+HssElF6dvoNNOZeuCSbfSgIGMjILMo4/ExZf7TqghNLmlwm1gpSC2tmaLAZMvWGz0Iu7XpqBm2NrQNN5cD+Y5ZOTdZyok3RZMusZOJUN+QZrQFb0oQkG6xIIYHe8A03Unx/Ryd6jS2ctAsbxmFRVynGKlM5na5ePVkUe0p+h9MmraS2zXqYgmSWjOPtElHbLTVB3Q79gqQlMScxqXpeav0UWiGMmXKSNOpZAAPvKs/U/1MRoxRxl+5WD+psUy2D5IdmRVoWjnqDnLlkyO+zwaPAf1zXwZL751PUAAAAASUVORK5CYII=
// @grant			GM_xmlhttpRequest
// @license			MIT
// ==/UserScript==

(function() {
	'use strict';
	const desktop_env = {'tweet_field': 'article[data-testid="tweet"]','retweeted': '[data-testid="socialContext"]','liked_color': 'r-vkub15','liked':'M20.884 13.19c-1.351 2.48-4.001 5.12-8.379 7.67l-.503.3-.504-.3c-4.379-2.55-7.029-5.19-8.382-7.67-1.36-2.5-1.41-4.86-.514-6.67.887-1.79 2.647-2.91 4.601-3.01 1.651-.09 3.368.56 4.798 2.01 1.429-1.45 3.146-2.1 4.796-2.01 1.954.1 3.714 1.22 4.601 3.01.896 1.81.846 4.17-.514 6.67z'};
	const mobile_env = {'tweet_field': 'article[data-testid="tweet"]','retweeted': '[data-testid="socialContext"]','liked_color': 'r-vkub15','liked':'M20.884 13.19c-1.351 2.48-4.001 5.12-8.379 7.67l-.503.3-.504-.3c-4.379-2.55-7.029-5.19-8.382-7.67-1.36-2.5-1.41-4.86-.514-6.67.887-1.79 2.647-2.91 4.601-3.01 1.651-.09 3.368.56 4.798 2.01 1.429-1.45 3.146-2.1 4.796-2.01 1.954.1 3.714 1.22 4.601 3.01.896 1.81.846 4.17-.514 6.67z'};
	const webHooks = {
		"Enter your preferred name": 'Your Web Hook URL',
		//"Enter your preferred name2": "Your Web Hook URL2"
	};
	const def_server = "Default Display Name for the Webhook";
	const timeZoneObject = Intl.DateTimeFormat().resolvedOptions();
	const lang = GetCookie("lang");
	let Text = {};
	Text.ja = {
		"various_links": "各種リンク",
		"link_to_tweet": "ツイートへ",
		"link_to_image": "画像へのリンク",
		"engagement": "エンゲージメント",
		"likes": "いいね",
		"retweets": "リツイート",
		"units": "万",
		"roundingScale": 10000,
		"decimalPlaces": 2,
		"postedDate": "投稿日時",
		"quotedTweet": "↓♻️引用元♻️↓",
		"submit": "送信"
	};

	Text.en = {
		"various_links": "Various Links",
		"link_to_tweet": "To Tweet",
		"link_to_image": "Link to Image",
		"engagement": "Engagement",
		"likes": "Likes",
		"retweets": "Retweets",
		"units": "k",
		"roundingScale": 1000,
		"decimalPlaces": 1,
		"postedDate": "Posted Date",
		"quotedTweet": "↓♻️Quoted Tweet♻️↓",
		"submit": "submit"
	};
	const env_Text = Text[lang] || Text.en;
	var env_selector;
	function isMobileDevice(){
		const userAgent = navigator.userAgent || navigator.vendor || window.opera;
		return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
	}
	if(isMobileDevice()){
		env_selector = mobile_env;
	}else{
		env_selector = desktop_env;
	}
	let updating = false;
	window.addEventListener("scroll", update);
	init();
	async function main(tweets){
		tweets.forEach(function(element){
			if(element.querySelector(".quickDimg")) return;
			let tweet_link = Array.from(element.querySelectorAll("a[aria-label]")).filter(function(tmp){return tmp.href.match(/\/status\/[0-9]*(\/analytics)?$/)})[0].href.replace(/\/analytics$/,'');
			let fotter = element.querySelector('div[id][role="group"]');
			const flexContainer = document.createElement('div');
			flexContainer.classList.add('quickDimg');
			flexContainer.style.display = 'flex';

			const dropdown_select_server = document.createElement('select');
			for(const server in webHooks){
				const option = document.createElement('option');
				option.value = webHooks[server];
				option.textContent = server;
				if(server === def_server){
					option.selected = true;
				}
				dropdown_select_server.appendChild(option);
			}
			flexContainer.appendChild(dropdown_select_server);
			dropdown_select_server.addEventListener('click', (event) => {
				event.stopPropagation();
			});

			const dropdown_send_image = document.createElement('select');
			for(let i = 1; i <= 5; i++){
				const option = document.createElement('option');
				option.value = i;
				option.textContent = i;
				if(i === 5){
					option.selected = true;
				}
				dropdown_send_image.appendChild(option);
			}
			flexContainer.appendChild(dropdown_send_image);

			dropdown_send_image.addEventListener('click', (event) => {
				event.stopPropagation();
			});
			const dropdown_post_quote = document.createElement('select');
			['false','true'].forEach(value => {
				const option = document.createElement('option');
				option.value = value;
				option.textContent = value;
				dropdown_post_quote.appendChild(option);
			});
			flexContainer.appendChild(dropdown_post_quote);

			dropdown_post_quote.addEventListener('click', (event) => {
				event.stopPropagation();
			});

			const dropdown_use_graphql = document.createElement('select');
			['false','true'].forEach(value => {
				const option = document.createElement('option');
				option.value = value;
				option.textContent = value;
				dropdown_use_graphql.appendChild(option);
			});
			flexContainer.appendChild(dropdown_use_graphql);

			dropdown_use_graphql.addEventListener('click', (event) => {
				event.stopPropagation();
			});
			// ボタンを作成
			const button = document.createElement('button');
			button.textContent = env_Text.submit;
			flexContainer.appendChild(button);

			// ボタンのクリックイベントを監視
			button.addEventListener('click',async function(){
				// ここでドロップダウンの選択値に基づいて処理を行う
				this.disabled = true;
				const selectedServer = dropdown_select_server.value;
				const selectedNumber = dropdown_send_image.value;
				const send_post_tweet = dropdown_post_quote.value === 'true';
				const useGraphql = dropdown_use_graphql.value === 'true';
				let num = new Date().getTime();
				let send_page;
				if(selectedNumber != 5){
					send_page = [selectedNumber-1]
				}else{
					send_page = [0,1,2,3]
				}
				const body = await make_send_data(tweet_link,send_page,send_post_tweet,useGraphql);
				await sleep(300)
				for(let target in body){
					let formData = new FormData();
					let payload = {};
					let tmp = body[target];
					if(tmp.embeds){
						payload.embeds = tmp.embeds;
					}
					if(tmp.content){
						payload.content = tmp.content;
					}
					formData.append('payload_json', JSON.stringify(payload));
					//console.log(formData)
					if(tmp.files){
						tmp.files.forEach((file, index) => {
							formData.append(`file${index}`, file.attachment, file.name);
						});
					}
					let res = await request(new sendObject(selectedServer,formData));
					//console.log(res)
					await sleep(1000)
				}
			});
			fotter.parentNode.appendChild(flexContainer);
		});
	}
	async function make_send_data(tweet_link,select_pages = [1],send_quoted_tweet,use_graphQL){
		const tweet_id = tweet_link.match(/https?:\/\/twitter\.com\/\w+\/status\/(\d+)/)[1];
		let tweet_data = await get_Tweet_data();
		let return_object = await make_embeds();
		if(send_quoted_tweet && return_object[return_object.length - 1].quoted_tweet_data){
			tweet_data = return_object.pop().quoted_tweet_data;
			return_object = return_object.concat([{content: env_Text.quotedTweet}],await make_embeds(1));
		}
		return return_object;
		async function make_embeds(quoted_tweet_mode = 0){
			let embeds = [];
			let tmpEmbed = {};
			let tmp_return_object = [];
			var twitter_user_data = {};
			var twitter_tweet_data = {};
			var tweet_user_data_json = {};
			var tweet_tweet_data_json = {};
			switch(tweet_data.APIsource){
				case "graphql":
					tweet_user_data_json = tweet_data.result.core?.user_results.result || tweet_data.result.tweet.core.user_results.result;
					tweet_tweet_data_json = tweet_data.result.legacy || tweet_data.result.tweet.legacy;
					twitter_user_data.ID = tweet_user_data_json.rest_id;
					twitter_user_data.screen_name = tweet_user_data_json.legacy.screen_name;
					twitter_user_data.name = tweet_user_data_json.legacy.name;
					twitter_user_data.profile_image = tweet_user_data_json.legacy.profile_image_url_https.replace('_normal.','.');;
					twitter_user_data.urls = tweet_user_data_json.legacy.entities;
					twitter_tweet_data.hashtags = get_only_particular_key_value(tweet_tweet_data_json.entities,"hashtags.text",[]);
					twitter_tweet_data.user_mentions = get_only_particular_key_value(tweet_tweet_data_json.entities,"user_mentions.screen_name",[]);
					break;
				case "1_1":
					tweet_user_data_json = tweet_data.user;
					tweet_tweet_data_json = tweet_data;
					twitter_user_data.ID = tweet_user_data_json.id_str;
					twitter_user_data.screen_name = tweet_user_data_json.screen_name;
					twitter_user_data.name = tweet_user_data_json.name;
					twitter_user_data.profile_image = tweet_user_data_json.profile_image_url_https.replace('_normal.','.');;
					twitter_user_data.urls = tweet_user_data_json.entities;
					twitter_tweet_data.hashtags = get_only_particular_key_value(tweet_tweet_data_json.entities,"hashtags.text",[]);
					twitter_tweet_data.user_mentions = get_only_particular_key_value(tweet_tweet_data_json.entities,"user_mentions.screen_name",[]);
					break;
			}
			try{
				twitter_user_data.pixiv_url = await find_pixiv_link(twitter_user_data.urls);
			}catch(error){
				console.log("pixivのURLの取得に失敗しました。");
				console.log(error);
			}
			twitter_tweet_data.full_text = replace_null_to_something(tweet_tweet_data_json.full_text);
			twitter_tweet_data.extended_entities = tweet_tweet_data_json.extended_entities;
			twitter_tweet_data.retweet_count = tweet_tweet_data_json.retweet_count;
			twitter_tweet_data.favorite_count = tweet_tweet_data_json.favorite_count;
			twitter_tweet_data.id = tweet_tweet_data_json.id_str;
			twitter_tweet_data.created_at = new Date(tweet_tweet_data_json.created_at).toLocaleString(timeZoneObject.locale, { timeZone: timeZoneObject.timeZone });
			twitter_tweet_data.urls = tweet_tweet_data_json.entities.urls;
			twitter_tweet_data.media = make_media_list(twitter_tweet_data.extended_entities,select_pages);
			twitter_tweet_data.hashtags.forEach(target => {
				twitter_tweet_data.full_text = twitter_tweet_data.full_text.replace(new RegExp(`(#|#)${target}(?=(\\s|$|\\u3000|\\W)(?!\\.|,))`, 'gu'), `[#${target}](https://twitter.com/hashtag/${target})`);
			});
			twitter_tweet_data.user_mentions.forEach(target =>{
				twitter_tweet_data.full_text = twitter_tweet_data.full_text.replace(new RegExp(`@${target}(?=(\\s|$|\\u3000|\\W)(?!\\.|,))`, 'gu'), `[@${target}](https://twitter.com/${target})`);
			});
			try{
				//文が長すぎるとエラーになるので一定の長さで切る。
				//普通のツイートではそんなことありえないが、Blueでは長いツイートが可能なのでそれに対応している。
				let note_tweet = tweet_data.result.note_tweet?.note_tweet_results.result||tweet_data.result.tweet.note_tweet.note_tweet_results.result;
				twitter_tweet_data.full_text = str_max_length(note_tweet.text,7000);
				twitter_tweet_data.urls = note_tweet.entity_set.urls;
				get_only_particular_key_value(note_tweet.entity_set,"hashtags.text",[]).forEach(target =>{
					twitter_tweet_data.full_text = twitter_tweet_data.full_text.replace(new RegExp(`(#|#)${target}(?=(\\s|$|\\u3000|\\W)(?!\\.|,))`, 'gu'), `[#${target}](https://twitter.com/hashtag/${target})`);
				});
				get_only_particular_key_value(note_tweet.entity_set,"user_mentions.screen_name",[]).forEach(target =>{
					twitter_tweet_data.full_text = twitter_tweet_data.full_text.replace(new RegExp(`@${target}(?=(\\s|$|\\u3000|\\W)(?!\\.|,))`, 'gu'), `[@${target}](https://twitter.com/${target})`);
				});
			}catch{}
			try{
				//複数メディアをつけるオプションのときに動画があるとうまくいかないので。
				if(select_pages.length > 1 && ! twitter_tweet_data.media.every(v => v.media_type == "photo")){
					twitter_tweet_data.media = [twitter_tweet_data.media[0]];
				}
			}catch{}

			let tweet_url;
			if(!quoted_tweet_mode == 1){
				tweet_url = tweet_link;
			}else{
				tweet_url = `https://twitter.com/${twitter_user_data.screen_name}/status/${twitter_tweet_data.id}`;
			}
			tmpEmbed.color = 1940464;
			tmpEmbed.title = "Tweet";
			tmpEmbed.url = tweet_url;
			tmpEmbed.author = {
				"name": twitter_user_data.name,
				"url": `https://twitter.com/${twitter_user_data.screen_name}`,
				"icon_url": `attachment://${twitter_user_data.profile_image.split('/').pop()}`
			};
			tmpEmbed.description = replace_t_co_to_original_url(twitter_tweet_data.full_text,twitter_tweet_data.urls,twitter_tweet_data.media);
			tmpEmbed.thumbnail = {"url": "https://pbs.twimg.com/profile_images/1488548719062654976/u6qfBBkF_400x400.jpg"};
			tmpEmbed.fields = [
				{
					"name": `${env_Text.various_links}:link:`,
					"value": `[${env_Text.link_to_tweet}](${tweet_url})` + `\n[TwitterID: ${twitter_user_data.ID}](https://twitter.com/intent/user?user_id=${twitter_user_data.ID})` + if_exsit_return_text(twitter_tweet_data.media[0]?.url,`\n[${env_Text.link_to_image}](${image_url_to_original(twitter_tweet_data.media[0]?.url)})`) + if_exsit_return_text(twitter_user_data.pixiv_url,`\n[Pixiv](${twitter_user_data.pixiv_url})`)
				},{
					"name": env_Text.engagement,
					"value": `${env_Text.likes} ${round_half_up(twitter_tweet_data.retweet_count,env_Text.roundingScale,env_Text.decimalPlaces,env_Text.units)}:recycle:	${env_Text.retweets} ${round_half_up(twitter_tweet_data.favorite_count,env_Text.roundingScale,env_Text.decimalPlaces,env_Text.units)}:heart:`
				},{
					"name": env_Text.postedDate,
					"value": twitter_tweet_data.created_at
				}
			];
			if(twitter_tweet_data.media[0]?.url){
				tmpEmbed.image = {
					"url": `attachment://${twitter_tweet_data.media[0].url.split('/').pop()}`
				}
			}
			embeds.push(tmpEmbed);
			if(twitter_tweet_data.media[1]?.url){
				for(let i=1;i<twitter_tweet_data.media.length;i++){
					embeds[i] = {
						"url": tweet_url,
						"image": {
							"url": `attachment://${twitter_tweet_data.media[i].url.split('/').pop()}`,
						}
					}
				}
			}
			tmp_return_object.push({"embeds": embeds, "files": await fetchImages(twitter_tweet_data.media.concat([{"url": twitter_user_data.profile_image}]))});
			if(twitter_tweet_data.media[0]?.media_type.match(/(video|animated_gif)/)){
				tmp_return_object.push({"content": twitter_tweet_data.media[0].video_url});
			}
			if(send_quoted_tweet && quoted_tweet_mode == 0 && (tweet_tweet_data_json.result?.quoted_status_result||tweet_tweet_data_json.quoted_status)){
				let tmp_quoted_data = tweet_tweet_data_json.result?.quoted_status_result||tweet_tweet_data_json.quoted_status;
				tmp_quoted_data.APIsource = tweet_data.APIsource;
				tmp_return_object.push({"quoted_tweet_data": tmp_quoted_data});
			}
			return tmp_return_object;
		}
		async function get_Tweet_data(){
			let response;
			if(use_graphQL){
				response = await request(new requestObject_twitter_graphQL(tweet_id));
				response = response.response.entries[response.entries.findIndex((tmp) => tmp.entryId == `tweet-${tweet_id}`)].content.itemContent.tweet_results;
				response.APIsource = 'graphql';
			}else{
				response = await request(new requestObject_twitter_1_1(tweet_id));
				response = response.response[0];
				response.APIsource = '1_1';
			}
			return response;
		}
	}

	async function find_pixiv_link(urls){
		const Pixiv_url_regex = /^https?:\/\/(((www|touch)\.)?pixiv\.(net\/([a-z]{2}\/)?((member(_illust)?\.php\?id\=|(users|u)\/)[0-9]*)|me\/.*))/;
		const Fanbox_url_regex = /^https?:(\/\/www\.pixiv\.net\/fanbox\/creator\/[0-9]*|\/\/.*\.fanbox\.cc\/?)/;
		return new Promise(async function(resolve){
			const urls_in_description = get_only_particular_key_value(urls,'description.urls.url',[]);
			const urls_in_description_expanded = get_only_particular_key_value(urls,'description.urls.expanded_url',[]);
			const urls_in_url_place = get_only_particular_key_value(urls,'url.urls.url',[]);
			const urls_in_url_place_expanded = get_only_particular_key_value(urls,'url.urls.expanded_url',[]);
			var tmp_urls = urls_in_description.concat(urls_in_description_expanded,urls_in_url_place,urls_in_url_place_expanded).filter(item => !/^https?:\/\/t\.co\//.test(item));
			var Pixiv_url;
			if(tmp_urls.length > 0){
				Pixiv_url = await find_pixiv_link_from_profile_urls(tmp_urls);
				if(Pixiv_url === undefined || Pixiv_url === null || Pixiv_url === false){
					tmp_urls = await expand_shortening_link(tmp_urls);
					Pixiv_url = await find_pixiv_link_from_profile_urls(tmp_urls);
					return resolve(Pixiv_url);
				}else{
					return resolve(Pixiv_url);
				}
			}
			return resolve(null);
		});
		async function expand_shortening_link(urls_in_profile){
			return new Promise(async function(resolve){
				var return_urls = [];
				if(urls_in_profile.length == 0 || urls_in_profile.length === null || urls_in_profile.length === undefined) return ;
				var promise_list = [];
				urls_in_profile.forEach(target=>{
					switch(true){
						case /^https?:\/\/bit\.ly\/[\w]{1,9}$/.test(target):
							promise_list.push(request(new requestObject('https://lab.syncer.jp/api/32/' + target)));
							break;
						default:
							break;
					}
				});
				await Promise.allSettled(promise_list).then(results => {
					 const res_tmp = get_only_particular_key_value(results, 'value.response', undefined);
					 var tmp;
					 for(let i=0;i<res_tmp.length;i++){
					 	tmp = JSON.parse(res_tmp[i]).pop();
						tmp = tmp.pop();
					 	return_urls[i] = tmp[0];
					 }
				});
				resolve(return_urls);
			});
		}
		async function find_pixiv_link_from_profile_urls(urls_in_profile){
			return new Promise(async function(resolve,reject){
				var tmp_pixiv_url;
				tmp_pixiv_url = findMatch_from_array(urls_in_profile,Pixiv_url_regex,true);
				if (tmp_pixiv_url !== undefined) return resolve(tmp_pixiv_url);
				if(findMatch_from_array(urls_in_profile,Fanbox_url_regex) !== undefined){
					tmp_pixiv_url = await when_fanbox(findMatch_from_array(urls_in_profile,Fanbox_url_regex,true));
					if(Pixiv_url_regex.test(tmp_pixiv_url)){
						return resolve(tmp_pixiv_url);
					}
				}else{
					var get_url_promise_list = [];
					urls_in_profile.forEach(target=>{
						switch(true){
							case /^https?:\/\/((skeb\.jp\/\@.*)|(fantia\.jp\/(fanclubs\/[0-9])?.*)|(.*\.booth\.pm)|(.*linktr\.ee)|(.*profcard\.info)|(.*lit\.link)|(potofu\.me)|(.*\.carrd\.co)|(.*\.tumblr\.com$)|(twpf\.jp))\/?/.test(target):
								get_url_promise_list.push(new Promise(
									async function(resolve,reject){
										try{
											return resolve(await when_general(target));
										}catch(error){
											return reject(error);
										}
									}
								));
								break;
							case /^https?:\/\/.*\.creatorlink\.net(\/.*)?/.test(target):
								get_url_promise_list.push(new Promise(
									async function(resolve,reject){
										try{
											return resolve(await when_general(`${target.match(/^https?:\/\/.*\.creatorlink\.net/)[0]}\/Contact`));
										}catch(error){
											return reject(error);
										}
									}
								));
								break;
							case /^https?:\/\/sketch\.pixiv\.net\//.test(target):
								get_url_promise_list.push(new Promise(
									async function(resolve,reject){
										try{
											return resolve(await when_pixiv_sketch(target));
										}catch(error){
											return reject(error);
										}
									}
								));
								break;
							default:
								break;
						}
					});
					if(get_url_promise_list.length > 0){
						await Promise.any(get_url_promise_list).then((value) => {tmp_pixiv_url = value}).catch(() => {tmp_pixiv_url = undefined});
						if(!Pixiv_url_regex.test(tmp_pixiv_url)) return resolve(undefined);
						return resolve(tmp_pixiv_url.replace(/^https?/,'https').replace(/(\/|\\)$/,''));
					}
				}
				return resolve(false);
				async function when_general(target_url){
					return new Promise(async function(resolve,reject){
						const response_data = await request(new requestObject(target_url.replace(/^https?/,"https")));
						var response_data_urls = response_data.response.split(/\"|\<|\>/).filter(function(data_str){return data_str.match(/^https?:(\/\/(((www|touch)\.)?pixiv\.(net\/([a-z]{2}\/)?((member(_illust)?\.php\?id\=|(users|u|fanbox\/creator)\/)[0-9].*)|me\/.*))|.*\.fanbox\.cc\/?)$/)});
						if(response_data_urls.find(function(element){return element.match(Pixiv_url_regex)}) !== undefined){
							return resolve(response_data_urls.find(function(element){return element.match(Pixiv_url_regex)}));
						}else if(response_data_urls.find(function(element){return element.match(Fanbox_url_regex)}) !== undefined){
							return when_fanbox(response_data_urls.find(function(element){return element.match(Fanbox_url_regex)}));
						}else{
							return reject(undefined);
						}
					});
				}
				async function when_pixiv_sketch(target_url){
						return new Promise(async function(resolve,reject){
							const response_data = await request(new requestObject(target_url));
							var User_id = response_data.response.split(',').filter(function(data_str){return data_str.match(/\\"pixiv_user_id\\":\\"[0-9]*\\"/)});
							if(User_id){
								return resolve("https://www.pixiv.net/users/" + User_id[0].split(/\"|\\/)[6]);
							}else{
								return reject(undefined);
							}
						});
					}
				async function when_fanbox(fanbox_url){
					if(fanbox_url.match(/^https?:\/\/www\.pixiv\.net\/fanbox\/creator\/[0-9]*/)){
						return fanbox_url.replace('fanbox/creator', 'users');
					}else{
						const fanbox_response = await request(new requestObject_fanbox(`https://api.fanbox.cc/creator.get?creatorId=${fanbox_url.replace(/(https?:\/\/|\.fanbox.*)/g,'')}`,fanbox_url.replace(/^http:/, 'https:').replace(/\/$/, '')));
						if(fanbox_response.status == "404") return undefined;
						tmp_pixiv_url = findMatch_from_array(fanbox_response.response.body.profileLinks,Pixiv_url_regex,true);
						if(tmp_pixiv_url !== undefined){
							return tmp_pixiv_url;
						}else{
							return `https://www.pixiv.net/users/${fanbox_response.response.body.user.userId}`;
						}
					}
				}
			}
		)}
	}
	function get_only_particular_key_value(object, path, defaultValue = undefined){
		//オブジェクトから任意のpathの値を取得する。
		//オブジェクト内に配列がある場合などに便利。
		/*
			{
				a: "1",
				b: [
					{hoge: "2"},
					{hoge: "3"}
				]
			}
			こんな感じになってるときに
			get_only_particular_key_value(object, "b.hoge")
			とやると
			>>[2,3]
			って帰ってくる。
			get_only_particular_key_value(object, "a")
			なら
			>> 1
			って帰ってくる。
		*/
		var isArray = Array.isArray;
		if(object == null || typeof object != 'object') return defaultValue;
		return (isArray(object)) ? object.map(createProcessFunction(path)) : createProcessFunction(path)(object);
		function createProcessFunction(path){
			if(typeof path == 'string') path = path.split('.');
			if(!isArray(path)) path = [path];
			return function(object){
				var index = 0,
					length = path.length;
				while(index < length){
					const key = toString_(path[index++]);
					if(object === undefined){
						return defaultValue;
					}
					// 配列に対する処理
					if(isArray(object)){
						object = object.map(item => item[key]);
					}else{
						object = object[key];
					}
				}
				return (index && index == length) ? object : void 0;
			};
		}
		function toString_(value){
			if(value == null) return '';
			if(typeof value == 'string') return value;
			if(isArray(value)) return value.map(toString) + '';
			var result = value + '';
			return '0' == result && 1 / value == -(1 / 0) ? '-0' : result;
		}
	}
	function findMatch_from_array(arr, regex, is_strict = false){
		//配列に正規表現にマッチするテキストがあるかを調べる。
		//「is_strict」が「true」ならマッチした部分だけど返す。
		for(let i = 0; i < arr.length; i++){
			if(regex.test(arr[i])){
				if(is_strict === true){
					return arr[i].match(regex)[0];
				}else{
					return arr[i];
				}
			}
		}
		return undefined;
	}
	function if_exsit_return_text(variable,return_text){
		if(!(variable === null || variable === undefined || variable === "")){
			return return_text
		}
		return "";
	}
	function image_url_to_original(image_url){
		//apiから帰ってくるURLをそのまま開くと小さい画像になってしまうので最大サイズの画像をダウンロードできるようにする。
		if(typeof image_url !== "undefined"){
			var extension = image_url.split(".").pop();
			return `${image_url.replace(`.${extension}`,"")}?format=${extension}&name=orig`
		}
	}
	function replace_t_co_to_original_url(full_text,urls,media_urls){
		//ツイート内のt.coで短縮されたリンクをもとにのリンクにもどす。
		try{
			if(typeof full_text !== "undefined"){
				full_text = full_text.replace(/\&amp\;/g,'&');
				full_text = full_text.replace(/\&gt\;/g,'\\>');
				full_text = full_text.replace(/\&lt\;/g,'\\<');
				if(typeof urls !== "undefined"){
					for(let i=0;i<=urls.length-1;i++){
						if(urls[i].expanded_url.length > 200){
							full_text = full_text.replace(urls[i].url,`[${urls[i].expanded_url.slice(0,150)}](${urls[i].expanded_url})... `);
						}else{
							full_text = full_text.replace(urls[i].url,urls[i].expanded_url);
						}
					}
				}
				//メディアがくっついてるツイートは末尾にメディアのURLが付随しているためそれを消す。
				if(typeof media_urls !== "undefined"){
					for(let i=0;i<=media_urls.length-1;i++){
						full_text = full_text.replace(media_urls[i].tco_url,"");
					}
				}
			}
		}catch{}
		return full_text;
	}
	function make_media_list(extended_entities,select_pages){
		var tmp_arr = [];
		var tmp_object = {};
		if(typeof extended_entities !== "undefined"){
			var j = 0;
			for(const target in select_pages){
				try{
					if(extended_entities.media.length > select_pages[target]){
						tmp_object = {};
						tmp_object.media_type = extended_entities.media[select_pages[target]].type;
						tmp_object.tco_url = extended_entities.media[select_pages[target]].url
						if(tmp_object.media_type == "animated_gif" || tmp_object.media_type == "video"){
							//動画はTwitterのサーバー上に複数の解像度のものがあるためその中で最も大きいものを選択する。
							tmp_object.video_url = extended_entities.media[select_pages[target]].video_info.variants.filter(function(obj){return obj.content_type == "video/mp4"}).reduce((a, b) => a.bitrate > b.bitrate ? a:b).url.split('?')[0];
						}else if(tmp_object.media_type == "photo"){
							tmp_object.url = extended_entities.media[select_pages[target]].media_url_https;
						}
						tmp_arr[j] = tmp_object;
						j++;
					}
				}catch{}
			}
			return tmp_arr;
		}
		return [];
	}
	function str_max_length(text, max_length,defaultValue = "……以下Discordの字数オーバー。"){
		var r = 0;
		for(var i = 0; i < text.length; i++){
			var c = text.charCodeAt(i);
			if((c >= 0x0 && c < 0x81) || (c == 0xf8f0) || (c >= 0xff61 && c < 0xffa0) || (c >= 0xf8f1 && c < 0xf8f4)){
				r += 1;
			}else{
				r += 2;
			}
			if(r >= max_length){
				text = `${text.slice(0, i - 1)} ${defaultValue}`;
				break;
			}
		}
		return text;
	}
	function round_half_up(original_value,where_round_off,decimal_place = 0,unit_str = ""){
		//四捨五入関数。
		/*
		original_valu: 元の値
		where_round_off: どこで四捨五入するか(0.1, 1, 10, 100, 1000など)
		decimal_place: 小数点以下を何桁にするか(1, 2, 3, 4, 5など)
		unit_str: 単位を末尾につける(千,万など)
		*/
		if(Number(original_value)>=Number(where_round_off)){
			var tmp_value;
			tmp_value = Math.round(Number(original_value) / Number(where_round_off) * Math.pow(10,Number(decimal_place))) / Math.pow(10,Number(decimal_place));
			if(unit_str == ""){
				return tmp_value;
			}else{
				return `${tmp_value}${unit_str}`
			}
		}else{
			return original_value;
		}
	}
	function replace_null_to_something(input_character,replace_character = " "){
		if(input_character === null || input_character === undefined || input_character === ""){
			return replace_character;
		}else{
			return input_character;
		}
	}
	async function sleep(time){
		return new Promise((resolve)=>{
			setTimeout(() => {return resolve(time)}, time);
		});
	}
	async function fetchImages(mediaUrlArray){
		if(mediaUrlArray?.length == 0) return;
		let downloadPromises = mediaUrlArray.map(async (target) => {
			if(!target.url)return;
			let image;
			if(target.url.match(/https?:\/\/pbs\.twimg\.com\/media\//)){
				image = await request(new requestObject_binary_data(image_url_to_original(target.url)));
			}else{
				image = await request(new requestObject_binary_data(target.url));
			}
			return {
				"attachment": image.response,
				"name": target.url.split('/').pop(),
			};
		});
		return remove_null_from_array(await Promise.all(downloadPromises));
	}
	function remove_null_from_array(arr){
		return arr.filter(function(x){return !(x === null || x === undefined || x === "")});
	}
	function init(){
		main(document.querySelectorAll(env_selector.tweet_field));
	}
	function update(){
		if(updating) return;
		updating = true;
		init();
		setTimeout(() => {updating = false;}, 1000);
	}
	function GetCookie(name){
		let arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
		if(arr = document.cookie.match(reg)){
			return decodeURIComponent(arr[2]);
    	}else{
        	return null;
		}
	}
	class sendObject{
		constructor(webhook,sendEmbeds){
			this.method = 'POST';
			this.respType = 'json';
			this.url = webhook;
			this.headers = {
				//'Content-Type': 'multipart/form-data'
			};
			this.package = null;
			this.anonymous = true;
			this.body = sendEmbeds;
		}
	}
	class requestObject{
		constructor(URL,addtional_cookie = undefined){
			this.method = 'GET';
			this.url = `${URL}`;
			this.headers = {
				"Content-Type": "text/html,application/xhtml+xml,application/xml",
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'accept': '*/*',
				'Referer': URL,
				"Sec-Fetch-Mode": "navigate",
				"Sec-Fetch-Site": "cross-site",
				'cookie': `${addtional_cookie}`
			};
			this.package = null;
		}
	}
	class requestObject_twitter_graphQL{
		constructor(ID){
			this.method = 'GET';
			this.respType = 'json';
			this.url = `https://api.twitter.com/graphql/NNiD2K-nEYUfXlMwGCocMQ/TweetDetail?variables=%7B%22focalTweetId%22%3A%22${ID}%22%2C%22with_rux_injections%22%3Afalse%2C%22includePromotedContent%22%3Atrue%2C%22withCommunity%22%3Atrue%2C%22withQuickPromoteEligibilityTweetFields%22%3Atrue%2C%22withBirdwatchNotes%22%3Atrue%2C%22withSuperFollowsUserFields%22%3Atrue%2C%22withDownvotePerspective%22%3Afalse%2C%22withReactionsMetadata%22%3Afalse%2C%22withReactionsPerspective%22%3Afalse%2C%22withSuperFollowsTweetFields%22%3Atrue%2C%22withVoice%22%3Atrue%2C%22withV2Timeline%22%3Atrue%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Atrue%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Afalse%2C%22verified_phone_label_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22tweetypie_unmention_optimization_enabled%22%3Atrue%2C%22vibe_api_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Afalse%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Afalse%2C%22interactive_text_enabled%22%3Atrue%2C%22responsive_web_text_conversations_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D`;
			this.body = null;
			this.headers = {
				"Content-Type": "application/json",
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'accept': '*/*',
				'Referer': "https://twitter.com/",
				'Host': 'api.twitter.com',
				'authorization': `Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA`,
				'x-csrf-token': GetCookie("ct0"),
			};
			this.package = null;
			this.anonymous = false;
		}
	}
	class requestObject_twitter_1_1{
		constructor(ID){
			this.method = 'GET';
			this.respType = 'json';
			this.url = "https://api.twitter.com/1.1/statuses/lookup.json?id=" + ID + "&tweet_mode=extended";
			this.body = null;
			this.headers = {
				"Content-Type": "application/json",
				'Referer': "https://twitter.com/",
				'Authorization': `Bearer AAAAAAAAAAAAAAAAAAAAAIK1zgAAAAAA2tUWuhGZ2JceoId5GwYWU5GspY4%3DUq7gzFoCZs1QfwGoVdvSac3IniczZEYXIcDyumCauIXpcAPorE`,
				'x-csrf-token': GetCookie("ct0"),
			};
			this.package = null;
			this.anonymous = false;
		}
	}

	class requestObject_fanbox{
		constructor(URL,fanbox_URL){
			this.method = 'GET';
			this.url = `${URL}`;
			this.body = null;
			this.headers = {
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'origin': fanbox_URL,
				'Host': 'api.fanbox.cc',
				'cookie': '',
			};
		}
	}
	class requestObject_binary_data{
		constructor(URL,addtional_cookie = undefined){
			this.method = 'GET';
			this.respType = 'blob';
			this.url = `${URL}`;
			this.body = null;
			this.encoding = null;
			this.headers = {
				"Content-Type": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*",
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'accept': '*/*',
				'Referer': URL,
				"Sec-Fetch-Mode": "navigate",
				'cookie': `${addtional_cookie}`
			};
			this.package = null;
		}
	}
	async function request(object, timeout = 60000) {
		return new Promise((resolve, reject) => {
			GM_xmlhttpRequest({
				method: object.method,
				url: object.url,
				headers: object.headers,
				responseType: object.respType,
				data: object.body,
				anonymous: object.anonymous,
				timeout: timeout,
				onload: function(responseDetails){
					return resolve(responseDetails);
				},
				ontimeout: function(responseDetails){
					return reject(`[request]time out:\nresponse ${responseDetails}`);
				},
				onerror: function(responseDetails){
					return reject(`[request]error:\nresponse ${responseDetails}`);
				}
			});
		});
	}
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址