ConversationBot doesn't detect installations or members (Teams App Express Server Typescript)
I have a Multitenant Microsoft Teams app, written as an Express App in Typescript, that uses a ConversationBot. I'm trying to track members who have installed the Teams app but am getting zero results. So far I've tried:
- TeamsInfo.getPagedMembers (in my TeamsBot class that extends
TeamsActivityHandler
- In my express App, getPagedInstallations and findAllMembers
I'm able to track onTeamsMemberAddedEvent, onMembersAddedActivity and on InstallationUpdateActivity. I initialize my ConversationBot
as:
const config = {
MicrosoftAppId: process.env.BOT_ID || process.env.MicrosoftAppId,
MicrosoftAppType: process.env.BOT_TYPE || process.env.MicrosoftAppType,
MicrosoftAppTenantId: process.env.MicrosoftAppTenantId,
MicrosoftAppPassword: process.env.BOT_PASSWORD || process.env.MicrosoftAppPassword,
};
const notificationApp = new ConversationBot({
adapterConfig: {
...config
},
notification: {
enabled: true,
},
});
This is my TeamsBot class:
class TeamsBot extends TeamsActivityHandler {
constructor() {
super();
this.onTeamsMembersAddedEvent(async (membersAdded: ChannelAccount[], teamInfo: TeamsInfo, context: TurnContext, next: () => Promise<void>): Promise<void> => {...}
this.onMembersAddedActivity(async (context, next) => {...}
this.onTeamsMembersAddedEvent(async (membersAdded: ChannelAccount[], teamInfo: TeamsInfo, context: TurnContext, next: () => Promise<void>): Promise<void> => {...}
});
}
// This fails
async getPagedMembers(context) {
console.log('[TeamsBot] Starting to get paged members');
let continuationToken;
const members = [];
do {
console.log('[TeamsBot] Fetching page of members:', {
continuationToken: continuationToken ? 'present' : 'none'
});
const page = await TeamsInfo.getPagedMembers(
context,
100,
continuationToken
);
continuationToken = page.continuationToken;
members.push(...page.members);
console.log('[TeamsBot] Retrieved page of members:', {
count: page.members.length,
hasMore: !!continuationToken
});
} while (continuationToken !== undefined);
console.log('[TeamsBot] Completed getting all members:', {
totalCount: members.length,
members: JSON.stringify(members, null, 2)
});
return members;
}
}
This is how I initialize my server and /api/messages
:
const server = expressApp.listen(process.env.port || process.env.PORT || 3978)
expressApp.post("/api/messages", async (req, res) => {
await notificationApp.requestHandler(req, res, async (context) => {
await teamsBot.run(context);
});
});
These are the parts of the code that I use to find all members and paged installations:
// Fails
const pageSize = 100;
let continuationToken: string | undefined = undefined;
let totalInstallations = 0;
const installationTypes: Record<string, number> = {};
const allMembers: any[] = [];
const allTeams: any[] = [];
do {
const pagedData = await notificationApp.notification.getPagedInstallations(
pageSize,
continuationToken
) || { data: [] };
const installations = pagedData.data;
continuationToken = pagedData.continuationToken;
for (const installation of installations) {
totalInstallations++;
installationTypes[type] = (installationTypes[type] || 0) + 1;
// Get members for this installation
try {
const membersData = await installation.getPagedMembers(pageSize);
if (membersData?.data?.length > 0) {
allMembers.push(...membersData.data);
}
} catch (memberError) {
...
}
} while (continuationToken);
//Fails -> cannot detect any member emails
const members = await notificationApp.notification.findAllMembers(
async (member) => {
logToAzure(`📝 Evaluating member: ${member.account.email}`);
return true;
}
);
The relevant section of my manifest.json
:
"bots": [
{
"botId": "${{BOT_ID}}",
"scopes": [
"personal"
],
"supportsFiles": false,
"isNotificationOnly": false
}
],
"composeExtensions": [],
"configurableTabs": [],
"staticTabs": [],
"permissions": [
"identity",
"messageTeamMembers"
],
"authorization": {
"permissions": {
"resourceSpecific": [
]
}
},
"validDomains": [
"${{BOT_DOMAIN}}", "token.botframework.com"
]
This is my teamsapp.yml
version: v1.7
environmentFolderPath: ./env
provision:
# Creates a Teams app
- uses: teamsApp/create
with:
name: ${{APP_NAME}}
writeToEnvironmentFile:
teamsAppId: TEAMS_APP_ID
- uses: botAadApp/create
with:
name: ${{APP_NAME}}-${{APP_NAME_SUFFIX}}
writeToEnvironmentFile:
botId: BOT_ID
botPassword: SECRET_BOT_PASSWORD
- uses: arm/deploy
with:
subscriptionId: ${{AZURE_SUBSCRIPTION_ID}}
resourceGroupName: ${{AZURE_RESOURCE_GROUP_NAME}}
templates:
- path: ./infra/azure.bicep
parameters: ./infra/azure.parameters.json
deploymentName: Create-resources-for-bot
- uses: teamsApp/validateManifest
with:
manifestPath: ./appPackage/manifest.json
- uses: teamsApp/zipAppPackage
with:
manifestPath: ./appPackage/manifest.json
outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
outputFolder: ./appPackage/build
- uses: teamsApp/validateAppPackage
with:
# Relative path to this file. This is the path for built zip file.
appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
- uses: teamsApp/update
with:
# Relative path to this file. This is the path for built zip file.
appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
deploy:
# Run npm command
- uses: cli/runNpmCommand
name: install dependencies
with:
args: install
- uses: cli/runNpmCommand
name: build app
with:
args: run build --if-present
- uses: azureAppService/zipDeploy
with:
artifactFolder: .
ignoreFile: .appserviceignore
resourceId: ${{BOT_AZURE_APP_SERVICE_RESOURCE_ID}}
- uses: file/createOrUpdateEnvironmentFile
with:
target: env/.env.dev
envs:
MicrosoftAppId: ${{BOT_ID}}
MicrosoftAppPassword: ${{SECRET_BOT_PASSWORD}}
BOT_ID: ${{BOT_ID}}
BOT_PASSWORD: ${{SECRET_BOT_PASSWORD}}
BOT_TYPE: 'MultiTenant'
BOT_DOMAIN: ${{BOT_DOMAIN}}
publish:
- uses: teamsApp/validateManifest
with:
manifestPath: ./appPackage/manifest.json
- uses: teamsApp/zipAppPackage
with:
manifestPath: ./appPackage/manifest.json
outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
outputFolder: ./appPackage/build
- uses: teamsApp/validateAppPackage
with:
appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
- uses: teamsApp/update
with:
appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
- uses: teamsApp/publishAppPackage
with:
appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
writeToEnvironmentFile:
publishedAppId: TEAMS_APP_PUBLISHED_APP_ID
projectId: ${{PROJECT_ID}}
I've been following the Microsoft guide to creating a Teams Conversation bot. At this point, I'm not sure how to detect installations (I always. get0 installations). My ultimate purpose is to message members who have installed the app, who have specific email addresses, when I had an endpoint on my Express app where the request contains a member's email address. I'm open to any recommendations where I can track the email addresses of all members who have the app currently installed. I have an app registration on the Azure portal but I haven't added any specific API permissions to it. In my resource group, I have an Azure bot, an App Service and an App Service Plan.