app/models/agents/pushbullet_agent.rb
module Agents
class PushbulletAgent < Agent
include FormConfigurable
cannot_be_scheduled!
cannot_create_events!
no_bulk_receive!
before_validation :create_device, on: :create
API_BASE = 'https://api.pushbullet.com/v2/'
TYPE_TO_ATTRIBUTES = {
'note' => [:title, :body],
'link' => [:title, :body, :url],
'address' => [:name, :address]
}
class Unauthorized < StandardError; end
description <<~MD
The Pushbullet agent sends pushes to a pushbullet device
To authenticate you need to either the `api_key` or create a `pushbullet_api_key` credential, you can find yours at your account page:
`https://www.pushbullet.com/account`
If you do not select an existing device, Huginn will create a new one with the name 'Huginn'.
To push to all of your devices, select `All Devices` from the devices list.
You have to provide a message `type` which has to be `note`, `link`, or `address`. The message types `checklist`, and `file` are not supported at the moment.
Depending on the message `type` you can use additional fields:
* note: `title` and `body`
* link: `title`, `body`, and `url`
* address: `name`, and `address`
In every value of the options hash you can use the liquid templating, learn more about it at the [Wiki](https://github.com/huginn/huginn/wiki/Formatting-Events-using-Liquid).
MD
def default_options
{
'api_key' => '',
'device_id' => '',
'title' => "{{title}}",
'body' => '{{body}}',
'type' => 'note',
}
end
form_configurable :api_key, roles: :validatable
form_configurable :device_id, roles: :completable
form_configurable :type, type: :array, values: ['note', 'link', 'address']
form_configurable :title
form_configurable :body, type: :text
form_configurable :url
form_configurable :name
form_configurable :address
def validate_options
errors.add(:base, "you need to specify a pushbullet api_key") if options['api_key'].blank?
errors.add(:base, "you need to specify a device_id") if options['device_id'].blank?
errors.add(:base, "you need to specify a valid message type") if options['type'].blank? ||
!['note', 'link', 'address'].include?(options['type'])
TYPE_TO_ATTRIBUTES[options['type']].each do |attr|
errors.add(:base,
"you need to specify '#{attr}' for the type '#{options['type']}'") if options[attr].blank?
end
end
def validate_api_key
devices
true
rescue Unauthorized
false
end
def complete_device_id
devices
.map { |d| { text: d['nickname'], id: d['iden'] } }
.unshift(text: 'All Devices', id: '__ALL__')
end
def working?
received_event_without_error?
end
def receive(incoming_events)
incoming_events.each do |event|
safely do
response = request(:post, 'pushes', query_options(event))
end
end
end
private
def safely
yield
rescue Unauthorized => e
error(e.message)
end
def request(http_method, method, options)
response = JSON.parse(HTTParty.send(http_method, API_BASE + method, options).body)
raise Unauthorized, response['error']['message'] if response['error'].present?
response
end
def devices
response = request(:get, 'devices', basic_auth)
response['devices'].select { |d| d['pushable'] == true }
rescue Unauthorized
[]
end
def create_device
return if options['device_id'].present?
safely do
response = request(:post, 'devices', basic_auth.merge(body: { nickname: 'Huginn', type: 'stream' }))
self.options[:device_id] = response['iden']
end
end
def basic_auth
{ basic_auth: { username: interpolated[:api_key].presence || credential('pushbullet_api_key'), password: '' } }
end
def query_options(event)
mo = interpolated(event)
dev_ident = mo[:device_id] == "__ALL__" ? '' : mo[:device_id]
basic_auth.merge(body: { device_iden: dev_ident, type: mo[:type] }.merge(payload(mo)))
end
def payload(mo)
Hash[TYPE_TO_ATTRIBUTES[mo[:type]].map { |k| [k, mo[k]] }]
end
end
end