How to Add DEV Comments to Your Orbit Workspace With Ruby

Developer Relations
April 27, 2021
Ben Greenberg
Developer Advocate

The DEV community has become one of the fastest-growing developer blogging platforms. Whether you are posting primarily on the DEV blog or cross-posting your content there from your primary content source, keeping on top of the comments left by users is critical. Few things can be more frustrating to someone trying to engage than receiving no response.

In this step-by-step tutorial, we are going to use both the DEV API and the Orbit API to retrieve blog post comments and add them as a custom activity into Orbit.

Example of DEV comment added to Orbit

tl;dr If you wish to skip the tutorial, the entire code for this project as a Ruby gem that can be installed in your project can be found on GitHub.

Prerequisites

You will need the following to complete the steps in this tutorial:

  • Ruby 2.7 or greater installed locally.
  • An Orbit account. If you don’t have one already, you can sign up today and start building with the Orbit API. Once you have an account, you can find your API Token in your Account Settings.
  • A DEV Community account. Once you have your DEV account, you can generate an API Key in your Account Settings.

Connecting to the DEV API

There are two separate API endpoints we need to access on the DEV API in order to get our blog posts comments. Namely, we need to retrieve a list of our articles, and then, we need to retrieve the comments for each article.

Get List of Published Articles

The first operation, GET Published Articles, accepts several query parameters. We will be using the username parameter. The username parameter allows us to narrow our article search for a specific user's articles.

The following is the HTTP request for our DEV articles:

{% c-block language="ruby" %}

url = URI("https://dev.to/api/articles?username=#{@username}")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Get.new(url)

response = https.request(request)

articles = JSON.parse(response.body)

{% c-block-end %}

In your code, replace #{@username} with the username of the DEV user you are searching for.

We are now ready to use the articles list to retrieve the comments for each article.

Get Comments for Each Article

Once we have our list of articles from DEV, we can go ahead and access the next DEV API endpoint of GET Comments by Article ID. This endpoint requires the article ID as a query parameter.

We will build an iterator in our code to loop through the list of articles and request the comments for each one:

{% c-block language="ruby" %}

comments = articles.each do |article|
 get_article_comments(article["id"])
end

def get_article_comments(id)
 url = URI("https://dev.to/api/comments?a_id=#{id}")
 https = Net::HTTP.new(url.host, url.port)
 https.use_ssl = true

 request = Net::HTTP::Get.new(url)

 response = https.request(request)

 comments = JSON.parse(response.body)

 return if comments.nil? || comments.empty?

 filter_comments(comments)
end

{% c-block-end %}

First, we iterate through each article in articles invoking a method called #get_article_comments passing in the article's ID as the method's parameter.

Next, we create the #get_article_comments method. The method makes an HTTP request to the DEV API endpoint. At the end of the method, we execute another method that we have not yet created called #filter_comments. Inside this next method, we can add any sort of datetime filtering we want to the data as a way to limit the scope if we are working with a lot of DEV blog post comments. In the following example, I restrict the data set to anything less than or equal to one day ago:

{% c-block language="ruby" %}

def filter_comments(comments)
 comments.select do |comment|
   comment["created_at"] <= 1.day.ago
 end
end

{% c-block-end %}

Note: The 1.day.ago functionality in the code snippet above comes from ActiveSupport. To take advantage of it you need to add require "active_support/time" at the top of your Ruby script.

We now have all of the comments that we want to add as custom activities to our Orbit workspace! Let's go ahead and start doing that.

Working with the Orbit API

The Orbit API lets you perform a wide range of activities in your Orbit workspace programmatically. The API reference guide is a good starting point for exploration. For our purposes, we will be using the create a new activity for existing or new member API operation.

API access included in every Orbit account
API access is included with every account! Try it out by signing up today.

Creating a New Custom Activity

The Orbit documentation informs us that when we send a new activity to Orbit through the API, it will also either retrieve an existing member in our workspace or create a new member if an existing one cannot be found. This means we only need to send one HTTP request to Orbit to both create the blog post comment as an activity and attach it to a member!

According to the API reference we need to define an activity_type and a title for the activity, along with member identity information. We will also send more descriptive information to further supplement the record in the workspace, such as a description and a link.

Constructing the Custom Activity Data Object

Let's first construct the request body that we will send in the HTTP request. We will create two methods first, #sanitize_commentand #construct_commenter. They both will clean the data we received from the DEV API and put it in the proper format for Orbit.

The #sanitize_comment method removes all the HTML tags from the comments to leave us with just the body of the message.

The #construct_commenter method forms a hash representing the identifying information of the commenter. If the DEV commenter has a Twitter or GitHub username in their DEV profile we add it to the information we send to Orbit.

{% c-block language="ruby" %}

 def sanitize_comment(comment)
   comment = ActionView::Base.full_sanitizer.sanitize(comment)

   comment.gsub("\n", " ")
 end

 def construct_commenter(commenter)
   hash = {
     'name': commenter[:name],
     'username': commenter[:username]
   }

   unless commenter[:twitter_username].nil? || commenter[:twitter_username] == ""
     hash.merge!('twitter': commenter[:twitter_username])
   end

   unless commenter[:github_username].nil? || commenter[:github_username] == ""
     hash.merge!('github': commenter[:github_username])
   end

   hash
 end

 def construct_body
   @commenter = construct_commenter(@commenter)

   hash = {
     activity: {
       activity_type: "dev:comment",
       key: "dev-comment-#{@comment[:id]}",
       title: "Commented on the DEV blog post: #{@article_title}",
       description: sanitize_comment(@comment[:body_html]),
       occurred_at: @article[:created_at],
       link: @article[:url],
       member: {
         name: @commenter[:name],
         devto: @commenter[:username]
       }
     },
     identity: {
       source: "devto",
       username: @commenter[:username]
     }
   }

   hash[:activity][:member].merge!(twitter: @commenter[:twitter]) if @commenter[:twitter]

   hash[:activity][:member].merge!(github: @commenter[:github]) if @commenter[:github]

   hash
 end

{% c-block-end %}

In the #construct_body method we create a hash with all the data we plan to send to Orbit.

  • The activity_type is dev:comment
  • A custom ID in the key field interpolating the string dev-comment with the DEV comment ID
  • The title of Commented on the DEV blog post interpolated with the blog post title
  • The comment itself in the description field stripped of all of its HTML tags
  • The URL to the DEV blog post in the link field
  • The member and identity objects with the user's name and DEV username
  • If the commenter has a Twitter or GitHub username in their profile those are added to the HTTP request body as well in the member object

Posting the Activity to Orbit

We are now ready to make the POST request to Orbit with our data. This will be a standard Ruby HTTP request using the net/http library:

{% c-block language="ruby" %}

url = URI("https://app.orbit.love/api/v1/#{@workspace_id}/activities")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
req = Net::HTTP::Post.new(url)
req["Accept"] = "application/json"
req["Content-Type"] = "application/json"
req["Authorization"] = "Bearer #{@api_key}"

req.body = construct_body

req.body = req.body.to_json

response = http.request(req)

JSON.parse(response.body)

{% c-block-end %}

Once the activity has been sent to the Orbit API, you should see it soon after in your workspace!

Screenshot of a DEV blog post comment on Orbit

Using the Ruby Gem

The code in this tutorial comes from a fully-featured Ruby gem that abstracts a lot of the work for you. It also includes a command-line interface (CLI) to make execution even more straightforward.

The code for the gem can be found on GitHub. To install the gem in your project add it to your Gemfile:

{% c-block language="ruby" %}

# Gemfile

gem 'dev_orbit'

{% c-block-end %}

Then, run bundle install from the command line. Once the gem has been included into your project, you can instantiate a client by passing in your relevant DEV and Orbit API credentials:

{% c-block language="ruby" %}

client = DevOrbit::Client.new(
 orbit_api_key: '...',
 orbit_workspace: '...',
 dev_api_key: '...',
 dev_username: '...'
)

{% c-block-end %}

To fetch all new comments on your DEV blog posts within the past day, you can invoke the #comments instance method on your client:

{% c-block language="ruby" %}

client.comments

{% c-block-end %}

This method will gather all the comments in the past day from DEV, format them for activities in your Orbit workspace, and make the POST request to the Orbit API to add them.

Automating with GitHub Actions

What if you would like to run this gem once a day to fetch all your latest DEV comments and add them to your Orbit workspace? You could manually run it daily, or you can use GitHub Actions to automate it for you!

GitHub Actions is an environment to run all your software workflows provided by GitHub for free for any public repository. You can use it to run your code's testing suite, to deploy to the cloud or any one of numerous use cases. In our example, we will use GitHub Actions to run this gem once a day on a cron schedule.

Inside your GitHub repository create a folder called .github, and another one called workflows inside the first one. Within the workflows folder create a YAML file called dev_comments.yml. Add the following YAML text into the file:

{% c-block language="ruby" %}

name: Check For New DEV Blog Post Comments and Add to Orbit Workspace
on:
 schedule:
   - cron: "0 0 */1 * *"
 workflow_dispatch:
   branches:
     - main
jobs:
 comments-workflow:
   runs-on: ubuntu-latest
   steps:
   - uses: actions/checkout@v2
     with:
       fetch-depth: 0
       submodules: recursive
   - name: Set up Ruby 2.7.2
     uses: ruby/setup-ruby@v1
     with:
       ruby-version: 2.7.2
   - name: Ruby gem cache
     uses: actions/cache@v1
     with:
       path: vendor/bundle
       key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
       restore-keys: |
         ${{ runner.os }}-gems-
   - name: Bundle Install
     run: |
       gem update --system 3.1.4 -N
       gem install --no-document bundler
       bundle config path vendor/bundle
       bundle install --jobs 4 --retry 3
   - name: Check for New Comments
     run: |
       bundle exec dev_orbit --check-comments
     env:
       DEV_API_KEY: ${{ secrets.DEV_API_KEY }}
       DEV_USERNAME: ${{ secrets.DEV_USERNAME }}
       ORBIT_API_KEY: ${{ secrets.ORBIT_API_KEY }}
       ORBIT_WORKSPACE_ID: ${{ secrets.ORBIT_WORKSPACE_ID }}

{% c-block-end %}

This YAML workflow assumes that you have uploaded your Gemfile with the dev_orbit gem listed inside of it.

The above workflow creates a Ruby developer environment inside your GitHub Actions instance. It then installs the dependencies listed in your Gemfile, and finally, uses the gem's CLI to check for new blog post comments and add them to your Orbit workspace.

The only other task you need to do in order for this automation to work is to add your credentials for DEV and Orbit into your GitHub repository's secrets settings. You can find your secrets by navigating to "Settings" from within your repository and clicking on "Secrets" in the side navigation bar.

Example of GitHub secrets settings page

Once your secrets have been added, this workflow will automatically run once a day for you! You can simply go to your Orbit workspace and find the latest DEV blog comments in your member activities without needing to do anything else.

Want to learn more about using the Orbit API? Check out these other resources:

Related Articles