diff --git a/README.md b/README.md new file mode 100644 index 0000000..8cc93a2 --- /dev/null +++ b/README.md @@ -0,0 +1,236 @@ +# Quote Generator API + +Generate Twitter-style quote images programmatically. Perfect for creating social media content, screenshots, or fake tweets. + +**Base URL:** `https://quotes.imbenji.net` + +## 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 + +## API Endpoints + +### POST /generate + +Generate a quote image using JSON request body. + +**Request:** +```bash +curl -X POST https://quotes.imbenji.net/generate \ + -H "Content-Type: application/json" \ + -d '{ + "displayName": "Geoff Marshall", + "username": "@geofftech", + "avatarUrl": "https://example.com/avatar.jpg", + "text": "Does anyone else find it immensely satisfying when you turn a pocket inside out and get rid of the crumbs and fluff stuck in the bottom.", + "imageUrl": null, + "timestamp": 1499766270 + }' --output quote.png +``` + +### GET /generate + +Generate a quote image using query parameters. + +**Request:** +```bash +curl "https://quotes.imbenji.net/generate?displayName=John%20Doe&username=@johndoe&text=Hello%20World×tamp=1735574400" --output quote.png +``` + +## Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `displayName` | string | No | Display name (defaults to "Anonymous") | +| `username` | string | No | Twitter handle with @ (defaults to "@anonymous") | +| `avatarUrl` | string | No | Avatar image URL or base64 data URI | +| `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 | + +## Response + +- **Content-Type:** `image/png` +- **Headers:** + - `X-Cache`: `HIT` (served from cache) or `MISS` (newly generated) + +## Examples + +### Text-only tweet + +```javascript +fetch('https://quotes.imbenji.net/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + displayName: 'John Doe', + username: '@johndoe', + text: 'Just deployed my new API!', + timestamp: Math.floor(Date.now() / 1000) + }) +}) +.then(res => res.blob()) +.then(blob => { + const url = URL.createObjectURL(blob); + document.getElementById('img').src = url; +}); +``` + +### Tweet with image + +```javascript +const response = await fetch('https://quotes.imbenji.net/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + displayName: 'Jane Smith', + username: '@janesmith', + avatarUrl: 'https://example.com/avatar.jpg', + text: 'Check out this amazing view! ๐ŸŒ„', + imageUrl: 'https://example.com/photo.jpg', + timestamp: 1735574400 + }) +}); + +const blob = await response.blob(); +// Save or display the image +``` + +### Using base64 images + +```javascript +fetch('https://quotes.imbenji.net/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + displayName: 'Alice', + username: '@alice', + avatarUrl: 'data:image/png;base64,iVBORw0KGgo...', + text: 'Using base64 images works too!', + imageUrl: 'data:image/jpeg;base64,/9j/4AAQSkZJ...', + timestamp: 1735574400 + }) +}); +``` + +### Python example + +```python +import requests +import time + +response = requests.post('https://quotes.imbenji.net/generate', json={ + 'displayName': 'Python User', + 'username': '@pythonista', + 'text': 'Making API calls with Python!', + 'timestamp': int(time.time()) +}) + +with open('quote.png', 'wb') as f: + f.write(response.content) +``` + +### cURL with GET + +```bash +curl -G "https://quotes.imbenji.net/generate" \ + --data-urlencode "displayName=Test User" \ + --data-urlencode "username=@testuser" \ + --data-urlencode "text=This is a test tweet" \ + --data-urlencode "timestamp=1735574400" \ + --output quote.png +``` + +## Caching + +The API automatically caches generated images for 24 hours from the last request. Identical requests will be served from cache instantly. + +- Cache hits include `X-Cache: HIT` header +- Cache misses include `X-Cache: MISS` header +- Cache is cleaned up hourly +- Images are stored based on SHA-256 hash of parameters + +## Timestamp Format + +The `timestamp` parameter expects a Unix epoch timestamp in **seconds** (not milliseconds). + +**JavaScript:** +```javascript +const timestamp = Math.floor(Date.now() / 1000); +``` + +**Python:** +```python +import time +timestamp = int(time.time()) +``` + +The timestamp will be formatted as: `5:58 PM ยท Dec 29, 2025` + +## Default Avatar + +If no `avatarUrl` is provided, a default Twitter-style placeholder avatar will be used. + +## Rate Limiting + +Currently, there are no rate limits. Please use responsibly. + +## Self-Hosting + +### Docker (Recommended) + +```bash +git clone +cd quotegen +docker-compose up -d +``` + +The API will be available at `http://localhost:3000` + +### Manual Setup + +```bash +npm install +npm start +``` + +## Health Check + +```bash +curl https://quotes.imbenji.net/health +``` + +Response: +```json +{ + "status": "ok" +} +``` + +## Technical Details + +- **Resolution:** 3240x3240 (1:1 aspect ratio) +- **Format:** PNG +- **Max tweet width:** 450px (auto-scaled to fit) +- **Cache TTL:** 24 hours from last access +- **Cleanup interval:** Every hour + +## Limitations + +- Tweet text is not wrapped intelligently (use `\n` for line breaks if needed) +- Very long tweets may be cut off +- External image URLs must be publicly accessible +- Base64 images should be reasonably sized + +## Support + +For issues or questions, please contact the maintainer. + +--- + +**Built with:** Node.js, Express, node-html-to-image, Puppeteer