您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
ボタン一つで絵文字ツイートができます。
// ==UserScript== // @name Emoji Tweet Buttons // @namespace http://tampermonkey.net/ // @version 2.1 // @description ボタン一つで絵文字ツイートができます。 // @author TwoSquirrels // @license MIT // @match https://twitter.com/* // @match https://x.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=x.com // @grant GM_getValue // @grant GM_setValue // ==/UserScript== // カラーパレット const Palette = { RED: "#FF5555", BG_GREEN: "#AAFFAA55", }; // 指定したミリ秒待つ Promise 関数 const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); // 絵文字設定 const emojis = GM_getValue("emojis", [ 'background-image: url("https://abs.twimg.com/responsive-web/client-web-legacy/twemoji_sprite_high_res.8b274d9a.png"); background-position: 0% 100%; background-size: 5000% 7200%;', 'background-image: url("https://abs.twimg.com/responsive-web/client-web-legacy/twemoji_sprite_high_res.8b274d9a.png"); background-position: 95.9184% 18.3099%; background-size: 5000% 7200%;', 'background-image: url("https://abs.twimg.com/responsive-web/client-web-legacy/twemoji_sprite_high_res.8b274d9a.png"); background-position: 24.4898% 53.5211%; background-size: 5000% 7200%;', 'background-image: url("https://abs.twimg.com/responsive-web/client-web-legacy/twemoji_sprite_high_res.8b274d9a.png"); background-position: 75.5102% 52.1127%; background-size: 5000% 7200%;', 'background-image: url("https://abs.twimg.com/responsive-web/client-web-legacy/twemoji_sprite_high_res.8b274d9a.png"); background-position: 2.04082% 66.1972%; background-size: 5000% 7200%;', "", "", "", ]); let emojiVersion = 0; // 絵文字を入力/ツイートする async 関数 let runningTweetEmoji = false; async function tweetEmoji(tweetButton, emoji) { try { if (runningTweetEmoji) throw new Error("tweetEmoji can't run in parallel"); runningTweetEmoji = true; let emojiButton; let timeout = false; wait(5000).then(() => { timeout = true; }); let emojiTabIndex = 0; while (true) { if (timeout) throw new Error(`Couldn't find the emoji: ${JSON.stringify(emoji)}`); const emojiPicker = document.querySelector("#emoji_picker_categories_dom_id > div"); if (!emojiPicker) { document.querySelector(`[data-testid="ScrollSnap-List"] button[aria-haspopup="menu"]`).click(); await wait(50); continue; } emojiButton = emojiPicker.querySelector(`[style=${JSON.stringify(emoji)}]`); emojiButton ??= emojiPicker.querySelector(`[style=${JSON.stringify(emoji.replace(/\/client-web-legacy\//g, "/client-web/"))}]`); emojiButton ??= emojiPicker.querySelector(`[style=${JSON.stringify(emoji.replace(/\/client-web\//g, "/client-web-legacy/"))}]`); if (emojiButton) break; // 順に絵文字タブを読み込む const emojiTabs = emojiPicker.parentNode.parentNode.childNodes[1].childNodes[0].childNodes; emojiTabs[++emojiTabIndex % emojiTabs.length].childNodes[0].click(); await wait(50); } emojiButton.click(); if (GM_getValue("autoTweet", true)) { await wait(50); tweetButton.click(); } runningTweetEmoji = false; } catch (error) { runningTweetEmoji = false; throw error; } } // 絵文字ボタンを追加 setInterval(() => { // 二種類のツイートボタンの前に追加 for (const tweetButtonId of ["tweetButton", "tweetButtonInline"]) { const emojiButtons = document.createElement("div"); emojiButtons.id = "emoji_tweet-" + tweetButtonId; { let button; if ((button = document.getElementById(emojiButtons.id))) { button.style.opacity = runningTweetEmoji ? 0.5 : 1.0; if (Number(button.dataset.emojiVersion) < emojiVersion) button.remove(); else continue; } emojiButtons.dataset.emojiVersion = emojiVersion; } const tweetButton = document.querySelector(`[data-testid=${JSON.stringify(tweetButtonId)}]`); if (!tweetButton || tweetButton.parentNode.parentNode.dataset.testid !== "toolBar") continue; // それぞれの絵文字ボタンを作る for (const emoji of emojis) { if (!emoji) continue; const button = document.createElement("button"); const emojiElm = document.createElement("div"); emojiElm.style = emoji; emojiElm.style.height = "20px"; emojiElm.style.width = "20px"; button.append(emojiElm); button.onclick = () => tweetEmoji(tweetButton, emoji).then(); button.style.padding = "1px 4px"; emojiButtons.append(button); } tweetButton.before(emojiButtons); } }, 50); // 絵文字設定 let selectingIndex = null; const updateEmojiDeleter = () => { const disabled = !emojis[selectingIndex]; const emojiDeleter = document.getElementById("emoji_tweet-delete"); if (!emojiDeleter) return; emojiDeleter.disabled = disabled; emojiDeleter.style.opacity = disabled ? 0.5 : 1.0; emojiDeleter.style.color = disabled ? null : Palette.RED; emojiDeleter.style.backgroundColor = disabled ? null : Palette.BG_GREEN; }; const updateSelectingEmoji = (style) => { if (selectingIndex == null) return; emojis[selectingIndex] = style; GM_setValue("emojis", emojis); ++emojiVersion; selectingIndex = null; updateEmojiDeleter(); }; // 絵文字設定画面を追加 setInterval(() => { const emojiSettings = document.createElement("div"); emojiSettings.id = "emoji_tweet-selector"; { let selector; if ((selector = document.getElementById(emojiSettings.id))) { if (Number(selector.dataset.emojiVersion) < emojiVersion) selector.remove(); else return; } emojiSettings.dataset.emojiVersion = emojiVersion; } if (document.getElementById(emojiSettings.id)) return; const emojiPicker = document.querySelector("#emoji_picker_categories_dom_id > div"); if (!emojiPicker || emojiPicker.childNodes.length <= 1) return; // 設定画面タイトル const emojiSettingsTitle = document.createElement("h3"); emojiSettingsTitle.innerText = "Emoji Tweet Buttons Settings"; // 絵文字設定ボタン const emojiSelector = document.createElement("div"); emojis.forEach((emoji, index) => { const button = document.createElement("button"); const emojiElm = document.createElement("div"); emojiElm.style = emoji; emojiElm.style.height = "20px"; emojiElm.style.width = "20px"; button.append(emojiElm); button.onclick = () => { if (selectingIndex != null) emojiSelector.childNodes[selectingIndex].style.backgroundColor = null; if (selectingIndex === index) selectingIndex = null; else emojiSelector.childNodes[(selectingIndex = index)].style.backgroundColor = Palette.BG_GREEN; updateEmojiDeleter(); }; button.style.padding = "1px 4px"; if (index == selectingIndex) button.style.backgroundColor = Palette.BG_GREEN; emojiSelector.append(button); }); // 絵文字削除ボタン const emojiDeleter = document.createElement("button"); emojiDeleter.id = "emoji_tweet-delete"; emojiDeleter.innerText = "Delete emoji"; emojiDeleter.onclick = () => { updateSelectingEmoji(""); }; emojiDeleter.style.float = "right"; emojiDeleter.style.margin = "1em 0"; // 自動投稿設定 const autoTweetSetting = document.createElement("div"); autoTweetSetting.style.margin = "1em 0 1em"; const autoTweetSettingLabel = document.createElement("label"); autoTweetSettingLabel.innerText = "Auto Tweet: "; const autoTweetSettingBox = document.createElement("input"); autoTweetSettingBox.type = "checkbox"; autoTweetSettingBox.checked = GM_getValue("autoTweet", true); autoTweetSettingBox.onchange = ({ target }) => GM_setValue("autoTweet", target.checked); autoTweetSettingBox.style.verticalAlign = "-4px"; autoTweetSettingBox.style.height = "20px"; autoTweetSettingBox.style.width = "20px"; autoTweetSettingBox.style.margin = "0 4px 0"; autoTweetSetting.append(autoTweetSettingLabel, autoTweetSettingBox); emojiSettings.append(emojiSettingsTitle, emojiSelector, emojiDeleter, autoTweetSetting); emojiSettings.style.margin = "1em"; emojiPicker.childNodes[1].after(emojiSettings); updateEmojiDeleter(); }, 50); // 絵文字設定モード setInterval(() => { const emojiPicker = document.querySelector("#emoji_picker_categories_dom_id > div"); if (!emojiPicker) return; for (const emojiButton of emojiPicker.querySelectorAll(`[role="option"]`)) { if (selectingIndex == null) { emojiButton.onclick = null; emojiButton.style.backgroundColor = null; } else { emojiButton.onclick = (event) => { event.stopPropagation(); updateSelectingEmoji(emojiButton.childNodes[0].style.cssText); }; emojiButton.style.backgroundColor = Palette.BG_GREEN; } } }, 50);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址