Skip to content

Instantly share code, notes, and snippets.

@Gkiokan
Created June 29, 2024 21:51
Show Gist options
  • Save Gkiokan/b970a10100700ce79155044920bd9bab to your computer and use it in GitHub Desktop.
Save Gkiokan/b970a10100700ce79155044920bd9bab to your computer and use it in GitHub Desktop.
GrammyJS BotController (JS)
import store from '../store'
import log from '../log'
import { Bot, Context, session, lazySession } from "grammy"
import { sequentialize } from '@grammyjs/runner'
import MessageComposer from './MessageComposer'
import MenuComposer from './MenuComposer'
let BotController = {
bot: null,
isSetup: false,
setup: async() => {
log.info("Call Setup ")
await BotController.instance()
if( BotController.isSetup )
return BotController.bot
// Add a couple process Handlers
BotController.shutdownHandler()
// Setup here
// Session Key Definition
let getSessionKey = BotController.getSessionKey.chat_id
BotController.bot.use(sequentialize( getSessionKey ))
BotController.bot.use(session({
getSessionKey,
initial: () => {
}
}))
// Logic
BotController.bot.use( MenuComposer )
BotController.bot.use( MessageComposer )
log.info("Bot Setup done")
BotController.isSetup = true
return BotController.bot
},
async init(){
log.info("[init] >> Init BotController has been run")
await BotController.instance()
log.info("[init] << Return bot from init")
return BotController.bot
},
async instance(){
log.info("Get Bot Instance called")
if( BotController.bot ){
log.info("Bot Instance exists, return existing")
return BotController.bot
}
log.info("Create new Bot Instance")
log.info(`Token ${store.getToken()}`)
const bot = new Bot( store.getToken() );
BotController.bot = bot
return BotController.bot
},
getSessionKey: {
// Stores data per chat (default).
chat_id(ctx) {
// Let all users in a group chat share the same session,
// but give an independent private one to each user in private chats
return ctx.chat?.id.toString();
},
// Stores data per user.
from_id(ctx) {
// Give every user their personal session storage
// (will be shared across groups and in their private chat)
return ctx.from?.id.toString();
},
// Stores data per user-chat combination.
from_id_chat_id(ctx) {
// Give every user their one personal session storage per chat with the bot
// (an independent session for each group and their private chat)
return ctx.from === undefined || ctx.chat === undefined
? undefined
: `${ctx.from.id}/${ctx.chat.id}`;
},
},
shutdownHandler(){
log.info("Setup SIGINT and SIGTERM for bot");
process.once("SIGINT", async (e) => {
log.error("Got SIGINT. Stopping bot with await\n", e)
BotController.bot.stop();
})
process.once("SIGTERM", async (e) => {
log.error("Got SIGTERM. Stopping bot with await\n", e)
BotController.bot.stop();
})
process.on("unhandledRejection", function(e) {
console.log(e)
console.log("Trace", e.stack)
log.error("[unhandledRejection]\n", e)
});
},
}
export default BotController
@Gkiokan
Copy link
Author

Gkiokan commented Jun 29, 2024

Updated TS version

import store from '../store';
import log from '../log';

import { Bot, Context, session, SessionFlavor } from "grammy";
import { sequentialize } from '@grammyjs/runner';

import MessageComposer from './MessageComposer';
import MenuComposer from './MenuComposer';

type BotControllerType = {  
  isSetup: boolean;
  setup: () => Promise<Bot<BotContext>>;
  init: () => Promise<Bot<BotContext>>;
  instance: () => Promise<Bot<BotContext>>;
  getSessionKey: {
    chat_id: (ctx: Context) => string | undefined;
    from_id: (ctx: Context) => string | undefined;
    from_id_chat_id: (ctx: Context) => string | undefined;
  };
  shutdownHandler: () => void;
};

type BotContext = Context & SessionFlavor<any>;

let bot: Bot<BotContext>

const BotController: BotControllerType = {
    isSetup: false,

    setup: async () => {
        log.info("Call Setup ");
        await BotController.instance();

        if (BotController.isSetup)
        return bot;

        // Add a couple process Handlers 
        BotController.shutdownHandler();

        // Session Key Definition 
        let getSessionKey = BotController.getSessionKey.chat_id;

        bot.use(sequentialize(getSessionKey));
        bot.use(session({
        getSessionKey,
        initial: () => ({})
        }));

        // Logic
        bot.use(MenuComposer);
        bot.use(MessageComposer);

        log.info("Bot Setup done");

        BotController.isSetup = true;

        return bot;
    },

    init: async () => {
        log.info("[init] >> Init BotController has been run");
        await BotController.instance();
        log.info("[init] << Return bot from init");
        return bot;
    },

    instance: async () => {
        log.info("Get Bot Instance called");

        if (bot) {
        log.info("Bot Instance exists, return existing");
        return bot;
        }

        log.info("Create new Bot Instance");
        log.info(`Token ${store.getToken()}`);

        bot = new Bot<BotContext>(store.getToken()!);

        return bot;
    },

    getSessionKey: {
        // Stores data per chat (default).
        chat_id(ctx: Context) {
        // Let all users in a group chat share the same session,
        // but give an independent private one to each user in private chats
        return ctx.chat?.id.toString();
        },

        // Stores data per user.
        from_id(ctx: Context) {
        // Give every user their personal session storage
        // (will be shared across groups and in their private chat)
        return ctx.from?.id.toString();
        },

        // Stores data per user-chat combination.
        from_id_chat_id(ctx: Context) {
        // Give every user their one personal session storage per chat with the bot
        // (an independent session for each group and their private chat)
        return ctx.from === undefined || ctx.chat === undefined
            ? undefined
            : `${ctx.from.id}/${ctx.chat.id}`;
        },
    },

    shutdownHandler() {
        log.info("Setup SIGINT and SIGTERM for bot");

        process.once("SIGINT", async (e) => {
        console.error("Got SIGINT. Stopping bot with await\n", e)
        bot.stop();
        });

        process.once("SIGTERM", async (e) => {
        console.error("Got SIGTERM. Stopping bot with await\n", e)
        bot.stop();
        });

        process.on("unhandledRejection", function (e: any) {
        console.log(e);
        console.log("Trace", e.stack);
        console.error("[unhandledRejection]\n", e);
        });
    },
};

export { bot }
export default BotController;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment