-
Notifications
You must be signed in to change notification settings - Fork 5
/
feed.rb
123 lines (100 loc) · 3.4 KB
/
feed.rb
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
require 'blue_factory/errors'
require 'rainbow'
require 'time'
require_relative '../models/feed_post'
class Feed
DEFAULT_LIMIT = 50
MAX_LIMIT = 100
# any unique number to use as a key in the database
def feed_id
raise NotImplementedError
end
def post_matches?(post)
raise NotImplementedError
end
# name of your feed, e.g. "What's Hot"
def display_name
raise NotImplementedError
end
# (optional) description of the feed, e.g. "Top trending content from the whole network"
def description
nil
end
# (optional) path of the feed avatar file
def avatar_file
nil
end
# (optional) should posts be added to the feed from the firehose?
def is_updating?
true
end
# if the feed matches posts using keywords/regexps, highlight these keywords in the passed text
def colored_text(text)
text
end
def get_posts(params)
limit = check_query_limit(params)
query = FeedPost.where(feed_id: feed_id).joins(:post).select('posts.repo, posts.rkey, feed_posts.time, post_id')
.order('feed_posts.time DESC, post_id DESC').limit(limit)
if params[:cursor].to_s != ""
time, last_id = parse_cursor(params)
query = query.where("feed_posts.time < ? OR (feed_posts.time = ? AND post_id < ?)", time, time, last_id)
end
posts = query.to_a
last = posts.last
cursor = last && sprintf('%.06f', last.time.to_f) + ':' + last.post_id.to_s
{ cursor: cursor, posts: posts.map { |p| 'at://' + p.repo + '/app.bsky.feed.post/' + p.rkey }}
end
# Use this version of the method when enable_unsafe_auth is set to true in app/config.rb.
# This method is called with the DID of the user viewing the feed decoded from the auth header.
#
# Important: BlueFactory does not verify auth signatures of the JWT token at the moment,
# which means that anyone can easily spoof the DID in the header - do not use for anything critical!
#
# def get_posts(params, visitor_did)
# # You can use this DID to e.g.:
# # 1) Provide a personalized feed:
#
# user = User.find_by(did: visitor_did)
# raise BlueFactory::AuthorizationError unless user
# build_feed_for_user(user)
#
# # 2) Only allow whitelisted users to view the feed and show it as empty to others:
# if visitor_did == BlueFactory.publisher_did || ALLOWED_USERS.include?(visitor_did)
# get_feed_posts(params)
# else
# { posts: [] }
# end
#
# # 3) Ban some users from accessing the feed:
# # (note: you can return a custom error message that will be shown in the error banner in the app)
# if BANNED_USERS.include?(visitor_did)
# raise BlueFactory::AuthorizationError, "You shall not pass!"
# else
# get_feed_posts(params)
# end
#
# # 4) Collect feed analytics (please take the privacy of your users into account)
# Stats.count_visit(visitor_did)
# end
private
def check_query_limit(params)
if params[:limit]
limit = params[:limit].to_i
(limit < 0) ? 0 : [limit, MAX_LIMIT].min
else
DEFAULT_LIMIT
end
end
def parse_cursor(params)
parts = params[:cursor].split(':')
if parts.length != 2 || parts[0] !~ /^\d+(\.\d+)?$/ || parts[1] !~ /^\d+$/
raise BlueFactory::InvalidRequestError.new("Malformed cursor")
end
sec = parts[0].to_i
usec = (parts[0].to_f * 1_000_000).to_i % 1_000_000
time = Time.at(sec, usec)
last_id = parts[1].to_i
[time, last_id]
end
end