twitchrb 1.10.0 → 2.0.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +104 -1
  5. data/Gemfile.lock +5 -5
  6. data/README.md +360 -45
  7. data/lib/twitch/client.rb +109 -20
  8. data/lib/twitch/collection.rb +58 -11
  9. data/lib/twitch/error_generator.rb +6 -4
  10. data/lib/twitch/eventsub_webhook.rb +140 -0
  11. data/lib/twitch/oauth.rb +50 -15
  12. data/lib/twitch/object.rb +28 -0
  13. data/lib/twitch/objects/ad_schedule.rb +4 -0
  14. data/lib/twitch/objects/analytics_report.rb +4 -0
  15. data/lib/twitch/objects/bits_leaderboard_entry.rb +4 -0
  16. data/lib/twitch/objects/charity_donation.rb +4 -0
  17. data/lib/twitch/objects/chat_settings.rb +4 -0
  18. data/lib/twitch/objects/cheermote.rb +4 -0
  19. data/lib/twitch/objects/content_classification_label.rb +4 -0
  20. data/lib/twitch/objects/drops_entitlement.rb +4 -0
  21. data/lib/twitch/objects/drops_entitlement_update.rb +4 -0
  22. data/lib/twitch/objects/extension.rb +4 -0
  23. data/lib/twitch/objects/extension_bits_product.rb +4 -0
  24. data/lib/twitch/objects/extension_configuration_segment.rb +4 -0
  25. data/lib/twitch/objects/extension_live_channel.rb +4 -0
  26. data/lib/twitch/objects/extension_secret.rb +4 -0
  27. data/lib/twitch/objects/extension_transaction.rb +4 -0
  28. data/lib/twitch/objects/guest_star_invite.rb +4 -0
  29. data/lib/twitch/objects/guest_star_session.rb +4 -0
  30. data/lib/twitch/objects/guest_star_settings.rb +4 -0
  31. data/lib/twitch/objects/shield_mode_status.rb +4 -0
  32. data/lib/twitch/objects/{tag.rb → team.rb} +1 -1
  33. data/lib/twitch/objects/user_active_extensions.rb +4 -0
  34. data/lib/twitch/objects/user_extension.rb +4 -0
  35. data/lib/twitch/resource.rb +49 -9
  36. data/lib/twitch/resources/ads.rb +18 -0
  37. data/lib/twitch/resources/analytics.rb +19 -0
  38. data/lib/twitch/resources/announcements.rb +1 -1
  39. data/lib/twitch/resources/automod.rb +8 -8
  40. data/lib/twitch/resources/badges.rb +3 -3
  41. data/lib/twitch/resources/banned_users.rb +4 -4
  42. data/lib/twitch/resources/bits.rb +16 -0
  43. data/lib/twitch/resources/blocked_terms.rb +5 -5
  44. data/lib/twitch/resources/channels.rb +7 -7
  45. data/lib/twitch/resources/charity_campaigns.rb +9 -1
  46. data/lib/twitch/resources/chat_messages.rb +1 -1
  47. data/lib/twitch/resources/chat_settings.rb +20 -0
  48. data/lib/twitch/resources/chatters.rb +1 -1
  49. data/lib/twitch/resources/clips.rb +3 -9
  50. data/lib/twitch/resources/content_classification_labels.rb +8 -0
  51. data/lib/twitch/resources/custom_power_ups.rb +1 -1
  52. data/lib/twitch/resources/custom_reward_redemptions.rb +3 -4
  53. data/lib/twitch/resources/custom_rewards.rb +5 -5
  54. data/lib/twitch/resources/drops_entitlements.rb +17 -0
  55. data/lib/twitch/resources/emotes.rb +5 -5
  56. data/lib/twitch/resources/eventsub_conduits.rb +14 -4
  57. data/lib/twitch/resources/eventsub_subscriptions.rb +9 -13
  58. data/lib/twitch/resources/extensions.rb +99 -0
  59. data/lib/twitch/resources/games.rb +5 -7
  60. data/lib/twitch/resources/goals.rb +1 -1
  61. data/lib/twitch/resources/guest_star.rb +102 -0
  62. data/lib/twitch/resources/moderators.rb +4 -4
  63. data/lib/twitch/resources/pinned_chat_messages.rb +4 -11
  64. data/lib/twitch/resources/polls.rb +1 -1
  65. data/lib/twitch/resources/predictions.rb +1 -1
  66. data/lib/twitch/resources/raids.rb +3 -4
  67. data/lib/twitch/resources/search.rb +2 -2
  68. data/lib/twitch/resources/shield_mode.rb +18 -0
  69. data/lib/twitch/resources/shoutouts.rb +1 -1
  70. data/lib/twitch/resources/stream_markers.rb +1 -1
  71. data/lib/twitch/resources/stream_schedule.rb +4 -5
  72. data/lib/twitch/resources/streams.rb +2 -2
  73. data/lib/twitch/resources/subscriptions.rb +13 -2
  74. data/lib/twitch/resources/suspicious_users.rb +6 -2
  75. data/lib/twitch/resources/teams.rb +20 -0
  76. data/lib/twitch/resources/unban_requests.rb +2 -2
  77. data/lib/twitch/resources/users.rb +37 -43
  78. data/lib/twitch/resources/videos.rb +3 -3
  79. data/lib/twitch/resources/vips.rb +3 -3
  80. data/lib/twitch/resources/warnings.rb +2 -2
  81. data/lib/twitch/resources/whispers.rb +1 -1
  82. data/lib/twitch/version.rb +1 -1
  83. data/lib/twitch.rb +33 -9
  84. data/twitchrb.gemspec +5 -5
  85. metadata +42 -18
  86. data/lib/twitch/objects/banned_event.rb +0 -4
  87. data/lib/twitch/objects/followed_user.rb +0 -4
  88. data/lib/twitch/objects/hype_train_event.rb +0 -4
  89. data/lib/twitch/objects/moderator_event.rb +0 -4
  90. data/lib/twitch/resources/banned_events.rb +0 -8
  91. data/lib/twitch/resources/hype_train_events.rb +0 -10
  92. data/lib/twitch/resources/moderator_events.rb +0 -9
  93. data/lib/twitch/resources/tags.rb +0 -18
