├── static ├── robots.txt ├── favicon.ico └── style.css ├── app.yaml ├── index.yaml ├── templates ├── main.html └── base.html └── main.py /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/progrium/clickhooks/master/static/favicon.ico -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: clickhooks 2 | version: 1 3 | runtime: python 4 | api_version: 1 5 | 6 | handlers: 7 | - url: / 8 | script: main.py 9 | - url: /favicon.ico 10 | static_files: static/favicon.ico 11 | upload: static/favicon.ico 12 | - url: /robots.txt 13 | static_files: static/robots.txt 14 | upload: static/robots.txt 15 | - url: /static 16 | static_dir: static 17 | - url: /.* 18 | script: main.py -------------------------------------------------------------------------------- /index.yaml: -------------------------------------------------------------------------------- 1 | indexes: 2 | 3 | # AUTOGENERATED 4 | 5 | # This index.yaml is automatically updated whenever the dev_appserver 6 | # detects that a new type of query is run. If you want to manage the 7 | # index.yaml file manually, remove the above marker line (the line 8 | # saying "# AUTOGENERATED"). If you want to manage some indexes 9 | # manually, move them above the marker line. The index.yaml file is 10 | # automatically uploaded to the admin console when you next deploy 11 | # your application using appcfg.py. 12 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | body { font-family: Helvetica,Arial,sans-serif; font-size: smaller;} 2 | #header h1 { display: block; margin: 0;} 3 | #header { border-bottom: 2px solid darkblue; padding: 5px; padding-bottom: 1px; background-color: lightblue;} 4 | #wrapper { margin-left: auto; margin-right: auto; width: 800px;} 5 | #content { margin: 10px; } 6 | pre { font-size: larger;} 7 | ul { padding-left: 20px;} 8 | #make-form { margin: 20px; text-align: center; margin-right: 80px;} 9 | #make-form input { font-size: larger;} 10 | #footer { 11 | margin-top: 20px; 12 | border-top: 1px solid darkblue; 13 | background-color: lightblue; 14 | padding: 5px; 15 | padding-left: 10px; 16 | } 17 | #footer a:visited { color: blue;} -------------------------------------------------------------------------------- /templates/main.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 | {% if user %} 4 |

These are your ClickHooks:

5 |

6 | {% for hook in hooks %} 7 |
{{ hook }}
8 |
⇒ {{ hook.hook_url }}
9 |
⌊ {{ hook.redirect_url }}
10 | {% endfor %} 11 |
12 |
13 | Redirect URL:
14 | Callback URL:
15 | 16 |
17 | 18 | 19 | {% else %} 20 |

ClickHooks is like a URL shortener service, except its purpose is to provide you with a callback via HTTP POST (aka webhooks) when people go to your link. This should really be a feature of something like tr.im, but apparently they don't know what's up.

21 |

Login

22 | {% endif %} 23 | {% endblock %} -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% block title %}ClickHooks - Click-through Callbacks{% endblock %} 4 | 5 | 6 | 7 |
8 | 11 |
12 |
13 | {% if user %} 14 | {{ user.nickname }} | Logout 15 | {% else %} 16 | Not logged in. Login? 17 | {% endif %} 18 |
19 |
20 | {% block content %}{% endblock %} 21 |
22 |
23 | 27 | 41 | 45 | 50 | 51 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | import wsgiref.handlers 5 | from google.appengine.ext import webapp 6 | from google.appengine.api import users 7 | from google.appengine.ext.webapp import template 8 | from google.appengine.ext import db 9 | from google.appengine.api import urlfetch 10 | import time, urllib 11 | 12 | def baseN(num,b,numerals="0123456789abcdefghijklmnopqrstuvwxyz"): 13 | return ((num == 0) and "0" ) or (baseN(num // b, b).lstrip("0") + numerals[num % b]) 14 | 15 | class MainHandler(webapp.RequestHandler): 16 | def get(self): 17 | user = users.get_current_user() 18 | if user: 19 | logout_url = users.create_logout_url("/") 20 | hooks = ClickHook.all().filter('user =', user) 21 | else: 22 | login_url = users.create_login_url('/') 23 | self.response.out.write(template.render('templates/main.html', locals())) 24 | 25 | def post(self): 26 | if self.request.POST.get('name', None): 27 | h = ClickHook.all().filter('name =', self.request.POST['name']).get() 28 | h.delete() 29 | else: 30 | h = ClickHook(hook_url=self.request.POST['hook_url'],redirect_url=self.request.POST['redirect_url']) 31 | h.put() 32 | self.redirect('/') 33 | 34 | class RedirectHandler(webapp.RequestHandler): 35 | def get(self): 36 | if self.request.path[-1] == '/': 37 | self.redirect(self.request.path[:-1]) 38 | name = self.request.path.replace('/', '') 39 | hook = ClickHook.all().filter('name =', name).get() 40 | params = {'url': hook.redirect_url, '_url': hook.hook_url} 41 | urlfetch.fetch(url='http://hookah.progrium.com/dispatch', payload=urllib.urlencode(params), method='POST') 42 | self.redirect(hook.redirect_url) 43 | 44 | class ClickHook(db.Model): 45 | user = db.UserProperty(auto_current_user_add=True) 46 | hook_url = db.StringProperty(required=True) 47 | redirect_url = db.StringProperty(required=True) 48 | name = db.StringProperty(required=True) 49 | created = db.DateTimeProperty(auto_now_add=True) 50 | updated = db.DateTimeProperty(auto_now=True) 51 | 52 | def __init__(self, *args, **kwargs): 53 | kwargs['name'] = kwargs.get('name', baseN(abs(hash(time.time())), 36)) 54 | super(ClickHook, self).__init__(*args, **kwargs) 55 | 56 | def __str__(self): 57 | return "http://www.clickhooks.com/%s" % self.name 58 | 59 | def main(): 60 | application = webapp.WSGIApplication([('/', MainHandler), ('/.*', RedirectHandler)], debug=True) 61 | wsgiref.handlers.CGIHandler().run(application) 62 | 63 | if __name__ == '__main__': 64 | main() 65 | --------------------------------------------------------------------------------