openim-sdk-core 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. openim_sdk_core-0.1.3/MANIFEST.in +11 -0
  2. openim_sdk_core-0.1.3/PKG-INFO +297 -0
  3. openim_sdk_core-0.1.3/README.md +275 -0
  4. openim_sdk_core-0.1.3/openim_sdk/__init__.py +57 -0
  5. openim_sdk_core-0.1.3/openim_sdk/client.py +381 -0
  6. openim_sdk_core-0.1.3/openim_sdk/example.py +35 -0
  7. openim_sdk_core-0.1.3/openim_sdk/example_ws.py +53 -0
  8. openim_sdk_core-0.1.3/openim_sdk/gob_codec.py +243 -0
  9. openim_sdk_core-0.1.3/openim_sdk/http_api.py +48 -0
  10. openim_sdk_core-0.1.3/openim_sdk/models.py +583 -0
  11. openim_sdk_core-0.1.3/openim_sdk/proto/__init__.py +21 -0
  12. openim_sdk_core-0.1.3/openim_sdk/proto/conversation/__init__.py +1 -0
  13. openim_sdk_core-0.1.3/openim_sdk/proto/conversation/conversation_pb2.py +145 -0
  14. openim_sdk_core-0.1.3/openim_sdk/proto/msg/__init__.py +1 -0
  15. openim_sdk_core-0.1.3/openim_sdk/proto/msg/msg_pb2.py +235 -0
  16. openim_sdk_core-0.1.3/openim_sdk/proto/sdkws/__init__.py +1 -0
  17. openim_sdk_core-0.1.3/openim_sdk/proto/sdkws/sdkws_pb2.py +214 -0
  18. openim_sdk_core-0.1.3/openim_sdk/proto/wrapperspb/__init__.py +1 -0
  19. openim_sdk_core-0.1.3/openim_sdk/proto/wrapperspb/wrapperspb_pb2.py +45 -0
  20. openim_sdk_core-0.1.3/openim_sdk/self_user_info_api.py +48 -0
  21. openim_sdk_core-0.1.3/openim_sdk/storage.py +178 -0
  22. openim_sdk_core-0.1.3/openim_sdk/ws_client.py +830 -0
  23. openim_sdk_core-0.1.3/openim_sdk_core.egg-info/PKG-INFO +297 -0
  24. openim_sdk_core-0.1.3/openim_sdk_core.egg-info/SOURCES.txt +27 -0
  25. openim_sdk_core-0.1.3/openim_sdk_core.egg-info/dependency_links.txt +1 -0
  26. openim_sdk_core-0.1.3/openim_sdk_core.egg-info/requires.txt +6 -0
  27. openim_sdk_core-0.1.3/openim_sdk_core.egg-info/top_level.txt +1 -0
  28. openim_sdk_core-0.1.3/pyproject.toml +41 -0
  29. openim_sdk_core-0.1.3/setup.cfg +4 -0
@@ -0,0 +1,11 @@
1
+ include README.md
2
+
3
+ prune .idea
4
+ prune __pycache__
5
+ prune openim_sdk/.idea
6
+ prune openim_sdk/__pycache__
7
+ prune openim_sdk/openim_py_data
8
+
9
+ recursive-exclude * __pycache__
10
+ global-exclude *.py[cod]
11
+ global-exclude .DS_Store
@@ -0,0 +1,297 @@
1
+ Metadata-Version: 2.4
2
+ Name: openim-sdk-core
3
+ Version: 0.1.3
4
+ Summary: Python SDK for OpenIM-style login, messaging, and websocket sync.
5
+ Author: OpenIM Contributors
6
+ License-Expression: AGPL-3.0-or-later
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3 :: Only
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: protobuf<7,>=6.32.0
18
+ Requires-Dist: websocket-client<2,>=1.8.0
19
+ Provides-Extra: release
20
+ Requires-Dist: build>=1.2.2; extra == "release"
21
+ Requires-Dist: twine>=6.0.1; extra == "release"
22
+
23
+ # openim_sdk
24
+
25
+ Python SDK for OpenIM-style login, send message, and receive message.
26
+
27
+ ## Modes
28
+
29
+ - `OpenIMWSSDK` (recommended): websocket direct connection (`gob + gzip + protobuf`) with heartbeat and auto reconnect.
30
+ - `OpenIMSDK`: HTTP polling fallback.
31
+
32
+ ## WebSocket quick start
33
+
34
+ ```python
35
+ from openim_sdk import OpenIMWSSDK, WSConfig
36
+
37
+ sdk = OpenIMWSSDK(
38
+ WSConfig(ws_addr="ws://127.0.0.1:10001/msg_gateway", data_dir="./openim_py_data")
39
+ )
40
+ sdk.login(user_id="u1", token="YOUR_TOKEN")
41
+ sdk.start()
42
+ sdk.send_text("hello", recv_id="u2")
43
+ ```
44
+
45
+ See `openim_sdk/example_ws.py` for a runnable demo script.
46
+
47
+ ## WebSocket detailed usage
48
+
49
+ ### 1. Build config
50
+
51
+ `WSConfig` parameters:
52
+
53
+ - `ws_addr` (required): websocket gateway address, e.g. `ws://127.0.0.1:10001/msg_gateway`
54
+ - `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`
55
+ - `data_dir`: local sqlite directory, default `./data`
56
+ - `platform_id`: platform ID sent to server, default `1`
57
+ - `timeout_seconds`: request/response wait timeout, default `1000`
58
+ - `pull_batch_size`: sync page size, default `100`
59
+ - `sdk_version`: value sent in ws query, default `python-ws-0.1.0`
60
+ - `auto_sync_on_connect`: run `sync_once()` immediately after first connect, default `False`
61
+ - `auto_sync_on_reconnect`: auto sync after reconnect, default `True`
62
+ - `auto_reconnect`: reconnect automatically on disconnect, default `True`
63
+ - `max_reconnect_attempts`: reconnect attempt cap, default `300`
64
+ - `reconnect_backoff_seconds`: reconnect interval sequence, default `(1, 2, 4, 8, 16)`
65
+ - `enable_heartbeat`: send ws ping periodically, default `True`
66
+ - `heartbeat_interval_seconds`: ping interval, default `24`
67
+ - `heartbeat_pong_timeout_seconds`: pong timeout, default `30`
68
+ - `self_user_info_cache_ttl_seconds`: in-memory cache ttl for self profile (`senderNickname/senderFaceURL`), default `600` seconds
69
+
70
+ ### 2. Register callbacks
71
+
72
+ Available callbacks in `OpenIMWSSDK(...)`:
73
+
74
+ - `on_recv_new_message(msg: WSMessage)`: called for online real-time messages
75
+ - `on_recv_offline_new_message(msg: WSMessage)`: called for offline/sync messages
76
+ - `on_connecting()`: called before connect/reconnect attempts
77
+ - `on_connect_success()`: called after successful connect/reconnect
78
+ - `on_connect_failed(exc)`: called when one connect attempt fails
79
+ - `on_kicked_offline()`: called when server returns kick event
80
+ - `on_error(exc)`: called on internal errors (decode/send/read/reconnect, etc.)
81
+ - `logger(text)`: sdk log output callback
82
+
83
+ ### 2.1 `WSMessage` fields
84
+
85
+ Raw payload keys:
86
+
87
+ - `conversationID`: conversation id (sdk adds this key)
88
+ - `sendID`, `recvID`, `groupID`
89
+ - `clientMsgID`, `serverMsgID`
90
+ - `senderPlatformID`, `senderNickname`, `senderFaceURL`
91
+ - `sessionType` (`1` single, `2` group, `3` super group)
92
+ - `msgFrom`, `contentType`, `content`
93
+ - `seq`, `sendTime`, `createTime`
94
+ - `status`, `isRead`
95
+ - `options` (`dict[str, bool]`)
96
+ - `offlinePushInfo` (`dict`)
97
+ - `atUserIDList` (`list[str]`)
98
+ - `attachedInfo`, `ex`
99
+
100
+ Python field names in `WSMessage`:
101
+
102
+ - `conversation_id`, `send_id`, `recv_id`, `group_id`
103
+ - `client_msg_id`, `server_msg_id`
104
+ - `sender_platform_id`, `sender_nickname`, `sender_face_url`
105
+ - `session_type`, `msg_from`, `content_type`, `content`
106
+ - `seq`, `send_time`, `create_time`, `status`, `is_read`
107
+ - `options`, `offline_push_info`, `at_user_id_list`, `attached_info`, `ex`
108
+ - `raw`: original dictionary payload
109
+ - `content_json`: parsed JSON from `content` (or `None` when not valid JSON)
110
+ - `content_obj`: typed content object parsed by `content_type`
111
+
112
+ `content_type -> content_obj` mapping (same as `sdk_struct.go`):
113
+
114
+ - `101` -> `TextElem`
115
+ - `102` -> `PictureElem`
116
+ - `103` -> `SoundElem`
117
+ - `104` -> `VideoElem`
118
+ - `105` -> `FileElem`
119
+ - `106` -> `AtTextElem`
120
+ - `107` -> `MergeElem`
121
+ - `108` -> `CardElem`
122
+ - `109` -> `LocationElem`
123
+ - `110/119/120` -> `CustomElem`
124
+ - `113` -> `TypingElem`
125
+ - `114` -> `QuoteElem`
126
+ - `115` -> `FaceElem`
127
+ - `117` -> `AdvancedTextElem`
128
+ - `118` -> `MarkdownTextElem`
129
+ - other content types -> `NotificationElem`
130
+
131
+ Minimal callback example:
132
+
133
+ ```python
134
+ from openim_sdk import OpenIMWSSDK, WSConfig, WSMessage, TextElem, PictureElem
135
+
136
+
137
+ def on_new_model(msg: WSMessage) -> None:
138
+ if isinstance(msg.content_obj, TextElem):
139
+ print("[text]", msg.content_obj.content)
140
+ elif isinstance(msg.content_obj, PictureElem):
141
+ print("[picture]", msg.content_obj.source_picture)
142
+ else:
143
+ print("[other]", msg.content_type, msg.content_obj)
144
+
145
+
146
+ sdk = OpenIMWSSDK(
147
+ WSConfig(ws_addr="ws://127.0.0.1:10001/msg_gateway"),
148
+ on_recv_new_message=on_new_model,
149
+ )
150
+ ```
151
+
152
+ ### 3. Full example (recommended pattern)
153
+
154
+ ```python
155
+ import time
156
+
157
+ from openim_sdk import OpenIMWSSDK, TextElem, WSConfig, WSSDKError, WSMessage
158
+
159
+
160
+ def on_new(msg: WSMessage) -> None:
161
+ if isinstance(msg.content_obj, TextElem):
162
+ print("[new text]", msg.send_id, "->", msg.recv_id, msg.content_obj.content)
163
+ else:
164
+ print("[new]", msg.send_id, "->", msg.recv_id, msg.content_type, msg.content_obj)
165
+
166
+
167
+ def on_offline(msg: WSMessage) -> None:
168
+ print("[offline]", msg.client_msg_id, msg.seq)
169
+
170
+
171
+ def on_err(exc: Exception) -> None:
172
+ print("[error]", exc)
173
+
174
+
175
+ sdk = OpenIMWSSDK(
176
+ WSConfig(
177
+ ws_addr="ws://127.0.0.1:10001/msg_gateway",
178
+ api_addr="http://127.0.0.1:10002",
179
+ data_dir="./openim_py_data",
180
+ auto_sync_on_connect=True,
181
+ auto_sync_on_reconnect=True,
182
+ ),
183
+ on_recv_new_message=on_new,
184
+ on_recv_offline_new_message=on_offline,
185
+ on_connecting=lambda: print("[ws] connecting"),
186
+ on_connect_success=lambda: print("[ws] connected"),
187
+ on_connect_failed=lambda e: print("[ws] connect failed:", e),
188
+ on_kicked_offline=lambda: print("[ws] kicked offline"),
189
+ on_error=on_err,
190
+ logger=lambda s: print("[sdk]", s),
191
+ )
192
+
193
+ try:
194
+ # 1) login first
195
+ sdk.login(user_id="u1", token="YOUR_TOKEN")
196
+ # 2) then start websocket threads
197
+ sdk.start()
198
+
199
+ # send peer message
200
+ ack = sdk.send_text("hello from ws sdk", recv_id="u2")
201
+ print("send ack:", ack) # {"serverMsgID": "...", "clientMsgID": "...", "sendTime": ...}
202
+
203
+ # keep process alive
204
+ while True:
205
+ time.sleep(1)
206
+ except WSSDKError as e:
207
+ print("business error:", e.err_code, e.err_msg)
208
+ except KeyboardInterrupt:
209
+ pass
210
+ finally:
211
+ # stop background threads and close db
212
+ sdk.logout()
213
+ ```
214
+
215
+ ### 4. Sending messages
216
+
217
+ - Single chat:
218
+
219
+ ```python
220
+ sdk.send_text("hello", recv_id="u2")
221
+ ```
222
+
223
+ - Group chat:
224
+
225
+ ```python
226
+ sdk.send_text("hello group", group_id="g123", session_type=2)
227
+ ```
228
+
229
+ `send_text(...)` notes:
230
+
231
+ - must pass one of `recv_id` or `group_id`
232
+ - `session_type` defaults to single chat (`1`) if `recv_id` is set, otherwise group (`2`)
233
+ - 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
234
+ - supported session values:
235
+ - `1`: single chat
236
+ - `2`: group chat
237
+ - `3`: super group chat
238
+
239
+ ### 5. Manual sync
240
+
241
+ If you want explicit control (instead of `auto_sync_on_connect/reconnect`), call:
242
+
243
+ ```python
244
+ sdk.sync_once()
245
+ ```
246
+
247
+ This pulls missing seq ranges and dispatches them through `on_recv_offline_new_message`.
248
+
249
+ ### 6. Lifecycle and best practices
250
+
251
+ - call order should be: `login(...) -> start() -> send/recv -> logout()`
252
+ - `stop()` only stops ws threads; `logout()` stops threads and closes local storage
253
+ - keep your process alive after `start()`, otherwise daemon threads exit with process
254
+ - for production:
255
+ - keep `auto_reconnect=True`
256
+ - keep heartbeat enabled
257
+ - handle `on_error` and `on_connect_failed` for observability/retry alerts
258
+ - persist your own app-level offsets/state if required
259
+
260
+ ## HTTP polling quick start
261
+
262
+ ```python
263
+ from openim_sdk import OpenIMSDK, SDKConfig
264
+
265
+ sdk = OpenIMSDK(
266
+ SDKConfig(api_addr="http://127.0.0.1:10002", data_dir="./openim_py_data")
267
+ )
268
+ sdk.login(user_id="u1", token="YOUR_TOKEN")
269
+ sdk.start_receiving()
270
+ sdk.send_text("hello", recv_id="u2")
271
+ me = sdk.get_self_user_info()
272
+ print(me.user_id, me.nickname, me.face_url)
273
+ ```
274
+
275
+ ## WebSocket features
276
+
277
+ - Login with `user_id + token`
278
+ - Send text message with protobuf payload (`MsgData`)
279
+ - Receive `PushMessages` from ws binary frames
280
+ - Gob encode/decode for `GeneralWsReq/GeneralWsResp`
281
+ - Gzip frame compression/decompression
282
+ - Heartbeat (`ping/pong`)
283
+ - Auto reconnect with backoff (`1,2,4,8,16` seconds loop by default)
284
+ - Auto sync missed messages on connect/reconnect
285
+ - Local SQLite persistence:
286
+ - message rows
287
+ - per-conversation latest synced seq
288
+
289
+ ## Local database
290
+
291
+ - WS mode: `<data_dir>/OpenIM_pyws_<user_id>.db`
292
+ - HTTP mode: `<data_dir>/OpenIM_py_<user_id>.db`
293
+
294
+ Tables:
295
+
296
+ - `conversation_seq(conversation_id, last_seq)`
297
+ - `messages(...)`
@@ -0,0 +1,275 @@
1
+ # openim_sdk
2
+
3
+ Python SDK for OpenIM-style login, send message, and receive message.
4
+
5
+ ## Modes
6
+
7
+ - `OpenIMWSSDK` (recommended): websocket direct connection (`gob + gzip + protobuf`) with heartbeat and auto reconnect.
8
+ - `OpenIMSDK`: HTTP polling fallback.
9
+
10
+ ## WebSocket quick start
11
+
12
+ ```python
13
+ from openim_sdk import OpenIMWSSDK, WSConfig
14
+
15
+ sdk = OpenIMWSSDK(
16
+ WSConfig(ws_addr="ws://127.0.0.1:10001/msg_gateway", data_dir="./openim_py_data")
17
+ )
18
+ sdk.login(user_id="u1", token="YOUR_TOKEN")
19
+ sdk.start()
20
+ sdk.send_text("hello", recv_id="u2")
21
+ ```
22
+
23
+ See `openim_sdk/example_ws.py` for a runnable demo script.
24
+
25
+ ## WebSocket detailed usage
26
+
27
+ ### 1. Build config
28
+
29
+ `WSConfig` parameters:
30
+
31
+ - `ws_addr` (required): websocket gateway address, e.g. `ws://127.0.0.1:10001/msg_gateway`
32
+ - `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`
33
+ - `data_dir`: local sqlite directory, default `./data`
34
+ - `platform_id`: platform ID sent to server, default `1`
35
+ - `timeout_seconds`: request/response wait timeout, default `1000`
36
+ - `pull_batch_size`: sync page size, default `100`
37
+ - `sdk_version`: value sent in ws query, default `python-ws-0.1.0`
38
+ - `auto_sync_on_connect`: run `sync_once()` immediately after first connect, default `False`
39
+ - `auto_sync_on_reconnect`: auto sync after reconnect, default `True`
40
+ - `auto_reconnect`: reconnect automatically on disconnect, default `True`
41
+ - `max_reconnect_attempts`: reconnect attempt cap, default `300`
42
+ - `reconnect_backoff_seconds`: reconnect interval sequence, default `(1, 2, 4, 8, 16)`
43
+ - `enable_heartbeat`: send ws ping periodically, default `True`
44
+ - `heartbeat_interval_seconds`: ping interval, default `24`
45
+ - `heartbeat_pong_timeout_seconds`: pong timeout, default `30`
46
+ - `self_user_info_cache_ttl_seconds`: in-memory cache ttl for self profile (`senderNickname/senderFaceURL`), default `600` seconds
47
+
48
+ ### 2. Register callbacks
49
+
50
+ Available callbacks in `OpenIMWSSDK(...)`:
51
+
52
+ - `on_recv_new_message(msg: WSMessage)`: called for online real-time messages
53
+ - `on_recv_offline_new_message(msg: WSMessage)`: called for offline/sync messages
54
+ - `on_connecting()`: called before connect/reconnect attempts
55
+ - `on_connect_success()`: called after successful connect/reconnect
56
+ - `on_connect_failed(exc)`: called when one connect attempt fails
57
+ - `on_kicked_offline()`: called when server returns kick event
58
+ - `on_error(exc)`: called on internal errors (decode/send/read/reconnect, etc.)
59
+ - `logger(text)`: sdk log output callback
60
+
61
+ ### 2.1 `WSMessage` fields
62
+
63
+ Raw payload keys:
64
+
65
+ - `conversationID`: conversation id (sdk adds this key)
66
+ - `sendID`, `recvID`, `groupID`
67
+ - `clientMsgID`, `serverMsgID`
68
+ - `senderPlatformID`, `senderNickname`, `senderFaceURL`
69
+ - `sessionType` (`1` single, `2` group, `3` super group)
70
+ - `msgFrom`, `contentType`, `content`
71
+ - `seq`, `sendTime`, `createTime`
72
+ - `status`, `isRead`
73
+ - `options` (`dict[str, bool]`)
74
+ - `offlinePushInfo` (`dict`)
75
+ - `atUserIDList` (`list[str]`)
76
+ - `attachedInfo`, `ex`
77
+
78
+ Python field names in `WSMessage`:
79
+
80
+ - `conversation_id`, `send_id`, `recv_id`, `group_id`
81
+ - `client_msg_id`, `server_msg_id`
82
+ - `sender_platform_id`, `sender_nickname`, `sender_face_url`
83
+ - `session_type`, `msg_from`, `content_type`, `content`
84
+ - `seq`, `send_time`, `create_time`, `status`, `is_read`
85
+ - `options`, `offline_push_info`, `at_user_id_list`, `attached_info`, `ex`
86
+ - `raw`: original dictionary payload
87
+ - `content_json`: parsed JSON from `content` (or `None` when not valid JSON)
88
+ - `content_obj`: typed content object parsed by `content_type`
89
+
90
+ `content_type -> content_obj` mapping (same as `sdk_struct.go`):
91
+
92
+ - `101` -> `TextElem`
93
+ - `102` -> `PictureElem`
94
+ - `103` -> `SoundElem`
95
+ - `104` -> `VideoElem`
96
+ - `105` -> `FileElem`
97
+ - `106` -> `AtTextElem`
98
+ - `107` -> `MergeElem`
99
+ - `108` -> `CardElem`
100
+ - `109` -> `LocationElem`
101
+ - `110/119/120` -> `CustomElem`
102
+ - `113` -> `TypingElem`
103
+ - `114` -> `QuoteElem`
104
+ - `115` -> `FaceElem`
105
+ - `117` -> `AdvancedTextElem`
106
+ - `118` -> `MarkdownTextElem`
107
+ - other content types -> `NotificationElem`
108
+
109
+ Minimal callback example:
110
+
111
+ ```python
112
+ from openim_sdk import OpenIMWSSDK, WSConfig, WSMessage, TextElem, PictureElem
113
+
114
+
115
+ def on_new_model(msg: WSMessage) -> None:
116
+ if isinstance(msg.content_obj, TextElem):
117
+ print("[text]", msg.content_obj.content)
118
+ elif isinstance(msg.content_obj, PictureElem):
119
+ print("[picture]", msg.content_obj.source_picture)
120
+ else:
121
+ print("[other]", msg.content_type, msg.content_obj)
122
+
123
+
124
+ sdk = OpenIMWSSDK(
125
+ WSConfig(ws_addr="ws://127.0.0.1:10001/msg_gateway"),
126
+ on_recv_new_message=on_new_model,
127
+ )
128
+ ```
129
+
130
+ ### 3. Full example (recommended pattern)
131
+
132
+ ```python
133
+ import time
134
+
135
+ from openim_sdk import OpenIMWSSDK, TextElem, WSConfig, WSSDKError, WSMessage
136
+
137
+
138
+ def on_new(msg: WSMessage) -> None:
139
+ if isinstance(msg.content_obj, TextElem):
140
+ print("[new text]", msg.send_id, "->", msg.recv_id, msg.content_obj.content)
141
+ else:
142
+ print("[new]", msg.send_id, "->", msg.recv_id, msg.content_type, msg.content_obj)
143
+
144
+
145
+ def on_offline(msg: WSMessage) -> None:
146
+ print("[offline]", msg.client_msg_id, msg.seq)
147
+
148
+
149
+ def on_err(exc: Exception) -> None:
150
+ print("[error]", exc)
151
+
152
+
153
+ sdk = OpenIMWSSDK(
154
+ WSConfig(
155
+ ws_addr="ws://127.0.0.1:10001/msg_gateway",
156
+ api_addr="http://127.0.0.1:10002",
157
+ data_dir="./openim_py_data",
158
+ auto_sync_on_connect=True,
159
+ auto_sync_on_reconnect=True,
160
+ ),
161
+ on_recv_new_message=on_new,
162
+ on_recv_offline_new_message=on_offline,
163
+ on_connecting=lambda: print("[ws] connecting"),
164
+ on_connect_success=lambda: print("[ws] connected"),
165
+ on_connect_failed=lambda e: print("[ws] connect failed:", e),
166
+ on_kicked_offline=lambda: print("[ws] kicked offline"),
167
+ on_error=on_err,
168
+ logger=lambda s: print("[sdk]", s),
169
+ )
170
+
171
+ try:
172
+ # 1) login first
173
+ sdk.login(user_id="u1", token="YOUR_TOKEN")
174
+ # 2) then start websocket threads
175
+ sdk.start()
176
+
177
+ # send peer message
178
+ ack = sdk.send_text("hello from ws sdk", recv_id="u2")
179
+ print("send ack:", ack) # {"serverMsgID": "...", "clientMsgID": "...", "sendTime": ...}
180
+
181
+ # keep process alive
182
+ while True:
183
+ time.sleep(1)
184
+ except WSSDKError as e:
185
+ print("business error:", e.err_code, e.err_msg)
186
+ except KeyboardInterrupt:
187
+ pass
188
+ finally:
189
+ # stop background threads and close db
190
+ sdk.logout()
191
+ ```
192
+
193
+ ### 4. Sending messages
194
+
195
+ - Single chat:
196
+
197
+ ```python
198
+ sdk.send_text("hello", recv_id="u2")
199
+ ```
200
+
201
+ - Group chat:
202
+
203
+ ```python
204
+ sdk.send_text("hello group", group_id="g123", session_type=2)
205
+ ```
206
+
207
+ `send_text(...)` notes:
208
+
209
+ - must pass one of `recv_id` or `group_id`
210
+ - `session_type` defaults to single chat (`1`) if `recv_id` is set, otherwise group (`2`)
211
+ - 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
212
+ - supported session values:
213
+ - `1`: single chat
214
+ - `2`: group chat
215
+ - `3`: super group chat
216
+
217
+ ### 5. Manual sync
218
+
219
+ If you want explicit control (instead of `auto_sync_on_connect/reconnect`), call:
220
+
221
+ ```python
222
+ sdk.sync_once()
223
+ ```
224
+
225
+ This pulls missing seq ranges and dispatches them through `on_recv_offline_new_message`.
226
+
227
+ ### 6. Lifecycle and best practices
228
+
229
+ - call order should be: `login(...) -> start() -> send/recv -> logout()`
230
+ - `stop()` only stops ws threads; `logout()` stops threads and closes local storage
231
+ - keep your process alive after `start()`, otherwise daemon threads exit with process
232
+ - for production:
233
+ - keep `auto_reconnect=True`
234
+ - keep heartbeat enabled
235
+ - handle `on_error` and `on_connect_failed` for observability/retry alerts
236
+ - persist your own app-level offsets/state if required
237
+
238
+ ## HTTP polling quick start
239
+
240
+ ```python
241
+ from openim_sdk import OpenIMSDK, SDKConfig
242
+
243
+ sdk = OpenIMSDK(
244
+ SDKConfig(api_addr="http://127.0.0.1:10002", data_dir="./openim_py_data")
245
+ )
246
+ sdk.login(user_id="u1", token="YOUR_TOKEN")
247
+ sdk.start_receiving()
248
+ sdk.send_text("hello", recv_id="u2")
249
+ me = sdk.get_self_user_info()
250
+ print(me.user_id, me.nickname, me.face_url)
251
+ ```
252
+
253
+ ## WebSocket features
254
+
255
+ - Login with `user_id + token`
256
+ - Send text message with protobuf payload (`MsgData`)
257
+ - Receive `PushMessages` from ws binary frames
258
+ - Gob encode/decode for `GeneralWsReq/GeneralWsResp`
259
+ - Gzip frame compression/decompression
260
+ - Heartbeat (`ping/pong`)
261
+ - Auto reconnect with backoff (`1,2,4,8,16` seconds loop by default)
262
+ - Auto sync missed messages on connect/reconnect
263
+ - Local SQLite persistence:
264
+ - message rows
265
+ - per-conversation latest synced seq
266
+
267
+ ## Local database
268
+
269
+ - WS mode: `<data_dir>/OpenIM_pyws_<user_id>.db`
270
+ - HTTP mode: `<data_dir>/OpenIM_py_<user_id>.db`
271
+
272
+ Tables:
273
+
274
+ - `conversation_seq(conversation_id, last_seq)`
275
+ - `messages(...)`
@@ -0,0 +1,57 @@
1
+ __version__ = "0.1.3"
2
+
3
+ from .client import OpenIMSDK, SDKConfig, SDKError, SDKHTTPError
4
+ from .models import (
5
+ CONTENT_TYPE_NAMES,
6
+ AdvancedTextElem,
7
+ AtTextElem,
8
+ CardElem,
9
+ ContentType,
10
+ CustomElem,
11
+ FaceElem,
12
+ FileElem,
13
+ LocationElem,
14
+ MarkdownTextElem,
15
+ MergeElem,
16
+ NotificationElem,
17
+ PictureElem,
18
+ QuoteElem,
19
+ SelfUserInfo,
20
+ SoundElem,
21
+ TextElem,
22
+ TypingElem,
23
+ VideoElem,
24
+ WSMessage,
25
+ )
26
+ from .ws_client import OpenIMWSSDK, WSConfig, SDKError as WSSDKError
27
+
28
+ __all__ = [
29
+ "__version__",
30
+ "OpenIMSDK",
31
+ "SDKConfig",
32
+ "SDKError",
33
+ "SDKHTTPError",
34
+ "WSSDKError",
35
+ "WSMessage",
36
+ "SelfUserInfo",
37
+ "ContentType",
38
+ "CONTENT_TYPE_NAMES",
39
+ "TextElem",
40
+ "PictureElem",
41
+ "SoundElem",
42
+ "VideoElem",
43
+ "FileElem",
44
+ "AtTextElem",
45
+ "MergeElem",
46
+ "CardElem",
47
+ "LocationElem",
48
+ "CustomElem",
49
+ "QuoteElem",
50
+ "FaceElem",
51
+ "AdvancedTextElem",
52
+ "TypingElem",
53
+ "MarkdownTextElem",
54
+ "NotificationElem",
55
+ "OpenIMWSSDK",
56
+ "WSConfig",
57
+ ]