messaging-api-messenger
Version:
Messaging API client for Messenger
1,627 lines (1,533 loc) • 115 kB
text/typescript
import crypto from 'crypto';
import fs from 'fs';
import querystring from 'querystring';
import url from 'url';
import AxiosError from 'axios-error';
import FormData from 'form-data';
import appendQuery from 'append-query';
import axios, {
AxiosInstance,
AxiosTransformer,
AxiosError as BaseAxiosError,
} from 'axios';
import get from 'lodash/get';
import invariant from 'ts-invariant';
import isPlainObject from 'lodash/isPlainObject';
import omit from 'lodash/omit';
import warning from 'warning';
import {
OnRequestFunction,
camelcaseKeysDeep,
createRequestInterceptor,
snakecaseKeysDeep,
} from 'messaging-api-common';
import Messenger from './Messenger';
import * as MessengerTypes from './MessengerTypes';
function extractVersion(version: string): string {
if (version.startsWith('v')) {
return version.slice(1);
}
return version;
}
function handleError(
err: BaseAxiosError<{
error: {
code: number;
type: string;
message: string;
};
}>
): never {
if (err.response && err.response.data) {
const error = get(err, 'response.data.error');
if (error) {
const msg = `Messenger API - ${error.code} ${error.type} ${error.message}`;
throw new AxiosError(msg, err);
}
}
throw new AxiosError(err.message, err);
}
export default class MessengerClient {
/**
* @deprecated Use `new MessengerClient(...)` instead.
*/
static connect(config: MessengerTypes.ClientConfig): MessengerClient {
warning(
false,
'`MessengerClient.connect(...)` is deprecated. Use `new MessengerClient(...)` instead.'
);
return new MessengerClient(config);
}
/**
* The underlying axios instance.
*/
readonly axios: AxiosInstance;
/**
* The version of the Facebook Graph API.
*/
readonly version: string;
/**
* The access token used by the client.
*/
readonly accessToken: string;
/**
* The app secret used by the client.
*/
readonly appSecret?: string;
/**
* The app ID used by the client.
*/
readonly appId?: string;
/**
* The callback to be called when receiving requests.
*/
private onRequest?: OnRequestFunction;
constructor(config: MessengerTypes.ClientConfig) {
invariant(
typeof config !== 'string',
`MessengerClient: do not allow constructing client with ${config} string. Use object instead.`
);
this.accessToken = config.accessToken;
invariant(
!config.version || typeof config.version === 'string',
'Type of `version` must be string.'
);
this.appId = config.appId;
this.appSecret = config.appSecret;
this.version = extractVersion(config.version || '6.0');
this.onRequest = config.onRequest;
const { origin } = config;
let skipAppSecretProof;
if (typeof config.skipAppSecretProof === 'boolean') {
skipAppSecretProof = config.skipAppSecretProof;
} else {
skipAppSecretProof = this.appSecret == null;
}
this.axios = axios.create({
baseURL: `${origin || 'https://graph.facebook.com'}/v${this.version}/`,
headers: { 'Content-Type': 'application/json' },
transformRequest: [
// axios use any as type of the data in AxiosTransformer
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(data: any): any =>
data && isPlainObject(data) ? snakecaseKeysDeep(data) : data,
...(axios.defaults.transformRequest as AxiosTransformer[]),
],
// `transformResponse` allows changes to the response data to be made before
// it is passed to then/catch
transformResponse: [
...(axios.defaults.transformResponse as AxiosTransformer[]),
// axios use any as type of the data in AxiosTransformer
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(data: any): any =>
data && isPlainObject(data) ? camelcaseKeysDeep(data) : data,
],
});
this.axios.interceptors.request.use(
createRequestInterceptor({ onRequest: this.onRequest })
);
// add appsecret_proof to request
if (!skipAppSecretProof) {
invariant(
this.appSecret,
'Must provide appSecret when skipAppSecretProof is false'
);
const appSecret = this.appSecret as string;
this.axios.interceptors.request.use((requestConfig) => {
const isBatch =
requestConfig.url === '/' && Array.isArray(requestConfig.data.batch);
if (isBatch) {
// eslint-disable-next-line no-param-reassign
requestConfig.data.batch = requestConfig.data.batch.map(
(item: any) => {
const urlParts = url.parse(item.relativeUrl, true);
let accessToken = get(urlParts, 'query.access_token');
if (!accessToken && item.body) {
const entries = decodeURIComponent(item.body)
.split('&')
.map((pair) => pair.split('='));
const accessTokenEntry = entries.find(
([key]) => key === 'access_token'
);
if (accessTokenEntry) {
accessToken = accessTokenEntry[1];
}
}
if (accessToken) {
const appSecretProof = crypto
.createHmac('sha256', appSecret)
.update(accessToken, 'utf8')
.digest('hex');
return {
...item,
relativeUrl: appendQuery(item.relativeUrl, {
appsecret_proof: appSecretProof,
}),
};
}
return item;
}
);
}
const urlParts = url.parse(requestConfig.url || '', true);
const accessToken = get(
urlParts,
'query.access_token',
this.accessToken
);
const appSecretProof = crypto
.createHmac('sha256', appSecret)
.update(accessToken, 'utf8')
.digest('hex');
// eslint-disable-next-line no-param-reassign
requestConfig.url = appendQuery(requestConfig.url || '', {
appsecret_proof: appSecretProof,
});
return requestConfig;
});
}
}
/**
* Gets page info using Graph API.
*
* @returns Page info
*
* @see https://developers.facebook.com/docs/graph-api/reference/page/
*
* @example
*
* ```js
* await client.getPageInfo();
* // {
* // name: 'Bot Demo',
* // id: '1895382890692546',
* // }
* ```
*/
getPageInfo({
fields,
}: { fields?: string[] } = {}): Promise<MessengerTypes.PageInfo> {
return this.axios
.get('/me', {
params: {
access_token: this.accessToken,
fields: fields ? fields.join(',') : undefined,
},
})
.then((res) => res.data, handleError);
}
/**
* Gets token information.
*
* @returns Token information
*
* @see https://developers.facebook.com/docs/facebook-login/access-tokens/debugging-and-error-handling
*
* @example
*
* ```js
* await client.debugToken();
* // {
* // appId: '000000000000000',
* // application: 'Social Cafe',
* // expiresAt: 1352419328,
* // isValid: true,
* // issuedAt: 1347235328,
* // scopes: ['email', 'user_location'],
* // userId: 1207059,
* // }
* ```
*/
debugToken(): Promise<MessengerTypes.TokenInfo> {
invariant(this.appId, 'App ID is required to debug token');
invariant(this.appSecret, 'App Secret is required to debug token');
const accessToken = `${this.appId}|${this.appSecret}`;
return this.axios
.get(`/debug_token`, {
params: {
input_token: this.accessToken,
access_token: accessToken,
},
})
.then((res) => res.data.data, handleError);
}
/**
* Create new Webhooks subscriptions.
*
* @param subscription - Subscription parameters.
* @param subscription.accessToken - App access token.
* @param subscription.callbackUrl - The URL to receive the POST request when an update is triggered, and a GET request when attempting this publish operation.
* @param subscription.verifyToken - An arbitrary string that can be used to confirm to your server that the request is valid.
* @param subscription.fields - One or more of the set of valid fields in this object to subscribe to. Default Fields: `messages`, `messaging_postbacks`, `messaging_optins`, `messaging_referrals`, `messaging_handovers` and `messaging_policy_enforcement`.
* @param subscription.object - Indicates the object type that this subscription applies to. Defaults to `page`.
* @param subscription.includeValues - Indicates if change notifications should include the new values.
* @returns Success status
*
* @see https://developers.facebook.com/docs/graph-api/reference/app/subscriptions
*
* @example
*
* ```js
* await client.createSubscription({
* accessToken: APP_ACCESS_TOKEN,
* callbackUrl: 'https://mycallback.com',
* fields: ['messages', 'messaging_postbacks', 'messaging_referrals'],
* verifyToken: VERIFY_TOKEN,
* });
*
* // Or provide app id and app secret instead of app access token:
* client.createSubscription({
* accessToken: `${APP_ID}|${APP_SECRET}`,
* callbackUrl: 'https://mycallback.com',
* fields: ['messages', 'messaging_postbacks', 'messaging_referrals'],
* verifyToken: VERIFY_TOKEN,
* });
* ```
*/
createSubscription({
object = 'page',
callbackUrl,
fields = [
'messages',
'messaging_postbacks',
'messaging_optins',
'messaging_referrals',
'messaging_handovers',
'messaging_policy_enforcement',
],
includeValues,
verifyToken,
accessToken: appAccessToken,
}: {
object?: 'user' | 'page' | 'permissions' | 'payments';
callbackUrl: string;
fields?: string[];
includeValues?: boolean;
verifyToken: string;
accessToken: string;
}): Promise<{ success: boolean }> {
const { appId } = this;
invariant(appId, 'App ID is required to create subscription');
invariant(
this.appSecret || appAccessToken,
'App Secret or App Token is required to create subscription'
);
const accessToken = appAccessToken || `${appId}|${this.appSecret}`;
return this.axios
.post(`/${appId}/subscriptions?access_token=${accessToken}`, {
object,
callbackUrl,
fields: fields.join(','),
includeValues,
verifyToken,
})
.then((res) => res.data, handleError);
}
/**
* Gets the current Webhook subscriptions set up on your app.
*
* @param options - The other parameters.
* @param options.accessToken - App access token.
* @returns An array of subscriptions.
*
* @see https://developers.facebook.com/docs/graph-api/reference/app/subscriptions
*
* @example
*
* ```js
* await client.getSubscriptions({
* accessToken: APP_ACCESS_TOKEN,
* });
* // [{
* // object: 'page',
* // callbackUrl: 'https://www.example.com/callback'
* // fields: ['messages', 'messaging_postbacks', 'messaging_optins'],
* // active: true,
* // }]
*
* // Or provide app id and app secret instead of app access token:
* await client.getSubscriptions({
* accessToken: `${APP_ID}|${APP_SECRET}`,
* });
* ```
*/
getSubscriptions({
accessToken: appAccessToken,
}: {
accessToken?: string;
} = {}): Promise<MessengerTypes.MessengerSubscription[]> {
const { appId } = this;
invariant(appId, 'App ID is required to get subscriptions');
invariant(
this.appSecret || appAccessToken,
'App Secret or App Token is required to get subscriptions'
);
const accessToken = appAccessToken || `${appId}|${this.appSecret}`;
return this.axios
.get(`/${appId}/subscriptions?access_token=${accessToken}`)
.then((res) => res.data.data, handleError);
}
/**
* Get the current page subscription set up on your app.
*
* @param options - The other parameters.
* @param options.accessToken - App access token.
* @returns The current page subscription
*
* @see https://developers.facebook.com/docs/graph-api/reference/app/subscriptions
*
* @example
*
* ```js
* await client.getPageSubscription({
* accessToken: APP_ACCESS_TOKEN,
* });
*
* // Or provide app id and app secret instead of app access token:
* await client.getPageSubscription({
* accessToken: `${APP_ID}|${APP_SECRET}`,
* });
* ```
*/
getPageSubscription({
accessToken: appAccessToken,
}: {
accessToken?: string;
} = {}): Promise<MessengerTypes.MessengerSubscription> {
const { appId } = this;
invariant(appId, 'App ID is required to get subscription');
invariant(
this.appSecret || appAccessToken,
'App Secret or App Token is required to get subscription'
);
const accessToken = appAccessToken || `${appId}|${this.appSecret}`;
return this.getSubscriptions({
accessToken,
}).then(
(subscriptions: MessengerTypes.MessengerSubscription[]) =>
subscriptions.filter(
(subscription) => subscription.object === 'page'
)[0] || null
);
}
/**
* Programmatically check the feature submission status of page-level platform features
*
* @returns An array of all submitted feature submission requests. If no request has been submitted, the array will be empty.
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messaging-feature-review-api
*
* @example
*
* ```js
* await client.getMessagingFeatureReview();
* // [{
* // "feature": "subscription_messaging",
* // "status": "<pending|rejected|approved|limited>"
* // }]
*/
getMessagingFeatureReview(): Promise<
MessengerTypes.MessagingFeatureReview[]
> {
return this.axios
.get<{ data: MessengerTypes.MessagingFeatureReview[] }>(
`/me/messaging_feature_review?access_token=${this.accessToken}`
)
.then((res) => res.data.data, handleError);
}
/**
* Retrieves a person's profile.
*
* @param userId - Facebook page-scoped user ID.
* @param options - Other optional parameters.
* @param options.fields - Value must be among `id`, `name`, `first_name`, `last_name`, `profile_pic`, `locale`, `timezone` and `gender`, default with `id`, `name`, `first_name`, `last_name` and `profile_pic`.
* @returns Profile of the user.
*
* @see https://www.quora.com/How-connect-Facebook-user-id-to-sender-id-in-the-Facebook-messenger-platform
*
* @example
*
* ```js
* await client.getUserProfile(USER_ID);
* // {
* // id: '5566'
* // firstName: 'Johnathan',
* // lastName: 'Jackson',
* // profilePic: 'https://example.com/pic.png',
* // }
* ```
*/
getUserProfile(
userId: string,
{
fields = ['id', 'name', 'first_name', 'last_name', 'profile_pic'],
}: { fields?: MessengerTypes.UserProfileField[] } = {}
): Promise<MessengerTypes.User> {
return this.axios
.get<MessengerTypes.User>(
`/${userId}?fields=${fields.join(',')}&access_token=${this.accessToken}`
)
.then((res) => res.data, handleError);
}
/**
* Retrieves the current value of one or more Messenger Profile properties by name.
*
* @param fields - An array of Messenger profile properties to retrieve. Value must be among `account_linking_url`, `persistent_menu`, `get_started`, `greeting`, `ice_breakers` and `whitelisted_domains`.
* @returns The current value of the requested properties
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api
*
* @example
*
* ```js
* await client.getMessengerProfile(['get_started', 'persistent_menu']);
* // [
* // {
* // getStarted: {
* // payload: 'GET_STARTED',
* // },
* // },
* // {
* // persistentMenu: [
* // {
* // locale: 'default',
* // composerInputDisabled: true,
* // callToActions: [
* // {
* // type: 'postback',
* // title: 'Restart Conversation',
* // payload: 'RESTART',
* // },
* // ],
* // },
* // ],
* // },
* // ]
* ```
*/
getMessengerProfile(
fields: string[]
): Promise<MessengerTypes.MessengerProfile[]> {
return this.axios
.get<{ data: MessengerTypes.MessengerProfile[] }>(
`/me/messenger_profile?fields=${fields.join(',')}&access_token=${
this.accessToken
}`
)
.then((res) => res.data.data, handleError);
}
/**
* Sets the values of one or more Messenger Profile properties. Only properties set in the request body will be overwritten.
*
* @param profile - [Profile](https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api#profile_properties) object.
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api#post
*
* @example
*
* ```js
* await client.setMessengerProfile({
* getStarted: {
* payload: 'GET_STARTED',
* },
* persistentMenu: [
* {
* locale: 'default',
* composerInputDisabled: true,
* callToActions: [
* {
* type: 'postback',
* title: 'Restart Conversation',
* payload: 'RESTART',
* },
* ],
* },
* ],
* });
* ```
*/
setMessengerProfile(
profile: MessengerTypes.MessengerProfile
): Promise<MessengerTypes.MutationSuccessResponse> {
return this.axios
.post<MessengerTypes.MutationSuccessResponse>(
`/me/messenger_profile?access_token=${this.accessToken}`,
profile
)
.then((res) => res.data, handleError);
}
/**
* Deletes one or more Messenger Profile properties. Only properties specified in the fields array will be deleted.
*
* @param fields - An array of Messenger profile properties to delete. Value must be among `account_linking_url`, `persistent_menu`, `get_started`, `greeting`, `ice_breakers` and `whitelisted_domains`.
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api#delete
*
* @example
*
* ```js
* await client.deleteMessengerProfile(['get_started', 'persistent_menu']);
* ```
*/
deleteMessengerProfile(
fields: string[]
): Promise<MessengerTypes.MutationSuccessResponse> {
return this.axios
.delete<MessengerTypes.MutationSuccessResponse>(
`/me/messenger_profile?access_token=${this.accessToken}`,
{
data: {
fields,
},
}
)
.then((res) => res.data, handleError);
}
/**
* Retrieves the current value of get started button.
*
* @returns Config of get started button
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/get-started-button
*
* @example
*
* ```js
* await client.getGetStarted();
* // {
* // payload: 'GET_STARTED',
* // }
* ```
*/
getGetStarted(): Promise<{
payload: string;
} | null> {
return this.getMessengerProfile(['get_started']).then((res) =>
res[0]
? (res[0].getStarted as {
payload: string;
})
: null
);
}
/**
* Sets the values of get started button.
*
* @param payload - Payload sent back to your webhook in a `messaging_postbacks` event when the 'Get Started' button is tapped.
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/get-started-button
*
* @example
* *
* ```js
* await client.setGetStarted('GET_STARTED');
* ```
*/
setGetStarted(
payload: string
): Promise<MessengerTypes.MutationSuccessResponse> {
return this.setMessengerProfile({
getStarted: {
payload,
},
});
}
/**
* Deletes get started button.
*
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/get-started-button
*
* @example
*
* ```js
* await client.deleteGetStarted();
* ```
*/
deleteGetStarted(): Promise<MessengerTypes.MutationSuccessResponse> {
return this.deleteMessengerProfile(['get_started']);
}
/**
* Retrieves the current value of persistent menu.
*
* @returns Array of persistent menus.
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/persistent-menu
*
* @example
*
* ```js
* await client.getPersistentMenu();
* // [
* // {
* // locale: 'default',
* // composerInputDisabled: true,
* // callToActions: [
* // {
* // type: 'postback',
* // title: 'Restart Conversation',
* // payload: 'RESTART',
* // },
* // {
* // type: 'web_url',
* // title: 'Powered by ALOHA.AI, Yoctol',
* // url: 'https://www.yoctol.com/',
* // },
* // ],
* // },
* // ]
* ```
*/
getPersistentMenu(): Promise<MessengerTypes.PersistentMenu | null> {
return this.getMessengerProfile(['persistent_menu']).then((res) =>
res[0] ? (res[0].persistentMenu as MessengerTypes.PersistentMenu) : null
);
}
/**
* Sets the values of persistent menu.
*
* @param menu - Array of [menu](https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/persistent-menu#properties).
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/persistent-menu
*
* @example
*
* ```js
* await client.setPersistentMenu([
* {
* locale: 'default',
* callToActions: [
* {
* title: 'Play Again',
* type: 'postback',
* payload: 'RESTART',
* },
* {
* title: 'Explore',
* type: 'web_url',
* url: 'https://www.youtube.com/watch?v=v',
* webviewHeightRatio: 'tall',
* },
* {
* title: 'Powered by YOCTOL',
* type: 'web_url',
* url: 'https://www.yoctol.com/',
* webviewHeightRatio: 'tall',
* },
* ],
* },
* ]);
* ```
*
* @note You must set a get started button to use the persistent menu.
*/
setPersistentMenu(
menuItems: MessengerTypes.MenuItem[] | MessengerTypes.PersistentMenuItem[],
{
composerInputDisabled = false,
}: {
composerInputDisabled?: boolean;
} = {}
): Promise<MessengerTypes.MutationSuccessResponse> {
// locale is in type PersistentMenuItem
if (
menuItems.some(
(item: MessengerTypes.MenuItem | MessengerTypes.PersistentMenuItem) =>
'locale' in item && item.locale === 'default'
)
) {
return this.setMessengerProfile({
persistentMenu: menuItems as MessengerTypes.PersistentMenu,
});
}
// menuItems is in type MenuItem[]
return this.setMessengerProfile({
persistentMenu: [
{
locale: 'default',
composerInputDisabled,
callToActions: menuItems as MessengerTypes.MenuItem[],
},
],
});
}
/**
* Deletes persistent menu.
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/persistent-menu
*
* @example
*
* ```js
* await client.deletePersistentMenu();
* ```
*/
deletePersistentMenu(): Promise<MessengerTypes.MutationSuccessResponse> {
return this.deleteMessengerProfile(['persistent_menu']);
}
/**
* User Level Persistent Menu
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages/persistent-menu#user_level_menu
*
* @example
*
* ```js
* ```
*/
getUserPersistentMenu(
userId: string
): Promise<MessengerTypes.PersistentMenu | null> {
return this.axios
.get(
`/me/custom_user_settings?psid=${userId}&access_token=${this.accessToken}`
)
.then(
(res) =>
res.data.data[0]
? (res.data.data[0]
.userLevelPersistentMenu as MessengerTypes.PersistentMenu)
: null,
handleError
);
}
/**
*
* @example
*
* ```js
* ```
*/
setUserPersistentMenu(
userId: string,
menuItems: MessengerTypes.MenuItem[] | MessengerTypes.PersistentMenuItem[],
{
composerInputDisabled = false,
}: {
composerInputDisabled?: boolean;
} = {}
): Promise<MessengerTypes.MutationSuccessResponse> {
// locale is in type PersistentMenuItem
if (
menuItems.some(
(item: MessengerTypes.MenuItem | MessengerTypes.PersistentMenuItem) =>
'locale' in item && item.locale === 'default'
)
) {
return this.axios
.post<MessengerTypes.MutationSuccessResponse>(
`/me/custom_user_settings?access_token=${this.accessToken}`,
{
psid: userId,
persistentMenu: menuItems as MessengerTypes.PersistentMenu,
}
)
.then((res) => res.data, handleError);
}
// menuItems is in type MenuItem[]
return this.axios
.post(`/me/custom_user_settings?access_token=${this.accessToken}`, {
psid: userId,
persistentMenu: [
{
locale: 'default',
composerInputDisabled,
callToActions: menuItems as MessengerTypes.MenuItem[],
},
],
})
.then((res) => res.data, handleError);
}
/**
*
* @example
*
* ```js
* ```
*/
deleteUserPersistentMenu(
userId: string
): Promise<MessengerTypes.MutationSuccessResponse> {
return this.axios
.delete(
`/me/custom_user_settings?psid=${userId}¶ms=[%22persistent_menu%22]&access_token=${this.accessToken}`
)
.then((res) => res.data, handleError);
}
/**
* Retrieves the current value of greeting text.
*
* @returns Array of greeting configs
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/greeting
*
* @example
*
* ```js
* await client.getGreeting();
* // [
* // {
* // locale: 'default',
* // text: 'Hello!',
* // },
* // ]
* ```
*/
getGreeting(): Promise<MessengerTypes.GreetingConfig[] | null> {
return this.getMessengerProfile(['greeting']).then((res) =>
res[0] ? (res[0].greeting as MessengerTypes.GreetingConfig[]) : null
);
}
/**
* Sets the values of greeting text.
*
* @param greeting - Array of [greeting](https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/greeting#properties).
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/greeting
*
* @example
*
* ```js
* await client.setGreeting([
* {
* locale: 'default',
* text: 'Hello!',
* },
* ]);
* ```
*/
setGreeting(
greeting: string | MessengerTypes.GreetingConfig[]
): Promise<MessengerTypes.MutationSuccessResponse> {
if (typeof greeting === 'string') {
return this.setMessengerProfile({
greeting: [
{
locale: 'default',
text: greeting,
},
],
});
}
return this.setMessengerProfile({
greeting,
});
}
/**
* Deletes greeting text.
*
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/greeting
*
* @example
*
* ```js
* await client.deleteGreeting();
* ```
*/
deleteGreeting(): Promise<MessengerTypes.MutationSuccessResponse> {
return this.deleteMessengerProfile(['greeting']);
}
/**
* Retrieves the current value of ice breakers.
*
* @returns Array of ice breakers.
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/ice-breakers
*
* @example
*
* ```js
* await client.getIceBreakers()
* // [
* // {
* // "question": "<QUESTION>",
* // "payload": "<PAYLOAD>",
* // },
* // {
* // "question": "<QUESTION>",
* // "payload": "<PAYLOAD>",
* // }
* // ]
* ```
*/
getIceBreakers(): Promise<MessengerTypes.IceBreaker[] | null> {
return this.getMessengerProfile(['ice_breakers']).then((res) =>
res[0] ? (res[0].iceBreakers as MessengerTypes.IceBreaker[]) : null
);
}
/**
* Sets the values of ice breakers.
*
* @param iceBreakers - Array of ice breakers.
* @returns Success status
*
* @example
*
* ```js
* await client.setIceBreakers([
* {
* question: '<QUESTION>',
* payload: '<PAYLOAD>',
* },
* {
* question: '<QUESTION>',
* payload: '<PAYLOAD>',
* }
* ]);
* ```
*/
setIceBreakers(
iceBreakers: MessengerTypes.IceBreaker[]
): Promise<MessengerTypes.MutationSuccessResponse> {
return this.setMessengerProfile({
iceBreakers,
});
}
/**
* Deletes ice breakers.
*
* @returns Success status
*
* @example
*
* ```js
* await client.deleteIceBreakers();
* ```
*/
deleteIceBreakers(): Promise<MessengerTypes.MutationSuccessResponse> {
return this.deleteMessengerProfile(['ice_breakers']);
}
/**
* Retrieves the current value of whitelisted domains.
*
* @returns Array of whitelisted domains.
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/domain-whitelisting
*
* @example
*
* ```js
* await client.getWhitelistedDomains();
* // ['http://www.example.com/']
* ```
*/
getWhitelistedDomains(): Promise<string[] | null> {
return this.getMessengerProfile(['whitelisted_domains']).then((res) =>
res[0] ? (res[0].whitelistedDomains as string[]) : null
);
}
/**
* Sets the values of whitelisted domains.
*
* @param whitelistedDomains - Array of [whitelisted_domain](https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/domain-whitelisting#properties).
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/domain-whitelisting
*
* @example
*
* ```js
* await client.setWhitelistedDomains(['www.example.com']);
* ```
*/
setWhitelistedDomains(
whitelistedDomains: string[]
): Promise<MessengerTypes.MutationSuccessResponse> {
return this.setMessengerProfile({
whitelistedDomains,
});
}
/**
* Deletes whitelisted domains.
*
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/domain-whitelisting
*
* @example
*
* ```js
* await client.deleteWhitelistedDomains();
* ```
*/
deleteWhitelistedDomains(): Promise<MessengerTypes.MutationSuccessResponse> {
return this.deleteMessengerProfile(['whitelisted_domains']);
}
/**
* Retrieves the current value of account linking URL.
*
* @returns Account linking URL
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/account-linking-url
*
* @example
*
* ```js
* await client.getAccountLinkingURL();
* // 'https://www.example.com/oauth?response_type=code&client_id=1234567890&scope=basic'
* ```
*/
getAccountLinkingURL(): Promise<string | null> {
return this.getMessengerProfile(['account_linking_url']).then((res) =>
res[0] ? (res[0].accountLinkingUrl as string) : null
);
}
/**
* Sets the values of account linking URL.
*
* @param url - Account linking URL.
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/account-linking-url
*
* @example
*
* ```js
* await client.setAccountLinkingURL(
* 'https://www.example.com/oauth?response_type=code&client_id=1234567890&scope=basic'
* );
* ```
*/
setAccountLinkingURL(
accountLinkingUrl: string
): Promise<MessengerTypes.MutationSuccessResponse> {
return this.setMessengerProfile({
accountLinkingUrl,
});
}
/**
* Deletes account linking URL.
*
* @returns Success status
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/messenger-profile-api/account-linking-url
*
* @example
*
* ```js
* await client.deleteAccountLinkingURL();
* ```
*/
deleteAccountLinkingURL(): Promise<MessengerTypes.MutationSuccessResponse> {
return this.deleteMessengerProfile(['account_linking_url']);
}
/**
* Sends request raw body using the Send API.
*
* @param body - The raw body to be sent.
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/reference/send-api#request
*
* @example
*
* ```js
* await client.sendRawBody({
* recipient: {
* id: USER_ID,
* },
* message: {
* text: 'Hello!',
* },
* });
* // {
* // recipientId: '1254477777772919',
* // messageId: 'AG5Hz2Uq7tuwNEhXfYYKj8mJEM_QPpz5jdCK48PnKAjSdjfipqxqMvK8ma6AC8fplwlqLP_5cgXIbu7I3rBN0P'
* // }
* ```
*/
sendRawBody(
body: Record<string, any>
): Promise<MessengerTypes.SendMessageSuccessResponse> {
return this.axios
.post<MessengerTypes.SendMessageSuccessResponse>(
`/me/messages?access_token=${this.accessToken}`,
body
)
.then((res) => res.data, handleError);
}
/**
* Sends messages to the specified user using the Send API.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param message - A [message](https://developers.facebook.com/docs/messenger-platform/reference/send-api#message) object.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @returns An object includes recipientId and messageId.
*
* @example
*
* ```js
* await client.sendMessage(USER_ID, {
* text: 'Hello!',
* });
* ```
*
* You can specify [messaging type](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) using options. If `messagingType` and `tag` is not provided, `UPDATE` will be used as default messaging type.
*
* ```js
* await client.sendMessage(
* USER_ID,
* { text: 'Hello!' },
* { messagingType: 'RESPONSE' }
* );
* ```
*
* Available messaging types:
* - `UPDATE` as default
* - `RESPONSE` using `{ messagingType: 'RESPONSE' }` options
* - `MESSAGE_TAG` using `{ tag: 'ANY_TAG' }` options
*/
sendMessage(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
message: MessengerTypes.Message,
options: MessengerTypes.SendOption = {}
): Promise<MessengerTypes.SendMessageSuccessResponse> {
const recipient =
typeof psidOrRecipient === 'string'
? {
id: psidOrRecipient,
}
: psidOrRecipient;
let messagingType = 'UPDATE';
if (options.messagingType) {
messagingType = options.messagingType;
} else if (options.tag) {
messagingType = 'MESSAGE_TAG';
}
return this.sendRawBody({
messagingType,
recipient,
message: Messenger.createMessage(message, options),
...omit(options, 'quickReplies'),
});
}
/**
* Sends messages to the specified user using the Send API with form-data format.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param formdata - A FromData object
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @returns An object includes recipientId and messageId.
*
* @example
*/
sendMessageFormData(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
formdata: FormData,
options: MessengerTypes.SendOption = {}
): Promise<MessengerTypes.SendMessageSuccessResponse> {
const recipient =
typeof psidOrRecipient === 'string'
? {
id: psidOrRecipient,
}
: psidOrRecipient;
let messagingType = 'UPDATE';
if (options.messagingType) {
messagingType = options.messagingType;
} else if (options.tag) {
messagingType = 'MESSAGE_TAG';
}
formdata.append('messaging_type', messagingType);
formdata.append('recipient', JSON.stringify(snakecaseKeysDeep(recipient)));
return this.axios
.post<MessengerTypes.SendMessageSuccessResponse>(
`/me/messages?access_token=${this.accessToken}`,
formdata,
{
headers: formdata.getHeaders(),
}
)
.then((res) => res.data, handleError);
}
/**
* Sends attachment messages to the specified user using the Send API.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param attachment - The attachment of media or template to be sent.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages#sending_attachments
*
* @example
*
* ```js
* await client.sendAttachment(USER_ID, {
* type: 'image',
* payload: {
* url: 'https://example.com/pic.png',
* },
* });
* ```
*/
sendAttachment(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
attachment: MessengerTypes.Attachment,
options?: MessengerTypes.SendOption
): Promise<MessengerTypes.SendMessageSuccessResponse> {
return this.sendMessage(
psidOrRecipient,
Messenger.createAttachment(attachment, options),
options
);
}
/**
* Sends plain text messages to the specified user using the Send API.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param text - The text to be sent.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages#sending_text
*
* @example
*
* ```js
* await client.sendText(USER_ID, 'Hello!', { tag: 'CONFIRMED_EVENT_UPDATE' });
* ```
*/
sendText(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
text: string,
options?: MessengerTypes.SendOption
): Promise<MessengerTypes.SendMessageSuccessResponse> {
return this.sendMessage(
psidOrRecipient,
Messenger.createText(text, options),
options
);
}
/**
* Sends sounds to the specified user by uploading them or sharing a URL using the Send API.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param audio - The audio to be sent.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @param options.filename - Required when upload from a buffer.
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages#types
*
* @example
*
* ```js
* // Send audio using url string:
* await client.sendAudio(USER_ID, 'https://example.com/audio.mp3');
*
* // Use `AttachmentPayload` to send cached attachment:
* await client.sendAudio(USER_ID, { attachmentId: '55688' });
*
* // Use `ReadStream` created from the local file:
* const fs = require('fs');
* await client.sendAudio(USER_ID, fs.createReadStream('audio.mp3'));
*
* // Use `Buffer` to send attachment:
* await client.sendAudio(USER_ID, buffer, { filename: 'audio.mp3' });
* ```
*/
sendAudio(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
audio:
| string
| MessengerTypes.FileData
| MessengerTypes.MediaAttachmentPayload,
options?: MessengerTypes.SendOption
): Promise<MessengerTypes.SendMessageSuccessResponse> {
if (Buffer.isBuffer(audio) || audio instanceof fs.ReadStream) {
const message = Messenger.createAudioFormData(audio, options);
return this.sendMessageFormData(psidOrRecipient, message, options);
}
const message = Messenger.createAudio(audio, options);
return this.sendMessage(psidOrRecipient, message, options);
}
/**
* Sends images to the specified user by uploading them or sharing a URL using the Send API. Supported formats are jpg, png and gif.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param image - The image to be sent.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @param options.filename - Required when upload from a buffer.
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages#types
*
* @example
*
* ```js
* // Send image using url string:
* await client.sendImage(USER_ID, 'https://example.com/image.jpg');
*
* // Use `AttachmentPayload` to send cached attachment:
* await client.sendImage(USER_ID, { attachmentId: '55688' });
*
* // Use `ReadStream` created from the local file:
* const fs = require('fs');
* await client.sendImage(USER_ID, fs.createReadStream('image.jpg'));
*
* // Use `Buffer` to send attachment:
* await client.sendImage(USER_ID, buffer, { filename: 'image.jpg' });
* ```
*/
sendImage(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
image:
| string
| MessengerTypes.FileData
| MessengerTypes.MediaAttachmentPayload,
options?: MessengerTypes.SendOption
): Promise<MessengerTypes.SendMessageSuccessResponse> {
if (Buffer.isBuffer(image) || image instanceof fs.ReadStream) {
const message = Messenger.createImageFormData(image, options);
return this.sendMessageFormData(psidOrRecipient, message, options);
}
const message = Messenger.createImage(image, options);
return this.sendMessage(psidOrRecipient, message, options);
}
/**
* Sends videos to the specified user by uploading them or sharing a URL using the Send API.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param video - The video to be sent.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @param options.filename - Required when upload from a buffer.
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages#types
*
* @example
*
* ```js
* // Send video using url string:
* await client.sendVideo(USER_ID, 'https://example.com/video.mp4');
*
* // Use `AttachmentPayload` to send cached attachment:
* await client.sendVideo(USER_ID, { attachmentId: '55688' });
*
* // Use `ReadStream` created from the local file:
* const fs = require('fs');
* await client.sendVideo(USER_ID, fs.createReadStream('video.mp4'));
*
* // Use `Buffer` to send attachment:
* await client.sendVideo(USER_ID, buffer, { filename: 'video.mp4' });
* ```
*/
sendVideo(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
video:
| string
| MessengerTypes.FileData
| MessengerTypes.MediaAttachmentPayload,
options?: MessengerTypes.SendOption
): Promise<MessengerTypes.SendMessageSuccessResponse> {
if (Buffer.isBuffer(video) || video instanceof fs.ReadStream) {
const message = Messenger.createVideoFormData(video, options);
return this.sendMessageFormData(psidOrRecipient, message, options);
}
const message = Messenger.createVideo(video, options);
return this.sendMessage(psidOrRecipient, message, options);
}
/**
* Sends files to the specified user by uploading them or sharing a URL using the Send API.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param file - The file to be sent.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @param options.filename - Required when upload from a buffer.
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages#types
*
* @example
*
* ```js
* // Send file using url string:
* await client.sendFile(USER_ID, 'https://example.com/file.pdf');
*
* // Use `AttachmentPayload` to send cached attachment:
* await client.sendFile(USER_ID, { attachmentId: '55688' });
*
* // Use `ReadStream` created from the local file:
* const fs = require('fs');
* await client.sendFile(USER_ID, fs.createReadStream('file.pdf'));
*
* // Use `Buffer` to send attachment:
* await client.sendFile(USER_ID, buffer, { filename: 'file.pdf' });
* ```
*/
sendFile(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
file:
| string
| MessengerTypes.FileData
| MessengerTypes.MediaAttachmentPayload,
options?: MessengerTypes.SendOption
): Promise<MessengerTypes.SendMessageSuccessResponse> {
if (Buffer.isBuffer(file) || file instanceof fs.ReadStream) {
const message = Messenger.createFileFormData(file, options);
return this.sendMessageFormData(psidOrRecipient, message, options);
}
const message = Messenger.createFile(file, options);
return this.sendMessage(psidOrRecipient, message, options);
}
/**
* Sends structured template messages to the specified user using the Send API.
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param payload - The template object.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages/templates
*
* @example
*
* ```js
* await client.sendTemplate(USER_ID, {
* templateType: 'button',
* text: 'title',
* buttons: [
* {
* type: 'postback',
* title: 'Start Chatting',
* payload: 'USER_DEFINED_PAYLOAD',
* },
* ],
* });
* ```
*/
sendTemplate(
psidOrRecipient: MessengerTypes.PsidOrRecipient,
payload: MessengerTypes.TemplateAttachmentPayload,
options?: MessengerTypes.SendOption
): Promise<MessengerTypes.SendMessageSuccessResponse> {
return this.sendMessage(
psidOrRecipient,
Messenger.createTemplate(payload, options),
options
);
}
/**
* Sends button template messages to the specified user using the Send API.
*
* <img src="https://user-images.githubusercontent.com/3382565/37410664-0b80b080-27dc-11e8-8854-4408d6f32fdf.png" alt="sendButtonTemplate" width="250" />
*
* @param psidOrRecipient - A facebook page-scoped ID of the recipient or a recipient object
* @param title - Text that appears above the buttons.
* @param buttons - Array of [button](https://developers.facebook.com/docs/messenger-platform/send-messages/template/button#button). Set of 1-3 buttons that appear as call-to-actions.
* @param options - Other optional parameters. For example, [messaging types](https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types) or [tags](https://developers.facebook.com/docs/messenger-platform/message-tags).
* @returns An object includes recipientId and messageId.
*
* @see https://developers.facebook.com/docs/messenger-platform/send-messages/template/button
*
* @example
*
* ```js