Skip to content

Instantly share code, notes, and snippets.

@jaredcat
Last active April 14, 2025 11:52
Show Gist options
  • Save jaredcat/5ae6bbc6838d854a858c04080672334d to your computer and use it in GitHub Desktop.
Save jaredcat/5ae6bbc6838d854a858c04080672334d to your computer and use it in GitHub Desktop.
Revancify CLI Wrapper

Revancify CLI Wrapper: Installation and Usage Guide

This tool allows you to automate the process of downloading, patching, and installing multiple apps using Revancify and Shizuku on Android. It works directly from Termux without requiring a PC.

Prerequisites

  • Android device
  • Termux installed from F-Droid
  • Shizuku installed and properly set up (for installation using system APIs)
  • Revancify installed in Termux

Installation

  1. Open Termux
  2. Run the following commands:
# Install dependencies
pkg update -y && pkg install curl jq expect -y

# Download the script
curl -o /data/data/com.termux/files/usr/bin/revancify-cli https://gist.githubusercontent.com/jaredcat/5ae6bbc6838d854a858c04080672334d/raw/revancify-cli.sh


# Make it executable
chmod +x /data/data/com.termux/files/usr/bin/revancify-cli

Alternatively, you can copy the script content manually and save it in Termux:

nano /data/data/com.termux/files/usr/bin/revancify-cli
# Paste the script content, then press Ctrl+O, Enter, Ctrl+X
chmod +x /data/data/com.termux/files/usr/bin/revancify-cli

Creating a Configuration File

The wrapper uses a JSON configuration file to determine which apps to process. You can create a template with:

revancify-wrapper --create-config

This creates a file called apps.json in your current directory. You can edit this file to add your desired apps.

Configuration Format

The configuration file uses the following format:

{
  "apps": [
    {
      "name": "app_name",
      "displayName": "App Display Name",
      "source": "source_type",
      "version": "version_number_or_latest",
      "patches": ["all", "-patch_to_exclude", "+patch_to_include"]
    }
  ],
  "options": {
    "patchSelection": "auto",
    "installAfterPatch": true,
    "uninstallBeforeInstall": false,
    "useShizuku": true
  }
}

App Configuration

Each app in the apps array can have the following properties:

  • name: Internal name used by Revancify (required)
  • displayName: Human-readable name (required)
  • version: App version to download:
    • recommended: Use the recommended version (marked with [RECOMMENDED] in Revancify)
    • latest: Use Auto Select to find the latest compatible version
    • stable, beta, alpha: Use latest version of specified type
    • Or specify an exact version number (e.g., "17.45.53")
  • versionType: Filter versions by type:
    • stable: Only select from stable versions
    • beta: Only select from beta versions
    • alpha: Only select from alpha versions
    • any: No filtering (default)
  • patchSource: Source of patches to use (any available source in Revancify)
    • revanced: Use official ReVanced patches
    • revanced-extended: Use ReVanced Extended patches
    • default: Use the global setting from options
  • patches: Array of patches to apply (see below)
  • root: Whether to use root installation for this app (default: false)

Patches Format

The patches array can include:

  • "all": Include all available patches
  • "recommended": Use the recommended set of patches
  • "selected": Enter interactive patch selection mode
  • "last": Use previously selected patches
  • "-patch_name": Exclude a specific patch
  • "+patch_name": Include a specific patch

Global Options

The global options object can include:

  • patchSource: Default patch source to use for all apps (unless overridden)
  • installAfterPatch: Whether to install apps after patching
  • uninstallBeforeInstall: Whether to uninstall existing apps before installing
  • useShizuku: Whether to use Shizuku for installation (will be ignored if root is true)
  • root: Whether to use root installation mode for all apps (default: false)

Usage Examples

Basic Usage

# Use default apps.json in current directory
revancify-cli

# Use a specific config file
revancify-cli my-apps.json

Advanced Options

# Enable verbose output
revancify-cli --verbose my-apps.json

# Disable Shizuku integration
revancify-cli --no-shizuku

# Show help message
revancify-cli --help

Troubleshooting

If you encounter issues:

  1. Run with --verbose to see detailed output
  2. Ensure Revancify is properly installed
  3. If installation fails, verify Shizuku is running and properly authorized
  4. Check that apps.json is properly formatted (valid JSON)

