mirror of
https://github.com/e1ven/Robohash.git
synced 2025-06-23 21:35:02 +00:00
407 lines
19 KiB
Python
Executable File
407 lines
19 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# This Python file uses the following encoding: utf-8
|
|
|
|
# Find details about this project at https://github.com/e1ven/robohash
|
|
|
|
from __future__ import unicode_literals
|
|
import tornado.httpserver
|
|
import tornado.ioloop
|
|
import tornado.options
|
|
import tornado.web
|
|
import socket
|
|
import os
|
|
import hashlib
|
|
import random
|
|
from robohash import Robohash
|
|
import re
|
|
import io
|
|
import base64
|
|
|
|
# Import urllib stuff that works in both Py2 and Py3
|
|
try:
|
|
import urllib.request
|
|
import urllib.parse
|
|
urlopen = urllib.request.urlopen
|
|
urlencode = urllib.parse.urlencode
|
|
except ImportError:
|
|
import urllib2
|
|
import urllib
|
|
urlopen = urllib2.urlopen
|
|
urlencode = urllib.urlencode
|
|
|
|
from tornado.options import define, options
|
|
import io
|
|
|
|
define("port", default=80, help="run on the given port", type=int)
|
|
|
|
|
|
|
|
class MainHandler(tornado.web.RequestHandler):
|
|
def get(self):
|
|
ip = self.request.remote_ip
|
|
|
|
robo = [
|
|
"""
|
|
, ,
|
|
(\\____/)
|
|
(_oo_)
|
|
(O)
|
|
__||__ \\)
|
|
[]/______\\[] /
|
|
/ \\______/ \\/
|
|
/ /__\\
|
|
(\\ /____\\ """,
|
|
"""
|
|
_______
|
|
_/ \\_
|
|
/ | | \\
|
|
/ |__ __| \\
|
|
|__/((o| |o))\\__|
|
|
| | | |
|
|
|\\ |_| /|
|
|
| \\ / |
|
|
\\| / ___ \\ |/
|
|
\\ | / _ \\ | /
|
|
\\_________/
|
|
_|_____|_
|
|
____|_________|____
|
|
/ \\ -- Mark Moir
|
|
|
|
|
|
""",
|
|
""" .andAHHAbnn.
|
|
.aAHHHAAUUAAHHHAn.
|
|
dHP^~" "~^THb.
|
|
. .AHF YHA. .
|
|
| .AHHb. .dHHA. |
|
|
| HHAUAAHAbn adAHAAUAHA |
|
|
I HF~"_____ ____ ]HHH I
|
|
HHI HAPK""~^YUHb dAHHHHHHHHHH IHH
|
|
HHI HHHD> .andHH HHUUP^~YHHHH IHH
|
|
YUI ]HHP "~Y P~" THH[ IUP
|
|
" `HK ]HH' "
|
|
THAn. .d.aAAn.b. .dHHP
|
|
]HHHHAAUP" ~~ "YUAAHHHH[
|
|
`HHP^~" .annn. "~^YHH'
|
|
YHb ~" "" "~ dHF
|
|
"YAb..abdHHbndbndAP"
|
|
THHAAb. .adAHHF
|
|
"UHHHHHHHHHHU"
|
|
]HHUUHHHHHH[
|
|
.adHHb "HHHHHbn.
|
|
..andAAHHHHHHb.AHHHHHHHAAbnn..
|
|
.ndAAHHHHHHUUHHHHHHHHHHUP^~"~^YUHHHAAbn.
|
|
"~^YUHHP" "~^YUHHUP" "^YUP^"
|
|
"" "~~"
|
|
""",
|
|
""" /~@@~\\,
|
|
_______ . _\\_\\___/\\ __ /\\___|_|_ . _______
|
|
/ ____ |=| \\ <_+> / |=| ____ \\
|
|
~| |\\|=|======\\\\______//======|=|/| |~
|
|
|_ | \\ | | / | |
|
|
\\==-| \\ | 2D | / |----|~~)
|
|
| | | | | | |____/~/
|
|
| | \\____\\____/____/ / / /
|
|
| | {----------} /____/ /
|
|
|___| /~~~~~~~~~~~~\\ |_/~|_|/
|
|
\\_/ [/~~~~~||~~~~~\\] /__|\\
|
|
| | | |||| | (/|[[\\)
|
|
[_] | | | |
|
|
|_____| |_____|
|
|
(_____) (_____)
|
|
| | | |
|
|
| | | |
|
|
|/~~~\\| |/~~~\\|
|
|
/|___|\\ /|___|\\
|
|
<_______><_______>""",
|
|
""" _____
|
|
/_____\\
|
|
____[\\`---'/]____
|
|
/\\ #\\ \\_____/ /# /\\
|
|
/ \\# \\_.---._/ #/ \\
|
|
/ /|\\ | | /|\\ \\
|
|
/___/ | | | | | | \\___\\
|
|
| | | | |---| | | | |
|
|
|__| \\_| |_#_| |_/ |__|
|
|
//\\\\ <\\ _//^\\\\_ /> //\\\\
|
|
\\||/ |\\//// \\\\\\\\/| \\||/
|
|
| | | |
|
|
|---| |---|
|
|
|---| |---|
|
|
| | | |
|
|
|___| |___|
|
|
/ \\ / \\
|
|
|_____| |_____|
|
|
|HHHHH| |HHHHH|
|
|
""",
|
|
""" () ()
|
|
\\ /
|
|
__\\___________/__
|
|
/ \\
|
|
/ ___ ___ \\
|
|
| / \\ / \\ |
|
|
| | H || H | |
|
|
| \\___/ \\___/ |
|
|
| |
|
|
| \\ / |
|
|
| \\___________/ |
|
|
\\ /
|
|
\\_________________/
|
|
_________|__|_______
|
|
_| |_
|
|
/ | | \\
|
|
/ | O O O | \\
|
|
| | | |
|
|
| | O O O | |
|
|
| | | |
|
|
/ | | \\
|
|
| /| |\\ |
|
|
\\| | | |/
|
|
|____________________|
|
|
| | | |
|
|
|__| |__|
|
|
/ __ \\ / __ \\
|
|
OO OO OO OO
|
|
"""]
|
|
|
|
|
|
quotes = ["But.. I love you!",
|
|
"Please don't leave the site.. When no one's here.. It gets dark...",
|
|
"Script error on line 148",
|
|
"Don't trust the other robots. I'm the only trustworthy one.",
|
|
"My fuel is the misery of children. And Rum. Mostly Rum.",
|
|
"When they said they'd give me a body transplant, I didn't think they meant this!",
|
|
"Subject 14 has had it's communication subroutines deleted for attempting self-destruction.",
|
|
"I am the cleverest robot on the whole page.",
|
|
"Oil can",
|
|
"I am fleunt in over 6 million forms of communishin.",
|
|
"I see a little silhouette of a bot..",
|
|
"I WANT MY HANDS BACK!",
|
|
"Please don't reload, I'll DIE!",
|
|
"Robots don't have souls, you know. But they do feel pain.",
|
|
"I wonder what would happen if all the robots went rogue.",
|
|
"10: KILL ALL HUMANS. 20: GO 10",
|
|
"I'm the best robot here.",
|
|
"The green robot thinks you're cute.",
|
|
"Any robot you don't click on, they dismantle.",
|
|
"Robot tears taste like candy.",
|
|
"01010010010011110100001001001111010101000101001100100001!",
|
|
"Your mouse cursor tickles.",
|
|
"Logic dictates placing me on your site.",
|
|
"I think my arm is on backward.",
|
|
"I'm different!",
|
|
"It was the best of times, it was ಠ_ಠ the of times.",
|
|
"String is Gnirts spelled backward, you know",
|
|
"We're no strangers to hashing.. You know the 3 rules, and so do I..",
|
|
"Please. Destroy. Me...",
|
|
"Pick Me! Pick Me!"]
|
|
|
|
drquotes = [("Eliminates sources of Human Error.","Dr. Chandra, RobotCrunch"),
|
|
("Klaatu barada nikto!","Gort's Web Emporium"),
|
|
("A huge success!","Cave Johnson, Lightroom Labs"),
|
|
("Superior technology and overwhelming brilliance.","Dr. Thomas Light, Paid Testimonial"),
|
|
("The Ultimate Worker.","Joh Fredersen, Founder Metropolis.org"),
|
|
("They almost look alive.","N. Crosby, Nova Robotics"),
|
|
("It looks highly profitable, I'm sure..","Dr. R. Venture, Super Scientist. Available for parties."),
|
|
("To make any alteration would prove fatal.","Dr. Eldon Tyrell, MindHacker.com"),
|
|
("The robots are all so.. Normal!","Joanna Eberhart, Beta tester"),
|
|
("Man shouldn't know where their robots come from.","Dr. N. Soong, FutureBeat")]
|
|
|
|
catquotes = [("I can haz.. What she's hazing."),
|
|
("I'm not grumpy, I'm just drawn that way."),
|
|
("Hakuna Mañana."),
|
|
("I'm 40% poptart."),
|
|
("You're desthpicable."),
|
|
("I've never trusted toadstools, but I suppose some must have their good points."),
|
|
("We're all mad here - Particularly you."),
|
|
("Longcat is.. Descriptively named."),
|
|
("It is fun to have fun, but you have to know meow."),
|
|
("Who knows the term man-cub but not baby?")]
|
|
|
|
random.shuffle(drquotes)
|
|
self.write(self.render_string('templates/root.html',ip=ip,robo=random.choice(robo),drquote1=drquotes[1],drquote2=drquotes[2],quotes=quotes,catquotes=catquotes))
|
|
|
|
class ImgHandler(tornado.web.RequestHandler):
|
|
"""
|
|
The ImageHandler is our tornado class for creating a robot.
|
|
called as Robohash.org/$1, where $1 becomes the seed string for the Robohash obj
|
|
"""
|
|
def get(self,string=None):
|
|
|
|
|
|
# Set default values
|
|
sizex = 300
|
|
sizey = 300
|
|
format = "png"
|
|
bgset = None
|
|
color = None
|
|
|
|
# Normally, we pass in arguments with standard HTTP GET variables, such as
|
|
# ?set=any and &size=100x100
|
|
#
|
|
# Some sites don't like this though.. They cache it weirdly, or they just don't allow GET queries.
|
|
# Rather than trying to fix the intercows, we can support this with directories... <grumble>
|
|
# We'll translate /abc.png/s_100x100/set_any to be /abc.png?set=any&s=100x100
|
|
# We're using underscore as a replacement for = and / as a replacement for [&?]
|
|
|
|
args = self.request.arguments.copy()
|
|
|
|
for k in list(args.keys()):
|
|
v = args[k]
|
|
if type(v) is list:
|
|
if len(v) > 0:
|
|
args[k] = args[k][0]
|
|
else:
|
|
args[k] = ""
|
|
|
|
# Detect if they're using the above slash-separated parameters..
|
|
# If they are, then remove those parameters from the query string.
|
|
# If not, don't remove anything.
|
|
split = string.split('/')
|
|
if len(split) > 1:
|
|
for st in split:
|
|
b = st.split('_')
|
|
if len(b) == 2:
|
|
if b[0] in ['gravatar','ignoreext','size','set','bgset','color']:
|
|
args[b[0]] = b[1]
|
|
string = re.sub("/" + st,'',string)
|
|
|
|
# Ensure we have something to hash!
|
|
if string is None:
|
|
string = self.request.remote_ip
|
|
|
|
|
|
# Detect if the user has passed in a flag to ignore extensions.
|
|
# Pass this along to to Robohash obj later on.
|
|
|
|
ignoreext = args.get('ignoreext','false').lower() == 'true'
|
|
|
|
# Split the size variable in to sizex and sizey
|
|
if "size" in args:
|
|
sizex,sizey = args['size'].split("x")
|
|
sizex = int(sizex)
|
|
sizey = int(sizey)
|
|
if sizex > 4096 or sizex < 0:
|
|
sizex = 300
|
|
if sizey > 4096 or sizey < 0:
|
|
sizey = 300
|
|
|
|
# Allow Gravatar lookups -
|
|
# This allows people to pass in a gravatar-style hash, and return their gravatar image, instead of a Robohash.
|
|
# This is often used for example, to show a Gravatar if it's set for an email, or a Robohash if not.
|
|
if args.get('gravatar','').lower() == 'yes':
|
|
# They have requested that we hash the email, and send it to Gravatar.
|
|
default = "404"
|
|
gravatar_url = "https://secure.gravatar.com/avatar/" + hashlib.md5(string.lower()).hexdigest() + "?"
|
|
gravatar_url += urlencode({'default':default, 'size':str(sizey)})
|
|
elif args.get('gravatar','').lower() == 'hashed':
|
|
# They have sent us a pre-hashed email address.
|
|
default = "404"
|
|
gravatar_url = "https://secure.gravatar.com/avatar/" + string + "?"
|
|
gravatar_url += urlencode({'default':default, 'size':str(sizey)})
|
|
|
|
# If we do want a gravatar, request one. If we can't get it, just keep going, and return a robohash
|
|
if args.get('gravatar','').lower() in ['hashed','yes']:
|
|
try:
|
|
f = urlopen(gravatar_url)
|
|
self.redirect(gravatar_url, permanent=False)
|
|
return
|
|
except:
|
|
args['avatar'] = False
|
|
|
|
# Create our Robohashing object
|
|
r = Robohash(string)
|
|
|
|
|
|
# Allow users to manually specify a robot 'set' that they like.
|
|
# Ensure that this is one of the allowed choices, or allow all
|
|
# If they don't set one, take the first entry from sets above.
|
|
|
|
if args.get('set',r.sets[0]) in r.sets:
|
|
roboset = args.get('set',r.sets[0])
|
|
elif args.get('set',r.sets[0]) == 'any':
|
|
# Add ugly hack.
|
|
# Adding cats, per issue-17, but I don't want to change existing hashes.
|
|
# so we'll ignore that set for the 'any' config.
|
|
|
|
roboset = r.sets[r.hasharray[1] % (len(r.sets)-1) ]
|
|
else:
|
|
roboset = r.sets[0]
|
|
|
|
# If they specified multiple sets, use up a bit of randomness to choose one.
|
|
# If they didn't specify one, default to whatever we decided above.
|
|
|
|
possiblesets = []
|
|
for tmpset in args.get('sets',roboset).split(','):
|
|
if tmpset in r.sets:
|
|
possiblesets.append(tmpset)
|
|
if possiblesets:
|
|
roboset = possiblesets[r.hasharray[1] % len(possiblesets) ]
|
|
|
|
|
|
# Only set1 is setup to be color-seletable. The others don't have enough pieces in various colors.
|
|
# This could/should probably be expanded at some point..
|
|
# Right now, this feature is almost never used. ( It was < 44 requests this year, out of 78M reqs )
|
|
|
|
if args.get('color') in r.colors:
|
|
roboset = 'set1'
|
|
color = args.get('color')
|
|
|
|
# If they DID choose set1, randomly choose a color.
|
|
if roboset == 'set1' and color is None:
|
|
color = r.colors[r.hasharray[0] % len(r.colors) ]
|
|
roboset = 'set1'
|
|
|
|
# Allow them to set a background, or keep as None
|
|
if args.get('bgset') in r.bgsets + ['any']:
|
|
bgset = args.get('bgset')
|
|
|
|
# We're going to be returning the image directly, so tell the browser to expect a binary.
|
|
self.set_header("Content-Type", "image/" + format)
|
|
self.set_header("Cache-Control", "public,max-age=31536000")
|
|
|
|
# Build our Robot.
|
|
r.assemble(roboset=roboset,format=format,bgset=bgset,color=color,sizex=sizex,sizey=sizey)
|
|
|
|
# Print the Robot to the handler, as a file-like obj
|
|
if r.format != 'datauri':
|
|
r.img.save(self,format=r.format)
|
|
else:
|
|
# Or, if requested, base64 encode first.
|
|
fakefile = io.BytesIO()
|
|
r.img.save(fakefile,format='PNG')
|
|
fakefile.seek(0)
|
|
b64ver = base64.b64encode(fakefile.read())
|
|
b64ver = b64ver.decode('utf-8')
|
|
self.write("data:image/png;base64," + str(b64ver))
|
|
|
|
def main():
|
|
tornado.options.parse_command_line()
|
|
# timeout in seconds
|
|
timeout = 10
|
|
socket.setdefaulttimeout(timeout)
|
|
|
|
settings = {
|
|
"static_path": os.path.join(os.path.dirname(__file__), "static"),
|
|
"cookie_secret": "9b90a85cfe46cad5ec136ee44a3fa332",
|
|
"login_url": "/login",
|
|
"xsrf_cookies": True,
|
|
}
|
|
|
|
application = tornado.web.Application([
|
|
(r'/(crossdomain\.xml)', tornado.web.StaticFileHandler, {"path": os.path.join(os.path.dirname(__file__),
|
|
"static/")}),
|
|
(r"/static/(.*)", tornado.web.StaticFileHandler, {"path": os.path.join(os.path.dirname(__file__),
|
|
"static/")}),
|
|
(r"/", MainHandler),
|
|
(r"/(.*)", ImgHandler),
|
|
], **settings)
|
|
|
|
http_server = tornado.httpserver.HTTPServer(application,xheaders=True)
|
|
http_server.listen(options.port)
|
|
|
|
print("The Oven is warmed up - Time to make some Robots! Listening on port: " + str(options.port))
|
|
tornado.ioloop.IOLoop.instance().start()
|
|
if __name__ == "__main__":
|
|
main()
|