fix: restore mentionless paired room bot flow

This commit is contained in:
Eyejoker
2026-03-23 04:18:44 +09:00
parent be521eb903
commit f49d0f1732
2 changed files with 3 additions and 138 deletions

View File

@@ -190,127 +190,6 @@ describe('createMessageRuntime', () => {
});
});
it('ignores untriggered bot messages in paired rooms', async () => {
const chatJid = 'group@test';
const group = makeGroup('codex');
const channel = makeChannel(chatJid);
const saveState = vi.fn();
const lastAgentTimestamps: Record<string, string> = {};
vi.mocked(db.isPairedRoomJid).mockReturnValue(true);
vi.mocked(db.getMessagesSince).mockReturnValue([
{
id: 'msg-1',
chat_jid: chatJid,
sender: 'other-bot@test',
sender_name: 'Other Bot',
content: '요청을 완료하지 못했습니다. 다시 시도해 주세요.',
timestamp: '2026-03-18T09:00:00.000Z',
is_bot_message: true,
},
]);
const runtime = createMessageRuntime({
assistantName: 'Andy',
idleTimeout: 1_000,
pollInterval: 1_000,
timezone: 'UTC',
triggerPattern: /^@Andy\b/i,
channels: [channel],
queue: {
registerProcess: vi.fn(),
closeStdin: vi.fn(),
notifyIdle: vi.fn(),
} as any,
getRegisteredGroups: () => ({ [chatJid]: group }),
getSessions: () => ({}),
getLastTimestamp: () => '',
setLastTimestamp: vi.fn(),
getLastAgentTimestamps: () => lastAgentTimestamps,
saveState,
persistSession: vi.fn(),
clearSession: vi.fn(),
});
const result = await runtime.processGroupMessages(chatJid, {
runId: 'run-ignore-bot-loop',
reason: 'messages',
});
expect(result).toBe(true);
expect(agentRunner.runAgentProcess).not.toHaveBeenCalled();
expect(lastAgentTimestamps[chatJid]).toBe('0');
expect(saveState).toHaveBeenCalled();
});
it('processes triggered bot messages in paired rooms', async () => {
const chatJid = 'group@test';
const group = makeGroup('codex');
const channel = makeChannel(chatJid);
const saveState = vi.fn();
const lastAgentTimestamps: Record<string, string> = {};
vi.mocked(db.isPairedRoomJid).mockReturnValue(true);
vi.mocked(db.getMessagesSince).mockReturnValue([
{
id: 'msg-1',
chat_jid: chatJid,
sender: 'other-bot@test',
sender_name: 'Other Bot',
content: '@Andy 확인 부탁',
timestamp: '2026-03-18T09:00:00.000Z',
is_bot_message: true,
},
]);
vi.mocked(agentRunner.runAgentProcess).mockImplementation(
async (_group, _input, _onProcess, onOutput) => {
await onOutput?.({
status: 'success',
result: '확인했습니다.',
newSessionId: 'session-triggered-bot',
});
return {
status: 'success',
result: '확인했습니다.',
newSessionId: 'session-triggered-bot',
};
},
);
const runtime = createMessageRuntime({
assistantName: 'Andy',
idleTimeout: 1_000,
pollInterval: 1_000,
timezone: 'UTC',
triggerPattern: /^@Andy\b/i,
channels: [channel],
queue: {
registerProcess: vi.fn(),
closeStdin: vi.fn(),
notifyIdle: vi.fn(),
} as any,
getRegisteredGroups: () => ({ [chatJid]: group }),
getSessions: () => ({}),
getLastTimestamp: () => '',
setLastTimestamp: vi.fn(),
getLastAgentTimestamps: () => lastAgentTimestamps,
saveState,
persistSession: vi.fn(),
clearSession: vi.fn(),
});
const result = await runtime.processGroupMessages(chatJid, {
runId: 'run-triggered-bot-message',
reason: 'messages',
});
expect(result).toBe(true);
expect(agentRunner.runAgentProcess).toHaveBeenCalledTimes(1);
expect(channel.sendMessage).toHaveBeenCalledWith(chatJid, '확인했습니다.');
expect(lastAgentTimestamps[chatJid]).toBe('1');
expect(saveState).toHaveBeenCalled();
});
it('clears Claude sessions and closes stdin immediately on poisoned output', async () => {
const chatJid = 'group@test';
const group = makeGroup('claude-code');

View File

@@ -126,21 +126,6 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
channel?.isOwnMessage?.bind(channel),
);
const filterTriggeredPairedBotMessages = (
chatJid: string,
messages: Parameters<typeof filterProcessableMessages>[0],
) => {
if (!isPairedRoomJid(chatJid)) return messages;
// In paired rooms, require an explicit trigger before reacting to another
// bot's message. This keeps the room usable without reintroducing bot loops.
return messages.filter(
(message) =>
!message.is_bot_message ||
deps.triggerPattern.test(message.content.trim()),
);
};
const deliverOpenWorkItem = async (
channel: Channel,
item: WorkItem,
@@ -590,9 +575,10 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
sinceSeqCursor,
deps.assistantName,
);
const missedMessages = filterTriggeredPairedBotMessages(
const missedMessages = getProcessableMessages(
chatJid,
getProcessableMessages(chatJid, rawMissedMessages, channel),
rawMissedMessages,
channel,
);
if (missedMessages.length === 0) {