@@ -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
@@ -1,7 +1,7 @@
1
1
  module Twitch
2
2
  class GamesResource < Resource
3
3
  def retrieve(id: nil, ids: nil, name: nil, names: nil)
4
- raise "Either id, ids, name or names is required" unless !id.nil? || !ids.nil? || !name.nil? || !names.nil?
4
+ raise ArgumentError, "Either id, ids, name or names is required" unless !id.nil? || !ids.nil? || !name.nil? || !names.nil?
5
5
 
6
6
  if id
7
7
  response = get_request("games", params: { id: id })
@@ -14,18 +14,16 @@ module Twitch
14
14
  end
15
15
 
16
16
  body = response.body.dig("data")
17
- if id || name && body.count == 1
18
- Game.new body[0]
19
- elsif ids || names && body.count > 1
20
- Collection.from_response(response, type: Game)
17
+ if id || name
18
+ body.empty? ? nil : Game.new(body[0])
21
19
  else
22
- nil
20
+ collection(response, type: Game)
23
21
  end
24
22
  end
25
23
 
26
24
  def top(**params)
27
25
  response = get_request("games/top", params: params)
28
- Collection.from_response(response, type: Game)
26
+ collection(response, type: Game)
29
27
  end
30
28
  end
31
29
  end
@@ -4,7 +4,7 @@ module Twitch
4
4
  # Broadcaster ID must match the user in the OAuth token
5
5
  def list(broadcaster_id:)
6
6
  response = get_request("goals", params: { broadcaster_id: broadcaster_id })
7
- Collection.from_response(response, type: Goal)
7
+ collection(response, type: Goal)
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,102 @@
1
+ module Twitch
2
+ # Guest Star is in beta, so Twitch may change these endpoints
3
+ class GuestStarResource < Resource
4
+ # Required scope: channel:read:guest_star, channel:manage:guest_star, moderator:read:guest_star or moderator:manage:guest_star
5
+ # moderator_id must match the user in the OAuth token
6
+ def settings(broadcaster_id:, moderator_id:)
7
+ response = get_request("guest_star/channel_settings", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id })
8
+ GuestStarSettings.new(response.body.dig("data")[0])
9
+ end
10
+
11
+ # Required scope: channel:manage:guest_star
12
+ # broadcaster_id must match the user in the OAuth token
13
+ # Available attributes: is_moderator_send_live_enabled, slot_count, is_browser_source_audio_enabled,
14
+ # group_layout, regenerate_browser_sources
15
+ def update_settings(broadcaster_id:, **attributes)
16
+ put_request(query_path("guest_star/channel_settings", broadcaster_id: broadcaster_id), body: attributes)
17
+ end
18
+
19
+ # Required scope: channel:read:guest_star, channel:manage:guest_star, moderator:read:guest_star or moderator:manage:guest_star
20
+ def session(broadcaster_id:, moderator_id:)
21
+ response = get_request("guest_star/session", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id })
22
+ data = response.body.dig("data")
23
+
24
+ return nil if data.nil? || data.empty?
25
+
26
+ GuestStarSession.new(data[0])
27
+ end
28
+
29
+ # Required scope: channel:manage:guest_star
30
+ # broadcaster_id must match the user in the OAuth token
31
+ def create_session(broadcaster_id:)
32
+ response = post_request(query_path("guest_star/session", broadcaster_id: broadcaster_id), body: {})
33
+ GuestStarSession.new(response.body.dig("data")[0])
34
+ end
35
+
36
+ # Required scope: channel:manage:guest_star
37
+ # broadcaster_id must match the user in the OAuth token
38
+ def end_session(broadcaster_id:, session_id:)
39
+ response = delete_request("guest_star/session", params: { broadcaster_id: broadcaster_id, session_id: session_id })
40
+ GuestStarSession.new(response.body.dig("data")[0])
41
+ end
42
+
43
+ # Required scope: channel:read:guest_star, channel:manage:guest_star, moderator:read:guest_star or moderator:manage:guest_star
44
+ def invites(broadcaster_id:, moderator_id:, session_id:)
45
+ response = get_request("guest_star/invites", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id, session_id: session_id })
46
+ collection(response, type: GuestStarInvite)
47
+ end
48
+
49
+ # Required scope: channel:manage:guest_star or moderator:manage:guest_star
50
+ # moderator_id must match the user in the OAuth token
51
+ def send_invite(broadcaster_id:, moderator_id:, session_id:, guest_id:)
52
+ params = { broadcaster_id: broadcaster_id, moderator_id: moderator_id, session_id: session_id, guest_id: guest_id }
53
+ post_request(query_path("guest_star/invites", params), body: {})
54
+ end
55
+
56
+ # Required scope: channel:manage:guest_star or moderator:manage:guest_star
57
+ # moderator_id must match the user in the OAuth token
58
+ def delete_invite(broadcaster_id:, moderator_id:, session_id:, guest_id:)
59
+ delete_request("guest_star/invites", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id, session_id: session_id, guest_id: guest_id })
60
+ end
61
+
62
+ # Required scope: channel:manage:guest_star or moderator:manage:guest_star
63
+ # moderator_id must match the user in the OAuth token
64
+ def assign_slot(broadcaster_id:, moderator_id:, session_id:, guest_id:, slot_id:)
65
+ params = { broadcaster_id: broadcaster_id, moderator_id: moderator_id, session_id: session_id, guest_id: guest_id, slot_id: slot_id }
66
+ post_request(query_path("guest_star/slot", params), body: {})
67
+ end
68
+
69
+ # Moves a guest to another slot, or swaps two guests when destination_slot_id is occupied
70
+ # Required scope: channel:manage:guest_star or moderator:manage:guest_star
71
+ def update_slot(broadcaster_id:, moderator_id:, session_id:, source_slot_id:, destination_slot_id: nil)
72
+ params = {
73
+ broadcaster_id: broadcaster_id,
74
+ moderator_id: moderator_id,
75
+ session_id: session_id,
76
+ source_slot_id: source_slot_id,
77
+ destination_slot_id: destination_slot_id
78
+ }
79
+ patch_request(query_path("guest_star/slot", params), body: {})
80
+ end
81
+
82
+ # Required scope: channel:manage:guest_star or moderator:manage:guest_star
83
+ def delete_slot(broadcaster_id:, moderator_id:, session_id:, guest_id:, slot_id:, should_reinvite_guest: nil)
84
+ params = {
85
+ broadcaster_id: broadcaster_id,
86
+ moderator_id: moderator_id,
87
+ session_id: session_id,
88
+ guest_id: guest_id,
89
+ slot_id: slot_id,
90
+ should_reinvite_guest: should_reinvite_guest
91
+ }.compact
92
+ delete_request("guest_star/slot", params: params)
93
+ end
94
+
95
+ # Required scope: channel:manage:guest_star or moderator:manage:guest_star
96
+ # Available attributes: is_audio_enabled, is_video_enabled, is_live, volume
97
+ def update_slot_settings(broadcaster_id:, moderator_id:, session_id:, slot_id:, **attributes)
98
+ params = attributes.merge(broadcaster_id: broadcaster_id, moderator_id: moderator_id, session_id: session_id, slot_id: slot_id)
99
+ patch_request(query_path("guest_star/slot_settings", params), body: {})
100
+ end
101
+ end
102
+ end
@@ -3,21 +3,21 @@ module Twitch
3
3
  # User ID must match the user in the OAuth token
4
4
  def channels(user_id:)
5
5
  response = get_request("moderation/channels", params: { user_id: user_id })
6
- Collection.from_response(response, type: Channel)
6
+ collection(response, type: Channel)
7
7
  end
8
8
 
9
9
  # Broadcaster ID must match the user in the OAuth token
10
10
  def list(broadcaster_id:, **params)
11
11
  response = get_request("moderation/moderators", params: params.merge(broadcaster_id: broadcaster_id))
12
- Collection.from_response(response, type: Moderator)
12
+ collection(response, type: Moderator)
13
13
  end
14
14
 
15
15
  def create(broadcaster_id:, user_id:)
16
- post_request("moderation/moderators", body: { broadcaster_id: broadcaster_id, user_id: user_id })
16
+ post_request(query_path("moderation/moderators", broadcaster_id: broadcaster_id, user_id: user_id), body: {})
17
17
  end
18
18
 
19
19
  def delete(broadcaster_id:, user_id:)
20
- delete_request("moderation/moderators?broadcaster_id=#{broadcaster_id}&user_id=#{user_id}")
20
+ delete_request("moderation/moderators", params: { broadcaster_id: broadcaster_id, user_id: user_id })
21
21
  end
22
22
  end
23
23
  end
@@ -11,12 +11,12 @@ module Twitch
11
11
 
12
12
  # moderator_id must match the user in the OAuth token
13
13
  def create(broadcaster_id:, moderator_id:, message_id:, duration_seconds: nil)
14
- put_request(query_path(broadcaster_id:, moderator_id:, message_id:, duration_seconds:), body: {})
14
+ put_request(pins_path(broadcaster_id:, moderator_id:, message_id:, duration_seconds:), body: {})
15
15
  end
16
16
 
17
17
  # moderator_id must match the user in the OAuth token
18
18
  def update(broadcaster_id:, moderator_id:, message_id:, duration_seconds: nil)
19
- patch_request(query_path(broadcaster_id:, moderator_id:, message_id:, duration_seconds:), body: {})
19
+ patch_request(pins_path(broadcaster_id:, moderator_id:, message_id:, duration_seconds:), body: {})
20
20
  end
