feat(bot): add session management and user tracking

- Implement Prisma storage adapter for Telegram session management
- Add middleware to upsert Telegram user data
- Extend TelegramUser model with additional fields
- Add db:push command to turbo.json and package.json
- Extend Prisma client with exists helper method
This commit is contained in:
Yuu Ottosoka
2025-07-09 20:36:07 +03:00
parent 84760349c0
commit 49f0385d00
8 changed files with 115 additions and 15 deletions

View File

@@ -1,12 +1,22 @@
import { serve } from '@hono/node-server';
import { config } from './config';
import { Bot, webhookCallback } from 'grammy';
import { Bot, session, webhookCallback } from 'grammy';
import { Hono } from 'hono';
import { logger } from '@repo/logger';
import { PrismaStorageAdapter } from './session-storage';
import { BotContext } from './types';
import { upsertUser } from './middlewares/upsert-user';
const bot = new Bot(config.BOT_TOKEN);
const bot = new Bot<BotContext>(config.BOT_TOKEN);
const storage = new PrismaStorageAdapter();
bot.command('start', (ctx) => ctx.reply('Привет, мир'));
bot.use(session({ storage, initial: () => ({}) }));
bot.use(upsertUser);
bot.command('start', async (ctx) => {
const session = await ctx.session;
await ctx.reply('Привет, ' + JSON.stringify(session));
});
if (config.MODE === 'webhook') {
logger.info('Starting in webhook mode...');

View File

@@ -0,0 +1,30 @@
import { MiddlewareFn } from 'grammy';
import { BotContext } from '../types';
import { Prisma, prisma } from '@repo/db';
export const upsertUser: MiddlewareFn<BotContext> = async (ctx, next) => {
const telegramId = ctx.from?.id.toString();
if (!telegramId) {
return next();
}
const payload: Prisma.TelegramUserUncheckedCreateInput = {
id: telegramId,
username: ctx.from?.username,
firstName: ctx.from?.first_name,
lastName: ctx.from?.last_name,
languageCode: ctx.from?.language_code,
isPremium: ctx.from?.is_premium,
};
await prisma.telegramUser.upsert({
where: {
id: telegramId,
},
create: payload,
update: payload,
});
return next();
};

View File

@@ -0,0 +1,37 @@
import { prisma } from '@repo/db';
import { StorageAdapter } from 'grammy';
export class PrismaStorageAdapter<T extends object>
implements StorageAdapter<T>
{
async read(key: string): Promise<T | undefined> {
return prisma.telegramSession
.findUnique({
where: {
id: key,
},
})
.then((session) => (session?.data as T) || undefined);
}
async write(key: string, data: T): Promise<void> {
console.log('write', data);
await prisma.telegramSession.upsert({
where: {
id: key,
},
create: {
id: key,
data,
},
update: {
data,
},
});
}
async delete(key: string): Promise<void> {
await prisma.telegramSession.delete({ where: { id: key } });
}
async has(key: string): Promise<boolean> {
return prisma.telegramSession.exists({ id: key });
}
}

5
apps/bot/src/types.ts Normal file
View File

@@ -0,0 +1,5 @@
import { Context, LazySessionFlavor } from 'grammy';
// config your session data
export type SessionData = { };
export type BotContext = Context & LazySessionFlavor<SessionData>;