misarblog 1.1.0 → 5.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/README.md +261 -51
- data/lib/misarblog/client.rb +6 -1
- metadata +16 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8085dfc4b323a9b12d169e1e5a5f28a45bd28511c239f1a7d318bdb8c1d0db0
|
|
4
|
+
data.tar.gz: 17064b11fcb64cb83755c8d13fd5475b21edad5680da643b50f6c9e41c3a0dee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e586d6906800b2fd4917ee3468d3e8187d4f7e991d8e57af09a7f5f30d076439acddffa1df8ef9eb0075c2a59389164c09afedecc3a74d4320c688c4bc8c9445
|
|
7
|
+
data.tar.gz: 8934679336eafb88534627b17d73760f8f6d2f8c2fc33766366b57d0d8ffb5ca6eec44679eba5acdc74907df5aa060a68ede2f839f0d451bf9bec31dd02cd891
|
data/README.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Misar.Blog Ruby SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> The official Ruby client for the Misar.Blog developer API.
|
|
4
|
+
|
|
5
|
+
[](https://rubygems.org/gems/misarblog) [](https://www.ruby-lang.org) [](./LICENSE)
|
|
6
|
+
|
|
7
|
+
**9 resource groups · 25 operations · standard library only (net/http)**
|
|
8
|
+
|
|
9
|
+
Works with any Ruby 2.7+ program that needs to drive a
|
|
10
|
+
[Misar.Blog](https://www.misar.blog) account: automating publishing, syncing a
|
|
11
|
+
blog out of CI or another CMS, or building a reader, dashboard or integration on
|
|
12
|
+
top of the API at `https://api.misar.io/blog/v1`.
|
|
13
|
+
|
|
14
|
+
---
|
|
4
15
|
|
|
5
16
|
## Install
|
|
6
17
|
|
|
@@ -8,74 +19,273 @@ Official Ruby client for the Misar.Blog developer API. Standard library only, re
|
|
|
8
19
|
gem install misarblog
|
|
9
20
|
```
|
|
10
21
|
|
|
11
|
-
|
|
22
|
+
Or in a `Gemfile`:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem "misarblog", "~> 1.1"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Ruby 2.7+.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Authentication
|
|
33
|
+
|
|
34
|
+
Mint a key at <https://www.misar.blog/dashboard/settings/api>. Keys are prefixed
|
|
35
|
+
`mbk_`; an OAuth 2.1 access token works on the same header. Key management
|
|
36
|
+
`MisarBlog.new(api_key:, base_url:, timeout:, max_retries:)` is a shorthand that
|
|
37
|
+
delegates to `MisarBlog::Client.new`; the key is sent as
|
|
38
|
+
`Authorization: Bearer`. See the first example below. The full request/response
|
|
39
|
+
contract is published as an OpenAPI document at
|
|
40
|
+
<https://api.misar.io/blog/v1/openapi.json>.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## API surface
|
|
45
|
+
|
|
46
|
+
| Resource | Method | Endpoint | What it does |
|
|
47
|
+
| --- | --- | --- | --- |
|
|
48
|
+
| `articles` | `list` | `GET /articles` | list your articles, filtered by status/visibility/sort |
|
|
49
|
+
| `articles` | `get` | `GET /articles/{slug}` | fetch one article by slug or UUID, full Markdown body |
|
|
50
|
+
| `articles` | `publish` | `POST /articles` | publish or schedule an article from Markdown |
|
|
51
|
+
| `articles` | `update` | `PATCH /articles/{slug}` | update title/body/tags in place; `publish: true` flips a draft live |
|
|
52
|
+
| `articles` | `create_draft` | `POST /drafts` | save a draft without publishing |
|
|
53
|
+
| `articles` | `search` | `GET /search` | full-text search across articles, profiles and tags |
|
|
54
|
+
| `articles` | `recommendations` | `GET /recommendations` | related articles for an article id |
|
|
55
|
+
| `series` | `list` | `GET /series` | list your series |
|
|
56
|
+
| `series` | `create` | `POST /series` | create a series |
|
|
57
|
+
| `series` | `add_article` | `POST /series/{slug}/articles` | add an article to a series at a position |
|
|
58
|
+
| `reactions` | `get` | `GET /reactions` | reaction counts and the caller's own reactions |
|
|
59
|
+
| `reactions` | `add` | `POST /reactions` | add a `like` / `clap` / `bookmark` |
|
|
60
|
+
| `reactions` | `remove` | `DELETE /reactions` | remove a reaction |
|
|
61
|
+
| `comments` | `list` | `GET /comments` | an article's comment thread, newest first, replies one level deep |
|
|
62
|
+
| `follows` | `status` | `GET /follows` | follower/following counts and whether the key's owner follows |
|
|
63
|
+
| `ai` | `complete` | `POST /ai/complete` | free-form system + user completion |
|
|
64
|
+
| `ai` | `titles` | `POST /ai/titles` | SEO/AEO/GEO title suggestions (`seo` from a keyword, `suggest` from copy) |
|
|
65
|
+
| `images` | `generate` | `POST /images/generate` | AI cover image (`1024x1024`, `1792x1024`, `1024x1792`) |
|
|
66
|
+
| `images` | `upload` | `POST /images/upload` | upload an image to the CDN |
|
|
67
|
+
| `account` | `profile` | `GET /me` | the authenticated creator profile |
|
|
68
|
+
| `account` | `plan` | `GET /plan` | live plan and per-feature quota |
|
|
69
|
+
| `account` | `trial_status` | `GET /trial` | whether a self-serve trial is active |
|
|
70
|
+
| `account` | `start_trial` | `POST /trial` | start a self-serve trial |
|
|
71
|
+
| `account` | `upsell_funnel` | `GET /upsell-funnel` | per-feature upsell funnel (platform-admin keys only; a creator key gets 403) |
|
|
72
|
+
| `analytics` | `get` | `GET /analytics` | views, gross/net revenue, active subscribers for trailing N days |
|
|
73
|
+
|
|
74
|
+
Note the two groupings that differ from the other Misar.Blog SDKs: profile,
|
|
75
|
+
plan, trial and upsell all hang off `account`, and the analytics summary is
|
|
76
|
+
`analytics.get` rather than `analytics.summary`.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## What's in the package
|
|
81
|
+
|
|
82
|
+
| Item | What it is |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `MisarBlog::Client` | The client. `MisarBlog.new(api_key:, base_url:, timeout: 30, max_retries: 3)` is the shorthand constructor. Resource readers: `articles`, `series`, `reactions`, `comments`, `follows`, `ai`, `images`, `account`, `analytics`. |
|
|
85
|
+
| Errors | `MisarBlog::ApiError`, `MisarBlog::PlanLimitError`, `MisarBlog::NetworkError`. |
|
|
86
|
+
| `MisarBlog.embed_url(username:, slug:, theme:)` | Pure string building for public embeds. |
|
|
87
|
+
| `MisarBlog::Models` | `Article`, `ArticleList`, `Series`, `SeriesList`, `Profile`, `Plan`, `PlanUsage`, `Analytics`, `ArticleReactions`, `ReactionResult`, `TrialStatus`, `TitlesResult`, `TitleSuggestion`, `AiText`, `ImageResult`. Each wraps the decoded body: named readers for the documented fields, `#[]` for string-key access, and `#raw` / `#to_h` for the untouched Hash, so a field the API adds after this release is still reachable. |
|
|
88
|
+
| `client.request(method, path, data)` | Public, so an endpoint this SDK does not wrap yet is still one call away. |
|
|
89
|
+
|
|
90
|
+
**Not everything is modelled.** `comments.list`, `follows.status`,
|
|
91
|
+
`articles.search`, `articles.recommendations`, `account.start_trial` and
|
|
92
|
+
`account.upsell_funnel` return the plain decoded `Hash` with string keys.
|
|
93
|
+
|
|
94
|
+
**Transport.** Standard library only — `net/http`, `uri`, `json`; no runtime
|
|
95
|
+
gem dependencies. Base URL `https://api.misar.io/blog/v1`; the key goes on
|
|
96
|
+
`Authorization: Bearer`. Statuses 429/500/502/503/504 and connection failures
|
|
97
|
+
are retried up to `max_retries` attempts (default 3) with exponential back-off
|
|
98
|
+
from 300 ms; the final attempt is always surfaced. Open timeout 10 s, read
|
|
99
|
+
timeout `timeout:` (default 30 s). A `204` or empty body returns `{}`.
|
|
100
|
+
|
|
101
|
+
**No streaming or webhooks.** Every operation is a single request/response. No
|
|
102
|
+
SSE or WebSocket endpoint accepts an API key, and the API has no webhook
|
|
103
|
+
registration route — `webhook_only` is an article *visibility* value, not a
|
|
104
|
+
subscription.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Examples
|
|
109
|
+
|
|
110
|
+
### Authenticate and publish
|
|
12
111
|
|
|
13
112
|
```ruby
|
|
14
113
|
require "misarblog"
|
|
15
114
|
|
|
16
|
-
blog = MisarBlog.new(api_key: "
|
|
115
|
+
blog = MisarBlog.new(api_key: ENV.fetch("MISARBLOG_API_KEY"))
|
|
17
116
|
|
|
18
117
|
me = blog.account.profile
|
|
19
|
-
|
|
20
|
-
follows = blog.follows.status(user_id: me["id"])
|
|
118
|
+
puts "authenticated as @#{me.username}"
|
|
21
119
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
120
|
+
article = blog.articles.publish(
|
|
121
|
+
title: "Shipping a blog from CI",
|
|
122
|
+
body_markdown: "# Shipping a blog from CI\n\nMarkdown in, article out.",
|
|
123
|
+
tags: %w[ci automation]
|
|
124
|
+
)
|
|
125
|
+
puts article.url
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Publish (or schedule) an article
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
article = blog.articles.publish(
|
|
132
|
+
title: "Hello, Misar",
|
|
133
|
+
body_markdown: "# Hello\n\nFirst post.",
|
|
134
|
+
tags: ["intro"],
|
|
135
|
+
cover_image_url: "https://cdn.example.com/cover.png",
|
|
136
|
+
visibility: "public", # public | subscribers | paid | private | webhook_only
|
|
137
|
+
schedule_at: "2026-09-01T09:00:00Z" # omit to publish immediately
|
|
138
|
+
)
|
|
139
|
+
puts "#{article.slug} #{article.status} #{article.url}"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`title:` and `body_markdown:` are required keywords; every other keyword is
|
|
143
|
+
dropped from the request body when left nil.
|
|
144
|
+
|
|
145
|
+
### Save a draft
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
draft = blog.articles.create_draft(
|
|
149
|
+
title: "Work in progress",
|
|
150
|
+
body_markdown: "Notes so far…",
|
|
151
|
+
tags: ["draft"]
|
|
152
|
+
)
|
|
153
|
+
puts draft.editor_url # open in the Misar.Blog editor
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### List your articles
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
result = blog.articles.list(status: "published", limit: 20)
|
|
160
|
+
result.articles.each { |a| puts "#{a.slug} #{a["view_count"]}" }
|
|
161
|
+
puts "#{result.articles.size} of #{result.total}"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`status:` accepts `draft`, `published`, `scheduled`, `archived` or `flagged`;
|
|
165
|
+
`visibility:`, `webhook_only:` and `sort:` narrow it further. `Article` exposes
|
|
166
|
+
the common fields as readers and everything else through `#[]` or `#raw`.
|
|
167
|
+
|
|
168
|
+
### Update an article — and publish a draft
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
updated = blog.articles.update(
|
|
172
|
+
"work-in-progress",
|
|
173
|
+
title: "Finished at last",
|
|
174
|
+
body_markdown: "The complete post.",
|
|
175
|
+
publish: true # flips a draft to published in the same call
|
|
176
|
+
)
|
|
177
|
+
puts "#{updated.status} #{updated.published_at}"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The slug is positional; everything else is a keyword, and omitted keywords are
|
|
181
|
+
left out of the body so those fields stay unchanged.
|
|
182
|
+
|
|
183
|
+
### Read an article's comment thread
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
thread = blog.comments.list(article_id: article.id, limit: 50, offset: 0)
|
|
187
|
+
thread["comments"].each do |c|
|
|
188
|
+
puts "@#{c["user"]["username"]}: #{c["content"]} (#{c["reply_count"]} replies)"
|
|
26
189
|
end
|
|
190
|
+
puts "#{thread["totalCount"]} #{thread["hasMore"]}"
|
|
27
191
|
```
|
|
28
192
|
|
|
29
|
-
|
|
193
|
+
This one returns a plain Hash, not a model. Leave `limit:`/`offset:` out to take
|
|
194
|
+
the server defaults of 20 (max 100) and 0.
|
|
30
195
|
|
|
31
|
-
|
|
32
|
-
with your developer key as a Bearer token. Mint a key in the dashboard at
|
|
33
|
-
<https://www.misar.blog/dashboard/settings/api> — key management is a
|
|
34
|
-
cookie-session flow and is deliberately not exposed by this SDK.
|
|
196
|
+
### Read and add reactions
|
|
35
197
|
|
|
36
|
-
|
|
198
|
+
```ruby
|
|
199
|
+
counts = blog.reactions.get(article_id: article.id)
|
|
200
|
+
puts "#{counts.counts["clap"]} #{counts.total} #{counts.user_reactions}"
|
|
37
201
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
| `403` | The key is scoped and lacks the scope this route needs |
|
|
42
|
-
| `429` (plain) | Rate limit — 100 requests/minute per key. The SDK retries with back-off |
|
|
43
|
-
| `429` + `plan_limit_exceeded` | A metered allowance is spent. Retrying will not help until it resets |
|
|
44
|
-
| `402` + `plan_limit_exceeded` | The feature is not on this plan |
|
|
202
|
+
blog.reactions.add(article_id: article.id, type: "clap") # like | clap | bookmark
|
|
203
|
+
blog.reactions.remove(article_id: article.id, type: "clap")
|
|
204
|
+
```
|
|
45
205
|
|
|
46
|
-
|
|
47
|
-
plan slug, the pricing URL and (when the API supplies it) seconds until reset.
|
|
48
|
-
Show the upgrade URL instead of reporting a bare failure — the SDK does not
|
|
49
|
-
retry these, because retrying cannot change the outcome.
|
|
206
|
+
### Generate SEO titles
|
|
50
207
|
|
|
51
|
-
|
|
208
|
+
```ruby
|
|
209
|
+
result = blog.ai.titles(
|
|
210
|
+
action: "seo", # "seo" from a keyword, "suggest" from existing copy
|
|
211
|
+
prompt: "shipping a static blog from GitHub Actions"
|
|
212
|
+
)
|
|
213
|
+
result.titles.each { |t| puts "#{t.title} — #{t.hint}" }
|
|
214
|
+
```
|
|
52
215
|
|
|
53
|
-
|
|
216
|
+
For `"suggest"`, pass the article text as `context:` instead of `prompt:`.
|
|
54
217
|
|
|
55
|
-
|
|
56
|
-
| --- | --- |
|
|
57
|
-
| Articles | list, get, create, update, create draft, search, recommendations |
|
|
58
|
-
| Series | list, create, add article |
|
|
59
|
-
| Reactions | get, add, remove |
|
|
60
|
-
| Comments | list |
|
|
61
|
-
| Follows | status |
|
|
62
|
-
| AI | complete, titles |
|
|
63
|
-
| Images | generate, upload |
|
|
64
|
-
| Account | profile, plan, trial status, start trial |
|
|
65
|
-
| Analytics | summary, upsell funnel |
|
|
66
|
-
|
|
67
|
-
The API exposes no SSE or WebSocket endpoint that accepts an API key, so this
|
|
68
|
-
SDK is request/response only. See [`openapi/blog.openapi.json`][spec] for the
|
|
69
|
-
machine-readable contract.
|
|
70
|
-
|
|
71
|
-
[spec]: https://api.misar.io/blog/v1/openapi.json
|
|
218
|
+
### Read the analytics summary
|
|
72
219
|
|
|
73
|
-
|
|
220
|
+
```ruby
|
|
221
|
+
summary = blog.analytics.get(days: 30)
|
|
222
|
+
puts "#{summary.views} #{summary.revenue_cents} #{summary.active_subscribers}"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Generate a cover image
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
image = blog.images.generate(
|
|
229
|
+
prompt: "a dark editorial illustration of a printing press",
|
|
230
|
+
size: "1792x1024"
|
|
231
|
+
)
|
|
232
|
+
puts image.url
|
|
233
|
+
```
|
|
74
234
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
235
|
+
`blog.images.upload(data)` posts to the CDN upload route as JSON — pass the Hash
|
|
236
|
+
the API expects (a base64 `data` field). This SDK does not build a multipart
|
|
237
|
+
request for you; the Go and Python clients do.
|
|
238
|
+
|
|
239
|
+
### Embed a public article
|
|
240
|
+
|
|
241
|
+
```ruby
|
|
242
|
+
puts MisarBlog.embed_url(username: "gulshan", slug: "hello-misar", theme: "dark")
|
|
243
|
+
# https://misar.blog/gulshan/hello-misar/embed?theme=dark
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Omit `slug:` to embed the whole profile; `theme:` defaults to `"auto"`, which
|
|
247
|
+
adds no query parameter.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Errors
|
|
252
|
+
|
|
253
|
+
Every failure raises. `PlanLimitError` and `NetworkError` both subclass
|
|
254
|
+
`ApiError`, which subclasses `StandardError` — so a single `rescue
|
|
255
|
+
MisarBlog::ApiError` catches everything from this SDK. Order narrowest-first.
|
|
256
|
+
|
|
257
|
+
| Type | Raised when | Readers |
|
|
258
|
+
| --- | --- | --- |
|
|
259
|
+
| `ApiError` | Any non-2xx the SDK did not classify further — `400` bad payload, `401` missing/expired/revoked key, `403` the key lacks the route's scope, `404` unknown slug, plain `429` rate limit (100 req/min per key) after retries are exhausted, `5xx` after retries | `status`, `error_type`, `body` (the decoded error Hash) |
|
|
260
|
+
| `PlanLimitError` | The subscription blocks the call: `429` + `code: "plan_limit_exceeded"` (a metered allowance is spent) or `402` (the feature is not on this plan). **Never retried** — retrying cannot help until the allowance resets or the plan changes | `plan`, `upgrade_url`, `retry_after`, `upgrade` |
|
|
261
|
+
| `NetworkError` | The request never reached the API — DNS, TLS, connection refused/reset, open or read timeout — on the final attempt | `cause_error`; `status` is `0` |
|
|
262
|
+
|
|
263
|
+
```ruby
|
|
264
|
+
begin
|
|
265
|
+
blog.ai.complete(prompt: "Draft an intro paragraph")
|
|
266
|
+
rescue MisarBlog::PlanLimitError => e
|
|
267
|
+
# Route the reader to checkout instead of reporting a bare failure.
|
|
268
|
+
puts "#{e.plan} plan is out of credits — upgrade at #{e.upgrade_url}"
|
|
269
|
+
rescue MisarBlog::NetworkError => e
|
|
270
|
+
puts "could not reach the API: #{e.cause_error}"
|
|
271
|
+
rescue MisarBlog::ApiError => e
|
|
272
|
+
puts "#{e.status} #{e.body && e.body["required_scope"]}"
|
|
273
|
+
end
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The 403 scope details are not promoted to named readers here — read
|
|
277
|
+
`required_scope` and `granted_scopes` off `e.body`. If a failure lands in the
|
|
278
|
+
wrong class, file it at <https://github.com/Misar-AI/misarblog-sdks/issues>.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Links
|
|
78
283
|
|
|
79
|
-
|
|
284
|
+
- Website — https://www.misar.blog
|
|
285
|
+
- App — https://www.misar.blog
|
|
286
|
+
- Parent — https://misar.io
|
|
287
|
+
- Documentation — https://docs.misar.io/blog
|
|
288
|
+
- Source — https://github.com/Misar-AI/misarblog-sdks
|
|
289
|
+
- RubyGems — https://rubygems.org/gems/misarblog
|
|
80
290
|
|
|
81
|
-
MIT
|
|
291
|
+
MIT © [Misar AI](https://misar.io)
|
data/lib/misarblog/client.rb
CHANGED
|
@@ -347,7 +347,10 @@ module MisarBlog
|
|
|
347
347
|
end
|
|
348
348
|
|
|
349
349
|
def parse_response(resp, status)
|
|
350
|
-
|
|
350
|
+
# Only a *successful* empty body means "no content". An error with an empty
|
|
351
|
+
# body — a bare 401, or anything a proxy stripped — must still raise, or the
|
|
352
|
+
# caller reads a failure as an empty result set.
|
|
353
|
+
return {} if status < 400 && (status == 204 || resp.body.nil? || resp.body.empty?)
|
|
351
354
|
|
|
352
355
|
decoded = begin
|
|
353
356
|
JSON.parse(resp.body)
|
|
@@ -357,6 +360,8 @@ module MisarBlog
|
|
|
357
360
|
|
|
358
361
|
if status >= 400
|
|
359
362
|
msg = decoded.is_a?(Hash) ? (decoded["error"] || decoded["message"] || resp.body) : resp.body
|
|
363
|
+
# A stripped body leaves nothing to report; fall back to the reason phrase.
|
|
364
|
+
msg = resp.message.to_s if msg.nil? || msg.empty?
|
|
360
365
|
if decoded.is_a?(Hash) && decoded["code"] == "plan_limit_exceeded"
|
|
361
366
|
raise PlanLimitError.new(status, msg, decoded, response_headers(resp))
|
|
362
367
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: misarblog
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 5.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Misar AI
|
|
@@ -38,9 +38,14 @@ dependencies:
|
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '3.23'
|
|
41
|
-
description:
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
description: 'Ruby client for the developer API of Misar.Blog (misar.blog), a hosted
|
|
42
|
+
blogging platform. Publish or schedule Markdown articles, save and update drafts,
|
|
43
|
+
group articles into series, read comment threads, reactions and follows, pull an
|
|
44
|
+
analytics summary and live plan/quota state, generate SEO/AEO/GEO title suggestions,
|
|
45
|
+
completions and AI cover images, search articles, profiles and tags, and build public
|
|
46
|
+
iframe embed URLs — all 25 key-authenticated operations. Standard library only (net/http):
|
|
47
|
+
mbk_ bearer auth, retry with exponential back-off, and a typed PlanLimitError carrying
|
|
48
|
+
the upgrade URL.'
|
|
44
49
|
email:
|
|
45
50
|
- hello@misar.io
|
|
46
51
|
executables: []
|
|
@@ -55,13 +60,15 @@ files:
|
|
|
55
60
|
- lib/misarblog/embed.rb
|
|
56
61
|
- lib/misarblog/errors.rb
|
|
57
62
|
- lib/misarblog/models.rb
|
|
58
|
-
homepage: https://www.misar.blog
|
|
63
|
+
homepage: https://www.misar.blog
|
|
59
64
|
licenses:
|
|
60
65
|
- MIT
|
|
61
66
|
metadata:
|
|
62
|
-
homepage_uri: https://www.misar.blog
|
|
67
|
+
homepage_uri: https://www.misar.blog
|
|
63
68
|
source_code_uri: https://github.com/Misar-AI/misarblog-sdks
|
|
64
69
|
changelog_uri: https://github.com/Misar-AI/misarblog-sdks/releases
|
|
70
|
+
documentation_uri: https://docs.misar.io/blog
|
|
71
|
+
bug_tracker_uri: https://github.com/Misar-AI/misarblog-sdks/issues
|
|
65
72
|
post_install_message:
|
|
66
73
|
rdoc_options: []
|
|
67
74
|
require_paths:
|
|
@@ -80,5 +87,7 @@ requirements: []
|
|
|
80
87
|
rubygems_version: 3.5.22
|
|
81
88
|
signing_key:
|
|
82
89
|
specification_version: 4
|
|
83
|
-
summary:
|
|
90
|
+
summary: 'Ruby client for misar.blog, a hosted blogging platform: publish and schedule
|
|
91
|
+
Markdown articles, manage drafts and series, read comments, reactions, follows and
|
|
92
|
+
analytics, and generate AI titles and covers.'
|
|
84
93
|
test_files: []
|