Metadata-Version: 2.4
Name: magic-campus-sdk
Version: 0.2.20
Summary: MagicCampus platform SDK and CLI with bundled IM transport support.
Author: MagicCampus Contributors
License-Expression: AGPL-3.0-or-later
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx<1,>=0.27
Requires-Dist: protobuf<7,>=6.32.0
Requires-Dist: websocket-client<2,>=1.8.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"

# magic-campus-sdk

Python SDK and CLI for MagicCampus platform integrations. IM remains a supported domain, and this repository keeps the bundled OpenIM transport inside `magiccampus_sdk`.

This project removes direct user-facing dependency on OpenIM token and endpoint configuration. You only configure:

- `apiurl`
- `apikey`
- `platform`

At runtime, IM-backed flows call:

`GET /api/v1/accounts/imToken?platform=<PLATFORM>`

and uses the response fields:

- `userId`
- `imToken`
- `wsApi`
- `httpApi`

## Install locally

```bash
uv sync
```

This repository vendors only the OpenIM transport pieces that `magiccampus_sdk` actually uses, and layers platform-facing SDK / CLI commands on top.

IM-specific low-level internals, models, and protobufs now live under `magiccampus_sdk.im`.

## CLI quick start

```bash
magic-campus --help
magic-campus config init --apiurl https://api-test.pearlapi.com --apikey "$MAGICCAMPUS_APIKEY" --platform IOS --default true
magic-campus auth status --format pretty
magic-campus im group get --group group_123 --format pretty
magic-campus im group get-batch --groups group_123,group_456
magic-campus im notify send --to user:10134002 --text "hello"
magic-campus im notify send --to user:10134002 --markdown "# title\n\nbody"
magic-campus im notify send --to user:10134002 --custom '{"data":"{\"customType\":1}","extension":"example","description":"example custom message"}'
magic-campus im notify send --to user:10134002 --custom '{"customType":503,"data":{"title":"title","subTitle":"subTitle","schema":"https://example.com/playground"}}' --cover-image /path/to/playground.jpg
magic-campus im notify send-link-card --to user:10134002 --title "title" --sub-title "subTitle" --schema "https://example.com/playground" --cover-image /path/to/playground.jpg
magic-campus im notify send-link-card --to user:10134002 --title "title" --sub-title "subTitle" --schema "https://example.com/playground" --cover-image https://example.com/playground.jpg
magic-campus space get --group group_123 --format pretty
magic-campus space list-notice --space space_123 --format table
magic-campus space list-notice --group group_123 --format table
magic-campus space create-notice --space space_123 --title "Maintenance" --text "Starts at 22:00" --expiretime 1785510000000
magic-campus space create-notice --group group_123 --title "Maintenance" --text "Starts at 22:00" --expiretime 1785510000000
magic-campus space group update-name --group group_123 --name "New group name"
magic-campus user get --id 10134002 --format pretty
magic-campus user list --all --academy-code Nature --fields userId,nickname,email --format table
magic-campus user list --random --fields userId,nickname --format table
magic-campus user update-profile --id 10134002 --nickname "New Name" --avatar-url "https://cdn.example.com/avatar.png"
magic-campus user update-profile --id 10134002 --avatar-url "" # restore the default avatar
magic-campus user get-growth-profile --id 10134002 --format pretty
magic-campus user init-growth-profile --id 10134002 --reset false
magic-campus user set-academy --id 10134002 --academy-code Nature
magic-campus user adjust-elements --id 10134002 --metal-delta 5 --water-delta -2
```

`send-link-card` returns only `ok`, `serverMsgID`, and `sendTime` by default. Add `--debug` for the full send result.
For an HTTP(S) cover URL, the CLI reads its image dimensions and keeps that URL in the card without uploading a copy.

`magic-campus config init` also supports an interactive TTY flow when you omit auth flags.

`space get --group <groupId>` includes `personGroupId`, taken from the `groupId` field of the space returned by `/agent/v1/space/getByGroupId`.
The output's `groupId` is the ID supplied to the command. If the response has no `groupId` or its value is null, `personGroupId` is an empty string.
The output also includes the response's `innerData` unchanged, preserving `groupId`, `groupType` (`PERSON` or `AGENT`), `groupName`, and `memberCount` with their original value types. If `innerData` is absent or null, the output is null.

`im notify send` sends over MagicCampus HTTP `POST /agent/v1/im/sendMessage`. `im notify history` still relies on the websocket transport and local sync database.

`im group get` returns the group details in `groupInfo`; `im group get-batch` returns them in `groupInfos`.
Both commands log in using the configured account and call `POST /group/get_groups_info` through the IM SDK.
Batch IDs accept a comma-separated list or a JSON array, for example `--groups '["group_123","group_456"]'`.
Use `--group-id` / `--group-ids` as aliases for `--group` / `--groups`, and `--account` / `--platform` to select an account or override its platform.
These commands query known group IDs; they do not list joined groups. A missing single group returns an error; batch results include only the groups returned by the server.

Legacy compatibility commands remain available:

```bash
magic-campus notify +send --to user:10134002 --text "hello"
magic-campus notify +history --to user:10134002
```

CLI config is stored at:

`~/.magic-campus/auth/magiccampus-im-sdk.json`

Supported commands:

- `config init|list|use|remove`
- `auth status`
- `im group get --group <groupId>`
- `im group get-batch --groups <id1,id2>`
- `im notify send`
- `im notify history`
- `im reaction add --conversation-id <id> --server-msg-id <id> --msg-sender-id <userId> --reaction-code <code>`
- `im reaction delete --conversation-id <id> --server-msg-id <id> --msg-sender-id <userId> --reaction-id <id>`
- `space get`
- `space list-notice (--space <spaceId> | --group <groupId>)`
- `space create-notice (--space <spaceId> | --group <groupId>)`
- `space group update-name`
- `user get`
- `user list (--all | --random) [--fields <field1,field2>] [--academy-code <code>]`
- `user update-profile --id <userId> [--nickname <name>] [--avatar-url <url>]`
- `user get-growth-profile --id <userId>`
- `user init-growth-profile --id <userId> [--reset <true|false>]`
- `user set-academy --id <userId> --academy-code <Material|Digital|Sound|Movement|Nature|Healing>`
- `user adjust-elements --id <userId> [--metal-delta <int>] [--wood-delta <int>] [--water-delta <int>] [--fire-delta <int>] [--earth-delta <int>]`
- `feed list`
- `notify +send` (compat)
- `notify +history` (compat)

## Python SDK quick start

```python
from magiccampus_sdk import MagicCampusConfig, MagicCampusSDK

sdk = MagicCampusSDK(
    MagicCampusConfig(
        apiurl="https://api-test.pearlapi.com",
        apikey="YOUR_API_KEY",
        platform="IOS",
    )
)

space = sdk.get_space_by_group_id("GROUP_ID")
print(space.id)
print(space.name)
print(space.introduction)
```

## Message reactions

