(
messageText: string,
threadMessages: Array<{ user?: string; ts: string }>,
currentMessageTs: string,
currentUserId: string,
botUserId: string
)
| 91 | * indicating it is addressed to them, not to Addie. |
| 92 | */ |
| 93 | export function isDirectedAtAddie( |
| 94 | messageText: string, |
| 95 | threadMessages: Array<{ user?: string; ts: string }>, |
| 96 | currentMessageTs: string, |
| 97 | currentUserId: string, |
| 98 | botUserId: string |
| 99 | ): boolean { |
| 100 | if (/\baddie\b/i.test(messageText)) { |
| 101 | return true; |
| 102 | } |
| 103 | |
| 104 | // If the message starts with a Slack @mention of another user, it's addressed to them. |
| 105 | const startsWithMention = /^<@(U[A-Z0-9]+)>/.exec(messageText.trim()); |
| 106 | if (startsWithMention && startsWithMention[1] !== botUserId) { |
| 107 | return false; |
| 108 | } |
| 109 | |
| 110 | // Walk backwards: skip the sender's consecutive messages, then skip Addie's |
| 111 | // consecutive responses, and check who's underneath. If it's the sender |
| 112 | // again, Addie was responding to them (back-and-forth). If it's another |
| 113 | // human, the sender is talking to that person. |
| 114 | const prior = threadMessages.filter(msg => msg.ts < currentMessageTs && msg.user); |
| 115 | let i = prior.length - 1; |
| 116 | |
| 117 | while (i >= 0 && prior[i].user === currentUserId) { |
| 118 | i--; |
| 119 | } |
| 120 | while (i >= 0 && prior[i].user === botUserId) { |
| 121 | i--; |
| 122 | } |
| 123 | |
| 124 | if (i < 0) { |
| 125 | return prior.some(msg => msg.user === botUserId); |
| 126 | } |
| 127 | |
| 128 | return prior[i].user === currentUserId; |
| 129 | } |
| 130 | |
| 131 | /** |
| 132 | * Build compact thread summary lines for the router. |
no test coverage detected