or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bot-controller.mdbot-worker.mdconversations.mddialog-wrapper.mdindex.mdmessage-handling.mdteams-integration.mdtesting.md
tile.json

teams-integration.mddocs/

Microsoft Teams Integration

Specialized functionality for Microsoft Teams including task modules, channel management, and Teams-specific event handling.

Capabilities

Teams Bot Worker

Extended bot worker with Teams-specific functionality.

/**
 * Specialized version of BotWorker with additional methods for Microsoft Teams
 */
class TeamsBotWorker extends BotWorker {
  /** Grants access to the TeamsInfo helper class */
  teams: TeamsInfo;
  
  /**
   * Reply to a Teams task module task/fetch or task/submit with a task module response
   * @param message The incoming message
   * @param taskInfo An object in the form {type, value}
   */
  replyWithTaskInfo(message: BotkitMessage, taskInfo: any): Promise<any>;
}

Usage Examples:

// Handle task module fetch
controller.on("task/fetch", async (bot, message) => {
  const taskInfo = {
    type: "continue",
    value: {
      title: "Survey Form",
      height: 400,
      width: 500,
      url: "https://myapp.com/survey"
    }
  };
  
  await bot.replyWithTaskInfo(message, taskInfo);
});

// Handle task module submit
controller.on("task/submit", async (bot, message) => {
  console.log("Task module data:", message.value);
  
  // Process the submitted data
  const result = await processTaskData(message.value);
  
  // Return response
  await bot.replyWithTaskInfo(message, {
    type: "message",
    value: "Thanks for your submission!"
  });
});

// Access Teams API
controller.on("message", async (bot, message) => {
  // Get team members
  const members = await bot.teams.getTeamMembers(bot.getConfig("context"));
  console.log(`Team has ${members.length} members`);
});

Teams Adapter

Extended Bot Framework adapter with Teams-specific features.

/**
 * Extended BotFrameworkAdapter with Teams support
 */
class BotkitBotFrameworkAdapter extends BotFrameworkAdapter {
  /** Custom bot worker class for Teams */
  botkit_worker: TeamsBotWorker;
  
  /**
   * Get the list of channels in a MS Teams team
   * @param context A TurnContext object from a team conversation
   * @returns Array of channels in format [{name: string, id: string}]
   */
  getChannels(context: TurnContext): Promise<ChannelInfo[]>;
  
  /**
   * Create connector client with custom user agent
   * @param serviceUrl Clients service url
   */
  createConnectorClient(serviceUrl: string): ConnectorClient;
}

Usage Examples:

// Create Teams-enabled controller
const controller = new Botkit({
  adapter: new BotkitBotFrameworkAdapter({
    appId: process.env.MICROSOFT_APP_ID,
    appPassword: process.env.MICROSOFT_APP_PASSWORD
  })
});

// Get team channels
controller.on("message", async (bot, message) => {
  if (message.channelId === "msteams") {
    const channels = await bot.getConfig("adapter").getChannels(bot.getConfig("context"));
    
    const channelList = channels.map(ch => `- ${ch.name}`).join("\n");
    await bot.reply(message, `Available channels:\n${channelList}`);
  }
});

Teams Event Middleware

Middleware for processing Teams-specific invoke events.

/**
 * Middleware that emits special events for Teams "invokes"
 */
class TeamsInvokeMiddleware extends MiddlewareSet {
  /**
   * Process Teams invoke events and set appropriate event types
   * @param context Turn context
   * @param next Next middleware function
   */
  onTurn(context: TurnContext, next: () => Promise<any>): Promise<void>;
}

Usage Examples:

import { TeamsInvokeMiddleware } from "botkit";

// Add Teams middleware to adapter
const adapter = new BotFrameworkAdapter(adapterConfig);
adapter.use(new TeamsInvokeMiddleware());

const controller = new Botkit({ adapter });

// Now you can handle Teams-specific events
controller.on("task/fetch", async (bot, message) => {
  // Handle task module fetch
});

controller.on("task/submit", async (bot, message) => {
  // Handle task module submit
});

controller.on("composeExtension/query", async (bot, message) => {
  // Handle compose extension query
});

Teams-Specific Events

Task Module Events

Handle Teams task modules for rich interactive experiences.

// Task module fetch - show a form or webpage
controller.on("task/fetch", async (bot, message) => {
  const taskInfo = {
    type: "continue",
    value: {
      title: "User Information",
      height: 300,
      width: 400,
      card: {
        type: "AdaptiveCard",
        version: "1.0",
        body: [
          {
            type: "TextBlock",
            text: "Enter your details"
          },
          {
            type: "Input.Text",
            id: "name",
            placeholder: "Your name"
          }
        ],
        actions: [
          {
            type: "Action.Submit",
            title: "Submit"
          }
        ]
      }
    }
  };
  
  await bot.replyWithTaskInfo(message, taskInfo);
});

// Task module submit - process form data
controller.on("task/submit", async (bot, message) => {
  const userData = message.value;
  
  // Process the data
  await saveUserData(userData);
  
  // Return success message
  await bot.replyWithTaskInfo(message, {
    type: "message",
    value: `Hello ${userData.name}! Your information has been saved.`
  });
});

Compose Extension Events

Handle Teams compose extensions (messaging extensions).

