| 1 | #!/opt/local/bin/python2.5 |
|---|
| 2 | # -*- coding: utf-8 -*- |
|---|
| 3 | # http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%B0%A5%E9%A5%D5api |
|---|
| 4 | # http://rubyforge.org/projects/hatenaapigraph/ |
|---|
| 5 | # |
|---|
| 6 | |
|---|
| 7 | import base64 |
|---|
| 8 | import random |
|---|
| 9 | import sha |
|---|
| 10 | import urllib2 |
|---|
| 11 | import urllib |
|---|
| 12 | from datetime import date,datetime |
|---|
| 13 | |
|---|
| 14 | import yaml |
|---|
| 15 | |
|---|
| 16 | def __http_response(self, request, response): |
|---|
| 17 | code, msg, hdrs = response.code, response.msg, response.info() |
|---|
| 18 | |
|---|
| 19 | if not (200 <= code < 300): |
|---|
| 20 | response = self.parent.error( |
|---|
| 21 | 'http', request, response, code, msg, hdrs) |
|---|
| 22 | |
|---|
| 23 | return response |
|---|
| 24 | |
|---|
| 25 | urllib2.HTTPErrorProcessor.http_response = __http_response |
|---|
| 26 | |
|---|
| 27 | class WSSEHeader(urllib2.BaseHandler) : |
|---|
| 28 | |
|---|
| 29 | def __init__(self, userid, passwd): |
|---|
| 30 | self.userid = userid |
|---|
| 31 | self.passwd = passwd |
|---|
| 32 | |
|---|
| 33 | def get_nonce(self): |
|---|
| 34 | private = str(random.random()) |
|---|
| 35 | now = datetime.now() |
|---|
| 36 | timestamp = now.strftime('%Y-%m-%dT%H:%M:%SZ') |
|---|
| 37 | return '%s %s' % (timestamp, sha.new('%s:%s' % (timestamp, private)).hexdigest()) |
|---|
| 38 | |
|---|
| 39 | def get_wsse(self): |
|---|
| 40 | nonce = self.get_nonce() |
|---|
| 41 | base64_encoded_nonce = base64.encodestring(nonce).replace('\n', '') |
|---|
| 42 | now = datetime.now() |
|---|
| 43 | post_creation_time = now.strftime('%Y-%m-%dT%H:%M:%SZ') |
|---|
| 44 | password_digest = base64.encodestring(sha.new(nonce + post_creation_time + self.passwd).digest()).replace('\n', '') |
|---|
| 45 | return 'UsernameToken Username="%s", PasswordDigest="%s", Created="%s", Nonce="%s"' \ |
|---|
| 46 | % (self.userid, password_digest, post_creation_time, base64_encoded_nonce) |
|---|
| 47 | |
|---|
| 48 | def http_request(self,req): |
|---|
| 49 | req.add_header('X-WSSE', self.get_wsse()) |
|---|
| 50 | return req |
|---|
| 51 | |
|---|
| 52 | class HatenaGraph: |
|---|
| 53 | |
|---|
| 54 | GRAPH_API_URL = 'http://graph.hatena.ne.jp/api/' |
|---|
| 55 | GRAPH_API_DATA_URI = GRAPH_API_URL + 'data' |
|---|
| 56 | GRAPH_API_CONFIG_URI = GRAPH_API_URL + 'config' |
|---|
| 57 | |
|---|
| 58 | def __init__(self, userid, passwd): |
|---|
| 59 | self.userid = userid |
|---|
| 60 | self.passwd = passwd |
|---|
| 61 | |
|---|
| 62 | |
|---|
| 63 | def get_data(self, graphname, userid=None): |
|---|
| 64 | if userid is None: |
|---|
| 65 | userid = self.userid |
|---|
| 66 | params = { |
|---|
| 67 | 'graphname' : graphname, |
|---|
| 68 | 'type' : 'yaml', |
|---|
| 69 | 'username' : userid |
|---|
| 70 | } |
|---|
| 71 | opener = urllib2.build_opener( WSSEHeader(self.userid, self.passwd)) |
|---|
| 72 | params = urllib.urlencode(params) |
|---|
| 73 | data = opener.open(self.GRAPH_API_DATA_URI+'?%s' % params) |
|---|
| 74 | return yaml.load(data) |
|---|
| 75 | |
|---|
| 76 | def post_data(self, graphname, value, updatedate=date.today()): |
|---|
| 77 | params = { |
|---|
| 78 | 'graphname' : graphname, |
|---|
| 79 | 'value' : value, |
|---|
| 80 | 'date' : updatedate.strftime('%Y-%m-%d') |
|---|
| 81 | } |
|---|
| 82 | params = urllib.urlencode(params) |
|---|
| 83 | opener = urllib2.build_opener( WSSEHeader(self.userid, self.passwd)) |
|---|
| 84 | return opener.open(self.GRAPH_API_DATA_URI, params) |
|---|
| 85 | |
|---|
| 86 | def get_config(self, graphname, userid=None): |
|---|
| 87 | params = { |
|---|
| 88 | 'graphname' : graphname, |
|---|
| 89 | 'type' : 'yaml', |
|---|
| 90 | } |
|---|
| 91 | opener = urllib2.build_opener( WSSEHeader(self.userid, self.passwd)) |
|---|
| 92 | params = urllib.urlencode(params) |
|---|
| 93 | data = opener.open(self.GRAPH_API_CONFIG_URI+'?%s' % params) |
|---|
| 94 | return yaml.load(data) |
|---|
| 95 | |
|---|
| 96 | def post_config(self, graphname, params): |
|---|
| 97 | params['graphname'] = graphname |
|---|
| 98 | params = urllib.urlencode(params) |
|---|
| 99 | opener = urllib2.build_opener( WSSEHeader(self.userid, self.passwd)) |
|---|
| 100 | return opener.open(self.GRAPH_API_CONFIG_URI, params) |
|---|