Skip to content

A continuation of telegram_sgcheckpoint_pytutorial (https://github.com/tengfone/telegram_sgcheckpoint_pytutorial). This version uses ConversationHandler and KeyboardMarkUp for better UI.

License

Notifications You must be signed in to change notification settings

tengfone/telegram_sgcheckpointv2_pytutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

telegram_sgcheckpointv2_pytutorial

Check out the functioning bot on Telegram here.

This is a continuation of the previous tutorial telegram_sg_checkpoint_pytutorial. In this version, we will use a Conversational style bot and a custom keyboard.

Do check it out if you haven't as this tutorial is heavily dependent on that.

A basic tutorial to write a telegram bot using python written by someone who does not know how to code at all (Learnt on the go).

This version uses telegram ConversationHandler for simplicity purposes. We will be using python-telegram-bot as a wrapper.

Screenshots

SGCustom Camera

Pre-Requisite

Getting Started

This tutorial will be using PyCharm as an IDE.

Importing files from previous project

We can always install a fresh copy of everything by using pip install but that will take too long and you do not know which version you will be using. Therefore, we can use someone's/your own previously done project to use the same library version.

Start off by creating a new project. Ensure it is utilizing pipenv as an environment.

newproj

Once created, you will realize that the IDE will auto generate a Pipfile for you in your root directory. Delete that. Instead we will be using requirements.txt from your previous project to load all the libraries in.

After deleting the auto generated Pipfile copy and paste/download from my repo, the requirements.txt. Now your project directory should look like this

req

Once that is done. Run the command inside your IDE terminal (ensure directory is your project root directory).

$ pipenv shell
$ pipenv update

pipenv

What this does is to load the requirements.txt file into a Pipfile and then it will download and install all the required libraries into your Python Environment.

Set Environments Variables

Remember to add TOKEN, API_KEY, MODE, HEROKU_APP_NAME in the console config.

Code

The difference in V1 and V2 is how the code is being processed. For V1 what we were doing is using commands /commands to invoke functions. While for V2 we will be using a conversational style bot. A good example of a conversational bot can be found here and here.

State Machine

You will need to understand State Machines to be able to fully understand the passing of arguments(eg User Information) in a conversation handler.

MENU CHOOSING_CAMERA CHOOSING_RATES CALCULATE_SG_MY CALCULATE_MY_SG

MENU, CHOOSING_CAMERA, CHOOSING_RATES, CALCULATE_SG_MY, CALCULATE_MY_SG = range(5)

We will be using these 5 states. States can be anything you labelled it as. In this case I will be calling it in integers form, state 0, state 1 etc.

menu_reply_keyboard = [['Camera πŸ“·'], ['Rates πŸ’°'], ['Info ℹ️']]
camera_reply_keyboard = [['Woodlands', 'Tuas'], ['Back']]
rates_reply_keyboard = [['πŸ‡ΈπŸ‡¬βž‘οΈπŸ‡²πŸ‡Ύ', 'πŸ‡²πŸ‡Ύβž‘οΈπŸ‡ΈπŸ‡¬'], ['Back']]

Let's look at menu_reply_keyboard

emoji

Emoji's can be added easily into the string function (Only for Python 3.x) for Python 2.x you will have to include the Unicode function. To add the emoji in Python 3.0 just launch Telegram, copy and paste the emoji from Telegram into the string.

layout

If you noticed, the keyboard is stored as a list. By putting it in [['A'],['B'],['C']], your keyboard layout will look like this:

hori

If you place it as [['A','B','C']] it will be like this:

verti

Bear in mind you can mix and match the layout. Eg: camera_reply_keyboard = [['Woodlands', 'Tuas'], ['Back']]

Reply Keyboard

To "enable" the keyboard, you will have to attach it to a text message. As such, we use update.message.reply_text function to call the keyboard.

An example:

@send_typing_action         # enable typing... when processing
def start(update, context):
    """
    This is  what happens when you type /start. It displays a message with a custom keyboard and returns to the state MENU
    """
    update.message.reply_text(
        "Going to πŸ‡²πŸ‡Ύ or coming back πŸ‡ΈπŸ‡¬ \nCome, I let you see if got jam anot. \nOr you want exchange rate also can.",
        reply_markup=ReplyKeyboardMarkup(menu_reply_keyboard, one_time_keyboard=True))
    return MENU

When you use the function update.message.reply_text, the User ID and chat ID will automatically be passed in the function. reply_markup will identify what kind of keyboard you would like to pass it to. ReplyKeyboardMarkup(YOUR_KEY_BOARD, FUNCTION_TO_EXECUTE) Read up telegram.ReplyKeyboardMarkup for more info.

message

This function passes all kinds of messages, from replies to getting input/output. The format can be update.message.reply_photo or update.message.reply_text whereby it takes in the User's input / Chat ID etc.

update.message.reply_photo(woodlands_bke_image, caption=(''...'))

The whole list of commands should be read and fully understand here

Adds the typing... status to the bot while its processing a function. Aesthetic purposes, makes it looks like its a human. Add a @send_typing_action on top of any function for it to be called.

def send_typing_action(func):
    """Sends typing action while processing func command."""

    @wraps(func)
    def command_func(update, context, *args, **kwargs):
        context.bot.send_chat_action(chat_id=update.effective_message.chat_id, action=ChatAction.TYPING)
        return func(update, context, *args, **kwargs)

    return command_func

This handler will be our primary way of communicating between User and bot

class telegram.ext.ConversationHandler(entry_points, states, fallbacks, allow_reentry=False, run_async_timeout=None, 
timed_out_behavior=None, per_chat=True, per_user=True, per_message=False, conversation_timeout=None)

Sample Conversation Handler format:

conv_handler = ConversationHandler(
        entry_points=[CommandHandler('start', start)],
        states={
            MENU: [MessageHandler(Filters.regex('^Camera πŸ“·$'), camera, pass_user_data=True),
                   MessageHandler(Filters.regex('^Rates πŸ’°$'), rates, pass_user_data=True),
                   MessageHandler(Filters.regex('^Info ℹ️'), info, pass_chat_data=True)],
            CHOOSING_CAMERA: [MessageHandler(Filters.regex('^Woodlands$'), woodlands, pass_user_data=True),
                              MessageHandler(Filters.regex('^Tuas$'), tuas, pass_user_data=True)],
            CHOOSING_RATES: [MessageHandler(Filters.regex('^πŸ‡ΈπŸ‡¬βž‘οΈπŸ‡²πŸ‡Ύ$'), ask_rates_sg_my, pass_user_data=True),
                             MessageHandler(Filters.regex('^πŸ‡²πŸ‡Ύβž‘οΈπŸ‡ΈπŸ‡¬$'), ask_rates_my_sg, pass_user_data=True)],
            CALCULATE_SG_MY: [MessageHandler(Filters.text, calc_rates_sg_my, pass_user_data=True)],
            CALCULATE_MY_SG: [MessageHandler(Filters.text, calc_rates_my_sg, pass_user_data=True)],
        },
        fallbacks=[MessageHandler(Filters.regex('^Back$'), start, pass_user_data=True)],
        allow_reentry=True  # this line is important as it allows the user to talk to the bot anytime
    )

entry_points runs /start to execute your start function. This is to determine that you are "entering" the loop for a conversation.

states are the different state machines the bot is in when you are executing the script.

regex stands for regular expression, which basically strips the text you click and match it with a function. However since we have already pre-define our User input (Keyboard), we can just use the format ('^xxx$') which basically just matches both input and output.

pass_user_data is to ensure that the bot "remembers" the same User who is talking to the bot and also pushes the Chat ID and User ID along .

fallbacks you can think of it as a "Cancel" or "Back" function.

allow_reentry this is important as it allows a User to communicate with the bot even if the conversation is cleared. This sorts of resets the State of the machine is in for the particular User. This is extremely important if we deloy it in the FREE Heroku as after 30 minutes of inactivity of the bot, Heroku puts it into sleep. If you resume the bot (sending a command), it will forget the state you are in and will never enter back into the Conversation Handler.

The rest of the code can be found in bot.py.

Deployment

Same as telegram_sg_checkpoint_pytutorial.

Docker

I will not be doing an in-depth tutorial for Docker. You can Dockerize it to Heroku by creating a extension-less file called Dockerfile in your root directory

On the inside:

FROM python:3.7

RUN mkdir /app
ADD . /app
WORKDIR /app

RUN pip install -r requirements.txt

CMD python /app/bot.py

On your project folder directory terminal:

$ heroku container:login
$ heroku container:push --app <HEROKU_NAME> web
$ heroku container:release --app <HEROKU_APP_NAME> web

and you're done.

Further Development

  • Deploying to a VPS
  • Sending location gives you the nearest checkpoint camera

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Credits

python-telegram-bot examples

Creating Telegram Bot and Deploying it to Heroku

License

MIT

About

A continuation of telegram_sgcheckpoint_pytutorial (https://github.com/tengfone/telegram_sgcheckpoint_pytutorial). This version uses ConversationHandler and KeyboardMarkUp for better UI.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages