From 3f586d1d9f7a307a46a1b464a361859bf80fe1cf Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sat, 9 May 2026 22:35:38 +0700 Subject: [PATCH] refactor: dispatcher pre-parses args, drop arg helpers + info.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dispatcher now extracts both the command name and its arguments once and passes the args array to handlers via a 3rd parameter. Each command file drops its splitArgs(getCommandArguments(msg.text)) call and the corresponding imports. command-utils.js loses the now-unused arg helpers (only auth helpers remain). info.js — a 12-line file with one sendMessage — folds into bot.js's commands map. --- src/bot/bot.js | 11 +++++---- src/bot/commands/add-apple-app.js | 7 +++--- src/bot/commands/add-google-app.js | 7 +++--- src/bot/commands/add-group.js | 7 +++--- src/bot/commands/check-app-scores.js | 9 ++++---- src/bot/commands/check-app.js | 8 +++---- src/bot/commands/command-utils.js | 14 ------------ src/bot/commands/delete-apple-app.js | 7 +++--- src/bot/commands/delete-google-app.js | 7 +++--- src/bot/commands/delete-group.js | 7 +++--- src/bot/commands/info.js | 12 ---------- src/bot/commands/list-app.js | 8 +++---- src/bot/commands/list-group.js | 8 +++---- src/bot/commands/raw-apple-app.js | 7 ++---- src/bot/commands/raw-google-app.js | 6 ++--- src/bot/dispatch.js | 33 +++++++++++++++++---------- 16 files changed, 66 insertions(+), 92 deletions(-) delete mode 100644 src/bot/commands/info.js diff --git a/src/bot/bot.js b/src/bot/bot.js index 8ce99f4..9bee0f8 100644 --- a/src/bot/bot.js +++ b/src/bot/bot.js @@ -1,5 +1,4 @@ import { createTelegramApi } from './telegram-api.js'; -import { createInfoCommand } from './commands/info.js'; import { createAddGroupCommand } from './commands/add-group.js'; import { createDeleteGroupCommand } from './commands/delete-group.js'; import { createListGroupCommand } from './commands/list-group.js'; @@ -13,7 +12,6 @@ import { createCheckAppScoresCommand } from './commands/check-app-scores.js'; import { createRawAppleAppCommand } from './commands/raw-apple-app.js'; import { createRawGoogleAppCommand } from './commands/raw-google-app.js'; -// HTML parse mode for all messages (Java parity). const PARSE_MODE = 'HTML'; export function createBot(config, store, appleScraper, googleScraper) { @@ -51,9 +49,14 @@ export function createBot(config, store, appleScraper, googleScraper) { }, }; - // Java command identifiers — keep names matching exactly. const commands = { - info: createInfoCommand(), + info: async (msg, sender, args) => { + if (args.length !== 0) { + await sender.sendMessage(msg.chat.id, 'Invalid arguments'); + return; + } + await sender.sendMessage(msg.chat.id, `Id của nhóm là ${msg.chat.id}\n`); + }, addgroup: createAddGroupCommand(config, store), delgroup: createDeleteGroupCommand(config, store), listgroup: createListGroupCommand(config, store), diff --git a/src/bot/commands/add-apple-app.js b/src/bot/commands/add-apple-app.js index dc2fe93..7d649de 100644 --- a/src/bot/commands/add-apple-app.js +++ b/src/bot/commands/add-apple-app.js @@ -1,11 +1,10 @@ import { buildAppleRequestByBundleId, buildAppleRequestByTrackId } from '../../api/apple-scraper.js'; -import { authorizeGroup, getCommandArguments, splitArgs } from './command-utils.js'; +import { authorizeGroup } from './command-utils.js'; -// /addapple [country=vn] — Java AddAppleAppCommand. +// /addapple [country=vn] export function createAddAppleAppCommand(store, appleScraper) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await authorizeGroup(msg.chat.id, store, sender))) return; - const args = splitArgs(getCommandArguments(msg.text)); if (args.length < 1 || args.length > 2) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/commands/add-google-app.js b/src/bot/commands/add-google-app.js index 29b2fec..b9bcc52 100644 --- a/src/bot/commands/add-google-app.js +++ b/src/bot/commands/add-google-app.js @@ -1,11 +1,10 @@ import { buildGoogleRequest } from '../../api/google-scraper.js'; -import { authorizeGroup, getCommandArguments, splitArgs } from './command-utils.js'; +import { authorizeGroup } from './command-utils.js'; -// /addgoogle [country=vn] — Java AddGoogleAppCommand. +// /addgoogle [country=vn] export function createAddGoogleAppCommand(store, googleScraper) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await authorizeGroup(msg.chat.id, store, sender))) return; - const args = splitArgs(getCommandArguments(msg.text)); if (args.length < 1 || args.length > 2) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/commands/add-group.js b/src/bot/commands/add-group.js index 34e0513..e692a77 100644 --- a/src/bot/commands/add-group.js +++ b/src/bot/commands/add-group.js @@ -1,10 +1,9 @@ -import { getCommandArguments, requireAdminUser, splitArgs } from './command-utils.js'; +import { requireAdminUser } from './command-utils.js'; -// /addgroup [groupId] — Java AddGroupCommand. Admin-only. +// /addgroup [groupId] — admin-only. Defaults to current chat. export function createAddGroupCommand(config, store) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await requireAdminUser(msg.from.id, msg.chat.id, config, sender))) return; - const args = splitArgs(getCommandArguments(msg.text)); if (args.length > 1) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/commands/check-app-scores.js b/src/bot/commands/check-app-scores.js index 56acd37..9162d9e 100644 --- a/src/bot/commands/check-app-scores.js +++ b/src/bot/commands/check-app-scores.js @@ -1,12 +1,11 @@ import { buildTable } from '../../util/table.js'; -import { authorizeGroup, getCommandArguments, splitArgs } from './command-utils.js'; +import { authorizeGroup } from './command-utils.js'; -// /checkappscore — Java CheckAppScoreCommand. Reports score + ratings. -// Score rounded to 1 decimal (Java Precision.round(score, 1) parity). +// /checkappscore — reports score + ratings. Score rounded to 1 decimal. export function createCheckAppScoresCommand(store, appleScraper, googleScraper) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await authorizeGroup(msg.chat.id, store, sender))) return; - if (splitArgs(getCommandArguments(msg.text)).length !== 0) { + if (args.length !== 0) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; } diff --git a/src/bot/commands/check-app.js b/src/bot/commands/check-app.js index c4fc550..fdb6024 100644 --- a/src/bot/commands/check-app.js +++ b/src/bot/commands/check-app.js @@ -1,12 +1,12 @@ import { buildTable } from '../../util/table.js'; import { daysBetween, formatDateInTz } from '../../util/time.js'; -import { authorizeGroup, getCommandArguments, splitArgs } from './command-utils.js'; +import { authorizeGroup } from './command-utils.js'; -// /checkapp — Java CheckAppCommand. Reports update status per app, per store. +// /checkapp — reports update status per app, per store. export function createCheckAppCommand(config, store, appleScraper, googleScraper) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await authorizeGroup(msg.chat.id, store, sender))) return; - if (splitArgs(getCommandArguments(msg.text)).length !== 0) { + if (args.length !== 0) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; } diff --git a/src/bot/commands/command-utils.js b/src/bot/commands/command-utils.js index 223a39b..63ba5fb 100644 --- a/src/bot/commands/command-utils.js +++ b/src/bot/commands/command-utils.js @@ -1,17 +1,3 @@ -export function splitArgs(text) { - if (!text) return []; - return text.trim().split(/\s+/).filter((s) => s.length > 0); -} - -// Strips the "/" or "/@botname" prefix from message.text. -export function getCommandArguments(text) { - if (!text) return ''; - const trimmed = text.trim(); - const space = trimmed.indexOf(' '); - if (space < 0) return ''; - return trimmed.slice(space + 1).trim(); -} - export async function authorizeGroup(chatId, store, sender) { try { const ok = await store.admin.hasGroup(chatId); diff --git a/src/bot/commands/delete-apple-app.js b/src/bot/commands/delete-apple-app.js index e970db3..37c8262 100644 --- a/src/bot/commands/delete-apple-app.js +++ b/src/bot/commands/delete-apple-app.js @@ -1,10 +1,9 @@ -import { authorizeGroup, getCommandArguments, splitArgs } from './command-utils.js'; +import { authorizeGroup } from './command-utils.js'; -// /delapple — Java DeleteAppleAppCommand. +// /delapple export function createDeleteAppleAppCommand(store) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await authorizeGroup(msg.chat.id, store, sender))) return; - const args = splitArgs(getCommandArguments(msg.text)); if (args.length !== 1) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/commands/delete-google-app.js b/src/bot/commands/delete-google-app.js index f9ecc1a..32537fa 100644 --- a/src/bot/commands/delete-google-app.js +++ b/src/bot/commands/delete-google-app.js @@ -1,10 +1,9 @@ -import { authorizeGroup, getCommandArguments, splitArgs } from './command-utils.js'; +import { authorizeGroup } from './command-utils.js'; -// /delgoogle — Java DeleteGoogleAppCommand. +// /delgoogle export function createDeleteGoogleAppCommand(store) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await authorizeGroup(msg.chat.id, store, sender))) return; - const args = splitArgs(getCommandArguments(msg.text)); if (args.length !== 1) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/commands/delete-group.js b/src/bot/commands/delete-group.js index 4778550..7e0bfb0 100644 --- a/src/bot/commands/delete-group.js +++ b/src/bot/commands/delete-group.js @@ -1,10 +1,9 @@ -import { getCommandArguments, requireAdminUser, splitArgs } from './command-utils.js'; +import { requireAdminUser } from './command-utils.js'; -// /delgroup [groupId] — Java DeleteGroupCommand. Admin-only. +// /delgroup [groupId] — admin-only. export function createDeleteGroupCommand(config, store) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await requireAdminUser(msg.from.id, msg.chat.id, config, sender))) return; - const args = splitArgs(getCommandArguments(msg.text)); if (args.length > 1) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/commands/info.js b/src/bot/commands/info.js deleted file mode 100644 index 0caa790..0000000 --- a/src/bot/commands/info.js +++ /dev/null @@ -1,12 +0,0 @@ -import { getCommandArguments, splitArgs } from './command-utils.js'; - -// /info — Java InfoCommand. Reports the chat (group) ID. -export function createInfoCommand() { - return async (msg, sender) => { - if (splitArgs(getCommandArguments(msg.text)).length !== 0) { - await sender.sendMessage(msg.chat.id, 'Invalid arguments'); - return; - } - await sender.sendMessage(msg.chat.id, `Id của nhóm là ${msg.chat.id}\n`); - }; -} diff --git a/src/bot/commands/list-app.js b/src/bot/commands/list-app.js index 4119307..40e5671 100644 --- a/src/bot/commands/list-app.js +++ b/src/bot/commands/list-app.js @@ -1,11 +1,11 @@ import { buildTable } from '../../util/table.js'; -import { authorizeGroup, getCommandArguments, splitArgs } from './command-utils.js'; +import { authorizeGroup } from './command-utils.js'; -// /listapp — Java ListAppCommand. Two tables (Apple / Google) of tracked apps. +// /listapp — two tables (Apple / Google) of tracked apps. export function createListAppCommand(store) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await authorizeGroup(msg.chat.id, store, sender))) return; - if (splitArgs(getCommandArguments(msg.text)).length !== 0) { + if (args.length !== 0) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; } diff --git a/src/bot/commands/list-group.js b/src/bot/commands/list-group.js index af70d72..47e134f 100644 --- a/src/bot/commands/list-group.js +++ b/src/bot/commands/list-group.js @@ -1,10 +1,10 @@ -import { getCommandArguments, requireAdminUser, splitArgs } from './command-utils.js'; +import { requireAdminUser } from './command-utils.js'; -// /listgroup — Java ListGroupCommand. Admin-only. +// /listgroup — admin-only. export function createListGroupCommand(config, store) { - return async (msg, sender) => { + return async (msg, sender, args) => { if (!(await requireAdminUser(msg.from.id, msg.chat.id, config, sender))) return; - if (splitArgs(getCommandArguments(msg.text)).length !== 0) { + if (args.length !== 0) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; } diff --git a/src/bot/commands/raw-apple-app.js b/src/bot/commands/raw-apple-app.js index 2a2342a..82a62f0 100644 --- a/src/bot/commands/raw-apple-app.js +++ b/src/bot/commands/raw-apple-app.js @@ -1,11 +1,8 @@ import { buildAppleRequestByBundleId, buildAppleRequestByTrackId } from '../../api/apple-scraper.js'; -import { getCommandArguments, splitArgs } from './command-utils.js'; -// /rawappleapp [country=vn] — Java RawAppleAppCommand. -// Sends raw upstream JSON as a Telegram document attachment. +// /rawappleapp [country=vn] — sends raw upstream JSON as a document. export function createRawAppleAppCommand(appleScraper) { - return async (msg, sender) => { - const args = splitArgs(getCommandArguments(msg.text)); + return async (msg, sender, args) => { if (args.length < 1 || args.length > 2) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/commands/raw-google-app.js b/src/bot/commands/raw-google-app.js index 73dd2c3..f3c3ddd 100644 --- a/src/bot/commands/raw-google-app.js +++ b/src/bot/commands/raw-google-app.js @@ -1,10 +1,8 @@ import { buildGoogleRequest } from '../../api/google-scraper.js'; -import { getCommandArguments, splitArgs } from './command-utils.js'; -// /rawgoogleapp [country=vn] — Java RawGoogleAppCommand. +// /rawgoogleapp [country=vn] export function createRawGoogleAppCommand(googleScraper) { - return async (msg, sender) => { - const args = splitArgs(getCommandArguments(msg.text)); + return async (msg, sender, args) => { if (args.length < 1 || args.length > 2) { await sender.sendMessage(msg.chat.id, 'Invalid arguments'); return; diff --git a/src/bot/dispatch.js b/src/bot/dispatch.js index c3a809d..e5dcc52 100644 --- a/src/bot/dispatch.js +++ b/src/bot/dispatch.js @@ -4,8 +4,9 @@ export async function dispatch(message, deps) { const { sender, commands, config, logger } = deps; if (!message?.text || message.text[0] !== '/') return; - const name = parseCommandName(message.text, config.telegramBotUsername); - if (!name) return; + const parsed = parseCommand(message.text, config.telegramBotUsername); + if (!parsed) return; + const { name, args } = parsed; const handler = commands[name]; if (!handler) { @@ -18,7 +19,7 @@ export async function dispatch(message, deps) { 'Executing command', ); try { - await handler(message, sender); + await handler(message, sender, args); } catch (err) { logger.error({ err: err.message, command: name }, 'command failed'); try { @@ -29,14 +30,22 @@ export async function dispatch(message, deps) { } } -// Extracts "info" from "/info", "/info arg", "/info@bot", "/info@bot arg". -function parseCommandName(text, botUsername) { - const space = text.indexOf(' '); - const head = space < 0 ? text.slice(1) : text.slice(1, space); +// Extracts name + args from "/cmd", "/cmd arg", "/cmd@bot", "/cmd@bot arg". +// Returns null if the @bot suffix targets a different bot. +function parseCommand(text, botUsername) { + const trimmed = text.trim(); + const space = trimmed.indexOf(' '); + const head = space < 0 ? trimmed.slice(1) : trimmed.slice(1, space); + const argText = space < 0 ? '' : trimmed.slice(space + 1).trim(); + const at = head.indexOf('@'); - if (at < 0) return head; - const cmd = head.slice(0, at); - const target = head.slice(at + 1); - if (botUsername && target && target.toLowerCase() !== botUsername.toLowerCase()) return null; - return cmd; + let name = head; + if (at >= 0) { + name = head.slice(0, at); + const target = head.slice(at + 1); + if (botUsername && target && target.toLowerCase() !== botUsername.toLowerCase()) return null; + } + + const args = argText ? argText.split(/\s+/) : []; + return { name, args }; }