Made conversation nicer
Messages now get deleted and deleted for a smoother experience. Other small changes: - States as constants - String formatting with f-string-literals
This commit is contained in:
parent
d6debf6a46
commit
4f403561ba
3 changed files with 112 additions and 76 deletions
23
bot.py
23
bot.py
|
@ -3,15 +3,16 @@ import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import schedule
|
import schedule
|
||||||
|
from telegram import InlineKeyboardMarkup, ParseMode
|
||||||
|
from telegram.error import BadRequest
|
||||||
from telegram.ext import Updater, CommandHandler, InlineQueryHandler, CallbackQueryHandler
|
from telegram.ext import Updater, CommandHandler, InlineQueryHandler, CallbackQueryHandler
|
||||||
|
|
||||||
from exceptions import *
|
from exceptions import *
|
||||||
|
from push_information import __init__ as push_init
|
||||||
from vvs import inline_station_search, handle_vvs, handle_multiple_stations_reply
|
from vvs import inline_station_search, handle_vvs, handle_multiple_stations_reply
|
||||||
from weather_meteomedia import handle_meteomedia
|
from weather_meteomedia import handle_meteomedia
|
||||||
from weather_openweathermap import handle_weather
|
|
||||||
from weather_openweathermap import __init__ as openweathermap_init
|
from weather_openweathermap import __init__ as openweathermap_init
|
||||||
from push_information import __init__ as push_init
|
from weather_openweathermap import handle_weather
|
||||||
|
|
||||||
|
|
||||||
token_file = open("token", "r")
|
token_file = open("token", "r")
|
||||||
updater = Updater(token=token_file.read(), use_context=True)
|
updater = Updater(token=token_file.read(), use_context=True)
|
||||||
|
@ -23,7 +24,21 @@ logging.basicConfig(format='%(acstime)s - %(name)s - %(levelname)s - %(message)s
|
||||||
|
|
||||||
|
|
||||||
def send_message(chat_id: int, message: str):
|
def send_message(chat_id: int, message: str):
|
||||||
dispatcher.bot.send_message(chat_id=chat_id, text=message, disable_notification=True, parse_mode="Markdown")
|
dispatcher.bot.send_message(chat_id=chat_id, text=message, disable_notification=True, parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
|
||||||
|
def edit_message_and_delete_answer(chat_id: int, message_id: int, message: str,
|
||||||
|
reply_markup: InlineKeyboardMarkup = None):
|
||||||
|
dispatcher.bot.delete_message(chat_id=chat_id, message_id=message_id)
|
||||||
|
try:
|
||||||
|
dispatcher.bot.edit_message_text(chat_id=chat_id, message_id=message_id - 1, text=message,
|
||||||
|
reply_markup=reply_markup)
|
||||||
|
except BadRequest:
|
||||||
|
try:
|
||||||
|
dispatcher.bot.edit_message_text(chat_id=chat_id, message_id=message_id - 2, text=message,
|
||||||
|
reply_markup=reply_markup)
|
||||||
|
except BadRequest:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
cease_continuous_run = threading.Event()
|
cease_continuous_run = threading.Event()
|
||||||
|
|
|
@ -6,11 +6,12 @@ from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, CallbackQueryHandler, CallbackContext
|
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, CallbackQueryHandler, CallbackContext
|
||||||
from telegram.ext.filters import Filters
|
from telegram.ext.filters import Filters
|
||||||
|
|
||||||
from bot import send_message
|
from bot import send_message, edit_message_and_delete_answer
|
||||||
from weather_openweathermap import City, get_city_by_name
|
from weather_openweathermap import City, get_city_by_name
|
||||||
|
|
||||||
users = {}
|
users = {}
|
||||||
subs_in_edit = {}
|
|
||||||
|
LIST, ADD_CITY, ADD_TIME, EDIT, EDIT_CITY, EDIT_TIME = range(6)
|
||||||
|
|
||||||
|
|
||||||
class Subscription(object):
|
class Subscription(object):
|
||||||
|
@ -107,8 +108,7 @@ def push_callback(update: Update, context: CallbackContext):
|
||||||
else:
|
else:
|
||||||
user = User(update.effective_chat.id)
|
user = User(update.effective_chat.id)
|
||||||
users[update.effective_chat.id] = user
|
users[update.effective_chat.id] = user
|
||||||
string = f"Hello, {update.effective_user.first_name}!\n"
|
string = f"Hello, {update.effective_user.first_name}!\nYou have {len(user.subs)} subscriptions:\n"
|
||||||
string += f"You have {len(user.subs)} subscriptions:\n"
|
|
||||||
|
|
||||||
button_list = []
|
button_list = []
|
||||||
for i, subscription in enumerate(user.subs):
|
for i, subscription in enumerate(user.subs):
|
||||||
|
@ -116,116 +116,147 @@ def push_callback(update: Update, context: CallbackContext):
|
||||||
InlineKeyboardButton(text=f"{subscription.city.name} at {time.strftime('%H:%M', subscription.time)}",
|
InlineKeyboardButton(text=f"{subscription.city.name} at {time.strftime('%H:%M', subscription.time)}",
|
||||||
callback_data=f"/push_edit {i}"))
|
callback_data=f"/push_edit {i}"))
|
||||||
button_list.append(InlineKeyboardButton(text="Add", callback_data="/push_add"))
|
button_list.append(InlineKeyboardButton(text="Add", callback_data="/push_add"))
|
||||||
|
button_list.append(InlineKeyboardButton(text="End", callback_data="/cancel"))
|
||||||
reply_markup = InlineKeyboardMarkup.from_column(button_list)
|
reply_markup = InlineKeyboardMarkup.from_column(button_list)
|
||||||
update.message.reply_text(string, reply_markup=reply_markup)
|
update.message.reply_text(string, reply_markup=reply_markup)
|
||||||
return "list"
|
context.bot.delete_message(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id)
|
||||||
|
return LIST
|
||||||
|
|
||||||
|
|
||||||
def edit_sub_callback(update: Update, context: CallbackContext):
|
def edit_sub_callback(update: Update, context: CallbackContext):
|
||||||
global users, subs_in_edit
|
global users
|
||||||
user = users[update.effective_chat.id]
|
user = users[update.effective_chat.id]
|
||||||
sub_idx = int(update.callback_query.data[11:])
|
sub_idx = int(update.callback_query.data[11:])
|
||||||
sub = user.subs[sub_idx]
|
sub = user.subs[sub_idx]
|
||||||
subs_in_edit[update.effective_chat.id] = sub
|
context.user_data['sub'] = sub
|
||||||
button_list = [[InlineKeyboardButton(text="City", callback_data="/push_edit_city"),
|
button_list = [[InlineKeyboardButton(text="City", callback_data="/push_edit_city"),
|
||||||
InlineKeyboardButton(text="Time", callback_data="/push_edit_time")],
|
InlineKeyboardButton(text="Time", callback_data="/push_edit_time")],
|
||||||
[InlineKeyboardButton(text="Delete", callback_data=f"/push_edit_delete {sub_idx}")]]
|
[InlineKeyboardButton(text="Delete", callback_data=f"/push_edit_delete {sub_idx}"),
|
||||||
|
InlineKeyboardButton(text="Cancel", callback_data="/cancel")]]
|
||||||
reply_markup = InlineKeyboardMarkup(button_list)
|
reply_markup = InlineKeyboardMarkup(button_list)
|
||||||
update.effective_chat.send_message("Change subscription.\nWhat do you want to change?", reply_markup=reply_markup)
|
update.callback_query.answer()
|
||||||
return "edit"
|
update.callback_query.edit_message_text(f"Change subscription {sub}.\nWhat do you want to change?",
|
||||||
|
reply_markup=reply_markup)
|
||||||
|
return EDIT
|
||||||
|
|
||||||
|
|
||||||
def edit_city_callback(update: Update, context: CallbackContext):
|
def edit_city_callback(update: Update, context: CallbackContext):
|
||||||
update.effective_chat.send_message("Tell me the city you want information for:")
|
update.callback_query.answer()
|
||||||
return "edit_city"
|
update.callback_query.edit_message_text("Tell me the city you want information for:")
|
||||||
|
return EDIT_CITY
|
||||||
|
|
||||||
|
|
||||||
def edited_city_callback(update: Update, context: CallbackContext):
|
def edited_city_callback(update: Update, context: CallbackContext):
|
||||||
global users, subs_in_edit
|
global users
|
||||||
user = users[update.effective_chat.id]
|
user = users[update.effective_chat.id]
|
||||||
city_name = update.message.text
|
city_name = update.message.text
|
||||||
city = get_city_by_name(city_name)
|
city = get_city_by_name(city_name)
|
||||||
if city.name == "none":
|
if city.name == "none":
|
||||||
update.message.reply_text("This city was not found! Try again")
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
|
message=f"The city {city_name} was not found! Try again")
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
subs_in_edit[user.chat_id].edit_city(city)
|
context.user_data['sub'].edit_city(city)
|
||||||
update.message.reply_text(f"City changed to {city.name}.")
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
del subs_in_edit[update.effective_chat.id]
|
message=f"Subscription now is {context.user_data['sub']}")
|
||||||
update.message.reply_text(f"Subscription now is {subs_in_edit[user.chat_id]}")
|
|
||||||
|
del context.user_data['sub']
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def edit_time_callback(update: Update, context: CallbackContext):
|
def edit_time_callback(update: Update, context: CallbackContext):
|
||||||
update.effective_chat.send_message("When do you want to receive updates?")
|
update.callback_query.answer()
|
||||||
return "edit_time"
|
update.callback_query.edit_message_text("When do you want to receive updates?")
|
||||||
|
return EDIT_TIME
|
||||||
|
|
||||||
|
|
||||||
def edited_time_callback(update: Update, context: CallbackContext):
|
def edited_time_callback(update: Update, context: CallbackContext):
|
||||||
global users
|
global users
|
||||||
user = users[update.effective_chat.id]
|
user = users[update.effective_chat.id]
|
||||||
sub_in_edit = subs_in_edit[user.chat_id]
|
sub_in_edit = context.user_data['sub']
|
||||||
time_string = update.message.text
|
time_string = update.message.text
|
||||||
|
try:
|
||||||
if time_string.find(':') == -1:
|
if time_string.find(':') == -1:
|
||||||
sub_in_edit.edit_time(time.strptime(time_string, "%H"))
|
sub_in_edit.edit_time(time.strptime(time_string, "%H"))
|
||||||
else:
|
else:
|
||||||
sub_in_edit.edit_time(time.strptime(time_string, "%H:%M"))
|
sub_in_edit.edit_time(time.strptime(time_string, "%H:%M"))
|
||||||
del subs_in_edit[user.chat_id]
|
except ValueError:
|
||||||
update.message.reply_text(f"Subscription now is {sub_in_edit}")
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
|
message="This time was not recognized! Try again")
|
||||||
|
return None
|
||||||
|
del context.user_data['sub']
|
||||||
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
|
message=f"Subscription now is {sub_in_edit}")
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def delete_subscription_callback(update: Update, context: CallbackContext):
|
def delete_subscription_callback(update: Update, context: CallbackContext):
|
||||||
global users
|
global users
|
||||||
users[update.effective_chat.id].del_subscription(int(update.callback_query.data[18:]))
|
users[update.effective_chat.id].del_subscription(int(update.callback_query.data[18:]))
|
||||||
update.effective_chat.send_message("Deleted")
|
update.callback_query.answer()
|
||||||
|
update.callback_query.edit_message_text("Deleted")
|
||||||
schedule_subscriptions()
|
schedule_subscriptions()
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def add_sub_callback(update: Update, context: CallbackContext):
|
def add_sub_callback(update: Update, context: CallbackContext):
|
||||||
update.effective_chat.send_message("Add a new subscription.\nTell me the city you want information for:")
|
update.callback_query.answer()
|
||||||
return "add_city"
|
update.callback_query.edit_message_text("Add a new subscription.\nTell me the city you want information for:")
|
||||||
|
return ADD_CITY
|
||||||
|
|
||||||
|
|
||||||
def add_city_callback(update: Update, context: CallbackContext):
|
def add_city_callback(update: Update, context: CallbackContext):
|
||||||
global users, subs_in_edit
|
global users
|
||||||
user = users[update.effective_chat.id]
|
user = users[update.effective_chat.id]
|
||||||
city_name = update.message.text
|
city_name = update.message.text
|
||||||
city = get_city_by_name(city_name)
|
city = get_city_by_name(city_name)
|
||||||
if city.name == "none":
|
if city.name == "none":
|
||||||
update.message.reply_text("This city was not found! Try again")
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
|
message="This city was not found! Try again")
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
update.message.reply_text(f"I found {city.name}. When do you want to receive updates?")
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
subs_in_edit[user.chat_id] = Subscription(user.chat_id, city)
|
message=f"I found {city.name}. When do you want to receive updates?")
|
||||||
return "time"
|
context.user_data['sub'] = Subscription(user.chat_id, city)
|
||||||
|
return ADD_TIME
|
||||||
|
|
||||||
|
|
||||||
def time_callback(update: Update, context: CallbackContext):
|
def add_time_callback(update: Update, context: CallbackContext):
|
||||||
global users, subs_in_edit
|
global users
|
||||||
user = users[update.effective_chat.id]
|
user = users[update.effective_chat.id]
|
||||||
sub_in_edit = subs_in_edit[update.effective_chat.id]
|
sub_in_edit = context.user_data['sub']
|
||||||
time_string = update.message.text
|
time_string = update.message.text
|
||||||
|
try:
|
||||||
if time_string.find(':') == -1:
|
if time_string.find(':') == -1:
|
||||||
sub_in_edit.time = time.strptime(time_string, "%H")
|
sub_in_edit.edit_time(time.strptime(time_string, "%H"))
|
||||||
else:
|
else:
|
||||||
sub_in_edit.time = time.strptime(time_string, "%H:%M")
|
sub_in_edit.edit_time(time.strptime(time_string, "%H:%M"))
|
||||||
|
except ValueError:
|
||||||
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
|
message="This time was not recognized! Try again")
|
||||||
|
return None
|
||||||
user.add_subscription(sub_in_edit)
|
user.add_subscription(sub_in_edit)
|
||||||
update.message.reply_text(f"Subscription now is: {str(sub_in_edit)}")
|
edit_message_and_delete_answer(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id,
|
||||||
del subs_in_edit[update.effective_chat.id]
|
message=f"Subscription now is: {str(sub_in_edit)}")
|
||||||
|
del context.user_data['sub']
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def cancel_callback(update: Update, context: CallbackContext):
|
def cancel_callback(update: Update, context: CallbackContext):
|
||||||
update.message.reply_text("Timeout")
|
context.bot.delete_message(chat_id=update.effective_chat.id, message_id=update.effective_message.message_id)
|
||||||
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
|
def cancel_button_callback(update: Update, context: CallbackContext):
|
||||||
|
update.callback_query.answer()
|
||||||
|
update.effective_message.delete()
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
|
||||||
|
|
||||||
def __init__() -> ConversationHandler:
|
def __init__() -> ConversationHandler:
|
||||||
load_users_from_file()
|
load_users_from_file()
|
||||||
|
|
||||||
push_handler = CommandHandler(command="push", callback=push_callback)
|
push_handler = CommandHandler(command=["push", "subs"], callback=push_callback)
|
||||||
|
|
||||||
edit_sub_handler = CallbackQueryHandler(callback=edit_sub_callback, pattern="^\/push_edit ")
|
edit_sub_handler = CallbackQueryHandler(callback=edit_sub_callback, pattern="^\/push_edit ")
|
||||||
edit_city_handler = CallbackQueryHandler(callback=edit_city_callback, pattern="^\/push_edit_city")
|
edit_city_handler = CallbackQueryHandler(callback=edit_city_callback, pattern="^\/push_edit_city")
|
||||||
|
@ -237,14 +268,14 @@ def __init__() -> ConversationHandler:
|
||||||
|
|
||||||
add_sub_handler = CallbackQueryHandler(callback=add_sub_callback, pattern="^\/push_add")
|
add_sub_handler = CallbackQueryHandler(callback=add_sub_callback, pattern="^\/push_add")
|
||||||
add_city_handler = MessageHandler(callback=add_city_callback, filters=Filters.text)
|
add_city_handler = MessageHandler(callback=add_city_callback, filters=Filters.text)
|
||||||
|
add_time_handler = MessageHandler(callback=add_time_callback, filters=Filters.text)
|
||||||
time_handler = MessageHandler(callback=time_callback, filters=Filters.text)
|
|
||||||
|
|
||||||
fallback_handler = CommandHandler(command="cancel", callback=cancel_callback)
|
fallback_handler = CommandHandler(command="cancel", callback=cancel_callback)
|
||||||
|
fallback_callback_handler = CallbackQueryHandler(callback=cancel_button_callback, pattern="^\/cancel")
|
||||||
|
|
||||||
return ConversationHandler(entry_points=[push_handler],
|
return ConversationHandler(entry_points=[push_handler],
|
||||||
states={"list": [edit_sub_handler, add_sub_handler], "add_city": [add_city_handler],
|
states={LIST: [edit_sub_handler, add_sub_handler], ADD_CITY: [add_city_handler],
|
||||||
"edit": [edit_city_handler, edit_time_handler, delete_subscription_handler],
|
EDIT: [edit_city_handler, edit_time_handler, delete_subscription_handler],
|
||||||
"edit_city": [edited_city_handler], "edit_time": [edited_time_handler],
|
EDIT_CITY: [edited_city_handler], EDIT_TIME: [edited_time_handler],
|
||||||
"time": [time_handler]},
|
ADD_TIME: [add_time_handler]},
|
||||||
fallbacks=[fallback_handler], conversation_timeout=10)
|
fallbacks=[fallback_handler, fallback_callback_handler])
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from telegram import ParseMode
|
||||||
|
|
||||||
from exceptions import ServerCommunicationError, NoArgError, WeatherStationNotFoundError
|
from exceptions import ServerCommunicationError, NoArgError, WeatherStationNotFoundError
|
||||||
|
|
||||||
|
@ -38,16 +39,11 @@ class HourlyWeather:
|
||||||
self.description = json_data['weather'][0]['description']
|
self.description = json_data['weather'][0]['description']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
string = ""
|
string = f"*{self.time.strftime('%Hh')}*: {self.main} | {self.temperature:.0f}°C 🧑: {self.feels_like:.0f}°C"
|
||||||
string += "*{time}*: {main} | {temperature:.0f}°C 🧑: {feels_like:.0f}°C" \
|
|
||||||
.format(time=self.time.strftime("%Hh"),
|
|
||||||
main=self.main,
|
|
||||||
temperature=self.temperature,
|
|
||||||
feels_like=self.feels_like)
|
|
||||||
if self.probability_of_precipitation != 0:
|
if self.probability_of_precipitation != 0:
|
||||||
string += " ☔: {pop:.0f}% {rain}mm".format(pop=100 * self.probability_of_precipitation, rain=self.rain)
|
string += f" ☔: {100 * self.probability_of_precipitation:.0f}% {self.rain}mm"
|
||||||
|
|
||||||
string += " 💨{wind_speed:.1f}km/h".format(wind_speed=3.6 * self.wind_speed)
|
string += f" 💨{3.6 * self.wind_speed:.1f}km/h"
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,17 +71,11 @@ class DailyWeather:
|
||||||
self.description = json_data['weather'][0]['description']
|
self.description = json_data['weather'][0]['description']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
string = ""
|
string = f"*{self.time.strftime('%d')}*: {self.main} | {self.temp['min']:.0f}°C - {self.temp['day']:.0f}°C" \
|
||||||
string += "*{time}*: {main} | {min}°C - {day}°C - {max}°C 🧑: {feels_like_day:.0f}°C" \
|
f" - {self.temp['max']:.0f}°C 🧑: {self.feels_like:.0f}°C"
|
||||||
.format(time=self.time.strftime("%d"),
|
|
||||||
main=self.main,
|
|
||||||
min=self.temp['min'],
|
|
||||||
day=self.temp['day'],
|
|
||||||
max=self.temp['max'],
|
|
||||||
feels_like_day=self.feels_like)
|
|
||||||
if self.probability_of_precipitation != 0:
|
if self.probability_of_precipitation != 0:
|
||||||
string += " ☔: {pop:.0f}% {rain}mm".format(pop=100 * self.probability_of_precipitation, rain=self.rain)
|
string += f" ☔: {100 * self.probability_of_precipitation:.0f}% {self.rain}mm"
|
||||||
string += " 💨: {wind_speed:.1f}km/h".format(wind_speed=3.6 * self.wind_speed)
|
string += f" 💨: {3.6 * self.wind_speed:.1f}km/h"
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,6 +184,6 @@ def handle_weather(update, context):
|
||||||
if requested_city.name == "none":
|
if requested_city.name == "none":
|
||||||
raise WeatherStationNotFoundError
|
raise WeatherStationNotFoundError
|
||||||
requested_city.query_weather()
|
requested_city.query_weather()
|
||||||
update.message.reply_text(str(requested_city.weather), disable_notification=True, parse_mode="Markdown")
|
update.message.reply_text(str(requested_city.weather), disable_notification=True, parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
requested_city.weather.clear()
|
requested_city.weather.clear()
|
||||||
|
|
Loading…
Reference in a new issue