paint-brush
Creating a Telegram Bot for my Friend's Businessby@levorak
3,224 reads
3,224 reads

Creating a Telegram Bot for my Friend's Business

by CarolinaJanuary 20th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Telegram Bot for a herb shop with the following 1. Pre-registration required 2. Automatic welcome message 3.Menu and sub-menus with buttons 4. Confirm and clean order 5. At the end of the order delete the chat menu
featured image - Creating a Telegram Bot for my Friend's Business
Carolina HackerNoon profile picture


Last week my friend asked for help with coding a Telegram bot for the VIP 😏 customers of his herbs shop. The requirements of the said bot were:


  1. Pre-registration required
  2. Automatic welcome message
  3. Menu and sub-menus with buttons
  4. Confirm and clean order
  5. At the end of the order delete the chat menu


Ok, get to work!!

What we need

  • [ ]Telegram account
  • [ ]ChatID from the customers that are going to be allowed to use the bot.
  • [ ]Server with a public IP with the following installed
    • [ ]python3
    • [ ]pip3
    • [ ]python-telegram-bot
    • [ ]telegram_menu


Telegram configuration

  1. Create a bot in telegram, using BotFather /newbot, and take note of the API token and store it safely

    If you need to regenerate a token for an old bot, use BotFather /token


  2. Set privacy for the bot, using BotFather /setprivacy

  3. Avoid the bot to be added to a group, using BotFather /setjoingroups

Get chat id

On the search bar look for @myidbot, this is a public bot that returns the id of your account or group.


You’ll need every user to do this and share their id.

Server side

In this case, once the users are pre-registered, we need access to the db, txt, json, file or however you want to store the customer data. For ease I will use json here

[
    {
        "name": "Albus",
        "chatid": 142347567,
        "phone": 1234567890,
        "Address": "12 Grimmauld Place"
    },
    {
        "name": "Bob",
        "chatid": 123456789
        "phone": 1234567891,
        "Address": "124 Conch St., Bikini Bottom, Pacific Ocean"
    },
    {
        "name": "Crush",
        "chatid": 987654321
        "phone": 1234567892,
        "Address": "Sherman, 42 Wallaby Way, Sydney."
    }
]


Now we have all we need to set up the server using python


  1. Let’s import libraries


import telegram
from telegram.ext.updater import Updater
from telegram.ext.callbackcontext import CallbackContext
from telegram.update import Update
from telegram.ext.commandhandler import CommandHandler
from telegram.ext.messagehandler import MessageHandler
from telegram.ext.filters import Filters
from telegram.ext import CommandHandler, CallbackQueryHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
from functools import wraps
import asyncio
import json
import csv
import numpy as np


  1. Define variables
############################ Initialize bot and updater #########################################
API_TOKEN = '' #change the API key for your bot
updater = Updater(API_TOKEN, use_context=True)
dispatcher = updater.dispatcher

############################ Initialize  global variables #########################################
sub_options = [
    [
        ["💐Bouquet", "🪷Flowers - Bouquet added to the order"],
        ["🎍Basket", "🪷Flowers - Basket added to the order"]
    ],
    [
        ["🌱Seeds", "🌿Herbs - Seeds added to the order"],
        ["🪴Potted", "🌿Herbs - Potted added to the order"]
    ]
]

order = []
List_of_users = []
clear_message ="Order cleared"


  1. Limit access
def allowed_users():
    fileObject = open("list.json", "r") #list of users that can use the bot 
    jsonContent = fileObject.read()
    Users_List = json.loads(jsonContent)

    for i in range(len(Users_List)):
        List_of_users.append(Users_List[i]['chatid'])

allowed_users()

def restricted(func):
    @wraps(func)
    def wrapped(update, context, *args, **kwargs):
        user_id = update.effective_user.id
        if user_id not in List_of_users:
            print(f"Unauthorized access denied for {user_id}.")
            return
        return func(update, context, *args, **kwargs)
    return wrapped
@restricted


  1. Commands definition:

Here are listed the commands that the user will be able to execute

############################ Command definition #########################################

def start(update, context):
    reply_markup = main_menu_keyboard()
    message = context.bot.send_message(chat_id=update.effective_chat.id, text="Welcome! Please select a category from the menu below, or type /help", reply_markup=reply_markup)
    context.user_data['messages'] = [message.message_id]

def confirm(update, context):
    message = "Current order: "
    for o in order:
        message += o + " "
    update.message.reply_text(message)# comment this if you dont want to keep the order on the chat
    order.append(str(update.effective_user.id ))
    save_to_csv(order)
    global clear_message
    clear_message= 'Thanks, bye'
    clear(update, context)

