create-verse-bot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const prompts_1 = require("@inquirer/prompts");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const node_child_process_1 = require("node:child_process");
11
+ async function main() {
12
+ const projectName = await (0, prompts_1.input)({
13
+ message: 'Project name:',
14
+ default: 'bot',
15
+ });
16
+ if (!projectName) {
17
+ console.log('Aborted.');
18
+ process.exit(1);
19
+ }
20
+ const platforms = await (0, prompts_1.checkbox)({
21
+ message: 'Select platforms:',
22
+ choices: [
23
+ { name: 'Telegram', value: 'telegram' },
24
+ { name: 'VK', value: 'vk' },
25
+ ],
26
+ required: true,
27
+ });
28
+ if (platforms.length === 0) {
29
+ console.log('No platforms selected. Exiting.');
30
+ process.exit(1);
31
+ }
32
+ const useLocal = process.argv.includes('--local');
33
+ const targetDir = node_path_1.default.resolve(process.cwd(), projectName);
34
+ if (fs_extra_1.default.existsSync(targetDir)) {
35
+ console.error(`Directory "${projectName}" already exists.`);
36
+ process.exit(1);
37
+ }
38
+ // Копируем шаблон
39
+ const templateDir = node_path_1.default.resolve(__dirname, '../template');
40
+ fs_extra_1.default.copySync(templateDir, targetDir);
41
+ // Обновляем package.json для зависимостей
42
+ const pkgPath = node_path_1.default.join(targetDir, 'package.json');
43
+ const pkg = fs_extra_1.default.readJsonSync(pkgPath);
44
+ const deps = {};
45
+ // Определяем способ подключения пакетов
46
+ const resolvePackage = (name) => useLocal ? `file:${node_path_1.default.resolve(__dirname, '../..', name.split('/')[1])}` : 'latest';
47
+ deps['@verse-bot/shared'] = resolvePackage('@verse-bot/shared');
48
+ if (platforms.includes('telegram')) {
49
+ deps['@verse-bot/tg-core'] = resolvePackage('@verse-bot/tg-core');
50
+ }
51
+ if (platforms.includes('vk')) {
52
+ deps['@verse-bot/vk-core'] = resolvePackage('@verse-bot/vk-core');
53
+ }
54
+ pkg.dependencies = { ...pkg.dependencies, ...deps };
55
+ // Удаляем лишние зависимости, если платформа не выбрана
56
+ if (!platforms.includes('telegram')) {
57
+ delete pkg.dependencies['@verse-bot/tg-core'];
58
+ }
59
+ if (!platforms.includes('vk')) {
60
+ delete pkg.dependencies['@verse-bot/vk-core'];
61
+ }
62
+ fs_extra_1.default.writeJsonSync(pkgPath, pkg, { spaces: 2 });
63
+ console.log(`Project "${projectName}" created. Installing dependencies...`);
64
+ (0, node_child_process_1.execSync)('npm install', { cwd: targetDir, stdio: 'inherit' });
65
+ console.log('\nDone! Start your bot:\n');
66
+ console.log(` cd ${projectName}`);
67
+ console.log(' cp .env.example .env # fill in your tokens');
68
+ console.log(' npm run dev\n');
69
+ }
70
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "create-verse-bot",
3
+ "version": "0.1.0",
4
+ "description": "CLI to scaffold a new Verse Bot project",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "create-verse-bot": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch"
12
+ },
13
+ "dependencies": {
14
+ "@inquirer/prompts": "^7.1.0",
15
+ "fs-extra": "^11.2.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/fs-extra": "^11.0.4"
19
+ }
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import { input, checkbox } from '@inquirer/prompts';
3
+ import path from 'node:path';
4
+ import fs from 'fs-extra';
5
+ import { execSync } from 'node:child_process';
6
+
7
+ async function main() {
8
+ const projectName = await input({
9
+ message: 'Project name:',
10
+ default: 'bot',
11
+ });
12
+ if (!projectName) {
13
+ console.log('Aborted.');
14
+ process.exit(1);
15
+ }
16
+
17
+ const platforms = await checkbox({
18
+ message: 'Select platforms:',
19
+ choices: [
20
+ { name: 'Telegram', value: 'telegram' },
21
+ { name: 'VK', value: 'vk' },
22
+ ],
23
+ required: true,
24
+ });
25
+ if (platforms.length === 0) {
26
+ console.log('No platforms selected. Exiting.');
27
+ process.exit(1);
28
+ }
29
+
30
+ const useLocal = process.argv.includes('--local');
31
+ const targetDir = path.resolve(process.cwd(), projectName);
32
+ if (fs.existsSync(targetDir)) {
33
+ console.error(`Directory "${projectName}" already exists.`);
34
+ process.exit(1);
35
+ }
36
+
37
+ // Копируем шаблон
38
+ const templateDir = path.resolve(__dirname, '../template');
39
+ fs.copySync(templateDir, targetDir);
40
+
41
+ // Обновляем package.json для зависимостей
42
+ const pkgPath = path.join(targetDir, 'package.json');
43
+ const pkg = fs.readJsonSync(pkgPath);
44
+ const deps: Record<string, string> = {};
45
+
46
+ // Определяем способ подключения пакетов
47
+ const resolvePackage = (name: string) =>
48
+ useLocal ? `file:${path.resolve(__dirname, '../..', name.split('/')[1])}` : 'latest';
49
+
50
+ deps['@verse-bot/shared'] = resolvePackage('@verse-bot/shared');
51
+ if (platforms.includes('telegram')) {
52
+ deps['@verse-bot/tg-core'] = resolvePackage('@verse-bot/tg-core');
53
+ }
54
+ if (platforms.includes('vk')) {
55
+ deps['@verse-bot/vk-core'] = resolvePackage('@verse-bot/vk-core');
56
+ }
57
+
58
+ pkg.dependencies = { ...pkg.dependencies, ...deps };
59
+ // Удаляем лишние зависимости, если платформа не выбрана
60
+ if (!platforms.includes('telegram')) {
61
+ delete pkg.dependencies['@verse-bot/tg-core'];
62
+ }
63
+ if (!platforms.includes('vk')) {
64
+ delete pkg.dependencies['@verse-bot/vk-core'];
65
+ }
66
+ fs.writeJsonSync(pkgPath, pkg, { spaces: 2 });
67
+
68
+ console.log(`Project "${projectName}" created. Installing dependencies...`);
69
+ execSync('npm install', { cwd: targetDir, stdio: 'inherit' });
70
+ console.log('\nDone! Start your bot:\n');
71
+ console.log(` cd ${projectName}`);
72
+ console.log(' cp .env.example .env # fill in your tokens');
73
+ console.log(' npm run dev\n');
74
+ }
75
+
76
+ main().catch(console.error);
@@ -0,0 +1,7 @@
1
+ TELEGRAM_BOT_TOKEN=""
2
+ VK_GROUP_TOKEN=""
3
+ VK_GROUP_ID=""
4
+ POSTGRES_USER="postgres"
5
+ POSTGRES_PASSWORD=""
6
+ POSTGRES_HOST="localhost"
7
+ POSTGRES_DB="postgres"
@@ -0,0 +1,34 @@
1
+ name: Deploy Bot
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ deploy:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - name: Deploy via SSH
14
+ uses: appleboy/ssh-action@v1.2.0
15
+ with:
16
+ host: ${{ secrets.VPS_HOST }}
17
+ username: ${{ secrets.VPS_USERNAME }}
18
+ key: ${{ secrets.SSH_PRIVATE_KEY }}
19
+ script: |
20
+ PROJECT_PATH=~/projects/${{ github.event.repository.name }}
21
+ if [ ! -d "$PROJECT_PATH" ]; then
22
+ mkdir -p ~/projects
23
+ git clone git@github.com:${{ github.repository }}.git "$PROJECT_PATH"
24
+ fi
25
+ cd "$PROJECT_PATH"
26
+ git pull origin main
27
+ echo "TELEGRAM_BOT_TOKEN=${{ secrets.TELEGRAM_BOT_TOKEN }}" > .env
28
+ echo "VK_GROUP_TOKEN=${{ secrets.VK_GROUP_TOKEN }}" >> .env
29
+ echo "VK_GROUP_ID=${{ secrets.VK_GROUP_ID }}" >> .env
30
+ echo "POSTGRES_DB=postgres" >> .env
31
+ echo "POSTGRES_USER=postgres" >> .env
32
+ echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env
33
+ echo "POSTGRES_HOST=db" >> .env
34
+ docker compose -f docker-compose.prod.yml up -d --build
@@ -0,0 +1,13 @@
1
+ FROM node:20-alpine AS builder
2
+ WORKDIR /app
3
+ COPY package*.json ./
4
+ RUN npm ci --ignore-scripts
5
+ COPY . .
6
+ RUN npm run build
7
+
8
+ FROM node:20-alpine
9
+ WORKDIR /app
10
+ COPY --from=builder /app/dist ./dist
11
+ COPY --from=builder /app/node_modules ./node_modules
12
+ COPY package*.json ./
13
+ CMD ["node", "dist/index.js"]
@@ -0,0 +1,25 @@
1
+ name: verse-bot-prod
2
+
3
+ services:
4
+ db:
5
+ image: postgres:16-alpine
6
+ restart: always
7
+ env_file: .env
8
+ volumes:
9
+ - ./data/prod_db:/var/lib/postgresql/data
10
+ - ./schema.sql:/docker-entrypoint-initdb.d/01-schema.sql
11
+ healthcheck:
12
+ test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB']
13
+ interval: 5s
14
+ timeout: 5s
15
+ retries: 5
16
+
17
+ bot:
18
+ build:
19
+ context: .
20
+ dockerfile: Dockerfile
21
+ restart: always
22
+ env_file: .env
23
+ depends_on:
24
+ db:
25
+ condition: service_healthy
@@ -0,0 +1,17 @@
1
+ name: verse-bot-dev
2
+
3
+ services:
4
+ db:
5
+ image: postgres:16-alpine
6
+ restart: always
7
+ env_file: .env
8
+ ports:
9
+ - '5432:5432'
10
+ volumes:
11
+ - ./data/dev_db:/var/lib/postgresql/data
12
+ - ./schema.sql:/docker-entrypoint-initdb.d/01-schema.sql
13
+ healthcheck:
14
+ test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB']
15
+ interval: 5s
16
+ timeout: 5s
17
+ retries: 5
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "verse-bot-project",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "prepare": "cp -n .env.example .env",
7
+ "dev": "tsx src/index.ts",
8
+ "build": "tsc",
9
+ "start": "node dist/index.js"
10
+ }
11
+ }
@@ -0,0 +1,15 @@
1
+ CREATE TABLE IF NOT EXISTS users (
2
+ id SERIAL PRIMARY KEY,
3
+ tg_id TEXT UNIQUE,
4
+ vk_id TEXT UNIQUE,
5
+ created_at TIMESTAMP DEFAULT now(),
6
+ updated_at TIMESTAMP DEFAULT now()
7
+ );
8
+
9
+ CREATE TABLE IF NOT EXISTS command_logs (
10
+ id SERIAL PRIMARY KEY,
11
+ user_id INTEGER REFERENCES users(id),
12
+ platform TEXT CHECK (platform IN ('telegram', 'vk')),
13
+ command TEXT,
14
+ created_at TIMESTAMP DEFAULT now()
15
+ );
@@ -0,0 +1,3 @@
1
+ export const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
2
+ export const VK_GROUP_TOKEN = process.env.VK_GROUP_TOKEN;
3
+ export const VK_GROUP_ID = process.env.VK_GROUP_ID ? Number(process.env.VK_GROUP_ID) : undefined;
@@ -0,0 +1,47 @@
1
+ import { initPool, getPool } from '@verse-bot/shared';
2
+ import { createUniversalTelegramBot } from '@verse-bot/tg-core';
3
+ import { createUniversalVKBot } from '@verse-bot/vk-core';
4
+ import { TELEGRAM_BOT_TOKEN, VK_GROUP_TOKEN, VK_GROUP_ID } from './env.js';
5
+
6
+ // Инициализация БД, если заданы переменные
7
+ if (process.env.POSTGRES_USER) {
8
+ const poolConfig = {
9
+ user: process.env.POSTGRES_USER,
10
+ password: process.env.POSTGRES_PASSWORD!,
11
+ host: process.env.POSTGRES_HOST || 'localhost',
12
+ database: process.env.POSTGRES_DB!,
13
+ port: 5432,
14
+ };
15
+ initPool(poolConfig);
16
+ }
17
+
18
+ // Заглушки команд – замените на свои
19
+ const commands = {
20
+ ping: async (ctx: any) => {
21
+ await ctx.reply('pong');
22
+ },
23
+ };
24
+
25
+ // Telegram
26
+ if (TELEGRAM_BOT_TOKEN) {
27
+ const bot = createUniversalTelegramBot({
28
+ token: TELEGRAM_BOT_TOKEN,
29
+ commands,
30
+ buttons: [],
31
+ });
32
+ bot.start().then(() => console.log('🤖 Telegram bot started'));
33
+ }
34
+
35
+ // VK
36
+ if (VK_GROUP_TOKEN && VK_GROUP_ID) {
37
+ const pool = getPool(); // если БД не используется, getPool() выбросит ошибку,
38
+ // но можно обернуть в try или проверять наличие process.env.POSTGRES_USER
39
+ const bot = createUniversalVKBot({
40
+ token: VK_GROUP_TOKEN,
41
+ groupId: VK_GROUP_ID,
42
+ commands,
43
+ buttons: [],
44
+ pool,
45
+ });
46
+ bot.start().then(() => console.log('🤖 VK bot started'));
47
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "types": ["node"]
12
+ },
13
+ "include": ["src/**/*.ts"]
14
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "declaration": true,
7
+ "composite": true
8
+ },
9
+ "include": ["src/**/*.ts"],
10
+ "exclude": ["node_modules", "dist"]
11
+ }