Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/microsoft

src/teams.ts

205 lines · 6.2 KB · TypeScript
import { resolveMicrosoftIntegration, type MicrosoftAccountParams } from './accounts.ts'
import { graphItems, graphRequest, requireConfirmOrDryRun, type JsonObject } from './core.ts'
import { boundedTop } from './validation.ts'

export type TeamsListParams = MicrosoftAccountParams & {
  top?: number
}

export type TeamsChatMessagesParams = MicrosoftAccountParams & {
  chatId: string
  top?: number
}

export type TeamsSendChatParams = MicrosoftAccountParams & {
  chatId: string
  content: string
  contentType?: 'text' | 'html'
  dryRun?: boolean
  confirm?: boolean
}

export type TeamsChannelsParams = MicrosoftAccountParams & {
  teamId: string
  top?: number
}

export type TeamsChannelMessagesParams = MicrosoftAccountParams & {
  teamId: string
  channelId: string
  top?: number
}

export type TeamsSendChannelParams = MicrosoftAccountParams & {
  teamId: string
  channelId: string
  content: string
  contentType?: 'text' | 'html'
  dryRun?: boolean
  confirm?: boolean
}

/** List Teams the signed-in user has joined. */
export async function listJoinedTeams(params: TeamsListParams = {}) {
  const data = await graphRequest<JsonObject>({
    integration: resolveMicrosoftIntegration(params),
    path: '/me/joinedTeams',
    query: {
      $top: boundedTop(params.top, 20, 100),
      $select: 'id,displayName,description,isArchived,visibility',
    },
  })
  return graphItems<JsonObject>(data)
}

/** List 1:1 and group chats available to the signed-in user. */
export async function listChats(params: TeamsListParams = {}) {
  const data = await graphRequest<JsonObject>({
    integration: resolveMicrosoftIntegration(params),
    path: '/me/chats',
    query: {
      $top: boundedTop(params.top, 20, 50),
      $select: 'id,topic,chatType,createdDateTime,lastUpdatedDateTime,webUrl',
    },
  })
  return graphItems<JsonObject>(data)
}

/** List messages in a Teams chat. */
export async function listChatMessages(params: TeamsChatMessagesParams) {
  const chatId = params.chatId?.trim()
  if (!chatId) throw new Error('listChatMessages requires chatId.')
  const data = await graphRequest<JsonObject>({
    integration: resolveMicrosoftIntegration(params),
    path: '/me/chats/' + encodeURIComponent(chatId) + '/messages',
    query: {
      $top: boundedTop(params.top, 20, 50),
      $select: 'id,createdDateTime,from,body,importance,webUrl',
    },
  })
  return graphItems<JsonObject>(data)
}

/** Preview or send a Teams chat message. Sending requires confirm: true. */
export async function sendChatMessage(params: TeamsSendChatParams) {
  const chatId = params.chatId?.trim()
  if (!chatId) throw new Error('sendChatMessage requires chatId.')
  const content = params.content
  if (typeof content !== 'string' || content.trim().length === 0) {
    throw new Error('sendChatMessage requires content.')
  }
  const path = '/me/chats/' + encodeURIComponent(chatId) + '/messages'
  const body = {
    body: {
      contentType: params.contentType === 'html' ? 'html' : 'text',
      content,
    },
  }
  const mode = requireConfirmOrDryRun({
    dryRun: params.dryRun,
    confirm: params.confirm,
    action: 'sendChatMessage',
  })
  if (mode.dryRun) {
    return { dryRun: true as const, method: 'POST', path, body }
  }
  return graphRequest<JsonObject>({
    integration: resolveMicrosoftIntegration(params),
    method: 'POST',
    path,
    body,
  })
}

/** List channels in a team. */
export async function listChannels(params: TeamsChannelsParams) {
  const teamId = params.teamId?.trim()
  if (!teamId) throw new Error('listChannels requires teamId.')
  const data = await graphRequest<JsonObject>({
    integration: resolveMicrosoftIntegration(params),
    path: '/teams/' + encodeURIComponent(teamId) + '/channels',
    query: {
      $top: boundedTop(params.top, 20, 100),
      $select: 'id,displayName,description,membershipType,webUrl',
    },
  })
  return graphItems<JsonObject>(data)
}

/** Preview or send a Teams channel message. Needs ChannelMessage.Send. Sending requires confirm: true. */
export async function sendChannelMessage(params: TeamsSendChannelParams) {
  const teamId = params.teamId?.trim()
  const channelId = params.channelId?.trim()
  if (!teamId) throw new Error('sendChannelMessage requires teamId.')
  if (!channelId) throw new Error('sendChannelMessage requires channelId.')
  const content = params.content
  if (typeof content !== 'string' || content.trim().length === 0) {
    throw new Error('sendChannelMessage requires content.')
  }
  const path =
    '/teams/' +
    encodeURIComponent(teamId) +
    '/channels/' +
    encodeURIComponent(channelId) +
    '/messages'
  const body = {
    body: {
      contentType: params.contentType === 'html' ? 'html' : 'text',
      content,
    },
  }
  const mode = requireConfirmOrDryRun({
    dryRun: params.dryRun,
    confirm: params.confirm,
    action: 'sendChannelMessage',
  })
  if (mode.dryRun) {
    return { dryRun: true as const, method: 'POST', path, body }
  }
  return graphRequest<JsonObject>({
    integration: resolveMicrosoftIntegration(params),
    method: 'POST',
    path,
    body,
  })
}

/** List messages in a team channel. Often needs admin-consented ChannelMessage.Read.All. */
export async function listChannelMessages(params: TeamsChannelMessagesParams) {
  const teamId = params.teamId?.trim()
  const channelId = params.channelId?.trim()
  if (!teamId) throw new Error('listChannelMessages requires teamId.')
  if (!channelId) throw new Error('listChannelMessages requires channelId.')
  const data = await graphRequest<JsonObject>({
    integration: resolveMicrosoftIntegration(params),
    path:
      '/teams/' +
      encodeURIComponent(teamId) +
      '/channels/' +
      encodeURIComponent(channelId) +
      '/messages',
    query: {
      $top: boundedTop(params.top, 20, 50),
      $select: 'id,createdDateTime,from,body,importance,webUrl',
    },
  })
  return graphItems<JsonObject>(data)
}

/**
 * Microsoft Teams helpers for Microsoft Graph.
 * @example
 * import teams from 'kody:@kody/microsoft/teams'
 * const { items } = await teams().listJoinedTeams()
 */
export default function teams() {
  return {
    listJoinedTeams,
    listChats,
    listChatMessages,
    sendChatMessage,
    listChannels,
    listChannelMessages,
    sendChannelMessage,
  }
}