@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
JavaScript
"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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