diff --git a/packages/backend/src/apps/slack/actions.ts b/packages/backend/src/apps/slack/actions.ts
deleted file mode 100644
index e4063b76..00000000
--- a/packages/backend/src/apps/slack/actions.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import SendMessageToChannel from './actions/send-message-to-channel';
-import FindMessage from './actions/find-message';
-import SlackClient from './client';
-
-export default class Actions {
- client: SlackClient;
- sendMessageToChannel: SendMessageToChannel;
- findMessage: FindMessage;
-
- constructor(client: SlackClient) {
- this.client = client;
- this.sendMessageToChannel = new SendMessageToChannel(client);
- this.findMessage = new FindMessage(client);
- }
-}
diff --git a/packages/backend/src/apps/slack/actions/find-message.ts b/packages/backend/src/apps/slack/actions/find-message.ts
deleted file mode 100644
index 4185d7b5..00000000
--- a/packages/backend/src/apps/slack/actions/find-message.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import SlackClient from '../client';
-
-export default class FindMessage {
- client: SlackClient;
-
- constructor(client: SlackClient) {
- this.client = client;
- }
-
- async run() {
- const parameters = this.client.step.parameters;
- const query = parameters.query as string;
- const sortBy = parameters.sortBy as string;
- const sortDirection = parameters.sortDirection as string;
- const count = 1;
-
- const messages = await this.client.findMessages.run(
- query,
- sortBy,
- sortDirection,
- count,
- );
-
- return messages;
- }
-}
diff --git a/packages/backend/src/apps/slack/actions/send-message-to-channel.ts b/packages/backend/src/apps/slack/actions/send-message-to-channel.ts
deleted file mode 100644
index e5f290fe..00000000
--- a/packages/backend/src/apps/slack/actions/send-message-to-channel.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import SlackClient from '../client';
-
-export default class SendMessageToChannel {
- client: SlackClient;
-
- constructor(client: SlackClient) {
- this.client = client;
- }
-
- async run() {
- const channelId = this.client.step.parameters.channel as string;
- const text = this.client.step.parameters.message as string;
-
- const message = await this.client.postMessageToChannel.run(channelId, text);
-
- return message;
- }
-}
diff --git a/packages/backend/src/apps/slack/assets/favicon.svg b/packages/backend/src/apps/slack/assets/favicon.svg
deleted file mode 100644
index 3fbb3e78..00000000
--- a/packages/backend/src/apps/slack/assets/favicon.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
diff --git a/packages/backend/src/apps/slack/authentication.ts b/packages/backend/src/apps/slack/authentication.ts
deleted file mode 100644
index b08394f9..00000000
--- a/packages/backend/src/apps/slack/authentication.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { IAuthentication, IJSONObject } from '@automatisch/types';
-import SlackClient from './client';
-
-export default class Authentication implements IAuthentication {
- client: SlackClient;
-
- static requestOptions: IJSONObject = {
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- };
-
- constructor(client: SlackClient) {
- this.client = client;
- }
-
- async verifyCredentials() {
- const { bot_id: botId, user: screenName } =
- await this.client.verifyAccessToken.run();
-
- return {
- botId,
- screenName,
- token: this.client.connection.formattedData.accessToken,
- };
- }
-
- async isStillVerified() {
- try {
- await this.client.verifyAccessToken.run();
- return true;
- } catch (error) {
- return false;
- }
- }
-}
diff --git a/packages/backend/src/apps/slack/client/endpoints/find-messages.ts b/packages/backend/src/apps/slack/client/endpoints/find-messages.ts
deleted file mode 100644
index 50d99819..00000000
--- a/packages/backend/src/apps/slack/client/endpoints/find-messages.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import SlackClient from '../index';
-
-export default class FindMessages {
- client: SlackClient;
-
- constructor(client: SlackClient) {
- this.client = client;
- }
-
- async run(query: string, sortBy: string, sortDirection: string, count = 1) {
- const headers = {
- Authorization: `Bearer ${this.client.connection.formattedData.accessToken}`,
- };
-
- const params = {
- query,
- sort: sortBy,
- sort_dir: sortDirection,
- count,
- };
-
- const response = await this.client.httpClient.get('/search.messages', {
- headers,
- params,
- });
-
- const data = response.data;
-
- if (!data.ok) {
- if (data.error === 'missing_scope') {
- throw new Error(
- `Error occured while finding messages; ${data.error}: ${data.needed}`
- );
- }
-
- throw new Error(`Error occured while finding messages; ${data.error}`);
- }
-
- const messages = data.messages.matches;
- const message = messages?.[0];
-
- return message;
- }
-}
diff --git a/packages/backend/src/apps/slack/client/endpoints/post-message-to-channel.ts b/packages/backend/src/apps/slack/client/endpoints/post-message-to-channel.ts
deleted file mode 100644
index c8f54d7e..00000000
--- a/packages/backend/src/apps/slack/client/endpoints/post-message-to-channel.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import SlackClient from '../index';
-import { IJSONObject } from '@automatisch/types';
-
-export default class PostMessageToChannel {
- client: SlackClient;
-
- constructor(client: SlackClient) {
- this.client = client;
- }
-
- async run(channelId: string, text: string) {
- const message: {
- data: IJSONObject | null;
- error: IJSONObject | null;
- } = {
- data: null,
- error: null,
- };
-
- const headers = {
- Authorization: `Bearer ${this.client.connection.formattedData.accessToken}`,
- };
-
- const params = {
- channel: channelId,
- text,
- };
-
- const response = await this.client.httpClient.post(
- '/chat.postMessage',
- params,
- { headers }
- );
-
- message.error = response?.integrationError;
- message.data = response?.data?.message;
-
- if (response.data.ok === false) {
- message.error = response.data;
- }
-
- return message;
- }
-}
diff --git a/packages/backend/src/apps/slack/client/endpoints/verify-access-token.ts b/packages/backend/src/apps/slack/client/endpoints/verify-access-token.ts
deleted file mode 100644
index d5b3bfb5..00000000
--- a/packages/backend/src/apps/slack/client/endpoints/verify-access-token.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import qs from 'qs';
-import SlackClient from '../index';
-
-export default class VerifyAccessToken {
- client: SlackClient;
-
- static requestOptions: IJSONObject = {
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- };
-
- constructor(client: SlackClient) {
- this.client = client;
- }
-
- async run() {
- const response = await this.client.httpClient.post(
- '/auth.test',
- qs.stringify({
- token: this.client.connection.formattedData.accessToken,
- }),
- VerifyAccessToken.requestOptions
- );
-
- if (response.data.ok === false) {
- throw new Error(
- `Error occured while verifying credentials: ${response.data.error}.(More info: https://api.slack.com/methods/auth.test#errors)`
- );
- }
-
- return response.data;
- }
-}
diff --git a/packages/backend/src/apps/slack/client/index.ts b/packages/backend/src/apps/slack/client/index.ts
deleted file mode 100644
index 5e5d79e3..00000000
--- a/packages/backend/src/apps/slack/client/index.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { IFlow, IStep, IConnection } from '@automatisch/types';
-import createHttpClient, { IHttpClient } from '../../../helpers/http-client';
-import VerifyAccessToken from './endpoints/verify-access-token';
-import PostMessageToChannel from './endpoints/post-message-to-channel';
-import FindMessages from './endpoints/find-messages';
-
-export default class SlackClient {
- flow: IFlow;
- step: IStep;
- connection: IConnection;
- httpClient: IHttpClient;
-
- verifyAccessToken: VerifyAccessToken;
- postMessageToChannel: PostMessageToChannel;
- findMessages: FindMessages;
-
- static baseUrl = 'https://slack.com/api';
-
- constructor(connection: IConnection, flow?: IFlow, step?: IStep) {
- this.connection = connection;
- this.flow = flow;
- this.step = step;
-
- this.httpClient = createHttpClient({ baseURL: SlackClient.baseUrl });
- this.verifyAccessToken = new VerifyAccessToken(this);
- this.postMessageToChannel = new PostMessageToChannel(this);
- this.findMessages = new FindMessages(this);
- }
-}
diff --git a/packages/backend/src/apps/slack/data.ts b/packages/backend/src/apps/slack/data.ts
deleted file mode 100644
index d2743b00..00000000
--- a/packages/backend/src/apps/slack/data.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import ListChannels from './data/list-channels';
-import SlackClient from './client';
-
-export default class Data {
- client: SlackClient;
- listChannels: ListChannels;
-
- constructor(client: SlackClient) {
- this.client = client;
- this.listChannels = new ListChannels(client);
- }
-}
diff --git a/packages/backend/src/apps/slack/data/list-channels.ts b/packages/backend/src/apps/slack/data/list-channels.ts
deleted file mode 100644
index e6b8593b..00000000
--- a/packages/backend/src/apps/slack/data/list-channels.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import SlackClient from '../client';
-
-export default class ListChannels {
- client: SlackClient;
-
- constructor(client: SlackClient) {
- this.client = client;
- }
-
- async run() {
- const response = await this.client.httpClient.get('/conversations.list', {
- headers: {
- Authorization: `Bearer ${this.client.connection.formattedData.accessToken}`,
- },
- });
-
- if (response.data.ok === 'false') {
- throw new Error(
- `Error occured while fetching slack channels: ${response.data.error}`
- );
- }
-
- return response.data.channels.map((channel: IJSONObject) => {
- return {
- value: channel.id,
- name: channel.name,
- };
- });
- }
-}
diff --git a/packages/backend/src/apps/slack/index.d.ts b/packages/backend/src/apps/slack/index.d.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/packages/backend/src/apps/slack/index.ts b/packages/backend/src/apps/slack/index.ts
deleted file mode 100644
index 6736e09a..00000000
--- a/packages/backend/src/apps/slack/index.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import {
- IService,
- IAuthentication,
- IConnection,
- IFlow,
- IStep,
-} from '@automatisch/types';
-import Authentication from './authentication';
-import Triggers from './triggers';
-import Actions from './actions';
-import Data from './data';
-import SlackClient from './client';
-
-export default class Slack implements IService {
- client: SlackClient;
-
- authenticationClient: IAuthentication;
- triggers: Triggers;
- actions: Actions;
- data: Data;
-
- constructor(connection: IConnection, flow?: IFlow, step?: IStep) {
- this.client = new SlackClient(connection, flow, step);
-
- this.authenticationClient = new Authentication(this.client);
- // this.triggers = new Triggers(this.client);
- this.actions = new Actions(this.client);
- this.data = new Data(this.client);
- }
-}
diff --git a/packages/backend/src/apps/slack/info.json b/packages/backend/src/apps/slack/info.json
deleted file mode 100644
index 45a4ccd2..00000000
--- a/packages/backend/src/apps/slack/info.json
+++ /dev/null
@@ -1,278 +0,0 @@
-{
- "name": "Slack",
- "key": "slack",
- "iconUrl": "{BASE_URL}/apps/slack/assets/favicon.svg",
- "docUrl": "https://automatisch.io/docs/slack",
- "authDocUrl": "https://automatisch.io/docs/connections/slack",
- "primaryColor": "2DAAE1",
- "supportsConnections": true,
- "baseUrl": "https://slack.com/api",
- "fields": [
- {
- "key": "accessToken",
- "label": "Access Token",
- "type": "string",
- "required": true,
- "readOnly": false,
- "value": null,
- "placeholder": null,
- "description": "Access token of slack that Automatisch will connect to.",
- "clickToCopy": false
- }
- ],
- "authenticationSteps": [
- {
- "step": 1,
- "type": "mutation",
- "name": "createConnection",
- "arguments": [
- {
- "name": "key",
- "value": "{key}"
- },
- {
- "name": "formattedData",
- "value": null,
- "properties": [
- {
- "name": "accessToken",
- "value": "{fields.accessToken}"
- }
- ]
- }
- ]
- },
- {
- "step": 2,
- "type": "mutation",
- "name": "verifyConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{createConnection.id}"
- }
- ]
- }
- ],
- "reconnectionSteps": [
- {
- "step": 1,
- "type": "mutation",
- "name": "resetConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- }
- ]
- },
- {
- "step": 2,
- "type": "mutation",
- "name": "updateConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- },
- {
- "name": "formattedData",
- "value": null,
- "properties": [
- {
- "name": "accessToken",
- "value": "{fields.accessToken}"
- }
- ]
- }
- ]
- },
- {
- "step": 3,
- "type": "mutation",
- "name": "verifyConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- }
- ]
- }
- ],
- "triggers": [
- {
- "name": "New message posted to a channel",
- "key": "newMessageToChannel",
- "pollInterval": 15,
- "description": "Triggers when a new message is posted to a channel",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "chooseTrigger",
- "name": "Set up a trigger",
- "arguments": [
- {
- "label": "Channel",
- "key": "channel",
- "type": "dropdown",
- "required": true,
- "variables": false,
- "source": {
- "type": "query",
- "name": "getData",
- "arguments": [
- {
- "name": "key",
- "value": "listChannels"
- }
- ]
- }
- },
- {
- "label": "Trigger for Bot Messages?",
- "key": "triggerForBotMessages",
- "type": "dropdown",
- "description": "Should this flow trigger for bot messages?",
- "required": true,
- "value": true,
- "variables": false,
- "options": [
- {
- "label": "Yes",
- "value": true
- },
- {
- "label": "No",
- "value": false
- }
- ]
- }
- ]
- },
- {
- "key": "testStep",
- "name": "Test trigger"
- }
- ]
- }
- ],
- "actions": [
- {
- "name": "Send a message to channel",
- "key": "sendMessageToChannel",
- "description": "Send a message to a specific channel you specify.",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "setupAction",
- "name": "Set up action",
- "arguments": [
- {
- "label": "Channel",
- "key": "channel",
- "type": "dropdown",
- "required": true,
- "description": "Pick a channel to send the message to.",
- "variables": false,
- "source": {
- "type": "query",
- "name": "getData",
- "arguments": [
- {
- "name": "key",
- "value": "listChannels"
- }
- ]
- }
- },
- {
- "label": "Message text",
- "key": "message",
- "type": "string",
- "required": true,
- "description": "The content of your new message.",
- "variables": true
- }
- ]
- },
- {
- "key": "testStep",
- "name": "Test action"
- }
- ]
- },
- {
- "name": "Find message",
- "key": "findMessage",
- "description": "Find a Slack message using the Slack Search feature.",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "setupAction",
- "name": "Set up action",
- "arguments": [
- {
- "label": "Search Query",
- "key": "query",
- "type": "string",
- "required": true,
- "description": "Search query to use for finding matching messages. See the Slack Search Documentation for more information on constructing a query.",
- "variables": true
- },
- {
- "label": "Sort by",
- "key": "sortBy",
- "type": "dropdown",
- "description": "Sort messages by their match strength or by their date. Default is score.",
- "required": true,
- "value": "score",
- "variables": false,
- "options": [
- {
- "label": "Match strength",
- "value": "score"
- },
- {
- "label": "Message date time",
- "value": "timestamp"
- }
- ]
- },
- {
- "label": "Sort direction",
- "key": "sortDirection",
- "type": "dropdown",
- "description": "Sort matching messages in ascending or descending order. Default is descending.",
- "required": true,
- "value": "desc",
- "variables": false,
- "options": [
- {
- "label": "Descending (newest or best match first)",
- "value": "desc"
- },
- {
- "label": "Ascending (oldest or worst match first)",
- "value": "asc"
- }
- ]
- }
- ]
- },
- {
- "key": "testStep",
- "name": "Test action"
- }
- ]
- }
- ]
-}
diff --git a/packages/backend/src/apps/slack/triggers.ts b/packages/backend/src/apps/slack/triggers.ts
deleted file mode 100644
index 8e5862e8..00000000
--- a/packages/backend/src/apps/slack/triggers.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import NewMessageToChannel from './triggers/new-message-to-channel';
-
-export default class Triggers {
- newMessageToChannel: NewMessageToChannel;
-
- constructor(connectionData: IJSONObject, parameters: IJSONObject) {
- this.newMessageToChannel = new NewMessageToChannel(
- connectionData,
- parameters
- );
- }
-}
diff --git a/packages/backend/src/apps/slack/triggers/new-message-to-channel.ts b/packages/backend/src/apps/slack/triggers/new-message-to-channel.ts
deleted file mode 100644
index af32c0d7..00000000
--- a/packages/backend/src/apps/slack/triggers/new-message-to-channel.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import axios, { AxiosInstance } from 'axios';
-
-export default class NewMessageToChannel {
- httpClient: AxiosInstance;
- parameters: IJSONObject;
- connectionData: IJSONObject;
- BASE_URL = 'https://slack.com/api';
-
- constructor(connectionData: IJSONObject, parameters: IJSONObject) {
- this.httpClient = axios.create({ baseURL: this.BASE_URL });
- this.connectionData = connectionData;
- this.parameters = parameters;
- }
-
- async run() {
- // TODO: Fix after webhook implementation.
- }
-
- async testRun() {
- const headers = {
- Authorization: `Bearer ${this.connectionData.accessToken}`,
- };
-
- const params = {
- channel: this.parameters.channel,
- };
-
- const response = await this.httpClient.get('/conversations.history', {
- headers,
- params,
- });
-
- let lastMessage;
-
- if (this.parameters.triggerForBotMessages) {
- lastMessage = response.data.messages[0];
- } else {
- lastMessage = response.data.messages.find(
- (message: IJSONObject) =>
- !Object.prototype.hasOwnProperty.call(message, 'bot_id')
- );
- }
-
- return [lastMessage];
- }
-}
diff --git a/packages/backend/src/apps/twitter/actions.ts b/packages/backend/src/apps/twitter/actions.ts
deleted file mode 100644
index 2b3aa93d..00000000
--- a/packages/backend/src/apps/twitter/actions.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import TwitterClient from './client';
-import CreateTweet from './actions/create-tweet';
-
-export default class Actions {
- client: TwitterClient;
- createTweet: CreateTweet;
-
- constructor(client: TwitterClient) {
- this.client = client;
- this.createTweet = new CreateTweet(client);
- }
-}
diff --git a/packages/backend/src/apps/twitter/actions/create-tweet.ts b/packages/backend/src/apps/twitter/actions/create-tweet.ts
deleted file mode 100644
index 482ae0b4..00000000
--- a/packages/backend/src/apps/twitter/actions/create-tweet.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import TwitterClient from '../client';
-
-export default class CreateTweet {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run() {
- const tweet = await this.client.createTweet.run(
- this.client.step.parameters.tweet as string
- );
-
- return tweet;
- }
-}
diff --git a/packages/backend/src/apps/twitter/assets/favicon.svg b/packages/backend/src/apps/twitter/assets/favicon.svg
deleted file mode 100644
index 752cdc8d..00000000
--- a/packages/backend/src/apps/twitter/assets/favicon.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/packages/backend/src/apps/twitter/authentication.ts b/packages/backend/src/apps/twitter/authentication.ts
deleted file mode 100644
index 5ddb83d7..00000000
--- a/packages/backend/src/apps/twitter/authentication.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import type { IAuthentication, IField } from '@automatisch/types';
-import { URLSearchParams } from 'url';
-import TwitterClient from './client';
-
-export default class Authentication implements IAuthentication {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async createAuthData() {
- const appFields = this.client.connection.appData.fields.find(
- (field: IField) => field.key == 'oAuthRedirectUrl'
- );
- const callbackUrl = appFields.value;
-
- const response = await this.client.oauthRequestToken.run(callbackUrl);
- const responseData = Object.fromEntries(new URLSearchParams(response.data));
-
- return {
- url: `${TwitterClient.baseUrl}/oauth/authorize?oauth_token=${responseData.oauth_token}`,
- accessToken: responseData.oauth_token,
- accessSecret: responseData.oauth_token_secret,
- };
- }
-
- async verifyCredentials() {
- const response = await this.client.verifyAccessToken.run();
- const responseData = Object.fromEntries(new URLSearchParams(response.data));
-
- return {
- consumerKey: this.client.connection.formattedData.consumerKey as string,
- consumerSecret: this.client.connection.formattedData
- .consumerSecret as string,
- accessToken: responseData.oauth_token,
- accessSecret: responseData.oauth_token_secret,
- userId: responseData.user_id,
- screenName: responseData.screen_name,
- };
- }
-
- async isStillVerified() {
- try {
- await this.client.getCurrentUser.run();
- return true;
- } catch (error) {
- return false;
- }
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/create-tweet.ts b/packages/backend/src/apps/twitter/client/endpoints/create-tweet.ts
deleted file mode 100644
index 52eade01..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/create-tweet.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import TwitterClient from '../index';
-
-export default class CreateTweet {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(text: string) {
- try {
- const token = {
- key: this.client.connection.formattedData.accessToken as string,
- secret: this.client.connection.formattedData.accessSecret as string,
- };
-
- const requestData = {
- url: `${TwitterClient.baseUrl}/2/tweets`,
- method: 'POST',
- };
-
- const authHeader = this.client.oauthClient.toHeader(
- this.client.oauthClient.authorize(requestData, token)
- );
-
- const response = await this.client.httpClient.post(
- `/2/tweets`,
- { text },
- { headers: { ...authHeader } }
- );
-
- const tweet = response.data.data;
-
- return tweet;
- } catch (error) {
- const errorMessage = error.response.data.detail;
- throw new Error(`Error occured while creating a tweet: ${errorMessage}`);
- }
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/get-current-user.ts b/packages/backend/src/apps/twitter/client/endpoints/get-current-user.ts
deleted file mode 100644
index 4ccaa575..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/get-current-user.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import TwitterClient from '../index';
-
-export default class GetCurrentUser {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run() {
- const token = {
- key: this.client.connection.formattedData.accessToken as string,
- secret: this.client.connection.formattedData.accessSecret as string,
- };
-
- const requestPath = '/2/users/me';
-
- const requestData = {
- url: `${TwitterClient.baseUrl}${requestPath}`,
- method: 'GET',
- };
-
- const authHeader = this.client.oauthClient.toHeader(
- this.client.oauthClient.authorize(requestData, token)
- );
-
- const response = await this.client.httpClient.get(requestPath, {
- headers: { ...authHeader },
- });
-
- const currentUser = response.data.data;
-
- return currentUser;
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/get-user-by-username.ts b/packages/backend/src/apps/twitter/client/endpoints/get-user-by-username.ts
deleted file mode 100644
index 23ac8b83..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/get-user-by-username.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import TwitterClient from '../index';
-
-export default class GetUserByUsername {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(username: string) {
- const token = {
- key: this.client.connection.formattedData.accessToken as string,
- secret: this.client.connection.formattedData.accessSecret as string,
- };
-
- const requestPath = `/2/users/by/username/${username}`;
-
- const requestData = {
- url: `${TwitterClient.baseUrl}${requestPath}`,
- method: 'GET',
- };
-
- const authHeader = this.client.oauthClient.toHeader(
- this.client.oauthClient.authorize(requestData, token)
- );
-
- const response = await this.client.httpClient.get(requestPath, {
- headers: { ...authHeader },
- });
-
- if (response.data?.errors) {
- const errorMessages = response.data.errors
- .map((error: IJSONObject) => error.detail)
- .join(' ');
-
- throw new Error(
- `Error occured while fetching user data: ${errorMessages}`
- );
- }
-
- const user = response.data.data;
- return user;
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/get-user-followers.ts b/packages/backend/src/apps/twitter/client/endpoints/get-user-followers.ts
deleted file mode 100644
index 543a007f..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/get-user-followers.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import { URLSearchParams } from 'url';
-import TwitterClient from '../index';
-import omitBy from 'lodash/omitBy';
-import isEmpty from 'lodash/isEmpty';
-
-export default class GetUserFollowers {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(userId: string, lastInternalId?: string) {
- const token = {
- key: this.client.connection.formattedData.accessToken as string,
- secret: this.client.connection.formattedData.accessSecret as string,
- };
-
- let response;
- const followers: IJSONObject[] = [];
-
- do {
- const params: IJSONObject = {
- pagination_token: response?.data?.meta?.next_token,
- };
-
- const queryParams = new URLSearchParams(omitBy(params, isEmpty));
-
- const requestPath = `/2/users/${userId}/followers${
- queryParams.toString() ? `?${queryParams.toString()}` : ''
- }`;
-
- const requestData = {
- url: `${TwitterClient.baseUrl}${requestPath}`,
- method: 'GET',
- };
-
- const authHeader = this.client.oauthClient.toHeader(
- this.client.oauthClient.authorize(requestData, token)
- );
-
- response = await this.client.httpClient.get(requestPath, {
- headers: { ...authHeader },
- });
-
- if (response.data.meta.result_count > 0) {
- response.data.data.forEach((tweet: IJSONObject) => {
- if (!lastInternalId || Number(tweet.id) > Number(lastInternalId)) {
- followers.push(tweet);
- } else {
- return;
- }
- });
- }
- } while (response.data.meta.next_token && lastInternalId);
-
- if (response.data?.errors) {
- const errorMessages = response.data.errors
- .map((error: IJSONObject) => error.detail)
- .join(' ');
-
- throw new Error(
- `Error occured while fetching user data: ${errorMessages}`
- );
- }
-
- return followers;
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/get-user-tweets.ts b/packages/backend/src/apps/twitter/client/endpoints/get-user-tweets.ts
deleted file mode 100644
index b6343ced..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/get-user-tweets.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import { URLSearchParams } from 'url';
-import TwitterClient from '../index';
-import omitBy from 'lodash/omitBy';
-import isEmpty from 'lodash/isEmpty';
-
-export default class GetUserTweets {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(userId: string, lastInternalId?: string) {
- const token = {
- key: this.client.connection.formattedData.accessToken as string,
- secret: this.client.connection.formattedData.accessSecret as string,
- };
-
- let response;
- const tweets: IJSONObject[] = [];
-
- do {
- const params: IJSONObject = {
- since_id: lastInternalId,
- pagination_token: response?.data?.meta?.next_token,
- };
-
- const queryParams = new URLSearchParams(omitBy(params, isEmpty));
-
- const requestPath = `/2/users/${userId}/tweets${
- queryParams.toString() ? `?${queryParams.toString()}` : ''
- }`;
-
- const requestData = {
- url: `${TwitterClient.baseUrl}${requestPath}`,
- method: 'GET',
- };
-
- const authHeader = this.client.oauthClient.toHeader(
- this.client.oauthClient.authorize(requestData, token)
- );
-
- response = await this.client.httpClient.get(requestPath, {
- headers: { ...authHeader },
- });
-
- if (response.data.meta.result_count > 0) {
- response.data.data.forEach((tweet: IJSONObject) => {
- if (!lastInternalId || Number(tweet.id) > Number(lastInternalId)) {
- tweets.push(tweet);
- } else {
- return;
- }
- });
- }
- } while (response.data.meta.next_token && lastInternalId);
-
- if (response.data?.errors) {
- const errorMessages = response.data.errors
- .map((error: IJSONObject) => error.detail)
- .join(' ');
-
- throw new Error(
- `Error occured while fetching user data: ${errorMessages}`
- );
- }
-
- return tweets;
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/oauth-request-token.ts b/packages/backend/src/apps/twitter/client/endpoints/oauth-request-token.ts
deleted file mode 100644
index 6ef9d899..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/oauth-request-token.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import TwitterClient from '../index';
-
-export default class OAuthRequestToken {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(callbackUrl: string) {
- try {
- const requestData = {
- url: `${TwitterClient.baseUrl}/oauth/request_token`,
- method: 'POST',
- data: { oauth_callback: callbackUrl },
- };
-
- const authHeader = this.client.oauthClient.toHeader(
- this.client.oauthClient.authorize(requestData)
- );
-
- const response = await this.client.httpClient.post(
- `/oauth/request_token`,
- null,
- {
- headers: { ...authHeader },
- }
- );
-
- return response;
- } catch (error) {
- const errorMessages = error.response.data.errors
- .map((error: IJSONObject) => error.message)
- .join(' ');
-
- throw new Error(
- `Error occured while verifying credentials: ${errorMessages}`
- );
- }
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/search-tweets.ts b/packages/backend/src/apps/twitter/client/endpoints/search-tweets.ts
deleted file mode 100644
index 89eddeba..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/search-tweets.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { IJSONObject } from '@automatisch/types';
-import { URLSearchParams } from 'url';
-import TwitterClient from '../index';
-import omitBy from 'lodash/omitBy';
-import isEmpty from 'lodash/isEmpty';
-import qs from 'qs';
-
-export default class SearchTweets {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(searchTerm: string, lastInternalId?: string) {
- const token = {
- key: this.client.connection.formattedData.accessToken as string,
- secret: this.client.connection.formattedData.accessSecret as string,
- };
-
- let response;
- const tweets: {
- data: IJSONObject[];
- error: IJSONObject | null;
- } = {
- data: [],
- error: null,
- };
-
- do {
- const params: IJSONObject = {
- query: searchTerm,
- since_id: lastInternalId,
- pagination_token: response?.data?.meta?.next_token,
- };
-
- const queryParams = qs.stringify(omitBy(params, isEmpty));
-
- const requestPath = `/2/tweets/search/recent${
- queryParams.toString() ? `?${queryParams.toString()}` : ''
- }`;
-
- const requestData = {
- url: `${TwitterClient.baseUrl}${requestPath}`,
- method: 'GET',
- };
-
- const authHeader = this.client.oauthClient.toHeader(
- this.client.oauthClient.authorize(requestData, token)
- );
-
- response = await this.client.httpClient.get(requestPath, {
- headers: { ...authHeader },
- });
-
- if (response.integrationError) {
- tweets.error = response.integrationError;
- return tweets;
- }
-
- if (response.data.meta.result_count > 0) {
- response.data.data.forEach((tweet: IJSONObject) => {
- if (!lastInternalId || Number(tweet.id) > Number(lastInternalId)) {
- tweets.data.push(tweet);
- } else {
- return;
- }
- });
- }
- } while (response.data.meta.next_token && lastInternalId);
-
- return tweets;
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/endpoints/verify-access-token.ts b/packages/backend/src/apps/twitter/client/endpoints/verify-access-token.ts
deleted file mode 100644
index 7c51a52f..00000000
--- a/packages/backend/src/apps/twitter/client/endpoints/verify-access-token.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import TwitterClient from '../index';
-
-export default class VerifyAccessToken {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run() {
- try {
- return await this.client.httpClient.post(
- `/oauth/access_token?oauth_verifier=${this.client.connection.formattedData.oauthVerifier}&oauth_token=${this.client.connection.formattedData.accessToken}`,
- null
- );
- } catch (error) {
- throw new Error(error.response.data);
- }
- }
-}
diff --git a/packages/backend/src/apps/twitter/client/index.ts b/packages/backend/src/apps/twitter/client/index.ts
deleted file mode 100644
index 4cb899b5..00000000
--- a/packages/backend/src/apps/twitter/client/index.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { IFlow, IStep, IConnection } from '@automatisch/types';
-import OAuth from 'oauth-1.0a';
-import crypto from 'crypto';
-import createHttpClient, { IHttpClient } from '../../../helpers/http-client';
-import OAuthRequestToken from './endpoints/oauth-request-token';
-import VerifyAccessToken from './endpoints/verify-access-token';
-import GetCurrentUser from './endpoints/get-current-user';
-import GetUserByUsername from './endpoints/get-user-by-username';
-import GetUserTweets from './endpoints/get-user-tweets';
-import CreateTweet from './endpoints/create-tweet';
-import SearchTweets from './endpoints/search-tweets';
-import GetUserFollowers from './endpoints/get-user-followers';
-
-export default class TwitterClient {
- flow: IFlow;
- step: IStep;
- connection: IConnection;
- oauthClient: OAuth;
- httpClient: IHttpClient;
-
- oauthRequestToken: OAuthRequestToken;
- verifyAccessToken: VerifyAccessToken;
- getCurrentUser: GetCurrentUser;
- getUserByUsername: GetUserByUsername;
- getUserTweets: GetUserTweets;
- createTweet: CreateTweet;
- searchTweets: SearchTweets;
- getUserFollowers: GetUserFollowers;
-
- static baseUrl = 'https://api.twitter.com';
-
- constructor(connection: IConnection, flow?: IFlow, step?: IStep) {
- this.connection = connection;
- this.flow = flow;
- this.step = step;
-
- this.httpClient = createHttpClient({ baseURL: TwitterClient.baseUrl });
-
- const consumerData = {
- key: this.connection.formattedData.consumerKey as string,
- secret: this.connection.formattedData.consumerSecret as string,
- };
-
- this.oauthClient = new OAuth({
- consumer: consumerData,
- signature_method: 'HMAC-SHA1',
- hash_function(base_string, key) {
- return crypto
- .createHmac('sha1', key)
- .update(base_string)
- .digest('base64');
- },
- });
-
- this.oauthRequestToken = new OAuthRequestToken(this);
- this.verifyAccessToken = new VerifyAccessToken(this);
- this.getCurrentUser = new GetCurrentUser(this);
- this.getUserByUsername = new GetUserByUsername(this);
- this.getUserTweets = new GetUserTweets(this);
- this.createTweet = new CreateTweet(this);
- this.searchTweets = new SearchTweets(this);
- this.getUserFollowers = new GetUserFollowers(this);
- }
-}
diff --git a/packages/backend/src/apps/twitter/index.d.ts b/packages/backend/src/apps/twitter/index.d.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/packages/backend/src/apps/twitter/index.ts b/packages/backend/src/apps/twitter/index.ts
deleted file mode 100644
index ee8e1005..00000000
--- a/packages/backend/src/apps/twitter/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import {
- IService,
- IAuthentication,
- IFlow,
- IStep,
- IConnection,
-} from '@automatisch/types';
-import Authentication from './authentication';
-import Triggers from './triggers';
-import Actions from './actions';
-import TwitterClient from './client';
-
-export default class Twitter implements IService {
- client: TwitterClient;
-
- authenticationClient: IAuthentication;
- triggers: Triggers;
- actions: Actions;
-
- constructor(connection: IConnection, flow?: IFlow, step?: IStep) {
- this.client = new TwitterClient(connection, flow, step);
-
- this.authenticationClient = new Authentication(this.client);
- this.triggers = new Triggers(this.client);
- this.actions = new Actions(this.client);
- }
-}
diff --git a/packages/backend/src/apps/twitter/info.json b/packages/backend/src/apps/twitter/info.json
deleted file mode 100644
index c268e290..00000000
--- a/packages/backend/src/apps/twitter/info.json
+++ /dev/null
@@ -1,339 +0,0 @@
-{
- "name": "Twitter",
- "key": "twitter",
- "iconUrl": "{BASE_URL}/apps/twitter/assets/favicon.svg",
- "docUrl": "https://automatisch.io/docs/twitter",
- "authDocUrl": "https://automatisch.io/docs/connections/twitter",
- "primaryColor": "2DAAE1",
- "supportsConnections": true,
- "baseUrl": "https://api.twitter.com",
- "fields": [
- {
- "key": "oAuthRedirectUrl",
- "label": "OAuth Redirect URL",
- "type": "string",
- "required": true,
- "readOnly": true,
- "value": "{WEB_APP_URL}/app/twitter/connections/add",
- "placeholder": null,
- "description": "When asked to input an OAuth callback or redirect URL in Twitter OAuth, enter the URL above.",
- "clickToCopy": true
- },
- {
- "key": "consumerKey",
- "label": "API Key",
- "type": "string",
- "required": true,
- "readOnly": false,
- "value": null,
- "placeholder": null,
- "description": null,
- "clickToCopy": false
- },
- {
- "key": "consumerSecret",
- "label": "API Secret",
- "type": "string",
- "required": true,
- "readOnly": false,
- "value": null,
- "placeholder": null,
- "description": null,
- "clickToCopy": false
- }
- ],
- "authenticationSteps": [
- {
- "step": 1,
- "type": "mutation",
- "name": "createConnection",
- "arguments": [
- {
- "name": "key",
- "value": "{key}"
- },
- {
- "name": "formattedData",
- "value": null,
- "properties": [
- {
- "name": "consumerKey",
- "value": "{fields.consumerKey}"
- },
- {
- "name": "consumerSecret",
- "value": "{fields.consumerSecret}"
- }
- ]
- }
- ]
- },
- {
- "step": 2,
- "type": "mutation",
- "name": "createAuthData",
- "arguments": [
- {
- "name": "id",
- "value": "{createConnection.id}"
- }
- ]
- },
- {
- "step": 3,
- "type": "openWithPopup",
- "name": "openAuthPopup",
- "arguments": [
- {
- "name": "url",
- "value": "{createAuthData.url}"
- }
- ]
- },
- {
- "step": 4,
- "type": "mutation",
- "name": "updateConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{createConnection.id}"
- },
- {
- "name": "formattedData",
- "value": null,
- "properties": [
- {
- "name": "oauthVerifier",
- "value": "{openAuthPopup.oauth_verifier}"
- }
- ]
- }
- ]
- },
- {
- "step": 5,
- "type": "mutation",
- "name": "verifyConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{createConnection.id}"
- }
- ]
- }
- ],
- "reconnectionSteps": [
- {
- "step": 1,
- "type": "mutation",
- "name": "resetConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- }
- ]
- },
- {
- "step": 2,
- "type": "mutation",
- "name": "updateConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- },
- {
- "name": "formattedData",
- "value": null,
- "properties": [
- {
- "name": "consumerKey",
- "value": "{fields.consumerKey}"
- },
- {
- "name": "consumerSecret",
- "value": "{fields.consumerSecret}"
- }
- ]
- }
- ]
- },
- {
- "step": 3,
- "type": "mutation",
- "name": "createAuthData",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- }
- ]
- },
- {
- "step": 4,
- "type": "openWithPopup",
- "name": "openAuthPopup",
- "arguments": [
- {
- "name": "url",
- "value": "{createAuthData.url}"
- }
- ]
- },
- {
- "step": 5,
- "type": "mutation",
- "name": "updateConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- },
- {
- "name": "formattedData",
- "value": null,
- "properties": [
- {
- "name": "oauthVerifier",
- "value": "{openAuthPopup.oauth_verifier}"
- }
- ]
- }
- ]
- },
- {
- "step": 6,
- "type": "mutation",
- "name": "verifyConnection",
- "arguments": [
- {
- "name": "id",
- "value": "{connection.id}"
- }
- ]
- }
- ],
- "triggers": [
- {
- "name": "My Tweets",
- "key": "myTweets",
- "pollInterval": 15,
- "description": "Will be triggered when you tweet something new.",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "testStep",
- "name": "Test trigger"
- }
- ]
- },
- {
- "name": "User Tweets",
- "key": "userTweets",
- "pollInterval": 15,
- "description": "Will be triggered when a specific user tweet something new.",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "chooseTrigger",
- "name": "Set up a trigger",
- "arguments": [
- {
- "label": "Username",
- "key": "username",
- "type": "string",
- "required": true
- }
- ]
- },
- {
- "key": "testStep",
- "name": "Test trigger"
- }
- ]
- },
- {
- "name": "Search Tweets",
- "key": "searchTweets",
- "pollInterval": 15,
- "description": "Will be triggered when any user tweet something containing a specific keyword, phrase, username or hashtag.",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "chooseTrigger",
- "name": "Set up a trigger",
- "arguments": [
- {
- "label": "Search Term",
- "key": "searchTerm",
- "type": "string",
- "required": true
- }
- ]
- },
- {
- "key": "testStep",
- "name": "Test trigger"
- }
- ]
- },
- {
- "name": "New follower of me",
- "key": "myFollowers",
- "pollInterval": 15,
- "description": "Will be triggered when you have a new follower.",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "testStep",
- "name": "Test trigger"
- }
- ]
- }
- ],
- "actions": [
- {
- "name": "Create Tweet",
- "key": "createTweet",
- "description": "Will create a tweet.",
- "substeps": [
- {
- "key": "chooseConnection",
- "name": "Choose connection"
- },
- {
- "key": "chooseAction",
- "name": "Set up action",
- "arguments": [
- {
- "label": "Tweet body",
- "key": "tweet",
- "type": "string",
- "required": true,
- "description": "The content of your new tweet.",
- "variables": true
- }
- ]
- },
- {
- "key": "testStep",
- "name": "Test action"
- }
- ]
- }
- ]
-}
diff --git a/packages/backend/src/apps/twitter/triggers.ts b/packages/backend/src/apps/twitter/triggers.ts
deleted file mode 100644
index 2d611581..00000000
--- a/packages/backend/src/apps/twitter/triggers.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import TwitterClient from './client';
-import UserTweets from './triggers/user-tweets';
-import SearchTweets from './triggers/search-tweets';
-import MyTweets from './triggers/my-tweets';
-import MyFollowers from './triggers/my-followers';
-
-export default class Triggers {
- client: TwitterClient;
- userTweets: UserTweets;
- searchTweets: SearchTweets;
- myTweets: MyTweets;
- myFollowers: MyFollowers;
-
- constructor(client: TwitterClient) {
- this.client = client;
- this.userTweets = new UserTweets(client);
- this.searchTweets = new SearchTweets(client);
- this.myTweets = new MyTweets(client);
- this.myFollowers = new MyFollowers(client);
- }
-}
diff --git a/packages/backend/src/apps/twitter/triggers/my-followers.ts b/packages/backend/src/apps/twitter/triggers/my-followers.ts
deleted file mode 100644
index 8aaaba51..00000000
--- a/packages/backend/src/apps/twitter/triggers/my-followers.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import TwitterClient from '../client';
-
-export default class MyFollowers {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(lastInternalId: string) {
- return this.getFollowers(lastInternalId);
- }
-
- async testRun() {
- return this.getFollowers();
- }
-
- async getFollowers(lastInternalId?: string) {
- const { username } = await this.client.getCurrentUser.run();
- const user = await this.client.getUserByUsername.run(username as string);
-
- const tweets = await this.client.getUserFollowers.run(
- user.id,
- lastInternalId
- );
- return tweets;
- }
-}
diff --git a/packages/backend/src/apps/twitter/triggers/my-tweets.ts b/packages/backend/src/apps/twitter/triggers/my-tweets.ts
deleted file mode 100644
index 60b049b7..00000000
--- a/packages/backend/src/apps/twitter/triggers/my-tweets.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import TwitterClient from '../client';
-
-export default class MyTweets {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run() {
- return this.getTweets(lastInternalId);
- }
-
- async testRun() {
- return this.getTweets();
- }
-
- async getTweets(lastInternalId?: string) {
- const { username } = await this.client.getCurrentUser.run();
- const user = await this.client.getUserByUsername.run(username as string);
-
- const tweets = await this.client.getUserTweets.run(user.id, lastInternalId);
- return tweets;
- }
-}
diff --git a/packages/backend/src/apps/twitter/triggers/search-tweets.ts b/packages/backend/src/apps/twitter/triggers/search-tweets.ts
deleted file mode 100644
index 44ccb5df..00000000
--- a/packages/backend/src/apps/twitter/triggers/search-tweets.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import TwitterClient from '../client';
-
-export default class SearchTweets {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(lastInternalId: string) {
- return this.getTweets(lastInternalId);
- }
-
- async testRun() {
- return this.getTweets();
- }
-
- async getTweets(lastInternalId?: string) {
- const tweets = await this.client.searchTweets.run(
- this.client.step.parameters.searchTerm as string,
- lastInternalId
- );
-
- return tweets;
- }
-}
diff --git a/packages/backend/src/apps/twitter/triggers/user-tweets.ts b/packages/backend/src/apps/twitter/triggers/user-tweets.ts
deleted file mode 100644
index bd4919b0..00000000
--- a/packages/backend/src/apps/twitter/triggers/user-tweets.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import TwitterClient from '../client';
-
-export default class UserTweets {
- client: TwitterClient;
-
- constructor(client: TwitterClient) {
- this.client = client;
- }
-
- async run(lastInternalId: string) {
- return this.getTweets(lastInternalId);
- }
-
- async testRun() {
- return this.getTweets();
- }
-
- async getTweets(lastInternalId?: string) {
- const user = await this.client.getUserByUsername.run(
- this.client.step.parameters.username as string
- );
-
- const tweets = await this.client.getUserTweets.run(user.id, lastInternalId);
-
- return tweets;
- }
-}
diff --git a/packages/backend/src/apps/twitter2/auth/create-auth-data.ts b/packages/backend/src/apps/twitter2/auth/create-auth-data.ts
index cb37e833..b8fe115f 100644
--- a/packages/backend/src/apps/twitter2/auth/create-auth-data.ts
+++ b/packages/backend/src/apps/twitter2/auth/create-auth-data.ts
@@ -4,7 +4,7 @@ import { URLSearchParams } from 'url';
export default async function createAuthData($: IGlobalVariable) {
try {
- const oauthRedirectUrlField = $.app.fields.find(
+ const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
diff --git a/packages/backend/src/apps/twitter2/triggers/new-follower-of-me/my-followers.ts b/packages/backend/src/apps/twitter2/triggers/new-follower-of-me/my-followers.ts
index 11665a5a..a4d0fa0e 100644
--- a/packages/backend/src/apps/twitter2/triggers/new-follower-of-me/my-followers.ts
+++ b/packages/backend/src/apps/twitter2/triggers/new-follower-of-me/my-followers.ts
@@ -1,17 +1,17 @@
-import { IGlobalVariable } from "@automatisch/types";
-import getCurrentUser from "../../common/get-current-user";
-import getUserByUsername from "../../common/get-user-by-username";
-import getUserFollowers from "../../common/get-user-followers";
+import { IGlobalVariable } from '@automatisch/types';
+import getCurrentUser from '../../common/get-current-user';
+import getUserByUsername from '../../common/get-user-by-username';
+import getUserFollowers from '../../common/get-user-followers';
-const myFollowers = async( $: IGlobalVariable, lastInternalId?: string) => {
+const myFollowers = async ($: IGlobalVariable, lastInternalId?: string) => {
const { username } = await getCurrentUser($);
const user = await getUserByUsername($, username as string);
const tweets = await getUserFollowers($, {
userId: user.id,
- lastInternalId
+ lastInternalId,
});
return tweets;
-});
+};
export default myFollowers;
diff --git a/packages/backend/src/graphql/mutations/create-auth-data.ts b/packages/backend/src/graphql/mutations/create-auth-data.ts
index 6d0972ad..c29444d3 100644
--- a/packages/backend/src/graphql/mutations/create-auth-data.ts
+++ b/packages/backend/src/graphql/mutations/create-auth-data.ts
@@ -27,7 +27,7 @@ const createAuthData = async (
const authInstance = (await import(`../../apps/${connection.key}2/auth`))
.default;
- const app = App.findOneByKey(connection.key);
+ const app = await App.findOneByKey(connection.key);
const $ = globalVariable(connection, app);
await authInstance.createAuthData($);
diff --git a/packages/backend/src/graphql/mutations/create-connection.ts b/packages/backend/src/graphql/mutations/create-connection.ts
index ac84cd74..fcaf093d 100644
--- a/packages/backend/src/graphql/mutations/create-connection.ts
+++ b/packages/backend/src/graphql/mutations/create-connection.ts
@@ -13,7 +13,7 @@ const createConnection = async (
params: Params,
context: Context
) => {
- App.findOneByKey(params.input.key);
+ await App.findOneByKey(params.input.key);
return await context.currentUser.$relatedQuery('connections').insert({
key: params.input.key,
diff --git a/packages/backend/src/graphql/mutations/verify-connection.ts b/packages/backend/src/graphql/mutations/verify-connection.ts
index 358afd3e..8cc02c78 100644
--- a/packages/backend/src/graphql/mutations/verify-connection.ts
+++ b/packages/backend/src/graphql/mutations/verify-connection.ts
@@ -20,7 +20,7 @@ const verifyConnection = async (
})
.throwIfNotFound();
- const app = App.findOneByKey(connection.key);
+ const app = await App.findOneByKey(connection.key);
const authInstance = (await import(`../../apps/${connection.key}2/auth`))
.default;
diff --git a/packages/backend/src/graphql/queries/get-app.ts b/packages/backend/src/graphql/queries/get-app.ts
index 32479096..83f116d9 100644
--- a/packages/backend/src/graphql/queries/get-app.ts
+++ b/packages/backend/src/graphql/queries/get-app.ts
@@ -6,7 +6,7 @@ type Params = {
};
const getApp = async (_parent: unknown, params: Params, context: Context) => {
- const app = App.findOneByKey(params.key);
+ const app = await App.findOneByKey(params.key);
if (context.currentUser) {
const connections = await context.currentUser
diff --git a/packages/backend/src/graphql/queries/get-apps.ts b/packages/backend/src/graphql/queries/get-apps.ts
index 351fc394..d0928b44 100644
--- a/packages/backend/src/graphql/queries/get-apps.ts
+++ b/packages/backend/src/graphql/queries/get-apps.ts
@@ -6,8 +6,8 @@ type Params = {
onlyWithTriggers: boolean;
};
-const getApps = (_parent: unknown, params: Params) => {
- const apps = App.findAll(params.name);
+const getApps = async (_parent: unknown, params: Params) => {
+ const apps = await App.findAll(params.name);
if (params.onlyWithTriggers) {
return apps.filter((app: IApp) => app.triggers?.length);
diff --git a/packages/backend/src/graphql/queries/get-connected-apps.ts b/packages/backend/src/graphql/queries/get-connected-apps.ts
index ecf18847..a988e60c 100644
--- a/packages/backend/src/graphql/queries/get-connected-apps.ts
+++ b/packages/backend/src/graphql/queries/get-connected-apps.ts
@@ -11,7 +11,7 @@ const getConnectedApps = async (
params: Params,
context: Context
) => {
- let apps = App.findAll(params.name);
+ let apps = await App.findAll(params.name);
const connections = await context.currentUser
.$relatedQuery('connections')
diff --git a/packages/backend/src/helpers/app-assets-handler.ts b/packages/backend/src/helpers/app-assets-handler.ts
index dd85808b..bb8d38cf 100644
--- a/packages/backend/src/helpers/app-assets-handler.ts
+++ b/packages/backend/src/helpers/app-assets-handler.ts
@@ -2,10 +2,10 @@ import express, { Application } from 'express';
import App from '../models/app';
const appAssetsHandler = async (app: Application) => {
- const appNames = App.list;
+ const appList = await App.findAll();
- appNames.forEach(appName => {
- const svgPath = `${__dirname}/../apps/${appName}/assets/favicon.svg`;
+ appList.forEach((appItem) => {
+ const svgPath = `${__dirname}/../apps/${appItem.name}/assets/favicon.svg`;
const staticFileHandlerOptions = {
/**
* Disabling fallthrough is important to respond with HTTP 404.
@@ -15,11 +15,8 @@ const appAssetsHandler = async (app: Application) => {
};
const staticFileHandler = express.static(svgPath, staticFileHandlerOptions);
- app.use(
- `/apps/${appName}/assets/favicon.svg`,
- staticFileHandler
- )
- })
-}
+ app.use(`/apps/${appItem.name}/assets/favicon.svg`, staticFileHandler);
+ });
+};
export default appAssetsHandler;
diff --git a/packages/backend/src/helpers/app-info-converter.ts b/packages/backend/src/helpers/app-info-converter.ts
index df736006..a58287d8 100644
--- a/packages/backend/src/helpers/app-info-converter.ts
+++ b/packages/backend/src/helpers/app-info-converter.ts
@@ -1,12 +1,20 @@
import type { IApp } from '@automatisch/types';
import appConfig from '../config/app';
-const appInfoConverter = (rawAppData: string) => {
- let computedRawData = rawAppData.replace('{BASE_URL}', appConfig.baseUrl);
- computedRawData = computedRawData.replace('{WEB_APP_URL}', appConfig.webAppUrl);
+const appInfoConverter = (rawAppData: IApp) => {
+ const stringifiedRawAppData = JSON.stringify(rawAppData);
- const computedJSONData: IApp = JSON.parse(computedRawData)
+ let computedRawData = stringifiedRawAppData.replace(
+ '{BASE_URL}',
+ appConfig.baseUrl
+ );
+ computedRawData = computedRawData.replace(
+ '{WEB_APP_URL}',
+ appConfig.webAppUrl
+ );
+
+ const computedJSONData: IApp = JSON.parse(computedRawData);
return computedJSONData;
-}
+};
export default appInfoConverter;
diff --git a/packages/backend/src/helpers/get-app.ts b/packages/backend/src/helpers/get-app.ts
new file mode 100644
index 00000000..c86ec73f
--- /dev/null
+++ b/packages/backend/src/helpers/get-app.ts
@@ -0,0 +1,67 @@
+import fs from 'fs';
+import { join } from 'path';
+import { IApp } from '@automatisch/types';
+
+const folderPath = join(__dirname, '../apps');
+
+const getApp = async (appKey: string) => {
+ const appData: IApp = (await import(`../apps/${appKey}`)).default;
+
+ let rawAuthData = (await import(`../apps/${appKey}/auth/index.ts`)).default;
+
+ rawAuthData = Object.fromEntries(
+ Object.entries(rawAuthData).filter(
+ ([key]) => typeof rawAuthData[key] !== 'function'
+ )
+ );
+
+ appData.auth = rawAuthData;
+
+ appData.triggers = [];
+
+ const triggersPath = join(folderPath, appKey, 'triggers');
+
+ if (fs.existsSync(triggersPath)) {
+ const triggersFolder = fs.readdirSync(join(folderPath, appKey, 'triggers'));
+
+ for (const triggerName of triggersFolder) {
+ let rawTriggerData = (
+ await import(`../apps/${appKey}/triggers/${triggerName}/index.ts`)
+ ).default;
+
+ rawTriggerData = Object.fromEntries(
+ Object.entries(rawTriggerData).filter(
+ ([key]) => typeof rawTriggerData[key] !== 'function'
+ )
+ );
+
+ appData.triggers.push(rawTriggerData);
+ }
+ }
+
+ appData.actions = [];
+
+ const actionsPath = join(folderPath, appKey, 'actions');
+
+ if (fs.existsSync(actionsPath)) {
+ const actionsFolder = fs.readdirSync(join(folderPath, appKey, 'actions'));
+
+ for await (const actionName of actionsFolder) {
+ let rawActionData = (
+ await import(`../apps/${appKey}/actions/${actionName}/index.ts`)
+ ).default;
+
+ rawActionData = Object.fromEntries(
+ Object.entries(rawActionData).filter(
+ ([key]) => typeof rawActionData[key] !== 'function'
+ )
+ );
+
+ appData.actions.push(rawActionData);
+ }
+ }
+
+ return appData;
+};
+
+export default getApp;
diff --git a/packages/backend/src/models/app.ts b/packages/backend/src/models/app.ts
index 62151633..25ba834c 100644
--- a/packages/backend/src/models/app.ts
+++ b/packages/backend/src/models/app.ts
@@ -2,6 +2,7 @@ import fs from 'fs';
import { join } from 'path';
import { IApp } from '@automatisch/types';
import appInfoConverter from '../helpers/app-info-converter';
+import getApp from '../helpers/get-app';
class App {
static folderPath = join(__dirname, '../apps');
@@ -9,30 +10,29 @@ class App {
// Temporaryly restrict the apps we expose until
// their actions/triggers are implemented!
- static temporaryList = ['slack', 'twitter', 'scheduler'];
+ static temporaryList = ['slack2', 'twitter2'];
+ // static temporaryList = ['slack', 'twitter', 'scheduler'];
- static findAll(name?: string): IApp[] {
+ static async findAll(name?: string): Promise {
if (!name)
- return this.temporaryList.map((name) => this.findOneByName(name));
+ return Promise.all(
+ this.temporaryList.map(async (name) => await this.findOneByName(name))
+ );
- return this.temporaryList
- .filter((app) => app.includes(name.toLowerCase()))
- .map((name) => this.findOneByName(name));
+ return Promise.all(
+ this.temporaryList
+ .filter((app) => app.includes(name.toLowerCase()))
+ .map((name) => this.findOneByName(name))
+ );
}
- static findOneByName(name: string): IApp {
- const rawAppData = fs.readFileSync(
- this.folderPath + `/${name}/info.json`,
- 'utf-8'
- );
+ static async findOneByName(name: string): Promise {
+ const rawAppData = await getApp(name.toLocaleLowerCase());
return appInfoConverter(rawAppData);
}
- static findOneByKey(key: string): IApp {
- const rawAppData = fs.readFileSync(
- this.folderPath + `/${key}/info.json`,
- 'utf-8'
- );
+ static async findOneByKey(key: string): Promise {
+ const rawAppData = await getApp(key);
return appInfoConverter(rawAppData);
}
}
diff --git a/packages/backend/src/models/connection.ts b/packages/backend/src/models/connection.ts
index c4d26513..34968a19 100644
--- a/packages/backend/src/models/connection.ts
+++ b/packages/backend/src/models/connection.ts
@@ -56,10 +56,6 @@ class Connection extends Base {
},
});
- get appData() {
- return App.findOneByKey(this.key);
- }
-
encryptData(): void {
if (!this.eligibleForEncryption()) return;
diff --git a/packages/backend/src/models/step.ts b/packages/backend/src/models/step.ts
index 70662981..7f121bb0 100644
--- a/packages/backend/src/models/step.ts
+++ b/packages/backend/src/models/step.ts
@@ -78,10 +78,6 @@ class Step extends Base {
return `${appConfig.baseUrl}/apps/${this.appKey}/assets/favicon.svg`;
}
- get appData() {
- return App.findOneByKey(this.appKey);
- }
-
async $afterInsert(queryContext: QueryContext) {
await super.$afterInsert(queryContext);
Telemetry.stepCreated(this);
diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts
index af658d4f..38b04952 100644
--- a/packages/types/index.d.ts
+++ b/packages/types/index.d.ts
@@ -158,14 +158,18 @@ export interface IApp {
primaryColor: string;
supportsConnections: boolean;
baseUrl: string;
+ auth: IAuth;
+ connectionCount: number;
+ flowCount: number;
+ triggers: ITrigger[];
+ actions: IAction[];
+ connections: IConnection[];
+}
+
+export interface IAuth {
fields: IField[];
authenticationSteps: IAuthenticationStep[];
reconnectionSteps: IAuthenticationStep[];
- connectionCount: number;
- flowCount: number;
- triggers: any[];
- actions: any[];
- connections: IConnection[];
}
export interface IService {
@@ -176,10 +180,23 @@ export interface IService {
}
export interface ITrigger {
+ name: string;
+ key: string,
+ pollInterval: number;
+ description: string;
+ substeps: ISubstep[];
run(startTime?: Date): Promise;
testRun(startTime?: Date): Promise;
}
+export interface IAction {
+ name: string;
+ key: string,
+ description: string;
+ substeps: ISubstep[];
+ run(startTime?: Date): Promise;
+}
+
export interface IAuthentication {
client: unknown;
verifyCredentials(): Promise;