21
21
 
22
22
  # moderator_id must match the user in the OAuth token
@@ -26,15 +26,8 @@ module Twitch
26
26
 
27
27
  private
28
28
 
29
- def query_path(broadcaster_id:, moderator_id:, message_id:, duration_seconds:)
30
- params = {
31
- broadcaster_id: broadcaster_id,
32
- moderator_id: moderator_id,
33
- message_id: message_id,
34
- duration_seconds: duration_seconds
35
- }.compact
36
-
37
- "chat/pins?#{URI.encode_www_form(params)}"
29
+ def pins_path(broadcaster_id:, moderator_id:, message_id:, duration_seconds:)
30
+ query_path("chat/pins", broadcaster_id: broadcaster_id, moderator_id: moderator_id, message_id: message_id, duration_seconds: duration_seconds)
38
31
  end
39
32
  end
40
33
  end
@@ -3,7 +3,7 @@ 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("polls", params: params.merge(broadcaster_id: broadcaster_id))
6
- Collection.from_response(response, type: Poll)
6
+ collection(response, type: Poll)
7
7
  end
8
8
 
9
9
  # Broadcaster ID must match the user in the OAuth token
@@ -5,7 +5,7 @@ module Twitch
5
5
  response = get_request("predictions", params: params.merge(broadcaster_id: broadcaster_id))
6
6
 
7
7
  if response.body["data"]
8
- Collection.from_response(response, type: Prediction)
8
+ collection(response, type: Prediction)
9
9
  else
10
10
  nil
11
11
  end
@@ -2,16 +2,15 @@ module Twitch
2
2
  class RaidsResource < Resource
3
3
  # from_broadcaster_id must match the user in the OAuth token
4
4
  def create(from_broadcaster_id:, to_broadcaster_id:)
5
- attrs = { from_broadcaster_id: from_broadcaster_id, to_broadcaster_id: to_broadcaster_id }
6
-
7
- response = post_request("raids", body: attrs)
5
+ path = query_path("raids", from_broadcaster_id: from_broadcaster_id, to_broadcaster_id: to_broadcaster_id)
6
+ response = post_request(path, body: {})
8
7
 
9
8
  Raid.new(response.body.dig("data")[0]) if response.success?
10
9
  end
11
10
 
12
11
  # broadcaster_id must match the user in the OAuth token
13
12
  def delete(broadcaster_id:)
14
- delete_request("raids?broadcaster_id=#{broadcaster_id}")
13
+ delete_request("raids", params: { broadcaster_id: broadcaster_id })
15
14
  end
16
15
  end
17
16
  end
@@ -3,13 +3,13 @@ module Twitch
3
3
  def categories(query:, **params)
4
4
  response = get_request("search/categories", params: params.merge(query: query))
5
5
 
6
- Collection.from_response(response, type: SearchResult)
6
+ collection(response, type: SearchResult)
7
7
  end
8
8
 
9
9
  def channels(query:, **params)
10
10
  response = get_request("search/channels", params: params.merge(query: query))
11
11
 
12
- Collection.from_response(response, type: SearchResult)
12
+ collection(response, type: SearchResult)
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,18 @@
1
+ module Twitch
2
+ class ShieldModeResource < Resource
3
+ # Required scope: moderator:read:shield_mode or moderator:manage:shield_mode
4
+ # moderator_id must match the user in the OAuth token
5
+ def retrieve(broadcaster_id:, moderator_id:)
6
+ response = get_request("moderation/shield_mode", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id })
7
+ ShieldModeStatus.new(response.body.dig("data")[0])
8
+ end
9
+
10
+ # Required scope: moderator:manage:shield_mode
11
+ # moderator_id must match the user in the OAuth token
12
+ def update(broadcaster_id:, moderator_id:, is_active:)
13
+ path = query_path("moderation/shield_mode", broadcaster_id: broadcaster_id, moderator_id: moderator_id)
14
+ response = put_request(path, body: { is_active: is_active })
15
+ ShieldModeStatus.new(response.body.dig("data")[0])
16
+ end
17
+ end
18
+ end
@@ -5,7 +5,7 @@ module Twitch
5
5
  # To: the ID of the Broadcaster the Shoutout will be for
