omnisocials 0.2.0 → 0.3.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 +61 -0
- data/lib/omnisocials/resources/inbox.rb +14 -4
- data/lib/omnisocials/resources/posts.rb +25 -9
- data/lib/omnisocials/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0200cd0b7aa77ce95c037b0f5fd93a9e80b7bfdc589194b53c80084bb560b165
|
|
4
|
+
data.tar.gz: 37e48405dd0871201467442b940d2bc1dabd8057deddd81f903e25250bdb5cbc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 886f262e3a7eb8c2164f75845e8c67c3e5d86c2869974ffb290b69d8ebeb29b4f29cba11130d1722152236c3b865d9651194709fef35a24bc15b9eba4fe798a9
|
|
7
|
+
data.tar.gz: '078aba4be97a9fbcceef92fc3587059ea6e7cef7d760da25949bbc5eff37cc1ea3ff4fb59d47b8b5ec2ad12b532b5e7f4dace15572df90b8a42cffb57e8e75e2'
|
data/README.md
CHANGED
|
@@ -143,6 +143,26 @@ end
|
|
|
143
143
|
|
|
144
144
|
From `enforce_from` (2026-08-14) the balance is checked at publish time, but credits are only deducted after the post successfully publishes (a failed publish is never charged). If the balance can't cover it, only the X target fails (other platforms publish normally); top up in the dashboard under Settings -> Organisation -> Billing -> Credits, then call `posts.retry`. Posts without links, analytics, and media on X stay free. There is no API endpoint for credits — they are managed in the dashboard.
|
|
145
145
|
|
|
146
|
+
Separately, from 2026-08-14 `posts.create` / `posts.update` / `posts.publish` on X can refuse the request itself, up front, with a `402` and error code `x_credits_insufficient` (`error.details` carries `credits_required`, `credits_balance`, and `credits_reserved`) when reserving this post's cost would push the company's total reserved credits past its balance:
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
begin
|
|
150
|
+
client.posts.create(
|
|
151
|
+
content: "Read the full story: https://example.com/post",
|
|
152
|
+
channels: ["x"],
|
|
153
|
+
scheduled_at: "2026-08-20T09:00:00Z"
|
|
154
|
+
)
|
|
155
|
+
rescue OmniSocials::APIError => e
|
|
156
|
+
if e.code == "x_credits_insufficient"
|
|
157
|
+
puts "Not enough reserved credits: #{e.body["error"]["details"]}"
|
|
158
|
+
else
|
|
159
|
+
raise
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Drafts are never gated, and posts scheduled to publish before 2026-08-14 are never gated either.
|
|
165
|
+
|
|
146
166
|
### Other post operations
|
|
147
167
|
|
|
148
168
|
```ruby
|
|
@@ -310,6 +330,47 @@ client.posts.create(
|
|
|
310
330
|
)
|
|
311
331
|
```
|
|
312
332
|
|
|
333
|
+
## Inbox
|
|
334
|
+
|
|
335
|
+
Read and reply to social inbox conversations (DMs, comments, mentions) across connected platforms. Requires an API key with the opt-in `inbox:read` / `inbox:write` scopes. The list endpoints are cursor-paginated (unlike the offset pagination used elsewhere): page while `pagination["has_more"]` is true by passing the previous response's `pagination["next_cursor"]` as `cursor`.
|
|
336
|
+
|
|
337
|
+
```ruby
|
|
338
|
+
conversations = client.inbox.list_conversations(platform: "instagram", unread: true, limit: 20)
|
|
339
|
+
conversations["data"].each do |conversation|
|
|
340
|
+
puts "#{conversation["platform"]}: #{conversation["preview"]}"
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
conversation_id = conversations["data"][0]["id"]
|
|
344
|
+
messages = client.inbox.get_messages(conversation_id)
|
|
345
|
+
messages["data"].each do |message|
|
|
346
|
+
puts "#{message["direction"]}: #{message["text"]}" # direction is "incoming" or "outgoing"
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
client.inbox.mark_read(conversation_id)
|
|
350
|
+
client.inbox.reply(conversation_id, text: "Thanks for reaching out!")
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
`platform` accepts `"instagram"`, `"facebook"`, `"linkedin"`, `"tiktok"`, or `"x"`; `type` accepts `"dm"`, `"comment"`, or `"mention"`. TikTok replies are comments only and capped at 150 characters. Conversation ids are URL-encoded for you, so pass them exactly as returned - LinkedIn ids contain `":"` and `"()"` (e.g. `"linkedin_comment_urn:li:activity:123"`).
|
|
354
|
+
|
|
355
|
+
Replying to an X DM costs 2 prepaid credits, debited from the company balance before the send and automatically refunded if the send fails:
|
|
356
|
+
|
|
357
|
+
```ruby
|
|
358
|
+
begin
|
|
359
|
+
client.inbox.reply(conversation_id, text: "On it, thanks!")
|
|
360
|
+
rescue OmniSocials::APIError => e
|
|
361
|
+
case e.code
|
|
362
|
+
when "insufficient_credits"
|
|
363
|
+
puts "Not enough credits to send this reply (need 2)."
|
|
364
|
+
when "x_inbox_suspended"
|
|
365
|
+
puts "X inbox is suspended - top up and re-enable it in the dashboard."
|
|
366
|
+
else
|
|
367
|
+
raise
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
`x_inbox_suspended` fires once a workspace's X inbox has auto-suspended after its credit balance hit zero; topping up and re-enabling it in the dashboard resumes delivery, but DMs that arrived while suspended are not recovered.
|
|
373
|
+
|
|
313
374
|
## Webhooks
|
|
314
375
|
|
|
315
376
|
Subscribe to `post.scheduled`, `post.published`, and `post.failed` events:
|
|
@@ -23,9 +23,10 @@ module OmniSocials
|
|
|
23
23
|
# GET /inbox/conversations - list conversations, newest activity first.
|
|
24
24
|
#
|
|
25
25
|
# All filters are optional: platform ("instagram", "facebook",
|
|
26
|
-
# "linkedin"), type ("dm", "comment", "mention"),
|
|
27
|
-
# conversations with unread messages), limit (1-100), and
|
|
28
|
-
# opaque cursor from a previous response's
|
|
26
|
+
# "linkedin", "tiktok", "x"), type ("dm", "comment", "mention"),
|
|
27
|
+
# unread (only conversations with unread messages), limit (1-100), and
|
|
28
|
+
# cursor (an opaque cursor from a previous response's
|
|
29
|
+
# pagination["next_cursor"]).
|
|
29
30
|
def list_conversations(platform: nil, type: nil, unread: nil, limit: nil, cursor: nil)
|
|
30
31
|
@client.request(
|
|
31
32
|
"GET", "/inbox/conversations",
|
|
@@ -59,7 +60,16 @@ module OmniSocials
|
|
|
59
60
|
#
|
|
60
61
|
# `text` is required. Optionally attach a single media asset by public
|
|
61
62
|
# URL with `attachment_url` plus `attachment_type` ("image", "video",
|
|
62
|
-
# "audio", or "file"). Returns the created
|
|
63
|
+
# "audio", or "file"). Returns the created outgoing message.
|
|
64
|
+
#
|
|
65
|
+
# Replying to an X DM costs 2 prepaid credits, debited from the
|
|
66
|
+
# company balance before the send and automatically refunded if the
|
|
67
|
+
# send fails. Two 402 error codes are specific to this call:
|
|
68
|
+
# "insufficient_credits" when the balance can't cover the 2 credits,
|
|
69
|
+
# and "x_inbox_suspended" when the workspace's X inbox was
|
|
70
|
+
# auto-suspended after the balance hit zero (top up and re-enable it
|
|
71
|
+
# in the dashboard to resume; DMs that arrived while suspended are
|
|
72
|
+
# not recovered).
|
|
63
73
|
def reply(conversation_id, text:, attachment_url: nil, attachment_type: nil)
|
|
64
74
|
body = Internal.drop_nil(
|
|
65
75
|
{
|
|
@@ -57,6 +57,14 @@ module OmniSocials
|
|
|
57
57
|
# and credits_balance: X's link-post fee is passed through as prepaid
|
|
58
58
|
# credits, debited at publish time (from 2026-08-14). Credits are
|
|
59
59
|
# managed in the dashboard, not the API.
|
|
60
|
+
#
|
|
61
|
+
# Separately, from 2026-08-14 this call (and #update / #publish) can
|
|
62
|
+
# refuse an X link post up front with a 402 and error code
|
|
63
|
+
# "x_credits_insufficient" (details: credits_required, credits_balance,
|
|
64
|
+
# credits_reserved) when reserving this post's cost would push the
|
|
65
|
+
# company's total reserved credits past its balance. Drafts are never
|
|
66
|
+
# gated, and posts scheduled to publish before 2026-08-14 are never
|
|
67
|
+
# gated either.
|
|
60
68
|
def create(content:, channels: nil, scheduled_at: nil, media_ids: nil,
|
|
61
69
|
media_urls: nil, type: nil, source: nil, link_url: nil,
|
|
62
70
|
link_title: nil, link_description: nil, link_thumbnail_url: nil,
|
|
@@ -65,7 +73,7 @@ module OmniSocials
|
|
|
65
73
|
hashtag_platforms: nil, pinterest: nil, youtube: nil,
|
|
66
74
|
instagram: nil, facebook: nil, linkedin: nil,
|
|
67
75
|
linkedin_page: nil, tiktok: nil, x: nil, bluesky: nil,
|
|
68
|
-
mastodon: nil, google_business: nil)
|
|
76
|
+
mastodon: nil, google_business: nil, linkedin_poll: nil)
|
|
69
77
|
body = create_body(
|
|
70
78
|
content: content, channels: channels, scheduled_at: scheduled_at,
|
|
71
79
|
media_ids: media_ids, media_urls: media_urls, type: type,
|
|
@@ -78,13 +86,14 @@ module OmniSocials
|
|
|
78
86
|
youtube: youtube, instagram: instagram, facebook: facebook,
|
|
79
87
|
linkedin: linkedin, linkedin_page: linkedin_page, tiktok: tiktok,
|
|
80
88
|
x: x, bluesky: bluesky, mastodon: mastodon,
|
|
81
|
-
google_business: google_business
|
|
89
|
+
google_business: google_business, linkedin_poll: linkedin_poll
|
|
82
90
|
)
|
|
83
91
|
@client.request("POST", "/posts/create", json: body)
|
|
84
92
|
end
|
|
85
93
|
|
|
86
94
|
# POST /posts/create-and-publish - create and publish immediately.
|
|
87
|
-
# See #create for the "warnings" array
|
|
95
|
+
# See #create for the "warnings" array and the 402
|
|
96
|
+
# "x_credits_insufficient" credit gate on X link posts.
|
|
88
97
|
def create_and_publish(content:, channels: nil, media_ids: nil,
|
|
89
98
|
media_urls: nil, type: nil, source: nil,
|
|
90
99
|
link_url: nil, link_title: nil, link_description: nil,
|
|
@@ -95,7 +104,7 @@ module OmniSocials
|
|
|
95
104
|
pinterest: nil, youtube: nil, instagram: nil,
|
|
96
105
|
facebook: nil, linkedin: nil, linkedin_page: nil,
|
|
97
106
|
tiktok: nil, x: nil, bluesky: nil, mastodon: nil,
|
|
98
|
-
google_business: nil)
|
|
107
|
+
google_business: nil, linkedin_poll: nil)
|
|
99
108
|
body = create_body(
|
|
100
109
|
content: content, channels: channels, scheduled_at: nil,
|
|
101
110
|
media_ids: media_ids, media_urls: media_urls, type: type,
|
|
@@ -108,7 +117,7 @@ module OmniSocials
|
|
|
108
117
|
youtube: youtube, instagram: instagram, facebook: facebook,
|
|
109
118
|
linkedin: linkedin, linkedin_page: linkedin_page, tiktok: tiktok,
|
|
110
119
|
x: x, bluesky: bluesky, mastodon: mastodon,
|
|
111
|
-
google_business: google_business
|
|
120
|
+
google_business: google_business, linkedin_poll: linkedin_poll
|
|
112
121
|
)
|
|
113
122
|
@client.request("POST", "/posts/create-and-publish", json: body)
|
|
114
123
|
end
|
|
@@ -119,12 +128,15 @@ module OmniSocials
|
|
|
119
128
|
# x: { "thread_parts" => nil } still clears an X thread (reverts the
|
|
120
129
|
# post to single-tweet mode). The same applies to bluesky and mastodon
|
|
121
130
|
# thread parts.
|
|
131
|
+
#
|
|
132
|
+
# See #create for the 402 "x_credits_insufficient" credit gate that
|
|
133
|
+
# can also refuse an update to a scheduled X link post.
|
|
122
134
|
def update(post_id, content: nil, scheduled_at: nil, channels: nil,
|
|
123
135
|
media_ids: nil, media_urls: nil, type: nil, location_id: nil,
|
|
124
136
|
collaborators: nil, user_tags: nil, pinterest: nil,
|
|
125
137
|
youtube: nil, instagram: nil, facebook: nil, linkedin: nil,
|
|
126
138
|
linkedin_page: nil, tiktok: nil, x: nil, bluesky: nil,
|
|
127
|
-
mastodon: nil, google_business: nil)
|
|
139
|
+
mastodon: nil, google_business: nil, linkedin_poll: nil)
|
|
128
140
|
body = Internal.drop_nil(
|
|
129
141
|
{
|
|
130
142
|
"content" => content,
|
|
@@ -146,7 +158,8 @@ module OmniSocials
|
|
|
146
158
|
"x" => x,
|
|
147
159
|
"bluesky" => bluesky,
|
|
148
160
|
"mastodon" => mastodon,
|
|
149
|
-
"google_business" => google_business
|
|
161
|
+
"google_business" => google_business,
|
|
162
|
+
"linkedin_poll" => linkedin_poll
|
|
150
163
|
}
|
|
151
164
|
)
|
|
152
165
|
@client.request("PATCH", "/posts/#{post_id}", json: body)
|
|
@@ -158,6 +171,8 @@ module OmniSocials
|
|
|
158
171
|
end
|
|
159
172
|
|
|
160
173
|
# POST /posts/{id}/publish - publish a draft or scheduled post now.
|
|
174
|
+
# See #create for the 402 "x_credits_insufficient" credit gate that
|
|
175
|
+
# can also refuse publishing a scheduled X link post.
|
|
161
176
|
def publish(post_id)
|
|
162
177
|
@client.request("POST", "/posts/#{post_id}/publish")
|
|
163
178
|
end
|
|
@@ -182,7 +197,7 @@ module OmniSocials
|
|
|
182
197
|
hashtag_set_id:, hashtag_placement:, hashtag_platforms:,
|
|
183
198
|
pinterest:, youtube:, instagram:, facebook:, linkedin:,
|
|
184
199
|
linkedin_page:, tiktok:, x:, bluesky:, mastodon:,
|
|
185
|
-
google_business:)
|
|
200
|
+
google_business:, linkedin_poll:)
|
|
186
201
|
Internal.drop_nil(
|
|
187
202
|
{
|
|
188
203
|
"content" => content,
|
|
@@ -213,7 +228,8 @@ module OmniSocials
|
|
|
213
228
|
"x" => x,
|
|
214
229
|
"bluesky" => bluesky,
|
|
215
230
|
"mastodon" => mastodon,
|
|
216
|
-
"google_business" => google_business
|
|
231
|
+
"google_business" => google_business,
|
|
232
|
+
"linkedin_poll" => linkedin_poll
|
|
217
233
|
}
|
|
218
234
|
)
|
|
219
235
|
end
|
data/lib/omnisocials/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omnisocials
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OmniSocials
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Schedule and publish social media posts, upload media, and read analytics
|
|
14
14
|
across Instagram, Facebook, LinkedIn, YouTube, TikTok, X, Pinterest, Bluesky, Threads,
|