Template of a custom feed generator service for the Bluesky network in Ruby
1require 'active_record'
2require 'json'
3
4require_relative 'feed_post'
5
6class Post < ActiveRecord::Base
7 validates_presence_of :repo, :time, :data, :rkey
8 validates :text, length: { minimum: 0, allow_nil: false }
9 validates_length_of :repo, maximum: 60
10 validates_length_of :rkey, maximum: 16
11 validates_length_of :text, maximum: 1000
12 validates_length_of :data, maximum: 10000
13
14 has_many :feed_posts, dependent: :destroy
15
16 attr_writer :record
17
18 def self.find_by_repo_rkey(repo, rkey)
19 # the '+' is to make sure that SQLite uses the rkey index and not a different one
20 Post.where("+repo = ?", repo).where(rkey: rkey).first
21 end
22
23 def self.find_by_at_uri(uri)
24 parts = uri.gsub(%r(^at://), '').split('/')
25 return nil unless parts.length == 3 && parts[1] == 'app.bsky.feed.post'
26
27 find_by_repo_rkey(parts[0], parts[2])
28 end
29
30 def record
31 @record ||= JSON.parse(data)
32 end
33
34 def at_uri
35 "at://#{repo}/app.bsky.feed.post/#{rkey}"
36 end
37
38 def quoted_post_uri
39 if embed = record['embed']
40 if embed['$type'] == "app.bsky.embed.record"
41 return embed['record']['uri']
42 elsif embed['$type'] == "app.bsky.embed.recordWithMedia"
43 if embed['record']['$type'] == "app.bsky.embed.record"
44 return embed['record']['record']['uri']
45 end
46 end
47 end
48
49 return nil
50 end
51
52 def thread_root_uri
53 if root = (record['reply'] && record['reply']['root'])
54 root['uri']
55 else
56 nil
57 end
58 end
59
60 def parent_uri
61 if parent = (record['reply'] && record['reply']['parent'])
62 parent['uri']
63 else
64 nil
65 end
66 end
67
68 def trim_too_long_data
69 if embed = record['embed']
70 if external = embed['external']
71 external['description'] = ''
72 end
73 end
74
75 if record['bridgyOriginalText']
76 record['bridgyOriginalText'] = ''
77 end
78
79 self.data = JSON.generate(record)
80 end
81end