From f737d46802f403c6f40a9d1fca5a02b9e55b5998 Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 20:13:18 +0000 Subject: [PATCH 1/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/dev.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/dev.yaml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/dev.yaml b/.gitea/workflows/dev.yaml index c56b602..0c43308 100644 --- a/.gitea/workflows/dev.yaml +++ b/.gitea/workflows/dev.yaml @@ -1,4 +1,5 @@ name: "Telegram Shop Bot CI [DEV]" + on: push: branches: ["dev"] @@ -7,7 +8,23 @@ on: jobs: lint: - runs-on: node:22-alpine + runs-on: ubuntu-latest # Запускаем на универсальной платформе steps: - name: Checkout code - uses: actions/checkout@v3 \ No newline at end of file + uses: actions/checkout@v3 + + - name: Set up Node.js 22 + run: | + # Устанавливаем nvm (Node Version Manager) + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install 22 + nvm use 22 + node -v # Проверка установленной версии Node.js + + - name: Install dependencies + run: npm install + + - name: Run lint + run: npm run lint \ No newline at end of file -- 2.45.2 From 97079ce1d5ca5c9a59c3046a77f027f067ab58aa Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 20:15:51 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/demo.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/demo.yaml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/demo.yaml b/.gitea/workflows/demo.yaml index 890f944..88328ff 100644 --- a/.gitea/workflows/demo.yaml +++ b/.gitea/workflows/demo.yaml @@ -1,4 +1,5 @@ name: "Telegram Shop Bot CI [DEMO]" + on: push: branches: ["demo"] @@ -7,7 +8,23 @@ on: jobs: lint: - runs-on: node:22-alpine + runs-on: ubuntu-latest # Запускаем на универсальной платформе steps: - name: Checkout code - uses: actions/checkout@v3 \ No newline at end of file + uses: actions/checkout@v3 + + - name: Set up Node.js 22 + run: | + # Устанавливаем nvm (Node Version Manager) + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install 22 + nvm use 22 + node -v # Проверка установленной версии Node.js + + - name: Install dependencies + run: npm install + + - name: Run lint + run: npm run lint -- 2.45.2 From 5eec9586b70cd868a85c278e946e6b8cbc7a267a Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 20:16:38 +0000 Subject: [PATCH 3/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/prod.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/prod.yaml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/prod.yaml b/.gitea/workflows/prod.yaml index 429055f..68628b7 100644 --- a/.gitea/workflows/prod.yaml +++ b/.gitea/workflows/prod.yaml @@ -1,4 +1,5 @@ name: "Telegram Shop Bot CI [PROD]" + on: push: branches: ["prod"] @@ -7,7 +8,23 @@ on: jobs: lint: - runs-on: node:22-alpine + runs-on: ubuntu-latest # Запускаем на универсальной платформе steps: - name: Checkout code - uses: actions/checkout@v3 \ No newline at end of file + uses: actions/checkout@v3 + + - name: Set up Node.js 22 + run: | + # Устанавливаем nvm (Node Version Manager) + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install 22 + nvm use 22 + node -v # Проверка установленной версии Node.js + + - name: Install dependencies + run: npm install + + - name: Run lint + run: npm run lint \ No newline at end of file -- 2.45.2 From 1cabe48594cb769389daaea9ba1e97c165634b79 Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 21:07:50 +0000 Subject: [PATCH 4/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/dev.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/dev.yaml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/dev.yaml b/.gitea/workflows/dev.yaml index 0c43308..7ab55b1 100644 --- a/.gitea/workflows/dev.yaml +++ b/.gitea/workflows/dev.yaml @@ -2,29 +2,30 @@ name: "Telegram Shop Bot CI [DEV]" on: push: - branches: ["dev"] + branches: + - dev pull_request: - branches: ["dev"] + branches: + - dev jobs: lint: - runs-on: ubuntu-latest # Запускаем на универсальной платформе + runs-on: ubuntu:latest # Запуск на Ubuntu steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up Node.js 22 run: | - # Устанавливаем nvm (Node Version Manager) - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install 22 - nvm use 22 + # Устанавливаем Node.js 22 + curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash - + sudo apt-get install -y nodejs node -v # Проверка установленной версии Node.js - name: Install dependencies - run: npm install + run: | + npm install - name: Run lint - run: npm run lint \ No newline at end of file + run: | + npm run lint \ No newline at end of file -- 2.45.2 From 38ba2356bcbeee541ad55030d444606bb2b4f33f Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 21:09:33 +0000 Subject: [PATCH 5/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/dev.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/dev.yaml b/.gitea/workflows/dev.yaml index 7ab55b1..741dc91 100644 --- a/.gitea/workflows/dev.yaml +++ b/.gitea/workflows/dev.yaml @@ -10,7 +10,7 @@ on: jobs: lint: - runs-on: ubuntu:latest # Запуск на Ubuntu + runs-on: ubuntu-latest # Запуск на Ubuntu steps: - name: Checkout code uses: actions/checkout@v3 -- 2.45.2 From 9c4253ba58af5e711f94753f556d3706fe9a600f Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 21:16:34 +0000 Subject: [PATCH 6/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/dev.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/dev.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/dev.yaml b/.gitea/workflows/dev.yaml index 741dc91..5432d93 100644 --- a/.gitea/workflows/dev.yaml +++ b/.gitea/workflows/dev.yaml @@ -17,15 +17,17 @@ jobs: - name: Set up Node.js 22 run: | - # Устанавливаем Node.js 22 + echo "Устанавливаем Node.js 22" curl -sL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs node -v # Проверка установленной версии Node.js - name: Install dependencies run: | + echo "Устанавливаем зависимости" npm install - name: Run lint run: | - npm run lint \ No newline at end of file + echo "Запуск линтинга" + npm run lint -- 2.45.2 From 82afcf98542759567a5e79d0486ab48332a2ff5c Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 21:18:41 +0000 Subject: [PATCH 7/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/dev.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/dev.yaml b/.gitea/workflows/dev.yaml index 5432d93..fc7a28f 100644 --- a/.gitea/workflows/dev.yaml +++ b/.gitea/workflows/dev.yaml @@ -9,7 +9,7 @@ on: - dev jobs: - lint: + build-and-push: runs-on: ubuntu-latest # Запуск на Ubuntu steps: - name: Checkout code -- 2.45.2 From 373e8e25677dee659a6d481af83d4a6876161ac1 Mon Sep 17 00:00:00 2001 From: Artyom Ashirov <1323ED5@gmail.com> Date: Fri, 15 Nov 2024 02:26:13 +0300 Subject: [PATCH 8/9] user deletion/blocking --- src/config/database.js | 1 + src/handlers/adminLocationHandler.js | 4 +- src/handlers/adminUserHandler.js | 74 ++++++++++++++++- src/index.js | 32 ++++++- src/models/User.js | 120 ++++++++++++++++++--------- 5 files changed, 187 insertions(+), 44 deletions(-) diff --git a/src/config/database.js b/src/config/database.js index e7a2e22..33c0ff6 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -94,6 +94,7 @@ const initDb = async () => { country TEXT, city TEXT, district TEXT, + status INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); diff --git a/src/handlers/adminLocationHandler.js b/src/handlers/adminLocationHandler.js index e23233b..7c05143 100644 --- a/src/handlers/adminLocationHandler.js +++ b/src/handlers/adminLocationHandler.js @@ -215,7 +215,7 @@ export default class AdminLocationHandler { const keyboard = { inline_keyboard: locations.map(loc => [{ text: `${loc.country} > ${loc.city} > ${loc.district} (P:${loc.product_count} C:${loc.category_count})`, - callback_data: `confirm_delete_${loc.country}_${loc.city}_${loc.district}` + callback_data: `confirm_delete_location_${loc.country}_${loc.city}_${loc.district}` }]) }; @@ -239,7 +239,7 @@ export default class AdminLocationHandler { async handleConfirmDelete(callbackQuery) { const chatId = callbackQuery.message.chat.id; const [country, city, district] = callbackQuery.data - .replace('confirm_delete_', '') + .replace('confirm_delete_location_', '') .split('_'); try { diff --git a/src/handlers/adminUserHandler.js b/src/handlers/adminUserHandler.js index 25717ef..7ee340d 100644 --- a/src/handlers/adminUserHandler.js +++ b/src/handlers/adminUserHandler.js @@ -265,7 +265,7 @@ ${purchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price} const chatId = callbackQuery.message.chat.id; try { - await User.delete(userId); + await User.updateUserStatus(userId, 1); const keyboard = { inline_keyboard: [ @@ -273,6 +273,12 @@ ${purchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price} ] }; + try { + await this.bot.sendMessage(userId, '⚠️Your account has been deleted by administrator'); + } catch (e) { + // ignore if we can't notify user + } + await this.bot.editMessageText( `✅ User ${userId} has been successfully deleted.`, { @@ -287,6 +293,72 @@ ${purchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price} } } + async handleBlockUser(callbackQuery) { + if (!this.isAdmin(callbackQuery.from.id)) return; + + const userId = callbackQuery.data.replace('block_user_', ''); + const chatId = callbackQuery.message.chat.id; + + try { + const keyboard = { + inline_keyboard: [ + [ + {text: '✅ Confirm Block', callback_data: `confirm_block_user_${userId}`}, + {text: '❌ Cancel', callback_data: `view_user_${userId}`} + ] + ] + }; + + await this.bot.editMessageText( + `⚠️ Are you sure you want to block user ${userId}?`, + { + chat_id: chatId, + message_id: callbackQuery.message.message_id, + reply_markup: keyboard, + parse_mode: 'HTML' + } + ); + } catch (error) { + console.error('Error in handleBlockUser:', error); + await this.bot.sendMessage(chatId, 'Error processing block request. Please try again.'); + } + } + + async handleConfirmBlock(callbackQuery) { + if (!this.isAdmin(callbackQuery.from.id)) return; + + const userId = callbackQuery.data.replace('confirm_block_user_', ''); + const chatId = callbackQuery.message.chat.id; + + try { + await User.updateUserStatus(userId, 2); + + const keyboard = { + inline_keyboard: [ + [{text: '« Back to User List', callback_data: 'admin_users'}] + ] + }; + + try { + await this.bot.sendMessage(userId, '⚠️Your account has been blocked by administrator'); + } catch (e) { + // ignore if we can't notify user + } + + await this.bot.editMessageText( + `✅ User ${userId} has been successfully blocked.`, + { + chat_id: chatId, + message_id: callbackQuery.message.message_id, + reply_markup: keyboard + } + ); + } catch (error) { + console.error('Error in handleConfirmBlock:', error); + await this.bot.sendMessage(chatId, 'Error blocking user. Please try again.'); + } + } + async handleEditUserBalance(callbackQuery) { if (!this.isAdmin(callbackQuery.from.id)) return; diff --git a/src/index.js b/src/index.js index fde65c1..1f832eb 100644 --- a/src/index.js +++ b/src/index.js @@ -43,6 +43,13 @@ const adminProductHandler = new AdminProductHandler(bot); // Start command - Create user profile bot.onText(/\/start/, async (msg) => { logDebug('/start', 'handleStart'); + + const canUse = await userHandler.canUseBot(msg); + + if (!canUse) { + return; + } + try { await userHandler.handleStart(msg); } catch (error) { @@ -64,6 +71,16 @@ bot.onText(/\/admin/, async (msg) => { bot.on('message', async (msg) => { if (!msg.text) return; + if (msg.text.toLowerCase() === '/start') { + return; + } + + const canUse = await userHandler.canUseBot(msg); + + if (!canUse) { + return; + } + try { // Check for admin location input if (await adminLocationHandler.handleLocationInput(msg)) { @@ -126,6 +143,13 @@ bot.on('callback_query', async (callbackQuery) => { const action = callbackQuery.data; const msg = callbackQuery.message; + const canUse = await userHandler.canUseBot(callbackQuery); + + if (!canUse) { + await bot.answerCallbackQuery(callbackQuery.id); + return; + } + try { // Profile and location management if (action === 'set_location') { @@ -211,7 +235,7 @@ bot.on('callback_query', async (callbackQuery) => { } else if (action === 'delete_location') { logDebug(action, 'handleDeleteLocation'); await adminLocationHandler.handleDeleteLocation(callbackQuery); - } else if (action.startsWith('confirm_delete_')) { + } else if (action.startsWith('confirm_delete_location_')) { logDebug(action, 'handleConfirmDelete'); await adminLocationHandler.handleConfirmDelete(callbackQuery); } else if (action === 'admin_menu') { @@ -264,9 +288,15 @@ bot.on('callback_query', async (callbackQuery) => { } else if (action.startsWith('delete_user_')) { logDebug(action, 'handleDeleteUser'); await adminUserHandler.handleDeleteUser(callbackQuery); + } else if (action.startsWith('block_user_')) { + logDebug(action, 'handleDeleteUser'); + await adminUserHandler.handleBlockUser(callbackQuery); } else if (action.startsWith('confirm_delete_user_')) { logDebug(action, 'handleConfirmDelete'); await adminUserHandler.handleConfirmDelete(callbackQuery); + } else if (action.startsWith('confirm_block_user_')) { + logDebug(action, 'handleConfirmBlock'); + await adminUserHandler.handleConfirmBlock(callbackQuery); } else if (action.startsWith('edit_user_balance_')) { logDebug(action, 'handleEditUserBalance'); await adminUserHandler.handleEditUserBalance(callbackQuery); diff --git a/src/models/User.js b/src/models/User.js index 9e1de17..75e3bf7 100644 --- a/src/models/User.js +++ b/src/models/User.js @@ -1,50 +1,50 @@ import db from '../config/database.js'; export default class User { - static async create(telegramId, username) { - try { - // First check if user exists - const existingUser = await this.getById(telegramId); - if (existingUser) { - return existingUser.id; - } + static async create(telegramId, username) { + try { + // First check if user exists + const existingUser = await this.getById(telegramId); + if (existingUser) { + return existingUser.id; + } - // Begin transaction - await db.runAsync('BEGIN TRANSACTION'); + // Begin transaction + await db.runAsync('BEGIN TRANSACTION'); - // Create new user - const result = await db.runAsync( - 'INSERT INTO users (telegram_id, username) VALUES (?, ?)', - [telegramId.toString(), username] - ); + // Create new user + const result = await db.runAsync( + 'INSERT INTO users (telegram_id, username) VALUES (?, ?)', + [telegramId.toString(), username] + ); - // Commit transaction - await db.runAsync('COMMIT'); + // Commit transaction + await db.runAsync('COMMIT'); - return result.lastID; - } catch (error) { - // Rollback on error - await db.runAsync('ROLLBACK'); - console.error('Error creating user:', error); - throw error; + return result.lastID; + } catch (error) { + // Rollback on error + await db.runAsync('ROLLBACK'); + console.error('Error creating user:', error); + throw error; + } } - } - static async getById(telegramId) { - try { - return await db.getAsync( - 'SELECT * FROM users WHERE telegram_id = ?', - [telegramId.toString()] - ); - } catch (error) { - console.error('Error getting user:', error); - throw error; + static async getById(telegramId) { + try { + return await db.getAsync( + 'SELECT * FROM users WHERE telegram_id = ?', + [telegramId.toString()] + ); + } catch (error) { + console.error('Error getting user:', error); + throw error; + } } - } - static async getUserStats(telegramId) { - try { - return await db.getAsync(` + static async getUserStats(telegramId) { + try { + return await db.getAsync(` SELECT u.*, COUNT(DISTINCT p.id) as purchase_count, @@ -58,9 +58,49 @@ export default class User { WHERE u.telegram_id = ? GROUP BY u.id `, [telegramId.toString()]); - } catch (error) { - console.error('Error getting user stats:', error); - throw error; + } catch (error) { + console.error('Error getting user stats:', error); + throw error; + } + } + + static async updateUserStatus(telegramId, status) { + // statuses + // 0 - active + // 1 - deleted + // 2 - blocked + + try { + await db.runAsync('BEGIN TRANSACTION'); + + // Update user status + await db.runAsync('UPDATE users SET status = ? WHERE telegram_id = ?', [status, telegramId.toString()]); + + // Commit transaction + await db.runAsync('COMMIT'); + } catch (e) { + await db.runAsync("ROLLBACK"); + console.error('Error deleting user:', error); + throw error; + } + } + + static async delete(telegramId) { + try { + await db.runAsync('BEGIN TRANSACTION'); + + // Delete user and his data + await db.runAsync('DELETE FROM users WHERE telegram_id = ?', [telegramId.toString()]); + await db.runAsync('DELETE FROM transactions WHERE user_id = ?', [telegramId.toString()]); + await db.runAsync('DELETE FROM purchases WHERE user_id = ?', [telegramId.toString()]); + await db.runAsync('DELETE FROM crypto_wallets WHERE user_id = ?', [telegramId.toString()]); + + // Commit transaction + await db.runAsync('COMMIT'); + } catch (e) { + await db.runAsync("ROLLBACK"); + console.error('Error deleting user:', error); + throw error; + } } - } } \ No newline at end of file -- 2.45.2 From f7d21d9d0d9e8b1933130c8ddf37b41ed2f1af69 Mon Sep 17 00:00:00 2001 From: NW Date: Thu, 14 Nov 2024 23:32:46 +0000 Subject: [PATCH 9/9] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20.gitea/workflows/dev.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/dev.yaml b/.gitea/workflows/dev.yaml index fc7a28f..5432d93 100644 --- a/.gitea/workflows/dev.yaml +++ b/.gitea/workflows/dev.yaml @@ -9,7 +9,7 @@ on: - dev jobs: - build-and-push: + lint: runs-on: ubuntu-latest # Запуск на Ubuntu steps: - name: Checkout code -- 2.45.2