postproxy-sdk 1.1.0 → 1.4.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/README.md +102 -0
- data/lib/postproxy/client.rb +12 -0
- data/lib/postproxy/constants.rb +3 -1
- data/lib/postproxy/resources/posts.rb +26 -3
- data/lib/postproxy/resources/queues.rb +61 -0
- data/lib/postproxy/resources/webhooks.rb +59 -0
- data/lib/postproxy/types.rb +99 -2
- data/lib/postproxy/version.rb +1 -1
- data/lib/postproxy/webhook_signature.rb +28 -0
- data/lib/postproxy.rb +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8aa3207765c712cdc5887c3c94ae33010ec0e01f8487c867641929f84d8f08e7
|
|
4
|
+
data.tar.gz: 1f40051ca925f4eda94ce0883af44f456c564666afd4f4e766ef9040ff48e6db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a035631e203fdefda661eda488dd7384b4690e8dd490b430d35ed73d421e49a11927a7282ee489e197a16975a0874e8d95ec0337b347ef5b141c30c570f69301
|
|
7
|
+
data.tar.gz: 36e967ff4467ee46f261489cfc770ac09e3e0d2a092dc8faa6e6186585fd8b4df3cc9f3abc7db5cd2607befd3603715f835da647c1aa5213eebe63ab920fcfec
|
data/README.md
CHANGED
|
@@ -92,6 +92,17 @@ post = client.posts.create(
|
|
|
92
92
|
scheduled_at: (Time.now + 3600).iso8601
|
|
93
93
|
)
|
|
94
94
|
|
|
95
|
+
# Create a thread post
|
|
96
|
+
post = client.posts.create(
|
|
97
|
+
"Thread starts here",
|
|
98
|
+
profiles: ["prof-1"],
|
|
99
|
+
thread: [
|
|
100
|
+
{ body: "Second post in the thread" },
|
|
101
|
+
{ body: "Third with media", media: ["https://example.com/img.jpg"] },
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
post.thread.each { |child| puts "#{child.id}: #{child.body}" }
|
|
105
|
+
|
|
95
106
|
# Delete a post
|
|
96
107
|
client.posts.delete("post-id")
|
|
97
108
|
```
|
|
@@ -146,6 +157,97 @@ Stats vary by platform:
|
|
|
146
157
|
| TikTok | `impressions`, `likes`, `comments`, `shares` |
|
|
147
158
|
| Pinterest | `impressions`, `likes`, `comments`, `saved`, `outbound_clicks` |
|
|
148
159
|
|
|
160
|
+
## Queues
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
# List all queues
|
|
164
|
+
queues = client.queues.list.data
|
|
165
|
+
|
|
166
|
+
# Get a queue
|
|
167
|
+
queue = client.queues.get("queue-id")
|
|
168
|
+
|
|
169
|
+
# Get next available slot
|
|
170
|
+
next_slot = client.queues.next_slot("queue-id")
|
|
171
|
+
puts next_slot.next_slot
|
|
172
|
+
|
|
173
|
+
# Create a queue with timeslots
|
|
174
|
+
queue = client.queues.create(
|
|
175
|
+
"Morning Posts",
|
|
176
|
+
profile_group_id: "pg-abc",
|
|
177
|
+
description: "Weekday morning content",
|
|
178
|
+
timezone: "America/New_York",
|
|
179
|
+
jitter: 10,
|
|
180
|
+
timeslots: [
|
|
181
|
+
{ day: 1, time: "09:00" },
|
|
182
|
+
{ day: 2, time: "09:00" },
|
|
183
|
+
{ day: 3, time: "09:00" },
|
|
184
|
+
]
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Update a queue
|
|
188
|
+
queue = client.queues.update("queue-id",
|
|
189
|
+
jitter: 15,
|
|
190
|
+
timeslots: [
|
|
191
|
+
{ day: 6, time: "10:00" }, # add new timeslot
|
|
192
|
+
{ id: 1, _destroy: true }, # remove existing timeslot
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Pause/unpause a queue
|
|
197
|
+
client.queues.update("queue-id", enabled: false)
|
|
198
|
+
|
|
199
|
+
# Delete a queue
|
|
200
|
+
client.queues.delete("queue-id")
|
|
201
|
+
|
|
202
|
+
# Add a post to a queue
|
|
203
|
+
post = client.posts.create(
|
|
204
|
+
"This post will be scheduled by the queue",
|
|
205
|
+
profiles: ["prof-1"],
|
|
206
|
+
queue_id: "queue-id",
|
|
207
|
+
queue_priority: "high"
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Webhooks
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
# List webhooks
|
|
215
|
+
webhooks = client.webhooks.list.data
|
|
216
|
+
|
|
217
|
+
# Get a webhook
|
|
218
|
+
webhook = client.webhooks.get("wh-id")
|
|
219
|
+
|
|
220
|
+
# Create a webhook
|
|
221
|
+
webhook = client.webhooks.create(
|
|
222
|
+
"https://example.com/webhook",
|
|
223
|
+
events: ["post.published", "post.failed"],
|
|
224
|
+
description: "My webhook"
|
|
225
|
+
)
|
|
226
|
+
puts webhook.id, webhook.secret
|
|
227
|
+
|
|
228
|
+
# Update a webhook
|
|
229
|
+
webhook = client.webhooks.update("wh-id", events: ["post.published"], enabled: false)
|
|
230
|
+
|
|
231
|
+
# Delete a webhook
|
|
232
|
+
client.webhooks.delete("wh-id")
|
|
233
|
+
|
|
234
|
+
# List deliveries
|
|
235
|
+
deliveries = client.webhooks.deliveries("wh-id", page: 1, per_page: 10)
|
|
236
|
+
deliveries.data.each { |d| puts "#{d.event_type}: #{d.success}" }
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Signature verification
|
|
240
|
+
|
|
241
|
+
Verify incoming webhook signatures using HMAC-SHA256:
|
|
242
|
+
|
|
243
|
+
```ruby
|
|
244
|
+
PostProxy::WebhookSignature.verify(
|
|
245
|
+
payload: request.body.read,
|
|
246
|
+
signature_header: request.headers["X-PostProxy-Signature"],
|
|
247
|
+
secret: "whsec_..."
|
|
248
|
+
)
|
|
249
|
+
```
|
|
250
|
+
|
|
149
251
|
## Profiles
|
|
150
252
|
|
|
151
253
|
```ruby
|
data/lib/postproxy/client.rb
CHANGED
|
@@ -4,6 +4,8 @@ require "json"
|
|
|
4
4
|
require_relative "resources/posts"
|
|
5
5
|
require_relative "resources/profiles"
|
|
6
6
|
require_relative "resources/profile_groups"
|
|
7
|
+
require_relative "resources/webhooks"
|
|
8
|
+
require_relative "resources/queues"
|
|
7
9
|
|
|
8
10
|
module PostProxy
|
|
9
11
|
class Client
|
|
@@ -17,6 +19,8 @@ module PostProxy
|
|
|
17
19
|
@posts = nil
|
|
18
20
|
@profiles = nil
|
|
19
21
|
@profile_groups = nil
|
|
22
|
+
@webhooks = nil
|
|
23
|
+
@queues = nil
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
def posts
|
|
@@ -31,6 +35,14 @@ module PostProxy
|
|
|
31
35
|
@profile_groups ||= Resources::ProfileGroups.new(self)
|
|
32
36
|
end
|
|
33
37
|
|
|
38
|
+
def webhooks
|
|
39
|
+
@webhooks ||= Resources::Webhooks.new(self)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def queues
|
|
43
|
+
@queues ||= Resources::Queues.new(self)
|
|
44
|
+
end
|
|
45
|
+
|
|
34
46
|
def request(method, path, params: nil, json: nil, data: nil, files: nil, profile_group_id: nil)
|
|
35
47
|
url = "/api#{path}"
|
|
36
48
|
|
data/lib/postproxy/constants.rb
CHANGED
|
@@ -7,7 +7,9 @@ module PostProxy
|
|
|
7
7
|
|
|
8
8
|
PROFILE_STATUSES = %w[active expired inactive].freeze
|
|
9
9
|
|
|
10
|
-
POST_STATUSES = %w[pending draft processing processed scheduled].freeze
|
|
10
|
+
POST_STATUSES = %w[pending draft processing processed scheduled media_processing_failed].freeze
|
|
11
|
+
|
|
12
|
+
MEDIA_STATUSES = %w[pending processed failed].freeze
|
|
11
13
|
|
|
12
14
|
PLATFORM_POST_STATUSES = %w[pending processing published failed deleted].freeze
|
|
13
15
|
|
|
@@ -29,8 +29,12 @@ module PostProxy
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def create(body, profiles:, media: nil, media_files: nil, platforms: nil,
|
|
32
|
-
scheduled_at: nil, draft: nil,
|
|
33
|
-
|
|
32
|
+
thread: nil, scheduled_at: nil, draft: nil, queue_id: nil,
|
|
33
|
+
queue_priority: nil, profile_group_id: nil)
|
|
34
|
+
has_files = media_files && !media_files.empty?
|
|
35
|
+
has_thread_files = thread&.any? { |t| t[:media_files]&.any? }
|
|
36
|
+
|
|
37
|
+
if has_files || has_thread_files
|
|
34
38
|
form_data = { "post[body]" => body }
|
|
35
39
|
form_data["post[scheduled_at]"] = format_time(scheduled_at) if scheduled_at
|
|
36
40
|
form_data["post[draft]"] = draft.to_s if !draft.nil?
|
|
@@ -54,7 +58,7 @@ module PostProxy
|
|
|
54
58
|
end
|
|
55
59
|
end
|
|
56
60
|
|
|
57
|
-
media_files
|
|
61
|
+
media_files&.each do |path|
|
|
58
62
|
path = path.to_s
|
|
59
63
|
filename = File.basename(path)
|
|
60
64
|
content_type = mime_type_for(filename)
|
|
@@ -62,6 +66,22 @@ module PostProxy
|
|
|
62
66
|
files << ["media[]", filename, io, content_type]
|
|
63
67
|
end
|
|
64
68
|
|
|
69
|
+
thread&.each_with_index do |t, i|
|
|
70
|
+
form_data["thread[#{i}][body]"] = t[:body] if t[:body]
|
|
71
|
+
|
|
72
|
+
t[:media]&.each do |m|
|
|
73
|
+
files << ["thread[#{i}][media][]", nil, m, "text/plain"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
t[:media_files]&.each do |path|
|
|
77
|
+
path = path.to_s
|
|
78
|
+
filename = File.basename(path)
|
|
79
|
+
content_type = mime_type_for(filename)
|
|
80
|
+
io = File.open(path, "rb")
|
|
81
|
+
files << ["thread[#{i}][media][]", filename, io, content_type]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
65
85
|
result = @client.request(:post, "/posts",
|
|
66
86
|
data: form_data,
|
|
67
87
|
files: files,
|
|
@@ -75,6 +95,9 @@ module PostProxy
|
|
|
75
95
|
json_body = { post: post_payload, profiles: profiles }
|
|
76
96
|
json_body[:platforms] = platforms.is_a?(PlatformParams) ? platforms.to_h : platforms if platforms
|
|
77
97
|
json_body[:media] = media if media
|
|
98
|
+
json_body[:thread] = thread if thread
|
|
99
|
+
json_body[:queue_id] = queue_id if queue_id
|
|
100
|
+
json_body[:queue_priority] = queue_priority if queue_priority
|
|
78
101
|
|
|
79
102
|
result = @client.request(:post, "/posts", json: json_body, profile_group_id: profile_group_id)
|
|
80
103
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module PostProxy
|
|
2
|
+
module Resources
|
|
3
|
+
class Queues
|
|
4
|
+
def initialize(client)
|
|
5
|
+
@client = client
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def list(profile_group_id: nil)
|
|
9
|
+
result = @client.request(:get, "/post_queues", profile_group_id: profile_group_id)
|
|
10
|
+
queues = (result[:data] || []).map { |q| Queue.new(**q) }
|
|
11
|
+
ListResponse.new(data: queues)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(id)
|
|
15
|
+
result = @client.request(:get, "/post_queues/#{id}")
|
|
16
|
+
Queue.new(**result)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def next_slot(id)
|
|
20
|
+
result = @client.request(:get, "/post_queues/#{id}/next_slot")
|
|
21
|
+
NextSlotResponse.new(**result)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create(name, profile_group_id:, description: nil, timezone: nil, jitter: nil, timeslots: nil)
|
|
25
|
+
post_queue = { name: name }
|
|
26
|
+
post_queue[:description] = description if description
|
|
27
|
+
post_queue[:timezone] = timezone if timezone
|
|
28
|
+
post_queue[:jitter] = jitter unless jitter.nil?
|
|
29
|
+
post_queue[:queue_timeslots_attributes] = timeslots if timeslots
|
|
30
|
+
|
|
31
|
+
json_body = {
|
|
32
|
+
profile_group_id: profile_group_id,
|
|
33
|
+
post_queue: post_queue,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
result = @client.request(:post, "/post_queues", json: json_body)
|
|
37
|
+
Queue.new(**result)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def update(id, name: nil, description: nil, timezone: nil, enabled: nil, jitter: nil, timeslots: nil)
|
|
41
|
+
post_queue = {}
|
|
42
|
+
post_queue[:name] = name unless name.nil?
|
|
43
|
+
post_queue[:description] = description unless description.nil?
|
|
44
|
+
post_queue[:timezone] = timezone unless timezone.nil?
|
|
45
|
+
post_queue[:enabled] = enabled unless enabled.nil?
|
|
46
|
+
post_queue[:jitter] = jitter unless jitter.nil?
|
|
47
|
+
post_queue[:queue_timeslots_attributes] = timeslots if timeslots
|
|
48
|
+
|
|
49
|
+
json_body = { post_queue: post_queue }
|
|
50
|
+
|
|
51
|
+
result = @client.request(:patch, "/post_queues/#{id}", json: json_body)
|
|
52
|
+
Queue.new(**result)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def delete(id)
|
|
56
|
+
result = @client.request(:delete, "/post_queues/#{id}")
|
|
57
|
+
DeleteResponse.new(**result)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module PostProxy
|
|
2
|
+
module Resources
|
|
3
|
+
class Webhooks
|
|
4
|
+
def initialize(client)
|
|
5
|
+
@client = client
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def list
|
|
9
|
+
result = @client.request(:get, "/webhooks")
|
|
10
|
+
webhooks = (result[:data] || []).map { |w| Webhook.new(**w) }
|
|
11
|
+
ListResponse.new(data: webhooks)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(id)
|
|
15
|
+
result = @client.request(:get, "/webhooks/#{id}")
|
|
16
|
+
Webhook.new(**result)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create(url, events:, description: nil)
|
|
20
|
+
json_body = { url: url, events: events }
|
|
21
|
+
json_body[:description] = description if description
|
|
22
|
+
|
|
23
|
+
result = @client.request(:post, "/webhooks", json: json_body)
|
|
24
|
+
Webhook.new(**result)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update(id, url: nil, events: nil, enabled: nil, description: nil)
|
|
28
|
+
json_body = {}
|
|
29
|
+
json_body[:url] = url unless url.nil?
|
|
30
|
+
json_body[:events] = events unless events.nil?
|
|
31
|
+
json_body[:enabled] = enabled unless enabled.nil?
|
|
32
|
+
json_body[:description] = description unless description.nil?
|
|
33
|
+
|
|
34
|
+
result = @client.request(:patch, "/webhooks/#{id}", json: json_body)
|
|
35
|
+
Webhook.new(**result)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def delete(id)
|
|
39
|
+
result = @client.request(:delete, "/webhooks/#{id}")
|
|
40
|
+
DeleteResponse.new(**result)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def deliveries(id, page: nil, per_page: nil)
|
|
44
|
+
params = {}
|
|
45
|
+
params[:page] = page if page
|
|
46
|
+
params[:per_page] = per_page if per_page
|
|
47
|
+
|
|
48
|
+
result = @client.request(:get, "/webhooks/#{id}/deliveries", params: params)
|
|
49
|
+
deliveries = (result[:data] || []).map { |d| WebhookDelivery.new(**d) }
|
|
50
|
+
PaginatedResponse.new(
|
|
51
|
+
data: deliveries,
|
|
52
|
+
total: result[:total],
|
|
53
|
+
page: result[:page],
|
|
54
|
+
per_page: result[:per_page]
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/postproxy/types.rb
CHANGED
|
@@ -84,18 +84,115 @@ module PostProxy
|
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
class Media < Model
|
|
88
|
+
attr_accessor :id, :status, :error_message, :content_type, :source_url, :url
|
|
89
|
+
|
|
90
|
+
def initialize(**attrs)
|
|
91
|
+
@error_message = nil
|
|
92
|
+
@source_url = nil
|
|
93
|
+
@url = nil
|
|
94
|
+
super
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class ThreadChild < Model
|
|
99
|
+
attr_accessor :id, :body, :media
|
|
100
|
+
|
|
101
|
+
def initialize(**attrs)
|
|
102
|
+
@media = []
|
|
103
|
+
super
|
|
104
|
+
@media = (@media || []).map do |m|
|
|
105
|
+
m.is_a?(Media) ? m : Media.new(**m.transform_keys(&:to_sym))
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class Timeslot < Model
|
|
111
|
+
attr_accessor :id, :day, :time
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Queue < Model
|
|
115
|
+
attr_accessor :id, :name, :description, :timezone, :enabled, :jitter,
|
|
116
|
+
:profile_group_id, :timeslots, :posts_count
|
|
117
|
+
|
|
118
|
+
def initialize(**attrs)
|
|
119
|
+
@description = nil
|
|
120
|
+
@timeslots = []
|
|
121
|
+
@posts_count = 0
|
|
122
|
+
super
|
|
123
|
+
@timeslots = (@timeslots || []).map do |t|
|
|
124
|
+
t.is_a?(Timeslot) ? t : Timeslot.new(**t.transform_keys(&:to_sym))
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class NextSlotResponse < Model
|
|
130
|
+
attr_accessor :next_slot
|
|
131
|
+
end
|
|
132
|
+
|
|
87
133
|
class Post < Model
|
|
88
|
-
attr_accessor :id, :body, :status, :scheduled_at, :created_at, :platforms
|
|
134
|
+
attr_accessor :id, :body, :status, :scheduled_at, :created_at, :media, :platforms, :thread,
|
|
135
|
+
:queue_id, :queue_priority
|
|
89
136
|
|
|
90
137
|
def initialize(**attrs)
|
|
91
138
|
@scheduled_at = nil
|
|
139
|
+
@media = []
|
|
92
140
|
@platforms = []
|
|
141
|
+
@thread = []
|
|
142
|
+
@queue_id = nil
|
|
143
|
+
@queue_priority = nil
|
|
93
144
|
super
|
|
94
145
|
@scheduled_at = parse_time(@scheduled_at)
|
|
95
146
|
@created_at = parse_time(@created_at)
|
|
147
|
+
@media = (@media || []).map do |m|
|
|
148
|
+
m.is_a?(Media) ? m : Media.new(**m.transform_keys(&:to_sym))
|
|
149
|
+
end
|
|
96
150
|
@platforms = (@platforms || []).map do |p|
|
|
97
151
|
p.is_a?(PlatformResult) ? p : PlatformResult.new(**p.transform_keys(&:to_sym))
|
|
98
152
|
end
|
|
153
|
+
@thread = (@thread || []).map do |t|
|
|
154
|
+
t.is_a?(ThreadChild) ? t : ThreadChild.new(**t.transform_keys(&:to_sym))
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def parse_time(value)
|
|
161
|
+
return nil if value.nil?
|
|
162
|
+
value.is_a?(Time) ? value : Time.parse(value.to_s)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class Webhook < Model
|
|
167
|
+
attr_accessor :id, :url, :events, :enabled, :description, :secret,
|
|
168
|
+
:created_at, :updated_at
|
|
169
|
+
|
|
170
|
+
def initialize(**attrs)
|
|
171
|
+
@events = []
|
|
172
|
+
@description = nil
|
|
173
|
+
@secret = nil
|
|
174
|
+
super
|
|
175
|
+
@created_at = parse_time(@created_at)
|
|
176
|
+
@updated_at = parse_time(@updated_at)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
def parse_time(value)
|
|
182
|
+
return nil if value.nil?
|
|
183
|
+
value.is_a?(Time) ? value : Time.parse(value.to_s)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class WebhookDelivery < Model
|
|
188
|
+
attr_accessor :id, :event_id, :event_type, :response_status,
|
|
189
|
+
:attempt_number, :success, :attempted_at, :created_at
|
|
190
|
+
|
|
191
|
+
def initialize(**attrs)
|
|
192
|
+
@response_status = nil
|
|
193
|
+
super
|
|
194
|
+
@attempted_at = parse_time(@attempted_at)
|
|
195
|
+
@created_at = parse_time(@created_at)
|
|
99
196
|
end
|
|
100
197
|
|
|
101
198
|
private
|
|
@@ -213,7 +310,7 @@ module PostProxy
|
|
|
213
310
|
end
|
|
214
311
|
|
|
215
312
|
class YouTubeParams < Model
|
|
216
|
-
attr_accessor :format, :title, :privacy_status, :cover_url
|
|
313
|
+
attr_accessor :format, :title, :privacy_status, :cover_url, :made_for_kids
|
|
217
314
|
end
|
|
218
315
|
|
|
219
316
|
class PinterestParams < Model
|
data/lib/postproxy/version.rb
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "openssl"
|
|
2
|
+
|
|
3
|
+
module PostProxy
|
|
4
|
+
module WebhookSignature
|
|
5
|
+
def self.verify(payload, signature_header, secret)
|
|
6
|
+
parts = signature_header.split(",").map { |p| p.split("=", 2) }.to_h
|
|
7
|
+
timestamp = parts["t"]
|
|
8
|
+
expected = parts["v1"]
|
|
9
|
+
|
|
10
|
+
return false if timestamp.nil? || expected.nil?
|
|
11
|
+
|
|
12
|
+
signed_payload = "#{timestamp}.#{payload}"
|
|
13
|
+
computed = OpenSSL::HMAC.hexdigest("SHA256", secret, signed_payload)
|
|
14
|
+
|
|
15
|
+
secure_compare(computed, expected)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private_class_method def self.secure_compare(a, b)
|
|
19
|
+
return false unless a.bytesize == b.bytesize
|
|
20
|
+
|
|
21
|
+
l = a.unpack("C*")
|
|
22
|
+
r = b.unpack("C*")
|
|
23
|
+
result = 0
|
|
24
|
+
l.zip(r) { |x, y| result |= x ^ y }
|
|
25
|
+
result.zero?
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/postproxy.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: postproxy-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- PostProxy
|
|
@@ -81,8 +81,11 @@ files:
|
|
|
81
81
|
- lib/postproxy/resources/posts.rb
|
|
82
82
|
- lib/postproxy/resources/profile_groups.rb
|
|
83
83
|
- lib/postproxy/resources/profiles.rb
|
|
84
|
+
- lib/postproxy/resources/queues.rb
|
|
85
|
+
- lib/postproxy/resources/webhooks.rb
|
|
84
86
|
- lib/postproxy/types.rb
|
|
85
87
|
- lib/postproxy/version.rb
|
|
88
|
+
- lib/postproxy/webhook_signature.rb
|
|
86
89
|
homepage: https://postproxy.dev
|
|
87
90
|
licenses:
|
|
88
91
|
- MIT
|