@@ -3,6 +3,10 @@ import {
33 shouldDebounceTextInbound ,
44} from "openclaw/plugin-sdk/channel-inbound" ;
55import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime" ;
6+ import {
7+ asDateTimestampMs ,
8+ resolveExpiresAtMsFromDurationMs ,
9+ } from "openclaw/plugin-sdk/number-runtime" ;
610import type { ResolvedSlackAccount } from "../accounts.js" ;
711import type { SlackMessageEvent } from "../types.js" ;
812import { stripSlackMentionsForCommandDetection } from "./commands.js" ;
@@ -123,7 +127,7 @@ export function createSlackMessageHandler(params: {
123127 pruneAppMentionRetryKeys ( Date . now ( ) ) ;
124128 if ( last . opts . source === "app_mention" ) {
125129 // If app_mention wins the race and dispatches first, drop the later message dispatch.
126- appMentionDispatchedKeys . set ( seenMessageKey , Date . now ( ) + APP_MENTION_RETRY_TTL_MS ) ;
130+ rememberExpiringAppMentionKey ( appMentionDispatchedKeys , seenMessageKey ) ;
127131 } else if (
128132 last . opts . source === "message" &&
129133 appMentionDispatchedKeys . has ( seenMessageKey )
@@ -176,28 +180,46 @@ export function createSlackMessageHandler(params: {
176180 const appMentionRetryKeys = new Map < string , number > ( ) ;
177181 const appMentionDispatchedKeys = new Map < string , number > ( ) ;
178182
179- const pruneAppMentionRetryKeys = ( now : number ) => {
183+ const pruneAppMentionRetryKeys = ( rawNow : number ) : boolean => {
184+ const now = asDateTimestampMs ( rawNow ) ;
185+ if ( now === undefined ) {
186+ appMentionRetryKeys . clear ( ) ;
187+ appMentionDispatchedKeys . clear ( ) ;
188+ return false ;
189+ }
180190 for ( const [ key , expiresAt ] of appMentionRetryKeys ) {
181- if ( expiresAt <= now ) {
191+ if ( asDateTimestampMs ( expiresAt ) === undefined || expiresAt <= now ) {
182192 appMentionRetryKeys . delete ( key ) ;
183193 }
184194 }
185195 for ( const [ key , expiresAt ] of appMentionDispatchedKeys ) {
186- if ( expiresAt <= now ) {
196+ if ( asDateTimestampMs ( expiresAt ) === undefined || expiresAt <= now ) {
187197 appMentionDispatchedKeys . delete ( key ) ;
188198 }
189199 }
200+ return true ;
190201 } ;
191202
192- const rememberAppMentionRetryKey = ( key : string ) => {
203+ const rememberExpiringAppMentionKey = ( map : Map < string , number > , key : string ) : void => {
193204 const now = Date . now ( ) ;
194- pruneAppMentionRetryKeys ( now ) ;
195- appMentionRetryKeys . set ( key , now + APP_MENTION_RETRY_TTL_MS ) ;
205+ if ( ! pruneAppMentionRetryKeys ( now ) ) {
206+ return ;
207+ }
208+ const expiresAt = resolveExpiresAtMsFromDurationMs ( APP_MENTION_RETRY_TTL_MS , { nowMs : now } ) ;
209+ if ( expiresAt !== undefined ) {
210+ map . set ( key , expiresAt ) ;
211+ }
212+ } ;
213+
214+ const rememberAppMentionRetryKey = ( key : string ) => {
215+ rememberExpiringAppMentionKey ( appMentionRetryKeys , key ) ;
196216 } ;
197217
198218 const consumeAppMentionRetryKey = ( key : string ) => {
199219 const now = Date . now ( ) ;
200- pruneAppMentionRetryKeys ( now ) ;
220+ if ( ! pruneAppMentionRetryKeys ( now ) ) {
221+ return false ;
222+ }
201223 if ( ! appMentionRetryKeys . has ( key ) ) {
202224 return false ;
203225 }
0 commit comments