commit f62d7b15e218be6dd3f7cc1f7d8fb5f7abcadd5a Author: Michaël Date: Thu Dec 25 15:08:20 2025 -0300 first commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..69c2963 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +BOT_TOKEN=your_bot_token_here diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d82971 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.env +*.log +.DS_Store + diff --git a/.history/.env_20251216201616 b/.history/.env_20251216201616 new file mode 100644 index 0000000..e69de29 diff --git a/.history/.env_20251216201624 b/.history/.env_20251216201624 new file mode 100644 index 0000000..69c2963 --- /dev/null +++ b/.history/.env_20251216201624 @@ -0,0 +1 @@ +BOT_TOKEN=your_bot_token_here diff --git a/.history/.env_20251216201746 b/.history/.env_20251216201746 new file mode 100644 index 0000000..a18f77b --- /dev/null +++ b/.history/.env_20251216201746 @@ -0,0 +1 @@ +BOT_TOKEN=8531569553:AAFT-SvBHGCDnF_8dvwyEiLRA1ome-p-ttc diff --git a/.history/.env_20251216205724 b/.history/.env_20251216205724 new file mode 100644 index 0000000..d0ed4fc --- /dev/null +++ b/.history/.env_20251216205724 @@ -0,0 +1,2 @@ +#BOT_TOKEN=8531569553:AAFT-SvBHGCDnF_8dvwyEiLRA1ome-p-ttc +BOT_TOKEN=8566687126:AAGw-zhiTml3qZyXfdEkF0Jag5MTCqhO1BU \ No newline at end of file diff --git a/.history/.env_20251216210105 b/.history/.env_20251216210105 new file mode 100644 index 0000000..aee1ac2 --- /dev/null +++ b/.history/.env_20251216210105 @@ -0,0 +1 @@ +BOT_TOKEN=8531569553:AAFT-SvBHGCDnF_8dvwyEiLRA1ome-p-ttc \ No newline at end of file diff --git a/.history/.env_20251217082418 b/.history/.env_20251217082418 new file mode 100644 index 0000000..d0ed4fc --- /dev/null +++ b/.history/.env_20251217082418 @@ -0,0 +1,2 @@ +#BOT_TOKEN=8531569553:AAFT-SvBHGCDnF_8dvwyEiLRA1ome-p-ttc +BOT_TOKEN=8566687126:AAGw-zhiTml3qZyXfdEkF0Jag5MTCqhO1BU \ No newline at end of file diff --git a/.history/README_20251216201331.md b/.history/README_20251216201331.md new file mode 100644 index 0000000..4c1f45b --- /dev/null +++ b/.history/README_20251216201331.md @@ -0,0 +1,62 @@ +# Keyboard Reset Bot + +A minimal Telegram bot that removes reply keyboard buttons in Telegram group chats by sending a keyboard reset message. + +## Features + +- Works only in group chats and supergroups +- Single command: `/resetkeyboard` +- Admin-only authorization +- Automatically removes its confirmation message after 7 seconds +- Stateless (no database, no data storage) + +## Setup + +1. **Get a Bot Token** + - Talk to [@BotFather](https://t.me/botfather) on Telegram + - Create a new bot with `/newbot` + - Copy the bot token + +2. **Install Dependencies** + ```bash + npm install + ``` + +3. **Configure Environment** + - Copy `.env.example` to `.env` + - Add your bot token: + ``` + BOT_TOKEN=your_bot_token_here + ``` + +4. **Run the Bot** + ```bash + npm start + ``` + +## Usage + +1. Add the bot to your Telegram group +2. Grant the bot "Read messages" permission (and optionally "Delete messages" for cleanup) +3. An admin can use `/resetkeyboard` to remove all reply keyboards in the group + +## Requirements + +- Node.js 14+ (ES modules support) +- Bot permissions: "Read messages" (required), "Delete messages" (optional) + +## How It Works + +When an admin sends `/resetkeyboard` in a group: +1. The bot verifies the user is an administrator +2. Sends a message with `ReplyKeyboardRemove` (selective: false) +3. This resets reply keyboards for all users who receive the message +4. The bot deletes its own message after 7 seconds + +## Notes + +- The bot ignores private chats +- Only group administrators can use the command +- The bot is stateless and does not store any data +- If another bot sends a keyboard after reset, it will appear again (expected behavior) + diff --git a/.history/README_20251216201351.md b/.history/README_20251216201351.md new file mode 100644 index 0000000..4c1f45b --- /dev/null +++ b/.history/README_20251216201351.md @@ -0,0 +1,62 @@ +# Keyboard Reset Bot + +A minimal Telegram bot that removes reply keyboard buttons in Telegram group chats by sending a keyboard reset message. + +## Features + +- Works only in group chats and supergroups +- Single command: `/resetkeyboard` +- Admin-only authorization +- Automatically removes its confirmation message after 7 seconds +- Stateless (no database, no data storage) + +## Setup + +1. **Get a Bot Token** + - Talk to [@BotFather](https://t.me/botfather) on Telegram + - Create a new bot with `/newbot` + - Copy the bot token + +2. **Install Dependencies** + ```bash + npm install + ``` + +3. **Configure Environment** + - Copy `.env.example` to `.env` + - Add your bot token: + ``` + BOT_TOKEN=your_bot_token_here + ``` + +4. **Run the Bot** + ```bash + npm start + ``` + +## Usage + +1. Add the bot to your Telegram group +2. Grant the bot "Read messages" permission (and optionally "Delete messages" for cleanup) +3. An admin can use `/resetkeyboard` to remove all reply keyboards in the group + +## Requirements + +- Node.js 14+ (ES modules support) +- Bot permissions: "Read messages" (required), "Delete messages" (optional) + +## How It Works + +When an admin sends `/resetkeyboard` in a group: +1. The bot verifies the user is an administrator +2. Sends a message with `ReplyKeyboardRemove` (selective: false) +3. This resets reply keyboards for all users who receive the message +4. The bot deletes its own message after 7 seconds + +## Notes + +- The bot ignores private chats +- Only group administrators can use the command +- The bot is stateless and does not store any data +- If another bot sends a keyboard after reset, it will appear again (expected behavior) + diff --git a/.history/about_20251216204253.md b/.history/about_20251216204253.md new file mode 100644 index 0000000..e69de29 diff --git a/.history/about_20251216204256.md b/.history/about_20251216204256.md new file mode 100644 index 0000000..4323eb8 --- /dev/null +++ b/.history/about_20251216204256.md @@ -0,0 +1,128 @@ +BOT NAME +Keyboard Reset Bot (working name) + +PURPOSE +A minimal Telegram bot whose only responsibility is to remove reply keyboard buttons in Telegram group chats by sending a keyboard reset message. + +This bot does not manage data, users, state, or configuration beyond a single slash command. + +CORE FUNCTIONAL REQUIREMENTS + +Supported chats +• Group chats +• Supergroups + +The bot must ignore private chats. + +If used in a private chat, it should reply once with: +“This bot only works in groups.” + +Command +Single slash command: + +/resetkeyboard + +No aliases. No parameters. + +Authorization +Only group administrators may trigger the command. + +Authorization rules: +• Check the user who sent /resetkeyboard +• If the user is an administrator or creator → allow +• Otherwise → ignore or reply with: +“Only group admins can use this command.” + +No role persistence. Authorization is checked per command invocation. + +Keyboard removal behavior +When triggered by an authorized user in a group: + +The bot sends one normal message to the group with: + +• reply_markup = ReplyKeyboardRemove +• selective = false + +This message must: +• Be visible (not silent) +• Not be inline +• Not target a specific user + +This resets the reply keyboard for all users who receive the message. + +Message content +Message text should be short and neutral, for example: + +“Keyboard reset.” + +No emojis. +No markdown. +No mentions. + +Message lifecycle +Optional but recommended: + +• The bot deletes its own confirmation message after 5–10 seconds +• Do NOT delete immediately (clients need time to process the keyboard reset) + +If deletion fails (permissions, race condition), the bot must silently continue. + +What the bot must NOT do +The bot must NOT: + +• Store data +• Track users +• Use a database +• React to normal messages +• Remove inline keyboards +• Delete other bots’ messages +• Re-send keyboards +• Perform scheduled actions + +This is a stateless utility bot. + +EDGE CASES AND EXPECTED BEHAVIOR + +• If another bot sends a keyboard after this bot runs, that keyboard will appear again. This is expected. +• If a user does not receive the message (offline/network), their keyboard may persist until the next reset. +• This is the only technically correct way to reset keyboards in Telegram. + +IMPLEMENTATION CONSTRAINTS + +Update handling +Either polling or webhook is acceptable. + +Polling is preferred for simplicity. + +Libraries +Any official or widely used Telegram library is acceptable, such as: + +• python-telegram-bot +• aiogram +• Telegraf (Node.js) +• Raw Bot API (HTTP) + +No unofficial wrappers. + +Permissions required +The bot must be granted: + +• “Read messages” +• “Delete messages” (optional, only for cleanup) + +No admin rights beyond this are required. + +LOGGING AND ERROR HANDLING + +• Log startup success +• Log command usage with chat ID and user ID +• Log authorization failures at debug level +• Fail silently on Telegram API errors (do not spam the group) + +SUCCESS CRITERIA + +The bot is considered correct if: + +• An admin sends /resetkeyboard in a group +• All visible reply keyboards disappear for users in that group +• The bot does nothing else \ No newline at end of file diff --git a/.history/bot_20251216201350.js b/.history/bot_20251216201350.js new file mode 100644 index 0000000..a654944 --- /dev/null +++ b/.history/bot_20251216201350.js @@ -0,0 +1,86 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async () => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + // Only log to console for debugging + console.error('Error processing command:', error.message); + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); + diff --git a/.history/bot_20251216201358.js b/.history/bot_20251216201358.js new file mode 100644 index 0000000..3d47b14 --- /dev/null +++ b/.history/bot_20251216201358.js @@ -0,0 +1,85 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + // Only log to console for debugging + console.error('Error processing command:', error.message); + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216201948.js b/.history/bot_20251216201948.js new file mode 100644 index 0000000..685003e --- /dev/null +++ b/.history/bot_20251216201948.js @@ -0,0 +1,97 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + // Only log to console for debugging + console.error('Error processing command:', error.message); + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216201952.js b/.history/bot_20251216201952.js new file mode 100644 index 0000000..7cb4497 --- /dev/null +++ b/.history/bot_20251216201952.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216201956.js b/.history/bot_20251216201956.js new file mode 100644 index 0000000..a43ec76 --- /dev/null +++ b/.history/bot_20251216201956.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202004.js b/.history/bot_20251216202004.js new file mode 100644 index 0000000..44a7da6 --- /dev/null +++ b/.history/bot_20251216202004.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async(ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat ? .type, + chatId: ctx.chat ? .id, + fromId: ctx.from ? .id, + text: ctx.message ? .text || ctx.update.message ? .text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202006.js b/.history/bot_20251216202006.js new file mode 100644 index 0000000..44a7da6 --- /dev/null +++ b/.history/bot_20251216202006.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async(ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat ? .type, + chatId: ctx.chat ? .id, + fromId: ctx.from ? .id, + text: ctx.message ? .text || ctx.update.message ? .text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202021.js b/.history/bot_20251216202021.js new file mode 100644 index 0000000..31bc902 --- /dev/null +++ b/.history/bot_20251216202021.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202022.js b/.history/bot_20251216202022.js new file mode 100644 index 0000000..473d8e8 --- /dev/null +++ b/.history/bot_20251216202022.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async () => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202030.js b/.history/bot_20251216202030.js new file mode 100644 index 0000000..44a7da6 --- /dev/null +++ b/.history/bot_20251216202030.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async(ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat ? .type, + chatId: ctx.chat ? .id, + fromId: ctx.from ? .id, + text: ctx.message ? .text || ctx.update.message ? .text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202045.js b/.history/bot_20251216202045.js new file mode 100644 index 0000000..31bc902 --- /dev/null +++ b/.history/bot_20251216202045.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202046.js b/.history/bot_20251216202046.js new file mode 100644 index 0000000..1d2442b --- /dev/null +++ b/.history/bot_20251216202046.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202047.js b/.history/bot_20251216202047.js new file mode 100644 index 0000000..473d8e8 --- /dev/null +++ b/.history/bot_20251216202047.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async () => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202053.js b/.history/bot_20251216202053.js new file mode 100644 index 0000000..44a7da6 --- /dev/null +++ b/.history/bot_20251216202053.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async(ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat ? .type, + chatId: ctx.chat ? .id, + fromId: ctx.from ? .id, + text: ctx.message ? .text || ctx.update.message ? .text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202108.js b/.history/bot_20251216202108.js new file mode 100644 index 0000000..31bc902 --- /dev/null +++ b/.history/bot_20251216202108.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202110.js b/.history/bot_20251216202110.js new file mode 100644 index 0000000..1d2442b --- /dev/null +++ b/.history/bot_20251216202110.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202111.js b/.history/bot_20251216202111.js new file mode 100644 index 0000000..473d8e8 --- /dev/null +++ b/.history/bot_20251216202111.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async (ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat?.type, + chatId: ctx.chat?.id, + fromId: ctx.from?.id, + text: ctx.message?.text || ctx.update.message?.text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async () => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216202116.js b/.history/bot_20251216202116.js new file mode 100644 index 0000000..44a7da6 --- /dev/null +++ b/.history/bot_20251216202116.js @@ -0,0 +1,107 @@ +import { Telegraf, Markup } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot started successfully'); + +// Debug: Log all incoming updates to see if bot is receiving messages +bot.use(async(ctx, next) => { + console.log('Update received:', { + updateType: ctx.updateType, + chatType: ctx.chat ? .type, + chatId: ctx.chat ? .id, + fromId: ctx.from ? .id, + text: ctx.message ? .text || ctx.update.message ? .text || 'N/A' + }); + return next(); +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command received - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.log(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: Markup.removeKeyboard() + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + // This is expected behavior - do not log or spam + } + }, 7000); + + } catch (error) { + // Log errors for debugging + console.error('Error processing command:', error); + console.error('Error details:', { + message: error.message, + code: error.code, + response: error.response + }); + // Try to reply with error (but don't spam if it fails) + try { + await ctx.reply('An error occurred while processing the command.'); + } catch (replyError) { + // Silently continue if reply also fails + } + } +}); + +// Handle errors +bot.catch((err, ctx) => { + console.error('Bot error:', err); +}); + +// Start polling +bot.launch().then(() => { + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216204344.js b/.history/bot_20251216204344.js new file mode 100644 index 0000000..3ca3ca7 --- /dev/null +++ b/.history/bot_20251216204344.js @@ -0,0 +1,89 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot starting...'); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async () => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +bot.launch().then(() => { + console.log('Keyboard Reset Bot started successfully'); + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); diff --git a/.history/bot_20251216204356.js b/.history/bot_20251216204356.js new file mode 100644 index 0000000..251a5e4 --- /dev/null +++ b/.history/bot_20251216204356.js @@ -0,0 +1,89 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot starting...'); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +bot.launch().then(() => { + console.log('Keyboard Reset Bot started successfully'); + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216204358.js b/.history/bot_20251216204358.js new file mode 100644 index 0000000..251a5e4 --- /dev/null +++ b/.history/bot_20251216204358.js @@ -0,0 +1,89 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +const bot = new Telegraf(BOT_TOKEN); + +// Log startup success +console.log('Keyboard Reset Bot starting...'); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +bot.launch().then(() => { + console.log('Keyboard Reset Bot started successfully'); + console.log('Bot is polling for updates...'); +}).catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); +}); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216204824.js b/.history/bot_20251216204824.js new file mode 100644 index 0000000..d696f22 --- /dev/null +++ b/.history/bot_20251216204824.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async (ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat?.type, + text: ctx.message?.text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async (ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async (ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async () => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); diff --git a/.history/bot_20251216204836.js b/.history/bot_20251216204836.js new file mode 100644 index 0000000..a33cebf --- /dev/null +++ b/.history/bot_20251216204836.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat ? .type, + text: ctx.message ? .text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216204928.js b/.history/bot_20251216204928.js new file mode 100644 index 0000000..a33cebf --- /dev/null +++ b/.history/bot_20251216204928.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat ? .type, + text: ctx.message ? .text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216204946.js b/.history/bot_20251216204946.js new file mode 100644 index 0000000..1e41b89 --- /dev/null +++ b/.history/bot_20251216204946.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat?.type, + text: ctx.message?.text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216204950.js b/.history/bot_20251216204950.js new file mode 100644 index 0000000..a33cebf --- /dev/null +++ b/.history/bot_20251216204950.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat ? .type, + text: ctx.message ? .text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216205010.js b/.history/bot_20251216205010.js new file mode 100644 index 0000000..1e41b89 --- /dev/null +++ b/.history/bot_20251216205010.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat?.type, + text: ctx.message?.text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216205016.js b/.history/bot_20251216205016.js new file mode 100644 index 0000000..a33cebf --- /dev/null +++ b/.history/bot_20251216205016.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat ? .type, + text: ctx.message ? .text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216205023.js b/.history/bot_20251216205023.js new file mode 100644 index 0000000..a33cebf --- /dev/null +++ b/.history/bot_20251216205023.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat ? .type, + text: ctx.message ? .text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216205028.js b/.history/bot_20251216205028.js new file mode 100644 index 0000000..a33cebf --- /dev/null +++ b/.history/bot_20251216205028.js @@ -0,0 +1,120 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + console.log('Update received:', { + type: ctx.updateType, + chat: ctx.chat ? .type, + text: ctx.message ? .text + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216205056.js b/.history/bot_20251216205056.js new file mode 100644 index 0000000..8969236 --- /dev/null +++ b/.history/bot_20251216205056.js @@ -0,0 +1,122 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async (ctx, next) => { + const chatType = ctx.chat ? ctx.chat.type : undefined; + const messageText = ctx.message ? ctx.message.text : undefined; + console.log('Update received:', { + type: ctx.updateType, + chat: chatType, + text: messageText + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216205100.js b/.history/bot_20251216205100.js new file mode 100644 index 0000000..ed570b7 --- /dev/null +++ b/.history/bot_20251216205100.js @@ -0,0 +1,122 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + const chatType = ctx.chat ? ctx.chat.type : undefined; + const messageText = ctx.message ? ctx.message.text : undefined; + console.log('Update received:', { + type: ctx.updateType, + chat: chatType, + text: messageText + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/bot_20251216205103.js b/.history/bot_20251216205103.js new file mode 100644 index 0000000..ed570b7 --- /dev/null +++ b/.history/bot_20251216205103.js @@ -0,0 +1,122 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + const chatType = ctx.chat ? ctx.chat.type : undefined; + const messageText = ctx.message ? ctx.message.text : undefined; + console.log('Update received:', { + type: ctx.updateType, + chat: chatType, + text: messageText + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/.history/package_20251216201322.json b/.history/package_20251216201322.json new file mode 100644 index 0000000..f72481a --- /dev/null +++ b/.history/package_20251216201322.json @@ -0,0 +1,23 @@ +{ + "name": "keyboard-reset-bot", + "version": "1.0.0", + "description": "A minimal Telegram bot that removes reply keyboard buttons in group chats", + "main": "bot.js", + "type": "module", + "scripts": { + "start": "node bot.js" + }, + "keywords": [ + "telegram", + "bot", + "keyboard", + "reset" + ], + "author": "", + "license": "MIT", + "dependencies": { + "telegraf": "^4.15.0", + "dotenv": "^16.3.1" + } +} + diff --git a/.history/package_20251216201351.json b/.history/package_20251216201351.json new file mode 100644 index 0000000..33614cc --- /dev/null +++ b/.history/package_20251216201351.json @@ -0,0 +1,22 @@ +{ + "name": "keyboard-reset-bot", + "version": "1.0.0", + "description": "A minimal Telegram bot that removes reply keyboard buttons in group chats", + "main": "bot.js", + "type": "module", + "scripts": { + "start": "node bot.js" + }, + "keywords": [ + "telegram", + "bot", + "keyboard", + "reset" + ], + "author": "", + "license": "MIT", + "dependencies": { + "telegraf": "^4.15.0", + "dotenv": "^16.3.1" + } +} \ No newline at end of file diff --git a/.history/package_20251216202006.json b/.history/package_20251216202006.json new file mode 100644 index 0000000..33614cc --- /dev/null +++ b/.history/package_20251216202006.json @@ -0,0 +1,22 @@ +{ + "name": "keyboard-reset-bot", + "version": "1.0.0", + "description": "A minimal Telegram bot that removes reply keyboard buttons in group chats", + "main": "bot.js", + "type": "module", + "scripts": { + "start": "node bot.js" + }, + "keywords": [ + "telegram", + "bot", + "keyboard", + "reset" + ], + "author": "", + "license": "MIT", + "dependencies": { + "telegraf": "^4.15.0", + "dotenv": "^16.3.1" + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4c1f45b --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Keyboard Reset Bot + +A minimal Telegram bot that removes reply keyboard buttons in Telegram group chats by sending a keyboard reset message. + +## Features + +- Works only in group chats and supergroups +- Single command: `/resetkeyboard` +- Admin-only authorization +- Automatically removes its confirmation message after 7 seconds +- Stateless (no database, no data storage) + +## Setup + +1. **Get a Bot Token** + - Talk to [@BotFather](https://t.me/botfather) on Telegram + - Create a new bot with `/newbot` + - Copy the bot token + +2. **Install Dependencies** + ```bash + npm install + ``` + +3. **Configure Environment** + - Copy `.env.example` to `.env` + - Add your bot token: + ``` + BOT_TOKEN=your_bot_token_here + ``` + +4. **Run the Bot** + ```bash + npm start + ``` + +## Usage + +1. Add the bot to your Telegram group +2. Grant the bot "Read messages" permission (and optionally "Delete messages" for cleanup) +3. An admin can use `/resetkeyboard` to remove all reply keyboards in the group + +## Requirements + +- Node.js 14+ (ES modules support) +- Bot permissions: "Read messages" (required), "Delete messages" (optional) + +## How It Works + +When an admin sends `/resetkeyboard` in a group: +1. The bot verifies the user is an administrator +2. Sends a message with `ReplyKeyboardRemove` (selective: false) +3. This resets reply keyboards for all users who receive the message +4. The bot deletes its own message after 7 seconds + +## Notes + +- The bot ignores private chats +- Only group administrators can use the command +- The bot is stateless and does not store any data +- If another bot sends a keyboard after reset, it will appear again (expected behavior) + diff --git a/about.md b/about.md new file mode 100644 index 0000000..4323eb8 --- /dev/null +++ b/about.md @@ -0,0 +1,128 @@ +BOT NAME +Keyboard Reset Bot (working name) + +PURPOSE +A minimal Telegram bot whose only responsibility is to remove reply keyboard buttons in Telegram group chats by sending a keyboard reset message. + +This bot does not manage data, users, state, or configuration beyond a single slash command. + +CORE FUNCTIONAL REQUIREMENTS + +Supported chats +• Group chats +• Supergroups + +The bot must ignore private chats. + +If used in a private chat, it should reply once with: +“This bot only works in groups.” + +Command +Single slash command: + +/resetkeyboard + +No aliases. No parameters. + +Authorization +Only group administrators may trigger the command. + +Authorization rules: +• Check the user who sent /resetkeyboard +• If the user is an administrator or creator → allow +• Otherwise → ignore or reply with: +“Only group admins can use this command.” + +No role persistence. Authorization is checked per command invocation. + +Keyboard removal behavior +When triggered by an authorized user in a group: + +The bot sends one normal message to the group with: + +• reply_markup = ReplyKeyboardRemove +• selective = false + +This message must: +• Be visible (not silent) +• Not be inline +• Not target a specific user + +This resets the reply keyboard for all users who receive the message. + +Message content +Message text should be short and neutral, for example: + +“Keyboard reset.” + +No emojis. +No markdown. +No mentions. + +Message lifecycle +Optional but recommended: + +• The bot deletes its own confirmation message after 5–10 seconds +• Do NOT delete immediately (clients need time to process the keyboard reset) + +If deletion fails (permissions, race condition), the bot must silently continue. + +What the bot must NOT do +The bot must NOT: + +• Store data +• Track users +• Use a database +• React to normal messages +• Remove inline keyboards +• Delete other bots’ messages +• Re-send keyboards +• Perform scheduled actions + +This is a stateless utility bot. + +EDGE CASES AND EXPECTED BEHAVIOR + +• If another bot sends a keyboard after this bot runs, that keyboard will appear again. This is expected. +• If a user does not receive the message (offline/network), their keyboard may persist until the next reset. +• This is the only technically correct way to reset keyboards in Telegram. + +IMPLEMENTATION CONSTRAINTS + +Update handling +Either polling or webhook is acceptable. + +Polling is preferred for simplicity. + +Libraries +Any official or widely used Telegram library is acceptable, such as: + +• python-telegram-bot +• aiogram +• Telegraf (Node.js) +• Raw Bot API (HTTP) + +No unofficial wrappers. + +Permissions required +The bot must be granted: + +• “Read messages” +• “Delete messages” (optional, only for cleanup) + +No admin rights beyond this are required. + +LOGGING AND ERROR HANDLING + +• Log startup success +• Log command usage with chat ID and user ID +• Log authorization failures at debug level +• Fail silently on Telegram API errors (do not spam the group) + +SUCCESS CRITERIA + +The bot is considered correct if: + +• An admin sends /resetkeyboard in a group +• All visible reply keyboards disappear for users in that group +• The bot does nothing else \ No newline at end of file diff --git a/bot.js b/bot.js new file mode 100644 index 0000000..ed570b7 --- /dev/null +++ b/bot.js @@ -0,0 +1,122 @@ +import { Telegraf } from 'telegraf'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const BOT_TOKEN = process.env.BOT_TOKEN; + +if (!BOT_TOKEN) { + console.error('Error: BOT_TOKEN environment variable is not set'); + process.exit(1); +} + +console.log('Keyboard Reset Bot initializing...'); + +const bot = new Telegraf(BOT_TOKEN); + +// Debug middleware - log all incoming updates +bot.use(async(ctx, next) => { + const chatType = ctx.chat ? ctx.chat.type : undefined; + const messageText = ctx.message ? ctx.message.text : undefined; + console.log('Update received:', { + type: ctx.updateType, + chat: chatType, + text: messageText + }); + return next(); +}); + +// Handle /start command (for private DM) +bot.command('start', async(ctx) => { + const chatType = ctx.chat.type; + + if (chatType === 'private') { + await ctx.reply( + 'Keyboard Reset Bot\n\n' + + 'This bot removes reply keyboard buttons in group chats.\n\n' + + 'How to use:\n' + + '1. Add this bot to a group\n' + + '2. Use /resetkeyboard in the group (admins only)\n\n' + + 'The bot will send a message that clears all reply keyboards for users in the group.' + ); + } else { + // In groups, just acknowledge briefly + await ctx.reply('Use /resetkeyboard to clear reply keyboards (admins only).'); + } +}); + +// Handle /resetkeyboard command +bot.command('resetkeyboard', async(ctx) => { + const chatId = ctx.chat.id; + const userId = ctx.from.id; + const chatType = ctx.chat.type; + + // Log command usage + console.log(`Command /resetkeyboard - Chat ID: ${chatId}, User ID: ${userId}, Chat Type: ${chatType}`); + + // Check if chat is a group or supergroup + if (chatType !== 'group' && chatType !== 'supergroup') { + await ctx.reply('This bot only works in groups.'); + return; + } + + try { + // Check if user is an administrator + const member = await ctx.getChatMember(userId); + const isAdmin = member.status === 'administrator' || member.status === 'creator'; + + if (!isAdmin) { + // Log authorization failure at debug level + console.debug(`Authorization failed - Chat ID: ${chatId}, User ID: ${userId}`); + await ctx.reply('Only group admins can use this command.'); + return; + } + + // Send keyboard reset message with ReplyKeyboardRemove + // selective: false ensures it removes keyboard for ALL users + const sentMessage = await ctx.reply('Keyboard reset.', { + reply_markup: { + remove_keyboard: true, + selective: false + } + }); + + // Log successful reset + console.log(`Keyboard reset successful - Chat ID: ${chatId}, User ID: ${userId}`); + + // Delete the confirmation message after 7 seconds + // This gives clients time to process the keyboard reset + setTimeout(async() => { + try { + await ctx.deleteMessage(sentMessage.message_id); + } catch (error) { + // Silently continue if deletion fails (permissions, race condition, etc.) + } + }, 7000); + + } catch (error) { + // Fail silently on Telegram API errors (do not spam the group) + console.error(`Error processing command - Chat ID: ${chatId}, User ID: ${userId}:`, error.message); + } +}); + +// Handle errors globally +bot.catch((err, ctx) => { + console.error('Bot error:', err.message); +}); + +// Start polling +console.log('Starting bot polling...'); +bot.launch() + .then(() => { + console.log('Keyboard Reset Bot started successfully!'); + console.log('Bot is now polling for updates...'); + }) + .catch((error) => { + console.error('Failed to start bot:', error.message); + process.exit(1); + }); + +// Enable graceful stop +process.once('SIGINT', () => bot.stop('SIGINT')); +process.once('SIGTERM', () => bot.stop('SIGTERM')); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a82186b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,201 @@ +{ + "name": "keyboard-reset-bot", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "keyboard-reset-bot", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "dotenv": "^16.3.1", + "telegraf": "^4.15.0" + } + }, + "node_modules/@telegraf/types": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz", + "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==", + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/p-timeout": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", + "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-compare": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", + "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==", + "license": "MIT", + "dependencies": { + "buffer-alloc": "^1.2.0" + } + }, + "node_modules/sandwich-stream": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", + "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/telegraf": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", + "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==", + "license": "MIT", + "dependencies": { + "@telegraf/types": "^7.1.0", + "abort-controller": "^3.0.0", + "debug": "^4.3.4", + "mri": "^1.2.0", + "node-fetch": "^2.7.0", + "p-timeout": "^4.1.0", + "safe-compare": "^1.1.4", + "sandwich-stream": "^2.0.2" + }, + "bin": { + "telegraf": "lib/cli.mjs" + }, + "engines": { + "node": "^12.20.0 || >=14.13.1" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..33614cc --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "keyboard-reset-bot", + "version": "1.0.0", + "description": "A minimal Telegram bot that removes reply keyboard buttons in group chats", + "main": "bot.js", + "type": "module", + "scripts": { + "start": "node bot.js" + }, + "keywords": [ + "telegram", + "bot", + "keyboard", + "reset" + ], + "author": "", + "license": "MIT", + "dependencies": { + "telegraf": "^4.15.0", + "dotenv": "^16.3.1" + } +} \ No newline at end of file