Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add telegram agent #1381

Merged
merged 3 commits into from
Mar 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ gem 'hipchat', '~> 1.2.0' # HipchatAgent
gem 'xmpp4r', '~> 0.5.6' # JabberAgent
gem 'mqtt' # MQTTAgent
gem 'slack-notifier', '~> 1.0.0' # SlackAgent
gem 'hypdf', '~> 1.0.7' # PDFInfoAgent
gem 'hypdf', '~> 1.0.10' # PDFInfoAgent

# Weibo Agents
gem 'weibo_2', github: 'cantino/weibo_2', branch: 'master'
Expand Down Expand Up @@ -100,6 +100,7 @@ gem 'foreman', '~> 0.63.0'
gem 'geokit', '~> 1.8.4'
gem 'geokit-rails', '~> 2.0.1'
gem 'httparty', '~> 0.13'
gem 'httmultiparty', '~> 0.3.16'
gem 'jquery-rails', '~> 3.1.3'
gem 'json', '~> 1.8.1'
gem 'jsonpath', '~> 0.5.6'
Expand Down
14 changes: 9 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -267,19 +267,21 @@ GEM
hipchat (1.2.0)
httparty
hpricot (0.8.6)
httmultiparty (0.3.10)
httmultiparty (0.3.16)
httparty (>= 0.7.3)
mimemagic
multipart-post
http (0.6.4)
http_parser.rb (~> 0.6.0)
http-cookie (1.0.2)
domain_name (~> 0.5)
http_parser.rb (0.6.0)
httparty (0.13.1)
httparty (0.13.7)
json (~> 1.8)
multi_xml (>= 0.5.2)
hypdf (1.0.7)
httmultiparty (= 0.3.10)
hypdf (1.0.10)
httmultiparty (~> 0.3)
httparty (~> 0.13)
i18n (0.7.0)
jmespath (1.1.3)
jquery-rails (3.1.3)
Expand Down Expand Up @@ -318,6 +320,7 @@ GEM
thread_safe (~> 0.3, >= 0.3.1)
method_source (0.8.2)
mime-types (2.99.1)
mimemagic (0.3.1)
mini_magick (4.2.3)
mini_portile2 (2.0.0)
minitest (5.8.4)
Expand Down Expand Up @@ -616,8 +619,9 @@ DEPENDENCIES
guard-rspec (~> 4.6.4)
haversine
hipchat (~> 1.2.0)
httmultiparty (~> 0.3.16)
httparty (~> 0.13)
hypdf (~> 1.0.7)
hypdf (~> 1.0.10)
jquery-rails (~> 3.1.3)
json (~> 1.8.1)
jsonpath (~> 0.5.6)
Expand Down
96 changes: 96 additions & 0 deletions app/models/agents/telegram_agent.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
require 'httmultiparty'
require 'open-uri'
require 'tempfile'

module Agents
class TelegramAgent < Agent
cannot_be_scheduled!
cannot_create_events!
no_bulk_receive!

description <<-MD
The Telegram Agent receives and collects events and sends them via [Telegram](https://telegram.org/).

It is assumed that events have either a `text`, `photo`, `audio`, `document` or `video` key. You can use the EventFormattingAgent if your event does not provide these keys.

The value of `text` key is sent as a plain text message.
The value of `photo`, `audio`, `document` and `video` keys should be a url whose contents will be sent to you.

**Setup**

1. Obtain an `auth_token` by [creating a new bot](https://telegram.me/botfather).
2. Send a private message to your bot by visiting https://telegram.me/YourHuginnBot
3. Obtain your private `chat_id` from the recently started conversation by visiting https://api.telegram.org/bot<auth_token>/getUpdates
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that is why I was confused. The description contains the auth_token, but markdown seems to omit it! Thank you for reporting that!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that was it. Thank you for your hard work!

MD

def default_options
{
auth_token: 'xxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
chat_id: 'xxxxxxxx'
}
end

def validate_options
errors.add(:base, 'auth_token is required') unless options['auth_token'].present?
errors.add(:base, 'chat_id is required') unless options['chat_id'].present?
end

def working?
received_event_without_error? && !recent_error_logs?
end

def receive(incoming_events)
incoming_events.each do |event|
receive_event event
end
end

private

TELEGRAM_ACTIONS = {
text: :sendMessage,
photo: :sendPhoto,
audio: :sendAudio,
document: :sendDocument,
video: :sendVideo
}.freeze

def telegram_bot_uri(method)
"https://api.telegram.org/bot#{interpolated['auth_token']}/#{method}"
end

def receive_event(event)
TELEGRAM_ACTIONS.each do |field, method|
payload = load_field event, field
next unless payload
send_telegram_message method, field => payload
unlink_file payload if payload.is_a? Tempfile
end
end

def send_telegram_message(method, params)
params[:chat_id] = interpolated['chat_id']
HTTMultiParty.post telegram_bot_uri(method), query: params
end

def load_field(event, field)
payload = event.payload[field]
return false unless payload.present?
return payload if field == :text
load_file payload
end

def load_file(url)
file = Tempfile.new [File.basename(url), File.extname(url)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to ensure that the file gets unlinked afterwards.

file.binmode
file.write open(url).read
file.rewind
file
end

def unlink_file(file)
file.close
file.unlink
end
end
end
102 changes: 102 additions & 0 deletions spec/models/agents/telegram_agent_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require 'rails_helper'

describe Agents::TelegramAgent do
before do
default_options = {
auth_token: 'xxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
chat_id: 'xxxxxxxx'
}
@checker = Agents::TelegramAgent.new name: 'Telegram Tester', options: default_options
@checker.user = users(:bob)
@checker.save!

@sent_messages = []
stub_methods
end

def stub_methods
stub.any_instance_of(Agents::TelegramAgent).send_telegram_message do |method, params|
@sent_messages << { method => params }
end

stub.any_instance_of(Agents::TelegramAgent).load_file do |_url|
:stubbed_file
end
end

def event_with_payload(payload)
event = Event.new
event.agent = agents(:bob_weather_agent)
event.payload = payload
event.save!
event
end

describe 'validation' do
before do
expect(@checker).to be_valid
end

it 'should validate presence of of auth_token' do
@checker.options[:auth_token] = ''
expect(@checker).not_to be_valid
end

it 'should validate presence of of chat_id' do
@checker.options[:chat_id] = ''
expect(@checker).not_to be_valid
end
end

describe '#receive' do
it 'processes multiple events properly' do
event_0 = event_with_payload text: 'Looks like its going to rain'
event_1 = event_with_payload text: 'Another text message'
@checker.receive [event_0, event_1]

expect(@sent_messages).to eq([
{ sendMessage: { text: 'Looks like its going to rain' } },
{ sendMessage: { text: 'Another text message' } }
])
end

it 'accepts photo key and uses :send_photo to send the file' do
event = event_with_payload photo: 'https://example.com/image.png'
@checker.receive [event]

expect(@sent_messages).to eq([{ sendPhoto: { photo: :stubbed_file } }])
end

it 'accepts audio key and uses :send_audio to send the file' do
event = event_with_payload audio: 'https://example.com/sound.mp3'
@checker.receive [event]

expect(@sent_messages).to eq([{ sendAudio: { audio: :stubbed_file } }])
end

it 'accepts document key and uses :send_document to send the file' do
event = event_with_payload document: 'https://example.com/document.pdf'
@checker.receive [event]

expect(@sent_messages).to eq([{ sendDocument: { document: :stubbed_file } }])
end

it 'accepts video key and uses :send_video to send the file' do
event = event_with_payload video: 'https://example.com/video.avi'
@checker.receive [event]

expect(@sent_messages).to eq([{ sendVideo: { video: :stubbed_file } }])
end
end

describe '#working?' do
it 'is not working without having received an event' do
expect(@checker).not_to be_working
end

it 'is working after receiving an event without error' do
@checker.last_receive_at = Time.now
expect(@checker).to be_working
end
end
end