Newer
Older
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';
import { Entry } from '@prisma/client';
import { pubSub } from '../pubSub.instance';
private messageCache = new Map<number, Partial<Entry>>();
constructor(private prismaService: PrismaService) {
this.telegram = new TelegramBot(process.env.BOT_TOKEN, {
polling: true,
});
this.telegram.onText(
/^\/register ([\w|-]+) (.+)/,
this.register.bind(this),
);
this.telegram.onText(/^\/unregister/, this.unregister.bind(this));
this.telegram.onText(/^\/profile/, this.profile.bind(this));
this.telegram.onText(/^\/entry( (\w+))?/, this.entry.bind(this));
this.telegram.on('edited_message_text', this.editMessage.bind(this));
async start(msg: Message) {
await this.telegram.sendMessage(
msg.chat.id,
'/register <password> <name>\n - Registers you as a new Agent. The agentId will be assigned to you beforehand, while the name is what will be displayed to other users.\n' +
'/unregister\n - Deletes all your Entries and Profile.\n\n' +
'/entry <public|private>\n - Create new Public/Private Entry from your previous message cache.\n' +
'/entry <clear>\n - Clears your current message cache.\n' +
'/profile\n - Shows your current profile.',
function generateWord() {
return seedWords[Math.round(Math.random() * seedWords.length)];
}
return [
generateWord(),
generateWord(),
generateWord(),
generateWord(),
].join('-');
async entry(msg: Message, match: RegExpMatchArray) {
case 'private':
return await this.commitMessage(msg.from.id, true, msg);
case 'public':
return await this.commitMessage(msg.from.id, false, msg);
case 'clear':
this.messageCache.delete(msg.from.id);
));
default:
return this.sendError(
msg,
async commitMessage(id: number, isPrivate: boolean, msg: Message) {
if (!(await this.isRegistered(msg.from.id)))
return await this.sendError(msg, 'Not registered');
if (!this.messageCache.has(id))
return await this.sendError(msg, 'No queued Messages');
let entry: Partial<Entry> = this.messageCache.get(msg.from.id);
entry.private = isPrivate;
this.messageCache.delete(msg.from.id);
entry = await this.prismaService.entry.create({
data: entry as any,
await this.sendSuccess(
msg,
`Created new ${isPrivate ? 'private' : 'public'} Entry`,
await pubSub.publish('newEntry', {
newEntry: {
...entry,
locked: isPrivate,
},
});
await pubSub.publish('newEntry', {
newEntry: {
id: entry.id,
agentId: entry.agentId,
private: true,
createdAt: entry.createdAt,
locked: true,
},
});
}
}
async register(msg: Message, match: RegExpMatchArray) {
if (!match[1] && !match[2]) return;
return await this.sendError(msg, 'Already registered');
const existsCheck = await this.prismaService.agent.count({
where: { password: match[1], uid: null },
});
if (existsCheck == 0)
return await this.sendError(
msg,
'agentId not found or already taken',
);
const agent = await this.prismaService.agent.update({
tokenCode: {
create: { value: 1 },
},
`Successfully registered\\!\n` +
`\\- Id: \`${agent.id}\`\n` +
`\\- Name: \`${agent.name}\`\n` +
`\\- Code: \`${agent.slug}\``,
return await this.sendError(msg, '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.entry.deleteMany({
await this.prismaService.tokenCode.delete({
where: { id: agent.tokenCodeId },
});
await this.prismaService.agent.delete({
where: { id: agent.id },
});
await this.sendSuccess(msg, 'Deleted all Entries');
return await this.sendError(msg, 'Not registered');
const agent = await this.prismaService.agent.findFirst({
where: { uid: String(msg.from.id) },
});
const publicEntries = await this.prismaService.entry.count({
where: { agentId: agent.id, private: false },
const privateEntries = await this.prismaService.entry.count({
where: { agentId: agent.id, private: true },
});
await this.telegram.sendMessage(
msg.chat.id,
`\\- Id: \`${agent.id}\`\n` +
`\\- Name: \`${agent.name}\`\n` +
`\\- Code: \`${agent.slug}\`\n` +
`\\- Public Entries: \`${publicEntries}\`\n` +
`\\- Private Entries: \`${privateEntries}\``,
{ parse_mode: 'MarkdownV2' },
async receiveImage(msg: Message): Promise<Partial<Entry>> {
let 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(), 'photos', `${id}.jpg`),
);
const pipe = this.telegram.getFileStream(file).pipe(dest);
await new Promise((resolve) => pipe.on('finish', resolve));
return {
image: `${id}.jpg`,
};
}
async editMessage(msg: Message) {
this.telegram.sendMessage(
msg.chat.id,
'❌ Message Edits are not supported. Please send new values as new messages.',
{
reply_to_message_id: msg.message_id,
},
);
}
async supplyValue(msg: Message, metadata: Metadata) {
if (metadata.type === 'text' && msg.text.startsWith('/')) return;
if (!(await this.isRegistered(msg.from.id))) return;
if (metadata.type === 'document')
return await this.sendError(
msg,
"Unsupported DataType\\. Please send pictures as a 'Photo', not as a 'File'\\.",
);
const agent = await this.prismaService.agent.findFirst({
where: { uid: String(msg.from.id) },
});
let entry: Partial<Entry> = {};
entry = this.messageCache.get(msg.from.id);
} else {
await this.telegram.sendMessage(
msg.chat.id,
'📌 Message queue created\n' +
'<pre>/entry private</pre> to submit as private element\n' +
'<pre>/entry public</pre> to submit as public element\n' +
'<pre>/entry clear</pre> to reset the queue',
{ parse_mode: 'HTML' },
);
}
entry.agentId = agent.id;
switch (metadata.type) {
case 'photo':
const imageEntry = await this.receiveImage(msg);
if (entry.image)
await this.telegram.sendMessage(
msg.chat.id,
'✂ Overwriting previous image',
);
entry.image = imageEntry.image;
await this.telegram.sendMessage(msg.chat.id, '➡ Queued Image');
if (!msg.caption) {
// Allow captions to be handled like text messages
break;
}
case 'text':
if (entry.content)
await this.telegram.sendMessage(
msg.chat.id,
'✂ Overwriting previous text',
);
if (msg.entities)
await this.telegram.sendMessage(
msg.chat.id,
'⚠️ Note that Message Entities such as formatting are not supported and text will show up without formatting',
);
entry.content = msg.text || msg.caption;
await this.telegram.sendMessage(msg.chat.id, '➡ Queued Text');
break;
case 'location':
if (entry.lat)
await this.telegram.sendMessage(
msg.chat.id,
'✂ Overwriting previous location',
);
entry.lat = msg.location.latitude.toString();
entry.lon = msg.location.longitude.toString();
await this.telegram.sendMessage(
msg.chat.id,
default:
return await this.sendError(msg, 'Unsupported DataType');
this.messageCache.set(msg.from.id, entry);
private async isRegistered(uid: string | number): Promise<boolean> {
return (
(await this.prismaService.agent.count({
where: { uid: String(uid) },
})) > 0
);
}
private async sendError(msg: Message, message: string) {
await this.telegram.sendMessage(msg.chat.id, `❌ ${message}`, {
parse_mode: 'MarkdownV2',
});
}
private async sendSuccess(msg: Message, message: string) {
await this.telegram.sendMessage(msg.chat.id, `✅ ${message}`, {
parse_mode: 'MarkdownV2',
});