grammyjs / grammY

The Telegram Bot Framework.
https://grammy.dev
MIT License
2.37k stars 117 forks source link

How to make the middleware only work in some Composer. #633

Closed sqiao7 closed 2 months ago

sqiao7 commented 2 months ago

when i use the onlyAdmin middleware, i only want it work in some Composer, how to do that. Or do I need to filter message text in the middleware myself?

import { Composer, Keyboard } from 'grammy'
import moduleInfo from '../minfo.js'
import onlyAdmin  from '../../middleware/onlyAdmin.js'

const composer = new Composer()

composer.command(moduleInfo.tradeBill .cmd, async (ctx) => {
  // ...
})

composer.use(onlyAdmin)
export default composer
// mod.js
import { Composer } from 'grammy'
import start from './start/index.js'
import calculator from './calculator/index.js'
import usdtPrice from './usdt-price/index.js'
import tradeBill from './trade-bill/index.js'
import addressSearch from './address-search/index.js'

const composer = new Composer()

composer.use(tradeBill)  // <-- i use onlyAdmin in this mod,but it works in all modules
composer.use(start)
composer.use(calculator)

export default composer
import modules from '../module/mod.js'
const bot = new Bot(appConfig.telegram.botToken)

bot.use(modules)
import { Context, NextFunction } from 'grammy'

export const onlyAdmin =
  <T extends Context>(errorHandler?: (ctx: T) => unknown) =>
  async (ctx: T, next: NextFunction) => {
    // No chat = no service
    if (!ctx.chat) {
      return
    }
    // Channels and private chats are only postable by admins
    if (['channel', 'private'].includes(ctx.chat.type)) {
      return next()
    }
    // Anonymous users are always admins
    if (ctx.from?.username === 'GroupAnonymousBot') {
      return next()
    }
    // Surely not an admin
    if (!ctx.from?.id) {
      return
    }
    // Check the member status
    const chatMember = await ctx.getChatMember(ctx.from.id)
    if (['creator', 'administrator'].includes(chatMember.status)) {
      return next()
    }
    // Not an admin
    return errorHandler?.(ctx)
  }
KnorpelSenf commented 2 months ago

You need to filter down the updates before letting them reach onlyAdmin. Perhaps check out the two sections about middleware first. They explain thoroughly how updates flow through the handlers, and what you can do to influence this.

https://grammy.dev/guide/middleware https://grammy.dev/advanced/middleware

If you have any concrete question about the docs or how to apply them to your code, please don't hesitate to ask them.

sqiao7 commented 2 months ago

This means that I need to write a filter function,right?

import { Composer, Keyboard } from 'grammy'
import moduleInfo from '../minfo.js'
import onlyAdmin  from '../../middleware/onlyAdmin.js'

const composer = new Composer()

composer.command(moduleInfo.tradeBill .cmd, async (ctx) => {
  // ...
})

composer.filter(/* only this module filter func */).use(onlyAdmin)
export default composer
KnorpelSenf commented 2 months ago

This means that I need to write a filter function,right?

Yes

import { Composer, Keyboard } from 'grammy'
import moduleInfo from '../minfo.js'
import onlyAdmin  from '../../middleware/onlyAdmin.js'

const composer = new Composer()

composer.command(moduleInfo.tradeBill .cmd, async (ctx) => {
  // ...
})

composer.filter(/* only this module filter func */).use(onlyAdmin)
export default composer

This would mean that onlyAdmin is only executed when the filter function returns true. However, the command would always be run. That's probably not what you meant.

Instead, something like this is better:

const composer = new Composer()

const protected = composer.filter(/* only this module filter func */);
protected.use(onlyAdmin)
protected.command(moduleInfo.tradeBill .cmd, async (ctx) => {
  // ...
})

export default composer

That being said, why don't you refactor onlyAdmin in such a way that it can be passed to bot.filter? Instead of calling next after some checks, you could just return true or false, and then do

const protected = composer.filter(betterOnlyAdmin)
protected.command(..)
// …
export default composer
sqiao7 commented 2 months ago

Wow, this is a good idea. Maybe I have to ask whether this composer in Grammy is independent, like this. image

The reason for my problem is that UPDATE will be executed in order in the order of bot.use() or composer.use(). When I add a filter function with a module middleware , there will be no such problem, UPDATE after the complete walk

sqiao7 commented 2 months ago

I have now learned how to use filter correctly, thanks. hava a nice day!

KnorpelSenf commented 2 months ago

Great to hear!