UNPKG

@microsoft/teams.apps

Version:

<p> <a href="https://www.npmjs.com/package/@microsoft/teams.apps" target="_blank"> <img src="https://img.shields.io/npm/v/@microsoft/teams.apps/latest" /> </a> <a href="https://www.npmjs.com/package/@microsoft/teams.apps?activeTab=code

348 lines • 30.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ActivityContext = void 0; const teams_api_1 = require("@microsoft/teams.api"); const files_accessor_1 = require("../files/files-accessor"); const oauth_1 = require("../oauth"); const plan_1 = require("../plan"); class ActivityContext { appId; activity; ref; log; api; appGraph; userGraph; /** * @deprecated Use `state` for conversation or user state. */ storage; state; stream; files; isSignedIn; connectionName; next; activitySender; _plan; validateOAuthConnection; onOAuthSignInInitiated; getOAuthConnectionStatus; constructor(value) { // Extract activitySender and next before Object.assign to avoid overwriting methods const { activitySender, next, validateOAuthConnection, onOAuthSignInInitiated, getOAuthConnectionStatus, filesCredential, filesBotApiCredential, ...rest } = value; // Rehydrate the inbound payload into its activity instance so computed // accessors (channel/team/meeting/notification/tenant) resolve. Do NOT // call `toInterface()` here: it flattens the instance to a plain object via // `Object.assign`, which drops the prototype getters and makes those // accessors silently return `undefined`. if (rest.activity.type === 'message') { rest.activity = teams_api_1.MessageActivity.from(rest.activity); } if (rest.activity.type === 'messageUpdate') { rest.activity = teams_api_1.MessageUpdateActivity.from(rest.activity); } if (rest.activity.type === 'messageDelete') { rest.activity = teams_api_1.MessageDeleteActivity.from(rest.activity); } if (rest.activity.type === 'typing') { rest.activity = teams_api_1.TypingActivity.from(rest.activity); } // SECURITY: drop any keys in `rest` that would shadow prototype methods. // Plugin-supplied context can add new properties via the [key: string]: any // index signature, but must not overwrite methods that callers rely on for // trust (send, reply, quote, signin, signout). for (const key of PROTECTED_METHOD_NAMES) { if (key in rest) { delete rest[key]; } } Object.assign(this, rest); this.activitySender = activitySender; this.validateOAuthConnection = validateOAuthConnection; this.onOAuthSignInInitiated = onOAuthSignInInitiated; this.getOAuthConnectionStatus = getOAuthConnectionStatus; this.next = next; const placement = this.getInboundReplyChainPlacement(); this.stream = activitySender.createStream(value.ref, placement ? { replyChain: { chatOrChannelId: placement.chatOrChannelId, replyChainId: placement.replyChainId, }, } : undefined); this.connectionName = value.connectionName; this.files = new files_accessor_1.FilesAccessor(this.activity, this.log.child('files'), value.client, filesCredential, filesBotApiCredential); this._plan = undefined; } /** * Get the Plan bound to this inbound activity, scoping its client on first access. * In a chat thread, a standalone `complete()` is posted to the reply chain, as `ctx.send` does. * Typing updates, completions in a channel, and prompts outside a thread use the conversation. * * @experimental This API is in preview and may change in the future. * Diagnostic: ExperimentalTeamsPlan */ plan() { if (!this._plan) { const placement = this.getInboundReplyChainPlacement(); this._plan = new plan_1.Plan({ conversationId: this.ref.conversation.id, replyToId: this.activity.id, replyChain: placement && !placement.inChannel ? { chatOrChannelId: placement.chatOrChannelId, replyChainId: placement.replyChainId } : undefined, apiClient: this.api.fromServiceUrl({ serviceUrl: this.ref.serviceUrl }), }); } return this._plan; } async send(activity, conversationRef) { const params = (0, teams_api_1.toActivityParams)(activity); if (this.shouldOutboundBeAutoTargeted(params, conversationRef)) { this.applyTargetedRecipient(params); } if (this.isTargetedOutbound(params)) { // `targetedMessageInfo` points at the original targeted inbound message for prompt preview. // Do not add it for generic targeted sends; Teams can reject it if the referenced activity // was not itself delivered as a targeted message. // The quote of the inbound message is removed only together with it, so a generic targeted send keeps an explicit quote. if (this.isIncomingTargeted()) { this.stripQuotedReplyMetadata(params); this.addTargetedMessageInfo(params); } } const ref = conversationRef ?? this.ref; const placement = this.getChatReplyChainPlacement(params, conversationRef); if (placement) { try { return await this.activitySender.send(params, { ...ref, conversation: { ...ref.conversation, id: placement.chatOrChannelId } }, { replyChainId: placement.replyChainId }); } catch (err) { if (!placement.inChannel || err?.response?.status !== 404) { throw err; } } } return this.activitySender.send(params, ref); } async reply(activity) { if (isQuotableActivity(this.activity)) { return this.quote(this.activity.id, activity); } return this.send(activity); } async quote(messageId, activity) { activity = (0, teams_api_1.toActivityParams)(activity); if (activity.type === 'message') { const message = teams_api_1.MessageActivityInput.from(activity); message.prependQuote(messageId); return this.send(message); } return this.send(activity); } async signin(options = {}) { const connectionNameProvided = options.connectionName !== undefined; const connectionName = options.connectionName ?? this.connectionName; this.validateOAuthConnection?.(connectionName, connectionNameProvided); return (0, oauth_1.startOAuthSignIn)(this.toInterface(), connectionName, options, this.onOAuthSignInInitiated); } async signout(connectionName) { this.validateOAuthConnection?.(connectionName ?? this.connectionName, connectionName !== undefined); await this.api.users.signOut({ channelId: this.activity.channelId, userId: this.activity.from.id, connectionName: connectionName ?? this.connectionName, }); } async getConnectionStatus() { if (!this.getOAuthConnectionStatus) { throw new Error('OAuth connection status is unavailable.'); } return this.getOAuthConnectionStatus(this.toInterface()); } toInterface() { return { activity: this.activity, api: this.api, appGraph: this.appGraph, userGraph: this.userGraph, appId: this.appId, log: this.log, ref: this.ref, storage: this.storage, state: this.state, stream: this.stream, files: this.files, plan: this.plan.bind(this), isSignedIn: this.isSignedIn, connectionName: this.connectionName, userToken: this.userToken, next: this.next.bind(this), reply: this.reply.bind(this), quote: this.quote.bind(this), send: this.send.bind(this), signin: this.signin.bind(this), signout: this.signout.bind(this), getConnectionStatus: this.getConnectionStatus.bind(this), }; } isIncomingTargeted() { return this.activity.recipient?.isTargeted === true; } shouldOutboundBeAutoTargeted(params, conversationRef) { if (params.type !== 'message') { return false; } if (!this.isIncomingTargeted()) { return false; } if (!this.isSameConversation(conversationRef)) { return false; } return !params.id && !params.recipient; } /** * Finds the chat or channel and reply chain (thread) a response to the inbound activity is posted to. * * In a channel a response to a message always goes to the reply chain of the inbound message: `channelData.replyChain.id` is the root message of the thread, and it equals the inbound message's own id for a top-level post. * The destination is the channel, `channelData.channel.id`. * Other inbound activities in a channel are not placed. * * In a chat see `isInsideThread` for which activities are inside a thread. * The destination is the chat, `channelData.chat.id`. * When the inbound activity is on a top-level message the response is sent as a top-level message. * * Placement depends only on the payload: without `channelData.replyChain.id` and the chat or channel id the activity is not placed. * Returns undefined when an id is missing or blank, so the send goes to the inbound conversation. */ getInboundReplyChainPlacement() { const channelData = this.activity.channelData; const replyChainId = channelData?.replyChain?.id; if (!isPresent(replyChainId)) { return undefined; } if (this.activity.conversation?.conversationType === 'channel') { const channelId = channelData?.channel?.id; return this.activity.type === 'message' && isPresent(channelId) ? { chatOrChannelId: channelId, replyChainId, inChannel: true } : undefined; } const chatId = channelData?.chat?.id; return isPresent(chatId) && this.isInsideThread(replyChainId) ? { chatOrChannelId: chatId, replyChainId, inChannel: false } : undefined; } /** * Whether the inbound activity in a chat is inside a thread: `channelData.replyChain.id` differs from the id of the message the activity acts on. * The message it acts on is: * - `activity.id` for a message, an edit and a delete. * - `replyToId` (the reacted message) for a reaction and for a message sent by a card button (a message with `channelData.source`). * - `value.messagePayload.id` for a message action, and `replyToId` (the message the user acted on, for example a card or a message with feedback buttons) for any other invoke. * * An invoke that is not tied to a message (for example a compose-box extension or a `task/fetch` from a bot command) is inside a thread when `channelData.replyChain.id` is present. * A `replyToId` or `messagePayload.id` that is not a plain number is never inside a thread, and neither are other inbound activities. * A `source` without a non-empty string `name` and a `replyToId` that is null count as absent. */ isInsideThread(replyChainId) { const activity = this.activity; const isOtherPlainId = (id) => !!id && /^\d+$/.test(id) && !/^0+$/.test(id) && id !== replyChainId; switch (activity.type) { case 'message': return hasSource(activity.channelData) ? isOtherPlainId(activity.replyToId) : activity.id !== replyChainId; case 'messageUpdate': case 'messageDelete': return activity.id !== replyChainId; case 'messageReaction': return isOtherPlainId(activity.replyToId); case 'invoke': { const payloadId = typeof activity.value === 'object' && activity.value !== null && 'messagePayload' in activity.value ? activity.value.messagePayload?.id : undefined; const messageId = (typeof payloadId === 'string' ? payloadId : undefined) ?? activity.replyToId; return messageId === undefined || messageId === null ? true : isOtherPlainId(messageId); } default: return false; } } getChatReplyChainPlacement(params, conversationRef) { if (params.type !== 'message' || params.id || !this.isSameConversation(conversationRef)) { return undefined; } return this.getInboundReplyChainPlacement(); } isSameConversation(conversationRef) { return !conversationRef || conversationRef.conversation?.id === this.ref.conversation?.id; } applyTargetedRecipient(params) { params.recipient = { ...this.activity.from, isTargeted: true, }; } isTargetedOutbound(params) { return params.type === 'message' && params.recipient?.isTargeted === true; } stripQuotedReplyMetadata(params) { if (params.entities) { params.entities = params.entities.filter((e) => e.type !== 'quotedReply'); } if (params.text) { params.text = params.text.replace(`<quoted messageId="${this.activity.id}"/>`, '').trim(); } } addTargetedMessageInfo(params) { if (params.entities?.some((e) => e.type === 'targetedMessageInfo')) { return; } if (!params.entities) { params.entities = []; } params.entities.push({ type: 'targetedMessageInfo', messageId: this.activity.id, }); } } exports.ActivityContext = ActivityContext; /** * Whether a response to the inbound activity is automatically quoted. * That happens only when the inbound activity id is a plain numeric message id that is not all zeros. * Other ids, such as the service-generated `f:<guid>` ids on invokes and card button messages, are never quoted automatically. * The check looks only at the shape of the id and does not verify that the message exists. */ function isQuotableActivity(activity) { return !!activity.id && /^\d+$/.test(activity.id) && !/^0+$/.test(activity.id); } // An absent, null or nameless source means the activity was not sent by a card or a message action. function hasSource(channelData) { const name = channelData?.source?.name; return typeof name === 'string' && name !== ''; } // A blank id is treated like a missing one. function isPresent(id) { return typeof id === 'string' && id.trim() !== ''; } // Names of prototype methods (and getters) on ActivityContext that must not be // shadowed by instance properties from external context. Computed once from the // prototype chain at module load, so any method or accessor added to the class // in the future is protected automatically with no maintenance. const PROTECTED_METHOD_NAMES = (() => { const names = new Set(); let proto = ActivityContext.prototype; while (proto && proto !== Object.prototype) { for (const name of Object.getOwnPropertyNames(proto)) { if (name === 'constructor') continue; const descriptor = Object.getOwnPropertyDescriptor(proto, name); if (!descriptor) continue; if (typeof descriptor.value === 'function' || typeof descriptor.get === 'function') { names.add(name); } } proto = Object.getPrototypeOf(proto); } return names; })(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aXZpdHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29udGV4dHMvYWN0aXZpdHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsb0RBZ0I4QjtBQUs5Qiw0REFBd0Q7QUFFeEQsb0NBQWdFO0FBQ2hFLGtDQUErQjtBQXdVL0IsTUFBYSxlQUFlO0lBRTFCLEtBQUssQ0FBVTtJQUNmLFFBQVEsQ0FBSztJQUNiLEdBQUcsQ0FBeUI7SUFDNUIsR0FBRyxDQUFXO0lBQ2QsR0FBRyxDQUFhO0lBQ2hCLFFBQVEsQ0FBZTtJQUN2QixTQUFTLENBQWU7SUFDeEI7O09BRUc7SUFDSCxPQUFPLENBQVk7SUFDbkIsS0FBSyxDQUFzQjtJQUMzQixNQUFNLENBQWE7SUFDbkIsS0FBSyxDQUFrQjtJQUN2QixVQUFVLENBQVc7SUFDckIsY0FBYyxDQUFTO0lBQ3ZCLElBQUksQ0FFMEQ7SUFHdEQsY0FBYyxDQUFrQjtJQUNoQyxLQUFLLENBQVE7SUFDSix1QkFBdUIsQ0FBOEQ7SUFDckYsc0JBQXNCLENBQTZEO0lBQ25GLHdCQUF3QixDQUErRDtJQUV4RyxZQUFZLEtBQW9FO1FBQzlFLG9GQUFvRjtRQUNwRixNQUFNLEVBQ0osY0FBYyxFQUNkLElBQUksRUFDSix1QkFBdUIsRUFDdkIsc0JBQXNCLEVBQ3RCLHdCQUF3QixFQUN4QixlQUFlLEVBQ2YscUJBQXFCLEVBQ3JCLEdBQUcsSUFBSSxFQUNSLEdBQUcsS0FBSyxDQUFDO1FBRVYsdUVBQXVFO1FBQ3ZFLHVFQUF1RTtRQUN2RSw0RUFBNEU7UUFDNUUscUVBQXFFO1FBQ3JFLHlDQUF5QztRQUN6QyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ3JDLElBQUksQ0FBQyxRQUFRLEdBQUcsMkJBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3RELENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxRQUFR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