Skip to content
Browse files

tweet to @downfor when domains are down

  • Loading branch information...
1 parent d376a60 commit 2876ec4e8dc8bf7d209a0104c83e6bde696544f9 @al3x committed
Showing with 128 additions and 33 deletions.
  1. +3 −0 app.yaml
  2. +4 −0 cron.yml
  3. +5 −0 downer.py
  4. +22 −17 main.py
  5. +1 −15 templates/ads.html
  6. +10 −1 templates/layout.html
  7. +83 −0 tweetcheck.py
View
3 app.yaml
@@ -20,5 +20,8 @@ handlers:
- url: /test.*
script: gaeunit.py
+- url: /_tweetcheck
+ script: tweetcheck.py
+
- url: .*
script: main.py
View
4 cron.yml
@@ -0,0 +1,4 @@
+cron:
+- description: tweet on new top down domains
+ url: /_tweetcheck
+ schedule: every 5 minutes
View
5 downer.py
@@ -0,0 +1,5 @@
+from google.appengine.ext import db
+
+class Downer(db.Model):
+ domain = db.StringProperty(required=True)
+ down_at = db.DateTimeProperty(auto_now_add=True)
View
39 main.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python
-import cgi, logging, random, re, wsgiref.handlers
+import cgi, logging, re, wsgiref.handlers
from betterhandler import *
+from downer import *
+
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch
@@ -16,16 +18,16 @@ def valid_response_code(code):
return True
else:
return False
-
+
class Url:
def __init__(self, domain):
if domain.find("http%3A//") is not -1:
domain = domain.split("http%3A//")[1]
-
+
self.original_domain = domain
self.domain = self.clean_url(domain)
logging.debug("new Url: <original_domain: %s> <domain: %s>", self.original_domain, self.domain)
-
+
def clean_url(self, domain):
domain = cgi.escape(domain)
domain.encode("utf-8")
@@ -34,28 +36,28 @@ def clean_url(self, domain):
domain = 'http://' + domain
return domain
-
+
def dos(self):
doscheck = memcache.get(self.domain)
-
+
if doscheck is not None:
doscheck = memcache.incr(self.domain)
else:
doscheck = memcache.add(self.domain, 0, 60)
-
+
if not doscheck:
logging.error("Memcache set failed.")
-
+
doscheck = 0
-
+
if doscheck > 500:
return True
else:
return False
-
+
def isself(self):
logging.debug("in isself domain is %s", self.domain)
-
+
if DOWNRE.search(self.domain) == None:
return False
else:
@@ -76,15 +78,18 @@ def render_error(self, url, error='unknown'):
}
logging.error("Error on domain '%s': %s", url.domain, error)
return self.response.out.write(template.render(self.template_path('error.html'), for_template))
-
+
def render_down(self, url):
+ downer = Downer(domain=url.domain)
+ db.put(downer)
+
for_template = {
'title': "It's not just you!",
'domain': url.domain,
'original_domain': url.original_domain,
}
return self.response.out.write(template.render(self.template_path('down.html'), for_template))
-
+
def render_up(self, url):
for_template = {
'title': "It's just you.",
@@ -92,21 +97,21 @@ def render_up(self, url):
'original_domain': url.original_domain,
}
return self.response.out.write(template.render(self.template_path('up.html'), for_template))
-
+
def render_hurr(self):
for_template = {
'title': "It's just you.",
}
return self.response.out.write(template.render(self.template_path('hurr.html'), for_template))
-
+
def get(self, domain):
u = Url(domain)
-
+
if u.isself():
self.render_hurr()
elif u.dos():
self.render_error(u, "potential DoS")
- else:
+ else:
try:
response = urlfetch.fetch(u.domain, method=urlfetch.HEAD)
except urlfetch.Error:
View
16 templates/ads.html
@@ -1,18 +1,4 @@
<br />
<center>
- <div style="width:500px;height:200px;overflow:hidden;">
- <script type="text/javascript">
- _JS_AFF_ID = 21;
- _JS_JOB_COUNT = 4;
- _JS_FONT_SIZE = 10;
- _JS_FONT = "Arial";
- _JS_HEADER_COLOR = "#ff8000";
- _JS_TEXT_COLOR = "#000000";
- _JS_COMPANY_COLOR = "#0080ff";
- _JS_AD_ORIENTATION = "2";
- _JS_JOB_LIST = new Array();
- _JS_LINK_STYLE = 0;
- </script>
- <script type="text/javascript" src="http://jobsyndicate.com/JServ/Generic.js"></script>
- </div>
+<a href="http://twitter.com/downfor" class="adlink">Follow @downfor on Twitter!</a>
</center>
View
11 templates/layout.html
@@ -11,6 +11,7 @@
a:hover{text-decoration:none;}
input[type=text]{border:1px solid #ccc;color:#ccc;font-size:1em;padding:4px 6px 4px 6px;}
.domain{font-weight:bold;}
+ a.adlink{color: orange;}
#container{clear:both;font-size:3em;margin:auto;}
#domain_input{width:250px;}
</style>
@@ -22,7 +23,7 @@
e.style.color = '#000';
}
function formSubmit() {
- domain = document.getElementById('domain_input').value;
+ domain = document.getElementById('domain_input').value;
window.location = '/' + domain;
return false;
}
@@ -38,6 +39,14 @@
<a href="http://www.quantcast.com/p-7a3KxvGn-6BNw" target="_blank"><img src="http://pixel.quantserve.com/pixel/p-7a3KxvGn-6BNw.gif" style="display: none;" border="0" height="1" width="1" alt="Quantcast"/></a>
</noscript>
<script type="text/javascript">
+ var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+ document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+ </script>
+ <script type="text/javascript">
+ var pageTracker = _gat._getTracker("UA-853305-3");
+ pageTracker._trackPageview();
+ </script>
+ <script type="text/javascript">
var cbjspath = "static.chartbeat.com/js/chartbeat.js?uid=714&domain=downforeveryoneorjustme.com";
var cbjsprotocol = (("https:" == document.location.protocol) ? "https://s3.amazonaws.com/" : "http://");
document.write(unescape("%3Cscript src='"+cbjsprotocol+cbjspath+"' type='text/javascript'%3E%3C/script%3E"))
View
83 tweetcheck.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+
+import base64, cgi, logging, urllib, wsgiref.handlers
+from datetime import *
+from itertools import *
+
+from downer import *
+
+from google.appengine.ext import db
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+from google.appengine.api import memcache
+from google.appengine.api import urlfetch
+
+class DownError(Exception):
+ pass
+
+def get_top_domain():
+ five_minutes_ago = datetime.now() + timedelta(minutes=-5)
+ query = db.GqlQuery("SELECT * FROM Downer WHERE down_at >= :1", five_minutes_ago)
+ results = query.fetch(1000)
+
+ domains = []
+
+ for result in results:
+ domains.append(result.domain)
+
+ grouped_domains = dict([(a, len(list(b))) for a, b in groupby(sorted(domains))])
+ sorted_domains = sorted(grouped_domains.items(), reverse=True)
+
+ if len(sorted_domains) > 0:
+ return sorted_domains[0][0]
+ else:
+ raise DownError, "No domains recorded down"
+
+
+def tweet(msg):
+ url = "https://twitter.com/statuses/update.json"
+ username = "downfor"
+ password = "foobar23"
+
+ form_fields = { "status": msg }
+ payload = urllib.urlencode(form_fields)
+
+ authheader = "Basic %s" % base64.encodestring('%s:%s' % (username, password))
+ base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
+ authheader = "Basic %s" % base64string
+
+ result = urlfetch.fetch(url=url, payload=payload, method=urlfetch.POST,
+ headers={"Authorization": authheader})
+
+ if int(result.status_code) == 200:
+ return 200
+ else:
+ raise DownError, result.status_code
+
+
+class TweetCheck(webapp.RequestHandler):
+ def get(self):
+ try:
+ current_top_domain = get_top_domain()
+ except DownError, e:
+ return self.response.out.write("Exception: %s" % e)
+
+ last_top_domain = memcache.get("topdomain")
+
+ if current_top_domain != last_top_domain:
+ memcache.set("topdomain", current_top_domain)
+
+ try:
+ tweet("%s looks like it might be down." % current_top_domain)
+ except DownError, e:
+ return self.response.out.write("Exception: couldn't tweet, got a %s" % e)
+
+ return self.response.out.write("New top domain: %s" % current_top_domain)
+ else:
+ return self.response.out.write("Same old top domain: %s" % last_top_domain)
+
+
+def main():
+ application = webapp.WSGIApplication([('/_tweetcheck', TweetCheck)],
+ debug=True)
+ wsgiref.handlers.CGIHandler().run(application)

0 comments on commit 2876ec4

Please sign in to comment.
Something went wrong with that request. Please try again.