Last active
February 15, 2025 17:27
-
-
Save pedramamini/22f9d8ca7ff892e6806f to your computer and use it in GitHub Desktop.
Blaze your way through Tinder (the dating app).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 += " | <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 | |
# 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() |
Yes, there are many dating sites, but I can tell you about an unusual dating site. I decided to try something new, but I was always afraid of getting into a strange or unsafe community. Вdsm dating and fetish https://www.fetishgo.com/ turned out to be exactly the place where I felt comfortable and safe. There is no judgment here, only respect for other people's desires and boundaries. Thanks to this site, I found the perfect match for communication and practice.
Interesting, never heard of such sites
Who can recommend a great online dating site?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think there are enough dating sites now