summaryrefslogtreecommitdiffstats
path: root/src/index.ts
blob: 0b13af90b81f5514907c85edb2872ed6375426af (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { addTask, getTask, numOfTasksInQueue } from "./tasks";
import { runDownloadInstagramTask } from "./instagram_task";
import { runWithRetries, wait } from "./utils";
import { AppOptions, App as SlackApp } from "@slack/bolt";
import fs from "fs";
import dotenv from "dotenv";
import { runDownloadTiktokTask } from "./tiktok_task";
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 url = command.text.trim();

  const respondWithFile = async (filePath: string) => {
    try {
      console.log("Uploading file", filePath);
      const fileContent = fs.readFileSync(filePath);
      const extension = filePath.split(".").pop();
      const fileName = `veeti.${extension}`;
      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}`,
        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",
    });
  };

  if (url.includes("instagram.com")) {
    void respond({
      text: "Sharing instagram video, this can take a minute...",
      thread_ts: command.ts,
      response_type: "ephemeral",
    });

    addTask({
      type: "download_instagram",
      url,
      respondWithFile,
      respondTaskFailed,
    });
  } else if (url.includes("tiktok.com")) {
    void respond({
      text: "Sharing tiktok video, this can take a minute...",
      thread_ts: command.ts,
      response_type: "ephemeral",
    });

    addTask({
      type: "download_tiktok",
      url,
      respondWithFile,
      respondTaskFailed,
    });
  } else {
    void respond({
      text: "Invalid URL, please provide an Instagram or TikTok video URL.",
      response_type: "ephemeral",
    });
  }
});

const taskLoop = async () => {
  const MAX_RETRIES = 5;
  // Main loop
  while (true) {
    const task = getTask();
    if (task) {
      try {
        await runWithRetries(async () => {
          if (task.type === "download_instagram") {
            await runDownloadInstagramTask(task);
          } else if (task.type === "download_tiktok") {
            await runDownloadTiktokTask(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();