mirror of
https://github.com/hempyhemp/hh-auto-reply.git
synced 2026-06-08 18:04:57 +00:00
init
This commit is contained in:
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{js,json,yml}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/.yarn/** linguist-vendored
|
||||||
|
/.yarn/releases/* binary
|
||||||
|
/.yarn/plugins/**/* binary
|
||||||
|
/.pnp.* binary linguist-generated
|
||||||
64
.gitignore
vendored
Normal file
64
.gitignore
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
out/
|
||||||
|
|
||||||
|
# env files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# runtime data
|
||||||
|
pids/
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# coverage
|
||||||
|
coverage/
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# testing
|
||||||
|
.nyc_output/
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
.docker/
|
||||||
|
|
||||||
|
# optional cache
|
||||||
|
.cache/
|
||||||
|
.tmp/
|
||||||
|
|
||||||
|
# parcel / vite / webpack cache (если есть фронт сборка)
|
||||||
|
.parcel-cache/
|
||||||
|
.esbuild/
|
||||||
|
.vite/
|
||||||
|
|
||||||
|
# Prisma (если используешь)
|
||||||
|
prisma/migrations/dev.db*
|
||||||
|
prisma/migrations/*.db
|
||||||
|
|
||||||
|
# sensitive
|
||||||
|
session.json
|
||||||
|
prisma/dev.db
|
||||||
BIN
.yarn/install-state.gz
vendored
Normal file
BIN
.yarn/install-state.gz
vendored
Normal file
Binary file not shown.
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
60
CLAUDE.md
Normal file
60
CLAUDE.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev # run with hot reload (tsx watch)
|
||||||
|
yarn build # compile TypeScript → dist/
|
||||||
|
yarn start # run compiled output
|
||||||
|
yarn lint # ESLint check
|
||||||
|
yarn lint:fix # ESLint auto-fix
|
||||||
|
yarn db-view # open Prisma Studio
|
||||||
|
yarn db-migrate # create and apply a new migration (dev)
|
||||||
|
yarn db-deploy # apply migrations in production
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The bot is a single-process Node.js app (ESM) that starts polling Telegram on launch. There is no HTTP server — all user interaction is via Telegram messages and inline keyboards.
|
||||||
|
|
||||||
|
**Entry point:** `src/index.ts` — registers handlers and starts polling.
|
||||||
|
|
||||||
|
**Singletons:**
|
||||||
|
- `@bot` → `src/bot-singleton.ts` — one `TelegramBot` instance shared across all modules. The Telegram token is hardcoded here (should be moved to `.env`).
|
||||||
|
- `@prisma` → `src/prisma.ts` — one `PrismaClient` instance.
|
||||||
|
|
||||||
|
**Path aliases** (defined in `tsconfig.json`):
|
||||||
|
- `@bot` → `src/bot-singleton`
|
||||||
|
- `@prisma` → `src/prisma`
|
||||||
|
- `@/*` → `src/*`
|
||||||
|
|
||||||
|
**Feature module — `src/hh/`:**
|
||||||
|
- `bot-commands.ts` — registers all HH-related Telegram handlers. Holds an in-memory `state` object (awaitingEmail, awaitingQuery, etc.) that tracks multi-step dialog flow. This state is **not persisted** and resets on restart.
|
||||||
|
- `scraper.ts` — Playwright (headless Chromium) automation against hh.ru. Key functions: `login()` (email + OTP flow, saves cookies to DB), `getResume()` (fetches resume text from hh.ru and upserts into `Resume` table), `applyToJobs()` (searches vacancies, generates AI cover letters per vacancy). **The actual "Apply" button click is commented out** — the function currently only generates letters without submitting.
|
||||||
|
|
||||||
|
**AI — `src/openai.ts`:** Uses Groq API (`llama-3.3-70b-versatile`) via the OpenAI-compatible SDK. The Groq API key is hardcoded (should be moved to `.env`).
|
||||||
|
|
||||||
|
**Menu — `src/bot-menu.ts`:** Renders the persistent reply keyboard and handles top-level button presses (`💼 Меню`, `👤 Debug`).
|
||||||
|
|
||||||
|
## Database (Prisma + SQLite)
|
||||||
|
|
||||||
|
Schema at `prisma/schema.prisma`, DB file at `prisma/dev.db`.
|
||||||
|
|
||||||
|
| Model | Key fields |
|
||||||
|
|---|---|
|
||||||
|
| `User` | `telegramId` (BigInt, PK), `session` (hh.ru cookies as JSON string), `hhEmail`, `prompt` (AI system prompt) |
|
||||||
|
| `Resume` | `id` (hash from hh.ru URL), `data` (plain text), `telegramId` |
|
||||||
|
| `Settings` | `telegramId` (PK), `searchQuery` (default: "Vue"), `maxApplies` (default: 1) |
|
||||||
|
|
||||||
|
`telegramId` is `BigInt` — when looking up by `msg.chat.id` (which is `number`), pass it directly; Prisma handles the coercion. But in `index.ts` it is explicitly cast with `BigInt(chatId)`.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
Required variables in `.env`:
|
||||||
|
```
|
||||||
|
DATABASE_URL="file:./dev.db"
|
||||||
|
```
|
||||||
|
|
||||||
|
`OPENAI_API_KEY` is in `.env` but unused — the active Groq key is hardcoded in `src/openai.ts`.
|
||||||
111
README.md
Normal file
111
README.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# 🤖 HH Auto-Apply Bot
|
||||||
|
|
||||||
|
Telegram-бот для автоматизации откликов на вакансии с hh.ru. Бот логинится на hh.ru от твоего имени, парсит вакансии по запросу и с помощью LLM генерирует персонализированное сопроводительное письмо под каждую вакансию — с учётом твоего резюме и описания позиции.
|
||||||
|
|
||||||
|
## Стек
|
||||||
|
|
||||||
|
| Слой | Технология |
|
||||||
|
|---|---|
|
||||||
|
| Runtime | Node.js 20+ (ESM), TypeScript |
|
||||||
|
| Telegram | node-telegram-bot-api (long polling) |
|
||||||
|
| Браузерная автоматизация | Playwright (headless Chromium) |
|
||||||
|
| LLM / AI | Groq API — `llama-3.3-70b-versatile` |
|
||||||
|
| ORM | Prisma 6 + SQLite |
|
||||||
|
| Планировщик | node-cron |
|
||||||
|
| Пакетный менеджер | Yarn 4 |
|
||||||
|
|
||||||
|
## Как это работает
|
||||||
|
|
||||||
|
```
|
||||||
|
Пользователь → Telegram Bot
|
||||||
|
│
|
||||||
|
├─ Авторизация на hh.ru (Playwright + OTP через бот)
|
||||||
|
│ └─ Сохранение сессии (cookies) в БД
|
||||||
|
│
|
||||||
|
├─ Парсинг резюме с hh.ru → сохранение в БД
|
||||||
|
│
|
||||||
|
└─ Поиск вакансий по запросу
|
||||||
|
│
|
||||||
|
└─ Для каждой вакансии:
|
||||||
|
├─ Playwright парсит описание
|
||||||
|
└─ LLM (Llama 3.3 70B) генерирует
|
||||||
|
сопроводительное письмо на основе
|
||||||
|
резюме + описания вакансии
|
||||||
|
```
|
||||||
|
|
||||||
|
## AI / LLM
|
||||||
|
|
||||||
|
Сопроводительные письма генерирует **Llama 3.3 70B** через Groq API (OpenAI-совместимый интерфейс). Модель получает:
|
||||||
|
|
||||||
|
- Системный промпт с инструкциями по стилю (настраивается пользователем)
|
||||||
|
- Текст резюме пользователя (парсится с hh.ru)
|
||||||
|
- Описание конкретной вакансии (парсится Playwright'ом в реальном времени)
|
||||||
|
|
||||||
|
На выходе — живое, не шаблонное письмо, адаптированное под конкретную позицию.
|
||||||
|
|
||||||
|
В коде также подключён **Anthropic SDK** (`@anthropic-ai/sdk`) для возможности использования Claude.
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- **Авторизация** — вход на hh.ru через email + OTP-код, сессия сохраняется в БД и переиспользуется
|
||||||
|
- **Парсинг резюме** — автоматически скачивает твоё резюме с hh.ru после логина
|
||||||
|
- **Поиск вакансий** — поиск по настраиваемому запросу с лимитом откликов
|
||||||
|
- **AI-сопроводительные письма** — уникальное письмо под каждую вакансию через LLM
|
||||||
|
- **Авто-режим** — крон-задача (пн–пт, 10:00) для ежедневного автозапуска
|
||||||
|
- **Настройки через бот** — поисковый запрос, макс. кол-во откликов — всё меняется прямо в чате
|
||||||
|
|
||||||
|
## Команды бота
|
||||||
|
|
||||||
|
| Кнопка / команда | Действие |
|
||||||
|
|---|---|
|
||||||
|
| `/start` | Регистрация, главное меню |
|
||||||
|
| `💼 Меню` | Панель управления HH Auto-Apply |
|
||||||
|
| `🚀 Откликнуться сейчас` | Запустить поиск и генерацию писем |
|
||||||
|
| `🔍 Изменить запрос` | Сменить поисковый запрос |
|
||||||
|
| `🔢 Макс откликов` | Установить лимит вакансий за сессию |
|
||||||
|
| `⏰ Авто вкл / Авто выкл` | Включить/выключить ежедневный крон |
|
||||||
|
| `🔑 Логин` | Авторизоваться на hh.ru |
|
||||||
|
| `⚙️ Статус` | Текущие настройки и статус авторизации |
|
||||||
|
|
||||||
|
## База данных
|
||||||
|
|
||||||
|
```
|
||||||
|
User — telegramId, hhEmail, session (cookies), AI-промпт
|
||||||
|
Resume — текст резюме, привязан к пользователю
|
||||||
|
Settings — searchQuery, maxApplies
|
||||||
|
```
|
||||||
|
|
||||||
|
## Запуск
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Установить зависимости
|
||||||
|
yarn
|
||||||
|
|
||||||
|
# 2. Создать .env
|
||||||
|
echo 'DATABASE_URL="file:./dev.db"' > .env
|
||||||
|
|
||||||
|
# 3. Применить миграции
|
||||||
|
yarn db-migrate
|
||||||
|
|
||||||
|
# 4. Запустить в dev-режиме
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Команды разработки
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev # запуск с hot reload (tsx watch)
|
||||||
|
yarn build # компиляция TypeScript → dist/
|
||||||
|
yarn start # запуск скомпилированного кода
|
||||||
|
yarn lint # проверка ESLint
|
||||||
|
yarn lint:fix # автофикс ESLint
|
||||||
|
yarn db-view # Prisma Studio (GUI для БД)
|
||||||
|
yarn db-migrate # создать и применить миграцию
|
||||||
|
yarn db-deploy # применить миграции на проде
|
||||||
|
```
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
|
||||||
|
Одиночный Node.js процесс без HTTP-сервера. Весь UI — Telegram inline-клавиатуры и сообщения. Два синглтона (`TelegramBot`, `PrismaClient`) шарятся через path-алиасы `@bot` и `@prisma`.
|
||||||
|
|
||||||
|
Playwright запускается headless для каждой операции с hh.ru и закрывается после — никаких persistent browser-процессов. Для имитации человеческого поведения используются случайные задержки и скроллы.
|
||||||
7
eslint.config.mjs
Normal file
7
eslint.config.mjs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import antfu from '@antfu/eslint-config'
|
||||||
|
|
||||||
|
export default antfu({
|
||||||
|
rules: {
|
||||||
|
'no-console': 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
40
package.json
Normal file
40
package.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "oscr-test-bot",
|
||||||
|
"packageManager": "yarn@4.6.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsx watch src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/index.js",
|
||||||
|
"db-view": "yarn prisma studio",
|
||||||
|
"db-migrate": "npx prisma migrate dev --name init npx prisma generate ",
|
||||||
|
"db-deploy": "npx prisma migrate deploy",
|
||||||
|
"db-migrate-server": "git pull npx prisma migrate deploy",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "eslint . --fix"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "6",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"express": "^4.21.2",
|
||||||
|
"h3": "^1.13.1",
|
||||||
|
"ioredis": "^5.4.2",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
|
"node-telegram-bot-api": "^0.67.0",
|
||||||
|
"openai": "^6.36.0",
|
||||||
|
"playwright": "^1.59.1",
|
||||||
|
"prisma": "6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@antfu/eslint-config": "^3.14.0",
|
||||||
|
"@types/node": "^25.6.0",
|
||||||
|
"@types/node-cron": "^3.0.11",
|
||||||
|
"@types/node-telegram-bot-api": "^0.64.14",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"tsx": "^4.21.0",
|
||||||
|
"typescript": "^6.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
prisma/migrations/20260505105535_init/migration.sql
Normal file
10
prisma/migrations/20260505105535_init/migration.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "User" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"telegramId" BIGINT NOT NULL,
|
||||||
|
"username" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "User_telegramId_key" ON "User"("telegramId");
|
||||||
16
prisma/migrations/20260505114043_add_is_new/migration.sql
Normal file
16
prisma/migrations/20260505114043_add_is_new/migration.sql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_User" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"telegramId" BIGINT NOT NULL,
|
||||||
|
"username" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isNew" BOOLEAN NOT NULL DEFAULT true
|
||||||
|
);
|
||||||
|
INSERT INTO "new_User" ("createdAt", "id", "telegramId", "username") SELECT "createdAt", "id", "telegramId", "username" FROM "User";
|
||||||
|
DROP TABLE "User";
|
||||||
|
ALTER TABLE "new_User" RENAME TO "User";
|
||||||
|
CREATE UNIQUE INDEX "User_telegramId_key" ON "User"("telegramId");
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
2
prisma/migrations/20260505115109_init/migration.sql
Normal file
2
prisma/migrations/20260505115109_init/migration.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "User" ADD COLUMN "firstName" TEXT;
|
||||||
23
prisma/migrations/20260506085123_init/migration.sql
Normal file
23
prisma/migrations/20260506085123_init/migration.sql
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `isNew` on the `User` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_User" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"telegramId" BIGINT NOT NULL,
|
||||||
|
"username" TEXT,
|
||||||
|
"firstName" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"session" TEXT
|
||||||
|
);
|
||||||
|
INSERT INTO "new_User" ("createdAt", "firstName", "id", "telegramId", "username") SELECT "createdAt", "firstName", "id", "telegramId", "username" FROM "User";
|
||||||
|
DROP TABLE "User";
|
||||||
|
ALTER TABLE "new_User" RENAME TO "User";
|
||||||
|
CREATE UNIQUE INDEX "User_telegramId_key" ON "User"("telegramId");
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
2
prisma/migrations/20260506090341_init/migration.sql
Normal file
2
prisma/migrations/20260506090341_init/migration.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "User" ADD COLUMN "hhEmail" TEXT;
|
||||||
7
prisma/migrations/20260506135445_resume/migration.sql
Normal file
7
prisma/migrations/20260506135445_resume/migration.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Resume" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"data" TEXT NOT NULL,
|
||||||
|
"telegramId" BIGINT NOT NULL,
|
||||||
|
CONSTRAINT "Resume_telegramId_fkey" FOREIGN KEY ("telegramId") REFERENCES "User" ("telegramId") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
19
prisma/migrations/20260506143836_prompt/migration.sql
Normal file
19
prisma/migrations/20260506143836_prompt/migration.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_User" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"telegramId" BIGINT NOT NULL,
|
||||||
|
"username" TEXT,
|
||||||
|
"firstName" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"session" TEXT,
|
||||||
|
"hhEmail" TEXT,
|
||||||
|
"prompt" TEXT NOT NULL DEFAULT 'Напиши сопроводительное письмо опираясь на резюме. Пиши грамотно и коротко, простым языком не официально. Пиши только текст самого письма, ты пишешь напрямую в компанию. Мне отвечать или делать ремарки не нужно.'
|
||||||
|
);
|
||||||
|
INSERT INTO "new_User" ("createdAt", "firstName", "hhEmail", "id", "session", "telegramId", "username") SELECT "createdAt", "firstName", "hhEmail", "id", "session", "telegramId", "username" FROM "User";
|
||||||
|
DROP TABLE "User";
|
||||||
|
ALTER TABLE "new_User" RENAME TO "User";
|
||||||
|
CREATE UNIQUE INDEX "User_telegramId_key" ON "User"("telegramId");
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
8
prisma/migrations/20260506144645_settings/migration.sql
Normal file
8
prisma/migrations/20260506144645_settings/migration.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Settings" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"telegramId" BIGINT NOT NULL,
|
||||||
|
"searchQuery" TEXT NOT NULL DEFAULT 'Vue',
|
||||||
|
"maxApplies" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
CONSTRAINT "Settings_telegramId_fkey" FOREIGN KEY ("telegramId") REFERENCES "User" ("telegramId") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
21
prisma/migrations/20260506144929_settingsupd/migration.sql
Normal file
21
prisma/migrations/20260506144929_settingsupd/migration.sql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- The primary key for the `Settings` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||||
|
- You are about to drop the column `id` on the `Settings` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Settings" (
|
||||||
|
"telegramId" BIGINT NOT NULL PRIMARY KEY,
|
||||||
|
"searchQuery" TEXT NOT NULL DEFAULT 'Vue',
|
||||||
|
"maxApplies" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
CONSTRAINT "Settings_telegramId_fkey" FOREIGN KEY ("telegramId") REFERENCES "User" ("telegramId") ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Settings" ("maxApplies", "searchQuery", "telegramId") SELECT "maxApplies", "searchQuery", "telegramId" FROM "Settings";
|
||||||
|
DROP TABLE "Settings";
|
||||||
|
ALTER TABLE "new_Settings" RENAME TO "Settings";
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "sqlite"
|
||||||
42
prisma/schema.prisma
Normal file
42
prisma/schema.prisma
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// This is your Prisma schema file,
|
||||||
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||||
|
|
||||||
|
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
||||||
|
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
||||||
|
|
||||||
|
// output = "../src/generated/prisma"
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
telegramId BigInt @unique
|
||||||
|
username String?
|
||||||
|
firstName String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
session String?
|
||||||
|
hhEmail String?
|
||||||
|
resumes Resume[]
|
||||||
|
prompt String @default("Напиши сопроводительное письмо опираясь на резюме. Пиши грамотно и коротко, простым языком не официально. Пиши только текст самого письма, ты пишешь напрямую в компанию. Мне отвечать или делать ремарки не нужно.")
|
||||||
|
Settings Settings?
|
||||||
|
}
|
||||||
|
|
||||||
|
model Resume {
|
||||||
|
id String @id
|
||||||
|
user User @relation(references: [telegramId], fields: [telegramId], onDelete: Cascade)
|
||||||
|
data String
|
||||||
|
telegramId BigInt
|
||||||
|
}
|
||||||
|
|
||||||
|
model Settings {
|
||||||
|
telegramId BigInt @id
|
||||||
|
user User @relation(references: [telegramId], fields: [telegramId], onDelete: Cascade)
|
||||||
|
searchQuery String @default("Vue")
|
||||||
|
maxApplies Int @default(1)
|
||||||
|
}
|
||||||
60
src/bot-menu.ts
Normal file
60
src/bot-menu.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import bot from '@bot'
|
||||||
|
import { triggerHHStart } from '@/hh/bot-commands'
|
||||||
|
import { getResume } from '@/hh/scraper'
|
||||||
|
|
||||||
|
export function sendMenu(chatId: number, isFirstTime: boolean) {
|
||||||
|
const text = isFirstTime
|
||||||
|
? '👋 Добро пожаловать! Это твой первый вход'
|
||||||
|
: '💼 Главное меню'
|
||||||
|
|
||||||
|
return bot.sendMessage(chatId, text, {
|
||||||
|
reply_markup: {
|
||||||
|
keyboard: [
|
||||||
|
[{ text: '💼 Меню' }],
|
||||||
|
[{ text: '👤 Debug' }],
|
||||||
|
],
|
||||||
|
resize_keyboard: true,
|
||||||
|
one_time_keyboard: false,
|
||||||
|
is_persistent: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.on('message', async (msg) => {
|
||||||
|
const chatId = msg.chat.id
|
||||||
|
const text = msg.text
|
||||||
|
|
||||||
|
if (!text || text.startsWith('/'))
|
||||||
|
return
|
||||||
|
|
||||||
|
switch (text) {
|
||||||
|
case '💼 Меню':
|
||||||
|
|
||||||
|
triggerHHStart(chatId)
|
||||||
|
break
|
||||||
|
|
||||||
|
case '👤 Debug': {
|
||||||
|
const resume = await getResume(chatId)
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, resume || '--')
|
||||||
|
|
||||||
|
// const letter = await askGPT('test')
|
||||||
|
//
|
||||||
|
// const user = await prisma.user.findUnique({
|
||||||
|
// where: { telegramId: BigInt(chatId) },
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// await bot.sendMessage(
|
||||||
|
// chatId,
|
||||||
|
// `👤:\n\n
|
||||||
|
// Username: @${user?.username ?? 'нет'}\n
|
||||||
|
// Письмо: ${letter}\n
|
||||||
|
// HH Email: ${user?.hhEmail}\n
|
||||||
|
// Создан: ${user?.createdAt}
|
||||||
|
//
|
||||||
|
// `,
|
||||||
|
// )
|
||||||
|
// break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
6
src/bot-singleton.ts
Normal file
6
src/bot-singleton.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import TelegramBot from 'node-telegram-bot-api'
|
||||||
|
|
||||||
|
const token = '8150213101:AAFfkqu32aWImOfIaarnQqtWaUj8ZoAwHLE'
|
||||||
|
const bot = new TelegramBot(token, { polling: true })
|
||||||
|
|
||||||
|
export default bot
|
||||||
24
src/generated/prisma/browser.ts
Normal file
24
src/generated/prisma/browser.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* This file should be your main import to use Prisma-related types and utilities in a browser.
|
||||||
|
* Use it to get access to models, enums, and input types.
|
||||||
|
*
|
||||||
|
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
|
||||||
|
* See `client.ts` for the standard, server-side entry point.
|
||||||
|
*
|
||||||
|
* 🟢 You can import this file directly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Prisma from './internal/prismaNamespaceBrowser.js'
|
||||||
|
export { Prisma }
|
||||||
|
export * as $Enums from './enums.js'
|
||||||
|
export * from './enums.js';
|
||||||
|
/**
|
||||||
|
* Model User
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type User = Prisma.UserModel
|
||||||
51
src/generated/prisma/client.ts
Normal file
51
src/generated/prisma/client.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
|
||||||
|
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
|
||||||
|
*
|
||||||
|
* 🟢 You can import this file directly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as process from 'node:process'
|
||||||
|
import * as path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
import * as runtime from "@prisma/client/runtime/library"
|
||||||
|
import * as $Enums from "./enums.js"
|
||||||
|
import * as $Class from "./internal/class.js"
|
||||||
|
import * as Prisma from "./internal/prismaNamespace.js"
|
||||||
|
|
||||||
|
export * as $Enums from './enums.js'
|
||||||
|
export * from "./enums.js"
|
||||||
|
/**
|
||||||
|
* ## Prisma Client
|
||||||
|
*
|
||||||
|
* Type-safe database client for TypeScript
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const prisma = new PrismaClient()
|
||||||
|
* // Fetch zero or more Users
|
||||||
|
* const users = await prisma.user.findMany()
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client).
|
||||||
|
*/
|
||||||
|
export const PrismaClient = $Class.getPrismaClientClass(__dirname)
|
||||||
|
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
||||||
|
export { Prisma }
|
||||||
|
|
||||||
|
|
||||||
|
// file annotations for bundling tools to include these files
|
||||||
|
path.join(__dirname, "query_engine-windows.dll.node")
|
||||||
|
path.join(process.cwd(), "src/generated/prisma/query_engine-windows.dll.node")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model User
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type User = Prisma.UserModel
|
||||||
264
src/generated/prisma/commonInputTypes.ts
Normal file
264
src/generated/prisma/commonInputTypes.ts
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* This file exports various common sort, input & filter types that are not directly linked to a particular model.
|
||||||
|
*
|
||||||
|
* 🟢 You can import this file directly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type * as runtime from "@prisma/client/runtime/library"
|
||||||
|
import * as $Enums from "./enums.js"
|
||||||
|
import type * as Prisma from "./internal/prismaNamespace.js"
|
||||||
|
|
||||||
|
|
||||||
|
export type IntFilter<$PrismaModel = never> = {
|
||||||
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
in?: number[]
|
||||||
|
notIn?: number[]
|
||||||
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BigIntFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
in?: Date[] | string[]
|
||||||
|
notIn?: Date[] | string[]
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SortOrderInput = {
|
||||||
|
sort: Prisma.SortOrder
|
||||||
|
nulls?: Prisma.NullsOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
in?: number[]
|
||||||
|
notIn?: number[]
|
||||||
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BigIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
in?: Date[] | string[]
|
||||||
|
notIn?: Date[] | string[]
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedIntFilter<$PrismaModel = never> = {
|
||||||
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
in?: number[]
|
||||||
|
notIn?: number[]
|
||||||
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedBigIntFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
in?: Date[] | string[]
|
||||||
|
notIn?: Date[] | string[]
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
in?: number[]
|
||||||
|
notIn?: number[]
|
||||||
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedFloatFilter<$PrismaModel = never> = {
|
||||||
|
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
|
in?: number[]
|
||||||
|
notIn?: number[]
|
||||||
|
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
|
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
|
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
|
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedFloatFilter<$PrismaModel> | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedBigIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedIntNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: number[] | null
|
||||||
|
notIn?: number[] | null
|
||||||
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
in?: Date[] | string[]
|
||||||
|
notIn?: Date[] | string[]
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
15
src/generated/prisma/enums.ts
Normal file
15
src/generated/prisma/enums.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* This file exports all enum related types from the schema.
|
||||||
|
*
|
||||||
|
* 🟢 You can import this file directly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This file is empty because there are no enums in the schema.
|
||||||
|
export {}
|
||||||
219
src/generated/prisma/internal/class.ts
Normal file
219
src/generated/prisma/internal/class.ts
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* WARNING: This is an internal file that is subject to change!
|
||||||
|
*
|
||||||
|
* 🛑 Under no circumstances should you import this file directly! 🛑
|
||||||
|
*
|
||||||
|
* Please import the `PrismaClient` class from the `client.ts` file instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as runtime from "@prisma/client/runtime/library"
|
||||||
|
import type * as Prisma from "./prismaNamespace.js"
|
||||||
|
|
||||||
|
|
||||||
|
const config: runtime.GetPrismaClientConfig = {
|
||||||
|
"generator": {
|
||||||
|
"name": "client",
|
||||||
|
"provider": {
|
||||||
|
"fromEnvVar": null,
|
||||||
|
"value": "prisma-client"
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"value": "C:\\MyApps\\oscr-test-bot\\src\\generated\\prisma",
|
||||||
|
"fromEnvVar": null
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"engineType": "library"
|
||||||
|
},
|
||||||
|
"binaryTargets": [
|
||||||
|
{
|
||||||
|
"fromEnvVar": null,
|
||||||
|
"value": "windows",
|
||||||
|
"native": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"previewFeatures": [],
|
||||||
|
"sourceFilePath": "C:\\MyApps\\oscr-test-bot\\prisma\\schema.prisma",
|
||||||
|
"isCustomOutput": true
|
||||||
|
},
|
||||||
|
"relativePath": "../../../prisma",
|
||||||
|
"clientVersion": "6.19.3",
|
||||||
|
"engineVersion": "c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
|
||||||
|
"datasourceNames": [
|
||||||
|
"db"
|
||||||
|
],
|
||||||
|
"activeProvider": "sqlite",
|
||||||
|
"postinstall": false,
|
||||||
|
"inlineDatasources": {
|
||||||
|
"db": {
|
||||||
|
"url": {
|
||||||
|
"fromEnvVar": "DATABASE_URL",
|
||||||
|
"value": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inlineSchema": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../src/generated/prisma\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel User {\n id Int @id @default(autoincrement())\n telegramId BigInt @unique\n username String?\n createdAt DateTime @default(now())\n}\n",
|
||||||
|
"inlineSchemaHash": "76ba25cb1ed558fdbf78bedee3dacf7f0ac06422a484cbb6d02ac988e3c30c43",
|
||||||
|
"copyEngine": true,
|
||||||
|
"runtimeDataModel": {
|
||||||
|
"models": {},
|
||||||
|
"enums": {},
|
||||||
|
"types": {}
|
||||||
|
},
|
||||||
|
"dirname": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"dbName\":null,\"schema\":null,\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":true,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"Int\",\"nativeType\":null,\"default\":{\"name\":\"autoincrement\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"telegramId\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":true,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"BigInt\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"username\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":false,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":false,\"type\":\"String\",\"nativeType\":null,\"isGenerated\":false,\"isUpdatedAt\":false},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"isList\":false,\"isRequired\":true,\"isUnique\":false,\"isId\":false,\"isReadOnly\":false,\"hasDefaultValue\":true,\"type\":\"DateTime\",\"nativeType\":null,\"default\":{\"name\":\"now\",\"args\":[]},\"isGenerated\":false,\"isUpdatedAt\":false}],\"primaryKey\":null,\"uniqueFields\":[],\"uniqueIndexes\":[],\"isGenerated\":false}},\"enums\":{},\"types\":{}}")
|
||||||
|
config.engineWasm = undefined
|
||||||
|
config.compilerWasm = undefined
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type LogOptions<ClientOptions extends Prisma.PrismaClientOptions> =
|
||||||
|
'log' extends keyof ClientOptions ? ClientOptions['log'] extends Array<Prisma.LogLevel | Prisma.LogDefinition> ? Prisma.GetEvents<ClientOptions['log']> : never : never
|
||||||
|
|
||||||
|
export interface PrismaClientConstructor {
|
||||||
|
/**
|
||||||
|
* ## Prisma Client
|
||||||
|
*
|
||||||
|
* Type-safe database client for TypeScript
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const prisma = new PrismaClient()
|
||||||
|
* // Fetch zero or more Users
|
||||||
|
* const users = await prisma.user.findMany()
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client).
|
||||||
|
*/
|
||||||
|
|
||||||
|
new <
|
||||||
|
Options extends Prisma.PrismaClientOptions = Prisma.PrismaClientOptions,
|
||||||
|
LogOpts extends LogOptions<Options> = LogOptions<Options>,
|
||||||
|
OmitOpts extends Prisma.PrismaClientOptions['omit'] = Options extends { omit: infer U } ? U : Prisma.PrismaClientOptions['omit'],
|
||||||
|
ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
|
||||||
|
>(options?: Prisma.Subset<Options, Prisma.PrismaClientOptions> ): PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ## Prisma Client
|
||||||
|
*
|
||||||
|
* Type-safe database client for TypeScript
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const prisma = new PrismaClient()
|
||||||
|
* // Fetch zero or more Users
|
||||||
|
* const users = await prisma.user.findMany()
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client).
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface PrismaClient<
|
||||||
|
in LogOpts extends Prisma.LogLevel = never,
|
||||||
|
in out OmitOpts extends Prisma.PrismaClientOptions['omit'] = Prisma.PrismaClientOptions['omit'],
|
||||||
|
in out ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
|
||||||
|
> {
|
||||||
|
[K: symbol]: { types: Prisma.TypeMap<ExtArgs>['other'] }
|
||||||
|
|
||||||
|
$on<V extends LogOpts>(eventType: V, callback: (event: V extends 'query' ? Prisma.QueryEvent : Prisma.LogEvent) => void): PrismaClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect with the database
|
||||||
|
*/
|
||||||
|
$connect(): runtime.Types.Utils.JsPromise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from the database
|
||||||
|
*/
|
||||||
|
$disconnect(): runtime.Types.Utils.JsPromise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a prepared raw query and returns the number of affected rows.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const result = await prisma.$executeRaw`UPDATE User SET cool = ${true} WHERE email = ${'user@email.com'};`
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/raw-database-access).
|
||||||
|
*/
|
||||||
|
$executeRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<number>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a raw query and returns the number of affected rows.
|
||||||
|
* Susceptible to SQL injections, see documentation.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const result = await prisma.$executeRawUnsafe('UPDATE User SET cool = $1 WHERE email = $2 ;', true, 'user@email.com')
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/raw-database-access).
|
||||||
|
*/
|
||||||
|
$executeRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<number>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a prepared raw query and returns the `SELECT` data.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${1} OR email = ${'user@email.com'};`
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/raw-database-access).
|
||||||
|
*/
|
||||||
|
$queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a raw query and returns the `SELECT` data.
|
||||||
|
* Susceptible to SQL injections, see documentation.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const result = await prisma.$queryRawUnsafe('SELECT * FROM User WHERE id = $1 OR email = $2;', 1, 'user@email.com')
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/raw-database-access).
|
||||||
|
*/
|
||||||
|
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<T>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows the running of a sequence of read/write operations that are guaranteed to either succeed or fail as a whole.
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const [george, bob, alice] = await prisma.$transaction([
|
||||||
|
* prisma.user.create({ data: { name: 'George' } }),
|
||||||
|
* prisma.user.create({ data: { name: 'Bob' } }),
|
||||||
|
* prisma.user.create({ data: { name: 'Alice' } }),
|
||||||
|
* ])
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/concepts/components/prisma-client/transactions).
|
||||||
|
*/
|
||||||
|
$transaction<P extends Prisma.PrismaPromise<any>[]>(arg: [...P], options?: { isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<runtime.Types.Utils.UnwrapTuple<P>>
|
||||||
|
|
||||||
|
$transaction<R>(fn: (prisma: Omit<PrismaClient, runtime.ITXClientDenyList>) => runtime.Types.Utils.JsPromise<R>, options?: { maxWait?: number, timeout?: number, isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<R>
|
||||||
|
|
||||||
|
|
||||||
|
$extends: runtime.Types.Extensions.ExtendsHook<"extends", Prisma.TypeMapCb<OmitOpts>, ExtArgs, runtime.Types.Utils.Call<Prisma.TypeMapCb<OmitOpts>, {
|
||||||
|
extArgs: ExtArgs
|
||||||
|
}>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `prisma.user`: Exposes CRUD operations for the **User** model.
|
||||||
|
* Example usage:
|
||||||
|
* ```ts
|
||||||
|
* // Fetch zero or more Users
|
||||||
|
* const users = await prisma.user.findMany()
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
get user(): Prisma.UserDelegate<ExtArgs, { omit: OmitOpts }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPrismaClientClass(dirname: string): PrismaClientConstructor {
|
||||||
|
config.dirname = dirname
|
||||||
|
return runtime.getPrismaClient(config) as unknown as PrismaClientConstructor
|
||||||
|
}
|
||||||
739
src/generated/prisma/internal/prismaNamespace.ts
Normal file
739
src/generated/prisma/internal/prismaNamespace.ts
Normal file
@@ -0,0 +1,739 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* WARNING: This is an internal file that is subject to change!
|
||||||
|
*
|
||||||
|
* 🛑 Under no circumstances should you import this file directly! 🛑
|
||||||
|
*
|
||||||
|
* All exports from this file are wrapped under a `Prisma` namespace object in the client.ts file.
|
||||||
|
* While this enables partial backward compatibility, it is not part of the stable public API.
|
||||||
|
*
|
||||||
|
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
|
||||||
|
* model files in the `model` directory!
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as runtime from "@prisma/client/runtime/library"
|
||||||
|
import type * as Prisma from "../models.js"
|
||||||
|
import { type PrismaClient } from "./class.js"
|
||||||
|
|
||||||
|
export type * from '../models.js'
|
||||||
|
|
||||||
|
export type DMMF = typeof runtime.DMMF
|
||||||
|
|
||||||
|
export type PrismaPromise<T> = runtime.Types.Public.PrismaPromise<T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prisma Errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError
|
||||||
|
export type PrismaClientKnownRequestError = runtime.PrismaClientKnownRequestError
|
||||||
|
|
||||||
|
export const PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError
|
||||||
|
export type PrismaClientUnknownRequestError = runtime.PrismaClientUnknownRequestError
|
||||||
|
|
||||||
|
export const PrismaClientRustPanicError = runtime.PrismaClientRustPanicError
|
||||||
|
export type PrismaClientRustPanicError = runtime.PrismaClientRustPanicError
|
||||||
|
|
||||||
|
export const PrismaClientInitializationError = runtime.PrismaClientInitializationError
|
||||||
|
export type PrismaClientInitializationError = runtime.PrismaClientInitializationError
|
||||||
|
|
||||||
|
export const PrismaClientValidationError = runtime.PrismaClientValidationError
|
||||||
|
export type PrismaClientValidationError = runtime.PrismaClientValidationError
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-export of sql-template-tag
|
||||||
|
*/
|
||||||
|
export const sql = runtime.sqltag
|
||||||
|
export const empty = runtime.empty
|
||||||
|
export const join = runtime.join
|
||||||
|
export const raw = runtime.raw
|
||||||
|
export const Sql = runtime.Sql
|
||||||
|
export type Sql = runtime.Sql
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decimal.js
|
||||||
|
*/
|
||||||
|
export const Decimal = runtime.Decimal
|
||||||
|
export type Decimal = runtime.Decimal
|
||||||
|
|
||||||
|
export type DecimalJsLike = runtime.DecimalJsLike
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metrics
|
||||||
|
*/
|
||||||
|
export type Metrics = runtime.Metrics
|
||||||
|
export type Metric<T> = runtime.Metric<T>
|
||||||
|
export type MetricHistogram = runtime.MetricHistogram
|
||||||
|
export type MetricHistogramBucket = runtime.MetricHistogramBucket
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extensions
|
||||||
|
*/
|
||||||
|
export type Extension = runtime.Types.Extensions.UserArgs
|
||||||
|
export const getExtensionContext = runtime.Extensions.getExtensionContext
|
||||||
|
export type Args<T, F extends runtime.Operation> = runtime.Types.Public.Args<T, F>
|
||||||
|
export type Payload<T, F extends runtime.Operation = never> = runtime.Types.Public.Payload<T, F>
|
||||||
|
export type Result<T, A, F extends runtime.Operation> = runtime.Types.Public.Result<T, A, F>
|
||||||
|
export type Exact<A, W> = runtime.Types.Public.Exact<A, W>
|
||||||
|
|
||||||
|
export type PrismaVersion = {
|
||||||
|
client: string
|
||||||
|
engine: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prisma Client JS version: 6.19.3
|
||||||
|
* Query Engine version: c2990dca591cba766e3b7ef5d9e8a84796e47ab7
|
||||||
|
*/
|
||||||
|
export const prismaVersion: PrismaVersion = {
|
||||||
|
client: "6.19.3",
|
||||||
|
engine: "c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility Types
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Bytes = runtime.Bytes
|
||||||
|
export type JsonObject = runtime.JsonObject
|
||||||
|
export type JsonArray = runtime.JsonArray
|
||||||
|
export type JsonValue = runtime.JsonValue
|
||||||
|
export type InputJsonObject = runtime.InputJsonObject
|
||||||
|
export type InputJsonArray = runtime.InputJsonArray
|
||||||
|
export type InputJsonValue = runtime.InputJsonValue
|
||||||
|
|
||||||
|
|
||||||
|
export const NullTypes = {
|
||||||
|
DbNull: runtime.objectEnumValues.classes.DbNull as (new (secret: never) => typeof runtime.objectEnumValues.instances.DbNull),
|
||||||
|
JsonNull: runtime.objectEnumValues.classes.JsonNull as (new (secret: never) => typeof runtime.objectEnumValues.instances.JsonNull),
|
||||||
|
AnyNull: runtime.objectEnumValues.classes.AnyNull as (new (secret: never) => typeof runtime.objectEnumValues.instances.AnyNull),
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
|
||||||
|
*
|
||||||
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
|
*/
|
||||||
|
export const DbNull = runtime.objectEnumValues.instances.DbNull
|
||||||
|
/**
|
||||||
|
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
|
||||||
|
*
|
||||||
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
|
*/
|
||||||
|
export const JsonNull = runtime.objectEnumValues.instances.JsonNull
|
||||||
|
/**
|
||||||
|
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
|
||||||
|
*
|
||||||
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
|
*/
|
||||||
|
export const AnyNull = runtime.objectEnumValues.instances.AnyNull
|
||||||
|
|
||||||
|
|
||||||
|
type SelectAndInclude = {
|
||||||
|
select: any
|
||||||
|
include: any
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelectAndOmit = {
|
||||||
|
select: any
|
||||||
|
omit: any
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From T, pick a set of properties whose keys are in the union K
|
||||||
|
*/
|
||||||
|
type Prisma__Pick<T, K extends keyof T> = {
|
||||||
|
[P in K]: T[P];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Enumerable<T> = T | Array<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subset
|
||||||
|
* @desc From `T` pick properties that exist in `U`. Simple version of Intersection
|
||||||
|
*/
|
||||||
|
export type Subset<T, U> = {
|
||||||
|
[key in keyof T]: key extends keyof U ? T[key] : never;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SelectSubset
|
||||||
|
* @desc From `T` pick properties that exist in `U`. Simple version of Intersection.
|
||||||
|
* Additionally, it validates, if both select and include are present. If the case, it errors.
|
||||||
|
*/
|
||||||
|
export type SelectSubset<T, U> = {
|
||||||
|
[key in keyof T]: key extends keyof U ? T[key] : never
|
||||||
|
} &
|
||||||
|
(T extends SelectAndInclude
|
||||||
|
? 'Please either choose `select` or `include`.'
|
||||||
|
: T extends SelectAndOmit
|
||||||
|
? 'Please either choose `select` or `omit`.'
|
||||||
|
: {})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subset + Intersection
|
||||||
|
* @desc From `T` pick properties that exist in `U` and intersect `K`
|
||||||
|
*/
|
||||||
|
export type SubsetIntersection<T, U, K> = {
|
||||||
|
[key in keyof T]: key extends keyof U ? T[key] : never
|
||||||
|
} &
|
||||||
|
K
|
||||||
|
|
||||||
|
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XOR is needed to have a real mutually exclusive union type
|
||||||
|
* https://stackoverflow.com/questions/42123407/does-typescript-support-mutually-exclusive-types
|
||||||
|
*/
|
||||||
|
export type XOR<T, U> =
|
||||||
|
T extends object ?
|
||||||
|
U extends object ?
|
||||||
|
(Without<T, U> & U) | (Without<U, T> & T)
|
||||||
|
: U : T
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is T a Record?
|
||||||
|
*/
|
||||||
|
type IsObject<T extends any> = T extends Array<any>
|
||||||
|
? False
|
||||||
|
: T extends Date
|
||||||
|
? False
|
||||||
|
: T extends Uint8Array
|
||||||
|
? False
|
||||||
|
: T extends BigInt
|
||||||
|
? False
|
||||||
|
: T extends object
|
||||||
|
? True
|
||||||
|
: False
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If it's T[], return T
|
||||||
|
*/
|
||||||
|
export type UnEnumerate<T extends unknown> = T extends Array<infer U> ? U : T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From ts-toolbelt
|
||||||
|
*/
|
||||||
|
|
||||||
|
type __Either<O extends object, K extends Key> = Omit<O, K> &
|
||||||
|
{
|
||||||
|
// Merge all but K
|
||||||
|
[P in K]: Prisma__Pick<O, P & keyof O> // With K possibilities
|
||||||
|
}[K]
|
||||||
|
|
||||||
|
type EitherStrict<O extends object, K extends Key> = Strict<__Either<O, K>>
|
||||||
|
|
||||||
|
type EitherLoose<O extends object, K extends Key> = ComputeRaw<__Either<O, K>>
|
||||||
|
|
||||||
|
type _Either<
|
||||||
|
O extends object,
|
||||||
|
K extends Key,
|
||||||
|
strict extends Boolean
|
||||||
|
> = {
|
||||||
|
1: EitherStrict<O, K>
|
||||||
|
0: EitherLoose<O, K>
|
||||||
|
}[strict]
|
||||||
|
|
||||||
|
export type Either<
|
||||||
|
O extends object,
|
||||||
|
K extends Key,
|
||||||
|
strict extends Boolean = 1
|
||||||
|
> = O extends unknown ? _Either<O, K, strict> : never
|
||||||
|
|
||||||
|
export type Union = any
|
||||||
|
|
||||||
|
export type PatchUndefined<O extends object, O1 extends object> = {
|
||||||
|
[K in keyof O]: O[K] extends undefined ? At<O1, K> : O[K]
|
||||||
|
} & {}
|
||||||
|
|
||||||
|
/** Helper Types for "Merge" **/
|
||||||
|
export type IntersectOf<U extends Union> = (
|
||||||
|
U extends unknown ? (k: U) => void : never
|
||||||
|
) extends (k: infer I) => void
|
||||||
|
? I
|
||||||
|
: never
|
||||||
|
|
||||||
|
export type Overwrite<O extends object, O1 extends object> = {
|
||||||
|
[K in keyof O]: K extends keyof O1 ? O1[K] : O[K];
|
||||||
|
} & {};
|
||||||
|
|
||||||
|
type _Merge<U extends object> = IntersectOf<Overwrite<U, {
|
||||||
|
[K in keyof U]-?: At<U, K>;
|
||||||
|
}>>;
|
||||||
|
|
||||||
|
type Key = string | number | symbol;
|
||||||
|
type AtStrict<O extends object, K extends Key> = O[K & keyof O];
|
||||||
|
type AtLoose<O extends object, K extends Key> = O extends unknown ? AtStrict<O, K> : never;
|
||||||
|
export type At<O extends object, K extends Key, strict extends Boolean = 1> = {
|
||||||
|
1: AtStrict<O, K>;
|
||||||
|
0: AtLoose<O, K>;
|
||||||
|
}[strict];
|
||||||
|
|
||||||
|
export type ComputeRaw<A extends any> = A extends Function ? A : {
|
||||||
|
[K in keyof A]: A[K];
|
||||||
|
} & {};
|
||||||
|
|
||||||
|
export type OptionalFlat<O> = {
|
||||||
|
[K in keyof O]?: O[K];
|
||||||
|
} & {};
|
||||||
|
|
||||||
|
type _Record<K extends keyof any, T> = {
|
||||||
|
[P in K]: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
// cause typescript not to expand types and preserve names
|
||||||
|
type NoExpand<T> = T extends unknown ? T : never;
|
||||||
|
|
||||||
|
// this type assumes the passed object is entirely optional
|
||||||
|
export type AtLeast<O extends object, K extends string> = NoExpand<
|
||||||
|
O extends unknown
|
||||||
|
? | (K extends keyof O ? { [P in K]: O[P] } & O : O)
|
||||||
|
| {[P in keyof O as P extends K ? P : never]-?: O[P]} & O
|
||||||
|
: never>;
|
||||||
|
|
||||||
|
type _Strict<U, _U = U> = U extends unknown ? U & OptionalFlat<_Record<Exclude<Keys<_U>, keyof U>, never>> : never;
|
||||||
|
|
||||||
|
export type Strict<U extends object> = ComputeRaw<_Strict<U>>;
|
||||||
|
/** End Helper Types for "Merge" **/
|
||||||
|
|
||||||
|
export type Merge<U extends object> = ComputeRaw<_Merge<Strict<U>>>;
|
||||||
|
|
||||||
|
export type Boolean = True | False
|
||||||
|
|
||||||
|
export type True = 1
|
||||||
|
|
||||||
|
export type False = 0
|
||||||
|
|
||||||
|
export type Not<B extends Boolean> = {
|
||||||
|
0: 1
|
||||||
|
1: 0
|
||||||
|
}[B]
|
||||||
|
|
||||||
|
export type Extends<A1 extends any, A2 extends any> = [A1] extends [never]
|
||||||
|
? 0 // anything `never` is false
|
||||||
|
: A1 extends A2
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
|
||||||
|
export type Has<U extends Union, U1 extends Union> = Not<
|
||||||
|
Extends<Exclude<U1, U>, U1>
|
||||||
|
>
|
||||||
|
|
||||||
|
export type Or<B1 extends Boolean, B2 extends Boolean> = {
|
||||||
|
0: {
|
||||||
|
0: 0
|
||||||
|
1: 1
|
||||||
|
}
|
||||||
|
1: {
|
||||||
|
0: 1
|
||||||
|
1: 1
|
||||||
|
}
|
||||||
|
}[B1][B2]
|
||||||
|
|
||||||
|
export type Keys<U extends Union> = U extends unknown ? keyof U : never
|
||||||
|
|
||||||
|
export type GetScalarType<T, O> = O extends object ? {
|
||||||
|
[P in keyof T]: P extends keyof O
|
||||||
|
? O[P]
|
||||||
|
: never
|
||||||
|
} : never
|
||||||
|
|
||||||
|
type FieldPaths<
|
||||||
|
T,
|
||||||
|
U = Omit<T, '_avg' | '_sum' | '_count' | '_min' | '_max'>
|
||||||
|
> = IsObject<T> extends True ? U : T
|
||||||
|
|
||||||
|
export type GetHavingFields<T> = {
|
||||||
|
[K in keyof T]: Or<
|
||||||
|
Or<Extends<'OR', K>, Extends<'AND', K>>,
|
||||||
|
Extends<'NOT', K>
|
||||||
|
> extends True
|
||||||
|
? // infer is only needed to not hit TS limit
|
||||||
|
// based on the brilliant idea of Pierre-Antoine Mills
|
||||||
|
// https://github.com/microsoft/TypeScript/issues/30188#issuecomment-478938437
|
||||||
|
T[K] extends infer TK
|
||||||
|
? GetHavingFields<UnEnumerate<TK> extends object ? Merge<UnEnumerate<TK>> : never>
|
||||||
|
: never
|
||||||
|
: {} extends FieldPaths<T[K]>
|
||||||
|
? never
|
||||||
|
: K
|
||||||
|
}[keyof T]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert tuple to union
|
||||||
|
*/
|
||||||
|
type _TupleToUnion<T> = T extends (infer E)[] ? E : never
|
||||||
|
type TupleToUnion<K extends readonly any[]> = _TupleToUnion<K>
|
||||||
|
export type MaybeTupleToUnion<T> = T extends any[] ? TupleToUnion<T> : T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `Pick`, but additionally can also accept an array of keys
|
||||||
|
*/
|
||||||
|
export type PickEnumerable<T, K extends Enumerable<keyof T> | keyof T> = Prisma__Pick<T, MaybeTupleToUnion<K>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude all keys with underscores
|
||||||
|
*/
|
||||||
|
export type ExcludeUnderscoreKeys<T extends string> = T extends `_${string}` ? never : T
|
||||||
|
|
||||||
|
|
||||||
|
export type FieldRef<Model, FieldType> = runtime.FieldRef<Model, FieldType>
|
||||||
|
|
||||||
|
type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRef<Model, FieldType>
|
||||||
|
|
||||||
|
|
||||||
|
export const ModelName = {
|
||||||
|
User: 'User'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export interface TypeMapCb<GlobalOmitOptions = {}> extends runtime.Types.Utils.Fn<{extArgs: runtime.Types.Extensions.InternalArgs }, runtime.Types.Utils.Record<string, any>> {
|
||||||
|
returns: TypeMap<this['params']['extArgs'], GlobalOmitOptions>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> = {
|
||||||
|
globalOmitOptions: {
|
||||||
|
omit: GlobalOmitOptions
|
||||||
|
}
|
||||||
|
meta: {
|
||||||
|
modelProps: "user"
|
||||||
|
txIsolationLevel: TransactionIsolationLevel
|
||||||
|
}
|
||||||
|
model: {
|
||||||
|
User: {
|
||||||
|
payload: Prisma.$UserPayload<ExtArgs>
|
||||||
|
fields: Prisma.UserFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.UserFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.UserFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.UserFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.UserFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.UserFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.UserCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.UserCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.UserCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.UserDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.UserUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.UserDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.UserUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.UserUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.UserUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.UserAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateUser>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.UserGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.UserGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.UserCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.UserCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} & {
|
||||||
|
other: {
|
||||||
|
payload: any
|
||||||
|
operations: {
|
||||||
|
$executeRaw: {
|
||||||
|
args: [query: TemplateStringsArray | Sql, ...values: any[]],
|
||||||
|
result: any
|
||||||
|
}
|
||||||
|
$executeRawUnsafe: {
|
||||||
|
args: [query: string, ...values: any[]],
|
||||||
|
result: any
|
||||||
|
}
|
||||||
|
$queryRaw: {
|
||||||
|
args: [query: TemplateStringsArray | Sql, ...values: any[]],
|
||||||
|
result: any
|
||||||
|
}
|
||||||
|
$queryRawUnsafe: {
|
||||||
|
args: [query: string, ...values: any[]],
|
||||||
|
result: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enums
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const TransactionIsolationLevel = runtime.makeStrictEnum({
|
||||||
|
Serializable: 'Serializable'
|
||||||
|
} as const)
|
||||||
|
|
||||||
|
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||||
|
|
||||||
|
|
||||||
|
export const UserScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
telegramId: 'telegramId',
|
||||||
|
username: 'username',
|
||||||
|
createdAt: 'createdAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const SortOrder = {
|
||||||
|
asc: 'asc',
|
||||||
|
desc: 'desc'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field references
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Int'
|
||||||
|
*/
|
||||||
|
export type IntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Int'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'BigInt'
|
||||||
|
*/
|
||||||
|
export type BigIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BigInt'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'String'
|
||||||
|
*/
|
||||||
|
export type StringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'String'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'DateTime'
|
||||||
|
*/
|
||||||
|
export type DateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'DateTime'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Float'
|
||||||
|
*/
|
||||||
|
export type FloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float'>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch Payload for updateMany & deleteMany & createMany
|
||||||
|
*/
|
||||||
|
export type BatchPayload = {
|
||||||
|
count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type Datasource = {
|
||||||
|
url?: string
|
||||||
|
}
|
||||||
|
export type Datasources = {
|
||||||
|
db?: Datasource
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defineExtension = runtime.Extensions.defineExtension as unknown as runtime.Types.Extensions.ExtendsHook<"define", TypeMapCb, runtime.Types.Extensions.DefaultArgs>
|
||||||
|
export type DefaultPrismaClient = PrismaClient
|
||||||
|
export type ErrorFormat = 'pretty' | 'colorless' | 'minimal'
|
||||||
|
export interface PrismaClientOptions {
|
||||||
|
/**
|
||||||
|
* Overwrites the datasource url from your schema.prisma file
|
||||||
|
*/
|
||||||
|
datasources?: Datasources
|
||||||
|
/**
|
||||||
|
* Overwrites the datasource url from your schema.prisma file
|
||||||
|
*/
|
||||||
|
datasourceUrl?: string
|
||||||
|
/**
|
||||||
|
* @default "colorless"
|
||||||
|
*/
|
||||||
|
errorFormat?: ErrorFormat
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* // Shorthand for `emit: 'stdout'`
|
||||||
|
* log: ['query', 'info', 'warn', 'error']
|
||||||
|
*
|
||||||
|
* // Emit as events only
|
||||||
|
* log: [
|
||||||
|
* { emit: 'event', level: 'query' },
|
||||||
|
* { emit: 'event', level: 'info' },
|
||||||
|
* { emit: 'event', level: 'warn' }
|
||||||
|
* { emit: 'event', level: 'error' }
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* / Emit as events and log to stdout
|
||||||
|
* og: [
|
||||||
|
* { emit: 'stdout', level: 'query' },
|
||||||
|
* { emit: 'stdout', level: 'info' },
|
||||||
|
* { emit: 'stdout', level: 'warn' }
|
||||||
|
* { emit: 'stdout', level: 'error' }
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* Read more in our [docs](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/logging#the-log-option).
|
||||||
|
*/
|
||||||
|
log?: (LogLevel | LogDefinition)[]
|
||||||
|
/**
|
||||||
|
* The default values for transactionOptions
|
||||||
|
* maxWait ?= 2000
|
||||||
|
* timeout ?= 5000
|
||||||
|
*/
|
||||||
|
transactionOptions?: {
|
||||||
|
maxWait?: number
|
||||||
|
timeout?: number
|
||||||
|
isolationLevel?: TransactionIsolationLevel
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Instance of a Driver Adapter, e.g., like one provided by `@prisma/adapter-planetscale`
|
||||||
|
*/
|
||||||
|
adapter?: runtime.SqlDriverAdapterFactory | null
|
||||||
|
/**
|
||||||
|
* Global configuration for omitting model fields by default.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* const prisma = new PrismaClient({
|
||||||
|
* omit: {
|
||||||
|
* user: {
|
||||||
|
* password: true
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
omit?: GlobalOmitConfig
|
||||||
|
}
|
||||||
|
export type GlobalOmitConfig = {
|
||||||
|
user?: Prisma.UserOmit
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Types for Logging */
|
||||||
|
export type LogLevel = 'info' | 'query' | 'warn' | 'error'
|
||||||
|
export type LogDefinition = {
|
||||||
|
level: LogLevel
|
||||||
|
emit: 'stdout' | 'event'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CheckIsLogLevel<T> = T extends LogLevel ? T : never;
|
||||||
|
|
||||||
|
export type GetLogType<T> = CheckIsLogLevel<
|
||||||
|
T extends LogDefinition ? T['level'] : T
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type GetEvents<T extends any[]> = T extends Array<LogLevel | LogDefinition>
|
||||||
|
? GetLogType<T[number]>
|
||||||
|
: never;
|
||||||
|
|
||||||
|
export type QueryEvent = {
|
||||||
|
timestamp: Date
|
||||||
|
query: string
|
||||||
|
params: string
|
||||||
|
duration: number
|
||||||
|
target: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LogEvent = {
|
||||||
|
timestamp: Date
|
||||||
|
message: string
|
||||||
|
target: string
|
||||||
|
}
|
||||||
|
/* End Types for Logging */
|
||||||
|
|
||||||
|
|
||||||
|
export type PrismaAction =
|
||||||
|
| 'findUnique'
|
||||||
|
| 'findUniqueOrThrow'
|
||||||
|
| 'findMany'
|
||||||
|
| 'findFirst'
|
||||||
|
| 'findFirstOrThrow'
|
||||||
|
| 'create'
|
||||||
|
| 'createMany'
|
||||||
|
| 'createManyAndReturn'
|
||||||
|
| 'update'
|
||||||
|
| 'updateMany'
|
||||||
|
| 'updateManyAndReturn'
|
||||||
|
| 'upsert'
|
||||||
|
| 'delete'
|
||||||
|
| 'deleteMany'
|
||||||
|
| 'executeRaw'
|
||||||
|
| 'queryRaw'
|
||||||
|
| 'aggregate'
|
||||||
|
| 'count'
|
||||||
|
| 'runCommandRaw'
|
||||||
|
| 'findRaw'
|
||||||
|
| 'groupBy'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `PrismaClient` proxy available in interactive transactions.
|
||||||
|
*/
|
||||||
|
export type TransactionClient = Omit<DefaultPrismaClient, runtime.ITXClientDenyList>
|
||||||
|
|
||||||
92
src/generated/prisma/internal/prismaNamespaceBrowser.ts
Normal file
92
src/generated/prisma/internal/prismaNamespaceBrowser.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* WARNING: This is an internal file that is subject to change!
|
||||||
|
*
|
||||||
|
* 🛑 Under no circumstances should you import this file directly! 🛑
|
||||||
|
*
|
||||||
|
* All exports from this file are wrapped under a `Prisma` namespace object in the browser.ts file.
|
||||||
|
* While this enables partial backward compatibility, it is not part of the stable public API.
|
||||||
|
*
|
||||||
|
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
|
||||||
|
* model files in the `model` directory!
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as runtime from "@prisma/client/runtime/index-browser"
|
||||||
|
|
||||||
|
export type * from '../models.js'
|
||||||
|
export type * from './prismaNamespace.js'
|
||||||
|
|
||||||
|
export const Decimal = runtime.Decimal
|
||||||
|
|
||||||
|
|
||||||
|
export const NullTypes = {
|
||||||
|
DbNull: runtime.objectEnumValues.classes.DbNull as (new (secret: never) => typeof runtime.objectEnumValues.instances.DbNull),
|
||||||
|
JsonNull: runtime.objectEnumValues.classes.JsonNull as (new (secret: never) => typeof runtime.objectEnumValues.instances.JsonNull),
|
||||||
|
AnyNull: runtime.objectEnumValues.classes.AnyNull as (new (secret: never) => typeof runtime.objectEnumValues.instances.AnyNull),
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
|
||||||
|
*
|
||||||
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
|
*/
|
||||||
|
export const DbNull = runtime.objectEnumValues.instances.DbNull
|
||||||
|
/**
|
||||||
|
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
|
||||||
|
*
|
||||||
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
|
*/
|
||||||
|
export const JsonNull = runtime.objectEnumValues.instances.JsonNull
|
||||||
|
/**
|
||||||
|
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
|
||||||
|
*
|
||||||
|
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||||
|
*/
|
||||||
|
export const AnyNull = runtime.objectEnumValues.instances.AnyNull
|
||||||
|
|
||||||
|
|
||||||
|
export const ModelName = {
|
||||||
|
User: 'User'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enums
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const TransactionIsolationLevel = runtime.makeStrictEnum({
|
||||||
|
Serializable: 'Serializable'
|
||||||
|
} as const)
|
||||||
|
|
||||||
|
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||||
|
|
||||||
|
|
||||||
|
export const UserScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
telegramId: 'telegramId',
|
||||||
|
username: 'username',
|
||||||
|
createdAt: 'createdAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const SortOrder = {
|
||||||
|
asc: 'asc',
|
||||||
|
desc: 'desc'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
12
src/generated/prisma/models.ts
Normal file
12
src/generated/prisma/models.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||||
|
/* eslint-disable */
|
||||||
|
// biome-ignore-all lint: generated file
|
||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
* This is a barrel export file for all models and their related types.
|
||||||
|
*
|
||||||
|
* 🟢 You can import this file directly.
|
||||||
|
*/
|
||||||
|
export type * from './models/User.js'
|
||||||
|
export type * from './commonInputTypes.js'
|
||||||
1185
src/generated/prisma/models/User.ts
Normal file
1185
src/generated/prisma/models/User.ts
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/generated/prisma/query_engine-windows.dll.node
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node
Normal file
Binary file not shown.
212
src/hh/bot-commands.ts
Normal file
212
src/hh/bot-commands.ts
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
import bot from '@bot'
|
||||||
|
import prisma from '@prisma'
|
||||||
|
import cron, { type ScheduledTask } from 'node-cron'
|
||||||
|
import { applyToJobs, checkIsAuth, login } from './scraper.js'
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
autoCron: ScheduledTask | null
|
||||||
|
awaitingEmail: boolean
|
||||||
|
awaitingPassword: boolean
|
||||||
|
awaitingQuery: boolean
|
||||||
|
awaitingMax: boolean
|
||||||
|
awaitingOTP: boolean
|
||||||
|
tempEmail: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function triggerHHStart(chatId: number): void {
|
||||||
|
bot.sendMessage(chatId, '🤖 HH Auto-Apply', {
|
||||||
|
reply_markup: {
|
||||||
|
inline_keyboard: [
|
||||||
|
[
|
||||||
|
{ text: '🚀 Откликнуться сейчас', callback_data: 'hh_apply' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '🔍 Изменить запрос', callback_data: 'hh_query' },
|
||||||
|
{ text: '🔢 Макс откликов', callback_data: 'hh_max' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '⏰ Авто вкл', callback_data: 'hh_auto_start' },
|
||||||
|
{ text: '⛔ Авто выкл', callback_data: 'hh_auto_stop' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ text: '🔑 Логин', callback_data: 'hh_login' },
|
||||||
|
{ text: '⚙️ Статус', callback_data: 'hh_status' },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerHHCommands() {
|
||||||
|
const state: State = {
|
||||||
|
autoCron: null,
|
||||||
|
awaitingEmail: false,
|
||||||
|
awaitingPassword: false,
|
||||||
|
awaitingQuery: false,
|
||||||
|
awaitingMax: false,
|
||||||
|
awaitingOTP: false,
|
||||||
|
tempEmail: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.onText(/\/hhstart/, (msg) => {
|
||||||
|
triggerHHStart(msg.chat.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Инлайн кнопки
|
||||||
|
bot.on('callback_query', async (query) => {
|
||||||
|
if (!query.message)
|
||||||
|
return
|
||||||
|
const chatId = query.message.chat.id
|
||||||
|
|
||||||
|
bot.answerCallbackQuery(query.id)
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
include: { Settings: true },
|
||||||
|
})
|
||||||
|
|
||||||
|
const settings = user!.Settings!
|
||||||
|
|
||||||
|
switch (query.data) {
|
||||||
|
case 'hh_apply':
|
||||||
|
await bot.sendMessage(chatId, `🚀 Ищу: "${settings.searchQuery}"...`)
|
||||||
|
applyToJobs({ query: settings.searchQuery, maxApplies: settings.maxApplies }, {
|
||||||
|
chatId,
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.error)
|
||||||
|
return bot.sendMessage(chatId, `❌ ${result.error}`)
|
||||||
|
const lines = result.applied.map((v, i) => `${i + 1}. ${v}`).join('\n')
|
||||||
|
bot.sendMessage(chatId, `✅ Откликнулся: ${result.applied.length}\n${lines}\n\n`
|
||||||
|
+ `⏭ Пропущено: ${result.skipped.length}\n${
|
||||||
|
result.errors.length ? `❌ Ошибок: ${result.errors.length}` : ''}`)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'hh_status':
|
||||||
|
await bot.sendMessage(chatId, `⚙️ Настройки:\n
|
||||||
|
Запрос: ${settings.searchQuery}\n
|
||||||
|
Макс откликов: ${settings.maxApplies}\n
|
||||||
|
Авто: ${state.autoCron ? '✅ включено' : '❌ выключено'}\n
|
||||||
|
Авторизован: ${await checkIsAuth(chatId)}`)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'hh_login':
|
||||||
|
state.awaitingEmail = true
|
||||||
|
|
||||||
|
if (!user?.hhEmail) {
|
||||||
|
await bot.sendMessage(chatId, '📧 Введи email от hh.ru:')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await bot.sendMessage(chatId, `📧 Email от hh.ru: ${user?.hhEmail}`)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'hh_query':
|
||||||
|
state.awaitingQuery = true
|
||||||
|
await bot.sendMessage(chatId, '🔍 Введи поисковый запрос:')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'hh_max':
|
||||||
|
state.awaitingMax = true
|
||||||
|
await bot.sendMessage(chatId, '🔢 Введи максимальное количество откликов (1-50):')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'hh_auto_start':
|
||||||
|
if (state.autoCron) {
|
||||||
|
await bot.sendMessage(chatId, 'Уже запущено!')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
state.autoCron = cron.schedule('0 10 * * 1-5', async () => {
|
||||||
|
await bot.sendMessage(chatId, '⏰ Авто-отклик...')
|
||||||
|
// const result = await applyToJobs({ query: state.searchQuery, maxApplies: state.maxApplies }, { bot, chatId, msg })
|
||||||
|
// await bot.sendMessage(chatId, `✅ Откликнулся на ${result.applied.length} вакансий`)
|
||||||
|
})
|
||||||
|
await bot.sendMessage(chatId, '✅ Авто включён (пн-пт, 10:00)')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'hh_auto_stop':
|
||||||
|
state.autoCron?.stop()
|
||||||
|
state.autoCron = null
|
||||||
|
await bot.sendMessage(chatId, '⛔ Авто остановлен')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
bot.on('message', async (msg) => {
|
||||||
|
const chatId = msg.chat.id
|
||||||
|
|
||||||
|
if (!msg.text || msg.text.startsWith('/'))
|
||||||
|
return
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
include: { Settings: true },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (state.awaitingEmail) {
|
||||||
|
if (!user?.hhEmail) {
|
||||||
|
state.tempEmail = msg.text
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state.tempEmail = user?.hhEmail
|
||||||
|
}
|
||||||
|
|
||||||
|
state.awaitingEmail = false
|
||||||
|
|
||||||
|
await bot.deleteMessage(chatId, msg.message_id).catch(() => {
|
||||||
|
})
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, '🔄 Логинюсь...')
|
||||||
|
try {
|
||||||
|
await bot.sendMessage(chatId, `${state.tempEmail}, ${msg.text}`)
|
||||||
|
|
||||||
|
await login(state.tempEmail, chatId)
|
||||||
|
await bot.sendMessage(chatId, '✅ Авторизован! Куки сохранены.')
|
||||||
|
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
data: {
|
||||||
|
hhEmail: state.tempEmail,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
triggerHHStart(chatId)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
await bot.sendMessage(chatId, `😬 Ошибка: ${(e as Error).message}`)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.awaitingQuery) {
|
||||||
|
state.awaitingQuery = false
|
||||||
|
|
||||||
|
const updated = await prisma.settings.update({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
data: {
|
||||||
|
searchQuery: msg.text,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `✅ Запрос: "${updated.searchQuery}"`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.awaitingMax) {
|
||||||
|
state.awaitingMax = false
|
||||||
|
const num = Number(msg.text)
|
||||||
|
if (num < 1 || num > 50) {
|
||||||
|
await bot.sendMessage(chatId, '❌ Число от 1 до 50')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.settings.update({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
data: {
|
||||||
|
maxApplies: num,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await bot.sendMessage(chatId, `✅ Макс откликов: ${updated.maxApplies}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
307
src/hh/scraper.ts
Normal file
307
src/hh/scraper.ts
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
import type { Message } from 'node-telegram-bot-api'
|
||||||
|
import bot from '@bot'
|
||||||
|
import prisma from '@prisma'
|
||||||
|
import { type Browser, chromium, type Page } from 'playwright'
|
||||||
|
import { askGPT } from '../openai'
|
||||||
|
|
||||||
|
// const SESSION_FILE = path.resolve('./session.json')
|
||||||
|
|
||||||
|
interface ApplyOptions {
|
||||||
|
query: string
|
||||||
|
area?: number
|
||||||
|
maxApplies?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApplyResult {
|
||||||
|
applied: string[]
|
||||||
|
skipped: string[]
|
||||||
|
errors: string[]
|
||||||
|
error?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomDelay(min = 300, max = 2000) {
|
||||||
|
return min + Math.random() * (max - min)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function humanDelay(min = 300, max = 2000) {
|
||||||
|
return new Promise(r => setTimeout(r, randomDelay(min, max)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function randomScroll(page: Page) {
|
||||||
|
await page.mouse.move(100 + Math.random() * 500, 200 + Math.random() * 500)
|
||||||
|
await page.mouse.wheel(0, 300 + Math.random() * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getBrowser(): Promise<Browser> {
|
||||||
|
return chromium.launch({ headless: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSession(page: Page, telegramId: bigint | number): Promise<boolean> {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { telegramId },
|
||||||
|
})
|
||||||
|
|
||||||
|
const session = user?.session
|
||||||
|
|
||||||
|
if (!session)
|
||||||
|
return false
|
||||||
|
|
||||||
|
const cookies = JSON.parse(session)
|
||||||
|
await page.context().addCookies(cookies)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForOtp(chatId: number): Promise<string> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const handler = (msg: Message) => {
|
||||||
|
if (msg.chat.id !== chatId)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!msg.text)
|
||||||
|
return
|
||||||
|
|
||||||
|
bot.removeListener('message', handler)
|
||||||
|
resolve(msg.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.on('message', handler)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function login(
|
||||||
|
email: string,
|
||||||
|
chatId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
const browser = await getBrowser()
|
||||||
|
await bot.sendMessage(chatId, `Browser is connected: ${browser.isConnected()}`)
|
||||||
|
if (!browser.version())
|
||||||
|
return
|
||||||
|
await bot.sendMessage(chatId, `Browser version: ${browser.version()}`)
|
||||||
|
|
||||||
|
const context = await browser.newContext()
|
||||||
|
const page = await context.newPage()
|
||||||
|
|
||||||
|
await page.goto('https://hh.ru/account/login', { waitUntil: 'networkidle' })
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `page: ${page.url()}`)
|
||||||
|
|
||||||
|
await page.click('[data-qa="submit-button"]')
|
||||||
|
|
||||||
|
await page.waitForTimeout(randomDelay())
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `Клик по "Войти"`)
|
||||||
|
|
||||||
|
await page.click('label:has([data-qa="credential-type-EMAIL"])')
|
||||||
|
|
||||||
|
await page.waitForTimeout(randomDelay())
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `Клик по "Email"`)
|
||||||
|
|
||||||
|
await page.waitForTimeout(randomDelay())
|
||||||
|
|
||||||
|
await page.fill('[data-qa="applicant-login-input-email"]', email)
|
||||||
|
|
||||||
|
await page.waitForTimeout(randomDelay())
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `Ввод "Email"`)
|
||||||
|
|
||||||
|
await page.click('[data-qa="submit-button"]')
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `Клик по "Дальше"`)
|
||||||
|
|
||||||
|
await page.waitForTimeout(randomDelay())
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, '🔑 Введи код из email')
|
||||||
|
|
||||||
|
await page.waitForTimeout(randomDelay())
|
||||||
|
|
||||||
|
await page.click('[data-qa="applicant-login-input-otp"]')
|
||||||
|
|
||||||
|
const otp = await waitForOtp(chatId)
|
||||||
|
|
||||||
|
await page.fill('[data-qa="applicant-login-input-otp"] input', otp)
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `Введён ОТП: ${otp}`)
|
||||||
|
|
||||||
|
await page.waitForTimeout(randomDelay())
|
||||||
|
|
||||||
|
await page.waitForLoadState('networkidle')
|
||||||
|
|
||||||
|
const cookies = await context.cookies()
|
||||||
|
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
data: {
|
||||||
|
session: JSON.stringify(cookies, null, 2),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `cookies: ${cookies.length}`)
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
|
||||||
|
await getResume(chatId)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkIsAuth(telegramId: bigint | number) {
|
||||||
|
const browser = await getBrowser()
|
||||||
|
const context = await browser.newContext()
|
||||||
|
const page = await context.newPage()
|
||||||
|
|
||||||
|
await loadSession(page, telegramId)
|
||||||
|
|
||||||
|
console.log('Сессия загружена')
|
||||||
|
|
||||||
|
const url = `https://hh.ru/search/vacancy`
|
||||||
|
|
||||||
|
await page.goto(url, { waitUntil: 'networkidle' })
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await page.$('[data-qa="profileAndResumes-button"]')
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
// return await page.$('[data-qa="mainmenu_createResume"]')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getResume(chatId: number) {
|
||||||
|
const browser = await getBrowser()
|
||||||
|
const context = await browser.newContext()
|
||||||
|
const page = await context.newPage()
|
||||||
|
await loadSession(page, chatId)
|
||||||
|
const url = `https://hh.ru/applicant/resumes`
|
||||||
|
await page.goto(url, { waitUntil: 'networkidle' })
|
||||||
|
|
||||||
|
const text = await page
|
||||||
|
.$('[data-qa^="resume-card-link-"]')
|
||||||
|
.then(html => html?.getAttribute('href'))
|
||||||
|
|
||||||
|
const hhUrl = new URL(`https://hh.ru${text}`)
|
||||||
|
|
||||||
|
const id = hhUrl.pathname.split('/').pop()!
|
||||||
|
|
||||||
|
const resumeUrl = `https://hh.ru/resume_converter/resume.txt?hash=${id}&type=txt&hhtmFrom=&hhtmSource=resume`
|
||||||
|
|
||||||
|
await page.goto(resumeUrl, { waitUntil: 'networkidle' })
|
||||||
|
|
||||||
|
let resume
|
||||||
|
try {
|
||||||
|
resume = await page.locator('.resume').innerText()
|
||||||
|
|
||||||
|
await prisma.resume.upsert({
|
||||||
|
where: { id },
|
||||||
|
create: { data: resume, id, telegramId: chatId },
|
||||||
|
update: { data: resume },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
await bot.sendMessage(chatId, 'Нет резюме на ХХ, создайте')
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
await browser.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return resume
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function applyToJobs({
|
||||||
|
query,
|
||||||
|
area = 1,
|
||||||
|
maxApplies = 10,
|
||||||
|
}: ApplyOptions, { chatId }: {
|
||||||
|
chatId: number
|
||||||
|
}): Promise<ApplyResult> {
|
||||||
|
const browser = await getBrowser()
|
||||||
|
const context = await browser.newContext()
|
||||||
|
const page = await context.newPage()
|
||||||
|
const results: ApplyResult = { applied: [], skipped: [], errors: [] }
|
||||||
|
|
||||||
|
try {
|
||||||
|
await loadSession(page, chatId)
|
||||||
|
|
||||||
|
const url = `https://hh.ru/search/vacancy?text=${encodeURIComponent(query)}&area=${area}`
|
||||||
|
await page.goto(url, { waitUntil: 'networkidle' })
|
||||||
|
|
||||||
|
const isLoggedIn = await page.$('[data-qa="profileAndResumes-button"]')
|
||||||
|
// await page.$('[data-qa="mainmenu_myResumes"]')
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return { ...results, error: 'Не авторизован. Выполните login' }
|
||||||
|
}
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `✅ Авторизация выполнена`)
|
||||||
|
|
||||||
|
const vacancies = await page.$$eval(
|
||||||
|
'[data-qa="serp-item__title"]',
|
||||||
|
links => links.map(a => ({
|
||||||
|
href: (a as HTMLAnchorElement).href,
|
||||||
|
title: a.textContent?.trim() ?? '',
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `✅ Вакансий найдено: ${vacancies.length}`)
|
||||||
|
|
||||||
|
for (const vacancy of vacancies.slice(0, maxApplies)) {
|
||||||
|
try {
|
||||||
|
await bot.sendMessage(chatId, `🔄 Обрабатывается вакансия: ${vacancy.title}`)
|
||||||
|
await page.goto(vacancy.href, { waitUntil: 'networkidle' })
|
||||||
|
|
||||||
|
const description = await page
|
||||||
|
.locator('[data-qa="vacancy-description"]')
|
||||||
|
.innerText()
|
||||||
|
|
||||||
|
if (!description) {
|
||||||
|
await bot.sendMessage(chatId, `😬 Ошибка с получением описания`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `✅ Описание получено`)
|
||||||
|
|
||||||
|
const resume = await prisma.resume.findFirst({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!resume?.data) {
|
||||||
|
await getResume(chatId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { telegramId: chatId },
|
||||||
|
})
|
||||||
|
|
||||||
|
const letter = await askGPT(resume!.data, description, user!.prompt)
|
||||||
|
|
||||||
|
await bot.sendMessage(chatId, `✅ Сопроводительное письмо отправлено: ${letter}`)
|
||||||
|
|
||||||
|
// const applyBtn = await page.$('[data-qa="vacancy-response-link-top"]')
|
||||||
|
// if (!applyBtn) {
|
||||||
|
// results.skipped.push(vacancy.title)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// await randomScroll(page)
|
||||||
|
//
|
||||||
|
// await applyBtn.click()
|
||||||
|
// await page.waitForTimeout(randomDelay())
|
||||||
|
//
|
||||||
|
// const submitBtn = await page.$('[data-qa="vacancy-response-popup-submit"]')
|
||||||
|
// if (submitBtn) {
|
||||||
|
// await submitBtn.click()
|
||||||
|
// await page.waitForTimeout(randomDelay())
|
||||||
|
// }
|
||||||
|
|
||||||
|
results.applied.push(vacancy.title)
|
||||||
|
// await page.waitForTimeout(3000 + Math.random() * 2000)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
results.errors.push(`${vacancy.title}: ${(err as Error).message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
await browser.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
35
src/index.ts
Normal file
35
src/index.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import bot from '@bot'
|
||||||
|
import prisma from '@prisma'
|
||||||
|
|
||||||
|
import { sendMenu } from './bot-menu'
|
||||||
|
import { registerHHCommands } from './hh/bot-commands.js'
|
||||||
|
|
||||||
|
registerHHCommands()
|
||||||
|
|
||||||
|
bot.onText(/\/start/, async (msg) => {
|
||||||
|
const chatId = msg.chat.id
|
||||||
|
const telegramId = BigInt(chatId)
|
||||||
|
|
||||||
|
const existingUser = await prisma.user.findUnique({
|
||||||
|
where: { telegramId },
|
||||||
|
})
|
||||||
|
|
||||||
|
const isFirstTime = !existingUser
|
||||||
|
|
||||||
|
await prisma.user.upsert({
|
||||||
|
where: { telegramId },
|
||||||
|
update: {
|
||||||
|
username: msg.from?.username ?? null,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
telegramId,
|
||||||
|
username: msg.from?.username ?? null,
|
||||||
|
firstName: msg.from?.first_name ?? null,
|
||||||
|
Settings: { create: {} },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await sendMenu(chatId, isFirstTime)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Bot started 🚀')
|
||||||
26
src/openai.ts
Normal file
26
src/openai.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import Anthropic from '@anthropic-ai/sdk'
|
||||||
|
import OpenAI from 'openai'
|
||||||
|
|
||||||
|
|
||||||
|
export const claude = new Anthropic({
|
||||||
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const groq = new OpenAI({
|
||||||
|
apiKey: process.env.GROQ_API_KEY,
|
||||||
|
baseURL: 'https://api.groq.com/openai/v1',
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function askGPT(resume: string, message: string, prompt: string) {
|
||||||
|
// return 'test'
|
||||||
|
|
||||||
|
const res = await groq.chat.completions.create({
|
||||||
|
model: 'llama-3.3-70b-versatile',
|
||||||
|
messages: [
|
||||||
|
{ role: 'system', content: `${prompt} ${resume}` },
|
||||||
|
{ role: 'user', content: message },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.choices[0].message.content
|
||||||
|
}
|
||||||
5
src/prisma.ts
Normal file
5
src/prisma.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
export default prisma
|
||||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"],
|
||||||
|
"@bot": ["src/bot-singleton"],
|
||||||
|
"@prisma": ["src/prisma"]
|
||||||
|
},
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user