'Telegraf.js handle callback buttons
I have wizardScene:
const { Scenes, Markup } = require('telegraf')
const db = require('../lib/db')
const Post = require('../models/post')
const keyboard = require('../lib/keyboards')
const randomPost = new Scenes.WizardScene('randomPost',
async (ctx) => {
const post = await db.getRandomPost()
if (post['post'].type === 'chat') {
let reply = `Случайная запись:\n\n`
reply += `${post['post'].title}\n`
reply += `${post['post'].text}`
await ctx.reply(reply, await keyboard.getPostInline())
await ctx.reply('Продолжить?', await keyboard.getRandomKeyboard())
ctx.wizard.next()
}
},
async (ctx) => {
if (ctx.message.text === 'Да') {
return ctx.scene.reenter()
}
if (ctx.message.text === 'Нет') {
await ctx.reply('Выход...')
return ctx.scene.leave()
}
await ctx.reply('Нажмите на кнопку для продолжения.')
})
randomPost.hears('like', (ctx) => console.log('like'))
randomPost.hears('comment', (ctx) => console.log('comment')) // Doesn't work
module.exports = randomPost
async function getPostInline() {
return Markup.inlineKeyboard([
[Markup.button.callback('❤️', 'like'), Markup.button.callback('💬', 'comment')]
]).oneTime().resize()
}
getPostInline method returns an inline keyboard, when you press the button on it, it crashes with an error due to ctx.message.text - undefined - TypeError: Cannot read properties of undefined (reading 'text') (on the:
async (ctx) => {
if (ctx.message.text === 'Да') {
return ctx.scene.reenter()
I tried to intercept this with:
randomPost.hears('like', (ctx) => console.log('like'))
randomPost.hears('comment', (ctx) => console.log('comment')) // Doesn't work
But it doesn't work. What do I need to do so that there is no error, preferably without deleting await ctx.reply('Нажмите на кнопку для продолжения.')
P.S. I can add if but may be there is more correct solution
Solution 1:[1]
Update:
When we have to decide before context creation (.i.e. ApplicationListener is no Component/Bean), then we (just mimic profiles) set:
# env: SET MY_FLAG=foo -> System.getenv("MY_FLAG")
# system: java -jar -Dmy.flag=foo myApp.jar -> System.getProperty("my.flag")
# cmd arg:
java -jar myApp.jar aws
..and issue it in our (spring boot) main method like:
if("aws".equalsIgnoreCase(args[0])) // alternatively: System.get...
application.addListeners(new DatabasePropertiesListener());
Misunderstood answer
Sure we can: With spring (boot) core features!
Assuming our ApplicationListener is a "spring managed bean", we annotate it with:
@Component
@Profile("aws") // custom profile (name)
class MyApplicationListener implements ...
{ ... }
...this will not load this "bean" into our context, unless we define:
spring.profiles.acitve=aws,... # a comma separated list of profiles to activate
Profiles
Spring Profiles provide a way to segregate parts of your application configuration and make it be available only in certain environments. Any
@Component(Or@Bean,@Service,@Repository...descendants),@Configurationor@ConfigurationPropertiescan be marked with@Profileto limit when it is loaded, as shown in the ...
... above example.
An advanced application of @Profile annotation:
- with multiple profiles: "or"/"and" semantics
- "not" (
!) operator
@Profile("!local","aws") // (String[]: OR semantics) the annotated component/configuration/property will
// be loaded, when active profiles NOT contains "local" OR contains "aws"
// for AND semantics, we'd use (1 string): "!local & aws"
Activating Profile(s!)
spring.profiles.acitve can be set/added (like any spring property source) through several (14^^, precisely priorized) locations.
E.g. setting environment variable (5th lowest priority, but higher than application.properties (3rd lowest)):
SPRING_PROFILES_ACTIVE=aws
Or as a command line flag (when starting the application, 11th lowest/3rd highest priority):
java -jar myApp.jar --spring.profiles.active=aws,...
#comma separated list
For (spring) tests additionally exists an @ActiveProfiles annotation.
Remarks/Note
Deciding for profiles, we should ensure to "make it consistently" (not raising Nullpointer/BeaninitalizationExceptions ... with dependencies!;). If needed: Creating replacement/local/test (
@Profile("!aws")) beans.Activating a profile "xyz", automatically tries to load
application-xyz.properties(with higher priority thanapplication.properties(prio 3.1 - 3.4))...also in (spring-)cloud-config.Not to forget: The
defaultprofile (activated by spring automatically, only when no explicit profile is activated).
Reference
For detailed documentation, please refer to:
- Spring Boot - Externalized Configuration
- Spring Boot - Profiles
- Spring Environment Abstraction chapter
- @Profile Javadoc
- @ActiveProfiles Javadoc
Profile sample from Configuration Javadoc:
@Configurationclasses may be marked with the@Profileannotation to indicate they should be processed only if a given profile or profiles are active:@Profile("development") @Configuration public class EmbeddedDatabaseConfig { @Bean public DataSource dataSource() { // instantiate, configure and return embedded DataSource } }@Profile("production") @Configuration public class ProductionDatabaseConfig { @Bean public DataSource dataSource() { // instantiate, configure and return production DataSource } }Alternatively, you may also declare profile conditions at the
@Beanmethod level — for example, for alternative bean variants within the same configuration class:@Configuration public class ProfileDatabaseConfig { @Bean("dataSource") @Profile("development") public DataSource embeddedDatabase() { ... } @Bean("dataSource") @Profile("production") public DataSource productionDatabase() { ... } }See the
@ProfileandEnvironmentjavadocs for further details.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
