@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
449 lines • 31.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.App = void 0;
const axios_1 = require("axios");
const teams_api_1 = require("@microsoft/teams.api");
const events_1 = require("@microsoft/teams.common/events");
const http = __importStar(require("@microsoft/teams.common/http"));
const logging_1 = require("@microsoft/teams.common/logging");
const storage_1 = require("@microsoft/teams.common/storage");
const package_json_1 = __importDefault(require("../package.json"));
const api_1 = require("./api");
const app_embed_1 = require("./app.embed");
const app_events_1 = require("./app.events");
const app_oauth_1 = require("./app.oauth");
const app_plugins_1 = require("./app.plugins");
const app_process_1 = require("./app.process");
const app_routing_1 = require("./app.routing");
const container_1 = require("./container");
const middleware = __importStar(require("./middleware"));
const oauth_1 = require("./oauth");
const plugins_1 = require("./plugins");
const router_1 = require("./router");
/**
* The orchestrator for receiving/sending activities
*/
class App {
options;
api;
graph;
log;
http;
client;
storage;
credentials;
entraTokenValidator;
/**
* the apps id
*/
get id() {
return this.tokens.bot?.appId || this.tokens.graph?.appId;
}
/**
* the apps name
*/
get name() {
return this.tokens.bot?.appDisplayName || this.tokens.graph?.appDisplayName;
}
get oauth() {
return {
...oauth_1.DEFAULT_OAUTH_SETTINGS,
...this.options.oauth,
};
}
/**
* the apps manifest
*/
get manifest() {
return {
id: this.id,
name: {
short: this.name || '??',
full: this.name || '??',
...this._manifest.name,
},
bots: [
{
botId: this.id || '??',
scopes: ['personal'],
},
],
webApplicationInfo: {
id: this.credentials?.clientId || '??',
resource: `api://\${{BOT_DOMAIN}}/${this.credentials?.clientId || '??'}`,
...this._manifest.webApplicationInfo,
},
...this._manifest,
};
}
_manifest;
/**
* the apps auth tokens
*/
get tokens() {
return this._tokens;
}
_tokens = {};
container = new container_1.Container();
plugins = [];
router = new router_1.Router();
tenantTokens = new storage_1.LocalStorage({}, { max: 20000 });
events = new events_1.EventEmitter();
startedAt;
port;
_userAgent = `teams.ts[apps]/${package_json_1.default.version}`;
constructor(options = {}) {
this.options = options;
this.log = this.options.logger || new logging_1.ConsoleLogger('@teams/app');
this.storage = this.options.storage || new storage_1.LocalStorage();
this._manifest = this.options.manifest || {};
if (!options.client) {
this.client = new http.Client({
headers: {
'User-Agent': this._userAgent,
},
});
}
else if (typeof options.client === 'function') {
this.client = options.client().clone({
headers: {
'User-Agent': this._userAgent,
},
});
}
else if ('request' in options.client) {
this.client = options.client.clone({
headers: {
'User-Agent': this._userAgent,
},
});
}
else {
this.client = new http.Client({
...options.client,
headers: {
...options.client.headers,
'User-Agent': this._userAgent,
},
});
}
this.api = new api_1.ApiClient('https://smba.trafficmanager.net/teams', this.client.clone({ token: () => this._tokens.bot }));
this.graph = new api_1.GraphClient(this.client.clone({ token: () => this._tokens.graph }));
// initialize credentials
const clientId = this.options.clientId || process.env.CLIENT_ID;
const clientSecret = ('clientSecret' in this.options
? this.options.clientSecret
: undefined) || process.env.CLIENT_SECRET;
const tenantId = ('tenantId' in this.options ? this.options.tenantId : undefined) ||
process.env.TENANT_ID;
const token = 'token' in this.options ? this.options.token : undefined;
if (clientId && clientSecret) {
this.credentials = {
clientId,
clientSecret,
tenantId,
};
}
if (clientId && token) {
this.credentials = {
clientId,
tenantId,
token,
};
}
if (clientId) {
this.entraTokenValidator = middleware.createEntraTokenValidator(tenantId || 'common', clientId, { logger: this.log, });
}
// add/validate plugins
const plugins = this.options.plugins || [];
let httpPlugin = plugins.find((p) => {
const meta = (0, app_plugins_1.getMetadata)(p);
return meta.name === 'http';
});
if (!httpPlugin) {
httpPlugin = new plugins_1.HttpPlugin(undefined, { skipAuth: this.options.skipAuth });
// Casting to any here because a default HttpPlugin is not assignable to TPlugin
// without a silly level of indirection.
plugins.unshift(httpPlugin);
}
else if (this.options.skipAuth) {
this.log.warn('skipAuth option has no effect when a custom HTTP plugin is provided. Configure authentication on the plugin directly.');
}
this.http = httpPlugin;
// add injectable items to container
this.container.register('id', { useValue: this.id });
this.container.register('name', { useValue: this.name });
this.container.register('manifest', { useValue: this.manifest });
this.container.register('credentials', { useValue: this.credentials });
this.container.register('botToken', { useValue: () => this.tokens.bot });
this.container.register('graphToken', {
useValue: () => this.tokens.graph,
});
this.container.register('ILogger', { useValue: this.log });
this.container.register('IStorage', { useValue: this.storage });
this.container.register(this.client.constructor.name, {
useFactory: () => this.client,
});
for (const plugin of plugins) {
this.plugin(plugin);
}
if (this.options.activity?.mentions?.stripText) {
const options = this.options.activity?.mentions?.stripText;
this.use(middleware.stripMentionsText(typeof options === 'boolean' ? {} : options));
}
// default event handlers
this.router.register({
name: 'signin.token-exchange',
type: 'system',
select: activity => activity.type === 'invoke' && activity.name === 'signin/tokenExchange',
callback: ctx => this.onTokenExchange(ctx),
});
this.router.register({
name: 'signin.verify-state',
type: 'system',
select: activity => activity.type === 'invoke' && activity.name === 'signin/verifyState',
callback: ctx => this.onVerifyState(ctx),
});
this.event('error', ({ error }) => {
this.log.error(error.message);
if (error instanceof axios_1.AxiosError) {
this.log.error(error.request.path);
this.log.error(error.response?.data);
}
});
}
/**
* start the app
* @param port port to listen on
*/
async start(port) {
this.port = port || process.env.PORT || 3978;
try {
await this.refreshTokens(true);
// initialize plugins
for (const plugin of this.plugins) {
// inject dependencies
this.inject(plugin);
if (plugin.onInit) {
plugin.onInit();
}
}
// start plugins
for (const plugin of this.plugins) {
if (plugin.onStart) {
await plugin.onStart({ port: this.port });
}
}
this.events.emit('start', this.log);
this.startedAt = new Date();
}
catch (error) {
this.onError({ error });
}
}
/**
* stop the app
*/
async stop() {
try {
for (const plugin of this.plugins) {
if (plugin.onStop) {
await plugin.onStop();
}
}
}
catch (error) {
this.onError({ error });
}
}
/**
* send an activity proactively
* @param conversationId the conversation to send to
* @param activity the activity to send
*/
async send(conversationId, activity) {
if (!this.id || !this.name) {
throw new Error('app not started');
}
const ref = {
channelId: 'msteams',
serviceUrl: this.api.serviceUrl,
bot: {
id: this.id,
name: this.name,
role: 'bot',
},
conversation: {
id: conversationId,
conversationType: 'personal',
},
};
const res = await this.http.send((0, teams_api_1.toActivityParams)(activity), ref);
return res;
}
/**
* subscribe to an event
* @param name event to subscribe to
* @param cb callback to invoke
*/
on = app_routing_1.on; // eslint-disable-line @typescript-eslint/member-ordering
/**
* subscribe to a message event for a specific pattern
* @param pattern pattern to match against message text
* @param cb callback to invoke
*/
message = app_routing_1.message; // eslint-disable-line @typescript-eslint/member-ordering
/**
* register a middleware
* @param cb callback to invoke
*/
use = app_routing_1.use; // eslint-disable-line @typescript-eslint/member-ordering
/**
* subscribe to an event
* @param name the event to subscribe to
* @param cb the callback to invoke
*/
event = app_events_1.event; // eslint-disable-line @typescript-eslint/member-ordering
/**
* add a plugin
* @param plugin plugin to add
*/
plugin = app_plugins_1.plugin; // eslint-disable-line @typescript-eslint/member-ordering
/**
* get a plugin
*/
getPlugin = app_plugins_1.getPlugin; // eslint-disable-line @typescript-eslint/member-ordering
/**
* add/update a function that can be called remotely
* @param name The unique function name
* @param cb The callback to handle the function
*/
function = app_embed_1.func; // eslint-disable-line @typescript-eslint/member-ordering
/**
* add/update a static tab.
* the tab will be hosted at
* `http://localhost:{{PORT}}/tabs/{{name}}` or `https://{{BOT_DOMAIN}}/tabs/{{name}}`
* @remark scopes default to `personal`
* @param name A unique identifier for the entity which the tab displays.
* @param path The path to the web `dist` folder.
*/
tab = app_embed_1.tab; // eslint-disable-line @typescript-eslint/member-ordering
/**
* add a configurable tab
* @remark scopes defaults to `team`
* @param url The url to use when configuring the tab.
*/
configTab = app_embed_1.configTab; // eslint-disable-line @typescript-eslint/member-ordering
/**
* activity handler called when an inbound activity is received
* @param sender the plugin to use for sending activities
* @param event the received activity event
*/
process = app_process_1.$process; // eslint-disable-line @typescript-eslint/member-ordering
///
/// OAuth
///
onTokenExchange = app_oauth_1.onTokenExchange; // eslint-disable-line @typescript-eslint/member-ordering
onVerifyState = app_oauth_1.onVerifyState; // eslint-disable-line @typescript-eslint/member-ordering
///
/// Events
///
inject = app_plugins_1.inject; // eslint-disable-line @typescript-eslint/member-ordering
onError = app_events_1.onError; // eslint-disable-line @typescript-eslint/member-ordering
onActivity = app_events_1.onActivity; // eslint-disable-line @typescript-eslint/member-ordering
onActivitySent = app_events_1.onActivitySent; // eslint-disable-line @typescript-eslint/member-ordering
onActivityResponse = app_events_1.onActivityResponse; // eslint-disable-line @typescript-eslint/member-ordering
///
/// Token
///
/**
* Refresh the tokens for the app
*/
async refreshTokens(force = false) {
return Promise.all([
this.refreshBotToken(force),
this.refreshGraphToken(force),
]);
}
async refreshBotToken(force = false) {
if (!this.credentials)
return;
if (!this.tokens.bot?.isExpired() && !force)
return;
if (this.tokens.bot) {
this.log.debug('refreshing bot token');
}
const botResponse = await this.api.bots.token.get(this.credentials);
this._tokens.bot = new teams_api_1.JsonWebToken(botResponse.access_token);
}
async refreshGraphToken(force = false) {
if (!this.credentials)
return;
if (!this.tokens.graph?.isExpired() && !force)
return;
if (this.tokens.graph) {
this.log.debug('refreshing graph token');
}
const graphResponse = await this.api.bots.token.getGraph(this.credentials);
this._tokens.graph = new teams_api_1.JsonWebToken(graphResponse.access_token);
}
async getUserToken(channelId, userId) {
const res = await this.api.users.token.get({
channelId,
userId,
connectionName: this.oauth.defaultConnectionName,
});
return res.token;
}
async getOrRefreshTenantToken(tenantId) {
let appToken = this.tenantTokens.get(tenantId);
if (this.credentials && !this.tenantTokens.get(tenantId)) {
const { access_token } = await this.api.bots.token.getGraph({
...this.credentials,
tenantId: tenantId,
});
this.log.debug(`refreshing tenant token for ${tenantId}`);
appToken = access_token;
this.tenantTokens.set(tenantId, access_token);
}
return appToken;
}
}
exports.App = App;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2FwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxpQ0FBbUM7QUFFbkMsb0RBUzhCO0FBQzlCLDJEQUE4RDtBQUM5RCxtRUFBcUQ7QUFDckQsNkRBQXlFO0FBQ3pFLDZEQUF5RTtBQUV6RSxtRUFBa0M7QUFFbEMsK0JBQStDO0FBRS9DLDJDQUFtRDtBQUNuRCw2Q0FNc0I7QUFDdEIsMkNBR3FCO0FBQ3JCLCtDQUF1RTtBQUN2RSwrQ0FBeUM7QUFDekMsK0NBQWlEO0FBQ2pELDJDQUF3QztBQUV4Qyx5REFBMkM7QUFDM0MsbUNBQWdFO0FBQ2hFLHVDQUF1QztBQUN2QyxxQ0FBa0M7QUF1RWxDOztHQUVHO0FBQ0gsTUFBYSxHQUFHO0lBNkVPO0lBNUVaLEdBQUcsQ0FBWTtJQUNmLEtBQUssQ0FBYztJQUNuQixHQUFHLENBQVU7SUFDYixJQUFJLENBQWE7SUFDakIsTUFBTSxDQUFjO0lBQ3BCLE9BQU8sQ0FBVztJQUNsQixXQUFXLENBQWU7SUFDMUIsbUJBQW1CLENBQTJCO0lBRXZEOztPQUVHO0lBQ0gsSUFBSSxFQUFFO1FBQ0osT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDO0lBQzVELENBQUM7SUFFRDs7T0FFRztJQUNILElBQUksSUFBSTtRQUNOLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLEVBQUUsY0FBYyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLGNBQWMsQ0FBQztJQUM5RSxDQUFDO0lBRUQsSUFBSSxLQUFLO1FBQ1AsT0FBTztZQUNMLEdBQUcsOEJBQXNCO1lBQ3pCLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLO1NBQ3RCLENBQUM7SUFDSixDQUFDO0lB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