|
// ==UserScript== |
|
// @name Twitter Like Exporter |
|
// @namespace https://floris.debijl.xyz/ |
|
// @version 1.0 |
|
// @description Export all Twitter likes from any account to an CSV file. Warning: really slow with anything over 1000 likes and probably doesn't work with over 8000-ish. |
|
// @author @Fdebijl |
|
// @license GNU Affero General Public License 3.0 https://www.gnu.org/licenses/agpl-3.0.nl.html |
|
// @match https://twitter.com/*/likes |
|
// @match *twitter.com/*/likes |
|
// @match https://twitter.com/i/likes |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
(function() { |
|
'use strict'; |
|
const $ = window.jQuery; |
|
|
|
// Global variables |
|
let totalLikes = 0, currentLikes = 0, interrupt = false, mainTimer, auxTimer, once = false; |
|
|
|
// Timing variables |
|
let runningSeconds = 0; |
|
|
|
$(document).ready(function() { |
|
totalLikes = $('.ProfileNav-stat[data-nav="favorites"] .ProfileNav-value').data('count'); |
|
TaskTimer(); |
|
ScrollToEnd(); |
|
SetMessageDrawerVisible(true, "Initiating script..."); |
|
}); |
|
|
|
function DoWhenDone() { |
|
SetMessageDrawerVisible(true, "Like retrieval complete, starting download..."); |
|
$('.hijacked').off('click'); |
|
DownloadCSV(FormulateCSV()); |
|
|
|
setTimeout(SetMessageDrawerVisible(false), 3000); |
|
} |
|
|
|
function SetMessageDrawerVisible(visibility, notificationtext = "") { |
|
if(visibility) { |
|
$('#message-drawer').removeClass('hidden'); |
|
$('#message-drawer').addClass('hijacked'); |
|
$('.message-text').css('white-space', 'pre-line'); |
|
$('#message-drawer').attr('style', 'margin-bottom: -46px !important;'); |
|
$('#message-drawer .message-text').text(notificationtext); |
|
|
|
// Interrupt when notification is clicked |
|
$('.hijacked').click(function() { |
|
clearTimeout(mainTimer); |
|
clearTimeout(auxTimer); |
|
interrupt = true; |
|
SetMessageDrawerVisible(false); |
|
|
|
DoWhenDone(); |
|
}); |
|
} else if (!visibility) { |
|
$('#message-drawer').addClass('hidden'); |
|
$('#message-drawer').removeClass('hijacked'); |
|
$('#message-drawer').attr('style', 'margin-bottom: 46px;'); |
|
$('#message-drawer .message-text').text(notificationtext); |
|
} |
|
return; |
|
} |
|
|
|
function pad(val) { |
|
return val > 9 ? val : "0" + val; |
|
} |
|
|
|
function TaskTimer() { |
|
mainTimer = setInterval( function(){ |
|
++runningSeconds; |
|
}, 1000); |
|
|
|
if (totalLikes > 21) { |
|
auxTimer = setInterval( function(){ |
|
SetMessageDrawerVisible(true, |
|
"Retrieving likes: " + currentLikes + "/" + totalLikes + "\n" + |
|
"Estimated time to completion: " + GetTimeToCompletion() + "\n" + |
|
"Click anywhere on this notification to cancel the operation and download the CSV file as-is."); |
|
}, 5000); |
|
} |
|
} |
|
|
|
function SecondsToFullTime(seconds) { |
|
let remainingHours = pad(Math.round(seconds / 3600 % 60)); |
|
let remainingMinutes = pad(Math.round(seconds / 60 % 60)); |
|
let remaingSeconds = pad(Math.round(seconds % 60)); |
|
|
|
return remainingHours + ":" + remainingMinutes + ":" + remaingSeconds; |
|
} |
|
|
|
function GetTimeToCompletion() { |
|
let secondsPerLike = runningSeconds / currentLikes; |
|
let remainingLikes = totalLikes - currentLikes; |
|
return SecondsToFullTime(secondsPerLike * remainingLikes); |
|
} |
|
|
|
function ScrollToEnd() { |
|
if (interrupt) { |
|
return; |
|
} |
|
|
|
if ($('.timeline-end').hasClass('has-more-items') === false || interrupt) { |
|
DoWhenDone(); |
|
console.log("Done loading likes"); |
|
return; |
|
} |
|
|
|
setTimeout(function() { |
|
$('html, body').animate({ |
|
scrollTop: $(document).height()}, |
|
2500, |
|
"swing" |
|
); |
|
currentLikes = $('.stream-item > .tweet').length; |
|
ScrollToEnd(); |
|
}, 2500); |
|
} |
|
|
|
function FormulateCSV() { |
|
let csv = "username,following,follows me,tweetid,userid" + "\n"; |
|
$('.stream-item > .tweet').each(function(index) { |
|
csv += $(this).data("screenName") + "," + $(this).data("youFollow") + "," + $(this).data("followsYou") + "," + $(this).data("tweetId") + "," + $(this).data("userId") + "\n"; |
|
}); |
|
|
|
return csv; |
|
} |
|
|
|
function DownloadCSV(dlstring) { |
|
if (!once) { |
|
once = true; |
|
|
|
let blob = new Blob([dlstring], { type: 'text/csv;charset=utf-8;' }); |
|
|
|
if (navigator.msSaveBlob) { |
|
navigator.msSaveBlob(blob, "likes.csv"); |
|
} else { |
|
var link = document.createElement("a"); |
|
if (link.download !== undefined) { |
|
let url = URL.createObjectURL(blob); |
|
link.setAttribute("href", url); |
|
link.setAttribute("download", "likes.csv"); |
|
link.style.visibility = 'hidden'; |
|
document.body.appendChild(link); |
|
link.click(); |
|
document.body.removeChild(link); |
|
} |
|
} |
|
} |
|
} |
|
})(); |