feat: expand outreach targeting and messaging
This commit is contained in:
1 parent
7defff422d
commit
d0a8bd0e2f
40 files changed
+890
-82
No files matched your search
@@ -0,0 +1,75 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { RedditUserMessenger } from '../src/services/reddit-user-messenger.js';
|
||||
import type { RedditAccountRuntime, RedditAccountRuntimeManager } from '../src/services/reddit-account-runtime.js';
|
||||
|
||||
function runtime(accountId: string, redditUsername: string, conversations: Array<Record<string, unknown>>, sends = 0) {
|
||||
return {
|
||||
session: { accountId, redditUsername },
|
||||
resources: {
|
||||
accountAuth: { isFullyAuthenticated: () => true },
|
||||
chatService: {
|
||||
getCachedConversations: vi.fn().mockResolvedValue(conversations),
|
||||
getInvitationCooldown: () => null,
|
||||
sendChatMessage: vi.fn().mockResolvedValue(true),
|
||||
startConversationDetailed: vi.fn().mockResolvedValue({
|
||||
status: 'sent',
|
||||
chat: { username: 'new-user', roomId: 'new-room', messages: [] },
|
||||
}),
|
||||
refreshTopChats: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
rateLimiter: {
|
||||
getStats: () => ({ messagesThisHour: sends }),
|
||||
throttle: vi.fn((work: () => Promise<unknown>) => work()),
|
||||
},
|
||||
activityLogRepo: { insert: vi.fn() },
|
||||
conversationStateRepo: { get: vi.fn(), save: vi.fn() },
|
||||
},
|
||||
} as unknown as RedditAccountRuntime;
|
||||
}
|
||||
|
||||
function manager(items: RedditAccountRuntime[]) {
|
||||
return {
|
||||
list: () => items.map((item) => ({
|
||||
accountId: item.session.accountId,
|
||||
redditUsername: item.session.redditUsername,
|
||||
matrixUserId: item.session.accountId,
|
||||
state: 'running',
|
||||
updatedAt: 1,
|
||||
failure: null,
|
||||
})),
|
||||
get: (accountId: string) => items.find((item) => item.session.accountId === accountId) ?? null,
|
||||
} as unknown as RedditAccountRuntimeManager;
|
||||
}
|
||||
|
||||
describe('RedditUserMessenger', () => {
|
||||
it('prefers the account with the most recent existing interaction', async () => {
|
||||
const first = runtime('one', 'owner-one', [{
|
||||
username: 'Alice', roomId: 'old-room', url: '/room/old-room', lastMessageTime: 10,
|
||||
messages: [],
|
||||
}]);
|
||||
const second = runtime('two', 'owner-two', [{
|
||||
username: 'u/alice', roomId: 'recent-room', url: '/room/recent-room', lastMessageTime: 20,
|
||||
messages: [],
|
||||
}]);
|
||||
|
||||
const result = await new RedditUserMessenger(manager([first, second])).send('u/Alice', 'hello');
|
||||
|
||||
expect(result).toMatchObject({ accountId: 'two', conversationId: 'recent-room', reusedConversation: true });
|
||||
expect(second.resources.chatService.sendChatMessage).toHaveBeenCalledWith('/room/recent-room', 'hello');
|
||||
expect(first.resources.chatService.sendChatMessage).not.toHaveBeenCalled();
|
||||
expect(second.resources.conversationStateRepo.save).toHaveBeenCalledWith('recent-room', expect.objectContaining({
|
||||
automation: expect.objectContaining({ status: 'user-overridden' }),
|
||||
}));
|
||||
});
|
||||
|
||||
it('starts a new conversation on the running account with the lowest send count', async () => {
|
||||
const busy = runtime('busy', 'z-owner', [], 4);
|
||||
const available = runtime('available', 'a-owner', [], 1);
|
||||
|
||||
const result = await new RedditUserMessenger(manager([busy, available])).send('new-user', 'hello');
|
||||
|
||||
expect(result).toMatchObject({ accountId: 'available', conversationId: 'new-room', reusedConversation: false });
|
||||
expect(available.resources.chatService.startConversationDetailed).toHaveBeenCalledWith('new-user', 'hello');
|
||||
expect(busy.resources.chatService.startConversationDetailed).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user