RSpec Tests: CircleCI

Code Coverage: codecov

TinderClient

A Ruby gem to interact with Tinder's REST API.

Usage

Rake Commands

rake tinder:updates          # Fetch updates
rake tinder:profile          # Fetch my profile
rake tinder:recommendations  # Fetch recommendations
rake tinder:save_token       # Save an API token to $token_path ake tinder:get_updates      # Fetch updates

To grab a token, call a rake command & specify the phone_number or api_token in your environment variables.

rake tinder:save_token

$ rake tinder:save_token \
phone_number=15556667777 \
tinder_token_path=/tmp
Enter the confirmation code sent to 15556667777> 
123456
Validating...
Done!
Your refresh token is eyJhbGciOiJIUzI1NiJ9.MTc3ODk5MDk4MDM.5q4R0H08rE0Dd9KgxMPp6jcTfIBLCXgEuVZfC9znJTE
Logging in...
Done!
Your tinder API token is 12a3bc45-a123-123a-1a23-1234abc4de5f
Saved to /tmp/tinder_access_token.txt

Use Tinder Test-Helpers in your RSpec Tests

tinder_client has webmock stubs you can include in your project to get fake responses back from Tinder:

gem_dir = Gem::Specification.find_by_name("tinder_client").gem_dir
require "#{gem_dir}/spec/tinder/contexts/http_request_stubs"

RSpec.describe 'some test' do
  include_context 'http_request_stubs'

  # Your tests that use Tinder HTTP requests go here
end 

Accessing your saved data

better_tinder converts responses from Tinder from raw JSON data to Dry::Struct, for your convenience.

That means, call the services with your API token to return a struct:

SaveRecommendations.call(api_token:)

  class Photo < Dry::Struct
    attribute :id, Types.string
    attribute? :crop_info do
      attribute? :user do
        attribute :width_pct, Dry::Types['coercible.float']
        attribute :x_offset_pct, Types.float
        attribute :height_pct, Types.float
        attribute :y_offset_pct, Types.float
      end
      attribute? :algo do
        attribute :width_pct, Types.float
        attribute :x_offset_pct, Types.float
        attribute :height_pct, Types.float
        attribute :y_offset_pct, Types.float
      end
      attribute :processed_by_bullseye, Types.bool
      attribute :user_customized, Types.bool
      attribute? :url, Types.string
      attribute? :processedFiles, Types.array
      attribute? :fileName, Types.string
      attribute? :extension, Types.string
    end
  end

  class User < Dry::Struct
    attribute :_id, Types.string
    attribute :bio, Types.string
    attribute :birth_date, Types.string
    attribute :name, Types.string
    attribute :photos, Types.array.of(Photo)
    attribute :gender, Types.integer
    attribute :jobs, Types.array
    attribute :schools, Types.array do
      attribute :name, Types.string
    end
    attribute? :city do
      attribute :name, Types.string
    end
    attribute? :is_traveling, Types.bool
    attribute? :hide_age, Types.bool
    attribute? :hide_distance, Types.bool
  end

  # Return this object
  class Recommendation < Dry::Struct
    attribute :type, Types.string
    attribute :user, User
    attribute :facebook do
      attribute :common_connections, Types.array
      attribute :connection_count, Types.integer
      attribute :common_interests, Types.array
    end
    attribute :spotify, Types.hash
    attribute :distance_mi, Types.integer
    attribute :content_hash, Types.string
    attribute :s_number, Types.integer
    attribute :teasers, Types.array do
      attribute :type, Types.string
      attribute :string, Types.string
    end
  end

SaveUpdates.call(api_token:)

  class Message < Dry::Struct
    attribute :_id, Types.string
    attribute :match_id, Types.string
    attribute :sent_date, Types.string
    attribute :message, Types.string
    attribute :to, Types.string
    attribute :from, Types.string
    attribute :created_date, Types.string
    attribute :timestamp, Types.send('coercible.string')
  end

  class LikedMessage < Dry::Struct
    attribute :message_id, Types.string
    attribute :updated_at, Types.string
    attribute :liker_id, Types.string
    attribute :match_id, Types.string
    attribute :is_liked, Types.bool
  end

  class Person < Dry::Struct
    attribute? :bio, Types.string
    attribute :birth_date, Types.string
    attribute :gender, Types.integer
    attribute :name, Types.string
    attribute :ping_time, Types.string
    attribute :photos, Types.array
  end

  class Match < Dry::Struct
    attribute :_id, Types.string
    attribute :closed, Types.bool
    attribute :common_friend_count, Types.integer
    attribute :common_like_count, Types.integer
    attribute :created_date, Types.string
    attribute :dead, Types.bool
    attribute :following, Types.bool
    attribute :following_moments, Types.bool
    attribute :id, Types.string
    attribute :is_boost_match, Types.bool
    attribute :is_fast_match, Types.bool
    attribute :is_super_like, Types.bool
    attribute :last_activity_date, Types.string
    attribute :message_count, Types.integer
    attribute :messages, Types.array.of(Message)
    attribute :muted, Types.bool
    attribute :participants, Types.array
    attribute :pending, Types.bool
    attribute :person, Person
    attribute :readreceipt, Types.hash
    attribute :seen, Types.hash
  end

  class Updates < Dry::Struct
    attribute :blocks, Types.array.of(Types.string)
    attribute :deleted_lists, Types.array
    attribute :goingout, Types.array
    attribute :harassing_messages, Types.array
    attribute :inbox, Types.array.of(Message)
    attribute :poll_interval, Types.hash
    attribute :liked_messages, Types.array.of(LikedMessage)
    attribute :lists, Types.array
    attribute :matches, Types.array.of(Match)
    attribute :squads, Types.array
  end