The first thing you need to know is that Python-Telegram-Bot has something called "filters", and well filters are just that, if a pattern happens and you have a filter for that pattern it will call that filter.
MessageHandler(Filters.reply & (~Filters.command), handle_reply)
On the example above, every time the bot receive a message, it will check that the message received is a location object or not, if it is then it will execute whatever function we have defined.
Now, speaking of
Filter.location, if you search the PTB documentation for
you'll see that there's no filter for a "live location" object,
because in the end the end a "live location" and a "static location" are
the same, in fact PTB differentiate it by adding a
live_period attribute to the
location object, so if
live_location has a value it's live, if not
We'll start by declaring the same handler:
If you run your bot and share a live location you'll see that the
handle_location function is called several times, sometimes it might be
every second, this means that the filter also works for live location and
static locations, but also means there's no way to differentiate the one
Then in order to differentiate the two types of locations we need to
edited_message attribute - why? - every time the location
is updated the Telegram API will return you the same message with an
updated date and a new location:
msg_type = 0 if message["edit_date"] is not None: msg_type += 1 if message["location"]["live_period"] is not None: msg_type += 1 << 1
What we're doing in the code above is:
Create a helper variable and init that to zero.
Sum one to the variable if the
edit_dateis not none.
live_periodis not none sum the result of right and let the leftmost bits.
Bitwise Left Shift Operatoror
Zero-Fill Left-Shiftand is used to push zero bits on the rightmost side and let the leftmost bits to overflow.
After that we just only need a simple
if/else validation in order to
execute one action or another based on the
if msg_type == 0: context.bot.send_message(user.id, "Single (non-live) location update.") elif msg_type == 1: context.bot.send_message(user.id, "End of live period.") elif msg_type == 2: context.bot.send_message(user.id, "Start of live period") elif msg_type == 3: context.bot.send_message(user.id, "Live location update.")
Explaining a bit that code:
- It will be zero if the
edit_datein the message is none and the
live_periodin the location object is empty too.
- It will be one if the message has and
- It will be two if the message doesn't have an
- And finally three if the message has an
edit_dateand as well the
live_periodis not none.
Glue'ing everything all together, the
handle_location function might
look like this:
def handle_location(update: Update, context: CallbackContext): user = update.effective_user msg_type = 0 if update.edited_message: message = update.edited_message else: message = update.message if message["edit_date"] is not None: msg_type += 1 if message["location"]["live_period"] is not None: msg_type += 1 << 1 if msg_type == 0: context.bot.send_message(user.id, "Single (non-live) location update.") elif msg_type == 1: context.bot.send_message(user.id, "End of live period.") elif msg_type == 2: context.bot.send_message(user.id, "Start of live period") elif msg_type == 3: context.bot.send_message(user.id, "Live location update.")
As you might think, the live location can be also stopped by user
interaction and not by the end of the
live_period, but also there's no
notification from the Telegram API once the live location has been
completed, that gives you two validations that are needed once the live
location has stopped:
- User manually stops the live location.
- Live location reachs the
In that case a simple solution might be to just start a timer once the user starts a live location:
my_timer = threading.Timer( interval=message.location.live_period, function=end_live_location, args=(update, context) ) my_timer.name = 'some unique id'
With that, the
end_live_location will be executed once the Timer reachs the
end of the interval, and finally when the user stops the live location
manually just stop the timer by their id:
timer_threads = filter( lambda t: isinstance(t, threading.Timer), threading.enumerate() ) current_user_thread = filter( lambda t: 'some unique id' in t.name, timer_threads, ) for t in current_user_thread: t.cancel()
And that's it, I hope this entry helps you with the implementation of live location tracking using the Telegram Bot Platform.