def clear(update, context):
    context.bot.send_message(chat_id=update.effective_chat.id,text=clear_message)
    context.bot.delete_message(chat_id=update.message.chat_id, message_id=context.user_data['messages'][-1])
    context.bot.delete_message(chat_id=update.message.chat_id, message_id=context.user_data['messages'][-2])

def help(update, context):
    message = "List of available commands: \n"
    message += "/start - Welcome message with options \n"
    message += "/confirm - Confirm and send order \n"
    message += "/clear - Clear order \n"
    message += "/help - List of available commands"
    update.message.reply_text(message)


  1. Menus and keyboard definition

I did the sub menu recursive because in practical a store doesn’t have only 2 categories with 2 products, so this could save time writing a lot of keyboards functions

############################ Menu definition #########################################

#basic menu
def main_menu_keyboard():
    keyboard = [[InlineKeyboardButton("Flowers", callback_data='1'),
                 InlineKeyboardButton("Herbs", callback_data='2')]]
    return InlineKeyboardMarkup(keyboard)

def main_menu(update, context):
  query = update.callback_query
  query.answer()
  query.edit_message_text(
                        text="Please select a product or type /help",
                        reply_markup=main_menu_keyboard())

#recursive menu based on the sub_options matrix
def sub_menu(update, context, sub_menu_num):
    query = update.callback_query
    keyboard = []
    query = update.callback_query
    for i in range(len(sub_options[sub_menu_num])):
        keyboard.append([InlineKeyboardButton(sub_options[sub_menu_num][i][0], callback_data=f'{sub_menu_num+1}-{i+1}')])
    keyboard.append([InlineKeyboardButton("Return to main menu", callback_data='main')])
    reply_markup = InlineKeyboardMarkup(keyboard)
    query.edit_message_text(text="Please select a product, the type /confirm to send your order or /clear to restart", reply_markup=reply_markup)


  1. Menu options
############################ Handle menu options #########################################

def button(update, context):
    query = update.callback_query
    if query.data == '1':
        sub_menu(update,context, 0)
    elif query.data == '2':
        sub_menu(update,context, 1)
    elif query.data == "main":
        main_menu(update,context)    
    else:
        sub_menu_num,sub_option_num = [int(x) for x in query.data.split("-")]
        query.answer(sub_options[sub_menu_num-1][sub_option_num-1][1]) #send a notification but not a message in the chat
        order.append(sub_options[sub_menu_num-1][sub_option_num-1][0])


  1. Handlers
############################# Handle Commands #########################################
        
start_handler = CommandHandler('start', start)
dispatcher.add_handler(start_handler)

confirm_handler = CommandHandler('confirm', confirm)
dispatcher.add_handler(confirm_handler)

help_handler = CommandHandler('help', help)
dispatcher.add_handler(help_handler)

clear_handler = CommandHandler('clear', clear)
dispatcher.add_handler(clear_handler)

dispatcher.add_handler(CallbackQueryHandler(main_menu, pattern='main'))

callback_handler = CallbackQueryHandler(button)
dispatcher.add_handler(callback_handler)

Start bot

############################# Start the bot #########################################
updater.start_polling()


  1. Save order to csv

To keep all the orders, I’ll use a simple csv file

