Archive for the 'Hacks' Category

Kill All Quizzes…

Updated 2009-08-03

Well the script stopped working a while ago, and I didn’t bother to update it… but this evening I thought I would…

Intro

So, Facebook sucks, but I can’t quit it. Anyone want to look up the definition of “addiction”? The newly redesigned news-feed removed the filter-by-app feature and added quizzes. Stupid fucking quizzes!

Googling for a way to remove them, I stumbled upon this Greasemonkey script by pepcza. It’s very simple and clever, using <jargon>XPath</jargon>, something that I’ve never touched before. It works, but doesn’t catch all quizzes, it requires you to use Firefox and install Greasemonkey, and it doesn’t delete those dag-named quizzes on the second viewing.

So I decided to make a bookmarklet that everyone can install in their bookmarks toolbar. One click, and bam, the quizzes disappear. Well almost all of them anyway.

How-To – Kuneri’s Version

Kuneri wrote a filter that is more comprehensive that it deletes all “news” from external apps (so he claims), which is a good idea because Facebook apps are 99.9% shit anyway. It also catches new items and filters them straight away.

I’ve expanded on his work and made different options, e.g. one that just fades the spam and makes it readable when you click on it, and another one that makes it faded and not take up that much space.

Total Deletion
javascript:void(( function () { document.addEventListener("DOMNodeInserted",function(event) {if(location.href.match('\/home\.php') && event.target.getElementsByClassName && event.target.getElementsByClassName('UIIntentionalStory_AttachmentInfo')) cleartheshizzle(event.target);}, false ); var cleartheshizzle=function(storyNode) {var footernodes = storyNode.getElementsByClassName('UIStoryAttachment');for (var i = 0; i < footernodes.length; i++) {if (footernodes[i].innerHTML.match('apps\.facebook\.com\/')) {storyNode.style.display='none';}};footernodes=null; }; if(location.href.match('\/home\.php')) {var storyNodes = document.getElementsByClassName('UIIntentionalStory');for (var i = 0; i < storyNodes.length; i++) {cleartheshizzle(storyNodes[i]);} }})());

Faded and Shrunk, Click to View
javascript:void(( function () { document.addEventListener("DOMNodeInserted",function(event) {if(location.href.match('\/home\.php') && event.target.getElementsByClassName && event.target.getElementsByClassName('UIIntentionalStory_AttachmentInfo')) cleartheshizzle(event.target);}, false ); var cleartheshizzle=function(storyNode) {var footernodes = storyNode.getElementsByClassName('UIStoryAttachment');for (var i = 0; i < footernodes.length; i++) {if (footernodes[i].innerHTML.match('apps\.facebook\.com\/')) {var tgt = storyNode;tgt.onclick = function() {this.style.height = (this.style.height == '20px')?'auto':'20px';this.style.opacity = (this.style.opacity < 1)?1:0.2;};tgt.style.height = '20px';tgt.style.opacity = 0.2;tgt.style.overflow = 'hidden';}};footernodes=null; }; if(location.href.match('\/home\.php')) {var storyNodes = document.getElementsByClassName('UIIntentionalStory');for (var i = 0; i < storyNodes.length; i++) {cleartheshizzle(storyNodes[i]);} }})());

Here’s a preview of what it does, with pixelation to protect the innocent and the stupid:
Fade and Shrink

How To
  1. Copy one of the codes in the gray boxes above (you can triple-click to select the whole thing) into your clipboard.
  2. Go to your Bookmarks toolbar (in Firefox, this is the bar where the “Smart Bookmarks” appears), right click in an empty area, and select “New Bookmark…”
  3. Add Bookmark
    Paste the copied code into the “Location:”
  4. Give the bookmark a name. I label mine “Fuck Quizzes”, and click “Add”.
  5. To test, go to your Facebook news feed, click the bookmark, and see the quizzes (if there were any) disappear.
  6. Profit!

Old and Doesn’t Always Work – pepcza Version

pepcza’s version of the script tries to find the words “took the” and “quiz”, but I think the quizmakers are changing it to “completed the quiz”, so it doesn’t always work.
(Tried on Firefox and Opera. If you use IE, my condolonces.)

  1. Copy the following code (you can triple-click to select the whole thing) into your clipboard:
    javascript:void((function(){var filterString = []; var h = 0; filterString[h++] = "//h3[@class = 'UIIntentionalStory_Message' and ((contains(string(.), 'took the') and contains(string(.), 'quiz')) or contains(string(.), 'Check out this quiz!'))]/ancestor::div[contains(@class, 'UIStory')][1]"; filterString[h++] = "//div[@class = 'UIStoryAttachment_Copy' and ((contains(string(.), 'took') and contains(string(.), 'quiz')) or contains(string(.), 'Check out this quiz!'))]/ancestor::div[contains(@class, 'UIStory')]"; for (var h = 0; h < filterString.length; h++) { var quizStories = document.evaluate(filterString[h], document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); for (var i = 0; i < quizStories.snapshotLength; i++) { quizStories.snapshotItem(i).style.display = 'none';}}})());
  2. Continue with Step 2 above!

Thoughts

I wonder if there’s a way to “OR” XPath statements, there probably is, I’m just too lazy to read up on it. And I wonder if this will just make the problem worse, because people who remove the damned quiz-spam will not complain about it, and the original problem won’t get fixed.

Oh No, A Diary!

Anyway, I happened upon avoision.com through BoingBoing, and wasted a few hours reading through the entries. Heh, blogs that go back years and years and have a lot of entries are always fun like that. It also reminded me of my own attempts of blogging, and then returning to the entries years later to think, “Wow, I’ve forgotten about that!”

So then I thought, “I should start (re-)doing that, blogging my daily life.”. Then I thought, “But all I do is sit around and watch my life tick away.” (not really, but that’s the “executive summary”).

Anyway, today I’ve been hacking a PHP script… hey, where did everyone go? … that creates HTTP Post requests, especially multi-part requests. The post body is made as a MIME stream, with a separator string that is unique and doesn’t occur in the contents. So I created said MIME stream, sent it to the server, and the server doesn’t see it properly. After a few hours of debugging, I saw the problem.

If the boundary (i.e. separator) is “abcd”, the first part is indicated with "--abcd", the last part is terminated with "--abcd--". Unfortunately the boundary Firefox generates is usually something like "------------1231231312". So two more -’s in front of that wasn’t really noticable.

Aarrgghhh!!!!

Oh well, that’s the problem with not reading the specs… you FAIL.

iPhone-Dev: Creating a Full-Screen Camera Preview, Part 2.

Damn, this blog is all about development nowadays, and to think, I told one guy I didn’t want to put his blog on my blogroll because it mostly had development stuff. Actually all it had was his “portfolio” of websites/apps he made, not bad, but not really a blog. So I said no. So he got offended. Eh, bite me, you SEO-weenie.

Well to get back to the point, like Erica, I hate the fact that the user has to click touch “Take Photo” to take a picture. I looked ather instructions, and after some a lot of struggling managed to get it to work.

But a problem was, the camera overlay would return if one returns to the camera view after leaving it, so I realized it had to go into its own view controller.

I tried improving it today, and it was really simple!

In the .h file:

@interface MyImagePickerController : UIImagePickerController {
  // Empty
}
@end

And in the .m file:

@implementation MyImagePickerController
- (void) viewDidAppear: (BOOL)animated {
  [super viewDidAppear:animated];
  UIView *plCameraView = [[[[[[self.view subviews] objectAtIndex:0] subviews] objectAtIndex:0] subviews] objectAtIndex:0];
  // [[[plCameraView subviews] objectAtIndex:3] removeFromSuperview];
  [[[plCameraView subviews] objectAtIndex:3] setHidden:YES];
}

Initializing is just like before:

  MyImagePickerController *imagePickerController = [[MyImagePickerController alloc] init];
  imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;

And to capture the preview, e.g. with an NSTimer (I haven’t looked up how to fake the clicking of the “Take Photo” button):

  CGImageRef img = UIGetScreenImage();
  UIImage *grabbedImage = [UIImage imageWithCGImage:img];
  // you can now manipulate the grabbedImage;
  CFRelease(img);

Update 2009-01-17: for some reason the overlay reappears when the view is redrawn, and it doesn’t seem that viewDidAppear gets recalled. Peeking at lajos’ code, he uses the method setHidden:YES, and it works for what I did too. So I’ve “commented out” my previous code that deletes the overlay and replaced it with one that just makes the overlay – as the method tells you – hidden.

error: invalid suffix “f” on integer constant

Apparently the way to fix this is to convert the integer to a float; from e.g. “45f” to “45.0f”.

At least it compiles after that!

Removing High-Pitched Noise from Canon Powershot Videos

One for the Googlers:

My Canon Powershot A630 also records videos. Unfortunately the microphone catches the sound of the internal mechanism (I think it’s the screen) and the video it produces has an annoying ringing noise.

Using this tip on audio removal from the wiki for the software Audacity, I managed to figure out that the high-pitched noise has a frequency of 4290 Hz.

I extracted the audio from the video using VirtualDub, and opened it with Audacity 1.3.4 beta. Like the Wiki page instructed, I used the Nyquist prompt and the command (notch2 s 4290 50) (yes the brackets are necessary), and this removed the noise flawlessly!

All that needs to be done is now to reinsert the audio into the video file. I can combine this with re-encoding the video with a more size- (and web-) friendly codec.

My First Word Processor

Talking about dot-matrix printers, a childhood memory of war (against the computer) comes back to me…

The year was when I was going to 7th grade (I’m 27 now, so calculate it yourself). A teacher wanted an assignment to be handed in computer-typed. No problem, I thought, I have WordStar and a printer.

At home, I booted up my DOS PC, put in the WordStar disk (5.25 inch!), and typed “ws.exe”. Bzzt, bzzt, I was back in DOS. Damnit, the program was infected with a virus!

With the assignment due the next day, I was desperate.

“I know, I’ll use Quick Basic’s features to control the printer, and make it print what I want”. All my program did: wait for input, and print it out. But the variable to hold the input can have a maximum of 255 characters!

“No problem, I’ll just stop after typing 3 full lines with 80 characters each, and press Enter. Put the whole thing in a while loop.”.

So that’s what I did, the printer screeching every time I hit Enter. One time I hit Enter too soon, leaving, aieieie, a big nasty gap in the middle of the page, catastrophe! (Was it in the middle of a word too? Can’t recall).

I was quite proud handing in that piece of paper with unformatted lines filling it. I tried to explain to my physics teacher why there was a gap in the middle, but looking back at it, I don’t think he cared…

Oo, Geeky: Opera Wikipedia Direct Access

Anyway, I usually access Wikipedia articles by trying to predict the URL of the topic instead of using Wikipedia’s in-built search (which, when I tried it back in the old days, was usually overloaded and not working) or Google. It’s very easy, just figure out what you want to read about (e.g. “Formula One”), convert the spaces to underscores (“Formula_One”), and append this term to the end of the Wikipedia URL (“http://en.wikipedia.org/wiki/Formula_One&#8221;).

Opera, like other browsers, support searches using the “%s” parameter, so that before today, I’ve automatized the 3rd step, saving me the typing of the long URL before the search term. But until now I’ve been manually adding the underscores.

But I tried something today, and it worked!

Apparently the search also support javascript: URLs (there’s probably an exploit in there somewhere), so I used Javascript to convert the spaces to underscores, and to go to the Wikipedia page.

Anyway, the “search” URL turns to:

javascript:searchstr="%s";window.location="http://en.wikipedia.org/wiki/"+searchstr.replace(/\+/g, '_');

As you can see, this breaks when the search term contains the double quote. You might also notice the function replaces the plus character, instead of the space, with the underscore. This is because Opera’s already replaced the space with the plus-sign before converting it as the search URL.

Damn, apparently if you want to search something that really contains the + sign, it also breaks. Ah well. :)

Stupid Computers…

foo.zip contains executable files. For security reasons, Google Mail does not allow you to send this type of file.

“Fine. Rename foo.zip to foo.zip.yeszip. Attach. Send.”

It works.

“Dumbasses.”

Not to mention it was a ZIP file that didn’t contain EXEs, just HTML and JavaScript files. Oh no, is somebody afraid of JS files?

OK, it’s for the protection of the idiots on the receiving end of the file. If it’s ZIP they’ll open it without thinking, whereas giving it the “yeszip” extension will stop them. But really, I’m sick of computers telling me what I can’t do.

YouTube via VLC, Version 2

Note: This post has been superseded with a newer one.

