skyfall 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -10
  3. data/LICENSE.txt +1 -1
  4. data/README.md +10 -11
  5. data/lib/skyfall/car_archive.rb +42 -6
  6. data/lib/skyfall/cid.rb +2 -0
  7. data/lib/skyfall/collection.rb +23 -1
  8. data/lib/skyfall/errors.rb +58 -4
  9. data/lib/skyfall/events.rb +19 -0
  10. data/lib/skyfall/extensions.rb +4 -0
  11. data/lib/skyfall/firehose/account_message.rb +32 -4
  12. data/lib/skyfall/firehose/commit_message.rb +45 -5
  13. data/lib/skyfall/firehose/identity_message.rb +30 -2
  14. data/lib/skyfall/firehose/info_message.rb +35 -1
  15. data/lib/skyfall/firehose/labels_message.rb +28 -10
  16. data/lib/skyfall/firehose/message.rb +133 -24
  17. data/lib/skyfall/firehose/operation.rb +57 -5
  18. data/lib/skyfall/firehose/sync_message.rb +31 -0
  19. data/lib/skyfall/firehose/unknown_message.rb +8 -0
  20. data/lib/skyfall/firehose.rb +103 -9
  21. data/lib/skyfall/jetstream/account_message.rb +21 -1
  22. data/lib/skyfall/jetstream/commit_message.rb +35 -2
  23. data/lib/skyfall/jetstream/identity_message.rb +22 -1
  24. data/lib/skyfall/jetstream/message.rb +94 -7
  25. data/lib/skyfall/jetstream/operation.rb +56 -4
  26. data/lib/skyfall/jetstream/unknown_message.rb +8 -0
  27. data/lib/skyfall/jetstream.rb +95 -13
  28. data/lib/skyfall/label.rb +33 -0
  29. data/lib/skyfall/stream.rb +304 -53
  30. data/lib/skyfall/version.rb +1 -1
  31. metadata +9 -14
  32. data/example/block_tracker.rb +0 -84
  33. data/example/jet_monitor_phrases.rb +0 -54
  34. data/example/print_all_posts.rb +0 -34
  35. data/example/push_notifications.rb +0 -262
  36. data/lib/skyfall/firehose/handle_message.rb +0 -14
  37. data/lib/skyfall/firehose/tombstone_message.rb +0 -11
@@ -1,262 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # Example: send push notifications to a client app about interactions with a given account.
4
-
5
- # load skyfall from a local folder - you normally won't need this
6
- $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
7
-
8
- require 'json'
9
- require 'open-uri'
10
- require 'skyfall'
11
-
12
- monitored_did = ARGV[0]
13
-
14
- if monitored_did.to_s.empty?
15
- puts "Usage: #{$PROGRAM_NAME} <monitored_did>"
16
- exit 1
17
- elsif monitored_did !~ /^did:plc:[a-z0-9]{24}$/
18
- puts "Not a valid DID: #{monitored_did}"
19
- exit 1
20
- end
21
-
22
- class InvalidURIException < StandardError
23
- def initialize(uri)
24
- super("Invalid AT URI: #{uri}")
25
- end
26
- end
27
-
28
- class AtURI
29
- attr_reader :did, :collection, :rkey
30
-
31
- def initialize(uri)
32
- if uri =~ /\Aat:\/\/(did:[\w]+:[\w\.\-]+)\/([\w\.]+)\/([\w\-]+)\z/
33
- @did = $1
34
- @collection = $2
35
- @rkey = $3
36
- else
37
- raise InvalidURIException, uri
38
- end
39
- end
40
- end
41
-
42
- class NotificationEngine
43
- def initialize(user_did)
44
- @user_did = user_did
45
- end
46
-
47
- def connect
48
- @sky = Skyfall::Firehose.new('bsky.network', :subscribe_repos)
49
-
50
- @sky.on_connect { puts "Connected, monitoring #{@user_did}" }
51
- @sky.on_disconnect { puts "Disconnected" }
52
- @sky.on_reconnect { puts "Reconnecting..." }
53
- @sky.on_error { |e| puts "ERROR: #{e}" }
54
-
55
- @sky.on_message do |msg|
56
- process_message(msg)
57
- end
58
-
59
- @sky.connect
60
- end
61
-
62
- def disconnect
63
- @sky.disconnect
64
- end
65
-
66
- def process_message(msg)
67
- # we're only interested in repo commit messages
68
- return if msg.type != :commit
69
-
70
- # ignore user's own actions
71
- return if msg.repo == @user_did
72
-
73
- msg.operations.each do |op|
74
- next if op.action != :create
75
-
76
- begin
77
- case op.type
78
- when :bsky_post
79
- process_post(msg, op)
80
- when :bsky_like
81
- process_like(msg, op)
82
- when :bsky_repost
83
- process_repost(msg, op)
84
- when :bsky_follow
85
- process_follow(msg, op)
86
- end
87
- rescue StandardError => e
88
- puts "Error: #{e}"
89
- end
90
- end
91
- end
92
-
93
-
94
- # posts
95
-
96
- def process_post(msg, op)
97
- data = op.raw_record
98
-
99
- if reply = data['reply']
100
- # check for replies (direct only)
101
- if reply['parent'] && reply['parent']['uri']
102
- parent_uri = AtURI.new(reply['parent']['uri'])
103
-
104
- if parent_uri.did == @user_did
105
- send_reply_notification(msg, op)
106
- end
107
- end
108
- end
109
-
110
- if embed = data['embed']
111
- # check for quotes
112
- if embed['record'] && embed['record']['uri']
113
- quoted_uri = AtURI.new(embed['record']['uri'])
114
-
115
- if quoted_uri.did == @user_did
116
- send_quote_notification(msg, op)
117
- end
118
- end
119
-
120
- # second type of quote (recordWithMedia)
121
- if embed['record'] && embed['record']['record'] && embed['record']['record']['uri']
122
- quoted_uri = AtURI.new(embed['record']['record']['uri'])
123
-
124
- if quoted_uri.did == @user_did
125
- send_quote_notification(msg, op)
126
- end
127
- end
128
- end
129
-
130
- if facets = data['facets']
131
- # check for mentions
132
- if facets.any? { |f| f['features'] && f['features'].any? { |x| x['did'] == @user_did }}
133
- send_mention_notification(msg, op)
134
- end
135
- end
136
- end
137
-
138
- def send_reply_notification(msg, op)
139
- handle = get_user_handle(msg.repo)
140
-
141
- send_push("@#{handle} replied:", op.raw_record)
142
- end
143
-
144
- def send_quote_notification(msg, op)
145
- handle = get_user_handle(msg.repo)
146
-
147
- send_push("@#{handle} quoted you:", op.raw_record)
148
- end
149
-
150
- def send_mention_notification(msg, op)
151
- handle = get_user_handle(msg.repo)
152
-
153
- send_push("@#{handle} mentioned you:", op.raw_record)
154
- end
155
-
156
-
157
- # likes
158
-
159
- def process_like(msg, op)
160
- data = op.raw_record
161
-
162
- if data['subject'] && data['subject']['uri']
163
- liked_uri = AtURI.new(data['subject']['uri'])
164
-
165
- if liked_uri.did == @user_did
166
- case liked_uri.collection
167
- when 'app.bsky.feed.post'
168
- send_post_like_notification(msg, liked_uri)
169
- when 'app.bsky.feed.generator'
170
- send_feed_like_notification(msg, liked_uri)
171
- end
172
- end
173
- end
174
- end
175
-
176
- def send_post_like_notification(msg, uri)
177
- handle = get_user_handle(msg.repo)
178
- post = get_record(uri)
179
-
180
- send_push("@#{handle} liked your post", post)
181
- end
182
-
183
- def send_feed_like_notification(msg, uri)
184
- handle = get_user_handle(msg.repo)
185
- feed = get_record(uri)
186
-
187
- send_push("@#{handle} liked your feed", feed)
188
- end
189
-
190
-
191
- # reposts
192
-
193
- def process_repost(msg, op)
194
- data = op.raw_record
195
-
196
- if data['subject'] && data['subject']['uri']
197
- reposted_uri = AtURI.new(data['subject']['uri'])
198
-
199
- if reposted_uri.did == @user_did && reposted_uri.collection == 'app.bsky.feed.post'
200
- send_repost_notification(msg, reposted_uri)
201
- end
202
- end
203
- end
204
-
205
- def send_repost_notification(msg, uri)
206
- handle = get_user_handle(msg.repo)
207
- post = get_record(uri)
208
-
209
- send_push("@#{handle} reposted your post", post)
210
- end
211
-
212
-
213
- # follows
214
-
215
- def process_follow(msg, op)
216
- if op.raw_record['subject'] == @user_did
217
- send_follow_notification(msg)
218
- end
219
- end
220
-
221
- def send_follow_notification(msg)
222
- handle = get_user_handle(msg.repo)
223
-
224
- send_push("@#{handle} followed you", msg.repo)
225
- end
226
-
227
-
228
- #
229
- # Note: in this example, we're calling the Bluesky AppView to get details about the person interacting with the user
230
- # and the post/feed that was liked/reposted etc. In a real app, you might run into rate limits if you do that,
231
- # because these requests will all be sent from the server's IP.
232
- #
233
- # So you might need to take a different route and send just the info that you have here in the push notification data
234
- # (the AT URI / DID) and fetch the details on the client side, e.g. in a Notification Service Extension on iOS.
235
- #
236
-
237
- def get_user_handle(did)
238
- url = "https://api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=#{did}"
239
- json = JSON.parse(URI.open(url).read)
240
- json['handle']
241
- end
242
-
243
- def get_record(uri)
244
- url = "https://api.bsky.app/xrpc/com.atproto.repo.getRecord?" +
245
- "repo=#{uri.did}&collection=#{uri.collection}&rkey=#{uri.rkey}"
246
- json = JSON.parse(URI.open(url).read)
247
- json['value']
248
- end
249
-
250
- def send_push(message, data = nil)
251
- # send the message to APNS/FCM here
252
- puts
253
- puts "[#{Time.now}] #{message} #{data&.inspect}"
254
- end
255
- end
256
-
257
- engine = NotificationEngine.new(monitored_did)
258
-
259
- # close the connection cleanly on Ctrl+C
260
- trap("SIGINT") { engine.disconnect }
261
-
262
- engine.connect
@@ -1,14 +0,0 @@
1
- require_relative '../firehose'
2
-
3
- module Skyfall
4
-
5
- #
6
- # Note: this event type is deprecated and will stop being emitted at some point.
7
- # You should instead listen for 'identity' events (Skyfall::Firehose::IdentityMessage).
8
- #
9
- class Firehose::HandleMessage < Firehose::Message
10
- def handle
11
- @data_object['handle']
12
- end
13
- end
14
- end
@@ -1,11 +0,0 @@
1
- require_relative '../firehose'
2
-
3
- module Skyfall
4
-
5
- #
6
- # Note: this event type is deprecated and will stop being emitted at some point.
7
- # You should instead listen for 'account' events (Skyfall::Firehose::AccountMessage).
8
- #
9
- class Firehose::TombstoneMessage < Firehose::Message
10
- end
11
- end