Fred

A Java Framework for Building Slack Bots.

License

License

Categories

Categories

CLI User Interface
GroupId

GroupId

com.clivern
ArtifactId

ArtifactId

fred
Last Version

Last Version

1.0.2
Release Date

Release Date

Type

Type

jar
Description

Description

Fred
A Java Framework for Building Slack Bots.
Project URL

Project URL

https://github.com/clivern/fred
Source Code Management

Source Code Management

https://github.com/clivern/fred

Download fred

How to add to project

<!-- https://jarcasting.com/artifacts/com.clivern/fred/ -->
<dependency>
    <groupId>com.clivern</groupId>
    <artifactId>fred</artifactId>
    <version>1.0.2</version>
</dependency>
// https://jarcasting.com/artifacts/com.clivern/fred/
implementation 'com.clivern:fred:1.0.2'
// https://jarcasting.com/artifacts/com.clivern/fred/
implementation ("com.clivern:fred:1.0.2")
'com.clivern:fred:jar:1.0.2'
<dependency org="com.clivern" name="fred" rev="1.0.2">
  <artifact name="fred" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.clivern', module='fred', version='1.0.2')
)
libraryDependencies += "com.clivern" % "fred" % "1.0.2"
[com.clivern/fred "1.0.2"]

Dependencies

compile (3)

Group / Artifact Type Version
com.mashape.unirest : unirest-java jar 1.4.9
org.json : json jar 20170516
org.tinylog : tinylog jar 1.3.2

test (1)

Group / Artifact Type Version
junit : junit jar 4.12

Project Modules

There are no modules declared in this project.

Fred

Fred is a Java Framework for Building Slack Bots.

Current Version: 1.0.2

Build Status Javadocs

Installation

To add a dependency using Maven, use the following:

<dependency>
  <groupId>com.clivern</groupId>
  <artifactId>fred</artifactId>
  <version>1.0.2</version>
</dependency>

To add a dependency using Gradle, use the following:

dependencies {
  compile 'com.clivern:fred:1.0.2'
}

To add a dependency using Scala SBT, use the following:

libraryDependencies += "com.clivern" % "fred" % "1.0.2"

Usage

After adding the package as a dependency, Please read the following steps:

Basic Configurations

In order to cofigure the package create config.properties file with the following data

client_id=Application Client ID
client_secret=Application Client Secret
scope=Application Scope
redirect_uri=Oauth Secured URL
state_type=State Type (for example. vary)
state=State Key
team=Slack Team Name
verification_token=Verification Token Goes Here

logging_level=tarce or debug or info or warning or error
logging_file_path=src/main/java/resources/
logging_file_format=current_date or app
logging_log_type=file or console or both
logging_current_date_format=yyyy-MM-dd
logging_append=true or false
logging_buffered=true or false

Build Oauth Redirect URL

Create a route to return the Oauth redirect URL like the following:

import com.clivern.fred.util.*;
import com.mashape.unirest.http.exceptions.UnirestException;


Config config = new Config();
config.loadPropertiesFile("config.properties");
config.configLogger();
Oauth oauth = new Oauth(config, log);
return "<a href='" + oauth.getRedirectURL() + "'>Auth</a>";

So let's say we use Spark Java Framework for our bot, Our route and callback will look like the following:

import static spark.Spark.*;
import com.clivern.fred.util.*;
import com.mashape.unirest.http.exceptions.UnirestException;

public class Main {

    public static void main(String[] args) throws UnirestException
    {
        get("/", (request, response) -> {
            Config config = new Config();
            config.loadPropertiesFile("config.properties");
            config.configLogger();
            Oauth oauth = new Oauth(config);
            return "<a href='" + oauth.getRedirectURL() + "'>Auth</a>";
        });
    }
}

Build Oauth Webhook

In order to verify the incoming user token and fetch the access token for that user, We will build and a route that do these tasks and we already provided this URL in the config.properties file as redirect_uri:

import static spark.Spark.*;
import com.clivern.fred.util.*;
import com.mashape.unirest.http.exceptions.UnirestException;



String code = // Get code query parameter value from the current URL
String state = // Get state query parameter value from the current URL
String error = // Get error query parameter value from the current URL

Config config = new Config();
config.loadPropertiesFile("config.properties");
config.configLogger();
Oauth oauth = new Oauth(config);

Boolean status = oauth.issueToken(code, state, error);

Boolean fetch = oauth.fetchAccessToken();

