Skip to content
Snippets Groups Projects
telegram.service.ts 6.09 KiB
Newer Older
Pascal Kosak's avatar
Pascal Kosak committed
import { Injectable } from '@nestjs/common';
import { Message, Metadata } from 'node-telegram-bot-api';
import * as TelegramBot from 'node-telegram-bot-api';
import { PrismaService } from '../prisma/prisma.service';
import * as fs from 'fs';
import * as path from 'path';
import { v4 as uuid } from 'uuid';

@Injectable()
export class TelegramService {
  private telegram: TelegramBot;

  constructor(private prismaService: PrismaService) {
    this.telegram = new TelegramBot(
      process.env.BOT_TOKEN,
      { polling: true },
    );

    this.telegram.onText(/^\/register (.+)/, this.register.bind(this));
    this.telegram.onText(/^\/unregister/, this.unregister.bind(this));
    this.telegram.onText(/^\/state/, this.state.bind(this));
    this.telegram.onText(/^\/start/, this.start.bind(this));
    this.telegram.onText(/^\/commit (.+)/, this.commit.bind(this));
    this.telegram.on('message', this.supplyValue.bind(this));
  }

  private lower = parseInt('aaaaaaa', 36);
  private upper = parseInt('zzzzzzzz', 36);

  private generateSlug() {
    return Math.floor(
      Math.random() * (this.upper - this.lower) + this.lower,
    ).toString(36);
  }

  async start(msg: Message, match: RegExpMatchArray) {
    await this.telegram.sendMessage(
      msg.chat.id,
      `/register [Name]\n/state\n/unregister`,
    );
  }

  async commit(msg: Message, match: RegExpMatchArray) {
    if (!match[1])
      return void (await this.telegram.sendMessage(msg.chat.id, 'No message'));

    if (!(await this.isRegistered(msg.from.id)))
      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Not registered',
      ));

    const agent = await this.prismaService.agent.findFirst({
      where: { uid: String(msg.from.id) },
    });

    await this.prismaService.entry.create({
      data: {
        agentId: agent.id,
        content: match[1],
      },
    });

    await this.telegram.sendMessage(msg.chat.id, 'Commit accepted');
  }

  async register(msg: Message, match: RegExpMatchArray) {
    if (!match[1])
      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Invalid name format',
      ));

    if (await this.isRegistered(msg.from.id))
      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Already registered',
      ));

    const agent = await this.prismaService.agent.create({
      data: {
        uid: String(msg.from.id),
        name: match[1],
        slug: this.generateSlug(),
      },
    });

    await this.telegram.sendMessage(
      msg.chat.id,
      `Id: ${agent.id}\nName: ${agent.name}\nCode: ${agent.slug}`,
    );
  }

  async unregister(msg: Message, match: RegExpMatchArray) {
    if (!(await this.isRegistered(msg.from.id)))
      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Not registered!',
      ));

    const agent = await this.prismaService.agent.findFirst({
      where: { uid: String(msg.from.id) },
    });

    await this.prismaService.entry.deleteMany({
      where: { agentId: agent.id },
    });

    await this.prismaService.unlockEntry.deleteMany({
      where: { agentId: agent.id },
    });

    await this.prismaService.agent.delete({
      where: { id: agent.id },
    });

    await this.telegram.sendMessage(
      msg.chat.id,
      'Successfully deleted all Entries',
    );
  }

  async state(msg: Message, match: RegExpMatchArray) {
    if (!(await this.isRegistered(msg.from.id)))
      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Not registered!',
      ));

    const agent = await this.prismaService.agent.findFirst({
      where: { uid: String(msg.from.id) },
    });

    const entries = await this.prismaService.entry.count({
      where: { agentId: agent.id },
    });

    const unlockEntries = await this.prismaService.unlockEntry.count({
      where: { agentId: agent.id },
    });

    await this.telegram.sendMessage(
      msg.chat.id,
      `Id: ${agent.id}\nName: ${agent.name}\nCode: ${agent.slug}\nPublic Entries: ${entries}\nUnlockable Entries: ${unlockEntries}`,
    );
  }

  async supplyValue(msg: Message, metadata: Metadata) {
    if (metadata.type === 'text' && msg.text.startsWith('/')) return;

    if (!(await this.isRegistered(msg.from.id))) return;

    const agent = await this.prismaService.agent.findFirst({
      where: { uid: String(msg.from.id) },
    });

    if (metadata.type === 'photo') {
      let file,
        size = -1;
      for (const p of msg.photo) {
        if (p.width > size) {
          size = p.width;
          file = p.file_id;
        }
      }

      const id = uuid();

      const dest = fs.createWriteStream(
        path.join(process.cwd(), 'static', 'photos', `${id}.jpg`),
      );
      const pipe = this.telegram.getFileStream(file).pipe(dest);

      await new Promise((resolve) => pipe.on('finish', resolve));

      await this.prismaService.unlockEntry.create({
        data: {
          type: 'picture',
          agentId: agent.id,
          path: `${id}.jpg`,
        },
      });

      return void (await this.telegram.sendMessage(
        msg.chat.id,
        `Accepted Photo`,
      ));
    } else if (metadata.type === 'text') {
      await this.prismaService.unlockEntry.create({
        data: {
          type: 'text',
          agentId: agent.id,
          content: msg.text,
        },
      });

      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Accepted Text',
      ));
    } else if (metadata.type === 'location') {
      await this.prismaService.unlockEntry.create({
        data: {
          agentId: agent.id,
          type: 'location',
          lat: msg.location.latitude.toString(),
          lon: msg.location.longitude.toString(),
        },
      });

      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Accepted Location',
      ));
    } else {
      return void (await this.telegram.sendMessage(
        msg.chat.id,
        'Unsupported DataType',
      ));
    }
  }

  private async isRegistered(uid: string | number): Promise<boolean> {
    return (
      (await this.prismaService.agent.count({
        where: { uid: String(uid) },
      })) > 0
    );
  }
}