> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-Inbox トランザクションアクション

検証/消費/入手の各トランザクションアクションの仕様




## アクションの組み合わせと同時実行

すべてのサービスに共通する前提は [トランザクションアクションの組み合わせ]() にまとめています。先にそちらを読んでください。この節の残りは GS2-Inbox 固有の内容です。

GS2-Inbox のトランザクションアクションは、ネームスペース・ユーザー・メッセージの組で決まる 1 通のメッセージを対象にします。送信は常に新しいメッセージを作るので、送信ごとに別の対象になります。

操作 | 同じ行を重ねたとき | 入れ子越し | 別の対象になる境界 | 直列実行モード有効時
--- | --- | --- | --- | ---
メッセージの送信<br>`SendMessageByUserId` | それぞれが別のメッセージになる。衝突しない | 衝突しない | 送信のたびに別の対象 | 変わらない。もともと衝突しない
メッセージの開封<br>`OpenMessageByUserId` | 1 件に統合される | 失敗する | ネームスペース・ユーザー・メッセージ | 引き続き失敗する。1 件にまとめられなくなるため、2 件目は 1 件目がすでに開封したメッセージに対して実行され、開封済み (400) として弾かれる
メッセージの削除<br>`DeleteMessageByUserId` | 1 件に統合される | 失敗する | ネームスペース・ユーザー・メッセージ | 引き続き失敗する。2 件目はすでに無くなったメッセージに対して実行され、見つからない (404) として弾かれる

開封と削除は行が違い、境界が同じです。1 通のメッセージを同じトランザクションで開封して削除することはできません。メッセージが違えば別の対象なので、複数のメッセージを開封したり削除したりするのは問題ありません。**直列実行モード（`enableSequentialExecution` または `TransactionSettingV2`）を有効にしても、これはできません。どちらも消費アクションで、フェーズの中での実行順はアクション名で決まるため、`DeleteMessageByUserId` が `OpenMessageByUserId` より必ず先に実行されます。開封を先に書いても順序は変わりません。開封が実行される時点でメッセージはすでに存在しないため、これまでのマージの衝突ではなく「見つからない」エラーで失敗します。**

トランザクションで送信したメッセージは、そのトランザクションの他のアクションからはまだ存在していないものとして扱われます。すべてのアクションがトランザクション開始時点の状態を基準に動くためです。メッセージを送ってから操作したい場合は、トランザクションを分けてください。

メッセージの開封は、添付されたものを配ります。それらは自身のトランザクションとして発行されるため、配られるものが属するサービスの制限がそのまま当てはまり、下記も当てはまります。同じアイテムを添付した 2 通を開封する場合が注意すべきケースです。

### 入れ子になったトランザクションに注意

内側から開封されたメッセージと、外側から削除された同じメッセージが衝突して、トランザクションが失敗します。開封が配るものは他のアクションと同じトランザクションに集まるので、それらが属するサービスのページを確認してください。**直列実行モードは、この状況を解消するのではなく、起こることを変えます。削除は消費アクションなので、内側のトランザクションを起動する入手アクションより先に、外側の削除が実行されます。そのため内側の開封は、すでに削除済みのメッセージに対して行われることになり、マージの衝突ではなく「見つからない」エラーで失敗します。**

### 制限を回避したい場合

メッセージの送信は入手アクション、開封と削除はどちらも消費アクションです。そのため `acquireActionUseJobQueue` では開封と削除を分離できず、解消するには `enableAtomicCommit` を無効にする必要があります。

### 同時実行とリトライ

送信は、リクエストが何本重なってもコンフリクトしません。送信するたびに自分のメッセージを作るためです。

開封と削除はメッセージ全体に対して書き込むため、同じメッセージへの同時更新があると後から確定した側がコンフリクト (409) になります。リクエストの内容に問題があるわけではないので、リトライすれば成功します。すでに開封済み・削除済みの場合は、リトライするとその旨のエラーが返ります。

---



## Consume Action

消費アクション

### Gs2Inbox:OpenMessageByUserId

