Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

atsq — Asynchronous TeamSpeak Query

CI Release PyPI Python License Ruff uv

The successor to py-ts3: an asyncio-native ServerQuery client for TeamSpeak 3 and TeamSpeak 6 over SSH — automatic keepalive, reconnect and event dispatch, on modern Python (3.12–3.14+). Every release is tested live against real TS3 and TS6 servers.

Install

uv add atsq          # or: pip install atsq

Usage

import atsq

client = atsq.Client("ts.example.com", 10022, password="...",
                     server_id=1,                    # or server_port=9987
                     nickname="My Bot",              # re-applied on reconnect
                     register_events=atsq.ALL_EVENTS)

@client.on("cliententerview")
async def on_join(event: atsq.Event) -> None:
    if event["client_type"] == atsq.ClientType.VOICE:
        print("joined:", event["client_unique_identifier"])

@client.on("clientleftview")
async def on_leave(event: atsq.Event) -> None:
    print("left:", event.get("clid"))

await client.run_forever()   # reconnects with backoff; keepalive automatic
One-shot administrative session
async with await atsq.connect("ts.example.com", 10022,
                              password="...", server_id=1) as ts:
    for row in await ts.client_list("uid", "mytsid"):    # -mytsid: TS6 only
        print(row["clid"], row["client_nickname"])
    cid = await ts.channel_create("Lounge", channel_flag_permanent=1)
    bans = await ts.ban_find(ip="203.0.113.7")            # TS6 only, [] if none

Pull-style event consumption instead of handlers:

async for event in client.events():
    handle(event)
# or: event = await client.wait_for_event(timeout=240)
Generic commands, pipelining and wire constants

Anything without a typed wrapper goes through the escape-safe exec(), including pipelined bulk commands (many parameter blocks, one round trip):

rows = await ts.exec("servergrouplist")
await ts.exec("clientmove", clid=5, cid=42)
await ts.exec("channeladdperm", cid=60, blocks=[
    {"permsid": "i_channel_needed_join_power", "permvalue": 75},
    {"permsid": "i_channel_needed_subscribe_power", "permvalue": 60},
])

Wire constants are StrEnums that compare directly against event/row values:

from atsq import ReasonId, TargetMode, ClientType, LEAVE_REASONS

if event["reasonid"] == ReasonId.CONNECT and event["client_type"] == ClientType.VOICE:
    ...
if event.get("reasonid") in LEAVE_REASONS:
    ...
File transfer (icons, avatars, channel files)

Same API against TS3 and TS6:

ft = atsq.FileTransfer(client)
icon_id = await ft.upload_icon(png_bytes)            # crc32-named, returns the id
data = await ft.download("/file.bin", cid=42)
rows = await ft.file_list(cid=42, path="/")          # [] for empty dirs
await ft.delete_file("/file.bin", cid=42)
Error handling
try:
    await ts.use(99)
except atsq.QueryError as e:        # error id != 0; str(e) carries the server msg
    print(e.error_id, e.msg)
except atsq.QueryTimeoutError:      # no response in time (connection is closed)
    ...
except atsq.ConnectionClosedError:  # connection gone
    ...

atsq.FloodError (id 524) is retried automatically (flood_retries, default 2).

Defaults worth knowing
  • Keepalive: automatic whoami after 240 s idle (servers kick at ~300 s). Configure via keepalive_interval; 0 disables.
  • Flood protection: an error 524 is retried automatically after the wait the server asks for (flood_retries, default 2; 0 disables). Allowlisted IPs (query_ip_allowlist.txt) never hit it in the first place.
  • Reconnect (run_forever): exponential backoff 5 s → 300 s; a server message containing "banned" waits 300 s. use/servernotifyregister and on_ready re-run after every reconnect.
  • Host keys: verification is off by default (TeamSpeak servers generate ephemeral query host keys). Pin one in production: atsq.connect(..., known_hosts=...) (forwarded to asyncssh).
  • close() sends quit: on TS6 a query client that silently drops the SSH connection never produces a notifyclientleftview; a clean quit does (on both generations).
  • Guest sessions (TS6 beta13+): atsq.connect(host, username="guest") needs no password; await ts.login("serveradmin", pw) elevates in-band (and re-selects the virtual server, which login/logout reset).
  • Event sources: ALL_EVENTS includes the TS6-only bans source (banupdate events with op=add|del); it is skipped on TS3. server_notify_unregister("textserver") is selective on both generations (TS3 drops everything, so atsq re-registers the rest).
  • Snapshots work via plain exec("serversnapshotcreate") / exec("serversnapshotdeploy", version=..., data=...) — deploy deselects the session; call use again afterwards.
Migrating from py-ts3
py-ts3 atsq
TS3ServerConnection("telnet://user:pass@host:10011") await atsq.connect(host, 10022, username=..., password=...) (SSH)
conn.exec_("clientlist", "uid") await ts.exec("clientlist", "uid") or await ts.client_list("uid")
response resp[0]["cldbid"] same shape: rows[0]["cldbid"] (list[dict[str, str]])
conn.wait_for_event(timeout=240) await client.wait_for_event(timeout=240) or @client.on(...)
event[0]["reasonid"] event["reasonid"] (Event is a Mapping[str, str])
conn.send_keepalive() automatic (or await ts.send_keepalive())
ts3.query.TS3QueryError atsq.QueryError (str() still contains the server msg)
ts3.query.TS3TimeoutError atsq.QueryTimeoutError
query builder .pipe() exec(cmd, blocks=[{...}, {...}], **shared)
ts3.definitions constants atsq.ReasonId / TargetMode / ClientType / LEAVE_REASONS
ts3.filetransfer.TS3FileTransfer atsq.FileTransfer (asyncio, TS3+TS6)
manual reconnect loop await client.run_forever()

Enable SSH query on a TS3 server with TS3SERVER_QUERY_PROTOCOLS=raw,ssh; on TS6 it is the only line protocol (TSSERVER_QUERY_SSH_ENABLED=1, password via TSSERVER_QUERY_ADMIN_PASSWORD).

Docs

  • TS3 vs TS6 dialect findings — probe-verified differences between the generations and how atsq handles them.
  • Testing policy — every function is covered by a unit test and a live test against real servers; CI enforces it with a coverage gate.

Development

uv sync
uv run pytest                        # unit + fake-transport tests (no docker)
./scripts/run-integration-tests.sh   # full suite vs real TS3 + TS6 in docker

License

MIT

Metadata

Release files for atsq 1.0.0a5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for atsq 1.0.0a5
File Size Uploaded
atsq-1.0.0a5.tar.gz 93.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for atsq 1.0.0a5
File Interpreter ABI Platform
atsq-1.0.0a5-py3-none-any.whl Python 3 none any Details

Total release size: 120.9 kB

Release files / atsq-1.0.0a5.tar.gz

Download URL atsq-1.0.0a5.tar.gz
Size 93.8 kB
Tags Source
SHA-256 checksum
How to use checksums
4de87e6cafa9c86bd2a7a4db9be6e0983f2d449f4dad4ac21aa12e11a636321e
BLAKE2b-256 checksum
How to use checksums
b1bf6401eba9ec774179234037b263c05bff7396cf88d128a757757c2bf4126d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / atsq-1.0.0a5-py3-none-any.whl

Download URL atsq-1.0.0a5-py3-none-any.whl
Size 27.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f9f45147639ca5d34ea3dfb0994a5663a7b1e94bc1cf63eeb6023dbaf234f838
BLAKE2b-256 checksum
How to use checksums
694260378fad92a9f64aa6ff4ccfcf10384fb44023a10c49eaec39dd2bc04961
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page