fopost 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/CHANGELOG.md +169 -0
- data/README.md +593 -3
- data/lib/fopost/client.rb +29 -1
- data/lib/fopost/errors.rb +25 -0
- data/lib/fopost/file_part.rb +73 -0
- data/lib/fopost/http/client.rb +36 -4
- data/lib/fopost/http/multipart.rb +38 -0
- data/lib/fopost/models.rb +1855 -28
- data/lib/fopost/platforms.rb +27 -0
- data/lib/fopost/resources/account_groups.rb +37 -0
- data/lib/fopost/resources/accounts.rb +445 -3
- data/lib/fopost/resources/activity.rb +27 -0
- data/lib/fopost/resources/ads.rb +910 -7
- data/lib/fopost/resources/analytics.rb +63 -0
- data/lib/fopost/resources/blog.rb +206 -0
- data/lib/fopost/resources/blogs.rb +115 -0
- data/lib/fopost/resources/broadcasts.rb +189 -0
- data/lib/fopost/resources/contacts.rb +136 -0
- data/lib/fopost/resources/google_ads.rb +346 -0
- data/lib/fopost/resources/google_business.rb +166 -0
- data/lib/fopost/resources/inbox.rb +73 -7
- data/lib/fopost/resources/knowledge.rb +74 -0
- data/lib/fopost/resources/media.rb +56 -0
- data/lib/fopost/resources/posts.rb +75 -4
- data/lib/fopost/resources/validate.rb +37 -0
- data/lib/fopost/resources/whatsapp.rb +255 -0
- data/lib/fopost/version.rb +1 -1
- data/lib/fopost/webhooks.rb +97 -0
- data/lib/fopost.rb +2 -0
- metadata +17 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70877df67981ee15f3e6d672df5edad494ae08fb8ff412fd48814366fb1e3012
|
|
4
|
+
data.tar.gz: c592df81ea3019c84d7effb9a15ddfc868d3e111d32d2ee8f3e2db8503bdf4da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: beee8ec7c7d035cd0bf7a824e683df0a0b4c6ef469b5db38fa6b6c0a3b2d2c2429269dd305f473c50cd5af8070e7f9c1b594560416cf6d210fe62621700919dc
|
|
7
|
+
data.tar.gz: 54240d5ec9aa83a78553c7c5d32f19d112e0079d48b214d62d9f15f60d2d8ea4044a9bb4810b5669dc75ac9e02f39115c433fb784584b2365f5a88a3e16f016d
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,173 @@ All notable changes to this gem are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the gem follows
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.0] - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `inbox.like`, `unlike`, `pin`, `unpin`, `react`, `edit_comment`, `start_conversation` and
|
|
12
|
+
`set_typing` (`inbox` scope, plus `publish`).
|
|
13
|
+
- `inbox.reply` takes `media_ids` and `quick_replies`; `text` is optional when `media_ids` is given.
|
|
14
|
+
- `InboxItem` reads `liked`, `pinned`, `reaction`, `edited_at` and the `can_like`, `can_pin`,
|
|
15
|
+
`can_edit`, `can_react`, `can_send_media`, `can_quick_reply` and `can_private_reply` flags;
|
|
16
|
+
`InboxAccount` reads `can_start_conversation`.
|
|
17
|
+
- `validate` resource: `post`, `length` and `media` preflight checks (`posts` scope).
|
|
18
|
+
- `account_groups` resource: `list`, `create`, `get`, `update`, `delete` and `set_members`
|
|
19
|
+
(`accounts` scope).
|
|
20
|
+
- `accounts.update` renames an account and `accounts.move` moves it to another workspace;
|
|
21
|
+
`accounts.list` takes `group_id`, and `SocialAccount#platform_name` is read.
|
|
22
|
+
- `posts.create` takes `account_group_id`.
|
|
23
|
+
- `media.presign`, `complete` and `upload_direct` upload a file straight to storage instead of
|
|
24
|
+
through the API (`posts` scope).
|
|
25
|
+
- `ads` campaign tree: `account_tree`, and create, get, update, delete and duplicate for
|
|
26
|
+
campaigns, ad sets and network ads, plus `bulk_set_status` (`ads` scope; writes also need
|
|
27
|
+
`publish`).
|
|
28
|
+
- `ads` creatives (`creatives`, `create_creative`, `get_creative`, `delete_creative`), audiences
|
|
29
|
+
(`get_audience`, `update_audience`, `delete_audience`, `add_audience_users`), `estimate_reach`,
|
|
30
|
+
`insights` and `ad_insights`.
|
|
31
|
+
- `ads` lead forms and leads: `get_lead_form`, `archive_lead_form`, `leads_feed` (cursor
|
|
32
|
+
paginated), `lead_pages`, `subscribe_lead_page` and `unsubscribe_lead_page`.
|
|
33
|
+
- `ads.create` takes `url_tags`.
|
|
34
|
+
- `blog.suggest_seeds(workspace_id, seeds:, prompt:)` suggests about 10 search terms from the
|
|
35
|
+
blog goal and topics (needs `ai`, 402 on an empty balance), and `blog.submit_article` moves a
|
|
36
|
+
draft, failed or rejected article to `in_review` (needs `posts`, 409 otherwise).
|
|
37
|
+
- `BlogSettings#goal`, `#focus_topics`, `#avoid_topics` and `#plan_run` (`BlogPlanRun`: `status`,
|
|
38
|
+
`error`, `added`, `finished_at`); `update_settings` takes `goal:`, `focus_topics:` and
|
|
39
|
+
`avoid_topics:`. `BlogKeyword#fit_reason` says why a keyword serves the goal.
|
|
40
|
+
- `in_review` on `BLOG_ARTICLE_STATUSES`.
|
|
41
|
+
- `whatsapp` resource for a WhatsApp Business connection: the business profile
|
|
42
|
+
(`profile`, `update_profile`, `request_display_name`, `set_username`), message
|
|
43
|
+
templates including the platform's own library (`templates`, `create_template`,
|
|
44
|
+
`import_template`, …), groups, blocking, commerce settings and flows
|
|
45
|
+
(`create_flow`, `upload_flow_json`, `publish_flow`, `flow_responses`, …), plus
|
|
46
|
+
`account_events`. All need the `accounts` scope.
|
|
47
|
+
- `whatsapp.create_sandbox_session` and `sandbox_sessions` invite a tester to the
|
|
48
|
+
platform-owned WhatsApp test number. Inviting sends a template, so it needs the
|
|
49
|
+
`publish` scope.
|
|
50
|
+
- `whatsapp` on `Fopost::PLATFORMS`.
|
|
51
|
+
- `client.blog`: articles for a website. Settings (`get_settings`, `update_settings`,
|
|
52
|
+
`rotate_website_secret`, `test_website`), keywords (`list_keywords`, `plan_keywords`,
|
|
53
|
+
`add_keyword`, `update_keyword`, `delete_keyword`) and articles (`list_articles`,
|
|
54
|
+
`request_article`, `get_article`, `update_article`, `regenerate_article`, `publish_article`,
|
|
55
|
+
`reject_article`, `delete_article`), with the `BlogSettings`, `BlogSettingsUpdate`,
|
|
56
|
+
`BlogWebsiteTest`, `BlogKeyword`, `BlogArticle`, `BlogArticleFull` and `BlogArticleImage`
|
|
57
|
+
models and the `BLOG_ARTICLE_STATUSES`, `BLOG_KEYWORD_STATUSES` and `BLOG_TARGET_KINDS`
|
|
58
|
+
constants. Reads need `posts`; `plan_keywords`, `request_article` and `regenerate_article`
|
|
59
|
+
need `ai` and answer 402 on an empty balance; `publish_article` needs `publish` and answers
|
|
60
|
+
409 `no_target` with no target set.
|
|
61
|
+
- `Fopost::Webhooks.verify(raw_body, headers, secret)` checks the signature on any FoPost
|
|
62
|
+
webhook over the raw bytes in constant time, preferring the timestamped signature (300 s
|
|
63
|
+
tolerance) and falling back to the plain body signature only when no timestamp was sent.
|
|
64
|
+
It returns a `WebhookEvent` (`event`, `data`, `timestamp`); `#article` types the
|
|
65
|
+
`article.published` payload as `BlogArticlePublished`. A bad signature raises
|
|
66
|
+
`Fopost::WebhookSignatureError`.
|
|
67
|
+
|
|
68
|
+
- `media.limits` reads the account's upload ceilings (`UploadLimits`: `max_file_bytes`,
|
|
69
|
+
`multipart_max_file_bytes`, `max_files_per_upload`, `next_tier`, `allowed_types`).
|
|
70
|
+
- `posts.create` and `posts.update` take `files:` (paths, IOs, or
|
|
71
|
+
`{ filename:, io:/data:, mime_type: }` hashes). Files at or under the multipart ceiling go
|
|
72
|
+
in one multipart request; larger ones go through direct upload and are attached to the first
|
|
73
|
+
block.
|
|
74
|
+
- `media.presign` and `upload_direct` take `folder_id`, `origin`, `brand_role`, `brand_voice_id`
|
|
75
|
+
and `person_consent`; `size` may go up to the account's `max_file_bytes`, which
|
|
76
|
+
`PresignedUpload#max_file_bytes` reports.
|
|
77
|
+
- `Fopost::FileTooLargeError` for a 413 `file_too_large`, with `max_file_bytes`, `file_bytes`
|
|
78
|
+
and `upgrade_url`.
|
|
79
|
+
|
|
80
|
+
- `InboxItem#moderation_status` carries the platform's own state for a comment
|
|
81
|
+
(`published`, `held`, `spam`, `rejected`), and `InboxAccount#reconnect_required`
|
|
82
|
+
flags an account connected before the inbox asked for a permission it needs.
|
|
83
|
+
- `client.broadcasts`: one message into every conversation the workspace already has
|
|
84
|
+
with a segment of its contacts. `list`, `get`, `create`, `update`, `delete`, `send`,
|
|
85
|
+
`cancel`, `recipients`. Reading needs the `inbox` scope; `send` and `cancel` also need
|
|
86
|
+
`publish`.
|
|
87
|
+
- `client.sequences`: a series of messages on a delay. `list`, `get`, `create`, `update`,
|
|
88
|
+
`delete`, `enroll`, `unenroll`, `enrollments`. `enroll` and `unenroll` need `publish`.
|
|
89
|
+
- Both honour each network's messaging window server-side. Messenger and Instagram take a
|
|
90
|
+
business-initiated message only within 24 hours of the contact's last one, so recipients
|
|
91
|
+
outside it come back `skipped` with `skip_reason` `window_closed` and nothing is
|
|
92
|
+
attempted — the number sent is often lower than the audience.
|
|
93
|
+
|
|
94
|
+
- `contacts` resource: the people behind the inbox. `list`, `get`, `create`, `update`,
|
|
95
|
+
`delete`, `conversations` (the threads one person appears in), `import` (CSV), and
|
|
96
|
+
`list_fields` / `create_field` / `update_field` / `delete_field` for the custom columns
|
|
97
|
+
a workspace keeps. All need the `inbox` scope.
|
|
98
|
+
- `contacts.conversation_analytics` reads `/v1/analytics/inbox/conversations`: volume and
|
|
99
|
+
median reply time per thread. Needs the `analytics` scope.
|
|
100
|
+
- `snapchat` added to `Fopost::PLATFORMS`.
|
|
101
|
+
|
|
102
|
+
- `accounts.create_telegram_connect_code`, `get_telegram_connect_status`, `get_telegram_bot_commands`,
|
|
103
|
+
`set_telegram_bot_commands` and `delete_telegram_bot_commands` (`accounts` scope).
|
|
104
|
+
- Meta messaging settings on `accounts`: `get_ice_breakers`, `set_ice_breakers` and
|
|
105
|
+
`delete_ice_breakers` (Facebook Pages and Instagram), plus `get_persistent_menu`,
|
|
106
|
+
`set_persistent_menu`, `delete_persistent_menu`, `get_greeting`, `set_greeting` and
|
|
107
|
+
`delete_greeting` (Facebook Pages). A network without a field answers 400.
|
|
108
|
+
- `accounts.get_webhook_subscription` reports whether the network is still delivering events
|
|
109
|
+
for an account, and `resubscribe_webhook` puts a lapsed subscription back.
|
|
110
|
+
- `inbox.handover` passes a Messenger thread to another Meta app, or takes it back when no
|
|
111
|
+
`app_id` is given (`inbox` scope, plus `publish`).
|
|
112
|
+
- `accounts.list_slack_channels`, `list_slack_members`, `get_slack_identity` and `update_slack_identity`
|
|
113
|
+
(`accounts` scope), with the `SlackChannel`, `SlackMember` and `SlackIdentity` models.
|
|
114
|
+
- The Discord bot surface on `accounts` (`accounts` scope, plus `publish` for anything that
|
|
115
|
+
posts): `list_discord_channels`, `switch_discord_channel`, `get_discord_identity`,
|
|
116
|
+
`update_discord_identity`, `list_discord_pins`, `delete_discord_message`,
|
|
117
|
+
`pin_discord_message`, `unpin_discord_message`, `crosspost_discord_message`,
|
|
118
|
+
`create_discord_thread`, `send_discord_dm`, `list_discord_events`, `get_discord_event`,
|
|
119
|
+
`create_discord_event`, `update_discord_event`, `delete_discord_event`,
|
|
120
|
+
`list_discord_members`, `get_discord_member`, `list_discord_roles`, `create_discord_role`,
|
|
121
|
+
`update_discord_role`, `delete_discord_role`, `add_discord_member_role` and
|
|
122
|
+
`remove_discord_member_role`, with the `DiscordChannel`, `DiscordIdentity`,
|
|
123
|
+
`DiscordMessage`, `DiscordMessageRef`, `DiscordThread`, `DiscordScheduledEvent`,
|
|
124
|
+
`DiscordMember` and `DiscordRole` models. A webhook connection answers
|
|
125
|
+
`409 webhook_connection`.
|
|
126
|
+
- `DiscordChannel#can_post` says whether the bot may actually post in a channel;
|
|
127
|
+
a channel permission in Discord can shut it out. `switch_discord_channel` answers
|
|
128
|
+
`409 channel_not_writable` for one.
|
|
129
|
+
- Per-network extras on `accounts`, all `accounts` scope: Pinterest boards
|
|
130
|
+
(`list_pinterest_boards`, `create_pinterest_board`), YouTube playlists and captions
|
|
131
|
+
(`list_youtube_playlists`, `create_youtube_playlist`, `set_default_youtube_playlist`,
|
|
132
|
+
`list_youtube_captions`, `upload_youtube_captions`, `read_youtube_transcript`), Bluesky post
|
|
133
|
+
languages (`get_bluesky_languages`, `set_bluesky_languages`), TikTok creator info
|
|
134
|
+
(`get_tiktok_creator_info`), TikTok music and place search plus video lookup
|
|
135
|
+
(`search_tiktok_music`, `search_tiktok_locations`, `lookup_tiktok_video`),
|
|
136
|
+
Instagram audio, publishing limit and stories
|
|
137
|
+
(`search_instagram_audio`, `get_instagram_publishing_limit`, `list_instagram_stories`,
|
|
138
|
+
`get_instagram_story_insights`) and LinkedIn mentions (`search_linkedin_mentions`), with a
|
|
139
|
+
model each.
|
|
140
|
+
- `accounts.list_reddit_subreddits`, `list_reddit_subreddit_rules`, `list_reddit_flairs` and
|
|
141
|
+
`set_reddit_default_subreddit` (`accounts` scope): a Reddit account's destinations, a
|
|
142
|
+
subreddit's rules and post flairs, and where posts go when one names no subreddit. With the
|
|
143
|
+
`RedditSubreddit`, `RedditSubredditRule(s)`, `RedditFlair(s)` and `RedditDefaultSubreddit`
|
|
144
|
+
models. An account connected before those permissions were asked for answers 409
|
|
145
|
+
`reconnect_required`.
|
|
146
|
+
- `validate.subreddit` (`posts` scope), with the `SubredditCheck` model.
|
|
147
|
+
- `inbox.vote` (`inbox` and `publish`): up, down, or `'none'` to take an earlier vote back.
|
|
148
|
+
- `SocialAccount#reconnect_required`, and the inbox item's `vote` and `can_vote`.
|
|
149
|
+
- `client.blogs` reaches content a connected site already owns, by the platform's own ids:
|
|
150
|
+
`list_blogs`, `list_articles`, `get_article`, `create_article`, `update_article`,
|
|
151
|
+
`delete_article`, plus `list_products` and `update_product` for a Shopify store, with the
|
|
152
|
+
`RemoteBlog`, `RemoteArticle` and `RemoteProduct` models. `update_article` changes the live
|
|
153
|
+
article in place and never creates a second post. Reads need `posts`; the writes need `publish`.
|
|
154
|
+
|
|
155
|
+
### Changed
|
|
156
|
+
|
|
157
|
+
- `client.blog` matches the live API contract: the workspace travels as `workspace_id` on the
|
|
158
|
+
query string, single objects are read bare and lists from `{ data }`, and every response is
|
|
159
|
+
read in snake_case. `BlogWebsiteTest` now carries `http_status` (what the website answered)
|
|
160
|
+
instead of `ok`, `status` and `error`; a failed test is still a 502 `Fopost::Error` with
|
|
161
|
+
`http_status` on its body.
|
|
162
|
+
- `blog.plan_keywords` no longer fills the calendar: results land as `suggested` keywords, and
|
|
163
|
+
`update_keyword(status: 'planned')` picks one and gives it the next free date.
|
|
164
|
+
- `blog.publish_article` and `update_article(publish_at:)` need an article in review (or already
|
|
165
|
+
scheduled or failed); a draft answers 409 until it is submitted.
|
|
166
|
+
- `BlogSettings#review_hold_days` is now the number of days before its planned date that a
|
|
167
|
+
picked keyword starts being written.
|
|
168
|
+
|
|
169
|
+
### Removed
|
|
170
|
+
|
|
171
|
+
- **Breaking:** `BlogSettings#auto_publish` and the `auto_publish:` keyword on
|
|
172
|
+
`blog.update_settings`. Every article now goes through `submit_article` before it publishes.
|
|
173
|
+
|
|
7
174
|
## [0.2.0] - 2026-09-19
|
|
8
175
|
|
|
9
176
|
### Added
|
|
@@ -26,5 +193,7 @@ Initial release.
|
|
|
26
193
|
- Response models that accept either wire casing and keep unknown fields on `#raw`.
|
|
27
194
|
- A pluggable transport, so the HTTP stack can be swapped or stubbed.
|
|
28
195
|
|
|
196
|
+
[Unreleased]: https://github.com/fopost/fopost-ruby/compare/v0.3.0...HEAD
|
|
197
|
+
[0.3.0]: https://github.com/fopost/fopost-ruby/releases/tag/v0.3.0
|
|
29
198
|
[0.2.0]: https://github.com/fopost/fopost-ruby/releases/tag/v0.2.0
|
|
30
199
|
[0.1.0]: https://github.com/fopost/fopost-ruby/releases/tag/v0.1.0
|