// Handle compose extension queries
controller.on("composeExtension/query", async (bot, message) => {
  const query = message.value.commandId;
  const searchText = message.value.parameters?.[0]?.value || "";
  
  let results = [];
  
  if (query === "searchUsers") {
    results = await searchUsers(searchText);
  }
  
  // Return results
  await bot.reply(message, {
    composeExtension: {
      type: "result",
      attachmentLayout: "list",
      attachments: results.map(user => ({
        contentType: "application/vnd.microsoft.card.thumbnail",
        content: {
          title: user.name,
          subtitle: user.email,
          text: user.department
        }
      }))
    }
  });
});

// Handle compose extension submissions
controller.on("composeExtension/submitAction", async (bot, message) => {
  const action = message.value;
  
  // Process the action
  const result = await processAction(action);
  
  await bot.reply(message, {
    composeExtension: {
      type: "result",
      attachmentLayout: "list",
      attachments: [result]
    }
  });
});

File and Card Events

Handle file consent and card action events.

// Handle file consent
controller.on("fileConsent/invoke", async (bot, message) => {
  const fileInfo = message.value;
  
  if (fileInfo.action === "accept") {
    // Upload file to the accepted location
    await uploadFile(fileInfo.uploadInfo.uploadUrl, fileData);
    await bot.reply(message, "File uploaded successfully!");
  } else {
    await bot.reply(message, "File upload was declined.");
  }
});

// Handle card actions  
controller.on("cardAction", async (bot, message) => {
  const action = message.value;
  
  switch (action.verb) {
    case "approve":
      await handleApproval(action.data);
      await bot.reply(message, "Request approved!");
      break;
      
    case "reject":
      await handleRejection(action.data);
      await bot.reply(message, "Request rejected.");
      break;
  }
});

Teams API Integration

Channel Operations

Work with Teams channels and conversations.

// Get team channels
controller.hears("list channels", "message", async (bot, message) => {
  try {
    const channels = await bot.getConfig("adapter").getChannels(bot.getConfig("context"));
    
    const channelList = channels.map(ch => 
      `• **${ch.name || "General"}** (${ch.id})`
    ).join("\n");
    
    await bot.reply(message, `Team channels:\n${channelList}`);
  } catch (error) {
    await bot.reply(message, "Could not retrieve channels. Make sure this is a team conversation.");
  }
});

// Send message to specific channel
controller.hears("announce", "message", async (bot, message) => {
  const channels = await bot.getConfig("adapter").getChannels(bot.getConfig("context"));
  const generalChannel = channels.find(ch => ch.name === "General");
  
  if (generalChannel) {
    const announcement = {
      type: "message",
      text: "📢 Important announcement for the team!",
      conversation: { id: generalChannel.id }
    };
    
    await bot.say(announcement);
  }
});

Member Information

Access team and conversation member details.

// Get team members
controller.hears("team members", "message", async (bot, message) => {
  const context = bot.getConfig("context");
  
  try {
    const members = await bot.teams.getTeamMembers(context);
    
    const memberList = members.map(member => 
      `• ${member.name} (${member.email})`
    ).join("\n");
    
    await bot.reply(message, `Team members:\n${memberList}`);
  } catch (error) {
    await bot.reply(message, "Unable to retrieve team members.");
  }
});

// Get conversation members (for group chats)
controller.hears("chat members", "message", async (bot, message) => {
  const context = bot.getConfig("context");
  
  try {
    const members = await bot.teams.getMembers(context);
    
    const memberCount = members.length;
    await bot.reply(message, `This conversation has ${memberCount} members.`);
  } catch (error) {
    await bot.reply(message, "Unable to retrieve conversation members.");
  }
});

Advanced Teams Features

Adaptive Cards

Send rich Adaptive Cards in Teams.

controller.hears("show card", "message", async (bot, message) => {
  const card = {
    type: "message",
    attachments: [{
      contentType: "application/vnd.microsoft.card.adaptive",
      content: {
        type: "AdaptiveCard",
        version: "1.2",
        body: [
          {
            type: "TextBlock",
            text: "Poll Question",
            weight: "Bolder",
            size: "Medium"
          },
          {
            type: "TextBlock",
            text: "What's your favorite programming language?",
            wrap: true
          }
        ],
        actions: [
          {
            type: "Action.Submit",
            title: "JavaScript",
            data: { vote: "javascript" }
          },
          {
            type: "Action.Submit", 
            title: "Python",
            data: { vote: "python" }
          },
          {
            type: "Action.Submit",
            title: "TypeScript", 
            data: { vote: "typescript" }
          }
        ]
      }
    }]
  };
  
  await bot.reply(message, card);
});

// Handle card submissions
controller.on("cardAction", async (bot, message) => {
  const vote = message.value.data.vote;
  await recordVote(vote);
  await bot.reply(message, `Thanks for voting for ${vote}!`);
});

Proactive Messages in Teams

Send proactive messages to Teams users and channels.

// Save conversation reference for later use
let savedReferences = {};

controller.on("conversationUpdate", async (bot, message) => {
  if (message.membersAdded) {
    // Save reference for proactive messaging
    savedReferences[message.from.id] = message.reference;
  }
});

// Send proactive message
async function sendProactiveMessage(userId, messageText) {
  const reference = savedReferences[userId];
  if (reference) {
    const bot = await controller.spawn();
    await bot.changeContext(reference);
    await bot.say(messageText);
  }
}

// Example: Send daily reminder
setInterval(async () => {
  for (const userId in savedReferences) {
    await sendProactiveMessage(userId, "Daily reminder: Don't forget to submit your timesheet!");
  }
}, 24 * 60 * 60 * 1000); // Once per day