UNPKG

x-developer

Version:

X (Twitter) data platform skill for AI coding agents. 100+ REST API endpoints, 2 MCP tools, 23 extraction types, HMAC webhooks.

168 lines (133 loc) 5.02 kB
# Xquik Python Examples Python equivalents of the JavaScript examples in SKILL.md. ## Authentication ```python import json import urllib.error import urllib.request API_KEY = "xq_YOUR_KEY_HERE" BASE = "https://xquik.com/api/v1" HEADERS = {"x-api-key": API_KEY, "Content-Type": "application/json"} ``` ## Retry with Exponential Backoff ```python import time, random def xquik_fetch(path, method="GET", json_body=None, max_retries=3): base_delay = 1.0 for attempt in range(max_retries + 1): retry_after = None body = json.dumps(json_body).encode() if json_body is not None else None request = urllib.request.Request( f"{BASE}{path}", data=body, headers=HEADERS, method=method ) try: with urllib.request.urlopen(request, timeout=30) as response: return json.loads(response.read()) except urllib.error.HTTPError as error: status = error.code payload = json.loads(error.read() or b"{}") retry_after = error.headers.get("Retry-After") retryable = status == 429 or status >= 500 if not retryable or attempt == max_retries: raise Exception(f"Xquik API {status}: {payload.get('error', 'request failed')}") delay = int(retry_after) if retry_after else base_delay * (2 ** attempt) + random.uniform(0, 1) time.sleep(delay) ``` ## Extraction Workflow ```python # Step 1: Estimate estimate = xquik_fetch("/extractions/estimate", method="POST", json_body={ "toolType": "reply_extractor", "targetTweetId": "1893704267862470862", }) if not estimate["allowed"]: print(f"Need {estimate['creditsRequired']} credits; available {estimate['creditsAvailable']}") exit() # Step 2: Create job job = xquik_fetch("/extractions", method="POST", json_body={ "toolType": "reply_extractor", "targetTweetId": "1893704267862470862", }) # Step 3: Poll until complete (large jobs may return "running") while job["status"] in ("pending", "running"): time.sleep(2) job = xquik_fetch(f"/extractions/{job['id']}") # Step 4: Get results cursor = None results = [] while True: path = f"/extractions/{job['id']}" if cursor: path += f"?after={cursor}" page = xquik_fetch(path) results.extend(page["results"]) if not page["hasMore"]: break cursor = page["nextCursor"] print(f"Extracted {len(results)} results") ``` ## Giveaway Draw ```python # Create draw with all filters draw = xquik_fetch("/draws", method="POST", json_body={ "tweetUrl": "https://x.com/burakbayir/status/1893456789012345678", "winnerCount": 3, "backupCount": 2, "uniqueAuthorsOnly": True, "mustRetweet": True, "mustFollowUsername": "burakbayir", "filterMinFollowers": 50, "filterAccountAgeDays": 30, "requiredKeywords": ["giveaway"], }) # Get winners details = xquik_fetch(f"/draws/{draw['id']}") for winner in details["winners"]: role = "BACKUP" if winner["isBackup"] else "WINNER" print(f"{role} #{winner['position']}: @{winner['authorUsername']}") ``` ## Webhook Handler (Python Standard Library) ```python import hashlib import hmac import json import os from http.server import BaseHTTPRequestHandler, HTTPServer # Per-webhook secret from POST /webhooks response, not a Xquik account credential WEBHOOK_SECRET = os.environ["XQUIK_WEBHOOK_SECRET"] processed_hashes = set() # Use Redis/DB in production def verify_signature(payload: bytes, signature: str, secret: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature) EVENT_HANDLERS = { "tweet.new": lambda u, d: print(f"New tweet from @{u}: {d['text']}"), "tweet.reply": lambda u, d: print(f"Reply from @{u}: {d['text']}"), "tweet.quote": lambda u, d: print(f"Quote from @{u}: {d['text']}"), "tweet.retweet": lambda u, d: print(f"Retweet by @{u}"), } class WebhookHandler(BaseHTTPRequestHandler): def do_POST(self): length = int(self.headers.get("Content-Length", "0")) signature = self.headers.get("X-Xquik-Signature", "") payload = self.rfile.read(length) if not verify_signature(payload, signature, WEBHOOK_SECRET): self.send_response(401) self.end_headers() self.wfile.write(b"Invalid signature") return payload_hash = hashlib.sha256(payload).hexdigest() if payload_hash in processed_hashes: self.send_response(200) self.end_headers() self.wfile.write(b"Already processed") return processed_hashes.add(payload_hash) event = json.loads(payload) handler = EVENT_HANDLERS.get(event["eventType"]) if handler: handler(event["username"], event["data"]) self.send_response(200) self.end_headers() self.wfile.write(b"OK") HTTPServer(("", 3000), WebhookHandler).serve_forever() ```