Skip to content

Commit 1a63029

Browse files
committed
beta: Scheduled Verse
1 parent 722ad12 commit 1a63029

17 files changed

+181
-55
lines changed

ChangeLogs.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
**Latests are on top**
44

55

6+
## 13) 19th April, 2024
7+
- `/schedule` command added. Now, you can schedule a verse to be sent at a specific time of every day.
8+
69

710
## 12) 5th April, 2024
811
- Integrate custom local database logic to make database operations faster and more efficient.

TODO.md

-6
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,5 @@
22

33

44
- Code Refactoring
5-
- [ ] make a variable named `handlers` in every module which will contain the handlers for the module
6-
- [ ] Use decorators. eg. `@usableBy("admin")`, `@usableBy("group_admin")`
75
- [ ] Implement active users in the database
86

9-
• Scheduled Ayah Sending for channel
10-
11-
- Admins will be able to set a time when they want to receive a random ayah from the Quran
12-
- Admins will be able to set the time zone

bot/bot.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from datetime import datetime, timezone
88

99
from .handlers.database import db
10+
from .handlers.sendScheduled import jobSendScheduled
1011
from .handlers import exportedHandlers, handleErrors, middleware
1112

1213

13-
1414
# Load Environment Variables
1515
load_dotenv()
1616
LOCAL = os.environ.get("LOCAL")
@@ -30,6 +30,9 @@ def runBot(token):
3030
.connect_timeout(333)
3131
.build()
3232
)
33+
34+
app.job_queue.run_repeating(jobSendScheduled, 60, name="scheduledMessages")
35+
3336
app.add_handler(
3437
TypeHandler(Update, middleware), group=-19
3538
) # called in every update, then passed to other handlers
@@ -40,7 +43,7 @@ def runBot(token):
4043

4144
# Send a message to the admin when the bot starts
4245
loop = asyncio.get_event_loop()
43-
46+
4447
msg = f"<b>Bot started at {datetime.now(timezone.utc).strftime('%d %B %Y, %H:%M:%S %A UTC')} 🚀</b>"
4548
loop.run_until_complete(app.bot.sendMessage(5596148289, msg))
4649

@@ -52,13 +55,12 @@ def startBot():
5255
print("Please put your bot token in `.env` file")
5356
print()
5457
return
55-
58+
5659
if LOCAL:
5760
print("-" * 27)
5861
print("Running on local test Bot")
5962
print("-" * 27)
6063

61-
6264
runBot(TOKEN)
6365

6466

bot/handlers/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from ..quran import QuranClass
22

3-
# Variables has to be declared before importing handlers
3+
# Import after the class definition to avoid circular import
44
Quran = QuranClass()
55

66
from .helpers import generateSurahButtons
@@ -32,5 +32,5 @@ class Constants:
3232
*commandHandlers,
3333
*messageHandlers,
3434
*inlineQueryHandlers,
35-
*callbackQueryHandlers
36-
]
35+
*callbackQueryHandlers,
36+
]

bot/handlers/callbackQuery/handleButtonPress.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ async def handleButtonPress(u: Update, c):
198198

199199
elif method == "close":
200200
return await message.delete()
201-
201+
202202
elif method == "schedule":
203203
return await handleSchedule(u, c)
204204

bot/handlers/command/adminCommand.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,7 @@ async def evaluateCode(u: Update, c):
198198

199199

