From c4105fddbe91c98356d83841111e75f6302a20df Mon Sep 17 00:00:00 2001 From: yasir Date: Thu, 16 Feb 2023 14:12:03 +0700 Subject: [PATCH] Add nightmode plugin and some fix --- database/nightmode_db.py | 17 +++ misskaty/core/decorator/ratelimiter.py | 2 +- misskaty/core/ratelimiter_func.py | 16 +-- misskaty/plugins/misc_tools.py | 2 +- misskaty/plugins/nightmodev2.py | 190 +++++++++++++++++++++++++ 5 files changed, 217 insertions(+), 10 deletions(-) create mode 100644 database/nightmode_db.py create mode 100644 misskaty/plugins/nightmodev2.py 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