( name: string )
| 1045 | * @returns The created channel info, or null on error |
| 1046 | */ |
| 1047 | export async function createChannel( |
| 1048 | name: string |
| 1049 | ): Promise<{ channel: SlackChannel; url: string } | null> { |
| 1050 | try { |
| 1051 | // Normalize name: lowercase, replace spaces with hyphens, remove invalid chars |
| 1052 | const normalizedName = name |
| 1053 | .toLowerCase() |
| 1054 | .replace(/\s+/g, '-') |
| 1055 | .replace(/[^a-z0-9-_]/g, '') |
| 1056 | .slice(0, 80); |
| 1057 | |
| 1058 | const response = await slackPostRequest<{ channel: SlackChannel }>('conversations.create', { |
| 1059 | name: normalizedName, |
| 1060 | is_private: false, |
| 1061 | }); |
| 1062 | |
| 1063 | // Get workspace info for URL |
| 1064 | const authInfo = await testSlackConnection(); |
| 1065 | const workspaceUrl = authInfo.team_id |
| 1066 | ? `https://app.slack.com/client/${authInfo.team_id}/${response.channel.id}` |
| 1067 | : `https://agenticads.slack.com/archives/${response.channel.id}`; |
| 1068 | |
| 1069 | logger.info( |
| 1070 | { channelId: response.channel.id, name: normalizedName }, |
| 1071 | 'Created Slack channel' |
| 1072 | ); |
| 1073 | |
| 1074 | return { |
| 1075 | channel: response.channel, |
| 1076 | url: workspaceUrl, |
| 1077 | }; |
| 1078 | } catch (error) { |
| 1079 | const errorMessage = error instanceof Error ? error.message : 'Unknown error'; |
| 1080 | |
| 1081 | // Handle "name_taken" error specifically |
| 1082 | if (errorMessage.includes('name_taken')) { |
| 1083 | logger.warn({ name }, 'Channel name already taken'); |
| 1084 | } else { |
| 1085 | logger.error({ error: errorMessage, name }, 'Failed to create Slack channel'); |
| 1086 | } |
| 1087 | |
| 1088 | return null; |
| 1089 | } |
| 1090 | } |
| 1091 | |
| 1092 | /** |
| 1093 | * Invite users to a channel |
no test coverage detected