180 lines
6.3 KiB
Python
180 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
probe_cliproxy.py — Thăm dò CLIProxy để tìm command/channel hợp lệ
|
|
"""
|
|
import socket
|
|
import sys
|
|
|
|
HOST = "127.0.0.1"
|
|
PORT = 8317
|
|
PASSWORD = "123456"
|
|
|
|
|
|
# ── RESP helpers ────────────────────────────────────────────────────────────
|
|
|
|
def resp_command(*parts):
|
|
data = [f"*{len(parts)}\r\n".encode()]
|
|
for part in parts:
|
|
b = str(part).encode()
|
|
data.append(f"${len(b)}\r\n".encode())
|
|
data.append(b + b"\r\n")
|
|
return b"".join(data)
|
|
|
|
|
|
class Client:
|
|
def __init__(self, host, port, password, timeout=5):
|
|
self.sock = socket.create_connection((host, port), timeout=timeout)
|
|
self.file = self.sock.makefile("rb")
|
|
self._send("AUTH", password)
|
|
reply = self._read()
|
|
if not (isinstance(reply, str) and reply.upper() == "OK"):
|
|
raise RuntimeError(f"AUTH failed: {reply!r}")
|
|
|
|
def close(self):
|
|
try: self.file.close()
|
|
finally: self.sock.close()
|
|
|
|
def _send(self, *parts):
|
|
self.sock.sendall(resp_command(*parts))
|
|
|
|
def _read_line(self):
|
|
line = self.file.readline()
|
|
if not line:
|
|
raise EOFError("Connection closed")
|
|
return line.rstrip(b"\r\n")
|
|
|
|
def _read(self):
|
|
line = self._read_line()
|
|
prefix, payload = line[:1], line[1:]
|
|
if prefix == b"+": return payload.decode()
|
|
if prefix == b"-": raise RuntimeError(payload.decode())
|
|
if prefix == b":": return int(payload)
|
|
if prefix == b"$":
|
|
length = int(payload)
|
|
if length == -1: return None
|
|
data = self.file.read(length)
|
|
self.file.read(2)
|
|
return data.decode("utf-8", "replace")
|
|
if prefix == b"*":
|
|
count = int(payload)
|
|
if count == -1: return None
|
|
return [self._read() for _ in range(count)]
|
|
raise RuntimeError(f"Unknown RESP prefix: {line!r}")
|
|
|
|
def cmd(self, *parts):
|
|
"""Gửi command, trả về (ok, result). Không raise exception."""
|
|
self._send(*parts)
|
|
try:
|
|
return True, self._read()
|
|
except RuntimeError as e:
|
|
return False, str(e)
|
|
except Exception as e:
|
|
return False, f"[{type(e).__name__}] {e}"
|
|
|
|
|
|
# ── Probe logic ──────────────────────────────────────────────────────────────
|
|
|
|
def section(title):
|
|
print(f"\n{'─'*55}")
|
|
print(f" {title}")
|
|
print(f"{'─'*55}")
|
|
|
|
|
|
def probe(c):
|
|
# 1. Các meta-command thường gặp
|
|
section("1. Meta commands (HELP / INFO / PING / COMMAND)")
|
|
for cmd in ["PING", "HELP", "INFO", "COMMAND", "COMMAND COUNT", "COMMAND DOCS"]:
|
|
ok, val = c.cmd(*cmd.split())
|
|
status = "✅ OK" if ok else "❌"
|
|
preview = str(val)[:120].replace("\r\n", "\\r\\n") if val else "nil"
|
|
print(f" {cmd:<20} {status} → {preview}")
|
|
|
|
# 2. Thử RPOP với các channel name phổ biến
|
|
section("2. RPOP — thử các channel name")
|
|
channels = [
|
|
"events", "queue", "logs", "usage", "requests",
|
|
"stream", "data", "codex", "proxy", "api",
|
|
"messages", "output", "items", "records",
|
|
]
|
|
valid_channels = []
|
|
for ch in channels:
|
|
ok, val = c.cmd("RPOP", ch)
|
|
if ok:
|
|
preview = str(val)[:80] if val else "nil (rỗng)"
|
|
print(f" RPOP {ch:<16} ✅ OK → {preview}")
|
|
valid_channels.append(ch)
|
|
else:
|
|
print(f" RPOP {ch:<16} ❌ {val}")
|
|
|
|
# 3. Thử LRANGE (nếu server dùng list-based)
|
|
section("3. LRANGE — xem nội dung list (không xoá)")
|
|
for ch in channels:
|
|
ok, val = c.cmd("LRANGE", ch, "0", "2")
|
|
if ok:
|
|
preview = str(val)[:100] if val else "[]"
|
|
print(f" LRANGE {ch:<14} ✅ OK → {preview}")
|
|
|
|
# 4. Thử LLEN (độ dài list)
|
|
section("4. LLEN — độ dài list")
|
|
for ch in channels:
|
|
ok, val = c.cmd("LLEN", ch)
|
|
if ok:
|
|
print(f" LLEN {ch:<16} ✅ OK → {val}")
|
|
|
|
# 5. Thử KEYS nếu server hỗ trợ
|
|
section("5. KEYS * — liệt kê tất cả key")
|
|
ok, val = c.cmd("KEYS", "*")
|
|
if ok:
|
|
print(f" ✅ KEYS * → {val}")
|
|
else:
|
|
print(f" ❌ KEYS * → {val}")
|
|
|
|
# 6. Thử SUBSCRIBE/PSUBSCRIBE (pub/sub mode)
|
|
section("6. SUBSCRIBE — thử pub/sub channels")
|
|
for ch in ["events", "logs", "usage", "requests"]:
|
|
ok, val = c.cmd("SUBSCRIBE", ch)
|
|
if ok:
|
|
print(f" SUBSCRIBE {ch:<12} ✅ OK → {val}")
|
|
else:
|
|
print(f" SUBSCRIBE {ch:<12} ❌ {val}")
|
|
|
|
# 7. Thử XREAD / XLEN (Redis Streams)
|
|
section("7. XREAD / XLEN — thử Redis Streams")
|
|
for ch in ["events", "logs", "usage"]:
|
|
ok, val = c.cmd("XLEN", ch)
|
|
if ok:
|
|
print(f" XLEN {ch:<16} ✅ OK → {val}")
|
|
else:
|
|
print(f" XLEN {ch:<16} ❌ {val}")
|
|
|
|
# 8. Tổng kết
|
|
section("TỔNG KẾT")
|
|
if valid_channels:
|
|
print(f" ✅ Channel RPOP hợp lệ: {valid_channels}")
|
|
print(f"\n → Thêm vào config.json:")
|
|
print(f' "queue_channel": "{valid_channels[0]}"')
|
|
else:
|
|
print(" ⚠️ Không tìm thấy channel RPOP hợp lệ nào.")
|
|
print(" → CLIProxy có thể dùng giao thức khác (pub/sub, stream, HTTP).")
|
|
print(" → Kiểm tra tài liệu hoặc source code của CLIProxy.")
|
|
|
|
|
|
# ── Main ─────────────────────────────────────────────────────────────────────
|
|
|
|
def main():
|
|
print(f"Kết nối tới CLIProxy {HOST}:{PORT} ...")
|
|
try:
|
|
c = Client(HOST, PORT, PASSWORD)
|
|
except Exception as e:
|
|
print(f"❌ Kết nối thất bại: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
print(f"✅ AUTH OK\n")
|
|
try:
|
|
probe(c)
|
|
finally:
|
|
c.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|