-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathapi.py
97 lines (76 loc) · 3.63 KB
/
api.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
from __future__ import annotations
from datetime import datetime, timedelta, timezone
from typing import TYPE_CHECKING
from models import ScoredPost
if TYPE_CHECKING:
from mastodon import Mastodon
def fetch_posts_and_boosts(
hours: int, mastodon_client: Mastodon, timeline: str
) -> tuple[list[ScoredPost], list[ScoredPost]]:
"""Fetches posts from the home timeline that the account hasn't interacted with"""
TIMELINE_LIMIT = 1000 # Should this be documented? Configurable?
# First, get our filters
filters = mastodon_client.filters()
# Set our start query
start = datetime.now(timezone.utc) - timedelta(hours=hours)
posts = []
boosts = []
seen_post_urls = set()
total_posts_seen = 0
# If timeline name is specified as hashtag:tagName or list:list-name, look-up with those names,
# else accept 'federated' and 'local' to process from the server public and local timelines.
#
# We default to 'home' if the name is unrecognized
if ":" in timeline:
timelineType, timelineId = timeline.lower().split(":", 1)
else:
timelineType = timeline.lower()
if timelineType == "hashtag":
response = mastodon_client.timeline_hashtag(timelineId, min_id=start)
elif timelineType == "list":
if not timelineId.isnumeric():
raise TypeError('Cannot load list timeline: ID must be numeric, e.g.: https://example.social/lists/4 would be list:4')
response = mastodon_client.timeline_list(timelineId, min_id=start)
elif timelineType == "federated":
response = mastodon_client.timeline_public(min_id=start)
elif timelineType == "local":
response = mastodon_client.timeline_local(min_id=start)
else:
response = mastodon_client.timeline(min_id=start)
mastodon_acct = mastodon_client.me()['acct'].strip().lower()
# Iterate over our timeline until we run out of posts or we hit the limit
while response and total_posts_seen < TIMELINE_LIMIT:
# Apply our server-side filters
if filters:
filtered_response = mastodon_client.filters_apply(response, filters, "home")
else:
filtered_response = response
for post in filtered_response:
total_posts_seen += 1
boost = False
if post["reblog"] is not None:
post = post["reblog"] # look at the boosted post
boost = True
scored_post = ScoredPost(post) # wrap the post data as a ScoredPost
if scored_post.url not in seen_post_urls:
# Apply our local filters
# Basically ignore my posts or posts I've interacted with
# and ignore posts from accounts that have "#noindex" or "#nobot"
if (
not scored_post.info["reblogged"]
and not scored_post.info["favourited"]
and not scored_post.info["bookmarked"]
and scored_post.info["account"]["acct"].strip().lower() != mastodon_acct
and "#noindex" not in scored_post.info["account"]["note"].lower()
and "#nobot" not in scored_post.info["account"]["note"].lower()
):
# Append to either the boosts list or the posts lists
if boost:
boosts.append(scored_post)
else:
posts.append(scored_post)
seen_post_urls.add(scored_post.url)
response = mastodon_client.fetch_previous(
response
) # fetch the previous (because of reverse chron) page of results
return posts, boosts