Well, I updated my YouTube VLC viewer, surprisingly I’ve managed to make it work with Windows Media Player as well, I wonder how I accomplished that…

Here’s the script.

YouTube made some small changes to the script, but apparently it’s a one character fix to the script, change one “&” to “;” and everything works again. Even more strange…

Google Maps Latitude and Longitude

Updated

Inspired by the post from Lifehacker, which is a re-post from tech-recipes, here’s how to get Google Maps coordinates in degrees, minutes and seconds:

javascript:coord = [gApplication.getMap().getCenter().lat(),gApplication.getMap().getCenter().lng()];
output = '';
for (x in [0,1]) {
	neg = (coord[x] < 0);
	if (neg) coord[x] *= -1;
	deg = Math.floor(coord[x]);
	minr = (coord[x] - deg) * 60;
	min = Math.floor(minr);
	sec = Math.floor((minr - min) * 60 * 100000)/100000;
	output += (x==0?'Lat':', Long') + ': ' + deg + "° " + (min < 10?'0':'') + min + "' " + (sec < 10?'0':'') + sec + '" ' + (x==0?(neg?'S':'N'):(neg?'W':'E'));
};
void(prompt('', output));

Also do-able in one line ;-) :

javascript:coord=[gApplication.getMap().getCenter().lat(),gApplication.getMap().getCenter().lng()];output='';for(x in [0,1]){neg=(coord[x]<0);if (neg) coord[x]*=-1;deg=Math.floor(coord[x]);minr=(coord[x]-deg)*60;min=Math.floor(minr);sec=Math.floor((minr - min)*60*100000)/100000;output+=(x==0?'Lat':', Long')+': '+deg+"° "+(min<10?'0':'')+min+"' "+(sec<10?'0':'')+sec+'" '+(x==0?(neg?'S':'N'):(neg?'W':'E'));};void(prompt('',output));

How to use? Just open up Google Maps, center the view to the place you want the coordinates of and paste the one-liner into the address bar. Or make a new bookmark, paste the line into “Location”, and “open” the bookmark while you’re in Google Maps.

Yay!

Of course, the question is, use +/- notation or north/south and east/west?

Le Update 1

Roger wanted the output to be pastable into Google Maps. Not hard actually…

Google Maps Compatible Output
javascript:coord=[gApplication.getMap().getCenter().lat(),gApplication.getMap().getCenter().lng()];output='';for(x in [0,1]){neg=(coord[x]<0);if (neg) coord[x]*=-1;deg=Math.floor(coord[x]);minr=(coord[x]-deg)*60;min=Math.floor(minr);sec=Math.floor((minr - min)*60*100000)/100000;output+=deg+"° "+(min<10?'0':'')+min+"' "+(sec<10?'0':'')+sec+'" '+(x==0?(neg?'S ':'N '):(neg?'W':'E'));};void(prompt('',output));

and ThomasV tried to comment but WordPress trying to be smart broke his quotes. I guess MapSource is a program?

MapSource Compatible Output
javascript:coord = [gApplication.getMap().getCenter().lat(),gApplication.getMap().getCenter().lng()]; output = ''; for (x in [0,1]) { neg = (coord[x] < 0); if (neg) coord[x] *= -1; output += (x==0?(neg?'S':'N'):(neg?'W':'E')) + coord[x] + ' '; }; void(prompt('', output));
Update 2009-02-08: Target Box

I updated the code, it now inserts a small semi-transparent box in the middle of the map, click on it and it will tell you the coordinates of the cent(re|er) of the map. Yay!

javascript:var tgt = document.createElement('div'); tgt.setAttribute('style', 'border: 1px solid blue; background-color: white; position: absolute; top: 49%; left: 49%; height: 2%; width: 2%; opacity: 0.5;'); tgt.onclick = function() { coord=[gApplication.getMap().getCenter().lat(),gApplication.getMap().getCenter().lng()];output='';for(x in [0,1]){neg=(coord[x]<0);if (neg) coord[x]*=-1;deg=Math.floor(coord[x]);minr=(coord[x]-deg)*60;min=Math.floor(minr);sec=Math.floor((minr - min)*60*100000)/100000;output+=(x==0?'Lat':', Long')+': '+deg+"° "+(min<10?'0':'')+min+"' "+(sec<10?'0':'')+sec+'" '+(x==0?(neg?'S':'N') : (neg?'W':'E'));};prompt('Coordinates',output); }; void(document.getElementById('map').appendChild(tgt));