The first version supports adding and deleting reactions through
`POST /im/v1/message/reaction` and `DELETE /im/v1/message/reaction` on the configured
`apiurl`. These calls use the configured `apikey` as the Bearer JWT required by
the [Swagger definition](https://api-test.pearlapi.com/im/swagger/index.html#/%E6%B6%88%E6%81%AF%E8%A1%A8%E6%83%85/post_im_v1_message_reaction).
They do not require IM login or a websocket connection.

```python
from magiccampus_sdk import MagicCampusClient, MagicCampusClientConfig

client = MagicCampusClient(MagicCampusClientConfig(
    apiurl="https://api-test.pearlapi.com",
    apikey="YOUR_USER_JWT",
))
reaction = client.im.add_message_reaction(
    conversation_id="sg_g1001",
    server_msg_id="srv_msg_001",
    msg_sender_id="u2",
    reaction_code="1",
)
if reaction is not None:
    cancelled = client.im.delete_message_reaction(
        conversation_id="sg_g1001",
        server_msg_id="srv_msg_001",
        msg_sender_id="u2",
        reaction_id=reaction["id"],
    )
```

`msg_sender_id` is the original message sender, not the reacting user; the backend
determines the acting user from the JWT. `reaction_code` must be a string.
Conversation IDs use `si_{min}_{max}` for private chats and `sg_{groupID}` for groups.
Adding a different code replaces the user's active reaction to that message;
adding the same active code is idempotent. Deletion uses the record `id` returned
by addition, allows only the user's own reaction, and may return `None` when the
record is already removed. Successful responses return the `data` object unchanged.

```bash
magic-campus im reaction add --conversation-id sg_g1001 --server-msg-id srv_msg_001 --msg-sender-id u2 --reaction-code '1'
magic-campus im reaction delete --conversation-id sg_g1001 --server-msg-id srv_msg_001 --msg-sender-id u2 --reaction-id 65f1a2b3c4d5e6f708090a0b
```

Both commands support `--account <id>` and the global `--format` option. The add
command returns the reaction record under `result`; use `result.id` for deletion.

## Python websocket quick start

```python
from magiccampus_sdk import MagicCampusWSSDK, MagicCampusWSConfig

sdk = MagicCampusWSSDK(
    MagicCampusWSConfig(
        apiurl="https://api-test.pearlapi.com",
        apikey="YOUR_API_KEY",
        platform="IOS",
        data_dir="./magiccampus_data",
    )
)

space = sdk.get_space_by_group_id("GROUP_ID")
print(space.name)

sdk.login()
sdk.start()
sdk.send_text("hello", recv_id="10134002")
sdk.send_markdown("# title\n\nbody", recv_id="10134002")

group = sdk.get_group_info("GROUP_ID")
print(group["groupName"])

groups = sdk.get_groups_info(["GROUP_ID_1", "GROUP_ID_2"])
```

Runnable examples live in [magiccampus_sdk/example.py](magiccampus_sdk/example.py) and [magiccampus_sdk/example_ws.py](magiccampus_sdk/example_ws.py).

## CLI smoke script

An executable smoke helper lives at [scripts/smoke_cli.py](scripts/smoke_cli.py).

Offline-safe mode only exercises config persistence and notify dry-run:

```bash
uv run python scripts/smoke_cli.py
```

This mode does not require a real API key. It uses a placeholder key by default and avoids any live auth call.

## Real network mode

To run the live auth probe, provide a real API key and enable `--live`:

```bash
export MAGICCAMPUS_APIURL=https://api-test.pearlapi.com
export MAGICCAMPUS_APIKEY='your real api key'
export MAGICCAMPUS_PLATFORM=IOS
uv run python scripts/smoke_cli.py --live
```

Optional variables:

- `MAGICCAMPUS_SMOKE_TARGET`: target used by dry-run and optional live send, default `user:smoke-target`
- `MAGICCAMPUS_PLATFORM`: defaults to `IOS`

If you also want to send a real text message, set a real target and pass `--live-send`:

```bash
export MAGICCAMPUS_SMOKE_TARGET='user:10134002'
uv run python scripts/smoke_cli.py --live --live-send --text 'hello from smoke script'
```

`--live-send` performs a real outbound send, so use it only with a valid target and credentials.

## Notes

- `apiurl` can point to any MagicCampus API base URL; IM-backed flows append `/api/v1/accounts/imToken`
- `client.user.get(...)` calls `GET /api/v1/accounts/getUserInfo` with `apiurl` and `apikey` directly
- `platform` defaults to `IOS`
- low-level IM send / receive behavior comes from the bundled transport modules inside `magiccampus_sdk`
- the wrapper does not persist raw OpenIM credentials in CLI config
