From 6d889d364624b51397e8233c52b4989105d4250e Mon Sep 17 00:00:00 2001 From: Abhi <85984486+AbhiTheModder@users.noreply.github.com> Date: Sun, 6 Apr 2025 08:09:46 +0530 Subject: [PATCH] feat: refactor rentry paste and implement cleanup job --- main.py | 3 ++ modules/open.py | 15 +++--- utils/rentry.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 133 insertions(+), 11 deletions(-) diff --git a/main.py b/main.py index e975f78..3246391 100644 --- a/main.py +++ b/main.py @@ -59,6 +59,7 @@ from utils import config from utils.db import db from utils.misc import gitrepo, userbot_version from utils.scripts import restart, load_module +from utils.rentry import rentry_cleanup_job SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__)) if SCRIPT_PATH != os.getcwd(): @@ -183,6 +184,8 @@ async def main(): logging.info("Moon-Userbot started!") + app.loop.create_task(rentry_cleanup_job()) + await idle() await app.stop() diff --git a/modules/open.py b/modules/open.py index d89f790..dd6da30 100644 --- a/modules/open.py +++ b/modules/open.py @@ -25,7 +25,7 @@ from pyrogram.types import Message from utils.misc import modules_help, prefix from utils.scripts import edit_or_reply, format_exc, progress -from utils.rentry import new +from utils.rentry import paste as rentry_paste async def read_file(file_path): @@ -96,17 +96,20 @@ async def openfile(client: Client, message: Message): "File Content is too long... Pasting to rentry..." ) content_new = f"```{code_start[11:-2]}\n{content}```" - paste = new(url="", edit_code="", text=content_new) - if paste["status"] != "200": - await ms.edit_text(f"Error: {paste['content']}") + try: + rentry_url, edit_code = await rentry_paste( + text=content_new, return_edit=True + ) + except RuntimeError: + await ms.edit_text("Error: Failed to paste to rentry") return await client.send_message( "me", - f"Here's your edit code for Url: {paste['url']}\nEdit code: {paste['edit_code']}", + f"Here's your edit code for Url: {rentry_url}\nEdit code: {edit_code}", disable_web_page_preview=True, ) await ms.edit_text( - f"File Name: {file_name[0]}\nSize: {file_size} bytes\nLast Modified: {last_modified}\nContent: {paste['url']}\nNote: Edit Code has been sent to your saved messages", + f"File Name: {file_name[0]}\nSize: {file_size} bytes\nLast Modified: {last_modified}\nContent: {rentry_url}\nNote: Edit Code has been sent to your saved messages", disable_web_page_preview=True, ) diff --git a/utils/rentry.py b/utils/rentry.py index f38d811..dd6d7ad 100644 --- a/utils/rentry.py +++ b/utils/rentry.py @@ -1,13 +1,18 @@ #!/usr/bin/env python3 # @source: https://github.com/radude/rentry/blob/master/rentry.py - +import asyncio import http.cookiejar import urllib.parse import urllib.request + +from uuid import uuid4 +from datetime import datetime, timedelta from http.cookies import SimpleCookie from json import loads as json_loads +from utils.db import db + BASE_PROTOCOL = "https://" BASE_URL = "rentry.co" @@ -44,12 +49,12 @@ class UrllibClient: return response -def raw(url): +def raw(url: str): client = UrllibClient() return json_loads(client.get(f"{BASE_PROTOCOL}{BASE_URL}/api/raw/{url}").data) -def new(url, edit_code, text): +def new(text: str, edit_code: str = "", url: str = ""): client, cookie = UrllibClient(), SimpleCookie() cookie.load(vars(client.get(f"{BASE_PROTOCOL}{BASE_URL}"))["headers"]["Set-Cookie"]) @@ -69,7 +74,7 @@ def new(url, edit_code, text): ) -def edit(url, edit_code, text): +def edit(url_short: str, edit_code: str, text: str): client, cookie = UrllibClient(), SimpleCookie() cookie.load(vars(client.get(f"{BASE_PROTOCOL}{BASE_URL}"))["headers"]["Set-Cookie"]) @@ -79,6 +84,117 @@ def edit(url, edit_code, text): return json_loads( client.post( - f"{BASE_PROTOCOL}{BASE_URL}/api/edit/{url}", payload, headers=_headers + f"{BASE_PROTOCOL}{BASE_URL}/api/edit/{url_short}", payload, headers=_headers ).data ) + + +def delete(url_short: str, edit_code: str): + client, cookie = UrllibClient(), SimpleCookie() + cookie.load(vars(client.get(f"{BASE_PROTOCOL}{BASE_URL}"))["headers"]["Set-Cookie"]) + csrftoken = cookie["csrftoken"].value + payload = {"csrfmiddlewaretoken": csrftoken, "edit_code": edit_code} + return json_loads( + client.post( + f"{BASE_PROTOCOL}{BASE_URL}/api/delete/{url_short}", + payload, + headers=_headers, + ).data + ) + + +async def paste( + text: str, + return_edit: bool = False, + edit_bin: bool = False, + edit_code: str = None, + url: str = None, + permanent: bool = False, +) -> str | tuple[str, str]: + """Pastes some text to rentry bin. + args: + text: Input text to paste + return_edit: If it should return edit code also + edit_bin: If this request is to edit an already existing bin + edit_code: Only required if edit_bin is True. It is the edit code used to edit bin. + url: Only required if edit_bin is True. It is the url on which the bin is located. + permanent: If the pasted content should not be deleted automatically + + returns: + The url of the paste or return a tuple containing url and edit code + """ + if not str(text): + return + + if edit_bin: + if not (url and edit_code): + raise ValueError("Please provide both, url and edit code") + response = edit(url_short=url, edit_code=edit_code, text=text) + else: + response = new(text=text) + + if response.get("status") != "200": + raise RuntimeError( + f"paste task terminated with status: {response.get('status')}\n" + f"Message: {response.get('content', 'No message provided')}" + ) + + url = response["url"] + edit_code = response["edit_code"] + + if not permanent: + short_url = response["url_short"] + time_now = datetime.now() + ftime = time_now.strftime("%d %I:%M:%S %p %Y") + + print(f"URL: {url} - Edit Code: {edit_code} - Time: {ftime}") + + rallUrls = db.get("core.rentry", "urls", default={"allUrls": {}}) + entry_id = str(uuid4()) + rallUrls["allUrls"][entry_id] = { + "url": short_url, + "edit_code": edit_code, + "time": ftime, + } + db.set("core.rentry", "urls", rallUrls) + + if return_edit: + return (url, edit_code) + return url + + +async def rentry_cleanup_job(): + """Periodically checks and deletes rentry pastes older than 24 hours""" + while True: + try: + rallUrls = db.get("core.rentry", "urls", default={"allUrls": {}}) + now = datetime.now() + deleted_count = 0 + error_count = 0 + + for entry_id, entry in list(rallUrls["allUrls"].items()): + url = entry["url"] + entry_time = datetime.strptime(entry["time"], "%d %I:%M:%S %p %Y") + + if now - entry_time > timedelta(days=1): + try: + delete(url, entry["edit_code"]) + del rallUrls["allUrls"][entry_id] + deleted_count += 1 + print(f"[#] Deleted expired rentry paste: {url}") + except Exception as e: + error_count += 1 + print(f"[!] Failed to delete rentry paste {url}: {str(e)}") + + if deleted_count or error_count: + print( + f"[*] Cleanup summary: {deleted_count} deleted, {error_count} failed" + ) + + if deleted_count: + db.set("core.rentry", "urls", rallUrls) + + except Exception as e: + print(f"[!] Error in rentry cleanup job: {str(e)}") + + await asyncio.sleep(12 * 60 * 60)