
We continue the story of the development of Telegram-bot to find tickets - HappyTicketsBot, the beginning can be read
in the first part .
In the second I will talk about the bot itself, share the code, as well as ideas that are most likely not destined to become a reality. Most of the functionality at the time of creation of the bot was already written in a script format, so the main task was to set up the user interaction interface via a Telegram-messenger. It turned out not boltologically as in the 1st part, so attention is a lot of code.
')
Spoiler: HappyTicketsBot did not fly away to spin on a foreign server, it is local and Russian, but one day (I believe) it will take place =)
1. Starting from scratch
Since there was no experience at all in designing Telegram bots, I had to start with basic articles and tutorials, which are very numerous on the web. Yes, by the way, what was a back-end at that time, I also had a bad idea))
This set of lessons became the most informative and applied. The module on which interaction was conducted with Telegram - pyTelegramBotAPI (
github ).
The longest took the development of the ideology of decorators, read about them in
this article . There in two parts and very understandable.
2. Bot interaction script with the user. Basic search
As already mentioned in the preface and
the 1st part of the article , almost the entire parsing code was ready. It remained to change the way the search parameters were set. Based on this, the script of the bot's behavior was constructed. Commands available to the user are limited to the following set:
- / Find - start a new search,
- / Reset - reset the search and start a new one,
- / LastSearch - returns results using the parameters of the last query
- / addURL add the URL of the performance to the interests to track the price reduction,
- / checkURL - update the prices of the performances of interest,
- / showURL - display all URLs added to the list of interests.
In the basic search script
/ find, the user moves from one status to another, sequentially entering the necessary filter data. After entering the last parameter — the place of presentation — the bill is directly parsed using globally declared dictionaries, where the key is the user ID, the values ​​are the entered search parameters.
In order to remember the state of the user, they are stored in the database. To work with it, the Vedis modules are used (a key-value database configurator, read the
documentation ) and Enum (work with listings, see details
1 ,
2 ).
In a separate Myconfig.py configuration file, we set the bot parameters (including the unique token received from Telegram) and list the statuses a user can be in. They came out a bit.
from enum import Enum token = "4225555:AAGmfghjuGOI4sdfsdfs5656sdfsdf_c"
As a result, we get a straightforward chain of status transitions from one to another.

For storage, use the database Vedis. The user who sent the message is always initialized via message.chat.id.
The code of the dbwoker.py file, which describes the interaction with the database from vedis import Vedis import Myconfig as config
Below is an example of a handler that is activated by the command / find. As you can see, in this example there is no data entry - there is only a change of status to “S_ENTER_MONTH”. After seeing the message about entering the number, the user enters it and sends a message. When receiving a message with the status S_ENTER_MONTH, the next stage is initiated. In case of input errors, the status does not change.
If the bot receives a message from the user with the status S_ENTER_MONTH, then the following handler will be launched. Ideologically, it also happens at other stages of the basic search script.
@bot.message_handler(func=lambda message: dbworker.get_current_state(message.chat.id) == config.States.S_ENTER_MONTH.value) def user_entering_month(message): if not message.text.isdigit(): bot.send_message(message.chat.id, ", ") return # 1 num[message.chat.id]=message.text # if int(num[message.chat.id])>12 or int(num[message.chat.id])<1: bot.send_message(message.chat.id, " 1 12. ") # 2 return url_list[message.chat.id]=take_url(num[message.chat.id]) # URL- if url_list[message.chat.id]==[]: # bot.send_message(message.chat.id, " , . ") return bot.send_message(message.chat.id, "! .") dbworker.set_state(message.chat.id, config.States.S_ENTER_PRICE.value) #
In addition to the standard search, it is possible to save interesting performances.
3. Tracking price changes
The user can add URLs to the interest list to receive an alert when the price drops. We remember that we still have the status S_ENTER_URL which is not limited in the basic search. AT
@bot.message_handler(commands=["addURL"]) def cmd_add_url(message): bot.send_message(message.chat.id, " url, . https://") dbworker.set_state(message.chat.id, config.States.S_ENTER_URL.value) # @bot.message_handler(func=lambda message: dbworker.get_current_state(message.chat.id) == config.States.S_ENTER_URL.value) def user_entering_URL(message): perf_url=message.text user_id=message.chat.id try: add_new_URL(user_id,perf_url) bot.send_message(message.chat.id, ' !') dbworker.set_state(message.chat.id, config.States.S_START.value) # except: bot.send_message(message.chat.id, 'URL ! !') dbworker.set_state(message.chat.id, config.States.S_ENTER_URL.value)
To store the list, use the .csv file. To interact with it, you need a couple of functions - writing and reading with checking the price change. If it changes, we notify the user.
def add_new_URL(user_id,perf_url): WAITING_FILE = "waiting_list.csv" with open(WAITING_FILE, "a", newline="") as file: curent_url='https://'+perf_url text=get_text(curent_url)
The price update feature code is slightly longer. def update_prices(bot): WAITING_FILE = "waiting_list.csv" with open(WAITING_FILE, "r", newline="") as file: reader = csv.reader(file) waitingList=[] for row in reader: waitingList.append(list(row)) L=len(waitingList) lowest={} with open(WAITING_FILE, "w", newline="") as fl: writer = csv.writer(fl) for i in range(L): lowest[waitingList[i][1]]=waitingList[i][2]
As a result, by the command
/ checkURL, the user can get such a result (now I understand that the name of the performance should also be output, but these are things from the “hands have not reached” series).

