-
-
Save tiffany352/9ee7e0d4fd7e08ede9d0314df9eab672 to your computer and use it in GitHub Desktop.
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8" /> | |
<title>Twitter Archive Browser</title> | |
<script src="https://unpkg.com/react@16/umd/react.development.js"></script> | |
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> | |
<script src="https://unpkg.com/[email protected]/babel.min.js"></script> | |
<style> | |
* { | |
font-family: sans-serif; | |
} | |
.tweet { | |
border: 1px solid grey; | |
border-radius: 4px; | |
display: block; | |
padding: 8px; | |
margin: 4px; | |
max-width: 600px; | |
} | |
.date { | |
font-size: 0.8em; | |
color: grey; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="root"></div> | |
<script type="text/javascript"> | |
window.YTD = { | |
tweet: {}, | |
account: {}, | |
} | |
</script> | |
<script type="text/javascript" src="./tweet.js"></script> | |
<script type="text/javascript" src="./account.js"></script> | |
<script type="text/babel"> | |
const accountName = window.YTD.account.part0[0].account.username; | |
const createdStr = window.YTD.account.part0[0].account.createdAt; | |
const createdDate = new Date(/^[0-9]+$/.test(createdStr) ? parseInt(createdStr) : createdStr); | |
function Media(props) { | |
const data = props.data; | |
if (data.type == 'photo') { | |
return <img src={data.media_url_https} />; | |
} | |
else { | |
return <p>Unknown media attachment.</p>; | |
} | |
} | |
function Tweet(props) { | |
const data = props.data.tweet ? props.data.tweet : props.data; | |
const url = "https://twitter.com/" + accountName + "/status/" + data.id_str; | |
var media = []; | |
if (data.extended_entities) { | |
media = data.extended_entities.media.map((media, index) => { | |
return <Media data={media} key={index} />; | |
}); | |
} | |
return ( | |
<div className="tweet"> | |
<p>{data.full_text}</p> | |
<div>{media}</div> | |
<a target="_blank" href={url} className="date">{data.created_at}</a> | |
</div> | |
); | |
} | |
class App extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
year: createdDate.getFullYear(), | |
month: 'Jan', | |
searchTerm: "", | |
}; | |
} | |
render() { | |
const years = []; | |
const now = new Date(); | |
for (var i = createdDate.getFullYear(); i <= now.getFullYear(); i++) { | |
const year = i; | |
const onClick = () => { | |
this.setState((prevState) => { | |
return { | |
...prevState, | |
year, | |
}; | |
}); | |
}; | |
years.push(<button key={i} onClick={onClick}>{i}</button>); | |
} | |
const months = [ | |
'All', | |
'Jan', | |
'Feb', | |
'Mar', | |
'Apr', | |
'May', | |
'Jun', | |
'Jul', | |
'Aug', | |
'Sep', | |
'Oct', | |
'Nov', | |
'Dec', | |
].map((name) => { | |
const onClick = () => { | |
this.setState((prevState) => { | |
return { | |
...prevState, | |
month: name, | |
} | |
}) | |
} | |
return <button key={name} onClick={onClick}>{name}</button>; | |
}); | |
const month = this.state.month; | |
const year = this.state.year; | |
const searchTerm = this.state.searchTerm; | |
const tweets = window.YTD.tweet.part0; | |
const filteredTweets = tweets.filter((tweet) => { | |
if (tweet.tweet) { | |
tweet = tweet.tweet | |
} | |
if (searchTerm != "") { | |
const haystack = tweet.full_text.toLowerCase(); | |
const needle = searchTerm.toLowerCase(); | |
return haystack.indexOf(needle) != -1; | |
} | |
else { | |
return tweet.created_at.endsWith(year.toString()) && | |
(month == 'All' || tweet.created_at.indexOf(month) != -1) | |
} | |
}); | |
const maxTweets = 1000; | |
var elements = []; | |
for (var i = 0; i < Math.min(maxTweets, filteredTweets.length); i++) { | |
const data = filteredTweets[i]; | |
elements.push(<Tweet key={data.id_str} data={data} />); | |
} | |
if (maxTweets < filteredTweets.length) { | |
elements.push(<div key="toomany"> | |
<p>Too many results, only showing {maxTweets}</p> | |
</div>) | |
} | |
if (filteredTweets.length == 0) { | |
elements.push(<div key="none"> | |
<p>No tweets found!</p> | |
</div>) | |
} | |
var title; | |
if (searchTerm != "") { | |
title = "Search: " + searchTerm; | |
} | |
else { | |
title = month + " " + year; | |
} | |
title += " (" + filteredTweets.length + " results)"; | |
const handleChange = (event) => { | |
const newText = event.target.value; | |
this.setState((prevState) => { | |
return { | |
...prevState, | |
searchTerm: newText, | |
} | |
}); | |
}; | |
return ( | |
<div> | |
<nav> | |
{years} | |
</nav> | |
<nav> | |
{months} | |
</nav> | |
<input type="text" onChange={handleChange} value={searchTerm} /> | |
<h2>{title}</h2> | |
<div> | |
{elements} | |
</div> | |
</div> | |
); | |
} | |
} | |
ReactDOM.render( | |
<App />, | |
document.getElementById('root') | |
); | |
</script> | |
</body> | |
</html> |
In my archive downloaded today there is the usual tweet.js file, but also one called a tweet-part1.js which seems to contain the next bunch of tweets.
Tiffany's index file only gives me the tweets in the tweet.js; when I change line 36 to tweet-part1.js to also get the tweets from that file I get a blank page.
What could be done about that?
I have found a workaround: copy the content of tweet-part1.js at in tweet.js at the end of the file; then the index file works fine for the complete archive. :)
@owns86 hm, this index file also only works with the tweets in the "tweet.js" file, but not with those in the "tweet-part1.js" file?
yes, with that copy&paste workaround it works.
it seems this "scattering" happens when you pass a certain amount of tweets.
twitter has changed the structure of the archive again, and so this helpful file is not working anymore. sigh
@vossviola It’s The Difference Of The Former tweet.js File and The New One Which Contains The PArt WHeir A TWēEt Is SupposEd To Bē EditAble.
So the quickest rout I saw is selecting the first example of the addition/difference, and using a Keyboard Command/Control To Select All Similar BrackEts And THeir Contents, and then simply deleting them so that the formatting of The New tweet.js File be as The Former One, YEt: Still Contain All TWēEt Data.
The Other (More PreferABLE Option) is Adapting The index.html File of The Twitter Archive Browser To CompensATE For The Addition of “Edit TWēEt” Section. (But I couldn’t EVEn Select all within similar BrackEts Using MY BeLovEd Atom Web Developer App (Attacked Devices..).
Thanks!
Thank you for pointing to the difference!
I gave it a short try to delete those sections -- which wasn't very successful. 😎
So I am hoping that somebody will be able and willing to update the index.html file. 🤞
I have found out that Excel can now import json files.
there is only a little change needed to the twitter js files.
see https://www.zdnet.com/paid-content/article/want-to-analyse-your-tweets-how-to-import-twitter-json-data-exports-into-excel/
Hello! I'm very unexperienced in this, but could someone please tell me how do i use this? It would mean a lot to me! (I mostly need it to browse through my DMs.)
I have downloaded the file, put it in the same folder where my Twitter data is (already extracted), but I'm not sure what to do next, as it does nothing. I would really appreciate the help <3
As far as I can see it this script does not work any more because of the new structure of the Twitter archive files. :(
ohh i understand...:( thank you for the reply!<3
Hi! Where do I insert this? And if you can give me a free executor it would be helpful, Ive been searching the whole web for one.