if( status && fetch ){

    return  "State: " +  oauth.getState() + "<br/>" +
            "Client ID: " +  oauth.getClientId() + "<br/>" +
            "Client Secret: " +  oauth.getClientSecret() + "<br/>" +
            "Scope: " +  oauth.getScope() + "<br/>" +
            "Redirect Uri: " +  oauth.getRedirectUri() + "<br/>" +
            "State Type: " +  oauth.getStateType() + "<br/>" +
            "Team: " +  oauth.getTeam() + "<br/>" +
            "Incoming Code: " +  oauth.getIncomingCode() + "<br/>" +
            "Incoming State: " +  oauth.getIncomingState() + "<br/>" +
            "Incoming Error: " +  oauth.getIncomingError() + "<br/>" +
            "Incoming Access Token: " +  oauth.getIncomingAccessToken() + "<br/>" +
            "Incoming Scope: " +  oauth.getIncomingScope() + "<br/>" +
            "Incoming User ID: " +  oauth.getIncomingUserId() + "<br/>" +
            "Incoming Team Name: " +  oauth.getIncomingTeamName() + "<br/>" +
            "Incoming Team ID: " +  oauth.getIncomingTeamId() + "<br/>" +
            "Incoming Webhook URL: " +  oauth.getIncomingWebhookUrl() + "<br/>" +
            "Incoming Webhook Channel: " +  oauth.getIncomingWebhookChannel() + "<br/>" +
            "Incoming Webhook Config URL: " +  oauth.getIncomingWebhookConfigUrl() + "<br/>" +
            "Incoming Bot User ID: " +  oauth.getIncomingBotUserId() + "<br/>" +
            "Incoming Bot Access Token: " +  oauth.getIncomingBotAccessToken() + "<br/>";

}else{

    return "Error";

}

So let's say we use Spark Java Framework for our bot, Our route and callback will look like the following:

import static spark.Spark.*;
import com.clivern.fred.util.*;
import com.mashape.unirest.http.exceptions.UnirestException;


public class Main {

    public static void main(String[] args) throws UnirestException
    {
        get("/", (request, response) -> {
            Config config = new Config();
            config.loadPropertiesFile("config.properties");
            config.configLogger();
            Oauth oauth = new Oauth(config);
            return "<a href='" + oauth.getRedirectURL() + "'>Auth</a>";
        });

        get("/oauth", (request, response) -> {
            Config config = new Config();
            config.loadPropertiesFile("config.properties");
            config.configLogger();
            Oauth oauth = new Oauth(config);

            Boolean status = oauth.issueToken(
                ( request.queryParams("code") != null ) ? request.queryParams("code") : "",
                ( request.queryParams("state") != null ) ? request.queryParams("state") : "",
                ( request.queryParams("error") != null ) ? request.queryParams("error") : ""
            );

            Boolean fetch = oauth.fetchAccessToken();

            if( status && fetch ){

                return  "State: " +  oauth.getState() + "<br/>" +
                        "Client ID: " +  oauth.getClientId() + "<br/>" +
                        "Client Secret: " +  oauth.getClientSecret() + "<br/>" +
                        "Scope: " +  oauth.getScope() + "<br/>" +
                        "Redirect Uri: " +  oauth.getRedirectUri() + "<br/>" +
                        "State Type: " +  oauth.getStateType() + "<br/>" +
                        "Team: " +  oauth.getTeam() + "<br/>" +
                        "Incoming Code: " +  oauth.getIncomingCode() + "<br/>" +
                        "Incoming State: " +  oauth.getIncomingState() + "<br/>" +
                        "Incoming Error: " +  oauth.getIncomingError() + "<br/>" +
                        "Incoming Access Token: " +  oauth.getIncomingAccessToken() + "<br/>" +
                        "Incoming Scope: " +  oauth.getIncomingScope() + "<br/>" +
                        "Incoming User ID: " +  oauth.getIncomingUserId() + "<br/>" +
                        "Incoming Team Name: " +  oauth.getIncomingTeamName() + "<br/>" +
                        "Incoming Team ID: " +  oauth.getIncomingTeamId() + "<br/>" +
                        "Incoming Webhook URL: " +  oauth.getIncomingWebhookUrl() + "<br/>" +
                        "Incoming Webhook Channel: " +  oauth.getIncomingWebhookChannel() + "<br/>" +
                        "Incoming Webhook Config URL: " +  oauth.getIncomingWebhookConfigUrl() + "<br/>" +
                        "Incoming Bot User ID: " +  oauth.getIncomingBotUserId() + "<br/>" +
                        "Incoming Bot Access Token: " +  oauth.getIncomingBotAccessToken() + "<br/>";

            }else{

                return "Error";

            }
        });
    }
}

Build Slash Commands

Slash Command enable users to interact with your app from within Slack. We will have two tasks to do:

  1. First to Create the Commnad on Slack Application and Configure its Request URL (The URL that slack will POST the command data once used by any user).
  2. Build a New Route (Accept POST Requests) to handle all Incoming Requests from Slack.

So Lets Start by the Easy Part Creating Commands On Slack App:

  1. Please visit Your Apps Page from Slack.
  2. Open You App Settings Page (By clicking on the App).
  3. Go to Slash Commands From Side Menu Under Features.
  4. Then Click to Create New Command. A Command Form Will Open To be Filled.
  5. Set the Commnad for example /fred.
  6. Set Request URL to you Application URL Handling Slack Commands for example https://b2f78bbb.ngrok.io/commands.
  7. Set Short Description for example Launch The Rocket!.
  8. Set Usage Hint
  9. And Finally Set to Escape channels, users, and links sent to your app or Not and Click Save.

Then Let's Build Our Route That will Accept and Process All Incoming Requests from Slack for example https://b2f78bbb.ngrok.io/commands. Our route and callback will look like the following Using Spark Java Framework:

import static spark.Spark.*;
import java.util.HashMap;
import java.util.Map;
import com.clivern.fred.util.*;
import com.clivern.fred.sender.BaseSender;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.clivern.fred.receiver.BaseReceiver;
import com.clivern.fred.receiver.command.Command;


public class Main {

    public static void main(String[] args) throws UnirestException
    {

        post("/commands", (request, response) -> {
            Config config = new Config();
            config.loadPropertiesFile("config.properties");
            config.configLogger();
            BaseReceiver baseReceiver = new BaseReceiver(config);

            // Build Our First Command (/fred Command)
            Command fredCommand = new Command("/fred", false, (ct) -> "You Typed -> " + ct.getText() + " To /fred");

            // Build Another Command (/frog Command)
            Command frogCommand = new Command("/frog", false, (ct) -> "You Typed -> " + ct.getText() + " To /frog");

            // Pass Commands To The Receiver
            baseReceiver.setCommand("/fred", fredCommand);
            baseReceiver.setCommand("/frog", frogCommand);

            // Check If Incoming Data Related to Any Configured Command (/fred or /frog)
            if( baseReceiver.commandExists(request.queryParams("command")) ){
                Map<String, String> incomingData = new HashMap<String, String>();
                incomingData.put("channel_name", request.queryParams("channel_name"));
                incomingData.put("user_id", request.queryParams("user_id"));
                incomingData.put("user_name", request.queryParams("user_name"));
                incomingData.put("trigger_id", request.queryParams("trigger_id"));
                incomingData.put("team_domain", request.queryParams("team_domain"));
                incomingData.put("team_id", request.queryParams("team_id"));
                incomingData.put("text", request.queryParams("text"));
                incomingData.put("channel_id", request.queryParams("channel_id"));
                incomingData.put("command", request.queryParams("command"));
                incomingData.put("token", request.queryParams("token"));
                incomingData.put("response_url", request.queryParams("response_url"));

                return baseReceiver.callCurrentCommand(request.queryParams("command"), incomingData);
            }
            return "Command Not Configured In App!";
        });
    }
}

Now You Finished, Just go to Slack Messaging and Interact With You Commands. Type /fred Hello World and You will Get You Typed -> Hello World To /fred.

Listen To Slack Events

Slack Event API allows your app to be notified of events in Slack (for example, when a user adds a reaction or creates a file) at a URL you choose.

Let's discuss how to implement this using Fred.

Misc

Todo & Contributing

In case you want to share some love, Show your awesomeness in the following sub-packages:

  • ๐Ÿš€ Config and Oauth com.clivern.fred.util.
  • ๐Ÿš€ Slash Commands com.clivern.fred.receiver.
  • ๐Ÿ”ฅ Web API com.clivern.fred.sender.
  • ๐Ÿ”ฅ Events API com.clivern.fred.event.
  • ๐Ÿš€ Add More Test Cases.
  • ๐Ÿš€ Add More Docs.
  • ๐Ÿš€ Update and Fix Code Docs and Remove Line 32-38 in maven-push.gradle.
  • ๐Ÿš€ Add Examples & Write Tutorials.

And then please do the following:

  • Fork the master branch.
  • Create a feature branch git branch my-feature.
  • Move to your branch git checkout my-feature.
  • Do Your Changes.
  • It will be great if you write some tests to your feature and check ./gradlew test but not required ;).
  • Track the changes git add --all.
  • Commit your changes git commit -m 'new awesome feature'.
  • Push to your newly created branch git push origin my-feature.
  • Create a new Pull Request.

Tutorials & Examples

For almost all supported features you can take a look at examples/ folder for working examples.

Changelog

Version 1.0.2:

Update logging package.
Update Configurations.

Version 1.0.1:

Add Java 8 Support.

Version 1.0.0:

Initial Release.

Acknowledgements

ยฉ 2017, Clivern. Released under The Apache Software License, Version 2.0.

Fred is authored and maintained by @clivern.

Versions

Version
1.0.2
1.0.1
1.0.0