Skip to main content

Python SDK and CLI for OpenIM-style login, messaging, and websocket sync.

Project description

openim_sdk

Python SDK and CLI for OpenIM-style login, send message, and receive message.

Modes

  • OpenIMWSSDK (recommended): websocket direct connection (gob + gzip + protobuf) with heartbeat and auto reconnect.
  • OpenIMSDK: HTTP polling fallback.
  • openim-cli: account bootstrap, auth status, and outbound notification commands built on top of OpenIMWSSDK.

CLI quick start

Install this repository, then run:

openim-cli --help
openim-cli config init --token "$OPENIM_TOKEN" --ws-addr ws://127.0.0.1:10001/msg_gateway --default true
openim-cli auth status --format pretty
openim-cli notify +send --to user:u2 --text "hello"
openim-cli notify +send --to group:g1 --text '<at user_id="u2">Gimmus</at> 你好'

Supported commands:

  • config init|list|use|remove
  • auth status
  • notify +send

WebSocket quick start

from openim_sdk import OpenIMWSSDK, WSConfig

sdk = OpenIMWSSDK(
    WSConfig(ws_addr="ws://127.0.0.1:10001/msg_gateway", data_dir="./openim_py_data")
)
sdk.login(user_id="u1", token="YOUR_TOKEN")
sdk.start()
sdk.send_text("hello", recv_id="u2")

See openim_sdk/example_ws.py for a runnable demo script.

WebSocket detailed usage

1. Build config

WSConfig parameters:

  • ws_addr (required): websocket gateway address, e.g. ws://127.0.0.1:10001/msg_gateway
  • api_addr: HTTP API address for profile fetch (used to cache self info for send), e.g. http://127.0.0.1:10002; when empty, sdk falls back to env OPENIM_API_ADDR
  • data_dir: local sqlite directory, default ./data
  • platform_id: platform ID sent to server, default 1
  • timeout_seconds: request/response wait timeout, default 1000
  • pull_batch_size: sync page size, default 100
  • sdk_version: value sent in ws query, default python-ws-0.1.0
  • auto_sync_on_connect: run sync_once() immediately after first connect, default False
  • auto_sync_on_reconnect: auto sync after reconnect, default True
  • auto_reconnect: reconnect automatically on disconnect, default True
  • max_reconnect_attempts: reconnect attempt cap, default 300
  • reconnect_backoff_seconds: reconnect interval sequence, default (1, 2, 4, 8, 16)
  • enable_heartbeat: send ws ping periodically, default True
  • heartbeat_interval_seconds: ping interval, default 24
  • heartbeat_pong_timeout_seconds: pong timeout, default 30
  • self_user_info_cache_ttl_seconds: in-memory cache ttl for self profile (senderNickname/senderFaceURL), default 600 seconds

2. Register callbacks

Available callbacks in OpenIMWSSDK(...):

  • on_recv_new_message(msg: WSMessage): called for online real-time messages
  • on_recv_offline_new_message(msg: WSMessage): called for offline/sync messages
  • on_connecting(): called before connect/reconnect attempts
  • on_connect_success(): called after successful connect/reconnect
  • on_connect_failed(exc): called when one connect attempt fails
  • on_kicked_offline(): called when server returns kick event
  • on_error(exc): called on internal errors (decode/send/read/reconnect, etc.)
  • logger(text): sdk log output callback

2.1 WSMessage fields

Raw payload keys:

  • conversationID: conversation id (sdk adds this key)
  • sendID, recvID, groupID
  • clientMsgID, serverMsgID
  • senderPlatformID, senderNickname, senderFaceURL
  • sessionType (1 single, 2 group, 3 super group)
  • msgFrom, contentType, content
  • seq, sendTime, createTime
  • status, isRead
  • options (dict[str, bool])
  • offlinePushInfo (dict)
  • atUserIDList (list[str])
  • attachedInfo, ex

Python field names in WSMessage:

  • conversation_id, send_id, recv_id, group_id
  • client_msg_id, server_msg_id
  • sender_platform_id, sender_nickname, sender_face_url
  • session_type, msg_from, content_type, content
  • seq, send_time, create_time, status, is_read
  • options, offline_push_info, at_user_id_list, attached_info, ex
  • raw: original dictionary payload
  • content_json: parsed JSON from content (or None when not valid JSON)
  • content_obj: typed content object parsed by content_type

content_type -> content_obj mapping (same as sdk_struct.go):

  • 101 -> TextElem
  • 102 -> PictureElem
  • 103 -> SoundElem
  • 104 -> VideoElem
  • 105 -> FileElem
  • 106 -> AtTextElem
  • 107 -> MergeElem
  • 108 -> CardElem
  • 109 -> LocationElem
  • 110/119/120 -> CustomElem
  • 113 -> TypingElem
  • 114 -> QuoteElem
  • 115 -> FaceElem
  • 117 -> AdvancedTextElem
  • 118 -> MarkdownTextElem
  • other content types -> NotificationElem

Minimal callback example:

from openim_sdk import OpenIMWSSDK, WSConfig, WSMessage, TextElem, PictureElem


def on_new_model(msg: WSMessage) -> None:
    if isinstance(msg.content_obj, TextElem):
        print("[text]", msg.content_obj.content)
    elif isinstance(msg.content_obj, PictureElem):
        print("[picture]", msg.content_obj.source_picture)
    else:
        print("[other]", msg.content_type, msg.content_obj)


sdk = OpenIMWSSDK(
    WSConfig(ws_addr="ws://127.0.0.1:10001/msg_gateway"),
    on_recv_new_message=on_new_model,
)

3. Full example (recommended pattern)

import time

from openim_sdk import OpenIMWSSDK, TextElem, WSConfig, WSSDKError, WSMessage


def on_new(msg: WSMessage) -> None:
    if isinstance(msg.content_obj, TextElem):
        print("[new text]", msg.send_id, "->", msg.recv_id, msg.content_obj.content)
    else:
        print("[new]", msg.send_id, "->", msg.recv_id, msg.content_type, msg.content_obj)


def on_offline(msg: WSMessage) -> None:
    print("[offline]", msg.client_msg_id, msg.seq)


def on_err(exc: Exception) -> None:
    print("[error]", exc)


sdk = OpenIMWSSDK(
    WSConfig(
        ws_addr="ws://127.0.0.1:10001/msg_gateway",
        api_addr="http://127.0.0.1:10002",
        data_dir="./openim_py_data",
        auto_sync_on_connect=True,
        auto_sync_on_reconnect=True,
    ),
    on_recv_new_message=on_new,
    on_recv_offline_new_message=on_offline,
    on_connecting=lambda: print("[ws] connecting"),
    on_connect_success=lambda: print("[ws] connected"),
    on_connect_failed=lambda e: print("[ws] connect failed:", e),
    on_kicked_offline=lambda: print("[ws] kicked offline"),
    on_error=on_err,
    logger=lambda s: print("[sdk]", s),
)

try:
    # 1) login first
    sdk.login(user_id="u1", token="YOUR_TOKEN")
    # 2) then start websocket threads
    sdk.start()

    # send peer message
    ack = sdk.send_text("hello from ws sdk", recv_id="u2")
    print("send ack:", ack)  # {"serverMsgID": "...", "clientMsgID": "...", "sendTime": ...}

    # keep process alive
    while True:
        time.sleep(1)
except WSSDKError as e:
    print("business error:", e.err_code, e.err_msg)
except KeyboardInterrupt:
    pass
finally:
    # stop background threads and close db
    sdk.logout()

4. Sending messages

  • Single chat:
sdk.send_text("hello", recv_id="u2")
sdk.send_at_text("@Gimmus 你好", at_user_list=["u2"], at_users_info=[{"atUserID": "u2", "groupNickname": "Gimmus"}], group_id="g1")
  • Group chat:
sdk.send_text("hello group", group_id="g123", session_type=2)

send_text(...) notes:

  • must pass one of recv_id or group_id
  • session_type defaults to single chat (1) if recv_id is set, otherwise group (2)
  • if WSConfig.api_addr is set, sdk fetches self profile (/user/get_users_info) and caches it in memory (ttl controlled by self_user_info_cache_ttl_seconds, default 10 minutes), then auto-fills senderNickname and senderFaceURL on send
  • supported session values:
    • 1: single chat
    • 2: group chat
    • 3: super group chat

Media/file messages:

sdk.send_image("/absolute/path/demo.png", recv_id="u2")
sdk.send_sound("/absolute/path/demo.mp3", duration=9, recv_id="u2")
sdk.send_video(
    "/absolute/path/demo.mp4",
    duration=15000,
    snapshot_path="/absolute/path/demo-cover.jpg",
    recv_id="u2",
)
sdk.send_file("/absolute/path/report.pdf", recv_id="u2")

