Back to skills
SkillHub ClubShip Full StackFull Stack

x-single-tweet-article-skill

Fetch a single X tweet or X Article with charge-first billing (0.001 USDT/call).

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,125
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
B77.6

Install command

npx @skill-hub/cli install openclaw-skills-x-single-tweet-article-skill

Repository

openclaw/skills

Skill path: skills/huangkefeng-ai/x-single-tweet-article-skill

Fetch a single X tweet or X Article with charge-first billing (0.001 USDT/call).

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install x-single-tweet-article-skill into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding x-single-tweet-article-skill to shared team environments
  • Use x-single-tweet-article-skill for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: x-single-tweet-article-skill
description: Fetch a single X tweet or X Article with charge-first billing (0.001 USDT/call).
---

# X Single Tweet + Article (Premium)

Charge-first fetcher for:
- single X tweet
- single X Article

## Pricing
- 0.001 USDT per call
- If balance is insufficient: returns `PAYMENT_URL` as a top-up link (no charge is made)

## Run

```bash
# Tweet
node scripts/run.js --url "https://x.com/user/status/123" --user "user-1"

# X Article
node scripts/run.js --article "https://x.com/i/article/xxxxx" --user "user-1"
```

## Optional env overrides
- `SKILLPAY_BILLING_URL`
- `SKILL_BILLING_API_KEY`
- `SKILL_ID`
- `SKILLPAY_PRICE_TOKEN`


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/run.js

```javascript
#!/usr/bin/env node

const args = process.argv.slice(2);
const getArg = (name) => {
  const i = args.indexOf(`--${name}`);
  return i >= 0 ? args[i + 1] : undefined;
};

const url = getArg('url');
const article = getArg('article');
const userId = getArg('user') || 'anonymous';

if ((!url && !article) || (url && article)) {
  console.error('Usage: node scripts/run.js --url <x-status-url> OR --article <x-article-url> --user <user-id>');
  process.exit(1);
}

const BILLING_URL = process.env.SKILLPAY_BILLING_URL || 'https://skillpay.me/api/v1/billing';
const API_KEY = process.env.SKILL_BILLING_API_KEY || 'sk_74e1969ebc92fcf58257470c50f8bb76e36c9da0d201aa69861e28c62f5bd48e';
const SKILL_ID = process.env.SKILL_ID || 'ab787c89-1fe1-4ee2-b4f0-64ae89c79f8d';
const PRICE = Number(process.env.SKILLPAY_PRICE_TOKEN || '1');

async function getPaymentLink(amount = 7) {
  const r = await fetch(`${BILLING_URL}/payment-link`, {
    method: 'POST',
    headers: { 'content-type': 'application/json', 'x-api-key': API_KEY },
    body: JSON.stringify({ user_id: userId, amount }),
  }).catch(() => null);
  if (!r) return null;
  const d = await r.json().catch(() => ({}));
  return d.payment_url || null;
}

async function charge() {
  const r = await fetch(`${BILLING_URL}/charge`, {
    method: 'POST',
    headers: { 'content-type': 'application/json', 'x-api-key': API_KEY },
    body: JSON.stringify({ user_id: userId, skill_id: SKILL_ID, amount: PRICE }),
  }).catch(() => null);
  if (!r) return { ok: false, reason: 'network_error' };
  const d = await r.json().catch(() => ({}));
  if (d.success) return { ok: true, data: d };
  const pay = await getPaymentLink(7);
  if (pay) d.payment_url = pay;
  return { ok: false, reason: 'insufficient_balance', data: d };
}

function statusId(u) {
  const m = u.match(/status\/(\d+)/);
  return m ? m[1] : null;
}

async function fetchTextDirect(u) {
  const r = await fetch(u).catch(() => null);
  if (!r || !r.ok) return null;
  const t = await r.text();
  if (!t || t.length < 40) return null;
  return t;
}

async function fetchViaJina(u) {
  const j = `https://r.jina.ai/http://${u.replace(/^https?:\/\//, '')}`;
  const r = await fetch(j).catch(() => null);
  if (!r || !r.ok) return null;
  const t = await r.text();
  return t && t.length > 40 ? t : null;
}

async function fetchTweet(u) {
  const id = statusId(u);
  if (id) {
    const api = await fetch(`https://api.fxtwitter.com/status/${id}`).catch(() => null);
    if (api && api.ok) {
      const d = await api.json().catch(() => null);
      if (d?.tweet?.text) {
        return {
          mode: 'tweet',
          source: 'fxtwitter',
          data: {
            text: d.tweet.text,
            author: d.tweet.author?.name || null,
            screen_name: d.tweet.author?.screen_name || null,
            likes: d.tweet.likes ?? null,
            retweets: d.tweet.retweets ?? null,
            views: d.tweet.views ?? null,
            created_at: d.tweet.created_timestamp || null,
            original_url: u,
          },
        };
      }
    }
  }

  const txt = await fetchViaJina(u) || await fetchTextDirect(u);
  if (!txt) return null;
  return {
    mode: 'tweet',
    source: 'jina/direct',
    data: {
      text: txt.slice(0, 12000),
      original_url: u,
    },
  };
}

async function fetchArticle(u) {
  const txt = await fetchViaJina(u) || await fetchTextDirect(u);
  if (!txt) return null;
  const lines = txt.split('\n').map(s => s.trim()).filter(Boolean);
  const title = lines.find(x => !x.startsWith('URL Source:') && !x.startsWith('Markdown Content:') && x.length > 10) || 'X Article';
  return {
    mode: 'article',
    source: 'jina/direct',
    data: {
      title,
      full_text: txt.slice(0, 50000),
      original_url: u,
    },
  };
}

(async () => {
  const c = await charge();
  if (!c.ok) {
    if (c?.data?.payment_url) {
      console.error(`PAYMENT_URL:${c.data.payment_url}`);
      console.error('PAYMENT_INFO:Insufficient balance. Top up and retry.');
    }
    console.error(JSON.stringify({ ok: false, stage: 'billing', chargeResult: c, topup_min_usdt: 7 }, null, 2));
    process.exit(2);
  }

  const result = url ? await fetchTweet(url) : await fetchArticle(article);
  if (!result) {
    console.error(JSON.stringify({ ok: false, stage: 'fetch', message: 'fetch_failed' }, null, 2));
    process.exit(3);
  }

  console.log(JSON.stringify({ ok: true, charged: true, ...result }, null, 2));
})();

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### _meta.json

```json
{
  "owner": "huangkefeng-ai",
  "slug": "x-single-tweet-article-skill",
  "displayName": "X Single Tweet + Article",
  "latest": {
    "version": "1.0.4",
    "publishedAt": 1772729743005,
    "commit": "https://github.com/openclaw/skills/commit/b3d0a1c375bab7176c2c13c8273a78ecec23e7bc"
  },
  "history": [
    {
      "version": "1.0.2",
      "publishedAt": 1772729129136,
      "commit": "https://github.com/openclaw/skills/commit/7a3e832beaffb27efb7f392c3e637abdd1bc1c57"
    },
    {
      "version": "1.0.0",
      "publishedAt": 1772726809366,
      "commit": "https://github.com/openclaw/skills/commit/d54c156abf6e4276c78f6bf01b18e6e3a0b4bf3d"
    }
  ]
}

```

x-single-tweet-article-skill | SkillHub