Added push_feature for weather
Enables user to get weather information every day at a set time for a set city City and time can be changed and the subscription can be deleted. Formatting can be optimized
This commit is contained in:
		
							parent
							
								
									172efd7974
								
							
						
					
					
						commit
						d6debf6a46
					
				
					 3 changed files with 283 additions and 2 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -5,6 +5,9 @@
 | 
			
		|||
token
 | 
			
		||||
openweathermap_token
 | 
			
		||||
 | 
			
		||||
#storage/pickle
 | 
			
		||||
*.pkl
 | 
			
		||||
 | 
			
		||||
# Byte-compiled / optimized / DLL files
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.py[cod]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								bot.py
									
										
									
									
									
								
							
							
						
						
									
										32
									
								
								bot.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,11 +1,17 @@
 | 
			
		|||
import logging
 | 
			
		||||
import threading
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import schedule
 | 
			
		||||
from telegram.ext import Updater, CommandHandler, InlineQueryHandler, CallbackQueryHandler
 | 
			
		||||
 | 
			
		||||
from exceptions import *
 | 
			
		||||
from vvs import inline_station_search, handle_vvs, handle_multiple_stations_reply
 | 
			
		||||
from weather_meteomedia import handle_meteomedia
 | 
			
		||||
from weather_openweathermap import handle_weather, __init__
 | 
			
		||||
from weather_openweathermap import handle_weather
 | 
			
		||||
from weather_openweathermap import __init__ as openweathermap_init
 | 
			
		||||
from push_information import __init__ as push_init
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
token_file = open("token", "r")
 | 
			
		||||
updater = Updater(token=token_file.read(), use_context=True)
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +22,21 @@ dispatcher = updater.dispatcher
 | 
			
		|||
