Implement v2 API for stateful sessions and add engagement metrics support
This commit is contained in:
266
README.md
266
README.md
@@ -6,12 +6,15 @@ Generate Twitter-style quote images programmatically. Perfect for creating socia
|
||||
|
||||
## Features
|
||||
|
||||
- 🎨 Twitter-authentic styling
|
||||
- 🖼️ Support for images and text-only tweets
|
||||
- 📸 High-resolution output (3240x3240)
|
||||
- ⚡ Built-in caching (24-hour TTL)
|
||||
- 🔒 Base64 image support
|
||||
- 🐳 Docker-ready
|
||||
- Twitter-authentic styling
|
||||
- Support for images and text-only tweets
|
||||
- Verified badge (blue checkmark)
|
||||
- Engagement metrics (likes, retweets, replies, views)
|
||||
- High-resolution output (3240x3240)
|
||||
- Built-in caching (24-hour TTL)
|
||||
- Base64 image support
|
||||
- Docker-ready
|
||||
- v2 API with stateful sessions for incremental updates
|
||||
|
||||
## API Endpoints
|
||||
|
||||
@@ -51,6 +54,8 @@ curl "https://quotes.imbenji.net/generate?displayName=John%20Doe&username=@johnd
|
||||
| `text` | string | No | Tweet text content |
|
||||
| `imageUrl` | string | No | Tweet image URL, base64 data URI, or `null` |
|
||||
| `timestamp` | integer | No | Unix epoch timestamp in seconds |
|
||||
| `verified` | boolean | No | Show verified badge (blue checkmark) next to name |
|
||||
| `engagement` | object | No | Engagement metrics (likes, retweets, replies, views) |
|
||||
|
||||
## Response
|
||||
|
||||
@@ -145,6 +150,251 @@ curl -G "https://quotes.imbenji.net/generate" \
|
||||
--output quote.png
|
||||
```
|
||||
|
||||
### Verified badge
|
||||
|
||||
```javascript
|
||||
fetch('https://quotes.imbenji.net/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
displayName: 'Elon Musk',
|
||||
username: '@elonmusk',
|
||||
text: 'Just bought Twitter!',
|
||||
verified: true,
|
||||
timestamp: 1735574400
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Engagement metrics
|
||||
|
||||
```javascript
|
||||
fetch('https://quotes.imbenji.net/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
displayName: 'Popular User',
|
||||
username: '@viral',
|
||||
text: 'This tweet went viral!',
|
||||
verified: true,
|
||||
engagement: {
|
||||
replies: 1234,
|
||||
retweets: 5678,
|
||||
likes: 98765,
|
||||
views: 1500000
|
||||
},
|
||||
timestamp: 1735574400
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
**Note:** All four engagement fields (replies, retweets, likes, views) must be provided for the engagement bar to appear. Numbers are automatically formatted (e.g., 1234 → 1.2K, 1500000 → 1.5M).
|
||||
|
||||
---
|
||||
|
||||
## v2 API (Stateful Sessions)
|
||||
|
||||
The v2 API lets you build quote images incrementally. Instead of sending everything in one request, you create a session and update fields as needed. This is more efficient when making multiple edits since you dont need to resend large images every time.
|
||||
|
||||
### How it works
|
||||
|
||||
1. Create a session with `POST /v2/quote`
|
||||
2. Update fields with `PATCH /v2/quote/:id` (only send whats changed)
|
||||
3. Render the image with `GET /v2/quote/:id/image`
|
||||
|
||||
Sessions expire after 24 hours of inactivity.
|
||||
|
||||
### POST /v2/quote
|
||||
|
||||
Create a new session.
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
curl -X POST https://quotes.imbenji.net/v2/quote \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"displayName": "John Doe",
|
||||
"username": "@johndoe",
|
||||
"text": "Hello world"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response (201):**
|
||||
```json
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"displayName": "John Doe",
|
||||
"username": "@johndoe",
|
||||
"text": "Hello world",
|
||||
"avatarUrl": null,
|
||||
"imageUrl": null,
|
||||
"timestamp": null,
|
||||
"verified": false,
|
||||
"engagement": null,
|
||||
"createdAt": 1735600000,
|
||||
"updatedAt": 1735600000
|
||||
}
|
||||
```
|
||||
|
||||
### GET /v2/quote/:id
|
||||
|
||||
Get current session state.
|
||||
|
||||
```bash
|
||||
curl https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
```
|
||||
|
||||
### PATCH /v2/quote/:id
|
||||
|
||||
Update specific fields. Only send the fields you want to change.
|
||||
|
||||
```bash
|
||||
curl -X PATCH https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"text": "Updated text!",
|
||||
"avatarUrl": "data:image/png;base64,..."
|
||||
}'
|
||||
```
|
||||
|
||||
**Engagement updates:**
|
||||
|
||||
```bash
|
||||
# Set all engagement metrics
|
||||
curl -X PATCH https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"engagement": {
|
||||
"replies": 100,
|
||||
"retweets": 250,
|
||||
"likes": 5000,
|
||||
"views": 50000
|
||||
}
|
||||
}'
|
||||
|
||||
# Partial update (update only likes, keep other fields)
|
||||
curl -X PATCH https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"engagement": {
|
||||
"likes": 10000
|
||||
}
|
||||
}'
|
||||
|
||||
# Clear engagement entirely (removes engagement bar)
|
||||
curl -X PATCH https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"engagement": null
|
||||
}'
|
||||
```
|
||||
|
||||
**Verified badge:**
|
||||
|
||||
```bash
|
||||
# Add verified badge
|
||||
curl -X PATCH https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"verified": true
|
||||
}'
|
||||
```
|
||||
|
||||
### GET /v2/quote/:id/image
|
||||
|
||||
Render the current session state as a PNG image.
|
||||
|
||||
```bash
|
||||
curl https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890/image --output quote.png
|
||||
```
|
||||
|
||||
**Response headers:**
|
||||
- `Content-Type: image/png`
|
||||
- `X-Session-Id: <session-id>`
|
||||
- `X-Cache: HIT` or `MISS`
|
||||
|
||||
### DELETE /v2/quote/:id
|
||||
|
||||
Delete a session.
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://quotes.imbenji.net/v2/quote/a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
```
|
||||
|
||||
Returns `204 No Content` on success.
|
||||
|
||||
### v2 Example Workflow
|
||||
|
||||
```javascript
|
||||
// 1. Create session with initial data
|
||||
const res = await fetch('https://quotes.imbenji.net/v2/quote', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
displayName: 'Jane Doe',
|
||||
username: '@janedoe',
|
||||
text: 'Working on something cool'
|
||||
})
|
||||
});
|
||||
const session = await res.json();
|
||||
const sessionId = session.id;
|
||||
|
||||
// 2. Later, add an avatar (only sends the avatar, not everything again)
|
||||
await fetch(`https://quotes.imbenji.net/v2/quote/${sessionId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
avatarUrl: 'data:image/png;base64,iVBORw0KGgo...'
|
||||
})
|
||||
});
|
||||
|
||||
// 3. Update the text
|
||||
await fetch(`https://quotes.imbenji.net/v2/quote/${sessionId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
text: 'Finished building something cool!'
|
||||
})
|
||||
});
|
||||
|
||||
// 4. Generate the final image
|
||||
const imgRes = await fetch(`https://quotes.imbenji.net/v2/quote/${sessionId}/image`);
|
||||
const blob = await imgRes.blob();
|
||||
```
|
||||
|
||||
### v2 Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `displayName` | string | Display name |
|
||||
| `username` | string | Twitter handle with @ |
|
||||
| `avatarUrl` | string | Avatar image URL or base64 |
|
||||
| `text` | string | Tweet text content |
|
||||
| `imageUrl` | string | Tweet image URL or base64 |
|
||||
| `timestamp` | integer | Unix epoch in seconds |
|
||||
| `verified` | boolean | Show verified badge (blue checkmark) |
|
||||
| `engagement` | object/null | Engagement metrics (see below) |
|
||||
|
||||
**Engagement object:**
|
||||
```json
|
||||
{
|
||||
"engagement": {
|
||||
"replies": 89,
|
||||
"retweets": 567,
|
||||
"likes": 1234,
|
||||
"views": 50000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Engagement behavior:**
|
||||
- All four fields must be provided for the engagement bar to appear
|
||||
- Partial updates: Only update specific fields, others remain unchanged
|
||||
- Set to `null` to clear all engagement and hide the engagement bar
|
||||
- Numbers are auto-formatted (1234 → 1.2K, 1000000 → 1M)
|
||||
|
||||
---
|
||||
|
||||
## Caching
|
||||
|
||||
The API automatically caches generated images for 24 hours from the last request. Identical requests will be served from cache instantly.
|
||||
@@ -217,7 +467,9 @@ Response:
|
||||
- **Format:** PNG
|
||||
- **Max tweet width:** 450px (auto-scaled to fit)
|
||||
- **Cache TTL:** 24 hours from last access
|
||||
- **Session TTL:** 24 hours from last update (v2 API)
|
||||
- **Cleanup interval:** Every hour
|
||||
- **Database:** SQLite (for v2 sessions)
|
||||
|
||||
## Limitations
|
||||
|
||||
@@ -232,4 +484,4 @@ For issues or questions, please contact the maintainer.
|
||||
|
||||
---
|
||||
|
||||
**Built with:** Node.js, Express, node-html-to-image, Puppeteer
|
||||
**Built with:** Node.js, Express, Puppeteer, SQLite
|
||||
|
||||
Reference in New Issue
Block a user