4 # OSDNのサーバでrequests_oauthlibを使う方法のメモ
6 # 1. 環境変数PYTHONUSERBASEでインストール先をWebコンテンツ上の任意のディレクトリに指定し、pipに--userオプションをつけてインストールを実行
7 # 以下は /home/groups/h/he/hengband/htdocs/score/local 以下にインストールする例
9 # `$ PYTHONUSERBASE=/home/groups/h/he/hengband/htdocs/score/local pip install --user requests_oauthlib`
11 # 2. パスは通っているはずなのにシステムにインストールされているrequestsとurllib3が何故か読み込みに失敗するので、上でインストールしたディレクトリにコピーする
13 # `$ cp -a /usr/lib/python2.7/dist-packages/requests /usr/lib/python2.7/dist-packages/urllib3 /home/groups/h/he/hengband/htdocs/score/local/lib/python2.7/site-packages`
15 # 3. sys.path.appendで上でインストールしたディレクトリにパスを通してからrequests_oauthlibをimportする
18 # `sys.path.append('/home/groups/h/he/hengband/htdocs/score/local/lib/python2.7/site-packages')`
19 # `import requests_oauthlib`
27 def get_config(config_file):
28 ini = ConfigParser.ConfigParser()
31 config = {s: {i[0]: i[1] for i in ini.items(s)}
32 for s in ini.sections()}
37 def get_score_data(score_db_path, score_id):
39 cond = 'ORDER BY score_id DESC LIMIT 1'
41 cond = 'WHERE score_id = :score_id'
43 with sqlite3.connect(score_db_path) as con:
44 con.row_factory = sqlite3.Row
49 WHEN realm_id IS NOT NULL THEN '(' || group_concat(realm_name) || ')'
53 WHEN killer = 'ripe' THEN '勝利の後引退'
54 WHEN killer = 'Seppuku' THEN '勝利の後切腹'
55 ELSE killer || 'に殺された'
70 c = con.execute(sql, {'score_id': score_id})
73 return score[0] if len(score) == 1 else None
76 def create_tweet(score_data):
77 summary = (u"【新着スコア】{personality_name}{name} Score:{score} "
78 u"{race_name} {class_name}{realms_name} {death_reason} {depth}階"
79 ).format(**score_data)
81 dump_url = ("http://hengband.osdn.jp/score/show_dump.php?score_id={}"
82 ).format(score_data['score_id'])
83 screen_url = ("http://hengband.osdn.jp/score/show_screen.php?score_id={}"
84 ).format(score_data['score_id'])
86 tweet = (u"{summary}\n\n"
88 u"screen: {screen_url}\n"
90 ).format(summary=summary,
92 screen_url=screen_url)
96 def tweet(oauth, tweet_contents):
97 from requests_oauthlib import OAuth1Session
98 from requests.adapters import HTTPAdapter
99 from requests import codes
100 from logging import getLogger
101 logger = getLogger(__name__)
103 twitter = OAuth1Session(**oauth)
105 url = "https://api.twitter.com/1.1/statuses/update.json"
107 params = {"status": tweet_contents}
108 twitter.mount("https://", HTTPAdapter(max_retries=5))
110 logger.info("Posting to Twitter...")
111 logger.info(u"Tweet contents:\n{}".format(tweet_contents))
112 res = twitter.post(url, params=params)
114 if res.status_code == codes.ok:
115 logger.info("Success.")
117 logger.warning("Failed to post: {code}, {json}"
118 .format(code=res.status_code, json=res.json()))
122 from optparse import OptionParser
123 parser = OptionParser()
124 parser.add_option("-s", "--score-id",
125 type="int", dest="score_id",
126 help="Target score id.\n"
127 "If this option is not set, latest score is used.")
128 parser.add_option("-c", "--config",
129 type="string", dest="config_file",
130 default="tweet_score.cfg",
131 help="Configuration INI file [default: %default]")
132 parser.add_option("-l", "--log-file",
133 type="string", dest="log_file",
134 help="Logging file name")
135 parser.add_option("-n", "--dry-run",
136 action="store_true", dest="dry_run",
138 help="Output to stdout instead of posting to Twitter.")
139 return parser.parse_args()
142 def setup_logger(log_file):
143 from logging import getLogger, StreamHandler, FileHandler, Formatter, INFO
144 logger = getLogger(__name__)
145 logger.setLevel(INFO)
147 logger.addHandler(sh)
149 formatter = Formatter('[%(asctime)s] %(message)s')
150 fh = FileHandler(log_file)
151 fh.setFormatter(formatter)
152 logger.addHandler(fh)
155 if __name__ == '__main__':
156 (options, arg) = parse_option()
157 setup_logger(options.log_file)
158 from logging import getLogger
159 logger = getLogger(__name__)
162 config = get_config(options.config_file)
163 if 'Python' in config:
164 sys.path.append(config['Python']['local_lib_path'])
165 score_data = get_score_data(config['ScoreDB']['path'],
167 if score_data is None:
168 logger.warning('No score data found.')
171 tweet_contents = create_tweet(score_data)
172 if (options.dry_run):
173 print(tweet_contents)
175 tweet(config['TwitterOAuth'], tweet_contents)
177 from traceback import format_exc
178 logger.critical(format_exc())