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('message', this.supplyValue.bind(this));
}
async start(msg: Message) {
await this.telegram.sendMessage(
msg.chat.id,
'/register <agentId> <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.',
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 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,
'Usage: /entry <private|public|clear>',
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: { id: 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({
where: {
id: match[1],
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 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;
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
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',
);
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',
});