Skip to content

Commit

Permalink
Add support for reading Threads media posts
Browse files Browse the repository at this point in the history
  • Loading branch information
davidcelis committed Jun 19, 2024
1 parent 0a69592 commit 826b3c2
Show file tree
Hide file tree
Showing 5 changed files with 397 additions and 1 deletion.
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,39 @@ access_token = response.access_token
expires_at = Time.now + response.expires_in
```

Once you have a valid access token, whether it's short-lived or long-lived, you can use it to make requests to the Threads API.
Once you have a valid access token, whether it's short-lived or long-lived, you can use it to make requests to the Threads API using the `Threads::API::Client`.

## Reading Threads

To fetch threads for a user:

```ruby
client = Threads::API::Client.new(access_token)

# List recent threads for a user.
response = client.threads # Defaults to the authenticated user
response = client.threads(user_id: "7770386109746442")

# By default, the Threads API returns 25 threads at a time. You can paginate through them like so:
next_page = client.threads(after: response.after_cursor) # or
previous_page = client.threads(before: response.before_cursor)

# Fetch a specific thread by ID.
thread = client.thread("18050206876707110") # Defaults to the authenticated user
thread = client.thread("18050206876707110", user_id: "7770386109746442")
```

`Threads::API::Client#threads` accepts the following options:

* `user_id` - The ID of the user whose threads you want to fetch. Defaults to `"me"`, the authenticated user.
* `fields` - An Array (or comma-separated String) of fields to include in the response. By default, only `id` is requested. See the [Threads API documentation](https://developers.facebook.com/docs/threads/threads-media#fields) for a list of available fields.
* `since` - An ISO 8601 date string. Only threads published after this date will be returned.
* `until` - An ISO 8601 date string. Only threads published before this date will be returned.
* `before` - A cursor string returned by a previous request for pagination.
* `after` - A cursor string returned by a previous request for pagination.
* `limit` - The number of threads to return. Defaults to `25`, with a maximum of `100`.

`Threads::API::Client#thread` accepts only the `user_id` and `fields` options.

## Contributing

Expand Down
1 change: 1 addition & 0 deletions lib/threads/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

require "faraday"

require_relative "api/client"
require_relative "api/oauth2/client"
require_relative "api/version"
43 changes: 43 additions & 0 deletions lib/threads/api/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require_relative "thread"

module Threads
module API
class Client
def initialize(access_token)
@access_token = access_token
end

def threads(user_id: "me", **options)
params = options.slice(:since, :until, :before, :after, :limit).compact
params[:access_token] = @access_token

fields = Array(options[:fields]).join(",")
params[:fields] = fields unless fields.empty?

response = connection.get("#{user_id}/threads", params)

Threads::API::Thread::List.new(response.body)
end

def thread(thread_id, user_id: "me", fields: nil)
params = {access_token: @access_token}
params[:fields] = Array(fields).join(",") if fields

response = connection.get("#{user_id}/threads/#{thread_id}", params)

Threads::API::Thread.new(response.body)
end

private

def connection
@connection ||= Faraday.new(url: "https://graph.threads.net/v1.0/") do |f|
f.request :url_encoded

f.response :json
f.response :raise_error
end
end
end
end
end
43 changes: 43 additions & 0 deletions lib/threads/api/thread.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require "time"

module Threads
module API
class Thread
class List
attr_reader :threads, :before, :after

def initialize(json)
@threads = json["data"].map { |t| Threads::API::Thread.new(t) }

@before = json.dig("paging", "cursors", "before")
@after = json.dig("paging", "cursors", "after")
end
end

attr_reader :id, :type, :media_url, :permalink, :user_id, :username, :text, :timestamp, :created_at, :shortcode, :video_thumbnail_url, :children

def initialize(json)
@id = json["id"]
@type = json["media_type"]
@permalink = json["permalink"]
@shortcode = json["shortcode"]
@text = json["text"]
@media_url = json["media_url"]
@video_thumbnail_url = json["thumbnail_url"]
@user_id = json.dig("owner", "id")
@username = json["username"]
@is_quote_post = json["is_quote_post"]

@timestamp = json["timestamp"]
@created_at = Time.iso8601(@timestamp) if @timestamp

children = Array(json["children"])
@children = children.map { |c| Thread.new(c) } if children.any?
end

def quote_post?
@is_quote_post
end
end
end
end
Loading

0 comments on commit 826b3c2

Please sign in to comment.