diff --git a/admin/src/pages/sms/SmsSetupPage.tsx b/admin/src/pages/sms/SmsSetupPage.tsx index 051ddd71..a404163d 100644 --- a/admin/src/pages/sms/SmsSetupPage.tsx +++ b/admin/src/pages/sms/SmsSetupPage.tsx @@ -460,8 +460,8 @@ export default function SmsSetupPage() { configures auto-start, and launches the server.
SMS Campaign Manager โข Android Interface
+{json.dumps(battery_data, indent=2)}
+
+ """
+ except Exception as e:
+ return f"{str(e)}"
+
+@app.route('/notification')
+def notification():
+ try:
+ subprocess.run(['termux-notification', '--title', 'Flask Test', '--content', 'Hello from SMS Campaign Manager!'], capture_output=True, text=True)
+ return f"""
+ Check your Android notifications.
+ + """ + except Exception as e: + return f"{str(e)}"
+
+if __name__ == '__main__':
+ print("๐ Starting SMS Campaign Manager on Termux...")
+ print("๐ฑ Device IP: 10.0.0.193")
+ print("๐ Access from Ubuntu: http://10.0.0.193:5000")
+ app.run(host='0.0.0.0', port=5000, debug=True)
diff --git a/termux-sms/network-monitor.sh b/termux-sms/network-monitor.sh
new file mode 100755
index 00000000..476b59b4
--- /dev/null
+++ b/termux-sms/network-monitor.sh
@@ -0,0 +1,58 @@
+# Network monitor and auto-service starter
+# Monitors for home network connection and starts services
+
+HOME_NETWORK_SSID="The Bunker V3" # Replace with your home network name
+HOME_NETWORK_IP_RANGE="10.0.0" # Your home network IP prefix
+LOG_FILE="$HOME/logs/network-monitor.log"
+SERVICES_STARTED=false
+
+# Ensure logs directory exists
+mkdir -p ~/logs
+
+log() {
+ echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
+}
+
+check_home_network() {
+ # Check if connected to home network by IP range
+ current_ip=$(ifconfig 2>/dev/null | grep -A1 wlan0 | grep inet | awk '{print $2}' | cut -d: -f2)
+
+ if [[ "$current_ip" == $HOME_NETWORK_IP_RANGE* ]]; then
+ return 0 # On home network
+ else
+ return 1 # Not on home network
+ fi
+}
+
+start_services() {
+ if [ "$SERVICES_STARTED" = false ]; then
+ log "๐ Home network detected - starting services..."
+ ~/bin/start-all-services.sh >> "$LOG_FILE" 2>&1
+ SERVICES_STARTED=true
+ log "โ
Services startup completed"
+ fi
+}
+
+stop_services() {
+ if [ "$SERVICES_STARTED" = true ]; then
+ log "๐ Left home network - stopping services..."
+ pkill -f "termux-sms-api-server.py" 2>/dev/null
+ pkill -f "python app.py" 2>/dev/null
+ SERVICES_STARTED=false
+ log "โน๏ธ Services stopped"
+ fi
+}
+
+log "๐ก Network monitor started"
+
+# Main monitoring loop
+while true; do
+ if check_home_network; then
+ start_services
+ else
+ stop_services
+ fi
+
+ # Check every 30 seconds
+ sleep 30
+done
diff --git a/termux-sms/services/sms-api/log/run b/termux-sms/services/sms-api/log/run
new file mode 100644
index 00000000..5b22e372
--- /dev/null
+++ b/termux-sms/services/sms-api/log/run
@@ -0,0 +1,8 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# runit log service for sms-api
+# Captures stdout/stderr via svlogd (automatic rotation)
+#
+
+mkdir -p "$HOME/logs/sms-api-sv"
+exec svlogd -tt "$HOME/logs/sms-api-sv"
diff --git a/termux-sms/services/sms-api/run b/termux-sms/services/sms-api/run
new file mode 100644
index 00000000..e2c18bbd
--- /dev/null
+++ b/termux-sms/services/sms-api/run
@@ -0,0 +1,19 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# runit service: sms-api
+# Runs the Flask SMS API server under termux-services supervision.
+# Install: ln -s ~/sms-server/termux-sms/services/sms-api $PREFIX/var/service/
+#
+
+# Source environment (API key, etc.)
+[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"
+
+# Ensure log directory exists
+mkdir -p "$HOME/logs"
+
+# Acquire wake lock (idempotent โ safe to call multiple times)
+termux-wake-lock 2>/dev/null
+
+# exec replaces the shell with python so runit tracks the real PID
+exec python "$HOME/sms-server/termux-sms/termux-sms-api-server.py" \
+ >> "$HOME/logs/sms-api.log" 2>&1
diff --git a/termux-sms/services/sshd/run b/termux-sms/services/sshd/run
new file mode 100644
index 00000000..101ca0e6
--- /dev/null
+++ b/termux-sms/services/sshd/run
@@ -0,0 +1,19 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# runit service: sshd
+# Keeps SSH daemon running so the server can manage the phone remotely.
+# Install: ln -s ~/sms-server/termux-sms/services/sshd $PREFIX/var/service/
+#
+
+# Generate host keys if missing
+if [ ! -f "$PREFIX/etc/ssh/ssh_host_rsa_key" ]; then
+ ssh-keygen -A
+fi
+
+# Kill any standalone sshd first so we can bind the port
+# (only kills the listener, not active sessions)
+pkill -x sshd 2>/dev/null
+sleep 1
+
+# sshd -D = foreground (required for runit), -e = log to stderr
+exec sshd -D -e 2>&1
diff --git a/termux-sms/setup-api-key.sh b/termux-sms/setup-api-key.sh
new file mode 100755
index 00000000..54a8e86c
--- /dev/null
+++ b/termux-sms/setup-api-key.sh
@@ -0,0 +1,151 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# Termux SMS API Server - Security Setup Script
+# Generates and configures the required SMS_API_SECRET environment variable
+#
+
+# Color codes for pretty output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+CYAN='\033[0;36m'
+BOLD='\033[1m'
+NC='\033[0m' # No Color
+
+# Banner
+clear
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo -e "${CYAN}โ${NC} ${BOLD}๐ Termux SMS API Server - Security Setup${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo ""
+
+# Check if Python is available
+if ! command -v python &> /dev/null; then
+ echo -e "${RED}โ ERROR: Python is not installed${NC}"
+ echo -e "${YELLOW}Please install Python: pkg install python${NC}"
+ exit 1
+fi
+
+echo -e "${BLUE}๐ Step 1: Generating secure API key...${NC}"
+echo ""
+
+# Generate the API key
+API_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
+
+if [ -z "$API_KEY" ]; then
+ echo -e "${RED}โ Failed to generate API key${NC}"
+ exit 1
+fi
+
+echo -e "${GREEN}โ
Secure API key generated successfully!${NC}"
+echo ""
+
+# Determine shell config file
+SHELL_RC=""
+if [ -f "$HOME/.bashrc" ]; then
+ SHELL_RC="$HOME/.bashrc"
+elif [ -f "$HOME/.zshrc" ]; then
+ SHELL_RC="$HOME/.zshrc"
+else
+ SHELL_RC="$HOME/.bashrc"
+ touch "$SHELL_RC"
+fi
+
+echo -e "${BLUE}๐ Step 2: Saving to ${SHELL_RC}...${NC}"
+echo ""
+
+# Check if SMS_API_SECRET already exists in config
+if grep -q "SMS_API_SECRET" "$SHELL_RC" 2>/dev/null; then
+ echo -e "${YELLOW}โ ๏ธ SMS_API_SECRET already exists in ${SHELL_RC}${NC}"
+ echo -e "${YELLOW}Do you want to replace it? (y/n)${NC}"
+ read -r response
+ if [[ "$response" =~ ^[Yy]$ ]]; then
+ # Remove old entry
+ sed -i '/export SMS_API_SECRET=/d' "$SHELL_RC"
+ echo "export SMS_API_SECRET=\"$API_KEY\"" >> "$SHELL_RC"
+ echo -e "${GREEN}โ
Updated existing API key${NC}"
+ else
+ echo -e "${YELLOW}โญ๏ธ Skipping - keeping existing key${NC}"
+ # Use existing key for display
+ API_KEY=$(grep "SMS_API_SECRET" "$SHELL_RC" | cut -d'"' -f2)
+ fi
+else
+ # Add new entry
+ echo "" >> "$SHELL_RC"
+ echo "# Termux SMS API Server Authentication" >> "$SHELL_RC"
+ echo "export SMS_API_SECRET=\"$API_KEY\"" >> "$SHELL_RC"
+ echo -e "${GREEN}โ
API key saved to ${SHELL_RC}${NC}"
+fi
+
+echo ""
+echo -e "${BLUE}๐ Step 3: Activating in current session...${NC}"
+echo ""
+
+# Export for current session
+export SMS_API_SECRET="$API_KEY"
+
+echo -e "${GREEN}โ
API key activated${NC}"
+echo ""
+
+# Display summary box
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo -e "${CYAN}โ${NC} ${BOLD}๐ Setup Complete!${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ${NC}"
+echo -e "${CYAN}โ${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${BOLD}Your API Key:${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${GREEN}${API_KEY}${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${BOLD}Saved to:${NC} ${YELLOW}${SHELL_RC}${NC}"
+# Pad the line to align with box
+printf "${CYAN}โ${NC}\n"
+echo -e "${CYAN}โ${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${BOLD}Status:${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${GREEN}โ
Active in current session${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${GREEN}โ
Will persist across restarts${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โ${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo ""
+
+# Next steps
+echo -e "${BOLD}๐ Next Steps:${NC}"
+echo ""
+echo -e " ${YELLOW}1.${NC} ${BOLD}Copy this key to your Ubuntu homelab .env file:${NC}"
+echo -e " ${CYAN}TERMUX_API_KEY=${API_KEY}${NC}"
+echo -e " ${CYAN}SMS_API_SECRET=${API_KEY}${NC}"
+echo ""
+echo -e " ${YELLOW}2.${NC} ${BOLD}Start the SMS API server:${NC}"
+echo -e " ${CYAN}python ~/projects/sms-campaign-manager/android/termux-sms-api-server.py${NC}"
+echo ""
+echo -e " ${YELLOW}3.${NC} ${BOLD}Or use the service manager:${NC}"
+echo -e " ${CYAN}~/bin/sms-service.sh start${NC}"
+echo ""
+
+# Verification section
+echo -e "${BOLD}๐ Verification:${NC}"
+echo ""
+echo -e " Check if key is set: ${CYAN}echo \$SMS_API_SECRET${NC}"
+echo -e " Expected output: ${GREEN}${API_KEY}${NC}"
+echo ""
+
+# Test the environment variable
+CURRENT_VALUE="${SMS_API_SECRET}"
+if [ "$CURRENT_VALUE" == "$API_KEY" ]; then
+ echo -e "${GREEN}โ
Verification passed - API key is correctly set!${NC}"
+else
+ echo -e "${YELLOW}โ ๏ธ Note: New terminal sessions will have the key automatically loaded${NC}"
+fi
+
+echo ""
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo -e "${GREEN}${BOLD} Setup complete! Your SMS API server is now secure. ๐${NC}"
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo ""
+
+# Optional: Save key to a file for easy copying (secured with permissions)
+KEY_FILE="$HOME/.sms-api-key"
+echo "$API_KEY" > "$KEY_FILE"
+chmod 600 "$KEY_FILE"
+
+echo -e "${BLUE}๐พ API key also saved to: ${KEY_FILE} (readable only by you)${NC}"
+echo ""
diff --git a/termux-sms/setup-services.sh b/termux-sms/setup-services.sh
new file mode 100644
index 00000000..aef5422e
--- /dev/null
+++ b/termux-sms/setup-services.sh
@@ -0,0 +1,94 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# One-time setup: install termux-services and register sms-api + sshd
+# Run this ON THE PHONE in Termux.
+#
+
+set -e
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+CYAN='\033[0;36m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo -e "${CYAN}โ${NC} ${BOLD}SMS Server โ Service Setup${NC} ${CYAN}โ${NC}"
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo ""
+
+# 1. Install termux-services (provides runit supervisor)
+echo -e "${YELLOW}[1/5]${NC} Installing termux-services..."
+pkg install -y termux-services 2>/dev/null || {
+ echo -e "${RED}Failed to install termux-services${NC}"
+ exit 1
+}
+echo -e "${GREEN} OK${NC}"
+
+# 2. Source sv helper (adds sv command to PATH)
+echo -e "${YELLOW}[2/5]${NC} Loading service helpers..."
+source "$PREFIX/etc/profile.d/start-services.sh" 2>/dev/null || true
+echo -e "${GREEN} OK${NC}"
+
+# 3. Make run scripts executable
+echo -e "${YELLOW}[3/5]${NC} Setting permissions..."
+SVC_DIR="$HOME/sms-server/termux-sms/services"
+chmod +x "$SVC_DIR/sms-api/run"
+chmod +x "$SVC_DIR/sms-api/log/run"
+chmod +x "$SVC_DIR/sshd/run"
+echo -e "${GREEN} OK${NC}"
+
+# 4. Symlink services into runit service directory
+echo -e "${YELLOW}[4/5]${NC} Registering services with runit..."
+mkdir -p "$PREFIX/var/service"
+
+# Remove old symlinks if they exist
+rm -f "$PREFIX/var/service/sms-api" 2>/dev/null
+rm -f "$PREFIX/var/service/sshd-custom" 2>/dev/null
+
+ln -s "$SVC_DIR/sms-api" "$PREFIX/var/service/sms-api"
+ln -s "$SVC_DIR/sshd" "$PREFIX/var/service/sshd-custom"
+echo -e "${GREEN} OK${NC}"
+
+# 5. Kill old watchdog and standalone processes
+echo -e "${YELLOW}[5/5]${NC} Cleaning up old processes..."
+pkill -f "sms-watchdog.sh" 2>/dev/null || true
+pkill -f "termux-sms-api-server.py" 2>/dev/null || true
+# Don't kill sshd โ runit will take it over
+echo -e "${GREEN} OK${NC}"
+
+# Acquire wake lock
+termux-wake-lock 2>/dev/null
+
+echo ""
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo -e "${GREEN}${BOLD} Setup complete!${NC}"
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo ""
+echo -e "${BOLD}Services registered:${NC}"
+echo -e " ${GREEN}sms-api${NC} โ Flask SMS API server (port 5001)"
+echo -e " ${GREEN}sshd-custom${NC} โ SSH daemon (port 8022)"
+echo ""
+echo -e "${BOLD}Management commands:${NC}"
+echo -e " ${CYAN}sv status sms-api${NC} โ Check status"
+echo -e " ${CYAN}sv restart sms-api${NC} โ Restart"
+echo -e " ${CYAN}sv down sms-api${NC} โ Stop"
+echo -e " ${CYAN}sv up sms-api${NC} โ Start"
+echo -e " ${CYAN}cat ~/logs/sms-api.log${NC} โ View logs"
+echo ""
+echo -e "${BOLD}runit will automatically restart services if they crash.${NC}"
+echo ""
+
+# Wait a moment for runit to pick up the new services
+sleep 3
+
+# Show status
+echo -e "${BOLD}Current status:${NC}"
+sv status sms-api 2>/dev/null || echo " sms-api: starting..."
+sv status sshd-custom 2>/dev/null || echo " sshd-custom: starting..."
+
+echo ""
+echo -e "${BOLD}Health check:${NC}"
+sleep 2
+curl -s http://127.0.0.1:5001/health && echo "" || echo -e "${YELLOW} Server still starting โ wait a few seconds and retry${NC}"
diff --git a/termux-sms/setup.sh b/termux-sms/setup.sh
new file mode 100755
index 00000000..45a54b62
--- /dev/null
+++ b/termux-sms/setup.sh
@@ -0,0 +1,193 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# Changemaker SMS Server โ One-Command Phone Setup
+#
+# Usage:
+# bash setup.sh YOUR_API_KEY
+#
+# This script:
+# 1. Installs required packages (python, termux-api, flask)
+# 2. Saves the API key to ~/.bashrc
+# 3. Requests SMS & Contacts permissions
+# 4. Sets up Termux:Boot auto-start (if installed)
+# 5. Starts the SMS server
+#
+
+set -e
+
+# --- Colors ---
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+CYAN='\033[0;36m'
+BOLD='\033[1m'
+NC='\033[0m'
+
+step() { echo -e "\n${CYAN}[$1/6]${NC} ${BOLD}$2${NC}"; }
+ok() { echo -e " ${GREEN}โ${NC} $1"; }
+warn() { echo -e " ${YELLOW}!${NC} $1"; }
+fail() { echo -e " ${RED}โ${NC} $1"; exit 1; }
+
+# --- Banner ---
+echo ""
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo -e "${BOLD} Changemaker SMS Server โ Phone Setup${NC}"
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+
+# --- Validate API key ---
+API_KEY="$1"
+if [ -z "$API_KEY" ]; then
+ echo ""
+ echo -e "${RED}Usage: bash setup.sh YOUR_API_KEY${NC}"
+ echo ""
+ echo "Get the API key from the admin dashboard:"
+ echo " SMS Setup โ Step 3 โ Generate API Key โ Copy"
+ exit 1
+fi
+
+if [ ${#API_KEY} -lt 32 ]; then
+ fail "API key looks too short (expected 64 hex characters). Check you copied the full key."
+fi
+
+# --- Step 1: Install packages ---
+step 1 "Installing packages"
+
+pkg update -y 2>/dev/null || true
+pkg install -y python git termux-api openssh 2>/dev/null
+
+if ! command -v python &>/dev/null; then
+ fail "Python failed to install"
+fi
+ok "python, git, termux-api, openssh installed"
+
+if ! pip show flask &>/dev/null; then
+ pip install flask 2>/dev/null
+fi
+ok "Flask installed"
+
+# --- Step 2: Save API key ---
+step 2 "Saving API key"
+
+export SMS_API_SECRET="$API_KEY"
+
+SHELL_RC="$HOME/.bashrc"
+[ -f "$HOME/.zshrc" ] && ! [ -f "$HOME/.bashrc" ] && SHELL_RC="$HOME/.zshrc"
+
+# Remove any existing SMS_API_SECRET lines
+if grep -q "SMS_API_SECRET" "$SHELL_RC" 2>/dev/null; then
+ sed -i '/SMS_API_SECRET/d' "$SHELL_RC"
+ warn "Replaced existing API key"
+fi
+
+echo "" >> "$SHELL_RC"
+echo "# Changemaker SMS Server" >> "$SHELL_RC"
+echo "export SMS_API_SECRET=\"$API_KEY\"" >> "$SHELL_RC"
+ok "Key saved to $SHELL_RC"
+ok "Key active in current session"
+
+# --- Step 3: Grant permissions ---
+step 3 "Requesting Android permissions"
+echo -e " ${YELLOW}Tap 'Allow' if Android shows permission prompts${NC}"
+
+# These trigger permission dialogs; capture output to suppress noise
+termux-sms-list -l 1 >/dev/null 2>&1 && ok "SMS permission granted" || warn "SMS permission โ check Android Settings โ Termux:API โ Permissions"
+termux-contact-list >/dev/null 2>&1 && ok "Contacts permission granted" || warn "Contacts permission โ check Android Settings โ Termux:API โ Permissions"
+termux-battery-status >/dev/null 2>&1 && ok "Battery status accessible" || true
+
+# --- Step 4: Create logs directory ---
+step 4 "Setting up directories"
+
+mkdir -p ~/logs
+ok "~/logs created"
+
+# --- Step 5: Install service supervisor ---
+step 5 "Installing termux-services (runit)"
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+pkg install -y termux-services 2>/dev/null
+ok "termux-services (runit) installed"
+
+# Source runit helpers
+source "$PREFIX/etc/profile.d/start-services.sh" 2>/dev/null || true
+
+# Make run scripts executable
+chmod +x "$SCRIPT_DIR/services/sms-api/run" 2>/dev/null || true
+chmod +x "$SCRIPT_DIR/services/sms-api/log/run" 2>/dev/null || true
+chmod +x "$SCRIPT_DIR/services/sshd/run" 2>/dev/null || true
+
+# Register services with runit
+mkdir -p "$PREFIX/var/service"
+rm -f "$PREFIX/var/service/sms-api" 2>/dev/null
+rm -f "$PREFIX/var/service/sshd-custom" 2>/dev/null
+ln -s "$SCRIPT_DIR/services/sms-api" "$PREFIX/var/service/sms-api"
+ln -s "$SCRIPT_DIR/services/sshd" "$PREFIX/var/service/sshd-custom"
+ok "sms-api and sshd-custom services registered"
+
+# Set up Termux:Boot auto-start
+if dpkg -l 2>/dev/null | grep -q termux-boot 2>/dev/null || [ -d "$HOME/.termux/boot" ]; then
+ mkdir -p ~/.termux/boot
+ cp "$SCRIPT_DIR/termux-boot-start.sh" ~/.termux/boot/start-sms-server 2>/dev/null || {
+ cat > ~/.termux/boot/start-sms-server << 'BOOT'
+#!/data/data/com.termux/files/usr/bin/bash
+termux-wake-lock
+[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"
+. "$PREFIX/etc/profile.d/start-services.sh" 2>/dev/null || true
+BOOT
+ }
+ chmod +x ~/.termux/boot/start-sms-server
+ ok "Boot script installed (auto-start on reboot)"
+else
+ warn "Termux:Boot not detected โ install from F-Droid for auto-start on reboot"
+fi
+
+# --- Step 6: Start the server ---
+step 6 "Starting SMS server"
+
+# Kill any old watchdog/standalone instances
+pkill -f sms-watchdog.sh 2>/dev/null || true
+pkill -f termux-sms-api-server.py 2>/dev/null || true
+sleep 1
+
+# Acquire wake lock
+termux-wake-lock 2>/dev/null || true
+
+# runit auto-starts services โ just wait for it
+sleep 3
+
+# Check if it's running
+if curl -s --max-time 5 http://127.0.0.1:5001/health >/dev/null 2>&1; then
+ ok "Server is running via runit!"
+ sv status sms-api 2>/dev/null || true
+else
+ warn "Server starting up... check: sv status sms-api"
+fi
+
+# --- Summary ---
+echo ""
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo -e "${GREEN}${BOLD} Setup complete!${NC}"
+echo -e "${CYAN}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${NC}"
+echo ""
+
+# Get device IP
+DEVICE_IP=$(ip -4 addr show 2>/dev/null | grep -oP '(?<=inet\s)(?!127\.)\d+\.\d+\.\d+\.\d+' | head -1)
+if [ -n "$DEVICE_IP" ]; then
+ echo -e " Phone URL: ${BOLD}http://${DEVICE_IP}:5001${NC}"
+fi
+echo -e " Health: ${BOLD}http://127.0.0.1:5001/health${NC}"
+echo -e " Status: ${BOLD}sv status sms-api${NC}"
+echo -e " Restart: ${BOLD}sv restart sms-api${NC}"
+echo -e " Logs: ${BOLD}tail -f ~/logs/sms-api.log${NC}"
+echo ""
+echo -e " ${YELLOW}Next:${NC} Go back to the admin dashboard SMS Setup wizard"
+echo -e " and enter this phone's URL in Step 2 (Connect)."
+echo ""
+
+# Show Tailscale IP if available
+TS_IP=$(ip -4 addr show tailscale0 2>/dev/null | grep -oP '(?<=inet\s)\d+\.\d+\.\d+\.\d+' || true)
+if [ -n "$TS_IP" ]; then
+ echo -e " ${GREEN}Tailscale IP: ${BOLD}http://${TS_IP}:5001${NC} (recommended)"
+ echo ""
+fi
diff --git a/termux-sms/sms-service.sh b/termux-sms/sms-service.sh
new file mode 100755
index 00000000..f5de2ace
--- /dev/null
+++ b/termux-sms/sms-service.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# SMS API Service management script
+
+case "$1" in
+ start)
+ echo "Starting SMS API Server..."
+ nohup ~/bin/start-sms-api.sh > ~/logs/sms-api-service.log 2>&1 &
+ echo $! > ~/.sms-api.pid
+ echo "Service started (PID: $(cat ~/.sms-api.pid))"
+ ;;
+ stop)
+ if [ -f ~/.sms-api.pid ]; then
+ PID=$(cat ~/.sms-api.pid)
+ kill $PID 2>/dev/null || true
+ rm -f ~/.sms-api.pid
+ echo "Service stopped"
+ else
+ echo "Service not running"
+ fi
+ ;;
+ status)
+ if [ -f ~/.sms-api.pid ]; then
+ PID=$(cat ~/.sms-api.pid)
+ if kill -0 $PID 2>/dev/null; then
+ echo "Service running (PID: $PID)"
+ else
+ echo "Service dead (stale PID file)"
+ rm -f ~/.sms-api.pid
+ fi
+ else
+ echo "Service not running"
+ fi
+ ;;
+ restart)
+ $0 stop
+ sleep 2
+ $0 start
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|status|restart}"
+ exit 1
+ ;;
+esac
diff --git a/termux-sms/sms-watchdog.sh b/termux-sms/sms-watchdog.sh
new file mode 100755
index 00000000..b60b9b5e
--- /dev/null
+++ b/termux-sms/sms-watchdog.sh
@@ -0,0 +1,78 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# SMS API Server Watchdog
+# Monitors the Flask server and restarts it if it crashes.
+# Run this from ~/.termux/boot/start-sms-server or manually.
+#
+
+SERVER_SCRIPT="$HOME/sms-server/android/termux-sms-api-server.py"
+LOG_DIR="$HOME/logs"
+LOG_FILE="$LOG_DIR/sms-api.log"
+PID_FILE="$LOG_DIR/sms-api.pid"
+CHECK_INTERVAL=30 # seconds between health checks
+
+# Source environment variables (SMS_API_SECRET etc.)
+# Non-interactive shells (nohup, boot scripts) don't source .bashrc automatically
+[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"
+
+mkdir -p "$LOG_DIR"
+
+log() {
+ echo "$(date '+%Y-%m-%d %H:%M:%S') [watchdog] $1" >> "$LOG_FILE"
+ echo "[watchdog] $1"
+}
+
+start_server() {
+ if [ -f "$PID_FILE" ]; then
+ local old_pid
+ old_pid=$(cat "$PID_FILE")
+ if kill -0 "$old_pid" 2>/dev/null; then
+ log "Server already running (PID $old_pid)"
+ return 0
+ fi
+ fi
+
+ log "Starting SMS API server..."
+ nohup python "$SERVER_SCRIPT" >> "$LOG_FILE" 2>&1 &
+ echo $! > "$PID_FILE"
+ log "Server started (PID $!)"
+ sleep 3 # Give it time to start
+}
+
+check_health() {
+ curl -s --max-time 5 http://127.0.0.1:5001/health > /dev/null 2>&1
+ return $?
+}
+
+# Acquire wake lock to prevent Android from killing us
+termux-wake-lock 2>/dev/null
+
+log "Watchdog starting..."
+start_server
+
+# Main watchdog loop
+while true; do
+ sleep "$CHECK_INTERVAL"
+
+ if ! check_health; then
+ log "Health check FAILED โ restarting server"
+
+ # Kill old process if it exists
+ if [ -f "$PID_FILE" ]; then
+ kill "$(cat "$PID_FILE")" 2>/dev/null
+ sleep 2
+ fi
+
+ # Also kill any orphaned instances
+ pkill -f "termux-sms-api-server.py" 2>/dev/null
+ sleep 1
+
+ start_server
+
+ if check_health; then
+ log "Server restarted successfully"
+ else
+ log "Server failed to restart โ will retry in ${CHECK_INTERVAL}s"
+ fi
+ fi
+done
diff --git a/termux-sms/start-all-services.sh b/termux-sms/start-all-services.sh
new file mode 100755
index 00000000..36751fab
--- /dev/null
+++ b/termux-sms/start-all-services.sh
@@ -0,0 +1,32 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# Start all SMS Campaign Manager services via runit (termux-services)
+# Prerequisite: run setup-services.sh first to install and register services
+#
+
+echo "Starting SMS Campaign Manager Services..."
+echo ""
+
+# Acquire wake lock
+termux-wake-lock 2>/dev/null
+
+# Source runit helpers
+source "$PREFIX/etc/profile.d/start-services.sh" 2>/dev/null || true
+
+# Ensure services are up
+sv up sms-api 2>/dev/null && echo " sms-api: started" || echo " sms-api: failed (run setup-services.sh first)"
+sv up sshd-custom 2>/dev/null && echo " sshd-custom: started" || echo " sshd-custom: failed (run setup-services.sh first)"
+
+echo ""
+
+# Wait for startup
+sleep 3
+
+# Status
+echo "Service Status:"
+sv status sms-api 2>/dev/null || echo " sms-api: not registered"
+sv status sshd-custom 2>/dev/null || echo " sshd-custom: not registered"
+
+echo ""
+echo "Health Check:"
+curl -s http://localhost:5001/health > /dev/null && echo " SMS API: Healthy" || echo " SMS API: Not responding"
diff --git a/termux-sms/start-monitoring.sh b/termux-sms/start-monitoring.sh
new file mode 100755
index 00000000..50d5c7c7
--- /dev/null
+++ b/termux-sms/start-monitoring.sh
@@ -0,0 +1,16 @@
+# Monitoring Interface startup script
+
+cd ~/projects/sms-campaign-manager
+
+echo "๐ Starting Monitoring Interface..."
+echo "๐ฑ Device IP: $(ifconfig 2>/dev/null | grep -A1 wlan0 | grep inet | awk '{print $2}' | cut -d: -f2)"
+echo "๐ Monitoring URL: http://$(ifconfig 2>/dev/null | grep -A1 wlan0 | grep inet | awk '{print $2}' | cut -d: -f2):5000"
+
+# Kill any existing monitoring process
+pkill -f "python app.py" 2>/dev/null
+
+# Start the monitoring interface in background
+nohup python app.py > ~/logs/monitoring.log 2>&1 &
+
+echo "โ
Monitoring interface started (PID: $!)"
+echo "๐ Access: http://10.0.0.193:5000"
diff --git a/termux-sms/start-sms-api.sh b/termux-sms/start-sms-api.sh
new file mode 100755
index 00000000..ecf80c0a
--- /dev/null
+++ b/termux-sms/start-sms-api.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# SMS API Server startup script
+
+cd ~/projects/sms-campaign-manager
+
+echo "๐ Starting SMS API Server..."
+echo "๐ฑ Device IP: $(ifconfig 2>/dev/null | grep -A1 wlan0 | grep inet | awk '{print $2}' | cut -d: -f2)"
+echo "๐ API URL: http://$(ifconfig 2>/dev/null | grep -A1 wlan0 | grep inet | awk '{print $2}' | cut -d: -f2):5001"
+
+# Start the server
+python termux-sms-api-server.py
diff --git a/termux-sms/termux-boot-start.sh b/termux-sms/termux-boot-start.sh
new file mode 100644
index 00000000..6f62f55b
--- /dev/null
+++ b/termux-sms/termux-boot-start.sh
@@ -0,0 +1,16 @@
+#!/data/data/com.termux/files/usr/bin/bash
+#
+# Termux:Boot startup script
+# Install: cp this to ~/.termux/boot/start-sms-server
+# Requires: Termux:Boot app from F-Droid
+#
+
+# Prevent Android from killing background processes
+termux-wake-lock
+
+# Source env (API key, PATH, etc.)
+[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"
+
+# Source runit helpers โ this starts the runit supervisor
+# which automatically picks up all services in $PREFIX/var/service/
+. "$PREFIX/etc/profile.d/start-services.sh" 2>/dev/null || true
diff --git a/termux-sms/termux-sms-api-server.py b/termux-sms/termux-sms-api-server.py
new file mode 100644
index 00000000..1f8d388e
--- /dev/null
+++ b/termux-sms/termux-sms-api-server.py
@@ -0,0 +1,746 @@
+#!/usr/bin/env python3
+"""
+Termux SMS API Server
+Bridges SMS Campaign Manager (Ubuntu) with Termux API (Android)
+
+This server runs on the Android device in Termux and provides REST API
+endpoints for the main SMS Campaign Manager to send messages using
+native Android SMS capabilities instead of ADB automation.
+
+All endpoints require API key authentication via X-API-Key header.
+Localhost requests (127.0.0.1, ::1) are exempt for watchdog health checks.
+"""
+
+from flask import Flask, request, jsonify
+import subprocess
+import json
+import time
+import logging
+import hmac
+import hashlib
+import os
+from datetime import datetime
+from typing import Dict, List, Optional, Any
+
+# Configure logging โ ensure log directory exists before creating FileHandler
+_log_dir = os.path.expanduser('~/logs')
+os.makedirs(_log_dir, exist_ok=True)
+
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(levelname)s - %(message)s',
+ handlers=[
+ logging.FileHandler(os.path.join(_log_dir, 'sms-api.log')),
+ logging.StreamHandler()
+ ]
+)
+logger = logging.getLogger(__name__)
+
+app = Flask(__name__)
+
+# Configuration
+CONFIG = {
+ 'SECRET_KEY': os.environ.get('SMS_API_SECRET') or os.environ.get('TERMUX_API_KEY', ''),
+ 'MAX_MESSAGE_LENGTH': 1600, # Increased from 160 to support longer messages (SMS can be up to 1600 chars)
+ 'RATE_LIMIT_DELAY': 1.0, # Reduced from 2.0 to 1.0 seconds between messages for faster campaigns
+ 'ALLOWED_COMMANDS': [
+ 'termux-sms-send',
+ 'termux-sms-list',
+ 'termux-battery-status',
+ 'termux-location',
+ 'termux-notification',
+ 'termux-contact-list'
+ ]
+}
+
+class SMSApiServer:
+ """Main SMS API server class"""
+
+ def __init__(self):
+ self.last_send_time = 0
+ self.message_count = 0
+ self.start_time = time.time()
+
+ def authenticate_request(self, request_data: str, signature: str) -> bool:
+ """Verify HMAC signature for request authentication"""
+ try:
+ expected_signature = hmac.new(
+ CONFIG['SECRET_KEY'].encode(),
+ request_data.encode(),
+ hashlib.sha256
+ ).hexdigest()
+ return hmac.compare_digest(signature, expected_signature)
+ except Exception as e:
+ logger.error(f"Authentication error: {e}")
+ return False
+
+ def execute_termux_command(self, command: List[str]) -> Dict[str, Any]:
+ """Execute Termux API command with error handling"""
+ if not command or command[0] not in CONFIG['ALLOWED_COMMANDS']:
+ return {'success': False, 'error': 'Command not allowed'}
+
+ try:
+ logger.info(f"Executing: {' '.join(command)}")
+ result = subprocess.run(
+ command,
+ capture_output=True,
+ text=True,
+ timeout=30
+ )
+
+ return {
+ 'success': result.returncode == 0,
+ 'stdout': result.stdout.strip(),
+ 'stderr': result.stderr.strip(),
+ 'return_code': result.returncode
+ }
+ except subprocess.TimeoutExpired:
+ return {'success': False, 'error': 'Command timeout'}
+ except Exception as e:
+ return {'success': False, 'error': str(e)}
+
+ def get_sms_history(self, phone: Optional[str] = None, limit: int = 100) -> Dict[str, Any]:
+ """Get SMS history for a specific phone number or all messages"""
+ try:
+ command = ['termux-sms-list']
+ if limit:
+ command.extend(['-l', str(limit)])
+
+ result = self.execute_termux_command(command)
+
+ if result['success'] and result['stdout']:
+ try:
+ messages = json.loads(result['stdout'])
+
+ # Filter by phone number if specified
+ if phone:
+ # Clean phone number for comparison
+ clean_phone = phone.replace('+', '').replace('-', '').replace(' ', '').replace('(', '').replace(')', '')
+ messages = [msg for msg in messages
+ if msg.get('number', '').replace('+', '').replace('-', '').replace(' ', '').replace('(', '').replace(')', '') == clean_phone]
+
+ return {
+ 'success': True,
+ 'messages': messages,
+ 'count': len(messages)
+ }
+ except json.JSONDecodeError:
+ return {'success': False, 'error': 'Failed to parse SMS data'}
+
+ return {'success': False, 'error': 'Failed to retrieve SMS history'}
+ except Exception as e:
+ logger.error(f"Error getting SMS history: {e}")
+ return {'success': False, 'error': str(e)}
+
+ def get_contact_name(self, phone: str) -> Optional[str]:
+ """Get contact name from phone's contact list"""
+ try:
+ # Use termux-contact-list command if available
+ result = self.execute_termux_command(['termux-contact-list'])
+
+ if result['success'] and result['stdout']:
+ try:
+ contacts = json.loads(result['stdout'])
+ clean_phone = phone.replace('+', '').replace('-', '').replace(' ', '').replace('(', '').replace(')', '')
+
+ for contact in contacts:
+ if 'phoneNumbers' in contact:
+ for phone_entry in contact['phoneNumbers']:
+ contact_phone = phone_entry.get('number', '').replace('+', '').replace('-', '').replace(' ', '').replace('(', '').replace(')', '')
+ if contact_phone == clean_phone:
+ return contact.get('name')
+ except json.JSONDecodeError:
+ pass
+
+ return None
+ except Exception as e:
+ logger.error(f"Error getting contact name for {phone}: {e}")
+ return None
+
+ def rate_limit_check(self) -> bool:
+ """Check if enough time has passed since last message"""
+ current_time = time.time()
+ if current_time - self.last_send_time < CONFIG['RATE_LIMIT_DELAY']:
+ return False
+ self.last_send_time = current_time
+ return True
+
+ def send_sms(self, phone: str, message: str) -> Dict[str, Any]:
+ """Send SMS using Termux API"""
+ # Input validation
+ if not phone or not message:
+ error_msg = 'Phone and message required'
+ logger.error(f"SMS validation failed: {error_msg}")
+ return {'success': False, 'error': error_msg}
+
+ # Clean phone number
+ clean_phone = phone.replace('+', '').replace('-', '').replace(' ', '').replace('(', '').replace(')', '')
+
+ if len(message) > CONFIG['MAX_MESSAGE_LENGTH']:
+ error_msg = f'Message too long ({len(message)} chars, max {CONFIG["MAX_MESSAGE_LENGTH"]})'
+ logger.error(f"SMS validation failed: {error_msg}")
+ return {'success': False, 'error': error_msg}
+
+ # Rate limiting
+ if not self.rate_limit_check():
+ error_msg = 'Rate limit exceeded, please wait'
+ logger.warning(f"SMS rate limited for {phone}")
+ return {'success': False, 'error': error_msg}
+
+ # Log the SMS attempt
+ logger.info(f"Attempting to send SMS to {phone} (length: {len(message)} chars)")
+
+ # Execute SMS send command
+ command = ['termux-sms-send', '-n', clean_phone, message]
+ result = self.execute_termux_command(command)
+
+ if result['success']:
+ self.message_count += 1
+ logger.info(f"SMS sent successfully to {phone}: {message[:50]}...")
+
+ # Send confirmation notification
+ self.execute_termux_command([
+ 'termux-notification',
+ '--title', 'SMS Sent',
+ '--content', f'Message sent to {phone}'
+ ])
+ else:
+ logger.error(f"SMS send failed to {phone}: {result.get('error', 'Unknown error')}")
+
+ return {
+ 'success': result['success'],
+ 'error': result.get('error') or result.get('stderr'),
+ 'timestamp': datetime.now().isoformat(),
+ 'phone': phone,
+ 'message_length': len(message),
+ 'total_sent': self.message_count
+ }
+
+# Global server instance
+sms_server = SMSApiServer()
+
+def verify_api_key():
+ """Verify API key from request headers"""
+ api_key = request.headers.get('X-API-Key') or request.headers.get('Authorization', '').replace('Bearer ', '')
+ expected_key = CONFIG['SECRET_KEY']
+
+ if not api_key:
+ return False
+
+ if not hmac.compare_digest(api_key, expected_key):
+ return False
+
+ return True
+
+# ---------------------------------------------------------------------------
+# Global auth: every request from a remote client must provide a valid API key.
+# Localhost (127.0.0.1 / ::1) is exempt so the watchdog health check works.
+# ---------------------------------------------------------------------------
+@app.before_request
+def require_api_key():
+ """Enforce API key authentication on all endpoints for remote clients."""
+ if request.remote_addr in ('127.0.0.1', '::1'):
+ return None # allow localhost (watchdog health checks)
+
+ if not verify_api_key():
+ logger.warning(f"Auth failed: {request.method} {request.path} from {request.remote_addr}")
+ return jsonify({
+ 'success': False,
+ 'error': 'Authentication required',
+ 'message': 'Please provide valid API key via X-API-Key header'
+ }), 401
+
+ return None
+
+# API Endpoints
+
+@app.route('/health', methods=['GET'])
+def health_check():
+ """Health check endpoint"""
+ uptime = time.time() - sms_server.start_time
+
+ return jsonify({
+ 'status': 'healthy',
+ 'timestamp': datetime.now().isoformat(),
+ 'uptime_seconds': int(uptime),
+ 'messages_sent': sms_server.message_count,
+ 'version': '1.0.0'
+ })
+
+@app.route('/api/sms/send', methods=['POST'])
+def send_sms():
+ """Send SMS message via Termux API"""
+ try:
+ data = request.get_json()
+ if not data:
+ logger.error("SMS send endpoint: No JSON data provided")
+ return jsonify({'success': False, 'error': 'JSON data required'}), 400
+
+ # Extract parameters
+ phone = data.get('phone', '').strip()
+ message = data.get('message', '').strip()
+ name = data.get('name', '')
+
+ logger.info(f"SMS send request: phone={phone}, name={name}, message_length={len(message) if message else 0}")
+
+ # Validate required fields
+ if not phone:
+ logger.error("SMS send validation: Missing phone number")
+ return jsonify({'success': False, 'error': 'Phone number required'}), 400
+
+ if not message:
+ logger.error("SMS send validation: Missing message")
+ return jsonify({'success': False, 'error': 'Message required'}), 400
+
+ # Message template substitution (like existing ui.sh)
+ if name and '{name}' in message:
+ message = message.replace('{name}', name)
+
+ # Send SMS
+ result = sms_server.send_sms(phone, message)
+
+ status_code = 200 if result['success'] else 400
+ logger.info(f"SMS send result: success={result['success']}, error={result.get('error', 'None')}")
+ return jsonify(result), status_code
+
+ except Exception as e:
+ logger.error(f"SMS send endpoint error: {e}")
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/sms/list', methods=['GET'])
+def list_sms():
+ """List SMS messages"""
+ try:
+ limit = request.args.get('limit', '10')
+ offset = request.args.get('offset', '0')
+
+ command = ['termux-sms-list', '-l', limit, '-o', offset]
+ result = sms_server.execute_termux_command(command)
+
+ if result['success']:
+ try:
+ sms_data = json.loads(result['stdout']) if result['stdout'] else []
+ return jsonify({
+ 'success': True,
+ 'messages': sms_data,
+ 'count': len(sms_data)
+ })
+ except json.JSONDecodeError:
+ return jsonify({
+ 'success': True,
+ 'messages': result['stdout'],
+ 'raw_output': True
+ })
+ else:
+ return jsonify({'success': False, 'error': result['error']}), 400
+
+ except Exception as e:
+ logger.error(f"SMS list error: {e}")
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/device/battery', methods=['GET'])
+def get_battery_status():
+ """Get device battery status"""
+ try:
+ result = sms_server.execute_termux_command(['termux-battery-status'])
+
+ if result['success']:
+ battery_data = json.loads(result['stdout'])
+ return jsonify({
+ 'success': True,
+ 'battery': battery_data,
+ 'timestamp': datetime.now().isoformat()
+ })
+ else:
+ return jsonify({'success': False, 'error': result['error']}), 400
+
+ except Exception as e:
+ logger.error(f"Battery status error: {e}")
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/device/location', methods=['GET'])
+def get_location():
+ """Get device GPS location"""
+ try:
+ result = sms_server.execute_termux_command(['termux-location'])
+
+ if result['success'] and result['stdout']:
+ try:
+ location_data = json.loads(result['stdout'])
+ return jsonify({
+ 'success': True,
+ 'location': location_data,
+ 'timestamp': datetime.now().isoformat()
+ })
+ except json.JSONDecodeError:
+ return jsonify({
+ 'success': True,
+ 'location': result['stdout'],
+ 'raw_output': True
+ })
+ else:
+ return jsonify({'success': False, 'error': 'Location unavailable'}), 400
+
+ except Exception as e:
+ logger.error(f"Location error: {e}")
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/device/info', methods=['GET'])
+def get_device_info():
+ """Get general device information"""
+ try:
+ # Get multiple device stats
+ battery_result = sms_server.execute_termux_command(['termux-battery-status'])
+
+ info = {
+ 'timestamp': datetime.now().isoformat(),
+ 'uptime': time.time() - sms_server.start_time,
+ 'messages_sent': sms_server.message_count,
+ 'api_version': '1.0.0',
+ 'termux_api_available': True
+ }
+
+ if battery_result['success']:
+ try:
+ battery_data = json.loads(battery_result['stdout'])
+ info['battery'] = battery_data
+ except json.JSONDecodeError:
+ pass
+
+ return jsonify({'success': True, 'device_info': info})
+
+ except Exception as e:
+ logger.error(f"Device info error: {e}")
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/sms/history', methods=['GET'])
+def get_sms_history():
+ """Get SMS history for conversation sync"""
+ try:
+ phone = request.args.get('phone')
+ limit = request.args.get('limit', 100, type=int)
+
+ result = sms_server.get_sms_history(phone, limit)
+
+ if result['success']:
+ return jsonify(result)
+ else:
+ return jsonify(result), 400
+
+ except Exception as e:
+ logger.error(f"SMS history error: {e}")
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/sms/inbox', methods=['GET'])
+def get_sms_inbox():
+ """Get SMS inbox messages (compatibility endpoint)"""
+ try:
+ since = request.args.get('since', type=int)
+ limit = request.args.get('limit', 100, type=int)
+
+ result = sms_server.get_sms_history(None, limit)
+
+ if result['success']:
+ messages = result['messages']
+
+ # Filter by timestamp if 'since' parameter provided
+ if since:
+ filtered_messages = []
+ for msg in messages:
+ # Convert received timestamp to compare with since
+ try:
+ msg_time = datetime.strptime(msg.get('received', ''), '%Y-%m-%d %H:%M:%S').timestamp()
+ if msg_time > since:
+ filtered_messages.append(msg)
+ except:
+ # If timestamp parsing fails, include the message
+ filtered_messages.append(msg)
+ messages = filtered_messages
+
+ return jsonify({
+ 'success': True,
+ 'messages': messages,
+ 'count': len(messages)
+ })
+ else:
+ return jsonify(result), 400
+
+ except Exception as e:
+ logger.error(f"SMS inbox error: {e}")
+ return jsonify({'success': False, 'error': str(e)}), 500
+
+@app.route('/api/contact/