Metadata-Version: 2.4
Name: imbot-sdk-python
Version: 0.1.2
Summary: JuggleIM Python Bot SDK — WebSocket long-connection IM client for bots and server-side agents
Author-email: Tyler <tyler@juggle.im>
Maintainer-email: Tyler <tyler@juggle.im>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/juggleim/imbot-sdk-python
Project-URL: Repository, https://github.com/juggleim/imbot-sdk-python
Project-URL: Issues, https://github.com/juggleim/imbot-sdk-python/issues
Project-URL: Documentation, https://github.com/juggleim/imbot-sdk-python#readme
Keywords: juggleim,im,instant-messaging,chat,bot,websocket,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: protobuf>=5.0
Requires-Dist: websocket-client>=1.6
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# imbot-sdk-python

**English** | [简体中文](README.zh-CN.md)

[![PyPI version](https://img.shields.io/pypi/v/imbot-sdk-python.svg)](https://pypi.org/project/imbot-sdk-python/)
[![Python versions](https://img.shields.io/pypi/pyversions/imbot-sdk-python.svg)](https://pypi.org/project/imbot-sdk-python/)
[![License](https://img.shields.io/pypi/l/imbot-sdk-python.svg)](https://github.com/juggleim/imbot-sdk-python/blob/main/LICENSE)

`imbot-sdk-python` is the JuggleIM Python SDK. It keeps a long-lived WebSocket
connection to the IM service, making it a good fit for bots, server-side message
agents, or any Python program that needs to actively send and receive messages.
It covers connection management, messaging, conversation queries, history,
chatrooms, user status, RTC rooms and file credential queries.

- Source: <https://github.com/juggleim/imbot-sdk-python>
- Distribution name: `imbot-sdk-python`; import name: `import imbot_sdk`

## Installation

```bash
pip install imbot-sdk-python
```

Dependencies (`protobuf>=5.0`, `websocket-client>=1.6`) are installed
automatically. Python 3.8+.

Install from source:

```bash
git clone https://github.com/juggleim/imbot-sdk-python.git
cd imbot-sdk-python
pip install .
```

## Test environment

| Item | Value |
| --- | --- |
| Server API | `http://127.0.0.1` |
| WebSocket | `wss://127.0.0.1` |
| AppKey | `AppKey` |

> A user token is issued by your JuggleIM app server / console using your AppKey
> + AppSecret via the Server API. The SDK itself only establishes the
> connection and sends/receives messages.

## Obtaining a token

`connect(token)` needs a per-user token. Tokens are minted by your app server
through the JuggleIM Server API using your AppKey + AppSecret. Registering a bot
(or a user) returns a token:

- Endpoint: `POST {ServerAPI}/bots/register` (or `/users/register`)
- Auth headers: `appkey`, `nonce` (random 0-9999), `timestamp` (epoch ms),
  `signature = lowercase_hex( SHA1(AppSecret + nonce + timestamp) )`,
  `Content-Type: application/json`
- Response: `{"code":0,"msg":"success","data":{"user_id":"...","token":"..."}}`

A ready-to-use helper (standard library only, no hard-coded credentials) is
[`examples/register_bot.py`](examples/register_bot.py):

```bash
export IMBOT_API_URL="https://api.juggleim.com/apigateway"
export IMBOT_APPKEY="<your-appkey>"
export IMBOT_APPSECRET="<your-appsecret>"

# register a bot and print its token
python examples/register_bot.py --bot-id my-bot --nickname "My Bot"

# or register a normal user
python examples/register_bot.py --user --user-id u1 --nickname "User 1"
```

> Keep your AppSecret on the server side — never ship it inside client apps.
> If the Server API host presents an expired/invalid TLS certificate, pass
> `--insecure` to bypass verification (use only against endpoints you trust).

## Feature overview

- Connection management: `connect`, `disconnect`, `logout`, heartbeat keep-alive,
  automatic reconnect.
- Messaging: send/receive for private, group, chatroom and public-channel
  conversations.
- Message management: history query, recall, modify, mark-read, search, top
  messages.
- Conversation management: list, unread count, top, mute, tags.
- User & group info: user profile, friend profile, group info, online-status
  subscription.
- Chatroom: join/quit, chatroom messages, attribute sync.
- RTC: create / join / query / quit RTC rooms.
- File: credential query via `get_file_cred`.

## Quick start

### 1. Create a client

```python
from imbot_sdk import ImBotClient

client = ImBotClient("wss://127.0.0.1", "your-appkey")
```

Notes:

- Pass the base `address`; the SDK appends the path automatically to form
  `ws://host/imbot` or `wss://host/imbot`.
- `client.platform` defaults to `"Bot"`.
- `client.auto_reconnect` defaults to `True`.

### 2. Connect

```python
code, ack = client.connect("your-token")
```

Notes:

- On success `code == ClientErrorCode.SUCCESS`.
- After success `ack.userId` is written to `client.user_id`.
- `connect` may only be called while disconnected; a repeated connect returns
  `ClientErrorCode.CONNECT_EXISTED`.
- After an explicit `disconnect()` or `logout()`, auto-reconnect stops.

### 3. Two ways to receive messages

Prefer the high-level listener (you receive a decoded `Message`):

```python
from imbot_sdk import MessageListener, messages

class MyListener(MessageListener):
    def on_message_receive(self, msg):
        if isinstance(msg.msg_content, messages.TextMessage):
            print("text:", msg.msg_content.content)

client.add_message_listener(MyListener())
```

If you want the raw protobuf payloads:

```python
client.on_message_callback = lambda down_msg: print(down_msg)
client.on_stream_msg_callback = lambda stream_msg: print(stream_msg)
```

### 4. Send a message

```python
from imbot_sdk import Conversation, messages
from imbot_sdk.pb import appmessages_pb2 as pb

text = messages.TextMessage("hello from imbot-sdk-python")
up = client.build_up_msg(text, client_uid="bot-1")

conv = Conversation(conversation_type=pb.Private, conversation="target-user-id")
code, ack = client.send_message(conv, up)
# ack.msgId / ack.msgSeqNo carry the server's acknowledgement
```

`build_up_msg` encodes a content model into `UpMsg.msgType / msgContent /
flags`; you can also construct a `pb.UpMsg` directly.

## Full example: connect, listen, echo

```python
import time
from imbot_sdk import ImBotClient, Conversation, MessageListener, ClientErrorCode, messages
from imbot_sdk.pb import appmessages_pb2 as pb

class Echo(MessageListener):
    def __init__(self, client):
        self.client = client
    def on_message_receive(self, msg):
        c = msg.msg_content
        if isinstance(c, messages.TextMessage) and msg.sender_id != self.client.user_id:
            up = self.client.build_up_msg(messages.TextMessage("echo: " + c.content),
                                          client_uid="echo-%d" % time.time_ns())
            self.client.send_message(msg.conversation, up)

client = ImBotClient("wss://127.0.0.1", "AppKey")
client.add_message_listener(Echo(client))

code, ack = client.connect("your-token")
assert code == ClientErrorCode.SUCCESS
print("connected:", ack.userId)

# Proactively send a private message to a user
up = client.build_up_msg(messages.TextMessage("hi"), client_uid="greet-1")
client.send_message(Conversation(conversation_type=pb.Private, conversation="target-user-id"), up)

try:
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    client.disconnect()
```

A runnable version lives in [`examples/echo_bot.py`](examples/echo_bot.py). The
recommended way is `.env` + the launch script:

```bash
cp examples/.env.example examples/.env
# edit examples/.env, at least set IMBOT_TOKEN (IMBOT_TARGET optional)
bash examples/run.sh
```

`run.sh` loads `examples/.env` and starts the echo bot; extra arguments are
passed through (e.g. `bash examples/run.sh --target some-user-id`). You can also
skip the script and pass args / export env vars directly:

```bash
python examples/echo_bot.py --token <your-token> --target <target-user-id>
```

Configuration (env vars / `.env`):

| Variable | Description | Required |
| --- | --- | --- |
| `IMBOT_TOKEN` | User token | Yes |
| `IMBOT_TARGET` | Target user id to greet on startup | No |
| `IMBOT_ADDRESS` | WebSocket base address | No |
| `IMBOT_APPKEY` | App AppKey | No |

Additional notes:

- Use `pb.Private` for private chats, `pb.Group` for groups, `pb.Chatroom` for
  chatrooms, `pb.PublicChannel` for public channels.
- Chatroom messages can also be sent via `client.send_chatroom_msg(chatroom_id, up_msg)`.
- A received `msg.msg_content` is already decoded by content type, so you can
  `isinstance`-check it directly.

## Message types

Built-in content models live in `imbot_sdk.messages`:

| Type | Identifier | Class |
| --- | --- | --- |
| Text | `jg:text` | `TextMessage` |
| Image | `jg:img` | `ImageMessage` |
| File | `jg:file` | `FileMessage` |
| Video | `jg:video` | `VideoMessage` |
| Voice | `jg:voice` | `VoiceMessage` |
| Stream text | `jg:streamtext` | `StreamTextMessage` |
| Recall notice | `jg:recallinfo` | `RecallInfoMessage` |
| Merged message | `jg:merge` | `MergeMessage` |
| Thumbnail-packed image | `jg:tpimg` | `ThumbnailPackedImageMessage` |
| Snapshot-packed video | `jg:spvideo` | `SnapshotPackedVideoMessage` |

For content types that are not built in, the SDK falls back to
`messages.UnknownMessage`. The JSON encode/decode fields of these models are
stable, so messages interoperate with the other JuggleIM client SDKs.

## Common APIs

> See [`docs/API.md`](docs/API.md) for the full method list. Naming follows
> Python conventions (snake_case).

### Connection & state

`connect(token)`, `reconnect()`, `disconnect()`, `logout()`, `ping()`,
`add_connection_status_change_listener(listener)`

### Messages

`send_message(conversation, up_msg)`, `qry_history_msgs(req)`, `recall_msg(req)`,
`modify_msg(req)`, `mark_read_msg(req)`, `msg_search(req)`,
`msg_global_search(req)`, `set_top_msg(req)`, `del_top_msg(req)`

### Conversations

`get_conversation(req)`, `get_conversations(req)`, `sync_conversations(req)`,
`clear_unread_count(req)`, `set_conversation_top(req)`, `set_mute(req)`,
`delete_conversations(req)`

### Users, groups, status

`fetch_user_info(user_id)`, `fetch_group_info(group_id)`,
`fetch_friend_info(friend_user_id)`, `get_user_status(req)`,
`subscribe_user_status(req)`, `unsubscribe_user_status(req)`

### Chatroom

`join_chatroom(chatroom_id)`, `quit_chatroom(chatroom_id)`,
`send_chatroom_msg(chatroom_id, up_msg)`, `set_attributes(chatroom_id, attributes)`,
`remove_attributes(chatroom_id, keys)`

### RTC

`create_rtc_room(req)`, `join_rtc_room(req)`, `qry_rtc_room(room_id)`,
`quit_rtc_room(room_id)`, `rtc_invite(req)`

### File

`get_file_cred(req)`

> The SDK provides file credential queries; the actual file upload/download flow
> must be handled by your application against your storage service.

## Usage notes

- `publish` and `query` both require the connection state to be `CONNECTED`,
  otherwise they return `ClientErrorCode.CONNECT_CLOSED`.
- Sends and queries wait up to 10 seconds for an ACK, returning
  `ClientErrorCode.SEND_TIMEOUT` / `ClientErrorCode.QUERY_TIMEOUT` on timeout.
- The SDK handles heartbeats automatically; if no downstream data arrives for
  more than two heartbeat windows (~20s), it disconnects and tries to reconnect.
- Message listener callbacks run on internal threads — avoid long blocking work,
  and hand off to a worker queue if needed.
- Image / file / voice / video content models only encode/decode content; they
  do not upload the underlying files.

## Regenerating protobuf

The `.proto` sources live in [`proto/`](proto/). Regenerate after editing:

```bash
bash proto/gen.sh
```

## Module layout

| Module | Description |
| --- | --- |
| `imbot_sdk.ImBotClient` | Main client entry point |
| `imbot_sdk.ClientErrorCode` / `ConnectState` | Error codes and connection state |
| `imbot_sdk.models` | Domain models (`Conversation` / `Message` / `UserInfo` …) |
| `imbot_sdk.messages` | Message content models |
| `imbot_sdk.pb` | Protobuf types (`appmessages_pb2` / `connect_pb2` / `chatroom_pb2` / `rtcroom_pb2`) |

## License

[LICENSE](LICENSE) (Apache-2.0)
