Skip to content

Instantly share code, notes, and snippets.

@pedramamini
Last active June 12, 2022 15:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pedramamini/22f9d8ca7ff892e6806f to your computer and use it in GitHub Desktop.
Save pedramamini/22f9d8ca7ff892e6806f to your computer and use it in GitHub Desktop.
Blaze your way through Tinder (the dating app).
#!/usr/bin/env python
"""
blaze through tinder
Setup:
- proxy / sniff out your auth token and edit constant under imports.
- API may have changed.
- it's hard coded to search for girls looking for guys.
- this was a quick hack, i'm not maintaining it or answering questions about it.
- the best same-track project by far is available here: https://github.com/crockpotveggies/tinderbox
Usage:
blaze ogle
blaze profile set <var> <val>
blaze teleport city <city>
blaze teleport <lat> <lon>
blaze sift youngest <age>
blaze sift oldest <age>
blaze sift furthest <miles>
blaze like <tid>
blaze pass <tid>
blaze web-ui
Todo:
- ajax background adjustment sliders for age/radius.
- map input for search area coordinates.
- itunes screen saver visuals, streaming or flipping.
- mouse over expand images, potentially fold out images.
- keyboard shortcuts.
"""
import BaseHTTPServer
import datetime
import string
import json
import sys
import os
# batteries not included.
import requests
import docopt
# who we be.
AUTH_TOKEN = ""
# where we bind.
HOST = "127.0.0.1"
PORT = 12345
# where we be.
LOCATIONS = \
{
"AUSTIN" : dict(lat=30.2500, lon= 97.7500),
"NYC" : dict(lat=40.7222, lon=-73.9941),
"SANDIEGO" : dict(lat=32.7150, lon=117.1625),
}
# how we talk.
URL = "https://api.gotinder.com"
HEADERS = \
{
"User-Agent" : "Tinder/3.0.4 (iPhone; iOS 7.1; Scale/2.00)",
"os_version" : "700001",
"platform" : "ios",
"Content-Type" : "application/json; charset=utf-8",
"Connection" : "keep-alive",
"X-Auth-Token" : AUTH_TOKEN,
"Authorization" : 'Token token="%s"' % AUTH_TOKEN,
"app_version" : 3,
}
# shared state container.
class shared_state:
def __init__ (self):
self.lat = 0
self.lon = 0
self.pic_size = 172
self.response = ""
SS = shared_state()
# persisent on disk file path and in-memory yay log.
YAY_LOG = os.sep.join([os.path.dirname(__file__), "log.yay"])
YAYS = []
if os.path.exists(YAY_LOG):
with open(YAY_LOG) as fh:
for line in fh.readlines():
YAYS.append(line.strip())
print "in-memory yay log initialized with %d ladies." % len(YAYS)
########################################################################################################################
def judge (tid, want=False):
"""
tid = tinder id
"""
# yay or nay?
if not want:
route = "/pass/%s" % tid
else:
route = "/like/%s" % tid
# yay log.
with open(YAY_LOG, "a+") as fh:
fh.write(tid + "\n")
# in-memory yay log.
YAYS.append(tid)
response = requests.get(URL + route, headers=HEADERS).content
return json.loads(response)
########################################################################################################################
def ogle (pic_size=None):
"""
pic_size: None, 84, 172, 320, 640.
"""
response = json.loads(requests.get(URL + "/user/recs", headers=HEADERS).content)
girls = []
# ensure we found some girls...
if response.get("message"):
raise Exception(response["message"])
# for each girl.
for girl in response.get("results", []):
# no sneaky sneaky.
if int(girl["gender"]) != 1:
continue
# high level container.
g = \
{
"id" : girl["_id"],
"age" : datetime.date.today().year - int(girl["birth_date"][:4]),
"bio" : girl["bio"],
"name" : girl["name"],
"photos" : [],
"distance" : girl["distance_mi"],
}
# add photos.
for photo in girl["photos"]:
# if no pic size is specified, default to full size.
if pic_size is None:
g["photos"].append(photo["url"])
# otherwise, search for and append only the desired size.
else:
for pf in photo["processedFiles"]:
if pf["height"] == pic_size:
g["photos"].append(pf["url"])
# add the girl to our list.
girls.append(g)
# return our list of processed girls.
return girls
########################################################################################################################
def ping ():
"""
ping / pong
"""
ping = dict(last_activity_date=datetime.datetime.now().isoformat())
pong = requests.post(URL + "/updates", headers=HEADERS, data=json.dumps(ping)).content
return json.loads(pong)
########################################################################################################################
def sift (age_filter_min=None, age_filter_max=None, distance_filter=None):
"""
min/max age/distance
"""
preferences = dict()
if age_filter_min:
preferences["age_filter_min"] = age_filter_min
if age_filter_max:
preferences["age_filter_max"] = age_filter_max
if distance_filter:
preferences["distance_filter"] = distance_filter
# post.
if preferences:
response = requests.post(URL + "/profile", headers=HEADERS, data=json.dumps(preferences)).content
# get.
else:
response = requests.get(URL + "/profile", headers=HEADERS).content
return json.loads(response)
########################################################################################################################
def teleport (lat, lon):
"""
@see: LOCATIONS
"""
location = dict(lat=lat, lon=lon)
response = requests.post(URL + "/user/ping", headers=HEADERS, data=json.dumps(location)).content
return json.loads(response)
########################################################################################################################
class web_ui (BaseHTTPServer.BaseHTTPRequestHandler):
"""
Web UI.
"""
####################################################################################################################
def do_GET (self):
self.do_EVERYTHANG()
def do_HEAD (self):
self.do_EVERYTHANG()
def do_POST (self):
self.do_EVERYTHANG()
def do_EVERYTHANG (self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
# construct and return HTML response starting with the page header and flush it immediately.
self.page_header()
self.flush()
# route processing.
if "/clear" in self.path:
self.route_clear()
elif "/ogle" in self.path:
self.route_ogle()
elif "/like" in self.path:
self.route_like()
elif "/pass" in self.path:
self.route_pass()
elif "/profile" in self.path:
self.route_profile()
elif "/ping" in self.path:
self.route_ping()
elif "/set/size" in self.path:
self.route_set_size()
elif "/teleport" in self.path:
self.route_teleport()
else:
SS.response += "why hello there... "
if not SS.lat or not SS.lon:
SS.response += "teleport to a city to begin ogling."
# footer.
self.page_footer()
# final flush.
self.flush()
####################################################################################################################
def error_out (self, msg):
"""
print error
"""
SS.response = "<h1><font color='red'>%s</font></h1>" % msg
self.flush()
####################################################################################################################
def flush (self):
"""
flush the response buffer.
"""
try:
self.wfile.write(SS.response)
except UnicodeDecodeError:
self.wfile.write(self.sanitize(SS.response))
except UnicodeEncodeError:
self.wfile.write(self.sanitize(SS.response))
SS.response = ""
####################################################################################################################
def page_header (self):
"""
render page header.
"""
SS.response += "<h4>"
# nav + ogle.
SS.response += "<a href='/'>top</a> | <a href='/ogle'>ogle</a> | "
# nav + teleport.
SS.response += "teleport: "
cities = LOCATIONS.keys()
cities.sort()
for city in cities:
coords = LOCATIONS[city]
SS.response += "<a href='/teleport/%f/%f'>%s</a>, " % (coords["lat"], coords["lon"], city.lower())
SS.response = SS.response.rstrip(" ,")
# nav + pic size.
for size in [84, 172, 320, 640]:
SS.response += " | <a href='/set/size/%d'>%d</a>" % (size, size)
# nav + profile.
SS.response += " | <a href='/profile'>profile</a>"
# nav + ping.
SS.response += " | <a href='/ping'>ping</a>"
# nav + clear stream.
SS.response += " &nbsp;&nbsp;&nbsp; | &nbsp;&nbsp;&nbsp; <a href='/clear'>clear</a></h4>"
####################################################################################################################
def page_footer (self):
"""
render page footer.
"""
# add the nav to the bottom of the page as well if we are in ogle mode.
if "/ogle" in self.path:
self.page_header()
SS.response += "<p><center><small>"
SS.response += "blaze!"
SS.response += " | lat=%s lng=%s" % (SS.lat, SS.lon)
SS.response += " | size=%dpx" % (SS.pic_size)
SS.response += "</small></center></p>"
####################################################################################################################
def route_clear (self):
"""
start fresh.
"""
SS.response += "<h1>clearing out the current buffer...</h1>"
self.flush()
for girl in ogle():
# don't next girls we've already liked.
if girl["id"] in YAYS:
SS.response += "<h2>sparing %s</h2>" % girl["name"]
self.flush()
# nix the rest.
else:
SS.response += "<h2>nixing %s</h2>" % girl["name"]
self.flush()
judge(girl["id"], want=False)
SS.response += "<h3>done. <a href='/ogle'>keep oglin...</a></h3>"
####################################################################################################################
def route_like (self):
"""
thumbs up.
"""
tid = self.path.split("/")[-1]
result = judge(tid, want=True)
if result.get("status") == 500:
SS.response += "<h1>error: %s</h1>" % result["error"]
elif result["match"]:
SS.response += "<h1>yay +match! on %s</h1>" % tid
else:
SS.response += "<h1>yay %s</h1>" % tid
####################################################################################################################
def route_ogle (self):
"""
take a gander.
"""
yay = "http://awesomeashild.files.wordpress.com/2013/10/borat-thumbs-up.jpg"
nay = "http://www.thefootballeducator.com/wp-content/uploads/2012/12/TGC-Thumbs-down.jpg"
# add a title on the ogle tab.
SS.response += "<title>ogle</title>"
try:
girls = ogle(SS.pic_size)
except Exception as e:
return self.error_out("ERROR: %s" % e.message)
for girl in girls:
SS.response += "<table><tr><td bgcolor=000>"
SS.response += "<a href='/like/%s' border=0><img src='%s' width=200></a>" % (girl["id"], yay)
SS.response += "</td><td bgcolor=#eeeeee>"
SS.response += "<h1>%s</h1>" % girl["name"].title()
SS.response += "<h2><b>%d</b> years old, <b>%d</b> miles away</h2>" % (girl["age"], girl["distance"])
SS.response += "<h3>%s</h3>" % self.sanitize(girl["bio"])
SS.response += "</td></tr><tr><td bgcolor=000>"
SS.response += "<a href='/pass/%s' border=0><img src='%s' width=200></a>" % (girl["id"], nay)
SS.response += "</td><td bgcolor=000>"
for photo in girl["photos"]:
base, orig = photo.rsplit("/", 1)
orig = "/".join([base, orig.split("_")[-1]])
SS.response += "<a href='%s'><img src='%s' border=0></a>" % (orig, photo)
SS.response += "</td></tr></table>"
SS.response += "<p>"
####################################################################################################################
def route_pass (self):
"""
thumbs down.
"""
tid = self.path.split("/")[-1]
result = judge(tid, want=False)
if result.get("status") == 500:
SS.response += "<h1>error: %s</h1>" % result["error"]
elif result["status"] == 200:
SS.response += "<h1>nay %s</h1>" % tid
else:
SS.response += "<h1>nay? %s</h1>" % tid
####################################################################################################################
def route_ping (self):
"""
ping / pong.
"""
SS.response += "<table>"
for k, v in ping().items():
SS.response += "<tr><td>%s</td><td>%s</td></tr>" % (k, v)
SS.response += "</table>"
####################################################################################################################
def route_profile (self):
"""
profile data (sift with no mods specified).
"""
SS.response += "<table>"
for k, v in sift().items():
SS.response += "<tr><td>%s</td><td>%s</td></tr>" % (k, v)
SS.response += "</table>"
####################################################################################################################
def route_set_size (self):
"""
set picture size we retrieve.
"""
SS.pic_size = int(self.path.split("/")[-1])
SS.response += "<h3>pic size = %d. <a href='/ogle'>keep oglin...</a></h3>" % SS.pic_size
####################################################################################################################
def route_teleport (self):
"""
teleport.
"""
SS.lat, SS.lon = map(float, self.path.split("/")[-2:])
SS.response += "<pre>"
SS.response += "teleporting to lat:%s, lon:%s...\n" % (SS.lat, SS.lon)
self.flush()
result = teleport(SS.lat, SS.lon)
if "error" in result:
SS.response += result["error"]
else:
SS.response += "success."
SS.response += "</pre>"
SS.response += "<h3><a href='/ogle'>start ogling</a></h3>"
####################################################################################################################
def sanitize (self, s):
"""
filter out chars that will result in encoding woes.
"""
r = ""
for c in s:
if c in string.printable:
r += c
return r
########################################################################################################################
if __name__ == "__main__":
# parse the arguments out of our documentation at the top.
arguments = docopt.docopt(__doc__)
# retrieve available matches given the current location and sift preferences.
if arguments["ogle"]:
print "ogling..."
try:
girls = ogle()
except Exception as e:
sys.stderr.write("failed: %s\n" % e.message)
sys.exit(1)
for girl in girls:
print "[%s] %s" % (girl["id"], girl["name"].title())
print "%d years old, %d miles away" % (girl["age"], girl["distance"])
if girl["bio"]:
print "bio: %s" % girl["bio"]
for photo in girl["photos"]:
print " %s" % photo
print
# arbitrary profile var/val setting tool.
elif arguments["profile"] and arguments["set"]:
preferences = {}
preferences[arguments["<var>"]] = arguments["<val>"]
print requests.post(URL + "/profile", headers=HEADERS, data=json.dumps(preferences)).content
# teleport to lat-lon coordinate pair or named city.
elif arguments["teleport"]:
if arguments["city"]:
city = arguments["<city>"].upper()
if city not in LOCATIONS:
sys.stderr.write("unknown city: %s\n" % city.title())
sys.stderr.write("known cities: %s\n" % ", ".join(map(str.title, LOCATIONS.keys())))
sys.exit(1)
print "teleporting to %s" % city.title()
print teleport(LOCATIONS[city]["lat"], LOCATIONS[city]["lon"])
elif "<lat>" in arguments and "<lon>" in arguments:
lat, lon = map(float, [arguments["<lat>"], arguments["<lon>"]])
print "teleporting to lat:%f lon:%f" % (lat, lon)
print teleport(lat, lon)
# update our filtering preferences for youngest, oldest, or furthest.
elif arguments["sift"]:
if arguments["youngest"]:
age = int(arguments["<age>"])
print "sifting for girls %d and over" % age
prefs = sift(age_filter_min=age)
elif arguments["oldest"]:
age = int(arguments["<age>"])
print "sifting for girls %d and younger" % age
prefs = sift(age_filter_max=age)
elif arguments["furthest"]:
miles = int(arguments["<miles>"])
print "sifting for girls within a %d mile radius" % miles
prefs = sift(distance_filter=miles)
banner = "looking for girls between %d and %d years old within %d miles"
banner %= prefs["age_filter_min"], prefs["age_filter_max"], prefs["distance_filter"]
print banner
# daddy likes.
elif arguments["like"]:
print "daddy likes %s" % arguments["<tid>"]
print judge(arguments["<tid>"], want=True)
# daddy dislikes.
elif arguments["pass"]:
print "bleh %s" % arguments["<tid>"]
print judge(arguments["<tid>"], want=False)
# spin up an interactive web-ui.
elif arguments["web-ui"]:
print "UI launched at http://%s:%s" % (HOST, PORT)
try:
ui = BaseHTTPServer.HTTPServer((HOST, PORT), web_ui)
ui.ss = SS
ui.serve_forever()
except KeyboardInterrupt:
print "exiting..."
ui.socket.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment