docs: explain Teams API limitation for message deletion

- Microsoft Teams Graph API does not support DELETE on channel messages
- This is a platform limitation, not an application error
- Updated UI to show clear warning about Teams API limitation
- Updated error messages to guide users on alternatives:
  - Delete via Teams client UI
  - Use delegated token with ChatMessage.ReadWrite.All permission
- HTTP 405 (Method Not Allowed) is the expected response from Graph API
This commit is contained in:
2025-12-20 20:07:02 +00:00
parent 7b4f58a807
commit 611be70362
3 changed files with 601 additions and 0 deletions
+83
View File
@@ -4,6 +4,7 @@ import { serveStatic } from 'hono/bun';
import { cors } from 'hono/cors';
import PocketBase from 'pocketbase';
import { Client } from '@microsoft/microsoft-graph-client';
import { listTeamChannels, listChannelMessages, listTeamChannelsWithToken, listChannelMessagesWithToken, deleteChannelMessage, deleteChannelMessageWithToken } from './services/teams/messages.ts';
import { mkdir, appendFile } from 'fs/promises';
import { existsSync } from 'fs';
import path from 'path';
@@ -753,6 +754,88 @@ app.get('/api/health', (c) => {
});
});
// List channels in a Team (to obtain Channel IDs)
app.get('/api/teams/:teamId/channels', async (c) => {
const teamId = c.req.param('teamId');
try {
const authHeader = c.req.header('authorization') || c.req.header('Authorization');
const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
const channels = bearer
? await listTeamChannelsWithToken(teamId, bearer)
: await listTeamChannels(teamId);
return c.json({ success: true, teamId, channels });
} catch (err: any) {
console.error('⚠️ Failed to list channels:', err);
await logError('Graph List Channels', err, { teamId });
return c.json({ success: false, message: err.message || 'Failed to list channels' }, 500);
}
});
// List messages in a channel (to obtain message IDs)
app.get('/api/teams/:teamId/channels/:channelId/messages', async (c) => {
const teamId = c.req.param('teamId');
const channelId = c.req.param('channelId');
const top = Number(c.req.query('top') || '50');
try {
const authHeader = c.req.header('authorization') || c.req.header('Authorization');
const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
const messages = bearer
? await listChannelMessagesWithToken(teamId, channelId, Math.min(top, 50), bearer)
: await listChannelMessages(teamId, channelId, Math.min(top, 50));
return c.json({ success: true, teamId, channelId, count: messages.length, messages });
} catch (err: any) {
console.error('⚠️ Failed to list messages:', err);
await logError('Graph List Messages', err, { teamId, channelId });
return c.json({ success: false, message: err.message || 'Failed to list messages' }, 500);
}
});
// Delete a message in a channel (requires appropriate Graph permissions)
app.delete('/api/teams/:teamId/channels/:channelId/messages/:messageId', async (c) => {
const teamId = c.req.param('teamId');
const channelId = c.req.param('channelId');
const messageId = c.req.param('messageId');
try {
const authHeader = c.req.header('authorization') || c.req.header('Authorization');
const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
const tokenType = bearer ? 'Bearer token provided' : 'Using app credentials';
console.log(`🗑️ DELETE request: ${tokenType} | Team: ${teamId} | Channel: ${channelId} | Message: ${messageId}`);
if (bearer) {
await deleteChannelMessageWithToken(teamId, channelId, messageId, bearer);
} else {
await deleteChannelMessage(teamId, channelId, messageId);
}
console.log(`✅ Successfully deleted message: ${messageId}`);
return c.json({ success: true, teamId, channelId, messageId });
} catch (err: any) {
const errMsg = err?.message || JSON.stringify(err);
const errStatus = err?.status || err?.statusCode;
// Check if it's the Teams API limitation error
const isTeamsLimitation = errMsg?.includes('Teams API limitation') || errMsg?.includes('not supported');
console.error(`${isTeamsLimitation ? '⚠️' : '❌'} Failed to delete message: ${errMsg} | Status: ${errStatus}`);
if (isTeamsLimitation) {
console.log(`📝 Note: This is a Microsoft Teams platform limitation, not an application error`);
}
await logError('Graph Delete Message', err, {
teamId,
channelId,
messageId,
error: errMsg,
status: errStatus,
isTeamsLimitation
});
return c.json({
success: false,
message: errMsg || 'Failed to delete message',
isTeamsLimitation: isTeamsLimitation || false
}, 500);
}
});
// Test route: sends an exact sample Adaptive Card payload (no envelope)
app.post('/api/test-notification', async (c) => {
const webhook = process.env.POWER_AUTOMATE_WEBHOOK_URL || process.env.TEAMS_WEBHOOK_URL;