Skip to content

Commit

Permalink
Merge pull request #422 from deutschebank/spring-bot-master-db
Browse files Browse the repository at this point in the history
Upload file feature for MS Teams
  • Loading branch information
vaibhav-db committed May 15, 2024
2 parents 9571941 + 0f3b4e4 commit c4e7505
Show file tree
Hide file tree
Showing 13 changed files with 341 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.finos.springbot.tests.controller;



import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -268,5 +270,13 @@ public void testButtonPress() throws Exception {
Assertions.assertEquals("desc", sc.description);

}

@Test
public void testAttachmentResponse() throws Exception {
execute("attachment");
Assertions.assertEquals("attachment", oc.lastMethod);
String data = getMessageContent();
Assertions.assertEquals("somefile.txt", data);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.finos.springbot.workflow.java.resolvers;

import java.util.Optional;

import org.finos.springbot.workflow.annotations.ChatButton;
import org.finos.springbot.workflow.java.mapping.ChatHandlerExecutor;

/**
* Get chat button detail in controller
* @author mankvaia
*
*/
public class ChatButtonWorkflowResolverFactory implements WorkflowResolverFactory {


private final class ChatButtonWorkflowResolver extends AbstractClassWorkflowResolver {
private final ChatHandlerExecutor che;

private ChatButtonWorkflowResolver(ChatHandlerExecutor che) {
this.che = che;
}

@Override
public Optional<Object> resolve(Class<?> cl) {
if (ChatButton.class.isAssignableFrom(cl) && che.getOriginatingMapping().getMapping() instanceof ChatButton) {

return Optional.of((ChatButton) che.getOriginatingMapping().getMapping());

} else {
return Optional.empty();
}
}


@Override
public boolean canResolve(Class<?> cl) {
return resolve(cl).isPresent();
}
}

@Override
public WorkflowResolver createResolver(ChatHandlerExecutor che) {
return new ChatButtonWorkflowResolver(che);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,19 @@ public FormDataArgumentWorkflowResolverFactory formDataArgumentWorkflowResolverF
return new FormDataArgumentWorkflowResolverFactory();
}

//to get Chat button detail in controller
@Bean
@ConditionalOnMissingBean
public ChatButtonWorkflowResolverFactory chatButtonWorkflowResolverFactory() {
return new ChatButtonWorkflowResolverFactory();
}

@Bean
@ConditionalOnMissingBean
public ChatVariableWorkflowResolverFactory chatVariableWorkflowResolverFactory() {
return new ChatVariableWorkflowResolverFactory();
}

@Bean
@ConditionalOnMissingBean
public AddressableWorkflowResolverFactory addressableWorkflowResolverFactory() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.io.IOException;
import java.util.List;

import javax.annotation.PostConstruct;

import org.finos.springbot.ChatWorkflowConfig;
import org.finos.springbot.teams.bot.BotController;
import org.finos.springbot.teams.content.TeamsContentConfig;
Expand All @@ -15,12 +13,14 @@
import org.finos.springbot.teams.form.TeamsFormConverter;
import org.finos.springbot.teams.form.TeamsFormDeserializerModule;
import org.finos.springbot.teams.handlers.ActivityHandler;
import org.finos.springbot.teams.handlers.AttachmentHandler;
import org.finos.springbot.teams.handlers.SimpleActivityHandler;
import org.finos.springbot.teams.handlers.SimpleAttachmentHandler;
import org.finos.springbot.teams.handlers.TeamsResponseHandler;
import org.finos.springbot.teams.history.StateStorageBasedTeamsHistory;
import org.finos.springbot.teams.history.StorageIDResponseHandler;
import org.finos.springbot.teams.history.TeamsHistory;
import org.finos.springbot.teams.messages.MessageActivityHandler;
import org.finos.springbot.teams.messages.FileActivityHandler;
import org.finos.springbot.teams.response.templating.EntityMarkupTemplateProvider;
import org.finos.springbot.teams.state.AzureBlobStateStorage;
import org.finos.springbot.teams.state.FileStateStorage;
Expand Down Expand Up @@ -60,6 +60,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.builder.teams.TeamsActivityHandler;
import com.microsoft.bot.integration.BotFrameworkHttpAdapter;
import com.microsoft.bot.schema.ChannelAccount;

Expand Down Expand Up @@ -122,16 +123,25 @@ public ThymeleafTemplateProvider thymeleafWorkTemplater(
return new ThymeleafTemplateProvider(prefix, suffix, defaultName, resourceLoader, formConverter);
}



@Bean
@ConditionalOnMissingBean
public AttachmentHandler attachmentHandler() {
return new SimpleAttachmentHandler();
}

@Bean
@ConditionalOnMissingBean
public TeamsResponseHandler teamsResponseHandler(
AttachmentHandler attachmentHandler,
EntityMarkupTemplateProvider markupTemplater,
AdaptiveCardTemplateProvider formTemplater,
ThymeleafTemplateProvider displayTemplater,
TeamsStateStorage th,
ActivityHandler ah) {
return new TeamsResponseHandler(
null, // attachment handler
attachmentHandler, // attachment handler
markupTemplater,
formTemplater,
displayTemplater,
Expand Down Expand Up @@ -200,21 +210,33 @@ public TeamsFormConverter teamsFormConverter(AllConversations tc) {
return new TeamsFormConverter(om);
}

// @Bean
// @ConditionalOnMissingBean
// public MessageActivityHandler teamsMessageActivityHandler(
// List<ActionConsumer> messageConsumers,
// TeamsHTMLParser parser,
// FormValidationProcessor fvp,
// TeamsConversations tc,
// TeamsStateStorage teamsStateStorage,
// TeamsFormConverter fc) {
// return new MessageActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp);
// }

@Bean
@ConditionalOnMissingBean
public MessageActivityHandler teamsMessageActivityHandler(
public TeamsActivityHandler teamsActivityHandler(
List<ActionConsumer> messageConsumers,
TeamsHTMLParser parser,
FormValidationProcessor fvp,
TeamsConversations tc,
TeamsStateStorage teamsStateStorage,
TeamsFormConverter fc) {
return new MessageActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp);
return new FileActivityHandler(messageConsumers, tc, teamsStateStorage, parser, fc, fvp);
}

@Bean
@ConditionalOnMissingBean
public BotController teamsBotController(MessageActivityHandler mah, BotFrameworkHttpAdapter bfa) {
public BotController teamsBotController(TeamsActivityHandler mah, BotFrameworkHttpAdapter bfa) {
return new BotController(bfa, mah);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.finos.springbot.workflow.response.AttachmentResponse;

import com.microsoft.bot.schema.Attachment;

public interface AttachmentHandler {

public Object formatAttachment(AttachmentResponse ar);
public Attachment formatAttachment(AttachmentResponse ar);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.finos.springbot.teams.handlers;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;

import org.finos.springbot.workflow.response.AttachmentResponse;

import com.microsoft.bot.schema.Attachment;
import com.microsoft.bot.schema.teams.FileConsentCard;

public class SimpleAttachmentHandler implements AttachmentHandler {

public Attachment formatAttachment(AttachmentResponse ar) {

try {
Attachment a = new Attachment();
String fileName = ar.getName().replaceAll("\\s+", "_") + "." + ar.getExtension();
File f = null;
f = File.createTempFile("temp", fileName);
Files.write(f.toPath(), ar.getAttachment());

Map<String, String> consentContext = new HashMap<>();
consentContext.put("filename", fileName);
consentContext.put("filepath", f.getAbsolutePath());

FileConsentCard fileCard = new FileConsentCard();
fileCard.setDescription("This is the file I want to send you");
fileCard.setSizeInBytes(ar.getAttachment().length);
fileCard.setAcceptContext(consentContext);
fileCard.setDeclineContext(consentContext);

a.setContent(fileCard);
a.setName(fileName);
a.setContentType(FileConsentCard.CONTENT_TYPE);
return a;
} catch (IOException e) {
throw new RuntimeException(e);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.BiFunction;
Expand Down Expand Up @@ -93,7 +94,7 @@ public ResourceResponse apply(Response t) {
try {
if (t instanceof MessageResponse) {
MessageResponse mr = (MessageResponse)t;
Object attachment = null;
Attachment attachment = null;
MarkupAndEntities mae = messageTemplater.template(mr);
String content = mae.getContents();
List<Entity> entities = mae.getEntities();
Expand Down Expand Up @@ -149,10 +150,14 @@ protected TemplateType getTemplateType(WorkResponse wr) {
return tt;
}

protected CompletableFuture<ResourceResponse> sendXMLResponse(String xml, Object attachment, TeamsAddressable address, List<Entity> entities, Map<String, Object> data) throws Exception {
protected CompletableFuture<ResourceResponse> sendXMLResponse(String xml, Attachment attachment, TeamsAddressable address, List<Entity> entities, Map<String, Object> data) throws Exception {
Activity out = Activity.createMessageActivity();
out.setEntities(entities);
out.setTextFormat(TextFormatTypes.XML);
if(Objects.nonNull(attachment)) {
out.getAttachments().add(attachment);
}else {
out.setEntities(entities);
out.setTextFormat(TextFormatTypes.XML);
}
out.setText(xml);
return ah.handleActivity(out, address);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@

import org.finos.springbot.teams.content.TeamsAddressable;
import org.finos.springbot.teams.conversations.TeamsConversations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ResourceResponse;

public class InMemoryRetryingActivityHandler extends AbstractRetryingActivityHandler {

protected static final Logger LOG = LoggerFactory.getLogger(InMemoryRetryingActivityHandler.class);

static final ScheduledExecutorService SCHEDULER = new ScheduledThreadPoolExecutor(10);

public InMemoryRetryingActivityHandler(TeamsConversations tc) {
Expand All @@ -29,6 +33,9 @@ public CompletableFuture<ResourceResponse> handleActivity(Activity activity, Tea
f = f.thenApply(CompletableFuture::completedFuture).exceptionally(t -> {
if (isTooManyRequest(t)) {
int ra = getRetryAfter((CompletionException) t);

LOG.info("message inserted into InMemory Retry after {} with address id {}",ra, to.getKey());

Executor afterRetryTime = createDelayedExecutor(ra, TimeUnit.SECONDS);
return CompletableFuture.supplyAsync(() -> null, afterRetryTime)
.thenCompose(m -> tc.handleActivity(activity, to));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*/
public class StateStorageBasedTeamsHistory implements TeamsHistory {

private static final Logger LOG = LoggerFactory.getLogger(MessageActivityHandler.class);
private static final Logger LOG = LoggerFactory.getLogger(StateStorageBasedTeamsHistory.class);

public final TeamsStateStorage tss;

Expand Down

0 comments on commit c4e7505

Please sign in to comment.