Implement MatrixClient API methods and add ECA event/action plugins
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3605358. -->
Reported by: [freelock](https://www.drupal.org/user/313537)
>>>
<h3 id="summary-problem-motivation">Problem/Motivation</h3>
<p> Several <code>MatrixClient</code> methods existed in the interface but were not implemented — <code>getState()</code>, <code>setState()</code>, and<br>
<code>messages()</code> all contained only a <code>// TODO</code> comment and returned nothing. <code>sendMessage()</code> returned the raw Guzzle response object<br>
rather than the event ID string declared in the interface.</p>
<p> Additionally, there were no ECA (Event-Condition-Action) plugins for the inbound event side: no ECA event plugin meant ECA models could not react to Matrix events at<br>
all. And while a basic <code>SendMessage</code> ECA action existed, there were no actions for thread replies, redacting events, or setting room state.</p>
<h4 id="summary-steps-reproduce">Steps to reproduce</h4>
<ul>
<li>Call <code>$matrixClient->getState($room, 'm.room.power_levels')</code> — returns <code>null</code> (method body is empty).</li>
<li>Call <code>$matrixClient->setState(...)</code> — does nothing.</li>
<li>Call <code>$matrixClient->sendMessage($room, 'hello')</code> — returns a Guzzle response object, not an event ID string.</li>
<li>Try to build an ECA model that reacts to a Matrix message — no Matrix event plugin appears in the ECA event list.</li>
</ul>
<h3 id="summary-proposed-resolution">Proposed resolution</h3>
<p> <strong>MatrixClient implementations</strong></p>
<ul>
<li><code>sendMessage()</code> — now returns <code>$response->event_id ?? ''</code> (string event ID).</li>
<li><code>sendThreadReply(string $roomId, string $threadRootEventId, ?string $inReplyToEventId, array $body): string</code> — sends <code>m.room.message</code> with<br>
a <code>m.thread</code> + <code>m.in_reply_to</code> relation structure per the Matrix spec.</li>
<li><code>redactEvent(string $roomId, string $eventId, string $reason = ''): string</code> — sends a redaction event; returns the redaction event ID.</li>
<li><code>getState($room, $eventType, $stateKey = ''): array|false</code> — fetches room state; returns <code>[]</code> on 404 (state not yet set),<br>
<code>false</code> on other errors.</li>
<li><code>setState($room, $eventType, $stateKey, array $state): string</code> — sets room state; handles 403 (insufficient power level) gracefully by logging a<br>
warning and returning <code>''</code>.</li>
<li><code>messages()</code> — returns <code>[]</code> (not yet implemented; stub made explicit with proper return type).</li>
</ul>
<p> <strong>ECA event plugin</strong></p>
<ul>
<li><code>MatrixEcaEvent</code> — fires on <code>MatrixEvents::EVENT_RECEIVED</code> and exposes ECA tokens: <code>[matrix_event:body]</code>,<br>
<code>[matrix_event:sender]</code>, <code>[matrix_event:room-id]</code>, <code>[matrix_event:event-id]</code>, <code>[matrix_event:thread-root]</code>.</li>
<li><code>MatrixEcaEventDeriver</code> — derives one ECA event variant per Matrix event type (message, reaction, etc.) so ECA models can target specific event<br>
types.</li>
</ul>
<p> <strong>ECA action plugins</strong></p>
<ul>
<li><code>matrix_api_send_message</code> — extended to fall back to <code>default_room</code> config when no room is set, and to support<br>
<code>com.freelock.data</code> structured payloads (JSON data field).</li>
<li><code>matrix_api_send_thread_reply</code> — send a message as a Matrix thread reply given a thread root event ID.</li>
<li><code>matrix_api_redact_event</code> — redact a Matrix event by event ID.</li>
<li><code>matrix_api_set_room_state</code> — set a Matrix room state event (event type + state key + JSON body).</li>
<li><code>matrix_api_trigger_poll</code> — trigger an immediate inbound sync poll (useful in ECA flows that need to flush pending events).</li>
</ul>
<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<ul>
<li>[x] All MatrixClient methods implemented.</li>
<li>[x] MatrixEcaEvent and deriver implemented.</li>
<li>[x] All ECA action plugins implemented.</li>
<li>[ ] <code>messages()</code> pagination is not yet implemented; it returns an empty array with a note in the docblock.</li>
</ul>
<h3 id="summary-ui-changes">User interface changes</h3>
<p> None. ECA actions and events appear in the ECA model builder UI automatically once the module is enabled.</p>
<h3 id="summary-api-changes">API changes</h3>
<p> <code>MatrixClientInterface</code> method signatures updated:</p>
<ul>
<li><code>sendMessage($room, $body, array $options = []): string</code> (was untyped return)</li>
<li><code>sendThreadReply(string $roomId, string $threadRootEventId, ?string $inReplyToEventId, array $body): string</code> (new)</li>
<li><code>redactEvent(string $roomId, string $eventId, string $reason = ''): string</code> (new)</li>
<li><code>getState($room, $eventType, $stateKey = ''): array|false</code> (now implemented)</li>
<li><code>setState($room, $eventType, $stateKey, array $state): string</code> (now implemented)</li>
</ul>
<p> ECA tokens available in <code>MatrixEcaEvent</code> handlers:<br>
<code>[matrix_event:body]</code>, <code>[matrix_event:sender]</code>, <code>[matrix_event:room-id]</code>, <code>[matrix_event:event-id]</code>,<br>
<code>[matrix_event:thread-root]</code></p>
<h3 id="summary-data-model-changes">Data model changes</h3>
<p> None.</p>
issue