diff --git a/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts index f0b35d78f58cb..dc852beea124c 100644 --- a/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/MicrosoftTeamsOAuth2Api.credentials.ts @@ -16,7 +16,7 @@ export class MicrosoftTeamsOAuth2Api implements ICredentialType { displayName: 'Scope', name: 'scope', type: 'hidden', - default: 'openid offline_access User.ReadWrite.All Group.ReadWrite.All', + default: 'openid offline_access User.ReadWrite.All Group.ReadWrite.All Chat.ReadWrite', }, ]; } diff --git a/packages/nodes-base/nodes/Microsoft/Teams/ChatMessageDescription.ts b/packages/nodes-base/nodes/Microsoft/Teams/ChatMessageDescription.ts new file mode 100644 index 0000000000000..7ad4030845b30 --- /dev/null +++ b/packages/nodes-base/nodes/Microsoft/Teams/ChatMessageDescription.ts @@ -0,0 +1,201 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const chatMessageOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'chatMessage', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a message', + }, + { + name: 'Get', + value: 'get', + description: 'Get a message', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all messages', + }, + ], + default: 'create', + description: 'The operation to perform.', + }, +]; + +export const chatMessageFields: INodeProperties[] = [ + + /* -------------------------------------------------------------------------- */ + /* chatMessage:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Chat ID', + name: 'chatId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChats', + }, + displayOptions: { + show: { + operation: [ + 'create', + 'get', + ], + resource: [ + 'chatMessage', + ], + }, + }, + default: '', + description: 'Chat ID', + }, + { + displayName: 'Message Type', + name: 'messageType', + required: true, + type: 'options', + options: [ + { + name: 'Text', + value: 'text', + }, + { + name: 'HTML', + value: 'html', + }, + ], + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'chatMessage', + ], + }, + }, + default: '', + description: 'The type of the content', + }, + { + displayName: 'Message', + name: 'message', + required: true, + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + displayOptions: { + show: { + operation: [ + 'create', + ], + resource: [ + 'chatMessage', + ], + }, + }, + default: '', + description: 'The content of the item.', + }, + + /* -------------------------------------------------------------------------- */ + /* chatMessage:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Message ID', + name: 'messageId', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'chatMessage', + ], + }, + }, + default: '', + }, + /* -------------------------------------------------------------------------- */ + /* chatMessage:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Chat ID', + name: 'chatId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getChats', + }, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'chatMessage', + ], + }, + }, + default: '', + description: 'Chat ID', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'chatMessage', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'chatMessage', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, +]; diff --git a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts index 0a2831850094f..e042dacb591c2 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts @@ -26,6 +26,11 @@ import { channelMessageOperations, } from './ChannelMessageDescription'; +import { + chatMessageFields, + chatMessageOperations, +} from './ChatMessageDescription'; + import { taskFields, taskOperations, @@ -65,6 +70,10 @@ export class MicrosoftTeams implements INodeType { name: 'Channel Message (Beta)', value: 'channelMessage', }, + { + name: 'Chat Message', + value: 'chatMessage', + }, { name: 'Task', value: 'task', @@ -79,6 +88,8 @@ export class MicrosoftTeams implements INodeType { /// MESSAGE ...channelMessageOperations, ...channelMessageFields, + ...chatMessageOperations, + ...chatMessageFields, ///TASK ...taskOperations, ...taskFields, @@ -189,6 +200,29 @@ export class MicrosoftTeams implements INodeType { } return returnData; }, + // Get all the chats to display them to user so that they can + // select them easily + async getChats(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const qs: IDataObject = { + $expand: 'members', + }; + const { value } = await microsoftApiRequest.call(this, 'GET', '/v1.0/chats', {}, qs); + for (const chat of value) { + if (!chat.topic) { + chat.topic = chat.members + .filter((member: IDataObject) => member.displayName) + .map((member: IDataObject) => member.displayName).join(', '); + } + const chatName = `${chat.topic || '(no title) - ' + chat.id} (${chat.chatType})`; + const chatId = chat.id; + returnData.push({ + name: chatName, + value: chatId, + }); + } + return returnData; + }, }, }; @@ -298,6 +332,39 @@ export class MicrosoftTeams implements INodeType { } } } + if (resource === 'chatMessage') { + // https://docs.microsoft.com/en-us/graph/api/channel-post-messages?view=graph-rest-1.0&tabs=http + if (operation === 'create') { + const chatId = this.getNodeParameter('chatId', i) as string; + const messageType = this.getNodeParameter('messageType', i) as string; + const message = this.getNodeParameter('message', i) as string; + const body: IDataObject = { + body: { + contentType: messageType, + content: message, + }, + }; + responseData = await microsoftApiRequest.call(this, 'POST', `/v1.0/chats/${chatId}/messages`, body); + } + // https://docs.microsoft.com/en-us/graph/api/chat-list-messages?view=graph-rest-1.0&tabs=http + if (operation === 'get') { + const chatId = this.getNodeParameter('chatId', i) as string; + const messageId = this.getNodeParameter('messageId', i) as string; + responseData = await microsoftApiRequest.call(this, 'GET', `/v1.0/chats/${chatId}/messages/${messageId}`); + } + // https://docs.microsoft.com/en-us/graph/api/chat-list-messages?view=graph-rest-1.0&tabs=http + if (operation === 'getAll') { + const chatId = this.getNodeParameter('chatId', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + if (returnAll) { + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/chats/${chatId}/messages`); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/chats/${chatId}/messages`, {}); + responseData = responseData.splice(0, qs.limit); + } + } + } if (resource === 'task') { //https://docs.microsoft.com/en-us/graph/api/planner-post-tasks?view=graph-rest-1.0&tabs=http if (operation === 'create') {