diff --git a/.env.sample b/.env.sample index cca9537..a8dd54b 100644 --- a/.env.sample +++ b/.env.sample @@ -66,6 +66,13 @@ TRONSCAN_API_KEY= TRONSCAN_WALLET_ADDRESS= +# ---- Optional: shutdown announcement --------------------------------------- +# Set SHUTDOWN_MODE=true to silence every handler and reply with the farewell +# in src/middlewares/shutdownMode.ts. Unset to restore the bot. + +SHUTDOWN_MODE= + + # ---- Optional: testing ----------------------------------------------------- # Telegram user ID used by some self-check probes. diff --git a/CONFIG.md b/CONFIG.md index 4ec8061..d0d2348 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -45,6 +45,12 @@ simply disable that data source. | `TRONSCAN_API_KEY` | [Tronscan](https://docs.tronscan.org/getting-started) API key. | | `TRONSCAN_WALLET_ADDRESS` | TRX wallet address; the bot polls it for incoming USDT-TRC20 payments and matches them to pending invoices. | +## Optional: shutdown announcement + +| Variable | Description | +|----------|-------------| +| `SHUTDOWN_MODE` | Set to `true` to silence every handler and reply with the farewell in `src/middlewares/shutdownMode.ts`. Unset to restore the bot. | + ## Optional: MongoDB tuning These knobs control connection retry / health-check behaviour. The built-in diff --git a/src/app.ts b/src/app.ts index a40df5b..d6c7d24 100644 --- a/src/app.ts +++ b/src/app.ts @@ -45,6 +45,7 @@ import { setupI18N } from './helpers/i18n' import { log } from './helpers/log' import { attachUser } from './middlewares/attachUser' import { checkTime } from './middlewares/checkTime' +import { shutdownMode } from './middlewares/shutdownMode' import { commonScenes } from './scenes' const Stage = require('telegraf/stage') const session = require('telegraf/session') @@ -71,6 +72,9 @@ const stage = new Stage([ ]) export const botInit = (bot: Telegraf) => { + // Kill switch: must run before session/stage so it can short-circuit every update. + bot.use(shutdownMode) + bot.use(session()) bot.use(stage.middleware()) diff --git a/src/middlewares/shutdownMode.test.ts b/src/middlewares/shutdownMode.test.ts new file mode 100644 index 0000000..4d65257 --- /dev/null +++ b/src/middlewares/shutdownMode.test.ts @@ -0,0 +1,33 @@ +import { SHUTDOWN_MESSAGE, shutdownMode } from '@/middlewares/shutdownMode' + +const makeCtx = () => + ({ + reply: jest.fn().mockResolvedValue(undefined), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any) + +describe('shutdownMode middleware', () => { + const originalEnv = process.env + + afterEach(() => { + process.env = { ...originalEnv } + }) + + it('passes through when SHUTDOWN_MODE is not "true"', async () => { + delete process.env.SHUTDOWN_MODE + const ctx = makeCtx() + const next = jest.fn() + await shutdownMode(ctx, next) + expect(next).toHaveBeenCalledTimes(1) + expect(ctx.reply).not.toHaveBeenCalled() + }) + + it('replies with the shutdown message when SHUTDOWN_MODE=true', async () => { + process.env.SHUTDOWN_MODE = 'true' + const ctx = makeCtx() + const next = jest.fn() + await shutdownMode(ctx, next) + expect(next).not.toHaveBeenCalled() + expect(ctx.reply).toHaveBeenCalledWith(SHUTDOWN_MESSAGE) + }) +}) diff --git a/src/middlewares/shutdownMode.ts b/src/middlewares/shutdownMode.ts new file mode 100644 index 0000000..57b8518 --- /dev/null +++ b/src/middlewares/shutdownMode.ts @@ -0,0 +1,25 @@ +import { Context } from 'telegraf' + +/* eslint-disable max-len */ +export const SHUTDOWN_MESSAGE = `привет. я сделал гуся пять лет назад — тогда нигде не было удобных алертов по ценам. начал для себя, потом подтянулись люди. + +решил, что пора закрывать. тянул так уже пару лет — жалко было пользователей. но дальше держать в анабиозе невыгодно никому: проект продолжает забирать деньги и фокус, а интереса и ресурса растить его у меня уже нет. + +это был мой первый проект с живой аудиторией. до сих пор отношусь к нему тепло — поэтому хочется закрыть честно, а не молча выключить однажды. + +если кому-то интересно взять бота себе и развивать — готов передать управление. не бесплатно. пишите @ashugaev. + +что дальше: +— алерты и команды я уже выключил, сейчас бот только показывает это сообщение +— через неделю остановлю совсем +— код открыт: https://github.com/ashugaev/GooseInvestAlertBot — поднимайте у себя, ai-агенты справятся с настройкой +— если у вас активный премиум — напишите, верну деньги + +спасибо что были с гусём 🖖` +/* eslint-enable max-len */ + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function shutdownMode(ctx: Context, next: () => any) { + if (process.env.SHUTDOWN_MODE !== 'true') return next() + await ctx.reply(SHUTDOWN_MESSAGE) +}