# Messaging

After [establishing a DIDComm connection](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/establishing-connections) with a peer, one of many supported interactions is the ability to exchange text messages back and forth along the E2EE channel. The Edge Agent supports Aries Basic Message Protocol ([RFC 0095](https://github.com/hyperledger/aries-rfcs/blob/main/features/0095-basic-message/README.md)) to accomplish this.&#x20;

The `MessagingModule` contains the methods to receive, send and manage these messages. It is accessed via the `agent`'s fields: `agent.connections.messaging`.

## Subscribe to Inbound Messages

As described in the [#subscribe-to-events](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/agent-management#subscribe-to-events "mention") section, subscription to new incoming Basic Messages is supported via the `subscribeToAgentEvents` API. The subscriber will be invoked once for each new message, and will include the inbound message data, such as the `content`, the `receivedTime` and the ID of the `Connection` who sent it.

{% tabs %}
{% tab title="Swift" %}

```swift
class MessageSubscriber : AgentEventSubscriber {
    func inboundBasicMessage(basicMessage: BasicMessage.Inbound) {
        // the following will be invoked whenever a new basic message is received.
        print("new message: \(basicMessage)")
    }
    
    func connectionExchangeStateChanged(connectionExchange: ConnectionExchange) {}
    func credentialExchangeStateChanged(credentialExchange: CredentialExchange) {}
    func proofExchangeStateChanged(proofExchange: ProofExchange) {}
    func messageProcessed(messageId: String) {}
}

let customSubscriber = MessageSubscriber()
let subscriberId = agent.subscribeToAgentEvents(subscriber: customSubscriber)
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val subscriber = object : AgentEventSubscriber {
    override fun inboundBasicMessage(basicMessage: BasicMessage.Inbound) {
        // the following will be invoked whenever a new basic message is received.
        println("new message: $basicMessage")
    }
}

val subscriberId = agent.subscribeToAgentEvents(subscriber)
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
See [#subscribe-to-events](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/agent-management#subscribe-to-events "mention") for more subscription management details.
{% endhint %}

## Send Messages

To send a basic message to a connection, the `sendBasicMessage` API can simply be used. On success, the outbound basic message that was sent to the peer will be returned.

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let connectionId: String // the identifier of the [Connection] to message

do {
    let sentMessage = try await agent.connections.messaging.sendBasicMessage(
        connectionId: connectionId,
        content: "hello world"
    )
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val connectionId: String // the identifier of the [Connection] to message

try {
    val sentMessage = agent.connections.messaging.sendBasicMessage(
        connectionId = connectionId,
        content = "hello world"
    )
} catch (e: MessagingModule.SendBasicMessageException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

## Query Messages

The SDK features one API for listing basic messages in a variety of ways: `listBasicMessages`.  The API is designed to support common peer to peer messaging use cases.&#x20;

The method takes a `ListBasicMessagesOptions` object as input as a way to configure pagination, sorting and filtering. It has the following fields within it:

* `filters` - A `ListBasicMessagesFilter` object with configurations for how results should be filtered
  * `filters.connectionId` - if provided, the resulting list of messages will only contain messages from the given connection
  * `filters.limitMessagesPerConnection` - if provided, the resulting list of messages will limit the number of messages from a single connection. Useful for "conversation list views": "list recent messages, limited to 1 per connection".
* `paging` - A common `Paging` object, configuring the limit of  items to fetch, and the current cursor position (if any)
* `sorting` - A `ListBasicMessagesSorting` object specifying how the results should be sorted (pre filtering and paging). Currently chronological sorting (ascending and descending) is supported

The value returned is a `PageResult<BasicMessage>`, which contains a list of `items` and a string cursor, `nextToken`, if there is more items to fetch. This `nextToken` can be passed into the `paging` item when performing the next `listBasicMessages` operation to fetch the next page of items from where the cursor was up to.

### List All Recent Messages

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let pageSize: UInt = 10

do {
    let page1 = try await agent.connections.messaging.listBasicMessages(
        options: .init(
            paging: .init(limit: pageSize)
        )
    )
    let pageItems1 = page1.items
    
    // fetch next page if nextToken is not nil
    let page2 = try await agent.connections.messaging.listBasicMessages(
        options: .init(
            paging: .init(limit: pageSize, nextToken: page1.nextToken)
        )
    )
    let pageItems2 = page2.items
    
    // continue..
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val pageSize = 10u

try {
    val page1 = agent.connections.messaging.listBasicMessages(
        options = ListBasicMessagesOptions(
            paging = Paging(pageSize)
        )
    )
    val pageItems1 = page1.items

    // fetch next page if nextToken is not null
    val page2 = agent.connections.messaging.listBasicMessages(
        options = ListBasicMessagesOptions(
            paging = Paging(pageSize, page1.nextToken)
        )
    )
    val pageItems2 = page2.items
    
    // continue..
} catch (e: MessagingModule.ListBasicMessagesException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

### List Oldest Messages

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let pageSize: UInt = 10

do {
    let page = try await agent.connections.messaging.listBasicMessages(
        options: .init(
            paging: .init(limit: pageSize),
            sorting: .chronological(direction: .ascending)
        )
    )
    let pageItems = page.items
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val pageSize = 10u

try {
    val page = agent.connections.messaging.listBasicMessages(
        options = ListBasicMessagesOptions(
            paging = Paging(pageSize),
            sorting = ListBasicMessagesSorting.Chronological(SortDirection.ASCENDING)
        )
    )
    val pageItems = page.items
} catch (e: MessagingModule.ListBasicMessagesException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

### List Recent Messages with a Connection

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let connectionId: String
let pageSize: UInt = 10

do {
    let page = try await agent.connections.messaging.listBasicMessages(
        options: .init(
            filters: .init(connectionId: connectionId),
            paging: .init(limit: pageSize)
        )
    )
    let pageItems = page.items
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val connectionId: String
val pageSize = 10u

try {
    val page = agent.connections.messaging.listBasicMessages(
        options = ListBasicMessagesOptions(
            filters = ListBasicMessagesFilter(connectionId = connectionId),
            paging = Paging(pageSize),
        )
    )
    val pageItems = page.items
} catch (e: MessagingModule.ListBasicMessagesException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

### List Most Recent Connection Conversations

To support use cases like a "recent conversations" list view, the list API may be used like so:

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let pageSize: UInt = 10

do {
    let page = try await agent.connections.messaging.listBasicMessages(
        options: .init(
            filters: .init(limitMessagesPerConnection: 1),
            paging: .init(limit: pageSize)
        )
    )
    let pageItems = page.items
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val pageSize = 10u

try {
    val page = agent.connections.messaging.listBasicMessages(
        options = ListBasicMessagesOptions(
            filters = ListBasicMessagesFilter(limitMessagesPerConnection = 1u),
            paging = Paging(pageSize),
        )
    )
    val pageItems = page.items
} catch (e: MessagingModule.ListBasicMessagesException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

## Delete Messages

### Delete All Messages with a Connection

All basic messages to/from a connection can be deleted from storage with the `deleteBasicMessagesForConnection` API.

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let connectionId: String

do {
    try await agent.connections.messaging.deleteBasicMessagesForConnection(
        connectionId: connectionId
    )
} catch {
// handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val connectionId: String

try {
    agent.connections.messaging.deleteBasicMessagesForConnection(
        connectionId = connectionId
    )
} catch (e: MessagingModule.DeleteBasicMessagesException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

### Delete Basic Messages by IDs

Alternatively, a known set of message IDs (`BasicMessage.id`) can be passed into the `deleteBasicMessages` API to delete them.

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let messageIds: [String]

do {
    try await agent.connections.messaging.deleteBasicMessages(
        ids: messageIds
    )
} catch {
// handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val messageIds: List<String>

try {
    agent.connections.messaging.deleteBasicMessages(
        ids = messageIds
    )
} catch (e: MessagingModule.DeleteBasicMessagesException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

## Opt Out of Message Storage

Some Edge Agent SDK consumers may wish to handle Basic Message storage themselves, for instance, to add their own indexes and data schema. Or even to just avoid message storage all together. If that is the case, the SDKs managed storage of Basic Messages can be opt out of with the `AgentConfiguration`'s `messagingConfiguration.storeBasicMessages` field. By default this is `true`, setting the configuration field to `false` will disable storage of Basic Messages.&#x20;

See [agent-management](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management "mention") for details on managing the agent's configuration.

{% hint style="info" %}
With storage disabled, Basic Messages will still be processed and will still invoke the subscription (see [#subscribe-to-inbound-messages](#subscribe-to-inbound-messages "mention")), but nothing will be put into the agent's database. Meaning the above APIs (list, delete) will have limited functionality.
{% endhint %}
