Update twitter skill to v1.4.0 - sync local changes

- Update SKILL.md with new tools and improved documentation
- Sync tools.py, client.py, __init__.py
- Bump version to 1.4.0 in skills.json

Co-authored-by: Starchild <noreply@iamstarchild.com>
This commit is contained in:
Starchild Bot
2026-04-12 03:40:03 +00:00
parent 7910e83ca7
commit a332433fc3
5 changed files with 288 additions and 39 deletions
+1 -1
View File
@@ -184,7 +184,7 @@
{
"name": "twitter",
"path": "twitter/SKILL.md",
"version": "1.3.0",
"version": "1.4.0",
"description": "Twitter/X (x.com) data lookup — fetch tweets by URL or ID, search tweets, user profiles, followers, replies. Use for ANY x.com or twitter.com URL."
},
{
+36 -31
View File
@@ -1,35 +1,34 @@
---
name: twitter
version: 1.3.0
description: Twitter/X (x.com) data lookup fetch tweets by URL or ID, search tweets, user profiles, followers, replies. Use for ANY x.com or twitter.com URL.
version: 1.4.0
description: "Twitter/X (x.com) data lookup \u2014 fetch tweets by URL or ID, search\
\ tweets, user profiles, followers, replies. Use for ANY x.com or twitter.com URL."
tools:
- twitter_search_tweets
- twitter_get_tweets
- twitter_user_info
- twitter_user_tweets
- twitter_user_followers
- twitter_user_followings
- twitter_tweet_replies
- twitter_tweet_retweeters
- twitter_search_users
- twitter_search_tweets
- twitter_get_tweets
- twitter_user_info
- twitter_user_tweets
- twitter_user_followers
- twitter_user_followings
- twitter_tweet_replies
- twitter_tweet_retweeters
- twitter_search_users
- twitter_get_article
- twitter_tweet_thread_context
- twitter_tweet_quote
- twitter_get_trends
metadata:
starchild:
emoji: "🐦"
emoji: "\U0001F426"
skillKey: twitter
requires:
env: [TWITTER_API_KEY]
env:
- TWITTER_API_KEY
user-invocable: false
disable-model-invocation: false
---
# Twitter / X Data
Read-only access to Twitter/X via twitterapi.io. Use these tools to look up tweets, users, followers, and social activity.
## 🔴 HARD LIMITS — READ FIRST
> **⛔ CALL AT MOST 3 TWITTER TOOLS PER RESPONSE. STOP AFTER 3 CALLS.**
> After each tool call, check: "Do I have enough data to answer?" If yes → STOP AND REPLY.
> **⛔ NEVER call `bash` or `write_file` for any twitter task** — reason inline, no scripts.
@@ -38,7 +37,6 @@ Read-only access to Twitter/X via twitterapi.io. Use these tools to look up twee
> **⛔ NEVER call `coin_price`, `cg_trending`, `cg_coins_markets`** — 价格数据超出 Twitter skill 范围。
## 🔗 URL Handling — x.com / twitter.com
> **⛔ NEVER use `web_fetch` for x.com or twitter.com URLs** — Twitter blocks scraping, you'll only get a login wall.
> **✅ ALWAYS extract the tweet ID from the URL and use `twitter_get_tweets`.**
@@ -54,7 +52,6 @@ Read-only access to Twitter/X via twitterapi.io. Use these tools to look up twee
→ Never: `web_fetch("https://x.com/...")`
## 💡 Few-Shot Examples
**Q: 找 3 个关于 BTC ETF 的高赞推文,只要 ID 和点赞数**
→ PLAN: 1 call `twitter_search_tweets("BTC ETF min_faves:100")` → pick top 3 from results → reply JSON
→ STOP after 1 call. Total tools: 1
@@ -72,7 +69,6 @@ Read-only access to Twitter/X via twitterapi.io. Use these tools to look up twee
→ Total tools: 3. STOP.
## ⚡ FAST PATHS (act immediately, no clarification needed)
| Trigger keywords | Action |
|-----------------|--------|
| x.com or twitter.com URL with `/status/{id}` | Extract tweet ID → `twitter_get_tweets(tweet_ids=["{id}"])`**never web_fetch** |
@@ -83,7 +79,6 @@ Read-only access to Twitter/X via twitterapi.io. Use these tools to look up twee
| what did @username post | Call `twitter_user_tweets` |
## Tool Decision Tree
**"Search for tweets about a topic"** → `twitter_search_tweets`
Advanced query with operators: keywords, from:user, #hashtag, $cashtag, min_faves, date ranges.
@@ -111,12 +106,23 @@ Users who retweeted a specific tweet.
**"Find accounts related to a topic"** → `twitter_search_users`
Search users by name or keyword.
**"Read a long X article"** → `twitter_get_article`
Pass the article tweet ID. Returns title, preview, cover, and content blocks.
**"Get full thread context"** → `twitter_tweet_thread_context`
One call returns parent chain + direct replies for the target tweet.
**"Who quoted this tweet?"** → `twitter_tweet_quote`
Get quote tweets for a specific tweet ID.
**"What is trending now?"** → `twitter_get_trends`
Get trends with optional `woeid`, `country`, `category`, `limit`.
**"Crypto sentiment scan / 情绪扫描 / market mood"** → `twitter_search_tweets` (call once per coin)
For BTC/ETH/SOL sentiment: search `"$BTC"`, `"$ETH"`, `"$SOL"` separately, then summarize tone inline.
⛔ NEVER call `twitter_user_info`, `twitter_user_followers`, or `twitter_user_tweets` during a sentiment scan — text analysis only.
## Available Tools
| Tool | Description | Key Params |
|------|-------------|------------|
| `twitter_search_tweets` | Advanced tweet search | `query` (required), `cursor` |
@@ -128,9 +134,12 @@ For BTC/ETH/SOL sentiment: search `"$BTC"`, `"$ETH"`, `"$SOL"` separately, then
| `twitter_tweet_replies` | Replies to a tweet | `tweet_id` (required), `cursor` |
| `twitter_tweet_retweeters` | Who retweeted | `tweet_id` (required), `cursor` |
| `twitter_search_users` | Search for users | `query` (required), `cursor` |
| `twitter_get_article` | Get long-form article | `tweet_id` (required) |
| `twitter_tweet_thread_context` | Get full thread context | `tweet_id` (required) |
| `twitter_tweet_quote` | Get quote tweets | `tweet_id` (required), `cursor` |
| `twitter_get_trends` | Get trends | `woeid`, `country`, `category`, `limit` |
## Usage Patterns
### ⚠️ Token Budget Rules
- Sentiment scan: max **3 `twitter_search_tweets` calls** (one per coin), then summarize. Stop.
- Account research: max **2 tool calls total** unless user asks for more depth.
@@ -146,7 +155,6 @@ For BTC/ETH/SOL sentiment: search `"$BTC"`, `"$ETH"`, `"$SOL"` separately, then
2. `twitter_search_users` with the topic — find relevant accounts
## Output Constraints (IMPORTANT for small models)
- **Max 1 `twitter_search_tweets` call per coin/topic** — do not repeat searches for same query. First result set is sufficient.
- **Max 3 `twitter_user_info` calls per response** — only look up the most relevant accounts.
- **Never call `bash` or `write_file` for Twitter data** — reason inline directly from tool results.
@@ -165,7 +173,6 @@ For BTC/ETH/SOL sentiment: search `"$BTC"`, `"$ETH"`, `"$SOL"` separately, then
3. `twitter_user_tweets` to check content quality
## Search Query Operators
The `twitter_search_tweets` tool supports advanced operators:
| Operator | Example | Description |
@@ -188,13 +195,11 @@ The `twitter_search_tweets` tool supports advanced operators:
Combine operators: `from:VitalikButerin $ETH min_faves:100 since:2024-01-01`
## Pagination
Most endpoints support cursor-based pagination. When a response includes a cursor value, pass it as the `cursor` parameter to get the next page. If no cursor is returned, you've reached the end.
## Notes
- **API key required**: Set `TWITTER_API_KEY` environment variable. Tools will error without it.
- **Read-only**: These tools only retrieve data. No posting, liking, or following.
- **Usernames**: Always pass without the `@` prefix (e.g. `"elonmusk"` not `"@elonmusk"`).
- **Tweet IDs**: Use string format for tweet IDs to avoid integer overflow issues.
- **Rate limits**: The API has rate limits. If you get rate-limited, wait before retrying.
- **Rate limits**: The API has rate limits. If you get rate-limited, wait before retrying.
+23 -3
View File
@@ -1,9 +1,13 @@
"""
Twitter/X Extension — Read-only data via twitterapi.io
Provides 9 tools for Twitter/X data:
Provides 13 tools for Twitter/X data:
- twitter_search_tweets: Advanced tweet search
- twitter_get_tweets: Get tweets by ID
- twitter_get_article: Get long-form article by tweet ID
- twitter_tweet_thread_context: Get complete thread context
- twitter_tweet_quote: Get quote tweets for a tweet
- twitter_get_trends: Get trends
- twitter_user_info: User profile lookup
- twitter_user_tweets: User's recent tweets
- twitter_user_followers: User's followers
@@ -41,6 +45,10 @@ def register(api) -> List[str]:
from .tools import (
TwitterSearchTweetsTool,
TwitterGetTweetsTool,
TwitterGetArticleTool,
TwitterTweetThreadContextTool,
TwitterTweetQuoteTool,
TwitterGetTrendsTool,
TwitterUserInfoTool,
TwitterUserTweetsTool,
TwitterUserFollowersTool,
@@ -52,6 +60,10 @@ def register(api) -> List[str]:
api.register_tool(TwitterSearchTweetsTool())
api.register_tool(TwitterGetTweetsTool())
api.register_tool(TwitterGetArticleTool())
api.register_tool(TwitterTweetThreadContextTool())
api.register_tool(TwitterTweetQuoteTool())
api.register_tool(TwitterGetTrendsTool())
api.register_tool(TwitterUserInfoTool())
api.register_tool(TwitterUserTweetsTool())
api.register_tool(TwitterUserFollowersTool())
@@ -63,6 +75,10 @@ def register(api) -> List[str]:
registered = [
"twitter_search_tweets",
"twitter_get_tweets",
"twitter_get_article",
"twitter_tweet_thread_context",
"twitter_tweet_quote",
"twitter_get_trends",
"twitter_user_info",
"twitter_user_tweets",
"twitter_user_followers",
@@ -82,11 +98,15 @@ def register(api) -> List[str]:
# Extension metadata
EXTENSION_INFO = {
"name": "twitter",
"version": "1.0.0",
"description": "Twitter/X data — search tweets, user profiles, followers, replies",
"version": "1.1.0",
"description": "Twitter/X data — search tweets, article, thread context, quote tweets, trends, users",
"tools": [
"twitter_search_tweets",
"twitter_get_tweets",
"twitter_get_article",
"twitter_tweet_thread_context",
"twitter_tweet_quote",
"twitter_get_trends",
"twitter_user_info",
"twitter_user_tweets",
"twitter_user_followers",
+33 -2
View File
@@ -1,7 +1,8 @@
"""
Twitter API client — wraps twitterapi.io endpoints.
Read-only: search tweets, user profiles, followers, replies, etc.
Read-only: search tweets, user profiles, followers, replies, thread context,
quote tweets, article, and trends.
Auth: X-API-Key header from TWITTER_API_KEY env var.
"""
@@ -11,6 +12,8 @@ from typing import Any, List
from core.http_client import proxied_get
CALLER_ID = "chat:twitter-skill"
logger = logging.getLogger(__name__)
TWITTERAPI_BASE_URL = "https://api.twitterapi.io"
@@ -31,7 +34,7 @@ class TwitterApiClient:
"""GET a twitterapi.io endpoint."""
url = f"{self.base_url}{path}"
headers = {}
headers = {"SC-CALLER-ID": CALLER_ID}
if self.api_key:
headers["X-API-Key"] = self.api_key
@@ -68,6 +71,34 @@ class TwitterApiClient:
params["cursor"] = cursor
return self._get("/twitter/tweet/retweeters", params)
def get_tweet_thread_context(self, tweet_id: str) -> dict:
"""Get complete thread context for a tweet (parents + direct replies)."""
return self._get("/twitter/tweet/thread_context", {"tweetId": tweet_id})
def get_tweet_quote(self, tweet_id: str, cursor: str = None) -> dict:
"""Get quote tweets for a tweet."""
params = {"tweetId": tweet_id}
if cursor:
params["cursor"] = cursor
return self._get("/twitter/tweet/quotes", params)
def get_article(self, tweet_id: str) -> dict:
"""Get article content by tweet ID (long-form X article)."""
return self._get("/twitter/article", {"tweet_id": tweet_id})
def get_trends(self, woeid: str = None, country: str = None, category: str = None, limit: int = None) -> dict:
"""Get trending topics."""
params = {}
if woeid:
params["woeid"] = woeid
if country:
params["country"] = country
if category:
params["category"] = category
if limit is not None:
params["limit"] = limit
return self._get("/twitter/trends", params)
# ── User Endpoints ───────────────────────────────────────────────────
def get_user_info(self, username: str) -> dict:
+195 -2
View File
@@ -1,8 +1,9 @@
"""
Twitter/X Tools — BaseTool subclasses for agent use.
9 read-only tools: search tweets, get tweets, user info, user tweets,
user followers, user followings, tweet replies, tweet retweeters, search users.
13 read-only tools: search tweets, get tweets, user info, user tweets,
user followers, user followings, tweet replies, tweet retweeters, search users,
article, thread context, quote tweets, trends.
"""
import asyncio
@@ -209,6 +210,198 @@ Returns: users array with profile data and next cursor"""
# ── User Tools ───────────────────────────────────────────────────────────────
class TwitterGetArticleTool(BaseTool):
"""Get long-form article for a tweet."""
@property
def name(self) -> str:
return "twitter_get_article"
@property
def description(self) -> str:
return """Get X/Twitter long-form article by tweet ID.
Parameters:
- tweet_id: Tweet ID of the article post (required)
Returns: article object with title, preview text, cover media URL, and content blocks"""
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"tweet_id": {
"type": "string",
"description": "Tweet ID of the article post",
},
},
"required": ["tweet_id"],
}
async def execute(self, ctx: ToolContext, tweet_id: str = "", **kwargs) -> ToolResult:
if not tweet_id:
return ToolResult(success=False, error="'tweet_id' is required")
try:
client = _get_client()
data = await asyncio.to_thread(client.get_article, tweet_id)
return ToolResult(success=True, output=data)
except Exception as e:
return ToolResult(success=False, error=str(e))
class TwitterTweetThreadContextTool(BaseTool):
"""Get complete thread context for a tweet."""
@property
def name(self) -> str:
return "twitter_tweet_thread_context"
@property
def description(self) -> str:
return """Get complete thread context for a tweet.
Parameters:
- tweet_id: Tweet ID to get thread context for (required)
Returns: parent tweets + direct replies in a single response"""
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"tweet_id": {
"type": "string",
"description": "Tweet ID to get thread context for",
},
},
"required": ["tweet_id"],
}
async def execute(self, ctx: ToolContext, tweet_id: str = "", **kwargs) -> ToolResult:
if not tweet_id:
return ToolResult(success=False, error="'tweet_id' is required")
try:
client = _get_client()
data = await asyncio.to_thread(client.get_tweet_thread_context, tweet_id)
return ToolResult(success=True, output=data)
except Exception as e:
return ToolResult(success=False, error=str(e))
class TwitterTweetQuoteTool(BaseTool):
"""Get quote tweets for a tweet."""
@property
def name(self) -> str:
return "twitter_tweet_quote"
@property
def description(self) -> str:
return """Get quote tweets for a specific tweet.
Parameters:
- tweet_id: Tweet ID to get quote tweets for (required)
- cursor: Pagination cursor from previous response (optional)
Returns: quote tweets list and next cursor"""
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"tweet_id": {
"type": "string",
"description": "Tweet ID to get quote tweets for",
},
"cursor": {
"type": "string",
"description": "Pagination cursor from previous response",
},
},
"required": ["tweet_id"],
}
async def execute(self, ctx: ToolContext, tweet_id: str = "", cursor: str = None, **kwargs) -> ToolResult:
if not tweet_id:
return ToolResult(success=False, error="'tweet_id' is required")
try:
client = _get_client()
data = await asyncio.to_thread(client.get_tweet_quote, tweet_id, cursor=cursor)
return ToolResult(success=True, output=data)
except Exception as e:
return ToolResult(success=False, error=str(e))
class TwitterGetTrendsTool(BaseTool):
"""Get Twitter/X trends."""
@property
def name(self) -> str:
return "twitter_get_trends"
@property
def description(self) -> str:
return """Get Twitter/X trends.
Parameters:
- woeid: Where On Earth ID (optional)
- country: Country code/name (optional)
- category: Trend category (optional)
- limit: Number of trends to return (optional)
Returns: trends list"""
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"woeid": {
"type": "string",
"description": "Where On Earth ID",
},
"country": {
"type": "string",
"description": "Country code/name",
},
"category": {
"type": "string",
"description": "Trend category",
},
"limit": {
"type": "integer",
"description": "Number of trends to return",
},
},
"required": [],
}
async def execute(
self,
ctx: ToolContext,
woeid: str = None,
country: str = None,
category: str = None,
limit: int = None,
**kwargs,
) -> ToolResult:
try:
client = _get_client()
data = await asyncio.to_thread(
client.get_trends,
woeid=woeid,
country=country,
category=category,
limit=limit,
)
return ToolResult(success=True, output=data)
except Exception as e:
return ToolResult(success=False, error=str(e))
class TwitterUserInfoTool(BaseTool):
"""Get a Twitter user's profile information."""