6
6
  # Required scope: moderator:manage:shoutouts
7
7
  def create(from:, to:, moderator_id:)
8
- post_request("chat/shoutouts?from_broadcaster_id=#{from}&to_broadcaster_id=#{to}&moderator_id=#{moderator_id}", body: nil)
8
+ post_request(query_path("chat/shoutouts", from_broadcaster_id: from, to_broadcaster_id: to, moderator_id: moderator_id), body: nil)
9
9
  end
10
10
  end
11
11
  end
@@ -4,7 +4,7 @@ module Twitch
4
4
  def list(**params)
5
5
  response = get_request("streams/markers", params: params)
6
6
 
7
- Collection.from_response(response, type: StreamMarker)
7
+ collection(response, type: StreamMarker)
8
8
  end
9
9
 
10
10
  # Required scope: channel:manage:broadcast
@@ -17,21 +17,20 @@ module Twitch
17
17
  # Broadcaster ID must match the user in the OAuth token
18
18
  # TODO: Allow the user to put any date format and then convert it to RFC3339
19
19
  def update(broadcaster_id:, **params)
20
- patch_request("schedule/settings", body: params.merge(broadcaster_id: broadcaster_id))
20
+ patch_request(query_path("schedule/settings", params.merge(broadcaster_id: broadcaster_id)), body: {})
21
21
  end
22
22
 
23
23
  # Broadcaster ID must match the user in the OAuth token
24
24
  def create_segment(broadcaster_id:, start_time:, timezone:, duration:, is_recurring:, **params)
25
- attrs = { broadcaster_id: broadcaster_id, start_time: start_time, duration: duration, timezone: timezone, is_recurring: is_recurring }
26
- response = post_request("schedule/segment", body: attrs.merge(params))
25
+ attrs = { start_time: start_time, duration: duration, timezone: timezone, is_recurring: is_recurring }
26
+ response = post_request(query_path("schedule/segment", broadcaster_id: broadcaster_id), body: attrs.merge(params))
27
27
 
28
28
  StreamSchedule.new(response.body) if response.success?
29
29
  end
30
30
 
31
31
  # Broadcaster ID must match the user in the OAuth token
32
32
  def update_segment(broadcaster_id:, id:, **params)
33
- attrs = { broadcaster_id: broadcaster_id, id: id }
34
- response = patch_request("schedule/segment", body: attrs.merge(params))
33
+ response = patch_request(query_path("schedule/segment", broadcaster_id: broadcaster_id, id: id), body: params)
35
34
 
36
35
  StreamSchedule.new(response.body) if response.success?
37
36
  end
@@ -3,7 +3,7 @@ module Twitch
3
3
  def list(**params)
4
4
  response = get_request("streams", params: params)
5
5
 
6
- Collection.from_response(response, type: Stream)
6
+ collection(response, type: Stream)
7
7
  end
8
8
 
9
9
  # Required scope: user:read:follows
@@ -11,7 +11,7 @@ module Twitch
11
11
  def followed(user_id:, **params)
12
12
  response = get_request("streams/followed", params: params.merge(user_id: user_id))
13
13
 
14
- Collection.from_response(response, type: Stream)
14
+ collection(response, type: Stream)
15
15
  end
16
16
  end
17
17
  end
@@ -5,7 +5,7 @@ module Twitch
5
5
  # Required scope: channel:read:subscriptions
6
6
  def list(broadcaster_id:, **params)
7
7
  response = get_request("subscriptions", params: params.merge(broadcaster_id: broadcaster_id))
8
- Collection.from_response(response, type: Subscription)
8
+ collection(response, type: Subscription)
9
9
  end
10
10
 
11
11
  # Checks if a User is subscribed to a Broadcaster
@@ -15,7 +15,18 @@ module Twitch
15
15
  def is_subscribed(broadcaster_id:, user_id:, **params)
16
16
  attrs = { broadcaster_id: broadcaster_id, user_id: user_id }
17
17
  response = get_request("subscriptions/user", params: attrs.merge(params))
18
- Collection.from_response(response, type: Subscription)
18
+ collection(response, type: Subscription)
19
+ end
20
+
21
+ # Returns true if the user subscribes to the broadcaster, false if not
22
+ # User ID must match the user in the OAuth token
23
+ # Required scope: user:read:subscriptions
24
+ def subscribed?(broadcaster_id:, user_id:)
25
+ get_request("subscriptions/user", params: { broadcaster_id: broadcaster_id, user_id: user_id })
26
+ true
27
+ rescue Errors::EntityNotFoundError
28
+ # Twitch responds with 404 when the user doesn't subscribe
29
+ false
19
30
  end
20
31
 
21
32
  # Grabs the number of Subscribers and Subscriber Points a broadcaster has
@@ -5,7 +5,7 @@ module Twitch
5
5
  def create(broadcaster_id:, moderator_id:, user_id:, status:)
6
6
  attrs = { user_id: user_id, status: status }
7
7
  response = post_request(
8
- "moderation/suspicious_users?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}",
8
+ query_path("moderation/suspicious_users", broadcaster_id: broadcaster_id, moderator_id: moderator_id),
9
9
  body: attrs
10
10
  )
11
11
 
@@ -16,9 +16,13 @@ module Twitch
16
16
  # moderator_id must match the currently authenticated moderator
17
17
  def delete(broadcaster_id:, moderator_id:, user_id:)
18
18
  response = delete_request(
19
- "moderation/suspicious_users?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}&user_id=#{user_id}"
19
+ "moderation/suspicious_users",
20
+ params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id, user_id: user_id }
20
21
  )
21
22
 
23
+ # delete_request returns true for 204 No Content responses
24
+ return true unless response.respond_to?(:body)
25
+
22
26
  SuspiciousUser.new(response.body.dig("data")[0])
23
27
  end
24
28
  end
@@ -0,0 +1,20 @@
1
+ module Twitch
2
+ class TeamsResource < Resource
3
+ def retrieve(id: nil, name: nil)
4
+ raise ArgumentError, "Either id or name is required" if id.nil? && name.nil?
5
+
6
+ response = get_request("teams", params: { id: id, name: name }.compact)
7
+ data = response.body.dig("data")
8
+
9
+ return nil if data.nil? || data.empty?
10
+
11
+ Team.new(data[0])
12
+ end
13
+
14
+ # Gets the teams that a broadcaster is a member of
15
+ def channel(broadcaster_id:)
16
+ response = get_request("teams/channel", params: { broadcaster_id: broadcaster_id })
17
+ collection(response, type: Team)
18
+ end
19
+ end
20
+ end
@@ -3,12 +3,12 @@ module Twitch
3
3
  def list(broadcaster_id:, moderator_id:, status:, **params)
4
4
  attrs = { broadcaster_id: broadcaster_id, moderator_id: moderator_id, status: status }
5
5
  response = get_request("moderation/unban_requests", params: attrs.merge(params))
6
- Collection.from_response(response, type: UnbanRequest)
6
+ collection(response, type: UnbanRequest)
7
7
  end
8
8
 
9
9
  def resolve(broadcaster_id:, moderator_id:, id:, status:, **params)
10
10
  attrs = { broadcaster_id: broadcaster_id, moderator_id: moderator_id, unban_request_id: id, status: status }
11
- response = patch_request("moderation/unban_requests", body: attrs.merge(params))
11
+ response = patch_request(query_path("moderation/unban_requests", attrs.merge(params)), body: {})
12
12
  UnbanRequest.new(response.body.dig("data")[0])
13
13
  end
14
14
  end