Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WizardScene stuck in first step #1913

Open
tair-rhyme opened this issue Dec 19, 2023 · 0 comments
Open

WizardScene stuck in first step #1913

tair-rhyme opened this issue Dec 19, 2023 · 0 comments

Comments

@tair-rhyme
Copy link

Context

Hi. I've function to register bot users before they can use it. Here is i'm using middleware to check auth of user for all incoming requests, if user is not registered then enters to wizard scene, where i have couple of steps to collect user info. To make better user experience i want to allow users to call telegram commands when they are already in wizard scene.

  • Telegraf.js Version: 4.14.0
  • Node.js Version: 18.18.2
  • Operating System: macOS Ventura 13.4
  • Used modules: @telegraf/session/redis, i18n-telegraf

Minimal Example Code Reproducing the Issue

const handleMyChatMemberUpdate = (ctx) => {
	if (ctx.update.my_chat_member){
		console.log('my_chat_member chat_id', ctx.update.my_chat_member.chat.id, ' status: ', ctx.update.my_chat_member.new_chat_member.status)
		console.log('ctx.session', ctx.session)
		if(ctx.update.my_chat_member.new_chat_member.status == 'kicked'){
			ctx.scene.reset();
			ctx.scene.state = {};
			ctx.session.order = {};
		} else{

		}
	}
}

const contactDataWizard = new Scenes.WizardScene(
	'CONTACT_DATA_WIZARD_SCENE_ID',
	async (ctx) => {
		// console.log('contactDataWizard:0 update:', ctx.update)
		console.log('scene', 'CONTACT_DATA_WIZARD_SCENE_ID', '0', 'enter')
		if(ctx.update.my_chat_member)
			return handleMyChatMemberUpdate(ctx);
		if(ctx.update.message) await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id);
		ctx.wizard.state.contactData = {};
		await SHOW_LANG_MENU(ctx);
		console.log('scene', 'CONTACT_DATA_WIZARD_SCENE_ID', '0', 'next')
		return ctx.wizard.next();
	}, //start scene, show lang menu then switch to next scene
	async (ctx) => {
		// console.log('contactDataWizard:1 message:', ctx.update.message, ' callback:',ctx.update.callback_query )
		console.log('scene', 'CONTACT_DATA_WIZARD_SCENE_ID', '1', 'enter')

		if(ctx.update.my_chat_member) return handleMyChatMemberUpdate(ctx);
		if(ctx.message && ctx.message.text?.startsWith('/')) { //check if user called command
			await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id);
			console.log('scene', 'CONTACT_DATA_WIZARD_SCENE_ID', '1', 'leave')
			return await ctx.scene.leave();
		}
		//if user clicked callback button
		if (ctx.update.callback_query){
			await ctx.answerCbQuery();
			if(ctx.update.callback_query.data === 'kz'){
				ctx.wizard.state.contactData.language = 'kz';
				ctx.i18n.locale("kz");
			}else if(ctx.update.callback_query.data === 'ru'){
				ctx.i18n.locale("ru");
				ctx.wizard.state.contactData.language = 'ru';
			}
		} else { //unhandled cases
			if(ctx.update.message) await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id);
			return SHOW_LANG_MENU(ctx);
		}
		await ctx.reply(ctx.i18n.t("welcome"), authKeyboard(ctx.i18n.t('share_number')))
			.then( reply => SAVE_MSG_ID(reply.chat.id, reply.message_id))
		console.log('scene', 'CONTACT_DATA_WIZARD_SCENE_ID', '1', 'next')
		return ctx.wizard.next();
	},//expecting callback query
	async (ctx) => {
		if(ctx.update.my_chat_member)
			return handleMyChatMemberUpdate(ctx);

		if(ctx.message && ctx.message.text?.startsWith('/')) { //check if user called command
			await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id)
			return ctx.scene.leave();
		}

		await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id);
		if (!ctx.message.contact) {//handle if user not shared contact data
			return ctx.reply(
				ctx.i18n.t("welcome"),
				authKeyboard(ctx.i18n.t('share_number')))
				.then( reply => SAVE_MSG_ID(reply.chat.id, reply.message_id))
		}

		ctx.wizard.state.contactData.phone_number = helper.normalizePhoneNumber(ctx.message.contact.phone_number);
		ctx.wizard.state.contactData.first_name = ctx.message.contact.first_name;
		ctx.wizard.state.contactData.last_name = ctx.message.contact.last_name;
		ctx.wizard.state.contactData.user_id = ctx.message.contact.user_id;

		await ctx.reply(ctx.i18n.t('enter_full_name'), removeKeyboard)
			.then( reply => SAVE_MSG_ID(reply.chat.id, reply.message_id));
		return ctx.wizard.next();
	}, //expecting contact
	async (ctx) => {
		if(ctx.update.my_chat_member)
			return handleMyChatMemberUpdate(ctx);

		if(ctx.message && ctx.message.text?.startsWith('/')) { //check if user called command
			await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id)
			return ctx.scene.leave();
		}
		await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id);
		if(ctx.message && !helper.isValidName(ctx.message.text)) //when user not send correct name
			return ctx.reply(ctx.i18n.t('enter_correct_full_name'))
				.then( reply => SAVE_MSG_ID(reply.chat.id, reply.message_id));

		ctx.wizard.state.contactData.fullname = ctx.message.text;
		await ctx.replyWithMarkdown(ctx.i18n.t('accept_rules_text'),
			Markup.inlineKeyboard(KEYBOARD(ctx, KEYBOARD_TYPE.ACCEPT_RULES)).resize().oneTime())
			.then( reply => SAVE_MSG_ID(reply.chat.id, reply.message_id))
		return ctx.wizard.next();
	}, //expecting user name in text
	async (ctx) => {
		if(ctx.update.my_chat_member)
			return handleMyChatMemberUpdate(ctx);
		if(ctx.update.callback_query && ctx.update.callback_query.data === 'accept'){
			ctx.wizard.state.contactData.chat_id = ctx.chat.id;
			await SAVE_AUTH(ctx.wizard.state.contactData);
			await CLEAR_MSGS(ctx);
			ctx.reply(ctx.i18n.t('welcome_to_service'))
				.then( reply => SAVE_MSG_ID(reply.chat.id, reply.message_id))
			await SHOW_MAIN_MENU(ctx)
			return ctx.scene.leave();
		}else{
			ctx.replyWithMarkdown(ctx.i18n.t('accept_rules_text'),
				Markup.inlineKeyboard(KEYBOARD(ctx, KEYBOARD_TYPE.ACCEPT_RULES)).resize().oneTime())
				.then( reply => SAVE_MSG_ID(reply.chat.id, reply.message_id))
			return;
		}
	}
);


