@@ -13,6 +13,7 @@ import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coer
1313import { checkTwitchAccessControl } from "./access-control.js" ;
1414import { getOrCreateClientManager } from "./client-manager-registry.js" ;
1515import { getTwitchRuntime } from "./runtime.js" ;
16+ import { createTwitchIngress } from "./twitch-ingress.js" ;
1617import type { TwitchAccountConfig , TwitchChatMessage } from "./types.js" ;
1718import { stripMarkdownForTwitch } from "./utils/markdown.js" ;
1819
@@ -31,10 +32,11 @@ type TwitchMonitorOptions = {
3132} ;
3233
3334type TwitchMonitorResult = {
34- stop : ( ) => void ;
35+ stop : ( ) => Promise < void > ;
3536} ;
3637
3738type TwitchCoreRuntime = ReturnType < typeof getTwitchRuntime > ;
39+ type TwitchIngressLifecycle = Parameters < Parameters < typeof createTwitchIngress > [ 0 ] [ "deliver" ] > [ 1 ] ;
3840
3941/**
4042 * Process an incoming Twitch message and dispatch to agent.
@@ -46,19 +48,22 @@ async function processTwitchMessage(params: {
4648 config : unknown ;
4749 runtime : TwitchRuntimeEnv ;
4850 core : TwitchCoreRuntime ;
51+ turnAdoptionLifecycle : TwitchIngressLifecycle ;
4952 statusSink ?: ( patch : { lastInboundAt ?: number ; lastOutboundAt ?: number } ) => void ;
5053} ) : Promise < void > {
51- const { message, account, accountId, config, runtime, core, statusSink } = params ;
54+ const { message, account, accountId, config, runtime, core, turnAdoptionLifecycle, statusSink } =
55+ params ;
5256 const cfg = config as OpenClawConfig ;
5357
5458 await core . channel . inbound . run ( {
5559 channel : "twitch" ,
5660 accountId,
5761 raw : message ,
62+ turnAdoptionLifecycle,
5863 adapter : {
5964 ingest : ( incoming ) => ( {
60- id : incoming . id ?? ` ${ incoming . channel } : ${ incoming . timestamp ?. getTime ( ) ?? Date . now ( ) } ` ,
61- timestamp : incoming . timestamp ?. getTime ( ) ,
65+ id : incoming . id ,
66+ timestamp : incoming . timestamp ,
6267 rawText : incoming . message ,
6368 textForAgent : incoming . message ,
6469 textForCommands : incoming . message ,
@@ -220,6 +225,7 @@ export async function monitorTwitchProvider(
220225
221226 const core = getTwitchRuntime ( ) ;
222227 let stopped = false ;
228+ let stopTask : Promise < void > | undefined ;
223229
224230 const coreLogger = core . logging . getChildLogger ( { module : "twitch" } ) ;
225231 const logVerboseMessage = ( message : string ) => {
@@ -249,12 +255,10 @@ export async function monitorTwitchProvider(
249255 throw error ;
250256 }
251257
252- const unregisterHandler = clientManager . onMessage ( account , ( message ) => {
253- if ( stopped ) {
254- return ;
255- }
256-
257- void ( async ( ) => {
258+ const ingress = createTwitchIngress ( {
259+ accountId,
260+ runtime,
261+ deliver : async ( message , turnAdoptionLifecycle ) => {
258262 const botUsername = normalizeLowercaseStringOrEmpty ( account . username ) ;
259263 if ( normalizeLowercaseStringOrEmpty ( message . username ) === botUsername ) {
260264 return ;
@@ -266,7 +270,7 @@ export async function monitorTwitchProvider(
266270 botUsername,
267271 } ) ;
268272
269- if ( stopped || ! access . allowed ) {
273+ if ( ! access . allowed ) {
270274 return ;
271275 }
272276
@@ -279,19 +283,41 @@ export async function monitorTwitchProvider(
279283 config,
280284 runtime,
281285 core,
286+ turnAdoptionLifecycle,
282287 statusSink,
283288 } ) ;
284- } ) ( ) . catch ( ( err : unknown ) => {
285- runtime . error ?.( `Message processing failed: ${ String ( err ) } ` ) ;
289+ } ,
290+ } ) ;
291+ ingress . start ( ) ;
292+
293+ const unregisterHandler = clientManager . onMessage ( account , ( message ) => {
294+ if ( stopped ) {
295+ return ;
296+ }
297+
298+ void ingress . accept ( message ) . catch ( ( err : unknown ) => {
299+ runtime . error ?.( `Message durable admission failed: ${ String ( err ) } ` ) ;
286300 } ) ;
287301 } ) ;
288302
289- const stop = ( ) => {
290- stopped = true ;
291- unregisterHandler ( ) ;
303+ const stop = ( ) : Promise < void > => {
304+ stopTask ??= ( async ( ) => {
305+ stopped = true ;
306+ unregisterHandler ( ) ;
307+ await ingress . stop ( ) ;
308+ } ) ( ) ;
309+ return stopTask ;
292310 } ;
293311
294- abortSignal . addEventListener ( "abort" , stop , { once : true } ) ;
312+ abortSignal . addEventListener (
313+ "abort" ,
314+ ( ) => {
315+ void stop ( ) . catch ( ( error : unknown ) => {
316+ runtime . error ?.( `Twitch ingress stop failed: ${ String ( error ) } ` ) ;
317+ } ) ;
318+ } ,
319+ { once : true } ,
320+ ) ;
295321
296322 return { stop } ;
297323}
0 commit comments