twitchrb 1.10.0 → 1.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9fbfacc0abeb1677c2f16b79eefeaaaedb1b7ff4f3506fcf2f465ffb82735f78
4
- data.tar.gz: 2a04bfacd176f1287cacfe55c939550a8b2b92f63b207aead8ae48dc47de0508
3
+ metadata.gz: 4a4de14d63208a337630d3530f24d90679645c2b4ad02d4f31beb96f687a6ff1
4
+ data.tar.gz: 2e49411b20156a55d93fa60e1dba87bf25121c228f601e10f6861e01aa9b0094
5
5
  SHA512:
6
- metadata.gz: d472b2b93b0aaacaf635079e4dd1a35d213026f0c517dd7c2f6b411f7a1caf8121fd652b46672c145730cd648f94aa5206c0655fd8d2bf08508f11b257facdb4
7
- data.tar.gz: f175c566b480a93032f9fb19cfeb490f9834d5c8f58aa4f2141fd2488f3191047f556cd90518f166e00acd8ffb581e3b214161a90aee317d4621e49dbdfa3158
6
+ metadata.gz: 81bcbe6402c4cdf5f3cc90ac181aec22d8d46990053846f1bce3b92aead9f71462857c6b92e1b5dcbd4c34d1e635e057ce86516c856db1944252d01dc1e90b56
7
+ data.tar.gz: cfff782da54f1ffde515c8911febf8d251c1df677d0ba481399cfc86d2bd95c9885e0d5f6682aadbb96be98dc4cecf0ceda8aa72b073abdda842b4dc8e7ce120
@@ -8,7 +8,7 @@ jobs:
8
8
  continue-on-error: true
9
9
  steps:
10
10
  - name: Checkout code
11
- uses: actions/checkout@v6
11
+ uses: actions/checkout@v7
12
12
  - name: Set up Ruby
13
13
  uses: ruby/setup-ruby@v1
14
14
  with:
@@ -28,7 +28,7 @@ jobs:
28
28
  - '3.4'
29
29
  - '4.0'
30
30
  steps:
31
- - uses: actions/checkout@v6
31
+ - uses: actions/checkout@v7
32
32
  - uses: ruby/setup-ruby@v1
33
33
  with:
34
34
  ruby-version: ${{ matrix.ruby_version }}
data/CHANGELOG.md CHANGED
@@ -4,7 +4,30 @@ All notable changes to `twitchrb` are documented in this file.
4
4
 
5
5
  Published release notes were sourced from GitHub releases where available. Older tag-only versions and the current unreleased work were reconstructed from local git history.
6
6
 
7
- ## [1.10.0] - Unreleased
7
+ ## [1.11.0] - 2026-08-11
8
+
9
+ ### Fixed
10
+ - Fixed a `NoMethodError` crash when API responses have non-JSON bodies, such as `stream_schedule.icalendar` (which was completely broken) and HTML error pages from proxies.
11
+ - Fixed `users.retrieve` and `games.retrieve` returning `nil` when a plural lookup (`ids`, `usernames`, `names`) matched exactly one item; plural lookups now always return a `Collection`. Singular lookups with no match now return `nil` instead of an empty object.
12
+ - Fixed `oauth.revoke` always returning `true`; it now returns `false` when revocation fails, matching `validate`, `create`, and `refresh`.
13
+ - Fixed `blocked_terms.create` returning a `BannedUser` instead of a `BlockedTerm`.
14
+ - Fixed `suspicious_users.delete` crashing on 204 No Content responses; it now returns `true`.
15
+ - Fixed query string values not being escaped in request paths. Notably, `users.update_color` silently dropped hex colors like `#9146FF`.
16
+ - Errors are now raised for any 4xx/5xx response; previously 422, 502, and 504 slipped through unraised.
17
+ - `Collection#total` now reports the `total` field from the API for paginated endpoints instead of the page size, and responses without a `data` key return an empty collection instead of raising.
18
+ - Removed a leftover debug `puts` in `users.get_color`.
19
+ - Error messages for unmapped statuses no longer duplicate the Twitch message.
20
+ - Updated the minimum Faraday version to 2.14.3 to fix the uncontrolled recursion denial-of-service vulnerability in `NestedParamsEncoder` (CVE-2026-54297).
21
+
22
+ ### Changed
23
+ - The `auto_retry_rate_limit` client option (default `true`) now works: requests that hit a 429 wait until the rate limit window resets and retry once. Previously the option was accepted but ignored.
24
+ - All API errors now inherit from `Twitch::Error`, so they can be rescued with a single base class.
25
+ - `eventsub_subscriptions.create` now goes through the shared request helpers, gaining request path validation and rate limit auto-retry.
26
+
27
+ ### Added
28
+ - `users.get_color` accepts an array for `user_ids`, in line with other multi-id endpoints. Comma-separated strings still work.
29
+
30
+ ## [1.10.0] - 2026-05-16
8
31
 
