您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Twitterの投稿画面にMisskeyへの同時投稿機能を追加します。
// ==UserScript== // @name Twiskey // @namespace https://github.com/ZOI-dayo // @version 1.2 // @description Twitterの投稿画面にMisskeyへの同時投稿機能を追加します。 // @author ZOI_dayo // @license MIT // @match https://twitter.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com // @grant GM_xmlhttpRequest // @grant GM_addElement // @grant GM_listValues // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/browser-image-compression.js // ==/UserScript== (function() { 'use strict'; let interval = setInterval(()=>{ const toolbar = document.querySelector('div[data-testid="toolBar"]') if(toolbar == null) return; if(toolbar.getAttribute("twiskey_ispached") !== null) return; toolbar.setAttribute("twiskey_ispached", "true") let misskey_button = document.createElement("span"); misskey_button.style.display = "flex"; misskey_button.style.justifyContent = "center"; misskey_button.style.alignItems = "center"; misskey_button.style.aspectRatio = "1"; misskey_button.style.height = "25px"; misskey_button.style.marginTop = "12px"; misskey_button.style.position = "relative"; let misskey_enabled = false; toolbar.children[0].after(misskey_button) GM_addElement(misskey_button , 'img', { src: 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png', height: '100%' }); misskey_button.querySelector("img").style.filter = "grayscale(100%) opacity(25%)" { const twitter_username = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]').href.substr(20) const api_key_box = document.createElement("div"); { api_key_box.style.backgroundColor = "rgb(25, 35, 32)" api_key_box.style.width = "300px" // api_key_box.style.height = "50px" api_key_box.style.position = "absolute" api_key_box.style.bottom = "calc(100% + 10px)" api_key_box.style.padding = "10px" api_key_box.style.borderRadius = "10px" api_key_box.style.color = "white" // api_key_box.style.filter = "drop-shadow(0 0 5px rgb(60, 80, 70))" } misskey_button.appendChild(api_key_box); misskey_button.parentNode.parentNode.parentNode.style.zIndex = "9999" const misskey_server_message = document.createElement("span") { misskey_server_message.innerText = "Misskeyサーバーアドレスを入力" } api_key_box.appendChild(misskey_server_message) const misskey_server_input = document.createElement("input") { misskey_server_input.style.width = "80%" misskey_server_input.style.display = "block" misskey_server_input.style.margin = "0 auto" misskey_server_input.setAttribute("type", "text"); misskey_server_input.value = GM_getValue(`misskey_server_${twitter_username}`, null) misskey_server_input.style.border = "none" misskey_server_input.style.backgroundColor = "rgb(125, 175, 160)" misskey_server_input.placeholder = "misskey.io" } api_key_box.appendChild(misskey_server_input) const misskey_token_message = document.createElement("span") { misskey_token_message.innerText = "Misskeyトークンを入力" } api_key_box.appendChild(misskey_token_message) const misskey_token_input = document.createElement("input") { misskey_token_input.style.width = "80%" misskey_token_input.style.display = "block" misskey_token_input.style.margin = "0 auto" misskey_token_input.setAttribute("type", "text"); misskey_token_input.value = GM_getValue(`misskey_api_key_${twitter_username}`, null) misskey_token_input.style.border = "none" misskey_token_input.style.backgroundColor = "rgb(125, 175, 160)" misskey_token_input.placeholder = "*******" misskey_token_input.type = "password" } api_key_box.appendChild(misskey_token_input) const misskey_data_submit_button = document.createElement("button") { misskey_data_submit_button.innerText = "保存" misskey_data_submit_button.onclick = () => { GM_setValue(`misskey_server_${twitter_username}`, misskey_server_input.value == "" ? null : misskey_server_input.value) GM_setValue(`misskey_api_key_${twitter_username}`, misskey_token_input.value == "" ? null : misskey_token_input.value) misskey_button.querySelector("div").style.display = "none" } misskey_data_submit_button.style.border = "none" misskey_data_submit_button.style.backgroundColor = "rgb(125, 175, 160)" misskey_data_submit_button.style.display = "block" misskey_data_submit_button.style.margin = "10px auto 0" misskey_data_submit_button.style.padding = "5px 10px" misskey_data_submit_button.style.borderRadius = "5px" } api_key_box.appendChild(misskey_data_submit_button) api_key_box.style.display = "none" } misskey_button.addEventListener('contextmenu', e => { e.preventDefault() const api_key_box = misskey_button.querySelector("div") api_key_box.style.display = "" }) misskey_button.querySelector("img").addEventListener('click', () => { misskey_enabled = !misskey_enabled; misskey_button.querySelector("img").style.filter = `grayscale(${misskey_enabled ? "0%" : "100%"}) opacity(${misskey_enabled ? "100%" : "25%"})` if(misskey_enabled) { const twitter_username = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]').href.substr(20) if(GM_getValue(`misskey_server_${twitter_username}`, null) == null || GM_getValue(`misskey_api_key_${twitter_username}`, null) == null) { misskey_button.querySelector("div").style.display = "block" } } else { misskey_button.querySelector("div").style.display = "none" } }) Array.from(toolbar.children).slice(-1)[0].addEventListener('click', async e => { if(!misskey_enabled) return; const twitter_username = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]').href.substr(20); console.log(twitter_username); const misskey_server = GM_getValue(`misskey_server_${twitter_username}`, null); if(misskey_server == null) { console.error("Please set misskey server URL.") return; } const misskey_api_key = GM_getValue(`misskey_api_key_${twitter_username}`, null); if(misskey_api_key == null) { console.error("Please set misskey API key.") return; } const content = document.querySelector('.public-DraftStyleDefault-block.public-DraftStyleDefault-ltr').innerText if(content == "") return; const text = document.querySelector('div[data-testid="tweetTextarea_0"]').innerText const media = Array.from(document.querySelectorAll('div[data-testid="attachments"] > div:nth-child(2) > div > div:nth-child(2) > div > div > div > div > div > img')).map(e => e.src) function urlToBlob(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, onload: function(response) { resolve(response.response) }, onerror: function(error) { reject(error); }, responseType: "blob" }); }); } let media_ids = [] for(let m of media) { console.log(m); var params = new FormData(); params.append("i", misskey_api_key); const file = await urlToBlob(m) params.append("file", file); const misskey_image_req = await new Promise((resolve, _) => { GM_xmlhttpRequest({ method: "POST", url: `https://${misskey_server}/api/drive/files/create`, data: params, onload: function(response) { media_ids.push(JSON.parse(response.responseText)["id"]) console.log(response.responseText); resolve(); } }); }); } console.log(misskey_api_key) GM_xmlhttpRequest({ method: "POST", url: `https://${misskey_server}/api/notes/create`, headers: { "Content-Type": "application/json" }, data: JSON.stringify({ "i": misskey_api_key, "text": text, "fileIds": media_ids.length > 0 ? media_ids : undefined, }), onload: function(response) { console.log(response.responseText); } }); }); // clearInterval(interval) }, 100); })();