yegle做过一个同步twitter到人人网状态的PHP脚本,但要求有点高——必须有php-cli组件,估计大多数虚拟主机都没有。Shellex又做了一个用GAE实现的版本,这个可用性高一点,不过他的原版需要webpy和simplejson,需要上传很多东西,而且拼写有错。我改了一下,直接引用django里面的simplejson,再用webapp替换webpy,这样只需要一个文件就能实现了。
更新记录:
  纪念冯正虎成功回国特别更新:Retweet一并同步。
  2010-4-10:跟随校内网更新。
  2010-4-25:跟随校内网更新。
  2010-5-16:跟随校内网更新。
使用指南:
  请避免使用“记事本/notepad“编辑twitter2renren.py。
  允许同步以"@"开头的tweet:注释掉第46行,同时将第47行的缩进减小一格。
  取消同步retweet:注释掉第38, 39, 42, 43, 48, 49行。
脚本文件twitter2renren.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env python2.6
#coding=utf8
 
import Cookie
import urllib
import re
from django.utils import simplejson
from google.appengine.api import urlfetch
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
 
renren_usr = '你的人人网用户名'
renren_passwd = '人人网密码'
twitter_usr = '推特用户名'
twitter_passwd = '推特密码'
last_non_sync_tweet = '最后一条不需要同步的tweet的id,比如:8726641859'
last_non_sync_retweet = '8883341324'
 
cookie_buf = Cookie.SimpleCookie();
 
class Record(db.Model):
	#key is twitter_usr
	last_tweet = db.StringProperty(required=True)
	last_retweet = db.StringProperty(required=True)
 
def make_cookie_header(cookie):
	ret = ''
	for v in cookie.values():
		ret += '%s=%s;' % (v.key, v.value)
	return ret
 
def get_tweets (usr, passwd):
	record = Record.get_or_insert(twitter_usr, last_tweet=last_non_sync_tweet, last_retweet=last_non_sync_retweet)
	new_timeline = []
	timeline_uri = 'http://%s:%s@twitter.com/statuses/user_timeline.json?count=10&since_id=%s' % (usr, passwd, record.last_tweet)
	timeline = simplejson.loads(urllib.urlopen(timeline_uri).read())
	retweets_uri = 'http://%s:%s@api.twitter.com/1/statuses/retweeted_by_me.json?count=10&since_id=%s' % (usr, passwd, record.last_retweet)
	retweets = simplejson.loads(urllib.urlopen(retweets_uri).read())
	if len(timeline) != 0:
		record.last_tweet = str(timeline[0]['id'])
	if len(retweets) != 0:
		record.last_retweet = str(retweets[0]['id'])
	record.put()
	for tweet in timeline:
		if tweet['text'][0] != '@' :
			new_timeline.append(tweet['text'].encode('utf8'))
	for tweet in retweets:
		new_timeline.append(tweet['text'].encode('utf8'))
	return new_timeline
 
def login2renren():
	verify_url = 'http://passport.renren.com/PLogin.do'
	verify_data= urllib.urlencode({
		'email':  renren_usr,
		'password': renren_passwd,
		'origURL': 'http://home.renren.com/Home.do',
		'domain': 'renren.com',
		'formName': '',
		'method': '',
		'isplogin': 'true',
		'submit': '登录'
	})
	result = urlfetch.fetch(
		url=verify_url,
		headers={
			'Cookie':make_cookie_header(cookie_buf),
			'Content-Type': 'application/x-www-form-urlencoded',
			'user-agent':'Mozilla/5.0 (Linux; U; Linux i686; en-US) Gecko/20100115 Firefox/3.6',
		},
		method=urlfetch.POST,
		payload=verify_data,
		follow_redirects = False,
	)
	return result
 
def do_redirect(url, cookie):
	result = urlfetch.fetch(
		url=url,
		headers={
			'Cookie':make_cookie_header(cookie),
			'Content-Type': 'application/x-www-form-urlencoded',
			'user-agent':'Mozilla/5.0 (Linux; U; Linux i686; en-US) Gecko/20100115 Firefox/3.6',
		},
		method=urlfetch.GET,
		follow_redirects = False,
	)
	return result
 
def send_status(status, cookie):
	status_url = 'http://status.renren.com/'
	result = urlfetch.fetch(
		url=status_url,
		headers={
			'Cookie':make_cookie_header(cookie),
			'Content-Type': 'application/x-www-form-urlencoded',
			'user-agent':'Mozilla/5.0 (Linux; U; Linux i686; en-US) Gecko/20100115 Firefox/3.6',
		},
		method=urlfetch.GET,
	)
	cookie_buf = Cookie.SimpleCookie(result.headers.get('set-cookie', ''))
	ticket = re.search('<input type="hidden" value="(-?\d+)" class="submit" id="publisher_form_ticket"/>', result.content)
	status_url = 'http://status.renren.com/doing/update.do?'
	status_data = urllib.urlencode({
		'c': status,
		'raw': status,
		'statusPage': 1,
		'publisher_form_ticket': ticket.group(1),
	})
	result = urlfetch.fetch(
		url=status_url,
		headers={
			'Cookie':make_cookie_header(cookie),
			'Content-Type': 'application/x-www-form-urlencoded',
			'user-agent':'Mozilla/5.0 (Linux; U; Linux i686; en-US) Gecko/20100115 Firefox/3.6',
			'Referer': 'http://status.renren.com/'
		},
		method=urlfetch.POST,
		payload=status_data,
	)
	return True
 
class sync(webapp.RequestHandler):
	def get(self):
		global cookie_buf
		#get timeline
		timeline = get_tweets(twitter_usr, twitter_passwd)
		if len(timeline) == 0:
			return 'no tweets to sync.'
		#login to renren
		result = login2renren()
		cookie_buf = Cookie.SimpleCookie(result.headers.get('set-cookie', ''));
		callback_url = result.headers.get('location','xx');
		result = do_redirect(callback_url, cookie_buf)
		cookie_buf = Cookie.SimpleCookie(result.headers.get('set-cookie', ''))
		#post status
		for tweet in timeline:
			result = send_status(tweet, cookie_buf)
		return 'ok'
 
application = webapp.WSGIApplication([('/twitter2renren', sync)])
 
if __name__ == "__main__":
	run_wsgi_app(application)

配置文件cron.yaml,每5分钟触发一次:

?Download cron.yaml
1
2
3
4
cron:
- description: twitter2renren
  url: /twitter2renren
  schedule: every 5 minutes

配置文件app.yaml,限制只有管理员才能访问/twitter2renren:

?Download app.yaml
1
2
3
4
5
6
7
8
9
application: 你的app名字
version: 8
runtime: python
api_version: 1
 
handlers:
- url: /twitter2renren
  script: twitter2renren.py
  login: admin

  另:前天刚拿到一个SAE的帐号,不知道能干点什么用……有谁知道的,请悄悄告诉我……