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.9.tar.gz (72.1 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.9-py3-none-any.whl (71.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: openim_sdk_core-0.1.9.tar.gz
  • Upload date:
  • Size: 72.1 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.9.tar.gz
Algorithm Hash digest
SHA256 f6fab2d3ba0ec1c346de8b871a0a7fc5d3d33dbd5053b3fe0afaf16f731e33f9
MD5 137e85b347718ee1262ce3f856f884ce
BLAKE2b-256 0a188d738b9222399af766279e4ac3baa491ee7724b6dd5cafe02ae19659889c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for openim_sdk_core-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 c91ac2b188f3d5111aa9b3afebc0cf3ae4ff45748aa700444f1f13bbb900bdf0
MD5 ec828e3f8064588e33d8b701e23cd389
BLAKE2b-256 02bb7d8b3f840dfdabc1a512c7e40bdced2eac5fc4ffa7570e27c5ae029c2f7f

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