summaryrefslogtreecommitdiffstats
path: root/src/index.ts
blob: 0615cd72908c36b96a507f6e94941d7885f891a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import {
  addTask,
  getTask,
  numOfTasksInQueue,
  runDownloadUrlTask,
} from "./tasks";
import { runWithRetries, wait } from "./utils";
import { AppOptions, App as SlackApp } from "@slack/bolt";
import fs from "fs";
import dotenv from "dotenv";
dotenv.config();

const QUEUE_MAX_LENGTH = process.env.QUEUE_MAX_LENGTH
  ? Number(process.env.QUEUE_MAX_LENGTH)
  : 3;

const slackAppConfig: AppOptions = {
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  socketMode: true,
  appToken: process.env.SLACK_APP_TOKEN,
  clientId: process.env.SLACK_CLIENT_ID,
  clientSecret: process.env.SLACK_CLIENT_SECRET,
};

const app = new SlackApp(slackAppConfig);

app.command("/veeti", async ({ command, ack, respond, client }) => {
  await ack();

  if (numOfTasksInQueue() >= QUEUE_MAX_LENGTH) {
    void respond({
      text: "Too many tasks in queue, please try again later.",
      response_type: "ephemeral",
    });
    return;
  }

  console.log("Received command: /veeti", command.text);
  const cmdText = command.text.trim();
  const cmdParts = cmdText.split(" ");
  const url = cmdParts[0];
  const msg =
    cmdParts.length > 1 ? `\nViesti: ${cmdParts.slice(1).join(" ")}` : "";

  const respondWithFile = async (fileName: string) => {
    try {
      console.log("Uploading file", fileName);
      const fileContent = fs.readFileSync(`downloads/${fileName}`);
      console.log("Sending message to channel", command.channel_id);
      await client.filesUploadV2({
        channel_id: command.channel_id,
        initial_comment: `Lähettäjä: @${command.user_name}${msg}`,
        file: fileContent,
        filename: fileName,
      });
      console.log("Success");
    } catch (e) {
      console.error("Failed to upload file", e);
      void respond({
        text:
          "Failed to share file, please try again later. Error: " + String(e),
        response_type: "ephemeral",
      });
    }
  };

  const respondTaskFailed = async (err: unknown) => {
    console.error("Task failed", err);
    void respond({
      text:
        "Failed to download video, please try again later. Error: " +
        String(err),
      response_type: "ephemeral",
    });
  };

  void respond({
    text: "Veeti link added to task queue. Wait a moment...",
    response_type: "ephemeral",
  });

  addTask({
    type: "download_url",
    url,
    respondWithFile,
    respondTaskFailed,
  });
});

const taskLoop = async () => {
  const MAX_RETRIES = 5;
  // Main loop
  while (true) {
    const task = getTask();
    if (task) {
      try {
        await runWithRetries(async () => {
          if (task.type === "download_url") {
            await runDownloadUrlTask(task);
          } else {
            console.log("Unknown task type, skipping", task);
          }
        }, MAX_RETRIES);
      } catch (e) {
        void task.respondTaskFailed(e);
        console.error("Task failed after max retries, skipping task.", task, e);
      }
    } else {
      await wait(1000);
    }
  }
};

// run Puppeteer tasks in background
void taskLoop();

// start listening for Slack events
app.start();