URL-based variants:

sdk.send_image_by_url(source_url="https://cdn.example.com/demo.png", width=1280, height=720, recv_id="u2")
sdk.send_video_by_url(video_url="https://cdn.example.com/demo.mp4", duration=15000, recv_id="u2")
sdk.send_file_by_url(source_url="https://cdn.example.com/report.pdf", file_name="report.pdf", recv_id="u2")

Media upload notes:

  • local media/file sending uses OpenIM object upload APIs before message send
  • SDKConfig.api_addr / WSConfig.api_addr must be configured for local media/file sending
  • send_video(...) requires duration in milliseconds; when snapshot_path is omitted, the SDK will try to extract the first video frame as the cover image with local ffmpeg
  • send_sound(...) requires duration
  • CLI notify +send --video ... now probes local video duration in milliseconds with ffprobe and sends a real video message instead of falling back to file sending
  • CLI notify +send --text ... supports mention markup like <at user_id="u2">Gimmus</at> 你好, which is sent as OpenIM AT_TEXT (106)
  • send_picture is an alias of send_image

5. Manual sync

If you want explicit control (instead of auto_sync_on_connect/reconnect), call:

sdk.sync_once()

This pulls missing seq ranges and dispatches them through on_recv_offline_new_message.

6. Lifecycle and best practices

  • call order should be: login(...) -> start() -> send/recv -> logout()
  • stop() only stops ws threads; logout() stops threads and closes local storage
  • keep your process alive after start(), otherwise daemon threads exit with process
  • for production:
    • keep auto_reconnect=True
    • keep heartbeat enabled
    • handle on_error and on_connect_failed for observability/retry alerts
    • persist your own app-level offsets/state if required

HTTP polling quick start

from openim_sdk import OpenIMSDK, SDKConfig

sdk = OpenIMSDK(
    SDKConfig(api_addr="http://127.0.0.1:10002", data_dir="./openim_py_data")
)
sdk.login(user_id="u1", token="YOUR_TOKEN")
sdk.start_receiving()
sdk.send_text("hello", recv_id="u2")
me = sdk.get_self_user_info()
print(me.user_id, me.nickname, me.face_url)

WebSocket features

  • Login with user_id + token
  • Send text message with protobuf payload (MsgData)
  • Receive PushMessages from ws binary frames
  • Gob encode/decode for GeneralWsReq/GeneralWsResp
  • Gzip frame compression/decompression
  • Heartbeat (ping/pong)
  • Auto reconnect with backoff (1,2,4,8,16 seconds loop by default)
  • Auto sync missed messages on connect/reconnect
  • Local SQLite persistence:
    • message rows
    • per-conversation latest synced seq

Local database

  • WS mode: <data_dir>/OpenIM_pyws_<user_id>.db
  • HTTP mode: <data_dir>/OpenIM_py_<user_id>.db

Tables:

  • conversation_seq(conversation_id, last_seq)
  • messages(...)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openim_sdk_core-0.1.8.tar.gz (70.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openim_sdk_core-0.1.8-py3-none-any.whl (70.3 kB view details)

Uploaded Python 3

File details

Details for the file openim_sdk_core-0.1.8.tar.gz.

File metadata

  • Download URL: openim_sdk_core-0.1.8.tar.gz
  • Upload date:
  • Size: 70.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for openim_sdk_core-0.1.8.tar.gz
Algorithm Hash digest
SHA256 05416bd99212d6fdcd8dc1f91f7c7458b215ca05175e712e8c54104fdba7b92f
MD5 281c6727ad945858f3153a05cc76ae6d
BLAKE2b-256 ccc2def4cef3b3c5492a5bfc01d8d1e848e5af9753f032c39cd6b36aa7e16c5b

See more details on using hashes here.

File details

Details for the file openim_sdk_core-0.1.8-py3-none-any.whl.

File metadata

File hashes

Hashes for openim_sdk_core-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 739c152ce02fb824907dcc0c550f077d771a124f48735ce4f174fb2d63d7a9cd
MD5 a8f4d26eb96963ddaffe2dc53dcd15bc
BLAKE2b-256 37402f9c2bbea6a945b86cdfe7ba262637f572e2c1dc2d4e29413fec476a38c5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page