From fde9cdaf4efb5629e3cc552c412ff821d477928c Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 18 Sep 2019 17:06:34 +0200 Subject: [PATCH] Create and use of User object, fix of notif, start of multi languages support and start help on commands and errors --- EDTcalendar.py | 12 +-- EDTuser.py | 16 ++++ Languages/en.json | 15 ++++ bot.py | 191 +++++++++++++++++++--------------------------- lang.py | 9 +++ 5 files changed, 123 insertions(+), 120 deletions(-) create mode 100644 EDTuser.py create mode 100644 Languages/en.json create mode 100644 lang.py diff --git a/EDTcalendar.py b/EDTcalendar.py index 3bdf1fc..f2a473d 100644 --- a/EDTcalendar.py +++ b/EDTcalendar.py @@ -16,7 +16,8 @@ class Calendar(ics.Calendar): self.events = events self.timeline = Timeline(self) - def _url(self, time: str, url: list, pass_week: bool): + @staticmethod + def _url(time: str, url: list, pass_week: bool): now = datetime.datetime.now(datetime.timezone.utc).astimezone(tz=None) if now.isoweekday() in [6, 7] and pass_week: now += datetime.timedelta(days=(7 - (now.isoweekday() - 1))) @@ -43,7 +44,6 @@ class Calendar(ics.Calendar): return msg - class Event(ics.Event): def __init__(self, event: ics.Event): super().__init__() @@ -57,8 +57,8 @@ class Event(ics.Event): def __str__(self): return markdown.text( markdown.bold(f"<{str(self.begin.date())[5:]}>"), - markdown.code(f"📓[{self.name}]:"), - markdown.text(f"⌚ī¸ {str(self.begin.time())[:-3]} -> {str(self.end.time())[:-3]}"), - markdown.italic(f"📍 {self.location} 👨‍đŸĢ {self.organizer}"), + markdown.code(f"\uD83D\uDCD3[{self.name}]:"), + markdown.text(f"\u231A{str(self.begin.time())[:-3]} -> {str(self.end.time())[:-3]}"), + markdown.italic(f"\uD83D\uDCCD{self.location} \uD83D\uDC68\u200D\uD83C\uDFEB{self.organizer}"), sep="\n" - ) + ) # surrogates not allowed diff --git a/EDTuser.py b/EDTuser.py new file mode 100644 index 0000000..6d69ae9 --- /dev/null +++ b/EDTuser.py @@ -0,0 +1,16 @@ +from EDTcalendar import Calendar +import datetime + + +class User: + def __init__(self, user_id: int, language: str): + self.id = user_id + self.language = language + self.resources = None + self.nt = False + self.nt_time = 20 + self.nt_cooldown = 20 + self.nt_last = datetime.datetime.now(datetime.timezone.utc).astimezone(tz=None) + + def calendar(self, time: str = "", pass_week: bool = False): + return Calendar(time, self.resources, pass_week=pass_week) diff --git a/Languages/en.json b/Languages/en.json new file mode 100644 index 0000000..b5e21c3 --- /dev/null +++ b/Languages/en.json @@ -0,0 +1,15 @@ +{ + "welcome": "\uD83D\uDCA0 *Welcome to the TelegramEDT bot !* \uD83D\uDCA0\n\uD83D\uDD39Please use the command /setedt to set your calendar\n\uD83D\uDD39Use /notif to configure events notification\n\u2139And /help for more commands", + "help": "\u2139 *Commands help* \u2139\n\uD83D\uDCC5/edt, show your next events\n\uD83D\uDD14/notif, set your events notifications\n\u2699/setedt, set your calendar", + "edt_err_set": "Your EDT is not set ! \u274C\n\u2139Use /setedt to fix that", + "edt_err_choice": "Invalid choice ! \u274C\n\u2139You can choose between: `day`, `next`, `week`, `next week`", + "setedt_err_res": "Invalid resources ! \u274C", + "setedt": "EDT set \u2705", + "getedt_err": "No EDT set ! \u274C", + "notif_set": "Notifications set on `{}` ! \u2705", + "notif_err_num": "Invalid number ! \u274C", + "notif_time_cooldown": "Notification `{}` set to `{}` ! \u2705", + "notif_info": "_Notification_\n*State:* {}\n*Time:* {}\n*Cooldown:* {}", + "notif_help": "\u2139 *Notif help* \u2139\n\uD83D\uDD39`toggle` to switch on/off notifications\n\uD83D\uDD39`time` to set time in minutes between notification and event\n\uD83D\uDD39`cooldown` set time in minutes between notification", + "notif_err_act": "Invalid action ! \u274C\n\u2139Use /notif to see help" +} \ No newline at end of file diff --git a/bot.py b/bot.py index f540121..1d609ac 100644 --- a/bot.py +++ b/bot.py @@ -13,6 +13,8 @@ from aiogram.types import InlineQuery, InputTextMessageContent, InlineQueryResul from aiogram.utils import markdown from aiogram.utils.exceptions import MessageIsTooLong from EDTcalendar import Calendar +from EDTuser import User +from lang import lang from ics.parse import ParseError from requests.exceptions import ConnectionError, InvalidSchema, MissingSchema @@ -40,14 +42,19 @@ dp = Dispatcher(bot) dbL = RLock() +def get_now(): + return datetime.datetime.now(datetime.timezone.utc).astimezone(tz=None) + + def calendar(time: str, user_id: int): with dbL: with shelve.open("edt", writeback=True) as db: - if str(user_id) not in db or "resources" not in db[str(user_id)]: - return markdown.bold("Your EDT is not set ! ❌") + user = db[str(user_id)] + if not user.resources: + return lang(user, "edt_err_set") elif time not in TIMES: - return markdown.bold("Invalid choice ! ❌") - return str(Calendar(time, db[str(user_id)]["resources"])) + return lang(user, "edt_err_choice") + return str(user.calendar(time)) async def notif(): @@ -55,16 +62,13 @@ async def notif(): with dbL: with shelve.open("edt", writeback=True) as db: for u in db: - if ("resources" in db[u]) and ("notif" in db[u]) and (db[u]["notif"]["state"]): - logger.info(f"notif check for {u}") - now = datetime.datetime.now(datetime.timezone.utc).astimezone(tz=None) - c = Calendar("day", db[u]["resources"], pass_week=False) + if db[u].resources and db[u].nt: + now = get_now() + c = db[u].calendar(pass_week=True) for e in c.timeline: - logger.info(f"{(e.begin - now).total_seconds().__abs__()//60.} <= {db[u]['notif']['time']} and {(now - db[u]['notif']['last']).total_seconds()//60} | >= {db[u]['notif']['cooldown']}") - logger.info(f"{(e.begin - now).total_seconds().__abs__()//60 <= db[u]['notif']['time']} and {(now - db[u]['notif']['last']).total_seconds()//60 >= db[u]['notif']['cooldown']}") - if (e.begin - now).total_seconds().__abs__()//60 <= db[u]["notif"]["time"] and\ - (now - db[u]["notif"]["last"]).total_seconds()//60 >= db[u]["notif"]["cooldown"]: - db[u]["notif"]["last"] = now + if 0 <= (e.begin - now).total_seconds().__abs__()//60 <= db[u].nt_time and \ + 0 <= (now - db[u].nt_last).total_seconds()//60 >= db[u].nt_cooldown: + db[u].nt_last = get_now() await bot.send_message(int(u), e, parse_mode=ParseMode.MARKDOWN) await sleep(60) @@ -83,49 +87,30 @@ async def inline_edt(inline_query: InlineQuery): await bot.answer_inline_query(inline_query.id, results=[item], cache_time=1) -@dp.message_handler(commands=["start", "help"]) -async def send_welcome(message: types.Message): +@dp.message_handler(commands="Start") +async def start(message: types.Message): + user_id = str(message.from_user.id) await message.chat.do(types.ChatActions.TYPING) - logger.info(f"{message.from_user.username} do start/help command: {message.text}") + logger.info(f"{message.from_user.username} start : {message.text}") with dbL: with shelve.open("edt", writeback=True) as db: - if str(message.from_user.id) not in db: - db[str(message.from_user.id)] = dict() - logger.info(f"db creation for {message.from_user.username}") - - msg = markdown.text( - markdown.text("💠 Welcome to the TelegramEDT, a calendar bot for the Lyon 1 University ! 💠\n"), - markdown.text( - markdown.text("🗓"), - markdown.code("/edt [day | next | week | next week]"), - markdown.text(", for show your next course") - ), - markdown.text( - markdown.text("🔔"), - markdown.code("/notif "), - markdown.text(", setup notifications") - ), - markdown.text( - markdown.text("⚙ī¸"), - markdown.code("/setedt "), - markdown.text(", to setup your calendar\nThe resources can be get on the url of exported calendar") - ), - markdown.text( - markdown.text("🔗"), - markdown.code("/getedt"), - markdown.text(", to get your calendar url") - ), - markdown.text( - markdown.text("ℹī¸"), - markdown.code("/help"), - markdown.text(", to show this command") - ), - sep="\n" - ) - await message.reply(msg, parse_mode=ParseMode.MARKDOWN) + if user_id not in db: + db[user_id] = User(int(user_id), message.from_user.locale.language) + user = db[user_id] + await message.reply(lang(user, "welcome"), parse_mode=ParseMode.MARKDOWN) -@dp.message_handler(commands=["edt"]) +@dp.message_handler(commands="help") +async def send_welcome(message: types.Message): + await message.chat.do(types.ChatActions.TYPING) + logger.info(f"{message.from_user.username} do help command: {message.text}") + with dbL: + with shelve.open("edt", writeback=True) as db: + user = db[str(message.from_user.id)] + await message.reply(lang(user, "help"), parse_mode=ParseMode.MARKDOWN) + + +@dp.message_handler(commands="edt") @dp.message_handler(lambda msg: msg.text.lower() in TIMES[1:]) async def edt_cmd(message: types.Message): await message.chat.do(types.ChatActions.TYPING) @@ -142,111 +127,89 @@ async def edt_cmd(message: types.Message): await message.reply(resp, parse_mode=ParseMode.MARKDOWN, reply_markup=key) -@dp.message_handler(commands=["setedt"]) +@dp.message_handler(commands="setedt") async def edt_set(message: types.Message): + user_id = str(message.from_user.id) await message.chat.do(types.ChatActions.TYPING) logger.info(f"{message.from_user.username} do setedt command: {message.text}") resources = message.text[8:] - try: - Calendar("", int(resources)) - except (ParseError, ConnectionError, InvalidSchema, MissingSchema, ValueError): - msg = markdown.bold("Invalid resources ! ❌") - else: - with dbL: - with shelve.open("edt", writeback=True) as db: - db[str(message.from_user.id)]["resources"] = int(resources) - msg = markdown.text("EDT set ✅") + with dbL: + with shelve.open("edt", writeback=True) as db: + try: + Calendar("", int(resources)) + except (ParseError, ConnectionError, InvalidSchema, MissingSchema, ValueError): + msg = lang(db[user_id], "setedt_err_res") + else: + db[user_id].resources = int(resources) + msg = lang(db[user_id], "setedt") - await message.reply(msg, parse_mode=ParseMode.MARKDOWN) + await message.reply(msg, parse_mode=ParseMode.MARKDOWN) -@dp.message_handler(commands=["getedt"]) +@dp.message_handler(commands="getedt") async def edt_geturl(message: types.Message): + user_id = str(message.from_user.id) await message.chat.do(types.ChatActions.TYPING) logger.info(f"{message.from_user.username} do getedt command: {message.text}") with dbL: with shelve.open("edt", writeback=True) as db: - if (str(message.from_user.id) in db) and ("resources" in db[str(message.from_user.id)]): - await message.reply(db[str(message.from_user.id)]["resources"]) + if db[user_id].resources: + await message.reply(db[user_id].resources) else: - await message.reply("No EDT set ! ❌") + await message.reply(lang(db[user_id], "getedt_err")) -@dp.message_handler(commands=["notif"]) +@dp.message_handler(commands="notif") async def notif_cmd(message: types.Message): + user_id = str(message.from_user.id) await message.chat.do(types.ChatActions.TYPING) logger.info(f"{message.from_user.username} do notif command: {message.text}") with dbL: with shelve.open("edt", writeback=True) as db: - if "notif" not in db[str(message.from_user.id)]: - db[str(message.from_user.id)]["notif"] = dict() - db[str(message.from_user.id)]["notif"]["state"] = False - db[str(message.from_user.id)]["notif"]["time"] = 20 - db[str(message.from_user.id)]["notif"]["cooldown"] = 20 - last = datetime.datetime.now(datetime.timezone.utc).astimezone(tz=None) - datetime.timedelta(minutes=20) - db[str(message.from_user.id)]["notif"]["last"] = last - - if message.text[7:10] == "set": - if db[str(message.from_user.id)]["notif"]["state"]: + if message.text[7:10] == "toggle": + if db[user_id].nt: res = False else: res = True - db[str(message.from_user.id)]["notif"]["state"] = res + db[user_id].nt = res + msg = lang(db[user_id], "notif_set").format(res) - msg = markdown.text( - markdown.text("Notifications set on "), - markdown.code(res), - markdown.text("✅") - ) elif message.text[7:11] == "time" or message.text[7:15] == "cooldown": cut = 11 if message.text[7:11] == "time" else 15 try: int(message.text[cut+1:]) except ValueError: - msg = markdown.bold("Invalid number ! ❌") + msg = lang(db[user_id], "notif_err_num") else: - db[str(message.from_user.id)]["notif"][message.text[7:cut]] = int(message.text[cut+1:]) + if cut == 11: + db[user_id]["notif"].nt_time = int(message.text[cut+1:]) + else: + db[user_id]["notif"].nt_cooldown = int(message.text[cut + 1:]) + + msg = lang(db(user_id), "notif_time_cooldown").format(message.text[7:cut], message.text[cut + 1:]) - msg = markdown.text( - markdown.text("Notification"), - markdown.code(message.text[7:cut]), - markdown.text("set to"), - markdown.bold(message.text[cut+1:]), - markdown.text("✅") - ) elif message.text[7:11] == "info": - msg = markdown.text( - markdown.code("Notification:"), - markdown.text( - markdown.bold("State:"), - markdown.text(db[str(message.from_user.id)]["notif"]["state"]) - ), - markdown.text( - markdown.bold("Time:"), - markdown.text(db[str(message.from_user.id)]["notif"]["time"]) - ), - markdown.text( - markdown.bold("Cooldown:"), - markdown.text(db[str(message.from_user.id)]["notif"]["cooldown"]) - ), - sep="\n" - ) + msg = lang(db[user_id], "notif_info").format(db[user_id].nt, db[user_id].nt_time, + db[user_id].nt_cooldown) + + elif message.text[7:] == "": + msg = lang(db[user_id], "notif_help") else: - msg = markdown.bold("Invalid action ! ❌") + msg = lang(db[user_id], "notif_err_act") await message.reply(msg, parse_mode=ParseMode.MARKDOWN) -@dp.message_handler(commands=["getid"]) +@dp.message_handler(commands="getid") async def get_id(message: types.Message): await message.chat.do(types.ChatActions.TYPING) logger.info(f"{message.from_user.username} do getid command: {message.text}") await message.reply(message.from_user.id) -@dp.message_handler(commands=["getlogs"]) +@dp.message_handler(commands="getlogs") async def get_logs(message: types.Message): logger.info(f"{message.from_user.username} do getlog command: {message.text}") if message.from_user.id == ADMIN_ID: @@ -272,7 +235,7 @@ async def get_logs(message: types.Message): await message.reply(markdown.bold("Too much logs ! ❌")) -@dp.message_handler(commands=["getdb"]) +@dp.message_handler(commands="getdb") async def get_db(message: types.Message): logger.info(f"{message.from_user.username} do getdb command: {message.text}") if message.from_user.id == ADMIN_ID: @@ -286,8 +249,8 @@ async def get_db(message: types.Message): await message.reply(msg, parse_mode=ParseMode.MARKDOWN) -@dp.message_handler(commands=["eval"]) -async def get_db(message: types.Message): +@dp.message_handler(commands="eval") +async def eval_cmd(message: types.Message): logger.info(f"{message.from_user.username} do eval command: {message.text}") if message.from_user.id == ADMIN_ID: msg = markdown.text( diff --git a/lang.py b/lang.py new file mode 100644 index 0000000..d05f46e --- /dev/null +++ b/lang.py @@ -0,0 +1,9 @@ +import json +from EDTuser import User + +LANG = ["en"] + + +def lang(user: User, message: str): + language = user.language if user.language in LANG else LANG[0] + return json.loads(open(f"Languages/{language}.json", "r").read())[message]