Tinderfish: Hacking Tinder for Maximum Bromance

I saw a great post a few days ago of a developer who had reverse engineered the Tinder API, and used it to catfish guys into flirting with each other through a proxy profile of an attractive woman. I was sold instantly.

tinderfish

The first step was to find a way to programmatically hook into the Tinder app. Fortunately the hard work of reverse engineering the app’s API has been done in this handy library https://github.com/nneal/tinder_pyro

Tinder uses OAuth with Facebook to authenticate, so the next step involved setting up a fake facebook account, and grabbing a valid Facebook ID and OAuth token.

Great, so now we can authenticate our Tinder catfishing bot and start scraping and sending messages. It’ll work like this: Guy #1 messages our fake profile, our fake profile forwards this message on to Guy #2, his response is then received by the fake profile, and forwarded to Guy #1. It’s classic man in the middle. Our fake profile acts as a proxy. Of course we may interject the odd message manually to help kick start our love story.

I decided to extent the TinderPyro::Client class provided by the tinder_pyro gem so that I could add my own useful methods on top of it, and wrap some sleeps and output messages to the console that would let me keep track with what’s going on. Here’s the sign in method, which inherits the original sign_in method, but with some nicely formatted messages letting me know the outcome.

Here’s where I hit my first problem. Inspecting the return of the original tinderfish.sign_in method, we see that my account has the banned parameter set to true. Nice try Tinder. I downloaded the app and tried to prod around to see what could be the issue. Logging in on my phone prompted me for an SMS confirmation. I entered my phone number and hit an error message every time. I gave up and eventually came back to it a few days later. Success. I ran the script again and was greeted with a JSON response of nearby users. This is going to be fun.

Let’s check the response. The first parameter is the HTTP response code; 200 if successful, and the second is an array of results, each element in the array representing a Tinder user nearby. Liberal use of ruby’s sleep(10) method here, 2-10 seconds between interactions – you don’t want to hit the API faster than a normal app user would and raise suspicion that could lead to your account getting banned.

Let’s experiment with getting nearby users:

This gives us a great look at how Tinder are structuring their response for our nearby users:

We can pull pretty much anything from the user we want at this stage, from date of birth to photos. Let’s take a look at grabbing the messages in our inbox. I ran into another problem here and stepped into the tinder_pyro gem to have a look at what was going on

Spot the problem. The method gets all updates after the last activity date, which is set by default to the current time. Hence it never gets any updates. I added a method to the gem to get all updates regardless of time.

Now we can write a method to fetch our matches that have sent us a particular amount of messages. Defaulting this to 1 message.

At this stage, we dont have any messages as our account is bare. So let’s generate some with a method for swiping right on N number of our nearby users. We’ll default this to 3, there are never less than 3 nearby users for me.

We’ll create a victim class to hold our victims with some handy methods built for purpose. Note we’re sorting our messages by sent_date to make it easier to grab the most recent from the end of the array. We’re also setting a last_message, and last_message sent at. These will come in handy later.

Then we’ll add the bulk of our program. We’ll create a new Tinderfish object, sign it in, grab our matches that have sent us one message, and assign the first two of the array to new victim objects.

So let’s do the set up, we need to grab our first message, send it to our other victim, then we can enter a loop where we’re just checking for new messages from a particular user and passing them back and forth, taking it in turns as to which user we look for.

We’ll also want to make sure we don’t give the game away by forwarding a message with our name in, so we’ll extend the String class and add a custom method to sanitise this.

Let’s get our first message and send it to victim_two. The send_message method takes a match ID and a string to send to that match ID. We can pull this from the initialisation of victim_one which pulled our first message into the last_message property:

We’ll add a method to the Tinderfish class that allows us to get new messages from a particular user. This will do a few things, and in retrospect, could (should) be broken down into smaller methods. We’re grabbing all our Tinder updates in any form since a certain time.

Then, we strip these down to any updates that match the id of the victim object we passed in to the method. Then, as this likes to match all updates including the since parameter, we strip out any updates that match the stored last_message for the victim.

If we have any updates from the victim, we’ll set their last_message and last_message_sent_at as our most recent message so that we can loop through and look for messages more recent than these. Then we simply return a true or false to this method:

I ran into another error here with the timestamp formatting so I decided to correct it in the gem. The error occured as everything being passed in was being formatted, even when this wasn’t necessary. I bypassed this so that it accepts an iso8601 string, rather than a Time object that gets formatted.

Change the above to this:

With the sending of our first message done, and a method for us to check for new messages, we can begin a loop. The loop alternates through our victims waiting for a message from each one, and then forwarding it to the other victim. It then switches to looking for messages from the other victim. Rinse and repeat.

And that’s it! Note once again the liberal use of sleeps, although I’ve randomised them here so that we don’t appear to Tinder like we’re making systematic calls.

Source available here: https://github.com/spencerldixon/tinderfish

Follow the readme and make the modifications to the tinder_pyro gem first.

 

tinderfish

IMG_2507

The moment of realisation just before he blocked me that he was talking to a guy.
I don’t think he ever figure it out completely.

Update: I built Slack integration. We now have a dedicated Slack channel where all messages get relayed to, complete with profile avatars. Slack version has also been added to github!

Screen Shot 2015-06-16 at 17.38.08

 

 

Branded Emails with Devise

I’ve been working on some email notifications for both admin and client recently and needed some nice branded emails instead of the default plain text ones that rails gives you. So for this post, we’ll look at how to create branded emails for your own mailers, and carry the same design over to our Devise emails for when a user signs up.

Firstly, lets create a template. I used a free responsive email template generator I found online to create my template and tweaked it accordingly. Responsive templates can be a hassle, so save the time and get one from someone who knows what they’re doing and adapt it to your needs.

I put this in a file in my layouts folder called mailer.html.erb and edited the template to have a <%= yield %> tag where my content would be.

Now we can use our mailer template for any emails from a mailer we’ve created. We can use them on a per method basis, or, as I prefer, we can just set it as a default template for our class to use. For instance, in the top of my admin_notification_mailer.rb, just below the default from setting, we can add:

To use our mailer layout. Rails is smart enough to know to look in the layouts folder for this. But what about getting Devise to adopt this same template?

Simple. In our config/application.rb

Now anything we send from Devise will automatically use the ‘mailer’ layout we created.

 

Validating Booleans

I came across a little gotcha today and thought it would be nice to start a series of ‘One Minute Rails’ posts. The idea is they’re super short, practical posts that you can digest within a minute and gain a practical piece of knowledge or insight that you can take away and apply to whatever you’re working on.

I was working on an application today where the Users model called for an admin boolean and I wanted to make this a required field for all users, so I stuck on the standard…

…and my test were still failing even though I knew admin was now a required field, or so I thought.

To understand why presence: true doesn’t work for boolean fields, we have to take a peak at how Rails is validating these fields. Rails’ way of validating presence is to use .blank? to check if something is blank, but if you try this on a false value ( false.blank? ) it returns true, confirming that the field is blank and failing validation miserably. The trick is to check that the boolean field is either one of an array of specified values. We can do this using inclusion like so…

A simple trick that will save you some serious frustration on those boolean validations.