ユーザーIDを指定してメッセージを開封済み化<br>

指定されたユーザーの受信箱にある指定されたメッセージを既読（開封済み）としてマークします。<br>
これは入手アクションを実行せずに isRead を true に設定する単純な状態遷移です。<br>
既読にすると同時に関連する報酬を実行するには、代わりに Read API を使用してください。

**数量指定可能なアクション：いいえ**

**反転可能なアクション：いいえ**

|  | 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128文字 | ネームスペース名<br>ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 |
| userId | string |  | ✓|  |  ~ 128文字 | ユーザーID<br>`#{userId}` と設定することでログイン中のユーザーIDに置換されます。 |
| messageName | string |  | ✓| UUID |  ~ 36文字 | メッセージ名<br>メッセージの一意な名前を保持します。<br>名前は UUID（Universally Unique Identifier）フォーマットで自動的に生成され、各メッセージを識別するために使用されます。 |
| timeOffsetToken | string |  | |  |  ~ 1024文字 | タイムオフセットトークン |




**JSON**
```json
{
    "action": "Gs2Inbox:OpenMessageByUserId",
    "request": {
        "namespaceName": "[string]ネームスペース名",
        "userId": "[string]ユーザーID",
        "messageName": "[string]メッセージ名",
        "timeOffsetToken": "[string]タイムオフセットトークン"
    }
}
```

**YAML**
```yaml

action: Gs2Inbox:OpenMessageByUserId
request:
  namespaceName: "[string]ネームスペース名"
  userId: "[string]ユーザーID"
  messageName: "[string]メッセージ名"
  timeOffsetToken: "[string]タイムオフセットトークン"
```

**GS2-Script**
```lua

transaction.service("inbox").consume.open_message_by_user_id({
    namespaceName="[string]ネームスペース名",
    userId="[string]ユーザーID",
    messageName="[string]メッセージ名",
    timeOffsetToken="[string]タイムオフセットトークン",
})
```


---

### Gs2Inbox:DeleteMessageByUserId

ユーザーIDを指定してメッセージを削除<br>

指定されたユーザーの受信箱からメッセージを完全に削除します。<br>
既読状態に関係なくメッセージレコードが削除されます。

**数量指定可能なアクション：いいえ**

**反転可能なアクション：いいえ**

|  | 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128文字 | ネームスペース名<br>ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 |
| userId | string |  | ✓|  |  ~ 128文字 | ユーザーID<br>`#{userId}` と設定することでログイン中のユーザーIDに置換されます。 |
| messageName | string |  | ✓| UUID |  ~ 36文字 | メッセージ名<br>メッセージの一意な名前を保持します。<br>名前は UUID（Universally Unique Identifier）フォーマットで自動的に生成され、各メッセージを識別するために使用されます。 |
| timeOffsetToken | string |  | |  |  ~ 1024文字 | タイムオフセットトークン |




**JSON**
```json
{
    "action": "Gs2Inbox:DeleteMessageByUserId",
    "request": {
        "namespaceName": "[string]ネームスペース名",
        "userId": "[string]ユーザーID",
        "messageName": "[string]メッセージ名",
        "timeOffsetToken": "[string]タイムオフセットトークン"
    }
}
```

**YAML**
```yaml

action: Gs2Inbox:DeleteMessageByUserId
request:
  namespaceName: "[string]ネームスペース名"
  userId: "[string]ユーザーID"
  messageName: "[string]メッセージ名"
  timeOffsetToken: "[string]タイムオフセットトークン"
```

**GS2-Script**
```lua

transaction.service("inbox").consume.delete_message_by_user_id({
    namespaceName="[string]ネームスペース名",
    userId="[string]ユーザーID",
    messageName="[string]メッセージ名",
    timeOffsetToken="[string]タイムオフセットトークン",
})
```


---

## Acquire Action

入手アクション

### Gs2Inbox:SendMessageByUserId

ユーザーIDを指定してメッセージの送信<br>

指定されたユーザーの受信箱に新しいメッセージを作成して配信します。<br>
メッセージにはメタデータ（任意の JSON コンテンツ）と readAcquireActions（メッセージの開封時に付与される報酬）を含めることができます。<br>
メッセージの有効期限は、絶対タイムスタンプ（expiresAt）または配信時点からの相対的な期間（expiresTimeSpan）で設定できます。expiresAt が指定された場合、expiresTimeSpan より優先されます。<br>
メッセージは未読状態（isRead=false）で開始されます。

**数量指定可能なアクション：いいえ**

**反転可能なアクション：いいえ**

|  | 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128文字 | ネームスペース名<br>ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 |
| userId | string |  | ✓|  |  ~ 128文字 | ユーザーID<br>`#{userId}` と設定することでログイン中のユーザーIDに置換されます。 |
| metadata | string |  | ✓|  |  ~ 4096文字 | メタデータ<br>メッセージのタイトル、本文、送信者情報、表示パラメータなどを含むJSON文字列など、メッセージの内容を表す任意のデータです。GS2はこの値を解釈せず、メッセージUIの描画のためにゲームクライアントにそのまま渡されます。最大4096文字です。 |
| readAcquireActions | [List&lt;AcquireAction&gt;](../sdk/#acquireaction) |  | | [] | 0 ~ 100 items | 開封時入手アクション<br>ユーザーがこのメッセージを開封した際に実行される入手アクションのリストです。アイテム、通貨、リソースなどの報酬をメッセージに添付するために使用されます。複数のアクションを組み合わせて異なる種類の報酬を同時に付与できます。メッセージあたり最大100アクションです。 |
| expiresAt | long |  | |  |  | 有効期限日時<br>UNIX 時間・ミリ秒 |
| expiresTimeSpan | [TimeSpan](../sdk/#timespan) |  | |  |  | メッセージを受信した時刻（基準時刻）からメッセージが削除されるまでの期間 |
| timeOffsetToken | string |  | |  |  ~ 1024文字 | タイムオフセットトークン |




**JSON**
```json
{
    "action": "Gs2Inbox:SendMessageByUserId",
    "request": {
        "namespaceName": "[string]ネームスペース名",
        "userId": "[string]ユーザーID",
        "metadata": "[string]メタデータ",
        "readAcquireActions": [
            {
                "action": "[string]入手アクションで実行するアクションの種類",
                "request": "[string]アクション実行時に使用されるリクエストのJSON文字列"
            }
        ],
        "expiresAt": "[long]有効期限日時",
        "expiresTimeSpan": {
            "days": "[int]日数",
            "hours": "[int]時間",
            "minutes": "[int]分"
        },
        "timeOffsetToken": "[string]タイムオフセットトークン"
    }
}
```

**YAML**
```yaml

action: Gs2Inbox:SendMessageByUserId
request:
  namespaceName: "[string]ネームスペース名"
  userId: "[string]ユーザーID"
  metadata: "[string]メタデータ"
  readAcquireActions: 
    - action: "[string]入手アクションで実行するアクションの種類"
      request: "[string]アクション実行時に使用されるリクエストのJSON文字列"
  expiresAt: "[long]有効期限日時"
  expiresTimeSpan: 
    days: "[int]日数"
    hours: "[int]時間"
    minutes: "[int]分"
  timeOffsetToken: "[string]タイムオフセットトークン"
```

**GS2-Script**
```lua

transaction.service("inbox").acquire.send_message_by_user_id({
    namespaceName="[string]ネームスペース名",
    userId="[string]ユーザーID",
    metadata="[string]メタデータ",
    readAcquireActions={
        {
            action="[string]入手アクションで実行するアクションの種類",
            request="[string]アクション実行時に使用されるリクエストのJSON文字列"
        }
    },
    expiresAt="[long]有効期限日時",
    expiresTimeSpan={
        days="[int]日数",
        hours="[int]時間",
        minutes="[int]分"
    },
    timeOffsetToken="[string]タイムオフセットトークン",
})
```


---



