Qismo Ruby

A Ruby API wrapper for Qiscus Omnichannel API

Installation

Install the gem and add to the application's Gemfile by executing:

bundle add qismo

Usage

qismo = Qismo::Client.new(app_id: "QISCUS_APP_ID", secret_key: "QISCUS_SECRET_KEY")

params = {
  channel: [{ channel_id: 12345, source: "wa" }],
  status: "unresolved",
  serve_status: "served",
  is_handled_by_bot: true,
}

pp qismo.list_rooms(params)

# [
#   #<Qismo::Objects::CustomerRoom
#     channel_id=126392
#     contact_id=21608346
#     id=68303333
#     is_handled_by_bot=true
#     is_resolved=false
#     is_waiting=false
#     last_comment_sender="[email protected]"
#     last_comment_sender_type="system"
#     last_comment_text="bayu joined this conversation"
#     last_comment_timestamp="2022-12-22T06:37:58Z"
#     last_customer_comment_text=nil
#     last_customer_timestamp="2022-12-19T13:32:51Z"
#     name="Oyis"
#     room_badge="https://d1edrlpyc25xu0.cloudfront.net/ods-cc6rmwouax2thbow3/image/upload/5sYxRCGJl_/qiscus_badge.svg"
#     room_id=103513607
#     room_type="individual"
#     source="qiscus"
#     user_avatar_url="https://d1edrlpyc25xu0.cloudfront.net/kiwari-prod/image/upload/wMWsDZP6ta/1516689726-ic_qiscus_client.png"
#     user_id="[email protected]">
# ]

Client optional configuration

Qismo ruby also provide some optional configuration that you can pass, they are:

url

Defaultly, Qismo ruby will use your QISCUS_OMNICHANNEL_URL env as base url. If its nil, it will use https://qismo.qiscus.com. If you need to customize URL other than that, you can pass it at client initialization

qismo.url = "https://qismo.qiscus.com"

logger

You can also log the request and response the any HTTP request you make in Qismo ruby by using this configuration

require "logger"

qismo.logger = Logger.new($stdout)

instrumentation

For advanced logging, you can also use laverage ActiveSupport instrumentation. If you are using Rails, you can use ActiveSupport without installing the gem by your self.

ActiveSupport::Notifications.subscribe('start_request.http') do |name, start, finish, id, payload|
  pp name: name, start: start.to_f, finish: finish.to_f, id: id, payload: payload
end

qismo.instrumentation = { instrumenter: ActiveSupport::Notifications.instrumenter }

You can also customize the instrumentation's namespace by using this configuration

qismo.instrumentation = { instrumenter: ActiveSupport::Notifications.instrumenter, namespace: "qiscus.http_request" }

timeout

By default, the Qismo ruby gem does not enforce timeout on a request. You can enable per operation timeouts (each read/write/connect call) or global timeouts (sum of all read/write/connect calls) by configuring them through the chaining API.

Per operation timeouts are what Net::HTTP and the majority of HTTP clients do:

qismo.timeout = { connect: 5, write: 2, read: 10 }

Global timeouts let you set an upper bound of how long a request can take

qismo.timeout = 5 # in seconds

proxy

Making request behind proxy is as simple as making them directly. Just specify hostname (or IP address) of your proxy server and its port, and here you go

qismo.proxy = ["proxy-hostname.local", 8080]

Proxy needs authentication?

qismo.proxy = ["proxy-hostname.local", 8080, "username", "password"]

Handling pagination

Some of the Qiscus Omnichannel API will return list of data with pagination. To handle the pagination, you can to that like this example:


all_rooms = []

rooms = qismo.list_rooms
all_rooms.append(rooms)

while rooms.next_page.present?
  rooms = qismo.list_rooms(cursor_after: rooms.next_page)
  all_rooms.append(rooms)
end

Handling error

Qismo ruby raise error while getting non successful http code from Qiscus Omnichannel API. To handle it, you follow this example:

begin
  qismo.list_rooms
rescue Qismo::BadRequestError => e
  puts e.message
  puts e.status_code
  puts e.response_body
rescue Qismo::UnauthorizedError => e
  puts e.message
  puts e.status_code
  puts e.response_body
rescue Qismo::PaymentRequiredError => e
  puts e.message
  puts e.status_code
  puts e.response_body
rescue Qismo::ForbiddenError => e
  puts e.message
  puts e.status_code
  puts e.response_body
rescue Qismo::NotFoundError => e
  puts e.message
  puts e.status_code
  puts e.response_body
rescue Qismo::TooManyRequestError => e
  puts e.message
  puts e.status_code
  puts e.response_body
rescue Qismo::InternalServerError => e
  puts e.message
  puts e.status_code
  puts e.response_body
end

Handle incoming webhook request

Qiscus Omnichannel provide webhooks that triggered from various action on your account. Qismo ruby gem provide convenient way to handle the request.

class QiscusWebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def handle_agent_allocation_webhook
    webhook = Qismo::WebhookRequests::OnAgentAllocationNeeded.new(JSON.parse(request.raw_body))

    # Do any action you want using payload that has been objectified
    if webhook.candidate_agent.present?
      qismo = Qismo::Client.new(app_id: "", secret_key: "")
      qismo.assign_agent(
        room_id: webhook.room_id,
        agent_id: webhook.candidate_agent.id
      )
    end
  end

  def handle_custom_button_webhook
    webhook = Qismo::WebhookRequests::OnCustomButtonClicked.new(JSON.parse(request.raw_body))

    # Do any action you want using payload that has been objectified. The
    # following example assuming you want to create Zendesk ticket
    # everytime your agent click custom button "Create Ticket". We are
    # assuming, you have setup Zendesk ruby client
    zendesk_ticket_subject = webhook.additional_info.find do |info|
      info.key == "Zendesk Ticket Subject"
    end

    if zendesk_ticket_subject.present?
      ZendeskAPI::Ticket.create!(
        zendesk_client,
        subject: zendesk_ticket_subject.value,
        submitter_id: webhook.agent.email,
        priority: "urgent"
      )
    end
  end

  def handle_custom_channel_webhook
    webhook = Qismo::WebhookRequests::OnCustomChannelMessageSent.new(JSON.parse(request.raw_body))

    # Do any action you want
    # The following example assuming you want to reply customer's message from Twitter
    # Assuming that you have installed and setup twitter ruby client

    # Fetch customer from room participants
    customer = webhook.payload.room.participants.find { |participant| participant.email.start_with?("twitter_customer_") }
    if customer.present?
      twitter_rest_client.create_direct_message(customer.email, webhook.payload.message.text)
    end
  end

  def handle_bot_webhook
    webhook = Qismo::WebhookRequests::OnMessageForBotSent.new(JSON.parse(request.raw_body))

    # Do any action you want. The following example assuming you want to use
    # Dialogflow as bot engine

    # Detect intent for customer's message
    response = dialogflow_client.detect_intent(
      session: "session_#{webhook.payload.room.id}",
      query_input: {
        text: {
          text: webhook.payload.message.text,
          language_code: "en-US"
        }
      }
    )

    # Parse bot message which will be sent back to customer
    bot_message = response.query_result.fulfillment_text

    # Send message to Qismo room
    qismo = Qismo::Client.new(app_id: "", secret_key: "")
    qismo.send_bot_message(
      room_id: webhook.payload.room.id,
      message: bot_message
    )
  end
end

Developer Experience

TODO documentation