Command Line Options

  • -h, --help: Show help message
  • -v, --verbose: Enable verbose output
  • -s, --no-shizuku: Don't use Shizuku for installation
  • -c, --create-config: Create a template config file
{
"apps": [
{
"name": "youtube",
"displayName": "YouTube",
"version": "recommended",
"versionType": "stable",
"patchSource": "revanced",
"patches": ["recommended"]
},
{
"name": "youtube_music",
"displayName": "YouTube-Music",
"version": "latest",
"versionType": "any",
"patchSource": "revanced-extended",
"patches": ["selected", "+background-play", "+minimized-playback-music"]
},
{
"name": "twitter",
"displayName": "Twitter",
"version": "17.45.0",
"versionType": "stable",
"patches": ["last"]
}
],
"options": {
"patchSource": "revanced-extended",
"installAfterPatch": true,
"uninstallBeforeInstall": false,
"useShizuku": true,
"root": false
}
}
#!/data/data/com.termux/files/usr/bin/bash
#
# revancify-cli: CLI wrapper for Revancify
# Usage: revancify-cli [options] [config_file]
# If no config_file is provided, it will use ./apps.json as default
# Default values
DEFAULT_CONFIG="./apps.json"
CONFIG_FILE="${DEFAULT_CONFIG}"
VERBOSE=false
DEBUG=false
DEBUG_CAPTURE=false
VERBOSE_DEBUG=false
ROOT_MODE=false
USE_SHIZUKU=true
TEMP_DIR="$HOME/.revancify-cli-tmp"
# ANSI colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Print usage information
usage() {
echo "revancify-cli - CLI interface for Revancify"
echo ""
echo "Usage: revancify-cli [options] [config_file]"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --verbose Enable verbose output"
echo " -d, --debug Enable debug mode (more verbose than -v)"
echo " -X, --verbose-debug Enable extreme debugging (very verbose)"
echo " -x, --debug-capture Capture UI for troubleshooting"
echo " -s, --no-shizuku Don't use Shizuku for installation"
echo " -r, --root Use root installation mode"
echo " -c, --create-config Create a template config file"
echo ""
echo "Examples:"
echo " revancify-cli # Use default ./apps.json"
echo " revancify-cli my-apps.json # Use custom config file"
echo " revancify-cli -c # Create template config"
echo " revancify-cli -v my-apps.json # Verbose mode with custom config"
echo " revancify-cli -r # Use root mode"
echo " revancify-cli -d # Debug mode for troubleshooting"
echo " revancify-cli -x # Capture UI for troubleshooting"
echo " revancify-cli -X # Extreme debugging mode"
echo ""
}
# Log messages
log() {
local level="$1"
local message="$2"
local color="$NC"
case "$level" in
"info") color="$BLUE" ;;
"success") color="$GREEN" ;;
"warning") color="$YELLOW" ;;
"error") color="$RED" ;;
"debug")
color="$YELLOW"
# Only show debug messages in debug mode
if [ "$DEBUG" != true ]; then
return
fi
;;
esac
echo -e "${color}[${level^^}]${NC} $message"
}
# Verbose logging
vlog() {
if [ "$VERBOSE" = true ] || [ "$DEBUG" = true ]; then
log "info" "$1"
fi
}
# Debug logging
dlog() {
if [ "$DEBUG" = true ]; then
log "debug" "$1"
fi
}
# Create a template config file
create_template_config() {
local template='{
"apps": [
{
"name": "youtube",
"displayName": "YouTube",
"version": "recommended",
"versionType": "stable",
"patchSource": "revanced-extended",
"patches": ["recommended"]
},
{
"name": "youtube_music",
"displayName": "YouTube Music",
"version": "latest",
"versionType": "any",
"patchSource": "revanced",
"patches": ["selected", "+background-play", "+minimized-playback-music"]
},
{
"name": "twitter",
"displayName": "Twitter",
"version": "17.45.0",
"versionType": "stable",
"patches": ["last"]
}
],
"options": {
"patchSource": "revanced-extended",
"installAfterPatch": true,
"uninstallBeforeInstall": false,
"useShizuku": true,
"root": false
}
}'
local readme=''
readme+="# Revancify CLI Config\n\n"
readme+="## App Configuration\n\n"
readme+="- **name**: Internal name of the app (used to select it in Revancify menu)\n"
readme+="- **displayName**: Human-readable name for logs and messages\n"
readme+="- **version**: App version to use. Options:\n"
readme+=" - \"recommended\": Use Revancify's recommended version\n"
readme+=" - \"latest\": Use the latest version\n"
readme+=" - Specific version (e.g. \"17.45.0\"): Try to use this specific version\n"
readme+="- **patchSource**: Which patch source to use. Common options:\n"
readme+=" - \"revanced\": ReVanced patches\n"
readme+=" - \"revanced-extended\": ReVanced Extended patches\n"
readme+=" - Any other patch source name shown in Revancify\n"
readme+="- **patches**: Which patches to apply. Options:\n"
readme+=" - [\"recommended\"]: Use Revancify's recommended patches\n"
readme+=" - [\"all\"]: Apply all available patches\n"
readme+=" - [\"last\"]: Reuse your last selection\n"
readme+=" - [\"selected\"]: Interactive patch selection (script will pause for user input)\n"
readme+=" - Add specific patches with prefix: [\"+patch-name1\", \"+patch-name2\"]\n"
readme+=" - Exclude specific patches with prefix: [\"-patch-name1\", \"-patch-name2\"]\n"
readme+=" - Combine approaches: [\"recommended\", \"+additional-patch\", \"-excluded-patch\"]\n\n"
readme+="## Global Options\n\n"
readme+="- **patchSource**: Default patch source for all apps (unless overridden)\n"
readme+="- **installAfterPatch**: Whether to install after patching\n"
readme+="- **uninstallBeforeInstall**: Whether to uninstall before installing\n"
readme+="- **useShizuku**: Whether to use Shizuku for installation\n"
readme+="- **root**: Whether to use root for installation\n"
if [ -f "./apps.json" ]; then
read -p "File ./apps.json already exists. Overwrite? (y/N): " confirm
if [[ $confirm != [yY] ]]; then
log "warning" "Operation cancelled."
exit 0
fi
fi
echo "$template" >"./apps.json"
echo -e "$readme" >"./apps-readme.md"
log "success" "Template config file created at ./apps.json"
log "success" "Documentation created at ./apps-readme.md"
echo "Edit these files to add your apps and configuration."
exit 0
}
# Check dependencies
check_dependencies() {
# Check for revancify
if ! command -v revancify &>/dev/null; then
log "error" "revancify is not installed."
echo "Please install revancify first with:"
echo "curl -sL https://github.com/decipher3114/Revancify/raw/refs/heads/main/install.sh | bash"
exit 1
fi
# Check for jq (needed to parse JSON config)
if ! command -v jq &>/dev/null; then
log "warning" "jq is not installed. Installing..."
pkg install jq -y
# Check if jq was installed successfully
if ! command -v jq &>/dev/null; then
log "error" "Failed to install jq. Please install it manually with 'pkg install jq -y'"
exit 1
fi
fi
# Check for expect (needed for automation)
if ! command -v expect &>/dev/null; then
log "warning" "expect is not installed. Installing..."
pkg install expect -y
# Check if expect was installed successfully
if ! command -v expect &>/dev/null; then
log "error" "Failed to install expect. Please install it manually with 'pkg install expect -y'"
exit 1
fi
else
# Verify expect works by running a simple command
if ! expect -c "puts \"expect works\"" | grep -q "expect works"; then
log "error" "expect is installed but not functioning properly."
log "info" "Attempting to reinstall expect..."
pkg reinstall expect -y
# Check again
if ! expect -c "puts \"expect works\"" | grep -q "expect works"; then
log "error" "expect still not working properly. Please try manually removing and reinstalling it."
echo "pkg remove expect -y && pkg install expect -y"
exit 1
fi
fi
fi
# Check for timeout command (needed for debug capture)
if ! command -v timeout &>/dev/null; then
log "warning" "timeout command is not installed. Installing coreutils..."
pkg install coreutils -y
# Check if timeout was installed successfully
if ! command -v timeout &>/dev/null; then
log "warning" "Failed to install timeout command. Debug capture may hang."
fi
fi
log "success" "All dependencies are installed"
}
# Check if config file exists
check_config() {
# Check if explicitly provided config exists
if [ "$CONFIG_FILE" != "$DEFAULT_CONFIG" ] && [ ! -f "$CONFIG_FILE" ]; then
log "error" "Specified config file not found: $CONFIG_FILE"
echo "Use -c or --create-config to create a template config file."
exit 1
fi
# For default config, check in current directory and home directory
if [ "$CONFIG_FILE" = "$DEFAULT_CONFIG" ]; then
if [ -f "$CONFIG_FILE" ]; then
log "info" "Using config file in current directory: $CONFIG_FILE"
elif [ -f "$HOME/apps.json" ]; then
CONFIG_FILE="$HOME/apps.json"
log "info" "Using config file from home directory: $CONFIG_FILE"
else
log "error" "Default config file not found: $CONFIG_FILE or $HOME/apps.json"
echo "Use -c or --create-config to create a template config file."
exit 1
fi
fi
# Validate the config file is valid JSON
if ! jq '.' "$CONFIG_FILE" &>/dev/null; then
log "error" "Config file is not valid JSON: $CONFIG_FILE"
exit 1
fi
# Validate the config file has the expected structure
if ! jq '.apps' "$CONFIG_FILE" &>/dev/null; then
log "error" "Config file is missing 'apps' array: $CONFIG_FILE"
echo "Use -c or --create-config to create a valid template config file."
exit 1
fi
}
# Create and manage temp directory
setup_temp_dir() {
# Create temp directory if it doesn't exist
if [ ! -d "$TEMP_DIR" ]; then
log "info" "Creating temporary directory at $TEMP_DIR"
if ! mkdir -p "$TEMP_DIR"; then
log "error" "Failed to create temporary directory at $TEMP_DIR"
# Try an alternative location
TEMP_DIR="/data/data/com.termux/files/usr/tmp/revancify-cli-tmp"
log "info" "Trying alternative location: $TEMP_DIR"
if ! mkdir -p "$TEMP_DIR"; then
log "error" "Failed to create temporary directory at alternate location. Exiting."
exit 1
fi
fi
else
# Clean up any leftover files
log "info" "Cleaning temporary directory at $TEMP_DIR"
if ! rm -rf "${TEMP_DIR:?}"/*; then
log "warning" "Failed to clean temporary directory. Proceeding anyway."
fi
fi
# Make sure the directory is writable
if [ ! -w "$TEMP_DIR" ]; then
log "error" "Temporary directory is not writable: $TEMP_DIR"
exit 1
fi
log "success" "Temporary directory ready: $TEMP_DIR"
}
# Check if device is rooted
check_root() {
if [ "$ROOT_MODE" = true ]; then
if ! command -v su &>/dev/null || ! su -c 'exit' &>/dev/null; then
log "error" "Root access is not available. Cannot use root mode."
exit 1
fi
log "info" "Root access confirmed. Using root mode."
fi
}
# Process command line arguments
process_args() {
# Process all arguments
for ((i = 1; i <= $#; i++)); do
ARG="${!i}"
case "$ARG" in
-h | --help)
usage
exit 0
;;
-v | --verbose)
VERBOSE=true
;;
-d | --debug)
DEBUG=true
VERBOSE=true # Debug implies verbose
;;
-X | --verbose-debug)
VERBOSE_DEBUG=true
DEBUG=true
VERBOSE=true # Verbose debug implies debug and verbose
;;
-x | --debug-capture)
DEBUG_CAPTURE=true
;;
-s | --no-shizuku)
USE_SHIZUKU=false
;;
-c | --create-config)
create_template_config
# This function calls exit
;;
-r | --root)
ROOT_MODE=true
;;
-*)
log "error" "Unknown option: $ARG"
usage
exit 1
;;
*)
# This is a config file
CONFIG_FILE="$ARG"
;;
esac
done
if [ "$VERBOSE" = true ]; then
log "info" "Verbose mode enabled"
log "info" "Using config file: $CONFIG_FILE"
if [ "$USE_SHIZUKU" = true ]; then
log "info" "Shizuku installation enabled"
else
log "info" "Shizuku installation disabled"
fi
if [ "$ROOT_MODE" = true ]; then
log "info" "Root mode enabled"
fi
fi
if [ "$DEBUG" = true ]; then
log "debug" "Debug mode enabled"
log "debug" "Environment details:"
log "debug" "- Bash version: $BASH_VERSION"
log "debug" "- Working directory: $(pwd)"
log "debug" "- User home: $HOME"
log "debug" "- Temp directory: $TEMP_DIR"
fi
if [ "$VERBOSE_DEBUG" = true ]; then
log "debug" "Verbose debug mode enabled - printing environment"
# Print all environment variables for debugging
env | sort
# Print all shell variables
log "debug" "Shell variables:"
set | grep -v "^BASH_EXECUTION_STRING=" | grep -v "^BASH_SOURCE=" | sort
# Print current shell and process info
log "debug" "Shell and process info:"
ps -p $$ -o pid,ppid,cmd,args
fi
if [ "$DEBUG_CAPTURE" = true ]; then
log "info" "Debug capture mode enabled"
# Enable verbose mode as well for better output
VERBOSE=true
fi
}
# Create common expect script for patch source selection
create_source_selection_script() {
local script_file="$1"
local source="$2"
local purpose="$3" # For debug logs
dlog "Creating source selection script for '$source' in $script_file ($purpose)"
# Create basic expect script structure
cat >"$script_file" <<EOF
#!/usr/bin/env expect
set timeout 300
EOF
# Add debug logging for expect script
if [ "$DEBUG" = true ]; then
cat >>"$script_file" <<EOF
exp_internal 1
EOF
fi
# Start revancify command
cat >>"$script_file" <<EOF
spawn revancify
EOF
# Add verbose logging if needed
if [ "$VERBOSE" = true ] || [ "$DEBUG" = true ]; then
echo "log_user 1" >>"$script_file"
else
echo "log_user 0" >>"$script_file"
fi
# Navigate to Sources menu
cat >>"$script_file" <<EOF
# Navigate to Sources menu
expect {
"*Main Menu*" {
send "3\r"
}
timeout {
puts "Timeout waiting for Main Menu"
exit 1
}
}
# Wait for Sources menu to appear
expect {
"*Sources*" {}
timeout {
puts "Timeout waiting for Source menu"
exit 1
}
}
EOF
# Build source selection logic
cat >>"$script_file" <<EOF
# Try multiple approaches to select the desired source: $source
# First try the first letter
send "[string tolower [string index "$source" 0]]\r"
# Check if that worked
expect {
"*Done*" {
# We found a menu option and selected it
puts "Source selected using first letter"
send "\r"
}
"*Invalid*" -
"*No such*" -
"*Sources*" {
# Try the full name lowercase
send "[string tolower "$source"]\r"
expect {
"*Done*" {
# Full name worked
puts "Source selected using full name"
send "\r"
}
"*Invalid*" -
"*No such*" -
"*Sources*" {
# Try pressing 1, 2, 3 etc. to select from the list
puts "Trying numeric selection"
# Try numbers 1-5 for common sources
foreach num {1 2 3 4 5} {
send "\${num}\r"
expect {
"*Done*" {
puts "Selected source using number \${num}"
send "\r"
break
}
"*Invalid*" -
"*No such*" -
"*Sources*" {
# Continue to next number
}
}
}
}
}
}
}
# Make sure we get back to Main Menu
expect {
"*Main Menu*" {}
timeout {
# Try once more to get back
send "\r"
expect {
"*Main Menu*" {}
timeout {
puts "Failed to return to Main Menu"
exit 1
}
}
}
}
EOF
# Complete with update assets
cat >>"$script_file" <<EOF
# Update assets
send "4\r"
expect {
"*Update completed*" { send "\r" }
"*Update*success*" { send "\r" }
"*Main Menu*" {}
timeout {
puts "Timeout during update"
# Try to get back to main menu
send "\r"
expect "*Main Menu*"
}
}
# Make sure we're at Main Menu
expect {
"*Main Menu*" {}
timeout {
puts "Failed to return to Main Menu after update"
send "\r"
}
}
EOF
# Make script executable
chmod +x "$script_file"
}
# Simplified setup_global_patch_source using shared function
setup_global_patch_source() {
local source="$1"
log "info" "Setting global patch source to $source"
# Create temp file for the expect script
local tmp_file="${TEMP_DIR}/revancify_setup.exp"
# Create the expect script using shared function
create_source_selection_script "$tmp_file" "$source" "global"
# Add exit command to the script
cat >>"$tmp_file" <<EOF
# Exit
send "0\r"
expect eof
EOF
# Run the expect script
"$tmp_file"
local status=$?
# Check if command succeeded
if [ $status -eq 0 ]; then
log "success" "Global patch source set to $source"
else
log "error" "Failed to set global patch source to $source"
fi
return $status
}
# Generate expect script for a single app
generate_expect_script() {
local app_json="$1"
local name=$(echo "$app_json" | jq -r '.name')
local display_name=$(echo "$app_json" | jq -r '.displayName')
local version=$(echo "$app_json" | jq -r '.version // "recommended"')
local version_type=$(echo "$app_json" | jq -r '.versionType // "any"')
local patch_source=$(echo "$app_json" | jq -r '.patchSource // "default"')
local patches_raw=$(echo "$app_json" | jq -r '.patches // []')
# Create temp file for the expect script
local tmp_file="${TEMP_DIR}/revancify_expect_${name}.exp"
dlog "Generating expect script for $name at $tmp_file"
# Handle custom patch source if needed
if [ "$patch_source" != "default" ] && [ "$patch_source" != "$GLOBAL_PATCH_SOURCE" ]; then
dlog "App $name needs to change patch source to $patch_source"
# Create a separate source selection script
local source_script="${TEMP_DIR}/revancify_source_${name}.exp"
create_source_selection_script "$source_script" "$patch_source" "app-$name"
# Add main menu return command
cat >>"$source_script" <<EOF
# Exit
send "0\r"
expect eof
EOF
# Run the source selection script
log "info" "Changing patch source to $patch_source for $display_name"
"$source_script"
if [ $? -ne 0 ]; then
log "error" "Failed to change patch source for $display_name"
return 1
fi
fi
# Now create the main patching script
cat >"$tmp_file" <<EOF
#!/usr/bin/env expect
set timeout 300
EOF
# Add debug logging for expect script
if [ "$DEBUG" = true ]; then
cat >>"$tmp_file" <<EOF
exp_internal 1
EOF
fi
# Start revancify command
cat >>"$tmp_file" <<EOF
spawn revancify
EOF
# Add verbose logging if needed
if [ "$VERBOSE" = true ] || [ "$DEBUG" = true ]; then
echo "log_user 1" >>"$tmp_file"
else
echo "log_user 0" >>"$tmp_file"
fi
# Start patching process
cat >>"$tmp_file" <<EOF
# Start patching
expect "*Main Menu*"
send "1\r"
# Select app
expect "*Apps Menu*"
EOF
# Apps menu - use simple approach to select the app
cat >>"$tmp_file" <<EOF
# Select app: $name
# First try typing the full name
send "$name\r"
# Handle potential selection issues
expect {
"*Version Selection Menu*" {
# Successfully selected the app
}
"*No such app*" -
"*Invalid selection*" {
# Try with first letter as fallback
send "${name:0:1}\r"
expect {
"*Version Selection Menu*" {
# Successfully selected the app with first letter
}
"*No such app*" -
"*Invalid selection*" {
puts "Failed to find app: $name"
exit 1
}
timeout {
puts "Timeout while selecting app: $name"
exit 1
}
}
}
timeout {
puts "Timeout while selecting app: $name"
exit 1
}
}
EOF
# Add version selection logic
cat >>"$tmp_file" <<EOF
# Select version
expect "*Version Selection Menu*"
EOF
if [ "$version" = "recommended" ]; then
cat >>"$tmp_file" <<EOF
# Select recommended version
# Try using 'r' key for recommended
send "r\r"
expect {
"*patch menu*" {
# Successfully selected recommended version
}
"*Invalid*" -
"*No such*" {
# Try searching for [RECOMMENDED] tag
set found_recommended 0
# Try pressing space to see if any are marked as recommended
send " "
expect {
"*\\\[RECOMMENDED\\\]*" {
set found_recommended 1
send " \r"
}
default {
# If not found, just select the first version
send "1\r"
}
}
}
timeout {
puts "Timeout while selecting recommended version"
# Try the first version as fallback
send "1\r"
}
}
EOF
elif [ "$version" = "latest" ]; then
cat >>"$tmp_file" <<EOF
# Select latest version
# Try using 'a' key for auto/latest
send "a\r"
expect {
"*patch menu*" {
# Successfully selected latest version
}
"*Invalid*" -
"*No such*" {
# Try selecting the first version as it's likely the latest
send "1\r"
}
timeout {
puts "Timeout while selecting latest version"
# Try the first version as fallback
send "1\r"
}
}
EOF
elif [[ "$version" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
# Specific version number format (e.g. 17.45.0)
cat >>"$tmp_file" <<EOF
# Try to select specific version: $version
# First try typing the version
send "$version\r"
expect {
"*patch menu*" {
# Successfully selected version
}
"*Invalid*" -
"*No such*" {
# Try searching for the version by pressing keys
puts "Trying to find version $version by scanning"
# Try recommended as fallback
send "r\r"
expect "*patch menu*"
}
timeout {
puts "Timeout while selecting version: $version"
# Try recommended as fallback
send "r\r"
}
}
EOF
else
# Default fallback
cat >>"$tmp_file" <<EOF
# No specific version strategy, try auto select
send "a\r"
expect {
"*patch menu*" {
# Successfully selected version
}
"*Invalid*" -
"*No such*" {
# Try 'r' for recommended
send "r\r"
}
timeout {
puts "Timeout while selecting version"
# Try '1' for first version
send "1\r"
}
}
expect "*patch menu*"
EOF
fi
# Add patch selection logic
cat >>"$tmp_file" <<EOF
# Select patches
expect "*patch menu*"
EOF
# Process patches array
if echo "$patches_raw" | jq -e 'index("last")' &>/dev/null; then
# Use last selected patches
cat >>"$tmp_file" <<EOF
# Try to use last selected patches
# Try 'l' for last
send "l\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
"*Invalid*" -
"*No such*" {
# Fall back to recommended patches
send "r\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
}
}
}
EOF
elif echo "$patches_raw" | jq -e 'index("recommended")' &>/dev/null; then
# Use recommended patches
cat >>"$tmp_file" <<EOF
# Try to use recommended patches
# Try 'r' for recommended
send "r\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
"*Invalid*" -
"*No such*" {
# Just try to select all patches as fallback
send "a\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
}
}
}
EOF
elif echo "$patches_raw" | jq -e 'index("all")' &>/dev/null; then
# Apply all patches
cat >>"$tmp_file" <<EOF
# Try to apply all patches
# Try 'a' for all
send "a\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
"*Invalid*" -
"*No such*" {
# Fall back to recommended patches
send "r\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
}
}
}
EOF
elif echo "$patches_raw" | jq -e 'index("selected")' &>/dev/null; then
# Interactive selection mode
echo 'send "s\r"' >>"$tmp_file"
echo 'interact' >>"$tmp_file"
# Make script executable
chmod +x "$tmp_file"
# Return early as we can't automate further
echo "$tmp_file"
return
else
# Default to recommended patches
cat >>"$tmp_file" <<EOF
# Default to recommended patches
# Try 'r' for recommended
send "r\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
"*Invalid*" -
"*No such*" {
# Fallback to all patches
send "a\r"
expect {
"*Include/Exclude Menu*" { }
"*Install Menu*" { }
}
}
}
EOF
fi
# Handle include/exclude patches
local include_patches=$(echo "$patches_raw" | jq -r '[.[] | select(startswith("+"))] | map(sub("\\\\+"; "")) | join(",")')
local exclude_patches=$(echo "$patches_raw" | jq -r '[.[] | select(startswith("-"))] | map(sub("-"; "")) | join(",")')
if [ -n "$include_patches" ] || [ -n "$exclude_patches" ]; then
cat >>"$tmp_file" <<EOF
# Include/Exclude specific patches
expect "*Include/Exclude Menu*"
EOF
if [ -n "$include_patches" ]; then
echo "send \"+$include_patches\r\"" >>"$tmp_file"
fi
if [ -n "$exclude_patches" ]; then
echo "send \"-$exclude_patches\r\"" >>"$tmp_file"
fi
echo 'send "d\r"' >>"$tmp_file" # Done with include/exclude
fi
# Complete patching and installation
cat >>"$tmp_file" <<EOF
# Start patching
expect "*patch menu*"
send "d\r"
# Wait for patching to complete
expect {
"*Patching completed*" { send "\r" }
"*Install Menu*" { }
"*Failed*" {
puts "Patching failed"
exit 1
}
timeout {
puts "Patching timed out"
exit 1
}
}
# Installation options
expect "*Install Menu*"
EOF
# Installation method
if [ "$ROOT_MODE" = true ]; then
cat >>"$tmp_file" <<EOF
# Try to use root installation
# Try 'r' for root
send "r\r"
expect {
"*successfully*" { send "\r" }
"*installation*" { send "\r" }
"*failed*" {
puts "Root installation failed, trying normal install"
send "\r"
# Try normal install as fallback
expect "*Install Menu*"
send "i\r"
}
timeout {
puts "Installation timed out"
exit 1
}
}
EOF
elif [ "$USE_SHIZUKU" = true ]; then
cat >>"$tmp_file" <<EOF
# Try to use Shizuku installation
# Try 's' for Shizuku
send "s\r"
expect {
"*successfully*" { send "\r" }
"*installation*" { send "\r" }
"*failed*" {
puts "Shizuku installation failed, trying normal install"
send "\r"
# Try normal install as fallback
expect "*Install Menu*"
send "i\r"
}
timeout {
puts "Installation timed out"
exit 1
}
}
EOF
else
cat >>"$tmp_file" <<EOF
# Try to use normal installation
# Try 'i' for install
send "i\r"
expect {
"*successfully*" { send "\r" }
"*installation*" { send "\r" }
"*failed*" {
puts "Installation failed"
send "\r"
}
timeout {
puts "Installation timed out"
exit 1
}
}
EOF
fi
# Complete the script
cat >>"$tmp_file" <<EOF
# Return to main menu
expect {
"*Main Menu*" { send "0\r" }
timeout { exit 1 }
}
# End script
expect eof
EOF
# Make script executable
chmod +x "$tmp_file"
echo "$tmp_file"
}
# Process a single app
process_app() {
local app_json="$1"
local name=$(echo "$app_json" | jq -r '.name')
local display_name=$(echo "$app_json" | jq -r '.displayName')
log "info" "Processing $display_name ($name)"
# Generate the expect script for this app
local expect_script=$(generate_expect_script "$app_json")
# Run the expect script
if [ "$VERBOSE" = true ]; then
log "info" "Running expect script: $expect_script"
cat "$expect_script"
fi
# Execute the expect script
"$expect_script"
local status=$?
# Check if command succeeded
if [ $status -eq 0 ]; then
log "success" "$display_name processed successfully"
else
log "error" "Failed to process $display_name"
fi
return $status
}
# Process all apps in config file
process_all_apps() {
local config=$(cat "$CONFIG_FILE")
local app_count=$(echo "$config" | jq '.apps | length')
# Override settings from config file
local config_use_shizuku=$(echo "$config" | jq -r '.options.useShizuku // true')
if [ "$config_use_shizuku" = "false" ]; then
USE_SHIZUKU=false
fi
# Override root mode from config if not already set
if [ "$ROOT_MODE" = false ]; then
local config_root_mode=$(echo "$config" | jq -r '.options.root // false')
if [ "$config_root_mode" = "true" ]; then
ROOT_MODE=true
check_root
fi
fi
# Get the global patch source
GLOBAL_PATCH_SOURCE=$(echo "$config" | jq -r '.options.patchSource // "revanced"')
log "info" "Found $app_count app(s) in config"
# Check if there are any apps to process
if [ "$app_count" -eq 0 ]; then
log "warning" "No apps found in config file"
return
fi
# Set up global patch source
setup_global_patch_source "$GLOBAL_PATCH_SOURCE"
# Process each app
local success_count=0
local fail_count=0
for i in $(seq 0 $(($app_count - 1))); do
local app_json=$(echo "$config" | jq ".apps[$i]")
if [ -z "$app_json" ] || [ "$app_json" = "null" ]; then
log "error" "Failed to extract app data for index $i"
continue
fi
process_app "$app_json"
if [ $? -eq 0 ]; then
((success_count++))
else
((fail_count++))
fi
done
log "info" "Processed $app_count app(s): $success_count succeeded, $fail_count failed"
}
# Create a debug capture of the revancify menu
capture_revancify_menu() {
log "info" "Attempting to capture revancify screens with direct commands"
# Debug info - print where we are
log "debug" "Current directory: $(pwd)"
log "debug" "Script path: $0"
log "debug" "USER: $USER, HOME: $HOME"
# Check if TEMP_DIR exists
if [ ! -d "$TEMP_DIR" ]; then
log "error" "Temp directory does not exist: $TEMP_DIR"
mkdir -p "$TEMP_DIR" || {
log "error" "Failed to create temp directory"
return 1
}
fi
# Check if we can write to TEMP_DIR
if [ ! -w "$TEMP_DIR" ]; then
log "error" "Cannot write to temp directory: $TEMP_DIR"
return 1
fi
# Check if expect is available
if ! command -v expect &>/dev/null; then
log "error" "expect command not found - cannot capture menus"
return 1
fi
# Check if revancify is available
if ! command -v revancify &>/dev/null; then
log "error" "revancify command not found"
return 1
fi
local output_dir="${TEMP_DIR}/capture"
mkdir -p "$output_dir" || {
log "error" "Failed to create output directory"
return 1
}
# Create a file to gather all output
local summary_file="${output_dir}/revancify_summary.txt"
# Log basic system information
{
echo "=== SYSTEM INFO ==="
echo "Date: $(date)"
echo "OS: $(uname -a)"
echo "Termux version: $(termux-info 2>/dev/null || echo "Unknown")"
echo "PATH: $PATH"
echo "Working directory: $(pwd)"
echo ""
} >"$summary_file" || {
log "error" "Failed to write to summary file"
return 1
}
# Try to get revancify version
log "info" "Checking revancify version..."
{
echo "=== REVANCIFY VERSION ==="
echo "Command: revancify --version"
revancify --version 2>&1 || echo "Error: Unable to get version"
echo ""
} >>"$summary_file" || {
log "error" "Failed to append to summary file"
return 1
}
# Try to capture help output
log "info" "Capturing revancify help..."
{
echo "=== REVANCIFY HELP ==="
echo "Command: revancify --help"
revancify --help 2>&1 || echo "Error: Unable to get help"
echo ""
} >>"$summary_file" || {
log "error" "Failed to append to summary file"
return 1
}
# Create a very simple expect script to just capture the main menu
local menu_script="${output_dir}/capture_menu.exp"
cat >"$menu_script" <<EOF
#!/usr/bin/env expect
set timeout 5
log_file -noappend "${output_dir}/main_menu.txt"
spawn revancify
expect {
"*" {
# Just capture whatever shows up
puts "==== CAPTURING SCREEN ===="
sleep 1
# Try to exit
send "0\r"
}
timeout {
puts "Timeout waiting for any output"
}
}
expect eof
EOF
chmod +x "$menu_script"
# Create a simple script to try getting sources menu
local sources_script="${output_dir}/capture_sources.exp"
cat >"$sources_script" <<EOF
#!/usr/bin/env expect
set timeout 5
log_file -noappend "${output_dir}/sources_menu.txt"
spawn revancify
expect {
"*" {
puts "==== CAPTURED INITIAL SCREEN ===="
# Try to go to sources menu
send "3\r"
sleep 1
puts "==== CAPTURING SOURCES SCREEN ===="
# Try to exit
send "\r0\r"
}
timeout {
puts "Timeout waiting for any output"
}
}
expect eof
EOF
chmod +x "$sources_script"
# Create a simple script to try getting apps menu
local apps_script="${output_dir}/capture_apps.exp"
cat >"$apps_script" <<EOF
#!/usr/bin/env expect
set timeout 5
log_file -noappend "${output_dir}/apps_menu.txt"
spawn revancify
expect {
"*" {
puts "==== CAPTURED INITIAL SCREEN ===="
# Try to go to apps menu
send "1\r"
sleep 1
puts "==== CAPTURING APPS SCREEN ===="
# Try to exit
send "\r0\r"
}
timeout {
puts "Timeout waiting for any output"
}
}
expect eof
EOF
chmod +x "$apps_script"
# Try running each script with short timeout
log "info" "Capturing main menu (5 sec timeout)..."
timeout 5 "$menu_script" || log "warning" "Main menu capture timed out"
log "info" "Capturing sources menu (5 sec timeout)..."
timeout 5 "$sources_script" || log "warning" "Sources menu capture timed out"
log "info" "Capturing apps menu (5 sec timeout)..."
timeout 5 "$apps_script" || log "warning" "Apps menu capture timed out"
# Gather all the data we captured
{
echo "=== CAPTURED SCREENS ==="
if [ -f "${output_dir}/main_menu.txt" ]; then
echo "--- MAIN MENU ---"
cat "${output_dir}/main_menu.txt"
echo ""
fi
if [ -f "${output_dir}/sources_menu.txt" ]; then
echo "--- SOURCES MENU ---"
cat "${output_dir}/sources_menu.txt"
echo ""
fi
if [ -f "${output_dir}/apps_menu.txt" ]; then
echo "--- APPS MENU ---"
cat "${output_dir}/apps_menu.txt"
echo ""
fi
} >>"$summary_file"
# Try to extract some useful info from the captures
{
echo "=== ANALYSIS ==="
# Look for sources in the sources menu
if [ -f "${output_dir}/sources_menu.txt" ]; then
echo "Sources found:"
grep -i "revanced\|source" "${output_dir}/sources_menu.txt" |
grep -v "Main Menu\|Source Menu" || echo "None detected"
echo ""
fi
# Look for apps in the apps menu
if [ -f "${output_dir}/apps_menu.txt" ]; then
echo "Apps found:"
grep -A 10 "Apps Menu" "${output_dir}/apps_menu.txt" |
grep -v "Apps Menu\|====" || echo "None detected"
echo ""
fi
} >>"$summary_file"
# Display the summary
if [ -f "$summary_file" ] && [ -s "$summary_file" ]; then
log "success" "Capture completed. Here's what we found:"
echo "============ MENU CAPTURE SUMMARY ============"
cat "$summary_file"
echo "=============================================="
return 0
else
log "error" "Failed to capture any revancify information"
return 1
fi
}
# Main function
main() {
# Process command line arguments
process_args "$@"
# Set up trap for SIGINT to clean up temp directory
trap cleanup SIGINT SIGTERM
# Check dependencies
check_dependencies
# Check for root if needed
if [ "$ROOT_MODE" = true ]; then
check_root
fi
# Special handling for debug capture mode
if [ "$DEBUG_CAPTURE" = true ]; then
# Set up temp directory
setup_temp_dir
log "info" "Running debug capture with 15-second timeout..."
# Check if timeout command exists
if command -v timeout >/dev/null 2>&1; then
# Use system timeout command directly on this script with the function
timeout 15 capture_revancify_menu
capture_exit=$?
if [ $capture_exit -eq 124 ] || [ $capture_exit -eq 143 ]; then
log "warning" "Debug capture timed out after 15 seconds"
elif [ $capture_exit -ne 0 ]; then
log "error" "Debug capture failed with exit code $capture_exit"
fi
else
# Fallback to direct execution with no timeout
log "warning" "timeout command not found, running without timeout protection"
capture_revancify_menu
capture_exit=$?
fi
# Always cleanup
cleanup
exit $capture_exit
fi
# Check config file
check_config
# Set up temp directory
setup_temp_dir
# Process all apps
process_all_apps
local status=$?
# Clean up
cleanup
log "success" "All operations completed!"
return $status
}
# Cleanup function
cleanup() {
if [ -d "$TEMP_DIR" ] && [[ "$TEMP_DIR" == *revancify-cli* ]]; then
log "info" "Cleaning up temporary files"
rm -rf "$TEMP_DIR"
fi
}
# Set up for better error handling
trap 'echo -e "${RED}[ERROR]${NC} Error occurred on line $LINENO. Command: $BASH_COMMAND" >&2' ERR
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment