com.vspr.ai.slack.service.SlackAPIImpl Maven / Gradle / Ivy
package com.vspr.ai.slack.service;
import static java.lang.Integer.parseInt;
import static java.lang.Thread.sleep;
import static java.util.Optional.ofNullable;
import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.apache.commons.lang3.StringEscapeUtils.unescapeXml;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import com.google.common.annotations.VisibleForTesting;
import com.vspr.ai.slack.api.ListUsersResponse;
import com.vspr.ai.slack.api.Message;
import com.vspr.ai.slack.api.OauthAccessResponse;
import com.vspr.ai.slack.api.SlackMessageResponse;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by cobb on 7/18/17.
*/
public class SlackAPIImpl implements SlackAPI {
private static final Logger logger = LoggerFactory.getLogger(SlackAPIImpl.class);
private static ObjectMapper MAPPER = new ObjectMapper().registerModule(new Jdk8Module());
private static String SLACK_BASE_WEB_API = "https://slack.com/api";
private static final String POST_MESSAGE = "/chat.postMessage";
private static final String LIST_USERS = "users.list";
private static final String OAUTH_ACCESS = "oauth.access";
private final Client client;
private final String clientId;
private final String clientSecret;
private final int maxRetries;
SlackAPIImpl(String clientId,
String clientSecret,
Client client, int maxRetries) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.maxRetries = maxRetries;
JacksonJaxbJsonProvider jacksonProvider = new JacksonJaxbJsonProvider();
jacksonProvider.setMapper(MAPPER);
this.client = client.register(jacksonProvider);
}
@Override
public SlackMessageResponse postMessage(Message message) {
return rateLimitAwareRequest(() -> client.target(postMessageUri())
.request()
.post(Entity.entity(toMap(message), APPLICATION_FORM_URLENCODED),
SlackMessageResponse.class));
}
@Override
public void postMessageToResponseUrl(Message message, URI uri) {
rateLimitAwareRequest(() -> client.target(uri)
.request()
.post(Entity.entity(message, APPLICATION_JSON),
Response.class));
}
@Override
public ListUsersResponse listUsers(String token, @Nullable String cursor, int limit,
boolean presence) {
MultivaluedMap requestMap = new MultivaluedHashMap<>();
requestMap.putSingle("token", token);
ofNullable(cursor).ifPresent(cursorValue -> requestMap.putSingle("cursor", cursorValue));
requestMap.putSingle("limit", Integer.toString(limit));
requestMap.putSingle("presence", Boolean.toString(presence));
return rateLimitAwareRequest(() -> client.target(listUsersUri())
.request()
.post(Entity.entity(requestMap, APPLICATION_FORM_URLENCODED),
ListUsersResponse.class));
}
@Override
public OauthAccessResponse getAccess(String code) {
MultivaluedMap requestMap = new MultivaluedHashMap<>();
requestMap.putSingle("code", code);
requestMap.putSingle("client_id", clientId);
requestMap.putSingle("client_secret", clientSecret);
return rateLimitAwareRequest(() -> client.target(oauthAccessUri())
.request()
.post(Entity.entity(requestMap, APPLICATION_FORM_URLENCODED),
OauthAccessResponse.class));
}
@VisibleForTesting
MultivaluedMap toMap(Message message) {
MultivaluedMap retValue = new MultivaluedHashMap<>();
message.getToken().ifPresent((token) -> retValue.putSingle("token", token));
message.getChannel().ifPresent((channel) -> retValue.putSingle("channel", channel));
retValue.putSingle("text", unescapeXml(message.getText()));
retValue.putSingle("as_user", Boolean.toString(message.getAsUser()));
message.getThreadTs().ifPresent(threadTs -> retValue.putSingle("thread_ts", threadTs));
if (message.getAttachments().size() > 0) {
try {
retValue.putSingle("attachments", MAPPER.writeValueAsString(message.getAttachments()));
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
}
return retValue;
}
@VisibleForTesting
URI postMessageUri() {
try {
return UriBuilder.fromUri(new URI(SLACK_BASE_WEB_API))
.path(POST_MESSAGE)
.build();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
@VisibleForTesting
URI listUsersUri() {
try {
return UriBuilder.fromUri(new URI(SLACK_BASE_WEB_API))
.path(LIST_USERS)
.build();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
@VisibleForTesting
URI oauthAccessUri() {
try {
return UriBuilder.fromUri(new URI(SLACK_BASE_WEB_API))
.path(OAUTH_ACCESS)
.build();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
@VisibleForTesting
T rateLimitAwareRequest(Supplier supplier) {
int counter = 0;
do {
try {
return supplier.get();
} catch (WebApplicationException e) {
Response response = e.getResponse();
if (response.getStatus() != 429) {
throw (e);
}
logger.warn("An 429 was received from slack. I will retry.", e);
try {
int retrySeconds = parseInt(response.getHeaderString("Retry-After"));
logger.warn("Retrying after {} seconds", retrySeconds);
sleep(retrySeconds * 1000);
} catch (InterruptedException e1) {
throw new IllegalArgumentException(e);
}
}
counter++;
} while (counter <= maxRetries);
logger.error("Unable to finish request after {} retries.", counter);
throw new IllegalArgumentException("Unable to finish request.");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy