pushover 3.0.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +28 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +11 -24
- data/.ruby-version +1 -0
- data/CONVENTIONS.md +19 -0
- data/Gemfile +1 -2
- data/Guardfile +3 -3
- data/README.md +236 -44
- data/Rakefile +9 -59
- data/exe/pushover +6 -5
- data/lib/pushover/client.rb +53 -0
- data/lib/pushover/glances.rb +86 -0
- data/lib/pushover/groups.rb +137 -0
- data/lib/pushover/licenses.rb +67 -0
- data/lib/pushover/limits.rb +18 -0
- data/lib/pushover/message.rb +29 -32
- data/lib/pushover/message_encryption.rb +48 -0
- data/lib/pushover/message_validator.rb +167 -0
- data/lib/pushover/messages.rb +20 -0
- data/lib/pushover/receipt.rb +23 -10
- data/lib/pushover/receipts.rb +60 -0
- data/lib/pushover/response.rb +8 -3
- data/lib/pushover/sounds.rb +18 -0
- data/lib/pushover/subscriptions.rb +48 -0
- data/lib/pushover/teams.rb +93 -0
- data/lib/pushover/users.rb +37 -0
- data/lib/pushover/version.rb +1 -1
- data/lib/pushover.rb +14 -1
- data/pushover.gemspec +9 -7
- data/spec/gem/specification_spec.rb +19 -0
- data/spec/pushover/client_spec.rb +149 -0
- data/spec/pushover/glances_spec.rb +206 -0
- data/spec/pushover/groups_spec.rb +486 -0
- data/spec/pushover/licenses_spec.rb +238 -0
- data/spec/pushover/limits_spec.rb +92 -0
- data/spec/pushover/message_spec.rb +146 -26
- data/spec/pushover/messages_spec.rb +199 -0
- data/spec/pushover/receipt_spec.rb +119 -11
- data/spec/pushover/receipts_spec.rb +232 -0
- data/spec/pushover/response_spec.rb +21 -16
- data/spec/pushover/sounds_spec.rb +92 -0
- data/spec/pushover/subscriptions_spec.rb +185 -0
- data/spec/pushover/teams_spec.rb +325 -0
- data/spec/pushover/users_spec.rb +115 -0
- data/spec/spec_helper.rb +2 -3
- metadata +54 -18
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7a19291da36f95fd19954df7a7b93aa1d4b78cb8708702fe4c8ebafb6d14a4c8
|
|
4
|
+
data.tar.gz: b416babb1ae31c5d7e2fe51707785c4bd87c6dfead296568dd378155ac852a82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ffbdf22b369588fd862768b8ad4ba7ce285d78b22c50a64334d9668deaadec498d29d8bde17a05de9969875d9773266f9dc68a90e5c470707eca5906dffd8272
|
|
7
|
+
data.tar.gz: c9aee830ca7abff85081dbda9ba6f912055667607e606e294a6aeaed551f16e574330079dc2362936264904f1cf7209e631ce96f4dbc8eb2d6ea96ed9ca98e7f
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
'on':
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
verify:
|
|
14
|
+
name: Verify
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 10
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Check out repository
|
|
20
|
+
uses: actions/checkout@v7
|
|
21
|
+
|
|
22
|
+
- name: Set up Ruby
|
|
23
|
+
uses: ruby/setup-ruby@v1
|
|
24
|
+
with:
|
|
25
|
+
bundler-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Run CI
|
|
28
|
+
run: bundle exec rake ci
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
plugins: rubocop-rspec
|
|
2
2
|
|
|
3
3
|
AllCops:
|
|
4
|
-
|
|
4
|
+
NewCops: enable
|
|
5
|
+
TargetRubyVersion: 3.3
|
|
5
6
|
Exclude:
|
|
6
|
-
- 'vendor/**/*'
|
|
7
7
|
- 'tmp/**/*'
|
|
8
|
-
- '
|
|
8
|
+
- 'vendor/bundle/**/*'
|
|
9
9
|
|
|
10
10
|
# The defaults for these seem to be based on a rails env, this is not rails.
|
|
11
11
|
Metrics/ModuleLength:
|
|
12
|
-
CountComments: false
|
|
13
12
|
Max: 250
|
|
14
13
|
|
|
15
14
|
Metrics/ClassLength:
|
|
@@ -27,8 +26,8 @@ Metrics/CyclomaticComplexity:
|
|
|
27
26
|
Metrics/PerceivedComplexity:
|
|
28
27
|
Max: 10
|
|
29
28
|
|
|
30
|
-
#
|
|
31
|
-
|
|
29
|
+
# Line length is intentionally unrestricted.
|
|
30
|
+
Layout/LineLength:
|
|
32
31
|
Enabled: false
|
|
33
32
|
|
|
34
33
|
Metrics/BlockLength:
|
|
@@ -47,7 +46,7 @@ Style/RegexpLiteral:
|
|
|
47
46
|
- 'Guardfile'
|
|
48
47
|
- '*.gemspec'
|
|
49
48
|
|
|
50
|
-
#
|
|
49
|
+
# String literal quote style is not enforced.
|
|
51
50
|
Style/StringLiterals:
|
|
52
51
|
Enabled: false
|
|
53
52
|
|
|
@@ -59,22 +58,10 @@ Style/FrozenStringLiteralComment:
|
|
|
59
58
|
|
|
60
59
|
Style/MixinUsage:
|
|
61
60
|
Exclude:
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
- 'bin/*'
|
|
62
|
+
- 'exe/*'
|
|
64
63
|
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
Enabled: true
|
|
68
|
-
Exclude:
|
|
69
|
-
- 'spec/**/*'
|
|
70
|
-
|
|
71
|
-
RSpec/NamedSubject:
|
|
72
|
-
Enabled: false
|
|
73
|
-
|
|
74
|
-
Lint/Debugger:
|
|
75
|
-
Enabled: false
|
|
76
|
-
|
|
77
|
-
# Purty hashes.
|
|
78
|
-
Layout/AlignHash:
|
|
64
|
+
# Align hash pairs as tables.
|
|
65
|
+
Layout/HashAlignment:
|
|
79
66
|
EnforcedHashRocketStyle: table
|
|
80
67
|
EnforcedColonStyle: table
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.3.0
|
data/CONVENTIONS.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Project Conventions
|
|
2
|
+
|
|
3
|
+
## Design
|
|
4
|
+
|
|
5
|
+
- Prefer modern Ruby patterns over legacy implementation patterns.
|
|
6
|
+
- Preserve released public behavior when practical, but do not preserve obsolete implementation details unless they are explicitly part of the supported contract.
|
|
7
|
+
|
|
8
|
+
## Ruby runtime
|
|
9
|
+
|
|
10
|
+
- Run Ruby, Bundler, Rake, RSpec, RuboCop, and other Ruby commands through `/opt/homebrew/bin/rbenv exec`.
|
|
11
|
+
- The Codex shell may resolve `/usr/bin/ruby` (Apple Ruby 2.6) instead of the project's Ruby version.
|
|
12
|
+
- The project runtime is defined by `.ruby-version`.
|
|
13
|
+
|
|
14
|
+
## HTTP client
|
|
15
|
+
|
|
16
|
+
- Use Excon for Pushover HTTP requests. Do not introduce another HTTP client without a specific need.
|
|
17
|
+
- Use Excon's request stubs for mocked HTTP tests.
|
|
18
|
+
- Consult the installed Excon documentation or source before assuming library behavior.
|
|
19
|
+
- it has no context 7 documentation, so read the source code for details.
|
data/Gemfile
CHANGED
|
@@ -4,7 +4,6 @@ gemspec
|
|
|
4
4
|
gem 'excon'
|
|
5
5
|
gem 'gli'
|
|
6
6
|
gem 'oj'
|
|
7
|
-
gem 'codecov', :require => false, :group => :test
|
|
8
7
|
|
|
9
8
|
group :test do
|
|
10
9
|
gem 'bump'
|
|
@@ -13,7 +12,7 @@ group :test do
|
|
|
13
12
|
gem 'rspec'
|
|
14
13
|
gem 'rubocop'
|
|
15
14
|
gem 'rubocop-rspec'
|
|
16
|
-
gem 'simplecov'
|
|
15
|
+
gem 'simplecov', '~> 1.0.3'
|
|
17
16
|
gem 'yard'
|
|
18
17
|
end
|
|
19
18
|
|
data/Guardfile
CHANGED
data/README.md
CHANGED
|
@@ -1,76 +1,268 @@
|
|
|
1
1
|
# Pushover
|
|
2
|
-
[](https://travis-ci.org/erniebrodeur/pushover) [](https://codecov.io/gh/erniebrodeur/pushover)
|
|
3
2
|
|
|
4
|
-
This gem provides a CLI and
|
|
3
|
+
This gem provides a CLI and a Ruby API for [Pushover](https://pushover.net).
|
|
5
4
|
|
|
6
5
|
## Installation
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
Install the gem:
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
```shell
|
|
10
|
+
gem install pushover
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or add it to your Gemfile and run `bundle install`:
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
```ruby
|
|
16
|
+
gem 'pushover'
|
|
17
|
+
```
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
## Ruby API
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
Create one client with your application token, then use its API resources:
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
```ruby
|
|
24
|
+
require 'pushover'
|
|
19
25
|
|
|
26
|
+
client = Pushover::Client.new(token: 'app-token')
|
|
27
|
+
|
|
28
|
+
response = client.messages.create(
|
|
29
|
+
user: 'user-or-group-key',
|
|
30
|
+
message: 'Deployment completed',
|
|
31
|
+
title: 'Production'
|
|
32
|
+
)
|
|
33
|
+
```
|
|
20
34
|
|
|
21
|
-
|
|
35
|
+
`messages.create` supports the Pushover Message API's JSON parameters:
|
|
22
36
|
|
|
23
|
-
|
|
37
|
+
- `user` and `message` are required.
|
|
38
|
+
- `device`, `title`, `url`, `url_title`, `priority`, `sound`, `timestamp`, and `ttl` are optional.
|
|
39
|
+
- `html` and `monospace` accept booleans or `1` and `0`, but cannot both be enabled.
|
|
40
|
+
- Emergency priority (`priority: 2`) requires `retry` and `expire`, and may include `callback` and `tags`.
|
|
41
|
+
- Attachments use `attachment_base64` with `attachment_type`, such as `image/png`, and are limited to 5 MiB after decoding.
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
To encrypt the supported message fields end to end, pass the 64-character hexadecimal key configured on the receiving devices:
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
45
|
+
```ruby
|
|
46
|
+
response = client.messages.create(
|
|
47
|
+
user: 'user-or-group-key',
|
|
48
|
+
message: 'Encrypted message',
|
|
49
|
+
encryption_key: ENV.fetch('PUSHOVER_ENCRYPTION_KEY')
|
|
50
|
+
)
|
|
51
|
+
```
|
|
29
52
|
|
|
30
|
-
|
|
53
|
+
Deterministic request errors raise `ArgumentError` before a network request. Pushover API errors, including HTTP 4xx responses, are returned as `Pushover::Response` objects:
|
|
31
54
|
|
|
55
|
+
```ruby
|
|
56
|
+
response.status # 0 or 1
|
|
57
|
+
response.request # Pushover request identifier
|
|
58
|
+
response.errors # API validation errors, if any
|
|
59
|
+
response.receipt # emergency message receipt, if any
|
|
60
|
+
response.headers # response headers, including application limits
|
|
61
|
+
response.attributes # other response fields
|
|
62
|
+
```
|
|
32
63
|
|
|
33
|
-
|
|
64
|
+
Retrieve the status of an emergency message receipt through the same client:
|
|
34
65
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
- setting timestamp
|
|
66
|
+
```ruby
|
|
67
|
+
response = client.receipts.get(receipt: 'AbCdEf0123456789GhIjKlMnOpQrSt')
|
|
68
|
+
```
|
|
39
69
|
|
|
70
|
+
Cancel future retries for an emergency message before it expires:
|
|
40
71
|
|
|
41
|
-
|
|
72
|
+
```ruby
|
|
73
|
+
response = client.receipts.cancel(receipt: 'AbCdEf0123456789GhIjKlMnOpQrSt')
|
|
74
|
+
```
|
|
42
75
|
|
|
43
|
-
|
|
44
|
-
require 'pushover'
|
|
76
|
+
Cancel future retries for all active emergency messages sent by the application with one tag:
|
|
45
77
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
78
|
+
```ruby
|
|
79
|
+
response = client.receipts.cancel_by_tag(tag: 'l=chicago')
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Receipt identifiers must contain exactly 30 alphanumeric characters. Receipt status fields are returned in `response.attributes`, including `acknowledged`, `acknowledged_at`, `acknowledged_by`, `acknowledged_by_device`, `last_delivered_at`, `expired`, `expires_at`, `called_back`, and `called_back_at`.
|
|
83
|
+
|
|
84
|
+
Cancellation stops future emergency-priority retries. `cancel_by_tag` accepts one nonempty tag and encodes it as a single URL path segment. Pushover permits receipt polling no more often than once every five seconds and retains receipt status for up to one week. The client does not enforce that interval or automatically retry requests. Connection failures continue to raise Excon transport exceptions.
|
|
85
|
+
|
|
86
|
+
Validate a user or group identifier, optionally for one device:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
response = client.users.validate(
|
|
90
|
+
user: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG',
|
|
91
|
+
device: 'droid2'
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`user` must be a 30-character alphanumeric user or group identifier. `device` is optional and, when supplied, must be 1 to 25 characters using letters, numbers, underscores, or hyphens. Without a device, Pushover validates that the account has at least one active device. Successful validation returns active device names in `response.attributes['devices']` and licensed platforms in `response.attributes['licenses']`. Invalid or inactive users and devices are returned as Pushover API errors in the response.
|
|
96
|
+
|
|
97
|
+
Retrieve the sounds available to the application account:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
response = client.sounds.get
|
|
101
|
+
available_sounds = response.attributes['sounds']
|
|
102
|
+
```
|
|
49
103
|
|
|
104
|
+
The sounds hash maps each value accepted by `messages.create(sound: ...)` to its display name. It includes Pushover's built-in sounds and custom sounds uploaded by the account that owns the application token. Omit `sound`, or pass a blank value, to use the recipient's default sound.
|
|
50
105
|
|
|
51
|
-
|
|
52
|
-
Pushover::Message.new(token: 'token', user: 'user_key', message: '...', 'priority': 2, expire: 1, retry: 60).push
|
|
106
|
+
Retrieve the current monthly message usage limits:
|
|
53
107
|
|
|
54
|
-
|
|
55
|
-
|
|
108
|
+
```ruby
|
|
109
|
+
response = client.limits.get
|
|
110
|
+
monthly_limit = response.attributes['limit']
|
|
111
|
+
remaining = response.attributes['remaining']
|
|
112
|
+
reset_at = Time.at(response.attributes['reset'])
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`limit` is the account or team's monthly allowance, including purchased capacity. `remaining` is the number of messages left, and `reset` is the Unix timestamp for the next reset. Despite the historical endpoint and header names, these values represent usage shared across all applications owned by the account or team.
|
|
116
|
+
|
|
117
|
+
Update one or more fields on a user's registered Glances widgets:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
response = client.glances.update(
|
|
121
|
+
user: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG',
|
|
122
|
+
title: 'Widgets Sold',
|
|
123
|
+
text: '30',
|
|
124
|
+
percent: 75
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`title`, `text`, and `subtext` accept up to 100 characters. `count` accepts any integer, including negative values, and `percent` accepts integers from 0 through 100. `device` may restrict the update to one device. At least one data field is required. Omitted data fields retain their previous values; pass an empty string to clear a field, including `count` or `percent`.
|
|
129
|
+
|
|
130
|
+
Glances updates do not create notifications and may take up to 10 minutes to appear. Pushover recommends at least 20 minutes between Apple Watch updates, and watchOS limits them to 50 per day. The client does not throttle updates.
|
|
131
|
+
|
|
132
|
+
Create an empty delivery group:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
response = client.groups.create(name: 'On-call West')
|
|
136
|
+
group_key = response.attributes['group']
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The application token may belong to any application on the account or team that will own the group. Group names must be unique within that account; Pushover validates uniqueness. The returned 30-character group key can be used as the `user` value when sending messages.
|
|
140
|
+
|
|
141
|
+
List the delivery groups owned by the application token's account or team:
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
response = client.groups.list
|
|
145
|
+
group_entries = response.attributes['groups']
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Each entry contains a `group` key and `name`. Pushover does not document a sort order or pagination contract, so callers should not depend on response ordering.
|
|
149
|
+
|
|
150
|
+
Retrieve a delivery group's name and memberships:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
response = client.groups.get(group: group_key)
|
|
154
|
+
group_name = response.attributes['name']
|
|
155
|
+
members = response.attributes['users']
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Each member contains `user`, `device`, `memo`, and `disabled`. A `device` of `nil` represents an unrestricted membership. Team members also include `name` and `email`. Pushover does not document membership ordering or pagination.
|
|
159
|
+
|
|
160
|
+
Manage group memberships and names:
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
client.groups.add_user(group: group_key, user: user_key, device: 'iphone', memo: 'Primary on-call')
|
|
164
|
+
client.groups.disable_user(group: group_key, user: user_key, device: 'iphone')
|
|
165
|
+
client.groups.enable_user(group: group_key, user: user_key, device: 'iphone')
|
|
166
|
+
client.groups.remove_user(group: group_key, user: user_key, device: 'iphone')
|
|
167
|
+
client.groups.rename(group: group_key, name: 'Operations')
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
An added membership may include one device and a memo of up to 200 characters. Omitting `device`, or passing it as blank, creates an all-device membership. For removal, disabling, and enabling, an omitted or blank device affects every membership matching the user key; pass a device to target only that membership. Pushover enforces group-name uniqueness on the owning account or team.
|
|
171
|
+
|
|
172
|
+
Optionally migrate an existing user key to an application subscription:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
response = client.subscriptions.migrate(
|
|
176
|
+
subscription: 'Forum-f504h08fhlasdfj',
|
|
177
|
+
user: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG',
|
|
178
|
+
device_name: 'droid2',
|
|
179
|
+
sound: 'pushover'
|
|
180
|
+
)
|
|
181
|
+
subscribed_user_key = response.attributes['subscribed_user_key']
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Pushover calls this user-key migration, but it is only needed when voluntarily adopting subscription-managed recipients for previously collected user keys. `subscription` must be a nonempty subscription code, and `user` must be a 30-character alphanumeric user key. `device_name` is optional and follows the standard device-name rules. `sound` may be any string because Pushover supports account-specific custom sounds; a blank sound selects the user's default. Pushover validates live subscriptions, users, devices, and sound availability.
|
|
185
|
+
|
|
186
|
+
Retrieve the application's available prepaid license credits, or permanently assign one credit:
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
credits = client.licenses.get.attributes['credits']
|
|
190
|
+
|
|
191
|
+
response = client.licenses.assign(
|
|
192
|
+
email: 'person@example.com',
|
|
193
|
+
os: 'Desktop'
|
|
194
|
+
)
|
|
195
|
+
remaining_credits = response.attributes['credits']
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`licenses.assign` requires a valid 30-character `user` key or a nonblank `email`; the gem does not invent an e-mail format beyond that documented requirement. `os` may be blank, `Android`, `iOS`, or `Desktop`. A blank or omitted value assigns the license to the first platform the user registers. Each call assigns one permanent, nonrefundable license. Assignment failures remain structured in `response.errors`, and the client never automatically retries the assignment.
|
|
199
|
+
|
|
200
|
+
Administer a Pushover for Teams organization with its Team API token:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
team = Pushover::Client.new(token: 'team-api-token').teams
|
|
204
|
+
|
|
205
|
+
response = team.get
|
|
206
|
+
team_name = response.attributes['name']
|
|
207
|
+
members = response.attributes['users']
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`client.teams` requires a Team API token, not an Application API Token. The client cannot distinguish the credential types locally, so Pushover returns an API error when the wrong token type is supplied. Team names, users, administrator flags, and nested device details are preserved in `response.attributes`.
|
|
211
|
+
|
|
212
|
+
Add a team user with only an e-mail address, or include the documented invitation and administration options:
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
response = team.add_user(
|
|
216
|
+
email: 'member@example.com',
|
|
217
|
+
name: 'Team Member',
|
|
218
|
+
password: '',
|
|
219
|
+
instant: true,
|
|
220
|
+
admin: false,
|
|
221
|
+
group: 'On-call'
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`email` must be a nonblank string. `name`, `password`, and `group` are optional strings. A blank or omitted password lets Pushover assign and e-mail a random password. `instant: true` requests a seven-day Instant Login link, and `admin: true` adds the user as an administrator. False flags are omitted. A named Delivery Group is created by Pushover if it does not exist; omitting `group` uses the auto-updating Team Delivery Group.
|
|
226
|
+
|
|
227
|
+
Revoke an invitation that has not been accepted, or remove a current team user:
|
|
228
|
+
|
|
229
|
+
```ruby
|
|
230
|
+
team.revoke_invitation(email: 'invitee@example.com')
|
|
231
|
+
team.remove_user(email: 'departing-member@example.com')
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Removing a user does not delete their Pushover account, but Pushover removes them from the team's Delivery Groups. Pushover also enforces that at least one administrator remains. Membership, invitation, and group-creation state are validated by Pushover.
|
|
235
|
+
|
|
236
|
+
### Legacy API compatibility
|
|
237
|
+
|
|
238
|
+
The former message and receipt interfaces remain available as deprecated compatibility wrappers, with no scheduled removal:
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
Pushover::Message.new(token: 'app-token', user: 'user-key', message: 'Hello').push
|
|
242
|
+
Pushover::Receipt.new(token: 'app-token', receipt: 'AbCdEf0123456789GhIjKlMnOpQrSt').get
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Calling `push` or `get` emits a deprecation warning naming the replacement and delegates to the shared client resources. New code should use `client.messages.create` and `client.receipts.get`. The legacy raw `attachment` keyword remains accepted for constructor compatibility, but non-nil values are rejected because they cannot be safely translated to the supported `attachment_base64` and `attachment_type` interface.
|
|
246
|
+
|
|
247
|
+
## CLI
|
|
248
|
+
|
|
249
|
+
Send a message:
|
|
250
|
+
|
|
251
|
+
```shell
|
|
252
|
+
pushover --token=app-token --user=user-key message here we go again
|
|
253
|
+
pushover -tapp-token -uuser-key message here we go again
|
|
254
|
+
```
|
|
56
255
|
|
|
57
|
-
|
|
58
|
-
response = Pushover::Message.new(token: 'token', user: 'user_key', message: '...').push
|
|
256
|
+
Get receipt details:
|
|
59
257
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
puts response.request # uuid of the request
|
|
63
|
-
puts response.errors # array of errors (if any)
|
|
64
|
-
puts response.receipt # receipt (if requested)
|
|
65
|
-
puts response.headers # response headers (includes limits)
|
|
66
|
-
puts response.attributes # any other k/v pair returned from pushover
|
|
258
|
+
```shell
|
|
259
|
+
pushover -tapp-token receipt AbCdEf0123456789GhIjKlMnOpQrSt
|
|
67
260
|
```
|
|
68
261
|
|
|
69
262
|
## Contributing
|
|
70
263
|
|
|
71
|
-
1. Fork
|
|
72
|
-
2.
|
|
73
|
-
3.
|
|
74
|
-
4. Commit your changes
|
|
75
|
-
5.
|
|
76
|
-
6. Create new Pull Request against `development`
|
|
264
|
+
1. Fork the repository.
|
|
265
|
+
2. Create a feature branch from `main`.
|
|
266
|
+
3. Run `bundle exec rake ci`.
|
|
267
|
+
4. Commit and push your changes.
|
|
268
|
+
5. Open a pull request against `main`.
|
data/Rakefile
CHANGED
|
@@ -1,63 +1,13 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
### Configurables
|
|
1
|
+
require 'bundler/gem_tasks'
|
|
2
|
+
require 'rspec/core/rake_task'
|
|
3
|
+
require 'rubocop/rake_task'
|
|
5
4
|
|
|
6
|
-
files = {
|
|
7
|
-
Gemfile: 'https://gist.githubusercontent.com/erniebrodeur/5a5518f5051210f1828a0712bf623dc8/raw',
|
|
8
|
-
Rakefile: 'https://gist.githubusercontent.com/erniebrodeur/afc92b72158413aa1f85d8d1facd267a/raw',
|
|
9
|
-
Rubocop: 'https://gist.githubusercontent.com/erniebrodeur/f7f63996ef1e017aee9bf9d8e680a1df/raw',
|
|
10
|
-
Tasks: 'https://gist.githubusercontent.com/erniebrodeur/03573fecf4f274101c14f6802abdbe83/raw',
|
|
11
|
-
BinAdd: 'https://gist.githubusercontent.com/erniebrodeur/a815ee4d8bd9ccc54d91025a5543ebb8/raw',
|
|
12
|
-
VsCodeFormatter: 'https://gist.githubusercontent.com/erniebrodeur/b24d757c0c625d108019eaceff2234cc/raw'
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
# spec
|
|
16
5
|
RSpec::Core::RakeTask.new(:spec)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# automagical updating
|
|
20
|
-
desc "updates for various bits of the development environment."
|
|
21
|
-
|
|
22
|
-
namespace :update do
|
|
23
|
-
desc "update everything (multitasked)"
|
|
24
|
-
multitask(all: %i[gemfile rakefile rubocop tasks vscode_formatter])
|
|
25
|
-
|
|
26
|
-
desc 'Update Gemfile from gist'
|
|
27
|
-
task :gemfile do
|
|
28
|
-
grab_file 'Gemfile', files[:Gemfile]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
desc 'Update Rakefile from gist'
|
|
32
|
-
task :rakefile do
|
|
33
|
-
grab_file 'Rakefile', files[:Rakefile]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
desc 'Update .rubocop.yml from gist'
|
|
37
|
-
task :rubocop do
|
|
38
|
-
grab_file '.rubocop.yml', files[:Rubocop]
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
desc 'Update .vscode/tasks.json from gist'
|
|
42
|
-
task :tasks do
|
|
43
|
-
mkdir_p '.vscode'
|
|
44
|
-
grab_file '.vscode/tasks.json', files[:Tasks]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
desc 'Update vscode_formatter.rb in spec_helper'
|
|
48
|
-
task :vscode_formatter do
|
|
49
|
-
mkdir_p 'spec'
|
|
50
|
-
grab_file 'spec/vscode_formatter.rb', files[:VsCodeFormatter]
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
desc 'Add a new lib and spec file (binary)'
|
|
54
|
-
task :binadd do
|
|
55
|
-
mkdir_p 'bin'
|
|
56
|
-
grab_file 'bin/add', files[:BinAdd]
|
|
57
|
-
end
|
|
6
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
7
|
+
task.options = ['--cache-root', 'tmp/rubocop_cache']
|
|
58
8
|
end
|
|
59
9
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
10
|
+
desc 'Run all continuous integration checks'
|
|
11
|
+
task ci: %i[spec rubocop]
|
|
12
|
+
|
|
13
|
+
task default: :ci
|
data/exe/pushover
CHANGED
|
@@ -4,7 +4,7 @@ require 'pushover'
|
|
|
4
4
|
|
|
5
5
|
include GLI::App
|
|
6
6
|
|
|
7
|
-
program_desc '
|
|
7
|
+
program_desc 'Send Pushover messages and retrieve receipts'
|
|
8
8
|
subcommand_option_handling :normal
|
|
9
9
|
|
|
10
10
|
flag %i[t token], type: String, desc: 'Application token'
|
|
@@ -21,16 +21,17 @@ command :message do |c|
|
|
|
21
21
|
c.flag :expire, type: String, desc: 'how long should a priority message live'
|
|
22
22
|
c.flag :retry, type: String, desc: 'how often should a resend be attempted (until acknowledged)'
|
|
23
23
|
c.action do |global_options, options, args|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
params = { message: args.join(" "), user: global_options[:user] }.merge(GLI::Options.new(options).to_h.compact)
|
|
25
|
+
%i[priority expire retry].each { |key| params[key] = params[key].to_i if params.key?(key) }
|
|
26
|
+
|
|
27
|
+
puts Pushover::Client.new(token: global_options[:token]).messages.create(**params)
|
|
27
28
|
end
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
desc 'get a receipt'
|
|
31
32
|
command :receipt do |c|
|
|
32
33
|
c.action do |global_options, _options, args|
|
|
33
|
-
puts Pushover::
|
|
34
|
+
puts Pushover::Client.new(token: global_options[:token]).receipts.get(receipt: args[0])
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Pushover
|
|
2
|
+
# Client holds shared application credentials and exposes API resources.
|
|
3
|
+
class Client
|
|
4
|
+
attr_reader :connection, :token
|
|
5
|
+
|
|
6
|
+
def initialize(token:, connection: Pushover::Excon)
|
|
7
|
+
raise ArgumentError, 'token must be supplied' unless token.is_a?(String) && !token.empty?
|
|
8
|
+
|
|
9
|
+
@token = token
|
|
10
|
+
@connection = connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def messages
|
|
14
|
+
@messages ||= Messages.new(self)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def receipts
|
|
18
|
+
@receipts ||= Receipts.new(self)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def users
|
|
22
|
+
@users ||= Users.new(self)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def sounds
|
|
26
|
+
@sounds ||= Sounds.new(self)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def limits
|
|
30
|
+
@limits ||= Limits.new(self)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def glances
|
|
34
|
+
@glances ||= Glances.new(self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def groups
|
|
38
|
+
@groups ||= Groups.new(self)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def subscriptions
|
|
42
|
+
@subscriptions ||= Subscriptions.new(self)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def licenses
|
|
46
|
+
@licenses ||= Licenses.new(self)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def teams
|
|
50
|
+
@teams ||= Teams.new(self)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|