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.


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.




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



One Week on Joylent

Rob Rhinehart is a software developer who created a drink that has been the centre of much debate and controversy over the past year. The idea is simple; what we eat gets broken down into nutrients and chemicals and the body is pretty indiscriminate about how it obtains these. So why not just give it what it wants and be done with the hassle of cooking, balancing meals and tracking your nutrition? Why not work backwards, and throw all the ingredients your body needs to survive into one tasty (debatable), beige shake and live off that instead of food?

Watch their creepy ass Orwellian video above.

I must admit, I’m fond of the concept. Not only because of the time it would save me, but because of the granular control of your diet. No longer are you overshooting your sugar intake because of the ludicrous amount of sugar in modern foods, or lacking in essential nutrients because of you’re just winging it all the time. It’s the perfect daily intake, everyday. You literally cannot eat anything healthier. In theory.

Rob dubbed his shake Soylent (http://www.soylent.me/) after the 1973 Soylent Green adapted from the book Make Room! Make Room!

But there’s an issue. Soylent is only available in the states, but never fear, the recipe has been detailed on Rob’s website allowing the product to be picked up and spun into action for the EU by an ex-drug dealer in Amsterdam named Joey for his company Joylent (http://www.joylent.eu/). Wonderful.

15 meals for €30. How could I resist?

For the next (working) week I’d give up food entirely. Living on 3 meals a day (approx 2100 calories) of a whey, oatflour, soyflour, maltodextrin and other assorted chemical mix.


I must admit I am favouring the original Soylent in my mind as Rob is constantly working hard to refine his formula and catch anything he has missed in making Soylent a 100% complete sustainable nutritional replacement for food. The inclusion of maltodextrin as source of carbohydrates is something the concerns me a touch as maltodextrin has a high gycemic response, meaning it breaks down to release sugar into the blood relatively quickly, causing insulin spikes. However Rob writes well informed posts about maltodextrin being coupled with oatflour for slow releasing sugars. Afterall, the body needs both. He also writes that Soylent’s overall glycemic response is within the normal acceptable levels.

Another concern is the lack of chewing. Some take up chewing gum to keep their facial muscles in good working order, but the real issue is that chewing holds the function of releasing enzymes as part of a pre-digestion stage of breaking food down in the mouth. Enzymes that Soylent potentially lacks. In fact, a study has linked long term feeding on powdered food with hypoglycemia and signs of systematic illnesses in mice (source: http://www.sciencedirect.com/science/article/pii/S0024320514003610). I guess in theory I could raise concerns with any food. I could talk about the carcinogenic nature of some foods, the effects of refined sugars, the list goes on. The fact is, Soylent isn’t as bad as it sounds. While you may not be replacing your entire intake with it just yet, I’m fond of it’s convenience for a quick meal on the go or when you have nothing in.

Day 1: Banana

I arrived at work and mixed up my first Joylent. Banana. It takes a bit of practice to get the consistency down. Initially I made it too thick and it was not pleasant. Mixing too thin will render equally disgusting results. The texture may also be an issue. I can taste the gritty feeling of the whey but I think it’s the oat flour or flax seed that just refuses to mix. I almost feel like I need to chew each gulp.
By lunch I was aware that my mental focus and energy levels were down, a kind of fog has been hanging over me all day, but it is Monday after all. There’s definitely a psychological aspect to food; even when I feel full in my stomach, I feel like my mouth needs to eat something. Its a strange sensation and one that I tried to rectify with chewing gum which helped temporarily.
Banana has a strange aftertaste of artificial sweetener that makes it difficult for me to keep down. In the end I only finished half of my third and final shake for the day and made a sandwich. It tasted amazing.

Day 2: Chocolate

Chocolate is much better. None of the artificial aftertaste and a lot more subtle than banana. I think it helps with making the texture more bearable. It kind of reminds me of a kind of chocolate weetabix flavour at this point. I could almost go as far as saying it tastes good. I still feel terrible though.

Day 3: Strawberry

Strawberry is good. I almost prefer it to chocolate. It’s made with real freeze dried strawberries so it doesn’t taste artificial at all. In fact, it tastes quite natural and refreshing. Like strawberry yoghurt. I enjoyed my strawberry day and felt a little more energised than previous days. I also took to drinking my third and final shake before I left work. It was extremely liberating to get home and have nothing to do; no cooking, no washing up, just me time. It bought me an extra hour of my day easily. The only downside was I did tend to get hungry around 9pm.

Day 4: Vanilla

Vanilla is good. My energy levels feel almost back to normal but not quite. I’m ready to give up and I decided to make this my final day. Perhaps it’s the radical shift in diet that has made me feel groggy and a phase out of food whilst fading in Joylent would be a more appropriate way to get my body used to an all liquid diet. The taste of vanilla is probably my favourite, it’s on a par with strawberry. I still miss the act of chewing something and feeling something in my stomach, which is, to me, the biggest drawback of Joylent. It’s just not as filling as a meal.

Conclusion and Ongoing Use

Can you live off Joylent? Probably. Would I recommend it? No. I left my experiment with a new appreciation of the psychological impact of food.

Did I feel hungry? Yes. A lot. Weighing myself at the beginning and end of the trial I noticed a slight gain in weight. Nothing significant, but enough to prove that the calories were there. My hunger was simply a side effect of consolidating my meals into 3 meals per day rather than my usual routine of eating little but often.

But will I still use Joylent? There are actually a large number of bags of Joylent sat in my cupboard right now (no banana). They sit there as my emergency meal when I either don’t feel like I can make the effort to cook, or just simply don’t have the time to cook as I have to get home and be straight out again. This is where I think Joylent, Soylent and other brands can offer something really sustainable. It’s almost the perfect quick meal, balanced, easily prepared, and fast.

Rob Rhinehart is funding a long term study on the effects of Soylent. I’m hoping this will lead to new developments, further revisions and credibility of a potentially great idea that isn’t quite yet the replacement for food it sets out to be.


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.