bot.use(async (ctx, next) => {
	console.log('ctx use', ctx)
	if(ctx.update.callback_query) //skip all stuff if callback query
		return next();
	else if (ctx.update.my_chat_member){
		if(ctx.update.my_chat_member.new_chat_member.status == 'kicked')
			return handleMyChatMemberUpdate(ctx);
	}
	else if (ctx.update.message.photo) { //skipp all stuff if photo received, but keep msg id
		await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id);
		return next();
	}

	await CLEAR_MSGS(ctx); //clear all messages from history

	if(ctx.update.message && ctx.update.message.message_id)
		await SAVE_MSG_ID(ctx.chat.id, ctx.update.message.message_id); //save incoming message id

	const user = await CHECK_AUTH(ctx.chat.id)

	if(user){
		await ctx.i18n.locale(user.language)
		await ctx.telegram.setMyCommands(
			[{command:"start", description:ctx.i18n.t('main_menu')}],
			{scope:{type:'chat', chat_id: ctx.chat.id}})
		console.log('middleware:auth passed', ctx.chat.id);
		return next(); //go next if user authorized
	}else { //otherwise, it is unauthorized user, enter scene
		ctx.scene.enter('CONTACT_DATA_WIZARD_SCENE_ID')
	}

})

Expected Behavior

Unauthorized user start bot, then goes to contactDataWizard, first step shows language menu then switches to next step, second step handles that user called a command, then handler should leave scene then call next() of middleware to give a chance to handle commands.

Current Behavior

If unauthorized user in step 2 (that expects callback_query), but user called /start again, my code handles this case and seems like leaves the scene, but nothing happening, but if i call /start again, user enters contactDataWizard again but not switch to step 2, but executes step 1 again. But step 1 calls return ctx.wizard.next(); anyways.

screen.mov
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant