Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APP-3031 Activity API Documentation #202

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 138 additions & 16 deletions docs/activity.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,173 @@
# Activity API

The Activity API is an abstraction built on top of the Datafeed's [_Real Time Events_](https://developers.symphony.com/restapi/docs/real-time-events).
An Activity is basically a user interaction triggered from the chat.

At the moment, two different kinds of activities are supported by the BDK:
The Activity API is an abstraction built on top of the Datafeed's [_Real Time Events_](https://developers.symphony.com/restapi/docs/real-time-events). An Activity is basically a user interaction triggered from the chat.
Two different kinds of activities are supported by the BDK:
- **Command Activity**: triggered when a message is sent in an `IM`, `MIM` or `Chatroom`
- **Form Activity**: triggered when a user replies to an [_Elements_](https://developers.symphony.com/symphony-developer/docs/overview-of-symphony-elements) form message

Using the Activity API will help you to make your bot interactions easier and faster to implement.
## Activity Registry
The central component for activities is the [`ActivityRegistry`](../symphony-bdk-core/src/main/java/com/symphony/bdk/core/activity/ActivityRegistry.java).
This component is used to either add or retrieve activities. It is accessible from the `SymphonyBdk` object:

```java
public class Example {

public static void main(String[] args) throws Exception {
// Create BDK entry point
final SymphonyBdk bdk = new SymphonyBdk(loadFromSymphonyDir("config.yaml"));
// Access to the registry for activities
final ActivityRegistry registry = bdk.activities();
}
}
```

## Command Activity
A command activity is triggered when a message is sent in an `IM`, `MIM` or `Chatroom`.
A command activity is triggered when a message is sent in an `IM`, `MIM` or `Chatroom`. This is the most basic interaction
between an end-user and the bot. Here are some command activity examples:

```
$ @BotMention /buy # (1)
$ /buy 1000 goog # (2)
$ I want to say hello to the world # (3)
```
1. the bot is mentioned followed by a [_slash_](#slash-command) command
2. a command with parameters, the bot is not mentioned
3. any message that contains 'hello' can be a command

### How to create a Command Activity

```java
public class CommandActivityExample {
public class Example {

public static void main(String[] args) {
// TODO
public static void main(String[] args) throws Exception {
// setup SymphonyBdk facade object
final SymphonyBdk bdk = new SymphonyBdk(loadFromSymphonyDir("config.yaml"));
// register Hello Command within the registry
bdk.activities().register(new HelloCommandActivity());
// finally, start the datafeed loop
bdk.datafeed().start();
}
}

class HelloCommandActivity extends CommandActivity<CommandContext> {

@Override
protected ActivityMatcher<CommandContext> matcher() {
return c -> c.getTextContent().contains("hello"); // (1)
}

@Override
protected void onActivity(CommandContext context) {
log.info("Hello command triggered by user {}", context.getInitiator().getUser().getDisplayName()); // (2)
}

@Override
protected ActivityInfo info() {
final ActivityInfo info = ActivityInfo.of(ActivityType.COMMAND); // (3)
info.setName("Hello Command");
return info;
}
}
```
1. the [`ActivityMatcher`](../symphony-bdk-core/src/main/java/com/symphony/bdk/core/activity/ActivityMatcher.java)
allows detecting if the activity logic has to be executed or not. In this case, it will execute `onActivity(CommandContext)`
each time a message that contains "hello" is sent in a stream where the bot is also a member
2. this is where the command logic has to be implemented
3. define activity information

### Slash Command
A _Slash_ command can be used to directly define a very simple bot command such as:
```
$ @BotMention /command
$ /command
```

#### How to create a Slash Command
> :information_source: a Slash cannot have parameters

```java
public class SlashCommandExample {
public class Example {

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

// setup SymphonyBdk facade object
final SymphonyBdk bdk = new SymphonyBdk(loadFromSymphonyDir("config.yaml"));

public static void main(String[] args) {
// TODO
bdk.activities().register(new SlashCommand("/hello" /*(1)*/, true /*(2)*/, context /*(3)*/ -> {
log.info("Hello slash command triggered by user {}", context.getInitiator().getUser().getDisplayName()); // (2)
}));

// finally, start the datafeed loop
bdk.datafeed().start();
}
}
```
1. `/hello` is the command name
2. `true` means that the bot has to be mentioned
3. the command callback provides the `CommandContext` that allows to retrieve some information about the source of the
event, or the event initiator (i.e. user that triggered the command)

## Form Activity
A form activity is triggered when an end-user reply or submit to an _Elements_ form.

### How to create a Form Activity
For this example, we will assume that the following Elements form has been posted into a room:
```xml
<messageML>
<h2>Hello Form</h2>
<form id="hello-form"> <!-- (1) -->

<text-field name="name" placeholder="Enter a name here..."/> <!-- (2) -->

<button name="submit" type="action">Submit</button> <!-- (3) -->
<button type="reset">Reset Data</button>

</form>
</messageML>
```
1. the formId is "**hello-form**"
2. the form has one unique `<text-field>` called "**name**"
3. the has one action button called "**submit**"

```java
public class FormActivityExample {
public class Example {

public static void main(String[] args) throws Exception {
// setup SymphonyBdk facade object
final SymphonyBdk bdk = new SymphonyBdk(loadFromSymphonyDir("config.yaml"));
// register Hello FormReply Activity within the registry
bdk.activities().register(new HelloFormReplyActivity(bdk.messages()));
// finally, start the datafeed loop
bdk.datafeed().start();
}
}

class HelloFormReplyActivity extends FormReplyActivity<FormReplyContext> {

private final MessageService messageService;

public HelloFormReplyActivity(MessageService messageService) {
this.messageService = messageService;
}

@Override
protected ActivityMatcher<FormReplyContext> matcher() {
return c -> "hello-form".equals(c.getFormId()) && "submit".equals(c.getFormValue("action")); // (1)
}

@Override
protected void onActivity(FormReplyContext context) {
final String message = "Hello, " + context.getFormValue("name") + "!"; // (2)
this.messageService.send(context.getSourceEvent().getStream(), "<messageML>" + message + "</messageML>");
}

public static void main(String[] args) {
// TODO
@Override
protected ActivityInfo info() {
final ActivityInfo info = ActivityInfo.of(ActivityType.FORM);
info.setName("Hello Form Reply Activity");
return info;
}
}
```
1. The `ActivityMatcher` ensures that activity logic is triggered only when the form with `id` "**hello-form**" has been
submitted from the action button "**submit**"
2. The activity context allows to directly retrieve form values. Here the "**name**" `<text-field>` value
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void onActivity(CommandContext context) {

@Override
protected ActivityInfo info() {
final ActivityInfo info = ActivityInfo.of(ActivityType.command);
final ActivityInfo info = ActivityInfo.of(ActivityType.COMMAND);
info.setName("Slash command '" + this.slashCommandName + "'");
info.setDescription("Usage: " + (this.requiresBotMention ? "@BotMention " : "") + this.slashCommandName);
return info;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ public enum ActivityType {
/**
* Message sent in the chat.
*/
command,
COMMAND,
/**
* Form submitted.
*/
form
FORM
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class TestCommandActivity extends CommandActivity<CommandContext> {

@Override
protected ActivityInfo info() {
return ActivityInfo.of(ActivityType.command);
return ActivityInfo.of(ActivityType.COMMAND);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void testSlashCommandNotTriggered() {
void testVerifyBotInfo() {
final SlashCommand cmd = new SlashCommand("/test", c -> {});
final ActivityInfo info = cmd.getInfo();
assertEquals(ActivityType.command, info.getType());
assertEquals(ActivityType.COMMAND, info.getType());
assertEquals("Slash command '/test'", info.getName());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class TestFormReplyActivity extends FormReplyActivity<FormReplyContext> {

@Override
protected ActivityInfo info() {
return ActivityInfo.of(ActivityType.form);
return ActivityInfo.of(ActivityType.FORM);
}

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

import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import org.junit.jupiter.api.io.TempDir;

import java.io.FileOutputStream;
Expand Down Expand Up @@ -118,8 +119,8 @@ void loadClientGlobalConfig() throws BdkConfigException {
assertEquals(config.getSessionAuth().getContext(), "context");
}

//@Test
// CircleCI does not allow to create file in the home directory
@Test
@DisabledIfEnvironmentVariable(named = "CIRCLECI", matches = "true") // cf. https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables
void testLoadConfigFromSymphonyDirectory() throws Exception {

final String tmpConfigFileName = UUID.randomUUID().toString() + "-config.yaml";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.symphony.bdk.examples.activity;

import static com.symphony.bdk.core.config.BdkConfigLoader.loadFromSymphonyDir;

import com.symphony.bdk.core.SymphonyBdk;
import com.symphony.bdk.core.activity.ActivityMatcher;
import com.symphony.bdk.core.activity.form.FormReplyActivity;
import com.symphony.bdk.core.activity.form.FormReplyContext;
import com.symphony.bdk.core.activity.model.ActivityInfo;
import com.symphony.bdk.core.activity.model.ActivityType;
import com.symphony.bdk.core.service.MessageService;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Example {

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

// setup SymphonyBdk facade object
final SymphonyBdk bdk = new SymphonyBdk(loadFromSymphonyDir("config.yaml"));
// register Hello FormReply Activity within the registry
bdk.activities().register(new HelloFormReplyActivity(bdk.messages()));
// finally, start the datafeed loop
bdk.datafeed().start();
}
}

@Slf4j
class HelloFormReplyActivity extends FormReplyActivity<FormReplyContext> {

private final MessageService messageService;

public HelloFormReplyActivity(MessageService messageService) {
this.messageService = messageService;
}

@Override
protected ActivityMatcher<FormReplyContext> matcher() {
return c -> "hello-form".equals(c.getFormId()) && "submit".equals(c.getFormValue("action"));
}

@Override
protected void onActivity(FormReplyContext context) {
final String message = "Hello, " + context.getFormValue("name") + "!";
this.messageService.send(context.getSourceEvent().getStream(), "<messageML>" + message + "</messageML>");
}

@Override
protected ActivityInfo info() {
final ActivityInfo info = ActivityInfo.of(ActivityType.FORM);
info.setName("Hello Form Reply Activity");
return info;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void onActivity(final GifCommandContext context) {

@Override
protected ActivityInfo info() {
final ActivityInfo info = ActivityInfo.of(ActivityType.command);
final ActivityInfo info = ActivityInfo.of(ActivityType.COMMAND);
info.setName("Gif Random by Category command");
info.setDescription("Usage: @BotMention /gif {category}");
return info;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void onActivity(GifFormReplyContext context) {

@Override
protected ActivityInfo info() {
final ActivityInfo info = ActivityInfo.of(ActivityType.form);
final ActivityInfo info = ActivityInfo.of(ActivityType.FORM);
info.setName("Gif Display category form command");
info.setDescription("Form handler for the Gif Category form");
return info;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<messageML>
<h2>Hello Form</h2>
<form id="hello-form">

<text-field name="name" placeholder="Enter a name here..."/>

<button name="submit" type="action">Submit</button>
<button type="reset">Reset Data</button>

</form>
</messageML>
14 changes: 13 additions & 1 deletion symphony-bdk-legacy/symphony-bdk-bot-sdk-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<description>Legacy Symphony Bot Developer Kit - Bot SDK</description>

<properties>
<handlebars.version>4.1.2</handlebars.version>
<handlebars.version>4.2.0</handlebars.version>
<resilience4j.version>1.4.0</resilience4j.version>
<guava.version>28.2-jre</guava.version>
<esapi.version>2.2.0.0</esapi.version>
Expand Down Expand Up @@ -62,6 +62,18 @@
<groupId>com.github.jknack</groupId>
<artifactId>handlebars-jackson2</artifactId>
<version>${handlebars.version}</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<!-- Lombok -->
Expand Down