Using ChatGPT for automating code generation in a JS project

In my previous articles on developing Telegram bots, I shared my experience of creating basic functionality and working with commands through arguments. While preparing for the third article, I faced the task of generating repetitive code for new commands, which turned into a routine task. To avoid this and speed up the process, I decided to use ChatGPT. To my surprise, its help turned out to be significantly more valuable than I expected.

In short, using ChatGPT for automating code generation is effective when working with templates. Generating repetitive fragments based on templates speeds up the development process and reduces the number of routine tasks. The more precise the structure and names in the templates, the easier it is for the model to correctly perform replacements,

For those who want to get more familiar with the process of human and ChatGPT interaction when creating a JavaScript project - welcome under the cut.

Introduction to the context

I develop Telegram bots on the Node.js platform using the grammY library and my own TeqFW toolkit. The main architecture of my framework is based on the use of an object container, which allows for late binding of dependencies during code execution through class constructors, making it easier to scale and maintain applications.

In my npm package flancer32/teq-telegram-bot, basic functionality for working with telegram bots is implemented, such as starting and stopping the bot, as well as connecting dependencies (grammY, teqfw/di, and others). In addition, there is a GitHub template flancer32/repo-tmpl-tg-bot, which allows you to quickly create new projects by setting the basic structure of directories and files of a Node.js application.

The principles of modern software development involve decomposing a program into logical units (packages, modules, files, classes, objects) and their subsequent integration at the compilation or code execution stage. In this regard, it is important to consider three key aspects:

  • Formalization of the structure of directories and files for simplified codebase management.

  • Naming rules that facilitate project navigation.

  • Templating of similar code fragments.

As for using ChatGPT, I work with the paid version, which provides me with advanced features for more accurate code generation and simplifies interaction within automation tasks.

Task Setting

In my third article (still in progress), I decided to consider working with dialogues using the grammy/conversations library. To make the demo project more interesting and closer to real tasks, I chose a scenario of booking a client for a service (e.g., manicure, haircut, or tire replacement). The project could be gradually complicated by adding aspects such as working with sessions, notifications, and other elements of bot development, which could become the topic for subsequent articles.

So, here it is:

task description in natural language

Formalize the technical specification and estimate the development time of a Telegram bot as a nodejs application using the grammY library. Here is the description in natural language.

Upon the /start command, the user selects one of the roles - Client or Vendor (provider of commercial services). If the user is already registered, they can change the current role to another.

If the user is a Vendor, they can use commands to manipulate services such as /service_create, /service_read, /service_update, /service_delete, /service_list.

If the user is a Client, they can use commands such as /service_list, /service_read, /visit_service (registering a visit to a service), /visit_list (viewing their visits), /visit_delete (cancelling a service visit)

Additionally, the Vendor has access to commands that process visits: /visit_list (viewing all visits to all their services), /visit_queue (viewing Client requests to visit services), /visit_approve (approving a Client's visit), /visit_decline (declining a Client's visit).

To simplify the functionality, we assume that all services operate from 10:00 to 18:00 on all working days of the week (Monday to Friday).

Save the text as markdown, using triple backticks for all code blocks, in a format available for download to the local disk.

result ChatGPT in format markdown

# Technical Specification for Developing a Telegram Bot

## Description

The bot allows users to choose one of the roles: Client or Vendor (provider of commercial services). The functionality of the bot depends on the selected role.

### Main Commands

#### General Commands:

- `/start` — choose a role: Client or Vendor. 
  If the user is already registered, they can switch the current role to another.

#### Commands for Vendor:

- `/service_create` — create a new service.
- `/service_read` — view information about a specific service.
- `/service_update` — update service information.
- `/service_delete` — delete a service.
- `/service_list` — view a list of all their services.
- `/visit_list` — view all visits to all their services.
- `/visit_queue` — view Client requests for service visits.
- `/visit_approve` — approve a Client's visit.
- `/visit_decline` — decline a Client's visit.

#### Commands for Client:

- `/service_list` — view a list of all services.
- `/service_read` — view information about a specific service.
- `/visit_service` — register for a service visit.
- `/visit_list` — view their visits.
- `/visit_delete` — cancel a service visit.

### Additional Requirements

- Working hours of all services: from 10:00 to 18:00 from Monday to Friday.
- Access to information and actions depends on the selected role.

## Development Time Estimate

1. Project setup and basic architecture — 4 hours.
2. User role registration and switching — 6 hours.
3. Implementation of Vendor commands — 12 hours.
4. Implementation of Client commands — 10 hours.
5. Processing visits for Vendors (approval, decline) — 8 hours.
6. Testing and debugging — 6 hours.
7. Documentation — 4 hours.

Total: ~50 hours.

Code Generation

For this article, I created a separate repository from the flancer32/repo-tmpl-tg-bot template - flancer64/tg-demo-ai-generate, which is used for pure code generation and development of examples mentioned in the article. I replaced the template namespace Ns_App with CodeGen (operation Replace in Files in the IDE).

Command Encoder

In short, the technical specification resulted in 11 additional commands. In my template repository, there is a command encoder with three standard commands:

const CodeGen_Back_Enum_Bot_Command = {
    HELP: 'help',
    SETTINGS: 'settings',
    START: 'start',
};

I was too lazy to copy-paste the command names from the technical specification, and since LLMs manipulate text perfectly, I gave this code fragment and simply asked ChatGPT to add the resulting commands to the code:

Here is the code template with the bot command encoder:

const CodeGen_Back_Enum_Bot_Command = {
    HELP: 'help',
    SETTINGS: 'settings',
    START: 'start',
};

Add all the new bot commands from the resulting technical specification to this encoder. The Client and Vendor can use the same commands. Output the result directly in the chat.

ChatGPT suggested I download the following fragment:

const CodeGen_Back_Enum_Bot_Command = {
    HELP: 'help',
    SETTINGS: 'settings',
    START: 'start',
    SERVICE_CREATE: 'service_create',
    SERVICE_READ: 'service_read',
    SERVICE_UPDATE: 'service_update',
    SERVICE_DELETE: 'service_delete',
    SERVICE_LIST: 'service_list',
    VISIT_LIST: 'visit_list',
    VISIT_QUEUE: 'visit_queue',
    VISIT_APPROVE: 'visit_approve',
    VISIT_DECLINE: 'visit_decline',
    VISIT_SERVICE: 'visit_service',
    VISIT_DELETE: 'visit_delete',
};

I just had to copy-paste it in place. Once instead of a dozen times, as if I were doing it line by line from the technical specification.

Command Handlers

If ChatGPT can generate a file based on a template, then maybe it can generate many similar files?

As the handler for the /start command, I have the following code, which is located in the file ./src/Back/Bot/Cmd/Start.js:

/**
 * The handler for the 'start' command.
 */
export default class CodeGen_Back_Bot_Cmd_Start {
    constructor() {
        return (ctx) => {
            ctx.reply('Welcome! You can start using the bot.');
        };
    }
}

Here is the prompt that asks ChatGPT to create a set of files according to the template:

Here is the template for implementing a separate command:

/**
 * The handler for the 'start' command.
 */
export default class CodeGen_Back_Bot_Cmd_Start {
    constructor() {
        return (ctx) => {
            ctx.reply('Welcome! You can start using the bot.');
        };
    }
}

This code is located in the file ./src/Back/Bot/Cmd/Start.js

Create similar files with JS code for all new commands. The namespace (${NS}) for all classes is CodeGen_Back_Bot_Cmd. It corresponds to the directory ./src/Back/Bot/Cmd/. The class for the /service_create command should be named ${NS}_Service_Create and should be located at ./src/Back/Bot/Cmd/Service/Create.js

Archive all files into one archive, available for download to the local disk.

As a result, ChatGPT creates an archive with the source files, where the necessary classes are located in the necessary files, and the code contains the necessary comments and messages:

This is the content of a separate file:


/**
 * The handler for the 'service_create' command.
 */
export default class CodeGen_Back_Bot_Cmd_Service_Create {
    constructor() {
        return (ctx) => {
            ctx.reply('Executing service_create command...');
        };
    }
}

All that remains is to unpack this archive into your project and put the files under version control (git).

Adding commands to help

Prompt for ChatGPT:

Here is a template for the /help command handler:

