Merge pull request #233 from akilism/pushover

Pushover Agent

Andrew Cantino 11 anni fa
parent
commit
764b4bd4bb
2 ha cambiato i file con 329 aggiunte e 0 eliminazioni
  1. 107 0
      app/models/agents/pushover_agent.rb
  2. 222 0
      spec/models/agents/pushover_agent_spec.rb

+ 107 - 0
app/models/agents/pushover_agent.rb

@@ -0,0 +1,107 @@
1
+module Agents
2
+  class PushoverAgent < Agent
3
+    cannot_be_scheduled!
4
+    cannot_create_events!
5
+
6
+    API_URL = 'https://api.pushover.net/1/messages.json'
7
+
8
+    description <<-MD
9
+      The PushoverAgent receives and collects events and sends them via push notification to a user/group.
10
+
11
+      **You need a Pushover API Token:** [https://pushover.net/apps/build](https://pushover.net/apps/build)
12
+
13
+      **You must provide** a `message` or `text` key that will contain the body of the notification. This can come from an event or be set as a default. Pushover API has a `512` Character Limit including `title`. `message` will be truncated.
14
+
15
+      * `token`: your application's API token
16
+      * `user`: the user or group key (not e-mail address).
17
+      * `expected_receive_period_in_days`:  is maximum number of days that you would expect to pass between events being received by this agent.
18
+
19
+      Your event can provide any of the following optional parameters or you can provide defaults:
20
+
21
+      * `device` - your user's device name to send the message directly to that device, rather than all of the user's devices
22
+      * `title` or `subject` - your notifications's title
23
+      * `url` - a supplementary URL to show with your message - `512` Character Limit
24
+      * `url_title` - a title for your supplementary URL, otherwise just the URL is shown - `100` Character Limit
25
+      * `priority` - send as `-1` to always send as a quiet notification, `0` is default, `1` to display as high-priority and bypass the user's quiet hours, or `2` for emergency priority: [Please read Pushover Docs on Emergency Priority](https://pushover.net/api#priority)
26
+      * `sound` - the name of one of the sounds supported by device clients to override the user's default sound choice. [See PushOver docs for sound options.](https://pushover.net/api#sounds)
27
+      * `retry` - Requred for emergency priority - Specifies how often (in seconds) the Pushover servers will send the same notification to the user. Minimum value: `30`
28
+      * `expire` - Requred for emergency priority - Specifies how many seconds your notification will continue to be retried for (every retry seconds). Maximum value: `86400`
29
+
30
+      Your event can also pass along a timestamp parameter:
31
+
32
+      * `timestamp` - a [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time) of your message's date and time to display to the user, rather than the time your message is received by the Pushover API.
33
+
34
+    MD
35
+
36
+    def default_options
37
+      {
38
+        'token' => '',
39
+        'user' => '',
40
+        'message' => 'a default message',
41
+        'device' => '',
42
+        'title' => '',
43
+        'url' => '',
44
+        'url_title' => '',
45
+        'priority' => 0,
46
+        'sound' => 'pushover',
47
+        'retry' => 0,
48
+        'expire' => 0,
49
+        'expected_receive_period_in_days' => '1'
50
+      }
51
+    end
52
+
53
+    def validate_options
54
+      unless options['token'].present? && options['user'].present? && options['expected_receive_period_in_days'].present?
55
+        errors.add(:base, 'token, user, and expected_receive_period_in_days are all required.')
56
+      end
57
+    end
58
+
59
+    def receive(incoming_events)
60
+      incoming_events.each do |event|
61
+        message = (event.payload['message'].presence  || event.payload['text'].presence  || options['message']).to_s
62
+        if message.present?
63
+            post_params = {
64
+              'token' => options['token'],
65
+              'user' => options['user'],
66
+              'message' => message
67
+            }
68
+
69
+            post_params['device'] = event.payload['device'].presence  || options['device']
70
+            post_params['title'] = event.payload['title'].presence  || event.payload['subject'].presence  || options['title']
71
+
72
+            url = (event.payload['url'].presence  || options['url'] || '').to_s
73
+            url = url.slice 0..512
74
+            post_params['url'] = url
75
+
76
+            url_title = (event.payload['url_title'].presence  || options['url_title']).to_s
77
+            url_title = url_title.slice 0..100
78
+            post_params['url_title'] = url_title
79
+
80
+            post_params['priority'] = (event.payload['priority'].presence  || options['priority']).to_i
81
+
82
+            if event.payload.has_key? 'timestamp'
83
+              post_params['timestamp'] = (event.payload['timestamp']).to_s
84
+            end
85
+
86
+            post_params['sound'] = (event.payload['sound'].presence  || options['sound']).to_s
87
+
88
+            post_params['retry'] = (event.payload['retry'].presence  || options['retry']).to_i
89
+
90
+            post_params['expire'] = (event.payload['expire'].presence  || options['expire']).to_i
91
+
92
+            send_notification(post_params)
93
+        end
94
+      end
95
+    end
96
+
97
+    def working?
98
+      last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs?
99
+    end
100
+
101
+    def send_notification(post_params)
102
+      response = HTTParty.post(API_URL, :query => post_params)
103
+      puts response
104
+    end
105
+
106
+  end
107
+end

+ 222 - 0
spec/models/agents/pushover_agent_spec.rb

@@ -0,0 +1,222 @@
1
+require 'spec_helper'
2
+
3
+describe Agents::PushoverAgent do
4
+  before do
5
+    @checker = Agents::PushoverAgent.new(:name => 'Some Name',
6
+                                       :options => { :token => 'x',
7
+                                                :user => 'x',
8
+                                                :message => 'Some Message',
9
+                                                :device => 'Some Device',
10
+                                                :title => 'Some Message Title',
11
+                                                :url => 'http://someurl.com',
12
+                                                :url_title => 'Some Url Title',
13
+                                                :priority => 0,
14
+                                                :timestamp => 'false',
15
+                                                :sound => 'pushover',
16
+                                                :retry => 0,
17
+                                                :expire => 0,
18
+                                                :expected_receive_period_in_days => '1'})
19
+     
20
+    @checker.user = users(:bob)
21
+    @checker.save!
22
+
23
+    @event = Event.new
24
+    @event.agent = agents(:bob_weather_agent)
25
+    @event.payload = { :message => 'Looks like its going to rain' }
26
+    @event.save!
27
+
28
+    @sent_notifications = []
29
+    stub.any_instance_of(Agents::PushoverAgent).send_notification  { |notification| @sent_notifications << notification}
30
+  end
31
+
32
+  describe '#receive' do
33
+    it 'should make sure multiple events are being received' do
34
+      event1 = Event.new
35
+      event1.agent = agents(:bob_rain_notifier_agent)
36
+      event1.payload = { :message => 'Some message' }
37
+      event1.save!
38
+
39
+      event2 = Event.new
40
+      event2.agent = agents(:bob_weather_agent)
41
+      event2.payload = { :message => 'Some other message' }
42
+      event2.save!
43
+
44
+      @checker.receive([@event,event1,event2])
45
+      @sent_notifications[0]['message'].should == 'Looks like its going to rain'
46
+      @sent_notifications[1]['message'].should == 'Some message'
47
+      @sent_notifications[2]['message'].should == 'Some other message'
48
+    end
49
+
50
+    it 'should make sure event message overrides default message' do
51
+      event = Event.new
52
+      event.agent = agents(:bob_rain_notifier_agent)
53
+      event.payload = { :message => 'Some new message'}
54
+      event.save!
55
+
56
+      @checker.receive([event])
57
+      @sent_notifications[0]['message'].should == 'Some new message'
58
+    end
59
+
60
+    it 'should make sure event text overrides default message' do
61
+      event = Event.new
62
+      event.agent = agents(:bob_rain_notifier_agent)
63
+      event.payload = { :text => 'Some new text'}
64
+      event.save!
65
+
66
+      @checker.receive([event])
67
+      @sent_notifications[0]['message'].should == 'Some new text'
68
+    end
69
+
70
+    it 'should make sure event title overrides default title' do
71
+      event = Event.new
72
+      event.agent = agents(:bob_rain_notifier_agent)
73
+      event.payload = { :message => 'Some message', :title => 'Some new title' }
74
+      event.save!
75
+
76
+      @checker.receive([event])
77
+      @sent_notifications[0]['title'].should == 'Some new title'
78
+    end
79
+
80
+    it 'should make sure event url overrides default url' do
81
+      event = Event.new
82
+      event.agent = agents(:bob_rain_notifier_agent)
83
+      event.payload = { :message => 'Some message', :url => 'Some new url' }
84
+      event.save!
85
+
86
+      @checker.receive([event])
87
+      @sent_notifications[0]['url'].should == 'Some new url'
88
+    end
89
+
90
+    it 'should make sure event url_title overrides default url_title' do
91
+      event = Event.new
92
+      event.agent = agents(:bob_rain_notifier_agent)
93
+      event.payload = { :message => 'Some message', :url_title => 'Some new url_title' }
94
+      event.save!
95
+
96
+      @checker.receive([event])
97
+      @sent_notifications[0]['url_title'].should == 'Some new url_title'
98
+    end
99
+
100
+    it 'should make sure event priority overrides default priority' do
101
+      event = Event.new
102
+      event.agent = agents(:bob_rain_notifier_agent)
103
+      event.payload = { :message => 'Some message', :priority => 1 }
104
+      event.save!
105
+
106
+      @checker.receive([event])
107
+      @sent_notifications[0]['priority'].should == 1
108
+    end
109
+
110
+    it 'should make sure event timestamp overrides default timestamp' do
111
+      event = Event.new
112
+      event.agent = agents(:bob_rain_notifier_agent)
113
+      event.payload = { :message => 'Some message', :timestamp => 'false' }
114
+      event.save!
115
+
116
+      @checker.receive([event])
117
+      @sent_notifications[0]['timestamp'].should == 'false'
118
+    end
119
+
120
+    it 'should make sure event sound overrides default sound' do
121
+      event = Event.new
122
+      event.agent = agents(:bob_rain_notifier_agent)
123
+      event.payload = { :message => 'Some message', :sound => 'Some new sound' }
124
+      event.save!
125
+
126
+      @checker.receive([event])
127
+      @sent_notifications[0]['sound'].should == 'Some new sound'
128
+    end
129
+
130
+    it 'should make sure event retry overrides default retry' do
131
+      event = Event.new
132
+      event.agent = agents(:bob_rain_notifier_agent)
133
+      event.payload = { :message => 'Some message', :retry => 1 }
134
+      event.save!
135
+
136
+      @checker.receive([event])
137
+      @sent_notifications[0]['retry'].should == 1
138
+    end
139
+
140
+    it 'should make sure event expire overrides default expire' do
141
+      event = Event.new
142
+      event.agent = agents(:bob_rain_notifier_agent)
143
+      event.payload = { :message => 'Some message', :expire => 60 }
144
+      event.save!
145
+
146
+      @checker.receive([event])
147
+      @sent_notifications[0]['expire'].should == 60
148
+    end
149
+  end
150
+
151
+  describe '#working?' do
152
+    it 'checks if events have been received within the expected receive period' do
153
+      # No events received
154
+      @checker.should_not be_working 
155
+      Agents::PushoverAgent.async_receive @checker.id, [@event.id]
156
+
157
+      # Just received events
158
+      @checker.reload.should be_working 
159
+      two_days_from_now = 2.days.from_now
160
+      stub(Time).now { two_days_from_now }
161
+      
162
+      # More time has passed than the expected receive period without any new events
163
+      @checker.reload.should_not be_working 
164
+    end
165
+  end
166
+
167
+  describe "validation" do
168
+    before do
169
+      @checker.should be_valid
170
+    end
171
+
172
+    it "should validate presence of token" do
173
+      @checker.options[:token] = ""
174
+      @checker.should_not be_valid
175
+    end
176
+
177
+    it "should validate presence of user" do
178
+      @checker.options[:user] = ""
179
+      @checker.should_not be_valid
180
+    end
181
+
182
+    it "should validate presence of expected_receive_period_in_days" do
183
+      @checker.options[:expected_receive_period_in_days] = ""
184
+      @checker.should_not be_valid
185
+    end
186
+
187
+    it "should make sure device is optional" do
188
+      @checker.options[:device] = ""
189
+      @checker.should be_valid
190
+    end
191
+
192
+    it "should make sure title is optional" do
193
+      @checker.options[:title] = ""
194
+      @checker.should be_valid
195
+    end
196
+
197
+    it "should make sure url is optional" do
198
+      @checker.options[:url] = ""
199
+      @checker.should be_valid
200
+    end
201
+
202
+    it "should make sure url_title is optional" do
203
+      @checker.options[:url_title] = ""
204
+      @checker.should be_valid
205
+    end
206
+
207
+    it "should make sure priority is optional" do
208
+      @checker.options[:priority] = ""
209
+      @checker.should be_valid
210
+    end
211
+
212
+    it "should make sure timestamp is optional" do
213
+      @checker.options[:timestamp] = ""
214
+      @checker.should be_valid
215
+    end
216
+
217
+    it "should make sure sound is optional" do
218
+      @checker.options[:sound] = ""
219
+      @checker.should be_valid
220
+    end
221
+  end
222
+end