import os import random import shutil import subprocess import sys from threading import Thread import time import traceback from modules.logging import logWrite from modules.utils import configGet, jsonLoad, jsonSave # Args ===================================================================================================================================== if "--move-sent" in sys.argv: for entry in jsonLoad(configGet("index", "locations"))["sent"]: try: shutil.move(configGet("queue", "locations")+os.sep+entry, configGet("sent", "locations")+os.sep+entry) except FileNotFoundError: logWrite(f"File '{entry}' is already moved or does not exist") except Exception as exp: logWrite(f"Could not move sent file '{entry}' to '{configGet('sent', 'locations')}' due to {exp}") logWrite(f"Moved all sent files to the sent folder") if "--cleanup" in sys.argv: if "--confirm" in sys.argv: index = jsonLoad(configGet("index", "locations")) for entry in index["sent"]: try: try: os.remove(configGet("queue", "locations")+os.sep+entry) except FileNotFoundError: pass try: os.remove(configGet("sent", "locations")+os.sep+entry) except FileNotFoundError: pass except Exception as exp: logWrite(f"Could not remove '{entry}' due to {exp}") jsonSave(index, jsonLoad(configGet("index", "locations"))) logWrite(f"Performed cleanup of the sent files") else: logWrite(f"Requested cleanup of sent files but not authorized. Please pass '--confirm' to perform that") if "--cleanup-index" in sys.argv: if "--confirm" in sys.argv: index = jsonLoad(configGet("index", "locations")) index["sent"] = [] jsonSave(index, jsonLoad(configGet("index", "locations"))) logWrite(f"Performed cleanup of sent files index") else: logWrite(f"Requested cleanup of sent files index but not authorized. Please pass '--confirm' to perform that") if "--norun" in sys.argv: logWrite("Argument --norun passed, not running the main script") sys.exit() #=========================================================================================================================================== # Import =================================================================================================================================== try: import schedule # type: ignore from pyrogram import Client, filters, idle # type: ignore from pyrogram.types import ChatPermissions, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton, BotCommand, BotCommandScopeChat # type: ignore except ModuleNotFoundError: print(f"Required modules are not installed. Run 'pip3 install -r requirements.txt' and restart the program.", flush=True) sys.exit() #=========================================================================================================================================== pid = os.getpid() app = Client("duptsiaposter", bot_token=configGet("bot_token", "bot"), api_id=configGet("api_id", "bot"), api_hash=configGet("api_hash", "bot")) def send_content(): try: list_sent = jsonLoad(configGet("index", "locations")) list_queue = os.listdir(configGet("queue", "locations")) for file in list_queue: if not file in list_sent["sent"]: ext_match = False for ext in configGet("photo", "posting", "extensions"): if file.endswith(ext): ext_match = True ext_type = "photo" break for ext in configGet("video", "posting", "extensions"): if file.endswith(ext): ext_match = True ext_type = "video" break if not ext_match: list_queue.remove(file) else: list_queue.remove(file) if len(list_queue) > 0: candidate_file = random.choice(list_queue) candidate = configGet("queue", "locations")+os.sep+candidate_file else: logWrite(f"Could not send content due to queue folder empty with allowed extensions") if configGet("error", "reports"): app.send_message(configGet("admin", "reports"), f"Could not send content: `Queue folder is empty or contains only unsupported or already sent files.`") # type: ignore return if ext_type == "photo": # type: ignore if configGet("enabled", "caption"): if configGet("link", "caption") != None: sent = app.send_photo(configGet("channel", "posting"), candidate, caption=f"[{configGet('text', 'caption')}]({configGet('link', 'caption')})", disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_photo(configGet("channel", "posting"), candidate, caption=configGet('text', 'caption'), disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_photo(configGet("channel", "posting"), candidate, disable_notification=configGet("silent", "posting")) # type: ignore elif ext_type == "video": # type: ignore if configGet("enabled", "caption"): if configGet("link", "caption") != None: sent = app.send_video(configGet("channel", "posting"), candidate, caption=f"[{configGet('text', 'caption')}]({configGet('link', 'caption')})", disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_video(configGet("channel", "posting"), candidate, caption=configGet('text', 'caption'), disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_video(configGet("channel", "posting"), candidate, disable_notification=configGet("silent", "posting")) # type: ignore list_sent["sent"].append(candidate_file) jsonSave(list_sent, configGet("index", "locations")) if configGet("move_sent", "posting"): shutil.move(candidate, configGet("sent", "locations")+os.sep+candidate_file) if configGet("sent", "reports"): app.send_message(configGet("admin", "reports"), f"Posted `{candidate_file}`", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup([ [InlineKeyboardButton("View in channel", url=sent.link)] # type: ignore ])) # type: ignore except Exception as exp: logWrite(f"Could not send content due to {exp}") if configGet("error", "reports"): app.send_message(configGet("admin", "reports"), f"Could not send content due to `{exp}`\n\nTraceback:\n```{traceback.format_exc()}```") # type: ignore @app.on_message(~ filters.scheduled & filters.command(["start", "help"], prefixes="/")) def start(app, msg): if msg.from_user.id not in jsonLoad(configGet("blocked", "locations")): msg.reply_text(f"Hi and welcome!\n\nYou can submit your pictures and videos here. We'll review and add them, if we like them. Make sure you send your stuff one at a time and have chosen media that corresponds to our rules.\n\nYou can also write something to us in the description field. We'll send it with the submission itself, if needed.\n\nHave fun and happy submitting!") @app.on_message(~ filters.scheduled & filters.command(["kill", "die", "reboot"], prefixes=["", "/"])) def kill(app, msg): if msg.from_user.id == configGet("admin", "reports"): msg.reply_text(f"Shutting down bot with pid `{pid}`") os.system('kill -9 '+str(pid)) # Submission ===================================================================================================================================== def subLimit(user): submit = jsonLoad(configGet("submit", "locations")) submit[str(user.id)] = time.time() jsonSave(submit, configGet("submit", "locations")) def subLimited(user): if user.id == configGet("admin", "reports"): return False else: submit = jsonLoad(configGet("submit", "locations")) if str(user.id) in submit: if (time.time() - submit[str(user.id)]) < configGet("timeout", "submission"): return True else: return False else: return False def subBlock(user): blocked = jsonLoad(configGet("blocked", "locations")) if user not in blocked: blocked.append(user) jsonSave(blocked, configGet("blocked", "locations")) def subUnblock(user): blocked = jsonLoad(configGet("blocked", "locations")) if user in blocked: blocked.remove(user) jsonSave(blocked, configGet("blocked", "locations")) @app.on_message(~ filters.scheduled & filters.photo | filters.video | filters.animation) def get_submission(_, msg): if msg.from_user.id not in jsonLoad(configGet("blocked", "locations")): if not subLimited(msg.from_user): if msg.caption != None: caption = str(msg.caption) else: caption = "" caption += "\n\nSubmitted by:" if msg.from_user.first_name != None: caption += f" {msg.from_user.first_name}" if msg.from_user.last_name != None: caption += f" {msg.from_user.last_name}" if msg.from_user.username != None: caption += f" (@{msg.from_user.username})" if msg.from_user.phone_number != None: caption += f" ({msg.from_user.phone_number})" msg.copy(configGet("admin", "reports"), caption=caption, reply_markup=InlineKeyboardMarkup([ [ InlineKeyboardButton(text="✅ Accept", callback_data=f"sub_yes_{msg.from_user.id}_{msg.id}"), InlineKeyboardButton(text="❌ Deny", callback_data=f"sub_no_{msg.from_user.id}_{msg.id}") ], [ InlineKeyboardButton(text="☠️ Block sender", callback_data=f"sub_block_{msg.from_user.id}") ], [ InlineKeyboardButton(text="🏳️ Unblock sender", callback_data=f"sub_unblock_{msg.from_user.id}") ] ] )) msg.reply_text(f"Media has been submitted.\nWe'll notify you whether it will be accepted or not soon.", quote=True) subLimit(msg.from_user) else: msg.reply_text(f'You can only submit 1 media per {configGet("timeout", "submission")} seconds') @app.on_callback_query(filters.regex("sub_yes_[\s\S]*_[\s\S]*")) # type: ignore def callback_query_yes(app, clb): # type: ignore fullclb = clb.data.split("_") try: submission = app.get_messages(int(fullclb[2]), int(fullclb[3])) except: clb.answer(text=f"Submission message no longer exist", show_alert=True) return try: app.download_media(submission, file_name=configGet("queue", "locations")+os.sep) except: clb.answer(text=f"Could not download submission", show_alert=True) return submission.reply_text(f"✅ Submission approved and accepted", quote=True) clb.answer(text=f"✅ Submission approved", show_alert=True) @app.on_callback_query(filters.regex("sub_no_[\s\S]*_[\s\S]*")) # type: ignore def callback_query_no(app, clb): # type: ignore fullclb = clb.data.split("_") try: submission = app.get_messages(int(fullclb[2]), int(fullclb[3])) except: clb.answer(text=f"Submission message no longer exist", show_alert=True) return submission.reply_text(f"❌ Submission reviewed and declined", quote=True) clb.answer(text=f"❌ Submission declined", show_alert=True) @app.on_callback_query(filters.regex("sub_block_[\s\S]*")) # type: ignore def callback_query_block(app, clb): # type: ignore fullclb = clb.data.split("_") app.send_message(int(fullclb[2]), "You were blocked and you can't submit media anymore.") subBlock(int(fullclb[2])) clb.answer(text=f"User {fullclb[2]} has been blocked", show_alert=True) @app.on_callback_query(filters.regex("sub_unblock_[\s\S]*")) # type: ignore def callback_query_unblock(app, clb): # type: ignore fullclb = clb.data.split("_") app.send_message(int(fullclb[2]), "You were unblocked and you can now submit media.") subUnblock(int(fullclb[2])) clb.answer(text=f"User {fullclb[2]} has been unblocked", show_alert=True) #=========================================================================================================================================== for entry in configGet("time", "posting"): schedule.every().day.at(entry).do(send_content) def background_task(): try: while True: try: schedule.run_pending() time.sleep(1) except: pass except Exception as exp: logWrite(f"Exception {exp} happened on task execution") except KeyboardInterrupt: logWrite('\nShutting down...') app.send_message(configGet("admin", "reports"), f"Shutting down with PID `{pid}`") # type: ignore os.system('kill -9 '+str(pid)) if __name__ == "__main__": logWrite(f'Starting with PID {pid}') app.start() # type: ignore app.send_message(configGet("admin", "reports"), f"Starting with pid `{pid}`") # type: ignore t = Thread(target=background_task) t.start() app.set_bot_commands([ # type: ignore BotCommand("start", "Start using the bot") ]) app.set_bot_commands([ # type: ignore BotCommand("reboot", "Reboot the bot"), ], scope=BotCommandScopeChat(chat_id=configGet("admin", "reports"))) idle() app.send_message(configGet("admin", "reports"), f"Shutting down with pid `{pid}`") # type: ignore logWrite(f'Shutting down with PID {pid}') subprocess.call(f'kill -9 {pid}', shell=True) # type: ignore