Okay, okay. We can search, we can track. A couple of friends began to use the bot, I wanted to know who they were and what they were looking for. This information is good to write to the logs.
4. We write activity and errors in the logs
This will help us module Logging. Information is recorded only at the stage of completion of the basic search, in the handler, in which the user status is transferred from S_ENTER_PLACE to S_START. Recording errors, in turn, occurs when they occur.
I will not be able to say a lot about how the module works, so it’s better to turn to information
outside .

Logger description def save_logs(str): loggerInfo.info(str)
Because the connection was broken, the bot periodically crashed, so the Internet connection error was caught and the bot restarted automatically after 10 seconds. But this did not always save, so I kept running TeamViewer, in order to raise if necessary.
5. Unrealized
We have a bot that replaces the functionality of the script, but allows you to receive information in a convenient form inside the messenger. He closed my basic needs.
Showdown with the modules and writing slender handlers lasted about a month in the mode of work on weekends and sometimes in the evenings. At the end of this period, interest has already begun to fade and the functional is stuck at the starting point. It was not possible to break through the principles of work on the webhook, and then Telegram was blocked. Before that, there was a plan to launch a back-end on a working server, but ... vpn for the sake of it will not be put there =)
Here is what is left in the plans, some of which may be realized once on a languid summer / winter evening:
- load testing with a large flow of users. It is not yet clear whether the bot will work stably and not confuse users;
- notification of the appearance of a new performance in the artist's schedule. I have a lot of favorite “white rabbits”, I don’t keep track of everyone (and I would like to);
- notification of the sale of tickets of a certain category. There was one acquaintance, an amateur of the first row of an orchestra who is difficult to catch manually;
- Regular automatic checking of URLs of interest for a timer price reduction. Now this is done on command, the timer could not be adjusted quickly, so it left it simple;
- preservation of its history of visits to performances. Somewhere in the .csv file, the date-name-composition of performers-your comment, so as not to lose;
- search for a given category of tickets. To set not only the price, but also the sector (parter-benoir, etc.);
- transfer everything to the skill for Alice. why not?
- make a mobile application with the same functionality. why not?
There was an approach to the Bolshoi Theater. In order to catch the tickets for “Nureyev”, it was not possible to pick up the html posters in two evenings, so it was also postponed to the list of unrealized.
TOTAL
Laziness was the engine of progress and she stopped him. It didn’t come to the point of uploading the bot to a third-party server, it still requires more extensive competencies and knowledge of the Web. The project turned out to be interesting and allowed to master Python a little better, to see another facet of it (besides the usual Machine learning), and also presented many wonderful evenings in the theater at a bargain price. Thanks to him for this, he closed the tasks assigned to him.
No matter how hard I tried, the article still got a lot of code and little text. I would be happy to explain the incomprehensible or little described in the comments =)