skyfall 0.3.1 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -2
- data/LICENSE.txt +1 -1
- data/README.md +28 -1
- data/example/follower_tracker.rb +84 -0
- data/lib/skyfall/collection.rb +1 -0
- data/lib/skyfall/operation.rb +1 -0
- data/lib/skyfall/stream.rb +47 -3
- data/lib/skyfall/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db537537fcd4e38f184c6cdea3f51ba1b8554848d4c3cd1de20f63ca2d956fc2
|
4
|
+
data.tar.gz: 34c51f6c8c0152589562bf0a3b3c5246cd8a1e95664f1128c86bd17c6a313d59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11477293a1bc0377ef5d9eaa68cc2d374f76ac4d942006572e8b9e1668cb8f54a582abdeda81b65c1d72f6452ba2cad1153c403027ba5d7efbfa093624713105
|
7
|
+
data.tar.gz: 8d86b1f71a4fd5fa7f01077fefe8d8e292dc6b5007b79ee4d1ed1343f887c4104c0480b297e7ce4be61db0cce65211a568b642411fa5c3e7f62c4c91a4fe0f7b
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
## [
|
1
|
+
## [0.4.0] - 2024-09-23
|
2
2
|
|
3
|
-
- added
|
3
|
+
- (re)added a "hearbeat" feature (removed earlier in 0.2.0) to fix the occasional issue when the websocket stops receiving data, but doesn't disconnect (not enabled by default, turn it on by setting `check_heartbeat` to true)
|
4
|
+
- added a way to set the user agent sent when connecting using the `user_agent` field (default is `"Skyfall/#{version}"`)
|
5
|
+
- added `app.bsky.feed.postgate` record type
|
4
6
|
|
5
7
|
## [0.3.1] - 2024-06-28
|
6
8
|
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -123,9 +123,36 @@ When Skyfall receives a message about a record type that's not on the list, whet
|
|
123
123
|
Do not however check if such operations have a `type` equal to `:unknown` first - just ignore the type and only check the `collection` string. The reason is that some next version of Skyfall might start recognizing those records and add a new `type` value for them like e.g. `:skygram_photo`, and then they won't match your condition anymore.
|
124
124
|
|
125
125
|
|
126
|
+
## Configuration
|
127
|
+
|
128
|
+
### User agent
|
129
|
+
|
130
|
+
`Skyfall::Stream` sends a user agent header when making a connection. This is set by default to `"Skyfall/0.x.y"`, but it's recommended that you override it using the `user_agent` field to something that identifies your app and its author – this will let the owner of the server you're connecting to know who to contact in case the client is causing some problems.
|
131
|
+
|
132
|
+
You can also append your user agent info to the default value like this:
|
133
|
+
|
134
|
+
```rb
|
135
|
+
sky.user_agent = "NewsBot (@news.bot) #{sky.default_user_agent}"
|
136
|
+
```
|
137
|
+
|
138
|
+
### Heartbeat and reconnecting
|
139
|
+
|
140
|
+
Occasionally, especially during times of very heavy traffic, the websocket can get into a stuck state where it stops receiving any data, but doesn't disconnect and just hangs like this forever. To work around this, there is a "heartbeat" feature which starts a background timer, which periodically checks how much time has passed since the last received event, and if the time exceeds a set limit, it manually disconnects and reconnects the stream.
|
141
|
+
|
142
|
+
The option is not enabled by default, because there are some firehoses which will not be sending events often, possibly only once in a while – e.g. labellers and independent PDS firehoses – and in this case we don't want any heartbeat since it will be completely normal not to have any events for a long time. It's not really possible to detect easily if we're connecting to a full network relay or one of those, so in order to avoid false alarms, you need to enable this manually using the `check_heartbeat` property.
|
143
|
+
|
144
|
+
You can also change the `heartbeat_interval`, i.e. how often the timer is triggered (default: 10s), and the `heartbeat_timeout`, i.e. the amount of time passed without events when it reconnects (default: 5 min):
|
145
|
+
|
146
|
+
```rb
|
147
|
+
sky.check_heartbeat = true
|
148
|
+
sky.heartbeat_interval = 5
|
149
|
+
sky.heartbeat_timeout = 120
|
150
|
+
```
|
151
|
+
|
152
|
+
|
126
153
|
## Credits
|
127
154
|
|
128
|
-
Copyright ©
|
155
|
+
Copyright © 2024 Kuba Suder ([@mackuba.eu](https://bsky.app/profile/mackuba.eu)).
|
129
156
|
|
130
157
|
The code is available under the terms of the [zlib license](https://choosealicense.com/licenses/zlib/) (permissive, similar to MIT).
|
131
158
|
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Example: track when people follow and unfollow your 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 ARGV[0] !~ /^did:plc:[a-z0-9]{24}$/
|
18
|
+
puts "Not a valid DID: #{$monitored_did}"
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
sky = Skyfall::Stream.new('bsky.network', :subscribe_repos)
|
23
|
+
|
24
|
+
sky.on_connect { puts "Connected, monitoring #{$monitored_did}" }
|
25
|
+
sky.on_disconnect { puts "Disconnected" }
|
26
|
+
sky.on_reconnect { puts "Reconnecting..." }
|
27
|
+
sky.on_error { |e| puts "ERROR: #{e}" }
|
28
|
+
|
29
|
+
sky.on_message do |msg|
|
30
|
+
# we're only interested in repo commit messages
|
31
|
+
next if msg.type != :commit
|
32
|
+
|
33
|
+
msg.operations.each do |op|
|
34
|
+
next if op.action != :create
|
35
|
+
|
36
|
+
begin
|
37
|
+
case op.type
|
38
|
+
when :bsky_block
|
39
|
+
process_block(msg, op)
|
40
|
+
when :bsky_listitem
|
41
|
+
process_list_item(msg, op)
|
42
|
+
end
|
43
|
+
rescue StandardError => e
|
44
|
+
puts "Error: #{e}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_block(msg, op)
|
50
|
+
if op.raw_record['subject'] == $monitored_did
|
51
|
+
owner_handle = get_user_handle(op.repo)
|
52
|
+
puts "@#{owner_handle} has blocked you! (#{msg.time.getlocal})"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def process_list_item(msg, op)
|
57
|
+
if op.raw_record['subject'] == $monitored_did
|
58
|
+
owner_handle = get_user_handle(op.repo)
|
59
|
+
|
60
|
+
list_uri = op.raw_record['list']
|
61
|
+
list_name = get_list_name(list_uri)
|
62
|
+
|
63
|
+
puts "@#{owner_handle} has added you to list \"#{list_name}\" (#{msg.time.getlocal})"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_user_handle(did)
|
68
|
+
url = "https://plc.directory/#{did}"
|
69
|
+
json = JSON.parse(URI.open(url).read)
|
70
|
+
json['alsoKnownAs'][0].gsub('at://', '')
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_list_name(list_uri)
|
74
|
+
repo, type, rkey = list_uri.gsub('at://', '').split('/')
|
75
|
+
url = "https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=#{repo}&collection=#{type}&rkey=#{rkey}"
|
76
|
+
|
77
|
+
json = JSON.parse(URI.open(url).read)
|
78
|
+
json['value']['name']
|
79
|
+
end
|
80
|
+
|
81
|
+
# close the connection cleanly on Ctrl+C
|
82
|
+
trap("SIGINT") { sky.disconnect }
|
83
|
+
|
84
|
+
sky.connect
|
data/lib/skyfall/collection.rb
CHANGED
@@ -4,6 +4,7 @@ module Skyfall
|
|
4
4
|
BSKY_FEED = "app.bsky.feed.generator"
|
5
5
|
BSKY_LIKE = "app.bsky.feed.like"
|
6
6
|
BSKY_POST = "app.bsky.feed.post"
|
7
|
+
BSKY_POSTGATE = "app.bsky.feed.postgate"
|
7
8
|
BSKY_REPOST = "app.bsky.feed.repost"
|
8
9
|
BSKY_THREADGATE = "app.bsky.feed.threadgate"
|
9
10
|
BSKY_BLOCK = "app.bsky.graph.block"
|
data/lib/skyfall/operation.rb
CHANGED
@@ -52,6 +52,7 @@ module Skyfall
|
|
52
52
|
when Collection::BSKY_LISTBLOCK then :bsky_listblock
|
53
53
|
when Collection::BSKY_LISTITEM then :bsky_listitem
|
54
54
|
when Collection::BSKY_POST then :bsky_post
|
55
|
+
when Collection::BSKY_POSTGATE then :bsky_postgate
|
55
56
|
when Collection::BSKY_PROFILE then :bsky_profile
|
56
57
|
when Collection::BSKY_REPOST then :bsky_repost
|
57
58
|
when Collection::BSKY_STARTERPACK then :bsky_starterpack
|
data/lib/skyfall/stream.rb
CHANGED
@@ -14,11 +14,12 @@ module Skyfall
|
|
14
14
|
:subscribe_labels => SUBSCRIBE_LABELS
|
15
15
|
}
|
16
16
|
|
17
|
-
EVENTS = %w(message raw_message connecting connect disconnect reconnect error)
|
17
|
+
EVENTS = %w(message raw_message connecting connect disconnect reconnect error timeout)
|
18
18
|
|
19
19
|
MAX_RECONNECT_INTERVAL = 300
|
20
20
|
|
21
|
-
attr_accessor :
|
21
|
+
attr_accessor :cursor, :auto_reconnect, :last_update, :user_agent
|
22
|
+
attr_accessor :heartbeat_timeout, :heartbeat_interval, :check_heartbeat
|
22
23
|
|
23
24
|
def initialize(server, endpoint, cursor = nil)
|
24
25
|
@endpoint = check_endpoint(endpoint)
|
@@ -26,7 +27,12 @@ module Skyfall
|
|
26
27
|
@cursor = check_cursor(cursor)
|
27
28
|
@handlers = {}
|
28
29
|
@auto_reconnect = true
|
30
|
+
@check_heartbeat = false
|
29
31
|
@connection_attempts = 0
|
32
|
+
@heartbeat_interval = 10
|
33
|
+
@heartbeat_timeout = 300
|
34
|
+
@last_update = nil
|
35
|
+
@user_agent = default_user_agent
|
30
36
|
|
31
37
|
@handlers[:error] = proc { |e| puts "ERROR: #{e}" }
|
32
38
|
end
|
@@ -47,15 +53,18 @@ module Skyfall
|
|
47
53
|
@handlers[:error]&.call(e)
|
48
54
|
end
|
49
55
|
|
50
|
-
@ws = Faye::WebSocket::Client.new(url)
|
56
|
+
@ws = Faye::WebSocket::Client.new(url, nil, { headers: { 'User-Agent' => user_agent }})
|
51
57
|
|
52
58
|
@ws.on(:open) do |e|
|
53
59
|
@handlers[:connect]&.call
|
60
|
+
@last_update = Time.now
|
61
|
+
start_heartbeat_timer
|
54
62
|
end
|
55
63
|
|
56
64
|
@ws.on(:message) do |msg|
|
57
65
|
@reconnecting = false
|
58
66
|
@connection_attempts = 0
|
67
|
+
@last_update = Time.now
|
59
68
|
|
60
69
|
data = msg.data.pack('C*')
|
61
70
|
@handlers[:raw_message]&.call(data)
|
@@ -85,6 +94,7 @@ module Skyfall
|
|
85
94
|
connect
|
86
95
|
end
|
87
96
|
else
|
97
|
+
stop_heartbeat_timer
|
88
98
|
@engines_on = false
|
89
99
|
@handlers[:disconnect]&.call
|
90
100
|
EM.stop_event_loop unless @ws
|
@@ -110,6 +120,40 @@ module Skyfall
|
|
110
120
|
|
111
121
|
alias close disconnect
|
112
122
|
|
123
|
+
def default_user_agent
|
124
|
+
"Skyfall/#{Skyfall::VERSION}"
|
125
|
+
end
|
126
|
+
|
127
|
+
def check_heartbeat=(value)
|
128
|
+
@check_heartbeat = value
|
129
|
+
|
130
|
+
if @check_heartbeat && @engines_on && @ws && !@heartbeat_timer
|
131
|
+
start_heartbeat_timer
|
132
|
+
elsif !@check_heartbeat && @heartbeat_timer
|
133
|
+
stop_heartbeat_timer
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def start_heartbeat_timer
|
138
|
+
return if !@check_heartbeat || @heartbeat_interval.to_f <= 0 || @heartbeat_timeout.to_f <= 0
|
139
|
+
return if @heartbeat_timer
|
140
|
+
|
141
|
+
@heartbeat_timer = EM::PeriodicTimer.new(@heartbeat_interval) do
|
142
|
+
next if @ws.nil? || @heartbeat_timeout.to_f <= 0
|
143
|
+
time_passed = Time.now - @last_update
|
144
|
+
|
145
|
+
if time_passed > @heartbeat_timeout
|
146
|
+
@handlers[:timeout]&.call
|
147
|
+
reconnect
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def stop_heartbeat_timer
|
153
|
+
@heartbeat_timer&.cancel
|
154
|
+
@heartbeat_timer = nil
|
155
|
+
end
|
156
|
+
|
113
157
|
EVENTS.each do |event|
|
114
158
|
define_method "on_#{event}" do |&block|
|
115
159
|
@handlers[event.to_sym] = block
|
data/lib/skyfall/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skyfall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kuba Suder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base32
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- LICENSE.txt
|
115
115
|
- README.md
|
116
116
|
- example/block_tracker.rb
|
117
|
+
- example/follower_tracker.rb
|
117
118
|
- example/monitor_phrases.rb
|
118
119
|
- example/print_all_posts.rb
|
119
120
|
- example/push_notifications.rb
|