Spaces:
Sleeping
Sleeping
omerXfaruq
commited on
Commit
•
4717db6
1
Parent(s):
23a4209
FULL COMMIT
Browse files- app.py +13 -0
- requirements.txt +13 -0
- run_with_ngrok.sh +3 -0
- src/__init__.py +62 -0
- src/__pycache__/__init__.cpython-310.pyc +0 -0
- src/__pycache__/constants.cpython-310.pyc +0 -0
- src/__pycache__/db.cpython-310.pyc +0 -0
- src/__pycache__/events.cpython-310.pyc +0 -0
- src/__pycache__/listener.cpython-310.pyc +0 -0
- src/__pycache__/message_validations.cpython-310.pyc +0 -0
- src/__pycache__/response_logic.cpython-310.pyc +0 -0
- src/constants.py +561 -0
- src/db.py +495 -0
- src/events.py +178 -0
- src/listener.py +84 -0
- src/message_validations.py +78 -0
- src/response_logic.py +334 -0
app.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import gradio as gr
|
3 |
+
|
4 |
+
def start_app(telegram_token: str, ngrok_token: str):
|
5 |
+
cmnd = f"bash run_with_ngrok.sh {telegram_token} {ngrok_token}"
|
6 |
+
os.system(cmnd)
|
7 |
+
return 'Application started'
|
8 |
+
|
9 |
+
demo = gr.Interface(
|
10 |
+
start_app,
|
11 |
+
[gr.Textbox(label="Telegram Token"),gr.Textbox(label="NGROK Token")],
|
12 |
+
[gr.Textbox(label="Result")]
|
13 |
+
).launch()
|
requirements.txt
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
black
|
2 |
+
pytest
|
3 |
+
asyncio
|
4 |
+
sqlmodel
|
5 |
+
sqlalchemy
|
6 |
+
pyngrok
|
7 |
+
fastapi
|
8 |
+
uvicorn
|
9 |
+
pydantic
|
10 |
+
typing
|
11 |
+
httpx
|
12 |
+
datetime
|
13 |
+
curl
|
run_with_ngrok.sh
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
export TELEGRAM_TOKEN=$1
|
2 |
+
export NGROK_TOKEN=$2
|
3 |
+
python3 -m src.__init__ ngrok >> logs.txt
|
src/__init__.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
import os
|
3 |
+
|
4 |
+
from pyngrok import ngrok
|
5 |
+
import uvicorn
|
6 |
+
import asyncio
|
7 |
+
|
8 |
+
from .events import Events
|
9 |
+
from .constants import Constants
|
10 |
+
|
11 |
+
__all__ = []
|
12 |
+
|
13 |
+
if __name__ == "__main__":
|
14 |
+
if Events.TOKEN is None:
|
15 |
+
sys.exit("No TELEGRAM_TOKEN found in the environment, exiting now.")
|
16 |
+
|
17 |
+
PORT = Events.PORT
|
18 |
+
loop = asyncio.get_event_loop()
|
19 |
+
|
20 |
+
# Run with ngrok if the parameter is given
|
21 |
+
running_option = None
|
22 |
+
if len(sys.argv) == 2:
|
23 |
+
running_option = sys.argv[1]
|
24 |
+
|
25 |
+
if running_option == "ngrok":
|
26 |
+
ngrok_token = str(os.environ.get("NGROK_TOKEN"))
|
27 |
+
if ngrok_token == "None":
|
28 |
+
print(
|
29 |
+
"NGROK auth token is not found in the environment. Ngrok will timeout after a few hours."
|
30 |
+
)
|
31 |
+
else:
|
32 |
+
print(f"NGROK TOKEN: {ngrok_token}")
|
33 |
+
ngrok.set_auth_token(ngrok_token)
|
34 |
+
http_tunnel = ngrok.connect(PORT, bind_tls=True)
|
35 |
+
ssh_tunnel = ngrok.connect(22, "tcp")
|
36 |
+
public_url = http_tunnel.public_url
|
37 |
+
ssh_url = ssh_tunnel.public_url
|
38 |
+
Events.HOST_URL = public_url
|
39 |
+
_ = loop.run_until_complete(
|
40 |
+
Events.send_a_message_to_user(
|
41 |
+
Constants.BROADCAST_CHAT_ID, f"ssh: {ssh_url}, http:{public_url}"
|
42 |
+
)
|
43 |
+
)
|
44 |
+
else:
|
45 |
+
public_url = loop.run_until_complete(Events.get_public_ip())
|
46 |
+
Events.HOST_URL = f"https://{public_url}"
|
47 |
+
if running_option == "self_signed":
|
48 |
+
Events.SELF_SIGNED = True
|
49 |
+
|
50 |
+
print(Events.HOST_URL)
|
51 |
+
success = loop.run_until_complete(Events.set_telegram_webhook_url())
|
52 |
+
|
53 |
+
if success:
|
54 |
+
uvicorn.run(
|
55 |
+
"src.listener:app",
|
56 |
+
host="0.0.0.0",
|
57 |
+
port=PORT,
|
58 |
+
reload=True,
|
59 |
+
log_level="info",
|
60 |
+
)
|
61 |
+
else:
|
62 |
+
print("Fail, closing the app.")
|
src/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (1.45 kB). View file
|
src/__pycache__/constants.cpython-310.pyc
ADDED
Binary file (25.1 kB). View file
|
src/__pycache__/db.cpython-310.pyc
ADDED
Binary file (10.3 kB). View file
|
src/__pycache__/events.cpython-310.pyc
ADDED
Binary file (5.83 kB). View file
|
src/__pycache__/listener.cpython-310.pyc
ADDED
Binary file (2.43 kB). View file
|
src/__pycache__/message_validations.cpython-310.pyc
ADDED
Binary file (3.11 kB). View file
|
src/__pycache__/response_logic.cpython-310.pyc
ADDED
Binary file (5.98 kB). View file
|
src/constants.py
ADDED
@@ -0,0 +1,561 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .db import default_schedule, get_user_status
|
2 |
+
|
3 |
+
|
4 |
+
class Constants:
|
5 |
+
smile = "😊"
|
6 |
+
hello = "👋🏻"
|
7 |
+
sad = "😔"
|
8 |
+
sun_glasses = "😎"
|
9 |
+
|
10 |
+
BROADCAST_CHAT_ID = -1001786782026
|
11 |
+
FEEDBACK_FORWARD_CHAT_ID = -683998033
|
12 |
+
BOT_ID = 5065052385
|
13 |
+
|
14 |
+
class Common:
|
15 |
+
@staticmethod
|
16 |
+
def inactive_user(name: str, language_code: str = "en") -> str:
|
17 |
+
if language_code == "tr":
|
18 |
+
return f"{name}, sistemime kayıtlı değilsin, you are not in the system, please join by typing; *join* or /join. {Constants.smile}"
|
19 |
+
else:
|
20 |
+
return f"{name}, you are not in the system, please join by typing; *join* or /join. {Constants.smile}"
|
21 |
+
|
22 |
+
@staticmethod
|
23 |
+
def no_memory_found(name: str, language_code: str = "en") -> str:
|
24 |
+
if language_code == "tr":
|
25 |
+
return f"{name}, ne yazık ki hatıra kasan boş. Lütfen bu komutla not ekle *add Cümle* {Constants.smile}"
|
26 |
+
else:
|
27 |
+
return f"{name}, I could not find any note in your Vault. Please add a note with, *add Sentence* {Constants.smile}"
|
28 |
+
|
29 |
+
@staticmethod
|
30 |
+
def unknown_command(name: str, language_code: str = "en") -> str:
|
31 |
+
|
32 |
+
if language_code == "tr":
|
33 |
+
return (
|
34 |
+
f"Sevgili {name}, ne yazık ki bu komutu bilmiyorum {Constants.sad}"
|
35 |
+
f"\n- /start veya *start* ile başlangıç mesajını görebilirsin"
|
36 |
+
f"\n- /leave veya *leave* ile günlük hatırlatmaları durdurabilirsin"
|
37 |
+
f"\n- /send veya *send* ile rastgele bir not yollarım"
|
38 |
+
f"\n- *send number* ile çok sayıda not yollarım"
|
39 |
+
f"\n- /status veya *status* ile status bilgini yollarım"
|
40 |
+
f"\n- /list veya *list* ile tüm notlarını gönderirim"
|
41 |
+
f"\nTemel komutlarım bunlardı, günlük takvimi ayarlama vb. diğer tüm detaylı komutları görmek için, *help veya /help"
|
42 |
+
)
|
43 |
+
else:
|
44 |
+
return (
|
45 |
+
f"Dear {name}, unfortunately I do not know that command {Constants.sad}"
|
46 |
+
f"\n- /start or *start* to see the start message"
|
47 |
+
f"\n- /leave or *leave* to deactivate daily reminders"
|
48 |
+
f"\n- /send or *send* to get a random note"
|
49 |
+
f"\n- *send number* to get multiple random notes"
|
50 |
+
f"\n- /status or *status* to get your status information"
|
51 |
+
f"\n- /list or *list* to list notes"
|
52 |
+
f"\nThese were my main commands, to see additional commands like editing daily schedule please use, *help* or /help"
|
53 |
+
)
|
54 |
+
|
55 |
+
class Start:
|
56 |
+
@staticmethod
|
57 |
+
def start_message(name: str, language_code: str = "eng") -> str:
|
58 |
+
if language_code == "tr":
|
59 |
+
return (
|
60 |
+
f"Merhabalar {name} {Constants.hello}"
|
61 |
+
f"\nHatıra Kasası notlarını kaydetmene imkan sağlar ve her gün sana rastgele notlar yollar."
|
62 |
+
f"\n\nHayatımızda karşılaştığımız güzel ve önemli cümleleri bir yere not almak ve sonrasında onları hatırlamak oldukça zor değil mi {Constants.sad}?"
|
63 |
+
f"\nİşte ben bu sorunu oldukça basit ve kullanması kolay bir yöntemle çözüyorum {Constants.sun_glasses}. Zira karışık çözümleri hayatımıza sokmak zor {Constants.sad}."
|
64 |
+
f"\nBu yöntemdeki güzellik şurada, bana verdiğin her notu elbet bir gün sana yollayacağım. Ne zaman yollayacağımı da düşünmene gerek yok."
|
65 |
+
f"\nBu güzel fikri verdiği için sevgili hanımım Seyyide'ye teşekkür ederim."
|
66 |
+
f"\n- Botu anlatan kısa rehbere geçmek için buyur tıkla, /tutorial1"
|
67 |
+
)
|
68 |
+
else:
|
69 |
+
return (
|
70 |
+
f"Hello {name} {Constants.hello}"
|
71 |
+
f"\nKeeping note of beautiful & important stuff that we come across throughout the life, and later remembering them is quite difficult isn't it 😔? Here is the Memory Vault for the rescue! Just take your notes, and I will occasionally remind them to you 😎"
|
72 |
+
f"\nUnfortunately in this era our days&agendas are very busy and it is very hard to follow something consistently. Memory Vault helps us on this front by continuously reminding our notes to us so that you won't ever forget them. You can use it for"
|
73 |
+
f"\n1. Habit Building"
|
74 |
+
f"\n2. Language Learning"
|
75 |
+
f"\n3. Learning the way of Entrepreneurship"
|
76 |
+
f"\n4. Remembering names"
|
77 |
+
f"\n5. Notetaking"
|
78 |
+
f"\n6. Or, Anything Custom, Memory Vault is very flexible and general solution! "
|
79 |
+
f"\n"
|
80 |
+
f"\n❤️ Sincerely thanks to my dear wife Seyyide for the beautiful idea."
|
81 |
+
f"\n"
|
82 |
+
f"\n- To start to a small tutorial please click, /tutorial1"
|
83 |
+
)
|
84 |
+
|
85 |
+
@staticmethod
|
86 |
+
def group_warning(name: str, language_code: str = "eng") -> str:
|
87 |
+
if language_code == "tr":
|
88 |
+
return (
|
89 |
+
f"\nBu arada Hatıra Kasasını gruplarda kullanmak için onu ya"
|
90 |
+
f"\n- Grup admini yapmalı -- Bu durumda Hatıra Kasası gruptaki tüm mesajları dinleyip cevap verecektir"
|
91 |
+
f"\n- Ya da Hatıra Kasası'nın herhangi bir mesajını yanıtlamalısın."
|
92 |
+
)
|
93 |
+
else:
|
94 |
+
return (
|
95 |
+
f"Btw, to use Memory Vault in a group you should either"
|
96 |
+
f"\n- Make Memory-Vault group admin -- In this case Memory Vault will listen and reply to every message."
|
97 |
+
f"\n- Or reply to any message from Memory Vault to interact."
|
98 |
+
)
|
99 |
+
|
100 |
+
class Help:
|
101 |
+
@staticmethod
|
102 |
+
def help_message(name: str, language_code: str = "eng") -> str:
|
103 |
+
if language_code == "tr":
|
104 |
+
return (
|
105 |
+
f"\n\nHafıza Kasası sana her gün, takvimindeki saatlerde kasandan rastgele notlar gönderir."
|
106 |
+
f"\n- /help veya *help* yardım mesajını alabilirsin"
|
107 |
+
f"\n- /join veya *join* ile günlük gönderimi aktifleştirebilirsin"
|
108 |
+
f"\n- /leave veya *leave* ile günlük hatırlatmayı durdurabilirsin"
|
109 |
+
f"\n- /send veya *send* ile rastgele bir not yollarım"
|
110 |
+
f"\n- *send number* ile çok sayıda not yollarım"
|
111 |
+
f"\n- /status veya *status* ile status bilgini yollarım"
|
112 |
+
f"\n- /list veya *list* ile tüm notlarını gönderirim"
|
113 |
+
f"\n\n- *add Note* ile kasana bir not ekleyebilirsin"
|
114 |
+
f"\nÖrnek:"
|
115 |
+
f"\n*add Vakit hiç bir zaman geri gelmez*"
|
116 |
+
f"\n\n- *delete id* ile bir notu silebilirsin. Not id'lerini bu komutlarla öğrenebilirsin, *list* veya /list"
|
117 |
+
f"\nÖrnek:"
|
118 |
+
f"\n*delete 2*"
|
119 |
+
f"\n\n- *gmt zaman-dilimi* ile zaman dilimi belirleyebilirsin, varsayılan zaman dilimi *GMT0*'dır"
|
120 |
+
f"\nÖrnek:"
|
121 |
+
f"\nGMT+3: *gmt 3*"
|
122 |
+
f"\nGMT0: *gmt 0*"
|
123 |
+
f"\nGMT-5: *gmt -5*"
|
124 |
+
f"\n\n- /support veya *support* ile beni nasıl destekleyebileceğini öğrenebilirsin"
|
125 |
+
f"\n- *feedback Cümle* ile bot hakkındaki düşüncelerini veya ƒeedback'lerini yollayabilirsin"
|
126 |
+
f"\n\n*Schedule(takvim) hakkındaki komutlar:*"
|
127 |
+
f"\nHer gün takvimindeki saat başlarında sana notlar yollarım. Varsayılan takvim saatleri *{default_schedule}*'dır. Yani her gün 8:00 ve 20:00'de sana bir adet not yollayacağım."
|
128 |
+
f"\nSchedule komutlarıyla kendi günlük takvimini oluşturabilirsin. Ayrıca bir saati birden fazla kez ekleyerek o saatte birden çok not alabilirsin."
|
129 |
+
f"\n- /schedule veya *schedule* ile şuanki takvimini yollarım"
|
130 |
+
f"\n- *schedule reset* ile takvimini varsayılan takvime({default_schedule}) çekerim"
|
131 |
+
f"\n- *schedule add saat1 saat2 saat3* ile saatleri takvimine eklerim"
|
132 |
+
f"\nÖrnek:"
|
133 |
+
f"\n*schedule add 1 3 9 11*"
|
134 |
+
f"\n- *schedule remove saat* ile bir saati takviminden tamamen kaldırabilirsin"
|
135 |
+
f"\nÖrnek:"
|
136 |
+
f"\n*schedule remove 8*"
|
137 |
+
f"\n\n*Grup Kullanımı*"
|
138 |
+
f"\n - Beni *gruplarda da kullanabilirsin*, gruba ekleyip yönetici yapman yeterli. Yönetici yapmak istemiyorsan da grupta benim mesajlarıma yanıtla yaparak da komutları kullanabilirsin."
|
139 |
+
f"\n- *Birden fazla Hatıra Kasasına* sahip olmak için beni farklı gruplarda kullanabilirsin. Mesela bir kelime öğrenme grubu kurabilirsin."
|
140 |
+
f"\n- Örnek grup: Kuran'ı Kerim'den Dualar(@PrayersFromQuran)"
|
141 |
+
)
|
142 |
+
else:
|
143 |
+
return (
|
144 |
+
f"\n\nMemory Vault will send you random notes from your memory vault, at the hours in your schedule every day."
|
145 |
+
f"\n- /help or *help* to get help message"
|
146 |
+
f"\n- /join or *join* to activate daily note sending"
|
147 |
+
f"\n- /leave or *leave* to deactivate daily reminders"
|
148 |
+
f"\n- /send or *send* to get a random note"
|
149 |
+
f"\n- *send number* to get multiple random notes"
|
150 |
+
f"\n- /status or *status* to get your status information"
|
151 |
+
f"\n- /list or *list* to list notes"
|
152 |
+
f"\n\n- *add Note* to add a note to your memory vault"
|
153 |
+
f"\nExample:"
|
154 |
+
f"\n*add Time never does come back*"
|
155 |
+
f"\n\n- *delete id* to delete a note. You can learn the note ids with the command, *list* or /list"
|
156 |
+
f"\nExample:"
|
157 |
+
f"\n*delete 2*"
|
158 |
+
f"\n\n- *gmt timezone* to set your timezone, the default timezone is *GMT0*"
|
159 |
+
f"\nExamples:"
|
160 |
+
f"\nGMT+3: *gmt 3*"
|
161 |
+
f"\nGMT0: *gmt 0*"
|
162 |
+
f"\nGMT-5: *gmt -5*"
|
163 |
+
f"\n\n- /support or *support* to learn how to support me"
|
164 |
+
f"\n- *feedback Sentence* to send your thoughts and feedbacks about the bot"
|
165 |
+
f"\n\n*Schedule related commands:*"
|
166 |
+
f"\nI send notes according to the hours in your schedule. Default schedule hours are *{default_schedule}*. I will send you a note at 8:00 and 20:00 everyday."
|
167 |
+
f"\nYou can create your own daily schedule. Furthermore you can add an hour multiple times to receive multiple notes at that hour."
|
168 |
+
f"\n- /schedule or *schedule* to display your current schedule"
|
169 |
+
f"\n- *schedule reset* to reset your schedule to the default schedule"
|
170 |
+
f"\n- *schedule add hour1 hour2 hour3* to add hours to your schedule"
|
171 |
+
f"\nExample:"
|
172 |
+
f"\n*schedule add 1 3 9 11*"
|
173 |
+
f"\n- *schedule remove hour* to remove an hour from your schedule"
|
174 |
+
f"\nExample:"
|
175 |
+
f"\n*schedule remove 8*"
|
176 |
+
f"\n\n*Group Usage*"
|
177 |
+
f"\n - You can *use me in groups* as well, just add me to a group and promote me to admin there. If you don't want to make me an admin, you can reply to my messages in the group to use my commands."
|
178 |
+
f"\n- Furthermore *you can have multiple memory vaults* by using different groups. For example I would serve you well in a *language learning group*, where you add words you want to remember to your memory vault."
|
179 |
+
f"\n- Example group: @PrayersFromQuran"
|
180 |
+
)
|
181 |
+
|
182 |
+
class Join:
|
183 |
+
@staticmethod
|
184 |
+
def successful_join(name: str, language_code: str = "en") -> str:
|
185 |
+
if language_code == "tr":
|
186 |
+
return (
|
187 |
+
f"Hoşgeldin, sefa geldin {name}! Günlük not yollamayı açtın. Takvimindeki saatlere göre sana hatıra kasandan her gün notlar yollayacağım."
|
188 |
+
f"Varsayılan takvimindeki saatleri {default_schedule}'dır. (8 -> 8:00, 20 -> 20:00). Daha detaylı bilgi için, *help* veya /help."
|
189 |
+
f"\nYeni bir kullanıcı isen lütfen bu komuta tıklayarak rehbere başla, /tutorial1 {Constants.smile}"
|
190 |
+
)
|
191 |
+
else:
|
192 |
+
return (
|
193 |
+
f"Welcome onboard {name}! "
|
194 |
+
f"\nYou activated daily note sending. I will send you random notes from your memory vault according to your schedule."
|
195 |
+
f"The default hours in the schedule are {default_schedule}(8 -> 8:00, 20 -> 20:00). You can get more detailed information by writing, *help* or /help."
|
196 |
+
f"\nIf you are a new user, please start the tutorial by clicking, /tutorial1 {Constants.smile}"
|
197 |
+
)
|
198 |
+
|
199 |
+
@staticmethod
|
200 |
+
def already_joined(name: str, language_code: str = "en") -> str:
|
201 |
+
if language_code == "tr":
|
202 |
+
return f"{name}, hesabın zaten aktif. Hesabının mevcut durumunu görmek için bu komutu kullanabilirsin, /status."
|
203 |
+
else:
|
204 |
+
return f"{name}, Your account is already active. You can see your status via, /status."
|
205 |
+
|
206 |
+
class Leave:
|
207 |
+
@staticmethod
|
208 |
+
def successful_leave(name: str, language_code: str = "en") -> str:
|
209 |
+
if language_code == "tr":
|
210 |
+
return (
|
211 |
+
f"Allah'a emanet ol {name}. Günlük not yollamamı kapattın, ama merak etme hatıra kasan benimle."
|
212 |
+
f"İstediğin zaman bu komutlarla geri gelebilirsin, *join veya /join."
|
213 |
+
)
|
214 |
+
else:
|
215 |
+
return (
|
216 |
+
f"Good bye {name}, you deactivated daily note sending. It was nice to have you here. "
|
217 |
+
f"Your memory vault remains with me, you can return whenever you wish with command, *join* or /join."
|
218 |
+
)
|
219 |
+
|
220 |
+
@staticmethod
|
221 |
+
def already_left(name: str, language_code: str = "en") -> str:
|
222 |
+
if language_code == "tr":
|
223 |
+
return f"{name}, hesabın zaten atıl durumda."
|
224 |
+
else:
|
225 |
+
return f"{name}, Your account is already inactive."
|
226 |
+
|
227 |
+
class Send:
|
228 |
+
@staticmethod
|
229 |
+
def send_count_out_of_bound(name: str, language_code: str = "en") -> str:
|
230 |
+
if language_code == "tr":
|
231 |
+
return f"{name}, lütfen 1<n<50 arasında bir sayı ver, örn: *send 3*."
|
232 |
+
else:
|
233 |
+
return f"{name}, please give a number which is 1<n<50, ie: *send 3*."
|
234 |
+
|
235 |
+
class List:
|
236 |
+
@staticmethod
|
237 |
+
def list_messages(name: str, note_count: int, language_code: str = "en") -> str:
|
238 |
+
if language_code == "tr":
|
239 |
+
return (
|
240 |
+
f"{name} Destur! {note_count} notun birer birer akacak."
|
241 |
+
f"\n\nHatıra kasasının kapılarını açın!"
|
242 |
+
f"\n*id | not*"
|
243 |
+
)
|
244 |
+
else:
|
245 |
+
return (
|
246 |
+
f"Brace yourself {name}, you will receive {note_count} notes one by one."
|
247 |
+
f"\n\nOpen the gates of the memory vault!"
|
248 |
+
f"\n*id | note*"
|
249 |
+
)
|
250 |
+
|
251 |
+
class Add:
|
252 |
+
@staticmethod
|
253 |
+
def no_sentence(name: str, language_code: str = "en") -> str:
|
254 |
+
if language_code == "tr":
|
255 |
+
return f"{name} add kelimesinden sonra bir cümle bulamadım. Lütfen bu komutu kullan: *add Cümle*."
|
256 |
+
else:
|
257 |
+
return f"There is no sentence found after the word *add*. Please use this command: *add Sentence*"
|
258 |
+
|
259 |
+
@staticmethod
|
260 |
+
def already_added(name: str, language_code: str = "en") -> str:
|
261 |
+
if language_code == "tr":
|
262 |
+
return f"{name} bu not zaten kasada {Constants.smile}"
|
263 |
+
|
264 |
+
else:
|
265 |
+
return (
|
266 |
+
f"{name} the note is already in your memory vault {Constants.smile}"
|
267 |
+
)
|
268 |
+
|
269 |
+
@staticmethod
|
270 |
+
def success(name: str, language_code: str = "en", note: str = "") -> str:
|
271 |
+
if language_code == "tr":
|
272 |
+
return (
|
273 |
+
f"{name}, not kasana eklendi. Merak etme, onu güvende tutacağım {Constants.smile}"
|
274 |
+
f"\n*Not*: \n{note}"
|
275 |
+
f""
|
276 |
+
f"\n\n Eğer son eklediğin notu silmek istiyorsan, bu komutu kullan */deletelast*"
|
277 |
+
)
|
278 |
+
|
279 |
+
else:
|
280 |
+
return (
|
281 |
+
f"{name}, note is added to your memory vault. No worries, I will keep it safe {Constants.smile}"
|
282 |
+
f"\n*Note*: \n{note}"
|
283 |
+
f""
|
284 |
+
f"\n\nIf you want to delete the last added note, you can use */deletelast*"
|
285 |
+
)
|
286 |
+
|
287 |
+
class Delete:
|
288 |
+
@staticmethod
|
289 |
+
def no_id(name: str, language_code: str = "en") -> str:
|
290 |
+
if language_code == "tr":
|
291 |
+
return f"{name}, bana notun id'sini vermen gerekiyor, örn: *delete 2*, bu komut ile id'leri öğrenebilirsin *list* veya /list"
|
292 |
+
|
293 |
+
else:
|
294 |
+
return f"{name}, need to give me id of the note, ie: *delete 2*, you can get it by using command, *list* or /list"
|
295 |
+
|
296 |
+
@staticmethod
|
297 |
+
def success(name: str, language_code: str = "en", note: str = "") -> str:
|
298 |
+
if language_code == "tr":
|
299 |
+
return (
|
300 |
+
f"{name}, not kasadan silindi. Unutulan hatıraya elveda {Constants.sad}"
|
301 |
+
f"\n*Silinen Not*:"
|
302 |
+
f"\n{note}"
|
303 |
+
)
|
304 |
+
else:
|
305 |
+
return (
|
306 |
+
f"{name}, your note is deleted from your memory vault. Good bye to the forgotten memory {Constants.sad}"
|
307 |
+
f"\n*Deleted Note*:"
|
308 |
+
f"\n{note}"
|
309 |
+
)
|
310 |
+
|
311 |
+
class Schedule:
|
312 |
+
@staticmethod
|
313 |
+
def empty_schedule(name: str, language_code: str = "en") -> str:
|
314 |
+
if language_code == "tr":
|
315 |
+
return f"{name}, takvimin boş, takvimine saatleri eklemek için bu komutu kullanabilirsin: *schedule add hour1 hour2 hour3*, örn: *schedule add 8 12*"
|
316 |
+
|
317 |
+
else:
|
318 |
+
return f"{name}, your schedule is empty, you can add hours to your schedule via, *schedule add hour1 hour2 hour3*, ie: *schedule add 8 12*"
|
319 |
+
|
320 |
+
@staticmethod
|
321 |
+
def success(name: str, language_code: str = "en", schedule: str = "") -> str:
|
322 |
+
if language_code == "tr":
|
323 |
+
return (
|
324 |
+
f"{name}, güncel takvimin aşağıda, takvimindeki saat başlarında rastgele bir not alacaksın. örn: 8 -> 8:00"
|
325 |
+
f"\n*Takvim*: {schedule}"
|
326 |
+
f""
|
327 |
+
f"\n\nUyarı: Eğer bu bottan faydalanmak istiyorsan, takvimini dolup taşırmamaya dikkat et ve gelen mesajlara dikkatini ver, göz atıp geçme."
|
328 |
+
)
|
329 |
+
else:
|
330 |
+
return (
|
331 |
+
f"{name}, your current schedule is below, You will get a random note at each of these hours everyday. ie: 8 -> 8:00"
|
332 |
+
f"\n*Schedule*: {schedule}"
|
333 |
+
f""
|
334 |
+
f"\n\nWarning: If you want to make use of this bot, be careful to not overflow your schedule and give attention to the incoming messages, do not just look and pass."
|
335 |
+
)
|
336 |
+
|
337 |
+
@staticmethod
|
338 |
+
def no_number_found(name: str, language_code: str = "en") -> str:
|
339 |
+
if language_code == "tr":
|
340 |
+
return f"{name}, *schedule add* komutu sonrasında bir sayı bulamadım, doğru kullanım örneği: *schedule add 1 3 5 21*"
|
341 |
+
|
342 |
+
else:
|
343 |
+
return f"{name}, there is no numbers found after *schedule add*, correct usage example: *schedule add 1 3 5 21*"
|
344 |
+
|
345 |
+
@staticmethod
|
346 |
+
def add_incorrect_number_input(name: str, language_code: str = "en") -> str:
|
347 |
+
if language_code == "tr":
|
348 |
+
return f"{name}, lütfen girdi olarak sayılar kullan, 0<=sayı<=23, örn: *schedule add 1 3 5 21*"
|
349 |
+
|
350 |
+
else:
|
351 |
+
return f"{name}, please use numbers 0<=number<=23, ie: *schedule add 1 3 5 21*"
|
352 |
+
|
353 |
+
@staticmethod
|
354 |
+
def remove_incorrect_number_input(name: str, language_code: str = "en") -> str:
|
355 |
+
if language_code == "tr":
|
356 |
+
return f"{name}, lütfen girdi olarak sayı kullan, 0<=sayı<=23, örn: *schedule remove 8*"
|
357 |
+
|
358 |
+
else:
|
359 |
+
return (
|
360 |
+
f"{name}, please use number 0<=number<=23, ie: *schedule remove 8*"
|
361 |
+
)
|
362 |
+
|
363 |
+
@staticmethod
|
364 |
+
def unknown_command(name: str, language_code: str = "en") -> str:
|
365 |
+
if language_code == "tr":
|
366 |
+
return (
|
367 |
+
f"{name}, bu komutu bilmiyorum. Aşağıdaki komutları kullanabilirsin."
|
368 |
+
f"\n*schedule*"
|
369 |
+
f"\n*schedule add 8 12*"
|
370 |
+
f"\n*schedule reset*"
|
371 |
+
f"\n*schedule remove 8*"
|
372 |
+
)
|
373 |
+
else:
|
374 |
+
return (
|
375 |
+
f"{name}, I do not know that command. You can support the commands below."
|
376 |
+
f"\n*schedule*"
|
377 |
+
f"\n*schedule add 8 12*"
|
378 |
+
f"\n*schedule reset*"
|
379 |
+
f"\n*schedule remove 8*"
|
380 |
+
)
|
381 |
+
|
382 |
+
class Gmt:
|
383 |
+
@staticmethod
|
384 |
+
def success(name: str, language_code: str = "en", gmt: int = 0) -> str:
|
385 |
+
if language_code == "tr":
|
386 |
+
return f"{name}, güncel saat dilimin: GMT{gmt}."
|
387 |
+
else:
|
388 |
+
return f"{name}, your current timezone is: GMT{gmt}."
|
389 |
+
|
390 |
+
@staticmethod
|
391 |
+
def incorrect_timezone(name: str, language_code: str = "en") -> str:
|
392 |
+
if language_code == "tr":
|
393 |
+
return f"{name}, lütfen saat dilimini doğru kullan, örn: *gmt 3* or *gmt -3*"
|
394 |
+
else:
|
395 |
+
return f"{name}, please give your timezone correctly, ie: *gmt 3* or *gmt -3*"
|
396 |
+
|
397 |
+
class Broadcast:
|
398 |
+
@staticmethod
|
399 |
+
def no_sentence_found(name: str, language_code: str = "en") -> str:
|
400 |
+
if language_code == "tr":
|
401 |
+
return f"{name}, *broadcast* kelimesi sonrasında herhangi bir cümle bulamadım {Constants.sad}, doğru örn: *broadcast Cümle*"
|
402 |
+
else:
|
403 |
+
return f"{name}, there is no sentence found after the word *broadcast* {Constants.sad}, correct usage: *broadcast Sentence*"
|
404 |
+
|
405 |
+
@staticmethod
|
406 |
+
def success(name: str, language_code: str = "en") -> str:
|
407 |
+
if language_code == "tr":
|
408 |
+
return f"{name}, broadcast yollandı {Constants.smile}"
|
409 |
+
else:
|
410 |
+
return f"{name}, broadcast is sent {Constants.smile}"
|
411 |
+
|
412 |
+
@staticmethod
|
413 |
+
def no_right(name: str, language_code: str = "en") -> str:
|
414 |
+
if language_code == "tr":
|
415 |
+
return f"{name}, broadcast hakkın yok {Constants.smile}"
|
416 |
+
else:
|
417 |
+
return f"{name}, you have no broadcast right {Constants.smile}"
|
418 |
+
|
419 |
+
class Status:
|
420 |
+
@staticmethod
|
421 |
+
def get_status(
|
422 |
+
name: str,
|
423 |
+
language_code: str = "en",
|
424 |
+
gmt: int = 0,
|
425 |
+
active: bool = True,
|
426 |
+
schedule: str = "",
|
427 |
+
note_count: int = 0,
|
428 |
+
) -> str:
|
429 |
+
if language_code == "tr":
|
430 |
+
if active:
|
431 |
+
is_active = "aktif"
|
432 |
+
else:
|
433 |
+
is_active = "pasif"
|
434 |
+
return (
|
435 |
+
f"Mevcut durumun:"
|
436 |
+
f"\n- Gmt: *GMT{gmt}*"
|
437 |
+
f"\n- Günlük gönderim aktif: *{is_active}*"
|
438 |
+
f"\n- Hatıra Kasandaki not sayısı: {note_count}"
|
439 |
+
f"\n- Takvim: {schedule}"
|
440 |
+
f""
|
441 |
+
f"\n\nUyarı: Eğer bu bottan faydalanmak istiyorsan, takvimini dolup taşırmamaya dikkat et ve gelen mesajlara dikkatini ver, göz atıp geçme."
|
442 |
+
)
|
443 |
+
else:
|
444 |
+
return (
|
445 |
+
f"Your current status:"
|
446 |
+
f"\n- Gmt: *GMT{gmt}*"
|
447 |
+
f"\n- Daily sending is active: *{active}*"
|
448 |
+
f"\n- Number of notes in your Memory Vault: {note_count}"
|
449 |
+
f"\n- Schedule: {schedule}"
|
450 |
+
f""
|
451 |
+
f"\n\nWarning: If you want to make use of this bot, be careful to not overflow your schedule and give attention to the incoming messages, do not just look and pass."
|
452 |
+
)
|
453 |
+
|
454 |
+
class Feedback:
|
455 |
+
@staticmethod
|
456 |
+
def no_message(name: str, language_code: str = "en"):
|
457 |
+
if language_code == "tr":
|
458 |
+
return f"{name}, *feedback* sonrasında bir cümle bulamadım {Constants.sad}, doğru örnek: *feedback Cümle*"
|
459 |
+
else:
|
460 |
+
return f"{name}, there is no message found after the word *feedback* {Constants.sad}, correct example: *feedback Sentence*"
|
461 |
+
|
462 |
+
@staticmethod
|
463 |
+
def success(name: str, language_code: str = "en", feedback: str = ""):
|
464 |
+
if language_code == "tr":
|
465 |
+
return (
|
466 |
+
f"{name}, feedback'ini yöneticiye ilettim, desteğin için çok teşekkür ederim {Constants.smile}"
|
467 |
+
f"\nFeedback: *{feedback}*"
|
468 |
+
)
|
469 |
+
else:
|
470 |
+
return (
|
471 |
+
f"{name}, I forwarded your feedback to the admin, thank you for your support {Constants.smile}"
|
472 |
+
f"\nFeedback: *{feedback}*"
|
473 |
+
)
|
474 |
+
|
475 |
+
@staticmethod
|
476 |
+
def fail(name: str, language_code: str = "en"):
|
477 |
+
if language_code == "tr":
|
478 |
+
return f"{name}, feedback'i yöneticiye iletemedim, bir hata oluştu."
|
479 |
+
else:
|
480 |
+
return f"{name}, I could not forward your feedback to the admin, an error occurred."
|
481 |
+
|
482 |
+
class Support:
|
483 |
+
@staticmethod
|
484 |
+
def support(name: str, language_code: str = "en"):
|
485 |
+
if language_code == "tr":
|
486 |
+
return (
|
487 |
+
f"Teşekkür ederim {name}, beni desteklemek için"
|
488 |
+
f"\n- @memory_vault_bot'u arkadaşlarınla paylaşabilir"
|
489 |
+
f"\n- Bu komutla bana feedback verebilirsin, *feedback cümle*"
|
490 |
+
f"\n- Github repo'ma yıldız verebilirsin, https://github.com/FarukOzderim/Memory-Vault/"
|
491 |
+
)
|
492 |
+
else:
|
493 |
+
return (
|
494 |
+
f"Thank you {name}, to support me you can"
|
495 |
+
f"\n- Share me with your friends"
|
496 |
+
f"\n- Give feedback using the command, *feedback sentence*"
|
497 |
+
f"\n- Star the github repository at https://github.com/FarukOzderim/Memory-Vault/"
|
498 |
+
)
|
499 |
+
|
500 |
+
class Tutorial:
|
501 |
+
@staticmethod
|
502 |
+
def tutorial_1(name: str, language_code: str = "en"):
|
503 |
+
if language_code == "tr":
|
504 |
+
return (
|
505 |
+
f"*gmt zaman-dilimi* ile zaman dilimi belirleyebilirsin, varsayılan zaman dilimi *GMT0*'dır. Bu arada Türkiye GMT+3 zaman diliminde."
|
506 |
+
f"\nÖrnek:"
|
507 |
+
f"\nGMT+3: *gmt 3*"
|
508 |
+
f"\nGMT0: *gmt 0*"
|
509 |
+
f"\nGMT-5: *gmt -5*"
|
510 |
+
f"\n\nBir sonraki rehber adımına geçmek için, /tutorial2"
|
511 |
+
)
|
512 |
+
else:
|
513 |
+
return (
|
514 |
+
f"Use *gmt timezone* to set your timezone, the default timezone is *GMT0*. Btw New York is GMT-5, London is GMT0, Malaysia is GMT+8."
|
515 |
+
f"\nExamples:"
|
516 |
+
f"\nGMT+3: *gmt 3*"
|
517 |
+
f"\nGMT0: *gmt 0*"
|
518 |
+
f"\nGMT-5: *gmt -5*"
|
519 |
+
f"\n\nFor the next tutorial step please use, /tutorial2"
|
520 |
+
)
|
521 |
+
|
522 |
+
@staticmethod
|
523 |
+
def tutorial_2(name: str, language_code: str = "en"):
|
524 |
+
if language_code == "tr":
|
525 |
+
return (
|
526 |
+
f"Hatıra Kasana bir not eklemek için *add Cümle* komutunu kullanabilirsin."
|
527 |
+
f"\nÖrnek:"
|
528 |
+
f"\n*add Zaman çok kıymetlidir, her daim eriyen bir dondurmaya benzer.*"
|
529 |
+
f"\n\nBir sonraki rehber adımına geçmek için, /tutorial3"
|
530 |
+
)
|
531 |
+
else:
|
532 |
+
return (
|
533 |
+
f"To add a note to your Memory Vault, please use the command, *add Sentence*."
|
534 |
+
f"\nExample:"
|
535 |
+
f"\n*add Time never does come back*"
|
536 |
+
f"\n\nFor the next tutorial step please use, /tutorial3"
|
537 |
+
)
|
538 |
+
|
539 |
+
@staticmethod
|
540 |
+
def tutorial_3(name: str, language_code: str = "en"):
|
541 |
+
if language_code == "tr":
|
542 |
+
return (
|
543 |
+
f"\n- /leave veya *leave* ile günlük hatırlatmayı durdurabilirsin"
|
544 |
+
f"\n- /send veya *send* ile rastgele bir not yollarım"
|
545 |
+
f"\n- *send number* ile çok sayıda not yollarım"
|
546 |
+
f"\n- /status veya *status* ile status bilgini yollarım"
|
547 |
+
f"\n- /list veya *list* ile tüm notlarını gönderirim"
|
548 |
+
f"\n\n{name} tebrikler rehberi bitirdin {Constants.smile}. "
|
549 |
+
f"\nTemel komutlarım bunlardı, günlük takvimi ayarlama vb. diğer komutları görmek için, /help."
|
550 |
+
)
|
551 |
+
|
552 |
+
else:
|
553 |
+
return (
|
554 |
+
f"\n- /leave or *leave* to deactivate daily reminders"
|
555 |
+
f"\n- /send or *send* to get a random note"
|
556 |
+
f"\n- *send number* to get multiple random notes"
|
557 |
+
f"\n- /status or *status* to get your status information"
|
558 |
+
f"\n- /list or *list* to list notes"
|
559 |
+
f"\n\n{name} congratulations, you finished the tutorial {Constants.smile}. "
|
560 |
+
f"\nThese were my main commands, to see additional commands like editing daily schedule please use, /help."
|
561 |
+
)
|
src/db.py
ADDED
@@ -0,0 +1,495 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import random
|
2 |
+
from typing import List, Optional, Union, Tuple
|
3 |
+
from fastapi import Depends, Query
|
4 |
+
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select, and_
|
5 |
+
from sqlalchemy import UniqueConstraint
|
6 |
+
|
7 |
+
default_schedule = "8,20"
|
8 |
+
|
9 |
+
|
10 |
+
class UserBase(SQLModel):
|
11 |
+
__table_args__ = (UniqueConstraint("telegram_chat_id"),)
|
12 |
+
name: str
|
13 |
+
telegram_chat_id: int
|
14 |
+
gmt: Optional[int] = 0
|
15 |
+
active: Optional[bool] = True
|
16 |
+
scheduled_hours: Optional[str] = default_schedule
|
17 |
+
|
18 |
+
|
19 |
+
class User(UserBase, table=True):
|
20 |
+
id: Optional[int] = Field(default=None, primary_key=True)
|
21 |
+
reminders: List["Reminder"] = Relationship(back_populates="user")
|
22 |
+
|
23 |
+
|
24 |
+
class UserRead(UserBase):
|
25 |
+
id: int
|
26 |
+
|
27 |
+
|
28 |
+
class UserCreate(UserBase):
|
29 |
+
pass
|
30 |
+
|
31 |
+
|
32 |
+
class ReminderBase(SQLModel):
|
33 |
+
reminder: str
|
34 |
+
user_id: Optional[int] = Field(default=None, foreign_key="user.id")
|
35 |
+
|
36 |
+
|
37 |
+
class Reminder(ReminderBase, table=True):
|
38 |
+
id: Optional[int] = Field(default=None, primary_key=True)
|
39 |
+
user: User = Relationship(back_populates="reminders")
|
40 |
+
|
41 |
+
|
42 |
+
class ReminderRead(ReminderBase):
|
43 |
+
id: int
|
44 |
+
|
45 |
+
|
46 |
+
class ReminderCreate(ReminderBase):
|
47 |
+
pass
|
48 |
+
|
49 |
+
|
50 |
+
sqlite_file_name = "database.db"
|
51 |
+
sqlite_url = f"sqlite:///{sqlite_file_name}"
|
52 |
+
|
53 |
+
connect_args = {"check_same_thread": False}
|
54 |
+
engine = create_engine(sqlite_url, echo=False, connect_args=connect_args)
|
55 |
+
|
56 |
+
|
57 |
+
def create_db_and_tables():
|
58 |
+
SQLModel.metadata.create_all(engine)
|
59 |
+
|
60 |
+
|
61 |
+
def get_session():
|
62 |
+
with Session(engine) as session:
|
63 |
+
yield session
|
64 |
+
|
65 |
+
|
66 |
+
def join_user(
|
67 |
+
user: UserCreate,
|
68 |
+
session: Session = next(get_session()),
|
69 |
+
) -> Optional[User]:
|
70 |
+
"""
|
71 |
+
Joins the user to the system.
|
72 |
+
|
73 |
+
Args:
|
74 |
+
user:
|
75 |
+
session:
|
76 |
+
|
77 |
+
Returns: User or None
|
78 |
+
|
79 |
+
"""
|
80 |
+
found_user = session.exec(
|
81 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
82 |
+
).first()
|
83 |
+
if found_user is None:
|
84 |
+
user = db_create_user(user, session)
|
85 |
+
return user
|
86 |
+
else:
|
87 |
+
if found_user.active:
|
88 |
+
return None
|
89 |
+
else:
|
90 |
+
found_user.active = True
|
91 |
+
session.add(found_user)
|
92 |
+
session.commit()
|
93 |
+
session.refresh(found_user)
|
94 |
+
return found_user
|
95 |
+
|
96 |
+
|
97 |
+
def leave_user(
|
98 |
+
user: UserCreate,
|
99 |
+
session: Session = next(get_session()),
|
100 |
+
) -> Optional[User]:
|
101 |
+
"""
|
102 |
+
Deactivates the user.
|
103 |
+
|
104 |
+
Args:
|
105 |
+
user:
|
106 |
+
session:
|
107 |
+
|
108 |
+
Returns: User or None
|
109 |
+
|
110 |
+
"""
|
111 |
+
found_user = session.exec(
|
112 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
113 |
+
).first()
|
114 |
+
if found_user is None:
|
115 |
+
return None
|
116 |
+
else:
|
117 |
+
if not found_user.active:
|
118 |
+
return None
|
119 |
+
else:
|
120 |
+
found_user.active = False
|
121 |
+
session.add(found_user)
|
122 |
+
session.commit()
|
123 |
+
session.refresh(found_user)
|
124 |
+
return found_user
|
125 |
+
|
126 |
+
|
127 |
+
def get_user_status(
|
128 |
+
telegram_chat_id: int,
|
129 |
+
session: Session = next(get_session()),
|
130 |
+
) -> Optional[Tuple[int, bool]]:
|
131 |
+
"""
|
132 |
+
Get status of the user.
|
133 |
+
|
134 |
+
Args:
|
135 |
+
telegram_chat_id:
|
136 |
+
session:
|
137 |
+
|
138 |
+
Returns: (gmt,active)
|
139 |
+
|
140 |
+
"""
|
141 |
+
found_user = session.exec(
|
142 |
+
select(User).where(User.telegram_chat_id == telegram_chat_id)
|
143 |
+
).first()
|
144 |
+
if found_user is None:
|
145 |
+
return None, None
|
146 |
+
else:
|
147 |
+
return found_user.gmt, found_user.active
|
148 |
+
|
149 |
+
|
150 |
+
def select_random_memory(
|
151 |
+
user: UserCreate,
|
152 |
+
session: Session = next(get_session()),
|
153 |
+
) -> Optional[Union[Reminder, bool]]:
|
154 |
+
"""
|
155 |
+
Select random memory from user's memory-vault.
|
156 |
+
|
157 |
+
Args:
|
158 |
+
user:
|
159 |
+
session:
|
160 |
+
|
161 |
+
Returns:
|
162 |
+
|
163 |
+
"""
|
164 |
+
found_user = session.exec(
|
165 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
166 |
+
).first()
|
167 |
+
if found_user is None:
|
168 |
+
return None
|
169 |
+
if len(found_user.reminders) > 0:
|
170 |
+
memory_list = found_user.reminders
|
171 |
+
random_memory = random.choice(memory_list)
|
172 |
+
return random_memory
|
173 |
+
else:
|
174 |
+
return False
|
175 |
+
|
176 |
+
|
177 |
+
def list_memories(
|
178 |
+
user: UserCreate,
|
179 |
+
session: Session = next(get_session()),
|
180 |
+
) -> Optional[List[Reminder]]:
|
181 |
+
"""
|
182 |
+
Return all the memory-vault.
|
183 |
+
|
184 |
+
Args:
|
185 |
+
user:
|
186 |
+
session:
|
187 |
+
|
188 |
+
Returns:
|
189 |
+
|
190 |
+
"""
|
191 |
+
found_user = session.exec(
|
192 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
193 |
+
).first()
|
194 |
+
if found_user is None:
|
195 |
+
return None
|
196 |
+
|
197 |
+
return found_user.reminders
|
198 |
+
|
199 |
+
|
200 |
+
def add_memory(
|
201 |
+
user: UserCreate,
|
202 |
+
memory: str,
|
203 |
+
session: Session = next(get_session()),
|
204 |
+
) -> Optional[Union[Reminder, bool]]:
|
205 |
+
"""
|
206 |
+
Add a memory to user's memory-vault.
|
207 |
+
|
208 |
+
Args:
|
209 |
+
user:
|
210 |
+
memory:
|
211 |
+
session:
|
212 |
+
|
213 |
+
Returns: Reminder
|
214 |
+
|
215 |
+
"""
|
216 |
+
found_user = session.exec(
|
217 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
218 |
+
).first()
|
219 |
+
|
220 |
+
if found_user is None:
|
221 |
+
return None
|
222 |
+
|
223 |
+
else:
|
224 |
+
found_memory = session.exec(
|
225 |
+
select(Reminder).where(
|
226 |
+
and_(Reminder.user == found_user, Reminder.reminder == memory)
|
227 |
+
)
|
228 |
+
).first()
|
229 |
+
if found_memory is not None:
|
230 |
+
return False
|
231 |
+
else:
|
232 |
+
reminder = ReminderCreate(
|
233 |
+
reminder=memory,
|
234 |
+
user_id=found_user.id,
|
235 |
+
)
|
236 |
+
|
237 |
+
db_reminder = Reminder.from_orm(reminder)
|
238 |
+
session.add(db_reminder)
|
239 |
+
session.commit()
|
240 |
+
session.refresh(db_reminder)
|
241 |
+
return db_reminder
|
242 |
+
|
243 |
+
|
244 |
+
def delete_memory(
|
245 |
+
user: UserCreate,
|
246 |
+
memory_id: int,
|
247 |
+
session: Session = next(get_session()),
|
248 |
+
) -> Union[bool, str, None]:
|
249 |
+
"""
|
250 |
+
Delete a memory from user's memory-vault.
|
251 |
+
|
252 |
+
Args:
|
253 |
+
user:
|
254 |
+
memory_id:
|
255 |
+
session:
|
256 |
+
|
257 |
+
Returns: bool
|
258 |
+
|
259 |
+
"""
|
260 |
+
|
261 |
+
found_user = session.exec(
|
262 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
263 |
+
).first()
|
264 |
+
if found_user is None:
|
265 |
+
return None
|
266 |
+
if len(found_user.reminders) > memory_id:
|
267 |
+
reminder = found_user.reminders[memory_id]
|
268 |
+
memory = reminder.reminder
|
269 |
+
session.delete(reminder)
|
270 |
+
session.commit()
|
271 |
+
return memory
|
272 |
+
else:
|
273 |
+
return False
|
274 |
+
|
275 |
+
|
276 |
+
def delete_last_memory(
|
277 |
+
user: UserCreate,
|
278 |
+
session: Session = next(get_session()),
|
279 |
+
) -> Union[bool, str, None]:
|
280 |
+
"""
|
281 |
+
Delete the last memory from user's memory-vault.
|
282 |
+
|
283 |
+
Args:
|
284 |
+
user:
|
285 |
+
session:
|
286 |
+
|
287 |
+
Returns: bool
|
288 |
+
|
289 |
+
"""
|
290 |
+
|
291 |
+
found_user = session.exec(
|
292 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
293 |
+
).first()
|
294 |
+
if found_user is None:
|
295 |
+
return None
|
296 |
+
if found_user.reminders:
|
297 |
+
reminder = found_user.reminders.pop()
|
298 |
+
memory = reminder.reminder
|
299 |
+
session.delete(reminder)
|
300 |
+
session.commit()
|
301 |
+
return memory
|
302 |
+
else:
|
303 |
+
return False
|
304 |
+
|
305 |
+
|
306 |
+
def update_gmt(
|
307 |
+
user: UserCreate,
|
308 |
+
gmt: int,
|
309 |
+
session: Session = next(get_session()),
|
310 |
+
) -> Optional[User]:
|
311 |
+
"""
|
312 |
+
Update GMT of the user.
|
313 |
+
|
314 |
+
Returns: User or None
|
315 |
+
"""
|
316 |
+
found_user = session.exec(
|
317 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
318 |
+
).first()
|
319 |
+
if found_user is None:
|
320 |
+
return None
|
321 |
+
else:
|
322 |
+
found_user.gmt = gmt
|
323 |
+
session.add(found_user)
|
324 |
+
session.commit()
|
325 |
+
session.refresh(found_user)
|
326 |
+
return found_user
|
327 |
+
|
328 |
+
|
329 |
+
def get_schedule(
|
330 |
+
user: UserCreate,
|
331 |
+
session: Session = next(get_session()),
|
332 |
+
) -> Optional[str]:
|
333 |
+
"""
|
334 |
+
Get schedule of the user.
|
335 |
+
|
336 |
+
Args:
|
337 |
+
user:
|
338 |
+
session:
|
339 |
+
|
340 |
+
Returns: str or None
|
341 |
+
"""
|
342 |
+
found_user = session.exec(
|
343 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
344 |
+
).first()
|
345 |
+
if found_user is None:
|
346 |
+
return None
|
347 |
+
else:
|
348 |
+
return found_user.scheduled_hours
|
349 |
+
|
350 |
+
|
351 |
+
def reset_schedule(
|
352 |
+
user: UserCreate,
|
353 |
+
session: Session = next(get_session()),
|
354 |
+
) -> Optional[str]:
|
355 |
+
"""
|
356 |
+
Reset schedule of the user.
|
357 |
+
|
358 |
+
Args:
|
359 |
+
user:
|
360 |
+
session:
|
361 |
+
|
362 |
+
Returns: str or None
|
363 |
+
|
364 |
+
"""
|
365 |
+
found_user = session.exec(
|
366 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
367 |
+
).first()
|
368 |
+
if found_user is None:
|
369 |
+
return None
|
370 |
+
else:
|
371 |
+
found_user.scheduled_hours = default_schedule
|
372 |
+
session.add(found_user)
|
373 |
+
session.commit()
|
374 |
+
session.refresh(found_user)
|
375 |
+
return found_user.scheduled_hours
|
376 |
+
|
377 |
+
|
378 |
+
def remove_hour_from_schedule(
|
379 |
+
user: UserCreate,
|
380 |
+
hour: int,
|
381 |
+
session: Session = next(get_session()),
|
382 |
+
) -> Optional[str]:
|
383 |
+
"""
|
384 |
+
Remove all appearances of hour from schedule of the user.
|
385 |
+
|
386 |
+
Args:
|
387 |
+
user:
|
388 |
+
hour:
|
389 |
+
session:
|
390 |
+
|
391 |
+
Returns: str or None
|
392 |
+
|
393 |
+
"""
|
394 |
+
found_user = session.exec(
|
395 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
396 |
+
).first()
|
397 |
+
if found_user is None:
|
398 |
+
return None
|
399 |
+
else:
|
400 |
+
old_schedule = create_schedule_array(found_user.scheduled_hours)
|
401 |
+
new_schedule = []
|
402 |
+
for number in old_schedule:
|
403 |
+
if not hour == number:
|
404 |
+
new_schedule.append(number)
|
405 |
+
str_schedule_list = [str(number) for number in new_schedule]
|
406 |
+
str_schedule = ",".join(str_schedule_list)
|
407 |
+
found_user.scheduled_hours = str_schedule
|
408 |
+
session.add(found_user)
|
409 |
+
session.commit()
|
410 |
+
session.refresh(found_user)
|
411 |
+
return found_user.scheduled_hours
|
412 |
+
|
413 |
+
|
414 |
+
def add_hours_to_the_schedule(
|
415 |
+
user: UserCreate,
|
416 |
+
schedule_list: List[int],
|
417 |
+
session: Session = next(get_session()),
|
418 |
+
) -> Optional[str]:
|
419 |
+
"""
|
420 |
+
Add hours to the schedule of the user.
|
421 |
+
|
422 |
+
Args:
|
423 |
+
user:
|
424 |
+
schedule_list:
|
425 |
+
session:
|
426 |
+
|
427 |
+
Returns: str or None
|
428 |
+
|
429 |
+
"""
|
430 |
+
found_user = session.exec(
|
431 |
+
select(User).where(User.telegram_chat_id == user.telegram_chat_id)
|
432 |
+
).first()
|
433 |
+
if found_user is None:
|
434 |
+
return None
|
435 |
+
else:
|
436 |
+
old_schedule = create_schedule_array(found_user.scheduled_hours)
|
437 |
+
new_schedule = []
|
438 |
+
for number in old_schedule:
|
439 |
+
new_schedule.append(number)
|
440 |
+
for number in schedule_list:
|
441 |
+
new_schedule.append(number)
|
442 |
+
sorted_schedule = sorted(new_schedule)
|
443 |
+
str_sorted_schedule = [str(number) for number in sorted_schedule]
|
444 |
+
str_schedule = ",".join(str_sorted_schedule)
|
445 |
+
found_user.scheduled_hours = str_schedule
|
446 |
+
session.add(found_user)
|
447 |
+
session.commit()
|
448 |
+
session.refresh(found_user)
|
449 |
+
return found_user.scheduled_hours
|
450 |
+
|
451 |
+
|
452 |
+
def db_create_user(
|
453 |
+
user: UserCreate,
|
454 |
+
session: Session = next(get_session()),
|
455 |
+
) -> Optional[User]:
|
456 |
+
try:
|
457 |
+
user = User.from_orm(user)
|
458 |
+
session.add(user)
|
459 |
+
session.commit()
|
460 |
+
session.refresh(user)
|
461 |
+
return user
|
462 |
+
|
463 |
+
except Exception as ex:
|
464 |
+
return None
|
465 |
+
|
466 |
+
|
467 |
+
def db_read_users(
|
468 |
+
*,
|
469 |
+
only_active_users: bool = True,
|
470 |
+
session: Session = next(get_session()),
|
471 |
+
offset: int = 0,
|
472 |
+
limit: int = 100,
|
473 |
+
) -> List[User]:
|
474 |
+
if only_active_users:
|
475 |
+
users = session.exec(
|
476 |
+
select(User).where(User.active).offset(offset).limit(limit)
|
477 |
+
).all()
|
478 |
+
else:
|
479 |
+
users = session.exec(select(User).offset(offset).limit(limit)).all()
|
480 |
+
return users
|
481 |
+
|
482 |
+
|
483 |
+
def create_schedule_array(schedule_str: str) -> List[int]:
|
484 |
+
"""
|
485 |
+
Create schedule array from schedule string splitted by comas(,).
|
486 |
+
"""
|
487 |
+
if schedule_str == "":
|
488 |
+
return []
|
489 |
+
else:
|
490 |
+
schedule_list = []
|
491 |
+
str_list = schedule_str.split(",")
|
492 |
+
for str_number in str_list:
|
493 |
+
schedule_list.append(int(str_number))
|
494 |
+
|
495 |
+
return schedule_list
|
src/events.py
ADDED
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import random
|
2 |
+
import os
|
3 |
+
import sys
|
4 |
+
|
5 |
+
from typing import List
|
6 |
+
import datetime
|
7 |
+
import asyncio
|
8 |
+
from httpx import AsyncClient, Response
|
9 |
+
|
10 |
+
from .message_validations import ResponseToMessage
|
11 |
+
from .db import db_read_users, Reminder, User
|
12 |
+
from .constants import Constants
|
13 |
+
|
14 |
+
|
15 |
+
class Events:
|
16 |
+
TOKEN = os.environ.get("TELEGRAM_TOKEN")
|
17 |
+
TELEGRAM_SEND_MESSAGE_URL = f"https://api.telegram.org/bot{TOKEN}/sendMessage"
|
18 |
+
TELEGRAM_SET_WEBHOOK_URL = f"https://api.telegram.org/bot{TOKEN}/setWebhook"
|
19 |
+
TELEGRAM_SEND_DOCUMENT_URL = f"https://api.telegram.org/bot{TOKEN}/sendDocument"
|
20 |
+
|
21 |
+
PORT = 8000
|
22 |
+
HOST_URL = None
|
23 |
+
SELF_SIGNED = False
|
24 |
+
|
25 |
+
@classmethod
|
26 |
+
async def main_event(cls) -> None:
|
27 |
+
"""
|
28 |
+
Main Event Loop
|
29 |
+
|
30 |
+
Runs in a while loop, Triggers Events.send_user_hourly_memories at every hour.
|
31 |
+
"""
|
32 |
+
while True:
|
33 |
+
await asyncio.sleep(cls.get_time_until_next_hour())
|
34 |
+
async with AsyncClient() as client:
|
35 |
+
endpoint = f"http://0.0.0.0:{cls.PORT}/trigger_send_user_hourly_memories/{Events.TOKEN}"
|
36 |
+
response = await client.post(url=endpoint)
|
37 |
+
endpoint = f"http://0.0.0.0:{cls.PORT}/trigger_archive_db/{Events.TOKEN}"
|
38 |
+
response = await client.post(url=endpoint)
|
39 |
+
|
40 |
+
@classmethod
|
41 |
+
def get_time_until_next_hour(cls) -> float:
|
42 |
+
# Ref: https://stackoverflow.com/a/52808375/15282482
|
43 |
+
delta = datetime.timedelta(hours=1)
|
44 |
+
now = datetime.datetime.now()
|
45 |
+
next_hour = (now + delta).replace(microsecond=0, second=0, minute=0)
|
46 |
+
return (next_hour - now).total_seconds()
|
47 |
+
|
48 |
+
@classmethod
|
49 |
+
def send_user_hourly_memories(
|
50 |
+
cls,
|
51 |
+
user: User,
|
52 |
+
hour: int,
|
53 |
+
) -> None:
|
54 |
+
"""
|
55 |
+
Sends memories to user if the current_hour is in his schedule.
|
56 |
+
"""
|
57 |
+
hour = (hour + user.gmt) % 24
|
58 |
+
if user.scheduled_hours == "":
|
59 |
+
return
|
60 |
+
scheduled_hours = user.scheduled_hours.split(",")
|
61 |
+
number_of_messages_at_this_hour = 0
|
62 |
+
|
63 |
+
for str_hour in scheduled_hours:
|
64 |
+
if (
|
65 |
+
int(str_hour) > hour
|
66 |
+
): # Scheduled_hours are sorted, next items will be > hour as well.
|
67 |
+
break
|
68 |
+
if int(str_hour) == hour:
|
69 |
+
number_of_messages_at_this_hour += 1
|
70 |
+
number_of_messages_at_this_hour = min(
|
71 |
+
len(user.reminders), number_of_messages_at_this_hour
|
72 |
+
)
|
73 |
+
selected_reminders = random.sample(
|
74 |
+
user.reminders, number_of_messages_at_this_hour
|
75 |
+
)
|
76 |
+
for reminder in selected_reminders: # Send the memory in background
|
77 |
+
asyncio.create_task(
|
78 |
+
cls.send_a_message_to_user(user.telegram_chat_id, reminder.reminder)
|
79 |
+
)
|
80 |
+
now = datetime.datetime.now()
|
81 |
+
print(
|
82 |
+
f"Created task to, {user.name}, {reminder.reminder}, hour: {hour}, gmt: {user.gmt}, now: {now}"
|
83 |
+
)
|
84 |
+
|
85 |
+
@classmethod
|
86 |
+
async def send_message_list_at_background(
|
87 |
+
cls, telegram_chat_id: int, message_list: List[str]
|
88 |
+
) -> bool:
|
89 |
+
for message in message_list:
|
90 |
+
print(f"sending the message: {message}, to chat: {telegram_chat_id} ")
|
91 |
+
await Events.send_a_message_to_user(
|
92 |
+
telegram_id=telegram_chat_id, message=message
|
93 |
+
)
|
94 |
+
return True
|
95 |
+
|
96 |
+
@classmethod
|
97 |
+
async def send_a_message_to_user(
|
98 |
+
cls,
|
99 |
+
telegram_id: int,
|
100 |
+
message: str,
|
101 |
+
retry_count: int = 3,
|
102 |
+
sleep_time: float = 0.1,
|
103 |
+
) -> bool:
|
104 |
+
message = ResponseToMessage(
|
105 |
+
**{
|
106 |
+
"text": message,
|
107 |
+
"chat_id": telegram_id,
|
108 |
+
}
|
109 |
+
)
|
110 |
+
await asyncio.sleep(sleep_time)
|
111 |
+
for retry in range(retry_count):
|
112 |
+
print(f"Sending the message in send_a_message_to_user, count {retry}")
|
113 |
+
# Avoid too many requests error from Telegram
|
114 |
+
response = await cls.request(cls.TELEGRAM_SEND_MESSAGE_URL, message.dict())
|
115 |
+
if response.status_code == 200:
|
116 |
+
return True
|
117 |
+
elif response.status_code == 429:
|
118 |
+
retry_after = int(response.json()["parameters"]["retry_after"])
|
119 |
+
print(f"Retry After: {retry_after}, message: {message}")
|
120 |
+
await asyncio.sleep(retry_after)
|
121 |
+
else:
|
122 |
+
print(
|
123 |
+
f"Unhandled response code: {response.status_code}, response: {response.json()}"
|
124 |
+
)
|
125 |
+
return False
|
126 |
+
|
127 |
+
@classmethod
|
128 |
+
async def broadcast_message(cls, message: str) -> None:
|
129 |
+
users = db_read_users(limit=100000, only_active_users=False)
|
130 |
+
await asyncio.gather(
|
131 |
+
*(
|
132 |
+
Events.send_a_message_to_user(
|
133 |
+
user.telegram_chat_id,
|
134 |
+
message,
|
135 |
+
)
|
136 |
+
for user in users
|
137 |
+
)
|
138 |
+
)
|
139 |
+
|
140 |
+
@classmethod
|
141 |
+
async def request(cls, url: str, payload: dict, debug: bool = True) -> Response:
|
142 |
+
async with AsyncClient(timeout=30 * 60) as client:
|
143 |
+
request = await client.post(url, json=payload)
|
144 |
+
if debug:
|
145 |
+
print(request.json())
|
146 |
+
return request
|
147 |
+
|
148 |
+
@classmethod
|
149 |
+
async def set_telegram_webhook_url(cls) -> bool:
|
150 |
+
print(f"webhook_url:{cls.HOST_URL}")
|
151 |
+
if cls.SELF_SIGNED:
|
152 |
+
payload = {
|
153 |
+
"url": f"{cls.HOST_URL}/webhook/{cls.TOKEN}",
|
154 |
+
"certificate": open(os.environ.get("PEM_FILE"), "rb"),
|
155 |
+
}
|
156 |
+
else:
|
157 |
+
payload = {"url": f"{cls.HOST_URL}/webhook/{cls.TOKEN}"}
|
158 |
+
req = await cls.request(cls.TELEGRAM_SET_WEBHOOK_URL, payload)
|
159 |
+
return req.status_code == 200
|
160 |
+
|
161 |
+
@classmethod
|
162 |
+
def archive_db(cls) -> bool:
|
163 |
+
command = f'curl -v -F "chat_id={Constants.BROADCAST_CHAT_ID}" -F document=@database.db {cls.TELEGRAM_SEND_DOCUMENT_URL}'
|
164 |
+
os.system(command)
|
165 |
+
|
166 |
+
@classmethod
|
167 |
+
async def get_public_ip(cls):
|
168 |
+
# Reference: https://pytutorial.com/python-get-public-ip
|
169 |
+
|
170 |
+
endpoint = "https://ipinfo.io/json"
|
171 |
+
async with AsyncClient() as client:
|
172 |
+
response = await client.get(endpoint)
|
173 |
+
|
174 |
+
if response.status_code != 200:
|
175 |
+
sys.exit("Could not get the public ip, exiting!")
|
176 |
+
data = response.json()
|
177 |
+
|
178 |
+
return data["ip"]
|
src/listener.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
import datetime
|
3 |
+
|
4 |
+
from fastapi import FastAPI, Depends
|
5 |
+
from fastapi.concurrency import run_in_threadpool
|
6 |
+
from .db import *
|
7 |
+
from .message_validations import MessageBodyModel, ResponseToMessage
|
8 |
+
from .constants import Constants
|
9 |
+
from .events import Events
|
10 |
+
from .response_logic import ResponseLogic
|
11 |
+
|
12 |
+
app = FastAPI(openapi_url=None)
|
13 |
+
|
14 |
+
|
15 |
+
@app.on_event("startup")
|
16 |
+
def on_startup():
|
17 |
+
create_db_and_tables()
|
18 |
+
asyncio.create_task(Events.main_event())
|
19 |
+
|
20 |
+
|
21 |
+
@app.get("/health")
|
22 |
+
async def health():
|
23 |
+
return {"healthy": True}
|
24 |
+
|
25 |
+
|
26 |
+
@app.post(f"/webhook/{Events.TOKEN}")
|
27 |
+
async def listen_telegram_messages(message: MessageBodyModel):
|
28 |
+
print(message.dict())
|
29 |
+
|
30 |
+
if message.message:
|
31 |
+
name = message.message.from_field.first_name
|
32 |
+
chat_id = message.message.chat.id
|
33 |
+
text = message.message.text
|
34 |
+
language_code = message.message.from_field.language_code
|
35 |
+
if not text: # Edit of message etc.
|
36 |
+
return
|
37 |
+
else:
|
38 |
+
response_message = await ResponseLogic.create_response(
|
39 |
+
text, name, chat_id, language_code
|
40 |
+
)
|
41 |
+
return ResponseToMessage(
|
42 |
+
**{
|
43 |
+
"text": response_message,
|
44 |
+
"chat_id": chat_id,
|
45 |
+
}
|
46 |
+
)
|
47 |
+
|
48 |
+
if not message.message: # Bot is added to a group
|
49 |
+
if not message.my_chat_member:
|
50 |
+
return
|
51 |
+
|
52 |
+
chat_id = message.my_chat_member.chat.id
|
53 |
+
name = message.my_chat_member.from_field.first_name
|
54 |
+
language_code = message.my_chat_member.from_field.language_code
|
55 |
+
|
56 |
+
new_member = message.my_chat_member.new_chat_member
|
57 |
+
if (
|
58 |
+
new_member
|
59 |
+
and new_member.user.id == Constants.BOT_ID
|
60 |
+
and new_member.status == "member"
|
61 |
+
):
|
62 |
+
await Events.send_a_message_to_user(chat_id, Constants.hello)
|
63 |
+
await Events.send_a_message_to_user(
|
64 |
+
chat_id, Constants.Start.start_message(name, language_code)
|
65 |
+
)
|
66 |
+
await Events.send_a_message_to_user(
|
67 |
+
chat_id, Constants.Start.group_warning(name, language_code)
|
68 |
+
)
|
69 |
+
return
|
70 |
+
|
71 |
+
return
|
72 |
+
|
73 |
+
@app.post(f"/trigger_archive_db/{Events.TOKEN}")
|
74 |
+
def trigger_archive_db():
|
75 |
+
Events.archive_db()
|
76 |
+
|
77 |
+
|
78 |
+
@app.post(f"/trigger_send_user_hourly_memories/{Events.TOKEN}")
|
79 |
+
async def trigger_send_user_hourly_memories(*, session: Session = Depends(get_session)):
|
80 |
+
users = db_read_users(limit=100000, session=session)
|
81 |
+
now = datetime.datetime.now(datetime.timezone.utc)
|
82 |
+
print(f"Sending is triggered at hour {now.hour}")
|
83 |
+
for user in users:
|
84 |
+
Events.send_user_hourly_memories(user, now.hour)
|
src/message_validations.py
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Optional, List
|
2 |
+
|
3 |
+
from pydantic import BaseModel, Field
|
4 |
+
|
5 |
+
|
6 |
+
class Chat(BaseModel):
|
7 |
+
last_name: Optional[str]
|
8 |
+
id: Optional[int]
|
9 |
+
type: Optional[str]
|
10 |
+
first_name: Optional[str]
|
11 |
+
username: Optional[str]
|
12 |
+
|
13 |
+
|
14 |
+
class From(BaseModel):
|
15 |
+
last_name: Optional[str]
|
16 |
+
id: Optional[int]
|
17 |
+
first_name: Optional[str]
|
18 |
+
user_name: Optional[str]
|
19 |
+
language_code: Optional[str]
|
20 |
+
is_bot: Optional[str]
|
21 |
+
|
22 |
+
|
23 |
+
class ReplyMessage(BaseModel):
|
24 |
+
date: Optional[int]
|
25 |
+
chat: Optional[Chat]
|
26 |
+
message_id: Optional[int]
|
27 |
+
text: Optional[str]
|
28 |
+
|
29 |
+
|
30 |
+
class Photo(BaseModel):
|
31 |
+
file_id: Optional[str]
|
32 |
+
|
33 |
+
|
34 |
+
class Message(BaseModel):
|
35 |
+
date: Optional[int]
|
36 |
+
chat: Optional[Chat]
|
37 |
+
message_id: Optional[str]
|
38 |
+
from_field: From = Field(alias="from")
|
39 |
+
text: Optional[str]
|
40 |
+
photo: Optional[List[Photo]]
|
41 |
+
|
42 |
+
|
43 |
+
class ChatGroup(BaseModel):
|
44 |
+
id: Optional[int]
|
45 |
+
title: Optional[str]
|
46 |
+
type: Optional[str]
|
47 |
+
|
48 |
+
|
49 |
+
class MockVal(BaseModel):
|
50 |
+
rand_int: Optional[int]
|
51 |
+
|
52 |
+
|
53 |
+
class OtherChatMember(BaseModel):
|
54 |
+
user: Optional[From]
|
55 |
+
status: Optional[str]
|
56 |
+
|
57 |
+
|
58 |
+
class MyChatMember(BaseModel):
|
59 |
+
rand_int: Optional[int]
|
60 |
+
chat: Optional[ChatGroup]
|
61 |
+
from_field: Optional[From] = Field(alias="from")
|
62 |
+
date: Optional[int]
|
63 |
+
old_chat_member: Optional[OtherChatMember]
|
64 |
+
new_chat_member: Optional[OtherChatMember]
|
65 |
+
|
66 |
+
|
67 |
+
class MessageBodyModel(BaseModel):
|
68 |
+
update_id: Optional[int]
|
69 |
+
message: Optional[Message]
|
70 |
+
my_chat_member: Optional[MyChatMember]
|
71 |
+
reply_to_message: Optional[ReplyMessage]
|
72 |
+
|
73 |
+
|
74 |
+
class ResponseToMessage(BaseModel):
|
75 |
+
method: Optional[str] = "sendMessage"
|
76 |
+
chat_id: Optional[int] = 861126057
|
77 |
+
text: Optional[str] = ""
|
78 |
+
parse_mode: Optional[str] = "Markdown"
|
src/response_logic.py
ADDED
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import random
|
2 |
+
import asyncio
|
3 |
+
|
4 |
+
from .db import *
|
5 |
+
from .events import Events
|
6 |
+
from .constants import Constants
|
7 |
+
|
8 |
+
|
9 |
+
class ResponseLogic:
|
10 |
+
@staticmethod
|
11 |
+
async def create_response(
|
12 |
+
text: str, name: str, chat_id: int, language_code: str
|
13 |
+
) -> str:
|
14 |
+
|
15 |
+
# Edge case check for "add\nSentence"
|
16 |
+
line_split_text = text.split("\n")
|
17 |
+
line_split_first_word = line_split_text[0]
|
18 |
+
split_text = text.split(" ")
|
19 |
+
first_word = split_text[0]
|
20 |
+
user = UserCreate(
|
21 |
+
name=name,
|
22 |
+
telegram_chat_id=chat_id,
|
23 |
+
)
|
24 |
+
if ResponseLogic.check_command_type(
|
25 |
+
first_word, "add"
|
26 |
+
) or ResponseLogic.check_command_type(line_split_first_word, "add"):
|
27 |
+
if ResponseLogic.check_command_type(line_split_first_word, "add"):
|
28 |
+
memory = "\n".join(split_text[1:])
|
29 |
+
else:
|
30 |
+
memory = " ".join(split_text[1:])
|
31 |
+
if str.isspace(memory) or memory == "":
|
32 |
+
return Constants.Add.no_sentence(name, language_code)
|
33 |
+
else:
|
34 |
+
reminder = add_memory(user, memory)
|
35 |
+
if reminder is None:
|
36 |
+
return Constants.Common.inactive_user(name, language_code)
|
37 |
+
elif reminder is False:
|
38 |
+
return Constants.Add.already_added(name, language_code)
|
39 |
+
else:
|
40 |
+
memory = reminder.reminder
|
41 |
+
return Constants.Add.success(name, language_code, memory)
|
42 |
+
elif ResponseLogic.check_command_type(first_word, "start"):
|
43 |
+
user = join_user(user)
|
44 |
+
await Events.send_a_message_to_user(chat_id, Constants.hello)
|
45 |
+
return Constants.Start.start_message(name, language_code)
|
46 |
+
elif ResponseLogic.check_command_type(first_word, "help"):
|
47 |
+
message = Constants.Help.help_message(name, language_code)
|
48 |
+
return message
|
49 |
+
|
50 |
+
elif ResponseLogic.check_command_type(first_word, "join"):
|
51 |
+
user = join_user(user)
|
52 |
+
|
53 |
+
if user is not None:
|
54 |
+
return Constants.Join.successful_join(name, language_code)
|
55 |
+
else:
|
56 |
+
return Constants.Join.already_joined(name, language_code)
|
57 |
+
|
58 |
+
elif ResponseLogic.check_command_type(first_word, "leave"):
|
59 |
+
user = leave_user(user)
|
60 |
+
if user is not None and user.telegram_chat_id == chat_id:
|
61 |
+
return Constants.Leave.successful_leave(name, language_code)
|
62 |
+
else:
|
63 |
+
return Constants.Leave.already_left(name, language_code)
|
64 |
+
|
65 |
+
elif ResponseLogic.check_command_type(first_word, "send"):
|
66 |
+
if len(split_text) == 1: # send
|
67 |
+
memory = select_random_memory(user)
|
68 |
+
if memory is None:
|
69 |
+
return Constants.Common.inactive_user(name, language_code)
|
70 |
+
elif memory is False:
|
71 |
+
return Constants.Common.no_memory_found(name, language_code)
|
72 |
+
else:
|
73 |
+
return memory.reminder
|
74 |
+
else: # send number
|
75 |
+
try:
|
76 |
+
number_of_sending = int(split_text[1])
|
77 |
+
if not (1 <= number_of_sending < 50):
|
78 |
+
return Constants.Send.send_count_out_of_bound(
|
79 |
+
name, language_code
|
80 |
+
)
|
81 |
+
except:
|
82 |
+
return Constants.Send.send_count_out_of_bound(name, language_code)
|
83 |
+
|
84 |
+
all_memories = list_memories(user)
|
85 |
+
if len(all_memories) == 0:
|
86 |
+
return Constants.Common.no_memory_found(name, language_code)
|
87 |
+
|
88 |
+
send_count = min(len(all_memories), number_of_sending)
|
89 |
+
selected_memories = random.sample(all_memories, send_count)
|
90 |
+
for memory in selected_memories: # Send the memories in background
|
91 |
+
asyncio.create_task(
|
92 |
+
Events.send_a_message_to_user(
|
93 |
+
user.telegram_chat_id, memory.reminder
|
94 |
+
)
|
95 |
+
)
|
96 |
+
return ""
|
97 |
+
|
98 |
+
elif ResponseLogic.check_command_type(first_word, "list"):
|
99 |
+
memories = list_memories(user)
|
100 |
+
memory_count = len(memories)
|
101 |
+
if memories is None:
|
102 |
+
return Constants.Common.inactive_user(name, language_code)
|
103 |
+
elif memory_count == 0:
|
104 |
+
return Constants.Common.no_memory_found(name, language_code)
|
105 |
+
else:
|
106 |
+
background_message_list = []
|
107 |
+
for message_id, reminder in enumerate(memories):
|
108 |
+
background_message_list.append(
|
109 |
+
f"\n{message_id}: {reminder.reminder}"
|
110 |
+
)
|
111 |
+
asyncio.create_task(
|
112 |
+
Events.send_message_list_at_background(
|
113 |
+
telegram_chat_id=chat_id, message_list=background_message_list
|
114 |
+
)
|
115 |
+
)
|
116 |
+
|
117 |
+
response_message = Constants.List.list_messages(
|
118 |
+
name, memory_count, language_code
|
119 |
+
)
|
120 |
+
return response_message
|
121 |
+
|
122 |
+
elif ResponseLogic.check_command_type(first_word, "delete"):
|
123 |
+
if len(split_text) < 2:
|
124 |
+
return Constants.Delete.no_id(name, language_code)
|
125 |
+
else:
|
126 |
+
try:
|
127 |
+
memory_id = int(split_text[1])
|
128 |
+
if memory_id < 0:
|
129 |
+
return Constants.Delete.no_id(name, language_code)
|
130 |
+
else:
|
131 |
+
response = delete_memory(user, memory_id)
|
132 |
+
if response is None:
|
133 |
+
return Constants.Common.inactive_user(name, language_code)
|
134 |
+
elif response is False:
|
135 |
+
return Constants.Delete.no_id(name, language_code)
|
136 |
+
else:
|
137 |
+
memory = response
|
138 |
+
|
139 |
+
return Constants.Delete.success(name, language_code, memory)
|
140 |
+
|
141 |
+
except Exception as ex:
|
142 |
+
return Constants.Delete.no_id(name, language_code)
|
143 |
+
|
144 |
+
elif ResponseLogic.check_command_type(first_word, "schedule"):
|
145 |
+
if len(split_text) == 1:
|
146 |
+
schedule = get_schedule(user)
|
147 |
+
if schedule is None:
|
148 |
+
return Constants.Common.inactive_user(name, language_code)
|
149 |
+
elif schedule == "":
|
150 |
+
return Constants.Schedule.empty_schedule(name, language_code)
|
151 |
+
else:
|
152 |
+
return Constants.Schedule.success(name, language_code, schedule)
|
153 |
+
|
154 |
+
elif len(split_text) > 1:
|
155 |
+
if split_text[1] == "reset":
|
156 |
+
new_schedule = reset_schedule(user)
|
157 |
+
if new_schedule is None:
|
158 |
+
return Constants.Common.inactive_user(name, language_code)
|
159 |
+
else:
|
160 |
+
return Constants.Schedule.success(
|
161 |
+
name, language_code, new_schedule
|
162 |
+
)
|
163 |
+
|
164 |
+
elif split_text[1] == "add":
|
165 |
+
str_numbers = []
|
166 |
+
preceding_text = " ".join(split_text[2:])
|
167 |
+
if str.isspace(preceding_text) or preceding_text == "":
|
168 |
+
return Constants.Schedule.no_number_found(name, language_code)
|
169 |
+
|
170 |
+
number_check = True
|
171 |
+
try:
|
172 |
+
for str_number in split_text[2:]:
|
173 |
+
number = int(str_number)
|
174 |
+
str_numbers.append(number)
|
175 |
+
if not (0 <= number <= 23):
|
176 |
+
number_check = False
|
177 |
+
break
|
178 |
+
except:
|
179 |
+
return Constants.Schedule.add_incorrect_number_input(
|
180 |
+
name, language_code
|
181 |
+
)
|
182 |
+
|
183 |
+
if not number_check:
|
184 |
+
return Constants.Schedule.add_incorrect_number_input(
|
185 |
+
name, language_code
|
186 |
+
)
|
187 |
+
|
188 |
+
else:
|
189 |
+
new_schedule = add_hours_to_the_schedule(user, str_numbers)
|
190 |
+
if new_schedule is None:
|
191 |
+
return Constants.Common.inactive_user(name, language_code)
|
192 |
+
else:
|
193 |
+
return Constants.Schedule.success(
|
194 |
+
name, language_code, new_schedule
|
195 |
+
)
|
196 |
+
|
197 |
+
elif split_text[1] == "remove":
|
198 |
+
preceding_text = " ".join(split_text[2:])
|
199 |
+
number_check = True
|
200 |
+
if str.isspace(preceding_text) or preceding_text == "":
|
201 |
+
return Constants.Schedule.remove_incorrect_number_input(
|
202 |
+
name, language_code
|
203 |
+
)
|
204 |
+
else:
|
205 |
+
try:
|
206 |
+
str_number = split_text[2]
|
207 |
+
number = int(str_number)
|
208 |
+
if not (0 <= number <= 23):
|
209 |
+
number_check = False
|
210 |
+
except:
|
211 |
+
return Constants.Schedule.remove_incorrect_number_input(
|
212 |
+
name, language_code
|
213 |
+
)
|
214 |
+
|
215 |
+
if not number_check:
|
216 |
+
return Constants.Schedule.remove_incorrect_number_input(
|
217 |
+
name, language_code
|
218 |
+
)
|
219 |
+
|
220 |
+
else:
|
221 |
+
new_schedule = remove_hour_from_schedule(
|
222 |
+
user, int(str_number)
|
223 |
+
)
|
224 |
+
if new_schedule is None:
|
225 |
+
return Constants.Common.inactive_user(
|
226 |
+
name, language_code
|
227 |
+
)
|
228 |
+
else:
|
229 |
+
return Constants.Schedule.success(
|
230 |
+
name, language_code, new_schedule
|
231 |
+
)
|
232 |
+
|
233 |
+
else:
|
234 |
+
return Constants.Schedule.unknown_command(name, language_code)
|
235 |
+
|
236 |
+
elif ResponseLogic.check_command_type(first_word, "gmt"):
|
237 |
+
try:
|
238 |
+
gmt = int(split_text[1])
|
239 |
+
except Exception as ex:
|
240 |
+
return Constants.Gmt.incorrect_timezone(name, language_code)
|
241 |
+
if not (-12 <= gmt <= 12):
|
242 |
+
return Constants.Gmt.incorrect_timezone(name, language_code)
|
243 |
+
else:
|
244 |
+
user = update_gmt(user, gmt)
|
245 |
+
if user is None:
|
246 |
+
return Constants.Common.inactive_user(name, language_code)
|
247 |
+
else:
|
248 |
+
return Constants.Gmt.success(name, language_code, user.gmt)
|
249 |
+
|
250 |
+
elif ResponseLogic.check_command_type(first_word, "broadcast"):
|
251 |
+
if not chat_id == Constants.BROADCAST_CHAT_ID:
|
252 |
+
return Constants.Broadcast.no_right(name, language_code)
|
253 |
+
else:
|
254 |
+
normalized_text = " ".join(split_text[1:])
|
255 |
+
if str.isspace(normalized_text) or normalized_text == "":
|
256 |
+
return Constants.Broadcast.no_sentence_found(name, language_code)
|
257 |
+
else:
|
258 |
+
await Events.broadcast_message(normalized_text)
|
259 |
+
return Constants.Broadcast.success(name, language_code)
|
260 |
+
|
261 |
+
elif ResponseLogic.check_command_type(first_word, "status"):
|
262 |
+
gmt, active = get_user_status(chat_id)
|
263 |
+
if gmt is None:
|
264 |
+
return Constants.Common.inactive_user(name, language_code)
|
265 |
+
else:
|
266 |
+
schedule = get_schedule(user)
|
267 |
+
memory_count = len(list_memories(user))
|
268 |
+
return Constants.Status.get_status(
|
269 |
+
name, language_code, gmt, active, schedule, memory_count
|
270 |
+
)
|
271 |
+
|
272 |
+
elif ResponseLogic.check_command_type(first_word, "feedback"):
|
273 |
+
|
274 |
+
message = " ".join(split_text[1:])
|
275 |
+
message_with_user_information = (
|
276 |
+
message + f"\nFrom the user: *{name}* \nchat id: *{chat_id}*"
|
277 |
+
)
|
278 |
+
|
279 |
+
if str.isspace(message) or message == "":
|
280 |
+
return Constants.Feedback.no_message(name, language_code)
|
281 |
+
else:
|
282 |
+
success = await Events.send_a_message_to_user(
|
283 |
+
Constants.FEEDBACK_FORWARD_CHAT_ID, message_with_user_information
|
284 |
+
)
|
285 |
+
if success:
|
286 |
+
return Constants.Feedback.success(name, language_code, message)
|
287 |
+
else:
|
288 |
+
return Constants.Feedback.fail(name, language_code)
|
289 |
+
|
290 |
+
elif ResponseLogic.check_command_type(first_word, "deletelast"):
|
291 |
+
response = delete_last_memory(user)
|
292 |
+
if response is None:
|
293 |
+
return Constants.Common.inactive_user(name, language_code)
|
294 |
+
elif response is False:
|
295 |
+
return Constants.Common.no_memory_found(name, language_code)
|
296 |
+
else:
|
297 |
+
return Constants.Delete.success(name, language_code, response)
|
298 |
+
|
299 |
+
elif ResponseLogic.check_command_type(first_word, "support"):
|
300 |
+
return Constants.Support.support(name, language_code)
|
301 |
+
|
302 |
+
elif ResponseLogic.check_command_type(first_word, "tutorial1"):
|
303 |
+
return Constants.Tutorial.tutorial_1(name, language_code)
|
304 |
+
|
305 |
+
elif ResponseLogic.check_command_type(first_word, "tutorial2"):
|
306 |
+
return Constants.Tutorial.tutorial_2(name, language_code)
|
307 |
+
|
308 |
+
elif ResponseLogic.check_command_type(first_word, "tutorial3"):
|
309 |
+
return Constants.Tutorial.tutorial_3(name, language_code)
|
310 |
+
|
311 |
+
else:
|
312 |
+
return Constants.Common.unknown_command(name, language_code)
|
313 |
+
|
314 |
+
@staticmethod
|
315 |
+
def check_command_type(input_command: str, correct_command: str) -> bool:
|
316 |
+
"""
|
317 |
+
Checks the first word of the command, which starts the decision tree.
|
318 |
+
|
319 |
+
Args:
|
320 |
+
input_command:
|
321 |
+
correct_command:
|
322 |
+
|
323 |
+
Returns: bool
|
324 |
+
|
325 |
+
"""
|
326 |
+
input_command = input_command.lower()
|
327 |
+
if (
|
328 |
+
input_command == correct_command
|
329 |
+
or input_command == f"/{correct_command}"
|
330 |
+
or input_command == f"/{correct_command}@memory_vault_bot"
|
331 |
+
):
|
332 |
+
return True
|
333 |
+
else:
|
334 |
+
return False
|