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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +373 -46
  5. data/lib/twitch/client.rb +109 -20
  6. data/lib/twitch/collection.rb +54 -8
  7. data/lib/twitch/eventsub_webhook.rb +140 -0
  8. data/lib/twitch/oauth.rb +51 -14
  9. data/lib/twitch/object.rb +93 -14
  10. data/lib/twitch/objects/ad_schedule.rb +4 -0
  11. data/lib/twitch/objects/analytics_report.rb +4 -0
  12. data/lib/twitch/objects/bits_leaderboard_entry.rb +4 -0
  13. data/lib/twitch/objects/charity_donation.rb +4 -0
  14. data/lib/twitch/objects/chat_settings.rb +4 -0
  15. data/lib/twitch/objects/cheermote.rb +4 -0
  16. data/lib/twitch/objects/content_classification_label.rb +4 -0
  17. data/lib/twitch/objects/drops_entitlement.rb +4 -0
  18. data/lib/twitch/objects/drops_entitlement_update.rb +4 -0
  19. data/lib/twitch/objects/extension.rb +4 -0
  20. data/lib/twitch/objects/extension_bits_product.rb +4 -0
  21. data/lib/twitch/objects/extension_configuration_segment.rb +4 -0
  22. data/lib/twitch/objects/extension_live_channel.rb +4 -0
  23. data/lib/twitch/objects/extension_secret.rb +4 -0
  24. data/lib/twitch/objects/extension_transaction.rb +4 -0
  25. data/lib/twitch/objects/guest_star_invite.rb +4 -0
  26. data/lib/twitch/objects/guest_star_session.rb +4 -0
  27. data/lib/twitch/objects/guest_star_settings.rb +4 -0
  28. data/lib/twitch/objects/shield_mode_status.rb +4 -0
  29. data/lib/twitch/objects/{tag.rb → team.rb} +1 -1
  30. data/lib/twitch/objects/user_active_extensions.rb +4 -0
  31. data/lib/twitch/objects/user_extension.rb +4 -0
  32. data/lib/twitch/resource.rb +29 -0
  33. data/lib/twitch/resources/ads.rb +18 -0
  34. data/lib/twitch/resources/analytics.rb +19 -0
  35. data/lib/twitch/resources/announcements.rb +1 -1
  36. data/lib/twitch/resources/automod.rb +7 -7
  37. data/lib/twitch/resources/badges.rb +2 -2
  38. data/lib/twitch/resources/banned_users.rb +3 -3
  39. data/lib/twitch/resources/bits.rb +16 -0
  40. data/lib/twitch/resources/blocked_terms.rb +3 -3
  41. data/lib/twitch/resources/channels.rb +4 -4
  42. data/lib/twitch/resources/charity_campaigns.rb +8 -0
  43. data/lib/twitch/resources/chat_settings.rb +20 -0
  44. data/lib/twitch/resources/chatters.rb +1 -1
  45. data/lib/twitch/resources/clips.rb +3 -9
  46. data/lib/twitch/resources/content_classification_labels.rb +8 -0
  47. data/lib/twitch/resources/custom_power_ups.rb +1 -1
  48. data/lib/twitch/resources/custom_reward_redemptions.rb +3 -4
  49. data/lib/twitch/resources/custom_rewards.rb +5 -5
  50. data/lib/twitch/resources/drops_entitlements.rb +17 -0
  51. data/lib/twitch/resources/emotes.rb +3 -3
  52. data/lib/twitch/resources/eventsub_conduits.rb +14 -4
  53. data/lib/twitch/resources/eventsub_subscriptions.rb +1 -1
  54. data/lib/twitch/resources/extensions.rb +99 -0
  55. data/lib/twitch/resources/games.rb +3 -3
  56. data/lib/twitch/resources/goals.rb +1 -1
  57. data/lib/twitch/resources/guest_star.rb +102 -0
  58. data/lib/twitch/resources/moderators.rb +3 -3
  59. data/lib/twitch/resources/pinned_chat_messages.rb +4 -11
  60. data/lib/twitch/resources/polls.rb +1 -1
  61. data/lib/twitch/resources/predictions.rb +1 -1
  62. data/lib/twitch/resources/raids.rb +2 -3
  63. data/lib/twitch/resources/search.rb +2 -2
  64. data/lib/twitch/resources/shield_mode.rb +18 -0
  65. data/lib/twitch/resources/shoutouts.rb +1 -1
  66. data/lib/twitch/resources/stream_markers.rb +1 -1
  67. data/lib/twitch/resources/stream_schedule.rb +4 -5
  68. data/lib/twitch/resources/streams.rb +2 -2
  69. data/lib/twitch/resources/subscriptions.rb +13 -2
  70. data/lib/twitch/resources/suspicious_users.rb +1 -1
  71. data/lib/twitch/resources/teams.rb +20 -0
  72. data/lib/twitch/resources/unban_requests.rb +2 -2
  73. data/lib/twitch/resources/users.rb +30 -34
  74. data/lib/twitch/resources/videos.rb +2 -2
  75. data/lib/twitch/resources/vips.rb +2 -2
  76. data/lib/twitch/resources/warnings.rb +2 -2
  77. data/lib/twitch/resources/whispers.rb +1 -1
  78. data/lib/twitch/version.rb +1 -1
  79. data/lib/twitch.rb +33 -10
  80. metadata +42 -46
  81. data/.env.example +0 -3
  82. data/.github/FUNDING.yml +0 -5
  83. data/.github/dependabot.yml +0 -11
  84. data/.github/workflows/ci.yml +0 -39
  85. data/.gitignore +0 -9
  86. data/.rubocop.yml +0 -16
  87. data/Gemfile +0 -13
  88. data/Gemfile.lock +0 -144
  89. data/Rakefile +0 -10
  90. data/bin/console +0 -23
  91. data/bin/setup +0 -8
  92. data/bin/smoke +0 -118
  93. data/lib/twitch/objects/banned_event.rb +0 -4
  94. data/lib/twitch/objects/followed_user.rb +0 -4
  95. data/lib/twitch/objects/hype_train_event.rb +0 -4
  96. data/lib/twitch/objects/moderator_event.rb +0 -4
  97. data/lib/twitch/resources/banned_events.rb +0 -8
  98. data/lib/twitch/resources/hype_train_events.rb +0 -10
  99. data/lib/twitch/resources/moderator_events.rb +0 -9
  100. data/lib/twitch/resources/tags.rb +0 -18
  101. data/mise.toml +0 -2
  102. data/twitchrb.gemspec +0 -29
@@ -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 })
@@ -17,13 +17,13 @@ module Twitch
17
17
  if id || name
18
18
  body.empty? ? nil : Game.new(body[0])
19
19
  else
20
- Collection.from_response(response, type: Game)
20
+ collection(response, type: Game)
21
21
  end
22
22
  end
23
23
 
24
24
  def top(**params)
25
25
  response = get_request("games/top", params: params)
26
- Collection.from_response(response, type: Game)
26
+ collection(response, type: Game)
27
27
  end
28
28
  end
29
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,17 +3,17 @@ 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:)
@@ -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,9 +2,8 @@ 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
@@ -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?#{URI.encode_www_form(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?#{URI.encode_www_form(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
 
@@ -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
@@ -1,7 +1,7 @@
1
1
  module Twitch
2
2
  class UsersResource < Resource
3
3
  def retrieve(id: nil, ids: nil, username: nil, usernames: nil)
4
- raise "Either id, ids, username or usernames is required" unless !id.nil? || !ids.nil? || !username.nil? || !usernames.nil?
4
+ raise ArgumentError, "Either id, ids, username or usernames is required" unless !id.nil? || !ids.nil? || !username.nil? || !usernames.nil?
5
5
 
6
6
  if id
7
7
  response = get_request("users", params: { id: id })
@@ -17,23 +17,22 @@ module Twitch
17
17
  if id || username
18
18
  body.empty? ? nil : User.new(body[0])
19
19
  else
20
- Collection.from_response(response, type: User)
20
+ collection(response, type: User)
21
21
  end
22
22
  end
23
23
 
24
24
  # Updates the current users description
25
25
  # Required scope: user:edit
26
26
  def update(description:)
27
- response = put_request("users", body: { description: description })
27
+ response = put_request(query_path("users", description: description), body: {})
28
28
  User.new response.body.dig("data")[0]
29
29
  end
30
30
 
31
31
  def get_color(user_id: nil, user_ids: nil)
32
32
  if user_ids
33
33
  ids = user_ids.is_a?(Array) ? user_ids : user_ids.split(",")
34
- query = URI.encode_www_form(ids.map { |i| [ "user_id", i.to_s.strip ] })
35
- response = get_request("chat/color?#{query}")
36
- Collection.from_response(response, type: UserColor)
34
+ response = get_request("chat/color", params: { user_id: ids.map { |i| i.to_s.strip } })
35
+ collection(response, type: UserColor)
37
36
  else
38
37
  response = get_request("chat/color", params: { user_id: user_id })
39
38
  UserColor.new response.body.dig("data")[0]
@@ -44,28 +43,18 @@ module Twitch
44
43
  # Required scope: user:manage:chat_color
45
44
  # user_id must be the currently authenticated user
46
45
  def update_color(user_id:, color:)
47
- put_request("chat/color?#{URI.encode_www_form(user_id: user_id, color: color)}", body: {})
48
- end
49
-
50
- # Deprecated.
51
- def follows(**params)
52
- warn "`users.follows` is deprecated. Use `channels.followers` or `channels.following` instead."
53
-
54
- raise "from_id or to_id is required" unless !params[:from_id].nil? || !params[:to_id].nil?
55
-
56
- response = get_request("users/follows", params: params)
57
- Collection.from_response(response, type: FollowedUser)
46
+ put_request(query_path("chat/color", user_id: user_id, color: color), body: {})
58
47
  end
59
48
 
60
49
  # Required scope: user:read:blocked_users
61
50
  def blocks(broadcaster_id:, **params)
62
51
  response = get_request("users/blocks", params: params.merge(broadcaster_id: broadcaster_id))
63
- Collection.from_response(response, type: BlockedUser)
52
+ collection(response, type: BlockedUser)
64
53
  end
65
54
 
66
55
  # Required scope: user:manage:blocked_users
67
56
  def block_user(target_user_id:, **attributes)
68
- put_request("users/blocks?#{URI.encode_www_form(target_user_id: target_user_id)}", body: attributes)
57
+ put_request(query_path("users/blocks", attributes.merge(target_user_id: target_user_id)), body: {})
69
58
  end
70
59
 
71
60
  # Required scope: user:manage:blocked_users
@@ -73,27 +62,34 @@ module Twitch
73
62
  delete_request("users/blocks", params: { target_user_id: target_user_id })
74
63
  end
75
64
 
76
- # A quick method to see if a user is following a channel
77
- def following?(from_id:, to_id:)
78
- warn "`users.following?` is deprecated. Use `channels.followers` or `channels.following` instead."
65
+ def emotes(user_id:, **params)
66
+ attrs = { user_id: user_id }
67
+ response = get_request("chat/emotes/user", params: attrs.merge(params))
68
+ collection(response, type: Emote)
69
+ end
79
70
 
80
- response = get_request("users/follows", params: { from_id: from_id, to_id: to_id })
71
+ # Gets all extensions the authenticated user has installed, active or not
72
+ # Required scope: user:read:broadcast or user:edit:broadcast (needed to include inactive extensions)
73
+ def extensions
74
+ response = get_request("users/extensions/list")
75
+ collection(response, type: UserExtension)
76
+ end
81
77
 
82
- if response.body["data"].empty?
83
- false
84
- else
85
- true
86
- end
78
+ # Gets the active extensions installed for a user, or the authenticated user if user_id is omitted
79
+ def active_extensions(user_id: nil)
80
+ response = get_request("users/extensions", params: { user_id: user_id }.compact)
81
+ UserActiveExtensions.new(response.body.dig("data"))
87
82
  end
88
83
 
89
- def emotes(user_id:, **params)
90
- attrs = { user_id: user_id }
91
- response = get_request("chat/emotes/user", params: attrs.merge(params))
92
- Collection.from_response(response, type: Emote)
84
+ # Required scope: user:edit:broadcast
85
+ # data: { panel: { "1" => { active: true, id: "abc", version: "1.0.0" } }, overlay: {...}, component: {...} }
86
+ def update_extensions(data:)
87
+ response = put_request("users/extensions", body: { data: data })
88
+ UserActiveExtensions.new(response.body.dig("data"))
93
89
  end
94
90
 
95
91
  def authorization(id: nil, ids: nil)
96
- raise "Either id or ids is required" unless !id.nil? || !ids.nil?
92
+ raise ArgumentError, "Either id or ids is required" unless !id.nil? || !ids.nil?
97
93
 
98
94
  if id
99
95
  response = get_request("authorization/users", params: { user_id: id })
@@ -101,7 +97,7 @@ module Twitch
101
97
  response = get_request("authorization/users", params: { user_id: ids })
102
98
  end
103
99
 
104
- Collection.from_response(response, type: UserAuthorization)
100
+ collection(response, type: UserAuthorization)
105
101
  end
106
102
  end
107
103
  end
@@ -1,10 +1,10 @@
1
1
  module Twitch
2
2
  class VideosResource < Resource
3
3
  def list(**params)
4
- raise "user_id or game_id is required" unless !params[:user_id].nil? || !params[:game_id].nil?
4
+ raise ArgumentError, "user_id, game_id or id is required" if params.values_at(:user_id, :game_id, :id).all?(&:nil?)
5
5
 
6
6
  response = get_request("videos", params: params)
7
- Collection.from_response(response, type: Video)
7
+ collection(response, type: Video)
8
8
  end
9
9
 
10
10
  def retrieve(id:)
@@ -3,11 +3,11 @@ 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("channels/vips", params: params.merge(broadcaster_id: broadcaster_id))
6
- Collection.from_response(response, type: Vip)
6
+ collection(response, type: Vip)
7
7
  end
8
8
 
9
9
  def create(broadcaster_id:, user_id:)
10
- post_request("channels/vips", body: { broadcaster_id: broadcaster_id, user_id: user_id })
10
+ post_request(query_path("channels/vips", broadcaster_id: broadcaster_id, user_id: user_id), body: {})
11
11
  end
12
12
 
13
13
  def delete(broadcaster_id:, user_id:)
@@ -5,8 +5,8 @@ module Twitch
5
5
  def create(broadcaster_id:, moderator_id:, user_id:, reason:)
6
6
  attrs = { user_id: user_id, reason: reason }
7
7
 
8
- response = post_request("moderation/warnings?#{URI.encode_www_form(broadcaster_id: broadcaster_id, moderator_id: moderator_id)}", body: { data: attrs })
9
- Collection.from_response(response, type: Warning)
8
+ response = post_request(query_path("moderation/warnings", broadcaster_id: broadcaster_id, moderator_id: moderator_id), body: { data: attrs })
9
+ collection(response, type: Warning)
10
10
  end
11
11
  end
12
12
  end
@@ -1,7 +1,7 @@
1
1
  module Twitch
2
2
  class WhispersResource < Resource
3
3
  def create(from_user_id:, to_user_id:, message:)
4
- post_request("whispers", body: { from_user_id: from_user_id, to_user_id: to_user_id, message: message })
4
+ post_request(query_path("whispers", from_user_id: from_user_id, to_user_id: to_user_id), body: { message: message })
5
5
  end
6
6
  end
7
7
  end
@@ -1,3 +1,3 @@
1
1
  module Twitch
2
- VERSION = "1.11.0"
2
+ VERSION = "2.1.0"
3
3
  end