ボタン一つで絵文字ツイートができます。
当前为
// ==UserScript==
// @name Emoji Tweet Buttons
// @namespace http://tampermonkey.net/
// @version 1.0
// @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==
// 指定したミリ秒待つ Promise 関数
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// 絵文字設定
const EMOJI_PREFIX = 'background-image: url("https://abs.twimg.com/responsive-web/client-web-legacy/twemoji_sprite_high_res.8b274d9a.png"); ';
const emojis = GM_getValue("emojis", [
EMOJI_PREFIX + "background-position: 0% 100%; background-size: 5000% 7200%;",
EMOJI_PREFIX + "background-position: 95.9184% 18.3099%; background-size: 5000% 7200%;",
EMOJI_PREFIX + "background-position: 24.4898% 53.5211%; background-size: 5000% 7200%;",
EMOJI_PREFIX + "background-position: 75.5102% 52.1127%; background-size: 5000% 7200%;",
EMOJI_PREFIX + "background-position: 2.04082% 66.1972%; background-size: 5000% 7200%;",
"",
"",
"",
]);
let emojiVersion = 0;
const updateEmoji = (index, style) => {
emojis[index] = style;
GM_setValue("emojis", emojis);
++emojiVersion;
};
let selectingIndex = null;
const updateSelectingEmoji = (style) => {
if (selectingIndex == null) return;
updateEmoji(selectingIndex, style);
selectingIndex = null;
};
// 絵文字をツイートする async 関数
async function tweetEmoji(tweetButton, emoji) {
let emojiButton,
timeout = false;
wait(5000).then(() => {
timeout = true;
});
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)}]`);
if (emojiButton) break;
// ランダムな絵文字タブを読み込む
const emojiTabs = emojiPicker.parentNode.parentNode.childNodes[1].childNodes[0].childNodes;
emojiTabs[Math.floor(emojiTabs.length * Math.random())].childNodes[0].click();
await wait(50);
}
emojiButton.click();
await wait(50);
tweetButton.click();
}
// 二種類のツイートボタンの前に絵文字ボタンを追加
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))) {
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);
emojiButtons.append(button);
}
tweetButton.before(emojiButtons);
}
}, 50);
// 絵文字設定画面を追加
setInterval(() => {
const emojiSelectorContainer = document.createElement("div");
emojiSelectorContainer.id = "emoji_tweet-selector";
{
let selector;
if ((selector = document.getElementById(emojiSelectorContainer.id))) {
if (Number(selector.dataset.emojiVersion) < emojiVersion) selector.remove();
else return;
}
emojiSelectorContainer.dataset.emojiVersion = emojiVersion;
}
if (document.getElementById(emojiSelectorContainer.id)) return;
const emojiPicker = document.querySelector("#emoji_picker_categories_dom_id > div");
if (!emojiPicker || emojiPicker.childNodes.length <= 1) return;
// 設定画面タイトル
const emojiSelectorTitle = document.createElement("h3");
emojiSelectorTitle.innerText = "Emoji Selector";
// 絵文字削除ボタン
const emojiDeleter = document.createElement("button");
emojiDeleter.innerText = "Delete emoji";
emojiDeleter.onclick = () => {
updateSelectingEmoji("");
};
emojiDeleter.style.float = "right";
emojiDeleter.style.margin = "1.5em 0 0.5em";
emojiDeleter.style.color = "#FF5555";
// 絵文字設定ボタン
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 = "#AAFFAAAA";
};
if (index == selectingIndex) button.style.backgroundColor = "#AAFFAAAA";
emojiSelector.append(button);
});
emojiSelectorContainer.append(emojiDeleter, emojiSelectorTitle, emojiSelector);
emojiSelectorContainer.style.margin = "1em";
emojiPicker.childNodes[1].after(emojiSelectorContainer);
}, 50);
// 絵文字設定モード
setInterval(() => {
const emojiPicker = document.querySelector("#emoji_picker_categories_dom_id > div");
if (!emojiPicker) return;
for (const emoji of emojiPicker.querySelectorAll(`[role="option"] > [style^=${JSON.stringify(EMOJI_PREFIX)}]`)) {
const emojiButton = emoji.parentNode;
if (selectingIndex == null) {
emojiButton.onclick = null;
emojiButton.style.backgroundColor = null;
} else {
emojiButton.onclick = (event) => {
event.stopPropagation();
updateSelectingEmoji(emoji.style.cssText);
};
emojiButton.style.backgroundColor = "#AAFFAAAA";
}
}
}, 50);