├── .cirrus.yaml ├── .editorconfig ├── .gitignore ├── .remarkrc.yaml ├── .rspec ├── .rubocop.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── lib ├── twitch-api.rb └── twitch │ ├── api_error.rb │ ├── automod_message_status.rb │ ├── bits_leader.rb │ ├── category.rb │ ├── channel.rb │ ├── cheermote.rb │ ├── cheermote_tier.rb │ ├── cheermote_tier_image.rb │ ├── cheermote_tier_images.rb │ ├── client.rb │ ├── client │ ├── custom_rewards.rb │ ├── extensions.rb │ ├── games.rb │ ├── moderation.rb │ ├── streams.rb │ ├── subscriptions.rb │ └── users.rb │ ├── clip.rb │ ├── custom_reward.rb │ ├── editor.rb │ ├── entitlement_grant_url.rb │ ├── extension.rb │ ├── extensions_by_types.rb │ ├── game.rb │ ├── game_analytic.rb │ ├── moderation_event.rb │ ├── moderator.rb │ ├── redemption.rb │ ├── response.rb │ ├── stream.rb │ ├── stream_marker.rb │ ├── stream_metadata.rb │ ├── subscription.rb │ ├── user.rb │ ├── user_ban.rb │ ├── user_follow.rb │ ├── version.rb │ └── video.rb ├── package.json ├── spec ├── cassettes │ └── Twitch_Client │ │ ├── _get_bits_leaderboard │ │ ├── when_token_type_is_application_ │ │ │ └── with_correct_client_credentials │ │ │ │ └── with_tokens │ │ │ │ └── 1_1_2_1_1_1.yml │ │ └── when_token_type_is_user_ │ │ │ ├── with_access_token_ │ │ │ ├── when_access_token_is_actual │ │ │ │ └── 1_1_1_1_1_1.yml │ │ │ ├── when_access_token_is_outdated │ │ │ │ ├── with_refresh_token_ │ │ │ │ │ └── 1_1_1_1_2_1_1.yml │ │ │ │ └── without_refresh_token_ │ │ │ │ │ └── 1_1_1_1_2_2_1.yml │ │ │ └── when_access_token_was_actual_but_became_outdated │ │ │ │ └── with_refresh_token_ │ │ │ │ └── 1_1_1_1_3_1_1.yml │ │ │ └── without_tokens │ │ │ └── 1_1_1_2_1.yml │ │ ├── _get_channel_editors │ │ ├── with_options │ │ │ ├── when_token_type_is_application │ │ │ │ └── 1_18_2_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ │ ├── when_broadcaster_ID_is_foreign_channel │ │ │ │ └── 1_18_2_2_1_1.yml │ │ │ │ └── when_broadcaster_ID_is_your_own │ │ │ │ └── data │ │ │ │ └── 1_18_2_2_2_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ └── 1_18_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── 1_18_1_2_1.yml │ │ ├── _get_channels │ │ ├── with_options │ │ │ ├── when_broadcaster_ID_is_an_Array_of_values │ │ │ │ └── data │ │ │ │ │ └── 1_9_2_2_1_1.yml │ │ │ └── when_broadcaster_ID_is_single_value │ │ │ │ └── data │ │ │ │ └── 1_9_2_1_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ └── 1_9_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── 1_9_1_2_1.yml │ │ ├── _get_cheermotes │ │ ├── with_options │ │ │ ├── when_token_type_is_application │ │ │ │ └── data │ │ │ │ │ └── 1_17_2_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ │ └── data │ │ │ │ └── 1_17_2_2_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ ├── 1_17_1_1_1.yml │ │ │ └── data │ │ │ │ └── 1_17_1_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── data │ │ │ └── 1_17_1_2_1_1.yml │ │ ├── _get_clips │ │ ├── 1_2_1.yml │ │ └── _broadcaster_id │ │ │ └── 1_2_2_1.yml │ │ ├── _get_custom_reward │ │ ├── with_options │ │ │ ├── when_token_type_is_application │ │ │ │ └── 1_14_2_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ │ └── data │ │ │ │ └── 1_14_2_2_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ └── 1_14_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── 1_14_1_2_1.yml │ │ ├── _get_games │ │ └── data_length │ │ │ └── 1_6_1_1.yml │ │ ├── _get_moderators │ │ ├── with_options │ │ │ ├── when_broadcaster_ID_is_foreign_channel │ │ │ │ └── 1_12_2_1_1.yml │ │ │ └── when_broadcaster_ID_is_your_own │ │ │ │ └── data │ │ │ │ └── 1_12_2_2_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ └── 1_12_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── 1_12_1_2_1.yml │ │ ├── _get_stream_key │ │ ├── with_options │ │ │ ├── when_token_type_is_application │ │ │ │ └── 1_16_2_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ │ └── data │ │ │ │ └── 1_16_2_2_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ └── 1_16_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── 1_16_1_2_1.yml │ │ ├── _get_streams │ │ ├── with_empty_kwargs │ │ │ └── data_length │ │ │ │ └── 1_3_1_1_1.yml │ │ └── with_username │ │ │ ├── 1_3_2_1.yml │ │ │ └── viewer_count │ │ │ └── 1_3_2_2_1.yml │ │ ├── _get_top_games │ │ └── data_length │ │ │ └── 1_7_1_1.yml │ │ ├── _get_user_active_extensions │ │ ├── with_options │ │ │ └── when_token_type_is_application │ │ │ │ └── data │ │ │ │ └── 1_13_2_1_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ └── 1_13_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── returns_extensions_for_the_authentificated_user.yml │ │ ├── _get_users │ │ └── when_token_type_is_application_ │ │ │ ├── with_correct_client_credentials │ │ │ ├── with_tokens │ │ │ │ └── 1_4_1_1_1_1.yml │ │ │ └── without_tokens │ │ │ │ └── 1_4_1_1_2_1.yml │ │ │ └── with_incorrect_client_credentials │ │ │ ├── with_tokens │ │ │ └── 1_4_1_2_1_1.yml │ │ │ └── without_tokens │ │ │ └── 1_4_1_2_2_1.yml │ │ ├── _get_users_follows │ │ └── 1_5_1.yml │ │ ├── _get_videos │ │ ├── data │ │ │ └── 1_8_1_1.yml │ │ └── pagination_cursor │ │ │ └── 1_8_2_1.yml │ │ ├── _modify_channel │ │ ├── when_everything_is_OK │ │ │ └── 1_11_1_1.yml │ │ └── when_game_ID_is_incorrect │ │ │ └── 1_11_2_1.yml │ │ ├── _search_categories │ │ ├── with_options │ │ │ ├── when_token_type_is_application │ │ │ │ └── data │ │ │ │ │ └── 1_15_2_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ │ └── data │ │ │ │ └── 1_15_2_2_1_1.yml │ │ └── without_options │ │ │ ├── when_token_type_is_application │ │ │ └── 1_15_1_1_1.yml │ │ │ └── when_token_type_is_user │ │ │ └── 1_15_1_2_1.yml │ │ └── _search_channels │ │ ├── with_options │ │ └── data │ │ │ └── 1_10_2_1_1.yml │ │ └── without_options │ │ ├── when_token_type_is_application │ │ └── 1_10_1_1_1.yml │ │ └── when_token_type_is_user │ │ └── 1_10_1_2_1.yml ├── spec_helper.rb └── twitch │ ├── client_spec.rb │ └── version_spec.rb └── twitch-api.gemspec /.cirrus.yaml: -------------------------------------------------------------------------------- 1 | bundle_cache: &bundle_cache 2 | bundle_cache: 3 | folder: /usr/local/bundle 4 | fingerprint_script: 5 | - echo $CIRRUS_OS 6 | - ruby -v 7 | - cat Gemfile 8 | - cat *.gemspec 9 | install_script: 10 | - gem install bundler 11 | - bundle update 12 | 13 | env: 14 | ## To suppress flood of warnings: 15 | ## https://github.com/cirruslabs/cirrus-ci-docs/issues/814 16 | ## https://github.com/rubygems/rubygems/issues/4466#issuecomment-818688569 17 | ## Global for: 18 | ## 1. different tasks (rubocop, test, etc.); 19 | ## 2. avoiding overriding `env` in specific cases like macOS. 20 | TMPDIR: $CIRRUS_WORKING_DIR 21 | 22 | remark_task: 23 | container: 24 | image: node 25 | node_modules_cache: 26 | folder: node_modules 27 | fingerprint_script: 28 | - echo $CIRRUS_OS 29 | - node -v 30 | - cat package.json 31 | install_script: npm install 32 | 33 | lint_script: npm run remark 34 | 35 | only_if: ($CIRRUS_BRANCH == 'master') || 36 | changesInclude( 37 | '.cirrus.yaml', '.gitignore', 'package.json', '.remarkrc.yaml', '**.md' 38 | ) 39 | 40 | rubocop_task: 41 | container: 42 | image: ruby:latest 43 | <<: *bundle_cache 44 | 45 | lint_script: bundle exec rubocop 46 | 47 | only_if: ($CIRRUS_BRANCH == 'master') || 48 | changesInclude( 49 | '.cirrus.yaml', '.gitignore', 'Gemfile', 'Rakefile', '.rubocop.yml', '*.gemspec', 50 | '**.rb', '**.ru' 51 | ) 52 | 53 | rspec_task: 54 | container: 55 | matrix: 56 | - image: ruby:3.0 57 | - image: ruby:3.1 58 | - image: ruby:3.2 59 | 60 | <<: *bundle_cache 61 | 62 | test_script: bundle exec rspec 63 | 64 | codecov_uploader_cache: 65 | folder: codecov 66 | fingerprint_script: 67 | - curl --no-progress-meter https://uploader.codecov.io/latest/linux/codecov.SHA256SUM 68 | populate_script: 69 | - mkdir -p codecov 70 | - curl https://uploader.codecov.io/latest/linux/codecov -o codecov/uploader 71 | - chmod +x codecov/uploader 72 | 73 | codecov_script: codecov/uploader 74 | 75 | only_if: ($CIRRUS_BRANCH == 'master') || 76 | changesInclude( 77 | '.cirrus.yaml', '.gitignore', 'Gemfile', 'Rakefile', '.rspec', '*.gemspec', 'lib/**', 78 | 'spec/**' 79 | ) 80 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | max_line_length = 100 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | 13 | # for a library or gem, you might want to ignore these files since the code is 14 | # intended to run in multiple environments; otherwise, check them in: 15 | Gemfile.lock 16 | .ruby-version 17 | .ruby-gemset 18 | package-lock.json 19 | /node_modules/ 20 | -------------------------------------------------------------------------------- /.remarkrc.yaml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - remark-preset-lint-recommended 3 | - - remark-lint-code-block-style 4 | - fenced 5 | - remark-gfm 6 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-performance 3 | - rubocop-rake 4 | - rubocop-rspec 5 | 6 | inherit_mode: 7 | merge: 8 | - Include 9 | - Exclude 10 | 11 | AllCops: 12 | TargetRubyVersion: 3.0 13 | NewCops: enable 14 | 15 | Layout/LineLength: 16 | Max: 100 17 | Layout/MultilineMethodCallIndentation: 18 | EnforcedStyle: indented 19 | Layout/MultilineOperationIndentation: 20 | EnforcedStyle: indented 21 | Layout/ArgumentAlignment: 22 | EnforcedStyle: with_fixed_indentation 23 | 24 | Naming/FileName: 25 | Exclude: 26 | - lib/twitch-api.rb 27 | 28 | Style/ParenthesesAroundCondition: 29 | AllowInMultilineConditions: true 30 | 31 | Metrics/BlockLength: 32 | Exclude: 33 | - spec/**/*.rb 34 | 35 | RSpec/MultipleMemoizedHelpers: 36 | Enabled: false 37 | RSpec/NestedGroups: 38 | Enabled: false 39 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | # Specify your gem's dependencies in twitch-api.gemspec 6 | gemspec 7 | 8 | group :development do 9 | gem 'bundler', '~> 2.1' 10 | gem 'pry-byebug', '~> 3.9' 11 | gem 'rake', '~> 13.0' 12 | end 13 | 14 | group :test do 15 | gem 'rspec', '~> 3.0' 16 | gem 'simplecov', '~> 0.22.0' 17 | gem 'simplecov-cobertura', '~> 2.1' 18 | gem 'vcr', '~> 6.0' 19 | end 20 | 21 | group :lint do 22 | gem 'rubocop', '~> 1.61.0' 23 | gem 'rubocop-performance', '~> 1.20.1' 24 | gem 'rubocop-rake', '~> 0.6.0' 25 | gem 'rubocop-rspec', '~> 2.26.1' 26 | end 27 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Maurice Wahba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | require 'rspec/core/rake_task' 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task default: :spec 9 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # frozen_string_literal: true 4 | 5 | require_relative '../lib/twitch-api' 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require 'irb' 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/twitch-api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'twitch/version' 4 | require_relative 'twitch/client' 5 | -------------------------------------------------------------------------------- /lib/twitch/api_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # An error returned by the API. 5 | class APIError < StandardError 6 | # HTTP status code of the response. 7 | attr_reader :status_code 8 | # Body content of the response. 9 | attr_reader :body 10 | 11 | def initialize(status_code, body) 12 | @status_code = status_code 13 | @body = body 14 | 15 | super(self.body['message']) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/twitch/automod_message_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A determination if AutoMod would allow a message in a particular channel's chat. 5 | class AutomodMessageStatus 6 | # ID of the message. Matches the field of the same name passed in the request. 7 | attr_reader :msg_id 8 | # Whether the message would meet AutoMod requirements for the channel. 9 | attr_reader :is_permitted 10 | 11 | def initialize(attributes = {}) 12 | attributes.each do |key, value| 13 | instance_variable_set :"@#{key}", value 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/twitch/bits_leader.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A user that is a leader for bits. 5 | class BitsLeader 6 | # ID of the user giving bits. 7 | attr_reader :user_id 8 | # Display name of the user giving bits. 9 | attr_reader :user_name 10 | # Ranking of the user giving bits. 11 | # Reflects the parent object's date range. 12 | attr_reader :rank 13 | # Number of bits given in the parent object's date range. 14 | attr_reader :score 15 | 16 | def initialize(attributes = {}) 17 | @user_id = attributes['user_id'] 18 | @user_name = attributes['user_name'] 19 | @rank = attributes['rank'] 20 | @score = attributes['score'] 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/twitch/category.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | ## Data class for categories 5 | class Category 6 | ## A URL to an image of the game’s box art or streaming category. 7 | attr_reader :box_art_url 8 | 9 | ## The name of the game or category. 10 | attr_reader :name 11 | 12 | ## An ID that uniquely identifies the game or category. 13 | attr_reader :id 14 | 15 | ## Not always available: 16 | 17 | ## The ID that IGDB uses to identify this game. 18 | ## If the IGDB ID is not available to Twitch, this field is set to an empty string. 19 | attr_reader :igdb_id 20 | 21 | def initialize(attributes = {}) 22 | attributes.each do |key, value| 23 | instance_variable_set :"@#{key}", value 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/twitch/channel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Data object for Twitch channels 5 | # https://dev.twitch.tv/docs/api/reference#get-channel-information 6 | class Channel 7 | # Twitch User ID of this channel owner. 8 | attr_reader :broadcaster_id 9 | # Broadcaster's user login name. 10 | attr_reader :broadcaster_login 11 | # Twitch user display name of this channel owner. 12 | attr_reader :broadcaster_name 13 | # Language of the channel. 14 | # A language value is either the ISO 639-1 two-letter code for a supported stream language 15 | # or "other". 16 | attr_reader :broadcaster_language 17 | # Current game ID being played on the channel . 18 | attr_reader :game_id 19 | # Name of the game being played on the channel. 20 | attr_reader :game_name 21 | # Title of the stream. 22 | attr_reader :title 23 | # Stream delay in seconds. 24 | attr_reader :delay 25 | 26 | # Some endpoints, like "Search Channels", can return different fields 27 | attr_reader :id 28 | attr_reader :display_name 29 | # A Boolean value that determines whether the broadcaster is streaming live. 30 | # Is true if the broadcaster is streaming live; otherwise, false. 31 | attr_reader :is_live 32 | # The tags applied to the channel. 33 | attr_reader :tags 34 | # A URL to a thumbnail of the broadcaster’s profile image. 35 | attr_reader :thumbnail_url 36 | # The UTC date and time (in RFC3339 format) of when the broadcaster started streaming. 37 | # The string is empty if the broadcaster is not streaming live. 38 | attr_reader :started_at 39 | 40 | def initialize(attributes = {}) 41 | attributes.each do |key, value| 42 | instance_variable_set :"@#{key}", value 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/twitch/cheermote.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'cheermote_tier' 4 | 5 | module Twitch 6 | ## Data class for Cheermotes, animated emotes that viewers can assign Bits to. 7 | class Cheermote 8 | ## The name portion of the Cheermote string that you use in chat to cheer Bits. 9 | ## The full Cheermote string is the concatenation of {prefix} + {number of Bits}. 10 | ## For example, if the prefix is “Cheer” and you want to cheer 100 Bits, 11 | ## the full Cheermote string is Cheer100. 12 | ## When the Cheermote string is entered in chat, 13 | ## Twitch converts it to the image associated with the Bits tier that was cheered. 14 | attr_reader :prefix 15 | 16 | ## A list of tier levels that the Cheermote supports. 17 | ## Each tier identifies the range of Bits that you can cheer at that tier level 18 | ## and an image that graphically identifies the tier level. 19 | attr_reader :tiers 20 | 21 | ## The type of Cheermote. Possible values are: 22 | ## * global_first_party — A Twitch-defined Cheermote that is shown in the Bits card. 23 | ## * global_third_party — A Twitch-defined Cheermote that is not shown in the Bits card. 24 | ## * channel_custom — A broadcaster-defined Cheermote. 25 | ## * display_only — Do not use; for internal use only. 26 | ## * sponsored — A sponsor-defined Cheermote. 27 | ## When used, the sponsor adds additional Bits to the amount that the user cheered. 28 | ## For example, if the user cheered Terminator100, the broadcaster might receive 110 Bits, 29 | ## which includes the sponsor's 10 Bits contribution. 30 | attr_reader :type 31 | 32 | ## The order that the Cheermotes are shown in the Bits card. 33 | ## The numbers may not be consecutive. For example, the numbers may jump from 1 to 7 to 13. 34 | ## The order numbers are unique within a Cheermote type (for example, global_first_party) 35 | ## but may not be unique amongst all Cheermotes in the response. 36 | attr_reader :order 37 | 38 | ## The date and time when this Cheermote was last updated. 39 | attr_reader :last_updated 40 | 41 | ## A Boolean value that indicates whether this Cheermote provides 42 | ## a charitable contribution match during charity campaigns. 43 | attr_reader :is_charitable 44 | 45 | def initialize(attributes = {}) 46 | attributes.each do |key, value| 47 | value = value.map { |tier| CheermoteTier.new(tier) } if key == 'tiers' 48 | 49 | instance_variable_set :"@#{key}", value 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/twitch/cheermote_tier.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'cheermote_tier_images' 4 | 5 | module Twitch 6 | ## Data class for Cheermote Tier. 7 | ## Each tier identifies the range of Bits that you can cheer at that tier level 8 | ## and an image that graphically identifies the tier level. 9 | class CheermoteTier 10 | ## The minimum number of Bits that you must cheer at this tier level. 11 | ## The maximum number of Bits that you can cheer at this level 12 | ## is determined by the required minimum Bits of the next tier level minus 1. 13 | ## For example, if `min_bits` is 1 and `min_bits` for the next tier is 100, 14 | ## the Bits range for this tier level is 1 through 99. 15 | ## The minimum Bits value of the last tier is the maximum number of Bits you can cheer 16 | ## using this Cheermote. For example, 10000. 17 | attr_reader :min_bits 18 | 19 | ## The tier level. Possible tiers are: 20 | ## * 1 21 | ## * 100 22 | ## * 500 23 | ## * 1000 24 | ## * 5000 25 | ## * 10000 26 | ## * 100000 27 | attr_reader :id 28 | 29 | ## The hex code of the color associated with this tier level (for example, `#979797`). 30 | attr_reader :color 31 | 32 | ## The animated and static image sets for the Cheermote. 33 | ## The dictionary of images is organized by theme, format, and size. 34 | ## The theme keys are `dark` and `light`. 35 | ## Each theme is a dictionary of formats: `animated` and `static`. 36 | ## Each format is a dictionary of sizes: `1`, `1.5`, `2`, `3`, and `4`. 37 | ## The value of each size contains the URL to the image. 38 | attr_reader :images 39 | 40 | ## A Boolean value that determines whether users can cheer at this tier level. 41 | attr_reader :can_cheer 42 | 43 | ## A Boolean value that determines whether this tier level is shown in the Bits card. 44 | ## Is `true` if this tier level is shown in the Bits card. 45 | attr_reader :show_in_bits_card 46 | 47 | def initialize(attributes = {}) 48 | attributes.each do |key, value| 49 | value = CheermoteTierImages.new(value) if key == 'images' 50 | 51 | instance_variable_set :"@#{key}", value 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/twitch/cheermote_tier_image.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | ## Data class for Cheermote Tier image, static and animated 5 | class CheermoteTierImage 6 | ## Each theme is a dictionary of formats: `animated` and `static`. 7 | ## Each format is a dictionary of sizes: `1`, `1.5`, `2`, `3`, and `4`. 8 | ## The value of each size contains the URL to the image. 9 | attr_reader :animated 10 | attr_reader :static 11 | 12 | def initialize(attributes = {}) 13 | attributes.each do |key, value| 14 | instance_variable_set :"@#{key}", value 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/twitch/cheermote_tier_images.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'cheermote_tier_image' 4 | 5 | module Twitch 6 | ## Data class for Cheermote Tier images by themes 7 | class CheermoteTierImages 8 | ## The theme keys are `dark` and `light`. 9 | ## Each theme is a dictionary of formats: `animated` and `static`. 10 | ## Each format is a dictionary of sizes: `1`, `1.5`, `2`, `3`, and `4`. 11 | ## The value of each size contains the URL to the image. 12 | attr_reader :dark 13 | attr_reader :light 14 | 15 | def initialize(attributes = {}) 16 | attributes.each do |key, value| 17 | instance_variable_set :"@#{key}", CheermoteTierImage.new(value) 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/twitch/client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'faraday' 4 | require 'faraday/parse_dates' 5 | require 'faraday/retry' 6 | require 'twitch_oauth2' 7 | 8 | require_relative 'response' 9 | 10 | require_relative 'api_error' 11 | require_relative 'bits_leader' 12 | require_relative 'category' 13 | require_relative 'channel' 14 | require_relative 'cheermote' 15 | require_relative 'clip' 16 | require_relative 'custom_reward' 17 | require_relative 'editor' 18 | require_relative 'entitlement_grant_url' 19 | require_relative 'extension' 20 | require_relative 'extensions_by_types' 21 | require_relative 'game' 22 | require_relative 'game_analytic' 23 | require_relative 'moderation_event' 24 | require_relative 'moderator' 25 | require_relative 'stream' 26 | require_relative 'stream_marker' 27 | require_relative 'stream_metadata' 28 | require_relative 'subscription' 29 | require_relative 'user' 30 | require_relative 'user_ban' 31 | require_relative 'user_follow' 32 | require_relative 'redemption' 33 | require_relative 'video' 34 | 35 | module Twitch 36 | # Core class for requests 37 | class Client 38 | # Base connection to Helix API. 39 | CONNECTION = Faraday.new( 40 | 'https://api.twitch.tv/helix', { 41 | headers: { 'User-Agent': "twitch-api ruby client #{Twitch::VERSION}" } 42 | } 43 | ) do |faraday| 44 | faraday.request :retry, 45 | exceptions: [*Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS, Faraday::ConnectionFailed] 46 | 47 | faraday.response :parse_dates 48 | 49 | faraday.request :json 50 | faraday.response :json 51 | end 52 | 53 | attr_reader :tokens 54 | 55 | # Initializes a Twitch client. 56 | # 57 | # - tokens [TwitchOAuth2::Tokens] Tokens object with their refreshing logic inside. 58 | # All client and authentication information (`client_id`, `:scopes`, etc.) stores there. 59 | def initialize(tokens:) 60 | @tokens = tokens 61 | 62 | CONNECTION.headers['Client-ID'] = self.tokens.client.client_id 63 | 64 | renew_authorization_header 65 | end 66 | 67 | def create_clip(options = {}) 68 | initialize_response Clip, post('clips', options) 69 | end 70 | 71 | def create_entitlement_grant_url(options = {}) 72 | initialize_response EntitlementGrantUrl, post('entitlements/upload', options) 73 | end 74 | 75 | def get_clips(options = {}) 76 | initialize_response Clip, get('clips', options) 77 | end 78 | 79 | def get_bits_leaderboard(options = {}) 80 | initialize_response BitsLeader, get('bits/leaderboard', options) 81 | end 82 | 83 | def get_cheermotes(options = {}) 84 | initialize_response Cheermote, get('bits/cheermotes', options) 85 | end 86 | 87 | require_relative 'client/extensions' 88 | include Extensions 89 | 90 | require_relative 'client/games' 91 | include Games 92 | 93 | require_relative 'client/moderation' 94 | include Moderation 95 | 96 | require_relative 'client/streams' 97 | include Streams 98 | 99 | require_relative 'client/subscriptions' 100 | include Subscriptions 101 | 102 | def get_videos(options = {}) 103 | initialize_response Video, get('videos', options) 104 | end 105 | 106 | require_relative 'client/users' 107 | include Users 108 | 109 | require_relative 'client/custom_rewards' 110 | include CustomRewards 111 | 112 | ## https://dev.twitch.tv/docs/api/reference#get-channel-information 113 | def get_channels(options = {}) 114 | initialize_response Channel, get('channels', options) 115 | end 116 | 117 | ## https://dev.twitch.tv/docs/api/reference/#search-channels 118 | def search_channels(options = {}) 119 | initialize_response Channel, get('search/channels', options) 120 | end 121 | 122 | ## https://dev.twitch.tv/docs/api/reference#modify-channel-information 123 | def modify_channel(options = {}) 124 | response = patch('channels', options) 125 | 126 | return true if response.body.empty? 127 | 128 | response.body 129 | end 130 | 131 | ## https://dev.twitch.tv/docs/api/reference/#start-commercial 132 | def start_commercial(options = {}) 133 | initialize_response nil, post('channels/commercial', options) 134 | end 135 | 136 | ## https://dev.twitch.tv/docs/api/reference/#get-channel-editors 137 | def get_channel_editors(options = {}) 138 | initialize_response Editor, get('channels/editors', options) 139 | end 140 | 141 | ## https://dev.twitch.tv/docs/api/reference/#search-categories 142 | def search_categories(options = {}) 143 | initialize_response Category, get('search/categories', options) 144 | end 145 | 146 | private 147 | 148 | def initialize_response(data_class, http_response) 149 | Response.new(data_class, http_response: http_response) 150 | end 151 | 152 | %w[get post put patch].each do |http_method| 153 | define_method http_method do |resource, params| 154 | request http_method, resource, params 155 | end 156 | end 157 | 158 | def renew_authorization_header 159 | CONNECTION.headers['Authorization'] = "Bearer #{tokens.access_token}" 160 | end 161 | 162 | def request(http_method, resource, params) 163 | http_response = CONNECTION.public_send http_method, resource, params 164 | 165 | if http_response.status == 401 166 | renew_authorization_header 167 | 168 | http_response = CONNECTION.public_send http_method, resource, params 169 | end 170 | 171 | return http_response if http_response.success? 172 | 173 | raise APIError.new(http_response.status, http_response.body) 174 | end 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /lib/twitch/client/custom_rewards.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | class Client 5 | ## API methods for custom rewards 6 | module CustomRewards 7 | def create_custom_reward(options = {}) 8 | initialize_response CustomReward, post('channel_points/custom_rewards', options) 9 | end 10 | 11 | def get_custom_reward(options = {}) 12 | initialize_response CustomReward, get('channel_points/custom_rewards', options) 13 | end 14 | 15 | def delete_custom_reward(options = {}) 16 | initialize_response CustomReward, delete('channel_points/custom_rewards', options) 17 | end 18 | 19 | def get_custom_reward_redemption(options = {}) 20 | initialize_response CustomReward, get('channel_points/custom_rewards/redemptions', options) 21 | end 22 | 23 | def update_custom_reward(options = {}) 24 | initialize_response CustomReward, patch('channel_points/custom_rewards', options) 25 | end 26 | 27 | def update_redemption_status(options = {}) 28 | initialize_response( 29 | CustomReward, 30 | patch('channel_points/custom_rewards/redemptions', options) 31 | ) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/twitch/client/extensions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | class Client 5 | # API methods for extensions 6 | module Extensions 7 | def get_user_extensions(options = {}) 8 | initialize_response Extension, get('users/extensions/list', options) 9 | end 10 | 11 | def get_user_active_extensions(options = {}) 12 | initialize_response ExtensionsByTypes, get('users/extensions', options) 13 | end 14 | 15 | def update_user_extensions(options = {}) 16 | initialize_response ExtensionsByTypes, put('users/extensions', options) 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/twitch/client/games.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | class Client 5 | ## API method for games 6 | module Games 7 | def get_games(options = {}) 8 | initialize_response Game, get('games', options) 9 | end 10 | 11 | def get_top_games(options = {}) 12 | initialize_response Game, get('games/top', options) 13 | end 14 | 15 | def get_game_analytics(options = {}) 16 | initialize_response GameAnalytic, get('analytics/games', options) 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/twitch/client/moderation.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | class Client 5 | # API methods for stream moderation 6 | module Moderation 7 | def check_automod_status(options = {}) 8 | initialize_response AutomodMessageStatus, post('moderation/enforcements/status', options) 9 | end 10 | 11 | def get_banned_events(options = {}) 12 | initialize_response ModerationEvent, get('moderation/banned/events', options) 13 | end 14 | 15 | def get_banned_users(options = {}) 16 | initialize_response UserBan, get('moderation/banned', options) 17 | end 18 | 19 | def get_moderators(options = {}) 20 | initialize_response Moderator, get('moderation/moderators', options) 21 | end 22 | 23 | def get_moderator_events(options = {}) 24 | initialize_response ModerationEvent, get('moderation/moderators/events', options) 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/twitch/client/streams.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | class Client 5 | ## API method for streams 6 | module Streams 7 | def create_stream_marker(options = {}) 8 | initialize_response StreamMarker, post('streams/markers', options) 9 | end 10 | 11 | def get_stream_markers(options = {}) 12 | initialize_response StreamMarkerResponse, get('streams/markers', options) 13 | end 14 | 15 | def get_streams(options = {}) 16 | initialize_response Stream, get('streams', options) 17 | end 18 | 19 | ## TODO: Can't find this method in documentation, test it 20 | def get_streams_metadata(options = {}) 21 | initialize_response StreamMetadata, get('streams/metadata', options) 22 | end 23 | 24 | def get_stream_key(options = {}) 25 | initialize_response nil, get('streams/key', options) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/twitch/client/subscriptions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | class Client 5 | # API methods for subscriptions 6 | module Subscriptions 7 | def get_broadcaster_subscription(options = {}) 8 | initialize_response Subscription, post('subscriptions', options) 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/twitch/client/users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | class Client 5 | ## API method for users 6 | module Users 7 | def get_users_follows(options = {}) 8 | initialize_response UserFollow, get('users/follows', options) 9 | end 10 | 11 | def get_users(options = {}) 12 | initialize_response User, get('users', options) 13 | end 14 | 15 | def update_user(options = {}) 16 | initialize_response User, put('users', options) 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/twitch/clip.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A small segment of a broadcast captured by another user. 5 | class Clip 6 | # ID of the clip. 7 | attr_reader :id 8 | # Title of the clip. 9 | attr_reader :title 10 | # Date the clip was created. 11 | attr_reader :created_at 12 | # URL of the clip. 13 | attr_reader :url 14 | # URL of the thumbnail image. 15 | attr_reader :thumbnail_url 16 | # URL for embedding the clip. 17 | attr_reader :embed_url 18 | # Number of views. 19 | attr_reader :view_count 20 | # Language of the originating broadcast. 21 | attr_reader :language 22 | # (User) ID of the clip's source broadcaster. 23 | attr_reader :broadcaster_id 24 | # (User) name of the clip's source broadcaster 25 | attr_reader :broadcaster_name 26 | # (User) ID of the clip's creator. 27 | attr_reader :creator_id 28 | # (User) name of the clip's creator. 29 | attr_reader :creator_name 30 | # ID of the game being played. 31 | attr_reader :game_id 32 | # ID of the archived broadcast (may not be available). 33 | attr_reader :video_id 34 | 35 | def initialize(attributes = {}) 36 | attributes.each do |key, value| 37 | instance_variable_set :"@#{key}", value 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/twitch/custom_reward.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | ## Data object for Twitch custom rewards 5 | class CustomReward 6 | ## Data object for images of Twitch custom rewards 7 | class CustomRewardImage 8 | # The URL to a small version of the image. 9 | attr_reader :url_1x 10 | # The URL to a medium version of the image. 11 | attr_reader :url_2x 12 | # The URL to a large version of the image. 13 | attr_reader :url_4x 14 | 15 | def initialize(attributes = {}) 16 | attributes.each do |key, value| 17 | instance_variable_set :"@#{key}", value 18 | end 19 | end 20 | end 21 | 22 | # The ID that uniquely identifies the broadcaster. 23 | attr_reader :broadcaster_id 24 | # The broadcaster’s login name. 25 | attr_reader :broadcaster_login 26 | # The broadcaster’s display name. 27 | attr_reader :broadcaster_name 28 | # The ID that uniquely identifies this custom reward. 29 | attr_reader :id 30 | # The title of the reward. 31 | attr_reader :title 32 | # The prompt shown to the viewer when they redeem the reward if user input is required 33 | # (see the `is_user_input_required` field). 34 | attr_reader :prompt 35 | # The cost of the reward in Channel Points. 36 | attr_reader :cost 37 | # A set of custom images for the reward. 38 | # This field is `nil` if the broadcaster didn’t upload images. 39 | attr_reader :image 40 | # A set of default images for the reward. 41 | attr_reader :default_image 42 | # The background color to use for the reward. 43 | # The color is in Hex format (for example, `#00E5CB`). 44 | attr_reader :background_color 45 | # A Boolean value that determines whether the reward is enabled. 46 | # Is `true` if enabled; otherwise, `false`. Disabled rewards aren’t shown to the user. 47 | attr_reader :is_enabled 48 | # A Boolean value that determines whether the user must enter information 49 | # when redeeming the reward. Is `true` if the user is prompted. 50 | attr_reader :is_user_input_required 51 | # The settings used to determine whether to apply a maximum to the number of redemptions 52 | # allowed per live stream. 53 | attr_reader :max_per_stream_setting 54 | # The settings used to determine whether to apply a maximum to the number of redemptions 55 | # allowed per user per live stream. 56 | attr_reader :max_per_user_per_stream_setting 57 | # The settings used to determine whether to apply a cooldown period between redemptions 58 | # and the length of the cooldown. 59 | attr_reader :global_cooldown_setting 60 | # A Boolean value that determines whether the reward is currently paused. 61 | # Is `true` if the reward is paused. Viewers can’t redeem paused rewards. 62 | attr_reader :is_paused 63 | # A Boolean value that determines whether the reward is currently in stock. 64 | # Is `true` if the reward is in stock. Viewers can’t redeem out of stock rewards. 65 | attr_reader :is_in_stock 66 | # A Boolean value that determines whether redemptions should be set to FULFILLED status 67 | # immediately when a reward is redeemed. If `false`, status is set to UNFULFILLED 68 | # and follows the normal request queue process. 69 | attr_reader :should_redemptions_skip_request_queue 70 | # The number of redemptions redeemed during the current live stream. 71 | # The number counts against the `max_per_stream_setting` limit. 72 | # This field is `nil` if the broadcaster’s stream isn’t live 73 | # or `max_per_stream_setting` isn’t enabled. 74 | attr_reader :redemptions_redeemed_current_stream 75 | # The timestamp of when the cooldown period expires. 76 | # Is `nil` if the reward isn’t in a cooldown state. 77 | # See the `global_cooldown_setting` field. 78 | attr_reader :cooldown_expires_at 79 | 80 | IMAGE_ATTRS = %w[image default_image].freeze 81 | private_constant :IMAGE_ATTRS 82 | 83 | def initialize(attributes = {}) 84 | attributes.each do |key, value| 85 | value = CustomRewardImage.new(value) if IMAGE_ATTRS.include?(key) && !value.nil? 86 | 87 | instance_variable_set :"@#{key}", value 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/twitch/editor.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | ## A user who can edit broadcast content, settings, etc. 5 | class Editor 6 | ## An ID that uniquely identifies a user with editor permissions. 7 | attr_reader :user_id 8 | 9 | ## The user’s display name. 10 | attr_reader :user_name 11 | 12 | ## The date and time when the user became one of the broadcaster’s editors. 13 | attr_reader :created_at 14 | 15 | def initialize(attributes = {}) 16 | attributes.each do |key, value| 17 | instance_variable_set :"@#{key}", value 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/twitch/entitlement_grant_url.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A URL that can be used to notify users of an entitlement. 5 | class EntitlementGrantUrl 6 | # The URL to upload an entitlement manifest. 7 | # See the Twitch API documentation on how to use this. 8 | attr_reader :url 9 | 10 | def initialize(attributes = {}) 11 | @url = attributes['url'] 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/twitch/extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A feature used to augment dynamic information on a stream. 5 | class Extension 6 | # ID of the extension. 7 | attr_reader :id 8 | # Version number of the extension. 9 | attr_reader :version 10 | # Name of the extension. 11 | attr_reader :name 12 | # Whether the extension is configured such that it can be activated. 13 | attr_reader :can_activate 14 | # The extension types that you can activate for this extension. Possible values are: 15 | # `component`, `mobile`, `overlay`, `panel` 16 | attr_reader :type 17 | 18 | # Optional attributes for extensions from `get` methods results 19 | 20 | # A Boolean value that determines the extension’s activation state. 21 | # If false, the user has not configured this component extension. 22 | attr_reader :active 23 | 24 | # Optional attributes for extensions with `component` type 25 | 26 | # The x-coordinate where the extension is placed. 27 | attr_reader :x 28 | # The y-coordinate where the extension is placed. 29 | attr_reader :y 30 | 31 | def initialize(attributes = {}) 32 | attributes.each do |key, value| 33 | instance_variable_set :"@#{key}", value 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/twitch/extensions_by_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A dictionary (Hash) of Extensions by their types and placements 5 | class ExtensionsByTypes 6 | # A dictionary that contains the data for a panel extension. 7 | # The dictionary’s key is a sequential number beginning with 1. 8 | # The following fields contain the panel’s data for each key. 9 | attr_reader :panel 10 | 11 | # A dictionary that contains the data for a video-overlay extension. 12 | # The dictionary’s key is a sequential number beginning with 1. 13 | # The following fields contain the overlay’s data for each key. 14 | attr_reader :overlay 15 | 16 | # A dictionary that contains the data for a video-component extension. 17 | # The dictionary’s key is a sequential number beginning with 1. 18 | # The following fields contain the component’s data for each key. 19 | attr_reader :component 20 | 21 | def initialize(attributes = {}) 22 | %w[panel overlay component].each do |type| 23 | instance_variable_set( 24 | :"@#{type}", 25 | attributes[type].transform_values do |extension_data| 26 | Extension.new(extension_data) 27 | end 28 | ) 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/twitch/game.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A filterable category for a stream. 5 | # (not necessarily limited to games, e.g. 'IRL') 6 | class Game 7 | # ID of the game. 8 | attr_reader :id 9 | # Name of the game 10 | attr_reader :name 11 | # Box art URL template. 12 | # 13 | # Substitute the {width} and {height} string tokens 14 | # with your desired numeric values. 15 | attr_reader :box_art_url 16 | 17 | def initialize(attributes = {}) 18 | @id = attributes['id'] 19 | @name = attributes['name'] 20 | @box_art_url = attributes['box_art_url'] 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/twitch/game_analytic.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Data object for `/analytics/games` requests 5 | class GameAnalytic 6 | # ID of the game requested. 7 | attr_reader :game_id 8 | # A link to a (CSV format) data report. 9 | attr_reader :url 10 | 11 | def initialize(attributes = {}) 12 | @game_id = attributes['game_id'] 13 | @url = attributes['URL'] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/twitch/moderation_event.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Information about a moderation action. 5 | # The action is determined based on the `event_type` field. 6 | class ModerationEvent 7 | # Fields to be converted from ISO 8601 string to a typed date. 8 | DATE_ATTRIBUTES = %i[event_timestamp].freeze 9 | 10 | # ID of the moderation event. 11 | attr_reader :id 12 | # Event type. 13 | # The `moderation.user` prefix is for user bans and unbans in a channel. 14 | # The `moderation.moderator` prefix is for addition and removal of moderators in a channel. 15 | attr_reader :event_type 16 | # Time at which the event happened. 17 | attr_reader :event_timestamp 18 | # Version of the endpoint the data was retrieved from. 19 | attr_reader :version 20 | # A hash containing information about the moderation action. 21 | attr_reader :event_data 22 | 23 | def initialize(attributes = {}) 24 | attributes.each do |key, value| 25 | if DATE_ATTRIBUTES.include?(key.to_sym) 26 | instance_variable_set :"@#{key}", Time.parse(value) 27 | else 28 | instance_variable_set :"@#{key}", value 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/twitch/moderator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A user who wields some form of power in a broadcaster's Twitch chat. 5 | # This is simply a user ID/name pair. 6 | class Moderator 7 | # User ID of the moderator. 8 | attr_reader :user_id 9 | # The user’s login name. 10 | attr_reader :user_login 11 | # The user’s display name. 12 | attr_reader :user_name 13 | 14 | def initialize(attributes = {}) 15 | attributes.each do |key, value| 16 | instance_variable_set :"@#{key}", value 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/twitch/redemption.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Data object for Twitch users 5 | class Redemption 6 | # ID of the broadcaster. 7 | attr_reader :broadcaster_id 8 | # Unformatted (lowercase) username of thebroadcaster. 9 | attr_reader :broadcaster_login 10 | # Formatted username of the broadcaster. 11 | attr_reader :broadcaster_display_name 12 | # ID of the redemption. 13 | attr_reader :id 14 | # ID of the user. 15 | attr_reader :user_id 16 | # Unformatted (lowercase) username of the user. 17 | attr_reader :user_login 18 | # Formatted username of the user. 19 | attr_reader :user_name 20 | # The associated reward being redeemed 21 | attr_reader :reward 22 | # The user input (e.g. text) if allowed by the reward 23 | attr_reader :user_input 24 | # The status of the redemption's fulfillment 25 | attr_reader :status 26 | # The timestamp of the redemption 27 | attr_reader :redeemed_at 28 | 29 | def initialize(attributes = {}) 30 | attributes.each do |key, value| 31 | instance_variable_set :"@#{key}", value 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/twitch/response.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A compiled response from the API. 5 | class Response 6 | extend Forwardable 7 | def_delegators :@http_response, :body, :success? 8 | 9 | # The requested data. 10 | attr_reader :data 11 | # A total amount of entities. 12 | # Applies to select endpoints. 13 | attr_reader :total 14 | # A range of dates in which data is effective. 15 | # Usually contains the keys start_date and end_date. 16 | # Applies to select endpoints. 17 | attr_reader :date_range 18 | # A hash containing a pagination token. 19 | # Access it with 20 | # pagination['cursor'] 21 | # Applies to select endpoints. 22 | attr_reader :pagination 23 | # The total amount of calls that can be used in 24 | # the rate limit period (one minute by default). 25 | attr_reader :rate_limit 26 | # The remaining amount of calls for the rate limit period. 27 | attr_reader :rate_limit_remaining 28 | # The date at which the rate limit is reset. 29 | attr_reader :rate_limit_resets_at 30 | # The total amount of clips that can be created in 31 | # the clip rate limit period (currently unknown). 32 | attr_reader :clip_rate_limit 33 | # The remaining amount of clips that can be created in 34 | # the clip rate limit period. 35 | attr_reader :clip_rate_limit_remaining 36 | # The HTTP raw response 37 | attr_reader :raw 38 | 39 | def initialize(data_class, http_response:) 40 | @http_response = http_response 41 | @raw = @http_response 42 | 43 | @data = parse_data data_class 44 | 45 | parse_rate_limits 46 | 47 | @pagination = body['pagination'] 48 | @total = body['total'] 49 | @date_range = body['date_range'] 50 | end 51 | 52 | private 53 | 54 | def parse_data(data_class) 55 | raw_data = body['data'] 56 | return raw_data unless data_class 57 | 58 | if raw_data.is_a?(Array) 59 | raw_data.map { |data_element| data_class.new(data_element) } 60 | else 61 | data_class.new(raw_data) 62 | end 63 | end 64 | 65 | def parse_rate_limits 66 | @rate_limit = rate_limit_headers[:limit].to_i 67 | @rate_limit_remaining = rate_limit_headers[:remaining].to_i 68 | @rate_limit_resets_at = Time.at(rate_limit_headers[:reset].to_i) 69 | 70 | @clip_rate_limit = rate_limit_headers[:'helixclipscreation-limit'] 71 | @clip_rate_limit_remaining = rate_limit_headers[:'helixclipscreation-remaining'] 72 | end 73 | 74 | def rate_limit_headers 75 | @rate_limit_headers ||= 76 | @http_response.headers 77 | .select { |key, _value| key.to_s.downcase.match(/^ratelimit/) } 78 | .transform_keys { |key| key.gsub('ratelimit-', '').to_sym } 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /lib/twitch/stream.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'time' 4 | 5 | module Twitch 6 | # A user's broadcasting session. 7 | class Stream 8 | # ID of the stream. 9 | attr_reader :id 10 | # ID of the user broadcasting. 11 | attr_reader :user_id 12 | # Username of the user broadcasting. 13 | attr_reader :user_name 14 | # ID of the game being broadcast. 15 | attr_reader :game_id 16 | # Name of the game being broadcast. 17 | attr_reader :game_name 18 | # Associated community IDs for the broadcaster. 19 | attr_reader :community_ids 20 | # The type of broadcast 21 | # which may include 'live', 'playlist', or 'watch_party'. 22 | attr_reader :type 23 | # Title of the stream session. 24 | attr_reader :title 25 | # Concurrent viewer count of the broadcast. 26 | attr_reader :viewer_count 27 | # Date at which the broadcast started. 28 | attr_reader :started_at 29 | # Language of the broadcast. 30 | attr_reader :language 31 | # URL of the latest thumbnail image for the broadcast. 32 | attr_reader :thumbnail_url 33 | # Ids of tags on the live stream 34 | attr_reader :tag_ids 35 | 36 | def initialize(attributes = {}) 37 | attributes.each do |key, value| 38 | instance_variable_set :"@#{key}", value 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/twitch/stream_marker.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A point of time in a stream VOD that was marked while it is live. 5 | class StreamMarker 6 | # ID of the stream marker. 7 | attr_reader :id 8 | # Date at which the stream marker was created. 9 | attr_reader :created_at 10 | # Description of the stream marker. 11 | attr_reader :description 12 | # Elapsed time in the VOD in seconds at which the stream marker was created. 13 | attr_reader :position_seconds 14 | # URL pointing to the video and timestamp of the stream marker. 15 | # Not returned upon creation. 16 | attr_reader :url 17 | 18 | def initialize(attributes = {}) 19 | @id = attributes['id'] 20 | @created_at = attributes['created_at'] 21 | @description = attributes['description'] 22 | @position_seconds = attributes['position_seconds'] 23 | @url = attributes['URL'] 24 | end 25 | end 26 | 27 | # The response envelope for the "Get Stream Markers" endpoint. 28 | class StreamMarkerResponse 29 | attr_reader :user_id, :user_name, :videos 30 | 31 | def initialize(attributes = {}) 32 | @user_id = attributes['user_id'] 33 | @user_name = attributes['user_name'] 34 | @videos = attributes['videos'].map { |video| VideoStreamMarkers.new(video) } 35 | end 36 | end 37 | 38 | # A video with a list of markers. 39 | class VideoStreamMarkers 40 | attr_reader :video_id, :markers 41 | 42 | def initialize(attributes = {}) 43 | @video_id = attributes['video_id'] 44 | @markers = attributes['markers'].map { |marker| StreamMarker.new(marker) } 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/twitch/stream_metadata.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A set of metadata to provide additional information about a 5 | # game being streamed. 6 | # Additional getters are assigned at initialization time, e.g. 7 | # self.hearthstone 8 | # has data when Hearthstone is being streamed. 9 | # Other games may be included, but will be set to nil 10 | # if they aren't the game currently being streamed. 11 | class StreamMetadata 12 | # ID of the streaming user. 13 | attr_reader :user_id 14 | # Display name of the streaming user. 15 | attr_reader :user_name 16 | # ID of the game being played. 17 | attr_reader :game_id 18 | 19 | def initialize(attributes = {}) 20 | @attributes = attributes 21 | 22 | @user_id = @attributes['user_id'] 23 | @user_name = @attributes['user_name'] 24 | @game_id = @attributes['game_id'] 25 | end 26 | 27 | # Since more games can be supported in the future, 28 | # this will ensure they will all be available. 29 | def method_missing(name, *args) 30 | name = name.to_s 31 | 32 | return super unless @attributes.key?(name) 33 | 34 | @attributes[name] 35 | end 36 | 37 | def respond_to_missing?(name) 38 | name = name.to_s 39 | 40 | @attributes.key?(name) ? true : super 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/twitch/subscription.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # A user's voluntary contribution to a streamer 5 | class Subscription 6 | # ID of the user being subscribed to. 7 | attr_reader :broadcaster_id 8 | # Username of the user being subscribed to. 9 | attr_reader :broadcaster_name 10 | # Whether the subscription was received as a gift. 11 | attr_reader :is_gift 12 | # Tier of the subscription. 13 | # 1000 = Tier 1, 2000 = Tier 2, 3000 = Tier 3 14 | attr_reader :tier 15 | # Descriptive name of the subscription. 16 | attr_reader :plan_name 17 | # ID of the subscribing user. 18 | attr_reader :user_id 19 | # Username of the subscribing user. 20 | attr_reader :user_name 21 | 22 | def initialize(attributes = {}) 23 | attributes.each do |key, value| 24 | instance_variable_set :"@#{key}", value 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/twitch/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Data object for Twitch users 5 | class User 6 | # ID of the user. 7 | attr_reader :id 8 | # Unformatted (lowercase) username of the user. 9 | attr_reader :login 10 | # Formatted username of the user. 11 | attr_reader :display_name 12 | # Represents a special role of a user. 13 | # (global mod, admin, staff) 14 | attr_reader :type 15 | # Represents a special broadcaster role of a user. 16 | # (partner, affiliate) 17 | attr_reader :broadcaster_type 18 | # Description/biographical info of a user. 19 | attr_reader :description 20 | # URL to the user's profile image. 21 | attr_reader :profile_image_url 22 | # URL to the image displayed in the video box 23 | # when the stream is offline. 24 | attr_reader :offline_image_url 25 | # Total number of visits to the user's stream page. 26 | attr_reader :view_count 27 | # The UTC date and time that the user’s account was created. The timestamp is in RFC3339 format. 28 | attr_reader :created_at 29 | 30 | def initialize(attributes = {}) 31 | attributes.each do |key, value| 32 | instance_variable_set :"@#{key}", value 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/twitch/user_ban.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Ban information for a user. 5 | class UserBan 6 | # Fields to be converted from ISO 8601 string to a typed date. 7 | DATE_ATTRIBUTES = %i[expires_at].freeze 8 | 9 | # ID of the banned/timed-out user. 10 | attr_reader :user_id 11 | # Username of the banned/timed-out user. 12 | attr_reader :user_name 13 | # Date of when the time-out will expire (nil for permanent bans) 14 | attr_reader :expires_at 15 | 16 | def initialize(attributes = {}) 17 | attributes.each do |key, value| 18 | if DATE_ATTRIBUTES.include?(key.to_sym) 19 | instance_variable_set :"@#{key}", Time.parse(value) 20 | else 21 | instance_variable_set :"@#{key}", value 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/twitch/user_follow.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Represents a relationship of one user following another. 5 | class UserFollow 6 | # User ID of the *following* user. 7 | attr_reader :from_id 8 | # Display name of the *following* user. 9 | attr_reader :from_name 10 | # User ID of the *followed* user. 11 | attr_reader :to_id 12 | # User name of the *followed* user. 13 | attr_reader :to_name 14 | # Date at which the follow action was performed. 15 | attr_reader :followed_at 16 | 17 | def initialize(attributes = {}) 18 | @from_id = attributes['from_id'] 19 | @from_name = attributes['from_name'] 20 | @to_id = attributes['to_id'] 21 | @to_name = attributes['to_name'] 22 | @followed_at = attributes['followed_at'] 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/twitch/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Twitch 4 | # Library version. 5 | VERSION = '1.0.0' 6 | end 7 | -------------------------------------------------------------------------------- /lib/twitch/video.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'time' 4 | 5 | module Twitch 6 | # A captured broadcast or portion of a broadcast. 7 | class Video 8 | # ID of the video. 9 | attr_reader :id 10 | # Title of the video. 11 | attr_reader :title 12 | # Description of the video. 13 | attr_reader :description 14 | # Language of the video. 15 | attr_reader :language 16 | # Number of views 17 | attr_reader :view_count 18 | # Date the video was created. 19 | attr_reader :created_at 20 | # Date the video was published. 21 | attr_reader :published_at 22 | # URL to the thumbnail image of the video. 23 | attr_reader :thumbnail_url 24 | # Type of the video (archive, highlight or upload). 25 | attr_reader :type 26 | # URL of the video. 27 | attr_reader :url 28 | # ID of the user who uploaded/broadcasted the video. 29 | attr_reader :user_id 30 | # Display name of the user who uploaded/broadcasted the video. 31 | attr_reader :user_name 32 | # Viewability of the video (public or private) 33 | attr_reader :viewable 34 | # Duration of the video, in the format `0h0m0s` 35 | attr_reader :duration 36 | # ID of the original stream if the `type` is archive; nil otherwise 37 | attr_reader :stream_id 38 | 39 | def initialize(attributes = {}) 40 | attributes.each do |key, value| 41 | instance_variable_set :"@#{key}", value 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "remark": "remark ." 4 | }, 5 | "devDependencies": { 6 | "remark-cli": "^12.0.0", 7 | "remark-gfm": "^4.0.0", 8 | "remark-lint-code-block-style": "^3.0.1", 9 | "remark-preset-lint-recommended": "^6.0.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_bits_leaderboard/when_token_type_is_application_/with_correct_client_credentials/with_tokens/1_1_2_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:13:33 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7ddad-5e73952e3a07d54d6c7bd884 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5356991} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:13:33 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/bits/leaderboard 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 401 55 | message: Unauthorized 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '74' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Mon, 31 Jul 2023 16:13:33 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400059-BFI, cache-lcy-eglc8600073-LCY 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1690820014.567303,VS0,VS0,VE258 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Unauthorized","status":401,"message":"Missing User OAUTH 84 | Token"}' 85 | recorded_at: Mon, 31 Jul 2023 16:13:33 GMT 86 | - request: 87 | method: get 88 | uri: https://api.twitch.tv/helix/bits/leaderboard 89 | body: 90 | encoding: US-ASCII 91 | string: '' 92 | headers: 93 | User-agent: 94 | - twitch-api ruby client 0.4.0 95 | Client-ID: 96 | - "" 97 | Authorization: 98 | - "" 99 | response: 100 | status: 101 | code: 401 102 | message: Unauthorized 103 | headers: 104 | connection: 105 | - keep-alive 106 | content-length: 107 | - '74' 108 | content-type: 109 | - application/json; charset=utf-8 110 | access-control-allow-origin: 111 | - "*" 112 | timing-allow-origin: 113 | - https://www.twitch.tv 114 | date: 115 | - Mon, 31 Jul 2023 16:13:34 GMT 116 | x-served-by: 117 | - cache-bfi-kbfi7400059-BFI, cache-lcy-eglc8600054-LCY 118 | x-cache: 119 | - MISS, MISS 120 | x-cache-hits: 121 | - 0, 0 122 | x-timer: 123 | - S1690820014.098012,VS0,VS0,VE149 124 | vary: 125 | - Accept-Encoding 126 | strict-transport-security: 127 | - max-age=300 128 | body: 129 | encoding: UTF-8 130 | string: '{"error":"Unauthorized","status":401,"message":"Missing User OAUTH 131 | Token"}' 132 | recorded_at: Mon, 31 Jul 2023 16:13:34 GMT 133 | recorded_with: VCR 6.2.0 134 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_bits_leaderboard/when_token_type_is_user_/with_access_token_/when_access_token_is_actual/1_1_1_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.1.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Mon, 16 Nov 2020 23:53:18 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.14.1 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-5fb310ee-3c4777be1f80a6e409f88b4c 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Mon, 16 Nov 2020 23:53:18 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","grant_type":"refresh_token","refresh_token":""}' 45 | headers: 46 | User-Agent: 47 | - Faraday v1.1.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Mon, 16 Nov 2020 23:53:19 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '301' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.14.1 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-5fb310ef-3aa739ac5c57b2c97867eef5 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":15267,"refresh_token":"","scope":["bits:read","channel_commercial","channel_editor","channel_read","channel_stream","user:read:email","user_blocks_edit","user_read"],"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Mon, 16 Nov 2020 23:53:19 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/bits/leaderboard 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 200 91 | message: OK 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '66' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | cache-control: 102 | - no-cache, no-store, must-revalidate, private 103 | expires: 104 | - '0' 105 | pragma: 106 | - no-cache 107 | ratelimit-limit: 108 | - '800' 109 | ratelimit-remaining: 110 | - '799' 111 | ratelimit-reset: 112 | - '1605570800' 113 | timing-allow-origin: 114 | - https://www.twitch.tv 115 | twitch-trace-id: 116 | - d31baa4e6bdadf1fcb4900dce7686d9d 117 | x-ctxlog-logid: 118 | - 1-5fb310ef-7ba526c55a564c371337fe13 119 | date: 120 | - Mon, 16 Nov 2020 23:53:19 GMT 121 | x-served-by: 122 | - cache-sea4424-SEA, cache-bma1634-BMA 123 | x-cache: 124 | - MISS, MISS 125 | x-cache-hits: 126 | - 0, 0 127 | x-timer: 128 | - S1605570800.695874,VS0,VS0,VE207 129 | vary: 130 | - Accept-Encoding 131 | strict-transport-security: 132 | - max-age=300 133 | body: 134 | encoding: UTF-8 135 | string: '{"data":[],"date_range":{"started_at":"","ended_at":""},"total":0}' 136 | recorded_at: Mon, 16 Nov 2020 23:53:19 GMT 137 | recorded_with: VCR 6.0.0 138 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_bits_leaderboard/when_token_type_is_user_/with_access_token_/when_access_token_is_outdated/with_refresh_token_/1_1_1_1_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.1.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Mon, 16 Nov 2020 23:53:20 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.14.1 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-5fb310f0-177b1cdd5cd44375170f8d65 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Mon, 16 Nov 2020 23:53:20 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","grant_type":"refresh_token","refresh_token":""}' 45 | headers: 46 | User-Agent: 47 | - Faraday v1.1.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Mon, 16 Nov 2020 23:53:21 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '301' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.14.1 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-5fb310f1-18be21b269e3a7784a3197e6 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":14248,"refresh_token":"","scope":["bits:read","channel_commercial","channel_editor","channel_read","channel_stream","user:read:email","user_blocks_edit","user_read"],"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Mon, 16 Nov 2020 23:53:21 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/bits/leaderboard 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 200 91 | message: OK 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '66' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | cache-control: 102 | - no-cache, no-store, must-revalidate, private 103 | expires: 104 | - '0' 105 | pragma: 106 | - no-cache 107 | ratelimit-limit: 108 | - '800' 109 | ratelimit-remaining: 110 | - '799' 111 | ratelimit-reset: 112 | - '1605570802' 113 | timing-allow-origin: 114 | - https://www.twitch.tv 115 | twitch-trace-id: 116 | - 57c63efd3c74dc0b058364493b8c6776 117 | x-ctxlog-logid: 118 | - 1-5fb310f1-3a9986b21a09350a08661ee0 119 | date: 120 | - Mon, 16 Nov 2020 23:53:22 GMT 121 | x-served-by: 122 | - cache-sea4483-SEA, cache-bma1645-BMA 123 | x-cache: 124 | - MISS, MISS 125 | x-cache-hits: 126 | - 0, 0 127 | x-timer: 128 | - S1605570802.889601,VS0,VS0,VE220 129 | vary: 130 | - Accept-Encoding 131 | strict-transport-security: 132 | - max-age=300 133 | body: 134 | encoding: UTF-8 135 | string: '{"data":[],"date_range":{"started_at":"","ended_at":""},"total":0}' 136 | recorded_at: Mon, 16 Nov 2020 23:53:22 GMT 137 | recorded_with: VCR 6.0.0 138 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_bits_leaderboard/when_token_type_is_user_/with_access_token_/when_access_token_is_outdated/without_refresh_token_/1_1_1_1_2_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.1.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Mon, 16 Nov 2020 23:53:22 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.14.1 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-5fb310f2-454f7b201b7d1cac265962f3 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Mon, 16 Nov 2020 23:53:22 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","grant_type":"refresh_token","refresh_token":null}' 45 | headers: 46 | User-Agent: 47 | - Faraday v1.1.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 400 53 | message: Bad Request 54 | headers: 55 | date: 56 | - Mon, 16 Nov 2020 23:53:23 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '49' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.14.1 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-5fb310f3-107558b5117a34861fbaaf3d 69 | body: 70 | encoding: UTF-8 71 | string: '{"status":400,"message":"missing refresh token"} 72 | 73 | ' 74 | recorded_at: Mon, 16 Nov 2020 23:53:23 GMT 75 | recorded_with: VCR 6.0.0 76 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_bits_leaderboard/when_token_type_is_user_/without_tokens/1_1_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/authorize?client_id=&redirect_uri=http%3A%2F%2Flocalhost&response_type=code&scope=bits%3Aread 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.1.0 12 | response: 13 | status: 14 | code: 302 15 | message: Found 16 | headers: 17 | date: 18 | - Mon, 16 Nov 2020 23:56:14 GMT 19 | content-type: 20 | - text/html; charset=utf-8 21 | content-length: 22 | - '247' 23 | connection: 24 | - keep-alive 25 | server: 26 | - nginx/1.14.1 27 | access-control-allow-origin: 28 | - "*" 29 | location: 30 | - https://www.twitch.tv/login?client_id=&redirect_params=client_id%3D%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%26response_type%3Dcode%26scope%3Dbits%253Aread 31 | set-cookie: 32 | - twilight-auth=; Path=/; Domain=twitch.tv; Expires=Thu, 01 Jan 1970 00:00:00 33 | GMT; Max-Age=0; Secure, twilight-user=; Path=/; Domain=twitch.tv; Expires=Thu, 34 | 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure, twilight-user.dev=; Path=/; Domain=twitch.tv; 35 | Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure, twilight-user.spotlight=; 36 | Path=/; Domain=twitch.tv; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; 37 | Secure, twilight-user.desklight=; Path=/; Domain=twitch.tv; Expires=Thu, 01 38 | Jan 1970 00:00:00 GMT; Max-Age=0; Secure, auth-token=; Path=/; Domain=twitch.tv; 39 | Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure, persistent=; Path=/; 40 | Domain=twitch.tv; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure, 41 | login=; Path=/; Domain=twitch.tv; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; 42 | Secure, name=; Path=/; Domain=twitch.tv; Expires=Thu, 01 Jan 1970 00:00:00 43 | GMT; Max-Age=0; Secure, sudo=; Path=/; Domain=twitch.tv; Expires=Thu, 01 Jan 44 | 1970 00:00:00 GMT; Max-Age=0; Secure, bits_sudo=; Path=/; Domain=twitch.tv; 45 | Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure, api_token=; Path=/; 46 | Domain=twitch.tv; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Secure, 47 | _twitch_session_id=; Path=/; Domain=twitch.tv; Expires=Thu, 01 Jan 1970 00:00:00 48 | GMT; Max-Age=0; Secure 49 | x-ctxlog-logid: 50 | - 1-5fb3119e-7a53e5ba0ac8e93e1feaad29 51 | x-frame-options: 52 | - DENY 53 | body: 54 | encoding: UTF-8 55 | string: "&redirect_params=client_id%3D%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%26response_type%3Dcode%26scope%3Dbits%253Aread\">Found.\n\n" 56 | recorded_at: Mon, 16 Nov 2020 23:56:14 GMT 57 | - request: 58 | method: post 59 | uri: https://id.twitch.tv/oauth2/token 60 | body: 61 | encoding: UTF-8 62 | string: '{"client_id":"","client_secret":"","code":"","grant_type":"authorization_code","redirect_uri":"http://localhost"}' 63 | headers: 64 | User-Agent: 65 | - Faraday v1.1.0 66 | Content-Type: 67 | - application/json 68 | response: 69 | status: 70 | code: 200 71 | message: OK 72 | headers: 73 | date: 74 | - Mon, 16 Nov 2020 23:56:59 GMT 75 | content-type: 76 | - application/json 77 | content-length: 78 | - '182' 79 | connection: 80 | - keep-alive 81 | server: 82 | - nginx/1.14.1 83 | access-control-allow-origin: 84 | - "*" 85 | x-ctxlog-logid: 86 | - 1-5fb311cb-269d6d1b399e1e7c202bd4ee 87 | body: 88 | encoding: UTF-8 89 | string: '{"access_token":"","expires_in":14517,"refresh_token":"","scope":["bits:read"],"token_type":"bearer"} 90 | 91 | ' 92 | recorded_at: Mon, 16 Nov 2020 23:56:59 GMT 93 | - request: 94 | method: get 95 | uri: https://api.twitch.tv/helix/bits/leaderboard 96 | body: 97 | encoding: US-ASCII 98 | string: '' 99 | headers: 100 | User-agent: 101 | - twitch-api ruby client 0.4.0 102 | Client-ID: 103 | - "" 104 | Authorization: 105 | - "" 106 | response: 107 | status: 108 | code: 200 109 | message: OK 110 | headers: 111 | connection: 112 | - keep-alive 113 | content-length: 114 | - '66' 115 | content-type: 116 | - application/json; charset=utf-8 117 | access-control-allow-origin: 118 | - "*" 119 | cache-control: 120 | - no-cache, no-store, must-revalidate, private 121 | expires: 122 | - '0' 123 | pragma: 124 | - no-cache 125 | ratelimit-limit: 126 | - '800' 127 | ratelimit-remaining: 128 | - '799' 129 | ratelimit-reset: 130 | - '1605571020' 131 | timing-allow-origin: 132 | - https://www.twitch.tv 133 | twitch-trace-id: 134 | - 10022a560b378fa8941cb11c404bdc8e 135 | x-ctxlog-logid: 136 | - 1-5fb311cb-3c4ea36f2f072c8c413bb9ac 137 | date: 138 | - Mon, 16 Nov 2020 23:56:59 GMT 139 | x-served-by: 140 | - cache-sea4448-SEA, cache-bma1632-BMA 141 | x-cache: 142 | - MISS, MISS 143 | x-cache-hits: 144 | - 0, 0 145 | x-timer: 146 | - S1605571019.446457,VS0,VS0,VE198 147 | vary: 148 | - Accept-Encoding 149 | strict-transport-security: 150 | - max-age=300 151 | body: 152 | encoding: UTF-8 153 | string: '{"data":[],"date_range":{"started_at":"","ended_at":""},"total":0}' 154 | recorded_at: Mon, 16 Nov 2020 23:56:59 GMT 155 | recorded_with: VCR 6.0.0 156 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channel_editors/with_options/when_token_type_is_application/1_18_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 21:16:29 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de8e2d-746b9cbe788bec962ddb14cd 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":3870015} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 21:16:29 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channels/editors?broadcaster_id=277558749 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 401 55 | message: Unauthorized 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '133' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1692306990' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Thu, 17 Aug 2023 21:16:29 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400068-BFI, cache-lhr7366-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1692306989.372168,VS0,VS0,VE159 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"error":"Unauthorized","status":401,"message":"The ID in broadcaster_id 90 | must match the user ID found in the request''s OAuth token."}' 91 | recorded_at: Thu, 17 Aug 2023 21:16:29 GMT 92 | - request: 93 | method: get 94 | uri: https://api.twitch.tv/helix/channels/editors?broadcaster_id=277558749 95 | body: 96 | encoding: US-ASCII 97 | string: '' 98 | headers: 99 | User-agent: 100 | - twitch-api ruby client 0.4.0 101 | Client-ID: 102 | - "" 103 | Authorization: 104 | - "" 105 | response: 106 | status: 107 | code: 401 108 | message: Unauthorized 109 | headers: 110 | connection: 111 | - keep-alive 112 | content-length: 113 | - '133' 114 | content-type: 115 | - application/json; charset=utf-8 116 | access-control-allow-origin: 117 | - "*" 118 | ratelimit-limit: 119 | - '800' 120 | ratelimit-remaining: 121 | - '799' 122 | ratelimit-reset: 123 | - '1692306990' 124 | timing-allow-origin: 125 | - https://www.twitch.tv 126 | date: 127 | - Thu, 17 Aug 2023 21:16:29 GMT 128 | x-served-by: 129 | - cache-bfi-kbfi7400068-BFI, cache-lhr7337-LHR 130 | x-cache: 131 | - MISS, MISS 132 | x-cache-hits: 133 | - 0, 0 134 | x-timer: 135 | - S1692306990.805006,VS0,VS0,VE155 136 | vary: 137 | - Accept-Encoding 138 | strict-transport-security: 139 | - max-age=300 140 | body: 141 | encoding: UTF-8 142 | string: '{"error":"Unauthorized","status":401,"message":"The ID in broadcaster_id 143 | must match the user ID found in the request''s OAuth token."}' 144 | recorded_at: Thu, 17 Aug 2023 21:16:29 GMT 145 | recorded_with: VCR 6.2.0 146 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channel_editors/with_options/when_token_type_is_user/when_broadcaster_ID_is_foreign_channel/1_18_2_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 21:17:11 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '713' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de8e57-20993425692f4dde0c35f773 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:manage:redemptions","channel:moderate","channel:read:editors","channel:read:polls","channel:read:predictions","channel:read:stream_key","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":14153} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 21:17:11 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channels/editors?broadcaster_id=23161357 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 401 55 | message: Unauthorized 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '133' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1692307032' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Thu, 17 Aug 2023 21:17:11 GMT 75 | x-served-by: 76 | - cache-bfi-krnt7300047-BFI, cache-lhr7356-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1692307032.715272,VS0,VS0,VE162 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"error":"Unauthorized","status":401,"message":"The ID in broadcaster_id 90 | must match the user ID found in the request''s OAuth token."}' 91 | recorded_at: Thu, 17 Aug 2023 21:17:11 GMT 92 | - request: 93 | method: get 94 | uri: https://api.twitch.tv/helix/channels/editors?broadcaster_id=23161357 95 | body: 96 | encoding: US-ASCII 97 | string: '' 98 | headers: 99 | User-agent: 100 | - twitch-api ruby client 0.4.0 101 | Client-ID: 102 | - "" 103 | Authorization: 104 | - "" 105 | response: 106 | status: 107 | code: 401 108 | message: Unauthorized 109 | headers: 110 | connection: 111 | - keep-alive 112 | content-length: 113 | - '133' 114 | content-type: 115 | - application/json; charset=utf-8 116 | access-control-allow-origin: 117 | - "*" 118 | ratelimit-limit: 119 | - '800' 120 | ratelimit-remaining: 121 | - '799' 122 | ratelimit-reset: 123 | - '1692307033' 124 | timing-allow-origin: 125 | - https://www.twitch.tv 126 | date: 127 | - Thu, 17 Aug 2023 21:17:12 GMT 128 | x-served-by: 129 | - cache-bfi-krnt7300047-BFI, cache-lhr7382-LHR 130 | x-cache: 131 | - MISS, MISS 132 | x-cache-hits: 133 | - 0, 0 134 | x-timer: 135 | - S1692307032.165820,VS0,VS0,VE163 136 | vary: 137 | - Accept-Encoding 138 | strict-transport-security: 139 | - max-age=300 140 | body: 141 | encoding: UTF-8 142 | string: '{"error":"Unauthorized","status":401,"message":"The ID in broadcaster_id 143 | must match the user ID found in the request''s OAuth token."}' 144 | recorded_at: Thu, 17 Aug 2023 21:17:12 GMT 145 | recorded_with: VCR 6.2.0 146 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channel_editors/with_options/when_token_type_is_user/when_broadcaster_ID_is_your_own/data/1_18_2_2_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 21:17:55 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '713' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de8e83-0b856a14552df14e1e17bb18 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:manage:redemptions","channel:moderate","channel:read:editors","channel:read:polls","channel:read:predictions","channel:read:stream_key","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":14109} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 21:17:55 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channels/editors?broadcaster_id=277558749 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '105' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1692307076' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Thu, 17 Aug 2023 21:17:56 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400115-BFI, cache-lhr7340-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1692307076.921277,VS0,VS0,VE162 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":[{"user_id":"117474239","user_name":"AlexWayfer","created_at":"2023-08-17T21:17:45.846381984Z"}]}' 90 | recorded_at: Thu, 17 Aug 2023 21:17:56 GMT 91 | recorded_with: VCR 6.2.0 92 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channel_editors/without_options/when_token_type_is_application/1_18_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 21:16:26 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de8e2a-5a8778c667e968960beca36a 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":3870018} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 21:16:26 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channels/editors 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '94' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Thu, 17 Aug 2023 21:16:26 GMT 69 | x-served-by: 70 | - cache-bfi-krnt7300051-BFI, cache-lcy-eglc8600026-LCY 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1692306987.567744,VS0,VS0,VE155 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"broadcaster_id\""}' 85 | recorded_at: Thu, 17 Aug 2023 21:16:26 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channel_editors/without_options/when_token_type_is_user/1_18_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 21:16:27 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '713' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de8e2b-7cd9e63050e29d0743bc9a8e 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:manage:redemptions","channel:moderate","channel:read:editors","channel:read:polls","channel:read:predictions","channel:read:stream_key","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":14197} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 21:16:27 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channels/editors 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '94' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Thu, 17 Aug 2023 21:16:28 GMT 69 | x-served-by: 70 | - cache-bfi-krnt7300051-BFI, cache-lhr7328-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1692306988.959243,VS0,VS0,VE150 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"broadcaster_id\""}' 85 | recorded_at: Thu, 17 Aug 2023 21:16:28 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channels/with_options/when_broadcaster_ID_is_an_Array_of_values/data/1_9_2_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Tue, 15 Nov 2022 17:30:38 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.22.0 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-6373ccbe-2fc0b4d40aacbfdd7b426c7f 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Tue, 15 Nov 2022 17:30:38 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","code":null,"grant_type":"client_credentials","redirect_uri":"http://localhost"}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Tue, 15 Nov 2022 17:30:40 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '93' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.22.0 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-6373ccbf-3c44148f7537d32970eb808c 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":4799083,"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Tue, 15 Nov 2022 17:30:40 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/channels?broadcaster_id%5B%5D=277558749&broadcaster_id%5B%5D=117474239 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 200 91 | message: OK 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '469' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | ratelimit-limit: 102 | - '800' 103 | ratelimit-remaining: 104 | - '799' 105 | ratelimit-reset: 106 | - '1668533441' 107 | timing-allow-origin: 108 | - https://www.twitch.tv 109 | date: 110 | - Tue, 15 Nov 2022 17:30:40 GMT 111 | x-served-by: 112 | - cache-bfi-krnt7300022-BFI, cache-hhn11569-HHN 113 | x-cache: 114 | - MISS, MISS 115 | x-cache-hits: 116 | - 0, 0 117 | x-timer: 118 | - S1668533441.717083,VS0,VS0,VE205 119 | vary: 120 | - Accept-Encoding 121 | strict-transport-security: 122 | - max-age=300 123 | body: 124 | encoding: UTF-8 125 | string: '{"data":[{"broadcaster_id":"117474239","broadcaster_login":"alexwayfer","broadcaster_name":"AlexWayfer","broadcaster_language":"ru","game_id":"1469308723","game_name":"Software 126 | and Game Development","title":"Делаю Тетрис","delay":0},{"broadcaster_id":"277558749","broadcaster_login":"streamassistantbot","broadcaster_name":"StreamAssistantBot","broadcaster_language":"en","game_id":"509670","game_name":"Science 127 | \u0026 Technology","title":"Test","delay":0}]}' 128 | recorded_at: Tue, 15 Nov 2022 17:30:40 GMT 129 | recorded_with: VCR 6.1.0 130 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channels/with_options/when_broadcaster_ID_is_single_value/data/1_9_2_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Tue, 15 Nov 2022 17:30:35 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.22.0 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-6373ccbb-09f75196302a9cfc54e50cb6 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Tue, 15 Nov 2022 17:30:35 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","code":null,"grant_type":"client_credentials","redirect_uri":"http://localhost"}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Tue, 15 Nov 2022 17:30:36 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '93' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.22.0 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-6373ccbc-772543d25f2825e174ff8707 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":5005602,"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Tue, 15 Nov 2022 17:30:37 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/channels?broadcaster_id=277558749 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 200 91 | message: OK 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '234' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | ratelimit-limit: 102 | - '800' 103 | ratelimit-remaining: 104 | - '799' 105 | ratelimit-reset: 106 | - '1668533438' 107 | timing-allow-origin: 108 | - https://www.twitch.tv 109 | date: 110 | - Tue, 15 Nov 2022 17:30:37 GMT 111 | x-served-by: 112 | - cache-bfi-kbfi7400094-BFI, cache-hhn11522-HHN 113 | x-cache: 114 | - MISS, MISS 115 | x-cache-hits: 116 | - 0, 0 117 | x-timer: 118 | - S1668533437.494282,VS0,VS0,VE222 119 | vary: 120 | - Accept-Encoding 121 | strict-transport-security: 122 | - max-age=300 123 | body: 124 | encoding: UTF-8 125 | string: '{"data":[{"broadcaster_id":"277558749","broadcaster_login":"streamassistantbot","broadcaster_name":"StreamAssistantBot","broadcaster_language":"en","game_id":"509670","game_name":"Science 126 | \u0026 Technology","title":"Test","delay":0}]}' 127 | recorded_at: Tue, 15 Nov 2022 17:30:37 GMT 128 | recorded_with: VCR 6.1.0 129 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channels/without_options/when_token_type_is_application/1_9_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:19:41 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7df1d-2f74f9f647f05efa36359451 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5356623} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:19:41 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channels 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '94' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Mon, 31 Jul 2023 16:19:41 GMT 69 | x-served-by: 70 | - cache-bfi-krnt7300027-BFI, cache-lhr7365-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1690820381.402827,VS0,VS0,VE149 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"broadcaster_id\""}' 85 | recorded_at: Mon, 31 Jul 2023 16:19:41 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_channels/without_options/when_token_type_is_user/1_9_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Tue, 15 Nov 2022 17:30:32 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.22.0 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-6373ccb8-32f58dc3259dccdd7a7faea8 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Tue, 15 Nov 2022 17:30:32 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","grant_type":"refresh_token","refresh_token":""}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Tue, 15 Nov 2022 17:30:33 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '344' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.22.0 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-6373ccb9-16987b052ebbe27d25b53d72 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":14733,"refresh_token":"","scope":["bits:read","channel:moderate","channel_commercial","channel_editor","channel_read","channel_stream","chat:edit","chat:read","user:read:email","user_blocks_edit","user_read"],"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Tue, 15 Nov 2022 17:30:33 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/channels 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 400 91 | message: Bad Request 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '94' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | timing-allow-origin: 102 | - https://www.twitch.tv 103 | date: 104 | - Tue, 15 Nov 2022 17:30:34 GMT 105 | x-served-by: 106 | - cache-bfi-krnt7300027-BFI, cache-hhn11538-HHN 107 | x-cache: 108 | - MISS, MISS 109 | x-cache-hits: 110 | - 0, 0 111 | x-timer: 112 | - S1668533434.337619,VS0,VS0,VE164 113 | vary: 114 | - Accept-Encoding 115 | strict-transport-security: 116 | - max-age=300 117 | body: 118 | encoding: UTF-8 119 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 120 | \"broadcaster_id\""}' 121 | recorded_at: Tue, 15 Nov 2022 17:30:34 GMT 122 | recorded_with: VCR 6.1.0 123 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_clips/1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.3.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 04 Feb 2021 04:08:10 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '215' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.14.1 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-601b732a-43344a945b92d6606c6f20a8 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"streamassistantbot","scopes":["channel:moderate","channel_editor","channel_read","chat:edit","chat:read","user_read"],"user_id":"277558749","expires_in":15437} 36 | 37 | ' 38 | recorded_at: Thu, 04 Feb 2021 04:08:10 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/clips?id=ObliqueEncouragingHumanHumbleLife 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '551' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | cache-control: 66 | - no-cache, no-store, must-revalidate, private 67 | expires: 68 | - '0' 69 | pragma: 70 | - no-cache 71 | ratelimit-limit: 72 | - '800' 73 | ratelimit-remaining: 74 | - '799' 75 | ratelimit-reset: 76 | - '1612411691' 77 | timing-allow-origin: 78 | - https://www.twitch.tv 79 | twitch-trace-id: 80 | - 71b158787110060c9398e85c2739c26c 81 | x-ctxlog-logid: 82 | - 1-601b732a-5cd4fe3c1e623fcc11f2df94 83 | date: 84 | - Thu, 04 Feb 2021 04:08:10 GMT 85 | x-served-by: 86 | - cache-sea4457-SEA, cache-bma1651-BMA 87 | x-cache: 88 | - MISS, MISS 89 | x-cache-hits: 90 | - 0, 0 91 | x-timer: 92 | - S1612411690.304709,VS0,VS0,VE207 93 | vary: 94 | - Accept-Encoding 95 | strict-transport-security: 96 | - max-age=300 97 | body: 98 | encoding: UTF-8 99 | string: '{"data":[{"id":"ObliqueEncouragingHumanHumbleLife","url":"https://clips.twitch.tv/ObliqueEncouragingHumanHumbleLife","embed_url":"https://clips.twitch.tv/embed?clip=ObliqueEncouragingHumanHumbleLife","broadcaster_id":"15310631","broadcaster_name":"Greekgodx","creator_id":"39950897","creator_name":"KillaTwitch","video_id":"","game_id":"","language":"en-gb","title":"wrong 100 | place wrong time","view_count":179002,"created_at":"2017-12-28T21:37:52Z","thumbnail_url":"https://clips-media-assets2.twitch.tv/168564772-preview-480x272.jpg"}],"pagination":{}}' 101 | recorded_at: Thu, 04 Feb 2021 04:08:10 GMT 102 | recorded_with: VCR 6.0.0 103 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_clips/_broadcaster_id/1_2_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.3.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 04 Feb 2021 04:08:11 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '215' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.14.1 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-601b732b-13d5a97a5dc3aa6d3ca48406 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"streamassistantbot","scopes":["channel:moderate","channel_editor","channel_read","chat:edit","chat:read","user_read"],"user_id":"277558749","expires_in":15436} 36 | 37 | ' 38 | recorded_at: Thu, 04 Feb 2021 04:08:11 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/clips?id=ObliqueEncouragingHumanHumbleLife 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '551' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | cache-control: 66 | - no-cache, no-store, must-revalidate, private 67 | expires: 68 | - '0' 69 | pragma: 70 | - no-cache 71 | ratelimit-limit: 72 | - '800' 73 | ratelimit-remaining: 74 | - '799' 75 | ratelimit-reset: 76 | - '1612411692' 77 | timing-allow-origin: 78 | - https://www.twitch.tv 79 | twitch-trace-id: 80 | - 37b97b0b2021f8457cf733da925b473c 81 | x-ctxlog-logid: 82 | - 1-601b732b-562b8ba25c5187806e140121 83 | date: 84 | - Thu, 04 Feb 2021 04:08:11 GMT 85 | x-served-by: 86 | - cache-sea4474-SEA, cache-bma1632-BMA 87 | x-cache: 88 | - MISS, MISS 89 | x-cache-hits: 90 | - 0, 0 91 | x-timer: 92 | - S1612411691.465144,VS0,VS0,VE193 93 | vary: 94 | - Accept-Encoding 95 | strict-transport-security: 96 | - max-age=300 97 | body: 98 | encoding: UTF-8 99 | string: '{"data":[{"id":"ObliqueEncouragingHumanHumbleLife","url":"https://clips.twitch.tv/ObliqueEncouragingHumanHumbleLife","embed_url":"https://clips.twitch.tv/embed?clip=ObliqueEncouragingHumanHumbleLife","broadcaster_id":"15310631","broadcaster_name":"Greekgodx","creator_id":"39950897","creator_name":"KillaTwitch","video_id":"","game_id":"","language":"en-gb","title":"wrong 100 | place wrong time","view_count":179002,"created_at":"2017-12-28T21:37:52Z","thumbnail_url":"https://clips-media-assets2.twitch.tv/168564772-preview-480x272.jpg"}],"pagination":{}}' 101 | recorded_at: Thu, 04 Feb 2021 04:08:11 GMT 102 | recorded_with: VCR 6.0.0 103 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_custom_reward/with_options/when_token_type_is_application/1_14_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Tue, 08 Aug 2023 10:09:24 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64d21454-70bf2ae97699d5c91ef3c6de 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":4687640} 36 | 37 | ' 38 | recorded_at: Tue, 08 Aug 2023 10:09:24 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id=117474239 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 401 55 | message: Unauthorized 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '74' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Tue, 08 Aug 2023 10:09:25 GMT 69 | x-served-by: 70 | - cache-bfi-krnt7300086-BFI, cache-lcy-eglc8600039-LCY 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1691489365.990949,VS0,VS0,VE149 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Unauthorized","status":401,"message":"Missing User OAUTH 84 | Token"}' 85 | recorded_at: Tue, 08 Aug 2023 10:09:25 GMT 86 | - request: 87 | method: get 88 | uri: https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id=117474239 89 | body: 90 | encoding: US-ASCII 91 | string: '' 92 | headers: 93 | User-agent: 94 | - twitch-api ruby client 0.4.0 95 | Client-ID: 96 | - "" 97 | Authorization: 98 | - "" 99 | response: 100 | status: 101 | code: 401 102 | message: Unauthorized 103 | headers: 104 | connection: 105 | - keep-alive 106 | content-length: 107 | - '74' 108 | content-type: 109 | - application/json; charset=utf-8 110 | access-control-allow-origin: 111 | - "*" 112 | timing-allow-origin: 113 | - https://www.twitch.tv 114 | date: 115 | - Tue, 08 Aug 2023 10:09:25 GMT 116 | x-served-by: 117 | - cache-bfi-krnt7300086-BFI, cache-lcy-eglc8600026-LCY 118 | x-cache: 119 | - MISS, MISS 120 | x-cache-hits: 121 | - 0, 0 122 | x-timer: 123 | - S1691489365.412215,VS0,VS0,VE147 124 | vary: 125 | - Accept-Encoding 126 | strict-transport-security: 127 | - max-age=300 128 | body: 129 | encoding: UTF-8 130 | string: '{"error":"Unauthorized","status":401,"message":"Missing User OAUTH 131 | Token"}' 132 | recorded_at: Tue, 08 Aug 2023 10:09:25 GMT 133 | recorded_with: VCR 6.2.0 134 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_custom_reward/without_options/when_token_type_is_application/1_14_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Tue, 08 Aug 2023 09:46:38 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64d20efe-552ff39a64a8b482203546df 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":4689006} 36 | 37 | ' 38 | recorded_at: Tue, 08 Aug 2023 09:46:39 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channel_points/custom_rewards 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '94' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Tue, 08 Aug 2023 09:46:39 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400115-BFI, cache-lhr7362-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1691487999.300406,VS0,VS0,VE151 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"broadcaster_id\""}' 85 | recorded_at: Tue, 08 Aug 2023 09:46:39 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_custom_reward/without_options/when_token_type_is_user/1_14_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Tue, 08 Aug 2023 09:47:01 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '635' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64d20f15-4cff18e8763d11b3414eec5b 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":13043} 36 | 37 | ' 38 | recorded_at: Tue, 08 Aug 2023 09:47:01 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/channel_points/custom_rewards 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '94' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Tue, 08 Aug 2023 09:47:01 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400115-BFI, cache-lhr7376-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1691488022.785693,VS0,VS0,VE149 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"broadcaster_id\""}' 85 | recorded_at: Tue, 08 Aug 2023 09:47:01 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_games/data_length/1_6_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Tue, 15 Nov 2022 17:29:14 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.22.0 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-6373cc6a-2dd89b0e0fcfc2bf02e5f77f 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Tue, 15 Nov 2022 17:29:14 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","code":null,"grant_type":"client_credentials","redirect_uri":"http://localhost"}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Tue, 15 Nov 2022 17:29:15 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '93' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.22.0 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-6373cc6b-2a5053267b2042cb506cadff 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":5365399,"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Tue, 15 Nov 2022 17:29:15 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/games?name%5B%5D=Heroes+of+the+Storm&name%5B%5D=Super+Mario+Odyssey 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 200 91 | message: OK 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '276' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | ratelimit-limit: 102 | - '800' 103 | ratelimit-remaining: 104 | - '799' 105 | ratelimit-reset: 106 | - '1668533357' 107 | timing-allow-origin: 108 | - https://www.twitch.tv 109 | date: 110 | - Tue, 15 Nov 2022 17:29:16 GMT 111 | x-served-by: 112 | - cache-bfi-krnt7300064-BFI, cache-hhn11561-HHN 113 | x-cache: 114 | - MISS, MISS 115 | x-cache-hits: 116 | - 0, 0 117 | x-timer: 118 | - S1668533356.475090,VS0,VS0,VE178 119 | vary: 120 | - Accept-Encoding 121 | strict-transport-security: 122 | - max-age=300 123 | body: 124 | encoding: UTF-8 125 | string: '{"data":[{"id":"32959","name":"Heroes of the Storm","box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/32959_IGDB-{width}x{height}.jpg"},{"id":"493997","name":"Super 126 | Mario Odyssey","box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/493997_IGDB-{width}x{height}.jpg"}]}' 127 | recorded_at: Tue, 15 Nov 2022 17:29:16 GMT 128 | recorded_with: VCR 6.1.0 129 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_moderators/with_options/when_broadcaster_ID_is_foreign_channel/1_12_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 14:50:46 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '635' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7ca46-19fe6a8f5b84e1cf7265d468 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":14473} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 14:50:46 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/moderation/moderators?broadcaster_id=23161357 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 401 55 | message: Unauthorized 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '133' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690815048' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 14:50:47 GMT 75 | x-served-by: 76 | - cache-bfi-krnt7300032-BFI, cache-lhr7351-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690815047.990141,VS0,VS0,VE157 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"error":"Unauthorized","status":401,"message":"The ID in broadcaster_id 90 | must match the user ID found in the request''s OAuth token."}' 91 | recorded_at: Mon, 31 Jul 2023 14:50:47 GMT 92 | - request: 93 | method: get 94 | uri: https://api.twitch.tv/helix/moderation/moderators?broadcaster_id=23161357 95 | body: 96 | encoding: US-ASCII 97 | string: '' 98 | headers: 99 | User-agent: 100 | - twitch-api ruby client 0.4.0 101 | Client-ID: 102 | - "" 103 | Authorization: 104 | - "" 105 | response: 106 | status: 107 | code: 401 108 | message: Unauthorized 109 | headers: 110 | connection: 111 | - keep-alive 112 | content-length: 113 | - '133' 114 | content-type: 115 | - application/json; charset=utf-8 116 | access-control-allow-origin: 117 | - "*" 118 | ratelimit-limit: 119 | - '800' 120 | ratelimit-remaining: 121 | - '799' 122 | ratelimit-reset: 123 | - '1690815048' 124 | timing-allow-origin: 125 | - https://www.twitch.tv 126 | date: 127 | - Mon, 31 Jul 2023 14:50:47 GMT 128 | x-served-by: 129 | - cache-bfi-krnt7300032-BFI, cache-lhr7323-LHR 130 | x-cache: 131 | - MISS, MISS 132 | x-cache-hits: 133 | - 0, 0 134 | x-timer: 135 | - S1690815047.423957,VS0,VS0,VE570 136 | vary: 137 | - Accept-Encoding 138 | strict-transport-security: 139 | - max-age=300 140 | body: 141 | encoding: UTF-8 142 | string: '{"error":"Unauthorized","status":401,"message":"The ID in broadcaster_id 143 | must match the user ID found in the request''s OAuth token."}' 144 | recorded_at: Mon, 31 Jul 2023 14:50:48 GMT 145 | recorded_with: VCR 6.2.0 146 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_moderators/with_options/when_broadcaster_ID_is_your_own/data/1_12_2_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 15:00:45 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '635' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7cc9d-7b47ebd8068ffb3b670ee3f7 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":13874} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 15:00:46 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/moderation/moderators?broadcaster_id=277558749 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '101' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690815647' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 15:00:46 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400026-BFI, cache-lhr7365-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690815646.341761,VS0,VS0,VE169 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":[{"user_id":"117474239","user_login":"alexwayfer","user_name":"AlexWayfer"}],"pagination":{}}' 90 | recorded_at: Mon, 31 Jul 2023 15:00:46 GMT 91 | recorded_with: VCR 6.2.0 92 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_moderators/without_options/when_token_type_is_application/1_12_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:19:55 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7df2b-1f47b8957b6950e4277b319f 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5356609} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:19:55 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/moderation/moderators 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '96' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690820397' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 16:19:56 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400030-BFI, cache-lcy-eglc8600023-LCY 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690820396.173239,VS0,VS0,VE160 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"error":"Bad Request","status":400,"message":"The broadcaster_id query 90 | parameter is required."}' 91 | recorded_at: Mon, 31 Jul 2023 16:19:56 GMT 92 | recorded_with: VCR 6.2.0 93 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_moderators/without_options/when_token_type_is_user/1_12_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 14:50:45 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '635' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7ca45-60f5a4162b4fe1141c14baf4 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":14474} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 14:50:45 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/moderation/moderators 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '96' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690815046' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 14:50:45 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400030-BFI, cache-lhr7325-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690815046.603467,VS0,VS0,VE151 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"error":"Bad Request","status":400,"message":"The broadcaster_id query 90 | parameter is required."}' 91 | recorded_at: Mon, 31 Jul 2023 14:50:45 GMT 92 | recorded_with: VCR 6.2.0 93 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_stream_key/with_options/when_token_type_is_application/1_16_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:38:33 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de06b9-654a008a12795aa06b87559c 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":3904691} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:38:33 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/streams/key?broadcaster_id=277558749 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 401 55 | message: Unauthorized 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '74' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Thu, 17 Aug 2023 11:38:34 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400030-BFI, cache-lhr7346-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1692272314.022513,VS0,VS0,VE180 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Unauthorized","status":401,"message":"Missing User OAUTH 84 | Token"}' 85 | recorded_at: Thu, 17 Aug 2023 11:38:34 GMT 86 | - request: 87 | method: get 88 | uri: https://api.twitch.tv/helix/streams/key?broadcaster_id=277558749 89 | body: 90 | encoding: US-ASCII 91 | string: '' 92 | headers: 93 | User-agent: 94 | - twitch-api ruby client 0.4.0 95 | Client-ID: 96 | - "" 97 | Authorization: 98 | - "" 99 | response: 100 | status: 101 | code: 401 102 | message: Unauthorized 103 | headers: 104 | connection: 105 | - keep-alive 106 | content-length: 107 | - '74' 108 | content-type: 109 | - application/json; charset=utf-8 110 | access-control-allow-origin: 111 | - "*" 112 | timing-allow-origin: 113 | - https://www.twitch.tv 114 | date: 115 | - Thu, 17 Aug 2023 11:38:34 GMT 116 | x-served-by: 117 | - cache-bfi-kbfi7400030-BFI, cache-lcy-eglc8600073-LCY 118 | x-cache: 119 | - MISS, MISS 120 | x-cache-hits: 121 | - 0, 0 122 | x-timer: 123 | - S1692272314.480682,VS0,VS0,VE280 124 | vary: 125 | - Accept-Encoding 126 | strict-transport-security: 127 | - max-age=300 128 | body: 129 | encoding: UTF-8 130 | string: '{"error":"Unauthorized","status":401,"message":"Missing User OAUTH 131 | Token"}' 132 | recorded_at: Thu, 17 Aug 2023 11:38:34 GMT 133 | recorded_with: VCR 6.2.0 134 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_stream_key/with_options/when_token_type_is_user/data/1_16_2_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:47:12 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '690' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de08c0-16d3409016c0054824b71567 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:manage:redemptions","channel:moderate","channel:read:polls","channel:read:predictions","channel:read:stream_key","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":13206} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:47:13 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/streams/key?broadcaster_id=277558749 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '73' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1692272834' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Thu, 17 Aug 2023 11:47:13 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400030-BFI, cache-lhr7371-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1692272833.306239,VS0,VS0,VE196 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":[{"stream_key":""}]}' 90 | recorded_at: Thu, 17 Aug 2023 11:47:13 GMT 91 | recorded_with: VCR 6.2.0 92 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_stream_key/without_options/when_token_type_is_application/1_16_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:38:30 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de06b6-01d0256f39ccce213d5b69ab 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":3904694} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:38:30 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/streams/key 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '94' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Thu, 17 Aug 2023 11:38:31 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400090-BFI, cache-lcy-eglc8600063-LCY 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1692272311.230395,VS0,VS0,VE147 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"broadcaster_id\""}' 85 | recorded_at: Thu, 17 Aug 2023 11:38:31 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_stream_key/without_options/when_token_type_is_user/1_16_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:38:32 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '656' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64de06b8-0b2fe2647b2dc07c78219b96 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:manage:redemptions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":10192} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:38:32 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/streams/key 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '94' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Thu, 17 Aug 2023 11:38:32 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400090-BFI, cache-lcy-eglc8600073-LCY 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1692272313.626920,VS0,VS0,VE146 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"broadcaster_id\""}' 85 | recorded_at: Thu, 17 Aug 2023 11:38:32 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_streams/with_username/1_3_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.3.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 04 Feb 2021 04:08:13 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '215' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.14.1 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-601b732d-60fe69f24ff60cf066c958e8 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"streamassistantbot","scopes":["channel:moderate","channel_editor","channel_read","chat:edit","chat:read","user_read"],"user_id":"277558749","expires_in":15434} 36 | 37 | ' 38 | recorded_at: Thu, 04 Feb 2021 04:08:13 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/streams?user_login=SunsetClub 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '514' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | cache-control: 66 | - no-cache, no-store, must-revalidate, private 67 | expires: 68 | - '0' 69 | pragma: 70 | - no-cache 71 | ratelimit-limit: 72 | - '800' 73 | ratelimit-remaining: 74 | - '799' 75 | ratelimit-reset: 76 | - '1612411694' 77 | timing-allow-origin: 78 | - https://www.twitch.tv 79 | twitch-trace-id: 80 | - 6c42024e40e905a8a34f1fdab983abda 81 | x-ctxlog-logid: 82 | - 1-601b732d-7552ae447cf845af2c5ce189 83 | date: 84 | - Thu, 04 Feb 2021 04:08:13 GMT 85 | x-served-by: 86 | - cache-sea4477-SEA, cache-bma1645-BMA 87 | x-cache: 88 | - MISS, MISS 89 | x-cache-hits: 90 | - 0, 0 91 | x-timer: 92 | - S1612411694.757509,VS0,VS0,VE197 93 | vary: 94 | - Accept-Encoding 95 | strict-transport-security: 96 | - max-age=300 97 | body: 98 | encoding: UTF-8 99 | string: '{"data":[{"id":"40971485260","user_id":"508623691","user_login":"sunsetclub","user_name":"SunsetClub","game_id":"499973","game_name":"Always 100 | On","type":"live","title":"LIVE view of the World Trade Center, The Hudson 101 | River, Downtown NYC, and the Verrazzano- Narrows Bridge ","viewer_count":2,"started_at":"2021-02-03T03:45:19Z","language":"en","thumbnail_url":"https://static-cdn.jtvnw.net/previews-ttv/live_user_sunsetclub-{width}x{height}.jpg","tag_ids":["6ea6bca4-4712-4ab9-a906-e3336a9d8039"]}],"pagination":{}}' 102 | recorded_at: Thu, 04 Feb 2021 04:08:13 GMT 103 | recorded_with: VCR 6.0.0 104 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_streams/with_username/viewer_count/1_3_2_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v1.3.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 04 Feb 2021 04:08:14 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '215' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.14.1 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-601b732e-7be1d597002515c5057efea8 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"streamassistantbot","scopes":["channel:moderate","channel_editor","channel_read","chat:edit","chat:read","user_read"],"user_id":"277558749","expires_in":15433} 36 | 37 | ' 38 | recorded_at: Thu, 04 Feb 2021 04:08:14 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/streams?user_login=SunsetClub 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '514' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | cache-control: 66 | - no-cache, no-store, must-revalidate, private 67 | expires: 68 | - '0' 69 | pragma: 70 | - no-cache 71 | ratelimit-limit: 72 | - '800' 73 | ratelimit-remaining: 74 | - '799' 75 | ratelimit-reset: 76 | - '1612411696' 77 | timing-allow-origin: 78 | - https://www.twitch.tv 79 | twitch-trace-id: 80 | - 8c39e448ced61b6d4a0c84e85cc661db 81 | x-ctxlog-logid: 82 | - 1-601b732f-1fc63bd163ff88d466f816b7 83 | date: 84 | - Thu, 04 Feb 2021 04:08:15 GMT 85 | x-served-by: 86 | - cache-sea4461-SEA, cache-bma1647-BMA 87 | x-cache: 88 | - MISS, MISS 89 | x-cache-hits: 90 | - 0, 0 91 | x-timer: 92 | - S1612411695.930424,VS0,VS0,VE182 93 | vary: 94 | - Accept-Encoding 95 | strict-transport-security: 96 | - max-age=300 97 | body: 98 | encoding: UTF-8 99 | string: '{"data":[{"id":"40971485260","user_id":"508623691","user_login":"sunsetclub","user_name":"SunsetClub","game_id":"499973","game_name":"Always 100 | On","type":"live","title":"LIVE view of the World Trade Center, The Hudson 101 | River, Downtown NYC, and the Verrazzano- Narrows Bridge ","viewer_count":2,"started_at":"2021-02-03T03:45:19Z","language":"en","thumbnail_url":"https://static-cdn.jtvnw.net/previews-ttv/live_user_sunsetclub-{width}x{height}.jpg","tag_ids":["6ea6bca4-4712-4ab9-a906-e3336a9d8039"]}],"pagination":{}}' 102 | recorded_at: Thu, 04 Feb 2021 04:08:15 GMT 103 | recorded_with: VCR 6.0.0 104 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_top_games/data_length/1_7_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.0 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Tue, 15 Nov 2022 17:29:17 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx/1.22.0 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-6373cc6d-1087eac41b22b6ef36bb54cf 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Tue, 15 Nov 2022 17:29:17 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","code":null,"grant_type":"client_credentials","redirect_uri":"http://localhost"}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.0 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Tue, 15 Nov 2022 17:29:19 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '93' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx/1.22.0 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-6373cc6e-41ba5a03431f77f948dcdc95 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":4849280,"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Tue, 15 Nov 2022 17:29:19 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/games/top?first=5 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 200 91 | message: OK 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '711' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | ratelimit-limit: 102 | - '800' 103 | ratelimit-remaining: 104 | - '799' 105 | ratelimit-reset: 106 | - '1668533360' 107 | timing-allow-origin: 108 | - https://www.twitch.tv 109 | date: 110 | - Tue, 15 Nov 2022 17:29:19 GMT 111 | x-served-by: 112 | - cache-bfi-krnt7300037-BFI, cache-hhn11558-HHN 113 | x-cache: 114 | - MISS, MISS 115 | x-cache-hits: 116 | - 0, 0 117 | x-timer: 118 | - S1668533360.650438,VS0,VS0,VE181 119 | vary: 120 | - Accept-Encoding 121 | strict-transport-security: 122 | - max-age=300 123 | body: 124 | encoding: UTF-8 125 | string: '{"data":[{"id":"509658","name":"Just Chatting","box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/509658-{width}x{height}.jpg"},{"id":"516575","name":"VALORANT","box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/516575-{width}x{height}.jpg"},{"id":"32982","name":"Grand 126 | Theft Auto V","box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/32982_IGDB-{width}x{height}.jpg"},{"id":"21779","name":"League 127 | of Legends","box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/21779-{width}x{height}.jpg"},{"id":"102007682","name":"God 128 | of War Ragnarök","box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/102007682_IGDB-{width}x{height}.jpg"}],"pagination":{"cursor":"eyJzIjo1LCJkIjpmYWxzZSwidCI6dHJ1ZX0="}}' 129 | recorded_at: Tue, 15 Nov 2022 17:29:19 GMT 130 | recorded_with: VCR 6.1.0 131 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_user_active_extensions/with_options/when_token_type_is_application/data/1_13_2_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 15:49:50 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7d81e-4d1e0c7c7a4ba5b97b875323 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5358414} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 15:49:50 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/users/extensions?user_id=277558749 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '325' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690818591' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 15:49:50 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400108-BFI, cache-lhr7393-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690818591.768398,VS0,VS0,VE226 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":{"panel":{"1":{"active":true,"id":"5tbyqce941455yffg7fzg36tp6or8p","version":"5.0.4","name":"Stream 90 | Stickers"},"2":{"active":false},"3":{"active":false}},"overlay":{"1":{"active":true,"id":"4xbv0wcmq7w91n66b3bh417irlsja1","version":"1.0.2","name":"Quiz 91 | Kit"}},"component":{"1":{"active":false},"2":{"active":false}}}}' 92 | recorded_at: Mon, 31 Jul 2023 15:49:51 GMT 93 | recorded_with: VCR 6.2.0 94 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_user_active_extensions/without_options/when_token_type_is_application/1_13_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 15:48:33 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7d7d1-314e645442491b7c7ccecad2 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5358491} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 15:48:33 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/users/extensions 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '124' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690818514' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 15:48:33 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400038-BFI, cache-lhr7381-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690818513.431626,VS0,VS0,VE168 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"error":"Bad Request","status":400,"message":"The user_id query parameter 90 | is required if you specify an app access token."}' 91 | recorded_at: Mon, 31 Jul 2023 15:48:33 GMT 92 | recorded_with: VCR 6.2.0 93 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_user_active_extensions/without_options/when_token_type_is_user/returns_extensions_for_the_authentificated_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 15:49:48 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '635' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7d81c-7f5f60f4716f745c687f3aaf 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":10931} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 15:49:49 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/users/extensions 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '325' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690818590' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 15:49:49 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400038-BFI, cache-lhr7343-LHR 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690818589.345004,VS0,VS0,VE187 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":{"panel":{"1":{"active":true,"id":"5tbyqce941455yffg7fzg36tp6or8p","version":"5.0.4","name":"Stream 90 | Stickers"},"2":{"active":false},"3":{"active":false}},"overlay":{"1":{"active":true,"id":"4xbv0wcmq7w91n66b3bh417irlsja1","version":"1.0.2","name":"Quiz 91 | Kit"}},"component":{"1":{"active":false},"2":{"active":false}}}}' 92 | recorded_at: Mon, 31 Jul 2023 15:49:49 GMT 93 | recorded_with: VCR 6.2.0 94 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_users/when_token_type_is_application_/with_correct_client_credentials/with_tokens/1_4_1_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:19:48 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7df24-056729185a95cc9e40ceb2dd 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5356616} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:19:48 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/users?id=18587270 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '573' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690820390' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 16:19:49 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400066-BFI, cache-lcy-eglc8600050-LCY 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690820389.959829,VS0,VS0,VE154 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":[{"id":"18587270","login":"day9tv","display_name":"Day9tv","type":"","broadcaster_type":"partner","description":"A 90 | mixture of thoughtful gaming strategy, comedy, and silly fun. Featuring MTGA, 91 | Dota 2, adventure games, and other community favorites.","profile_image_url":"https://static-cdn.jtvnw.net/jtv_user_pictures/day9tv-profile_image-2226c4b27aaf487a-300x300.png","offline_image_url":"https://static-cdn.jtvnw.net/jtv_user_pictures/b5b2dabd-d720-465b-bd0e-2e8aef3d9802-channel_offline_image-1920x1080.jpeg","view_count":0,"created_at":"2010-12-09T05:50:55Z"}]}' 92 | recorded_at: Mon, 31 Jul 2023 16:19:49 GMT 93 | recorded_with: VCR 6.2.0 94 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_users/when_token_type_is_application_/with_correct_client_credentials/without_tokens/1_4_1_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://id.twitch.tv/oauth2/token 6 | body: 7 | encoding: UTF-8 8 | string: '{"client_id":"","client_secret":"","code":null,"grant_type":"client_credentials","redirect_uri":"http://localhost"}' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Content-Type: 13 | - application/json 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:19:50 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '93' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7df25-54b287fb0ff8f42f7a3d8f2f 33 | body: 34 | encoding: UTF-8 35 | string: '{"access_token":"","expires_in":4803057,"token_type":"bearer"} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:19:50 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/users?id=18587270 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '573' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1690820391' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Mon, 31 Jul 2023 16:19:50 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400096-BFI, cache-lcy-eglc8600074-LCY 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1690820391.527769,VS0,VS0,VE165 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":[{"id":"18587270","login":"day9tv","display_name":"Day9tv","type":"","broadcaster_type":"partner","description":"A 90 | mixture of thoughtful gaming strategy, comedy, and silly fun. Featuring MTGA, 91 | Dota 2, adventure games, and other community favorites.","profile_image_url":"https://static-cdn.jtvnw.net/jtv_user_pictures/day9tv-profile_image-2226c4b27aaf487a-300x300.png","offline_image_url":"https://static-cdn.jtvnw.net/jtv_user_pictures/b5b2dabd-d720-465b-bd0e-2e8aef3d9802-channel_offline_image-1920x1080.jpeg","view_count":0,"created_at":"2010-12-09T05:50:55Z"}]}' 92 | recorded_at: Mon, 31 Jul 2023 16:19:50 GMT 93 | recorded_with: VCR 6.2.0 94 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_users/when_token_type_is_application_/with_incorrect_client_credentials/with_tokens/1_4_1_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:19:51 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7df27-4346fb360d2e498b6c170330 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5356613} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:19:51 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/users?id=18587270 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: [] 49 | Authorization: 50 | - "" 51 | response: 52 | status: 53 | code: 401 54 | message: Unauthorized 55 | headers: 56 | connection: 57 | - keep-alive 58 | content-length: 59 | - '70' 60 | content-type: 61 | - application/json; charset=utf-8 62 | access-control-allow-origin: 63 | - "*" 64 | timing-allow-origin: 65 | - https://www.twitch.tv 66 | date: 67 | - Mon, 31 Jul 2023 16:19:52 GMT 68 | x-served-by: 69 | - cache-bfi-krnt7300069-BFI, cache-lcy-eglc8600061-LCY 70 | x-cache: 71 | - MISS, MISS 72 | x-cache-hits: 73 | - 0, 0 74 | x-timer: 75 | - S1690820392.925200,VS0,VS0,VE153 76 | vary: 77 | - Accept-Encoding 78 | strict-transport-security: 79 | - max-age=300 80 | body: 81 | encoding: UTF-8 82 | string: '{"error":"Unauthorized","status":401,"message":"Client ID is missing"}' 83 | recorded_at: Mon, 31 Jul 2023 16:19:52 GMT 84 | - request: 85 | method: get 86 | uri: https://api.twitch.tv/helix/users?id=18587270 87 | body: 88 | encoding: US-ASCII 89 | string: '' 90 | headers: 91 | User-agent: 92 | - twitch-api ruby client 0.4.0 93 | Client-ID: [] 94 | Authorization: 95 | - "" 96 | response: 97 | status: 98 | code: 401 99 | message: Unauthorized 100 | headers: 101 | connection: 102 | - keep-alive 103 | content-length: 104 | - '70' 105 | content-type: 106 | - application/json; charset=utf-8 107 | access-control-allow-origin: 108 | - "*" 109 | timing-allow-origin: 110 | - https://www.twitch.tv 111 | date: 112 | - Mon, 31 Jul 2023 16:19:52 GMT 113 | x-served-by: 114 | - cache-bfi-krnt7300069-BFI, cache-lcy-eglc8600079-LCY 115 | x-cache: 116 | - MISS, MISS 117 | x-cache-hits: 118 | - 0, 0 119 | x-timer: 120 | - S1690820392.352191,VS0,VS0,VE155 121 | vary: 122 | - Accept-Encoding 123 | strict-transport-security: 124 | - max-age=300 125 | body: 126 | encoding: UTF-8 127 | string: '{"error":"Unauthorized","status":401,"message":"Client ID is missing"}' 128 | recorded_at: Mon, 31 Jul 2023 16:19:52 GMT 129 | recorded_with: VCR 6.2.0 130 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_get_users/when_token_type_is_application_/with_incorrect_client_credentials/without_tokens/1_4_1_2_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://id.twitch.tv/oauth2/token 6 | body: 7 | encoding: UTF-8 8 | string: '{"client_id":null,"client_secret":null,"code":null,"grant_type":"client_credentials","redirect_uri":"http://localhost"}' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Content-Type: 13 | - application/json 14 | response: 15 | status: 16 | code: 400 17 | message: Bad Request 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:19:53 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '45' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7df29-551d591e1880555f2e280a78 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":400,"message":"missing client id"} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:19:53 GMT 39 | recorded_with: VCR 6.2.0 40 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_modify_channel/when_everything_is_OK/1_11_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 13:42:37 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7ba4d-48439de85aa435d73c6cb352 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 13:42:37 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","grant_type":"refresh_token","refresh_token":""}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.10 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Mon, 31 Jul 2023 13:42:38 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '677' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-64c7ba4e-22c896265fa57c3b59063f74 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":13750,"refresh_token":"","scope":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Mon, 31 Jul 2023 13:42:38 GMT 75 | - request: 76 | method: patch 77 | uri: https://api.twitch.tv/helix/channels 78 | body: 79 | encoding: UTF-8 80 | string: '{"broadcaster_id":"277558749","game_id":"509670","title":"Test"}' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | Content-Type: 89 | - application/json 90 | response: 91 | status: 92 | code: 204 93 | message: No Content 94 | headers: 95 | connection: 96 | - keep-alive 97 | access-control-allow-origin: 98 | - "*" 99 | ratelimit-limit: 100 | - '800' 101 | ratelimit-remaining: 102 | - '799' 103 | ratelimit-reset: 104 | - '1690810959' 105 | timing-allow-origin: 106 | - https://www.twitch.tv 107 | date: 108 | - Mon, 31 Jul 2023 13:42:38 GMT 109 | x-served-by: 110 | - cache-bfi-krnt7300027-BFI, cache-lcy-eglc8600077-LCY 111 | x-cache: 112 | - MISS, MISS 113 | x-cache-hits: 114 | - 0, 0 115 | x-timer: 116 | - S1690810959.555869,VS0,VS0,VE180 117 | vary: 118 | - Accept-Encoding 119 | strict-transport-security: 120 | - max-age=300 121 | body: 122 | encoding: UTF-8 123 | string: '' 124 | recorded_at: Mon, 31 Jul 2023 13:42:38 GMT 125 | recorded_with: VCR 6.2.0 126 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_modify_channel/when_game_ID_is_incorrect/1_11_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 13:42:39 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7ba4f-5a6700a670a3e7b57d600d32 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 13:42:39 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","grant_type":"refresh_token","refresh_token":""}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.10 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Mon, 31 Jul 2023 13:42:40 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '677' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-64c7ba50-4c5059e56b873a275ee6602d 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":14958,"refresh_token":"","scope":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Mon, 31 Jul 2023 13:42:40 GMT 75 | - request: 76 | method: patch 77 | uri: https://api.twitch.tv/helix/channels 78 | body: 79 | encoding: UTF-8 80 | string: '{"broadcaster_id":"277558749","game_id":"abc","title":"Test"}' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | Content-Type: 89 | - application/json 90 | response: 91 | status: 92 | code: 400 93 | message: Bad Request 94 | headers: 95 | connection: 96 | - keep-alive 97 | content-length: 98 | - '80' 99 | content-type: 100 | - application/json; charset=utf-8 101 | access-control-allow-origin: 102 | - "*" 103 | ratelimit-limit: 104 | - '800' 105 | ratelimit-remaining: 106 | - '799' 107 | ratelimit-reset: 108 | - '1690810962' 109 | timing-allow-origin: 110 | - https://www.twitch.tv 111 | date: 112 | - Mon, 31 Jul 2023 13:42:41 GMT 113 | x-served-by: 114 | - cache-bfi-krnt7300020-BFI, cache-lcy-eglc8600070-LCY 115 | x-cache: 116 | - MISS, MISS 117 | x-cache-hits: 118 | - 0, 0 119 | x-timer: 120 | - S1690810961.109400,VS0,VS0,VE156 121 | vary: 122 | - Accept-Encoding 123 | strict-transport-security: 124 | - max-age=300 125 | body: 126 | encoding: UTF-8 127 | string: '{"error":"Bad Request","status":400,"message":"The ID in game_id is 128 | not valid."}' 129 | recorded_at: Mon, 31 Jul 2023 13:42:41 GMT 130 | recorded_with: VCR 6.2.0 131 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_search_categories/with_options/when_token_type_is_application/data/1_15_2_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:02:40 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64ddfe50-37aca29a26dc6723030b1519 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":3906844} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:02:41 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/search/categories?query=angel+of+death 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '1101' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1692270162' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Thu, 17 Aug 2023 11:02:41 GMT 75 | x-served-by: 76 | - cache-bfi-kbfi7400098-BFI, cache-lcy-eglc8600079-LCY 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1692270161.311611,VS0,VS0,VE188 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":[{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/138_IGDB-52x72.jpg","id":"138","name":"Police 90 | Quest: In Pursuit of the Death Angel"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/7126_IGDB-52x72.jpg","id":"7126","name":"Astaroth: 91 | The Angel of Death"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/496471_IGDB-52x72.jpg","id":"496471","name":"Angels 92 | of Death"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/36463168_IGDB-52x72.jpg","id":"36463168","name":"Angels 93 | of Death Episode.Eddie"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/719631982-52x72.jpg","id":"719631982","name":"Death 94 | Angel Nightmare"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/1151716863_IGDB-52x72.jpg","id":"1151716863","name":"Gravity 95 | Angels Part 4: Death Force"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/1341963614_IGDB-52x72.jpg","id":"1341963614","name":"Police 96 | Quest: In Pursuit of the Death Angel"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/1509140451_IGDB-52x72.jpg","id":"1509140451","name":"Death 97 | Angel"}],"pagination":{}}' 98 | recorded_at: Thu, 17 Aug 2023 11:02:41 GMT 99 | recorded_with: VCR 6.2.0 100 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_search_categories/with_options/when_token_type_is_user/data/1_15_2_2_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:03:46 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '656' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64ddfe92-1056ac63160b2d1a7df82690 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:manage:redemptions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":12278} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:03:46 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/search/categories?query=angel+of+death 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 200 55 | message: OK 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '1101' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | ratelimit-limit: 66 | - '800' 67 | ratelimit-remaining: 68 | - '799' 69 | ratelimit-reset: 70 | - '1692270228' 71 | timing-allow-origin: 72 | - https://www.twitch.tv 73 | date: 74 | - Thu, 17 Aug 2023 11:03:47 GMT 75 | x-served-by: 76 | - cache-bfi-krnt7300105-BFI, cache-lcy-eglc8600067-LCY 77 | x-cache: 78 | - MISS, MISS 79 | x-cache-hits: 80 | - 0, 0 81 | x-timer: 82 | - S1692270227.958039,VS0,VS0,VE183 83 | vary: 84 | - Accept-Encoding 85 | strict-transport-security: 86 | - max-age=300 87 | body: 88 | encoding: UTF-8 89 | string: '{"data":[{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/138_IGDB-52x72.jpg","id":"138","name":"Police 90 | Quest: In Pursuit of the Death Angel"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/7126_IGDB-52x72.jpg","id":"7126","name":"Astaroth: 91 | The Angel of Death"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/496471_IGDB-52x72.jpg","id":"496471","name":"Angels 92 | of Death"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/36463168_IGDB-52x72.jpg","id":"36463168","name":"Angels 93 | of Death Episode.Eddie"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/719631982-52x72.jpg","id":"719631982","name":"Death 94 | Angel Nightmare"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/1151716863_IGDB-52x72.jpg","id":"1151716863","name":"Gravity 95 | Angels Part 4: Death Force"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/1341963614_IGDB-52x72.jpg","id":"1341963614","name":"Police 96 | Quest: In Pursuit of the Death Angel"},{"box_art_url":"https://static-cdn.jtvnw.net/ttv-boxart/1509140451_IGDB-52x72.jpg","id":"1509140451","name":"Death 97 | Angel"}],"pagination":{}}' 98 | recorded_at: Thu, 17 Aug 2023 11:03:47 GMT 99 | recorded_with: VCR 6.2.0 100 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_search_categories/without_options/when_token_type_is_application/1_15_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:02:38 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64ddfe4e-1b9c83af686519780ad9d6f2 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":3906846} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:02:38 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/search/categories 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '85' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Thu, 17 Aug 2023 11:02:38 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400069-BFI, cache-lhr7320-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1692270158.489358,VS0,VS0,VE150 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"query\""}' 85 | recorded_at: Thu, 17 Aug 2023 11:02:38 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_search_categories/without_options/when_token_type_is_user/1_15_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Thu, 17 Aug 2023 11:02:39 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '656' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64ddfe4f-715990b81f0e009f550c1724 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","login":"","scopes":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:manage:redemptions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"user_id":"","expires_in":12345} 36 | 37 | ' 38 | recorded_at: Thu, 17 Aug 2023 11:02:39 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/search/categories 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '85' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Thu, 17 Aug 2023 11:02:40 GMT 69 | x-served-by: 70 | - cache-bfi-kbfi7400069-BFI, cache-lhr7377-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1692270160.904939,VS0,VS0,VE150 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"query\""}' 85 | recorded_at: Thu, 17 Aug 2023 11:02:40 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_search_channels/without_options/when_token_type_is_application/1_10_1_1_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 200 17 | message: OK 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 16:19:54 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '82' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7df2a-755e46bf5453fd70684d4343 33 | body: 34 | encoding: UTF-8 35 | string: '{"client_id":"","scopes":null,"expires_in":5356610} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 16:19:54 GMT 39 | - request: 40 | method: get 41 | uri: https://api.twitch.tv/helix/search/channels 42 | body: 43 | encoding: US-ASCII 44 | string: '' 45 | headers: 46 | User-agent: 47 | - twitch-api ruby client 0.4.0 48 | Client-ID: 49 | - "" 50 | Authorization: 51 | - "" 52 | response: 53 | status: 54 | code: 400 55 | message: Bad Request 56 | headers: 57 | connection: 58 | - keep-alive 59 | content-length: 60 | - '85' 61 | content-type: 62 | - application/json; charset=utf-8 63 | access-control-allow-origin: 64 | - "*" 65 | timing-allow-origin: 66 | - https://www.twitch.tv 67 | date: 68 | - Mon, 31 Jul 2023 16:19:54 GMT 69 | x-served-by: 70 | - cache-bfi-krnt7300039-BFI, cache-lhr7347-LHR 71 | x-cache: 72 | - MISS, MISS 73 | x-cache-hits: 74 | - 0, 0 75 | x-timer: 76 | - S1690820395.761744,VS0,VS0,VE150 77 | vary: 78 | - Accept-Encoding 79 | strict-transport-security: 80 | - max-age=300 81 | body: 82 | encoding: UTF-8 83 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 84 | \"query\""}' 85 | recorded_at: Mon, 31 Jul 2023 16:19:54 GMT 86 | recorded_with: VCR 6.2.0 87 | -------------------------------------------------------------------------------- /spec/cassettes/Twitch_Client/_search_channels/without_options/when_token_type_is_user/1_10_1_2_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://id.twitch.tv/oauth2/validate 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: 11 | - Faraday v2.7.10 12 | Authorization: 13 | - "" 14 | response: 15 | status: 16 | code: 401 17 | message: Unauthorized 18 | headers: 19 | date: 20 | - Mon, 31 Jul 2023 13:33:58 GMT 21 | content-type: 22 | - application/json 23 | content-length: 24 | - '48' 25 | connection: 26 | - keep-alive 27 | server: 28 | - nginx 29 | access-control-allow-origin: 30 | - "*" 31 | x-ctxlog-logid: 32 | - 1-64c7b846-382439e01aa67838788b79d7 33 | body: 34 | encoding: UTF-8 35 | string: '{"status":401,"message":"invalid access token"} 36 | 37 | ' 38 | recorded_at: Mon, 31 Jul 2023 13:33:58 GMT 39 | - request: 40 | method: post 41 | uri: https://id.twitch.tv/oauth2/token 42 | body: 43 | encoding: UTF-8 44 | string: '{"client_id":"","client_secret":"","grant_type":"refresh_token","refresh_token":""}' 45 | headers: 46 | User-Agent: 47 | - Faraday v2.7.10 48 | Content-Type: 49 | - application/json 50 | response: 51 | status: 52 | code: 200 53 | message: OK 54 | headers: 55 | date: 56 | - Mon, 31 Jul 2023 13:33:59 GMT 57 | content-type: 58 | - application/json 59 | content-length: 60 | - '677' 61 | connection: 62 | - keep-alive 63 | server: 64 | - nginx 65 | access-control-allow-origin: 66 | - "*" 67 | x-ctxlog-logid: 68 | - 1-64c7b847-3a05af9c4b83460b56def88b 69 | body: 70 | encoding: UTF-8 71 | string: '{"access_token":"","expires_in":14840,"refresh_token":"","scope":["bits:read","channel:edit:commercial","channel:manage:broadcast","channel:manage:polls","channel:manage:predictions","channel:moderate","channel:read:polls","channel:read:predictions","chat:edit","chat:read","clips:edit","moderation:read","moderator:manage:announcements","moderator:manage:banned_users","moderator:manage:chat_messages","moderator:manage:chat_settings","moderator:read:chat_settings","moderator:read:chatters","user:edit","user:manage:blocked_users","user:read:broadcast","user:read:email"],"token_type":"bearer"} 72 | 73 | ' 74 | recorded_at: Mon, 31 Jul 2023 13:33:59 GMT 75 | - request: 76 | method: get 77 | uri: https://api.twitch.tv/helix/search/channels 78 | body: 79 | encoding: US-ASCII 80 | string: '' 81 | headers: 82 | User-agent: 83 | - twitch-api ruby client 0.4.0 84 | Client-ID: 85 | - "" 86 | Authorization: 87 | - "" 88 | response: 89 | status: 90 | code: 400 91 | message: Bad Request 92 | headers: 93 | connection: 94 | - keep-alive 95 | content-length: 96 | - '85' 97 | content-type: 98 | - application/json; charset=utf-8 99 | access-control-allow-origin: 100 | - "*" 101 | timing-allow-origin: 102 | - https://www.twitch.tv 103 | date: 104 | - Mon, 31 Jul 2023 13:34:00 GMT 105 | x-served-by: 106 | - cache-bfi-krnt7300039-BFI, cache-lcy-eglc8600029-LCY 107 | x-cache: 108 | - MISS, MISS 109 | x-cache-hits: 110 | - 0, 0 111 | x-timer: 112 | - S1690810440.076423,VS0,VS0,VE146 113 | vary: 114 | - Accept-Encoding 115 | strict-transport-security: 116 | - max-age=300 117 | body: 118 | encoding: UTF-8 119 | string: '{"error":"Bad Request","status":400,"message":"Missing required parameter 120 | \"query\""}' 121 | recorded_at: Mon, 31 Jul 2023 13:34:00 GMT 122 | recorded_with: VCR 6.2.0 123 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pry-byebug' 4 | 5 | require 'simplecov' 6 | 7 | if ENV['CI'] 8 | require 'simplecov-cobertura' 9 | SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter 10 | end 11 | 12 | SimpleCov.start 13 | 14 | ENV['TWITCH_CLIENT_ID'] ||= 'test_client_id' 15 | ENV['TWITCH_CLIENT_SECRET'] ||= 'test_client_secret' 16 | ENV['TWITCH_ACCESS_TOKEN'] ||= 'test_access_token' 17 | ENV['TWITCH_REFRESH_TOKEN'] ||= 'test_refresh_token' 18 | ENV['TWITCH_APPLICATION_ACCESS_TOKEN'] ||= 'test_application_access_token' 19 | 20 | RSpec.configure do |config| 21 | # Enable flags like --only-failures and --next-failure 22 | config.example_status_persistence_file_path = '.rspec_status' 23 | 24 | # Disable RSpec exposing methods globally on `Module` and `main` 25 | config.disable_monkey_patching! 26 | end 27 | 28 | RSpec::Matchers.define :a_boolean do 29 | match do |value| 30 | [true, false].include? value 31 | end 32 | end 33 | 34 | # require 'webmock/rspec' 35 | require 'vcr' 36 | 37 | VCR.configure do |vcr_config| 38 | vcr_config.cassette_library_dir = "#{__dir__}/cassettes" 39 | vcr_config.configure_rspec_metadata! 40 | vcr_config.hook_into :faraday 41 | 42 | vcr_config.filter_sensitive_data('') do 43 | ENV.fetch('TWITCH_CLIENT_ID') 44 | end 45 | 46 | vcr_config.filter_sensitive_data('') do 47 | ENV.fetch('TWITCH_CLIENT_SECRET') 48 | end 49 | 50 | vcr_config.filter_sensitive_data('') do 51 | ENV.fetch('TWITCH_ACCESS_TOKEN') 52 | end 53 | 54 | vcr_config.filter_sensitive_data('') do 55 | ENV.fetch('TWITCH_REFRESH_TOKEN') 56 | end 57 | 58 | vcr_config.filter_sensitive_data('') do 59 | ENV.fetch('TWITCH_APPLICATION_ACCESS_TOKEN') 60 | end 61 | 62 | vcr_config.filter_sensitive_data('') do |interaction| 63 | if interaction.response.headers['content-type']&.include? 'application/json' 64 | JSON.parse(interaction.response.body)['access_token'] 65 | end 66 | end 67 | 68 | vcr_config.filter_sensitive_data('') do |interaction| 69 | if interaction.response.headers['content-type']&.include? 'application/json' 70 | JSON.parse(interaction.response.body)['refresh_token'] 71 | end 72 | end 73 | 74 | vcr_config.filter_sensitive_data('') do |interaction| 75 | if interaction.request.uri == 'https://id.twitch.tv/oauth2/token' 76 | JSON.parse(interaction.request.body)['code'] 77 | end 78 | end 79 | 80 | vcr_config.filter_sensitive_data('') do |interaction| 81 | interaction.request.headers['Authorization']&.first 82 | end 83 | 84 | vcr_config.filter_sensitive_data('') do |interaction| 85 | if interaction.request.uri == 'https://id.twitch.tv/oauth2/validate' 86 | JSON.parse(interaction.response.body)['login'] 87 | end 88 | end 89 | 90 | vcr_config.filter_sensitive_data('') do |interaction| 91 | if interaction.request.uri == 'https://id.twitch.tv/oauth2/validate' 92 | JSON.parse(interaction.response.body)['user_id'] 93 | end 94 | end 95 | 96 | vcr_config.filter_sensitive_data('') do |interaction| 97 | if ( 98 | interaction.request.uri.include?('streams/key') && 99 | interaction.response.headers['content-type'] 100 | &.any? { |type| type.include?('application/json') } 101 | ) 102 | data = JSON.parse(interaction.response.body)['data'] 103 | 104 | next unless data 105 | 106 | data.first['stream_key'] 107 | end 108 | end 109 | end 110 | 111 | require_relative '../lib/twitch-api' 112 | -------------------------------------------------------------------------------- /spec/twitch/version_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'Twitch::VERSION' do 4 | subject { Object.const_get(self.class.description) } 5 | 6 | it { is_expected.to match(/^\d+\.\d+\.\d+$/) } 7 | end 8 | -------------------------------------------------------------------------------- /twitch-api.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'lib/twitch/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'twitch-api' 7 | spec.version = Twitch::VERSION 8 | spec.authors = ['Maurice Wahba', 'Alexander Popov'] 9 | spec.email = ['maurice.wahba@gmail.com', 'alex.wayfer@gmail.com'] 10 | 11 | spec.summary = 'Ruby client for the Twitch Helix API.' 12 | spec.homepage = 'https://github.com/mauricew/ruby-twitch-api' 13 | spec.license = 'MIT' 14 | 15 | github_uri = 'https://github.com/mauricew/ruby-twitch-api' 16 | 17 | spec.metadata = { 18 | 'bug_tracker_uri' => "#{github_uri}/issues", 19 | 'documentation_uri' => "http://www.rubydoc.info/gems/#{spec.name}/#{spec.version}", 20 | 'homepage_uri' => spec.homepage, 21 | 'rubygems_mfa_required' => 'true', 22 | 'source_code_uri' => github_uri 23 | } 24 | 25 | spec.required_ruby_version = '>= 3.0', '< 4' 26 | 27 | spec.files = Dir['lib/**/*.rb', 'README.md', 'LICENSE.txt'] 28 | 29 | spec.add_dependency 'faraday', '~> 2.3' 30 | spec.add_dependency 'faraday-parse_dates', '~> 0.1.1' 31 | spec.add_dependency 'faraday-retry', '~> 2.0' 32 | spec.add_dependency 'twitch_oauth2', '~> 0.5.0' 33 | end 34 | --------------------------------------------------------------------------------