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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/LICENSE +21 -0
  4. data/README.md +127 -3
  5. data/lib/cloudflare/configuration.rb +11 -0
  6. data/lib/cloudflare/connection.rb +52 -0
  7. data/lib/cloudflare/errors.rb +25 -0
  8. data/lib/cloudflare/realtime_kit/README.md +209 -0
  9. data/lib/cloudflare/realtime_kit/active_livestream_session.rb +46 -0
  10. data/lib/cloudflare/realtime_kit/active_session.rb +68 -0
  11. data/lib/cloudflare/realtime_kit/analytics.rb +62 -0
  12. data/lib/cloudflare/realtime_kit/app.rb +56 -0
  13. data/lib/cloudflare/realtime_kit/chat.rb +14 -0
  14. data/lib/cloudflare/realtime_kit/livestream.rb +27 -0
  15. data/lib/cloudflare/realtime_kit/livestream_session.rb +26 -0
  16. data/lib/cloudflare/realtime_kit/meeting.rb +82 -0
  17. data/lib/cloudflare/realtime_kit/participant.rb +40 -0
  18. data/lib/cloudflare/realtime_kit/preset.rb +31 -0
  19. data/lib/cloudflare/realtime_kit/recording.rb +85 -0
  20. data/lib/cloudflare/realtime_kit/session.rb +69 -0
  21. data/lib/cloudflare/realtime_kit/session_participant.rb +30 -0
  22. data/lib/cloudflare/realtime_kit/summary.rb +28 -0
  23. data/lib/cloudflare/realtime_kit/transcript.rb +17 -0
  24. data/lib/cloudflare/realtime_kit/webhook.rb +36 -0
  25. data/lib/cloudflare/realtime_kit.rb +40 -0
  26. data/lib/cloudflare/relation.rb +22 -0
  27. data/lib/cloudflare/resource.rb +333 -0
  28. data/lib/cloudflare/version.rb +3 -0
  29. data/lib/cloudflare-ruby.rb +1 -1
  30. data/lib/cloudflare.rb +34 -0
  31. data/sig/cloudflare/configuration.rbs +11 -0
  32. data/sig/cloudflare/connection.rbs +8 -0
  33. data/sig/cloudflare/errors.rbs +25 -0
  34. data/sig/cloudflare/realtime_kit.rbs +4 -0
  35. data/sig/cloudflare/relation.rbs +9 -0
  36. data/sig/cloudflare/resource.rbs +40 -0
  37. data/sig/cloudflare.rbs +11 -0
  38. data/spec/slices/realtime_kit.json +9627 -0
  39. 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