YouTube via VLC!

Update, SitRep @ 2012-02-05

Sorry, I haven’t touched this script in ages, in any case, it hasn’t worked for a long time, and I don’t have time to fix it. Sorry about that. Maybe some other JavaScript expert can take a look at the code?

Note: This post has been superseded with a newer one. All updates will now be put at the bottom of this page :)

I stumbled upon Mike Sheldon’s GreaseMonkey script that allows people to watch YouTube videos without Flash, and was excited when I found out it also works on Windows. It’s not supposed to, because it depends on mplayerplug-in, which is only available for Linux, but VLC’s multimedia plugin took care of that problem and played it for me on my Windows system!

So now I can watch YouTube videos with 30fps, and even go full-screen, with no stuttering. You might not have these problems, but because I have a 1 GHz-PC I do (it’s a sorry state of affairs when software can’t play video smoothly on a GHz-PC). This is because VLC also uses hardware overlay to display the video, rather than the slow-ass, boo lame, software rendering method that the Flash player uses.

The only problem is, there are no controls to play/pause or change the volume. Apparently it can all be done via JavaScript. I really liked the smooth video, so I decided to extend the GreaseMonkey script and build those features in.

And so it’s born, my first GreaseMonkey script!

To use, install VLC (Update 2009-01-11: version 0.9.8a, Update 2009-08-10: version 1.0.1 did not work for me, unfortunately, must look into it) and GreaseMonkey, and then download:

vlcflvplayer.user.js

There are some drawbacks to this solution though; you have to wait for the whole video to load before viewing it (so much for the concept of streaming), and seeking works unreliably in my experience.

But hey, smooth video wins it for me! Enjoy!

And, in anticipation that this script will be popular, allow me to advertise about my upcoming trip.

Because this is the most googled page of my blog, allow me to advertise my iPhone app: Got an iPhone? Need a cool world clock? Get nHands Clock!

Here’s a demo of the script so you can see the effects of the script without installing it nor GreaseMonkey. Or not really, considering YouTube likes to change its video URLs so all you get is “this video is no longer valid”.

Here’s a demo of the script so you can see the effects of the script without installing it nor GreaseMonkey. Or not really, considering YouTube likes to change its video URLs so all you get is “this video is no longer valid”.
Update 2009-01-11: Well, I updated VLC to 0.9.6, and either the plugin or my script didn’t work. It worked again with 0.9.8a, although with broken controls because they changed the API. So now I fixed the API-calls in my script. I also updated the size to adjust to YouTube’s new widescreen format.

Update 2009-02-06: Well, this has become my most-visited post, as well as the #1 result in Google for “vlc youtube” (we’re number one, we’re number one!). If you gave the script a try, please leave a comment below to tell me what you think, did it work, what do you think can be improved, etc.

Update 2009-05-30: After a long wait, I’ve finally updated the script, adding the HQ button, as well as making the disk icon (hmm, I wonder what it does…) link to the HQ/HD format when they’re playing.

Update 2009-06-01: Crap, the last update didn’t really work as expected, great job testing it! (It was never possible to get the HD link to show up). It should work now.

Multiple Firefox Profiles

Elvy said

so much love to gina for the great post,
and thanks to louis for the pointer. ^_^

No problem, Elvy gets credit for making me remember that multiple Firefox profiles is possible. I actually have the same problem and without her complaining I wouldn’t have recalled this possibility.

I just tried it now, but because I still have FFox 1.5, I have to use this bit of Windows Scripting hack (stolen from here):

var shell = WScript.CreateObject("WScript.Shell");
var env = shell.Environment("User");
env("MOZ_NO_REMOTE") = 1;
shell.Exec('C:\\\\Program Files\\\\Mozilla Firefox 1.5\\\\firefox.exe -P "Profile 2"');
env("MOZ_NO_REMOTE") = 0;

You need to first start Firefox with “Firefox.exe -P” so you see the Profile Manager, create a new profile (mine is called “Profile 2″), modify the above code to make sure the directory and profile name on the fourth line are correct and save it as a .js file. Double clicking the JS file should execute it using Windows Scripting Host, or if you hate the icon, make a shortcut with Fox’s icon and this command:

