Closed roviks closed 1 year ago
I didn't fully grasp what you're doing with the session/context, but I think you're misusing it. grammy-scenes saves the current step on its own, it's supposed to gracefully handle bot restart.
On each wait
, it saves the execution stack (current step position):
which means simply putting it to the session:
The session is supposed to be persisted with something like https://github.com/grammyjs/storages.
Then in the middleware, on the next user event (possibly after bot restart), it resumes from where it wait
'ed:
If you still want to abuse the scene runner workflow, I suppose you could add a pre-step doing nothing:
scene.label("pre-step-1").step(async (ctx) => {
// Do nothing. Use it for goto.
})
scene.wait("step-1").setup(() { /* do as before */ })
Then if you goto
to it, it won't run whatever handlers are defined in wait().setup()
until the next event.
For example, When the user has an active scene and waits for "step-1" at that moment the bot restarts (for whatever reason). In this case, after the user writes he will receive the next step?
For example, When the user has an active scene and waits for "step-1" at that moment the bot restarts (for whatever reason). In this case, after the user writes he will receive the next step?
If the scene stopped on wait, the bot restarted, and then the user sends a message, the scene manager will execute whatever handlers (middlewares) are defined inside setup()
that immediately follows wait()
. You should not handle bot restart yourself, that works fully transparently.
That, of course, assumes that the grammy sessions are using some kind of persistent storage backend.
I think it's not working. After bot restart no active scene, that's why i use middleware to enter scene. In this case, the step is reset
bot.use(async (ctx, next) => {
const tgId = ctx.session.tgId ?? ''
const startedScene = ctx.session.startedScene
if (
startedScene && !getActiveScene(tgId)
) {
await ctx.scenes.enter(getSceneDayKey(startedScene))
setActiveScene(tgId, getSceneDayKey(startedScene))
await next()
return
}
await next()
})
I think I've told everything above and I'm not following what else do you want. The scenes module does not store any local variables and it is thus agnostic to bot restarts. It keeps its state in ctx.session.scenes
(you don't overwrite it by chance?) and relies on its persistence.
The active scene is invoked here:
// Actually run scenes
bot.use(scenes)
You may check if the control flow comes there and if the stack is valid (if it points to the correct step):
bot.use((ctx, next) => {
console.log('resuming scene from', ctx.session.scenes)
return next()
})
// Actually run scenes
bot.use(scenes)
Unless you come up with a proper reproduction (the code that I could run and repeat the problem), I won't be able to assist further.
One thing I would have checked is if you're not forgetting to use await
. See, if you call an async handler without awaiting it, the middleware will return immediately and the session will be persisted too soon (the scene stack will be put into a session object that've been already persisted).
By the way, in the code that you've included, you're probably mistaken in that you're calling await ctx.scenes.enter(...)
followed by await next()
. In grammy, you're supposed to call next()
only if you didn't handle the event. However, as you've called scene enter, you're presumably achieved what you wanted and should not have called next
.
That itself unlikely has something to do with your problem, but this kind of mistake (if that is a mistake — sorry if it's there for a reason) shows probable lack of understanding of how middleware stack together. Which means something similar could be causing the original problem.
I checked my code and sure that I'm not overriding ctx.session.scenes
.
Entering scene:
After bot restart, i have nothing in ctx.session.scenes
:
Middlewares:
bot.use(
session({
initial: () => ({})
})
)
bot.use(wizardScenes.manager())
bot.use((ctx, next) => {
console.log('resuming scene from', ctx.session.scenes)
return next()
})
bot.use(pseudoUpdate)
bot.use(wizardScenes)
As I already mentioned three times, the scenes module relies on persisted sessions.
This code does not persist sessions, they are gone on bot restart:
bot.use(
session({
initial: () => ({})
})
)
This (for example) does:
import { PsqlAdapter } from '@grammyjs/storage-psql';
bot.use(
session({
initial: () => ({}),
storage: await PsqlAdapter.create({ tableName: 'sessions', client }),
})
);
Hi! If the person did not click on the button, but wrote the text, I have to resend the text with the button so that he clicks in order to continue
When my bot restarts, the entire session is cleared, so I save the step in the scene in the database. Therefore, at the beginning, I check at which step I stopped and continue from that moment. With this method, I have 2 times called composer. Is there any other way to do this?
Console: