e621stream 0.0.0 → 0.0.2
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/README.md +10 -12
- data/lib/e621stream/client.rb +7 -12
- data/lib/e621stream/close_codes.rb +3 -1
- data/lib/e621stream/events.rb +10 -10
- data/lib/e621stream/version.rb +1 -1
- data/lib/e621stream.rb +1 -2
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d44d1f8f30f4f8d3931eb4a5b1f428413191a84297dbf9f879bef61307205b88
|
|
4
|
+
data.tar.gz: 7524d28a0ec987855de8e63c9c5efae8c2bd0bd0d3d07d846d03bcd16eaa8402
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6d4fae99737f6f2b1b57ffe083662ba32b0a64ca6bacf9bc048ced7afe8da4216d0ebe70515757667a1d6ed86fac452310acb2ef500170fea921f91f28ea690
|
|
7
|
+
data.tar.gz: d276a848290525a9ca77d3a5c432ca2d8baf17a34142b1d52f756d07688e69d608a6a47d9da4c3a33a7bed99e9bc6d932c5b2ad571ddca5fec3cd1ff843ffa7b
|
data/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# e621stream
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A client for [E621Stream](https://e621.stream), using [async-websocket](https://github.com/socketry/async-websocket) and typed with sorbet.
|
|
4
4
|
|
|
5
|
-
```
|
|
5
|
+
```sh
|
|
6
6
|
gem install e621stream
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
require
|
|
10
|
+
require("e621stream")
|
|
11
11
|
|
|
12
12
|
client = E621Stream::Client.new(
|
|
13
13
|
username: "your_name",
|
|
@@ -22,14 +22,12 @@ client.on(:error) { |error| warn(error.message) }
|
|
|
22
22
|
client.run
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
- **Running:** `run` blocks. Inside a reactor already, it runs as a task, so `client.close` from another task stops it.
|
|
27
|
-
- **Reconnecting:** the connection is reopened with backoff when it drops, and resumes from the last event it got. It isn't reopened after a close retrying won't fix, like bad credentials. `reconnect: false` turns it off.
|
|
28
|
-
- **Resuming:** pass `cursor:` (an event id, or a millisecond timestamp) to replay from there. `client.cursor` is the last event received.
|
|
29
|
-
- **Changing subscriptions:** `client.subscribe([...])`.
|
|
30
|
-
- **Private events:** like `dmail.create`, these are polled with your own credentials and have to be subscribed to by name.
|
|
31
|
-
- **Other events:** `:ready`, `:replay_complete`, `:subscribed`, `:reconnecting`, `:close` and `:error`.
|
|
25
|
+
`run` blocks, unless you're already in a reactor, then it runs as a task and `client.close` stops it.
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
`on("post.create")` listens for one type, `on(:dispatch)` for everything. Events have `id`, `type`, `time` and `data` (the record as the api returns it), see [Events](https://e621.stream/events/). There's also `:ready`, `:replay_complete`, `:subscribed`, `:reconnecting`, `:close` and `:error`.
|
|
28
|
+
|
|
29
|
+
It reconnects when the connection drops (`reconnect: false` turns that off), and picks up from `client.cursor`. Pass `cursor:` to replay from an event id or ms timestamp. `client.subscribe([...])` changes subscriptions. Private events like `dmail.create` have to be subscribed to by name.
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
bundle exec rake # rubocop, zeitwerk, tests & sorbet
|
|
35
33
|
```
|
data/lib/e621stream/client.rb
CHANGED
|
@@ -18,7 +18,7 @@ module E621Stream
|
|
|
18
18
|
# An event type, `<resource>.*`, `*`, or `{ "type" => ..., "filter" => { ... } }`.
|
|
19
19
|
Subscription = T.type_alias { T.any(String, T::Hash[T.any(String, Symbol), T.untyped]) }
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# Tests swap this out.
|
|
22
22
|
class Connector
|
|
23
23
|
extend(T::Sig)
|
|
24
24
|
|
|
@@ -30,7 +30,6 @@ module E621Stream
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
HEARTBEAT_SECONDS = 30
|
|
33
|
-
# The server answers heartbeats and pings, nothing for this long means the connection is gone without having closed.
|
|
34
33
|
SILENT_SECONDS = 75
|
|
35
34
|
MAX_DELAY_SECONDS = 60
|
|
36
35
|
|
|
@@ -89,9 +88,8 @@ module E621Stream
|
|
|
89
88
|
break unless reconnecting
|
|
90
89
|
|
|
91
90
|
@attempt += 1
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
delay = [base * (2**(@attempt - 1)), MAX_DELAY_SECONDS].min * rand(0.75..1.25)
|
|
91
|
+
delay = code == CloseCodes::RATE_LIMITED ? 30 : [@attempt**2, MAX_DELAY_SECONDS].min
|
|
92
|
+
delay += rand
|
|
95
93
|
emit("reconnecting", { attempt: @attempt, delay: delay })
|
|
96
94
|
sleep(delay)
|
|
97
95
|
end
|
|
@@ -114,10 +112,8 @@ module E621Stream
|
|
|
114
112
|
|
|
115
113
|
private
|
|
116
114
|
|
|
117
|
-
# resolves to the close code and reason
|
|
118
115
|
sig { returns([Integer, String]) }
|
|
119
116
|
def connect_once
|
|
120
|
-
result = T.let([1000, ""], [Integer, String])
|
|
121
117
|
@connector.connect(options.url) do |connection|
|
|
122
118
|
@connection = connection
|
|
123
119
|
@last_message = now
|
|
@@ -132,11 +128,11 @@ module E621Stream
|
|
|
132
128
|
@connection = nil
|
|
133
129
|
end
|
|
134
130
|
end
|
|
135
|
-
|
|
131
|
+
# anything but a normal close raises out of read
|
|
132
|
+
[1000, ""]
|
|
136
133
|
rescue Protocol::WebSocket::ClosedError => e
|
|
137
134
|
[e.code || 1006, e.message]
|
|
138
135
|
rescue StandardError => e
|
|
139
|
-
# closing tears the connection down under the read, which isn't worth reporting
|
|
140
136
|
return [1000, "closed by client"] if closing?
|
|
141
137
|
|
|
142
138
|
emit("error", e)
|
|
@@ -148,7 +144,7 @@ module E621Stream
|
|
|
148
144
|
loop do
|
|
149
145
|
sleep(HEARTBEAT_SECONDS)
|
|
150
146
|
if now - @last_message > SILENT_SECONDS
|
|
151
|
-
connection.close(
|
|
147
|
+
connection.close(1001, "timed out")
|
|
152
148
|
break
|
|
153
149
|
end
|
|
154
150
|
send_message({ op: "heartbeat" })
|
|
@@ -170,7 +166,6 @@ module E621Stream
|
|
|
170
166
|
emit("ready", data)
|
|
171
167
|
when "dispatch"
|
|
172
168
|
event = Event.new(id: message["id"], type: message["type"], time: message["time"], data: message["data"], private: message["private"] == true)
|
|
173
|
-
# private events aren't in the stream, so they can't be resumed from
|
|
174
169
|
@cursor = event.id unless event.private?
|
|
175
170
|
emit("dispatch", event)
|
|
176
171
|
emit(event.type, event)
|
|
@@ -197,7 +192,7 @@ module E621Stream
|
|
|
197
192
|
@listeners[name]&.each { |listener| listener.call(value) }
|
|
198
193
|
end
|
|
199
194
|
|
|
200
|
-
#
|
|
195
|
+
# #close changes this from another fiber
|
|
201
196
|
sig { returns(T::Boolean) }
|
|
202
197
|
def closing?
|
|
203
198
|
@closing
|
|
@@ -16,8 +16,10 @@ module E621Stream
|
|
|
16
16
|
SLOW_CONSUMER = 4009
|
|
17
17
|
TOO_MANY_CONNECTIONS = 4010
|
|
18
18
|
AUTHENTICATION_EXPIRED = 4011
|
|
19
|
+
DISCONNECTED = 4012
|
|
20
|
+
BANNED = 4013
|
|
19
21
|
|
|
20
22
|
# Closing for any of these again would just get the same result, so they aren't reconnected from.
|
|
21
|
-
FATAL = T.let([UNKNOWN_OP, DECODE_ERROR, AUTHENTICATION_FAILED, ALREADY_IDENTIFIED, INVALID_SUBSCRIPTION, INVALID_CURSOR, AUTHENTICATION_EXPIRED].freeze, T::Array[Integer])
|
|
23
|
+
FATAL = T.let([UNKNOWN_OP, DECODE_ERROR, AUTHENTICATION_FAILED, ALREADY_IDENTIFIED, INVALID_SUBSCRIPTION, INVALID_CURSOR, AUTHENTICATION_EXPIRED, DISCONNECTED, BANNED].freeze, T::Array[Integer])
|
|
22
24
|
end
|
|
23
25
|
end
|
data/lib/e621stream/events.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
module E621Stream
|
|
5
5
|
module Events
|
|
6
|
-
#
|
|
6
|
+
# Generated by scripts/clientTypes.ts in the E621Stream repository, from the server's src/events.ts.
|
|
7
7
|
EVENT_TYPES = %w[
|
|
8
8
|
artist.create artist.delete artist.update artist_url.create artist_version.create avoid_posting.create
|
|
9
9
|
avoid_posting.delete avoid_posting.destroy avoid_posting.undelete avoid_posting.update
|
|
@@ -12,18 +12,18 @@ module E621Stream
|
|
|
12
12
|
forum_post.create forum_post.delete forum_post.hide forum_post.unhide forum_post.update forum_topic.create
|
|
13
13
|
forum_topic.delete forum_topic.hide forum_topic.unhide forum_topic.update help_page.create help_page.update
|
|
14
14
|
mod_action.create news_update.create news_update.update note.create note.update note_version.create
|
|
15
|
-
pool.add_posts pool.create pool.delete pool.remove_posts pool.update pool_version.create post.create
|
|
16
|
-
post_approval.create post_event.create post_flag.create post_flag.update
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
wiki_page.delete wiki_page.update wiki_page_version.create
|
|
15
|
+
pool.add_posts pool.create pool.delete pool.remove_posts pool.update pool_version.create post.create
|
|
16
|
+
post.destroy post.stats post.update post_approval.create post_event.create post_flag.create post_flag.update
|
|
17
|
+
post_replacement.create post_replacement.destroy post_replacement.update post_set.add_posts post_set.create
|
|
18
|
+
post_set.delete post_set.remove_posts post_set.update post_version.create post_version.hide post_version.unhide
|
|
19
|
+
tag.create tag.delete tag.update tag_alias.create tag_alias.update tag_implication.create tag_implication.update
|
|
20
|
+
tag_type_version.create takedown.create takedown.delete takedown.update upload_karma_event.create user.create
|
|
21
|
+
user.delete user.update user_feedback.create user_feedback.delete user_feedback.destroy user_feedback.undelete
|
|
22
|
+
user_feedback.update wiki_page.create wiki_page.delete wiki_page.update wiki_page_version.create
|
|
23
23
|
].freeze
|
|
24
24
|
|
|
25
25
|
# Polled with your own credentials and only sent to you. They have to be subscribed to by name, `*` doesn't include
|
|
26
26
|
# them.
|
|
27
|
-
PRIVATE_EVENT_TYPES = %w[dmail.create].freeze
|
|
27
|
+
PRIVATE_EVENT_TYPES = %w[dmail.create private_post_set.add_posts private_post_set.remove_posts].freeze
|
|
28
28
|
end
|
|
29
29
|
end
|
data/lib/e621stream/version.rb
CHANGED
data/lib/e621stream.rb
CHANGED
|
@@ -4,8 +4,7 @@ require("json")
|
|
|
4
4
|
require("zeitwerk")
|
|
5
5
|
require("sorbet-runtime")
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
# which defines E621Stream before Zeitwerk::Loader#setup runs.
|
|
7
|
+
# required here since the gemspec loads version.rb before zeitwerk is set up
|
|
9
8
|
require_relative("e621stream/version")
|
|
10
9
|
|
|
11
10
|
loader = Zeitwerk::Loader.for_gem
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: e621stream
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Donovan_DMC
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: async-websocket
|
|
@@ -169,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
169
169
|
- !ruby/object:Gem::Version
|
|
170
170
|
version: '0'
|
|
171
171
|
requirements: []
|
|
172
|
-
rubygems_version: 3.6.
|
|
172
|
+
rubygems_version: 3.6.9
|
|
173
173
|
specification_version: 4
|
|
174
174
|
summary: A client for E621Stream, a live WebSocket stream of e621 activity.
|
|
175
175
|
test_files: []
|