slackapi/java-slack-sdk

@Autowired private App app; is not listening the events in Spring Boot, it says No BoltEventHandler registered for event: app_mention

mallik30 opened this issue · 9 comments

Autowiring the App in the spring boot is not listening to Slack events

Reproducible in: https://github.com/mallik30/actionbot/tree/slack-autowired-not-listening

mvn dependency:tree | grep com.slack.api
gradle dependencies | grep com.slack.api
java -version 
sw_vers && uname -v # or `ver`

The Slack SDK version

(Paste the output of mvn dependency:tree | grep com.slack.api or gradle dependencies | grep com.slack.api)
attached actual github codebase url above

Java Runtime version

(Paste the output of java -version)
java version "13" 2019-09-17

OS info

(Paste the output of sw_vers && uname -v on macOS/Linux or ver on Windows OS)
ProductName: macOS
ProductVersion: 13.3.1
ProductVersionExtra: (a)
BuildVersion: 22E772610a
Darwin Kernel Version 22.4.0: Mon Mar 6 21:00:17 PST 2023; root:xnu-8796.101.5~3/RELEASE_X86_64

Steps to reproduce:

(Share the commands to run, source code, and project settings (e.g., pom.xml/build.gradle))

  1. add tokens in the application.properties
  2. run as a Java application
  3. and trigger the flow using @MYBOT create

Expected result:

expected to listen the events from @Autowired private App app dependency injection
as its a bad practice to code inside a @bean in configuration class

Actual result:

it throws the following error

No BoltEventHandler registered for event: app_mention

Requirements

Please make sure if this topic is specific to this SDK. For general questions/issues about Slack API platform or its server-side, could you submit questions at https://my.slack.com/help/requests/new instead. 🙇

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you agree to those rules.

Hi @mallik30, thanks for writing in!

It seems that these listener registration methods (https://github.com/mallik30/actionbot/blob/4e9bc923e654bcb096998494d46a7c514d781c0b/src/main/java/com/arjun/slack/api/ActionBotManager.java#L39-L71) are not called in your app. In this case, the event handling error can arise as expected becuse your app does not have corresponding listeners for it.

This is our recommendation to register listeners to your App instance (as a Spring bean component): https://github.com/slackapi/java-slack-sdk/blob/v1.29.2/bolt-spring-boot-examples/spring-boot-3/src/main/java/example/SlackApp.java#L36-L46 It is the same way with this disabled code in your app.

If you're exploring a different way to initialize the App instnace, please consult Sping Boot community and/or their documents. We are a small team, so we don't provide supports for general Spring Boot questions.

I hope this helps and you'll find a viable solution for you!

Hi @seratch with the way Bean is defined in config class
i have to do this to listen to multiple events, which will make the method way big, I think there should be another way to listen the bean from slack side

public App appMention(/* App app */) {
//AppMentionEvent event
		app.event(AppMentionEvent.class, (payload, ctx) -> {
			return ctx.ack();
		});
//ReactionAddedEvent event
		app.event(ReactionAddedEvent.class, (payload, ctx) -> {
			return ctx.ack();
		});
		return app;
// other events
// other events
}

@mallik30 You don't need to consolidate all the code into a single method. Our recommendation is to divide the code into separate files, as shown in this example: https://github.com/slack-samples/bolt-java-starter-template/blob/main/src/main/java/Main.java This approach is entirely independent from the Spring Boot bean registration. Concurrently, you can still inject other bean components into listener functions if you define the listener classes as beans.

@seratch sorry the sample you provide is for Socket mode

@mallik30 Sorry for the confusion, but you can ignore the line new SocketModeApp(app).start();.

I meant you can go with the example app's approach to organize listeners: https://github.com/slack-samples/bolt-java-starter-template/tree/main/src/main/java/listeners You don't need to use static methods like the app does. Instead, you can have a bean component that manages listeners.

@seratch still a lot of unnecessary changes, something simple like the below would beneficial

config class

@Configuration
public class SlackConfig {

	@Value("${SLACK_BOT_TOKEN}")
	private String botToken;

	@Value("${SLACK_SIGNING_SECRET}")
	private String signingSecret;

	@Bean
	AppConfig appConfig() {
		return AppConfig.builder().singleTeamBotToken(botToken).signingSecret(signingSecret).build();
	}

	@Bean
	App app(AppConfig appConfig) {
		return new App(appConfig);
	}
}

manager class

@Service
public class Manager {

	@Value("${SLACK_BOT_TOKEN}")
	private String botToken;

	@Value("${SLACK_USER_TOKEN}")
	private String userToken;

	@Autowired
	private App app;

	public App reactionAdded() {
		return app.event(ReactionAddedEvent.class, (payload, ctx) -> {
			return ctx.ack();
		});
	}
	public App appMention() {
		return app.event(AppMentionEvent.class, (payload, ctx) -> {
			return ctx.ack();
		});
	}
	
}

@mallik30 If you prefer the approach, calling these listener registraton methods in the serivce/bean initialization method would be a good option. You can go with either @Bean(initMethod="initMethodNameHere") or adding @PostConstruct to the initMethod. Inside the init method, you can call reactionAdded, appMention, and many more you'll add in the future.

We don't have plans to provide anything specific to Spring Boot (because it can bring extra maintenance cost to our small SDK maintainer team). Please consider any of the suggestions I provided here.

@seratch am moving forward with the below approach

@Service
public class Manager {

	@Autowired
	private App app;

	@PostConstruct
	public void init() {
		reactionAdded();
		appMention();
	}

	public void reactionAdded() {
		app.event(ReactionAddedEvent.class, (payload, ctx) -> {
			return ctx.ack();
		});
	}

	public void appMention() {
		app.event(AppMentionEvent.class, (payload, ctx) -> {
			return ctx.ack();
		});
	}

}

Thank you for your assistance.

also, I have another question, I got stuck on the threaded approach and am unable to find the relevant events to listen for user responses in threads will you be able to assist with this one?

You can subscribe message events for it: https://api.slack.com/events/message With bolt-java, you can add a new listener like app.event(MessageEvent.class, ....

Since I've provided an answer to your original question here, let me close this thread. Whenever you have a new question, you can create a new issue for it. Thanks again for writing in!