All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codelibs.fess.ds.slack.SlackClient Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012-2024 CodeLibs Project and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.codelibs.fess.ds.slack;

import java.io.Closeable;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;

import org.codelibs.core.lang.StringUtil;
import org.codelibs.curl.Curl;
import org.codelibs.curl.CurlResponse;
import org.codelibs.fess.Constants;
import org.codelibs.fess.ds.slack.api.Authentication;
import org.codelibs.fess.ds.slack.api.method.bots.BotsInfoRequest;
import org.codelibs.fess.ds.slack.api.method.chat.ChatGetPermalinkRequest;
import org.codelibs.fess.ds.slack.api.method.conversations.ConversationsHistoryRequest;
import org.codelibs.fess.ds.slack.api.method.conversations.ConversationsHistoryResponse;
import org.codelibs.fess.ds.slack.api.method.conversations.ConversationsInfoRequest;
import org.codelibs.fess.ds.slack.api.method.conversations.ConversationsListRequest;
import org.codelibs.fess.ds.slack.api.method.conversations.ConversationsListResponse;
import org.codelibs.fess.ds.slack.api.method.conversations.ConversationsRepliesRequest;
import org.codelibs.fess.ds.slack.api.method.conversations.ConversationsRepliesResponse;
import org.codelibs.fess.ds.slack.api.method.files.FilesInfoRequest;
import org.codelibs.fess.ds.slack.api.method.files.FilesListRequest;
import org.codelibs.fess.ds.slack.api.method.files.FilesListResponse;
import org.codelibs.fess.ds.slack.api.method.team.TeamInfoRequest;
import org.codelibs.fess.ds.slack.api.method.users.UsersInfoRequest;
import org.codelibs.fess.ds.slack.api.method.users.UsersListRequest;
import org.codelibs.fess.ds.slack.api.method.users.UsersListResponse;
import org.codelibs.fess.ds.slack.api.type.Bot;
import org.codelibs.fess.ds.slack.api.type.Channel;
import org.codelibs.fess.ds.slack.api.type.File;
import org.codelibs.fess.ds.slack.api.type.Message;
import org.codelibs.fess.ds.slack.api.type.Team;
import org.codelibs.fess.ds.slack.api.type.User;
import org.codelibs.fess.entity.DataStoreParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

public class SlackClient implements Closeable {

    private static final Logger logger = LoggerFactory.getLogger(SlackClient.class);

    protected static final String TOKEN_PARAM = "token";
    protected static final String INCLUDE_PRIVATE_PARAM = "include_private";
    protected static final String CHANNELS_PARAM = "channels";
    protected static final String CHANNELS_ALL = "*all";
    protected static final String CHANNELS_SEPARATOR = ",";
    protected static final String CHANNEL_COUNT_PARAM = "channel_count";
    protected static final String USER_COUNT_PARAM = "user_count";
    protected static final String MESSAGE_COUNT_PARAM = "message_count";
    protected static final String FILE_COUNT_PARAM = "file_count";
    protected static final String PROXY_HOST_PARAM = "proxy_host";
    protected static final String PROXY_PORT_PARAM = "proxy_port";
    protected static final String FILE_TYPES_PARAM = "file_types";

    protected static final String USER_CACHE_SIZE_PARAM = "user_cache_size";
    protected static final String BOT_CACHE_SIZE_PARAM = "bot_cache_size";
    protected static final String CHANNEL_CACHE_SIZE_PARAM = "channel_cache_size";

    protected static final String DEFAULT_CHANNEL_COUNT = "100";
    protected static final String DEFAULT_USER_COUNT = "100";
    protected static final String DEFAULT_MESSAGE_COUNT = "100";
    protected static final String DEFAULT_FILE_COUNT = "20";
    protected static final String DEFAULT_CACHE_SIZE = "10000";

    protected final Boolean includePrivate;
    protected final Authentication authentication;
    protected DataStoreParams paramMap;
    protected LoadingCache usersCache;
    protected LoadingCache botsCache;
    protected LoadingCache channelsCache;