export default class CodeGen_Back_Bot_Cmd_Help {
    /**
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        return async (ctx) => {
            // https://core.telegram.org/bots/api#sendmessage
            await ctx.reply(
                `This is a test bot for demo.

Available commands are:

/${CMD.HELP} - display this text. 
/${CMD.SETTINGS} - configure this bot. 
/${CMD.START} - start the bot.`,
                {parse_mode: 'HTML',}
            );
        };
    }
}

Add all new bot commands from the resulting terms of reference to the handler. The explanations for the commands should be in English. Output the result directly to the chat.

Here is the result:

export default class CodeGen_Back_Bot_Cmd_Help {
    /**
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        return async (ctx) => {
            // https://core.telegram.org/bots/api#sendmessage
            await ctx.reply(
                `This is a test bot for demo.

Available commands are:

/${CMD.HELP} - display this text. 
/${CMD.SETTINGS} - configure this bot. 
/${CMD.START} - start the bot. 
/${CMD.SERVICE_CREATE} - create a new service (Vendor only). 
/${CMD.SERVICE_READ} - view details of a service. 
/${CMD.SERVICE_UPDATE} - update an existing service (Vendor only). 
/${CMD.SERVICE_DELETE} - delete a service (Vendor only). 
/${CMD.SERVICE_LIST} - list all available services. 
/${CMD.VISIT_SERVICE} - register for a service (Client only). 
/${CMD.VISIT_LIST} - view your visit history (Client only). 
/${CMD.VISIT_QUEUE} - view visit requests (Vendor only). 
/${CMD.VISIT_APPROVE} - approve a client's visit (Vendor only). 
/${CMD.VISIT_DECLINE} - decline a client's visit (Vendor only). 
/${CMD.VISIT_DELETE} - cancel your visit (Client only).`,
                {parse_mode: 'HTML',}
            );
        };
    }
}

Модификация в нескольких точках

Класс CodeGen_Back_Bot_Setup имплементирует интерфейс Telegram_Bot_Back_Api_Setup из базового пакета flancer32/teq-telegram-bot и позволяет встраивать код нашего бота в код базового пакета (запускающий приожение в CLI и в виде web-сервера). В этот раз ChatGPT должен внедрить код для новых команд в трёх местах:

  • в конструкторе в виде зависимости

  • в методе commands, который добавляет команды в меню

  • в методе handlers, который добавляет обработчики событий

Вот промпт для ChatGPT:

Вот код для подключения команд к боту:

export default class CodeGen_Back_Bot_Setup {
    /**
     * @param {TeqFw_Core_Shared_Api_Logger} logger -  instance
     * @param {CodeGen_Back_Bot_Cmd_Help} cmdHelp
     * @param {CodeGen_Back_Bot_Cmd_Settings} cmdSettings
     * @param {CodeGen_Back_Bot_Cmd_Start} cmdStart
     * @param {CodeGen_Back_Bot_Filter_Message} filterMessage
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            TeqFw_Core_Shared_Api_Logger$$: logger,
            CodeGen_Back_Bot_Cmd_Help$: cmdHelp,
            CodeGen_Back_Bot_Cmd_Settings$: cmdSettings,
            CodeGen_Back_Bot_Cmd_Start$: cmdStart,
            CodeGen_Back_Bot_Filter_Message$: filterMessage,
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        // INSTANCE METHODS
        this.commands = async function (bot) {
            bot.api.setMyCommands([
                {command: CMD.HELP, description: 'Get help.'},
                {command: CMD.SETTINGS, description: 'Configure bot settings.'},
                {command: CMD.START, description: 'Start using the bot.'},
            ]);
            logger.info(`A total of ${Object.keys(CMD).length} commands have been set for the bot.`);
            return bot;
        };

        this.handlers = function (bot) {
            bot.command(CMD.HELP, cmdHelp);
            bot.command(CMD.SETTINGS, cmdSettings);
            bot.command(CMD.START, cmdStart);
            bot.on('message', filterMessage);
            return bot;
        };
    }
}
Result in spoiler
export default class CodeGen_Back_Bot_Setup {
    /**
     * @param {TeqFw_Core_Shared_Api_Logger} logger -  instance
     * @param {CodeGen_Back_Bot_Cmd_Help} cmdHelp
     * @param {CodeGen_Back_Bot_Cmd_Settings} cmdSettings
     * @param {CodeGen_Back_Bot_Cmd_Start} cmdStart
     * @param {CodeGen_Back_Bot_Cmd_Service_Create} cmdServiceCreate
     * @param {CodeGen_Back_Bot_Cmd_Service_Read} cmdServiceRead
     * @param {CodeGen_Back_Bot_Cmd_Service_Update} cmdServiceUpdate
     * @param {CodeGen_Back_Bot_Cmd_Service_Delete} cmdServiceDelete
     * @param {CodeGen_Back_Bot_Cmd_Service_List} cmdServiceList
     * @param {CodeGen_Back_Bot_Cmd_Visit_Service} cmdVisitService
     * @param {CodeGen_Back_Bot_Cmd_Visit_List} cmdVisitList
     * @param {CodeGen_Back_Bot_Cmd_Visit_Queue} cmdVisitQueue
     * @param {CodeGen_Back_Bot_Cmd_Visit_Approve} cmdVisitApprove
     * @param {CodeGen_Back_Bot_Cmd_Visit_Decline} cmdVisitDecline
     * @param {CodeGen_Back_Bot_Cmd_Visit_Delete} cmdVisitDelete
     * @param {CodeGen_Back_Bot_Filter_Message} filterMessage
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            TeqFw_Core_Shared_Api_Logger$$: logger,
            CodeGen_Back_Bot_Cmd_Help$: cmdHelp,
            CodeGen_Back_Bot_Cmd_Settings$: cmdSettings,
            CodeGen_Back_Bot_Cmd_Start$: cmdStart,
            CodeGen_Back_Bot_Cmd_Service_Create$: cmdServiceCreate,
            CodeGen_Back_Bot_Cmd_Service_Read$: cmdServiceRead,
            CodeGen_Back_Bot_Cmd_Service_Update$: cmdServiceUpdate,
            CodeGen_Back_Bot_Cmd_Service_Delete$: cmdServiceDelete,
            CodeGen_Back_Bot_Cmd_Service_List$: cmdServiceList,
            CodeGen_Back_Bot_Cmd_Visit_Service$: cmdVisitService,
            CodeGen_Back_Bot_Cmd_Visit_List$: cmdVisitList,
            CodeGen_Back_Bot_Cmd_Visit_Queue$: cmdVisitQueue,
            CodeGen_Back_Bot_Cmd_Visit_Approve$: cmdVisitApprove,
            CodeGen_Back_Bot_Cmd_Visit_Decline$: cmdVisitDecline,
            CodeGen_Back_Bot_Cmd_Visit_Delete$: cmdVisitDelete,
            CodeGen_Back_Bot_Filter_Message$: filterMessage,
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        // INSTANCE METHODS
        this.commands = async function (bot) {
            bot.api.setMyCommands([
                {command: CMD.HELP, description: 'Get help.'},
                {command: CMD.SETTINGS, description: 'Configure bot settings.'},
                {command: CMD.START, description: 'Start using the bot.'},
                {command: CMD.SERVICE_CREATE, description: 'Create a new service (Vendor only).'},
                {command: CMD.SERVICE_READ, description: 'View details of a service.'},
                {command: CMD.SERVICE_UPDATE, description: 'Update an existing service (Vendor only).'},
                {command: CMD.SERVICE_DELETE, description: 'Delete a service (Vendor only).'},
                {command: CMD.SERVICE_LIST, description: 'List all available services.'},
                {command: CMD.VISIT_SERVICE, description: 'Register for a service (Client only).'},
                {command: CMD.VISIT_LIST, description: 'View your visit history (Client only).'},
                {command: CMD.VISIT_QUEUE, description: 'View visit requests (Vendor only).'},
                {command: CMD.VISIT_APPROVE, description: 'Approve a client\'s visit (Vendor only).'},
                {command: CMD.VISIT_DECLINE, description: 'Decline a client\'s visit (Vendor only).'},
                {command: CMD.VISIT_DELETE, description: 'Cancel your visit (Client only).'}
            ]);
            logger.info(`A total of ${Object.keys(CMD).length} commands have been set for the bot.`);
            return bot;
        };

        this.handlers = function (bot) {
            bot.command(CMD.HELP, cmdHelp);
            bot.command(CMD.SETTINGS, cmdSettings);
            bot.command(CMD.START, cmdStart);
            bot.command(CMD.SERVICE_CREATE, cmdServiceCreate);
            bot.command(CMD.SERVICE_READ, cmdServiceRead);
            bot.command(CMD.SERVICE_UPDATE, cmdServiceUpdate);
            bot.command(CMD.SERVICE_DELETE, cmdServiceDelete);
            bot.command(CMD.SERVICE_LIST, cmdServiceList);
            bot.command(CMD.VISIT_SERVICE, cmdVisitService);
            bot.command(CMD.VISIT_LIST, cmdVisitList);
            bot.command(CMD.VISIT_QUEUE, cmdVisitQueue);
            bot.command(CMD.VISIT_APPROVE, cmdVisitApprove);
            bot.command(CMD.VISIT_DECLINE, cmdVisitDecline);
            bot.command(CMD.VISIT_DELETE, cmdVisitDelete);
            bot.on('message', filterMessage);
            return bot;
        };
    }
}

Conclusion

As a result of cooperation with ChatGPT, based on the template code, a bot framework with 14 commands (3 main and 11 additional) was created.



Example of using ChatGPT to automate code writing in a JavaScript project

In the article, I did not describe in detail the problems encountered during generation (for example, incorrect case of class names, incorrect prefix, doubling of escape characters when writing code to a file, etc.). Some of these problems were solved using "Replace in Files", while others were solved by changing the prompt.

Nevertheless, ChatGPT handles generation quite well if you work in the "do as I do" mode — when there is a ready-made example to follow and replacements need to be made according to a given template.

It is important to remember that ChatGPT is a tool, not a thinking assistant. Of course, you can expect "creative solutions" or "flights of fancy" from it, but this is more likely to lead to disappointment in its capabilities. However, if you perceive it as a powerful tool similar to a "large regular expression" with instructions in natural language, the results can be pleasantly surprising. Since LLM works with text, the more textual hints in the code (e.g., correct naming and comments), the easier it is for the model to navigate and meet your expectations.

  • flancer64/tg-demo-ai-generate - the resulting project

  • chat with code generation

Thank you for reading and good luck with your interaction with LLM!

Comments