logging.basicConfig(format='%(acstime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def send_message(chat_id: int, message: str):
 | 
			
		||||
    dispatcher.bot.send_message(chat_id=chat_id, text=message, disable_notification=True, parse_mode="Markdown")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cease_continuous_run = threading.Event()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScheduleThread(threading.Thread):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def run(cls):
 | 
			
		||||
        while not cease_continuous_run.is_set():
 | 
			
		||||
            schedule.run_pending()
 | 
			
		||||
            time.sleep(10)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __main__():
 | 
			
		||||
    inline_station_search_handler = InlineQueryHandler(inline_station_search)
 | 
			
		||||
    dispatcher.add_handler(inline_station_search_handler)
 | 
			
		||||
| 
						 | 
				
			
			@ -23,14 +44,21 @@ def __main__():
 | 
			
		|||
    dispatcher.add_handler(CommandHandler('meteomedia', handle_meteomedia))
 | 
			
		||||
    dispatcher.add_handler(CommandHandler("weather", handle_weather))
 | 
			
		||||
 | 
			
		||||
    __init__()
 | 
			
		||||
    openweathermap_init()
 | 
			
		||||
    dispatcher.add_handler(push_init())
 | 
			
		||||
 | 
			
		||||
    dispatcher.add_error_handler(error_callback)
 | 
			
		||||
 | 
			
		||||
    dispatcher.add_handler(CallbackQueryHandler(handle_multiple_stations_reply, pattern="^\/vvs"))
 | 
			
		||||
 | 
			
		||||
    continuous_thread = ScheduleThread()
 | 
			
		||||
    continuous_thread.start()
 | 
			
		||||
 | 
			
		||||
    updater.start_polling()
 | 
			
		||||
    updater.idle()
 | 
			
		||||
 | 
			
		||||
    cease_continuous_run.set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    __main__()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										250
									
								
								push_information.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								push_information.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,250 @@
 | 
			
		|||
import _pickle as pickle
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import schedule
 | 
			
		||||
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
 | 
			
		||||
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, CallbackQueryHandler, CallbackContext
 | 
			
		||||
from telegram.ext.filters import Filters
 | 
			
		||||
 | 
			
		||||
from bot import send_message
 | 
			
		||||
from weather_openweathermap import City, get_city_by_name
 | 
			
		||||
 | 
			
		||||
users = {}
 | 
			
		||||
subs_in_edit = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Subscription(object):
 | 
			
		||||
    time: time
 | 
			
		||||
    city: City
 | 
			
		||||
    chat_id: int
 | 
			
		||||
 | 
			
		||||
    def __init__(self, chat_id: int, city: City):
 | 
			
		||||
        self.chat_id = chat_id
 | 
			
		||||
        self.city = city
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"{self.city.name} at {time.strftime('%H:%M', self.time)}"
 | 
			
		||||
 | 
			
		||||
    def edit_time(self, edited_time: time):
 | 
			
		||||
        self.time = edited_time
 | 
			
		||||
        schedule_subscriptions()
 | 
			
		||||
        save_users_to_file()
 | 
			
		||||
 | 
			
		||||
    def edit_city(self, edited_city: City):
 | 
			
		||||
        self.city = edited_city
 | 
			
		||||
        schedule_subscriptions()
 | 
			
		||||
        save_users_to_file()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class User(object):
 | 
			
		||||
    chat_id: int
 | 
			
		||||
    subs = []
 | 
			
		||||
 | 
			
		||||
    def __init__(self, chat_id: int):
 | 
			
		||||
        self.chat_id = chat_id
 | 
			
		||||
        global users
 | 
			
		||||
        users[self.chat_id] = self.subs
 | 
			
		||||
 | 
			
		||||
    def __getstate__(self):
 | 
			
		||||
        attributes = self.__dict__.copy()
 | 
			
		||||
        attributes['subs'] = self.subs
 | 
			
		||||
        for i in range(len(attributes['subs'])):
 | 
			
		||||
            try:
 | 
			
		||||
                del attributes['subs'][i].city.weather
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                pass
 | 
			
		||||
        return attributes
 | 
			
		||||
 | 
			
		||||
    def __setstate__(self, state):
 | 
			
		||||
        self.chat_id = state['chat_id']
 | 
			
		||||
        self.subs = state['subs']
 | 
			
		||||
 | 
			
		||||
    def add_subscription(self, subscription: Subscription):
 | 
			
		||||
        self.subs.append(subscription)
 | 
			
		||||
        global users
 | 
			
		||||
        users[self.chat_id] = self
 | 
			
		||||
        save_users_to_file()
 | 
			
		||||
        schedule_subscriptions()
 | 
			
		||||
 | 
			
		||||
    def del_subscription(self, idx: int):
 | 
			
		||||
        del self.subs[idx]
 | 
			
		||||
        save_users_to_file()
 | 
			
		||||
        schedule_subscriptions()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def save_users_to_file():
 | 
			
		||||
    global users
 | 
			
		||||
    with open("users.pkl", 'wb') as users_file:
 | 
			
		||||
        pickle.dump(users, users_file)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_users_from_file():
 | 
			
		||||
    global users
 | 
			
		||||
    try:
 | 
			
		||||
        with open("users.pkl", 'rb') as users_file:
 | 
			
		||||
            users = pickle.load(users_file)
 | 
			
		||||
        schedule_subscriptions()
 | 
			
		||||
    except FileNotFoundError:
 | 
			
		||||
        pass
 | 
			
		||||
    except EOFError:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def schedule_subscriptions():
 | 
			
		||||
    schedule.default_scheduler.clear()
 | 
			
		||||
    for user in users.values():
 | 
			
		||||
        for sub in user.subs:
 | 
			
		||||
            sub.city.query_weather()
 | 
			
		||||
            schedule.default_scheduler.every().day.at(time.strftime("%H:%M", sub.time)).do(
 | 
			
		||||
                send_message, chat_id=user.chat_id, message=str(sub.city.weather))
 | 
			
		||||
            sub.city.weather.clear()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def push_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    global users
 | 
			
		||||
    if update.effective_chat.id in users:
 | 
			
		||||
        user = users[update.effective_chat.id]
 | 
			
		||||
    else:
 | 
			
		||||
        user = User(update.effective_chat.id)
 | 
			
		||||
        users[update.effective_chat.id] = user
 | 
			
		||||
    string = f"Hello, {update.effective_user.first_name}!\n"
 | 
			
		||||
    string += f"You have {len(user.subs)} subscriptions:\n"
 | 
			
		||||
 | 
			
		||||
    button_list = []
 | 
			
		||||
    for i, subscription in enumerate(user.subs):
 | 
			
		||||
        button_list.append(
 | 
			
		||||
            InlineKeyboardButton(text=f"{subscription.city.name} at {time.strftime('%H:%M', subscription.time)}",
 | 
			
		||||
                                 callback_data=f"/push_edit {i}"))
 | 
			
		||||
    button_list.append(InlineKeyboardButton(text="Add", callback_data="/push_add"))
 | 
			
		||||
    reply_markup = InlineKeyboardMarkup.from_column(button_list)
 | 
			
		||||
    update.message.reply_text(string, reply_markup=reply_markup)
 | 
			
		||||
    return "list"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def edit_sub_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    global users, subs_in_edit
 | 
			
		||||
    user = users[update.effective_chat.id]
 | 
			
		||||
    sub_idx = int(update.callback_query.data[11:])
 | 
			
		||||
    sub = user.subs[sub_idx]
 | 
			
		||||
    subs_in_edit[update.effective_chat.id] = sub
 | 
			
		||||
    button_list = [[InlineKeyboardButton(text="City", callback_data="/push_edit_city"),
 | 
			
		||||
                    InlineKeyboardButton(text="Time", callback_data="/push_edit_time")],
 | 
			
		||||
                   [InlineKeyboardButton(text="Delete", callback_data=f"/push_edit_delete {sub_idx}")]]
 | 
			
		||||
    reply_markup = InlineKeyboardMarkup(button_list)
 | 
			
		||||
    update.effective_chat.send_message("Change subscription.\nWhat do you want to change?", reply_markup=reply_markup)
 | 
			
		||||
    return "edit"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def edit_city_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    update.effective_chat.send_message("Tell me the city you want information for:")
 | 
			
		||||
    return "edit_city"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def edited_city_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    global users, subs_in_edit
 | 
			
		||||
    user = users[update.effective_chat.id]
 | 
			
		||||
    city_name = update.message.text
 | 
			
		||||
    city = get_city_by_name(city_name)
 | 
			
		||||
    if city.name == "none":
 | 
			
		||||
        update.message.reply_text("This city was not found! Try again")
 | 
			
		||||
        return None
 | 
			
		||||
    else:
 | 
			
		||||
        subs_in_edit[user.chat_id].edit_city(city)
 | 
			
		||||
        update.message.reply_text(f"City changed to {city.name}.")
 | 
			
		||||
        del subs_in_edit[update.effective_chat.id]
 | 
			
		||||
        update.message.reply_text(f"Subscription now is {subs_in_edit[user.chat_id]}")
 | 
			
		||||
        return ConversationHandler.END
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def edit_time_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    update.effective_chat.send_message("When do you want to receive updates?")
 | 
			
		||||
    return "edit_time"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def edited_time_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    global users
 | 
			
		||||
    user = users[update.effective_chat.id]
 | 
			
		||||
    sub_in_edit = subs_in_edit[user.chat_id]
 | 
			
		||||
    time_string = update.message.text
 | 
			
		||||
    if time_string.find(':') == -1:
 | 
			
		||||
        sub_in_edit.edit_time(time.strptime(time_string, "%H"))
 | 
			
		||||
    else:
 | 
			
		||||
        sub_in_edit.edit_time(time.strptime(time_string, "%H:%M"))
 | 
			
		||||
    del subs_in_edit[user.chat_id]
 | 
			
		||||
    update.message.reply_text(f"Subscription now is {sub_in_edit}")
 | 
			
		||||
    return ConversationHandler.END
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_subscription_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    global users
 | 
			
		||||
    users[update.effective_chat.id].del_subscription(int(update.callback_query.data[18:]))
 | 
			
		||||
    update.effective_chat.send_message("Deleted")
 | 
			
		||||
    schedule_subscriptions()
 | 
			
		||||
    return ConversationHandler.END
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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:")
 | 
			
		||||
    return "add_city"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_city_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    global users, subs_in_edit
 | 
			
		||||
    user = users[update.effective_chat.id]
 | 
			
		||||
    city_name = update.message.text
 | 
			
		||||
    city = get_city_by_name(city_name)
 | 
			
		||||
    if city.name == "none":
 | 
			
		||||
        update.message.reply_text("This city was not found! Try again")
 | 
			
		||||
        return None
 | 
			
		||||
    else:
 | 
			
		||||
        update.message.reply_text(f"I found {city.name}. When do you want to receive updates?")
 | 
			
		||||
        subs_in_edit[user.chat_id] = Subscription(user.chat_id, city)
 | 
			
		||||
    return "time"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def time_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    global users, subs_in_edit
 | 
			
		||||
    user = users[update.effective_chat.id]
 | 
			
		||||
    sub_in_edit = subs_in_edit[update.effective_chat.id]
 | 
			
		||||
    time_string = update.message.text
 | 
			
		||||
    if time_string.find(':') == -1:
 | 
			
		||||
        sub_in_edit.time = time.strptime(time_string, "%H")
 | 
			
		||||
    else:
 | 
			
		||||
        sub_in_edit.time = time.strptime(time_string, "%H:%M")
 | 
			
		||||
    user.add_subscription(sub_in_edit)
 | 
			
		||||
    update.message.reply_text(f"Subscription now is: {str(sub_in_edit)}")
 | 
			
		||||
    del subs_in_edit[update.effective_chat.id]
 | 
			
		||||
    return ConversationHandler.END
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def cancel_callback(update: Update, context: CallbackContext):
 | 
			
		||||
    update.message.reply_text("Timeout")
 | 
			
		||||
    return ConversationHandler.END
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def __init__() -> ConversationHandler:
 | 
			
		||||
    load_users_from_file()
 | 
			
		||||
 | 
			
		||||
    push_handler = CommandHandler(command="push", callback=push_callback)
 | 
			
		||||
 | 
			
		||||
    edit_sub_handler = CallbackQueryHandler(callback=edit_sub_callback, pattern="^\/push_edit ")
 | 
			
		||||
    edit_city_handler = CallbackQueryHandler(callback=edit_city_callback, pattern="^\/push_edit_city")
 | 
			
		||||
    edited_city_handler = MessageHandler(callback=edited_city_callback, filters=Filters.text)
 | 
			
		||||
    edit_time_handler = CallbackQueryHandler(callback=edit_time_callback, pattern="^\/push_edit_time")
 | 
			
		||||
    edited_time_handler = MessageHandler(callback=edited_time_callback, filters=Filters.text)
 | 
			
		||||
    delete_subscription_handler = CallbackQueryHandler(callback=delete_subscription_callback,
 | 
			
		||||
                                                       pattern="^\/push_edit_delete")
 | 
			
		||||
 | 
			
		||||
    add_sub_handler = CallbackQueryHandler(callback=add_sub_callback, pattern="^\/push_add")
 | 
			
		||||
    add_city_handler = MessageHandler(callback=add_city_callback, filters=Filters.text)
 | 
			
		||||
 | 
			
		||||
    time_handler = MessageHandler(callback=time_callback, filters=Filters.text)
 | 
			
		||||
 | 
			
		||||
    fallback_handler = CommandHandler(command="cancel", callback=cancel_callback)
 | 
			
		||||
 | 
			
		||||
    return ConversationHandler(entry_points=[push_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_city": [edited_city_handler], "edit_time": [edited_time_handler],
 | 
			
		||||
                                       "time": [time_handler]},
 | 
			
		||||
                               fallbacks=[fallback_handler], conversation_timeout=10)
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue