🏠 返回首頁 

Greasy Fork is available in English.

[Twitter]こっそりいいね

リツイートした人に通知を飛ばさずいいねするボタンを追加する。

// ==UserScript==
// @name			[Twitter]こっそりいいね
// @namespace		https://greasyfork.org/ja/users/1023652
// @version			######1919810.0.2
// @description		リツイートした人に通知を飛ばさずいいねするボタンを追加する。
// @author			ゆにてぃー
// @match			https://twitter.com/*
// @connect			twitter.com
// @icon			https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @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'};
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(".sneakilyLike") || ! element.querySelector(env_selector.retweeted) || !element.querySelector('[data-testid="like"]')) 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 button = document.createElement('button');
let fotter = element.querySelector('div[id][role="group"]');
let like_element = fotter.querySelector('[data-testid="like"]');
button.textContent = "いいね!";
button.style.fontSize = '0.7em';
button.style.whiteSpace = 'nowrap';
button.classList.add('sneakilyLike');
button.addEventListener('click',async function(event){
this.disabled = true;
const status = await request(new requestObject(tweet_link));
if(status.response.data.favorite_tweet == "Done"){
like_element.querySelector('div[dir="ltr"]').classList.add(env_selector.liked_color);
like_element.querySelector('div[dir="ltr"]').style.color = "rgb(249, 24, 128)";
like_element.querySelector("path").setAttribute('d',env_selector.liked);
like_element.setAttribute('data-testid', 'unlike');
}
this.remove();
});
fotter.insertBefore(button, like_element.parentElement.nextSibling);
});
}
function init() {
main(document.querySelectorAll(env_selector.tweet_field));
}
function update() {
if(updating) return;
updating = true;
init();
setTimeout(() => {updating = false;}, 1000);
}
function getCookieArray() {
var arr = [];
if(document.cookie != '') {
var tmp = document.cookie.split('; ');
for(var i = 0; i < tmp.length; i++) {
var data = tmp[i].split('=');
arr[data[0]] = decodeURIComponent(data[1]);
}
}
return arr;
}
const x_csrf_token = getCookieArray().ct0;
class requestObject{
constructor(URL){
this.method = 'POST';
this.respType = 'json';
this.url = 'https://twitter.com/i/api/graphql/lI07N6Otwv1PhnEgXILM7A/FavoriteTweet';
this.body = `{"variables":{"tweet_id":"${URL.split('/').pop()}"},"queryId":"lI07N6Otwv1PhnEgXILM7A"}`;
this.headers = {
'Content-Type': 'application/json',
'User-agent': window.navigator.userAgent,
'accept': '*/*',
'Referer': URL,
'Origin': 'https://twitter.com',
'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA',
"x-csrf-token": x_csrf_token,
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'navigate',
};
this.package = null;
this.anonymous = false;
}
}
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}`);
}
});
});
}
})();