Skip to content

Commit

Permalink
Add Agents & Assistant support (#1372)
Browse files Browse the repository at this point in the history
  • Loading branch information
seratch authored Oct 17, 2024
1 parent 18ff9b5 commit f65dc3d
Show file tree
Hide file tree
Showing 34 changed files with 1,124 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public static void main(String[] args) throws Exception {
ctx.client().assistantThreadsSetSuggestedPrompts(r -> r
.channelId(channelId)
.threadTs(threadTs)
.title("How are you?")
.prompts(Collections.singletonList(new SuggestedPrompt("What does SLACK stand for?")))
);
} catch (Exception e) {
Expand Down Expand Up @@ -71,7 +70,7 @@ public static void main(String[] args) throws Exception {
.text("Here you are!")
);
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant thread started event: {e}", e);
ctx.logger.error("Failed to handle assistant user message event: {e}", e);
try {
ctx.client().chatPostMessage(r -> r
.channel(channelId)
Expand Down Expand Up @@ -112,7 +111,7 @@ public static void main(String[] args) throws Exception {
.text("Your files do not have any issues!")
);
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant thread started event: {e}", e);
ctx.logger.error("Failed to handle assistant user message event: {e}", e);
try {
ctx.client().chatPostMessage(r -> r
.channel(channelId)
Expand Down
114 changes: 114 additions & 0 deletions bolt-socket-mode/src/test/java/samples/AssistantInteractionApp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package samples;

import com.slack.api.bolt.App;
import com.slack.api.bolt.AppConfig;
import com.slack.api.bolt.middleware.builtin.Assistant;
import com.slack.api.bolt.socket_mode.SocketModeApp;
import com.slack.api.model.Message;
import com.slack.api.model.event.AppMentionEvent;
import com.slack.api.model.event.MessageEvent;

import java.security.SecureRandom;
import java.util.*;

import static com.slack.api.model.block.Blocks.actions;
import static com.slack.api.model.block.Blocks.section;
import static com.slack.api.model.block.composition.BlockCompositions.plainText;
import static com.slack.api.model.block.element.BlockElements.button;

public class AssistantInteractionApp {

public static void main(String[] args) throws Exception {
String botToken = System.getenv("SLACK_BOT_TOKEN");
String appToken = System.getenv("SLACK_APP_TOKEN");

App app = new App(AppConfig.builder()
.singleTeamBotToken(botToken)
.ignoringSelfAssistantMessageEventsEnabled(false)
.build()
);

Assistant assistant = new Assistant(app.executorService());

assistant.threadStarted((req, ctx) -> {
try {
ctx.say(r -> r
.text("Hi, how can I help you today?")
.blocks(Arrays.asList(
section(s -> s.text(plainText("Hi, how I can I help you today?"))),
actions(a -> a.elements(Collections.singletonList(
button(b -> b.actionId("assistant-generate-numbers").text(plainText("Generate numbers")))
)))
))
);
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant thread started event: {e}", e);
}
});

app.blockAction("assistant-generate-numbers", (req, ctx) -> {
app.executorService().submit(() -> {
Map<String, Object> eventPayload = new HashMap<>();
eventPayload.put("num", 20);
try {
ctx.client().chatPostMessage(r -> r
.channel(req.getPayload().getChannel().getId())
.threadTs(req.getPayload().getMessage().getThreadTs())
.text("OK, I will generate numbers for you!")
.metadata(new Message.Metadata("assistant-generate-numbers", eventPayload))
);
} catch (Exception e) {
ctx.logger.error("Failed to post a bot message: {e}", e);
}
});
return ctx.ack();
});

assistant.botMessage((req, ctx) -> {
if (req.getEvent().getMetadata() != null
&& req.getEvent().getMetadata().getEventType().equals("assistant-generate-numbers")) {
try {
ctx.setStatus("is typing...");
Double num = (Double) req.getEvent().getMetadata().getEventPayload().get("num");
Set<String> numbers = new HashSet<>();
SecureRandom random = new SecureRandom();
while (numbers.size() < num) {
numbers.add(String.valueOf(random.nextInt(100)));
}
Thread.sleep(1000L);
ctx.say(r -> r.text("Her you are: " + String.join(", ", numbers)));
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant bot message event: {e}", e);
}
}
});

assistant.userMessage((req, ctx) -> {
try {
ctx.setStatus("is typing...");
ctx.say(r -> r.text("Sorry, I couldn't understand your comment."));
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant user message event: {e}", e);
try {
ctx.say(r -> r.text(":warning: Sorry, something went wrong during processing your request!"));
} catch (Exception ee) {
ctx.logger.error("Failed to inform the error to the end-user: {ee}", ee);
}
}
});


app.assistant(assistant);

app.event(MessageEvent.class, (req, ctx) -> {
return ctx.ack();
});

app.event(AppMentionEvent.class, (req, ctx) -> {
ctx.say("You can help you at our 1:1 DM!");
return ctx.ack();
});

new SocketModeApp(appToken, app).start();
}
}
84 changes: 84 additions & 0 deletions bolt-socket-mode/src/test/java/samples/AssistantSimpleApp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package samples;

import com.slack.api.bolt.App;
import com.slack.api.bolt.AppConfig;
import com.slack.api.bolt.middleware.builtin.Assistant;
import com.slack.api.bolt.socket_mode.SocketModeApp;
import com.slack.api.model.assistant.SuggestedPrompt;
import com.slack.api.model.event.AppMentionEvent;
import com.slack.api.model.event.MessageEvent;

import java.util.Collections;

public class AssistantSimpleApp {

public static void main(String[] args) throws Exception {
String botToken = System.getenv("SLACK_BOT_TOKEN");
String appToken = System.getenv("SLACK_APP_TOKEN");

App app = new App(AppConfig.builder().singleTeamBotToken(botToken).build());

Assistant assistant = new Assistant(app.executorService());

assistant.threadStarted((req, ctx) -> {
try {
ctx.say(r -> r.text("Hi, how can I help you today?"));
ctx.setSuggestedPrompts(Collections.singletonList(
SuggestedPrompt.create("What does SLACK stand for?")
));
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant thread started event: {e}", e);
}
});

assistant.userMessage((req, ctx) -> {
try {
ctx.setStatus("is typing...");
Thread.sleep(500L);
if (ctx.getThreadContext() != null) {
String contextChannel = ctx.getThreadContext().getChannelId();
ctx.say(r -> r.text("I am ware of the channel context: <#" + contextChannel + ">"));
} else {
ctx.say(r -> r.text("Here you are!"));
}
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant user message event: {e}", e);
try {
ctx.say(r -> r.text(":warning: Sorry, something went wrong during processing your request!"));
} catch (Exception ee) {
ctx.logger.error("Failed to inform the error to the end-user: {ee}", ee);
}
}
});

assistant.userMessageWithFiles((req, ctx) -> {
try {
ctx.setStatus("is analyzing the files...");
Thread.sleep(500L);
ctx.setStatus("is still checking the files...");
Thread.sleep(500L);
ctx.say(r -> r.text("Your files do not have any issues!"));
} catch (Exception e) {
ctx.logger.error("Failed to handle assistant user message event: {e}", e);
try {
ctx.say(r -> r.text(":warning: Sorry, something went wrong during processing your request!"));
} catch (Exception ee) {
ctx.logger.error("Failed to inform the error to the end-user: {ee}", ee);
}
}
});

app.use(assistant);

app.event(MessageEvent.class, (req, ctx) -> {
return ctx.ack();
});

app.event(AppMentionEvent.class, (req, ctx) -> {
ctx.say("You can help you at our 1:1 DM!");
return ctx.ack();
});

new SocketModeApp(appToken, app).start();
}
}
21 changes: 19 additions & 2 deletions bolt/src/main/java/com/slack/api/bolt/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ protected List<Middleware> buildDefaultMiddlewareList(AppConfig config) {

// ignoring the events generated by this bot user
if (config.isIgnoringSelfEventsEnabled()) {
middlewareList.add(new IgnoringSelfEvents(config.getSlack().getConfig()));
middlewareList.add(new IgnoringSelfEvents(
config.getSlack().getConfig(),
config.isIgnoringSelfAssistantMessageEventsEnabled()
));
}

return middlewareList;
Expand Down Expand Up @@ -307,6 +310,8 @@ public App enableTokenRevocationHandlers() {

private OAuthCallbackService oAuthCallbackService; // will be initialized by initOAuthServicesIfNecessary()

private AssistantThreadContextService assistantThreadContextService;

private void initOAuthServicesIfNecessary() {
if (appConfig.isDistributedApp() && appConfig.isOAuthRedirectUriPathEnabled()) {
if (this.oAuthCallbackService == null) {
Expand Down Expand Up @@ -605,6 +610,13 @@ public App use(Middleware middleware) {
return this;
}

public App assistant(Assistant assistant) {
if (this.assistantThreadContextService != null && assistant.getThreadContextService() == null) {
assistant.setThreadContextService(this.assistantThreadContextService);
}
return this.use(assistant);
}

// ----------------------
// App routing methods

Expand All @@ -613,7 +625,7 @@ public App use(Middleware middleware) {
// https://api.slack.com/events-api

public <E extends Event> App event(
Class<E> eventClass,BoltEventHandler<E> handler) {
Class<E> eventClass, BoltEventHandler<E> handler) {
// Note that having multiple handlers is allowed only for message event handlers.
// If we revisit this to unlock the option to all event types, it should work well.
// We didn't decide to do so in 2022 in respect of better backward compatibility.
Expand Down Expand Up @@ -936,6 +948,11 @@ public App asOpenIDConnectApp(boolean enabled) {
return this;
}

public App service(AssistantThreadContextService assistantThreadContextService) {
this.assistantThreadContextService = assistantThreadContextService;
return this;
}

public App service(OAuthCallbackService oAuthCallbackService) {
this.oAuthCallbackService = oAuthCallbackService;
putServiceInitializer(OAuthCallbackService.class, oAuthCallbackService.initializer());
Expand Down
3 changes: 3 additions & 0 deletions bolt/src/main/java/com/slack/api/bolt/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -404,4 +404,7 @@ public void setOauthRedirectUriPath(String oauthRedirectUriPath) {
@Builder.Default
private boolean ignoringSelfEventsEnabled = true;

@Builder.Default
private boolean ignoringSelfAssistantMessageEventsEnabled = true;

}
Loading

0 comments on commit f65dc3d

Please sign in to comment.