# Presence (/zh-CN/api-reference/rtm/unity/presence)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

Presence 提供监控用户上线、下线及用户历史状态变更通知的能力。通过 Presence 功能，你可以实时获取以下信息：

- 用户加入或离开指定频道的实时事件通知
- 自定义临时用户状态及其变更实时事件通知
- 查询指定用户加入或订阅了哪些频道
- 查询指定频道有哪些用户加入及其用户临时状态数据

:::info[信息]
Presence 能力适用于 Message Channel 和 Stream Channel。
:::

## WhoNowAsync [#whonowasync]

### 接口描述 [#接口描述]

调用 `WhoNowAsync` 方法，你可以实时查询指定频道的在线用户数量、在线用户列表及在线用户的临时状态等信息。

### 接口方法 [#接口方法]

你可以通过以下方式调用 `WhoNowAsync` 方法：

```csharp
RtmResult<WhoNowResult> rtmClient.GetPresence().WhoNowAsync(
    string channelName,
    RTM_CHANNEL_TYPE channelType,
    PresenceOptions options
    );
```

|     参数      |     类型      | 是否必填 |       默认值        | 描述     |
| ---------- | --------------- | ----- | ---- | ----------- |
| `channelName` |    string     |   选填   |          -          | 频道名称。如果未填写该参数，则 `WhoNowAsync` 方法会返回符合 `RTM_CHANNEL_TYPE` 条件的所有频道的在线用户信息。 |
| `channelType` | `RTM_CHANNEL_TYPE` |   必填   | `STREAM` | 频道类型。详见 [`RTM_CHANNEL_TYPE`](/zh-CN/api-reference/rtm/unity/enumv#rtm_channel_type)。        |
|   `options`   | `PresenceOptions`  |   选填   |          -          | 在线用户的信息选项。           |

`PresenceOptions` 包含以下属性：

|      属性       |  类型  | 是否必填 | 默认值  | 描述                  |
| ------------ | --- | ----- | ---- | ---------- |
| `includeUserId` |  bool  |   选填   | `true`  | 是否获取在线用户的用户 ID。                    |
| `includeState`  |  bool  |   选填   | `false` | 是否获取在线用户的临时用户状态。如需设置该属性为 `true`，则需确保 `includeUIDs` 属性也为 `true`，否则方法会调用失败。 |
|     `page`      | string |   选填   |   -   | 页码书签。如不填写该属性，则默认返回第一页结果。在返回的 `Response` 中查看是否还有下页。              |

### 基本用法 [#基本用法]

```cs
var options = new PresenceOptions();
options.withState = true;

var (status,response) = await rtmClient.GetPresence().WhoNowAsync("Chat_room", RTM_CHANNEL_TYPE.MESSAGE, options);
if (result.Status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log(string.Format("You have got {0} users information ", response.TotalOccupancy));
    if (response.NextPage != null)
    {
        Debug.Log("you have the next page information waiting for read!");
    }
}
```

### 返回值 [#返回值]

`WhoNowAsync` 操作返回一个 `RtmResult<WhoNowResult>` 类型数据结构的返回值，其中包含：

|    属性    |       类型        | 描述        |
| ------- | ----- | ----------- |
|  `Status`  |   `RtmStatus`    | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。 |
| `Response` | `WhoNowResult` | 操作成功后，返回一个 `WhoNowResult` 类型的数据结构。      |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

`WhoNowResult` 包含以下属性：

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| `UserStateList` | `UserState[]` | 频道在线用户及其临时状态信息列表。 |
| `NextPage` | string | [presence-unity-html-0-2-2 details](#table-slot-presence-unity-html-0-2-2) |
| `TotalOccupancy` | int | `UserStateList` 列表长度，当 `WhoNowAsync` 属性中 `includeUserId`、`includeState` 属性都设置为 `false` 时，此值表示频道当前总在线人数。 |

<a id="table-slot-presence-unity-html-0-2-2"></a>

**presence-unity-html-0-2-2 details**

下一页书签。通过该属性可以确认是否有下一页：

- 值为空表示没有下一页。
- 值不为空表示有下一页。你可以将该值填写在 `WhoNowAsync` 的 `page` 属性中，从而查询下一页结果。

`UserState[]` 包含以下属性：

| 属性     |     类型      | 描述                 |
| ------ | ---------- | ----- |
| `states` | `StateItem[]` | 用户的临时状态信息。 |
| `userId` |    string     | 用户的 ID。          |

`StateItem` 数据类型包含以下属性：

|  属性  |  类型  | 描述                            |
| ------ | ------- | ------ |
| `key`         |         string        | 用户状态的键。如果指定的键已经存在，则该键的值会被新值覆盖；如果指定的键不存在，则 SDK 会新增该键/值对。     |
| `value` | string | 用户状态的值。 |

## WhereNowAsync [#wherenowasync]

### 接口描述 [#接口描述-1]

在数据统计、App 调试等场景中，你可能需要了解指定用户订阅或加入的所有频道。调用 `WhereNowAsync` 方法，你可以实时获取指定用户所在频道的列表。

### 接口方法 [#接口方法-1]

你可以通过以下方式调用 `WhereNowAsync` 方法：

```csharp
RtmResult<WhereNowResult> rtmClient.GetPresence().WhereNowAsync(string userId);
```

|  参数  |  类型  | 是否必填 |       默认值        | 描述      |
| --- | --- | ----- | ---------------- | -------- |
| userId | string |   选填   | 当前用户的 `userId` | 用户 ID。 |

### 接口方法 [#接口方法-2]

```cs
var (status,respones) = await rtmClient.GetPresence().WhereNowAsync("Tony");
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log(string.Format("User Tony is now in {0} channels ", response.Channels.Length));
    if (response.Channels.Length > 0)
    {
        for (int i = 0; i < response.Channels.Length; i++)
        {
            var channelInfo = response.Channels[i];
            string infor = string.Format("Tony is in channelName:{0}, channelType:{1}", channelInfo.channelName, channelInfo.channelType);
            Debug.Log(infor);
        }
    }
}
```

### 返回值 [#返回值-1]

`WhereNowAsync` 操作返回一个 `RtmResult<WhereNowResult>` 类型数据结构的返回值，其中包含：

|    属性    |        类型         | 描述        |
| ------- | ---- | ----------- |
|  `Status`  |    `RtmStatus`     | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。 |
| `Response` | `WhereNowResult` | 操作成功后，返回一个 `WhereNowResult` 类型的数据结构。    |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

`WhereNowResult` 包含以下属性：

| 属性       |        类型        | 描述          |
| --------- | --- | -------- |
| `Channels` | `ChannelInfo[]` | 用户所在频道信息类表，其中包含频道名和频道类型。 |

`ChannelInfo` 包含以下属性：

|     属性      |     类型      | 描述     |
| ---------- | --------------- | ------ |
| `channelName` |    string          | 频道名称。
| `channelType` | `RTM_CHANNEL_TYPE` | 频道类型。详见 [`RTM_CHANNEL_TYPE`](/zh-CN/api-reference/rtm/unity/enumv#channeltype)。 |

## SetStateAsync [#setstateasync]

### 接口描述 [#接口描述-2]

为满足不同业务场景对用户状态的设置需求，RTM 提供 `SetStateAsync` 方法自定义临时用户状态。用户可以为自己添加分数、游戏状态、位置、心情、连麦状态等自定义状态。

设置成功后，只要用户保持订阅频道并一直在线，自定义状态就会在频道中持续存在。`SetStateAsync` 方法设置的是临时用户状态，当用户离开频道或断开与 RTM 的连接时，状态会消失。如果需要在重新加入频道或者重新连接时恢复用户状态，你需要实时在本地缓存该数据。如果你希望永久保存用户状态数据，声网推荐改用 Storage 功能的 `SetUserMetadataAsync` 方法。

如果用户修改了临时用户状态，RTM 会实时触发 `REMOTE_STATE_CHANGED` 类型的 `OnPresenceEvent` 事件通知。你可以通过订阅频道并配置对应属性来接收该事件。

### 接口方法 [#接口方法-3]

你可以通过以下方式调用 `SetStateAsync` 方法：

```cs
RtmResult<SetStateResult> rtmClient.GetPresence().SetStateAsync(
    string channelName,
    RTM_CHANNEL_TYPE channelType,
    StateItem[] items
    );
```

|     参数      |     类型      | 是否必填 |       默认值        | 描述          |
| ---------- | --------------- | ----- | ---- | ----------- |
| `channelName` |    string     |   必填   |          -          | 频道名称      |
| `channelType` | `RTM_CHANNEL_TYPE` |   必填   | `STREAM` | 频道类型。详见 [`RTM_CHANNEL_TYPE`](/zh-CN/api-reference/rtm/unity/enumv#rtm_channel_type)。 |
|    `items`    |   `StateItem[]`      |   选填   |          -          | 用户状态，类型为 `StateItem` 数组。            |

`StateItem` 数据类型包含以下属性：

|  属性  |  类型  | 描述                            |
| ------ | ------- | ------ |
| `key`         |         string        | 用户状态的键。如果指定的键已经存在，则该键的值会被新值覆盖；如果指定的键不存在，则 SDK 会新增该键/值对。     |
| `value` | string | 用户状态的值。 |

### 基本用法 [#基本用法-1]

```cs
var stateItems = new StateItem[1];
stateItems[0] = new StateItem("state", "Online");
var (status,response) = await rtmClient.GetPresence().SetStateAsync(channelName, RTM_CHANNEL_TYPE.MESSAGE, stateItems);
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log("Set State Success!");
}
```

### 返回值 [#返回值-2]

`SetStateAsync` 操作返回一个 `RtmResult<SetStateResult>` 类型数据结构的返回值，其中包含：

|    属性    |        类型         | 描述        |
| ------- | ---- | ----------- |
|  `Status`  |    `RtmStatus`     | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。 |
| `Response` | `SetStateResult` | 操作成功后，返回一个 `SetStateResult` 类型的数据结构。    |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

## GetStateAsync [#getstateasync]

### 接口描述 [#接口描述-3]

如需获取指定频道中指定用户的临时用户状态，你可以调用 `GetStateAsync` 方法。

### 接口方法 [#接口方法-4]

你可以通过以下方式调用 `GetStateAsync` 方法：

```cs
RtmResult<GetStateResult> rtmClient.GetPresence().GetStateAsync(
    string channelName,
    RTM_CHANNEL_TYPE channelType,
    string userId
    );
```

|     参数      |     类型      | 是否必填 |       默认值        | 描述          |
| ---------- | --------------- | ----- | ---- | ----------- |
|   `userId`    |    string     |   选填   |                       当前用户的 `userId`                       | 用户 ID。     |
| `channelName` |     string     |   选填   |          -          | 频道名称。    |
| `channelType` | `RTM_CHANNEL_TYPE` |   必填   | `STREAM` | 频道类型。详见 [`RTM_CHANNEL_TYPE`](/zh-CN/api-reference/rtm/unity/enumv#rtm_channel_type)。 |

### 基本用法 [#基本用法-2]

```cs
var (status,response) = await rtmClient.GetPresence().GetStateAsync(channelName, RTM_CHANNEL_TYPE.MESSAGE, userId);
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log(string.Format("User:{0}, have stateCount:{1} states",response.State.userId, response.State.states.Length));
}
```

### 返回值 [#返回值-3]

`GetStateAsync` 操作返回一个 `RtmResult<GetStateResult>` 类型数据结构的返回值，其中包含：

|    属性    |        类型         | 描述        |
| ------- | ---- | ----------- |
|  `Status`  |    `RtmStatus`     | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。 |
| `Response` | `GetStateResult` | 操作成功后，返回一个 `GetStateResult` 类型的数据结构。    |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。

`GetStateResult` 包含以下属性：

| 属性    |    类型     | 描述           |
| ------ | -------- | ------------- |
| `State` | `UserState` | 用户临时状态。 |

`UserState` 包含以下属性：

| 属性     |     类型      | 描述              |
| ------- | ---------- | ---------- |
| `states` | `StateItem[]` | 用户临时状态，类型为 `StateItem` 数组。 |
| `userId` |    string     | 用户 ID。         |

`StateItem` 数据类型包含以下属性：

|  属性  |  类型  | 描述                            |
| ------ | ------- | ------ |
| `key`         |         string        | 用户状态的键。如果指定的键已经存在，则该键的值会被新值覆盖；如果指定的键不存在，则 SDK 会新增该键/值对。     |
| `value` | string | 用户状态的值。 |

## RemoveStateAsync [#removestateasync]

### 接口描述 [#接口描述-4]

当不再需要某个临时用户状态时，你可以调用 `RemoveStateAsync` 方法删除自己的一个或多个临时状态。成功删除用户状态后，订阅该频道且开启 Presence 事件监听的用户会收到 `REMOTE_STATE_CHANGED` 类型的 `OnPresenceEvent` 事件通知，详见[事件监听](/zh-CN/api-reference/rtm/unity/configuration#事件监听)。

### 接口方法 [#接口方法-5]

你可以通过以下方式调用 `RemoveStateAsync` 方法：

```cs
RtmResult<RemoveStateResult> rtmClient.GetPresence().RemoveStateAsync(
    string channelName,
    RTM_CHANNEL_TYPE channelType,
    string[] keys
    );
```

|     参数      |     类型      | 是否必填 |       默认值        | 描述          |
| ---------- | --------------- | ----- | ---- | ----------- |
| `channelName` |    string     |   必填   |          -          | 频道名称。    |
| `channelType` | `RTM_CHANNEL_TYPE` |   必填   | `STREAM` | 频道类型。详见 [`RTM_CHANNEL_TYPE`](/zh-CN/api-reference/rtm/unity/enumv#rtm_channel_type)。 |
|    `keys`     |  string[]   |   选填   |          -          | 所需要删除的用户状态对应的键的列表。           |

### 基本用法 [#基本用法-3]

```cs
var channelName = "Chat_room";
string[] keys = new string[] { "mode" };
var (status,respones) = await rtmClient.GetPresence().RemoveStateAsync(channelName, RTM_CHANNEL_TYPE.MESSAGE, keys);
if (status.Error)
{
    Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
    Debug.Log("Remove State Success!");
}
```

### 返回值 [#返回值-4]

`RemoveStateAsync` 操作返回一个 `RtmResult<RemoveStateResult>` 类型数据结构的返回值，其中包含：

|    属性    |          类型          | 描述        |
| ------- | ------- | ----------- |
|  `Status`  |      `RtmStatus`      | 无论操作是否成功，都返回一个 `RtmStatus` 类型的数据结构，其中包含本次操作的状态。 |
| `Response` | `RemoveStateResult` | 操作成功后，返回一个 `RemoveStateResult` 类型的数据结构。                       |

`RtmStatus` 包含以下属性：

|    属性     |  类型  | 描述               |
| ------- | -- | ------- |
|   `Error`   |  bool  | 本次操作是否出错。 |
| `ErrorCode` | string | 错误码             |
| `Operation` | string | 本次操作类型。     |
|  `Reason`   | string | 本次操作出错原因。 |

你可以通过检索 `errorCode` 字段的[错误码](/zh-CN/realtime-media/rtm/build/troubleshooting)了解错误原因，并找到对应的解决方法。
