cloudflare-ruby 0.0.1 → 0.2.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 +50 -0
- data/LICENSE +21 -0
- data/README.md +127 -3
- data/lib/cloudflare/configuration.rb +11 -0
- data/lib/cloudflare/connection.rb +52 -0
- data/lib/cloudflare/errors.rb +25 -0
- data/lib/cloudflare/realtime_kit/README.md +209 -0
- data/lib/cloudflare/realtime_kit/active_livestream_session.rb +46 -0
- data/lib/cloudflare/realtime_kit/active_session.rb +68 -0
- data/lib/cloudflare/realtime_kit/analytics.rb +62 -0
- data/lib/cloudflare/realtime_kit/app.rb +56 -0
- data/lib/cloudflare/realtime_kit/chat.rb +14 -0
- data/lib/cloudflare/realtime_kit/livestream.rb +27 -0
- data/lib/cloudflare/realtime_kit/livestream_session.rb +26 -0
- data/lib/cloudflare/realtime_kit/meeting.rb +82 -0
- data/lib/cloudflare/realtime_kit/participant.rb +40 -0
- data/lib/cloudflare/realtime_kit/preset.rb +31 -0
- data/lib/cloudflare/realtime_kit/recording.rb +85 -0
- data/lib/cloudflare/realtime_kit/session.rb +69 -0
- data/lib/cloudflare/realtime_kit/session_participant.rb +30 -0
- data/lib/cloudflare/realtime_kit/summary.rb +28 -0
- data/lib/cloudflare/realtime_kit/transcript.rb +17 -0
- data/lib/cloudflare/realtime_kit/webhook.rb +36 -0
- data/lib/cloudflare/realtime_kit.rb +40 -0
- data/lib/cloudflare/relation.rb +22 -0
- data/lib/cloudflare/resource.rb +333 -0
- data/lib/cloudflare/version.rb +3 -0
- data/lib/cloudflare-ruby.rb +1 -1
- data/lib/cloudflare.rb +34 -0
- data/sig/cloudflare/configuration.rbs +11 -0
- data/sig/cloudflare/connection.rbs +8 -0
- data/sig/cloudflare/errors.rbs +25 -0
- data/sig/cloudflare/realtime_kit.rbs +4 -0
- data/sig/cloudflare/relation.rbs +9 -0
- data/sig/cloudflare/resource.rbs +40 -0
- data/sig/cloudflare.rbs +11 -0
- data/spec/slices/realtime_kit.json +9627 -0
- metadata +70 -7
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# Analytics endpoints. Not a CRUD resource — a thin namespace for the
|
|
4
|
+
# two reporting endpoints upstream exposes. Both return raw report data
|
|
5
|
+
# as a Hash; no per-resource modeling.
|
|
6
|
+
#
|
|
7
|
+
# Cloudflare::RealtimeKit::Analytics.daywise(
|
|
8
|
+
# app_id: "app-1",
|
|
9
|
+
# start_date: "2025-01-01",
|
|
10
|
+
# end_date: "2025-01-31"
|
|
11
|
+
# )
|
|
12
|
+
#
|
|
13
|
+
# Cloudflare::RealtimeKit::Analytics.livestreams_overall(
|
|
14
|
+
# app_id: "app-1",
|
|
15
|
+
# start_time: "2025-01-01T00:00:00Z",
|
|
16
|
+
# end_time: "2025-01-31T23:59:59Z"
|
|
17
|
+
# )
|
|
18
|
+
module Analytics
|
|
19
|
+
class << self
|
|
20
|
+
# GET /analytics/daywise — daily breakdown of meeting activity.
|
|
21
|
+
def daywise(start_date:, end_date:, app_id: nil, account_id: nil)
|
|
22
|
+
fetch("/accounts/{account_id}/realtime/kit/{app_id}/analytics/daywise",
|
|
23
|
+
app_id: app_id, account_id: account_id,
|
|
24
|
+
params: { start_date: start_date, end_date: end_date })
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# GET /analytics/livestreams/overall — overall livestream metrics.
|
|
28
|
+
def livestreams_overall(start_time:, end_time:, app_id: nil, account_id: nil)
|
|
29
|
+
fetch("/accounts/{account_id}/realtime/kit/{app_id}/analytics/livestreams/overall",
|
|
30
|
+
app_id: app_id, account_id: account_id,
|
|
31
|
+
params: { start_time: start_time, end_time: end_time })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
def fetch(path_template, app_id:, account_id:, params:)
|
|
36
|
+
scope = build_scope(account_id: account_id, app_id: app_id)
|
|
37
|
+
path = interpolate(path_template, scope)
|
|
38
|
+
response = Connection.instance.request(:get, path, params: params)
|
|
39
|
+
Resource.unwrap_envelope(response)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Local copies of the two private helpers from Resource — Analytics
|
|
43
|
+
# isn't a Resource subclass (no instances, just a function namespace),
|
|
44
|
+
# so it can't inherit the private class methods. Falls back to
|
|
45
|
+
# +Cloudflare.account_id+ and +Cloudflare::RealtimeKit.app_id+ for
|
|
46
|
+
# the same per-process default behavior the Resource subclasses
|
|
47
|
+
# get for free.
|
|
48
|
+
def build_scope(account_id:, app_id:)
|
|
49
|
+
account = account_id || Cloudflare.account_id
|
|
50
|
+
app = app_id || RealtimeKit.app_id
|
|
51
|
+
raise ArgumentError, "missing required scope param: account_id" unless account
|
|
52
|
+
raise ArgumentError, "missing required scope param: app_id" unless app
|
|
53
|
+
{ account_id: account, app_id: app }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def interpolate(path, values)
|
|
57
|
+
path.gsub(/\{(\w+)\}/) { values[$1.to_sym] || raise(ArgumentError, "missing path param: #{$1}") }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# An app in your Cloudflare RealtimeKit account. Apps are the top-level
|
|
4
|
+
# tenant — meetings, sessions, recordings, presets, webhooks, livestreams,
|
|
5
|
+
# and analytics all live underneath an app.
|
|
6
|
+
#
|
|
7
|
+
# Apps are list+create only; the API doesn't expose a member endpoint
|
|
8
|
+
# (no GET /apps/{id}, no PATCH, no DELETE).
|
|
9
|
+
#
|
|
10
|
+
# Cloudflare.configure do |c|
|
|
11
|
+
# c.api_token = ENV["CLOUDFLARE_API_TOKEN"]
|
|
12
|
+
# c.account_id = ENV["CLOUDFLARE_ACCOUNT_ID"]
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# app = Cloudflare::RealtimeKit::App.create(name: "Production")
|
|
16
|
+
# app.id # => "abc123"
|
|
17
|
+
# app.name # => "Production"
|
|
18
|
+
# app.created_at # => Time
|
|
19
|
+
#
|
|
20
|
+
# Cloudflare::RealtimeKit::App.all
|
|
21
|
+
class App < Resource
|
|
22
|
+
collection_path "/accounts/{account_id}/realtime/kit/apps"
|
|
23
|
+
|
|
24
|
+
attribute :id, String
|
|
25
|
+
attribute :name, String
|
|
26
|
+
attribute :created_at, Time
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# POST /accounts/{account_id}/realtime/kit/apps
|
|
30
|
+
#
|
|
31
|
+
# The create response double-wraps the app payload as
|
|
32
|
+
# +{ data: { app: {...} } }+, so we peel both layers before
|
|
33
|
+
# constructing the instance. List responses use the standard
|
|
34
|
+
# +{ data: [...] }+ envelope and don't need this dance.
|
|
35
|
+
def create(name:, account_id: nil)
|
|
36
|
+
scope = build_scope(account_id: account_id)
|
|
37
|
+
response = Connection.instance.request(:post, interpolate(_collection_path, scope), body: { "name" => name })
|
|
38
|
+
new(unwrap_create_payload(response), scope: scope)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# GET /accounts/{account_id}/realtime/kit/apps
|
|
42
|
+
def all(account_id: nil)
|
|
43
|
+
scope = build_scope(account_id: account_id)
|
|
44
|
+
response = Connection.instance.request(:get, interpolate(_collection_path, scope))
|
|
45
|
+
Array(unwrap_envelope(response)).map { new(_1, scope: scope) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def unwrap_create_payload(response)
|
|
50
|
+
payload = unwrap_envelope(response)
|
|
51
|
+
payload.is_a?(Hash) && payload["app"].is_a?(Hash) ? payload["app"] : payload
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# The persisted chat log for a historical session. Read-only singleton.
|
|
4
|
+
# Reached via +session.chat+, which auto-loads on first access.
|
|
5
|
+
#
|
|
6
|
+
# session.chat.messages
|
|
7
|
+
class Chat < Resource
|
|
8
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/sessions/{session_id}/chat"
|
|
9
|
+
scope_required :app_id, :session_id
|
|
10
|
+
|
|
11
|
+
attribute :messages, Array
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A standalone livestream — a broadcast outlet that exists outside any
|
|
4
|
+
# particular meeting. (For per-meeting broadcasting, use
|
|
5
|
+
# +meeting.start_livestreaming+ instead.)
|
|
6
|
+
#
|
|
7
|
+
# livestream = Livestream.create(app_id: "app-1", name: "Conference")
|
|
8
|
+
# livestream.active_session # → ActiveLivestreamSession
|
|
9
|
+
class Livestream < Resource
|
|
10
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/livestreams"
|
|
11
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/livestreams/{id}"
|
|
12
|
+
scope_required :app_id
|
|
13
|
+
|
|
14
|
+
attribute :id, String
|
|
15
|
+
attribute :name, String
|
|
16
|
+
attribute :status, String
|
|
17
|
+
attribute :ingest_server, String
|
|
18
|
+
attribute :stream_key, String
|
|
19
|
+
attribute :playback_url, String
|
|
20
|
+
attribute :disabled, :boolean
|
|
21
|
+
attribute :created_at, Time
|
|
22
|
+
attribute :updated_at, Time
|
|
23
|
+
|
|
24
|
+
has_one :active_session, class_name: "Cloudflare::RealtimeKit::ActiveLivestreamSession"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A historical broadcast — a session-shaped record of one livestream
|
|
4
|
+
# invocation. Read-only.
|
|
5
|
+
#
|
|
6
|
+
# Two ways in:
|
|
7
|
+
# LivestreamSession.find("ls-1", app_id: "app-1")
|
|
8
|
+
# session.livestream_sessions.all # session-scoped list
|
|
9
|
+
class LivestreamSession < Resource
|
|
10
|
+
# Note: the upstream path uses +livestream-session-id+ (with hyphens).
|
|
11
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/livestreams/sessions/{id}"
|
|
12
|
+
scope_required :app_id
|
|
13
|
+
|
|
14
|
+
attribute :id, String
|
|
15
|
+
attribute :livestream_id, String
|
|
16
|
+
attribute :ingest_seconds, Integer
|
|
17
|
+
attribute :viewer_seconds, Integer
|
|
18
|
+
attribute :started_time, Time
|
|
19
|
+
attribute :stopped_time, Time
|
|
20
|
+
attribute :invoked_time, Time
|
|
21
|
+
attribute :err_message, String
|
|
22
|
+
attribute :created_at, Time
|
|
23
|
+
attribute :updated_at, Time
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A meeting room. Meetings live under an app and host one or more sessions
|
|
4
|
+
# when participants join. Use +has_many :participants+ for invitee
|
|
5
|
+
# management and +active_session+ for moderating the live session.
|
|
6
|
+
#
|
|
7
|
+
# == Lifecycle
|
|
8
|
+
#
|
|
9
|
+
# meeting = Meeting.create(app_id: "app-1", title: "Standup")
|
|
10
|
+
# meeting.update(title: "Daily standup")
|
|
11
|
+
#
|
|
12
|
+
# == Body fields (create / replace / update)
|
|
13
|
+
#
|
|
14
|
+
# +title+, +persist_chat+, +record_on_start+, +summarize_on_end+,
|
|
15
|
+
# +live_stream_on_start+, +session_keep_alive_time_in_secs+, +ai_config+,
|
|
16
|
+
# +recording_config+. Plus +status+ on update only.
|
|
17
|
+
#
|
|
18
|
+
# == Relations & actions
|
|
19
|
+
#
|
|
20
|
+
# meeting.participants # → Relation → Participant
|
|
21
|
+
# meeting.active_session # → ActiveSession (loaded)
|
|
22
|
+
# meeting.start_livestreaming(name:) # POST /meetings/{id}/livestreams
|
|
23
|
+
# meeting.stop_livestream # POST /meetings/{id}/active-livestream/stop
|
|
24
|
+
class Meeting < Resource
|
|
25
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/meetings"
|
|
26
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/meetings/{id}"
|
|
27
|
+
scope_required :app_id
|
|
28
|
+
|
|
29
|
+
attribute :id, String
|
|
30
|
+
attribute :title, String
|
|
31
|
+
attribute :status, String
|
|
32
|
+
attribute :created_at, Time
|
|
33
|
+
attribute :updated_at, Time
|
|
34
|
+
attribute :persist_chat, :boolean
|
|
35
|
+
attribute :record_on_start, :boolean
|
|
36
|
+
attribute :summarize_on_end, :boolean
|
|
37
|
+
attribute :live_stream_on_start, :boolean
|
|
38
|
+
attribute :session_keep_alive_time_in_secs, Integer
|
|
39
|
+
|
|
40
|
+
has_many :participants
|
|
41
|
+
has_one :active_session
|
|
42
|
+
|
|
43
|
+
# PUT /meetings/{id} — full replacement. Mirrors +update+ but uses PUT
|
|
44
|
+
# semantics, so omitted fields revert to their defaults upstream.
|
|
45
|
+
def replace(**attrs)
|
|
46
|
+
response = Connection.instance.request(:put, member_path, body: self.class.to_wire_keys(attrs))
|
|
47
|
+
set_attrs_from_response(response)
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# POST /meetings/{id}/livestreams — begin broadcasting this meeting.
|
|
52
|
+
def start_livestreaming(name: nil, video_config: nil)
|
|
53
|
+
Connection.instance.request(:post, "#{member_path}/livestreams", body: { name: name, video_config: video_config })
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# GET /meetings/{id}/active-livestream — fetch the currently-running
|
|
57
|
+
# livestream attached to this meeting. Returns a +Livestream+ scoped to
|
|
58
|
+
# the same app, so you can chain regular livestream operations on it
|
|
59
|
+
# (e.g., +meeting.active_livestream.active_session+).
|
|
60
|
+
def active_livestream
|
|
61
|
+
response = Connection.instance.request(:get, "#{member_path}/active-livestream")
|
|
62
|
+
Livestream.new(response, scope: { account_id: @scope[:account_id], app_id: @scope[:app_id] })
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# POST /meetings/{id}/active-livestream/stop
|
|
66
|
+
def stop_livestream
|
|
67
|
+
Connection.instance.request(:post, "#{member_path}/active-livestream/stop")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# GET /meetings/{id}/livestream — legacy "livestream-session-details"
|
|
71
|
+
# endpoint. Returns the meeting's livestream alongside a paginated list
|
|
72
|
+
# of its broadcast sessions: +{ "livestream" => {...}, "sessions" => [...] }+.
|
|
73
|
+
# Returned as a raw Hash because the response bundles two distinct
|
|
74
|
+
# shapes; the typed +Livestream+ + per-session lookup live elsewhere.
|
|
75
|
+
def livestream_details(page_no: nil, per_page: nil)
|
|
76
|
+
response = Connection.instance.request(:get, "#{member_path}/livestream",
|
|
77
|
+
params: { page_no: page_no, per_page: per_page })
|
|
78
|
+
self.class.unwrap_envelope(response)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A meeting participant — an invited user with a preset (host, speaker,
|
|
4
|
+
# viewer, etc.). Created in the context of a meeting via
|
|
5
|
+
# +meeting.participants.create(...)+.
|
|
6
|
+
#
|
|
7
|
+
# meeting.participants.create(
|
|
8
|
+
# custom_participant_id: "u-42",
|
|
9
|
+
# preset_name: "host_preset",
|
|
10
|
+
# name: "Alex",
|
|
11
|
+
# picture: "https://example.com/u/42.png"
|
|
12
|
+
# )
|
|
13
|
+
# participant.update(name: "Alexandra")
|
|
14
|
+
# participant.regenerate_token
|
|
15
|
+
# participant.destroy
|
|
16
|
+
class Participant < Resource
|
|
17
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/meetings/{meeting_id}/participants"
|
|
18
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/meetings/{meeting_id}/participants/{id}"
|
|
19
|
+
scope_required :app_id, :meeting_id
|
|
20
|
+
|
|
21
|
+
attribute :id, String
|
|
22
|
+
attribute :name, String
|
|
23
|
+
attribute :picture, String
|
|
24
|
+
attribute :preset_name, String
|
|
25
|
+
attribute :custom_participant_id, String
|
|
26
|
+
attribute :token, String
|
|
27
|
+
attribute :created_at, Time
|
|
28
|
+
attribute :updated_at, Time
|
|
29
|
+
|
|
30
|
+
# POST /meetings/{meeting_id}/participants/{id}/token
|
|
31
|
+
# Mints a fresh token for an existing participant (e.g., when their
|
|
32
|
+
# previous token expired or was leaked).
|
|
33
|
+
def regenerate_token
|
|
34
|
+
response = Connection.instance.request(:post, "#{member_path}/token")
|
|
35
|
+
set_attrs_from_response(response)
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A reusable participant configuration. Presets bundle UI options,
|
|
4
|
+
# capabilities (audio/video/chat permissions), and behavior — useful for
|
|
5
|
+
# setting up host vs viewer roles once and assigning by name when
|
|
6
|
+
# creating participants.
|
|
7
|
+
#
|
|
8
|
+
# preset = Preset.create(
|
|
9
|
+
# app_id: "app-1",
|
|
10
|
+
# name: "host_preset",
|
|
11
|
+
# config: { ... },
|
|
12
|
+
# ui: { ... },
|
|
13
|
+
# permissions: { ... }
|
|
14
|
+
# )
|
|
15
|
+
# preset.update(permissions: { ... })
|
|
16
|
+
# preset.destroy
|
|
17
|
+
class Preset < Resource
|
|
18
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/presets"
|
|
19
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/presets/{id}"
|
|
20
|
+
scope_required :app_id
|
|
21
|
+
|
|
22
|
+
attribute :id, String
|
|
23
|
+
attribute :name, String
|
|
24
|
+
attribute :config, Hash
|
|
25
|
+
attribute :ui, Hash
|
|
26
|
+
attribute :permissions, Hash
|
|
27
|
+
attribute :created_at, Time
|
|
28
|
+
attribute :updated_at, Time
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A meeting recording. The platform supports composite recordings
|
|
4
|
+
# (one MP4 of the whole meeting, via +Recording.create+) and per-track
|
|
5
|
+
# recordings (one file per participant audio/video track, via
|
|
6
|
+
# +Recording.start_track+).
|
|
7
|
+
#
|
|
8
|
+
# rec = Recording.create(meeting_id: "mtg-1", app_id: "app-1")
|
|
9
|
+
#
|
|
10
|
+
# # State transitions hit one upstream endpoint with a
|
|
11
|
+
# # body-discriminated +action+. The canonical form is +#transition+;
|
|
12
|
+
# # +#pause+, +#resume+, +#stop+ are 1-line forwarders.
|
|
13
|
+
# rec.transition(action: :pause)
|
|
14
|
+
# rec.pause
|
|
15
|
+
# rec.resume
|
|
16
|
+
# rec.stop
|
|
17
|
+
#
|
|
18
|
+
# Recording.active_for(meeting_id: "mtg-1", app_id: "app-1")
|
|
19
|
+
class Recording < Resource
|
|
20
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/recordings"
|
|
21
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/recordings/{id}"
|
|
22
|
+
scope_required :app_id
|
|
23
|
+
|
|
24
|
+
attribute :id, String
|
|
25
|
+
attribute :meeting_id, String
|
|
26
|
+
attribute :status, String
|
|
27
|
+
attribute :url, String
|
|
28
|
+
attribute :file_size, Integer
|
|
29
|
+
attribute :duration, Integer
|
|
30
|
+
attribute :created_at, Time
|
|
31
|
+
attribute :updated_at, Time
|
|
32
|
+
|
|
33
|
+
# Spec-enumerated values for #transition. Exposed so callers can
|
|
34
|
+
# iterate, validate, or build CLI flags from the canonical list.
|
|
35
|
+
TRANSITIONS = %i[pause resume stop].freeze
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
# POST /recordings/track — start a multi-file (per-track) recording.
|
|
39
|
+
# Different upstream endpoint than +create+; use this when you need
|
|
40
|
+
# one media file per participant track.
|
|
41
|
+
def start_track(meeting_id:, layers:, app_id: nil, account_id: nil, max_seconds: nil)
|
|
42
|
+
scope = build_scope(account_id: account_id, app_id: app_id)
|
|
43
|
+
path = interpolate("/accounts/{account_id}/realtime/kit/{app_id}/recordings/track", scope)
|
|
44
|
+
response = Connection.instance.request(:post, path,
|
|
45
|
+
body: { meeting_id: meeting_id, layers: layers, max_seconds: max_seconds })
|
|
46
|
+
new(response, scope: scope)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# GET /recordings/active-recording/{meeting_id} — fetch the recording
|
|
50
|
+
# currently in progress for a meeting (404 if none).
|
|
51
|
+
def active_for(meeting_id:, app_id: nil, account_id: nil)
|
|
52
|
+
scope = build_scope(account_id: account_id, app_id: app_id)
|
|
53
|
+
path = interpolate("/accounts/{account_id}/realtime/kit/{app_id}/recordings/active-recording/{meeting_id}",
|
|
54
|
+
scope.merge(meeting_id: meeting_id))
|
|
55
|
+
response = Connection.instance.request(:get, path)
|
|
56
|
+
new(response, scope: scope)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# PUT /recordings/{id} — body-discriminated transition. Canonical 1:1
|
|
61
|
+
# mapping to the upstream operation. Validates +action+ against
|
|
62
|
+
# TRANSITIONS so a typo fails at the call site, not in the upstream
|
|
63
|
+
# 422.
|
|
64
|
+
#
|
|
65
|
+
# recording.transition(action: :pause)
|
|
66
|
+
# recording.transition(action: "stop")
|
|
67
|
+
def transition(action:)
|
|
68
|
+
sym = action.to_sym
|
|
69
|
+
unless TRANSITIONS.include?(sym)
|
|
70
|
+
raise ArgumentError, "action must be one of #{TRANSITIONS.inspect}, got #{action.inspect}"
|
|
71
|
+
end
|
|
72
|
+
response = Connection.instance.request(:put, member_path, body: { action: sym.to_s })
|
|
73
|
+
set_attrs_from_response(response)
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Convenience verbs — each forwards to #transition with a fixed action.
|
|
78
|
+
# The wire mapping lives in #transition; these exist for readability at
|
|
79
|
+
# call sites.
|
|
80
|
+
def pause = transition(action: :pause)
|
|
81
|
+
def resume = transition(action: :resume)
|
|
82
|
+
def stop = transition(action: :stop)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A historical session — the record of what happened when participants
|
|
4
|
+
# joined a meeting. Sessions are read-only from the SDK; they're created
|
|
5
|
+
# automatically by the platform when a meeting goes live.
|
|
6
|
+
#
|
|
7
|
+
# session = Session.find("sess-1", app_id: "app-1")
|
|
8
|
+
# session.participants.all
|
|
9
|
+
# session.chat # → Chat (loaded)
|
|
10
|
+
# session.summary # → Summary (loaded)
|
|
11
|
+
# session.transcript # → Transcript (loaded)
|
|
12
|
+
#
|
|
13
|
+
# +Session.find_participant_by_peer_id+ does a reverse lookup from a
|
|
14
|
+
# peer-level identifier (e.g., what you get from a webhook payload) back
|
|
15
|
+
# to participant data, scoped only to the app — no session id needed.
|
|
16
|
+
class Session < Resource
|
|
17
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/sessions"
|
|
18
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/sessions/{id}"
|
|
19
|
+
scope_required :app_id
|
|
20
|
+
|
|
21
|
+
attribute :id, String
|
|
22
|
+
attribute :associated_id, String
|
|
23
|
+
attribute :type, String
|
|
24
|
+
attribute :status, String
|
|
25
|
+
attribute :meeting_display_name, String
|
|
26
|
+
attribute :organization_id, String
|
|
27
|
+
attribute :live_participants, Integer
|
|
28
|
+
attribute :max_concurrent_participants, Integer
|
|
29
|
+
attribute :minutes_consumed, Integer
|
|
30
|
+
attribute :started_at, Time
|
|
31
|
+
attribute :ended_at, Time
|
|
32
|
+
attribute :created_at, Time
|
|
33
|
+
attribute :updated_at, Time
|
|
34
|
+
|
|
35
|
+
has_many :participants, class_name: "Cloudflare::RealtimeKit::SessionParticipant"
|
|
36
|
+
has_one :chat
|
|
37
|
+
has_one :summary
|
|
38
|
+
has_one :transcript
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
# GET /sessions/peer-report/{peer_id} — reverse lookup from a peer id.
|
|
42
|
+
# Returns participant-shaped data, not a session, so we expose it as a
|
|
43
|
+
# SessionParticipant instance.
|
|
44
|
+
def find_participant_by_peer_id(peer_id, app_id: nil, account_id: nil, filters: nil)
|
|
45
|
+
scope = build_scope(account_id: account_id, app_id: app_id)
|
|
46
|
+
path = interpolate("/accounts/{account_id}/realtime/kit/{app_id}/sessions/peer-report/{peer_id}",
|
|
47
|
+
scope.merge(peer_id: peer_id))
|
|
48
|
+
response = Connection.instance.request(:get, path, params: { filters: filters })
|
|
49
|
+
SessionParticipant.new(response, scope: scope)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# GET /sessions/{session_id}/livestream-sessions — paginated list of
|
|
54
|
+
# broadcast sessions associated with this historical session. Returns
|
|
55
|
+
# a plain Array of LivestreamSession instances (not a Relation): the
|
|
56
|
+
# per-session collection lives at a different parent path than the
|
|
57
|
+
# global +/livestreams/sessions/{id}+ that LivestreamSession.find uses,
|
|
58
|
+
# so the two endpoints don't share scope and Relation can't model them
|
|
59
|
+
# together cleanly.
|
|
60
|
+
def livestream_sessions(page_no: nil, per_page: nil)
|
|
61
|
+
response = Connection.instance.request(:get, "#{member_path}/livestream-sessions",
|
|
62
|
+
params: { page_no: page_no, per_page: per_page })
|
|
63
|
+
Array(self.class.unwrap_envelope(response)).map do |item|
|
|
64
|
+
LivestreamSession.new(item, scope: { account_id: @scope[:account_id], app_id: @scope[:app_id] })
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# A participant as observed by a historical session. Distinct from
|
|
4
|
+
# +Participant+ (which is the meeting-level invitee record): a session
|
|
5
|
+
# participant carries timing data — when they joined, when they left,
|
|
6
|
+
# network/device events, etc.
|
|
7
|
+
#
|
|
8
|
+
# Read-only — the upstream spec exposes only GET endpoints for these.
|
|
9
|
+
# +session.participants.create+ raises NoMethodError before any HTTP
|
|
10
|
+
# call, citing the missing endpoint.
|
|
11
|
+
#
|
|
12
|
+
# Reached via +session.participants+ for the per-session list, or
|
|
13
|
+
# +Session.find_participant_by_peer_id(...)+ for a reverse lookup.
|
|
14
|
+
class SessionParticipant < Resource
|
|
15
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/sessions/{session_id}/participants"
|
|
16
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/sessions/{session_id}/participants/{id}"
|
|
17
|
+
scope_required :app_id, :session_id
|
|
18
|
+
|
|
19
|
+
attribute :id, String
|
|
20
|
+
attribute :name, String
|
|
21
|
+
attribute :picture, String
|
|
22
|
+
attribute :preset_name, String
|
|
23
|
+
attribute :custom_participant_id, String
|
|
24
|
+
attribute :joined_at, Time
|
|
25
|
+
attribute :left_at, Time
|
|
26
|
+
|
|
27
|
+
read_only
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# AI-generated summary of a historical session. Reached via
|
|
4
|
+
# +session.summary+, which auto-loads on first access.
|
|
5
|
+
#
|
|
6
|
+
# Use +#generate+ to (re)trigger summary generation when one isn't yet
|
|
7
|
+
# available — e.g., the meeting ended without +summarize_on_end+ set.
|
|
8
|
+
#
|
|
9
|
+
# session.summary.text
|
|
10
|
+
# session.summary.generate
|
|
11
|
+
class Summary < Resource
|
|
12
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/sessions/{session_id}/summary"
|
|
13
|
+
scope_required :app_id, :session_id
|
|
14
|
+
|
|
15
|
+
attribute :text, String
|
|
16
|
+
attribute :status, String
|
|
17
|
+
attribute :created_at, Time
|
|
18
|
+
attribute :updated_at, Time
|
|
19
|
+
|
|
20
|
+
# POST /sessions/{session_id}/summary — generate a summary on demand.
|
|
21
|
+
def generate
|
|
22
|
+
response = Connection.instance.request(:post, member_path)
|
|
23
|
+
set_attrs_from_response(response)
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# Speech-to-text transcript of a historical session. Read-only singleton.
|
|
4
|
+
# Reached via +session.transcript+.
|
|
5
|
+
#
|
|
6
|
+
# session.transcript.segments
|
|
7
|
+
class Transcript < Resource
|
|
8
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/sessions/{session_id}/transcript"
|
|
9
|
+
scope_required :app_id, :session_id
|
|
10
|
+
|
|
11
|
+
attribute :segments, Array
|
|
12
|
+
attribute :status, String
|
|
13
|
+
attribute :created_at, Time
|
|
14
|
+
attribute :updated_at, Time
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
module RealtimeKit
|
|
3
|
+
# An HTTP endpoint that receives platform events. Each webhook subscribes
|
|
4
|
+
# to a list of event types and is invoked when matching events fire.
|
|
5
|
+
#
|
|
6
|
+
# Webhook.create(
|
|
7
|
+
# app_id: "app-1",
|
|
8
|
+
# name: "Recording finished",
|
|
9
|
+
# url: "https://example.com/hooks/realtimekit",
|
|
10
|
+
# events: [ "recording.finished", "session.ended" ]
|
|
11
|
+
# )
|
|
12
|
+
# webhook.update(enabled: false)
|
|
13
|
+
# webhook.replace(name: "...", url: "...", events: [ ... ]) # PUT
|
|
14
|
+
# webhook.destroy
|
|
15
|
+
class Webhook < Resource
|
|
16
|
+
collection_path "/accounts/{account_id}/realtime/kit/{app_id}/webhooks"
|
|
17
|
+
member_path "/accounts/{account_id}/realtime/kit/{app_id}/webhooks/{id}"
|
|
18
|
+
scope_required :app_id
|
|
19
|
+
|
|
20
|
+
attribute :id, String
|
|
21
|
+
attribute :name, String
|
|
22
|
+
attribute :url, String
|
|
23
|
+
attribute :events, Array
|
|
24
|
+
attribute :enabled, :boolean
|
|
25
|
+
attribute :created_at, Time
|
|
26
|
+
attribute :updated_at, Time
|
|
27
|
+
|
|
28
|
+
# PUT /webhooks/{id} — replace every field (vs PATCH +update+).
|
|
29
|
+
def replace(**attrs)
|
|
30
|
+
response = Connection.instance.request(:put, member_path, body: self.class.to_wire_keys(attrs))
|
|
31
|
+
set_attrs_from_response(response)
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Cloudflare
|
|
2
|
+
# Cloudflare RealtimeKit (formerly Dyte) — managed video/audio infrastructure
|
|
3
|
+
# for meetings, livestreams, recordings, sessions, and analytics. Each
|
|
4
|
+
# resource maps to a hand-written class under this namespace.
|
|
5
|
+
#
|
|
6
|
+
# == Default app_id
|
|
7
|
+
#
|
|
8
|
+
# Most resources (Meeting, Participant, Recording, ...) live under an app.
|
|
9
|
+
# Set +app_id+ once at process boot and every call defaults to it; pass
|
|
10
|
+
# +app_id:+ per-call only to override.
|
|
11
|
+
#
|
|
12
|
+
# Cloudflare::RealtimeKit.app_id = ENV["REALTIMEKIT_APP_ID"]
|
|
13
|
+
#
|
|
14
|
+
# Cloudflare::RealtimeKit::Meeting.create(title: "Standup") # uses default
|
|
15
|
+
# Cloudflare::RealtimeKit::Meeting.find(id, app_id: "other-app-id") # override
|
|
16
|
+
module RealtimeKit
|
|
17
|
+
class << self
|
|
18
|
+
# Default app_id used when a resource call doesn't pass one explicitly.
|
|
19
|
+
# Mirrors how +Cloudflare.account_id+ defaults the account scope.
|
|
20
|
+
attr_accessor :app_id
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
autoload :App, "cloudflare/realtime_kit/app"
|
|
24
|
+
autoload :Meeting, "cloudflare/realtime_kit/meeting"
|
|
25
|
+
autoload :Participant, "cloudflare/realtime_kit/participant"
|
|
26
|
+
autoload :ActiveSession, "cloudflare/realtime_kit/active_session"
|
|
27
|
+
autoload :Session, "cloudflare/realtime_kit/session"
|
|
28
|
+
autoload :SessionParticipant, "cloudflare/realtime_kit/session_participant"
|
|
29
|
+
autoload :Chat, "cloudflare/realtime_kit/chat"
|
|
30
|
+
autoload :Summary, "cloudflare/realtime_kit/summary"
|
|
31
|
+
autoload :Transcript, "cloudflare/realtime_kit/transcript"
|
|
32
|
+
autoload :Recording, "cloudflare/realtime_kit/recording"
|
|
33
|
+
autoload :Preset, "cloudflare/realtime_kit/preset"
|
|
34
|
+
autoload :Webhook, "cloudflare/realtime_kit/webhook"
|
|
35
|
+
autoload :Livestream, "cloudflare/realtime_kit/livestream"
|
|
36
|
+
autoload :LivestreamSession, "cloudflare/realtime_kit/livestream_session"
|
|
37
|
+
autoload :ActiveLivestreamSession, "cloudflare/realtime_kit/active_livestream_session"
|
|
38
|
+
autoload :Analytics, "cloudflare/realtime_kit/analytics"
|
|
39
|
+
end
|
|
40
|
+
end
|