0.2.0 • Published 3 years ago

basic-scene v0.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

Basic Scene plugin for grammY

This plugin provides a middleware for hydrating Context with a basic implementation of scene, and a function for creating scenes.

Usage

import { Bot, session, SessionFlavor } from 'grammy';
import { createScene, hydrateScene, SceneContext } from 'basic-scene';

type SessionData = {
  motto: string | undefined
};

type AppContext = SceneContext & SessionFlavor<SessionData>;

// Create a scene for registering the user's motto
const {
  sceneMiddleware: mottoMiddleware,
  sceneBuilder: mottoBuilder,
} = createScene<AppContext>('register motto', async ctx => ctx.reply('Please provide your motto!'));
mottoBuilder.on(':text', async ctx => {
  ctx.session.motto = ctx.msg.text;
  await ctx.scene.exit();
  await ctx.reply(`Your motto is ${ctx.session.motto}!`);
});
mottoBuilder.use(async ctx => ctx.reply('Please provide a valid text motto!'));

const bot = new Bot<AppContext>('');

// Install session first!
bot.use(session({
  initial(): SessionData {
    return { motto: undefined };
  }
}));

// Install scene to ctx
bot.use(hydrateScene);

// Install the motto scene
bot.use(mottoMiddleware);

// Gets the user's motto, or make them register one if they don't have one
bot.command('motto', async ctx => {
  if (ctx.session.motto === undefined) {
    await ctx.scene.enter('register motto');
    return;
  }

  await ctx.reply(`Your motto is ${ctx.session.motto}!`);
});

bot.start();

Complex Usage (working with external Context types)

import { Bot, Composer, Context, NextFunction, session, SessionFlavor } from 'grammy';
import { hydrateReply, ParseModeContext } from '@grammyjs/parse-mode';
import { createScene, EntryExitFunction, hydrateScene, SceneContext } from 'basic-scene';

type SessionData = {
  name: string | undefined
  age: number | undefined
};

type AppContext = ParseModeContext & SceneContext & SessionFlavor<SessionData>;

enum Scenes {
  RegisterAge = 'register age',
  RegisterName = 'register name',
};

const {
  sceneMiddleware: nameMiddleware,
  sceneBuilder: nameBuilder,
} = createScene<AppContext>(
  Scenes.RegisterName,
  async ctx => {
    await ctx.reply('Please provide your name!');
  },
);
nameBuilder.on(':text', async ctx => {
  const name = ctx.msg.text;
  if (name.length < 3 || name.length > 18) {
    ctx.replyWithHTML('Please ensure that your name is <b>between 4 and 17 characters long</b>!');
    return;
  }
  
  ctx.session.name = name;
  await ctx.scene.enter(Scenes.RegisterAge);
});
nameBuilder.use(async ctx => ctx.replyWithHTML('Please provide a <i>valid</i> name!'));

const {
  sceneMiddleware: ageMiddleware,
  sceneBuilder: ageBuilder,
} = createScene<AppContext>(
  Scenes.RegisterAge,
  async ctx => {
    await ctx.reply('Please provide your age!');
  },
);
ageBuilder.on(':text', async ctx => {
  const ageInput = ctx.msg.text;
  const age = Number(ageInput);
  if (Number.isNaN(age) || !Number.isInteger(age) || age < 8 || age > 120) {
    await ctx.replyWithHTML('Please provide a valid age <b>between 8 and 119 years old</b>!');
    return;
  }

  ctx.session.age = age;
  await ctx.scene.exit();
  await ctx.replyWithHTML(`Your name is <b>${ctx.session.name}</b> and your age is <b>${ctx.session.age}</b>!`);
});
ageBuilder.use(async ctx => ctx.replyWithHTML('Please provide a <i>valid</i> age!'));

const bot = new Bot<AppContext>('');
bot.use(session({
  initial(): SessionData {
    return { name: undefined, age: undefined };
  }
}));
bot.use(hydrateReply);
bot.use(hydrateScene);
bot.use(nameMiddleware);
bot.use(ageMiddleware);
bot.command('profile', async ctx => {
  const name = ctx.session.name;
  const age = ctx.session.age;
  if (name === undefined || age === undefined) {
    await ctx.scene.enter(Scenes.RegisterName);
    return;
  }

  await ctx.replyWithHTML(`Your name is <b>${ctx.session.name}</b> and your age is <b>${ctx.session.age}</b>!`);
});

bot.start();