feat: Add AIsa unified API skill - Twitter, Search, Scholar, Financial, LLM

This commit is contained in:
openclaw-aisa-starter 2026-01-30 20:11:33 +08:00
parent da71eaebd2
commit 4336368265
3 changed files with 864 additions and 0 deletions

210
skills/aisa/SKILL.md Normal file
View File

@ -0,0 +1,210 @@
---
name: openclaw-starter-kit
description: "OpenClaw Starter Kit - The definitive starting point for building autonomous agents. Powered by AIsa unified API: Twitter/X (read & write), web search, academic papers, news, and LLM routing with one API key."
homepage: https://openclaw.ai
metadata: {"openclaw":{"emoji":"🦞","requires":{"bins":["curl","python3"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}}
---
# OpenClaw Starter Kit 🦞
**The definitive starting point for autonomous agents. Powered by AIsa.**
One API key. All the data sources your agent needs.
## 🔥 What Can You Do?
### Morning Briefing (Scheduled)
```
"Send me a daily briefing at 8am with:
- My portfolio performance (NVDA, TSLA, BTC)
- Twitter trends in AI
- Top news in my industry"
```
### Competitor Intelligence
```
"Monitor @OpenAI - alert me on new tweets, news mentions, and paper releases"
```
### Investment Research
```
"Full analysis on NVDA: price trends, insider trades, analyst estimates,
SEC filings, and Twitter sentiment"
```
### Startup Validation
```
"Research the market for AI writing tools - find competitors,
Twitter discussions, and academic papers on the topic"
```
### Crypto Whale Alerts
```
"Track large BTC movements and correlate with Twitter activity"
```
## AIsa vs bird
| Feature | AIsa ⚡ | bird 🐦 |
|---------|---------|---------|
| Auth method | API Key (simple) | Browser cookies (complex) |
| Read Twitter | ✅ | ✅ |
| Post/Like/Retweet | ✅ (via login) | ✅ |
| Web Search | ✅ | ❌ |
| Scholar Search | ✅ | ❌ |
| News/Financial | ✅ | ❌ |
| LLM Routing | ✅ | ❌ |
| Server-friendly | ✅ | ❌ |
| Cost | Pay-per-use | Free |
**Use AIsa when**: Server environment, need search/scholar APIs, prefer simple API key setup.
**Use bird when**: Local machine with browser, need free access, complex Twitter interactions.
## Quick Start
```bash
export AISA_API_KEY="your-key"
```
## Core Capabilities
### Twitter/X Data (Read)
```bash
# Get user info
curl "https://api.aisa.one/apis/v1/twitter/user/info?userName=elonmusk" \
-H "Authorization: Bearer $AISA_API_KEY"
# Advanced tweet search
curl "https://api.aisa.one/apis/v1/twitter/tweet/advanced_search?query=AI+agents&queryType=Latest" \
-H "Authorization: Bearer $AISA_API_KEY"
# Get trending topics (worldwide)
curl "https://api.aisa.one/apis/v1/twitter/trends?woeid=1" \
-H "Authorization: Bearer $AISA_API_KEY"
```
### Twitter/X Post (Write)
> ⚠️ **Warning**: Posting requires account login. Use responsibly to avoid rate limits or account suspension.
```bash
# Step 1: Login first (async, check status after)
curl -X POST "https://api.aisa.one/apis/v1/twitter/user_login_v3" \
-H "Authorization: Bearer $AISA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"user_name":"myaccount","email":"me@example.com","password":"xxx","proxy":"http://user:pass@ip:port"}'
# Step 2: Send tweet
curl -X POST "https://api.aisa.one/apis/v1/twitter/send_tweet_v3" \
-H "Authorization: Bearer $AISA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"user_name":"myaccount","text":"Hello from OpenClaw!"}'
# Like / Retweet
curl -X POST "https://api.aisa.one/apis/v1/twitter/like_tweet_v3" \
-H "Authorization: Bearer $AISA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"user_name":"myaccount","tweet_id":"1234567890"}'
```
### Search (Web + Academic)
```bash
# Web search
curl -X POST "https://api.aisa.one/apis/v1/scholar/search/web?query=AI+frameworks&max_num_results=10" \
-H "Authorization: Bearer $AISA_API_KEY"
# Academic/scholar search
curl -X POST "https://api.aisa.one/apis/v1/scholar/search/scholar?query=transformer+models&max_num_results=10" \
-H "Authorization: Bearer $AISA_API_KEY"
# Smart search (web + academic combined)
curl -X POST "https://api.aisa.one/apis/v1/scholar/search/smart?query=machine+learning&max_num_results=10" \
-H "Authorization: Bearer $AISA_API_KEY"
```
### Financial News
```bash
# Company news by ticker
curl "https://api.aisa.one/apis/v1/financial/news?ticker=AAPL&limit=10" \
-H "Authorization: Bearer $AISA_API_KEY"
```
### LLM Routing (OpenAI Compatible)
```bash
curl -X POST "https://api.aisa.one/v1/chat/completions" \
-H "Authorization: Bearer $AISA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]}'
```
Supported models: GPT-4, Claude-3, Gemini, Qwen, Deepseek, Grok, and more.
## Python Client
```bash
# Twitter Read
python3 {baseDir}/scripts/aisa_client.py twitter user-info --username elonmusk
python3 {baseDir}/scripts/aisa_client.py twitter search --query "AI agents"
python3 {baseDir}/scripts/aisa_client.py twitter trends --woeid 1
# Twitter Write (requires login first)
python3 {baseDir}/scripts/aisa_client.py twitter login --username myaccount --email me@example.com --password xxx --proxy "http://user:pass@ip:port"
python3 {baseDir}/scripts/aisa_client.py twitter post --username myaccount --text "Hello!"
python3 {baseDir}/scripts/aisa_client.py twitter like --username myaccount --tweet-id 1234567890
# Search
python3 {baseDir}/scripts/aisa_client.py search web --query "latest AI news"
python3 {baseDir}/scripts/aisa_client.py search scholar --query "LLM research"
python3 {baseDir}/scripts/aisa_client.py search smart --query "machine learning"
# News
python3 {baseDir}/scripts/aisa_client.py news --ticker AAPL
# LLM
python3 {baseDir}/scripts/aisa_client.py llm complete --model gpt-4 --prompt "Explain quantum computing"
```
## Pricing
| API | Cost |
|-----|------|
| Twitter query | ~$0.0004 |
| Twitter post/like | ~$0.001 |
| Web search | ~$0.001 |
| Scholar search | ~$0.002 |
| News | ~$0.001 |
| LLM | Token-based |
Every response includes `usage.cost` and `usage.credits_remaining`.
## Error Handling
Errors return JSON with `error` field:
```json
{
"error": "Invalid API key",
"code": 401
}
```
Common error codes:
- `401` - Invalid or missing API key
- `402` - Insufficient credits
- `429` - Rate limit exceeded
- `500` - Server error
## Get Started
1. Sign up at [aisa.one](https://aisa.one)
2. Get your API key
3. Add credits (pay-as-you-go)
4. Set environment variable: `export AISA_API_KEY="your-key"`
## Full API Reference
See [references/api-reference.md](references/api-reference.md) for complete endpoint documentation.

View File

@ -0,0 +1,198 @@
# OpenClaw Starter Kit - API Reference
**Powered by AIsa**
Complete API documentation based on [aisa.mintlify.app](https://aisa.mintlify.app/api-reference/introduction).
## Base URL
```
https://api.aisa.one/apis/v1
```
## Authentication
All requests require a Bearer token:
```
Authorization: Bearer YOUR_AISA_API_KEY
```
---
## Twitter/X APIs
### GET /twitter/user/info
Get user information by username.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| userName | string | Yes | Twitter username (without @) |
### GET /twitter/tweet/advanced_search
Advanced search for tweets.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| query | string | Yes | Search query |
| queryType | string | Yes | "Latest" or "Top" |
| cursor | string | No | Pagination cursor |
### GET /twitter/user/user_last_tweet
Get user's recent tweets.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| userName | string | Yes | Twitter username |
### GET /twitter/tweet/tweetById
Get tweets by IDs.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| tweet_ids | string | Yes | Comma-separated tweet IDs |
### GET /twitter/trends
Get trending topics by WOEID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| woeid | integer | Yes | WOEID (1 = worldwide) |
| count | integer | No | Number of trends (default 30) |
### GET /twitter/user/search_user
Search for users by keyword.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| keyword | string | Yes | Search keyword |
---
## Search APIs
### POST /scholar/search/web
Web search with structured results.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| query | string | Yes | Search query |
| max_num_results | integer | No | Max results (1-100, default 10) |
| as_ylo | integer | No | Year lower bound |
| as_yhi | integer | No | Year upper bound |
### POST /scholar/search/scholar
Academic paper search.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| query | string | Yes | Search query |
| max_num_results | integer | No | Max results (1-100, default 10) |
| as_ylo | integer | No | Year lower bound |
| as_yhi | integer | No | Year upper bound |
### POST /scholar/search/smart
Intelligent search combining web and academic results.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| query | string | Yes | Search query |
| max_num_results | integer | No | Max results |
---
## Tavily APIs
### POST /tavily/search
Tavily search integration.
### POST /tavily/extract
Extract content from URLs.
### POST /tavily/crawl
Crawl web pages.
---
## Financial APIs
### GET /financial/news/company
Company news by ticker.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| ticker | string | Yes | Stock ticker (e.g., AAPL) |
| limit | integer | No | Number of articles |
### Other Financial Endpoints
- `/financial/stock/prices` - Historical stock prices
- `/financial/financial_statements/*` - Income, balance, cash flow
- `/financial/company/facts` - Company facts by CIK
- `/financial/search/stock` - Stock screener
---
## LLM APIs (OpenAI Compatible)
Base URL for LLM: `https://api.aisa.one/v1`
### POST /v1/chat/completions
OpenAI-compatible chat completions.
```json
{
"model": "gpt-4",
"messages": [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Hello!"}
],
"max_tokens": 1000,
"temperature": 0.7
}
```
**Supported Models:**
| Provider | Models |
|----------|--------|
| OpenAI | gpt-4, gpt-4-turbo, gpt-3.5-turbo |
| Anthropic | claude-3-opus, claude-3-sonnet, claude-3-haiku |
| Google | gemini-pro, gemini-ultra |
| Alibaba | qwen-* |
| Deepseek | deepseek-* |
| xAI | grok-* |
---
## Error Handling
```json
{
"error": "error message",
"code": 400,
"details": "additional info"
}
```
---
## Full Documentation
For complete API documentation including all endpoints:
- [AIsa API Reference](https://aisa.mintlify.app/api-reference/introduction)
- [Documentation Index](https://aisa.mintlify.app/llms.txt)

View File

@ -0,0 +1,456 @@
#!/usr/bin/env python3
"""
OpenClaw Starter Kit - AIsa API Client
Powered by AIsa (https://aisa.one)
Unified API access for autonomous agents.
Usage:
python aisa_client.py twitter user-info --username <username>
python aisa_client.py twitter tweets --username <username> [--count <n>]
python aisa_client.py twitter search --query <query> [--count <n>]
python aisa_client.py twitter detail --tweet-id <id>
python aisa_client.py twitter trends
python aisa_client.py search web --query <query> [--count <n>]
python aisa_client.py search scholar --query <query> [--count <n>]
python aisa_client.py news --query <query> [--count <n>]
python aisa_client.py llm complete --model <model> --prompt <prompt>
python aisa_client.py llm chat --model <model> --messages <json>
"""
import argparse
import json
import os
import sys
import urllib.request
import urllib.parse
import urllib.error
from typing import Any, Dict, Optional
class AIsaClient:
"""OpenClaw Starter Kit - AIsa API Client for unified access to AI-native data sources."""
BASE_URL = "https://api.aisa.one/apis/v1"
LLM_BASE_URL = "https://api.aisa.one/v1"
def __init__(self, api_key: Optional[str] = None):
"""Initialize the client with an API key."""
self.api_key = api_key or os.environ.get("AISA_API_KEY")
if not self.api_key:
raise ValueError(
"AISA_API_KEY is required. Set it via environment variable or pass to constructor."
)
def _request(
self,
method: str,
endpoint: str,
params: Optional[Dict[str, Any]] = None,
data: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Make an HTTP request to the AIsa API."""
url = f"{self.BASE_URL}{endpoint}"
if params:
query_string = urllib.parse.urlencode(
{k: v for k, v in params.items() if v is not None}
)
url = f"{url}?{query_string}"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
"User-Agent": "OpenClaw-Starter-Kit/1.0"
}
request_data = None
if data:
request_data = json.dumps(data).encode("utf-8")
# For POST requests without body, send empty JSON
if method == "POST" and request_data is None:
request_data = b"{}"
req = urllib.request.Request(url, data=request_data, headers=headers, method=method)
try:
with urllib.request.urlopen(req, timeout=60) as response:
return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as e:
error_body = e.read().decode("utf-8")
try:
return json.loads(error_body)
except json.JSONDecodeError:
return {"success": False, "error": {"code": str(e.code), "message": error_body}}
except urllib.error.URLError as e:
return {"success": False, "error": {"code": "NETWORK_ERROR", "message": str(e.reason)}}
# ==================== Twitter APIs ====================
def twitter_user_info(self, username: str) -> Dict[str, Any]:
"""Get Twitter user information by username."""
return self._request("GET", "/twitter/user/info", params={"userName": username})
def twitter_user_tweets(self, username: str, count: int = 20) -> Dict[str, Any]:
"""Get tweets from a specific user."""
return self._request("GET", "/twitter/user/user_last_tweet", params={"userName": username})
def twitter_search(self, query: str, query_type: str = "Latest") -> Dict[str, Any]:
"""Search for tweets matching a query (Advanced Search)."""
return self._request("GET", "/twitter/tweet/advanced_search", params={
"query": query,
"queryType": query_type # "Latest" or "Top"
})
def twitter_tweet_detail(self, tweet_ids: str) -> Dict[str, Any]:
"""Get detailed information about tweets by IDs (comma-separated)."""
return self._request("GET", "/twitter/tweet/tweetById", params={"tweet_ids": tweet_ids})
def twitter_trends(self, woeid: int = 1) -> Dict[str, Any]:
"""Get current Twitter trending topics by WOEID (1 = worldwide)."""
return self._request("GET", "/twitter/trends", params={"woeid": woeid})
def twitter_user_search(self, keyword: str) -> Dict[str, Any]:
"""Search for Twitter users by keyword."""
return self._request("GET", "/twitter/user/search_user", params={"keyword": keyword})
# ==================== Twitter Post APIs (V3 - requires login) ====================
def twitter_login(self, username: str, email: str, password: str, proxy: str, totp_code: str = None) -> Dict[str, Any]:
"""Login to Twitter account (V3). Required before posting."""
data = {
"user_name": username,
"email": email,
"password": password,
"proxy": proxy
}
if totp_code:
data["totp_code"] = totp_code
return self._request("POST", "/twitter/user_login_v3", data=data)
def twitter_get_account(self, username: str) -> Dict[str, Any]:
"""Get logged-in account details (check login status)."""
return self._request("GET", "/twitter/get_my_x_account_detail_v3", params={"user_name": username})
def twitter_send_tweet(self, username: str, text: str, media_base64: str = None, media_type: str = None, community_id: str = None) -> Dict[str, Any]:
"""Send a tweet (requires prior login via twitter_login)."""
data = {
"user_name": username,
"text": text
}
if media_base64:
data["media_data_base64"] = media_base64
if media_type:
data["media_type"] = media_type
if community_id:
data["community_id"] = community_id
return self._request("POST", "/twitter/send_tweet_v3", data=data)
def twitter_like(self, username: str, tweet_id: str) -> Dict[str, Any]:
"""Like a tweet (requires prior login)."""
return self._request("POST", "/twitter/like_tweet_v3", data={
"user_name": username,
"tweet_id": tweet_id
})
def twitter_retweet(self, username: str, tweet_id: str) -> Dict[str, Any]:
"""Retweet a tweet (requires prior login)."""
return self._request("POST", "/twitter/retweet_v3", data={
"user_name": username,
"tweet_id": tweet_id
})
# ==================== Search APIs ====================
def search_web(self, query: str, max_results: int = 10) -> Dict[str, Any]:
"""Perform a web search (POST method)."""
return self._request("POST", "/scholar/search/web", params={
"query": query,
"max_num_results": max_results
})
def search_scholar(self, query: str, max_results: int = 10, year_from: int = None, year_to: int = None) -> Dict[str, Any]:
"""Search academic papers and scholarly content (POST method)."""
params = {
"query": query,
"max_num_results": max_results
}
if year_from:
params["as_ylo"] = year_from
if year_to:
params["as_yhi"] = year_to
return self._request("POST", "/scholar/search/scholar", params=params)
def search_smart(self, query: str, max_results: int = 10) -> Dict[str, Any]:
"""Perform intelligent search combining web and academic results."""
return self._request("POST", "/scholar/search/smart", params={
"query": query,
"max_num_results": max_results
})
# ==================== News API ====================
def news(self, ticker: str, count: int = 10) -> Dict[str, Any]:
"""Get company news by stock ticker."""
return self._request("GET", "/financial/news", params={"ticker": ticker, "limit": count})
# ==================== LLM APIs ====================
def _llm_request(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
"""Make an HTTP request to the AIsa LLM API (different base URL)."""
url = f"{self.LLM_BASE_URL}{endpoint}"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
"User-Agent": "OpenClaw-Starter-Kit/1.0"
}
request_data = json.dumps(data).encode("utf-8")
req = urllib.request.Request(url, data=request_data, headers=headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=120) as response:
return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as e:
error_body = e.read().decode("utf-8")
try:
return json.loads(error_body)
except json.JSONDecodeError:
return {"success": False, "error": {"code": str(e.code), "message": error_body}}
except urllib.error.URLError as e:
return {"success": False, "error": {"code": "NETWORK_ERROR", "message": str(e.reason)}}
def llm_complete(self, model: str, prompt: str, **kwargs) -> Dict[str, Any]:
"""Generate a completion using the specified LLM model."""
data = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
**kwargs
}
return self._llm_request("/chat/completions", data)
def llm_chat(self, model: str, messages: list, **kwargs) -> Dict[str, Any]:
"""Perform a chat completion with message history."""
data = {
"model": model,
"messages": messages,
**kwargs
}
return self._llm_request("/chat/completions", data)
def main():
"""Main CLI entry point."""
parser = argparse.ArgumentParser(
description="OpenClaw Starter Kit - Unified API access for autonomous agents (Powered by AIsa)",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s twitter user-info --username elonmusk
%(prog)s twitter search --query "AI agents" --count 10
%(prog)s search web --query "latest AI news"
%(prog)s search scholar --query "transformer architecture"
%(prog)s llm complete --model gpt-4 --prompt "Explain quantum computing"
"""
)
subparsers = parser.add_subparsers(dest="command", help="API category")
# Twitter commands
twitter_parser = subparsers.add_parser("twitter", help="Twitter/X API operations")
twitter_sub = twitter_parser.add_subparsers(dest="action", help="Twitter action")
# twitter user-info
user_info = twitter_sub.add_parser("user-info", help="Get user information")
user_info.add_argument("--username", "-u", required=True, help="Twitter username")
# twitter tweets
tweets = twitter_sub.add_parser("tweets", help="Get user's last tweets")
tweets.add_argument("--username", "-u", required=True, help="Twitter username")
# twitter search
search = twitter_sub.add_parser("search", help="Advanced tweet search")
search.add_argument("--query", "-q", required=True, help="Search query")
search.add_argument("--type", "-t", choices=["Latest", "Top"], default="Latest", help="Query type")
# twitter detail
detail = twitter_sub.add_parser("detail", help="Get tweets by IDs")
detail.add_argument("--tweet-ids", "-t", required=True, help="Tweet IDs (comma-separated)")
# twitter trends
trends = twitter_sub.add_parser("trends", help="Get trending topics")
trends.add_argument("--woeid", "-w", type=int, default=1, help="WOEID (1=worldwide)")
# twitter user-search
user_search = twitter_sub.add_parser("user-search", help="Search for users")
user_search.add_argument("--keyword", "-k", required=True, help="Search keyword")
# twitter login (V3)
login = twitter_sub.add_parser("login", help="Login to Twitter account (required for posting)")
login.add_argument("--username", "-u", required=True, help="Twitter username")
login.add_argument("--email", "-e", required=True, help="Account email")
login.add_argument("--password", "-p", required=True, help="Account password")
login.add_argument("--proxy", required=True, help="Proxy URL (http://user:pass@ip:port)")
login.add_argument("--totp", help="TOTP 2FA secret (recommended)")
# twitter account (check login status)
account = twitter_sub.add_parser("account", help="Check logged-in account status")
account.add_argument("--username", "-u", required=True, help="Twitter username")
# twitter post (send tweet)
post = twitter_sub.add_parser("post", help="Send a tweet (requires login)")
post.add_argument("--username", "-u", required=True, help="Twitter username")
post.add_argument("--text", "-t", required=True, help="Tweet text content")
post.add_argument("--media", help="Base64 encoded media data")
post.add_argument("--media-type", choices=["image/jpeg", "image/png", "image/gif", "video/mp4"], help="Media MIME type")
post.add_argument("--community", help="Community ID to post to")
# twitter like
like = twitter_sub.add_parser("like", help="Like a tweet (requires login)")
like.add_argument("--username", "-u", required=True, help="Twitter username")
like.add_argument("--tweet-id", "-t", required=True, help="Tweet ID to like")
# twitter retweet
retweet = twitter_sub.add_parser("retweet", help="Retweet a tweet (requires login)")
retweet.add_argument("--username", "-u", required=True, help="Twitter username")
retweet.add_argument("--tweet-id", "-t", required=True, help="Tweet ID to retweet")
# Search commands
search_parser = subparsers.add_parser("search", help="Search API operations")
search_sub = search_parser.add_subparsers(dest="action", help="Search type")
# search web
web_search = search_sub.add_parser("web", help="Web search")
web_search.add_argument("--query", "-q", required=True, help="Search query")
web_search.add_argument("--count", "-c", type=int, default=10, help="Max results (up to 100)")
# search scholar
scholar_search = search_sub.add_parser("scholar", help="Academic paper search")
scholar_search.add_argument("--query", "-q", required=True, help="Search query")
scholar_search.add_argument("--count", "-c", type=int, default=10, help="Max results (up to 100)")
scholar_search.add_argument("--year-from", type=int, help="Publication year lower bound")
scholar_search.add_argument("--year-to", type=int, help="Publication year upper bound")
# search smart
smart_search = search_sub.add_parser("smart", help="Smart search (web + academic)")
smart_search.add_argument("--query", "-q", required=True, help="Search query")
smart_search.add_argument("--count", "-c", type=int, default=10, help="Max results")
# News commands
news_parser = subparsers.add_parser("news", help="Company news by ticker")
news_parser.add_argument("--ticker", "-t", required=True, help="Stock ticker (e.g., AAPL)")
news_parser.add_argument("--count", "-c", type=int, default=10, help="Number of results")
# LLM commands
llm_parser = subparsers.add_parser("llm", help="LLM API operations")
llm_sub = llm_parser.add_subparsers(dest="action", help="LLM action")
# llm complete
complete = llm_sub.add_parser("complete", help="Generate completion")
complete.add_argument("--model", "-m", required=True, help="Model name (e.g., gpt-4, claude-3)")
complete.add_argument("--prompt", "-p", required=True, help="Prompt text")
complete.add_argument("--max-tokens", type=int, help="Maximum tokens to generate")
complete.add_argument("--temperature", type=float, help="Sampling temperature")
# llm chat
chat = llm_sub.add_parser("chat", help="Chat completion")
chat.add_argument("--model", "-m", required=True, help="Model name")
chat.add_argument("--messages", required=True, help="JSON array of messages")
chat.add_argument("--max-tokens", type=int, help="Maximum tokens to generate")
chat.add_argument("--temperature", type=float, help="Sampling temperature")
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
try:
client = AIsaClient()
except ValueError as e:
print(json.dumps({"success": False, "error": {"code": "AUTH_ERROR", "message": str(e)}}))
sys.exit(1)
result = None
# Execute the appropriate command
if args.command == "twitter":
if args.action == "user-info":
result = client.twitter_user_info(args.username)
elif args.action == "tweets":
result = client.twitter_user_tweets(args.username)
elif args.action == "search":
result = client.twitter_search(args.query, args.type)
elif args.action == "detail":
result = client.twitter_tweet_detail(args.tweet_ids)
elif args.action == "trends":
result = client.twitter_trends(args.woeid)
elif args.action == "user-search":
result = client.twitter_user_search(args.keyword)
# V3 APIs (require login)
elif args.action == "login":
result = client.twitter_login(args.username, args.email, args.password, args.proxy, args.totp)
elif args.action == "account":
result = client.twitter_get_account(args.username)
elif args.action == "post":
result = client.twitter_send_tweet(args.username, args.text, args.media, args.media_type, args.community)
elif args.action == "like":
result = client.twitter_like(args.username, args.tweet_id)
elif args.action == "retweet":
result = client.twitter_retweet(args.username, args.tweet_id)
else:
twitter_parser.print_help()
sys.exit(1)
elif args.command == "search":
if args.action == "web":
result = client.search_web(args.query, args.count)
elif args.action == "scholar":
year_from = getattr(args, 'year_from', None)
year_to = getattr(args, 'year_to', None)
result = client.search_scholar(args.query, args.count, year_from, year_to)
elif args.action == "smart":
result = client.search_smart(args.query, args.count)
else:
search_parser.print_help()
sys.exit(1)
elif args.command == "news":
result = client.news(args.ticker, args.count)
elif args.command == "llm":
kwargs = {}
if hasattr(args, "max_tokens") and args.max_tokens:
kwargs["max_tokens"] = args.max_tokens
if hasattr(args, "temperature") and args.temperature is not None:
kwargs["temperature"] = args.temperature
if args.action == "complete":
result = client.llm_complete(args.model, args.prompt, **kwargs)
elif args.action == "chat":
try:
messages = json.loads(args.messages)
except json.JSONDecodeError:
print(json.dumps({"success": False, "error": {"code": "INVALID_JSON", "message": "Invalid JSON in --messages"}}))
sys.exit(1)
result = client.llm_chat(args.model, messages, **kwargs)
else:
llm_parser.print_help()
sys.exit(1)
# Output result
if result:
# Handle encoding for Windows console
output = json.dumps(result, indent=2, ensure_ascii=False)
try:
print(output)
except UnicodeEncodeError:
# Fallback to ASCII-safe output
print(json.dumps(result, indent=2, ensure_ascii=True))
sys.exit(0 if result.get("success", True) else 1)
if __name__ == "__main__":
main()