twitchrb 1.11.0 → 2.1.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 +97 -0
- data/LICENSE.txt +1 -1
- data/README.md +373 -46
- data/lib/twitch/client.rb +109 -20
- data/lib/twitch/collection.rb +54 -8
- data/lib/twitch/eventsub_webhook.rb +140 -0
- data/lib/twitch/oauth.rb +51 -14
- data/lib/twitch/object.rb +93 -14
- data/lib/twitch/objects/ad_schedule.rb +4 -0
- data/lib/twitch/objects/analytics_report.rb +4 -0
- data/lib/twitch/objects/bits_leaderboard_entry.rb +4 -0
- data/lib/twitch/objects/charity_donation.rb +4 -0
- data/lib/twitch/objects/chat_settings.rb +4 -0
- data/lib/twitch/objects/cheermote.rb +4 -0
- data/lib/twitch/objects/content_classification_label.rb +4 -0
- data/lib/twitch/objects/drops_entitlement.rb +4 -0
- data/lib/twitch/objects/drops_entitlement_update.rb +4 -0
- data/lib/twitch/objects/extension.rb +4 -0
- data/lib/twitch/objects/extension_bits_product.rb +4 -0
- data/lib/twitch/objects/extension_configuration_segment.rb +4 -0
- data/lib/twitch/objects/extension_live_channel.rb +4 -0
- data/lib/twitch/objects/extension_secret.rb +4 -0
- data/lib/twitch/objects/extension_transaction.rb +4 -0
- data/lib/twitch/objects/guest_star_invite.rb +4 -0
- data/lib/twitch/objects/guest_star_session.rb +4 -0
- data/lib/twitch/objects/guest_star_settings.rb +4 -0
- data/lib/twitch/objects/shield_mode_status.rb +4 -0
- data/lib/twitch/objects/{tag.rb → team.rb} +1 -1
- data/lib/twitch/objects/user_active_extensions.rb +4 -0
- data/lib/twitch/objects/user_extension.rb +4 -0
- data/lib/twitch/resource.rb +29 -0
- data/lib/twitch/resources/ads.rb +18 -0
- data/lib/twitch/resources/analytics.rb +19 -0
- data/lib/twitch/resources/announcements.rb +1 -1
- data/lib/twitch/resources/automod.rb +7 -7
- data/lib/twitch/resources/badges.rb +2 -2
- data/lib/twitch/resources/banned_users.rb +3 -3
- data/lib/twitch/resources/bits.rb +16 -0
- data/lib/twitch/resources/blocked_terms.rb +3 -3
- data/lib/twitch/resources/channels.rb +4 -4
- data/lib/twitch/resources/charity_campaigns.rb +8 -0
- data/lib/twitch/resources/chat_settings.rb +20 -0
- data/lib/twitch/resources/chatters.rb +1 -1
- data/lib/twitch/resources/clips.rb +3 -9
- data/lib/twitch/resources/content_classification_labels.rb +8 -0
- data/lib/twitch/resources/custom_power_ups.rb +1 -1
- data/lib/twitch/resources/custom_reward_redemptions.rb +3 -4
- data/lib/twitch/resources/custom_rewards.rb +5 -5
- data/lib/twitch/resources/drops_entitlements.rb +17 -0
- data/lib/twitch/resources/emotes.rb +3 -3
- data/lib/twitch/resources/eventsub_conduits.rb +14 -4
- data/lib/twitch/resources/eventsub_subscriptions.rb +1 -1
- data/lib/twitch/resources/extensions.rb +99 -0
- data/lib/twitch/resources/games.rb +3 -3
- data/lib/twitch/resources/goals.rb +1 -1
- data/lib/twitch/resources/guest_star.rb +102 -0
- data/lib/twitch/resources/moderators.rb +3 -3
- data/lib/twitch/resources/pinned_chat_messages.rb +4 -11
- data/lib/twitch/resources/polls.rb +1 -1
- data/lib/twitch/resources/predictions.rb +1 -1
- data/lib/twitch/resources/raids.rb +2 -3
- data/lib/twitch/resources/search.rb +2 -2
- data/lib/twitch/resources/shield_mode.rb +18 -0
- data/lib/twitch/resources/shoutouts.rb +1 -1
- data/lib/twitch/resources/stream_markers.rb +1 -1
- data/lib/twitch/resources/stream_schedule.rb +4 -5
- data/lib/twitch/resources/streams.rb +2 -2
- data/lib/twitch/resources/subscriptions.rb +13 -2
- data/lib/twitch/resources/suspicious_users.rb +1 -1
- data/lib/twitch/resources/teams.rb +20 -0
- data/lib/twitch/resources/unban_requests.rb +2 -2
- data/lib/twitch/resources/users.rb +30 -34
- data/lib/twitch/resources/videos.rb +2 -2
- data/lib/twitch/resources/vips.rb +2 -2
- data/lib/twitch/resources/warnings.rb +2 -2
- data/lib/twitch/resources/whispers.rb +1 -1
- data/lib/twitch/version.rb +1 -1
- data/lib/twitch.rb +33 -10
- metadata +42 -46
- data/.env.example +0 -3
- data/.github/FUNDING.yml +0 -5
- data/.github/dependabot.yml +0 -11
- data/.github/workflows/ci.yml +0 -39
- data/.gitignore +0 -9
- data/.rubocop.yml +0 -16
- data/Gemfile +0 -13
- data/Gemfile.lock +0 -144
- data/Rakefile +0 -10
- data/bin/console +0 -23
- data/bin/setup +0 -8
- data/bin/smoke +0 -118
- data/lib/twitch/objects/banned_event.rb +0 -4
- data/lib/twitch/objects/followed_user.rb +0 -4
- data/lib/twitch/objects/hype_train_event.rb +0 -4
- data/lib/twitch/objects/moderator_event.rb +0 -4
- data/lib/twitch/resources/banned_events.rb +0 -8
- data/lib/twitch/resources/hype_train_events.rb +0 -10
- data/lib/twitch/resources/moderator_events.rb +0 -9
- data/lib/twitch/resources/tags.rb +0 -18
- data/mise.toml +0 -2
- data/twitchrb.gemspec +0 -29
data/lib/twitch/resource.rb
CHANGED
|
@@ -33,9 +33,38 @@ module Twitch
|
|
|
33
33
|
execute_request { client.connection.delete(url, params, headers) }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Builds a Collection that can fetch its following pages by repeating the
|
|
37
|
+
# original GET request with the cursor as the after param
|
|
38
|
+
def collection(response, type:)
|
|
39
|
+
Collection.from_response(response, type: type, next_page: next_page_fetcher(response, type))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def next_page_fetcher(response, type)
|
|
43
|
+
return unless response.respond_to?(:env) && response.env.method == :get
|
|
44
|
+
|
|
45
|
+
url = response.env.url
|
|
46
|
+
path = url.path.delete_prefix("#{URI(Client::BASE_URL).path}/")
|
|
47
|
+
params = Faraday::FlatParamsEncoder.decode(url.query) || {}
|
|
48
|
+
params.delete("before")
|
|
49
|
+
|
|
50
|
+
->(cursor) { collection(get_request(path, params: params.merge("after" => cursor)), type: type) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Builds a path with a query string, for write endpoints that take some parameters
|
|
54
|
+
# in the query string rather than the body. nil values are dropped.
|
|
55
|
+
def query_path(path, params)
|
|
56
|
+
"#{path}?#{URI.encode_www_form(params.compact)}"
|
|
57
|
+
end
|
|
58
|
+
|
|
36
59
|
def execute_request
|
|
60
|
+
access_token = client.access_token
|
|
37
61
|
response = yield
|
|
38
62
|
|
|
63
|
+
# Retry once with a refreshed token if the access token has expired
|
|
64
|
+
if response.status == 401 && client.refresh_after_unauthorized(response, access_token)
|
|
65
|
+
response = yield
|
|
66
|
+
end
|
|
67
|
+
|
|
39
68
|
# Retry once after a 429, waiting until the rate limit window resets
|
|
40
69
|
if response.status == 429 && client.auto_retry_rate_limit
|
|
41
70
|
update_rate_limit(response)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Twitch
|
|
2
|
+
class AdsResource < Resource
|
|
3
|
+
# Required scope: channel:read:ads
|
|
4
|
+
# broadcaster_id must match the user in the OAuth token
|
|
5
|
+
def schedule(broadcaster_id:)
|
|
6
|
+
response = get_request("channels/ads", params: { broadcaster_id: broadcaster_id })
|
|
7
|
+
AdSchedule.new(response.body.dig("data")[0])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Pushes back the next scheduled ad by 5 minutes
|
|
11
|
+
# Required scope: channel:manage:ads
|
|
12
|
+
# broadcaster_id must match the user in the OAuth token
|
|
13
|
+
def snooze(broadcaster_id:)
|
|
14
|
+
response = post_request(query_path("channels/ads/schedule/snooze", broadcaster_id: broadcaster_id), body: {})
|
|
15
|
+
AdSchedule.new(response.body.dig("data")[0])
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Twitch
|
|
2
|
+
class AnalyticsResource < Resource
|
|
3
|
+
# Gets URLs for downloadable CSV reports about the user's extensions
|
|
4
|
+
# Required scope: analytics:read:extensions
|
|
5
|
+
# Available parameters: extension_id, type, started_at, ended_at, first, after
|
|
6
|
+
def extensions(**params)
|
|
7
|
+
response = get_request("analytics/extensions", params: params)
|
|
8
|
+
collection(response, type: AnalyticsReport)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Gets URLs for downloadable CSV reports about the user's games
|
|
12
|
+
# Required scope: analytics:read:games
|
|
13
|
+
# Available parameters: game_id, type, started_at, ended_at, first, after
|
|
14
|
+
def games(**params)
|
|
15
|
+
response = get_request("analytics/games", params: params)
|
|
16
|
+
collection(response, type: AnalyticsReport)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -5,7 +5,7 @@ module Twitch
|
|
|
5
5
|
def create(broadcaster_id:, moderator_id:, message:, color: nil, for_source_only: nil)
|
|
6
6
|
attrs = { message: message, color: color, for_source_only: for_source_only }
|
|
7
7
|
|
|
8
|
-
post_request("chat/announcements
|
|
8
|
+
post_request(query_path("chat/announcements", broadcaster_id: broadcaster_id, moderator_id: moderator_id), body: attrs)
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
end
|
|
@@ -3,17 +3,17 @@ module Twitch
|
|
|
3
3
|
# Checks if a supplied message meets the channel's AutoMod requirements
|
|
4
4
|
# Required scope: moderation:read
|
|
5
5
|
def check_status(broadcaster_id:, id:, text:)
|
|
6
|
-
attrs = {
|
|
7
|
-
response = post_request("moderation/enforcements/status", body: attrs)
|
|
6
|
+
attrs = { data: [ { msg_id: id, msg_text: text } ] }
|
|
7
|
+
response = post_request(query_path("moderation/enforcements/status", broadcaster_id: broadcaster_id), body: attrs)
|
|
8
8
|
AutomodStatus.new response.body.dig("data")[0]
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
# Checks if multiple supplied messages meet the channel's AutoMod requirements
|
|
12
12
|
# Required scope: moderation:read
|
|
13
13
|
def check_status_multiple(broadcaster_id:, messages:)
|
|
14
|
-
attrs = {
|
|
15
|
-
response = post_request("moderation/enforcements/status", body: attrs)
|
|
16
|
-
|
|
14
|
+
attrs = { data: messages }
|
|
15
|
+
response = post_request(query_path("moderation/enforcements/status", broadcaster_id: broadcaster_id), body: attrs)
|
|
16
|
+
collection(response, type: AutomodStatus)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def manage_message(user_id:, msg_id:, action:)
|
|
@@ -27,8 +27,8 @@ module Twitch
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def update_settings(broadcaster_id:, moderator_id:, **params)
|
|
30
|
-
|
|
31
|
-
response = put_request(
|
|
30
|
+
path = query_path("moderation/automod/settings", broadcaster_id: broadcaster_id, moderator_id: moderator_id)
|
|
31
|
+
response = put_request(path, body: params)
|
|
32
32
|
AutomodSetting.new response.body.dig("data")[0]
|
|
33
33
|
end
|
|
34
34
|
end
|
|
@@ -2,12 +2,12 @@ module Twitch
|
|
|
2
2
|
class BadgesResource < Resource
|
|
3
3
|
def channel(broadcaster_id:)
|
|
4
4
|
response = get_request("chat/badges", params: { broadcaster_id: broadcaster_id })
|
|
5
|
-
|
|
5
|
+
collection(response, type: Badge)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def global
|
|
9
9
|
response = get_request("chat/badges/global")
|
|
10
|
-
|
|
10
|
+
collection(response, type: Badge)
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -3,14 +3,14 @@ module Twitch
|
|
|
3
3
|
# Broadcaster ID must match the user in the OAuth token
|
|
4
4
|
def list(broadcaster_id:, **params)
|
|
5
5
|
response = get_request("moderation/banned", params: params.merge(broadcaster_id: broadcaster_id))
|
|
6
|
-
|
|
6
|
+
collection(response, type: BannedUser)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
# Required scope: moderator:manage:banned_users
|
|
10
10
|
# moderator_id must match the currently authenticated user. Can be either the broadcaster ID or moderator ID
|
|
11
11
|
def create(broadcaster_id:, moderator_id:, user_id:, reason:, duration: nil)
|
|
12
|
-
|
|
13
|
-
response = post_request(
|
|
12
|
+
path = query_path("moderation/bans", broadcaster_id: broadcaster_id, moderator_id: moderator_id)
|
|
13
|
+
response = post_request(path, body: { data: { user_id: user_id, reason: reason, duration: duration }.compact })
|
|
14
14
|
BannedUser.new response.body.dig("data")[0]
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Twitch
|
|
2
|
+
class BitsResource < Resource
|
|
3
|
+
# Required scope: bits:read
|
|
4
|
+
# Available parameters: count, period, started_at, user_id
|
|
5
|
+
def leaderboard(**params)
|
|
6
|
+
response = get_request("bits/leaderboard", params: params)
|
|
7
|
+
collection(response, type: BitsLeaderboardEntry)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Pass broadcaster_id to include the broadcaster's custom Cheermotes
|
|
11
|
+
def cheermotes(broadcaster_id: nil)
|
|
12
|
+
response = get_request("bits/cheermotes", params: { broadcaster_id: broadcaster_id }.compact)
|
|
13
|
+
collection(response, type: Cheermote)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -5,14 +5,14 @@ module Twitch
|
|
|
5
5
|
def list(broadcaster_id:, moderator_id:, **params)
|
|
6
6
|
attrs = { broadcaster_id: broadcaster_id, moderator_id: moderator_id }
|
|
7
7
|
response = get_request("moderation/blocked_terms", params: attrs.merge(params))
|
|
8
|
-
|
|
8
|
+
collection(response, type: BlockedTerm)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
# Required scope: moderator:manage:blocked_terms
|
|
12
12
|
# moderator_id must match the currently authenticated user. Can be either the broadcaster ID or moderator ID
|
|
13
13
|
def create(broadcaster_id:, moderator_id:, text:)
|
|
14
|
-
|
|
15
|
-
response = post_request(
|
|
14
|
+
path = query_path("moderation/blocked_terms", broadcaster_id: broadcaster_id, moderator_id: moderator_id)
|
|
15
|
+
response = post_request(path, body: { text: text })
|
|
16
16
|
BlockedTerm.new response.body.dig("data")[0]
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -9,7 +9,7 @@ module Twitch
|
|
|
9
9
|
# user_id must match the authenticated user
|
|
10
10
|
def followed(user_id:, **params)
|
|
11
11
|
response = get_request("channels/followed", params: params.merge(user_id: user_id))
|
|
12
|
-
|
|
12
|
+
collection(response, type: User)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# Retrieve a list of users that follow a specified broadcaster
|
|
@@ -17,7 +17,7 @@ module Twitch
|
|
|
17
17
|
# broadcaster_id must match the authenticated user
|
|
18
18
|
def followers(broadcaster_id:, **params)
|
|
19
19
|
response = get_request("channels/followers", params: params.merge(broadcaster_id: broadcaster_id))
|
|
20
|
-
|
|
20
|
+
collection(response, type: User)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# Grabs the number of Followers a broadcaster has
|
|
@@ -37,13 +37,13 @@ module Twitch
|
|
|
37
37
|
|
|
38
38
|
# Requires scope: channel:manage:broadcast
|
|
39
39
|
def update(broadcaster_id:, **attributes)
|
|
40
|
-
patch_request("channels",
|
|
40
|
+
patch_request(query_path("channels", broadcaster_id: broadcaster_id), body: attributes)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# Requires scope: channel:read:editors
|
|
44
44
|
def editors(broadcaster_id:)
|
|
45
45
|
response = get_request("channels/editors", params: { broadcaster_id: broadcaster_id })
|
|
46
|
-
|
|
46
|
+
collection(response, type: ChannelEditor)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def stream_key(broadcaster_id:)
|
|
@@ -10,5 +10,13 @@ module Twitch
|
|
|
10
10
|
nil
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
# Required scope: channel:read:charity
|
|
15
|
+
# Broadcaster ID must match the user in the OAuth token
|
|
16
|
+
# Available parameters: first, after
|
|
17
|
+
def donations(broadcaster_id:, **params)
|
|
18
|
+
response = get_request("charity/donations", params: params.merge(broadcaster_id: broadcaster_id))
|
|
19
|
+
collection(response, type: CharityDonation)
|
|
20
|
+
end
|
|
13
21
|
end
|
|
14
22
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Twitch
|
|
2
|
+
class ChatSettingsResource < Resource
|
|
3
|
+
# moderator_id is only needed to include non_moderator_chat_delay settings,
|
|
4
|
+
# and must match the user in the OAuth token
|
|
5
|
+
def retrieve(broadcaster_id:, moderator_id: nil)
|
|
6
|
+
response = get_request("chat/settings", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id }.compact)
|
|
7
|
+
ChatSettings.new(response.body.dig("data")[0])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Required scope: moderator:manage:chat_settings
|
|
11
|
+
# moderator_id must match the user in the OAuth token
|
|
12
|
+
# Available attributes: emote_mode, follower_mode, follower_mode_duration, non_moderator_chat_delay,
|
|
13
|
+
# non_moderator_chat_delay_duration, slow_mode, slow_mode_wait_time, subscriber_mode, unique_chat_mode
|
|
14
|
+
def update(broadcaster_id:, moderator_id:, **attributes)
|
|
15
|
+
path = query_path("chat/settings", broadcaster_id: broadcaster_id, moderator_id: moderator_id)
|
|
16
|
+
response = patch_request(path, body: attributes)
|
|
17
|
+
ChatSettings.new(response.body.dig("data")[0])
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -7,7 +7,7 @@ module Twitch
|
|
|
7
7
|
attrs = { broadcaster_id: broadcaster_id, moderator_id: moderator_id }
|
|
8
8
|
response = get_request("chat/chatters", params: attrs.merge(params))
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
collection(response, type: Chatter)
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module Twitch
|
|
2
2
|
class ClipsResource < Resource
|
|
3
3
|
def list(**params)
|
|
4
|
-
raise "broadcaster_id or
|
|
4
|
+
raise ArgumentError, "broadcaster_id, game_id or id is required" if params.values_at(:broadcaster_id, :game_id, :id).all?(&:nil?)
|
|
5
5
|
|
|
6
6
|
response = get_request("clips", params: params)
|
|
7
|
-
|
|
7
|
+
collection(response, type: Clip)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def retrieve(id:)
|
|
@@ -37,13 +37,7 @@ module Twitch
|
|
|
37
37
|
ids = clip_ids || Array(clip_id)
|
|
38
38
|
response = get_request("clips/downloads", params: { editor_id: editor_id, broadcaster_id: broadcaster_id, clip_id: ids })
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def query_path(path, params)
|
|
46
|
-
"#{path}?#{URI.encode_www_form(params)}"
|
|
40
|
+
collection(response, type: ClipDownload)
|
|
47
41
|
end
|
|
48
42
|
end
|
|
49
43
|
end
|
|
@@ -5,15 +5,14 @@ module Twitch
|
|
|
5
5
|
def list(broadcaster_id:, reward_id:, status:, **params)
|
|
6
6
|
attributes = { broadcaster_id: broadcaster_id, reward_id: reward_id, status: status.upcase }
|
|
7
7
|
response = get_request("channel_points/custom_rewards/redemptions", params: attributes.merge(params))
|
|
8
|
-
|
|
8
|
+
collection(response, type: CustomRewardRedemption)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
# Required scope: channel:manage:redemptions
|
|
12
12
|
# Broadcaster ID must match the user in the OAuth token
|
|
13
13
|
def update(broadcaster_id:, reward_id:, redemption_id:, status:)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
response = patch_request("channel_points/custom_rewards/redemptions?#{query}", body: attributes)
|
|
14
|
+
path = query_path("channel_points/custom_rewards/redemptions", broadcaster_id: broadcaster_id, reward_id: reward_id, id: redemption_id)
|
|
15
|
+
response = patch_request(path, body: { status: status.upcase })
|
|
17
16
|
|
|
18
17
|
CustomRewardRedemption.new(response.body.dig("data")[0]) if response.success?
|
|
19
18
|
end
|
|
@@ -4,19 +4,19 @@ module Twitch
|
|
|
4
4
|
# Broadcaster ID must match the user in the OAuth token
|
|
5
5
|
def list(broadcaster_id:, **params)
|
|
6
6
|
response = get_request("channel_points/custom_rewards", params: params.merge(broadcaster_id: broadcaster_id))
|
|
7
|
-
|
|
7
|
+
collection(response, type: CustomReward)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def create(broadcaster_id:, title:, cost:, **params)
|
|
11
|
-
attributes = {
|
|
12
|
-
response = post_request("channel_points/custom_rewards", body: attributes.merge(params))
|
|
11
|
+
attributes = { title: title, cost: cost }
|
|
12
|
+
response = post_request(query_path("channel_points/custom_rewards", broadcaster_id: broadcaster_id), body: attributes.merge(params))
|
|
13
13
|
|
|
14
14
|
CustomReward.new(response.body.dig("data")[0]) if response.success?
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def update(broadcaster_id:, reward_id:, **params)
|
|
18
|
-
|
|
19
|
-
response = patch_request(
|
|
18
|
+
path = query_path("channel_points/custom_rewards", broadcaster_id: broadcaster_id, id: reward_id)
|
|
19
|
+
response = patch_request(path, body: params)
|
|
20
20
|
|
|
21
21
|
CustomReward.new(response.body.dig("data")[0]) if response.success?
|
|
22
22
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Twitch
|
|
2
|
+
class DropsEntitlementsResource < Resource
|
|
3
|
+
# The Client ID must be owned by a member of the organization that owns the game
|
|
4
|
+
# Available parameters: id, user_id, game_id, fulfillment_status, first, after
|
|
5
|
+
def list(**params)
|
|
6
|
+
response = get_request("entitlements/drops", params: params)
|
|
7
|
+
collection(response, type: DropsEntitlement)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# fulfillment_status: CLAIMED or FULFILLED
|
|
11
|
+
# Returns a collection of results grouped by status, each with the affected ids
|
|
12
|
+
def update(entitlement_ids:, fulfillment_status:)
|
|
13
|
+
response = patch_request("entitlements/drops", body: { entitlement_ids: Array(entitlement_ids), fulfillment_status: fulfillment_status })
|
|
14
|
+
collection(response, type: DropsEntitlementUpdate)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -2,17 +2,17 @@ module Twitch
|
|
|
2
2
|
class EmotesResource < Resource
|
|
3
3
|
def channel(broadcaster_id:)
|
|
4
4
|
response = get_request("chat/emotes", params: { broadcaster_id: broadcaster_id })
|
|
5
|
-
|
|
5
|
+
collection(response, type: Emote)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def global
|
|
9
9
|
response = get_request("chat/emotes/global")
|
|
10
|
-
|
|
10
|
+
collection(response, type: Emote)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def sets(emote_set_id:)
|
|
14
14
|
response = get_request("chat/emotes/set", params: { emote_set_id: emote_set_id })
|
|
15
|
-
|
|
15
|
+
collection(response, type: Emote)
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
module Twitch
|
|
2
2
|
class EventsubConduitsResource < Resource
|
|
3
|
+
MAX_SHARDS_PER_UPDATE = 100
|
|
4
|
+
|
|
3
5
|
def list(**params)
|
|
4
6
|
response = get_request("eventsub/conduits", params: params)
|
|
5
|
-
|
|
7
|
+
collection(response, type: EventsubConduit)
|
|
6
8
|
end
|
|
7
9
|
|
|
8
10
|
def create(shard_count:)
|
|
@@ -23,12 +25,20 @@ module Twitch
|
|
|
23
25
|
|
|
24
26
|
def shards(id:, **params)
|
|
25
27
|
response = get_request("eventsub/conduits/shards", params: { conduit_id: id }.merge(params))
|
|
26
|
-
|
|
28
|
+
collection(response, type: EventsubConduitShard)
|
|
27
29
|
end
|
|
28
30
|
|
|
31
|
+
# Twitch accepts up to 100 shards per request, so larger lists are sent in batches.
|
|
32
|
+
# Returns the updated shards. Shards that failed to update are listed in #errors
|
|
33
|
+
# (each with id, message and code), as Twitch applies the rest of the update anyway.
|
|
29
34
|
def update_shards(id:, shards:)
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
results = shards.each_slice(MAX_SHARDS_PER_UPDATE).map do |batch|
|
|
36
|
+
response = patch_request("eventsub/conduits/shards", body: { conduit_id: id, shards: batch })
|
|
37
|
+
collection(response, type: EventsubConduitShard)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
data = results.flat_map(&:data)
|
|
41
|
+
Collection.new(data: data, total: data.size, cursor: nil, errors: results.flat_map(&:errors))
|
|
32
42
|
end
|
|
33
43
|
end
|
|
34
44
|
end
|
|
@@ -2,7 +2,7 @@ module Twitch
|
|
|
2
2
|
class EventsubSubscriptionsResource < Resource
|
|
3
3
|
def list(**params)
|
|
4
4
|
response = get_request("eventsub/subscriptions", params: params)
|
|
5
|
-
|
|
5
|
+
collection(response, type: EventsubSubscription)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def create(type:, version:, condition:, transport:, **params)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module Twitch
|
|
2
|
+
# Most of these endpoints require a signed JWT created by your Extension Backend Service,
|
|
3
|
+
# rather than an OAuth token. Pass the JWT as the client's access_token.
|
|
4
|
+
# See https://dev.twitch.tv/docs/extensions/building/#signing-the-jwt
|
|
5
|
+
class ExtensionsResource < Resource
|
|
6
|
+
# Requires a signed JWT
|
|
7
|
+
def retrieve(extension_id:, extension_version: nil)
|
|
8
|
+
response = get_request("extensions", params: { extension_id: extension_id, extension_version: extension_version }.compact)
|
|
9
|
+
data = response.body.dig("data")
|
|
10
|
+
|
|
11
|
+
return nil if data.nil? || data.empty?
|
|
12
|
+
|
|
13
|
+
Extension.new(data[0])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def released(extension_id:, extension_version: nil)
|
|
17
|
+
response = get_request("extensions/released", params: { extension_id: extension_id, extension_version: extension_version }.compact)
|
|
18
|
+
data = response.body.dig("data")
|
|
19
|
+
|
|
20
|
+
return nil if data.nil? || data.empty?
|
|
21
|
+
|
|
22
|
+
Extension.new(data[0])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Available parameters: first, after
|
|
26
|
+
def live_channels(extension_id:, **params)
|
|
27
|
+
response = get_request("extensions/live", params: params.merge(extension_id: extension_id))
|
|
28
|
+
collection(response, type: ExtensionLiveChannel)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Requires a signed JWT
|
|
32
|
+
# segment: broadcaster, developer or global. Pass an array to get more than one.
|
|
33
|
+
def configuration(extension_id:, segment:, broadcaster_id: nil)
|
|
34
|
+
params = { extension_id: extension_id, segment: segment, broadcaster_id: broadcaster_id }.compact
|
|
35
|
+
response = get_request("extensions/configurations", params: params)
|
|
36
|
+
collection(response, type: ExtensionConfigurationSegment)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Requires a signed JWT
|
|
40
|
+
# Available attributes: broadcaster_id, content, version
|
|
41
|
+
def set_configuration(extension_id:, segment:, **attributes)
|
|
42
|
+
put_request("extensions/configurations", body: attributes.merge(extension_id: extension_id, segment: segment))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Requires a signed JWT
|
|
46
|
+
def set_required_configuration(broadcaster_id:, extension_id:, extension_version:, required_configuration:)
|
|
47
|
+
attrs = { extension_id: extension_id, extension_version: extension_version, required_configuration: required_configuration }
|
|
48
|
+
put_request(query_path("extensions/required_configuration", broadcaster_id: broadcaster_id), body: attrs)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Requires a signed JWT
|
|
52
|
+
# target: an array containing broadcast, global, or whisper-<user-id>
|
|
53
|
+
def send_pubsub_message(broadcaster_id:, target:, message:, is_global_broadcast: nil)
|
|
54
|
+
attrs = { broadcaster_id: broadcaster_id, target: Array(target), message: message, is_global_broadcast: is_global_broadcast }.compact
|
|
55
|
+
post_request("extensions/pubsub", body: attrs)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Requires a signed JWT
|
|
59
|
+
def send_chat_message(broadcaster_id:, text:, extension_id:, extension_version:)
|
|
60
|
+
attrs = { text: text, extension_id: extension_id, extension_version: extension_version }
|
|
61
|
+
post_request(query_path("extensions/chat", broadcaster_id: broadcaster_id), body: attrs)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Requires a signed JWT
|
|
65
|
+
def secrets(extension_id:)
|
|
66
|
+
response = get_request("extensions/jwt/secrets", params: { extension_id: extension_id })
|
|
67
|
+
collection(response, type: ExtensionSecret)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Requires a signed JWT
|
|
71
|
+
# delay: seconds before the new secret becomes active (minimum 300)
|
|
72
|
+
def create_secret(extension_id:, delay: nil)
|
|
73
|
+
response = post_request(query_path("extensions/jwt/secrets", extension_id: extension_id, delay: delay), body: {})
|
|
74
|
+
collection(response, type: ExtensionSecret)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Requires an app access token whose Client ID matches the extension's
|
|
78
|
+
def bits_products(should_include_all: nil)
|
|
79
|
+
response = get_request("bits/extensions", params: { should_include_all: should_include_all }.compact)
|
|
80
|
+
collection(response, type: ExtensionBitsProduct)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Requires an app access token whose Client ID matches the extension's
|
|
84
|
+
# cost: { amount: 100, type: "bits" }
|
|
85
|
+
# Available attributes: in_development, expiration, is_broadcast
|
|
86
|
+
def update_bits_product(sku:, cost:, display_name:, **attributes)
|
|
87
|
+
attrs = attributes.merge(sku: sku, cost: cost, display_name: display_name)
|
|
88
|
+
response = put_request("bits/extensions", body: attrs)
|
|
89
|
+
ExtensionBitsProduct.new(response.body.dig("data")[0])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Requires an app access token
|
|
93
|
+
# Available parameters: id, first, after
|
|
94
|
+
def transactions(extension_id:, **params)
|
|
95
|
+
response = get_request("extensions/transactions", params: params.merge(extension_id: extension_id))
|
|
96
|
+
collection(response, type: ExtensionTransaction)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|