mirror of
https://github.com/kuhyx/scripts.git
synced 2026-07-04 15:23:11 +02:00
feat: more restrictive android scripts, offline docs and plagiarization utils
This commit is contained in:
parent
0977343332
commit
3193a19937
121
scripts/utils/android_guardian/blocked_apps.txt
Normal file
121
scripts/utils/android_guardian/blocked_apps.txt
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
# Android Guardian - Blocked Apps List
|
||||||
|
# One package name per line
|
||||||
|
# Lines starting with # are comments
|
||||||
|
|
||||||
|
# ===== Food Delivery Apps =====
|
||||||
|
# Uber Eats
|
||||||
|
com.ubercab.eats
|
||||||
|
|
||||||
|
# Glovo
|
||||||
|
com.glovo
|
||||||
|
|
||||||
|
# Wolt
|
||||||
|
com.wolt.android
|
||||||
|
|
||||||
|
# Bolt Food
|
||||||
|
ee.mtakso.food
|
||||||
|
|
||||||
|
# Pyszne.pl / Takeaway
|
||||||
|
com.takeaway.android
|
||||||
|
com.pyszne.pl
|
||||||
|
|
||||||
|
# DoorDash
|
||||||
|
com.dd.doordash
|
||||||
|
|
||||||
|
# Grubhub
|
||||||
|
com.grubhub.android
|
||||||
|
|
||||||
|
# Deliveroo
|
||||||
|
com.deliveroo.orderapp
|
||||||
|
|
||||||
|
# Just Eat
|
||||||
|
com.justeat.app.uk
|
||||||
|
com.justeat.android
|
||||||
|
|
||||||
|
# Postmates
|
||||||
|
com.postmates.android
|
||||||
|
|
||||||
|
# Seamless
|
||||||
|
com.seamless.consumer
|
||||||
|
|
||||||
|
# Foodpanda
|
||||||
|
com.global.foodpanda.android
|
||||||
|
|
||||||
|
# ===== Browsers (to prevent bypassing blocks) =====
|
||||||
|
# Firefox
|
||||||
|
org.mozilla.firefox
|
||||||
|
org.mozilla.firefox_beta
|
||||||
|
org.mozilla.fenix
|
||||||
|
|
||||||
|
# Chrome (comment out if needed for some functionality)
|
||||||
|
# com.android.chrome
|
||||||
|
|
||||||
|
# Opera
|
||||||
|
com.opera.browser
|
||||||
|
com.opera.mini.native
|
||||||
|
com.opera.gx
|
||||||
|
|
||||||
|
# Brave
|
||||||
|
com.brave.browser
|
||||||
|
|
||||||
|
# Samsung Internet
|
||||||
|
com.sec.android.app.sbrowser
|
||||||
|
|
||||||
|
# Microsoft Edge
|
||||||
|
com.microsoft.emmx
|
||||||
|
|
||||||
|
# DuckDuckGo
|
||||||
|
com.duckduckgo.mobile.android
|
||||||
|
|
||||||
|
# Kiwi Browser
|
||||||
|
com.kiwibrowser.browser
|
||||||
|
|
||||||
|
# Vivaldi
|
||||||
|
com.vivaldi.browser
|
||||||
|
|
||||||
|
# UC Browser
|
||||||
|
com.UCMobile.intl
|
||||||
|
|
||||||
|
# Tor Browser
|
||||||
|
org.torproject.torbrowser
|
||||||
|
|
||||||
|
# ===== YouTube Apps =====
|
||||||
|
com.google.android.youtube
|
||||||
|
com.vanced.android.youtube
|
||||||
|
app.revanced.android.youtube
|
||||||
|
|
||||||
|
# ===== Fast Food Apps =====
|
||||||
|
# McDonald's
|
||||||
|
com.mcdonalds.app
|
||||||
|
com.mcdonalds.mobileapp
|
||||||
|
|
||||||
|
# KFC
|
||||||
|
com.yum.kfcpolska
|
||||||
|
com.kfc.app
|
||||||
|
|
||||||
|
# Burger King
|
||||||
|
com.emn8.mobilem8.nativeapp.bk
|
||||||
|
com.burgerking
|
||||||
|
|
||||||
|
# Pizza Hut
|
||||||
|
com.pizzahut.app
|
||||||
|
|
||||||
|
# Domino's
|
||||||
|
com.dominospizza
|
||||||
|
|
||||||
|
# Subway
|
||||||
|
com.subway.mobile
|
||||||
|
|
||||||
|
# ===== Other Distracting Apps =====
|
||||||
|
# TikTok
|
||||||
|
com.zhiliaoapp.musically
|
||||||
|
com.ss.android.ugc.trill
|
||||||
|
|
||||||
|
# Instagram (optional - comment out if needed)
|
||||||
|
# com.instagram.android
|
||||||
|
|
||||||
|
# Facebook (optional)
|
||||||
|
# com.facebook.katana
|
||||||
|
|
||||||
|
# Twitter/X
|
||||||
|
# com.twitter.android
|
||||||
6
scripts/utils/android_guardian/module.prop
Normal file
6
scripts/utils/android_guardian/module.prop
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
id=android_guardian
|
||||||
|
name=Android Guardian (Hosts + App Blocker)
|
||||||
|
version=1.0
|
||||||
|
versionCode=1
|
||||||
|
author=linux-configuration
|
||||||
|
description=Persistent hosts blocking and app installation guard. Can only be controlled via ADB.
|
||||||
9
scripts/utils/android_guardian/post-fs-data.sh
Executable file
9
scripts/utils/android_guardian/post-fs-data.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/system/bin/sh
|
||||||
|
# Runs early in boot - set up hosts file
|
||||||
|
# MODDIR is set by Magisk and points to this module's directory
|
||||||
|
GUARDIAN_DIR="/data/adb/android_guardian"
|
||||||
|
|
||||||
|
mkdir -p "$GUARDIAN_DIR"
|
||||||
|
|
||||||
|
# Log that we're starting
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] post-fs-data: Guardian module loading" >>"$GUARDIAN_DIR/guardian.log"
|
||||||
77
scripts/utils/android_guardian/service.sh
Executable file
77
scripts/utils/android_guardian/service.sh
Executable file
@ -0,0 +1,77 @@
|
|||||||
|
#!/system/bin/sh
|
||||||
|
# Android Guardian Service - runs at boot
|
||||||
|
# This service:
|
||||||
|
# 1. Monitors and protects the hosts file
|
||||||
|
# 2. Blocks installation of forbidden apps
|
||||||
|
# 3. Can only be stopped via ADB with the correct command
|
||||||
|
|
||||||
|
MODDIR=${0%/*}
|
||||||
|
GUARDIAN_DIR="/data/adb/android_guardian"
|
||||||
|
LOG_FILE="$GUARDIAN_DIR/guardian.log"
|
||||||
|
BLOCKED_APPS_FILE="$GUARDIAN_DIR/blocked_apps.txt"
|
||||||
|
CONTROL_FILE="$GUARDIAN_DIR/control"
|
||||||
|
HOSTS_BACKUP="$GUARDIAN_DIR/hosts.backup"
|
||||||
|
|
||||||
|
# Ensure guardian directory exists
|
||||||
|
mkdir -p "$GUARDIAN_DIR"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >>"$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize control file if not exists
|
||||||
|
[ ! -f "$CONTROL_FILE" ] && echo "ENABLED" >"$CONTROL_FILE"
|
||||||
|
|
||||||
|
log "=== Android Guardian starting ==="
|
||||||
|
|
||||||
|
# Function to check if guardian is enabled
|
||||||
|
is_enabled() {
|
||||||
|
[ "$(cat "$CONTROL_FILE" 2>/dev/null)" = "ENABLED" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to restore hosts file if tampered
|
||||||
|
protect_hosts() {
|
||||||
|
if [ -f "$HOSTS_BACKUP" ]; then
|
||||||
|
current_hash=$(md5sum /system/etc/hosts 2>/dev/null | cut -d' ' -f1)
|
||||||
|
backup_hash=$(md5sum "$HOSTS_BACKUP" 2>/dev/null | cut -d' ' -f1)
|
||||||
|
|
||||||
|
if [ "$current_hash" != "$backup_hash" ]; then
|
||||||
|
log "Hosts file tampering detected! Restoring..."
|
||||||
|
cp "$HOSTS_BACKUP" "$MODDIR/system/etc/hosts"
|
||||||
|
log "Hosts file restored"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to uninstall blocked apps
|
||||||
|
check_blocked_apps() {
|
||||||
|
if [ ! -f "$BLOCKED_APPS_FILE" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r package || [ -n "$package" ]; do
|
||||||
|
# Skip comments and empty lines
|
||||||
|
case "$package" in
|
||||||
|
\#* | "") continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Check if package is installed
|
||||||
|
if pm list packages 2>/dev/null | grep -q "package:$package"; then
|
||||||
|
log "Blocked app detected: $package - Uninstalling..."
|
||||||
|
pm uninstall "$package" 2>/dev/null && log "Uninstalled: $package" || log "Failed to uninstall: $package"
|
||||||
|
fi
|
||||||
|
done <"$BLOCKED_APPS_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main monitoring loop
|
||||||
|
while true; do
|
||||||
|
if is_enabled; then
|
||||||
|
protect_hosts
|
||||||
|
check_blocked_apps
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check every 30 seconds
|
||||||
|
sleep 30
|
||||||
|
done &
|
||||||
|
|
||||||
|
log "Guardian service started (PID: $!)"
|
||||||
16
scripts/utils/android_guardian/uninstall.sh
Executable file
16
scripts/utils/android_guardian/uninstall.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/system/bin/sh
|
||||||
|
# Cleanup when module is uninstalled
|
||||||
|
GUARDIAN_DIR="/data/adb/android_guardian"
|
||||||
|
|
||||||
|
# Only allow uninstall if control file says DISABLED
|
||||||
|
if [ -f "$GUARDIAN_DIR/control" ]; then
|
||||||
|
status=$(cat "$GUARDIAN_DIR/control")
|
||||||
|
if [ "$status" != "DISABLED" ]; then
|
||||||
|
echo "Guardian is still enabled! Use ADB to disable first:"
|
||||||
|
echo " adb shell 'echo DISABLED > /data/adb/android_guardian/control'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up guardian data
|
||||||
|
rm -rf "$GUARDIAN_DIR"
|
||||||
228
scripts/utils/install_offline_docs.sh
Normal file
228
scripts/utils/install_offline_docs.sh
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Install Zeal - Offline Documentation Browser
|
||||||
|
# Downloads official documentation for: C, C++, JavaScript, TypeScript, Python
|
||||||
|
#
|
||||||
|
# Zeal is a free, open source (GPL) offline documentation browser
|
||||||
|
# Similar to Dash for macOS, uses compatible docsets
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
success() { echo -e "${GREEN}✓ $1${NC}"; }
|
||||||
|
warn() { echo -e "${YELLOW}⚠ $1${NC}"; }
|
||||||
|
error() { echo -e "${RED}✗ $1${NC}"; }
|
||||||
|
info() { echo -e "${BLUE}ℹ $1${NC}"; }
|
||||||
|
|
||||||
|
echo "=============================================="
|
||||||
|
echo " Offline Documentation Installer"
|
||||||
|
echo " Languages: C, C++, JavaScript, TypeScript, Python"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Detect package manager and install Zeal
|
||||||
|
install_zeal() {
|
||||||
|
if command -v zeal &>/dev/null; then
|
||||||
|
success "Zeal is already installed"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing Zeal offline documentation browser..."
|
||||||
|
|
||||||
|
if command -v pacman &>/dev/null; then
|
||||||
|
# Arch Linux
|
||||||
|
sudo pacman -S --noconfirm zeal
|
||||||
|
elif command -v apt &>/dev/null; then
|
||||||
|
# Debian/Ubuntu
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y zeal
|
||||||
|
elif command -v dnf &>/dev/null; then
|
||||||
|
# Fedora
|
||||||
|
sudo dnf install -y zeal
|
||||||
|
elif command -v zypper &>/dev/null; then
|
||||||
|
# openSUSE
|
||||||
|
sudo zypper install -y zeal
|
||||||
|
elif command -v flatpak &>/dev/null; then
|
||||||
|
# Flatpak fallback
|
||||||
|
flatpak install -y flathub org.zealdocs.Zeal
|
||||||
|
else
|
||||||
|
error "Could not detect package manager. Please install Zeal manually:"
|
||||||
|
echo " https://zealdocs.org/download.html"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "Zeal installed successfully"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get Zeal docsets directory
|
||||||
|
get_docsets_dir() {
|
||||||
|
local docsets_dir
|
||||||
|
|
||||||
|
# Check if using Flatpak
|
||||||
|
if command -v flatpak &>/dev/null && flatpak list | grep -q "org.zealdocs.Zeal"; then
|
||||||
|
docsets_dir="$HOME/.var/app/org.zealdocs.Zeal/data/Zeal/Zeal/docsets"
|
||||||
|
else
|
||||||
|
# Standard installation
|
||||||
|
docsets_dir="$HOME/.local/share/Zeal/Zeal/docsets"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$docsets_dir"
|
||||||
|
echo "$docsets_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download a docset from Zeal feeds
|
||||||
|
download_docset() {
|
||||||
|
local name="$1"
|
||||||
|
local docsets_dir="$2"
|
||||||
|
|
||||||
|
# Check if already installed
|
||||||
|
if [ -d "$docsets_dir/${name}.docset" ]; then
|
||||||
|
warn "$name docset already installed"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Downloading $name documentation..."
|
||||||
|
|
||||||
|
# Use Zeal's built-in feed system via CLI or direct download
|
||||||
|
# Zeal stores docsets in .docset directories
|
||||||
|
|
||||||
|
# Try to get from dash-user-contributions or official feeds
|
||||||
|
local download_url=""
|
||||||
|
|
||||||
|
case "$name" in
|
||||||
|
"C")
|
||||||
|
download_url="http://kapeli.com/feeds/C.tgz"
|
||||||
|
;;
|
||||||
|
"C++")
|
||||||
|
download_url="http://kapeli.com/feeds/C%2B%2B.tgz"
|
||||||
|
;;
|
||||||
|
"JavaScript")
|
||||||
|
download_url="http://kapeli.com/feeds/JavaScript.tgz"
|
||||||
|
;;
|
||||||
|
"TypeScript")
|
||||||
|
download_url="http://kapeli.com/feeds/TypeScript.tgz"
|
||||||
|
;;
|
||||||
|
"Python_3")
|
||||||
|
download_url="http://kapeli.com/feeds/Python_3.tgz"
|
||||||
|
;;
|
||||||
|
"Python_2")
|
||||||
|
download_url="http://kapeli.com/feeds/Python_2.tgz"
|
||||||
|
;;
|
||||||
|
"Bash")
|
||||||
|
download_url="http://kapeli.com/feeds/Bash.tgz"
|
||||||
|
;;
|
||||||
|
"HTML")
|
||||||
|
download_url="http://kapeli.com/feeds/HTML.tgz"
|
||||||
|
;;
|
||||||
|
"CSS")
|
||||||
|
download_url="http://kapeli.com/feeds/CSS.tgz"
|
||||||
|
;;
|
||||||
|
"NodeJS")
|
||||||
|
download_url="http://kapeli.com/feeds/NodeJS.tgz"
|
||||||
|
;;
|
||||||
|
"React")
|
||||||
|
download_url="http://kapeli.com/feeds/React.tgz"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
warn "Unknown docset: $name"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Download and extract
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
|
||||||
|
echo " URL: $download_url"
|
||||||
|
if curl -fL --progress-bar "$download_url" -o "$temp_file"; then
|
||||||
|
echo " Extracting to $docsets_dir..."
|
||||||
|
tar -xzf "$temp_file" -C "$docsets_dir"
|
||||||
|
rm -f "$temp_file"
|
||||||
|
success "$name documentation downloaded"
|
||||||
|
else
|
||||||
|
rm -f "$temp_file"
|
||||||
|
warn "Failed to download $name - you can install it from Zeal's UI"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main installation
|
||||||
|
main() {
|
||||||
|
# Step 1: Install Zeal
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 1: Installing Zeal ==="
|
||||||
|
install_zeal || exit 1
|
||||||
|
|
||||||
|
# Step 2: Get docsets directory
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 2: Preparing docsets directory ==="
|
||||||
|
local docsets_dir
|
||||||
|
docsets_dir=$(get_docsets_dir)
|
||||||
|
success "Docsets directory: $docsets_dir"
|
||||||
|
|
||||||
|
# Step 3: Download requested docsets
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 3: Downloading Documentation ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Core requested languages
|
||||||
|
local docsets=("C" "C++" "JavaScript" "TypeScript" "Python_3")
|
||||||
|
|
||||||
|
# Optional extras (comment out if not needed)
|
||||||
|
local extras=("Bash" "HTML" "CSS" "NodeJS")
|
||||||
|
|
||||||
|
# Download core docsets
|
||||||
|
for docset in "${docsets[@]}"; do
|
||||||
|
download_docset "$docset" "$docsets_dir"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Ask about extras
|
||||||
|
echo ""
|
||||||
|
read -r -p "Install additional docsets (Bash, HTML, CSS, NodeJS)? [Y/n] " response
|
||||||
|
if [[ ! "$response" =~ ^[Nn]$ ]]; then
|
||||||
|
for docset in "${extras[@]}"; do
|
||||||
|
download_docset "$docset" "$docsets_dir"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo ""
|
||||||
|
echo "=============================================="
|
||||||
|
echo " Installation Complete!"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
echo "Installed documentation:"
|
||||||
|
for f in "$docsets_dir"/*.docset; do
|
||||||
|
if [[ -d "$f" ]]; then
|
||||||
|
echo " ✓ $(basename "$f" .docset)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "Usage:"
|
||||||
|
echo " Launch Zeal from your application menu, or run: zeal"
|
||||||
|
echo ""
|
||||||
|
echo "To download additional docsets:"
|
||||||
|
echo " 1. Open Zeal"
|
||||||
|
echo " 2. Go to Tools → Docsets"
|
||||||
|
echo " 3. Click 'Available' tab and download what you need"
|
||||||
|
echo ""
|
||||||
|
echo "Keyboard shortcut tip:"
|
||||||
|
echo " Set a global hotkey in Zeal → Preferences → Global Shortcuts"
|
||||||
|
echo " (e.g., Alt+Space for quick documentation lookup)"
|
||||||
|
echo ""
|
||||||
|
echo "=============================================="
|
||||||
|
|
||||||
|
# Offer to launch Zeal
|
||||||
|
read -r -p "Launch Zeal now? [y/N] " response
|
||||||
|
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
nohup zeal &>/dev/null &
|
||||||
|
success "Zeal launched"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
532
scripts/utils/install_plagiarism_tools.sh
Executable file
532
scripts/utils/install_plagiarism_tools.sh
Executable file
@ -0,0 +1,532 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Install Free & Open Source Plagiarism Detection Tools for Text
|
||||||
|
# Suitable for academic work (theses, papers, etc.)
|
||||||
|
#
|
||||||
|
# Tools installed:
|
||||||
|
# 1. Python NLP-based similarity detection (sklearn, NLTK, spaCy)
|
||||||
|
# 2. Sherlock text plagiarism detector
|
||||||
|
# 3. Ferret (Java-based, if Java available)
|
||||||
|
# 4. Optional: WCopyfind via Wine (Windows tool)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
INSTALL_DIR="${HOME}/.local/share/plagiarism-tools"
|
||||||
|
VENV_DIR="${HOME}/.local/share/plagiarism-venv"
|
||||||
|
|
||||||
|
echo "=============================================="
|
||||||
|
echo " Open Source Plagiarism Detection Installer"
|
||||||
|
echo " For Academic Text (Theses, Papers, etc.)"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
success() { echo -e "${GREEN}✓ $1${NC}"; }
|
||||||
|
warn() { echo -e "${YELLOW}⚠ $1${NC}"; }
|
||||||
|
error() { echo -e "${RED}✗ $1${NC}"; }
|
||||||
|
|
||||||
|
# Create installation directory
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 1. Python-based NLP Plagiarism Detection Environment
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
echo ""
|
||||||
|
echo "=== 1. Installing Python NLP-based Plagiarism Tools ==="
|
||||||
|
|
||||||
|
# Check for Python 3
|
||||||
|
if ! command -v python3 &>/dev/null; then
|
||||||
|
error "Python 3 is required but not installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create virtual environment
|
||||||
|
if [ ! -d "$VENV_DIR" ]; then
|
||||||
|
echo "Creating Python virtual environment..."
|
||||||
|
python3 -m venv "$VENV_DIR"
|
||||||
|
success "Virtual environment created at $VENV_DIR"
|
||||||
|
else
|
||||||
|
warn "Virtual environment already exists at $VENV_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate and install packages
|
||||||
|
source "$VENV_DIR/bin/activate"
|
||||||
|
|
||||||
|
echo "Installing Python packages for text similarity detection..."
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
pip install --progress-bar on \
|
||||||
|
scikit-learn \
|
||||||
|
nltk \
|
||||||
|
spacy \
|
||||||
|
gensim \
|
||||||
|
numpy \
|
||||||
|
pandas \
|
||||||
|
python-docx \
|
||||||
|
PyPDF2 \
|
||||||
|
beautifulsoup4 \
|
||||||
|
lxml \
|
||||||
|
textdistance \
|
||||||
|
fuzzywuzzy \
|
||||||
|
python-Levenshtein
|
||||||
|
|
||||||
|
success "Python NLP packages installed"
|
||||||
|
|
||||||
|
# Download NLTK data
|
||||||
|
echo "Downloading NLTK data (stopwords, punkt tokenizer)..."
|
||||||
|
python3 -c "
|
||||||
|
import nltk
|
||||||
|
nltk.download('punkt')
|
||||||
|
nltk.download('stopwords')
|
||||||
|
nltk.download('punkt_tab')
|
||||||
|
nltk.download('averaged_perceptron_tagger')
|
||||||
|
nltk.download('wordnet')
|
||||||
|
"
|
||||||
|
success "NLTK data downloaded"
|
||||||
|
|
||||||
|
# Download spaCy English model (small)
|
||||||
|
echo "Downloading spaCy English model..."
|
||||||
|
python3 -m spacy download en_core_web_sm 2>/dev/null || warn "spaCy model download may need manual install: python -m spacy download en_core_web_sm"
|
||||||
|
success "spaCy model installed"
|
||||||
|
|
||||||
|
# Create a simple plagiarism checker script
|
||||||
|
cat >"$INSTALL_DIR/check_plagiarism.py" <<'PYEOF'
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Simple Text Plagiarism Checker
|
||||||
|
Compares documents using multiple similarity algorithms.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python check_plagiarism.py file1.txt file2.txt [file3.txt ...]
|
||||||
|
python check_plagiarism.py --dir /path/to/documents/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||||
|
from sklearn.metrics.pairwise import cosine_similarity
|
||||||
|
import nltk
|
||||||
|
from nltk.corpus import stopwords
|
||||||
|
from nltk.tokenize import word_tokenize, sent_tokenize
|
||||||
|
|
||||||
|
# Ensure NLTK data is available
|
||||||
|
try:
|
||||||
|
stopwords.words('english')
|
||||||
|
except LookupError:
|
||||||
|
nltk.download('stopwords', quiet=True)
|
||||||
|
nltk.download('punkt', quiet=True)
|
||||||
|
|
||||||
|
|
||||||
|
def read_file(filepath: str) -> str:
|
||||||
|
"""Read text from various file formats."""
|
||||||
|
path = Path(filepath)
|
||||||
|
suffix = path.suffix.lower()
|
||||||
|
|
||||||
|
if suffix == '.pdf':
|
||||||
|
try:
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
reader = PdfReader(filepath)
|
||||||
|
return ' '.join(page.extract_text() or '' for page in reader.pages)
|
||||||
|
except ImportError:
|
||||||
|
print("Warning: PyPDF2 not installed, cannot read PDF files")
|
||||||
|
return ""
|
||||||
|
elif suffix == '.docx':
|
||||||
|
try:
|
||||||
|
from docx import Document
|
||||||
|
doc = Document(filepath)
|
||||||
|
return ' '.join(para.text for para in doc.paragraphs)
|
||||||
|
except ImportError:
|
||||||
|
print("Warning: python-docx not installed, cannot read DOCX files")
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
# Assume plain text
|
||||||
|
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def preprocess_text(text: str) -> str:
|
||||||
|
"""Clean and preprocess text for comparison."""
|
||||||
|
# Lowercase
|
||||||
|
text = text.lower()
|
||||||
|
# Tokenize and remove stopwords
|
||||||
|
try:
|
||||||
|
stop_words = set(stopwords.words('english'))
|
||||||
|
words = word_tokenize(text)
|
||||||
|
words = [w for w in words if w.isalnum() and w not in stop_words]
|
||||||
|
return ' '.join(words)
|
||||||
|
except Exception:
|
||||||
|
# Fallback: simple preprocessing
|
||||||
|
return ' '.join(text.split())
|
||||||
|
|
||||||
|
|
||||||
|
def compute_similarity_matrix(documents: List[str]) -> np.ndarray:
|
||||||
|
"""Compute TF-IDF cosine similarity matrix."""
|
||||||
|
vectorizer = TfidfVectorizer(
|
||||||
|
ngram_range=(1, 3), # Use unigrams, bigrams, trigrams
|
||||||
|
min_df=1,
|
||||||
|
max_df=0.95
|
||||||
|
)
|
||||||
|
tfidf_matrix = vectorizer.fit_transform(documents)
|
||||||
|
return cosine_similarity(tfidf_matrix)
|
||||||
|
|
||||||
|
|
||||||
|
def find_similar_passages(text1: str, text2: str, min_words: int = 5) -> List[Tuple[str, str, float]]:
|
||||||
|
"""Find similar sentence-level passages between two texts."""
|
||||||
|
sentences1 = sent_tokenize(text1)
|
||||||
|
sentences2 = sent_tokenize(text2)
|
||||||
|
|
||||||
|
if not sentences1 or not sentences2:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Filter short sentences
|
||||||
|
sentences1 = [s for s in sentences1 if len(s.split()) >= min_words]
|
||||||
|
sentences2 = [s for s in sentences2 if len(s.split()) >= min_words]
|
||||||
|
|
||||||
|
if not sentences1 or not sentences2:
|
||||||
|
return []
|
||||||
|
|
||||||
|
all_sentences = sentences1 + sentences2
|
||||||
|
preprocessed = [preprocess_text(s) for s in all_sentences]
|
||||||
|
|
||||||
|
try:
|
||||||
|
vectorizer = TfidfVectorizer(ngram_range=(1, 2))
|
||||||
|
tfidf_matrix = vectorizer.fit_transform(preprocessed)
|
||||||
|
except ValueError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
n1 = len(sentences1)
|
||||||
|
similarities = []
|
||||||
|
|
||||||
|
for i, s1 in enumerate(sentences1):
|
||||||
|
for j, s2 in enumerate(sentences2):
|
||||||
|
sim = cosine_similarity(
|
||||||
|
tfidf_matrix[i:i+1],
|
||||||
|
tfidf_matrix[n1+j:n1+j+1]
|
||||||
|
)[0][0]
|
||||||
|
if sim > 0.5: # Threshold for suspicious similarity
|
||||||
|
similarities.append((s1, s2, sim))
|
||||||
|
|
||||||
|
return sorted(similarities, key=lambda x: x[2], reverse=True)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Text Plagiarism Checker - Compare documents for similarity'
|
||||||
|
)
|
||||||
|
parser.add_argument('files', nargs='*', help='Files to compare')
|
||||||
|
parser.add_argument('--dir', '-d', help='Directory containing documents to compare')
|
||||||
|
parser.add_argument('--threshold', '-t', type=float, default=0.3,
|
||||||
|
help='Similarity threshold for flagging (0-1, default: 0.3)')
|
||||||
|
parser.add_argument('--detailed', '-v', action='store_true',
|
||||||
|
help='Show detailed similar passages')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Collect files
|
||||||
|
files = []
|
||||||
|
if args.files:
|
||||||
|
files.extend(args.files)
|
||||||
|
if args.dir:
|
||||||
|
dir_path = Path(args.dir)
|
||||||
|
for ext in ['*.txt', '*.pdf', '*.docx', '*.md', '*.tex']:
|
||||||
|
files.extend(str(f) for f in dir_path.glob(ext))
|
||||||
|
|
||||||
|
if len(files) < 2:
|
||||||
|
print("Error: Need at least 2 files to compare")
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f" Plagiarism Check - Analyzing {len(files)} documents")
|
||||||
|
print(f"{'='*60}\n")
|
||||||
|
|
||||||
|
# Read and preprocess documents
|
||||||
|
documents = []
|
||||||
|
filenames = []
|
||||||
|
for f in files:
|
||||||
|
if os.path.exists(f):
|
||||||
|
text = read_file(f)
|
||||||
|
if text.strip():
|
||||||
|
documents.append(preprocess_text(text))
|
||||||
|
filenames.append(os.path.basename(f))
|
||||||
|
else:
|
||||||
|
print(f"Warning: {f} is empty or unreadable")
|
||||||
|
else:
|
||||||
|
print(f"Warning: {f} does not exist")
|
||||||
|
|
||||||
|
if len(documents) < 2:
|
||||||
|
print("Error: Not enough valid documents to compare")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Compute similarity
|
||||||
|
print("Computing document similarities...\n")
|
||||||
|
sim_matrix = compute_similarity_matrix(documents)
|
||||||
|
|
||||||
|
# Report results
|
||||||
|
print(f"{'Document Pair':<50} {'Similarity':>12}")
|
||||||
|
print("-" * 62)
|
||||||
|
|
||||||
|
suspicious_pairs = []
|
||||||
|
for i in range(len(documents)):
|
||||||
|
for j in range(i + 1, len(documents)):
|
||||||
|
similarity = sim_matrix[i][j]
|
||||||
|
pair_name = f"{filenames[i]} <-> {filenames[j]}"
|
||||||
|
|
||||||
|
if similarity >= args.threshold:
|
||||||
|
suspicious_pairs.append((i, j, similarity, pair_name))
|
||||||
|
print(f"{pair_name:<50} {similarity:>10.1%} ⚠️")
|
||||||
|
else:
|
||||||
|
print(f"{pair_name:<50} {similarity:>10.1%}")
|
||||||
|
|
||||||
|
print("-" * 62)
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
if suspicious_pairs:
|
||||||
|
print(f"\n⚠️ {len(suspicious_pairs)} pair(s) exceed {args.threshold:.0%} similarity threshold\n")
|
||||||
|
|
||||||
|
if args.detailed:
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print(" Detailed Similar Passages")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
for i, j, sim, pair_name in suspicious_pairs[:3]: # Limit to top 3
|
||||||
|
print(f"\n{pair_name} ({sim:.1%} similar):")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
raw_docs = [read_file(files[i]), read_file(files[j])]
|
||||||
|
passages = find_similar_passages(raw_docs[0], raw_docs[1])
|
||||||
|
|
||||||
|
for s1, s2, psim in passages[:5]: # Top 5 passages
|
||||||
|
print(f"\n[{psim:.0%}] Document 1: \"{s1[:100]}...\"")
|
||||||
|
print(f" Document 2: \"{s2[:100]}...\"")
|
||||||
|
else:
|
||||||
|
print(f"\n✓ No document pairs exceed {args.threshold:.0%} similarity threshold")
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print(" Analysis complete")
|
||||||
|
print("="*60 + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
PYEOF
|
||||||
|
|
||||||
|
chmod +x "$INSTALL_DIR/check_plagiarism.py"
|
||||||
|
success "Created plagiarism checker script at $INSTALL_DIR/check_plagiarism.py"
|
||||||
|
|
||||||
|
# Create convenience wrapper
|
||||||
|
mkdir -p "$HOME/.local/bin"
|
||||||
|
cat >"$HOME/.local/bin/plagcheck" <<WRAPEOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Wrapper for plagiarism checker
|
||||||
|
source "$VENV_DIR/bin/activate"
|
||||||
|
python "$INSTALL_DIR/check_plagiarism.py" "\$@"
|
||||||
|
WRAPEOF
|
||||||
|
chmod +x "$HOME/.local/bin/plagcheck"
|
||||||
|
success "Created 'plagcheck' command in ~/.local/bin/"
|
||||||
|
|
||||||
|
deactivate
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 2. Sherlock for Text (Clone from GitHub)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
echo ""
|
||||||
|
echo "=== 2. Installing Sherlock Text Plagiarism Detector ==="
|
||||||
|
|
||||||
|
SHERLOCK_DIR="$INSTALL_DIR/sherlock"
|
||||||
|
if [ ! -d "$SHERLOCK_DIR" ]; then
|
||||||
|
# There are several Sherlock implementations; using a popular Python one
|
||||||
|
if command -v git &>/dev/null; then
|
||||||
|
# Clone a text-based similarity tool
|
||||||
|
git clone --depth 1 https://github.com/Zedeldi/sherlock-py.git "$SHERLOCK_DIR" 2>/dev/null || {
|
||||||
|
warn "Could not clone sherlock-py, trying alternative..."
|
||||||
|
# Alternative: Create a simple n-gram based sherlock
|
||||||
|
mkdir -p "$SHERLOCK_DIR"
|
||||||
|
cat >"$SHERLOCK_DIR/sherlock.py" <<'SHERLOCKEOF'
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Sherlock - Simple text plagiarism detector using n-gram fingerprinting.
|
||||||
|
Based on the original Sherlock algorithm.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def tokenize(text: str) -> list:
|
||||||
|
"""Simple word tokenization."""
|
||||||
|
return [w.lower() for w in text.split() if w.isalnum()]
|
||||||
|
|
||||||
|
|
||||||
|
def get_ngrams(tokens: list, n: int = 3) -> list:
|
||||||
|
"""Generate n-grams from token list."""
|
||||||
|
return [tuple(tokens[i:i+n]) for i in range(len(tokens) - n + 1)]
|
||||||
|
|
||||||
|
|
||||||
|
def fingerprint(text: str, ngram_size: int = 3, sample_rate: int = 4) -> set:
|
||||||
|
"""Create document fingerprint using sampled n-gram hashes."""
|
||||||
|
tokens = tokenize(text)
|
||||||
|
ngrams = get_ngrams(tokens, ngram_size)
|
||||||
|
|
||||||
|
fingerprints = set()
|
||||||
|
for i, ng in enumerate(ngrams):
|
||||||
|
if i % sample_rate == 0: # Sample every nth n-gram
|
||||||
|
h = hashlib.md5(''.join(ng).encode()).hexdigest()[:8]
|
||||||
|
fingerprints.add(h)
|
||||||
|
|
||||||
|
return fingerprints
|
||||||
|
|
||||||
|
|
||||||
|
def compare_documents(fp1: set, fp2: set) -> float:
|
||||||
|
"""Jaccard similarity between fingerprints."""
|
||||||
|
if not fp1 or not fp2:
|
||||||
|
return 0.0
|
||||||
|
intersection = len(fp1 & fp2)
|
||||||
|
union = len(fp1 | fp2)
|
||||||
|
return intersection / union if union > 0 else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def read_document(filepath: str) -> str:
|
||||||
|
"""Read document content."""
|
||||||
|
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Sherlock - Text Plagiarism Detector')
|
||||||
|
parser.add_argument('files', nargs='+', help='Files to compare')
|
||||||
|
parser.add_argument('--ngram', '-n', type=int, default=3, help='N-gram size (default: 3)')
|
||||||
|
parser.add_argument('--threshold', '-t', type=float, default=0.1, help='Similarity threshold')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if len(args.files) < 2:
|
||||||
|
print("Need at least 2 files to compare")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Read and fingerprint documents
|
||||||
|
docs = {}
|
||||||
|
for f in args.files:
|
||||||
|
if os.path.exists(f):
|
||||||
|
text = read_document(f)
|
||||||
|
docs[f] = fingerprint(text, args.ngram)
|
||||||
|
|
||||||
|
print(f"\nSherlock Plagiarism Analysis")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
# Compare all pairs
|
||||||
|
files = list(docs.keys())
|
||||||
|
for i in range(len(files)):
|
||||||
|
for j in range(i + 1, len(files)):
|
||||||
|
sim = compare_documents(docs[files[i]], docs[files[j]])
|
||||||
|
name1 = os.path.basename(files[i])
|
||||||
|
name2 = os.path.basename(files[j])
|
||||||
|
flag = " ⚠️ SUSPICIOUS" if sim >= args.threshold else ""
|
||||||
|
print(f"{name1} <-> {name2}: {sim:.1%}{flag}")
|
||||||
|
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
SHERLOCKEOF
|
||||||
|
chmod +x "$SHERLOCK_DIR/sherlock.py"
|
||||||
|
}
|
||||||
|
success "Sherlock installed at $SHERLOCK_DIR"
|
||||||
|
else
|
||||||
|
warn "Git not available, skipping Sherlock installation"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Sherlock already installed at $SHERLOCK_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 3. Ferret (Java-based) - Optional
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
echo ""
|
||||||
|
echo "=== 3. Checking for Ferret (Java-based plagiarism tool) ==="
|
||||||
|
|
||||||
|
if command -v java &>/dev/null; then
|
||||||
|
FERRET_DIR="$INSTALL_DIR/ferret"
|
||||||
|
if [ ! -d "$FERRET_DIR" ]; then
|
||||||
|
mkdir -p "$FERRET_DIR"
|
||||||
|
echo "Ferret is a Java-based tool from University of Hertfordshire."
|
||||||
|
echo "Download manually from: https://homepages.herts.ac.uk/~comqcln/Ferret/"
|
||||||
|
echo "Place JAR file in: $FERRET_DIR"
|
||||||
|
warn "Ferret requires manual download (academic license)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Java not installed, skipping Ferret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 4. WCopyfind via Wine (Optional)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
echo ""
|
||||||
|
echo "=== 4. WCopyfind Information (Windows tool, needs Wine) ==="
|
||||||
|
|
||||||
|
if command -v wine &>/dev/null; then
|
||||||
|
echo "Wine is available. WCopyfind can be run via Wine."
|
||||||
|
echo "Download from: https://plagiarism.bloomfieldmedia.com/software/wcopyfind/"
|
||||||
|
echo "Run with: wine /path/to/WCopyfind.exe"
|
||||||
|
warn "WCopyfind requires manual download"
|
||||||
|
else
|
||||||
|
echo "Wine not installed. To use WCopyfind:"
|
||||||
|
echo " 1. Install wine: sudo apt install wine (or equivalent)"
|
||||||
|
echo " 2. Download WCopyfind from: https://plagiarism.bloomfieldmedia.com/software/wcopyfind/"
|
||||||
|
warn "WCopyfind skipped (Wine not available)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Summary
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
echo ""
|
||||||
|
echo "=============================================="
|
||||||
|
echo " Installation Complete!"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
echo "Installed tools:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Python NLP Plagiarism Checker (TF-IDF, cosine similarity)"
|
||||||
|
echo " Usage: plagcheck file1.txt file2.txt"
|
||||||
|
echo " plagcheck --dir /path/to/documents/ --detailed"
|
||||||
|
echo " Location: $INSTALL_DIR/check_plagiarism.py"
|
||||||
|
echo ""
|
||||||
|
echo "2. Sherlock (n-gram fingerprinting)"
|
||||||
|
echo " Location: $SHERLOCK_DIR/sherlock.py"
|
||||||
|
echo ""
|
||||||
|
echo "3. Python virtual environment with NLP libraries:"
|
||||||
|
echo " - scikit-learn (TF-IDF, cosine similarity)"
|
||||||
|
echo " - NLTK (tokenization, stopwords)"
|
||||||
|
echo " - spaCy (NLP processing)"
|
||||||
|
echo " - gensim (document similarity)"
|
||||||
|
echo " - textdistance, fuzzywuzzy (string matching)"
|
||||||
|
echo " Activate with: source $VENV_DIR/bin/activate"
|
||||||
|
echo ""
|
||||||
|
echo "Quick Start:"
|
||||||
|
echo " plagcheck thesis_v1.pdf thesis_v2.pdf --detailed"
|
||||||
|
echo " plagcheck --dir ./student_papers/ --threshold 0.4"
|
||||||
|
echo ""
|
||||||
|
echo "Note: Ensure ~/.local/bin is in your PATH:"
|
||||||
|
echo ' export PATH="$HOME/.local/bin:$PATH"'
|
||||||
|
echo ""
|
||||||
|
echo "=============================================="
|
||||||
|
|
||||||
|
# Add to PATH reminder
|
||||||
|
if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
|
||||||
|
warn "Add ~/.local/bin to your PATH by adding this to ~/.bashrc or ~/.zshrc:"
|
||||||
|
echo ' export PATH="$HOME/.local/bin:$PATH"'
|
||||||
|
fi
|
||||||
@ -1,5 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# update_android_hosts.sh - Update Android hosts file from Linux config
|
# update_android_hosts.sh - Deploy Android Guardian (hosts blocking + app blocker)
|
||||||
|
# This creates a persistent protection that can ONLY be controlled via ADB
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Source common library
|
# Source common library
|
||||||
@ -9,51 +10,315 @@ source "$SCRIPT_DIR/../lib/common.sh"
|
|||||||
# shellcheck source=../lib/android.sh
|
# shellcheck source=../lib/android.sh
|
||||||
source "$SCRIPT_DIR/../lib/android.sh"
|
source "$SCRIPT_DIR/../lib/android.sh"
|
||||||
|
|
||||||
# Initialize Android script (handles sudo, sets WORK_DIR)
|
GUARDIAN_MODULE_DIR="$SCRIPT_DIR/android_guardian"
|
||||||
init_android_script "$@"
|
GUARDIAN_DATA_DIR="/data/adb/android_guardian"
|
||||||
|
MODULE_DEST="/data/adb/modules/android_guardian"
|
||||||
|
|
||||||
log "Updating Android hosts file from Linux configuration..."
|
# Ensure android-tools (adb) is installed
|
||||||
|
ensure_adb_installed() {
|
||||||
|
if command -v adb &>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Check device connection
|
log "adb not found, installing android-tools..."
|
||||||
check_adb_device
|
|
||||||
|
|
||||||
# Check root access
|
if command -v pacman &>/dev/null; then
|
||||||
check_adb_root
|
sudo pacman -S --noconfirm android-tools || die "Failed to install android-tools"
|
||||||
|
elif command -v apt-get &>/dev/null; then
|
||||||
|
sudo apt-get update && sudo apt-get install -y adb || die "Failed to install adb"
|
||||||
|
elif command -v dnf &>/dev/null; then
|
||||||
|
sudo dnf install -y android-tools || die "Failed to install android-tools"
|
||||||
|
else
|
||||||
|
die "adb not found and could not determine package manager. Please install android-tools manually."
|
||||||
|
fi
|
||||||
|
|
||||||
# Use the StevenBlack cache or /etc/hosts
|
# Verify installation
|
||||||
HOSTS_FILE="$WORK_DIR/hosts"
|
if ! command -v adb &>/dev/null; then
|
||||||
if [[ -f /etc/hosts.stevenblack ]]; then
|
die "adb installation failed"
|
||||||
log "Using StevenBlack hosts cache..."
|
fi
|
||||||
cp /etc/hosts.stevenblack "$HOSTS_FILE"
|
|
||||||
elif [[ -f /etc/hosts ]]; then
|
|
||||||
log "Using /etc/hosts..."
|
|
||||||
cp /etc/hosts "$HOSTS_FILE"
|
|
||||||
else
|
|
||||||
die "No hosts file found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Show stats
|
log "android-tools installed successfully"
|
||||||
TOTAL_ENTRIES=$(grep -c "^0\.0\.0\.0 " "$HOSTS_FILE" || echo 0)
|
}
|
||||||
log "Hosts file contains $TOTAL_ENTRIES blocked domains"
|
|
||||||
|
|
||||||
# Push to device
|
show_usage() {
|
||||||
log "Pushing hosts file to device..."
|
cat <<EOF
|
||||||
adb push "$HOSTS_FILE" /sdcard/hosts || die "Failed to push hosts file"
|
Usage: $(basename "$0") [COMMAND]
|
||||||
|
|
||||||
# Install systemlessly
|
Commands:
|
||||||
log "Updating systemless hosts..."
|
install Install/update Android Guardian module (default)
|
||||||
adb shell "su -c 'mkdir -p /data/adb/modules/systemless_hosts/system/etc'" || die "Failed to create module directory"
|
status Show guardian status
|
||||||
adb shell "su -c 'cp /sdcard/hosts /data/adb/modules/systemless_hosts/system/etc/hosts'" || die "Failed to install hosts file"
|
disable Temporarily disable guardian (requires ADB)
|
||||||
adb shell "su -c 'chmod 644 /data/adb/modules/systemless_hosts/system/etc/hosts'" || die "Failed to set permissions"
|
enable Re-enable guardian (requires ADB)
|
||||||
adb shell "su -c 'rm /sdcard/hosts'"
|
uninstall Remove guardian module (requires ADB + disable first)
|
||||||
|
logs Show guardian logs
|
||||||
|
block-app Add an app to block list
|
||||||
|
unblock-app Remove an app from block list
|
||||||
|
list-blocked Show blocked apps list
|
||||||
|
|
||||||
echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] ✓ Hosts file updated successfully"
|
pair Pair with device over WiFi (Android 11+, no USB needed)
|
||||||
|
connect Connect to already-paired device over WiFi
|
||||||
|
disconnect Disconnect wireless ADB
|
||||||
|
|
||||||
# Append custom blocking entries
|
Android Guardian provides:
|
||||||
echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] Adding custom blocking entries..."
|
- Persistent hosts-based ad/tracker blocking
|
||||||
adb shell "su -c 'cat >> /data/adb/modules/systemless_hosts/system/etc/hosts << \"CUSTOM_EOF\"
|
- Automatic uninstallation of forbidden apps (browsers, food delivery, etc.)
|
||||||
|
- Protection that can ONLY be controlled via ADB connection
|
||||||
|
|
||||||
|
The module CANNOT be disabled from the Magisk app on the phone.
|
||||||
|
You MUST connect the phone to a PC and use this script to control it.
|
||||||
|
|
||||||
|
Wireless Setup (Android 11+):
|
||||||
|
1. On phone: Settings > Developer Options > Wireless debugging > Enable
|
||||||
|
2. Tap "Pair device with pairing code" to get IP:port and code
|
||||||
|
3. Run: $0 pair
|
||||||
|
4. Future connections: $0 connect
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wireless ADB connection file
|
||||||
|
WIRELESS_CONFIG="$HOME/.config/android_guardian_wireless"
|
||||||
|
|
||||||
|
# Discover Android devices on the network using mDNS
|
||||||
|
discover_android_device() {
|
||||||
|
local found_address=""
|
||||||
|
|
||||||
|
# Ensure avahi-browse is available
|
||||||
|
if ! command -v avahi-browse &>/dev/null; then
|
||||||
|
if command -v pacman &>/dev/null; then
|
||||||
|
echo "Installing avahi for device discovery..." >&2
|
||||||
|
sudo pacman -S --noconfirm avahi nss-mdns &>/dev/null || true
|
||||||
|
sudo systemctl enable --now avahi-daemon &>/dev/null || true
|
||||||
|
elif command -v apt-get &>/dev/null; then
|
||||||
|
sudo apt-get install -y avahi-utils &>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v avahi-browse &>/dev/null; then
|
||||||
|
echo "Scanning for Android devices (5 seconds)..." >&2
|
||||||
|
|
||||||
|
# Android wireless debugging advertises as _adb-tls-connect._tcp
|
||||||
|
local discovery_result
|
||||||
|
discovery_result=$(timeout 5 avahi-browse -rpt _adb-tls-connect._tcp 2>/dev/null | grep "^=" | head -1)
|
||||||
|
|
||||||
|
if [[ -n "$discovery_result" ]]; then
|
||||||
|
# Parse: =;eth0;IPv4;adb-...;_adb-tls-connect._tcp;local;hostname.local;192.168.x.x;port;...
|
||||||
|
local ip port
|
||||||
|
ip=$(echo "$discovery_result" | cut -d';' -f8)
|
||||||
|
port=$(echo "$discovery_result" | cut -d';' -f9)
|
||||||
|
|
||||||
|
if [[ -n "$ip" && -n "$port" ]]; then
|
||||||
|
found_address="$ip:$port"
|
||||||
|
echo "✓ Found device: $found_address" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback: try adb's mdns discovery
|
||||||
|
if [[ -z "$found_address" ]]; then
|
||||||
|
echo "Trying adb mdns discovery..." >&2
|
||||||
|
|
||||||
|
# adb can discover devices via mdns
|
||||||
|
local mdns_result
|
||||||
|
mdns_result=$(timeout 5 adb mdns services 2>/dev/null | grep -E "adb-tls-connect|_adb\._tcp" | head -1)
|
||||||
|
|
||||||
|
if [[ -n "$mdns_result" ]]; then
|
||||||
|
# Try to extract IP:port from the result
|
||||||
|
local service_name
|
||||||
|
service_name=$(echo "$mdns_result" | awk '{print $1}')
|
||||||
|
if [[ -n "$service_name" ]]; then
|
||||||
|
# Try connecting via service name
|
||||||
|
echo "Found service: $service_name" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Return found address (or empty)
|
||||||
|
echo "$found_address"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pair with device over WiFi (Android 11+)
|
||||||
|
cmd_pair() {
|
||||||
|
ensure_adb_installed
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Wireless ADB Pairing (Android 11+) ==="
|
||||||
|
echo ""
|
||||||
|
echo "On your phone:"
|
||||||
|
echo " 1. Go to Settings > Developer Options > Wireless debugging"
|
||||||
|
echo " 2. Enable Wireless debugging"
|
||||||
|
echo " 3. Tap 'Pair device with pairing code'"
|
||||||
|
echo " 4. Note the IP:port and pairing code shown"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -rp "Enter pairing IP:port (e.g., 192.168.1.100:37123): " pair_address
|
||||||
|
read -rp "Enter pairing code: " pair_code
|
||||||
|
|
||||||
|
if [[ -z "$pair_address" || -z "$pair_code" ]]; then
|
||||||
|
die "Pairing address and code are required"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Pairing with device at $pair_address..."
|
||||||
|
if adb pair "$pair_address" "$pair_code"; then
|
||||||
|
echo ""
|
||||||
|
echo "✓ Pairing successful!"
|
||||||
|
echo ""
|
||||||
|
echo "Now get the connection address:"
|
||||||
|
echo " On phone: Wireless debugging screen shows IP:port under 'IP address & Port'"
|
||||||
|
echo " (This is DIFFERENT from the pairing port)"
|
||||||
|
echo ""
|
||||||
|
read -rp "Enter connection IP:port (e.g., 192.168.1.100:41567): " connect_address
|
||||||
|
|
||||||
|
if [[ -n "$connect_address" ]]; then
|
||||||
|
# Save for future connections
|
||||||
|
mkdir -p "$(dirname "$WIRELESS_CONFIG")"
|
||||||
|
echo "$connect_address" >"$WIRELESS_CONFIG"
|
||||||
|
log "Saved connection address for future use"
|
||||||
|
|
||||||
|
# Connect now
|
||||||
|
cmd_connect
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
die "Pairing failed. Make sure the code is correct and you're on the same network."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Connect to already-paired device
|
||||||
|
cmd_connect() {
|
||||||
|
ensure_adb_installed
|
||||||
|
|
||||||
|
local connect_address=""
|
||||||
|
|
||||||
|
# Check for saved address
|
||||||
|
if [[ -f "$WIRELESS_CONFIG" ]]; then
|
||||||
|
connect_address=$(cat "$WIRELESS_CONFIG")
|
||||||
|
log "Using saved address: $connect_address"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try auto-discovery if no saved address
|
||||||
|
if [[ -z "$connect_address" ]]; then
|
||||||
|
echo ""
|
||||||
|
log "Searching for Android devices on network..."
|
||||||
|
connect_address=$(discover_android_device)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Manual fallback
|
||||||
|
if [[ -z "$connect_address" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Auto-discovery failed. Enter address manually."
|
||||||
|
echo "On phone: Settings > Developer Options > Wireless debugging"
|
||||||
|
echo "Look for IP address & Port (NOT the pairing port)"
|
||||||
|
echo ""
|
||||||
|
read -rp "Enter connection IP:port (e.g., 192.168.1.100:41567): " connect_address
|
||||||
|
|
||||||
|
if [[ -z "$connect_address" ]]; then
|
||||||
|
die "Connection address is required"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save for future
|
||||||
|
mkdir -p "$(dirname "$WIRELESS_CONFIG")"
|
||||||
|
echo "$connect_address" >"$WIRELESS_CONFIG"
|
||||||
|
|
||||||
|
log "Connecting to $connect_address..."
|
||||||
|
if adb connect "$connect_address" | grep -q "connected"; then
|
||||||
|
echo ""
|
||||||
|
echo "✓ Connected to device wirelessly!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verify connection
|
||||||
|
if adb devices | grep -q "$connect_address"; then
|
||||||
|
echo "Device ready. You can now run other commands."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "Connection failed. Possible issues:"
|
||||||
|
echo " - Wireless debugging not enabled on phone"
|
||||||
|
echo " - Phone and PC not on same WiFi network"
|
||||||
|
echo " - Port changed (check Wireless debugging screen)"
|
||||||
|
echo " - May need to pair first: $0 pair"
|
||||||
|
echo ""
|
||||||
|
# Clear saved config since it failed
|
||||||
|
rm -f "$WIRELESS_CONFIG"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect wireless ADB
|
||||||
|
cmd_disconnect() {
|
||||||
|
ensure_adb_installed
|
||||||
|
|
||||||
|
log "Disconnecting all wireless devices..."
|
||||||
|
adb disconnect
|
||||||
|
echo "✓ Disconnected"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check device connection and root
|
||||||
|
ensure_device_ready() {
|
||||||
|
ensure_adb_installed
|
||||||
|
|
||||||
|
# Check if any device is connected
|
||||||
|
if ! adb devices | grep -qE "device$|:.*device$"; then
|
||||||
|
echo ""
|
||||||
|
echo "No device connected!"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " 1. Connect USB cable with debugging enabled"
|
||||||
|
echo " 2. Use wireless: $0 pair (first time) or $0 connect"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if we have a saved wireless config
|
||||||
|
if [[ -f "$WIRELESS_CONFIG" ]]; then
|
||||||
|
read -rp "Try connecting to saved wireless device? [Y/n]: " try_wireless
|
||||||
|
if [[ "${try_wireless,,}" != "n" ]]; then
|
||||||
|
cmd_connect
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_adb_device
|
||||||
|
check_adb_root
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build the module zip
|
||||||
|
build_module() {
|
||||||
|
local tmp_dir="$WORK_DIR/guardian_module"
|
||||||
|
local module_zip="$WORK_DIR/android_guardian.zip"
|
||||||
|
|
||||||
|
echo "[BUILD] Building Android Guardian module..." >&2
|
||||||
|
|
||||||
|
rm -rf "$tmp_dir"
|
||||||
|
mkdir -p "$tmp_dir/system/etc"
|
||||||
|
|
||||||
|
# Copy module files
|
||||||
|
cp "$GUARDIAN_MODULE_DIR/module.prop" "$tmp_dir/"
|
||||||
|
cp "$GUARDIAN_MODULE_DIR/service.sh" "$tmp_dir/"
|
||||||
|
cp "$GUARDIAN_MODULE_DIR/post-fs-data.sh" "$tmp_dir/"
|
||||||
|
cp "$GUARDIAN_MODULE_DIR/uninstall.sh" "$tmp_dir/"
|
||||||
|
|
||||||
|
# Build hosts file
|
||||||
|
local hosts_file="$tmp_dir/system/etc/hosts"
|
||||||
|
if [[ -f /etc/hosts.stevenblack ]]; then
|
||||||
|
echo "[BUILD] Using StevenBlack hosts cache..." >&2
|
||||||
|
cp /etc/hosts.stevenblack "$hosts_file"
|
||||||
|
elif [[ -f /etc/hosts ]]; then
|
||||||
|
echo "[BUILD] Using /etc/hosts..." >&2
|
||||||
|
cp /etc/hosts "$hosts_file"
|
||||||
|
else
|
||||||
|
die "No hosts file found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Append custom blocking entries
|
||||||
|
cat >>"$hosts_file" <<'CUSTOM_EOF'
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Custom blocking entries - Android Guardian
|
||||||
|
# ============================================
|
||||||
|
|
||||||
# Custom blocking entries
|
|
||||||
# YouTube
|
# YouTube
|
||||||
0.0.0.0 youtube.com
|
0.0.0.0 youtube.com
|
||||||
0.0.0.0 www.youtube.com
|
0.0.0.0 www.youtube.com
|
||||||
@ -69,120 +334,331 @@ adb shell "su -c 'cat >> /data/adb/modules/systemless_hosts/system/etc/hosts <<
|
|||||||
0.0.0.0 s.ytimg.com
|
0.0.0.0 s.ytimg.com
|
||||||
0.0.0.0 i9.ytimg.com
|
0.0.0.0 i9.ytimg.com
|
||||||
0.0.0.0 googlevideo.com
|
0.0.0.0 googlevideo.com
|
||||||
0.0.0.0 r1---sn-4g5e6nls.googlevideo.com
|
|
||||||
0.0.0.0 r1---sn-4g5lne7s.googlevideo.com
|
|
||||||
|
|
||||||
# Steam Store
|
# Discord (media only - voice chat allowed)
|
||||||
|
|
||||||
# Discord (selective blocking - media only, voice chat allowed)
|
|
||||||
0.0.0.0 cdn.discordapp.com
|
0.0.0.0 cdn.discordapp.com
|
||||||
0.0.0.0 media.discordapp.net
|
0.0.0.0 media.discordapp.net
|
||||||
0.0.0.0 images-ext-1.discordapp.net
|
0.0.0.0 images-ext-1.discordapp.net
|
||||||
0.0.0.0 images-ext-2.discordapp.net
|
0.0.0.0 images-ext-2.discordapp.net
|
||||||
0.0.0.0 attachments-1.discordapp.net
|
|
||||||
0.0.0.0 attachments-2.discordapp.net
|
|
||||||
0.0.0.0 tenor.com
|
0.0.0.0 tenor.com
|
||||||
0.0.0.0 giphy.com
|
0.0.0.0 giphy.com
|
||||||
|
|
||||||
# Food Delivery Services
|
# Food Delivery Services
|
||||||
# Polish services
|
|
||||||
0.0.0.0 pyszne.pl
|
0.0.0.0 pyszne.pl
|
||||||
0.0.0.0 www.pyszne.pl
|
0.0.0.0 www.pyszne.pl
|
||||||
0.0.0.0 m.pyszne.pl
|
|
||||||
0.0.0.0 glovo.com
|
0.0.0.0 glovo.com
|
||||||
0.0.0.0 www.glovo.com
|
0.0.0.0 www.glovo.com
|
||||||
0.0.0.0 m.glovo.com
|
|
||||||
0.0.0.0 bolt.eu
|
0.0.0.0 bolt.eu
|
||||||
0.0.0.0 food.bolt.eu
|
0.0.0.0 food.bolt.eu
|
||||||
0.0.0.0 woltwojta.pl
|
|
||||||
0.0.0.0 www.woltwojta.pl
|
|
||||||
0.0.0.0 wolt.com
|
0.0.0.0 wolt.com
|
||||||
0.0.0.0 www.wolt.com
|
0.0.0.0 www.wolt.com
|
||||||
0.0.0.0 m.wolt.com
|
|
||||||
|
|
||||||
# International services
|
|
||||||
0.0.0.0 ubereats.com
|
0.0.0.0 ubereats.com
|
||||||
0.0.0.0 www.ubereats.com
|
0.0.0.0 www.ubereats.com
|
||||||
0.0.0.0 m.ubereats.com
|
|
||||||
0.0.0.0 uber.com
|
|
||||||
0.0.0.0 www.uber.com
|
|
||||||
0.0.0.0 m.uber.com
|
|
||||||
0.0.0.0 deliveroo.com
|
0.0.0.0 deliveroo.com
|
||||||
0.0.0.0 www.deliveroo.com
|
0.0.0.0 www.deliveroo.com
|
||||||
0.0.0.0 m.deliveroo.com
|
|
||||||
0.0.0.0 deliveroo.co.uk
|
|
||||||
0.0.0.0 www.deliveroo.co.uk
|
|
||||||
0.0.0.0 foodpanda.com
|
0.0.0.0 foodpanda.com
|
||||||
0.0.0.0 www.foodpanda.com
|
0.0.0.0 www.foodpanda.com
|
||||||
0.0.0.0 m.foodpanda.com
|
|
||||||
0.0.0.0 grubhub.com
|
0.0.0.0 grubhub.com
|
||||||
0.0.0.0 www.grubhub.com
|
0.0.0.0 www.grubhub.com
|
||||||
0.0.0.0 m.grubhub.com
|
|
||||||
0.0.0.0 doordash.com
|
0.0.0.0 doordash.com
|
||||||
0.0.0.0 www.doordash.com
|
0.0.0.0 www.doordash.com
|
||||||
0.0.0.0 m.doordash.com
|
|
||||||
0.0.0.0 justeat.com
|
0.0.0.0 justeat.com
|
||||||
0.0.0.0 www.justeat.com
|
0.0.0.0 www.justeat.com
|
||||||
0.0.0.0 m.justeat.com
|
|
||||||
0.0.0.0 justeat.co.uk
|
|
||||||
0.0.0.0 www.justeat.co.uk
|
|
||||||
0.0.0.0 postmates.com
|
|
||||||
0.0.0.0 www.postmates.com
|
|
||||||
0.0.0.0 seamless.com
|
|
||||||
0.0.0.0 www.seamless.com
|
|
||||||
0.0.0.0 menulog.com.au
|
|
||||||
0.0.0.0 www.menulog.com.au
|
|
||||||
0.0.0.0 delivery.com
|
|
||||||
0.0.0.0 www.delivery.com
|
|
||||||
|
|
||||||
# Fast food chain apps and websites
|
# Fast Food
|
||||||
0.0.0.0 mcdonalds.com
|
0.0.0.0 mcdonalds.com
|
||||||
0.0.0.0 www.mcdonalds.com
|
0.0.0.0 www.mcdonalds.com
|
||||||
0.0.0.0 m.mcdonalds.com
|
|
||||||
0.0.0.0 mcdonalds.pl
|
0.0.0.0 mcdonalds.pl
|
||||||
0.0.0.0 www.mcdonalds.pl
|
0.0.0.0 www.mcdonalds.pl
|
||||||
0.0.0.0 kfc.com
|
0.0.0.0 kfc.com
|
||||||
0.0.0.0 www.kfc.com
|
0.0.0.0 www.kfc.com
|
||||||
0.0.0.0 m.kfc.com
|
|
||||||
0.0.0.0 kfc.pl
|
0.0.0.0 kfc.pl
|
||||||
0.0.0.0 www.kfc.pl
|
0.0.0.0 www.kfc.pl
|
||||||
0.0.0.0 burgerking.com
|
0.0.0.0 burgerking.com
|
||||||
0.0.0.0 www.burgerking.com
|
0.0.0.0 www.burgerking.com
|
||||||
0.0.0.0 m.burgerking.com
|
|
||||||
0.0.0.0 burgerking.pl
|
|
||||||
0.0.0.0 www.burgerking.pl
|
|
||||||
0.0.0.0 pizzahut.com
|
0.0.0.0 pizzahut.com
|
||||||
0.0.0.0 www.pizzahut.com
|
0.0.0.0 www.pizzahut.com
|
||||||
0.0.0.0 m.pizzahut.com
|
|
||||||
0.0.0.0 pizzahut.pl
|
|
||||||
0.0.0.0 www.pizzahut.pl
|
|
||||||
0.0.0.0 dominos.com
|
0.0.0.0 dominos.com
|
||||||
0.0.0.0 www.dominos.com
|
0.0.0.0 www.dominos.com
|
||||||
0.0.0.0 m.dominos.com
|
|
||||||
0.0.0.0 dominos.pl
|
|
||||||
0.0.0.0 www.dominos.pl
|
|
||||||
0.0.0.0 subway.com
|
|
||||||
0.0.0.0 www.subway.com
|
|
||||||
0.0.0.0 m.subway.com
|
|
||||||
0.0.0.0 subway.pl
|
|
||||||
0.0.0.0 www.subway.pl
|
|
||||||
CUSTOM_EOF
|
CUSTOM_EOF
|
||||||
'" || {
|
|
||||||
echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] ✗ Failed to add custom entries"
|
local total_entries
|
||||||
exit 1
|
total_entries=$(grep -c "^0\.0\.0\.0 " "$hosts_file" || echo 0)
|
||||||
|
echo "[BUILD] Hosts file contains $total_entries blocked domains" >&2
|
||||||
|
|
||||||
|
# Create zip
|
||||||
|
(cd "$tmp_dir" && zip -r "$module_zip" . -x "*.DS_Store") >/dev/null
|
||||||
|
|
||||||
|
echo "$module_zip"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] ✓ Custom entries added successfully"
|
# Install/update the guardian module
|
||||||
|
cmd_install() {
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
# Count and display blocked domains
|
local module_zip
|
||||||
domain_count=$(adb shell "su -c 'cat /system/etc/hosts | grep -c \"^0.0.0.0\"'" 2>/dev/null | tr -d '\r')
|
module_zip=$(build_module)
|
||||||
if [[ -n $domain_count ]]; then
|
|
||||||
echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] ✓ Total blocked domains: $domain_count"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] ✓ Changes will take effect immediately for new connections"
|
log "Pushing module to device..."
|
||||||
echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] (Optional: Toggle airplane mode or reboot to force all apps to reconnect)"
|
adb push "$module_zip" /sdcard/android_guardian.zip || die "Failed to push module"
|
||||||
log "✓ Total blocked domains: $TOTAL_ENTRIES"
|
|
||||||
log ""
|
log "Installing module..."
|
||||||
log "Changes will take effect immediately for new connections."
|
adb shell "su -c 'mkdir -p $MODULE_DEST'" || die "Failed to create module directory"
|
||||||
log "To apply to all apps, reboot the device or toggle airplane mode."
|
adb shell "su -c 'cd $MODULE_DEST && unzip -o /sdcard/android_guardian.zip'" || die "Failed to extract module"
|
||||||
|
adb shell "su -c 'chmod 755 $MODULE_DEST/*.sh'"
|
||||||
|
adb shell "su -c 'rm /sdcard/android_guardian.zip'"
|
||||||
|
|
||||||
|
# Set up guardian data directory
|
||||||
|
log "Setting up guardian data..."
|
||||||
|
adb shell "su -c 'mkdir -p $GUARDIAN_DATA_DIR'"
|
||||||
|
adb shell "su -c 'echo ENABLED > $GUARDIAN_DATA_DIR/control'"
|
||||||
|
|
||||||
|
# Copy blocked apps list
|
||||||
|
adb push "$GUARDIAN_MODULE_DIR/blocked_apps.txt" /sdcard/blocked_apps.txt || die "Failed to push blocked apps list"
|
||||||
|
adb shell "su -c 'cp /sdcard/blocked_apps.txt $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||||
|
adb shell "su -c 'rm /sdcard/blocked_apps.txt'"
|
||||||
|
|
||||||
|
# Create hosts backup for tamper protection
|
||||||
|
adb shell "su -c 'cp $MODULE_DEST/system/etc/hosts $GUARDIAN_DATA_DIR/hosts.backup'"
|
||||||
|
|
||||||
|
# Immediately uninstall any currently installed blocked apps
|
||||||
|
log "Checking for blocked apps to remove..."
|
||||||
|
uninstall_blocked_apps
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo " ✓ Android Guardian installed!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Features enabled:"
|
||||||
|
echo " • Hosts-based ad/tracker blocking"
|
||||||
|
echo " • App installation blocking"
|
||||||
|
echo " • Tamper protection"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ This can ONLY be controlled via ADB:"
|
||||||
|
echo " Disable: $0 disable"
|
||||||
|
echo " Enable: $0 enable"
|
||||||
|
echo " Status: $0 status"
|
||||||
|
echo ""
|
||||||
|
echo "Reboot your device to activate the module."
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Uninstall currently installed blocked apps
|
||||||
|
uninstall_blocked_apps() {
|
||||||
|
local blocked_apps
|
||||||
|
blocked_apps=$(grep -v '^#' "$GUARDIAN_MODULE_DIR/blocked_apps.txt" | grep -v '^$' || true)
|
||||||
|
|
||||||
|
for package in $blocked_apps; do
|
||||||
|
if adb shell "pm list packages" 2>/dev/null | grep -q "package:$package"; then
|
||||||
|
log "Uninstalling blocked app: $package"
|
||||||
|
adb shell "pm uninstall $package" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show status
|
||||||
|
cmd_status() {
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Android Guardian Status ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if module is installed
|
||||||
|
if adb shell "su -c 'test -d $MODULE_DEST'" 2>/dev/null; then
|
||||||
|
echo "Module: INSTALLED"
|
||||||
|
else
|
||||||
|
echo "Module: NOT INSTALLED"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check control status
|
||||||
|
local status
|
||||||
|
status=$(adb shell "su -c 'cat $GUARDIAN_DATA_DIR/control 2>/dev/null || echo UNKNOWN'" | tr -d '\r')
|
||||||
|
echo "Status: $status"
|
||||||
|
|
||||||
|
# Check hosts file
|
||||||
|
local hosts_entries
|
||||||
|
hosts_entries=$(adb shell "su -c 'grep -c \"^0.0.0.0\" /system/etc/hosts 2>/dev/null || echo 0'" | tr -d '\r')
|
||||||
|
echo "Blocked domains: $hosts_entries"
|
||||||
|
|
||||||
|
# Check blocked apps count
|
||||||
|
local blocked_count
|
||||||
|
blocked_count=$(adb shell "su -c 'grep -v \"^#\" $GUARDIAN_DATA_DIR/blocked_apps.txt 2>/dev/null | grep -v \"^$\" | wc -l || echo 0'" | tr -d '\r')
|
||||||
|
echo "Blocked app rules: $blocked_count packages"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disable guardian
|
||||||
|
cmd_disable() {
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
log "Disabling Android Guardian..."
|
||||||
|
adb shell "su -c 'echo DISABLED > $GUARDIAN_DATA_DIR/control'" || die "Failed to disable guardian"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✓ Guardian DISABLED"
|
||||||
|
echo " Hosts blocking still active until reboot"
|
||||||
|
echo " App blocking service paused"
|
||||||
|
echo ""
|
||||||
|
echo "To re-enable: $0 enable"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable guardian
|
||||||
|
cmd_enable() {
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
log "Enabling Android Guardian..."
|
||||||
|
adb shell "su -c 'echo ENABLED > $GUARDIAN_DATA_DIR/control'" || die "Failed to enable guardian"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✓ Guardian ENABLED"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Uninstall module
|
||||||
|
cmd_uninstall() {
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
# Check if disabled first
|
||||||
|
local status
|
||||||
|
status=$(adb shell "su -c 'cat $GUARDIAN_DATA_DIR/control 2>/dev/null || echo ENABLED'" | tr -d '\r')
|
||||||
|
|
||||||
|
if [[ "$status" != "DISABLED" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ Guardian must be disabled before uninstalling!"
|
||||||
|
echo " Run: $0 disable"
|
||||||
|
echo " Then: $0 uninstall"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Removing Android Guardian..."
|
||||||
|
adb shell "su -c 'rm -rf $MODULE_DEST'"
|
||||||
|
adb shell "su -c 'rm -rf $GUARDIAN_DATA_DIR'"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✓ Guardian uninstalled"
|
||||||
|
echo " Reboot to remove hosts blocking"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show logs
|
||||||
|
cmd_logs() {
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
echo "=== Guardian Logs ==="
|
||||||
|
adb shell "su -c 'cat $GUARDIAN_DATA_DIR/guardian.log 2>/dev/null || echo \"No logs yet\"'"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block an app
|
||||||
|
cmd_block_app() {
|
||||||
|
local package="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$package" ]]; then
|
||||||
|
echo "Usage: $0 block-app <package.name>"
|
||||||
|
echo "Example: $0 block-app com.ubercab.eats"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
log "Adding $package to block list..."
|
||||||
|
adb shell "su -c 'echo \"$package\" >> $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||||
|
|
||||||
|
# Also add to local file
|
||||||
|
echo "$package" >>"$GUARDIAN_MODULE_DIR/blocked_apps.txt"
|
||||||
|
|
||||||
|
# Try to uninstall if currently installed
|
||||||
|
if adb shell "pm list packages" 2>/dev/null | grep -q "package:$package"; then
|
||||||
|
log "Uninstalling $package..."
|
||||||
|
adb shell "pm uninstall $package" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ $package added to block list"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Unblock an app
|
||||||
|
cmd_unblock_app() {
|
||||||
|
local package="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$package" ]]; then
|
||||||
|
echo "Usage: $0 unblock-app <package.name>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
log "Removing $package from block list..."
|
||||||
|
adb shell "su -c 'grep -v \"^$package\$\" $GUARDIAN_DATA_DIR/blocked_apps.txt > $GUARDIAN_DATA_DIR/blocked_apps.tmp && mv $GUARDIAN_DATA_DIR/blocked_apps.tmp $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||||
|
|
||||||
|
# Also remove from local file
|
||||||
|
grep -v "^$package$" "$GUARDIAN_MODULE_DIR/blocked_apps.txt" >"$GUARDIAN_MODULE_DIR/blocked_apps.tmp" && mv "$GUARDIAN_MODULE_DIR/blocked_apps.tmp" "$GUARDIAN_MODULE_DIR/blocked_apps.txt"
|
||||||
|
|
||||||
|
echo "✓ $package removed from block list"
|
||||||
|
}
|
||||||
|
|
||||||
|
# List blocked apps
|
||||||
|
cmd_list_blocked() {
|
||||||
|
ensure_device_ready
|
||||||
|
|
||||||
|
echo "=== Blocked Apps ==="
|
||||||
|
adb shell "su -c 'cat $GUARDIAN_DATA_DIR/blocked_apps.txt 2>/dev/null'" | grep -v "^#" | grep -v "^$" || echo "No blocked apps"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main
|
||||||
|
# Initialize Android script (handles sudo, sets WORK_DIR)
|
||||||
|
init_android_script "$@"
|
||||||
|
|
||||||
|
COMMAND="${1:-install}"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
case "$COMMAND" in
|
||||||
|
install)
|
||||||
|
cmd_install
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
cmd_status
|
||||||
|
;;
|
||||||
|
disable)
|
||||||
|
cmd_disable
|
||||||
|
;;
|
||||||
|
enable)
|
||||||
|
cmd_enable
|
||||||
|
;;
|
||||||
|
uninstall)
|
||||||
|
cmd_uninstall
|
||||||
|
;;
|
||||||
|
logs)
|
||||||
|
cmd_logs
|
||||||
|
;;
|
||||||
|
block-app)
|
||||||
|
cmd_block_app "$@"
|
||||||
|
;;
|
||||||
|
unblock-app)
|
||||||
|
cmd_unblock_app "$@"
|
||||||
|
;;
|
||||||
|
list-blocked)
|
||||||
|
cmd_list_blocked
|
||||||
|
;;
|
||||||
|
pair)
|
||||||
|
cmd_pair
|
||||||
|
;;
|
||||||
|
connect)
|
||||||
|
cmd_connect
|
||||||
|
;;
|
||||||
|
disconnect)
|
||||||
|
cmd_disconnect
|
||||||
|
;;
|
||||||
|
-h | --help | help)
|
||||||
|
show_usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown command: $COMMAND"
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user