org.mule.modules.slack.SlackConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.modules.slack.SlackConnector.java

Source

/**
 * (c) 2003-2015 MuleSoft, Inc. The software in this package is published under the terms of the CPAL v1.0 license,
 * a copy of which has been included with this distribution in the LICENSE.md file.
 */

package org.mule.modules.slack;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.mule.api.MuleContext;
import org.mule.api.MuleEvent;
import org.mule.api.annotations.*;
import org.mule.api.annotations.display.FriendlyName;
import org.mule.api.annotations.display.Path;
import org.mule.api.annotations.display.Placement;
import org.mule.api.annotations.display.Summary;
import org.mule.api.annotations.oauth.OAuthProtected;
import org.mule.api.annotations.param.Default;
import org.mule.api.annotations.param.MetaDataKeyParam;
import org.mule.api.annotations.param.Optional;
import org.mule.api.callback.SourceCallback;
import org.mule.modules.slack.client.SlackClient;
import org.mule.modules.slack.client.exceptions.SlackException;
import org.mule.modules.slack.client.exceptions.UserNotFoundException;
import org.mule.modules.slack.client.model.User;
import org.mule.modules.slack.client.model.channel.Channel;
import org.mule.modules.slack.client.model.chat.Message;
import org.mule.modules.slack.client.model.chat.MessageResponse;
import org.mule.modules.slack.client.model.chat.attachment.ChatAttachment;
import org.mule.modules.slack.client.model.file.FileUploadResponse;
import org.mule.modules.slack.client.model.group.Group;
import org.mule.modules.slack.client.model.im.DirectMessageChannel;
import org.mule.modules.slack.client.model.im.DirectMessageChannelCreationResponse;
import org.mule.modules.slack.client.rtm.ConfigurableHandler;
import org.mule.modules.slack.client.rtm.filter.*;
import org.mule.modules.slack.config.BasicSlackConfig;
import org.mule.modules.slack.config.SlackOAuth2Config;
import org.mule.modules.slack.metadata.AllChannelCategory;
import org.mule.modules.slack.metadata.ChannelCategory;
import org.mule.modules.slack.metadata.GroupCategory;
import org.mule.modules.slack.metadata.UserCategory;
import org.mule.modules.slack.retrievers.ChannelMessageRetriever;
import org.mule.modules.slack.retrievers.DirectMessageRetriever;
import org.mule.modules.slack.retrievers.GroupMessageRetriever;
import org.mule.modules.slack.retrievers.MessageRetriever;

import javax.inject.Inject;
import javax.websocket.DeploymentException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Slack Anypoint Connector
 *
 * @author Esteban Wasinger
 */

@Connector(name = "slack", friendlyName = "Slack", minMuleVersion = "3.5.0")
public class SlackConnector {

    private static final Logger logger = Logger.getLogger(SlackConnector.class);
    private static final String NUMBER_OF_MESSAGES = "Number of messages to return, the value should be between 1 and 1000.";
    public static final String USER_TYPING_EVENT = "user_typing";
    private static final String IM_CREATED = "im_created";
    private static final String FILE_CREATED = "file_created";
    private static final String FILE_SHARED = "file_shared";
    private static final String FILE_PUBLIC = "file_public";

    @Inject
    MuleContext muleContext;

    public MuleContext getMuleContext() {
        return muleContext;
    }

    public void setMuleContext(MuleContext muleContext) {
        this.muleContext = muleContext;
    }

    @Config
    BasicSlackConfig slackConfig;

    //***********
    // Users methods
    //***********

    /**
     * This processor returns information about a team member.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-user-info}
     *
     * @param id ID of the desired User
     * @return The desired User
     * @throws org.mule.modules.slack.client.exceptions.UserNotFoundException When the user doesn't exist
     */

    @OAuthProtected
    @Processor(friendlyName = "User - Info")
    @Summary("This processor returns information about a team member.")
    @MetaDataScope(UserCategory.class)
    public User getUserInfo(
            @MetaDataKeyParam @Summary("User ID to get info on") @FriendlyName("User ID") String id) {
        return slack().getUserInfo(id);
    }

    /**
     * This processor returns information about a team member.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-user-info-by-name}
     *
     * @param username Username of the desired User
     * @return The desired User
     * @throws org.mule.modules.slack.client.exceptions.UserNotFoundException When the user doesn't exist
     */

    @OAuthProtected
    @Processor(friendlyName = "User - Info by name")
    @Summary("This processor returns information about a team member.")
    public User getUserInfoByName(@Summary("User name to get info on") @FriendlyName("Username") String username)
            throws UserNotFoundException {
        return slack().getUserInfoByName(username);
    }

    /**
     * This processor returns a list of all users in the team. This includes deleted/deactivated users.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-user-list}
     *
     * @return List of Slack Users
     */

    @OAuthProtected
    @Summary("This processor returns a list of all users in the team. This includes deleted/deactivated users.")
    @Processor(friendlyName = "User - List")
    public List<User> getUserList() {
        return slack().getUserList();
    }

    //***********
    // Channels methods
    //***********

    /**
     * This processor returns a list of all channels in the team. This includes channels the caller is in, channels they are not currently in, and archived channels. The number of (non-deactivated) members in each channel is also returned.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-channel-list}
     *
     * @return List of Slack Channels
     */

    @OAuthProtected
    @Summary("This processor returns a list of all channels in the team. This includes channels the caller is in, channels they are not currently in, and archived channels. The number of (non-deactivated) members in each channel is also returned.")
    @Processor(friendlyName = "Channel - List")
    public List<Channel> getChannelList() {
        return slack().getChannelList();
    }

    /**
     * This processor returns a portion of messages/events from the specified channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-channel-history}
     *
     * @param channelId       Channel to fetch history for
     * @param latestTimestamp End of time range of messages to include in results. Leave it blank to select current time.
     * @param oldestTimestamp Start of time range of messages to include in results. Leave it blank for timestamp 0
     * @param mountOfMessages Number of messages to return, the value should be between 1 and 1000.
     * @return List of messages of a Channel
     */

    @OAuthProtected
    @Summary("This processor returns a portion of messages/events from the specified channel.")
    @Processor(friendlyName = "Channel - History")
    @MetaDataScope(ChannelCategory.class)
    public List<Message> getChannelHistory(
            @FriendlyName("Channel ID") @Summary("Channel to fetch history for") @MetaDataKeyParam String channelId,
            @Optional @Summary("End of time range of messages to include in results. Leave it blank to select current time.") String latestTimestamp,
            @Optional @Summary("Start of time range of messages to include in results. Leave it blank for timestamp 0") String oldestTimestamp,
            @Default("100") @Summary(NUMBER_OF_MESSAGES) String mountOfMessages) {
        return slack().getChannelHistory(channelId, latestTimestamp, oldestTimestamp, mountOfMessages);
    }

    /**
     * This processor returns information about a team channel specifying the ID.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-channel-info}
     *
     * @param channelId Channel to get info on
     * @return A Channel
     */

    @OAuthProtected
    @Summary("This processor returns information about a team channel specifying the ID.")
    @Processor(friendlyName = "Channel - Info")
    @MetaDataScope(ChannelCategory.class)
    public Channel getChannelInfo(
            @Summary("Channel to get info on") @FriendlyName("Channel ID") @MetaDataKeyParam String channelId) {
        return slack().getChannelById(channelId);
    }

    /**
     * This processor returns information about a team channel specifyng the name.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-channel-by-name}
     *
     * @param channelName Channel name to get info on
     * @return A Channel
     */

    @OAuthProtected
    @Processor(friendlyName = "Channel - Info by Name")
    public Channel getChannelByName(String channelName) {
        return slack().getChannelByName(channelName);
    }

    /**
     * This processor is used to create a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:create-channel}
     *
     * @param channelName Name of channel to create
     * @return A Channel
     */

    @OAuthProtected
    @Summary("This processor is used to create a channel.")
    @Processor(friendlyName = "Channel - Create")
    public Channel createChannel(@Summary("Name of channel to create") String channelName) {
        return slack().createChannel(channelName);
    }

    /**
     * This method renames a team channel. The only people who can rename a channel are team admins, or the person that originally created the channel. Others will recieve a "not_authorized" error.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:rename-channel}
     *
     * @param channelId   Channel to rename
     * @param channelName New channel name
     * @return A Channel
     */

    @OAuthProtected
    @Summary("This method renames a team channel. The only people who can rename a channel are team admins, or the person that originally created the channel. Others will recieve a \"not_authorized\" error.")
    @Processor(friendlyName = "Channel - Rename")
    @MetaDataScope(ChannelCategory.class)
    public Channel renameChannel(
            @Summary("Channel to rename") @FriendlyName("Channel ID") @MetaDataKeyParam String channelId,
            @Summary("New name for channel") String channelName) {
        return slack().renameChannel(channelId, channelName);
    }

    /**
     * This method is used to join a channel. If the channel does not exist, it is created.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:join-channel}
     *
     * @param channelName Name of the channel to Join
     * @return If successful, the command returns a channel object, including state information:
     */

    @OAuthProtected
    @Processor(friendlyName = "Channel - Join")
    @MetaDataScope(ChannelCategory.class)
    public Channel joinChannel(@MetaDataKeyParam String channelName) {
        return slack().joinChannel(channelName);
    }

    /**
     * This processor is used to leave a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:leave-channel}
     *
     * @param channelId ID of the channel to Leave
     * @return Boolean result. This method will not return an error if the user was not in the channel before it was called.
     */

    @OAuthProtected
    @Processor(friendlyName = "Channel - Leave")
    @MetaDataScope(ChannelCategory.class)
    public Boolean leaveChannel(@FriendlyName("Channel ID") @MetaDataKeyParam String channelId) {
        return slack().leaveChannel(channelId);
    }

    /**
     * This processor archives a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:archive-channel}
     *
     * @param channelID ID of the Channel to Archive
     * @return Boolean result
     */

    @OAuthProtected
    @Processor(friendlyName = "Channel - Archive")
    @MetaDataScope(ChannelCategory.class)
    public Boolean archiveChannel(@MetaDataKeyParam String channelID) {
        return slack().archiveChannel(channelID);
    }

    /**
     * This processor unarchives a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:unarchive-channel}
     *
     * @param channelID ID of the Channel to Unarchive
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Channel - Unarchive")
    @MetaDataScope(ChannelCategory.class)
    public Boolean unarchiveChannel(@MetaDataKeyParam String channelID) {
        return slack().unarchiveChannel(channelID);
    }

    /**
     * This processor is used to change the topic of a channel. The calling user must be a member of the channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:set-channel-topic}
     *
     * @param channelID ID of the channel to change the topic
     * @param topic     New channel topic
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Channel - Set topic")
    @MetaDataScope(ChannelCategory.class)
    public Boolean setChannelTopic(@MetaDataKeyParam String channelID, String topic) {
        return slack().setChannelTopic(channelID, topic);
    }

    /**
     * This processor is used to change the purpose of a channel. The calling user must be a member of the channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:set-channel-purpose}
     *
     * @param channelID ID of the channel to change the purpose
     * @param purpose   New channel purpose
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Channel - Set purpose")
    @MetaDataScope(ChannelCategory.class)
    public Boolean setChannelPurpose(@MetaDataKeyParam String channelID, String purpose) {
        return slack().setChannelPurpose(channelID, purpose);
    }

    //***********
    // Chat methods
    //***********

    /**
     * This processor posts a message to a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:post-message}
     *
     * @param message   Message to post
     * @param channelId ID of the channel to post the message
     * @param username  Name to show in the message
     * @param iconURL   Icon URL of the icon to show in the message
     * @param asUser    Boolean indicating if the message is showed as a User or as a Bot
     * @return MessageResponse
     */
    @OAuthProtected
    @Processor(friendlyName = "Chat - Post message")
    @MetaDataScope(AllChannelCategory.class)
    public MessageResponse postMessage(String message,
            @FriendlyName("Channel ID") @MetaDataKeyParam String channelId,
            @Optional @FriendlyName("Name to show") String username,
            @Optional @FriendlyName("Icon URL") String iconURL, @Optional Boolean asUser) {
        return slack().sendMessage(message, channelId, username, iconURL, BooleanUtils.toBoolean(asUser));
    }

    /**
     * /**
     * This processor post a message with attachments in a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:post-message-with-attachment}
     *
     * @param message            Message to post
     * @param channelId          ID of the channel to post the message
     * @param username           Name to show in the message
     * @param iconURL            Icon URL of the icon to show in the message
     * @param chatAttachmentList List of attachments to be sent in the message
     * @param asUser             Boolean indicating if the message is showed as a User or as a Bot
     * @return MessageResponse
     */
    @OAuthProtected
    @Processor(friendlyName = "Chat - Post message with attachment")
    @MetaDataScope(AllChannelCategory.class)
    public MessageResponse postMessageWithAttachment(@Optional String message,
            @FriendlyName("Channel ID") @MetaDataKeyParam String channelId,
            @Optional @FriendlyName("Name to show") String username,
            @Optional @FriendlyName("Icon URL") String iconURL,
            @FriendlyName("Attachment List") @Default("#[payload]") List<ChatAttachment> chatAttachmentList,
            @Optional Boolean asUser) {
        return slack().sendMessageWithAttachment(message, channelId, username, iconURL, chatAttachmentList,
                BooleanUtils.toBoolean(asUser));
    }

    /**
     * This processor deletes a message from a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:delete-message}
     *
     * @param timeStamp TimeStamp of the message to delete
     * @param channelId ID of the channel of the message
     * @return Boolean
     */
    @OAuthProtected
    @Processor(friendlyName = "Chat - Delete message")
    @MetaDataScope(AllChannelCategory.class)
    public Boolean deleteMessage(@FriendlyName("Message TimeStamp") String timeStamp,
            @FriendlyName("Channel ID") @MetaDataKeyParam String channelId) {
        return slack().deleteMessage(timeStamp, channelId);
    }

    /**
     * This processor updates a message in a channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:update-message}
     *
     * @param timeStamp TimeStamp of the message to delete
     * @param channelId ID of the channel of the message
     * @param message   New message
     * @return Boolean
     */
    @OAuthProtected
    @Processor(friendlyName = "Chat - Update message")
    @MetaDataScope(AllChannelCategory.class)
    public Boolean updateMessage(@FriendlyName("Message TimeStamp") String timeStamp,
            @FriendlyName("Channel ID") @MetaDataKeyParam String channelId, String message) {
        return slack().updateMessage(timeStamp, channelId, message);
    }

    //***********
    // IM methods
    //***********

    /**
     * This processor opens a direct message channel with another member of your Slack team.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:open-direct-message-channel}
     *
     * @param userId User ID to open a direct message channel with.
     * @return DirectMessageChannelCreationResponse. If the channel was already open the response will include no_op and already_open properties:
     */
    @OAuthProtected
    @Processor(friendlyName = "IM - Open DM channel")
    @MetaDataScope(UserCategory.class)
    public DirectMessageChannelCreationResponse openDirectMessageChannel(
            @FriendlyName("User ID") @MetaDataKeyParam String userId) {
        return slack().openDirectMessageChannel(userId);
    }

    /**
     * Closes a direct message channel.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:close-direct-message-channel}
     *
     * @param channelId Direct message channel ID to close.
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "IM - Close DM channel")
    // @MetaDataScope(UserCategory.class) TODO
    public Boolean closeDirectMessageChannel(@FriendlyName("DM Channel ID") String channelId) {
        return slack().closeDirectMessageChannel(channelId);
    }

    /**
     * This processor returns a list of all im channels that the user has.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:list-direct-message-channels}
     *
     * @return List of the available Direct message channels
     */
    @OAuthProtected
    @Processor(friendlyName = "IM - List DM channels")
    public List<DirectMessageChannel> listDirectMessageChannels() {
        return slack().getDirectMessageChannelsList();
    }

    /**
     * This processor returns a portion of messages/events from the specified direct message channel. To read the entire history for a direct message channel, call the method with no latest or oldest arguments.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-d-m-history}
     *
     * @param channelID       Channel to fetch history for
     * @param latestTimestamp End of time range of messages to include in results. Leave it blank to select current time.
     * @param oldestTimestamp Start of time range of messages to include in results. Leave it blank for timestamp 0
     * @param mountOfMessages Number of messages to return. The value should be between 1 and 1000.
     * @return List of messages of a Channel
     */
    @OAuthProtected
    @Summary("This processor returns a portion of messages/events from the specified DM.")
    @Processor(friendlyName = "IM - History")
    @MetaDataScope(UserCategory.class)
    public List<Message> getDMHistory(
            @FriendlyName("Channel ID") @Summary("Channel to fetch history for") @MetaDataKeyParam String channelID,
            @Optional @Summary("End of time range of messages to include in results. Leave it blank to select current time.") String latestTimestamp,
            @Optional @Summary("Start of time range of messages to include in results. Leave it blank for timestamp 0") String oldestTimestamp,
            @Default("100") @Summary("Number of messages to return, between 1 and 1000.") String mountOfMessages) {
        return slack().getDirectChannelHistory(channelID, latestTimestamp, oldestTimestamp, mountOfMessages);
    }

    //***********
    // Groups methods
    //***********

    /**
     * This processor returns a list of groups in the team that the caller is in and archived groups that the caller was in. The list of (non-deactivated) members in each group is also returned.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-group-list}
     *
     * @return A list of the available Groups
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - List")
    public List<Group> getGroupList() {
        return slack().getGroupList();
    }

    /**
     * This processor returns a portion of messages/events from the specified private group. To read the entire history for a group, call the method with no latest or oldest arguments.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-group-history}
     *
     * @param groupID         Group to fetch history for
     * @param latestTimestamp End of time range of messages to include in results. Leave it blank to select current time.
     * @param oldestTimestamp Start of time range of messages to include in results. Leave it blank for timestamp 0
     * @param mountOfMessages Number of messages to return, between 1 and 1000.
     * @return List of messages of a Channel
     */
    @OAuthProtected
    @Summary("This processor returns a portion of messages/events from the specified group.")
    @Processor(friendlyName = "Group - History")
    @MetaDataScope(GroupCategory.class)
    public List<Message> getGroupHistory(
            @FriendlyName("Channel ID") @Summary("Group to fetch history for") @MetaDataKeyParam String groupID,
            @Optional @Summary("End of time range of messages to include in results. Leave it blank to select current time.") String latestTimestamp,
            @Optional @Summary("Start of time range of messages to include in results. Leave it blank for timestamp 0") String oldestTimestamp,
            @Default("100") @Summary("Number of messages to return, between 1 and 1000.") String mountOfMessages) {
        return slack().getGroupHistory(groupID, latestTimestamp, oldestTimestamp, mountOfMessages);
    }

    /**
     * This processor is used to change the topic of a private group. The calling user must be a member of the private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:set-group-topic}
     *
     * @param channelID ID of the group to set the new topic
     * @param topic     The new topic
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Set topic")
    @MetaDataScope(GroupCategory.class)
    public Boolean setGroupTopic(@MetaDataKeyParam String channelID, String topic) {
        return slack().setGroupTopic(channelID, topic);
    }

    /**
     * This processor is used to change the purpose of a private group. The calling user must be a member of the private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:set-group-purpose}
     *
     * @param channelID ID of the group to set the new purpose
     * @param purpose   The new group purpose
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Set purpose")
    @MetaDataScope(GroupCategory.class)
    public Boolean setGroupPurpose(@MetaDataKeyParam String channelID, String purpose) {
        return slack().setGroupPurpose(channelID, purpose);
    }

    /**
     * This processor creates a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:create-group}
     *
     * @param groupName Name of the group to create
     * @return If successful, the command returns a group object, including state information.
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Create")
    public Group createGroup(String groupName) {
        return slack().createGroup(groupName);
    }

    /**
     * This processor closes a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:close-group}
     *
     * @param channelID ID of the group to close
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Close")
    @MetaDataScope(GroupCategory.class)
    public Boolean closeGroup(@MetaDataKeyParam String channelID) {
        return slack().closeGroup(channelID);
    }

    /**
     * This processor opens a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:open-group}
     *
     * @param channelID ID of the group to open
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Open")
    @MetaDataScope(GroupCategory.class)
    public Boolean openGroup(@MetaDataKeyParam String channelID) {
        return slack().openGroup(channelID);
    }

    /**
     * This processor archives a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:archive-group}
     *
     * @param channelID ID of the group to archive
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Archive")
    @MetaDataScope(GroupCategory.class)
    public Boolean archiveGroup(@MetaDataKeyParam String channelID) {
        return slack().archiveGroup(channelID);
    }

    /**
     * This processor unarchives a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:unarchive-group}
     *
     * @param channelID ID of the group to unarchive
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Unarchive")
    @MetaDataScope(GroupCategory.class)
    public Boolean unarchiveGroup(@MetaDataKeyParam String channelID) {
        return slack().unarchiveGroup(channelID);
    }

    /**
     * This processor renames a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:rename-group}
     *
     * @param groupId   ID of the group to rename
     * @param groupName Channel
     * @return Group
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Rename")
    @MetaDataScope(GroupCategory.class)
    public Group renameGroup(@Summary("Group to rename") @FriendlyName("Group ID") @MetaDataKeyParam String groupId,
            @Summary("New name for group") String groupName) {
        return slack().renameGroup(groupId, groupName);
    }

    /**
     * This processor returns information about a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:get-group-info}
     *
     * @param channelId ID of the group to get info on
     * @return Group
     */
    @OAuthProtected
    @Summary("This processor returns information about a private group.")
    @Processor(friendlyName = "Group - Info")
    @MetaDataScope(GroupCategory.class)
    public Group getGroupInfo(
            @Summary("Group to get info on") @FriendlyName("Group ID") @MetaDataKeyParam String channelId) {
        return slack().getGroupInfo(channelId);
    }

    /**
     * This processor is used to leave a private group.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:leave-group}
     *
     * @param channelId ID of the group to leave
     * @return Boolean result
     */
    @OAuthProtected
    @Processor(friendlyName = "Group - Leave")
    @MetaDataScope(GroupCategory.class)
    public Boolean leaveGroup(@FriendlyName("Group ID") @MetaDataKeyParam String channelId) {
        return slack().leaveGroup(channelId);
    }

    //***********
    // Files methods
    //***********

    /**
     * This processor allows you to create or upload an existing file.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:upload-file}
     *
     * @param channelID      ID of the channel to upload a file
     * @param fileName       File name
     * @param fileType       File type
     * @param title          Message title
     * @param initialComment Message initial comment
     * @param filePath       Path of the file to upload
     * @return File Upload Response
     * @throws IOException When the selected file doesn't exist
     */
    @OAuthProtected
    @Processor(friendlyName = "File - Upload")
    @MetaDataScope(AllChannelCategory.class)
    public FileUploadResponse uploadFile(@FriendlyName("Channel ID") @MetaDataKeyParam String channelID,
            @Optional String fileName, @Optional String fileType, @Optional String title,
            @Optional String initialComment, @Summary("File path of the file to upload") @Path String filePath)
            throws IOException {
        return slack().sendFile(channelID, fileName, fileType, title, initialComment, filePath);
    }

    /**
     * This processor allows you to create or upload an existing file.
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:upload-file-as-input-streams}
     *
     * @param channelID      ID of the channel to upload a file
     * @param fileName       File name
     * @param fileType       File type
     * @param title          Message title
     * @param initialComment Message initial comment
     * @param inputStream    Input stream of the file to upload
     * @return File upload Response
     * @throws IOException When the selected inputStream doesn't exist
     */
    @OAuthProtected
    @Processor(friendlyName = "File - Upload as Input Stream")
    @MetaDataScope(AllChannelCategory.class)
    public FileUploadResponse uploadFileAsInputStreams(
            @Summary("Channel ID to send the message") @FriendlyName("Channel ID") @MetaDataKeyParam String channelID,
            @Summary("File name to show in the Slack message") @Optional String fileName, @Optional String fileType,
            @Summary("Message title") @Optional String title, @Optional String initialComment,
            @Summary("Input Stream Reference of where to look the file to upload") @Default("#[payload]") InputStream inputStream)
            throws IOException {
        return slack().sendFile(channelID, fileName, fileType, title, initialComment, inputStream);
    }

    @Processor
    public void classForName(String className, MuleEvent muleEvent)
            throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MuleContext muleContext = muleEvent.getMuleContext();
        System.out.println(className);
        Class<?> aClass = Class.forName(className, true, muleContext.getExecutionClassLoader());
        EventFilter o = (EventFilter) aClass.newInstance();
        System.out.println(o.shouldAccept(new HashMap<String, Object>()));
        System.out.println(o);
        System.out.println(aClass);
    }

    //************
    // Source methods
    //************

    @Source(friendlyName = "Retrieve events")
    public Message retrieveEvents(final SourceCallback sourceCallback,
            @Placement(group = "Events to accept") @Default("#[false]") Boolean messages,
            @Placement(group = "Events to accept") @Default("#[false]") Boolean userTyping,
            @Placement(group = "Events Filters") @FriendlyName(value = "Only Direct Messages") @Default("#[false]") Boolean directMessages,
            @Placement(group = "Events Filters") @FriendlyName(value = "Only New Messages") @Default("#[false]") Boolean onlyNewMessages,
            @Placement(group = "Events Filters") @Default("#[false]") Boolean ignoreSelfEvents,
            @Placement(group = "Events to accept") @Default("#[false]") Boolean imCreated,
            @Placement(group = "Events to accept") @Default("#[false]") Boolean fileCreated,
            @Placement(group = "Events to accept") @Default("#[false]") Boolean fileShared,
            @Placement(group = "Events to accept") @Default("#[false]") Boolean filePublic,
            @Placement(group = "Custom Filter", tab = "Advanced") @Summary("You can refer an external class to work as a custom filter. (This class must implement 'org.mule.modules.slack.client.rtm.filter.EventFilter')") @Optional String filterClassName,
            @Placement(group = "Custom Notifier", tab = "Advanced") @Summary("You can refer an external class to work as a custom notifier. (This class must implement 'org.mule.modules.slack.client.rtm.filter.EventNotifier')") @Optional String notifierClassName)
            throws IOException, InterruptedException, DeploymentException {

        if (getSlackConfig() instanceof SlackOAuth2Config) {
            logger.error("Retrieve Events source doesn't work with OAuth 2 configuration, please use Token Config");
            logger.error("Shutting down Retrieving of Messages!");
            return new Message();
        }

        List<EventNotifier> observerList = new ArrayList<>();
        List<EventFilter> eventFilterList = new ArrayList<>();

        if (messages) {
            observerList.add(new MessagesNotifier(directMessages, onlyNewMessages));
        }

        if (userTyping) {
            observerList.add(new OnlyTypeNotifier(USER_TYPING_EVENT));
        }

        if (imCreated) {
            observerList.add(new OnlyTypeNotifier(IM_CREATED));
        }

        if (fileCreated) {
            observerList.add(new OnlyTypeNotifier(FILE_CREATED));
        }

        if (fileShared) {
            observerList.add(new OnlyTypeNotifier(FILE_SHARED));
        }

        if (filePublic) {
            observerList.add(new OnlyTypeNotifier(FILE_PUBLIC));
        }

        if (StringUtils.isNotEmpty(filterClassName)) {
            eventFilterList.add(getFilterInstance(filterClassName));
        }

        if (StringUtils.isNotEmpty(notifierClassName)) {
            observerList.add(getNotifierInstance(notifierClassName));
        }

        if (ignoreSelfEvents) {
            eventFilterList.add(new SelfEventsFilter(slack().getSelfId()));
        }

        slack().startRealTimeCommunication(new ConfigurableHandler(sourceCallback, observerList, eventFilterList));

        System.out.println("Ending!");
        return null;
    }

    private EventFilter getFilterInstance(String className) {
        try {
            logger.info("Detected custom filter class: " + className);
            Class<?> aClass = Class.forName(className, true, getMuleContext().getExecutionClassLoader());
            return (EventFilter) aClass.newInstance();
        } catch (ClassCastException e) {
            String errorMessage = String.format(
                    "The configured class [%s] does not implements 'org.mule.modules.slack.client.rtm.filter.SlackEventFilter'",
                    className);
            logger.error(errorMessage);
            throw new SlackException(errorMessage);
        } catch (Exception e) {
            logger.error("Error loading Custom filter class", e);
            throw new SlackException("Error loading Custom filter class", e);
        }
    }

    private EventNotifier getNotifierInstance(String className) {
        try {
            logger.info("Detected custom filter class: " + className);
            Class<?> aClass = Class.forName(className, true, getMuleContext().getExecutionClassLoader());
            return (EventNotifier) aClass.newInstance();
        } catch (ClassCastException e) {
            String errorMessage = String.format(
                    "The configured class [%s] does not implements 'org.mule.modules.slack.client.rtm.filter.EventObserver'",
                    className);
            logger.error(errorMessage);
            throw new SlackException(errorMessage);
        } catch (Exception e) {
            logger.error("Error loading Custom filter class", e);
            throw new SlackException("Error loading Custom filter class", e);
        }
    }

    private Boolean falseIfNull(Boolean aBoolean) {
        if (aBoolean == null) {
            return false;
        } else {
            return aBoolean;
        }
    }

    /**
     * This Source retrieves messages from the desired channel, private group or direct message channel
     * <p/>
     * {@sample.xml ../../../doc/slack-connector.xml.sample
     * slack:retrieve-messages}
     *
     * @param source                   SourceCallback
     * @param messageRetrieverInterval Pool interval
     * @param channelID                ID of the channel/group/DMC to poll messages
     * @return Messages
     * @throws Exception When the SourceCallback fails processing the messages
     */
    @Source(friendlyName = "Retrieve messages (DEPRECATED)")
    public Message retrieveMessages(SourceCallback source, Integer messageRetrieverInterval,
            @Summary("This source stream messages/events from the specified channel, group or direct message channel") @FriendlyName("Channel ID") String channelID)
            throws Exception {
        String oldestTimeStamp;

        if (getSlackConfig() instanceof SlackOAuth2Config) {
            logger.error(
                    "Retrieve Messages source doesn't work with OAuth 2 configuration, please use Token Config");
            logger.error("Shutting down Retrieving of Messages!");
            return new Message();
        }

        logger.warn("This Retrieve Messages Endpoint is deprecated. Please use 'Retrieve Events' endpoint.");

        while (!getSlackConfig().isAuthorized()) {
            Thread.sleep(1000);
            logger.debug("Waiting authorization!");
        }

        MessageRetriever messageRetriever = getMessageVerifierForChannel(channelID);

        oldestTimeStamp = messageRetriever.retrieve(slack(), channelID, null, null, "1").get(0).getTs();

        while (true) {
            Thread.sleep(messageRetrieverInterval);
            List<Message> messages = messageRetriever.retrieve(slack(), channelID, null, oldestTimeStamp, "1000");
            if (!messages.isEmpty()) {
                oldestTimeStamp = messages.get(0).getTs();
            }
            Integer i = messages.size();

            while (i > 0) {
                source.process(messages.get(i - 1));
                i--;
            }
        }
    }

    private MessageRetriever getMessageVerifierForChannel(String channelID) throws Exception {
        if (channelID.toLowerCase().startsWith("g")) {
            logger.info(
                    "Started retrieving messages of channel: " + slack().getGroupInfo(channelID).getName() + "!");
            return new GroupMessageRetriever();
        }

        if (channelID.toLowerCase().startsWith("c")) {
            logger.info(
                    "Started retrieving messages of channel: " + slack().getChannelById(channelID).getName() + "!");
            return new ChannelMessageRetriever();
        }

        if (channelID.toLowerCase().startsWith("d")) {
            logger.info("Started retrieving messages of direct message channel!");
            return new DirectMessageRetriever();
        }

        throw new Exception("Incorrect name for channel");
    }

    public SlackClient slack() {
        return slackConfig.getSlackClient();
    }

    public BasicSlackConfig getSlackConfig() {
        return slackConfig;
    }

    public void setSlackConfig(BasicSlackConfig slackConfig) {
        this.slackConfig = slackConfig;
    }
}