Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Twitter archive browser
<!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/babel-standalone@6.15.0/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>
@tiffany352
Copy link
Author

tiffany352 commented Oct 10, 2019

@vicentory @superwills

The new version I'm making is here: https://github.com/tiffany352/twitter-archive-browser

It's not a script you drop into your archive, it's instead a desktop application you install, but it has many more features and looks more like the actual Twitter website.

@Downs86
Copy link

Downs86 commented Nov 21, 2019

would you please email me one of these with the years set to 2014, onward? Downs.86@gmail.com ChrisDownsBooks.com

@EnigmaticZee
Copy link

EnigmaticZee commented Jan 9, 2020

@vicentory @superwills

The new version I'm making is here: https://github.com/tiffany352/twitter-archive-browser

It's not a script you drop into your archive, it's instead a desktop application you install, but it has many more features and looks more like the actual Twitter website.

Hi, there is no mac version? Any suggestions for a mac user?

@Downs86
Copy link

Downs86 commented Jan 13, 2020

@Downs86
Copy link

Downs86 commented Jan 13, 2020

@Downs86
Copy link

Downs86 commented Jan 14, 2020

Tiffany, I don’t know if you noticed, but this page no longer works with the most recent twitter archives tweet.js . Would you please create a new version able to be a suitable replacement for the index.html file many of us need. Thank you,
Chris

@vossviola
Copy link

vossviola commented Jan 17, 2020

Same problem here: This helpful solution doesn't work with an archive I dowloaded today. :(

@Downs86
Copy link

Downs86 commented Jan 19, 2020

Apparently the failure to load tweet data might have something to do with oAuth and the Twitter API but i haven't been into coding that much to know where to start on that.

@mmogithub
Copy link

mmogithub commented Jan 20, 2020

Same here. Twitter archive created today and Index.html no longer works :(
Page is blank. It seems no tweet.fuction is working.
First error is shown in "TypeError: tweet.created_at is undefined".

01
02

Blame Twitter 'cause for sure they changed something.
I hope you would be able to find a solution. Your file IS extremelly usefull.
Thanks in advance.

@Nonesuch13
Copy link

Nonesuch13 commented Jan 21, 2020

This was so valuable to me as well. I hope the original contributor sees how much people used and appreciated. Not sure what Twitter had to go and change. Makes no sense they would make it so hard to use their archive files.

@Downs86
Copy link

Downs86 commented Jan 24, 2020

@tiffany352
Copy link
Author

tiffany352 commented Jan 25, 2020

@Downs86 @Nonesuch13 @mmogithub @vossviola

Sorry for the delay. I just put up an updated version that has the createdAt issue fixed and also has the issue where it's showing dates from 1969 as well.

The updated script should be compatible with both these new changes Twitter made as well as the archives you have from last year etc.

@tiffany352
Copy link
Author

tiffany352 commented Jan 25, 2020

@EnigmaticZee I just published a new version of the archive browser app that includes a Mac build. I don't have a way to test it since I don't have a Mac of my own, but you could try it and see if it works. I hope that helps!

@Downs86
Copy link

Downs86 commented Jan 25, 2020

@Nonesuch13
Copy link

Nonesuch13 commented Jan 27, 2020

Thank you so much!

@mmogithub
Copy link

mmogithub commented Jan 28, 2020

Thank you so much.
It works like a charm.
You're the best, amazing work :)

@Downs86
Copy link

Downs86 commented Jan 28, 2020

@Downs86
Copy link

Downs86 commented Feb 16, 2020

@Downs86
Copy link

Downs86 commented Feb 19, 2020

@euan-gwd
Copy link

euan-gwd commented Feb 20, 2020

@tiffany352 Thanks for doing this, good piece of code, i was going to do it myself then found this, great job. I like your implementation.

@0987e02
Copy link

0987e02 commented Jun 20, 2020

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.

@Downs86
Copy link

Downs86 commented Jul 16, 2020

@Downs86
Copy link

Downs86 commented Jul 16, 2020

@vossviola
Copy link

vossviola commented Dec 31, 2020

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?

@vossviola
Copy link

vossviola commented Feb 13, 2021

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. :)

@Downs86
Copy link

Downs86 commented Mar 19, 2021

@vossviola
Copy link

vossviola commented Apr 21, 2021

@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?

@Downs86
Copy link

Downs86 commented Apr 21, 2021

@vossviola
Copy link

vossviola commented Apr 21, 2021

yes, with that copy&paste workaround it works.
it seems this "scattering" happens when you pass a certain amount of tweets.

@Downs86
Copy link

Downs86 commented Apr 21, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment