Skip to content

Commit 761ab92

Browse files
AdametherzLabclaude
andcommitted
v0.5.0: Social media content engine — format, thread, build-in-public, post to Bluesky & X
6 new functions: socialFormat() platform-aware text formatting (X/Bluesky/IG/YT/Mastodon), thread() smart thread splitter with numbering, buildInPublic() metric-to-post generator with sparklines + kaomoji + hashtags, socialCaption() multi-section caption builder, postToBluesky() AT Protocol posting, postToX() OAuth 1.0a signed posting. 31 new tests (94 total passing). New file: src/social.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d5f5789 commit 761ab92

7 files changed

Lines changed: 975 additions & 8 deletions

File tree

README.md

Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# webhook-spark
22

3-
**Zero-dep terminal sparklines, gauges, kaomoji, heatmaps, tables, histograms & dashboards for Discord/Slack/Telegram, LCD screens, IoT & AI agents.**
3+
**Zero-dep sparklines, gauges, kaomoji, heatmaps, tables, histograms, social media content & posting for Discord/Slack/Telegram/Bluesky/X, LCD screens, IoT & AI agents.**
44

5-
Zero dependencies. TypeScript first. Under 20KB.
5+
Zero dependencies. TypeScript first. Under 25KB.
66

77
## Who is this for?
88

@@ -11,6 +11,8 @@ Zero dependencies. TypeScript first. Under 20KB.
1111
- **DIY / IoT makers** -- multi-sensor dashboards for greenhouses, aquariums, server racks
1212
- **AI agent builders** -- compact metric summaries that fit in LLM context windows
1313
- **Dashboard addicts** -- kaomoji status faces, histograms, comparison charts, all in pure text
14+
- **Indie hackers / #BuildInPublic** -- auto-generate social posts from metrics, post to Bluesky & X
15+
- **Content creators** -- structured captions, thread splitters, platform-aware formatting
1416

1517
## Installation
1618

@@ -22,7 +24,7 @@ bun add @adametherzlab/webhook-spark
2224
## Quick Start
2325