    public SlackClient(final DataStoreParams paramMap) {
        final String token = getToken(paramMap);

        if (token.isEmpty()) {
            throw new SlackDataStoreException("Parameter " + TOKEN_PARAM + " required");
        }

        this.paramMap = paramMap;
        includePrivate = isIncludePrivate(paramMap);

        authentication = new Authentication(token);

        final String httpProxyHost = getProxyHost(paramMap);
        final String httpProxyPort = getProxyPort(paramMap);
        if (!httpProxyHost.isEmpty()) {
            if (httpProxyPort.isEmpty()) {
                throw new SlackDataStoreException("parameter " + "'" + PROXY_PORT_PARAM + "' required.");
            }
            try {
                authentication.setHttpProxy(httpProxyHost, Integer.parseInt(httpProxyPort));
            } catch (final NumberFormatException e) {
                throw new SlackDataStoreException("parameter " + "'" + PROXY_PORT_PARAM + "' invalid.", e);
            }
        }

        usersCache =
                CacheBuilder.newBuilder().maximumSize(Integer.parseInt(paramMap.getAsString(USER_CACHE_SIZE_PARAM, DEFAULT_CACHE_SIZE)))
                        .build(new CacheLoader() {
                            @Override
                            public User load(final String key) {
                                return usersInfo(key).execute().getUser();
                            }
                        });
        botsCache = CacheBuilder.newBuilder().maximumSize(Integer.parseInt(paramMap.getAsString(BOT_CACHE_SIZE_PARAM, DEFAULT_CACHE_SIZE)))
                .build(new CacheLoader() {
                    @Override
                    public Bot load(final String key) {
                        return botsInfo().bot(key).execute().getBot();
                    }
                });
        channelsCache =
                CacheBuilder.newBuilder().maximumSize(Integer.parseInt(paramMap.getAsString(CHANNEL_CACHE_SIZE_PARAM, DEFAULT_CACHE_SIZE)))
                        .build(new CacheLoader() {
                            @Override
                            public Channel load(final String key) {
                                return conversationsInfo(key).execute().getChannel();
                            }
                        });
        // Initialize caches to avoid exceeding the rate limit of the Slack API
        getUsers(user -> {
            usersCache.put(user.getId(), user);
            usersCache.put(user.getName(), user);
        });
        getAllChannels(channel -> {
            channelsCache.put(channel.getId(), channel);
            channelsCache.put(channel.getName(), channel);
        });
    }

    public BotsInfoRequest botsInfo() {
        return new BotsInfoRequest(authentication);
    }

    public ChatGetPermalinkRequest chatGetPermalink(final String channel, final String ts) {
        return new ChatGetPermalinkRequest(authentication, channel, ts);
    }

    public ConversationsListRequest conversationsList() {
        return new ConversationsListRequest(authentication);
    }

    public ConversationsHistoryRequest conversationsHistory(final String channel) {
        return new ConversationsHistoryRequest(authentication, channel);
    }

    public ConversationsInfoRequest conversationsInfo(final String channel) {
        return new ConversationsInfoRequest(authentication, channel);
    }

    public ConversationsRepliesRequest conversationsReplies(final String channel, final String ts) {
        return new ConversationsRepliesRequest(authentication, channel, ts);
    }

    public FilesListRequest filesList() {
        return new FilesListRequest(authentication);
    }

    public FilesInfoRequest filesInfo(final String file) {
        return new FilesInfoRequest(authentication, file);
    }

    public TeamInfoRequest teamInfo() {
        return new TeamInfoRequest(authentication);
    }

    public UsersListRequest usersList() {
        return new UsersListRequest(authentication);
    }

    public UsersInfoRequest usersInfo(final String user) {
        return new UsersInfoRequest(authentication, user);
    }

    @Override
    public void close() {
        // TODO
        usersCache.invalidateAll();
        botsCache.invalidateAll();
        channelsCache.invalidateAll();
    }

    protected String getToken(final DataStoreParams paramMap) {
        return paramMap.getAsString(TOKEN_PARAM, StringUtil.EMPTY);
    }

    protected Boolean isIncludePrivate(final DataStoreParams paramMap) {
        return Constants.TRUE.equalsIgnoreCase(paramMap.getAsString(INCLUDE_PRIVATE_PARAM, Constants.FALSE));
    }

    protected String getProxyHost(final DataStoreParams paramMap) {
        return paramMap.getAsString(PROXY_HOST_PARAM, StringUtil.EMPTY);
    }

    protected String getProxyPort(final DataStoreParams paramMap) {
        return paramMap.getAsString(PROXY_PORT_PARAM, StringUtil.EMPTY);
    }

    protected String getTypes() {
        return includePrivate ? "public_channel,private_channel" : "public_channel";
    }

    protected String getFileTypes() {
        return paramMap.getAsString(FILE_TYPES_PARAM, "all");
    }

    public Team getTeam() {
        return teamInfo().execute().getTeam();
    }

    public Bot getBot(final String botName) throws ExecutionException {
        return botsCache.get(botName);
    }

    public User getUser(final String userName) throws ExecutionException {
        return usersCache.get(userName);
    }

    public Channel getChannel(final String channelName) throws ExecutionException {
        return channelsCache.get(channelName);
    }

    public String getPermalink(final String channelId, final String threadTs) {
        return chatGetPermalink(channelId, threadTs).execute().getPermalink();
    }

    public CurlResponse getFileResponse(final String fileUrl) {
        return Curl.get(fileUrl).header("Authorization", "Bearer " + getToken(paramMap))
                .header("Content-type", "application/x-www-form-urlencoded ").execute();
    }

    public void getChannels(final Consumer consumer) {
        if (!paramMap.containsKey(CHANNELS_PARAM) || CHANNELS_ALL.equals(paramMap.get(CHANNELS_PARAM))) {
            getAllChannels(consumer);
        } else {
            for (final String name : paramMap.getAsString(CHANNELS_PARAM, StringUtil.EMPTY).split(CHANNELS_SEPARATOR)) {
                try {
                    consumer.accept(getChannel(name));
                } catch (final ExecutionException e) {
                    logger.warn("Failed to get a channel.", e);
                }
            }
        }
    }

    public void getChannelFiles(final String channelId, final Consumer consumer) {
        getChannelFiles(channelId, Integer.parseInt(paramMap.getAsString(FILE_COUNT_PARAM, DEFAULT_FILE_COUNT)), consumer);
    }

    public void getChannelFiles(final String channelId, final Integer count, final Consumer consumer) {
        FilesListResponse response = filesList().channel(channelId).types(getFileTypes()).count(count).execute();
        while (true) {
            if (!response.ok()) {
                logger.warn("Slack API error occured on \"files.list\": {}", response.responseBody());
                return;
            }
            response.getFiles().forEach(consumer);
            if (response.getPaging().getPage() >= response.getPaging().getTotal()) {
                break;
            }
            response = filesList().channel(channelId).count(count).page(response.getPaging().getPage() + 1).execute();
        }
    }

    public void getAllChannels(final Consumer consumer) {
        getAllChannels(Integer.parseInt(paramMap.getAsString(CHANNEL_COUNT_PARAM, DEFAULT_CHANNEL_COUNT)), consumer);
    }

    public void getAllChannels(final Integer limit, final Consumer consumer) {
        ConversationsListResponse response = conversationsList().types(getTypes()).limit(limit).execute();
        while (true) {
            if (!response.ok()) {
                logger.warn("Slack API error occured on \"conversations.list\": {}", response.responseBody());
                return;
            }
            response.getChannels().forEach(consumer);
            final String nextCursor = response.getResponseMetadata().getNextCursor();
            if (nextCursor.isEmpty()) {
                break;
            }
            response = conversationsList().types(getTypes()).limit(limit).cursor(nextCursor).execute();
        }
    }

    public void getChannelMessages(final String channelId, final Consumer consumer) {
        getChannelMessages(channelId, Integer.parseInt(paramMap.getAsString(MESSAGE_COUNT_PARAM, DEFAULT_MESSAGE_COUNT)), consumer);
    }

    public void getChannelMessages(final String channelId, final Integer limit, final Consumer consumer) {
        ConversationsHistoryResponse response = conversationsHistory(channelId).limit(limit).execute();
        while (true) {
            if (!response.ok()) {
                logger.warn("Slack API error occured on \"conversations.history\": {}", response.responseBody());
                return;
            }
            response.getMessages().forEach(consumer);
            if (!response.hasMore()) {
                break;
            }
            response = conversationsHistory(channelId).limit(limit).cursor(response.getResponseMetadata().getNextCursor()).execute();
        }
    }

    public void getMessageReplies(final String channelId, final String threadTs, final Consumer consumer) {
        getMessageReplies(channelId, threadTs, Integer.parseInt(paramMap.getAsString(MESSAGE_COUNT_PARAM, DEFAULT_MESSAGE_COUNT)),
                consumer);
    }

    public void getMessageReplies(final String channelId, final String threadTs, final Integer limit, final Consumer consumer) {
        ConversationsRepliesResponse response = conversationsReplies(channelId, threadTs).limit(limit).execute();
        while (true) {
            if (!response.ok()) {
                logger.warn("Slack API error occured on \"conversations.replies\": {}", response.responseBody());
                return;
            }
            final List messages = response.getMessages();
            for (int i = 1; i < messages.size(); i++) {
                final Message message = messages.get(i);
                if (message.isThreadBroadcast()) {
                    continue;
                }
                consumer.accept(message);
            }
            if (!response.hasMore()) {
                break;
            }
            response =
                    conversationsReplies(channelId, threadTs).limit(limit).cursor(response.getResponseMetadata().getNextCursor()).execute();
        }
    }

    public void getUsers(final Consumer consumer) {
        getUsers(Integer.parseInt(paramMap.getAsString(USER_COUNT_PARAM, DEFAULT_USER_COUNT)), consumer);
    }

    public void getUsers(final Integer limit, final Consumer consumer) {
        UsersListResponse response = usersList().limit(limit).execute();
        while (true) {
            if (!response.ok()) {
                logger.warn("Slack API error occured on \"users.list\": {}", response.responseBody());
                return;
            }
            response.getMembers().forEach(consumer);
            final String nextCursor = response.getResponseMetadata().getNextCursor();
            if (nextCursor.isEmpty()) {
                break;
            }
            response = usersList().limit(limit).cursor(nextCursor).execute();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy