Akış
wss wss://ws.xsignalfeed.com/v1/stream
Bağlanma
Akış, anahtarınızı REST ile aynı üç yoldan kabul eder. Sunucudan başlık kullanın; URL parametresini yalnızca başlık ayarlanamayan yerlerde kullanın. Anahtar ulaşmazsa ya da kabul edilmezse bağlantı açılır ve hemen 4001 ile kapanır; kapanma nedeni sebebini söyler.
| Anahtarın gönderilme yolu | Ne zaman kullanılır |
|---|---|
Authorization: Bearer xsf_… | Sunucular için önerilir; REST ile aynı başlık |
X-API-Key: xsf_… | Yukarıdakiyle eşdeğer |
?api_key=xsf_… | Tarayıcılar ve el sıkışmada başlık ayarlayamayan istemciler. Anahtar URL'nin parçası olur; bu tür URL'leri günlüklerin dışında tutun |
python — anahtar başlıkta
import websockets async with websockets.connect("wss://ws.xsignalfeed.com/v1/stream", additional_headers={"Authorization": "Bearer xsf_your_key"}) as ws: print(await ws.recv()) # {"t": "system.hello", ...}
| Sorgu parametresi | Anlamı |
|---|---|
| api_key | Başlığı ayarlayamadığınızda anahtarınız |
| last_id | Bu sid değerinden sonra devam et. Anahtarda replay kapsamı yoksa sessizce yok sayılır |
import asyncio, json, websockets async def main(): url = "wss://ws.xsignalfeed.com/v1/stream?api_key=xsf_your_key" async with websockets.connect(url) as ws: async for raw in ws: ev = json.loads(raw) if ev["t"].startswith("system."): continue print(ev["t"], ev["target"]["screen_name"], ev["d"].get("text", "")[:60]) asyncio.run(main())
import WebSocket from "ws"; const ws = new WebSocket("wss://ws.xsignalfeed.com/v1/stream?api_key=xsf_your_key"); ws.on("message", (raw) => { const ev = JSON.parse(raw); if (ev.t.startsWith("system.")) return; console.log(ev.t, ev.target.screen_name, (ev.d.text ?? "").slice(0, 60)); });
c, _, err := websocket.DefaultDialer.Dial("wss://ws.xsignalfeed.com/v1/stream?api_key=xsf_your_key", nil) for { _, raw, err := c.ReadMessage() if err != nil { break } var ev Event json.Unmarshal(raw, &ev) if strings.HasPrefix(ev.T, "system.") { continue } log.Println(ev.T, ev.Target.ScreenName) }
Çerçeve
Üç çerçeve X'in değil, bizimdir:
| Çerçeve | Ne zaman |
|---|---|
system.hello | İlk çerçeve. targets, bu anahtarın şu anda izlediği hesap sayısıdır (sonradan eklenen hesaplar yeniden bağlanmadan katılır); replay_from, yeniden bağlanırken gönderdiğiniz last_id değerini geri verir, hiç göndermediyseniz null olur ve bu durumda hiçbir şey yeniden oynatılmaz |
system.ping | Sessizlik sırasında canlı tutma amacıyla gönderilir; boştaki proxy'ler soketi kapatmasın diye |
system.error | Bağlantıda bir sorun var; ardından kapanış gelir |
Kapanış kodu
El sıkışma her zaman başarılı olur ve kapanış hemen ardından gelir; böylece istemci kütüphaneniz çıplak bir HTTP hatası yerine kodu ve nedeni görür.
| Kapanış kodu | Anlamı |
|---|---|
1012 | Bakım sırasında hizmetin yeniden başlatılması. Geri çekilmeyle yeniden bağlanın ve işlediğiniz son sid değerini ?last_id= olarak gönderin: kesinti sırasındaki olaylar yeniden oynatılır, hiçbir şey kaybolmaz |
4001 | Anahtar kabul edilmedi. Kapanma nedeni sebebini söyler: missing_api_key (bize hiç anahtar ulaşmadı: başlık adını ya da proxy'nizin veya istemci kütüphanenizin onu iletip iletmediğini kontrol edin), invalid_api_key (böyle bir anahtar yok: yazım hatası ya da döndürmeyle değişmiş bir gizli anahtar), key_revoked (anahtar iptal edildi) |
4002 | Bu anahtar için çok fazla eşzamanlı bağlantı; kapanıştan önce bir system.error çerçevesi gelir |
4003 | Anahtarda stream kapsamı yok |
Doğru yeniden bağlanma
Bunu naif sürümüyle değil, bir kez doğru yazın. Önemli olan iki şey üstel geri çekilme ve sid değerini süreç yeniden başlatmasını atlatacak bir yerde kalıcı saklamaktır.
python — üretim biçimi
import asyncio, json, pathlib, random, websockets STATE = pathlib.Path("xsf.cursor") # survives restarts; a DB row is better still async def run(key, handle): backoff = 1 while True: last = STATE.read_text().strip() if STATE.exists() else None url = f"wss://ws.xsignalfeed.com/v1/stream?api_key={key}" + (f"&last_id={last}" if last else "") try: async with websockets.connect(url, ping_interval=20, max_size=2**22) as ws: backoff = 1 async for raw in ws: ev = json.loads(raw) if ev["t"].startswith("system."): continue handle(ev) # must be idempotent: dedupe on ev["id"] if ev.get("sid"): STATE.write_text(ev["sid"]) # only after handling except Exception as e: print("disconnected:", e) await asyncio.sleep(backoff + random.random()) # jitter, or every client returns at once backoff = min(60, backoff * 2)