2426
```typescript
25-
import { spark, gauge, kaomoji, kaomojiStatus, heatmap, dashboard } from '@adametherzlab/webhook-spark';
27+
import { spark, gauge, kaomoji, kaomojiStatus, heatmap, dashboard, buildInPublic, socialFormat, thread, postToBluesky } from '@adametherzlab/webhook-spark';
2628

2729
// Sparkline from numbers
2830
spark([10, 25, 60, 85, 90, 45, 30]);
@@ -236,6 +238,89 @@ trend([30, 20, 10]); // => "↓"
236238

237239
Supports Discord, Slack, and Telegram webhooks with validation, retry, and timeout.
238240

241+
### `socialFormat(text, options?)` -- Platform-Aware Formatting
242+
243+
Auto-truncates text to platform limits with smart word-boundary splitting.
244+
245+
```typescript
246+
socialFormat("Long status update...", { platform: "x", hashtags: ["devops", "monitoring"] });
247+
// => { text: "Long status update...\n\n#devops #monitoring", truncated: false, limit: 280 }
248+
249+
socialFormat("A".repeat(300), { platform: "bluesky" });
250+
// => { text: "AAAA...AAA...", truncated: true, limit: 300 }
251+
```
252+
253+
**Platforms:** `x` (280), `bluesky` (300), `mastodon` (500), `threads` (500), `instagram` (2200), `youtube` (5000)
254+
255+
### `thread(text, options?)` -- Thread Splitter
256+
257+
Splits long text into numbered posts. Smart paragraph/sentence/word splitting.
258+
259+
```typescript
260+
thread(longArticle, { platform: "x", numbering: true, header: "THREAD on monitoring:" });
261+
// => { posts: ["THREAD on monitoring:\n\nFirst part... (1/4)", "Second part... (2/4)", ...], count: 4 }
262+
```
263+
264+
Options: `platform`, `maxLength`, `numbering`, `header`, `footer`.
265+
266+
### `buildInPublic(metrics, options?)` -- #BuildInPublic Post Generator
267+
268+
Generates social media posts from dashboard metrics. Includes sparklines, trend arrows, kaomoji, and hashtags.
269+
270+
```typescript
271+
buildInPublic([
272+
{ name: 'Users', values: [100,120,150,180], thresholds: { warning: 200, critical: 500 } },
273+
{ name: 'Revenue', values: [50,75,90,110], unit: '$', thresholds: { warning: 200, critical: 1000 } },
274+
], { project: "webhook-spark", hashtags: ["buildinpublic", "opensource"] });
275+
// => webhook-spark update (today):
276+
//
277+
// ↑ Users: 180 ▁▃▅█ (*^▽^*)
278+
// ↑ Revenue: 110$ ▁▃▆█ (*^▽^*)
279+
//
280+
// ☆*:.。.o(≧▽≦)o.。.:*☆
281+
//
282+
// #buildinpublic #opensource
283+
```
284+
285+
Options: `project`, `period`, `hashtags`, `kaomoji`, `kaomojiTheme`, `includeSparklines`.
286+
287+
### `socialCaption(sections, options?)` -- Multi-Section Caption Builder
288+
289+
Build structured captions for Instagram, YouTube descriptions, etc.
290+
291+
```typescript
292+
socialCaption([
293+
{ title: "What we shipped", body: "v0.5.0 with social posting", emoji: "🚀" },
294+
{ title: "Stats", body: dashboard(metrics, { compact: true }), emoji: "📊" },
295+
], { hashtags: ["devtools", "typescript"], cta: "Star us on GitHub!" });
296+
```
297+
298+
Options: `platform`, `hashtags`, `cta`, `separator`.
299+
300+
### `postToBluesky(text, config)` -- Post to Bluesky
301+
302+
Post directly to Bluesky via AT Protocol. Just needs handle + app password (no OAuth).
303+
304+
```typescript
305+
const result = await postToBluesky("Hello from webhook-spark!", {
306+
handle: "you.bsky.social",
307+
appPassword: "xxxx-xxxx-xxxx-xxxx",
308+
});
309+
// => { success: true, platform: "bluesky", postUrl: "https://bsky.app/profile/you.bsky.social/post/..." }
310+
```
311+
312+
### `postToX(text, config)` -- Post to X (Twitter)
313+
314+
Post via X API v2 with OAuth 1.0a signing. Requires developer app credentials.
315+
316+
```typescript
317+
const result = await postToX("Server status: all green!", {
318+
apiKey: "...", apiSecret: "...",
319+
accessToken: "...", accessSecret: "...",
320+
});
321+
// => { success: true, platform: "x", postUrl: "https://x.com/i/status/..." }
322+
```
323+
239324
## Use Case Gallery
240325

241326
### IoT Greenhouse Dashboard
@@ -289,6 +374,52 @@ const line2 = `T:${temp}C ${trend(tempHistory)}`;
289374
lcd.print(line1 + '\n' + line2);
290375
```
291376

377+
### #BuildInPublic Automation
378+
379+
```typescript
380+
// Generate and post a daily update from your metrics
381+
const post = buildInPublic(todayMetrics, {
382+
project: "my-saas",
383+
period: "Week 12",
384+
hashtags: ["buildinpublic", "indiehacker"],
385+
kaomojiTheme: "cats",
386+
});
387+
388+
// Format for X and post
389+
const formatted = socialFormat(post, { platform: "x" });
390+
await postToX(formatted.text, xCredentials);
391+
392+
// Also post the full version to Bluesky (300 chars)
393+
const bskyFormatted = socialFormat(post, { platform: "bluesky" });
394+
await postToBluesky(bskyFormatted.text, bskyCredentials);
395+
```
396+
397+
### AI Agent Social Content Pipeline
398+
399+
```typescript
400+
// Agent generates a metric summary, then posts across platforms
401+
const caption = socialCaption([
402+
{ title: "Daily Report", body: dashboard(metrics, { compact: true }), emoji: "📊" },
403+
{ title: "Highlights", body: "Shipped v2.0, 50 new users, 99.9% uptime", emoji: "🎯" },
404+
], { hashtags: ["devops", "monitoring"], cta: "Try it free at example.com" });
405+
406+
// Thread it for X
407+
const xThread = thread(caption, { platform: "x", numbering: true });
408+
for (const post of xThread.posts) {
409+
await postToX(post, xConfig);
410+
}
411+
```
412+
413+
### YouTube Video Description
414+
415+
```typescript
416+
const description = socialCaption([
417+
{ title: "In this video", body: "I show you how to build a real-time server dashboard..." },
418+
{ title: "Tech stack", body: "webhook-spark + Bun + Discord webhooks" },
419+
{ title: "Timestamps", body: "0:00 Intro\n2:30 Setup\n5:00 Dashboard\n8:00 Alerts" },
420+
], { platform: "youtube", hashtags: ["coding", "devops", "typescript"], cta: "Subscribe for more!" });
421+
```
422+
292423
## Why webhook-spark?
293424

294425
| Feature | webhook-spark | blessed-contrib | cli-table3 | ink |
@@ -302,7 +433,10 @@ lcd.print(line1 + '\n' + line2);
302433
| Histograms | Yes | Yes | No | No |
303434
| Comparisons | Yes | No | No | No |
304435
| Webhooks | Yes | No | No | No |
305-
| Bundle size | <20KB | 2.5MB | 180KB | 400KB |
436+
| Social posting | Yes (X + Bluesky) | No | No | No |
437+
| Thread splitter | Yes | No | No | No |
438+
| #BuildInPublic | Yes | No | No | No |
439+
| Bundle size | <25KB | 2.5MB | 180KB | 400KB |
306440
| Maintained | Yes | No | Minimal | Yes |
307441

308442
## License

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@adametherzlab/webhook-spark",
3-
"version": "0.4.0",
4-
"description": "Zero-dep terminal sparklines, gauges, kaomoji, heatmaps, tables, histograms & dashboards for Discord/Slack/Telegram, LCD, IoT & AI agents.",
3+
"version": "0.5.0",
4+
"description": "Zero-dep sparklines, gauges, kaomoji, heatmaps, tables, histograms, social media content & posting for Discord/Slack/Telegram/Bluesky/X, LCD, IoT & AI agents.",
55
"type": "module",
66
"main": "src/index.ts",
77
"module": "src/index.ts",
@@ -41,6 +41,11 @@
4141
"arduino",
4242
"raspberry-pi",
4343
"zero-dependency",
44+
"social-media",
45+
"bluesky",
46+
"twitter-api",
47+
"content-generator",
48+
"build-in-public",
4449
"typescript",
4550
"bun",
4651
"homelab",

src/index.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,21 @@ import type {
3434
HistogramOptions,
3535
CompareOptions,
3636
CompareResult,
37+
SocialPlatform,
38+
SocialFormatOptions,
39+
SocialFormatResult,
40+
ThreadOptions,
41+
ThreadResult,
42+
BuildInPublicOptions,
43+
SocialCaptionSection,
44+
SocialCaptionOptions,
45+
BlueskyConfig,
46+
XConfig,
47+
SocialPostResult,
3748
} from "./types.js";
38-
import { generateSparkline, generateSparklineWithOutliers, generateASCIIArt, spark, barChart, trend, gauge, stats, sparkWithStatus, dashboard, kaomoji, kaomojiAll, kaomojiStatus, kaomojiThemes, heatmap, miniTable, kvTable, histogram, compare } from "./sparkline.js";
49+
import { generateSparkline, generateSparklineWithOutliers, generateASCIIArt, spark, barChart, trend, gauge, stats, sparkWithStatus, dashboard, kaomoji, kaomojiAll, kaomojiStatus, kaomojiThemes, heatmap, miniTable, kvTable, histogram, compare, socialFormat, thread, buildInPublic, socialCaption } from "./sparkline.js";
3950
import { sendWebhook } from "./webhook.js";
51+
import { postToBluesky, postToX } from "./social.js";
4052
import { isNumericArray, isSparklineConfig, isWebhookConfig } from "./types.js";
4153

4254
export {
@@ -59,7 +71,13 @@ export {
5971
kvTable,
6072
histogram,
6173
compare,
74+
socialFormat,
75+
thread,
76+
buildInPublic,
77+
socialCaption,
6278
sendWebhook,
79+
postToBluesky,
80+
postToX,
6381
isNumericArray,
6482
isSparklineConfig,
6583
isWebhookConfig,
@@ -100,4 +118,15 @@ export type {
100118
HistogramOptions,
101119
CompareOptions,
102120
CompareResult,
121+
SocialPlatform,
122+
SocialFormatOptions,
123+
SocialFormatResult,
124+
ThreadOptions,
125+
ThreadResult,
126+
BuildInPublicOptions,
127+
SocialCaptionSection,
128+
SocialCaptionOptions,
129+
BlueskyConfig,
130+
XConfig,
131+
SocialPostResult,
103132
};

0 commit comments

Comments
 (0)