Merge pull request #258 from mwerner/jabber_agent

Add Jabber Agent

Andrew Cantino 10 years ago
parent
commit
07243cee34
4 changed files with 154 additions and 7 deletions
  1. 1 0
      Gemfile
  2. 8 7
      Gemfile.lock
  3. 64 0
      app/models/agents/jabber_agent.rb
  4. 81 0
      spec/models/agents/jabber_agent_spec.rb

+ 1 - 0
Gemfile

@@ -59,6 +59,7 @@ gem 'twitter-stream', github: 'cantino/twitter-stream', branch: 'master'
59 59
 gem 'em-http-request', '~> 1.1.2'
60 60
 gem 'weibo_2', '~> 0.1.4'
61 61
 gem 'hipchat', '~> 1.1.0'
62
+gem 'xmpp4r',  '~> 0.5.6'
62 63
 
63 64
 gem 'therubyracer', '~> 0.12.1'
64 65
 

+ 8 - 7
Gemfile.lock

@@ -86,10 +86,11 @@ GEM
86 86
       warden (~> 1.2.3)
87 87
     diff-lcs (1.2.5)
88 88
     docile (1.1.3)
89
-    dotenv (0.10.0)
89
+    dotenv (0.11.1)
90
+      dotenv-deployment (~> 0.0.2)
90 91
     dotenv-deployment (0.0.2)
91
-    dotenv-rails (0.10.0)
92
-      dotenv (= 0.10.0)
92
+    dotenv-rails (0.11.1)
93
+      dotenv (= 0.11.1)
93 94
     em-http-request (1.1.2)
94 95
       addressable (>= 2.3.4)
95 96
       cookiejar
@@ -158,7 +159,7 @@ GEM
158 159
     mime-types (1.25.1)
159 160
     mini_portile (0.5.3)
160 161
     minitest (5.3.3)
161
-    multi_json (1.9.2)
162
+    multi_json (1.9.3)
162 163
     multi_xml (0.5.5)
163 164
     multipart-post (2.0.0)
164 165
     mysql2 (0.3.15)
@@ -226,7 +227,7 @@ GEM
226 227
       uuid (~> 2.3, >= 2.3.5)
227 228
     rufus-scheduler (3.0.7)
228 229
       tzinfo
229
-    safe_yaml (1.0.2)
230
+    safe_yaml (1.0.3)
230 231
     sass (3.2.19)
231 232
     sass-rails (4.0.3)
232 233
       railties (>= 4.0.0, < 5.0)
@@ -285,8 +286,6 @@ GEM
285 286
       ethon (>= 0.7.0)
286 287
     tzinfo (1.1.0)
287 288
       thread_safe (~> 0.1)
288
-    tzinfo-data (1.2014.2)
289
-      tzinfo (>= 1.0.0)
290 289
     uglifier (2.5.0)
291 290
       execjs (>= 0.3.0)
292 291
       json (>= 1.8.0)
@@ -306,6 +305,7 @@ GEM
306 305
       addressable
307 306
       httparty (> 0.6.0)
308 307
       json (> 1.4.0)
308
+    xmpp4r (0.5.6)
309 309
 
310 310
 PLATFORMS
311 311
   ruby
@@ -362,3 +362,4 @@ DEPENDENCIES
362 362
   webmock
363 363
   weibo_2 (~> 0.1.4)
364 364
   wunderground (~> 1.2.0)
365
+  xmpp4r (~> 0.5.6)

+ 64 - 0
app/models/agents/jabber_agent.rb

@@ -0,0 +1,64 @@
1
+module Agents
2
+  class JabberAgent < Agent
3
+    cannot_be_scheduled!
4
+    cannot_create_events!
5
+
6
+    description <<-MD
7
+      The JabberAgent will send any events it receives to your Jabber/XMPP IM account.
8
+
9
+      Specify the `jabber_server` and `jabber_port` for your Jabber server.
10
+
11
+      The `message` is sent from `jabber_sender` to `jaber_receiver`. This message
12
+      can contain any keys found in the source's payload, escaped using double curly braces.
13
+      ex: `"News Story: <$.title>: <$.url>"`
14
+    MD
15
+
16
+    def default_options
17
+      {
18
+        'jabber_server'   => '127.0.0.1',
19
+        'jabber_port'     => '5222',
20
+        'jabber_sender'   => 'huginn@localhost',
21
+        'jabber_receiver' => 'muninn@localhost',
22
+        'jabber_password' => '',
23
+        'message'         => 'It will be <$.temp> out tomorrow',
24
+        'expected_receive_period_in_days' => "2"
25
+      }
26
+    end
27
+
28
+    def working?
29
+      last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs?
30
+    end
31
+
32
+    def receive(incoming_events)
33
+      incoming_events.each do |event|
34
+        log "Sending IM to #{options['jabber_receiver']} with event #{event.id}"
35
+        deliver body(event)
36
+      end
37
+    end
38
+
39
+    def validate_options
40
+      errors.add(:base, "server and username is required") unless credentials_present?
41
+    end
42
+
43
+    def deliver(text)
44
+      client.send Jabber::Message::new(options['jabber_receiver'], text).set_type(:chat)
45
+    end
46
+
47
+    private
48
+
49
+    def client
50
+      Jabber::Client.new(Jabber::JID::new(options['jabber_sender'])).tap do |sender|
51
+        sender.connect(options['jabber_server'], (options['jabber_port'] || '5222'))
52
+        sender.auth(options['jabber_password'])
53
+      end
54
+    end
55
+
56
+    def credentials_present?
57
+      options['jabber_server'].present? && options['jabber_sender'].present? && options['jabber_receiver'].present?
58
+    end
59
+
60
+    def body(event)
61
+      Utils.interpolate_jsonpaths(options['message'], event.payload)
62
+    end
63
+  end
64
+end

+ 81 - 0
spec/models/agents/jabber_agent_spec.rb

@@ -0,0 +1,81 @@
1
+require 'spec_helper'
2
+
3
+describe Agents::JabberAgent do
4
+  let(:sent) { [] }
5
+  let(:config) {
6
+    {
7
+      jabber_server: '127.0.0.1',
8
+      jabber_port: '5222',
9
+      jabber_sender: 'foo@localhost',
10
+      jabber_receiver: 'bar@localhost',
11
+      jabber_password: 'password',
12
+      message: 'Warning! <$.title> - <$.url>',
13
+      expected_receive_period_in_days: '2'
14
+    }
15
+  }
16
+
17
+  let(:agent) do
18
+    Agents::JabberAgent.new(name: 'Jabber Agent', options: config).tap do |a|
19
+      a.user = users(:bob)
20
+      a.save!
21
+    end
22
+  end
23
+
24
+  let(:event) do
25
+    Event.new.tap do |e|
26
+      e.agent = agents(:bob_weather_agent)
27
+      e.payload = { :title => 'Weather Alert!', :url => 'http://www.weather.com/' }
28
+      e.save!
29
+    end
30
+  end
31
+
32
+  before do
33
+    stub.any_instance_of(Agents::JabberAgent).deliver { |message| sent << message }
34
+  end
35
+
36
+  describe "#working?" do
37
+    it "checks if events have been received within the expected receive period" do
38
+      agent.should_not be_working # No events received
39
+      Agents::JabberAgent.async_receive agent.id, [event.id]
40
+      agent.reload.should be_working # Just received events
41
+      two_days_from_now = 2.days.from_now
42
+      stub(Time).now { two_days_from_now }
43
+      agent.reload.should_not be_working # More time has passed than the expected receive period without any new events
44
+    end
45
+  end
46
+
47
+  describe "validation" do
48
+    before do
49
+      agent.should be_valid
50
+    end
51
+
52
+    it "should validate presence of of jabber_server" do
53
+      agent.options[:jabber_server] = ""
54
+      agent.should_not be_valid
55
+    end
56
+
57
+    it "should validate presence of jabber_sender" do
58
+      agent.options[:jabber_sender] = ""
59
+      agent.should_not be_valid
60
+    end
61
+
62
+    it "should validate presence of jabber_receiver" do
63
+      agent.options[:jabber_receiver] = ""
64
+      agent.should_not be_valid
65
+    end
66
+  end
67
+
68
+  describe "receive" do
69
+    it "should send an IM for each event" do
70
+      event2 = Event.new.tap do |e|
71
+        e.agent = agents(:bob_weather_agent)
72
+        e.payload = { :title => 'Another Weather Alert!', :url => 'http://www.weather.com/we-are-screwed' }
73
+        e.save!
74
+      end
75
+
76
+      agent.receive([event, event2])
77
+      sent.should == [ 'Warning! Weather Alert! - http://www.weather.com/',
78
+                       'Warning! Another Weather Alert! - http://www.weather.com/we-are-screwed']
79
+    end
80
+  end
81
+end