diff --git a/addons/afk.py b/addons/afk.py
index 74e06eb..81b6b78 100644
--- a/addons/afk.py
+++ b/addons/afk.py
@@ -5,30 +5,21 @@
# PLease read the GNU Affero General Public License in
# .
-from . import get_help
-
-__doc__ = get_help("help_afk")
-
-
import asyncio
from telethon import events
-from pyUltroid.dB.afk_db import add_afk, del_afk, is_afk
-from pyUltroid.dB.base import KeyManager
+from pyUltroid import asst, ultroid_bot
+from pyUltroid.config import LOG_CHANNEL, NOSPAM_CHAT
+from pyUltroid.db.afk_db import add_afk, del_afk, is_afk
+from pyUltroid.db.base import KeyManager
+from pyUltroid.fns.decorators import ultroid_cmd
+from pyUltroid.fns.helper import get_help, get_string
+from pyUltroid.fns.tools import mediainfo, upload_file
+from pyUltroid.udB import udB
+
+__doc__ = get_help("help_afk")
-from . import (
- LOG_CHANNEL,
- NOSPAM_CHAT,
- Redis,
- asst,
- get_string,
- mediainfo,
- udB,
- ultroid_bot,
- ultroid_cmd,
- upload_file
-)
old_afk_msg = []
@@ -93,7 +84,11 @@ async def set_afk(event):
async def remove_afk(event):
- if event.is_private and udB.get_key("PMSETTING") and not is_approved(event.chat_id):
+ if (
+ event.is_private
+ and udB.get_key("PMSETTING")
+ and not is_approved(event.chat_id)
+ ):
return
elif "afk" in event.text.lower():
return
@@ -114,7 +109,11 @@ async def remove_afk(event):
async def on_afk(event):
- if event.is_private and Redis("PMSETTING") and not is_approved(event.chat_id):
+ if (
+ event.is_private
+ and udB.get_key("PMSETTING")
+ and not is_approved(event.chat_id)
+ ):
return
elif "afk" in event.text.lower():
return
diff --git a/plugins/bot.py b/plugins/bot.py
index 91a02a6..85fd9ca 100644
--- a/plugins/bot.py
+++ b/plugins/bot.py
@@ -13,6 +13,7 @@ import os
import re
import sys
import time
+import asyncio
from platform import python_version as pyver
from random import choice
@@ -378,6 +379,29 @@ async def get_updates(ulttext, repo_url):
return repo, True, changelog
+def launch_update_script(repo_url=None):
+ """Launch the external update script and shutdown the bot."""
+ import subprocess
+ import sys
+
+ script_path = "update_script.py"
+
+ # Prepare command arguments
+ cmd = [sys.executable, script_path]
+ if repo_url:
+ cmd.append(repo_url)
+
+ # Add original start arguments so the script knows how to restart
+ if len(sys.argv) > 1:
+ cmd.extend(sys.argv[1:])
+
+ # Launch the update script
+ subprocess.Popen(cmd, cwd=os.getcwd())
+
+ # Shutdown the bot
+ os._exit(0)
+
+
@ultroid_cmd(
pattern="update(.*)",
command="update",
@@ -389,12 +413,12 @@ async def updater(event):
Description: Checks for updates for your userbot.
• `{tr}update`: Checks for updates from your forked repo (if set), otherwise from original.
-• `{tr}update now`: Forces an update from the configured repo.
+• `{tr}update now`: Forces an immediate update using external script from the configured repo.
• `{tr}update original`: Checks for updates from the official Ultroid repo.
-• `{tr}update now original`: Forces an update from the official Ultroid repo.
+• `{tr}update now original`: Forces an immediate update from the official Ultroid repo.
Note: Use `{tr}setrepo ` to update from your own fork."""
- if Var.HEROKU_API and Var.HEROKU_APP_NAME:
+ if Var.HEROKU_API:
return await event.eor(
"Heroku user! Please update from Heroku dashboard.",
)
@@ -411,10 +435,24 @@ Note: Use `{tr}setrepo ` to update from your own fork."""
or "https://github.com/ThePrateekBhatia/Ultroid"
)
- off_repo, is_new, changelog = await get_updates(
- ulttext,
- repo_url=repo_url,
- )
+ if is_now:
+ # Use external script for immediate update
+ await ulttext.edit(
+ "🔄 **Starting update process...**\n\n"
+ f"📦 Repository: `{repo_url}`\n"
+ "⚡ Using external script for reliable update\n\n"
+ "🤖 Bot will shutdown and restart automatically after update completes."
+ )
+
+ # Wait a moment for the message to be sent
+ await asyncio.sleep(2)
+
+ # Launch external update script and shutdown
+ launch_update_script(repo_url)
+ return
+
+ # Regular update check (non-destructive)
+ off_repo, is_new, changelog = await get_updates(ulttext, repo_url=repo_url)
if not off_repo:
return
@@ -422,41 +460,38 @@ Note: Use `{tr}setrepo ` to update from your own fork."""
branch = off_repo.active_branch.name
if is_new:
- if is_now:
- await ulttext.edit("`Force updating...`")
- try:
- await bash(f"git config remote.upstream.url {repo_url} && git pull -f upstream {branch}")
- await bash("pip3 install -r requirements.txt --break-system-packages")
- call_back()
- await ulttext.edit("`Update successful! Restarting...`")
- os.execl(sys.executable, sys.executable, "-m", "pyUltroid")
- except Exception as e:
- await ulttext.edit(f"**Update failed!**\n\n**Error:**\n`{e}`")
- finally:
- try:
- off_repo.delete_remote("upstream")
- except Exception:
- pass
- return
-
+ # Show update available with options
+ buttons = [
+ [
+ Button.inline("🔄 Update Now", data=f"update_now|{repo_url}"),
+ Button.inline("📋 View Changes", data=f"update_changelog|{repo_url}"),
+ ],
+ [Button.inline("❌ Dismiss", data="close_update")],
+ ]
+
m = await asst.send_message(
udB.get_key("LOG_CHANNEL"),
- changelog,
- buttons=[
- Button.inline("Update Now", data=f"update_now|{repo_url}"),
- Button.inline("Dismiss", data="close_update"),
- ],
+ f"🆕 **Update Available!**\n\n"
+ f"📦 Repository: `{repo_url}`\n"
+ f"🌿 Branch: `{branch}`\n\n"
+ f"Use the buttons below to update or view changes.",
+ buttons=buttons,
)
Link = m.message_link
await ulttext.edit(
- f'**Update available!**\n\nView changelog and update from your log channel.\n\n[View Changelog]({Link})',
+ f'**🆕 Update available!**\n\n'
+ f'📦 Repository: `{repo_url.replace(".git", "")}`\n'
+ f'🌿 Branch: `{branch}`\n\n'
+ f'[📋 View Options & Update]({Link})',
parse_mode="md",
link_preview=False,
)
else:
await ulttext.edit(
- f'Your BOT is up-to-date with [{branch}].',
- parse_mode="html",
+ f'✅ **Your bot is up-to-date!**\n\n'
+ f'📦 Repository: `{repo_url.replace(".git", "")}`\n'
+ f'🌿 Branch: `{branch}`',
+ parse_mode="md",
link_preview=False,
)
@@ -469,23 +504,56 @@ Note: Use `{tr}setrepo ` to update from your own fork."""
@callback(re.compile(b"update_now\\|(.*)"))
async def update_now_callback(event):
repo_url = event.data_match.group(1).decode("utf-8")
- await event.edit("`Updating now...`")
+ await event.edit(
+ "🔄 **Starting update process...**\n\n"
+ f"📦 Repository: `{repo_url}`\n"
+ "⚡ Using external script for reliable update\n\n"
+ "🤖 Bot will shutdown and restart automatically after update completes."
+ )
+
+ # Wait a moment for the message to be sent
+ await asyncio.sleep(2)
+
+ # Launch external update script and shutdown
+ launch_update_script(repo_url)
+
+
+@callback(re.compile(b"update_changelog\\|(.*)"))
+async def update_changelog_callback(event):
+ repo_url = event.data_match.group(1).decode("utf-8")
+
+ # Get changelog
try:
repo = Repo()
branch = repo.active_branch.name
- await bash(f"git config remote.upstream.url {repo_url} || git remote add upstream {repo_url}")
- await bash(f"git pull -f upstream {branch}")
- await bash("pip3 install -r requirements.txt --break-system-packages")
- call_back()
- await event.edit("`Update successful! Restarting...`")
- os.execl(sys.executable, sys.executable, "-m", "pyUltroid")
- except Exception as e:
- await event.edit(f"**Update failed!**\n\n**Error:**\n`{e}`")
- finally:
+
+ # Set up upstream remote
try:
- repo.delete_remote("upstream")
- except Exception:
- pass
+ upstream_remote = repo.remote("upstream")
+ upstream_remote.set_url(repo_url)
+ except ValueError:
+ upstream_remote = repo.create_remote("upstream", repo_url)
+
+ # Fetch updates
+ upstream_remote.fetch(branch)
+
+ # Generate changelog
+ changelog = f"**📋 Changelog for [{branch}]({repo_url.replace('.git', '')}/tree/{branch})**\n\n"
+ for commit in repo.iter_commits(f'{branch}..upstream/{branch}'):
+ changelog += f"• `{commit.summary}` by __{commit.author.name}__\n"
+
+ # Cleanup
+ repo.delete_remote("upstream")
+
+ await event.edit(
+ changelog,
+ buttons=[
+ [Button.inline("🔄 Update Now", data=f"update_now|{repo_url}")],
+ [Button.inline("❌ Close", data="close_update")],
+ ],
+ )
+ except Exception as e:
+ await event.edit(f"**Error getting changelog:**\n`{e}`")
@callback("close_update")
diff --git a/pyUltroid/fns/helper.py b/pyUltroid/fns/helper.py
index 2931798..59bf32f 100644
--- a/pyUltroid/fns/helper.py
+++ b/pyUltroid/fns/helper.py
@@ -227,13 +227,17 @@ if run_as_module:
f"{sys.executable} -m pip install --no-cache-dir -r requirements.txt"
)
+
@run_async
- def gen_chlog(repo, diff):
+ def gen_chlog(repo, diff, repo_url=None):
"""Generate Changelogs..."""
from .. import udB
- UPSTREAM_REPO_URL = (
- udB.get_key("UPSTREAM_REPO") or repo.remotes[0].config_reader.get("url")
- ).replace(".git", "")
+ if not repo_url:
+ UPSTREAM_REPO_URL = (
+ udB.get_key("UPSTREAM_REPO") or repo.remotes[0].config_reader.get("url")
+ ).replace(".git", "")
+ else:
+ UPSTREAM_REPO_URL = repo_url.replace(".git", "")
ac_br = repo.active_branch.name
ch_log = tldr_log = ""
ch = f"Ultroid {ultroid_version} updates for [{ac_br}]:"
@@ -271,7 +275,7 @@ async def bash(cmd, run_code=0):
# Will add in class
-async def updater():
+async def updater(repo_url=None):
from .. import LOGS, udB
if not Repo:
@@ -285,8 +289,7 @@ async def updater():
if isinstance(e, InvalidGitRepositoryError):
repo = Repo.init()
off_repo = (
- udB.get_key("UPSTREAM_REPO")
- or "https://github.com/ThePrateekBhatia/Ultroid"
+ repo_url or udB.get_key("UPSTREAM_REPO") or "https://github.com/ThePrateekBhatia/Ultroid"
)
if "upstream" not in repo.remotes:
origin = repo.create_remote("upstream", off_repo)
@@ -299,12 +302,13 @@ async def updater():
ac_br = repo.active_branch.name
- off_repo = udB.get_key("UPSTREAM_REPO") or repo.remotes[0].config_reader.get("url")
+ if not repo_url:
+ repo_url = udB.get_key("UPSTREAM_REPO") or repo.remotes[0].config_reader.get("url")
if "upstream" not in repo.remotes:
- repo.create_remote("upstream", off_repo)
+ repo.create_remote("upstream", repo_url)
else:
- repo.remote("upstream").set_url(off_repo)
+ repo.remote("upstream").set_url(repo_url)
ups_rem = repo.remote("upstream")
@@ -314,7 +318,7 @@ async def updater():
LOGS.info(f"Failed to fetch from upstream remote: {e}")
return False
- changelog, tl_chnglog = await gen_chlog(repo, f"HEAD..upstream/{ac_br}")
+ changelog, tl_chnglog = await gen_chlog(repo, f"HEAD..upstream/{ac_br}", repo_url)
return bool(changelog)
diff --git a/update_script.py b/update_script.py
new file mode 100644
index 0000000..5d50fce
--- /dev/null
+++ b/update_script.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+"""
+Ultroid Update Script
+This script handles updating the bot while it's not running.
+"""
+
+import os
+import sys
+import subprocess
+import time
+from pathlib import Path
+
+def run_command(cmd, shell=True):
+ """Run a command and return success status."""
+ try:
+ result = subprocess.run(cmd, shell=shell, capture_output=True, text=True)
+ print(f"Command: {cmd}")
+ print(f"Output: {result.stdout}")
+ if result.stderr:
+ print(f"Error: {result.stderr}")
+ return result.returncode == 0
+ except Exception as e:
+ print(f"Error running command '{cmd}': {e}")
+ return False
+
+def main():
+ """Main update function."""
+ print("🔄 Starting Ultroid update process...")
+
+ # Get script directory
+ script_dir = Path(__file__).parent.absolute()
+ os.chdir(script_dir)
+
+ print(f"📁 Working directory: {script_dir}")
+
+ # Check if we're in a git repository
+ if not (script_dir / ".git").exists():
+ print("❌ Not a git repository. Cannot update.")
+ return False
+
+ # Get the repository URL from command line args or database
+ repo_url = sys.argv[1] if len(sys.argv) > 1 else None
+
+ # Fetch and pull updates
+ print("📥 Fetching updates from repository...")
+
+ if repo_url:
+ print(f"🔗 Using repository: {repo_url}")
+ # Set up remote if needed
+ if not run_command("git remote get-url origin"):
+ run_command(f"git remote add origin {repo_url}")
+ else:
+ run_command(f"git remote set-url origin {repo_url}")
+
+ # Fetch latest changes
+ if not run_command("git fetch origin"):
+ print("❌ Failed to fetch updates")
+ return False
+
+ # Get current branch
+ result = subprocess.run("git branch --show-current", shell=True, capture_output=True, text=True)
+ current_branch = result.stdout.strip() or "main"
+
+ print(f"🌿 Current branch: {current_branch}")
+
+ # Pull updates
+ print("⬇️ Pulling updates...")
+ if not run_command(f"git pull origin {current_branch}"):
+ print("❌ Failed to pull updates")
+ return False
+
+ # Update dependencies
+ print("📦 Installing/updating dependencies...")
+ if not run_command("pip3 install -r requirements.txt --upgrade"):
+ print("⚠️ Warning: Failed to update some dependencies")
+
+ # Try alternative pip command
+ run_command("pip3 install -r requirements.txt --break-system-packages --upgrade")
+
+ print("✅ Update completed successfully!")
+ return True
+
+def restart_bot():
+ """Restart the bot after update."""
+ print("🔄 Restarting Ultroid...")
+
+ # Check if we have a virtual environment
+ venv_python = None
+ if os.path.exists("venv/bin/python"):
+ venv_python = "venv/bin/python"
+ elif os.path.exists("venv/Scripts/python.exe"):
+ venv_python = "venv/Scripts/python.exe"
+
+ # Determine how to start the bot
+ if len(sys.argv) > 1 and sys.argv[-1] == "main.py":
+ # Started with main.py
+ if venv_python:
+ os.execv(venv_python, [venv_python, "main.py"])
+ else:
+ os.execv(sys.executable, [sys.executable, "main.py"])
+ else:
+ # Started as module
+ if venv_python:
+ os.execv(venv_python, [venv_python, "-m", "pyUltroid"])
+ else:
+ os.execv(sys.executable, [sys.executable, "-m", "pyUltroid"])
+
+if __name__ == "__main__":
+ print("🚀 Ultroid Update Script")
+ print("=" * 40)
+
+ # Wait a moment for the bot to fully shutdown
+ time.sleep(2)
+
+ # Perform update
+ if main():
+ print("=" * 40)
+ restart_bot()
+ else:
+ print("❌ Update failed. Please check the errors above.")
+ sys.exit(1)