diff --git a/database/nightmode_db.py b/database/nightmode_db.py
new file mode 100644
index 00000000..ca70ec2a
--- /dev/null
+++ b/database/nightmode_db.py
@@ -0,0 +1,17 @@
+from . import mongo, dbname
+import os
+from apscheduler.schedulers.asyncio import AsyncIOScheduler
+from apscheduler.jobstores.mongodb import MongoDBJobStore
+
+TZ = os.environ.get("TIME_ZONE", "Asia/Jakarta")
+
+
+jobstores = {
+ 'default': MongoDBJobStore(
+ client=mongo,
+ database=dbname,
+ collection='nightmode')}
+
+scheduler = AsyncIOScheduler(
+ jobstores=jobstores,
+ timezone=TZ)
\ No newline at end of file
diff --git a/misskaty/core/decorator/ratelimiter.py b/misskaty/core/decorator/ratelimiter.py
index 53cec490..cd9a411d 100644
--- a/misskaty/core/decorator/ratelimiter.py
+++ b/misskaty/core/decorator/ratelimiter.py
@@ -18,7 +18,7 @@ def ratelimiter(func: Callable) -> Callable:
@wraps(func)
async def decorator(client: Client, update: Union[Message, CallbackQuery]):
- userid = update.from_user.id or update.sender_chat.id
+ userid = update.from_user.id if update.from_user else update.sender_chat.id
is_limited = await ratelimit.acquire(userid)
if is_limited and userid not in warned_users:
diff --git a/misskaty/core/ratelimiter_func.py b/misskaty/core/ratelimiter_func.py
index 97a26141..6252b10b 100644
--- a/misskaty/core/ratelimiter_func.py
+++ b/misskaty/core/ratelimiter_func.py
@@ -13,17 +13,17 @@ class RateLimiter:
def __init__(self) -> None:
- # 3 requests per seconds
- self.second_rate = RequestRate(3, Duration.SECOND)
+ # 2 requests per seconds
+ self.second_rate = RequestRate(2, Duration.SECOND)
- # 20 requests per minute.
- self.minute_rate = RequestRate(20, Duration.MINUTE)
+ # 15 requests per minute.
+ self.minute_rate = RequestRate(15, Duration.MINUTE)
- # 1000 requests per hour
- self.hourly_rate = RequestRate(1000, Duration.HOUR)
+ # 500 requests per hour
+ self.hourly_rate = RequestRate(500, Duration.HOUR)
- # 10000 requests per day
- self.daily_rate = RequestRate(10000, Duration.DAY)
+ # 1500 requests per day
+ self.daily_rate = RequestRate(1500, Duration.DAY)
self.limiter = Limiter(
self.minute_rate,
diff --git a/misskaty/plugins/misc_tools.py b/misskaty/plugins/misc_tools.py
index 69ef4126..c56673e5 100644
--- a/misskaty/plugins/misc_tools.py
+++ b/misskaty/plugins/misc_tools.py
@@ -312,7 +312,7 @@ async def who_is(client, message):
if message.chat.type.value in (("supergroup", "channel")):
try:
chat_member_p = await message.chat.get_member(from_user.id)
- joined_date = datetime.fromtimestamp(chat_member_p.joined_date or time.time()).strftime("%Y.%m.%d %H:%M:%S")
+ joined_date = chat_member_p.joined_date
message_out_str += "➲Joined this Chat on: " f"{joined_date}" "\n"
except UserNotParticipant:
pass
diff --git a/misskaty/plugins/nightmodev2.py b/misskaty/plugins/nightmodev2.py
new file mode 100644
index 00000000..0de6a2a8
--- /dev/null
+++ b/misskaty/plugins/nightmodev2.py
@@ -0,0 +1,190 @@
+from email import message
+import re
+from datetime import datetime, timedelta
+
+import pytz
+from apscheduler.jobstores.base import ConflictingIdError
+from pyrogram import filters
+from pyrogram.errors import (ChannelInvalid, ChannelPrivate, ChatAdminRequired,
+ ChatNotModified)
+from pyrogram.types import ChatPermissions
+
+from database.nightmode_db import TZ, scheduler
+from misskaty import BOT_NAME, app
+from misskaty.core.message_utils import *
+from misskaty.core.decorator.permissions import adminsOnly
+from misskaty.vars import COMMAND_HANDLER, LOG_CHANNEL
+
+__MODULE__ = "NightMode"
+__HELP__ = """Enable or disable nightmode (locks the chat at specified intervals everyday)
+Flags:
+'-s': "Specify starting time in 24hr format."
+'-e': "Specify duration in hours / minute"
+'-d': "Disable nightmode for chat."
+
+Examples:
+/nightmode -s=23:53 -e=6h
+/nightmode -s=23:50 -e=120m
+/nightmode -d
+"""
+
+TIME_ZONE = pytz.timezone(TZ)
+
+def extract_time(time_val: str):
+ if any(time_val.endswith(unit) for unit in ('m', 'h')):
+ unit = time_val[-1]
+ time_num = time_val[:-1]
+ if not time_num.isdigit():
+ return ""
+ if unit == 'm':
+ time = int(time_num) * 60
+ elif unit == 'h':
+ time = int(time_num) * 60 * 60
+ else:
+ return ""
+ return time
+ return ""
+
+async def un_mute_chat(chat_id: int, perm: ChatPermissions):
+ try:
+ await app.set_chat_permissions(chat_id, perm)
+ except ChatAdminRequired:
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_FAIL\nFailed to turn off nightmode at `{chat_id}`,"
+ f"since {BOT_NAME} is not an admin in chat `{chat_id}`")
+ except (ChannelInvalid, ChannelPrivate):
+ scheduler.remove_job(f"enable_nightmode_{chat_id}")
+ scheduler.remove_job(f"disable_nightmode_{chat_id}")
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_FAIL\nFailed to turn off nightmode at `{chat_id}`,"
+ f"since {BOT_NAME} is not present in chat `{chat_id}`"
+ " Removed group from list.")
+ except ChatNotModified:
+ pass
+ except Exception as e:
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_FAIL\nFailed to turn off nightmode at `{chat_id}`\n"
+ f"ERROR: `{e}`")
+ else:
+ job = scheduler.get_job(f"enable_nightmode_{chat_id}")
+ close_at = job.next_run_time
+ await app.send_message(
+ chat_id,
+ f"#AUTOMATED_HANDLER\nGroup is Opening.\nWill be closed at {close_at}")
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_SUCCESS\nSuccessfully turned off nightmode at `{chat_id}`,")
+
+
+async def mute_chat(chat_id: int):
+ try:
+ await app.set_chat_permissions(chat_id, ChatPermissions())
+ except ChatAdminRequired:
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_FAIL\nFailed to enable nightmode at `{chat_id}`,"
+ f"since {BOT_NAME} is not an admin in chat `{chat_id}`")
+ except (ChannelInvalid, ChannelPrivate):
+ scheduler.remove_job(f"enable_nightmode_{chat_id}")
+ scheduler.remove_job(f"disable_nightmode_{chat_id}")
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_FAIL\nFailed to enable nightmode at `{chat_id}`,"
+ f"since {BOT_NAME} is not present in chat `{chat_id}`"
+ " Removed group from list.")
+ except ChatNotModified:
+ pass
+ except Exception as e:
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_FAIL\nFailed to enable nightmode at `{chat_id}`\n"
+ f"ERROR: `{e}`")
+ else:
+ job = scheduler.get_job(f"disable_nightmode_{chat_id}")
+ open_at = job.next_run_time
+ await app.send_message(
+ chat_id,
+ f"#AUTOMATED_HANDLER\nGroup is closing.\nWill be opened at {open_at}")
+ await app.send_message(
+ LOG_CHANNEL,
+ f"#NIGHT_MODE_SUCCESS\nSuccessfully turned on nightmode at `{chat_id}`,")
+
+@app.on_message(filters.command("nightmode", COMMAND_HANDLER) & filters.group)
+@adminsOnly("can_change_info")
+async def nightmode_handler(c, msg):
+ chat_id = msg.chat.id
+
+ if "-d" in msg.text:
+ job = scheduler.get_job(job_id=f"enable_nightmode_{chat_id}")
+ if job:
+ scheduler.remove_job(job_id=f"enable_nightmode_{chat_id}")
+ scheduler.remove_job(job_id=f"disable_nightmode_{chat_id}")
+ if not bool(scheduler.get_jobs()) and bool(scheduler.state):
+ scheduler.shutdown()
+ return await kirimPesan(msg, 'Nightmode disabled.')
+ return await kirimPesan(msg, "Nightmode isn't enabled in this chat.")
+
+ starttime = re.findall(r"-s=(\d+:\d+)", msg.text)
+ start = starttime if starttime else "00:00"
+ now = datetime.now(TIME_ZONE)
+
+ try:
+ start_timestamp = TIME_ZONE.localize(
+ datetime.strptime(
+ (now.strftime('%m:%d:%Y - ') + start),
+ '%m:%d:%Y - %H:%M'))
+ except ValueError:
+ return await kirimPesan(msg, "Invalid time format. Use HH:MM format.")
+ lockdur = re.findall(r"-e=(\w+)", msg.text)
+ lockdur = lockdur if lockdur else "6h"
+ lock_dur = extract_time(lockdur.lower())
+
+ if not lock_dur:
+ return await kirimPesan(
+ msg,
+ 'Invalid time duration. Use proper format.'
+ '\nExample: 6h (for 6 hours), 10m for 10 minutes.')
+
+ if start_timestamp < now:
+ start_timestamp = start_timestamp + timedelta(days=1)
+ end_time_stamp = start_timestamp + timedelta(seconds=int(lock_dur))
+ try:
+ # schedule to enable nightmode
+ scheduler.add_job(
+ mute_chat,
+ 'interval',
+ [chat_id],
+ id=f"enable_nightmode_{chat_id}",
+ days=1,
+ next_run_time=start_timestamp,
+ max_instances=50,
+ misfire_grace_time=None)
+
+ # schedule to disable nightmode
+ scheduler.add_job(
+ un_mute_chat,
+ 'interval',
+ [chat_id,
+ msg.chat.permissions],
+ id=f"disable_nightmode_{chat_id}",
+ days=1,
+ next_run_time=end_time_stamp,
+ max_instances=50,
+ misfire_grace_time=None)
+ except ConflictingIdError:
+ return await kirimPesan(
+ msg,
+ 'Already a schedule is running in this chat. Disable it using `-d` flag.')
+ await kirimPesan(
+ msg,
+ 'Successfully enabled nightmode in this chat.\n'
+ f'Group will be locked at {start_timestamp.strftime("%H:%M:%S")}'
+ f' and will be opened after {lockdur} everyday.')
+ if not bool(scheduler.state):
+ scheduler.start()
+
+if bool(scheduler.get_jobs()):
+ scheduler.start()
\ No newline at end of file