@grammyjs/conversations
Version:
Conversational interfaces for grammY
603 lines (602 loc) • 23.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConversationForm = void 0;
/**
* A container for form building utilities.
*
* Each method on this class represents a differnt type of form field which can
* validate context objects and extract data from it.
*/
class ConversationForm {
/** Constructs a new form based on wait and skip callbacks */
constructor(conversation) {
this.conversation = conversation;
}
async build(builder) {
const { validate, action, otherwise, next, ...waitOptions } = builder;
const ctx = await this.conversation.wait({
collationKey: "form",
...waitOptions,
});
const result = await validate(ctx);
if (result.ok) {
if (action !== undefined)
await action(ctx, result.value);
return result.value;
}
else {
if (otherwise !== undefined) {
if ("error" in result) {
const callback = otherwise;
const reason = result.error;
await callback(ctx, reason);
}
else {
const callback = otherwise;
await callback(ctx);
}
}
return await this.conversation.skip({ next });
}
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with text, and returns this text as string. Does not check
* for captions.
*
* Accepts an optional options object that lets you perform actions when
* text is received, when a non-text update is received, and more.
*
* @param options Optional options
*/
async text(options) {
return await this.build({
collationKey: "form-text",
...options,
validate: (ctx) => {
var _a, _b;
const text = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.text;
if (text === undefined)
return { ok: false };
return { ok: true, value: text };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with text that can be parsed to a number, and returns this
* number. Does not check captions.
*
* The conversion to number uses `parseFloat`.
*
* Accepts an optional options object that lets you perform actions when a
* number is received, when a non-number update is received, and more.
*
* @param options Optional options
*/
async number(options) {
return await this.build({
collationKey: "form-number",
...options,
validate: (ctx) => {
var _a, _b;
const text = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.text;
if (text === undefined)
return { ok: false };
const num = parseFloat(text);
if (isNaN(num))
return { ok: false };
return { ok: true, value: num };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with text that can be parsed to an integer, and returns this
* integer as a `number`. Does not check for captions.
*
* The conversion to number uses `parseInt`.
*
* Accepts an optional options object that lets you specify the radix to use
* as well as perform actions when a number is received, when a non-number
* update is received, and more.
*
* @param options Optional options
*/
async int(options) {
const { radix, ...opts } = options !== null && options !== void 0 ? options : {};
return await this.build({
collationKey: "form-int",
...opts,
validate: (ctx) => {
var _a, _b;
const text = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.text;
if (text === undefined)
return { ok: false };
const num = parseInt(text, radix);
if (isNaN(num))
return { ok: false };
return { ok: true, value: num };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with one of several predefined strings, and returns the
* actual text as string. Does not check captions.
*
* This is especially useful when working with custom keyboards.
*
* ```ts
* const keyboard = new Keyboard()
* .text("A").text("B")
* .text("C").text("D")
* .oneTime()
* await ctx.reply("A, B, C, or D?", { reply_markup: keyboard })
* const answer = await conversation.form.select(["A", "B", "C", "D"], {
* otherwise: ctx => ctx.reply("Please use one of the buttons!")
* })
* switch (answer) {
* case "A":
* case "B":
* case "C":
* case "D":
* // ...
* }
* ```
*
* Accepts an optional options object that lets you perform actions when
* text is received, when a non-text update is received, and more.
*
* @param entries A string array of accepted values
* @param options Optional options
*/
async select(entries, options) {
const e = entries;
return await this.build({
collationKey: "form-select",
...options,
validate: (ctx) => {
var _a, _b;
const text = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.text;
if (text === undefined)
return { ok: false };
if (!e.includes(text))
return { ok: false };
return { ok: true, value: text };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a given type of message entity, and returns this
* entity. The form field relies on `ctx.entities()` for data extraction, so
* both texts and captions are checked.
*
* Accepts an optional options object that lets you perform actions when
* text is received, when a non-text update is received, and more.
*
* @param type One or more types of message entities to accept
* @param options Optional options
*/
async entity(type, options) {
return await this.build({
collationKey: "form-entity",
...options,
validate: (ctx) => {
const entities = ctx.entities(type);
if (entities.length === 0)
return { ok: false };
return { ok: true, value: entities[0] };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with an animation, and returns the received animation
* object.
*
* Accepts an optional options object that lets you perform actions when an
* animation is received, when a non-animation update is received, and more.
*
* @param options Optional options
*/
async animation(options) {
return await this.build({
collationKey: "form-animation",
...options,
validate: (ctx) => {
var _a, _b;
const animation = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.animation;
if (animation === undefined)
return { ok: false };
return { ok: true, value: animation };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with an audio message, and returns the received audio
* object.
*
* Accepts an optional options object that lets you perform actions when an
* audio message is received, when a non-audio update is received, and more.
*
* @param options Optional options
*/
async audio(options) {
return await this.build({
collationKey: "form-audio",
...options,
validate: (ctx) => {
var _a, _b;
const audio = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.audio;
if (audio === undefined)
return { ok: false };
return { ok: true, value: audio };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a document message, and returns the received document
* object.
*
* Accepts an optional options object that lets you perform actions when a
* document message is received, when a non-document update is received, and
* more.
*
* @param options Optional options
*/
async document(options) {
return await this.build({
collationKey: "form-document",
...options,
validate: (ctx) => {
var _a, _b;
const document = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.document;
if (document === undefined)
return { ok: false };
return { ok: true, value: document };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with paid media, and returns the received paid media object.
*
* Accepts an optional options object that lets you perform actions when a
* paid media message is received, when a non-paid media update is received,
* and more.
*
* @param options Optional options
*/
async paidMedia(options) {
return await this.build({
collationKey: "form-paid_media",
...options,
validate: (ctx) => {
var _a, _b;
const paid_media = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.paid_media;
if (paid_media === undefined)
return { ok: false };
return { ok: true, value: paid_media };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a photo, and returns the received array of `PhotoSize`
* objects.
*
* Accepts an optional options object that lets you perform actions when a
* photo is received, when a non-photo update is received, and more.
*
* @param options Optional options
*/
async photo(options) {
return await this.build({
collationKey: "form-photo",
...options,
validate: (ctx) => {
var _a, _b;
const photo = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.photo;
if (photo === undefined)
return { ok: false };
return { ok: true, value: photo };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a sticker, and returns the received sticker object.
*
* Accepts an optional options object that lets you perform actions when a
* sticker is received, when a non-sticker update is received, and more.
*
* @param options Optional options
*/
async sticker(options) {
return await this.build({
collationKey: "form-sticker",
...options,
validate: (ctx) => {
var _a, _b;
const sticker = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.sticker;
if (sticker === undefined)
return { ok: false };
return { ok: true, value: sticker };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a story, and returns the received story object.
*
* Accepts an optional options object that lets you perform actions when a
* story is received, when a non-story update is received, and more.
*
* @param options Optional options
*/
async story(options) {
return await this.build({
collationKey: "form-story",
...options,
validate: (ctx) => {
var _a, _b;
const story = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.story;
if (story === undefined)
return { ok: false };
return { ok: true, value: story };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a video, and returns the received video object.
*
* Accepts an optional options object that lets you perform actions when a
* video is received, when a non-video update is received, and more.
*
* @param options Optional options
*/
async video(options) {
return await this.build({
collationKey: "form-video",
...options,
validate: (ctx) => {
var _a, _b;
const video = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.video;
if (video === undefined)
return { ok: false };
return { ok: true, value: video };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a video note, and returns the received video note
* object.
*
* Accepts an optional options object that lets you perform actions when a
* video note is received, when a non-video note update is received, and
* more.
*
* @param options Optional options
*/
async video_note(options) {
return await this.build({
collationKey: "form-video_note",
...options,
validate: (ctx) => {
var _a, _b;
const video_note = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.video_note;
if (video_note === undefined)
return { ok: false };
return { ok: true, value: video_note };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a voice message, and returns the received voice object.
*
* Accepts an optional options object that lets you perform actions when a
* voice message is received, when a non-voice message update is received,
* and more.
*
* @param options Optional options
*/
async voice(options) {
return await this.build({
collationKey: "form-voice",
...options,
validate: (ctx) => {
var _a, _b;
const voice = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.voice;
if (voice === undefined)
return { ok: false };
return { ok: true, value: voice };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a contact, and returns the received contact object.
*
* Accepts an optional options object that lets you perform actions when a
* contact is received, when a non-contact update is received, and more.
*
* @param options Optional options
*/
async contact(options) {
return await this.build({
collationKey: "form-contact",
...options,
validate: (ctx) => {
var _a, _b;
const contact = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.contact;
if (contact === undefined)
return { ok: false };
return { ok: true, value: contact };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with dice, and returns the received dice object.
*
* Accepts an optional options object that lets you perform actions when
* dice are received, when a non-dice update is received, and more.
*
* @param options Optional options
*/
async dice(options) {
return await this.build({
collationKey: "form-dice",
...options,
validate: (ctx) => {
var _a, _b;
const dice = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.dice;
if (dice === undefined)
return { ok: false };
return { ok: true, value: dice };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a game, and returns the received game object.
*
* Accepts an optional options object that lets you perform actions when a
* game is received, when a non-game update is received, and more.
*
* @param options Optional options
*/
async game(options) {
return await this.build({
collationKey: "form-game",
...options,
validate: (ctx) => {
var _a, _b;
const game = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.game;
if (game === undefined)
return { ok: false };
return { ok: true, value: game };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a poll, and returns the received poll object.
*
* Accepts an optional options object that lets you perform actions when a
* poll is received, when a non-poll update is received, and more.
*
* @param options Optional options
*/
async poll(options) {
return await this.build({
collationKey: "form-poll",
...options,
validate: (ctx) => {
var _a, _b;
const poll = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.poll;
if (poll === undefined)
return { ok: false };
return { ok: true, value: poll };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a venue, and returns the received venue object.
*
* Accepts an optional options object that lets you perform actions when a
* venue is received, when a non-venue update is received, and more.
*
* @param options Optional options
*/
async venue(options) {
return await this.build({
collationKey: "form-venue",
...options,
validate: (ctx) => {
var _a, _b;
const venue = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.venue;
if (venue === undefined)
return { ok: false };
return { ok: true, value: venue };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a location, and returns the received location object.
*
* Accepts an optional options object that lets you perform actions when a
* location is received, when a non-location update is received, and more.
*
* @param options Optional options
*/
async location(options) {
return await this.build({
collationKey: "form-location",
...options,
validate: (ctx) => {
var _a, _b;
const location = (_b = ((_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost)) === null || _b === void 0 ? void 0 : _b.location;
if (location === undefined)
return { ok: false };
return { ok: true, value: location };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a photo or video, and returns the received media
* object.
*
* Accepts an optional options object that lets you perform actions when a
* media is received, when a non-media update is received, and more.
*
* @param options Optional options
*/
async media(options) {
return await this.build({
collationKey: "form-location",
...options,
validate: (ctx) => {
var _a, _b;
const msg = (_a = ctx.message) !== null && _a !== void 0 ? _a : ctx.channelPost;
const media = (_b = msg === null || msg === void 0 ? void 0 : msg.photo) !== null && _b !== void 0 ? _b : msg === null || msg === void 0 ? void 0 : msg.video;
if (media === undefined)
return { ok: false };
return { ok: true, value: media };
},
});
}
/**
* Form field that checks if the incoming update contains a message or
* channel post with a file, calls `await ctx.getFile()`, and returns the
* received file object.
*
* Accepts an optional options object that lets you perform actions when a
* file is received, when a non-file update is received, and more.
*
* @param options Optional options
*/
async file(options) {
return await this.build({
collationKey: "form-location",
...options,
validate: async (ctx) => {
if (!ctx.has(":file"))
return { ok: false };
const file = await ctx.getFile();
return { ok: true, value: file };
},
});
}
}
exports.ConversationForm = ConversationForm;