C:\\Windows\\system32\\wscript.exe "C:\\wherever\\you\\put\\your\\jsfile.js"

And to make sure you don’t get lost and confused, you can set a different theme for each profile!

A Simple Linux Bandwidth Meter Using sed

Well, I was bored yesterday and I came up with this:

getdata() {
    DATA=`ifconfig eth0|sed -n 's/\( *RX bytes:\)\([0-9]*\)\([^:]*\):\([0-9]*\)\(.*)\)/\2 \4/p'`
    RX=`echo $DATA|sed -n 's/\([0-9]*\) \(.*\)/\1/p'`
    TX=`echo $DATA|sed -n 's/\([^ ]*\) \(.*\)/\2/p'`
}
getdata
while true; do
    OLDRX=$RX
    OLDTX=$TX
    getdata
    echo -n -e "\fRX: $((RX-OLDRX)) B/s\nTX: $((TX-OLDTX)) B/s"
    sleep 1
done

What it does is call ifconfig eth0 every second, and compare the total number of bytes received and transmitted up to the previous second, with the number up to this second. The difference is, well, the number of bytes transferred this second. It’s a cheap network usage monitor. :)

Idea of a Semi-Effective Way to Guard Access to Mature Content

Just as I try to watch an episode from the recently launched Fark TV, the site stopped me and told me I have to be over 18 to do so and asked me for my birthdate. Ok, no biggie, entered some fake date (gotta be careful of identity theft these days) and I got to the content.

Obviously, it’s basically legalese to protect their own ass, an 11 year old can calculate a date that would make him/her 18. But then I had a A-ha idea. What if you surprise them after the first form, and ask them to enter that birthdate again? Someone faking the date would just choose one randomly and not remember it when asked again. If they put something different the second time, ban their IP and prevent them from accessing the content.

Kinda clever, huh? Of course, it’s still easy to circumvent. Hitting the back button on some browsers gets you to the previous page with the date you enter still being there. Some people might still remember the date they put in the first time, they definitely would when they realize what’s going on. I’d say it would block 0.01% of all access attempts. Perhaps even higher, because it would catch out legitimate adults who put in fake dates and don’t know how to circumvent it. And in the end, the content provider would prefer that more people have access to the content, instead of less, anyway, not caring if they’re underage or not.

An idea not worth implementing…

BSODOD

Or, “Blue Screen of Death – on Demand”.

Apparently Microsoft provides a way for you to generate BSOD‘s when you wish, by modifying a registry entry and then pressing Ctrl+ScrollLock, ScrollLock . I can envision prank possibilities.

In Windows 98 it’s a lot easier: press the CD-ROM’s eject button while a CD is being read – instant BSOD. Well, death is not guaranteed, it usually is able to resume normally. So I guess it’s just BS?

ToDo’s & My New Robot

So, instead of working today, I stumbled upon Todo.txt, which is yet another To-Do-organizing system. It is based on plaintext though, so I was sold. I remember reading in this Library of Babel about a man who asked top hackers how they organize themselves. Most of the hackers responsed “plain text files”.

So I adopted this method. My problem is I’m sure I have at least 5 To-Dos.txt spread around the computers and storage systems (various remote filesystems, my GMail Drafts folder) I use. So, not well organized at all.

Todo.txt has its own concept, where each line of To-Do can have attributes (or are they tags? :P ) which makes it easy to filter accoriding to attributes. You can add, list and delete items using Bash commands, but I guess because the idea is simplification of life, someone made a bash script that makes it easy to manipulate the list.

And then someone got even more clever and made a Jabber-bot so you can play around with your ToDo’s over the internet. I’ve seen Jabber used by a server elsewhere once (an automatic build system that IMs you the build-status afterwards) and I loved the idea, so I decided to install it on my server.

It was trivial enough, and now there’s a new buddy in my Gaim buddy-list, the TodoBot.

Now I just have to feed it with data. And now that I have the basics, I’m wondering what other things I can do with the bot. Basically it just forwards everything I tell it to a bash script. It would be trivial to either modify the script, or modify the bot so it calls another script, e.g. to etherwake (that’s not a verb!) my desktop. Could one make a bot that accepts files and puts them online? One probably could.

I’m intrigued…



Follow

Get every new post delivered to your Inbox.