├── requirements.txt ├── .env.example ├── bot.py ├── config.py ├── main.py ├── views_example.py ├── .gitignore └── handlers.py /requirements.txt: -------------------------------------------------------------------------------- 1 | python-dotenv 2 | pyTelegramBotAPI 3 | schedule 4 | fuzzywuzzy -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | API_TOKEN="" 2 | ADMIN_TELEGRAM_ID="" 3 | TARGET_TELEGRAM_ID="" 4 | LOG_GROUP_ID="" -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | from telebot import TeleBot 2 | import config 3 | 4 | API_TOKEN = config.API_TOKEN 5 | bot = TeleBot(API_TOKEN) -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | 5 | load_dotenv() 6 | 7 | API_TOKEN = os.getenv('API_TOKEN') 8 | ADMIN_TELEGRAM_ID = os.getenv('ADMIN_TELEGRAM_ID') 9 | TARGET_TELEGRAM_ID = os.getenv('TARGET_TELEGRAM_ID') 10 | LOG_GROUP_ID = os.getenv('LOG_GROUP_ID') 11 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import time 3 | import schedule 4 | from handlers import bot 5 | 6 | if __name__ == '__main__': 7 | threading.Thread(target=bot.infinity_polling, name='bot_infinity_polling', daemon=True).start() 8 | while True: 9 | schedule.run_pending() 10 | time.sleep(1) -------------------------------------------------------------------------------- /views_example.py: -------------------------------------------------------------------------------- 1 | from random import choice 2 | 3 | class ReminderTexts: 4 | 5 | def messages_tag(self): 6 | messages_tag = { 7 | self.meds_before_breakfast_text(): "meds_before_breakfast_text", 8 | self.meds_12_hour_morning_text(): "meds_12_hour_morning_text", 9 | self.meds_12_hour_night_text(): "meds_12_hour_night_text", 10 | self.meds_8_hour_text(): "meds_8_hour_text", 11 | self.meds_6_hour_text(): "meds_6_hour_text", 12 | self.meds_meanwhile_launch(): "meds_meanwhile_launch", 13 | self.meds_before_dinner(): "meds_before_dinner", 14 | self.meds_meanwhile_dinner(): "meds_meanwhile_dinner", 15 | self.meds_night(): "meds_night", 16 | self.tooth_care(): "tooth_care", 17 | self.dental_floss(): "dental_floss", 18 | } 19 | return messages_tag 20 | 21 | def meds_before_breakfast_text(self): 22 | message = """""" 23 | return message 24 | 25 | def meds_12_hour_morning_text(self): 26 | message = """""" 27 | return message 28 | 29 | def meds_12_hour_night_text(self): 30 | message = """""" 31 | return message 32 | 33 | def meds_8_hour_text(self): 34 | message = """""" 35 | return message 36 | 37 | def meds_6_hour_text(self): 38 | message = """""" 39 | return message 40 | 41 | def meds_meanwhile_launch(self): 42 | message = """""" 43 | return message 44 | 45 | def meds_before_dinner(self): 46 | message = """""" 47 | return message 48 | 49 | def meds_meanwhile_dinner(self): 50 | message = """""" 51 | return message 52 | 53 | def meds_night(self): 54 | message = """""" 55 | return message 56 | 57 | def dental_floss(self): 58 | message = """""" 59 | return message 60 | 61 | def took_medicine(self): 62 | return "" 63 | 64 | def snooze(self, snooze_time): 65 | return f"" 66 | 67 | def beep(self): 68 | messages = [""] 69 | return choice(messages) 70 | 71 | def start(self): 72 | message = "She Matters :)" 73 | return message 74 | 75 | def tooth_care(self): 76 | message = """""" 77 | return message 78 | 79 | def tooth_care_markup(self): 80 | return "" 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | views.py -------------------------------------------------------------------------------- /handlers.py: -------------------------------------------------------------------------------- 1 | from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton 2 | from bot import bot 3 | import schedule 4 | import threading 5 | from fuzzywuzzy import fuzz 6 | import config 7 | from views import ReminderTexts 8 | 9 | class Snooze: 10 | def __init__(self) -> None: 11 | self.cancel_tags = list() 12 | 13 | class TempMessages: 14 | def __init__(self) -> None: 15 | self.to_delete = dict() 16 | 17 | 18 | def find_tag(input_string, dictionary=ReminderTexts().messages_tag()): 19 | closest_key = None 20 | highest_similarity = -1 21 | 22 | for key in dictionary.keys(): 23 | similarity = fuzz.ratio(input_string, key) 24 | if similarity > highest_similarity: 25 | highest_similarity = similarity 26 | closest_key = key 27 | 28 | return dictionary[closest_key] 29 | 30 | 31 | snooze = Snooze() 32 | temp_messages = TempMessages() 33 | 34 | def gen_markup(snooze, done_text): 35 | markup = InlineKeyboardMarkup() 36 | markup.row_width = 2 37 | markup.add(InlineKeyboardButton(done_text, callback_data="done"), 38 | InlineKeyboardButton(ReminderTexts().snooze(snooze), callback_data="snooze")) 39 | return markup 40 | 41 | @bot.callback_query_handler(func=lambda call: True) 42 | def callback_query(call): 43 | tag = find_tag(call.message.text) 44 | cancel_reminder_schedule(tag) 45 | if call.data == "done": 46 | bot.edit_message_reply_markup(call.from_user.id, call.message.id) 47 | snooze.cancel_tags.append(tag) 48 | if tag in temp_messages.to_delete: 49 | for to_delete_message_id in temp_messages.to_delete.get(tag): 50 | bot.delete_message(call.from_user.id, to_delete_message_id) 51 | temp_messages.to_delete[tag] = list() 52 | bot.send_message(config.ADMIN_TELEGRAM_ID, f"Clicked on done {tag}.") 53 | elif call.data == "snooze": 54 | threading.Timer(10 * 60, set_reminder_schedule, args=(2 * 60, beep, config.TARGET_TELEGRAM_ID, tag, True)).start() 55 | bot.answer_callback_query(call.id, "Snoozed") 56 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Snoozed.") 57 | 58 | def set_reminder_schedule(remind_each, job, telegram_id, tag, snoozed=False): 59 | if tag in snooze.cancel_tags: 60 | snooze.cancel_tags.remove(tag) 61 | if snoozed: 62 | return 63 | 64 | schedule.every(remind_each).seconds.do(job, telegram_id, tag).tag(tag) 65 | 66 | def cancel_reminder_schedule(tag): 67 | schedule.clear(tag) 68 | 69 | def set_reminder(text, remind_each_minute, tag, markup_done_text=ReminderTexts().took_medicine()): 70 | remind_each_second = remind_each_minute * 60 71 | bot.send_message(config.TARGET_TELEGRAM_ID, text, reply_markup=gen_markup(10, markup_done_text)) 72 | bot.send_message(config.ADMIN_TELEGRAM_ID, f"Sent {tag} text.") 73 | set_reminder_schedule(remind_each_second, beep, config.TARGET_TELEGRAM_ID, tag) 74 | 75 | 76 | 77 | @bot.message_handler(commands=['help', 'start']) 78 | def send_welcome(message): 79 | bot.reply_to(message, ReminderTexts().start()) 80 | 81 | @bot.message_handler(commands=['woke_up']) 82 | def woke_up(message): 83 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_before_breakfast_text(), 7, "meds_before_breakfast_text")).start() 84 | 85 | threading.Timer(15 * 60, set_reminder, args=(ReminderTexts().meds_12_hour_morning_text(), 5, "meds_12_hour_morning_text")).start() 86 | threading.Timer(12 * 60 * 60, set_reminder, args=(ReminderTexts().meds_12_hour_night_text(), 5, "meds_12_hour_night_text")).start() 87 | 88 | threading.Timer(15 * 60, set_reminder, args=(ReminderTexts().meds_8_hour_text(), 5, "meds_8_hour_text")).start() 89 | threading.Timer(7 * 60 * 60, set_reminder, args=(ReminderTexts().meds_8_hour_text(), 5, "meds_8_hour_text")).start() 90 | threading.Timer(14 * 60 * 60, set_reminder, args=(ReminderTexts().meds_8_hour_text(), 5, "meds_8_hour_text")).start() 91 | 92 | threading.Timer(12.01 * 60 * 60, set_reminder, args=(ReminderTexts().meds_night(), 5, "meds_night")).start() 93 | 94 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 95 | 96 | @bot.message_handler(commands=['launch']) 97 | def launch(message): 98 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_meanwhile_launch(), 2, "meds_meanwhile_launch")).start() 99 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 100 | 101 | @bot.message_handler(commands=['dinner']) 102 | def dinner(message): 103 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_meanwhile_dinner(), 2, "meds_meanwhile_dinner")).start() 104 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 105 | 106 | 107 | 108 | @bot.message_handler(commands=['only_meds_12_hour_morning']) 109 | def only_meds_12_hour_morning(message): 110 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_12_hour_morning_text(), 2, "meds_12_hour_morning_text")).start() 111 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 112 | 113 | @bot.message_handler(commands=['only_meds_12_hour_night']) 114 | def only_meds_12_hour_night(message): 115 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_12_hour_night_text(), 2, "meds_12_hour_night_text")).start() 116 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 117 | 118 | @bot.message_handler(commands=['only_meds_before_dinner']) 119 | def only_meds_before_dinner(message): 120 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_before_dinner(), 2, "meds_before_dinner")).start() 121 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 122 | 123 | @bot.message_handler(commands=['only_meds_night']) 124 | def only_meds_night(message): 125 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_night(), 2, "meds_night")).start() 126 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 127 | 128 | @bot.message_handler(commands=['only_meds_8_hour']) 129 | def only_meds_8_hour(message): 130 | threading.Timer(0, set_reminder, args=(ReminderTexts().meds_8_hour_text(), 2, "meds_8_hour_text")).start() 131 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 132 | 133 | @bot.message_handler(commands=['only_tooth_care']) 134 | def only_tooth_care(message): 135 | threading.Timer(0, set_reminder, args=(ReminderTexts().tooth_care(), 2, "tooth_care", ReminderTexts().tooth_care_markup())).start() 136 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 137 | 138 | 139 | 140 | @bot.message_handler(commands=['sleep']) 141 | def sleep_routine(message): 142 | threading.Timer(0, set_reminder, args=(ReminderTexts().tooth_care(), 5, "tooth_care", ReminderTexts().tooth_care_markup())).start() 143 | threading.Timer(20, set_reminder, args=(ReminderTexts().dental_floss(), 5, "dental_floss", ReminderTexts().tooth_care_markup())).start() 144 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent it.") 145 | 146 | 147 | @bot.message_handler(content_types=['animation', 'audio', 'contact', 'dice', 'document', 'location', 'photo', 'poll', 'sticker', 'text', 'venue', 'video', 'video_note', 'voice']) 148 | def log(message): 149 | bot.forward_message(config.LOG_GROUP_ID, message.chat.id, message.id) 150 | 151 | def beep(chat_id, tag) -> None: 152 | """Send the beep message.""" 153 | if tag not in temp_messages.to_delete: 154 | temp_messages.to_delete[tag] = list() 155 | temp_message = bot.send_message(chat_id, text=ReminderTexts().beep()) 156 | temp_messages.to_delete[tag].append(temp_message.message_id) 157 | bot.send_message(config.ADMIN_TELEGRAM_ID, "Sent beep.") 158 | --------------------------------------------------------------------------------