maia 2.0.0 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +188 -0
- data/app/controllers/concerns/maia/controller.rb +1 -1
- data/lib/maia.rb +12 -0
- data/lib/maia/engine.rb +0 -1
- data/lib/maia/fcm.rb +7 -0
- data/lib/maia/fcm/connection.rb +34 -0
- data/lib/maia/fcm/notification.rb +27 -0
- data/lib/maia/fcm/response.rb +55 -0
- data/lib/maia/fcm/response_collection.rb +34 -0
- data/lib/maia/fcm/result.rb +31 -0
- data/lib/maia/fcm/result_collection.rb +35 -0
- data/lib/maia/fcm/service.rb +48 -0
- data/lib/maia/message.rb +23 -62
- data/lib/maia/messenger.rb +25 -31
- data/lib/maia/poke.rb +2 -2
- data/lib/maia/version.rb +1 -1
- metadata +34 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1164306f881ef2bbcafa53947387cd80389f0f41c7b7cc3b1b5c685ac2d52ad3
|
4
|
+
data.tar.gz: a0b5faf5699c23b693c161906ab14546e0a96656a88307e8d96cac82d81ff78e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b2b6a594dafbd284acfed06d67714910f04ee2795bd40b261260366ebd930334b649a2b4ed28a12a7cf9610a7931963445a2e32156ed11a930216c2ee773845
|
7
|
+
data.tar.gz: f8c80d2f8f1cfcb57cbad64db76b7cfc6600f161141982285221157fb0540f899b8950f03485089504e02b2f4796df6fe6011a9abacd80afed7de2432c2182d0
|
data/README.md
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
# Maia
|
2
|
+
|
3
|
+
This project maintains a `Maia::Device` model and facilitates the delivery of push notifications for iOS and Android through FCM (Firebase Cloud Messaging).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```
|
8
|
+
gem 'maia'
|
9
|
+
bundle
|
10
|
+
bin/rake railties:install:migrations
|
11
|
+
bin/rake db:migrate
|
12
|
+
```
|
13
|
+
|
14
|
+
This will copy the `maia_devices` table into your project.
|
15
|
+
|
16
|
+
## Setup
|
17
|
+
|
18
|
+
Maia relies on [ActiveJob](https://github.com/rails/rails/tree/master/activejob) to enqueue messages. Ensure your application is properly setup with ActiveJob!
|
19
|
+
|
20
|
+
Set your FCM key in an initializer, such as `config/initializers/maia.rb`:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
Maia::FCM.key = Rails.application.secrets[:fcm][:key]
|
24
|
+
```
|
25
|
+
|
26
|
+
Alternatively, you can set `ENV['FCM_KEY']`, which Maia will check first.
|
27
|
+
|
28
|
+
Include `Maia::Model` into your User model. This will attach the `has_many` relationship you need with `Maia::Device`:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class User
|
32
|
+
include Maia::Model
|
33
|
+
# ...
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
Create a Devices controller where you need it, which is most likely an API. The controller itself will be generated within your application so that Maia does not make any assumptions about your method of authentication, `respond_with` mimetypes, etc. The only requirement is that `current_user` exists and returns whatever model included `Maia::Model`.
|
38
|
+
|
39
|
+
Here's an example of getting setup with an API Devices controller that mobile apps can register with:
|
40
|
+
|
41
|
+
`bin/rails g controller api/devices`
|
42
|
+
|
43
|
+
After the controller is generated, include `Maia::Controller`:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class API::DevicesController
|
47
|
+
include Maia::Controller
|
48
|
+
# ...
|
49
|
+
```
|
50
|
+
|
51
|
+
Maia provides the `create` method for you, so devices can now register themselves by POSTing to that controller. If you'd like to add any other actions, feel free.
|
52
|
+
|
53
|
+
## Device Registration
|
54
|
+
|
55
|
+
Devices can register with your application by submitting a POST to your devices controller with these params:
|
56
|
+
|
57
|
+
```
|
58
|
+
{ "device": { "token": "<TOKEN>" } }
|
59
|
+
```
|
60
|
+
|
61
|
+
Where `<TOKEN>` is the token from FCM registration.
|
62
|
+
|
63
|
+
## Device Management
|
64
|
+
|
65
|
+
When FCM responds with an invalid or unregistered device token, the device record will be destroyed from the database.
|
66
|
+
|
67
|
+
When FCM responds with a canonical ID, the device record will be updated so that it's `token` field will be equal to the canonical ID given by FCM.
|
68
|
+
|
69
|
+
## Device Expiration
|
70
|
+
|
71
|
+
Devices will expire after 14 days. This is to ensure user's who sell or otherwise give away their device will not be tied to that device forever. Each time a POST to Devices is received, the token expiration will be refreshed.
|
72
|
+
|
73
|
+
## Defining Messages
|
74
|
+
|
75
|
+
Maia provides a `Maia::Message` class that provides an interface for defining push messages and sending them. To define a message, inherit from `Maia::Message` and override whatever you need to:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class ExampleMessage < Maia::Message
|
79
|
+
# Required
|
80
|
+
def title
|
81
|
+
'Something happened!'
|
82
|
+
end
|
83
|
+
|
84
|
+
# Required, the body of the message on Android, alert on iOS
|
85
|
+
def body
|
86
|
+
'Something very important has happened, check it out!'
|
87
|
+
end
|
88
|
+
|
89
|
+
# Determines the icon to load on Android phones
|
90
|
+
def icon
|
91
|
+
'icn_maia'
|
92
|
+
end
|
93
|
+
|
94
|
+
# Will use 'default' by default. Overriding to nil will prevent sound
|
95
|
+
def sound
|
96
|
+
'default'
|
97
|
+
end
|
98
|
+
|
99
|
+
# Badge to use on iOS
|
100
|
+
def badge
|
101
|
+
1
|
102
|
+
end
|
103
|
+
|
104
|
+
# #RRGGBB formatted color to use for the Android notification icon
|
105
|
+
def color
|
106
|
+
'#ffffff'
|
107
|
+
end
|
108
|
+
|
109
|
+
# click_action on Android, category on iOS
|
110
|
+
def on_click
|
111
|
+
'SOMETHING_HAPPENED'
|
112
|
+
end
|
113
|
+
|
114
|
+
# Any additional data to send with the payload
|
115
|
+
def data
|
116
|
+
{ foo: :bar }
|
117
|
+
end
|
118
|
+
|
119
|
+
# :normal or :high (:normal by default)
|
120
|
+
def priority
|
121
|
+
:normal
|
122
|
+
end
|
123
|
+
|
124
|
+
# Override to true in order to send the iOS content-available flag
|
125
|
+
def content_available?
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
# Override to true in order to send a dry run push. This can help debug any device errors without actually sending a push message
|
130
|
+
def dry_run?
|
131
|
+
false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
This message will generate the following FCM payload:
|
137
|
+
|
138
|
+
```json
|
139
|
+
{
|
140
|
+
"priority": "normal",
|
141
|
+
"dry_run": false,
|
142
|
+
"content_available": false,
|
143
|
+
"data": {
|
144
|
+
"foo": "bar"
|
145
|
+
},
|
146
|
+
"notification": {
|
147
|
+
"title": "Something happened!",
|
148
|
+
"body": "'Something very important has happened, check it out!'",
|
149
|
+
"icon": "icn_maia",
|
150
|
+
"sound": "default",
|
151
|
+
"badge": 1,
|
152
|
+
"color": "#ffffff",
|
153
|
+
"click_action": "SOMETHING_HAPPENED",
|
154
|
+
},
|
155
|
+
"registration_ids": ["<TOKEN1>", "<TOKEN2>"]
|
156
|
+
}
|
157
|
+
```
|
158
|
+
|
159
|
+
`Maia::Message` does not define a constructor so you can construct your message however you want.
|
160
|
+
|
161
|
+
## Sending messages
|
162
|
+
|
163
|
+
`Maia::Message` provides a `send_to` that pushes the message out to a user (or collection of users). The argument to `send_to` should be a single record or relation of records.
|
164
|
+
|
165
|
+
For example:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
ExampleMessage.new(...).send_to User.first
|
169
|
+
ExampleMessage.new(...).send_to User.where(beta: true)
|
170
|
+
```
|
171
|
+
|
172
|
+
`send_to` will batch users in groups of 999 tokens (FCM limitation) and send them via ActiveJob.
|
173
|
+
|
174
|
+
## Specifying job options (`wait`, `queue`, etc)
|
175
|
+
|
176
|
+
The `send_to` method passes it's last argument into [ActiveJob's `set` method](http://apidock.com/rails/ActiveJob/Core/ClassMethods/set), for example:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
ExampleMessage.new(...).send_to User.first, wait: 10.seconds, queue: :maia
|
180
|
+
```
|
181
|
+
|
182
|
+
## Sending a test push
|
183
|
+
|
184
|
+
Maia comes with a built-in message to use to test your configuration out:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
Maia::Poke.new.send_to user
|
188
|
+
```
|
data/lib/maia.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'rails'
|
2
|
+
require 'active_support/core_ext/enumerable'
|
3
|
+
|
2
4
|
require 'maia/engine'
|
3
5
|
require 'maia/message'
|
4
6
|
require 'maia/messenger'
|
@@ -6,5 +8,15 @@ require 'maia/poke'
|
|
6
8
|
require 'maia/dry_run'
|
7
9
|
require 'maia/error'
|
8
10
|
|
11
|
+
require 'maia/fcm'
|
12
|
+
require 'maia/fcm/connection'
|
13
|
+
require 'maia/fcm/notification'
|
14
|
+
require 'maia/fcm/response_collection'
|
15
|
+
require 'maia/fcm/response'
|
16
|
+
require 'maia/fcm/result_collection'
|
17
|
+
require 'maia/fcm/result'
|
18
|
+
require 'maia/fcm/service'
|
19
|
+
|
9
20
|
module Maia
|
21
|
+
BATCH_SIZE = 999
|
10
22
|
end
|
data/lib/maia/engine.rb
CHANGED
data/lib/maia/fcm.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Maia
|
2
|
+
module FCM
|
3
|
+
class Connection
|
4
|
+
URL = 'https://fcm.googleapis.com/fcm/send'.freeze
|
5
|
+
|
6
|
+
def initialize(key)
|
7
|
+
@key = key
|
8
|
+
end
|
9
|
+
|
10
|
+
def write(payload = {})
|
11
|
+
request = Net::HTTP::Post.new uri, headers
|
12
|
+
request.body = payload.to_json
|
13
|
+
http.request request
|
14
|
+
end
|
15
|
+
|
16
|
+
def uri
|
17
|
+
URI(URL)
|
18
|
+
end
|
19
|
+
|
20
|
+
def headers
|
21
|
+
{
|
22
|
+
'Content-Type' => 'application/json',
|
23
|
+
'Authorization' => "key=#{@key}"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def http
|
28
|
+
@_http ||= Net::HTTP.new(uri.host, uri.port).tap do |h|
|
29
|
+
h.use_ssl = true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Maia
|
2
|
+
module FCM
|
3
|
+
class Notification
|
4
|
+
attr_accessor :attributes
|
5
|
+
|
6
|
+
def initialize(attributes = {})
|
7
|
+
@attributes = attributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
@attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
attributes == other.attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(method_name, *args, &block)
|
19
|
+
@attributes.fetch(method_name) { super }
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(method_name)
|
23
|
+
@attributes.include? method_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Maia
|
2
|
+
module FCM
|
3
|
+
class Response
|
4
|
+
attr_reader :http_response, :tokens
|
5
|
+
|
6
|
+
def initialize(http_response, tokens = [])
|
7
|
+
@http_response = http_response
|
8
|
+
@tokens = tokens
|
9
|
+
end
|
10
|
+
|
11
|
+
def status
|
12
|
+
http_response.code
|
13
|
+
end
|
14
|
+
|
15
|
+
def success?
|
16
|
+
(200..399).cover? status
|
17
|
+
end
|
18
|
+
|
19
|
+
def fail?
|
20
|
+
!success?
|
21
|
+
end
|
22
|
+
|
23
|
+
def results
|
24
|
+
@_results ||= begin
|
25
|
+
results = to_h.fetch 'results', []
|
26
|
+
results.map!.with_index do |attributes, i|
|
27
|
+
Result.new attributes, tokens[i]
|
28
|
+
end
|
29
|
+
ResultCollection.new(results)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def error
|
34
|
+
case status
|
35
|
+
when 400
|
36
|
+
'Invalid JSON was sent to FCM.'
|
37
|
+
when 401
|
38
|
+
'Authentication error with FCM. Check the server whitelist and the validity of your project key.'
|
39
|
+
when 500..599
|
40
|
+
'FCM Internal server error.'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def retry_after
|
45
|
+
http_response.headers['Retry-After']
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_h
|
49
|
+
JSON.parse http_response.body
|
50
|
+
rescue JSON::ParserError
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Maia
|
2
|
+
module FCM
|
3
|
+
class ResponseCollection
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(notification, responses = [])
|
7
|
+
@notification = notification
|
8
|
+
@responses = responses
|
9
|
+
end
|
10
|
+
|
11
|
+
def results
|
12
|
+
collection = ResultCollection.new
|
13
|
+
@responses.each do |response|
|
14
|
+
response.results.each do |result|
|
15
|
+
collection << result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
collection
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](index)
|
22
|
+
@responses[index]
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<(response)
|
26
|
+
@responses.concat Array(response).flatten
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
@responses.each(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Maia
|
2
|
+
module FCM
|
3
|
+
class Result
|
4
|
+
include ActiveModel::Model
|
5
|
+
|
6
|
+
attr_accessor :message_id, :registration_id, :error
|
7
|
+
attr_reader :token
|
8
|
+
|
9
|
+
def initialize(attributes, token)
|
10
|
+
super attributes
|
11
|
+
@token = token
|
12
|
+
end
|
13
|
+
|
14
|
+
def success?
|
15
|
+
message_id.present?
|
16
|
+
end
|
17
|
+
|
18
|
+
def fail?
|
19
|
+
!success?
|
20
|
+
end
|
21
|
+
|
22
|
+
def canonical_id
|
23
|
+
registration_id
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_canonical_id?
|
27
|
+
canonical_id.present?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Maia
|
2
|
+
module FCM
|
3
|
+
class ResultCollection
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(results = [])
|
7
|
+
@results = results
|
8
|
+
end
|
9
|
+
|
10
|
+
def succeeded
|
11
|
+
@results.select(&:success?)
|
12
|
+
end
|
13
|
+
|
14
|
+
def failed
|
15
|
+
@results.select(&:fail?)
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_canonical_ids
|
19
|
+
@results.select(&:has_canonical_id?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](index)
|
23
|
+
@results[index]
|
24
|
+
end
|
25
|
+
|
26
|
+
def <<(result)
|
27
|
+
@results << result
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
@results.each(&block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Maia
|
2
|
+
module FCM
|
3
|
+
class Service
|
4
|
+
def initialize
|
5
|
+
@connection ||= FCM::Connection.new key
|
6
|
+
end
|
7
|
+
|
8
|
+
def key
|
9
|
+
ENV.fetch 'FCM_KEY', Maia::FCM.key
|
10
|
+
end
|
11
|
+
|
12
|
+
def deliver(notification, *tokens, topic: nil)
|
13
|
+
responses = ResponseCollection.new notification
|
14
|
+
responses << deliver_all(notification, tokens)
|
15
|
+
responses << deliver_all(notification, "/topics/#{topic}") if topic
|
16
|
+
responses
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def deliver_all(notification, tokens)
|
21
|
+
batch(tokens).map do |batch|
|
22
|
+
if batch.one?
|
23
|
+
unicast notification, batch.first
|
24
|
+
elsif batch.many?
|
25
|
+
multicast notification, batch
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def unicast(notification, recipient)
|
31
|
+
deliver! notification, recipient, to: recipient
|
32
|
+
end
|
33
|
+
|
34
|
+
def multicast(notification, recipients)
|
35
|
+
deliver! notification, recipients, registration_ids: recipients
|
36
|
+
end
|
37
|
+
|
38
|
+
def deliver!(notification, recipients, params = {})
|
39
|
+
payload = notification.to_h.merge params
|
40
|
+
Response.new @connection.write(payload), Array(recipients)
|
41
|
+
end
|
42
|
+
|
43
|
+
def batch(recipients, batch_size: Maia::BATCH_SIZE)
|
44
|
+
Array(recipients).flatten.compact.each_slice batch_size
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/maia/message.rb
CHANGED
@@ -1,19 +1,17 @@
|
|
1
1
|
module Maia
|
2
2
|
class Message
|
3
|
-
MAX_TOKENS_AT_ONCE = 999
|
4
|
-
|
5
3
|
def send_to(pushable, job_options = {})
|
6
4
|
devices = Device.owned_by pushable
|
7
|
-
worker
|
5
|
+
worker = Messenger.set job_options
|
8
6
|
|
9
|
-
enqueue worker, devices.android
|
10
|
-
enqueue worker, devices.ios
|
11
|
-
enqueue worker, devices.unknown
|
7
|
+
enqueue worker, devices.android
|
8
|
+
enqueue worker, devices.ios
|
9
|
+
enqueue worker, devices.unknown
|
12
10
|
end
|
13
11
|
|
14
|
-
def enqueue(worker, devices
|
15
|
-
devices.
|
16
|
-
worker.perform_later
|
12
|
+
def enqueue(worker, devices)
|
13
|
+
devices.in_batches(of: Maia::BATCH_SIZE) do |devices|
|
14
|
+
worker.perform_later devices.pluck(:token), to_h.deep_stringify_keys
|
17
15
|
end
|
18
16
|
end
|
19
17
|
|
@@ -23,11 +21,14 @@ module Maia
|
|
23
21
|
def body
|
24
22
|
end
|
25
23
|
|
24
|
+
def on_click
|
25
|
+
end
|
26
|
+
|
26
27
|
def icon
|
27
28
|
end
|
28
29
|
|
29
30
|
def sound
|
30
|
-
|
31
|
+
:default
|
31
32
|
end
|
32
33
|
|
33
34
|
def badge
|
@@ -36,17 +37,6 @@ module Maia
|
|
36
37
|
def color
|
37
38
|
end
|
38
39
|
|
39
|
-
def action
|
40
|
-
end
|
41
|
-
|
42
|
-
def title_i18n
|
43
|
-
[]
|
44
|
-
end
|
45
|
-
|
46
|
-
def body_i18n
|
47
|
-
[]
|
48
|
-
end
|
49
|
-
|
50
40
|
def data
|
51
41
|
end
|
52
42
|
|
@@ -58,12 +48,12 @@ module Maia
|
|
58
48
|
false
|
59
49
|
end
|
60
50
|
|
61
|
-
def
|
51
|
+
def content_mutable?
|
62
52
|
false
|
63
53
|
end
|
64
54
|
|
65
|
-
def
|
66
|
-
|
55
|
+
def dry_run?
|
56
|
+
false
|
67
57
|
end
|
68
58
|
|
69
59
|
def notification
|
@@ -71,51 +61,22 @@ module Maia
|
|
71
61
|
title: title,
|
72
62
|
body: body,
|
73
63
|
icon: icon,
|
74
|
-
sound: sound,
|
64
|
+
sound: sound.to_s,
|
75
65
|
badge: badge,
|
76
66
|
color: color,
|
77
|
-
click_action:
|
78
|
-
}.
|
67
|
+
click_action: on_click
|
68
|
+
}.compact
|
79
69
|
end
|
80
70
|
|
81
|
-
def to_h
|
82
|
-
|
71
|
+
def to_h
|
72
|
+
{
|
83
73
|
priority: priority.to_s,
|
84
74
|
dry_run: dry_run?,
|
85
75
|
content_available: content_available?,
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
payload.compact
|
76
|
+
mutable_content: content_mutable?,
|
77
|
+
data: data,
|
78
|
+
notification: notification
|
79
|
+
}.compact
|
91
80
|
end
|
92
|
-
|
93
|
-
private
|
94
|
-
def i18n
|
95
|
-
{}.tap do |hash|
|
96
|
-
hash[:body_loc_key] = body_i18n.first
|
97
|
-
hash[:body_loc_args] = body_i18n_args
|
98
|
-
hash[:title_loc_key] = title_i18n_key
|
99
|
-
hash[:title_loc_args] = title_i18n_args
|
100
|
-
end.compact
|
101
|
-
end
|
102
|
-
|
103
|
-
def body_i18n_key
|
104
|
-
body_i18n.first
|
105
|
-
end
|
106
|
-
|
107
|
-
def body_i18n_args
|
108
|
-
args = body_i18n.drop 1
|
109
|
-
args if args.any?
|
110
|
-
end
|
111
|
-
|
112
|
-
def title_i18n_key
|
113
|
-
title_i18n.first
|
114
|
-
end
|
115
|
-
|
116
|
-
def title_i18n_args
|
117
|
-
args = title_i18n.drop 1
|
118
|
-
args if args.any?
|
119
|
-
end
|
120
81
|
end
|
121
82
|
end
|
data/lib/maia/messenger.rb
CHANGED
@@ -4,46 +4,48 @@ module Maia
|
|
4
4
|
logger.info "Pushing to #{tokens.size} token(s)..."
|
5
5
|
logger.info "Payload: #{payload}"
|
6
6
|
|
7
|
-
notification =
|
8
|
-
responses
|
7
|
+
notification = FCM::Notification.new payload
|
8
|
+
responses = fcm.deliver notification, tokens
|
9
9
|
|
10
10
|
responses.each do |response|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
handle_failed_tokens response.results.failed
|
15
|
-
update_devices_to_use_canonical_ids response.results.with_canonical_ids
|
16
|
-
end
|
11
|
+
raise Maia::Error, response.error if response.error
|
12
|
+
handle_errors response.results.failed
|
13
|
+
update_devices_to_use_canonical_ids response.results.with_canonical_ids
|
17
14
|
end
|
18
15
|
end
|
19
16
|
|
20
17
|
private
|
21
|
-
def
|
22
|
-
@_service ||=
|
23
|
-
end
|
24
|
-
|
25
|
-
def connection
|
26
|
-
nil # use the default
|
18
|
+
def fcm
|
19
|
+
@_service ||= FCM::Service.new
|
27
20
|
end
|
28
21
|
|
29
|
-
def
|
22
|
+
def handle_errors(results)
|
30
23
|
results.each do |result|
|
31
|
-
device = Maia::Device.find_by
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
24
|
+
device = Maia::Device.find_by token: result.token
|
25
|
+
next unless device.present?
|
26
|
+
|
27
|
+
if device_unrecoverable? result.error
|
28
|
+
log_error "Destroying device #{device.id}", result, device
|
29
|
+
device.destroy
|
30
|
+
else
|
31
|
+
log_error "Push to device #{device.id} failed", result, device
|
39
32
|
end
|
40
33
|
end
|
41
34
|
end
|
42
35
|
|
36
|
+
def device_unrecoverable?(error)
|
37
|
+
error =~ /InvalidRegistration|NotRegistered|MismatchSenderId/
|
38
|
+
end
|
39
|
+
|
40
|
+
def log_error(message, result, device)
|
41
|
+
logger.info "#{message} (error: #{result.error}, token: #{device.token})"
|
42
|
+
end
|
43
|
+
|
43
44
|
def update_devices_to_use_canonical_ids(results)
|
44
45
|
results.each do |result|
|
45
46
|
device = Maia::Device.find_by token: result.token
|
46
47
|
next if device.nil?
|
48
|
+
|
47
49
|
if user_already_has_token_registered?(device.pushable, result.canonical_id)
|
48
50
|
device.destroy
|
49
51
|
else
|
@@ -52,16 +54,8 @@ module Maia
|
|
52
54
|
end
|
53
55
|
end
|
54
56
|
|
55
|
-
def device_unrecoverable?(error)
|
56
|
-
error =~ /InvalidRegistration|NotRegistered|MismatchSenderId/
|
57
|
-
end
|
58
|
-
|
59
57
|
def user_already_has_token_registered?(user, token)
|
60
58
|
user.devices.exists? token: token
|
61
59
|
end
|
62
|
-
|
63
|
-
def log_error(message, result, device)
|
64
|
-
logger.info "#{message} (error: #{result.error}, token: #{device.token})"
|
65
|
-
end
|
66
60
|
end
|
67
61
|
end
|
data/lib/maia/poke.rb
CHANGED
data/lib/maia/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Logan Serman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,78 +16,48 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5
|
19
|
+
version: '5'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '7'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5
|
29
|
+
version: '5'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: activejob
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5
|
34
|
-
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 5.0.2
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: responders
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
39
|
+
version: '5'
|
40
|
+
- - "<"
|
46
41
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
42
|
+
version: '7'
|
48
43
|
type: :runtime
|
49
44
|
prerelease: false
|
50
45
|
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '2.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: mercurius
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
46
|
requirements:
|
59
47
|
- - ">="
|
60
48
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
62
|
-
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
49
|
+
version: '5'
|
50
|
+
- - "<"
|
67
51
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
52
|
+
version: '7'
|
69
53
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rspec-rails
|
54
|
+
name: responders
|
85
55
|
requirement: !ruby/object:Gem::Requirement
|
86
56
|
requirements:
|
87
57
|
- - ">="
|
88
58
|
- !ruby/object:Gem::Version
|
89
59
|
version: '0'
|
90
|
-
type: :
|
60
|
+
type: :runtime
|
91
61
|
prerelease: false
|
92
62
|
version_requirements: !ruby/object:Gem::Requirement
|
93
63
|
requirements:
|
@@ -95,7 +65,7 @@ dependencies:
|
|
95
65
|
- !ruby/object:Gem::Version
|
96
66
|
version: '0'
|
97
67
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
68
|
+
name: sqlite3
|
99
69
|
requirement: !ruby/object:Gem::Requirement
|
100
70
|
requirements:
|
101
71
|
- - ">="
|
@@ -109,7 +79,7 @@ dependencies:
|
|
109
79
|
- !ruby/object:Gem::Version
|
110
80
|
version: '0'
|
111
81
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
82
|
+
name: rspec-rails
|
113
83
|
requirement: !ruby/object:Gem::Requirement
|
114
84
|
requirements:
|
115
85
|
- - ">="
|
@@ -123,7 +93,7 @@ dependencies:
|
|
123
93
|
- !ruby/object:Gem::Version
|
124
94
|
version: '0'
|
125
95
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
96
|
+
name: webmock
|
127
97
|
requirement: !ruby/object:Gem::Requirement
|
128
98
|
requirements:
|
129
99
|
- - ">="
|
@@ -137,7 +107,7 @@ dependencies:
|
|
137
107
|
- !ruby/object:Gem::Version
|
138
108
|
version: '0'
|
139
109
|
- !ruby/object:Gem::Dependency
|
140
|
-
name: webmock
|
110
|
+
name: webmock-rspec-helper
|
141
111
|
requirement: !ruby/object:Gem::Requirement
|
142
112
|
requirements:
|
143
113
|
- - ">="
|
@@ -150,7 +120,7 @@ dependencies:
|
|
150
120
|
- - ">="
|
151
121
|
- !ruby/object:Gem::Version
|
152
122
|
version: '0'
|
153
|
-
description: Manage device tokens and push messaging with Rails and
|
123
|
+
description: Manage device tokens and push messaging with Rails and FCM.
|
154
124
|
email:
|
155
125
|
- loganserman@gmail.com
|
156
126
|
executables: []
|
@@ -158,6 +128,7 @@ extensions: []
|
|
158
128
|
extra_rdoc_files: []
|
159
129
|
files:
|
160
130
|
- MIT-LICENSE
|
131
|
+
- README.md
|
161
132
|
- Rakefile
|
162
133
|
- app/controllers/concerns/maia/controller.rb
|
163
134
|
- app/models/concerns/maia/model.rb
|
@@ -171,6 +142,14 @@ files:
|
|
171
142
|
- lib/maia/dry_run.rb
|
172
143
|
- lib/maia/engine.rb
|
173
144
|
- lib/maia/error.rb
|
145
|
+
- lib/maia/fcm.rb
|
146
|
+
- lib/maia/fcm/connection.rb
|
147
|
+
- lib/maia/fcm/notification.rb
|
148
|
+
- lib/maia/fcm/response.rb
|
149
|
+
- lib/maia/fcm/response_collection.rb
|
150
|
+
- lib/maia/fcm/result.rb
|
151
|
+
- lib/maia/fcm/result_collection.rb
|
152
|
+
- lib/maia/fcm/service.rb
|
174
153
|
- lib/maia/message.rb
|
175
154
|
- lib/maia/messenger.rb
|
176
155
|
- lib/maia/poke.rb
|
@@ -195,8 +174,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
174
|
version: '0'
|
196
175
|
requirements: []
|
197
176
|
rubyforge_project:
|
198
|
-
rubygems_version: 2.
|
177
|
+
rubygems_version: 2.7.7
|
199
178
|
signing_key:
|
200
179
|
specification_version: 4
|
201
|
-
summary: Manage device tokens and push messaging with Rails and
|
180
|
+
summary: Manage device tokens and push messaging with Rails and FCM.
|
202
181
|
test_files: []
|