Skip to content

Commit

Permalink
feat: add request/reply support (#551)
Browse files Browse the repository at this point in the history
  • Loading branch information
KhudaDad414 authored Nov 21, 2023
1 parent e351224 commit 8f226b8
Show file tree
Hide file tree
Showing 27 changed files with 22,194 additions and 212 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"error",
"multi-line"
],
"security/detect-non-literal-fs-filename": "off",
"@typescript-eslint/no-explicit-any": "off",
"semi": [
"warn",
Expand Down
2 changes: 2 additions & 0 deletions examples/slack-reaction-listener/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.glee
59 changes: 59 additions & 0 deletions examples/slack-reaction-listener/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Slack Websocket API

This Slack Websocket API leverages the AsyncAPI specification to connect Slack with OpenAI's AI models. When a user reacts to a message on Slack, this API sends the reaction to OpenAI's server. ChatGPT then crafts a fun response, which is posted as a reply to the message thread on Slack.

## Table of Contents

- [Overview](#overview)
- [Prerequisites](#prerequisites)
- [Configuration](#configuration)
- [Usage](#usage)
- [Environment Variables](#environment-variables)

## Overview

The API listens for reaction events in Slack, processes them through OpenAI's API to generate responses, and sends those back to Slack as a threaded message.

## Prerequisites

- Node.js (version 12 or higher)
- A Slack app with permissions to read reactions and post messages
- Access to OpenAI API

## Configuration

Before running the project, you must update the `asyncapi.yaml` file with the current `ticket` and `app_id` for the Slack WebSocket connection:

```yaml
channels:
SlackEventStream:
address: /link/?ticket=[ticket]&app_id=[app_id]
```
Replace `[ticket]` and `[app_id]` with the respective values for your Slack app.

## Usage

Set the environment variables by creating a `.env` file in the root of the project:

```plaintext
SLACK_HTTP=xoxb-**********
CHAT_API=openai_token
```

Start the API server with:

```sh
npm run dev
```

The API will now listen for Slack reaction events, interact with OpenAI, and post responses on Slack.

## Environment Variables

The following environment variables are necessary for the API to function:

- `SLACK_HTTP`: Your Slack app's OAuth token.
- `CHAT_API`: Your OpenAI API key.

Ensure these are set in your environment.
173 changes: 173 additions & 0 deletions examples/slack-reaction-listener/asyncapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
asyncapi: 3.0.0
info:
title: Slack Websocket and OpenAI API
version: 1.0.0
servers:
OpenAI_HTTPS:
host: api.openai.com
protocol: https
Slack_WebSocket:
host: wss-primary.slack.com
protocol: wss
Slack_HTTPS:
host: slack.com
protocol: https
channels:
SlackPostMessage:
bindings:
http:
method: post
address: /api/chat.postMessage
servers:
- $ref: "#/servers/Slack_HTTPS"
messages:
slackResponse:
payload:
type: object
properties:
channel:
type: string
text:
type: string
OpenAICompletion:
bindings:
http:
method: post
servers:
- $ref: "#/servers/OpenAI_HTTPS"
address: /v1/chat/completions
messages:
OpenAIRequest:
$ref: "#/components/messages/OpenAIRequest"
SlackEventStream:
servers:
- $ref: "#/servers/Slack_WebSocket"
address: /link/?ticket={ticket}&app_id={app_id}
parameters:
ticket:
default: 38408ed1-5d39--82f0-6e1214dae1
app_id:
default: b02112d9364cb2fee8a0a920b8bcb03fab023b4e30f6ee2efb98f
messages:
SlackReactionAdded:
$ref: "#/components/messages/SlackReactionAdded"
GenericErrorPayload:
$ref: "#/components/messages/GenericErrorPayload"
operations:
sentSlackMessage:
action: send
channel:
$ref: "#/channels/SlackPostMessage"
messages:
- $ref: "#/components/messages/slackResponse"
receiveSlackConfirmation:
action: receive
channel:
$ref: "#/channels/SlackPostMessage"
AckEvent:
action: send
channel:
$ref: "#/channels/SlackEventStream"
messages:
- $ref: "#/components/messages/slackAckEvent"
SendToOpenAI:
action: send
channel:
$ref: "#/channels/OpenAICompletion"
messages:
- $ref: "#/components/messages/slackResponse"
ReceiveFromOpenAI:
action: receive
channel:
$ref: "#/channels/OpenAICompletion"
messages:
- $ref: "#/components/messages/OpenAICompletionResponse"
reply:
channel:
$ref: "#/channels/SlackPostMessage"
HandleSlackReaction:
reply:
channel:
$ref: "#/channels/SlackEventStream"
action: receive
channel:
$ref: "#/channels/SlackEventStream"
messages:
- $ref: "#/components/messages/SlackReactionAdded"
- $ref: "#/components/messages/GenericErrorPayload"
components:
messages:
slackAckEvent:
payload:
type: object
properties:
envelope_id:
type: string
slackResponse:
payload:
type: object
properties:
channel:
type: string
text:
type: string
OpenAIRequest:
payload:
type: object
properties:
model:
type: string
enum: ["gpt-3.5-turbo", "gpt-3.5-turbo-16k", "text-davinci-002"]
messages:
type: array
items:
type: object
properties:
role:
type: string
enum: ["user"]
content:
type: string
temperature:
type: number
minimum: 0.0
maximum: 1.0
required:
- model
- messages
- temperature
OpenAICompletionResponse:
payload:
type: object
properties:
choices:
type: array
items:
type: object
properties:
finish_reason:
type: string
enum: ['stop']
message:
type: object
properties:
content:
type: string
role:
type: string
enum: ['assistant']
required:
- finish_reason
- message
required:
- choices
SlackReactionAdded:
payload:
type: object
GenericErrorPayload:
payload:
type: string
x-remoteServers:
- Slack_HTTPS
- Slack_WebSocket
- OpenAI_HTTPS
32 changes: 32 additions & 0 deletions examples/slack-reaction-listener/functions/HandleSlackReaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { GleeFunction } from "@asyncapi/glee"

const myFunction: GleeFunction = async ({ payload }) => {
const { envelope_id } = payload
const reaction = payload?.payload?.event?.reaction
if (!reaction) return
return {
reply: [
{
payload: {
envelope_id
}
}
],
send: [{
server: "OpenAI_HTTPS",
channel: "OpenAICompletion",
headers: {
'Authorization': `Bearer ${process.env.CHAT_API}`
},
payload: {
model: "gpt-3.5-turbo",
messages: [{ "role": "user", "content": `Someone reacted with "${reaction}" emoji to my message on Slack, write something fun and short to them.` }],
temperature: 0.7
}
}]
}


}

export default myFunction
28 changes: 28 additions & 0 deletions examples/slack-reaction-listener/functions/ReceiveFromOpenAI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { GleeFunction } from "@asyncapi/glee"

const myFunction: GleeFunction = async (event) => {
const { payload } = event.request.request
const slack_event = payload?.payload?.event

if (!slack_event) return

const thread_ts = slack_event.item.ts
const channel = slack_event.item.channel
const text = event.payload.choices[0].message.content


return {
send: [{
channel: "SlackPostMessage",
server: "Slack_HTTPS",
payload: {
channel, thread_ts, text
},
headers: {
Authorization: `Bearer ${process.env.SLACK_HTTP}`,
}
}]
}
}

export default myFunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const myFunction = async ({ payload }) => {
if (payload.ok) {
console.log("everything went smoothly.")
}
}

export default myFunction
7 changes: 7 additions & 0 deletions examples/slack-reaction-listener/glee.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default async function () {
return {
docs: {
enabled: false
}
}
}
Loading

0 comments on commit 8f226b8

Please sign in to comment.