[twitter] simplify URL patterns with USER_PATTERN
This commit is contained in:
@@ -16,6 +16,7 @@ import random
|
|||||||
|
|
||||||
BASE_PATTERN = (r"(?:https?://)?(?:www\.|mobile\.)?"
|
BASE_PATTERN = (r"(?:https?://)?(?:www\.|mobile\.)?"
|
||||||
r"(?:(?:[fv]x)?twitter|(?:fix(?:up|v))?x)\.com")
|
r"(?:(?:[fv]x)?twitter|(?:fix(?:up|v))?x)\.com")
|
||||||
|
USER_PATTERN = rf"{BASE_PATTERN}/([^/?#]+)"
|
||||||
|
|
||||||
|
|
||||||
class TwitterExtractor(Extractor):
|
class TwitterExtractor(Extractor):
|
||||||
@@ -653,9 +654,52 @@ class TwitterHomeExtractor(TwitterExtractor):
|
|||||||
return self.api.home_timeline()
|
return self.api.home_timeline()
|
||||||
|
|
||||||
|
|
||||||
|
class TwitterSearchExtractor(TwitterExtractor):
|
||||||
|
"""Extractor for Twitter search results"""
|
||||||
|
subcategory = "search"
|
||||||
|
pattern = BASE_PATTERN + r"/search/?\?(?:[^&#]+&)*q=([^&#]+)"
|
||||||
|
example = "https://x.com/search?q=QUERY"
|
||||||
|
|
||||||
|
def metadata(self):
|
||||||
|
return {"search": text.unquote(self.user)}
|
||||||
|
|
||||||
|
def tweets(self):
|
||||||
|
query = text.unquote(self.user.replace("+", " "))
|
||||||
|
|
||||||
|
user = None
|
||||||
|
for item in query.split():
|
||||||
|
item = item.strip("()")
|
||||||
|
if item.startswith("from:"):
|
||||||
|
if user:
|
||||||
|
user = None
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
user = item[5:]
|
||||||
|
|
||||||
|
if user is not None:
|
||||||
|
try:
|
||||||
|
self._assign_user(self.api.user_by_screen_name(user))
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return self.api.search_timeline(query)
|
||||||
|
|
||||||
|
|
||||||
|
class TwitterHashtagExtractor(TwitterExtractor):
|
||||||
|
"""Extractor for Twitter hashtags"""
|
||||||
|
subcategory = "hashtag"
|
||||||
|
pattern = BASE_PATTERN + r"/hashtag/([^/?#]+)"
|
||||||
|
example = "https://x.com/hashtag/NAME"
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
url = f"{self.root}/search?q=%23{self.user}"
|
||||||
|
data = {"_extractor": TwitterSearchExtractor}
|
||||||
|
yield Message.Queue, url, data
|
||||||
|
|
||||||
|
|
||||||
class TwitterUserExtractor(Dispatch, TwitterExtractor):
|
class TwitterUserExtractor(Dispatch, TwitterExtractor):
|
||||||
"""Extractor for a Twitter user"""
|
"""Extractor for a Twitter user"""
|
||||||
pattern = (BASE_PATTERN + r"/(?!search\b|home\b|i/timeline)(?:"
|
pattern = (BASE_PATTERN + r"/(?:"
|
||||||
r"([^/?#]+)/?(?:$|\?|#)"
|
r"([^/?#]+)/?(?:$|\?|#)"
|
||||||
r"|i(?:/user/|ntent/user\?user_id=)(\d+))")
|
r"|i(?:/user/|ntent/user\?user_id=)(\d+))")
|
||||||
example = "https://x.com/USER"
|
example = "https://x.com/USER"
|
||||||
@@ -682,8 +726,7 @@ class TwitterUserExtractor(Dispatch, TwitterExtractor):
|
|||||||
class TwitterTimelineExtractor(TwitterExtractor):
|
class TwitterTimelineExtractor(TwitterExtractor):
|
||||||
"""Extractor for a Twitter user timeline"""
|
"""Extractor for a Twitter user timeline"""
|
||||||
subcategory = "timeline"
|
subcategory = "timeline"
|
||||||
pattern = (BASE_PATTERN +
|
pattern = rf"{USER_PATTERN}/timeline(?!\w)"
|
||||||
r"/(?!search\b|home\b|i\b)([^/?#]+)/timeline(?!\w)")
|
|
||||||
example = "https://x.com/USER/timeline"
|
example = "https://x.com/USER/timeline"
|
||||||
|
|
||||||
def _init_cursor(self):
|
def _init_cursor(self):
|
||||||
@@ -780,7 +823,7 @@ class TwitterTimelineExtractor(TwitterExtractor):
|
|||||||
class TwitterTweetsExtractor(TwitterExtractor):
|
class TwitterTweetsExtractor(TwitterExtractor):
|
||||||
"""Extractor for Tweets from a user's Tweets timeline"""
|
"""Extractor for Tweets from a user's Tweets timeline"""
|
||||||
subcategory = "tweets"
|
subcategory = "tweets"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/tweets(?!\w)"
|
pattern = rf"{USER_PATTERN}/tweets(?!\w)"
|
||||||
example = "https://x.com/USER/tweets"
|
example = "https://x.com/USER/tweets"
|
||||||
|
|
||||||
def tweets(self):
|
def tweets(self):
|
||||||
@@ -790,7 +833,7 @@ class TwitterTweetsExtractor(TwitterExtractor):
|
|||||||
class TwitterRepliesExtractor(TwitterExtractor):
|
class TwitterRepliesExtractor(TwitterExtractor):
|
||||||
"""Extractor for Tweets from a user's timeline including replies"""
|
"""Extractor for Tweets from a user's timeline including replies"""
|
||||||
subcategory = "replies"
|
subcategory = "replies"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/with_replies(?!\w)"
|
pattern = rf"{USER_PATTERN}/with_replies(?!\w)"
|
||||||
example = "https://x.com/USER/with_replies"
|
example = "https://x.com/USER/with_replies"
|
||||||
|
|
||||||
def tweets(self):
|
def tweets(self):
|
||||||
@@ -800,7 +843,7 @@ class TwitterRepliesExtractor(TwitterExtractor):
|
|||||||
class TwitterHighlightsExtractor(TwitterExtractor):
|
class TwitterHighlightsExtractor(TwitterExtractor):
|
||||||
"""Extractor for Tweets from a user's highlights timeline"""
|
"""Extractor for Tweets from a user's highlights timeline"""
|
||||||
subcategory = "highlights"
|
subcategory = "highlights"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/highlights(?!\w)"
|
pattern = rf"{USER_PATTERN}/highlights(?!\w)"
|
||||||
example = "https://x.com/USER/highlights"
|
example = "https://x.com/USER/highlights"
|
||||||
|
|
||||||
def tweets(self):
|
def tweets(self):
|
||||||
@@ -810,7 +853,7 @@ class TwitterHighlightsExtractor(TwitterExtractor):
|
|||||||
class TwitterMediaExtractor(TwitterExtractor):
|
class TwitterMediaExtractor(TwitterExtractor):
|
||||||
"""Extractor for Tweets from a user's Media timeline"""
|
"""Extractor for Tweets from a user's Media timeline"""
|
||||||
subcategory = "media"
|
subcategory = "media"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/media(?!\w)"
|
pattern = rf"{USER_PATTERN}/media(?!\w)"
|
||||||
example = "https://x.com/USER/media"
|
example = "https://x.com/USER/media"
|
||||||
|
|
||||||
def tweets(self):
|
def tweets(self):
|
||||||
@@ -820,7 +863,7 @@ class TwitterMediaExtractor(TwitterExtractor):
|
|||||||
class TwitterLikesExtractor(TwitterExtractor):
|
class TwitterLikesExtractor(TwitterExtractor):
|
||||||
"""Extractor for liked tweets"""
|
"""Extractor for liked tweets"""
|
||||||
subcategory = "likes"
|
subcategory = "likes"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/likes(?!\w)"
|
pattern = rf"{USER_PATTERN}/likes(?!\w)"
|
||||||
example = "https://x.com/USER/likes"
|
example = "https://x.com/USER/likes"
|
||||||
|
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
@@ -870,7 +913,7 @@ class TwitterListMembersExtractor(TwitterExtractor):
|
|||||||
class TwitterFollowingExtractor(TwitterExtractor):
|
class TwitterFollowingExtractor(TwitterExtractor):
|
||||||
"""Extractor for followed users"""
|
"""Extractor for followed users"""
|
||||||
subcategory = "following"
|
subcategory = "following"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/following(?!\w)"
|
pattern = rf"{USER_PATTERN}/following(?!\w)"
|
||||||
example = "https://x.com/USER/following"
|
example = "https://x.com/USER/following"
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
@@ -881,7 +924,7 @@ class TwitterFollowingExtractor(TwitterExtractor):
|
|||||||
class TwitterFollowersExtractor(TwitterExtractor):
|
class TwitterFollowersExtractor(TwitterExtractor):
|
||||||
"""Extractor for a user's followers"""
|
"""Extractor for a user's followers"""
|
||||||
subcategory = "followers"
|
subcategory = "followers"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/followers(?!\w)"
|
pattern = rf"{USER_PATTERN}/followers(?!\w)"
|
||||||
example = "https://x.com/USER/followers"
|
example = "https://x.com/USER/followers"
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
@@ -889,49 +932,6 @@ class TwitterFollowersExtractor(TwitterExtractor):
|
|||||||
return self._users_result(TwitterAPI(self).user_followers(self.user))
|
return self._users_result(TwitterAPI(self).user_followers(self.user))
|
||||||
|
|
||||||
|
|
||||||
class TwitterSearchExtractor(TwitterExtractor):
|
|
||||||
"""Extractor for Twitter search results"""
|
|
||||||
subcategory = "search"
|
|
||||||
pattern = BASE_PATTERN + r"/search/?\?(?:[^&#]+&)*q=([^&#]+)"
|
|
||||||
example = "https://x.com/search?q=QUERY"
|
|
||||||
|
|
||||||
def metadata(self):
|
|
||||||
return {"search": text.unquote(self.user)}
|
|
||||||
|
|
||||||
def tweets(self):
|
|
||||||
query = text.unquote(self.user.replace("+", " "))
|
|
||||||
|
|
||||||
user = None
|
|
||||||
for item in query.split():
|
|
||||||
item = item.strip("()")
|
|
||||||
if item.startswith("from:"):
|
|
||||||
if user:
|
|
||||||
user = None
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
user = item[5:]
|
|
||||||
|
|
||||||
if user is not None:
|
|
||||||
try:
|
|
||||||
self._assign_user(self.api.user_by_screen_name(user))
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return self.api.search_timeline(query)
|
|
||||||
|
|
||||||
|
|
||||||
class TwitterHashtagExtractor(TwitterExtractor):
|
|
||||||
"""Extractor for Twitter hashtags"""
|
|
||||||
subcategory = "hashtag"
|
|
||||||
pattern = BASE_PATTERN + r"/hashtag/([^/?#]+)"
|
|
||||||
example = "https://x.com/hashtag/NAME"
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
url = f"{self.root}/search?q=%23{self.user}"
|
|
||||||
data = {"_extractor": TwitterSearchExtractor}
|
|
||||||
yield Message.Queue, url, data
|
|
||||||
|
|
||||||
|
|
||||||
class TwitterCommunityExtractor(TwitterExtractor):
|
class TwitterCommunityExtractor(TwitterExtractor):
|
||||||
"""Extractor for a Twitter community"""
|
"""Extractor for a Twitter community"""
|
||||||
subcategory = "community"
|
subcategory = "community"
|
||||||
@@ -1069,7 +1069,7 @@ class TwitterQuotesExtractor(TwitterExtractor):
|
|||||||
class TwitterInfoExtractor(TwitterExtractor):
|
class TwitterInfoExtractor(TwitterExtractor):
|
||||||
"""Extractor for a user's profile data"""
|
"""Extractor for a user's profile data"""
|
||||||
subcategory = "info"
|
subcategory = "info"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/info"
|
pattern = rf"{USER_PATTERN}/info"
|
||||||
example = "https://x.com/USER/info"
|
example = "https://x.com/USER/info"
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
@@ -1088,7 +1088,7 @@ class TwitterAvatarExtractor(TwitterExtractor):
|
|||||||
subcategory = "avatar"
|
subcategory = "avatar"
|
||||||
filename_fmt = "avatar {date}.{extension}"
|
filename_fmt = "avatar {date}.{extension}"
|
||||||
archive_fmt = "AV_{user[id]}_{date}"
|
archive_fmt = "AV_{user[id]}_{date}"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/photo"
|
pattern = rf"{USER_PATTERN}/photo"
|
||||||
example = "https://x.com/USER/photo"
|
example = "https://x.com/USER/photo"
|
||||||
|
|
||||||
def tweets(self):
|
def tweets(self):
|
||||||
@@ -1110,7 +1110,7 @@ class TwitterBackgroundExtractor(TwitterExtractor):
|
|||||||
subcategory = "background"
|
subcategory = "background"
|
||||||
filename_fmt = "background {date}.{extension}"
|
filename_fmt = "background {date}.{extension}"
|
||||||
archive_fmt = "BG_{user[id]}_{date}"
|
archive_fmt = "BG_{user[id]}_{date}"
|
||||||
pattern = BASE_PATTERN + r"/(?!search)([^/?#]+)/header_photo"
|
pattern = rf"{USER_PATTERN}/header_photo"
|
||||||
example = "https://x.com/USER/header_photo"
|
example = "https://x.com/USER/header_photo"
|
||||||
|
|
||||||
def tweets(self):
|
def tweets(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user