Skip to content
Merged
57 changes: 56 additions & 1 deletion src/handlers/__tests__/message.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { createMessageHandler } from '../message.ts';
describe('createMessageHandler', () => {
const timesAllChannelId = 'C_TIMES_ALL';
const workspaceUrl = 'https://workspace.slack.com';
const handler = createMessageHandler(timesAllChannelId, workspaceUrl);

function makeArgs(
message: Record<string, unknown>,
Expand All @@ -29,6 +28,7 @@ describe('createMessageHandler', () => {
}

it('timesチャンネルのメッセージをtimes-allに転送する', async () => {
const handler = createMessageHandler(timesAllChannelId, workspaceUrl);
const message = {
type: 'message',
subtype: undefined,
Expand All @@ -53,6 +53,7 @@ describe('createMessageHandler', () => {
});

it('times-以外のチャンネルは転送しない', async () => {
const handler = createMessageHandler(timesAllChannelId, workspaceUrl);
const message = {
type: 'message',
subtype: undefined,
Expand All @@ -70,6 +71,7 @@ describe('createMessageHandler', () => {
});

it('subtypeを持つメッセージは転送しない', async () => {
const handler = createMessageHandler(timesAllChannelId, workspaceUrl);
const message = {
type: 'message',
subtype: 'channel_join',
Expand All @@ -86,6 +88,7 @@ describe('createMessageHandler', () => {
});

it('DMメッセージは転送しない', async () => {
const handler = createMessageHandler(timesAllChannelId, workspaceUrl);
const message = {
type: 'message',
subtype: undefined,
Expand All @@ -102,4 +105,56 @@ describe('createMessageHandler', () => {
assert.equal(args.client.conversations.info.mock.callCount(), 0);
assert.equal(args.client.chat.postMessage.mock.callCount(), 0);
});

it('同一チャンネルの2回目以降はconversations.infoを呼ばない', async () => {
const handler = createMessageHandler(timesAllChannelId, workspaceUrl);
const message = {
Comment thread
eai04191 marked this conversation as resolved.
type: 'message',
subtype: undefined,
channel: 'C_CACHED',
channel_type: 'channel',
text: 'first',
ts: '1234567890.000001',
user: 'U12345',
};
// biome-ignore lint/suspicious/noExplicitAny: テスト用のモック
const args1 = makeArgs(message) as any;
await handler(args1);

const message2 = { ...message, text: 'second', ts: '1234567890.000002' };
// biome-ignore lint/suspicious/noExplicitAny: テスト用のモック
const args2 = makeArgs(message2) as any;
await handler(args2);

assert.equal(args1.client.conversations.info.mock.callCount(), 1);
assert.equal(args2.client.conversations.info.mock.callCount(), 0);
});

it('conversations.infoが例外をスローした場合は転送しない', async () => {
const handler = createMessageHandler(timesAllChannelId, workspaceUrl);
const message = {
type: 'message',
subtype: undefined,
channel: 'C_ERROR',
channel_type: 'channel',
text: 'hello',
ts: '1234567890.123456',
user: 'U12345',
};
const args = {
message,
client: {
conversations: {
info: mock.fn(() => Promise.reject(new Error('platform_error'))),
},
chat: {
postMessage: mock.fn(() => Promise.resolve()),
},
},
};
// biome-ignore lint/suspicious/noExplicitAny: テスト用のモック
await handler(args as any);

assert.equal(args.client.chat.postMessage.mock.callCount(), 0);
});
});
64 changes: 57 additions & 7 deletions src/handlers/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,75 @@ import { isForwardableMessage } from '../lib/messageFilter.ts';
import { buildMessageLink } from '../lib/messageLink.ts';
import { isTimesChannel } from '../lib/timesChannel.ts';

type SlackClient = (AllMiddlewareArgs &
SlackEventMiddlewareArgs<'message'>)['client'];

async function getChannelName(
client: SlackClient,
channelId: string,
cache: Map<string, string>,
): Promise<string | undefined> {
const cached = cache.get(channelId);
if (cached !== undefined) {
return cached;
}
try {
const channelInfo = await client.conversations.info({
channel: channelId,
});
const channelName = channelInfo.channel?.name;
if (!channelName) {
console.error('channel name not found', {
channel: channelId,
ok: channelInfo.ok,
error: channelInfo.error,
});
return undefined;
}
cache.set(channelId, channelName);
return channelName;
} catch (error) {
console.error('conversations.info failed', {
channel: channelId,
error:
error instanceof Error
? { name: error.name, message: error.message, stack: error.stack }
: error,
});
return undefined;
}
}

export function createMessageHandler(
timesAllChannelId: string,
workspaceUrl: string,
) {
const channelNameCache = new Map<string, string>();

return async ({
message,
client,
}: AllMiddlewareArgs & SlackEventMiddlewareArgs<'message'>) => {
console.log('received message');
console.log('received message', {
channel: message.channel,
subtype: message.subtype,
ts: message.ts,
});
if (!isForwardableMessage(message, timesAllChannelId)) {
Comment thread
eai04191 marked this conversation as resolved.
console.log('skipped: not forwardable', { subtype: message.subtype });
return;
}

const channelInfo = await client.conversations.info({
channel: message.channel,
});
const channelName = channelInfo.channel?.name;
if (!channelName || !isTimesChannel(channelName)) {
console.log(channelName);
const channelName = await getChannelName(
client,
message.channel,
channelNameCache,
);
if (!channelName) {
return;
}
if (!isTimesChannel(channelName)) {
console.log('skipped: not a times channel', { channelName });
return;
}

Expand Down