Skip to content

Instantly share code, notes, and snippets.

@sbp
Created August 4, 2012 17:37
Show Gist options
  • Save sbp/3258877 to your computer and use it in GitHub Desktop.
Save sbp/3258877 to your computer and use it in GitHub Desktop.
WhitsBot
#!/usr/bin/env python
import sys, os.path, re, json, time, shutil, cgi
from htmlentitydefs import name2codepoint
sys.path.append(
os.path.expanduser("~/usr/opt/python-packages")
)
from twitter import Twitter, OAuth
now = time.gmtime()
year = time.strftime("%Y", now)
whits = os.path.expanduser("~/inamidst.com/whits")
config = os.path.expanduser("~/.whitsbot")
def get_auth_data():
with open(config) as f:
config_data = json.load(f)
return (
config_data["access_token"],
config_data["access_secret"],
config_data["consumer_key"],
config_data["consumer_secret"]
)
def error(msg):
print >> sys.stderr, msg
sys.exit(1)
def get_entry_data():
data = {}
if not len(sys.argv) == 2:
error("Expected one filename argument")
data["filename"] = sys.argv[1]
data["name"] = os.path.basename(data["filename"]).split('.', 1)[0]
if not os.path.exists(data["filename"]):
error("No such file: %s" % data["filename"])
if not os.path.exists(config):
error("Can't find a ~/.whitsbot for Twitter authentication data")
with open(data["filename"]) as f:
bytes = f.read()
r_title = re.compile(r"<title>(.*?)</title>")
r_description = re.compile(r'content="(.*?)"')
m = r_title.search(bytes)
if m: data["title"] = m.group(1)
else: error("No title found in %s" % data["filename"])
m = r_description.search(bytes)
if m: data["description"] = m.group(1)
else: error("No description found in %s" % data["filename"])
if len(data["description"]) > 119:
error("Description is longer than maximum length of 119 characters")
if not "\n\n" in bytes:
error("No content found in %s" % data["filename"])
data["content"] = bytes.split('\n\n', 1).pop()
return data
r_entity = re.compile(r'&([^;\s]+);')
def entity(match):
value = match.group(1).lower()
if value.startswith('#x'):
return unichr(int(value[2:], 16))
elif value.startswith('#'):
return unichr(int(value[1:]))
elif name2codepoint.has_key(value):
return unichr(name2codepoint[value])
return '[' + value + ']'
def decode(html):
return r_entity.sub(entity, html)
def publish_to_whits(entry):
destination = os.path.join(whits, year, entry["name"] + ".html")
shutil.move(entry["filename"], destination)
print "Published to", destination
def publish_to_feed(entry):
from StringIO import StringIO
s = StringIO()
print >> s, '<feed xmlns="http://www.w3.org/2005/Atom">'
print >> s, ' <title>Gallimaufry of Whits</title>'
print >> s, ' <link href="http://inamidst.com/whits/"/>'
print >> s, ' <id>tag:sbp.so,2011:inamidst.com/whits</id>'
print >> s, ' <author><name>Sean B. Palmer</name></author>'
updated = time.strftime("%Y-%m-%dT%H:%M:%SZ", now)
print >> s, ' <updated>%s</updated>' % updated
path = "/whits/%s/%s" % (year, entry["name"])
print >> s, ' <entry xml:base="http://inamidst.com%s">' % path
print >> s, ' <id>tag:sbp.so,2011:inamidst.com%s</id>' % path
print >> s, ' <title>%s</title>' % entry["title"].replace('<', '&lt;')
print >> s, ' <updated>%s</updated>' % updated
print >> s, ' <link href="http://inamidst.com%s"/>' % path
print >> s, ' <content type="html">%s</content>' % cgi.escape(entry["content"])
print >> s, ' </entry>'
r_entry = re.compile(r"(?ims)(<entry.*?</entry>)")
feed = os.path.join(whits, "feed.atom")
with open(feed) as f:
entries = r_entry.findall(f.read())
for entry in entries[:9]:
print >> s, ' ' + entry
print >> s, '</feed>'
s.seek(0)
with open(feed, "w") as w:
w.write(s.read())
print "Updated", feed
def publish_to_twitter(entry):
description = decode(entry["description"])
link = "http://inamidst.com/whits/%s/%s" % (year, entry["name"])
status = description + " " + link
authentication = get_auth_data()
t = Twitter(auth=OAuth(*authentication))
t.statuses.update(status=status)
print "Posted to @OfWhits:", status
def publish_entry():
entry = get_entry_data()
publish_to_whits(entry)
publish_to_feed(entry)
publish_to_twitter(entry)
if __name__ == '__main__':
publish_entry()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment