@@ -35,7 +35,7 @@ gem 'hipchat', '~> 1.2.0' # HipchatAgent |
||
35 | 35 |
gem 'xmpp4r', '~> 0.5.6' # JabberAgent |
36 | 36 |
gem 'mqtt' # MQTTAgent |
37 | 37 |
gem 'slack-notifier', '~> 1.0.0' # SlackAgent |
38 |
-gem 'hypdf', '~> 1.0.7' # PDFInfoAgent |
|
38 |
+gem 'hypdf', '~> 1.0.10' # PDFInfoAgent |
|
39 | 39 |
|
40 | 40 |
# Weibo Agents |
41 | 41 |
gem 'weibo_2', github: 'cantino/weibo_2', branch: 'master' |
@@ -100,6 +100,7 @@ gem 'foreman', '~> 0.63.0' |
||
100 | 100 |
gem 'geokit', '~> 1.8.4' |
101 | 101 |
gem 'geokit-rails', '~> 2.0.1' |
102 | 102 |
gem 'httparty', '~> 0.13' |
103 |
+gem 'httmultiparty', '~> 0.3.16' |
|
103 | 104 |
gem 'jquery-rails', '~> 3.1.3' |
104 | 105 |
gem 'json', '~> 1.8.1' |
105 | 106 |
gem 'jsonpath', '~> 0.5.6' |
@@ -267,19 +267,21 @@ GEM |
||
267 | 267 |
hipchat (1.2.0) |
268 | 268 |
httparty |
269 | 269 |
hpricot (0.8.6) |
270 |
- httmultiparty (0.3.10) |
|
270 |
+ httmultiparty (0.3.16) |
|
271 | 271 |
httparty (>= 0.7.3) |
272 |
+ mimemagic |
|
272 | 273 |
multipart-post |
273 | 274 |
http (0.6.4) |
274 | 275 |
http_parser.rb (~> 0.6.0) |
275 | 276 |
http-cookie (1.0.2) |
276 | 277 |
domain_name (~> 0.5) |
277 | 278 |
http_parser.rb (0.6.0) |
278 |
- httparty (0.13.1) |
|
279 |
+ httparty (0.13.7) |
|
279 | 280 |
json (~> 1.8) |
280 | 281 |
multi_xml (>= 0.5.2) |
281 |
- hypdf (1.0.7) |
|
282 |
- httmultiparty (= 0.3.10) |
|
282 |
+ hypdf (1.0.10) |
|
283 |
+ httmultiparty (~> 0.3) |
|
284 |
+ httparty (~> 0.13) |
|
283 | 285 |
i18n (0.7.0) |
284 | 286 |
jmespath (1.1.3) |
285 | 287 |
jquery-rails (3.1.3) |
@@ -318,6 +320,7 @@ GEM |
||
318 | 320 |
thread_safe (~> 0.3, >= 0.3.1) |
319 | 321 |
method_source (0.8.2) |
320 | 322 |
mime-types (2.99.1) |
323 |
+ mimemagic (0.3.1) |
|
321 | 324 |
mini_magick (4.2.3) |
322 | 325 |
mini_portile2 (2.0.0) |
323 | 326 |
minitest (5.8.4) |
@@ -616,8 +619,9 @@ DEPENDENCIES |
||
616 | 619 |
guard-rspec (~> 4.6.4) |
617 | 620 |
haversine |
618 | 621 |
hipchat (~> 1.2.0) |
622 |
+ httmultiparty (~> 0.3.16) |
|
619 | 623 |
httparty (~> 0.13) |
620 |
- hypdf (~> 1.0.7) |
|
624 |
+ hypdf (~> 1.0.10) |
|
621 | 625 |
jquery-rails (~> 3.1.3) |
622 | 626 |
json (~> 1.8.1) |
623 | 627 |
jsonpath (~> 0.5.6) |
@@ -0,0 +1,96 @@ |
||
1 |
+require 'httmultiparty' |
|
2 |
+require 'open-uri' |
|
3 |
+require 'tempfile' |
|
4 |
+ |
|
5 |
+module Agents |
|
6 |
+ class TelegramAgent < Agent |
|
7 |
+ cannot_be_scheduled! |
|
8 |
+ cannot_create_events! |
|
9 |
+ no_bulk_receive! |
|
10 |
+ |
|
11 |
+ description <<-MD |
|
12 |
+ The Telegram Agent receives and collects events and sends them via [Telegram](https://telegram.org/). |
|
13 |
+ |
|
14 |
+ 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. |
|
15 |
+ |
|
16 |
+ The value of `text` key is sent as a plain text message. |
|
17 |
+ The value of `photo`, `audio`, `document` and `video` keys should be a url whose contents will be sent to you. |
|
18 |
+ |
|
19 |
+ **Setup** |
|
20 |
+ |
|
21 |
+ 1. Obtain an `auth_token` by [creating a new bot](https://telegram.me/botfather). |
|
22 |
+ 2. Send a private message to your bot by visiting https://telegram.me/YourHuginnBot |
|
23 |
+ 3. Obtain your private `chat_id` from the recently started conversation by visiting https://api.telegram.org/bot<auth_token>/getUpdates |
|
24 |
+ MD |
|
25 |
+ |
|
26 |
+ def default_options |
|
27 |
+ { |
|
28 |
+ auth_token: 'xxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
|
29 |
+ chat_id: 'xxxxxxxx' |
|
30 |
+ } |
|
31 |
+ end |
|
32 |
+ |
|
33 |
+ def validate_options |
|
34 |
+ errors.add(:base, 'auth_token is required') unless options['auth_token'].present? |
|
35 |
+ errors.add(:base, 'chat_id is required') unless options['chat_id'].present? |
|
36 |
+ end |
|
37 |
+ |
|
38 |
+ def working? |
|
39 |
+ received_event_without_error? && !recent_error_logs? |
|
40 |
+ end |
|
41 |
+ |
|
42 |
+ def receive(incoming_events) |
|
43 |
+ incoming_events.each do |event| |
|
44 |
+ receive_event event |
|
45 |
+ end |
|
46 |
+ end |
|
47 |
+ |
|
48 |
+ private |
|
49 |
+ |
|
50 |
+ TELEGRAM_ACTIONS = { |
|
51 |
+ text: :sendMessage, |
|
52 |
+ photo: :sendPhoto, |
|
53 |
+ audio: :sendAudio, |
|
54 |
+ document: :sendDocument, |
|
55 |
+ video: :sendVideo |
|
56 |
+ }.freeze |
|
57 |
+ |
|
58 |
+ def telegram_bot_uri(method) |
|
59 |
+ "https://api.telegram.org/bot#{interpolated['auth_token']}/#{method}" |
|
60 |
+ end |
|
61 |
+ |
|
62 |
+ def receive_event(event) |
|
63 |
+ TELEGRAM_ACTIONS.each do |field, method| |
|
64 |
+ payload = load_field event, field |
|
65 |
+ next unless payload |
|
66 |
+ send_telegram_message method, field => payload |
|
67 |
+ unlink_file payload if payload.is_a? Tempfile |
|
68 |
+ end |
|
69 |
+ end |
|
70 |
+ |
|
71 |
+ def send_telegram_message(method, params) |
|
72 |
+ params[:chat_id] = interpolated['chat_id'] |
|
73 |
+ HTTMultiParty.post telegram_bot_uri(method), query: params |
|
74 |
+ end |
|
75 |
+ |
|
76 |
+ def load_field(event, field) |
|
77 |
+ payload = event.payload[field] |
|
78 |
+ return false unless payload.present? |
|
79 |
+ return payload if field == :text |
|
80 |
+ load_file payload |
|
81 |
+ end |
|
82 |
+ |
|
83 |
+ def load_file(url) |
|
84 |
+ file = Tempfile.new [File.basename(url), File.extname(url)] |
|
85 |
+ file.binmode |
|
86 |
+ file.write open(url).read |
|
87 |
+ file.rewind |
|
88 |
+ file |
|
89 |
+ end |
|
90 |
+ |
|
91 |
+ def unlink_file(file) |
|
92 |
+ file.close |
|
93 |
+ file.unlink |
|
94 |
+ end |
|
95 |
+ end |
|
96 |
+end |
@@ -0,0 +1,102 @@ |
||
1 |
+require 'rails_helper' |
|
2 |
+ |
|
3 |
+describe Agents::TelegramAgent do |
|
4 |
+ before do |
|
5 |
+ default_options = { |
|
6 |
+ auth_token: 'xxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', |
|
7 |
+ chat_id: 'xxxxxxxx' |
|
8 |
+ } |
|
9 |
+ @checker = Agents::TelegramAgent.new name: 'Telegram Tester', options: default_options |
|
10 |
+ @checker.user = users(:bob) |
|
11 |
+ @checker.save! |
|
12 |
+ |
|
13 |
+ @sent_messages = [] |
|
14 |
+ stub_methods |
|
15 |
+ end |
|
16 |
+ |
|
17 |
+ def stub_methods |
|
18 |
+ stub.any_instance_of(Agents::TelegramAgent).send_telegram_message do |method, params| |
|
19 |
+ @sent_messages << { method => params } |
|
20 |
+ end |
|
21 |
+ |
|
22 |
+ stub.any_instance_of(Agents::TelegramAgent).load_file do |_url| |
|
23 |
+ :stubbed_file |
|
24 |
+ end |
|
25 |
+ end |
|
26 |
+ |
|
27 |
+ def event_with_payload(payload) |
|
28 |
+ event = Event.new |
|
29 |
+ event.agent = agents(:bob_weather_agent) |
|
30 |
+ event.payload = payload |
|
31 |
+ event.save! |
|
32 |
+ event |
|
33 |
+ end |
|
34 |
+ |
|
35 |
+ describe 'validation' do |
|
36 |
+ before do |
|
37 |
+ expect(@checker).to be_valid |
|
38 |
+ end |
|
39 |
+ |
|
40 |
+ it 'should validate presence of of auth_token' do |
|
41 |
+ @checker.options[:auth_token] = '' |
|
42 |
+ expect(@checker).not_to be_valid |
|
43 |
+ end |
|
44 |
+ |
|
45 |
+ it 'should validate presence of of chat_id' do |
|
46 |
+ @checker.options[:chat_id] = '' |
|
47 |
+ expect(@checker).not_to be_valid |
|
48 |
+ end |
|
49 |
+ end |
|
50 |
+ |
|
51 |
+ describe '#receive' do |
|
52 |
+ it 'processes multiple events properly' do |
|
53 |
+ event_0 = event_with_payload text: 'Looks like its going to rain' |
|
54 |
+ event_1 = event_with_payload text: 'Another text message' |
|
55 |
+ @checker.receive [event_0, event_1] |
|
56 |
+ |
|
57 |
+ expect(@sent_messages).to eq([ |
|
58 |
+ { sendMessage: { text: 'Looks like its going to rain' } }, |
|
59 |
+ { sendMessage: { text: 'Another text message' } } |
|
60 |
+ ]) |
|
61 |
+ end |
|
62 |
+ |
|
63 |
+ it 'accepts photo key and uses :send_photo to send the file' do |
|
64 |
+ event = event_with_payload photo: 'https://example.com/image.png' |
|
65 |
+ @checker.receive [event] |
|
66 |
+ |
|
67 |
+ expect(@sent_messages).to eq([{ sendPhoto: { photo: :stubbed_file } }]) |
|
68 |
+ end |
|
69 |
+ |
|
70 |
+ it 'accepts audio key and uses :send_audio to send the file' do |
|
71 |
+ event = event_with_payload audio: 'https://example.com/sound.mp3' |
|
72 |
+ @checker.receive [event] |
|
73 |
+ |
|
74 |
+ expect(@sent_messages).to eq([{ sendAudio: { audio: :stubbed_file } }]) |
|
75 |
+ end |
|
76 |
+ |
|
77 |
+ it 'accepts document key and uses :send_document to send the file' do |
|
78 |
+ event = event_with_payload document: 'https://example.com/document.pdf' |
|
79 |
+ @checker.receive [event] |
|
80 |
+ |
|
81 |
+ expect(@sent_messages).to eq([{ sendDocument: { document: :stubbed_file } }]) |
|
82 |
+ end |
|
83 |
+ |
|
84 |
+ it 'accepts video key and uses :send_video to send the file' do |
|
85 |
+ event = event_with_payload video: 'https://example.com/video.avi' |
|
86 |
+ @checker.receive [event] |
|
87 |
+ |
|
88 |
+ expect(@sent_messages).to eq([{ sendVideo: { video: :stubbed_file } }]) |
|
89 |
+ end |
|
90 |
+ end |
|
91 |
+ |
|
92 |
+ describe '#working?' do |
|
93 |
+ it 'is not working without having received an event' do |
|
94 |
+ expect(@checker).not_to be_working |
|
95 |
+ end |
|
96 |
+ |
|
97 |
+ it 'is working after receiving an event without error' do |
|
98 |
+ @checker.last_receive_at = Time.now |
|
99 |
+ expect(@checker).to be_working |
|
100 |
+ end |
|
101 |
+ end |
|
102 |
+end |