from os import sep, remove, getpid, listdir from random import choice from shutil import move from sys import argv, exit from threading import Thread from time import time from traceback import format_exc from pathlib import Path from modules.logging import logWrite from modules.utils import configGet, jsonLoad, jsonSave, killProc, locale # Args ===================================================================================================================================== if "--move-sent" in argv: for entry in jsonLoad(configGet("index", "locations"))["sent"]: try: move(configGet("queue", "locations")+sep+entry, configGet("sent", "locations")+sep+entry) except FileNotFoundError: logWrite(locale("move_sent_doesnt_exist", "console", locale=configGet("locale_log")).format(entry)) except Exception as exp: logWrite(locale("move_sent_doesnt_exception", "console", locale=configGet("locale_log")).format(entry, exp)) logWrite(locale("move_sent_completed", "console", locale=configGet("locale_log"))) if "--cleanup" in argv: if "--confirm" in argv: index = jsonLoad(configGet("index", "locations")) for entry in index["sent"]: try: try: remove(configGet("queue", "locations")+sep+entry) except FileNotFoundError: pass try: remove(configGet("sent", "locations")+sep+entry) except FileNotFoundError: pass except Exception as exp: logWrite(locale("cleanup_exception", "console", locale=configGet("locale_log")).format(entry, exp)) jsonSave(index, jsonLoad(configGet("index", "locations"))) logWrite(locale("cleanup_completed", "console", locale=configGet("locale_log"))) else: logWrite(locale("cleanup_unathorized", "console", locale=configGet("locale_log"))) if "--cleanup-index" in argv: if "--confirm" in argv: index = jsonLoad(configGet("index", "locations")) index["sent"] = [] jsonSave(index, jsonLoad(configGet("index", "locations"))) logWrite(locale("cleanup_index_completed", "console", locale=configGet("locale_log"))) else: logWrite(locale("cleanup_index_unathorized", "console", locale=configGet("locale_log"))) if "--norun" in argv: logWrite(locale("passed_norun", "console", locale=configGet("locale_log"))) exit() #=========================================================================================================================================== # Import =================================================================================================================================== try: import schedule from pyrogram.sync import idle from pyrogram.client import Client from pyrogram import filters from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, BotCommand, BotCommandScopeChat except ModuleNotFoundError: print(locale("deps_missing", "console", locale=configGet("locale_log")), flush=True) exit() #=========================================================================================================================================== pid = getpid() app = Client("duptsiaposter", bot_token=configGet("bot_token", "bot"), api_id=configGet("api_id", "bot"), api_hash=configGet("api_hash", "bot")) # Work in progress # def check_forwards(app): # try: # index = jsonLoad(configGet("index", "locations")) # channel = app.get_chat(configGet("channel", "posting")) # peer = app.resolve_peer(configGet("channel", "posting")) # print(peer, flush=True) # posts_list = [i for i in range(index["last_id"]-100,index["last_id"])] # last_posts = app.get_messages(configGet("channel", "posting"), message_ids=posts_list) # for post in last_posts: # post_forwards = GetMessagePublicForwards(channel=peer, msg_id=post.id, offset_peer=peer, offset_rate=0, offset_id=0, limit=100) # print(post_forwards, flush=True) # for forward in post_forwards: # print(forward, flush=True) # except Exception as exp: # logWrite("Could not get last posts forwards due to {0} with traceback {1}".format(str(exp), traceback.format_exc()), debug=True) # if configGet("error", "reports"): # app.send_message(configGet("admin"), traceback.format_exc()) # type: ignore # pass def send_content(): # Send post to channel try: index = jsonLoad(configGet("index", "locations")) list_queue = listdir(configGet("queue", "locations")) for file in list_queue: if not file in index["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 = choice(list_queue) candidate = configGet("queue", "locations")+sep+candidate_file else: logWrite(locale("post_empty", "console", locale=configGet("locale_log"))) if configGet("error", "reports"): app.send_message(configGet("admin"), locale("post_empty", "message", locale=configGet("locale"))) # type: ignore return if candidate_file in index["captions"]: caption = index["captions"][candidate_file] else: caption = "" if configGet("enabled", "caption"): if configGet("link", "caption") != None: caption = f"{caption}\n\n[{configGet('text', 'caption')}]({configGet('link', 'caption')})" else: caption = f"{caption}\n\n{configGet('text', 'caption')}" else: caption = caption try: if ext_type == "photo": # type: ignore if configGet("enabled", "caption"): if configGet("link", "caption") != None: sent = app.send_photo(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_photo(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_photo(configGet("channel", "posting"), candidate, caption=caption, 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=caption, disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_video(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting")) # type: ignore else: sent = app.send_video(configGet("channel", "posting"), candidate, caption=caption, disable_notification=configGet("silent", "posting")) # type: ignore else: return except Exception as exp: logWrite(locale("post_exception", "console", locale=configGet("locale_log")).format(str(exp), format_exc())) if configGet("error", "reports"): app.send_message(configGet("admin"), locale("post_exception", "message", locale=configGet("locale")).format(exp, traceback.format_exc())) # type: ignore return index["sent"].append(candidate_file) index["last_id"] = sent.id # type: ignore jsonSave(index, configGet("index", "locations")) if configGet("move_sent", "posting"): move(candidate, configGet("sent", "locations")+sep+candidate_file) logWrite(locale("post_sent", "console", locale=configGet("locale_log")).format(candidate, ext_type, str(configGet("channel", "posting")), caption.replace("\n", "%n"), str(configGet("silent", "posting")))) # type: ignore if configGet("sent", "reports"): app.send_message(configGet("admin"), f"Posted `{candidate_file}`", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup([ [InlineKeyboardButton(locale("post_view", "button", locale=configGet("locale")), url=sent.link)] # type: ignore ])) # type: ignore except Exception as exp: logWrite(locale("post_exception", "console", locale=configGet("locale_log")).format(str(exp), format_exc())) if configGet("error", "reports"): app.send_message(configGet("admin"), locale("post_exception", "message", locale=configGet("locale")).format(exp, traceback.format_exc())) # type: ignore return # Work in progress # Check last posts forwards # check_forwards(app) if configGet("submit", "mode"): @app.on_message(~ filters.scheduled & filters.command(["start"], prefixes="/")) def cmd_start(app, msg): if msg.from_user.id not in jsonLoad(configGet("blocked", "locations")): msg.reply_text(locale("start", "message", locale=msg.from_user.language_code)) if configGet("submit", "mode"): @app.on_message(~ filters.scheduled & filters.command(["rules", "help"], prefixes="/")) def cmd_rules(app, msg): if msg.from_user.id not in jsonLoad(configGet("blocked", "locations")): msg.reply_text(locale("rules", "message", locale=msg.from_user.language_code)) # Work in progress # @app.on_message(~ filters.scheduled & filters.command(["forwards"], prefixes="/")) # def cmd_forwards(app, msg): # check_forwards(app) @app.on_message(~ filters.scheduled & filters.command(["kill", "die", "reboot"], prefixes=["", "/"])) def cmd_kill(app, msg): if msg.from_user.id == configGet("admin"): logWrite(locale("shutdown", "console", locale=configGet("locale_log")).format(str(pid))) msg.reply_text(locale("shutdown", "message", locale=configGet("locale")).format(str(pid))) killProc(pid) # Submission ===================================================================================================================================== def subLimit(user): submit = jsonLoad(configGet("submit", "locations")) submit[str(user.id)] = time() jsonSave(submit, configGet("submit", "locations")) def subLimited(user): if user.id == configGet("admin"): return False else: submit = jsonLoad(configGet("submit", "locations")) if str(user.id) in submit: if (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")) if configGet("submit", "mode"): @app.on_message(~ filters.scheduled & filters.photo | filters.video | filters.animation | filters.document) def get_submission(_, msg): try: if msg.from_user.id not in jsonLoad(configGet("blocked", "locations")): user_locale = msg.from_user.language_code if not subLimited(msg.from_user): if msg.document != None: if msg.document.mime_type not in configGet("mime_types", "submission"): logWrite(locale("sub_mime_not_allowed", "console", locale=configGet("locale_log")).format(str(msg.from_user.id), msg.document.mime_type)) msg.reply_text(locale("mime_not_allowed", "message", locale=user_locale), quote=True) return if msg.document.file_size > configGet("file_size", "submission"): logWrite(locale("sub_document_too_large", "console", locale=configGet("locale_log")).format(str(msg.from_user.id), str(msg.document.file_size), str(configGet("file_size", "submission")))) msg.reply_text(locale("document_too_large", "message", locale=user_locale).format(str(configGet("file_size", "submission")/1024/1024)), quote=True) return if msg.video != None: if msg.video.file_size > configGet("file_size", "submission"): logWrite(locale("sub_document_too_large", "console", locale=configGet("locale_log")).format(str(msg.from_user.id), str(msg.document.file_size), str(configGet("file_size", "submission")))) msg.reply_text(locale("document_too_large", "message", locale=user_locale).format(str(configGet("file_size", "submission")/1024/1024)), quote=True) return buttons = [ [ InlineKeyboardButton(text=locale("sub_yes", "button", locale=configGet("locale")), callback_data=f"sub_yes_{msg.from_user.id}_{msg.id}") ] ] if msg.caption != None: caption = str(msg.caption) buttons[0].append( InlineKeyboardButton(text=locale("sub_yes_caption", "button", locale=configGet("locale")), callback_data=f"sub_yes_{msg.from_user.id}_{msg.id}_caption") ) buttons[0].append( InlineKeyboardButton(text=locale("sub_no", "button", locale=configGet("locale")), callback_data=f"sub_no_{msg.from_user.id}_{msg.id}") ) else: caption = "" buttons[0].append( InlineKeyboardButton(text=locale("sub_no", "button", locale=configGet("locale")), callback_data=f"sub_no_{msg.from_user.id}_{msg.id}") ) caption += locale("sub_by", "message", locale=locale(configGet("locale"))) 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})" if msg.from_user.id != configGet("admin"): buttons += [ [ InlineKeyboardButton(text=locale("sub_block", "button", locale=configGet("locale")), callback_data=f"sub_block_{msg.from_user.id}") ] # [ # InlineKeyboardButton(text=locale("sub_unblock", "button", locale=configGet("locale")), callback_data=f"sub_unblock_{msg.from_user.id}") # ] ] msg.reply_text(locale("sub_sent", "message", locale=user_locale), quote=True) subLimit(msg.from_user) logWrite(locale("sub_received", "console", locale=configGet("locale_log")).format(str(msg.from_user.id), str(msg.caption))) msg.copy(configGet("admin"), caption=caption, reply_markup=InlineKeyboardMarkup(buttons)) else: logWrite(locale("sub_cooldown", "console", locale=configGet("locale_log")).format(str(msg.from_user.id))) msg.reply_text(locale("sub_cooldown", "message", locale=user_locale).format(str(configGet("timeout", "submission")))) except AttributeError: logWrite(locale("sub_received", "console", locale=configGet("locale_log"))) @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("_") user_locale = clb.from_user.language_code try: submission = app.get_messages(int(fullclb[2]), int(fullclb[3])) except: logWrite(locale("sub_msg_unavail", "console", locale=configGet("locale_log")).format(fullclb[3], fullclb[2])) clb.answer(text=locale("sub_msg_unavail", "message", locale=user_locale), show_alert=True) return try: logWrite(locale("sub_media_downloading", "console", locale=configGet("locale_log")).format(fullclb[3], fullclb[2])) media = app.download_media(submission, file_name=configGet("queue", "locations")+sep) if clb.data.endswith("_caption"): index = jsonLoad(configGet("index", "locations")) index["captions"][Path(media).name] = submission.caption jsonSave(index, configGet("index", "locations")) logWrite(locale("sub_media_downloaded", "console", locale=configGet("locale_log")).format(fullclb[3], fullclb[2])) except: logWrite(locale("sub_media_unavail", "console", locale=configGet("locale_log")).format(fullclb[3], fullclb[2])) clb.answer(text=locale("sub_media_unavail", "message", locale=user_locale), show_alert=True) return logWrite(locale("sub_accepted", "console", locale=configGet("locale_log")).format(fullclb[3], fullclb[2])) submission.reply_text(locale("sub_yes", "message", locale=submission.from_user.language_code), quote=True) edited_markup = clb.message.reply_markup.inline_keyboard edited_markup[0] = [InlineKeyboardButton(text=locale("accepted", "button", locale=configGet("locale")), callback_data=f"sub_done")] clb.message.edit(text=clb.message.caption, reply_markup=InlineKeyboardMarkup(edited_markup)) clb.answer(text=locale("sub_yes", "callback", locale=user_locale).format(fullclb[2]), 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("_") user_locale = clb.from_user.language_code try: submission = app.get_messages(int(fullclb[2]), int(fullclb[3])) except: logWrite(locale("sub_msg_unavail", "console", locale=configGet("locale_log")).format(fullclb[3], fullclb[2])) clb.answer(text=locale("sub_msg_unavail", "message", locale=user_locale), show_alert=True) return logWrite(locale("sub_declined", "console", locale=configGet("locale_log")).format(fullclb[3], fullclb[2])) submission.reply_text(locale("sub_no", "message", locale=submission.from_user.language_code), quote=True) edited_markup = clb.message.reply_markup.inline_keyboard edited_markup[0] = [InlineKeyboardButton(text=locale("declined", "button", locale=configGet("locale")), callback_data=f"sub_done")] clb.message.edit(text=clb.message.caption, reply_markup=InlineKeyboardMarkup(edited_markup)) clb.answer(text=locale("sub_no", "callback", locale=user_locale).format(fullclb[2]), 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("_") user_locale = clb.from_user.language_code app.send_message(int(fullclb[2]), locale("sub_blocked", "message", locale=configGet("locale"))) subBlock(int(fullclb[2])) logWrite(locale("sub_blocked", "console", locale=configGet("locale_log")).format(fullclb[2])) edited_markup = clb.message.reply_markup.inline_keyboard edited_markup[1] = [InlineKeyboardButton(text=locale("sub_unblock", "button", locale=configGet("locale")), callback_data=f"sub_unblock_{fullclb[2]}")] clb.message.edit(text=clb.message.caption, reply_markup=InlineKeyboardMarkup(edited_markup)) clb.answer(text=locale("sub_block", "callback", locale=user_locale).format(fullclb[2]), 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("_") user_locale = clb.from_user.language_code app.send_message(int(fullclb[2]), locale("sub_unblocked", "message", locale=configGet("locale"))) subUnblock(int(fullclb[2])) logWrite(locale("sub_unblocked", "console", locale=configGet("locale_log")).format(fullclb[2])) edited_markup = clb.message.reply_markup.inline_keyboard edited_markup[1] = [InlineKeyboardButton(text=locale("sub_block", "button", locale=configGet("locale")), callback_data=f"sub_block_{fullclb[2]}")] clb.message.edit(text=clb.message.caption, reply_markup=InlineKeyboardMarkup(edited_markup)) clb.answer(text=locale("sub_unblock", "callback", locale=user_locale).format(fullclb[2]), show_alert=True) @app.on_callback_query(filters.regex("sub_done")) # type: ignore def callback_query_block(app, clb): # type: ignore user_locale = clb.from_user.language_code clb.answer(text=locale("sub_done", "callback", locale=user_locale), show_alert=True) #=========================================================================================================================================== # Work in progress # Handle new forwards # @app.on_raw_update() # def fwd_got(app, update, users, chats): # if isinstance(update, UpdateChannelMessageForwards): # logWrite(f'Forward count increased to {update["forwards"]} on post {update["id"]} in channel {update["channel_id"]}') # logWrite(str(users), debug=True) # logWrite(str(chats), debug=True) # # else: # # logWrite(f"Got raw update of type {type(update)} with contents {update}", debug=True) if configGet("post", "mode"): 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(locale("exception_occured", "console", locale=configGet("locale_log")).format(exp)) except KeyboardInterrupt: logWrite(locale("keyboard_interrupt", "console", locale=configGet("locale_log"))) if configGet("shutdown", "reports"): app.send_message(configGet("admin"), locale("shutdown", "message", locale=configGet("locale")).format(str(pid))) # type: ignore killProc(pid) if __name__ == "__main__": logWrite(locale("startup", "console", locale=configGet("locale_log")).format(str(pid))) app.start() # type: ignore if configGet("startup", "reports"): app.send_message(configGet("admin"), locale("startup", "message", locale=configGet("locale")).format(str(pid))) # type: ignore if configGet("post", "mode"): t = Thread(target=background_task) # type: ignore t.start() if configGet("submit", "mode"): # Registering user commands for entry in listdir(configGet("locale", "locations")): if entry.endswith(".json"): commands_list = [] for command in configGet("commands"): commands_list.append(BotCommand(command, locale(command, "commands", locale=entry.replace(".json", "")))) app.set_bot_commands(commands_list, language_code=entry.replace(".json", "")) # type: ignore # Registering user commands for fallback locale commands_list = [] for command in configGet("commands"): commands_list.append(BotCommand(command, locale(command, "commands", locale=configGet("locale_fallback")))) app.set_bot_commands(commands_list) # type: ignore # Registering admin commands commands_admin_list = [] if configGet("submit", "mode"): for command in configGet("commands"): commands_admin_list.append(BotCommand(command, locale(command, "commands", locale=configGet("locale")))) for command in configGet("commands_admin"): commands_admin_list.append(BotCommand(command, locale(command, "commands_admin", locale=configGet("locale")))) app.set_bot_commands(commands_admin_list, scope=BotCommandScopeChat(chat_id=configGet("admin"))) # type: ignore idle() app.send_message(configGet("admin"), locale("shutdown", "message", locale=configGet("locale")).format(str(pid))) # type: ignore logWrite(locale("shutdown", "console", locale=configGet("locale_log")).format(str(pid))) killProc(pid)