def _forward(self, mID, text): ''' Raw forward method * Only support Renren message * Use 'text' as exact comment sequence ''' try: api_params = { 'method': 'status.forward', 'status': text, 'forward_owner': mID.source_user_id, 'place_id': 'RRAF04D95FA37892FFA88', 'forward_id': mID.status_id } ret = self.renren_request(api_params) if 'id' in ret: # ret['id'] is the ID of new status # X, their doc says the field name is 'result'... return True else: logger.warning( "'%s' forward status '%s' with comment '%s' fail. ret: %s", self.jsonconf.channel_name, mID, text, ret) return False except Exception as e: logger.warning( "'%s' forward status '%s' with comment '%s' fail: %s", self.jsonconf.channel_name, mID, text, e) return False
def reply(self, statusID, text): ''' docstring placeholder ''' #"""reply status #@param status: StatusID object #@param text: string, the reply message #@return: success or not #""" api_params = dict(method = "share.addComment", content = text, \ share_id = statusID.status_id, user_id = statusID.source_user_id) try: ret = self.renren_request(api_params) logger.debug("Reply to status '%s' return: %s", statusID, ret) if 'result' in ret and ret['result'] == 1: logger.info("Reply '%s' to status '%s' succeed", text, statusID) return True else: return False except Exception, e: logger.warning("Reply failed: %s", e)
def home_timeline(self, count=20): '''Get home timeline get statuses of yours and your friends' @param count: number of statuses ''' api_params = dict(method="feed.get", type=10, page=1, count=count) try: jsonlist = self.renren_request(api_params) except Exception as e: logger.warning("catch expection: %s", e) jsonlist = [] statuslist = snstype.MessageList() for j in jsonlist: try: statuslist.append(self.Message(j,\ platform = self.jsonconf['platform'],\ channel = self.jsonconf['channel_name']\ )) except Exception as e: logger.warning("catch expection '%s' in parsing '%s'", e, j) logger.info("Read %d statuses from '%s'", len(statuslist), self.jsonconf.channel_name) return statuslist
def forward(self, message, text): res = None try: if message.ID.feed_type == 'STATUS': res = self.renren_request( method='status.forward', status=text, forward_id=message.ID.status_id, forward_owner=message.ID.source_user_id, ) elif message.ID.feed_type != 'OTHER': res = self.renren_request(method='share.share', type=str({ 'BLOG': 1, 'PHOTO': 2, 'SHARE': 20 }[message.parsed.feed_type]), ugc_id=message.ID.status_id, user_id=message.ID.source_user_id, comment=text) else: return BooleanWrappedData(False, { 'errors': ['SNSAPI_NOT_SUPPORTED'], }) except Exception as e: logger.warning('Catch exception: %s', e) return BooleanWrappedData(False, { 'errors': ['PLATFORM_'], }) if res: return BooleanWrappedData(True) else: return BooleanWrappedData(False, { 'errors': ['PLATFORM_'], })
def reply(self, statusID, text): ''' docstring placeholder ''' """reply status * parameter status: StatusID object * paramter text: string, the reply message * return: success or not """ api_params = dict(method = "share.addComment", content = text, \ share_id = statusID.status_id, user_id = statusID.source_user_id) try: ret = self.renren_request(api_params) logger.debug("Reply to status '%s' return: %s", statusID, ret) if 'result' in ret and ret['result'] == 1: logger.info("Reply '%s' to status '%s' succeed", text, statusID) return True else: return False except Exception, e: logger.warning("Reply failed: %s", e)
def home_timeline(self, count=20): '''Get home timeline * function : get statuses of yours and your friends' * parameter count: number of statuses ''' url = "https://api.weibo.com/2/statuses/home_timeline.json" params = {} params['count'] = count params['access_token'] = self.token.access_token jsonobj = self._http_get(url, params) statuslist = snstype.MessageList() try: if("error" in jsonobj): logger.warning("error json object returned: %s", jsonobj) return [] for j in jsonobj['statuses']: statuslist.append(self.Message(j,\ platform = self.jsonconf['platform'],\ channel = self.jsonconf['channel_name']\ )) except Exception, e: logger.warning("Catch exception: %s", e)
def unlike(self, message): ''' Unlike method * Weibo doesn't provide an API for "unlike" * So "unfavourite" function supersedes "unlike" * Here "unlike" means "remove from my favourites" * Receive a message ''' mID = message.ID try: ret = self.weibo_request('favorites/destroy', 'POST', {'id': mID.id}) # error_code 20705 means this status had never been collected. # For the purpose of backward compatibility, we also view # it as a successful unlike if 'favorited_time' in ret or ret["error_code"] == 20705: return True else: logger.warning("'%s' unlikes status '%s' fail. ret: %s", self.jsonconf.channel_name, mID, ret) return False except Exception, e: logger.warning("'%s' unlike status '%s' fail. ret: %s", self.jsonconf.channel_name, mID, ret) return False
def reply(self, statusID, text): try: self.client.miniblog.comment.new(statusID.id, text) return True except Exception, e: logger.warning('DoubanAPIError: %s', str(e)) return False
def unlike(self, message): try: self.client.miniblog.unlike(message.ID.id) return True except Exception, e: logger.warning("DoubanAPIError, %s", e) return False
def forward(self, message, text): try: self.client.miniblog.reshare(message.ID.id) return True except Exception, e: logger.warning("DoubanAPIError: %s", e) return False
def update(self, text): try: self.client.miniblog.new(text) return True except Exception, e: logger.warning("DoubanAPIError: %s", e) return False
def unlike(self, message): try: self.client.miniblog.unlike(message.ID.id) return True except Exception, e: logger.warning("DoubanAPIError, %s", e) return False
def reply(self, statusID, text): try: self.client.miniblog.comment.new(statusID.id, text) return True except Exception, e: logger.warning('DoubanAPIError: %s', str(e)) return False
def update(self, text): try: self.client.miniblog.new(text) return True except Exception, e: logger.warning("DoubanAPIError: %s", e) return False
def update(self, text, title=None): ''' Post a blog :param text: Blog post body. :param title: Blog post title. (optional) :return: success or not ''' if title is None: title = self._cat(20, [(text, 1)]) api_params = { 'method': 'blog.addBlog', 'content': text, 'title': title } try: ret = self.renren_request(api_params) logger.debug("response: %s", ret) #TODO: # Preserve the id for further use? # Return it as multi-return-value? if 'id' in ret: logger.info("Update status '%s' on '%s' succeed", text, self.jsonconf.channel_name) return True except Exception, e: logger.warning("Catch Exception %s", e)
def forward(self, message, text): try: self.client.miniblog.reshare(message.ID.id) return True except Exception, e: logger.warning("DoubanAPIError: %s", e) return False
def reply(self, mID, text): ''' Reply a renren blog :param mID: MessageID object :param text: string, the reply message :return: success or not ''' if mID.user_type == 'user': owner_key = 'uid' owner_value = mID.source_user_id else: # 'page' owner_key = 'page_id' owner_value = mID.source_page_id api_params = { 'method': 'blog.addComment', 'content': text, 'id': mID.blog_id, owner_key: owner_value } logger.debug('request parameters: %s', api_params) try: ret = self.renren_request(api_params) if 'result' in ret and ret['result'] == 1: logger.info("Reply '%s' to status '%s' succeed", text, mID) return True except Exception, e: logger.warning("Catch Exception %s", e)
def update(self, text, pic=None): '''update a status * parameter text: the update message * return: success or not ''' text = self._cat(self.jsonconf['text_length_limit'], [(text, 1)]) if not pic: method = "t/add" else: method = "t/add_pic" try: if pic: ret = self.tencent_request(method, "POST", content=text, files={'pic': ('pic.jpg', pic)}) else: ret = self.tencent_request(method, "POST", content=text) if(ret['msg'] == "ok"): logger.info("Update status '%s' on '%s' succeed", text, self.jsonconf.channel_name) return True else: return ret except Exception, e: logger.warning("Catch Exception: %s", e) return False
def write_timeline_to_db(self, msglist): logger.debug("acquiring lock") self.dblock.acquire() try: conn = sqlite3.connect(self.sqlitefile) cursor = conn.cursor() what_to_write = [] for msg in msglist: try: pickled_msg = obj2str(msg) sig = unicode(msg.digest()) cursor.execute( "SELECT * FROM home_timeline WHERE digest = ?", (sig, )) if not cursor.fetchone(): what_to_write.append( (unicode(pickled_msg), sig, msg.parsed.text, msg.parsed.username, msg.parsed.userid, msg.parsed.time)) except Exception, e: logger.warning("Error while checking message: %s" % (str(e))) try: logger.info("Writing %d messages" % (len(what_to_write))) cursor.executemany( "INSERT INTO home_timeline (pickled_object, digest, text, username, userid, time) VALUES(?, ?, ?, ?, ?, ?)", what_to_write) except Exception, e: logger.warning("Error %s" % (str(e)))
def reply(self, statusId, text): res = None flag = False try: # The order in the bracket is important since there # exists "SHARE_XXXX" type. In order to figure out # the actual type, SHARE must be put in the first position. for msg_type in [ "SHARE", "BLOG", "PHOTO", "ALBUM", "STATUS", "VIDEO" ]: if msg_type in statusId.feed_type: flag = True break if flag: res = self.renren_request(method="comment/put", content=text, commentType=msg_type, entryOwnerId=statusId.source_user_id, entryId=statusId.resource_id) else: return BooleanWrappedData(False, { 'errors': ['SNSAPI_NOT_SUPPORTED'], }) except Exception, e: logger.warning('Catch exception: %s', e) return BooleanWrappedData(False, { 'errors': ['PLATFORM_'], })
def _forward(self, mID, text): """ Raw forward method * Only support Sina message * Use 'text' as exact comment sequence """ try: ret = self.weibo_request("statuses/repost", "POST", {"id": mID.id, "status": text}) if "id" in ret: return True else: logger.warning( "'%s' forward status '%s' with comment '%s' fail. ret: %s", self.jsonconf.channel_name, mID, text, ret, ) return False except Exception as e: logger.warning( "'%s' forward status '%s' with comment '%s' fail: %s", self.jsonconf.channel_name, mID, text, e ) return False
def unlike(self, message): res = None flag = False try: # The order in the bracket is important since there # exists "SHARE_XXXX" type. In order to figure out # the actual type, SHARE must be put in the first position. for msg_type in [ "SHARE", "BLOG", "PHOTO", "ALBUM", "STATUS", "VIDEO" ]: if msg_type in message.ID.feed_type: flag = True break if flag: res = self.renren_request(method="like/ugc/remove", ugcOwnerId=message.ID.source_user_id, likeUGCType="TYPE_" + msg_type, ugcId=message.ID.resource_id) else: return False except Exception as e: logger.warning('Catch exception: %s', type(e)) return False if res: return True else: return False
def _parse(self, dct): if "deleted" in dct and dct["deleted"]: logger.debug("This is a deleted message %s of SinaWeiboStatusMessage", dct["id"]) self.parsed.time = "unknown" self.parsed.username = "******" self.parsed.userid = "unknown" self.parsed.text = "unknown" self.deleted = True return self.ID.id = dct["id"] self.parsed.time = utils.str2utc(dct["created_at"]) self.parsed.username = dct["user"]["name"] self.parsed.userid = dct["user"]["id"] self.parsed.reposts_count = dct["reposts_count"] self.parsed.comments_count = dct["comments_count"] if "retweeted_status" in dct: self.parsed.username_orig = "unknown" try: self.parsed.username_orig = dct["retweeted_status"]["user"]["name"] except KeyError: logger.warning("KeyError when parsing SinaWeiboStatus. May be deleted original message") self.parsed.text_orig = dct["retweeted_status"]["text"] self.parsed.text_trace = dct["text"] self.parsed.text = ( self.parsed.text_trace + " || " + "@" + self.parsed.username_orig + " : " + self.parsed.text_orig ) else: self.parsed.text_orig = dct["text"] self.parsed.text_trace = None self.parsed.text = self.parsed.text_orig
def _forward(self, mID, text): ''' Raw forward method * Only support Renren message * Use 'text' as exact comment sequence ''' try: api_params = {'method': 'status.forward', 'status': text, 'forward_owner': mID.source_user_id, 'place_id': 'RRAF04D95FA37892FFA88', 'forward_id': mID.status_id } ret = self.renren_request(api_params) if 'id' in ret: # ret['id'] is the ID of new status # X, their doc says the field name is 'result'... return True else: logger.warning("'%s' forward status '%s' with comment '%s' fail. ret: %s", self.jsonconf.channel_name, mID, text, ret) return False except Exception as e: logger.warning("'%s' forward status '%s' with comment '%s' fail: %s", self.jsonconf.channel_name, mID, text, e) return False
def update(self, text, pic=None): '''update a status * parameter text: the update message * return: success or not ''' text = self._cat(self.jsonconf['text_length_limit'], [(text, 1)]) if not pic: method = "t/add" else: method = "t/add_pic" try: if pic: ret = self.tencent_request(method, "POST", content=text, files={'pic': ('pic.jpg', pic)}) else: ret = self.tencent_request(method, "POST", content=text) if (ret['msg'] == "ok"): logger.info("Update status '%s' on '%s' succeed", text, self.jsonconf.channel_name) return True else: return ret except Exception, e: logger.warning("Catch Exception: %s", e) return False
def reply(self, mID, text): ''' Reply a renren blog :param mID: MessageID object :param text: string, the reply message :return: success or not ''' if mID.user_type == 'user': owner_key = 'uid' owner_value = mID.source_user_id else: # 'page' owner_key = 'page_id' owner_value = mID.source_page_id api_params = {'method': 'blog.addComment', 'content': text, 'id': mID.blog_id, owner_key: owner_value} logger.debug('request parameters: %s', api_params) try: ret = self.renren_request(api_params) if 'result' in ret and ret['result'] == 1: logger.info("Reply '%s' to status '%s' succeed", text, mID) return True except Exception, e: logger.warning("Catch Exception %s", e)
def update(self, text, pic=None): '''update a status * parameter text: the update message * return: success or not ''' # NOTE: # * With this pre-shortening, we can post potentially longer messages. # * It consumes one more API quota. text = self._replace_with_short_url(text) text = self._cat(self.jsonconf['text_length_limit'], [(text, 1)], delim='//') try: if not pic: ret = self.weibo_request('statuses/update', 'POST', {'status': text}) else: ret = self.weibo_request( 'statuses/upload', 'POST', {'status': text}, files={'pic': ('pic.jpg', pic)} ) self.Message(ret) logger.info("Update status '%s' on '%s' succeed", text, self.jsonconf.channel_name) return True except Exception as e: logger.warning("Update status fail. Message: %s", e) return False
def _parse(self, dct): if 'deleted' in dct and dct['deleted']: logger.debug("This is a deleted message %s of SinaWeiboStatusMessage", dct["id"]) self.parsed.time = "unknown" self.parsed.username = "******" self.parsed.userid = "unknown" self.parsed.text = "unknown" self.deleted = True return self.ID.id = dct["id"] self.parsed.time = utils.str2utc(dct["created_at"]) self.parsed.username = dct['user']['name'] self.parsed.userid = dct['user']['id'] self.parsed.reposts_count = dct['reposts_count'] self.parsed.comments_count = dct['comments_count'] if 'retweeted_status' in dct: self.parsed.username_orig = "unknown" try: self.parsed.username_orig = dct['retweeted_status']['user']['name'] except KeyError: logger.warning('KeyError when parsing SinaWeiboStatus. May be deleted original message') self.parsed.text_orig = dct['retweeted_status']['text'] self.parsed.text_trace = dct['text'] self.parsed.text = self.parsed.text_trace \ + " || " + "@" + self.parsed.username_orig \ + " : " + self.parsed.text_orig else: self.parsed.text_orig = dct['text'] self.parsed.text_trace = None self.parsed.text = self.parsed.text_orig
def home_timeline(self, count=None, channel=None): """ Route to home_timeline method of snsapi. :param channel: The channel name. Use None to read all channels """ status_list = snstype.MessageList() if channel: if channel in self: if self[channel].is_expired(): logger.warning("channel '%s' is expired. Do nothing.", channel) else: status_list.extend( self._home_timeline(count, self[channel])) else: logger.warning("channel '%s' is not in pocket. Do nothing.", channel) else: for c in self.itervalues(): if self.__check_method(c, 'home_timeline') and not c.is_expired(): status_list.extend(self._home_timeline(count, c)) logger.info("Read %d statuses", len(status_list)) return status_list
def update_func(self): logger.debug("acquiring lock") self.dblock.acquire() try: conn = sqlite3.connect(self.sqlitefile) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM pending_update") i = cursor.fetchone() if i: cursor.execute("DELETE FROM pending_update WHERE id = ?", (i['id'], )) j = { 'id': str(i['id']), 'args': str2obj(str(i['args'])), 'kwargs': str2obj(str(i['kwargs'])), 'type': str(i['type']), 'callback': str2obj(str(i['callback'])) } res = getattr(self.sp, j['type'])(*j['args'], **j['kwargs']) if j['callback']: j['callback'](self, res) conn.commit() cursor.close() except Exception, e: logger.warning("Error while updating: %s" % (str(e)))
def unlike(self, message): ''' Unlike method * Weibo doesn't provide an API for "unlike" * So "unfavourite" function supersedes "unlike" * Here "unlike" means "remove from my favourites" * Receive a message ''' mID = message.ID try: ret = self.weibo_request('favorites/destroy', 'POST', {'id': mID.id}) # error_code 20705 means this status had never been collected. # For the purpose of backward compatibility, we also view # it as a successful unlike if 'favorited_time' in ret or ret["error_code"] == 20705: return True else: logger.warning("'%s' unlikes status '%s' fail. ret: %s", self.jsonconf.channel_name, mID, ret) return False except Exception, e: logger.warning("'%s' unlike status '%s' fail. ret: %s", self.jsonconf.channel_name, mID, ret) return False
def load_config(self, fn_channel=DIR_DEFAULT_CONF_CHANNEL, fn_pocket=DIR_DEFAULT_CONF_POCKET): """ Read configs: * channel.conf * pocket.conf """ count_add_channel = 0 try: with open(path.abspath(fn_channel), "r") as fp: allinfo = json.load(fp) for site in allinfo: if self.add_channel(utils.JsonDict(site)): count_add_channel += 1 except IOError: #raise snserror.config.nofile(fn_channel) logger.warning("'%s' does not exist. Use default", fn_channel) except ValueError as e: raise snserror.config.load("file: %s; message: %s" % (fn_channel, e)) try: with open(path.abspath(fn_pocket), "r") as fp: allinfo = json.load(fp) self.jsonconf = allinfo except IOError: #raise snserror.config.nofile(fn_pocket) logger.warning("'%s' does not exist. Use default", fn_pocket) except ValueError as e: raise snserror.config.load("file: %s; message:%s" % (fn_channel, e)) logger.info("Read configs done. Add %d channels" % count_add_channel)
def update_func(self): logger.debug("acquiring lock") self.dblock.acquire() try: conn = sqlite3.connect(self.sqlitefile) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM pending_update") i = cursor.fetchone() if i: cursor.execute("DELETE FROM pending_update WHERE id = ?", (i['id'], )) j = { 'id': str(i['id']), 'args': str2obj(str(i['args'])), 'kwargs': str2obj(str(i['kwargs'])), 'type': str(i['type']), 'callback': str2obj(str(i['callback'])) } res = getattr(self.sp, j['type'])(*j['args'], **j['kwargs']) if j['callback']: j['callback'](self, res) conn.commit() cursor.close() except Exception, e: logger.warning("Error while updating: %s" % (str(e)))
def update(self, text, title=None): ''' Post a blog :param text: Blog post body. :param title: Blog post title. (optional) :return: success or not ''' if title is None: title = self._cat(20, [(text, 1)]) api_params = {'method': 'blog.addBlog', 'content': text, 'title': title} try: ret = self.renren_request(api_params) logger.debug("response: %s", ret) #TODO: # Preserve the id for further use? # Return it as multi-return-value? if 'id' in ret: logger.info("Update status '%s' on '%s' succeed", text, self.jsonconf.channel_name) return True except Exception, e: logger.warning("Catch Exception %s", e)
def unlike(self, message): res = None flag = False try: # The order in the bracket is important since there # exists "SHARE_XXXX" type. In order to figure out # the actual type, SHARE must be put in the first position. for msg_type in ["SHARE", "BLOG", "PHOTO", "ALBUM", "STATUS", "VIDEO"]: if msg_type in message.ID.feed_type: flag = True break if flag: res = self.renren_request( method="like/ugc/remove", ugcOwnerId=message.ID.source_user_id, likeUGCType="TYPE_" + msg_type, ugcId=message.ID.resource_id ) else: return False except Exception as e: logger.warning('Catch exception: %s', type(e)) return False if res: return True else: return False
def home_timeline(self, count=20): '''Get home timeline get statuses of yours and your friends' @param count: number of statuses ''' api_params = dict(method = "feed.get", type = 10, page = 1, count = count) try: jsonlist = self.renren_request(api_params) except Exception as e: logger.warning("catch expection: %s", e) jsonlist = [] statuslist = snstype.MessageList() for j in jsonlist: try: statuslist.append(self.Message(j,\ platform = self.jsonconf['platform'],\ channel = self.jsonconf['channel_name']\ )) except Exception as e: logger.warning("catch expection '%s' in parsing '%s'", e, j) logger.info("Read %d statuses from '%s'", len(statuslist), self.jsonconf.channel_name) return statuslist
def get(self, attr, default_value = "(null)"): ''' dict entry reading with fault tolerance. :attr: A str or a list of str. If attr is a list, we will try all the candidates until one 'get' is successful. If none of the candidates succeed, we will return a "(null)" e.g. RSS format is very diverse. To my current knowledge, some formats have 'author' fields, but others do not: * rss : no * rss2 : yes * atom : yes * rdf : yes This function will return a string "(null)" by default if the field does not exist. The purpose is to expose unified interface to upper layers. seeing "(null)" is better than catching an error. ''' if isinstance(attr, str): return dict.get(self, attr, default_value) elif isinstance(attr, list): for a in attr: val = dict.get(self, a, None) if val: return val return default_value else: logger.warning("Unkown type: %s", type(attr)) return default_value
def reply(self, statusId, text): res = None flag = False try: # The order in the bracket is important since there # exists "SHARE_XXXX" type. In order to figure out # the actual type, SHARE must be put in the first position. for msg_type in ["SHARE", "BLOG", "PHOTO", "ALBUM", "STATUS", "VIDEO"]: if msg_type in statusId.feed_type: flag = True break if flag: res = self.renren_request( method="comment/put", content=text, commentType=msg_type, entryOwnerId=statusId.source_user_id, entryId=statusId.resource_id ) else: return BooleanWrappedData(False, { 'errors': ['SNSAPI_NOT_SUPPORTED'], }) except Exception, e: logger.warning('Catch exception: %s', e) return BooleanWrappedData(False, { 'errors': ['PLATFORM_'], })
def load_config(self, fn_channel = DIR_DEFAULT_CONF_CHANNEL, fn_pocket = DIR_DEFAULT_CONF_POCKET): """ Read configs: * channel.conf * pocket.conf """ count_add_channel = 0 try: with open(path.abspath(fn_channel), "r") as fp: allinfo = json.load(fp) for site in allinfo: if self.add_channel(utils.JsonDict(site)): count_add_channel += 1 except IOError: #raise snserror.config.nofile(fn_channel) logger.warning("'%s' does not exist. Use default", fn_channel) except ValueError as e: raise snserror.config.load("file: %s; message: %s" % (fn_channel, e)) try: with open(path.abspath(fn_pocket), "r") as fp: allinfo = json.load(fp) self.jsonconf = allinfo except IOError: #raise snserror.config.nofile(fn_pocket) logger.warning("'%s' does not exist. Use default", fn_pocket) except ValueError as e: raise snserror.config.load("file: %s; message:%s" % (fn_channel, e)) logger.info("Read configs done. Add %d channels" % count_add_channel)
def update(self, text, channel=None, **kwargs): """ Route to update method of snsapi. :param channel: The channel name. Use None to update all channels """ re = {} if channel: if channel in self: if self[channel].is_expired(): logger.warning("channel '%s' is expired. Do nothing.", channel) else: re[channel] = self[channel].update(text) else: logger.warning("channel '%s' is not in pocket. Do nothing.", channel) else: for c in self.itervalues(): if self.__check_method(c, 'update') and not c.is_expired(): re[c.jsonconf['channel_name']] = c.update(text, **kwargs) logger.info("Update status '%s'. Result:%s", text, re) return re
def update(self, text, pic=None): '''update a status * parameter text: the update message * return: success or not ''' # NOTE: # * With this pre-shortening, we can post potentially longer messages. # * It consumes one more API quota. text = self._replace_with_short_url(text) text = self._cat(self.jsonconf['text_length_limit'], [(text, 1)], delim='//') try: if not pic: ret = self.weibo_request('statuses/update', 'POST', {'status': text}) else: ret = self.weibo_request('statuses/upload', 'POST', {'status': text}, files={'pic': ('pic.jpg', pic)}) self.Message(ret) logger.info("Update status '%s' on '%s' succeed", text, self.jsonconf.channel_name) return True except Exception as e: logger.warning("Update status fail. Message: %s", e) return False
def _fetch_code_local_username_password(self): try: login_username = self.auth_info.login_username login_password = self.auth_info.login_password app_key = self.jsonconf.app_key app_secret = self.jsonconf.app_secret callback_url = self.auth_info.callback_url referer_url = self._last_requested_url postdata = {"client_id": app_key, "redirect_uri": callback_url, "userId": login_username, "passwd": login_password, "isLoginSina": "0", "action": "submit", "response_type": "code", } headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0", "Host": "api.weibo.com", "Referer": referer_url } auth_url = "https://api.weibo.com/oauth2/authorize" # auth_url = self.auth_info.auth_url resp_url = self._http_post(auth_url, data=postdata, headers=headers, json_parse=False).url logger.debug("response URL from local post: %s", resp_url) return resp_url except Exception, e: logger.warning("Catch exception: %s", e)
def like(self, message): ''' Like method * Weibo doesn't provide an API for "like" * So "favourite" function supersedes "like" * Here "like" means "add to my favourites" * Receive a message ''' mID = message.ID try: ret = self.weibo_request('favorites/create', 'POST', {'id': mID.id}) # error_code 20704 means this status had been collected. # For the purpose of backward compatibility, we also view # it as a successful like if 'favorited_time' in ret or ret["error_code"] == 20704: message.parsed.liked = True return True else: logger.warning("'%s' likes status '%s' fail. ret: %s", self.jsonconf.channel_name, mID, ret) return False except Exception, e: logger.warning("Exception: %s. '%s' like status '%s' fail. ret: %s", e, self.jsonconf.channel_name, mID, ret) return False
def _parse(self, dct): if 'deleted' in dct and dct['deleted']: logger.debug( "This is a deleted message %s of SinaWeiboStatusMessage", dct["id"]) self.parsed.time = "unknown" self.parsed.username = "******" self.parsed.userid = "unknown" self.parsed.text = "unknown" self.deleted = True return self.ID.id = dct["id"] self.parsed.time = utils.str2utc(dct["created_at"]) self.parsed.username = dct['user']['name'] self.parsed.userid = dct['user']['id'] self.parsed.reposts_count = dct['reposts_count'] self.parsed.comments_count = dct['comments_count'] if 'pic_urls' in dct: for pic in dct['pic_urls']: self.parsed.attachments.append({ 'type': 'picture', 'format': ['link'], 'data': pic['thumbnail_pic'].replace('/thumbnail/', '/woriginal/') }) if 'retweeted_status' in dct: self.parsed.username_orig = "unknown" if 'pic_urls' in dct['retweeted_status']: for pic in dct['retweeted_status']['pic_urls']: self.parsed.attachments.append({ 'type': 'picture', 'format': ['link'], 'data': pic['thumbnail_pic'].replace('/thumbnail/', '/woriginal/') }) try: self.parsed.username_orig = dct['retweeted_status']['user'][ 'name'] except KeyError: logger.warning( 'KeyError when parsing SinaWeiboStatus. May be deleted original message' ) self.parsed.text_orig = dct['retweeted_status']['text'] self.parsed.text_trace = dct['text'] self.parsed.text = self.parsed.text_trace \ + "//@" + self.parsed.username_orig \ + ": " + self.parsed.text_orig else: self.parsed.text_orig = dct['text'] self.parsed.text_trace = None self.parsed.text = self.parsed.text_orig
def _parse(self, dct): if 'deleted' in dct and dct['deleted']: logger.debug( "This is a deleted message %s of SinaWeiboStatusMessage", dct["id"]) self.parsed.time = "unknown" self.parsed.username = "******" self.parsed.userid = "unknown" self.parsed.text = "unknown" self.deleted = True return self.ID.id = dct["id"] self.parsed.time = utils.str2utc(dct["created_at"]) self.parsed.username = dct['user']['name'] self.parsed.userid = dct['user']['id'] self.parsed.reposts_count = dct['reposts_count'] self.parsed.comments_count = dct['comments_count'] if 'pic_urls' in dct: for pic in dct['pic_urls']: self.parsed.attachments.append({ 'type': 'picture', 'format': ['link'], 'data': pic['thumbnail_pic'].replace('/thumbnail/', '/woriginal/') }) if 'retweeted_status' in dct: self.parsed.username_orig = "unknown" if 'pic_urls' in dct['retweeted_status']: for pic in dct['retweeted_status']['pic_urls']: self.parsed.attachments.append({ 'type': 'picture', 'format': ['link'], 'data': pic['thumbnail_pic'].replace('/thumbnail/', '/woriginal/') }) try: self.parsed.username_orig = dct['retweeted_status']['user'][ 'name'] except KeyError: logger.warning( 'KeyError when parsing SinaWeiboStatus. May be deleted original message' ) self.parsed.text_orig = dct['retweeted_status']['text'] self.parsed.text_trace = dct['text'] self.parsed.text = self.parsed.text_trace \ + " || " + "@" + self.parsed.username_orig \ + " : " + self.parsed.text_orig else: self.parsed.text_orig = dct['text'] self.parsed.text_trace = None self.parsed.text = self.parsed.text_orig
def home_timeline(self, count=20): try: jsonlist = self.instagram_request(resource="users/self/feed", method="get", count=count) except Exception, e: logger.warning("InstagramAPIError, %s", e) return snstype.MessageList()
def unlike(self, message): try: jsonlist = self.instagram_request(resource="media/" + message.ID.id + "/likes", method="delete") return True except Exception, e: logger.warning("InstagramAPIError, %s", e) return False
def new_channel(self, pl=None, **kwarg): if pl: try: return getattr(platform, pl).new_channel(**kwarg) except AttributeError: logger.warning("can not find platform '%s'", pl) return utils.JsonDict() else: return utils.JsonDict(json.load(open(abspath("conf/init-channel.json.example"), "r")))
def _get_weibo(self, page = 1): #FIXME: 获取转发和评论数应该修改为分析DOM而不是正则表达式(以免与内容重复) #FIXME: 对于转发的微博,原微博信息不足 req = urllib2.Request('http://weibo.cn/?gsid=' + self.token['gsid'] + '&page=%d' % (page)) req = self._process_req(req) m = urllib2.urlopen(req, timeout = 10).read() h = lxml.html.fromstring(m) weibos = [] for i in h.find_class('c'): try: if i.get('id') and i.get('id')[0:2] == 'M_': weibo = None if i.find_class('cmt'): # 转发微博 weibo = { 'uid' : self._get_uid_by_pageurl(i.find_class('nk')[0].attrib['href'], self.jsonconf['uidtype']), 'author' : i.find_class('nk')[0].text, 'id': i.get('id')[2:], 'time': i.find_class('ct')[0].text.encode('utf-8').strip(' ').split(' ')[0].decode('utf-8'), 'text' : None, 'orig' : { 'text': unicode(i.find_class('ctt')[0].text_content()), 'author': re.search(u'转发了\xa0(.*)\xa0的微博', i.find_class('cmt')[0].text_content()).group(1), 'comments_count' : 0, 'reposts_count' : 0 }, 'comments_count' : 0, 'reposts_count' : 0 } parent = i.find_class('cmt')[-1].getparent() retweet_reason = re.sub(r'转发理由:(.*)赞\[[0-9]*\] 转发\[[0-9]*\] 评论\[[0-9]*\] 收藏.*$', r'\1', parent.text_content().encode('utf-8')) weibo['text'] = retweet_reason.decode('utf-8') zf = re.search(r'赞\[([0-9]*)\] 转发\[([0-9]*)\] 评论\[([0-9]*)\]', parent.text_content().encode('utf-8')) if zf: weibo['comments_count'] = int(zf.group(3)) weibo['reposts_count'] = int(zf.group(2)) zf = re.search(r'赞\[([0-9]*)\] 原文转发\[([0-9]*)\] 原文评论\[([0-9]*)\]', i.text_content().encode('utf-8')) if zf: weibo['orig']['comments_count'] = int(zf.group(3)) weibo['orig']['reposts_count'] = int(zf.group(2)) else: weibo = {'author' : i.find_class('nk')[0].text, 'uid' : self._get_uid_by_pageurl(i.find_class('nk')[0].attrib['href'], self.jsonconf['uidtype']), 'text': i.find_class('ctt')[0].text_content()[1:], 'id': i.get('id')[2:], 'time': i.find_class('ct')[0].text.encode('utf-8').strip(' ').split(' ')[0].decode('utf-8') } zf = re.search(r'赞\[([0-9]*)\] 转发\[([0-9]*)\] 评论\[([0-9]*)\]', i.text_content().encode('utf-8')) if zf: weibo['comments_count'] = int(zf.group(3)) weibo['reposts_count'] = int(zf.group(2)) if i.find_class('ib'): #FIXME: Still not able to process a collections of pictures weibo['attachment_img'] = i.find_class('ib')[0].get('src').replace('wap128', 'woriginal') weibos.append(weibo) except Exception, e: logger.warning("Catch exception: %s" % (str(e)))
def auth(self): if self.get_saved_token(): return self.is_authed() if self.jsonconf['auth_by'] == 'gsid': self.token['gsid'] = self.jsonconf['gsid'] elif self.jsonconf['auth_by'] == 'userpass': show_verification = False verification_code = '' req = urllib2.Request('http://login.weibo.cn/login/?vt=4&revalid=2&ns=1&pt=1') req = self._process_req(req) response = urllib2.urlopen(req, timeout = 10) p = response.read() while True: req = urllib2.Request('http://login.weibo.cn/login/?rand=' + (re.search("rand=([0-9]*)", p).group(1) )+ '&backURL=http%3A%2F%2Fweibo.cn&backTitle=%E6%89%8B%E6%9C%BA%E6%96%B0%E6%B5%AA%E7%BD%91&vt=4&revalid=2&ns=1') data = {'mobile': self.auth_info['login_username'], 'password_%s' % (re.search('name="password_([0-9]*)"', p).group(1)): self.auth_info['login_password'], 'backURL': 'http%3A%2F%2Fweibo.cn', 'backTitle': '手机新浪网', 'tryCount': '', 'vk': re.search('name="vk" value="([^"]*)"', p).group(1), 'submit' : '登录'} if show_verification: data['code'] = verification_code data['capId'] = re.search('name="capId" value="([^"]*)"', p).group(1) show_verification = False req = self._process_req(req) data = urllib.urlencode(data) response = urllib2.urlopen(req, data, timeout = 10) p = response.read() final_url = response.geturl() if 'newlogin' in final_url: final_gsid = re.search('g=([^&]*)', final_url).group(1) self.token = {'gsid' : final_gsid} break elif '验证码' in p: err_msg = re.search('class="me">([^>]*)<', p).group(1) if '请输入图片中的字符' in p: captcha_url = re.search(r'"([^"]*captcha[^"]*)', p).group(1) show_verification = True import Image import StringIO ss = urllib2.urlopen(captcha_url, timeout=10).read() sss = StringIO.StringIO(ss) img = Image.open(sss) img.show() verification_code = raw_input(err_msg) else: err_msg = re.search('class="me">([^>]*)<', p).group(1) logger.warning(err_msg) break else: return False res = self.is_authed() if res: self.save_token() return res
def auth(self): if self.get_saved_token(): return self.is_authed() if self.jsonconf['auth_by'] == 'gsid': self.token['gsid'] = self.jsonconf['gsid'] elif self.jsonconf['auth_by'] == 'userpass': show_verification = False verification_code = '' req = urllib2.Request('http://login.weibo.cn/login/?vt=4&revalid=2&ns=1&pt=1') req = self._process_req(req) response = urllib2.urlopen(req, timeout = 10) p = response.read() while True: req = urllib2.Request('http://login.weibo.cn/login/?rand=' + (re.search("rand=([0-9]*)", p).group(1) )+ '&backURL=http%3A%2F%2Fweibo.cn&backTitle=%E6%89%8B%E6%9C%BA%E6%96%B0%E6%B5%AA%E7%BD%91&vt=4&revalid=2&ns=1') data = {'mobile': self.auth_info['login_username'], 'password_%s' % (re.search('name="password_([0-9]*)"', p).group(1)): self.auth_info['login_password'], 'backURL': 'http%3A%2F%2Fweibo.cn', 'backTitle': '手机新浪网', 'tryCount': '', 'vk': re.search('name="vk" value="([^"]*)"', p).group(1), 'submit' : '登录'} if show_verification: data['code'] = verification_code data['capId'] = re.search('name="capId" value="([^"]*)"', p).group(1) show_verification = False req = self._process_req(req) data = urllib.urlencode(data) response = urllib2.urlopen(req, data, timeout = 10) p = response.read() final_url = response.geturl() if 'newlogin' in final_url: final_gsid = re.search('g=([^&]*)', final_url).group(1) self.token = {'gsid' : final_gsid} break elif '验证码' in p: err_msg = re.search('class="me">([^>]*)<', p).group(1) if '请输入图片中的字符' in p: captcha_url = re.search(r'"([^"]*captcha[^"]*)', p).group(1) show_verification = True import Image import StringIO ss = urllib2.urlopen(captcha_url, timeout=10).read() sss = StringIO.StringIO(ss) img = Image.open(sss) img.show() verification_code = raw_input(err_msg) else: err_msg = re.search('class="me">([^>]*)<', p).group(1) logger.warning(err_msg) break else: return False res = self.is_authed() if res: self.save_token() return res
def unlike(self, message): try: jsonlist = self.instagram_request( resource="media/" + message.ID.id + "/likes", method="delete" ) return True except Exception, e: logger.warning("InstagramAPIError, %s", e) return False
def new_channel(self, pl = None, **kwarg): if pl: try: return getattr(platform, pl).new_channel(**kwarg) except AttributeError: logger.warning("can not find platform '%s'", pl) return utils.JsonDict() else: _fn_conf = path.join(SNSConf._SNSAPI_DIR_STATIC_DATA, 'init-channel.json.example') return utils.JsonDict(json.load(open(_fn_conf)))
def _parse(self, dct): if 'deleted' in dct and dct['deleted']: logger.debug("This is a deleted message %s of SinaWeiboStatusMessage", dct["id"]) self.parsed.time = "unknown" self.parsed.username = "******" self.parsed.userid = "unknown" self.parsed.text = "unknown" self.deleted = True return self.ID.id = dct["id"] self.parsed.time = utils.str2utc(dct["created_at"]) self.parsed.username = dct['user']['name'] self.parsed.userid = dct['user']['id'] self.parsed.reposts_count = dct['reposts_count'] self.parsed.comments_count = dct['comments_count'] # accordian to http://open.weibo.com/qa/index.php?qa=448&qa_1=v2-%E5%B7%B2%E6%94%B6%E8%97%8F%E5%BE%AE%E5%8D%9A-%E6%8E%A5%E5%8F%A3statuses-friends-timeline%E8%BF%94%E5%9B%9E%E5%AD%97%E6%AE%B5-favorited-%E4%B8%BAfalse # Currently we have no way to tell whether # a weibo message is favorited Although there's a # specious property self.parsed.liked = False if 'pic_urls' in dct: for pic in dct['pic_urls']: self.parsed.attachments.append( { 'type': 'picture', 'format': ['link'], 'data': pic['thumbnail_pic'].replace('/thumbnail/', '/woriginal/') }) if 'retweeted_status' in dct: self.parsed.username_orig = "unknown" if 'pic_urls' in dct['retweeted_status']: for pic in dct['retweeted_status']['pic_urls']: self.parsed.attachments.append( { 'type': 'picture', 'format': ['link'], 'data': pic['thumbnail_pic'].replace('/thumbnail/', '/woriginal/') }) try: self.parsed.username_orig = dct['retweeted_status']['user']['name'] except KeyError: logger.warning('KeyError when parsing SinaWeiboStatus. May be deleted original message') self.parsed.text_orig = dct['retweeted_status']['text'] self.parsed.text_trace = dct['text'] self.parsed.text = self.parsed.text_trace \ + "//@" + self.parsed.username_orig \ + ": " + self.parsed.text_orig else: self.parsed.text_orig = dct['text'] self.parsed.text_trace = None self.parsed.text = self.parsed.text_orig
def home_timeline(self, count=20): try: jsonlist = self.instagram_request( resource="users/self/feed", method="get", count=count ) except Exception, e: logger.warning("InstagramAPIError, %s", e) return snstype.MessageList()
def new_channel(self, pl=None, **kwarg): if pl: try: return getattr(platform, pl).new_channel(**kwarg) except AttributeError: logger.warning("can not find platform '%s'", pl) return utils.JsonDict() else: _fn_conf = path.join(SNSConf._SNSAPI_DIR_STATIC_DATA, 'init-channel.json.example') return utils.JsonDict(json.load(open(_fn_conf)))
def new_channel(self, pl=None, **argd): if pl: try: return getattr(platform, pl).new_channel(**argd) except AttributeError: logger.warning("can not find platform '%s'", pl) return utils.JsonDict() else: return utils.JsonDict( json.load(open(abspath('conf/init-channel.json.example'), 'r')))
def _get_user_status_list(self, count, userid, username): try: jsonlist = self.renren_request( method="status.gets", page=1, count=count, uid = userid, ) except RenrenAPIError, e: logger.warning("RenrenAPIError, %s", e) return snstype.MessageList()
def _get_user_status_list(self, count, userid, username): try: jsonlist = self.renren_request( method="status/list", pageNumberint=1, pageSize=count, ownerId=userid, ) except RenrenAPIError, e: logger.warning("RenrenAPIError, %s", e) return snstype.MessageList()
def extend(self, l): if isinstance(l, MessageList): super(MessageList, self).extend(l) elif isinstance(l, list): # We still extend the list if the user asks to. # However, a warning will be placed. Doing this # may violate some properties of MessageList, e.g. # there is no Deleted Message in the list. super(MessageList, self).extend(l) logger.warning("Extend MessageList with non MessageList list.") else: logger.warning("Extend MessageList with unknown type.")