############################# Save to csv #########################################
def save_to_csv(options):
    with open("sample.csv", "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["New order"])
        for option in options:
            writer.writerow([option])


Finally let’s put all together

import telegram
from telegram.ext.updater import Updater
from telegram.ext.callbackcontext import CallbackContext
from telegram.update import Update
from telegram.ext.commandhandler import CommandHandler
from telegram.ext.messagehandler import MessageHandler
from telegram.ext.filters import Filters
from telegram.ext import CommandHandler, CallbackQueryHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
from functools import wraps
import asyncio
import json
import csv
import numpy as np

############################ Initialize bot and updater #########################################
API_TOKEN = '' #change the API key for your bot
updater = Updater(API_TOKEN, use_context=True)
dispatcher = updater.dispatcher

############################ Initialize  global variables #########################################
sub_options = [
    [
        ["💐Bouquet", "🪷Flowers - Bouquet added to the order"],
        ["🎍Basket", "🪷Flowers - Basket added to the order"]
    ],
    [
        ["🌱Seeds", "🌿Herbs - Seeds added to the order"],
        ["🪴Potted", "🌿Herbs - Potted added to the order"]
    ]
]

order = []
List_of_users = []
clear_message ="Order cleared"

############################ Limit access #########################################

def allowed_users():
    fileObject = open("list.json", "r") #list of users that can use the bot
    jsonContent = fileObject.read()
    Users_List = json.loads(jsonContent)

    for i in range(len(Users_List)):
        List_of_users.append(Users_List[i]['chatid'])

allowed_users()

def restricted(func):
    @wraps(func)
    def wrapped(update, context, *args, **kwargs):
        user_id = update.effective_user.id
        if user_id not in List_of_users:
            print(f"Unauthorized access denied for {user_id}.")
            return
        #return await func(update, context, *args, **kwargs)
        return func(update, context, *args, **kwargs)
    return wrapped
@restricted

############################ Command definition #########################################

def start(update, context):
    reply_markup = main_menu_keyboard()
    message = context.bot.send_message(chat_id=update.effective_chat.id, text="Welcome! Please select a category from the menu below, or type /help", reply_markup=reply_markup)
    context.user_data['messages'] = [message.message_id]

def confirm(update, context):
    message = "Current order: "
    for o in order:
        message += o + " "
    update.message.reply_text(message) # comment this if you dont want to keep the order on the chat
    order.append(str(update.effective_user.id ))
    save_to_csv(order)
    global clear_message
    clear_message= 'Thanks, bye'
    clear(update, context)

def clear(update, context):
    context.bot.send_message(chat_id=update.effective_chat.id,text=clear_message)
    context.bot.delete_message(chat_id=update.message.chat_id, message_id=context.user_data['messages'][-1])
    context.bot.delete_message(chat_id=update.message.chat_id, message_id=context.user_data['messages'][-2])

def help(update, context):
    message = "List of available commands: \n"
    message += "/start - Welcome message with options \n"
    message += "/confirm - Confirm and send order \n"
    message += "/clear - Clear order \n"
    message += "/help - List of available commands"
    update.message.reply_text(message)

############################ Menu definition #########################################

def main_menu_keyboard():
    keyboard = [[InlineKeyboardButton("Flowers", callback_data='1'),
                 InlineKeyboardButton("Herbs", callback_data='2')]]
    return InlineKeyboardMarkup(keyboard)

def main_menu(update, context):
  query = update.callback_query
  query.answer()
  query.edit_message_text(
                        text="Please select a product or type /help",
                        reply_markup=main_menu_keyboard())

def sub_menu(update, context, sub_menu_num):
    query = update.callback_query
    keyboard = []
    query = update.callback_query
    for i in range(len(sub_options[sub_menu_num])):
        keyboard.append([InlineKeyboardButton(sub_options[sub_menu_num][i][0], callback_data=f'{sub_menu_num+1}-{i+1}')])
    keyboard.append([InlineKeyboardButton("Return to main menu", callback_data='main')])
    reply_markup = InlineKeyboardMarkup(keyboard)
    query.edit_message_text(text="Please select a product, the type /confirm to send your order or /clear to restart", reply_markup=reply_markup)

############################ Handle menu options #########################################

def button(update, context):
    query = update.callback_query
    if query.data == '1':
#        context.user_data['messages'].append(message.message_id)
        sub_menu(update,context, 0)
    elif query.data == '2':
#        context.user_data['messages'].append(message.message_id)
        sub_menu(update,context, 1)
    elif query.data == "main":
#        context.user_data['messages'].append(message.message_id)
        main_menu(update,context)    
    else:
        #context.user_data['messages'].append(context.message.message_id)
        sub_menu_num,sub_option_num = [int(x) for x in query.data.split("-")]
        query.answer(sub_options[sub_menu_num-1][sub_option_num-1][1])
        order.append(sub_options[sub_menu_num-1][sub_option_num-1][0])

############################# Handle Commands #########################################
        
start_handler = CommandHandler('start', start)
dispatcher.add_handler(start_handler)

confirm_handler = CommandHandler('confirm', confirm)
dispatcher.add_handler(confirm_handler)

help_handler = CommandHandler('help', help)
dispatcher.add_handler(help_handler)

clear_handler = CommandHandler('clear', clear)
dispatcher.add_handler(clear_handler)

dispatcher.add_handler(CallbackQueryHandler(main_menu, pattern='main'))

callback_handler = CallbackQueryHandler(button)
dispatcher.add_handler(callback_handler)

############################# Start the bot #########################################
updater.start_polling()

############################# Save to csv #########################################
def save_to_csv(options):
    with open("sample.csv", "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["New order"])
        for option in options:
            writer.writerow([option])

Now let’s review the wishes list

  1. Pre-registration required - Completed on step 3
  2. Automatic welcome message - Completed on step 4 /start
  3. Menu with buttons - Completed on step 5,6,
  4. Confirm and clean order - Completed on step 4 /confirm and /clear
  5. At the end of the order delete the chat menu- Completed on step 4 /clear


That’s all for today, hope you enjoyed and find it useful for similar requests