200200
exportedHandlers = [
201-
CommandHandler(
202-
"admin", adminCommand, filters.User(db.getAllAdmins())
203-
), # TODO: use decorators instead
201+
CommandHandler("admin", adminCommand, filters.User(db.getAllAdmins())),
204202
CommandHandler("forward", forwardMessage, filters.User(db.getAllAdmins())),
205203
CommandHandler("getUser", getUser, filters.User(db.getAllAdmins())),
206204
CommandHandler("eval", evaluateCode, filters.User(db.getAllAdmins())),

bot/handlers/command/commands.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ async def aboutCommand(u: Update, c):
6464
async def useCommand(u: Update, c):
6565
"""Sends a message to the user on how to use the bot"""
6666
message = u.effective_message
67-
url = "https://telegra.ph/Al-Quran-05-29"
67+
url = "https://telegra.ph/Al-Quran-05-29" # old
68+
url = "https://telegra.ph/Usage-of-Quran-Bot-04-19"
6869
reply = replies.howToUse.format(telegraphURL=url)
6970

7071
buttons = InlineKeyboardMarkup(

bot/handlers/command/scheduleVerseSend.py

+44-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1+
import time
12
import regex
23

3-
from ..database import db
44
from .. import Quran
5+
from ..database import db
56
from ..helpers.decorators import onlyGroupAdmin
67

7-
from telegram import Update, Bot, InlineKeyboardButton, InlineKeyboardMarkup
8+
from datetime import datetime, tzinfo
89
from telegram.ext import MessageHandler, filters, ContextTypes
10+
from telegram import Update, Bot, InlineKeyboardButton, InlineKeyboardMarkup
911

1012

11-
def _addScheduleToTemp(chatID: int, time: dict, chatType: str, langs: list = None):
13+
def _addScheduleToTemp(chatID: int, time: str, chatType: str, langs: list = None):
1214
"""Add a schedule to the temp database."""
1315
langs = [i for i in langs if i] or ["english_1"]
16+
langs = langs[:3]
1417
collection = db.db.schedules
1518
return collection.update_one(
1619
{"_id": chatID},
@@ -44,9 +47,6 @@ def _addScheduleToTemp(chatID: int, time: dict, chatType: str, langs: list = Non
4447
regex.IGNORECASE,
4548
) # Match for: 11:49 pm - eng ara tur {'hour': '11', 'minute': '49', 'ampm': 'pm', 'langs': 'eng ara tur'}
4649

47-
# TODO: run repeating every minute to check if the time has come
48-
# Enable, Disable, Delete schedule
49-
5050

5151
# /schedule 11:49 pm - eng ara
5252
@onlyGroupAdmin
@@ -68,12 +68,29 @@ async def scheduleCommand(u: Update, c: ContextTypes.DEFAULT_TYPE):
6868

6969
langs = [Quran.detectLanguage(i) for i in langs]
7070
languages = [str(Quran.getTitleLanguageFromLang(i)) for i in langs]
71+
currentTime = datetime.utcnow()
72+
hours, minutes = result.split(":")
73+
remaining = (
74+
datetime(
75+
year=currentTime.year,
76+
month=currentTime.month,
77+
day=currentTime.day,
78+
hour=int(hours),
79+
minute=int(minutes),
80+
second=0,
81+
)
82+
- currentTime
83+
)
84+
remaining = remaining.total_seconds()
85+
86+
remaining = time.strftime("%H:%M:%S", time.gmtime(remaining))
7187

7288
msg = f"""
7389
<b>Schedule Enabled</b>
7490
A random verse will be sent at the following time:
7591
76-
<b>Time:</b> {result["hour"]:02d}:{result["minute"]:02d} UTC (24-hour format)
92+
<b>Time:</b> {result} UTC (24-hour format)
93+
<b>Remaining:</b> {remaining}
7794
7895
<b>Language:</b> {", ".join(languages) or "English"}
7996
@@ -114,18 +131,35 @@ async def showSchedule(u: Update, c: ContextTypes.DEFAULT_TYPE):
114131
"No schedule found. See /help for more information."
115132
)
116133

117-
time = data.get("time")
134+
runTime = data.get("time")
118135
langs = data.get("langs")
119136
chatType = data.get("chatType")
120137
enabled = data.get("enabled")
121138

122139
languages = [str(Quran.getTitleLanguageFromLang(i)) for i in langs]
140+
hours, minutes = runTime.split(":")
141+
142+
currentTime = datetime.utcnow()
143+
remaining = (
144+
datetime(
145+
year=currentTime.year,
146+
month=currentTime.month,
147+
day=currentTime.day,
148+
hour=int(hours),
149+
minute=int(minutes),
150+
second=0,
151+
)
152+
- currentTime
153+
)
154+
remaining = remaining.total_seconds()
155+
remaining = time.strftime("%H:%M:%S", time.gmtime(remaining))
123156

124157
msg = f"""
125158
<b>Schedule Information</b>
126159
A random verse will be sent at the following time:
127160
128-
<b>Time:</b> {time["hour"]:02d}:{time["minute"]:02d} UTC (24-hour format)
161+
<b>Time:</b> {hours}:{minutes} UTC (24-hour format)
162+
<b>Remaining:</b> {remaining}
129163
130164
<b>Languages:</b> <code>{", ".join(languages) or "English"}</code>
131165
<b>Status:</b> <i>{"Enabled" if enabled else "Disabled"}</i>
@@ -190,7 +224,7 @@ async def _validateTime(message, text: str):
190224
else:
191225
if hour >= 12:
192226
hour -= 12
193-
227+
return f"{hour:02d}:{minute:02d}" # 24-hour format
194228
return {"hour": hour, "minute": minute}
195229

196230

bot/handlers/database.py

+2-20
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class Database:
9494
def __init__(self) -> None:
9595
uri = os.environ.get("MONGODB_URI")
9696
self.client = MongoClient(uri, server_api=ServerApi("1"))
97-
self.db = self.client.quranbot_test # TODO: change it to quranbot
97+
self.db = self.client.quranbot
9898

9999
self.defaultSettings = {
100100
"font": 1, # 1 -> Uthmani, 2 -> Simple
@@ -267,9 +267,9 @@ def updateCounter(self):
267267
# self.db.drop_collection(self.db.chats)
268268

269269

270-
271270
db = Database()
272271

272+
273273
async def main():
274274
db = Database()
275275
users = db.getAllUsers()
@@ -278,24 +278,6 @@ async def main():
278278
print("Total Chats:", len(chats))
279279
print(db.getAllAdmins())
280280

281-
# test
282-
db = db.db
283-
if not db.schedules.find_one({"_id": 919919191}):
284-
db.schedules.insert_one(
285-
{
286-
"_id": 919919191,
287-
"time": "12:12", "say": "meow"
288-
}
289-
)
290-
291-
print(db.schedules.find_one({"_id": 919919191}))
292-
293-
db.schedules.update_one(
294-
{"_id": 919919191},
295-
{"$set": {"time": "12:34", "say": "hello"}},
296-
)
297-
print(db.schedules.find_one({"_id": 919919191}))
298-
299281

300282
if __name__ == "__main__":
301283
asyncio.run(main())

bot/handlers/errorHandler.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ async def handleErrors(u: Update, c: CallbackContext):
5656
admins = db.getAllAdmins()
5757
data = {
5858
"error_message": str(c.error),
59-
"error": tbString.replace("\\n", '\n'),
59+
"error": tbString.replace("\\n", "\n"),
6060
"update": u.to_dict(),
6161
"sendingError": messageSendingError,
6262
}

bot/handlers/helpers/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
from .generateSurahButtons import generateSurahButtons
44
from .getAyahReply import getAyahReply, getAyahReplyFromPreference
55

6-
from .getValidReply import getValidReply
6+
from .getValidReply import getValidReply

bot/handlers/inlineQuery/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .handleInlineQuery import exportedHandlers
22

3-
handlers = [*exportedHandlers]
3+
handlers = [*exportedHandlers]

bot/handlers/message/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .handleMessage import exportedHandlers
22

3-
handlers = [*exportedHandlers]
3+
handlers = [*exportedHandlers]

bot/handlers/middleware.py

-1
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,3 @@ async def middleware(u: Update, c):
3838

3939
if isGroup and not chat: # for groups
4040
chat = db.addChat(chatID)
41-

bot/handlers/replies.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@
3535
/settings - Change your settings
3636
/surah - The list of all the Surahs
3737
/get x:y - Get the ayah x from Surah y
38-
/&lt;lang&gt; x:y - Get the ayah x from Surah y in the language &lt;lang&gt;
39-
38+
/lang x:y - Get the ayah <code>x</code> from Surah <code>y</code> in the language &lt;lang&gt;
39+
/schedule 11:30 - Get a random ayah at 11:30 everyday
40+
<i>See the Telegraph for more in depth usage of schedule</i>
4041
4142
/langs, /languages, /translations - Get the list of all the available translations
4243
/rand, /random - Get a random ayah

0 commit comments

Comments
 (0)