9
32
  ### Added
10
33
  - Added custom power-up API support.
data/Gemfile.lock CHANGED
@@ -2,7 +2,7 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  twitchrb (1.10.0)
5
- faraday (>= 2.14.1, < 3)
5
+ faraday (>= 2.14.3, < 3)
6
6
  ostruct (~> 0.6.0)
7
7
 
8
8
  GEM
@@ -27,7 +27,7 @@ GEM
27
27
  benchmark (0.5.0)
28
28
  bigdecimal (4.1.2)
29
29
  cgi (0.5.1)
30
- concurrent-ruby (1.3.6)
30
+ concurrent-ruby (1.3.8)
31
31
  connection_pool (3.0.2)
32
32
  crack (1.0.1)
33
33
  bigdecimal
@@ -36,7 +36,7 @@ GEM
36
36
  dotenv (3.2.0)
37
37
  drb (2.2.3)
38
38
  erb (6.0.4)
39
- faraday (2.14.2)
39
+ faraday (2.14.3)
40
40
  faraday-net_http (>= 2.0, < 3.5)
41
41
  json
42
42
  logger
@@ -51,7 +51,7 @@ GEM
51
51
  prism (>= 1.3.0)
52
52
  rdoc (>= 4.0.0)
53
53
  reline (>= 0.4.2)
54
- json (2.19.5)
54
+ json (2.21.2)
55
55
  language_server-protocol (3.17.0.5)
56
56
  lint_roller (1.1.0)
57
57
  logger (1.7.0)
@@ -6,10 +6,11 @@ module Twitch
6
6
 
7
7
  def self.from_response(response, type:)
8
8
  body = response.body
9
+ data = body["data"] || []
9
10
 
10
11
  new(
11
- data: body["data"].map { |attrs| type.new(attrs) },
12
- total: body["data"].count,
12
+ data: data.map { |attrs| type.new(attrs) },
13
+ total: body["total"] || data.count,
13
14
  cursor: body.dig("pagination", "cursor")
14
15
  )
15
16
  end
@@ -17,7 +18,7 @@ module Twitch
17
18
  def initialize(data:, total:, cursor:)
18
19
  @data = data
19
20
  @total = total
20
- @cursor = cursor.nil? ? nil : cursor
21
+ @cursor = cursor
21
22
  end
22
23
 
23
24
  def each(&block)
@@ -1,5 +1,6 @@
1
1
  module Twitch
2
- class ErrorGenerator < StandardError
2
+ class ErrorGenerator < Error
3
+ attr_reader :response_body
3
4
  attr_reader :http_status_code
4
5
  attr_reader :twitch_error_code
5
6
  attr_reader :twitch_error_message
@@ -40,10 +41,11 @@ module Twitch
40
41
  end
41
42
 
42
43
  def build_message
43
- if twitch_error_code.nil?
44
- return "Error #{@http_status_code}: #{error_message}"
44
+ message = "Error #{@http_status_code}: #{error_message}"
45
+ if twitch_error_message && twitch_error_message != error_message
46
+ message += " '#{twitch_error_message}'"
45
47
  end
46
- "Error #{@http_status_code}: #{error_message} '#{twitch_error_message}'"
48
+ message
47
49
  end
48
50
  end
49
51
 
data/lib/twitch/oauth.rb CHANGED
@@ -43,9 +43,7 @@ module Twitch
43
43
  token: token
44
44
  })
45
45
 
46
- JSON.parse(response.body, object_class: OpenStruct) if response.status != 200
47
-
48
- true
46
+ response.status == 200
49
47
  end
50
48
 
51
49
  private
@@ -10,27 +10,40 @@ module Twitch
10
10
 
11
11
  def get_request(url, params: {}, headers: {})
12
12
  validate_request_path!(url)
13
- handle_response client.connection.get(url, params, headers)
13
+ execute_request { client.connection.get(url, params, headers) }
14
14
  end
15
15
 
16
16
  def post_request(url, body:, headers: {})
17
17
  validate_request_path!(url)
18
- handle_response client.connection.post(url, body, headers)
18
+ execute_request { client.connection.post(url, body, headers) }
19
19
  end
20
20
 
21
21
  def patch_request(url, body:, headers: {})
22
22
  validate_request_path!(url)
23
- handle_response client.connection.patch(url, body, headers)
23
+ execute_request { client.connection.patch(url, body, headers) }
24
24
  end
25
25
 
26
26
  def put_request(url, body:, headers: {})
27
27
  validate_request_path!(url)
28
- handle_response client.connection.put(url, body, headers)
28
+ execute_request { client.connection.put(url, body, headers) }
29
29
  end
30
30
 
31
31
  def delete_request(url, params: {}, headers: {})
32
32
  validate_request_path!(url)
33
- handle_response client.connection.delete(url, params, headers)
33
+ execute_request { client.connection.delete(url, params, headers) }
34
+ end
35
+
36
+ def execute_request
37
+ response = yield
38
+
39
+ # Retry once after a 429, waiting until the rate limit window resets
40
+ if response.status == 429 && client.auto_retry_rate_limit
41
+ update_rate_limit(response)
42
+ client.rate_limiter.wait_if_rate_limited
43
+ response = yield
44
+ end
45
+
46
+ handle_response(response)
34
47
  end
35
48
 
36
49
  def handle_response(response)
@@ -44,8 +57,8 @@ module Twitch
44
57
  end
45
58
 
46
59
  def error?(response)
47
- [ 400, 401, 403, 404, 409, 429, 500, 501, 503 ].include?(response.status) ||
48
- response.body&.key?("error")
60
+ response.status >= 400 ||
61
+ (response.body.is_a?(Hash) && response.body.key?("error"))
49
62
  end
50
63
 
51
64
  def raise_error(response)
@@ -58,8 +71,6 @@ module Twitch
58
71
  raise error if error
59
72
  end
60
73
 
61
- private
62
-
63
74
  def update_rate_limit(response)
64
75
  client.rate_limiter.update(response.headers)
65
76
  client.rate_limiter.warn_if_approaching(threshold: client.rate_limit_threshold)
@@ -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?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}", body: attrs)
8
+ post_request("chat/announcements?#{URI.encode_www_form(broadcaster_id: broadcaster_id, moderator_id: moderator_id)}", body: attrs)
9
9
  end
10
10
  end
11
11
  end
@@ -22,7 +22,7 @@ module Twitch
22
22
  end
23
23
 
24
24
  def settings(broadcaster_id:, moderator_id:)
25
- response = get_request("moderation/automod/settings?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}")
25
+ response = get_request("moderation/automod/settings", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id })
26
26
  AutomodSetting.new response.body.dig("data")[0]
27
27
  end
28
28
 
@@ -1,7 +1,7 @@
1
1
  module Twitch
2
2
  class BadgesResource < Resource
3
3
  def channel(broadcaster_id:)
4
- response = get_request("chat/badges?broadcaster_id=#{broadcaster_id}")
4
+ response = get_request("chat/badges", params: { broadcaster_id: broadcaster_id })
5
5
  Collection.from_response(response, type: Badge)
6
6
  end
7
7
 
@@ -17,7 +17,7 @@ module Twitch
17
17
  # Required scope: moderator:manage:banned_users
18
18
  # moderator_id must match the currently authenticated user. Can be either the broadcaster ID or moderator ID
19
19
  def delete(broadcaster_id:, moderator_id:, user_id:)
20
- delete_request("moderation/bans?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}&user_id=#{user_id}")
20
+ delete_request("moderation/bans", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id, user_id: user_id })
21
21
  end
22
22
  end
23
23
  end
@@ -13,13 +13,13 @@ module Twitch
13
13
  def create(broadcaster_id:, moderator_id:, text:)
14
14
  attrs = { broadcaster_id: broadcaster_id, moderator_id: moderator_id, text: text }
15
15
  response = post_request("moderation/blocked_terms", body: attrs)
16
- BannedUser.new response.body.dig("data")[0]
16
+ BlockedTerm.new response.body.dig("data")[0]
17
17
  end
18
18
 
19
19
  # Required scope: moderator:manage:blocked_terms
20
20
  # moderator_id must match the currently authenticated user. Can be either the broadcaster ID or moderator ID
21
21
  def delete(broadcaster_id:, moderator_id:, id:)
22
- delete_request("moderation/blocked_terms?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}&id=#{id}")
22
+ delete_request("moderation/blocked_terms", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id, id: id })
23
23
  end
24
24
  end
25
25
  end
@@ -1,7 +1,7 @@
1
1
  module Twitch
2
2
  class ChannelsResource < Resource
3
3
  def retrieve(id:)
4
- Channel.new get_request("channels?broadcaster_id=#{id}").body.dig("data")[0]
4
+ Channel.new get_request("channels", params: { broadcaster_id: id }).body.dig("data")[0]
5
5
  end
6
6
 
7
7
  # Retrieve a list of broadcasters a specified user follows
@@ -42,12 +42,12 @@ module Twitch
42
42
 
43
43
  # Requires scope: channel:read:editors
44
44
  def editors(broadcaster_id:)
45
- response = get_request("channels/editors?broadcaster_id=#{broadcaster_id}")
45
+ response = get_request("channels/editors", params: { broadcaster_id: broadcaster_id })
46
46
  Collection.from_response(response, type: ChannelEditor)
47
47
  end
48
48
 
49
49
  def stream_key(broadcaster_id:)
50
- response = get_request("streams/key?broadcaster_id=#{broadcaster_id}")
50
+ response = get_request("streams/key", params: { broadcaster_id: broadcaster_id })
51
51
  StreamKey.new(response.body.dig("data")[0])
52
52
  end
53
53
 
@@ -3,7 +3,7 @@ module Twitch
3
3
  # Required scope: channel:read:charity
4
4
  # Broadcaster ID must match the user in the OAuth token
5
5
  def list(broadcaster_id:)
6
- response = get_request("charity/campaigns?broadcaster_id=#{broadcaster_id}")
6
+ response = get_request("charity/campaigns", params: { broadcaster_id: broadcaster_id })
7
7
  if response.body.dig("data")[0]
8
8
  CharityCampaign.new(response.body.dig("data")[0])
9
9
  else
@@ -16,7 +16,7 @@ module Twitch
16
16
 
17
17
  # moderator_id must match the user in the OAuth token
18
18
  def delete(broadcaster_id:, moderator_id:, message_id:)
19
- delete_request("moderation/chat?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}&message_id=#{message_id}")
19
+ delete_request("moderation/chat", params: { broadcaster_id: broadcaster_id, moderator_id: moderator_id, message_id: message_id })
20
20
  end
21
21
  end
22
22
  end
@@ -12,8 +12,8 @@ module Twitch
12
12
  # Broadcaster ID must match the user in the OAuth token
13
13
  def update(broadcaster_id:, reward_id:, redemption_id:, status:)
14
14
  attributes = { status: status.upcase }
15
- url = "channel_points/custom_rewards/redemptions?broadcaster_id=#{broadcaster_id}&reward_id=#{reward_id}&id=#{redemption_id}&status=#{status.upcase}"
16
- response = patch_request(url, body: attributes)
15
+ query = URI.encode_www_form(broadcaster_id: broadcaster_id, reward_id: reward_id, id: redemption_id, status: status.upcase)
16
+ response = patch_request("channel_points/custom_rewards/redemptions?#{query}", body: attributes)
17
17
 
18
18
  CustomRewardRedemption.new(response.body.dig("data")[0]) if response.success?
19
19
  end
@@ -1,7 +1,7 @@
1
1
  module Twitch
2
2
  class EmotesResource < Resource
3
3
  def channel(broadcaster_id:)
4
- response = get_request("chat/emotes?broadcaster_id=#{broadcaster_id}")
4
+ response = get_request("chat/emotes", params: { broadcaster_id: broadcaster_id })
5
5
  Collection.from_response(response, type: Emote)
6
6
  end
7
7
 
@@ -11,7 +11,7 @@ module Twitch
11
11
  end
12
12
 
13
13
  def sets(emote_set_id:)
14
- response = get_request("chat/emotes/set?emote_set_id=#{emote_set_id}")
14
+ response = get_request("chat/emotes/set", params: { emote_set_id: emote_set_id })
15
15
  Collection.from_response(response, type: Emote)
16
16
  end
17
17
  end
@@ -7,20 +7,16 @@ module Twitch
7
7
 
8
8
  def create(type:, version:, condition:, transport:, **params)
9
9
  attributes = { type: type, version: version, condition: condition, transport: transport }.merge(params)
10
- response = client.connection.post("eventsub/subscriptions", attributes)
11
- update_rate_limit(response)
12
-
13
- if response.status == 409
14
- raise Twitch::Errors::EventsubSubscriptionConflictError.new(
15
- response.body,
16
- response.status,
17
- existing_subscription_id: response.body.dig("data", 0, "id")
18
- )
19
- end
20
-
21
- return raise_error(response) if error?(response)
10
+ response = post_request("eventsub/subscriptions", body: attributes)
22
11
 
23
12
  EventsubSubscription.new(response.body.dig("data")[0]) if response.success?
13
+ rescue Twitch::Errors::ConflictError => error
14
+ existing_id = error.response_body.dig("data", 0, "id") if error.response_body.is_a?(Hash)
15
+ raise Twitch::Errors::EventsubSubscriptionConflictError.new(
16
+ error.response_body,
17
+ error.http_status_code,
18
+ existing_subscription_id: existing_id
19
+ )
24
20
  end
25
21
 
26
22
  def delete(id:)
@@ -14,12 +14,10 @@ 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.from_response(response, type: Game)
23
21
  end
24
22
  end
25
23
 
@@ -17,7 +17,7 @@ module Twitch
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,7 +11,7 @@ module Twitch
11
11
 
12
12
  # broadcaster_id must match the user in the OAuth token
13
13
  def delete(broadcaster_id:)
14
- delete_request("raids?broadcaster_id=#{broadcaster_id}")
14
+ delete_request("raids", params: { broadcaster_id: broadcaster_id })
15
15
  end
16
16
  end
17
17
  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("chat/shoutouts?#{URI.encode_www_form(from_broadcaster_id: from, to_broadcaster_id: to, moderator_id: moderator_id)}", body: nil)
9
9
  end
10
10
  end
11
11
  end
@@ -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
+ "moderation/suspicious_users?#{URI.encode_www_form(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
@@ -14,12 +14,10 @@ module Twitch
14
14
  end
15
15
 
16
16
  body = response.body.dig("data")
17
- if id || username && body.count == 1
18
- User.new body[0]
19
- elsif ids || usernames && body.count > 1
20
- Collection.from_response(response, type: User)
17
+ if id || username
18
+ body.empty? ? nil : User.new(body[0])
21
19
  else
22
- nil
20
+ Collection.from_response(response, type: User)
23
21
  end
24
22
  end
25
23
 
@@ -31,13 +29,13 @@ module Twitch
31
29
  end
32
30
 
33
31
  def get_color(user_id: nil, user_ids: nil)
34
- if user_ids != nil
35
- users = user_ids.split(",").map { |i| "user_id=#{i.strip}" }.join("&")
36
- puts "chat/color?#{users}"
37
- response = get_request("chat/color?#{users}")
32
+ if user_ids
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}")
38
36
  Collection.from_response(response, type: UserColor)
39
37
  else
40
- response = get_request("chat/color?user_id=#{user_id}")
38
+ response = get_request("chat/color", params: { user_id: user_id })
41
39
  UserColor.new response.body.dig("data")[0]
42
40
  end
43
41
  end
@@ -46,7 +44,7 @@ module Twitch
46
44
  # Required scope: user:manage:chat_color
47
45
  # user_id must be the currently authenticated user
48
46
  def update_color(user_id:, color:)
49
- put_request("chat/color?user_id=#{user_id}&color=#{color}", body: {})
47
+ put_request("chat/color?#{URI.encode_www_form(user_id: user_id, color: color)}", body: {})
50
48
  end
51
49
 
52
50
  # Deprecated.
@@ -61,18 +59,18 @@ module Twitch
61
59
 
62
60
  # Required scope: user:read:blocked_users
63
61
  def blocks(broadcaster_id:, **params)
64
- response = get_request("users/blocks?broadcaster_id=#{broadcaster_id}", params: params)
62
+ response = get_request("users/blocks", params: params.merge(broadcaster_id: broadcaster_id))
65
63
  Collection.from_response(response, type: BlockedUser)
66
64
  end
67
65
 
68
66
  # Required scope: user:manage:blocked_users
69
67
  def block_user(target_user_id:, **attributes)
70
- put_request("users/blocks?target_user_id=#{target_user_id}", body: attributes)
68
+ put_request("users/blocks?#{URI.encode_www_form(target_user_id: target_user_id)}", body: attributes)
71
69
  end
72
70
 
73
71
  # Required scope: user:manage:blocked_users
74
72
  def unblock_user(target_user_id:)
75
- delete_request("users/blocks?target_user_id=#{target_user_id}")
73
+ delete_request("users/blocks", params: { target_user_id: target_user_id })
76
74
  end
77
75
 
78
76
  # A quick method to see if a user is following a channel
@@ -16,7 +16,7 @@ module Twitch
16
16
 
17
17
  # Required scope: channel:manage:videos
18
18
  def delete(video_id:)
19
- delete_request("videos?id=#{video_id}")
19
+ delete_request("videos", params: { id: video_id })
20
20
  end
21
21
  end
22
22
  end
@@ -11,7 +11,7 @@ module Twitch
11
11
  end
12
12
 
13
13
  def delete(broadcaster_id:, user_id:)
14
- delete_request("channels/vips?broadcaster_id=#{broadcaster_id}&user_id=#{user_id}")
14
+ delete_request("channels/vips", params: { broadcaster_id: broadcaster_id, user_id: user_id })
15
15
  end
16
16
  end
17
17
  end
@@ -5,7 +5,7 @@ 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?broadcaster_id=#{broadcaster_id}&moderator_id=#{moderator_id}", body: { data: attrs })
8
+ response = post_request("moderation/warnings?#{URI.encode_www_form(broadcaster_id: broadcaster_id, moderator_id: moderator_id)}", body: { data: attrs })
9
9
  Collection.from_response(response, type: Warning)
10
10
  end
11
11
  end
@@ -1,3 +1,3 @@
1
1
  module Twitch
2
- VERSION = "1.10.0"
2
+ VERSION = "1.11.0"
3
3
  end
data/twitchrb.gemspec CHANGED
@@ -24,6 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = [ "lib" ]
26
26
 
27
- spec.add_dependency "faraday", ">= 2.14.1", "< 3"
27
+ spec.add_dependency "faraday", ">= 2.14.3", "< 3"
28
28
  spec.add_dependency "ostruct", "~> 0.6.0"
29
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twitchrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dean Perry
@@ -15,7 +15,7 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 2.14.1
18
+ version: 2.14.3
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
21
  version: '3'
@@ -25,7 +25,7 @@ dependencies:
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 2.14.1
28
+ version: 2.14.3
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '3'
@@ -190,7 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
190
  - !ruby/object:Gem::Version
191
191
  version: '0'
192
192
  requirements: []
193
- rubygems_version: 4.0.10
193
+ rubygems_version: 4.0.16
194
194
  specification_version: 4
195
195
  summary: A Ruby library for interacting with the Twitch Helix API
196
196
  test_files: []