Skip to content

Commit

Permalink
Add internet detector utility
Browse files Browse the repository at this point in the history
Co-authored-by: Ana Martinez Gomez <[email protected]>
  • Loading branch information
emtuls and Ana06 committed Aug 28, 2024
1 parent 5efa360 commit c00bdd0
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 0 deletions.
13 changes: 13 additions & 0 deletions packages/internet_detector.vm/internet_detector.vm.nuspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>internet_detector.vm</id>
<version>1.0.0</version>
<authors>Elliot Chernofsky and Ana Martinez Gomez</authors>
<description>Tool to detect if internet connectivity exists</description>
<dependencies>
<dependency id="common.vm" version="0.0.0.20240821" />
<dependency id="python3" version="0.0.0.20231019" />
</dependencies>
</metadata>
</package>
33 changes: 33 additions & 0 deletions packages/internet_detector.vm/tools/chocolateyinstall.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
$ErrorActionPreference = 'Stop'
Import-Module vm.common -Force -DisableNameChecking

$toolName = 'internet_detector'
$category = 'Utilities'

# Install dependency for windows api
VM-Pip-Install "pywin32"

# Create tool directory
$toolDir = Join-Path ${Env:RAW_TOOLS_DIR} $toolName
New-Item -Path $toolDir -ItemType Directory -Force -ea 0
VM-Assert-Path $toolDir


$packageToolDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"

# Move images to common directory
$imagesPath = Join-Path $packageToolDir "Images"
Move-Item "$imagesPath\*" ${Env:VM_COMMON_DIR} -Force

# Get path to pythonw for executing python without terminal popup
$executablePath = (Get-Command pythonw).Source
$filePath = Join-Path $packageToolDir "$toolName.pyw"

VM-Install-Shortcut $toolName $category $executablePath -arguments $filePath

# Define the action to be performed
$action = New-ScheduledTaskAction -Execute $executablePath -Argument "$filePath"
# Define the trigger to run every 2 minutes
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 2)
# Create the scheduled task
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName 'Internet Detector' -Force
8 changes: 8 additions & 0 deletions packages/internet_detector.vm/tools/chocolateyuninstall.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$ErrorActionPreference = 'Continue'
Import-Module vm.common -Force -DisableNameChecking

$toolName = 'internet_detector'
$category = 'Utilities'

VM-Uninstall $toolName $category
Unregister-ScheduledTask -TaskName 'Internet Detector' -Confirm:$false
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
238 changes: 238 additions & 0 deletions packages/internet_detector.vm/tools/internet_detector.pyw
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import threading
import requests
import win32api
import win32gui
import win32con
import urllib3
import winreg
import signal
import ctypes
import time
import os
import re


# Define constants
CHECK_INTERVAL = 2 # Seconds
CONNECT_TEST_URL_AND_RESPONSES = {
"https://www.msftconnecttest.com/connecttest.txt": "Microsoft Connect Test", # HTTPS Test #1
"http://www.google.com": 'Google', # HTTP Test
"https://www.wikipedia.com": 'Wikipedia', # HTTPS Test #2
"https://www.youtube.com": 'YouTube' # HTTPS Test #3
}
SPI_SETDESKWALLPAPER = 20
SPIF_UPDATEINIFILE = 0x01
SPIF_SENDWININICHANGE = 0x02
COLOR_DESKTOP = 1
ICON_INDICATOR_ON = os.path.join(os.environ.get('VM_COMMON_DIR'), "indicator_on.ico")
ICON_INDICATOR_OFF = os.path.join(os.environ.get('VM_COMMON_DIR'), "indicator_off.ico")
DEFAULT_BACKGROUND = os.path.join(os.environ.get('VM_COMMON_DIR'), "background.png")
INTERNET_BACKGROUND = os.path.join(os.environ.get('VM_COMMON_DIR'),"background-internet.png")

# Global variables
tray_icon = None
stop_event = threading.Event() # To signal the background thread to stop
hwnd = None # We'll assign the window handle here later
check_thread = None
tray_icon_thread = None
# Win32 API icon handles
hicon_indicator_off = None
hicon_indicator_on = None

def signal_handler(sig, frame):
global check_thread, tray_icon_thread, tray_icon
print("Ctrl+C detected. Exiting...")
stop_event.set() # Signal the background thread to stop
if check_thread:
check_thread.join()
if tray_icon_thread:
tray_icon_thread.join()
if tray_icon:
del tray_icon
exit(0)

def load_icon(icon_path):
try:
return win32gui.LoadImage(None, icon_path, win32con.IMAGE_ICON, 0, 0, win32con.LR_LOADFROMFILE)
except Exception as e:
print(f"Error loading indicator icon: {e}")
return None

class SysTrayIcon:
def __init__(self, hwnd, icon, tooltip):
self.hwnd = hwnd
self.icon = icon
self.tooltip = tooltip
# System tray icon data structure
self.nid = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
win32con.WM_USER + 20, self.icon, self.tooltip)
# Add the icon to the system tray
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, self.nid)

def set_tooltip(self, new_tooltip):
self.tooltip = new_tooltip
self.nid = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
win32con.WM_USER + 20, self.icon, self.tooltip)
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self.nid)

def set_icon(self, icon):
self.icon = icon
self.nid = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
win32con.WM_USER + 20, self.icon, self.tooltip)
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self.nid)

def __del__(self):
# Remove the icon from the system tray when the object is destroyed
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, self.nid)

# Attempt to extract a known good value in response.
def extract_title(data):
match = re.search(r'<title>(.*?)</title>', data)
if match:
return match.group(1)
else:
return None

def check_internet():
for url, expected_response in CONNECT_TEST_URL_AND_RESPONSES.items():
try:
# Perform internet connectivity tests
response = requests.get(url, timeout=5, verify=False)
if expected_response in (extract_title(response.text) or response.text):
print(f"Internet connectivity detected via URL: {url}")
return True
except:
pass
return False

def check_internet_and_update_tray_icon():
global tray_icon, hicon_indicator_off, hicon_indicator_on
if check_internet():
tray_icon.set_icon(hicon_indicator_on)
tray_icon.set_tooltip("Internet Connection: Detected")
# Set the background to internet connection background
if get_wallpaper_path() != INTERNET_BACKGROUND: # Checked so program isn't continuously setting the wallpaper
set_wallpaper(INTERNET_BACKGROUND)
else:
tray_icon.set_icon(hicon_indicator_off)
tray_icon.set_tooltip("Internet Connection: Not Detected")
# Reset background when internet is not detected
if get_wallpaper_path() != DEFAULT_BACKGROUND: # Checked so program isn't continuously setting the wallpaper
set_wallpaper(DEFAULT_BACKGROUND)

def check_internet_loop():
while not stop_event.is_set():
check_internet_and_update_tray_icon()
time.sleep(CHECK_INTERVAL)

def tray_icon_loop():
global hwnd, tray_icon, hicon_indicator_off, hicon_indicator_on, stop_event
# Load icons
hicon_indicator_off = load_icon(ICON_INDICATOR_OFF)
hicon_indicator_on = load_icon(ICON_INDICATOR_ON)

# Wait for hwnd to be initialized
while hwnd is None:
time.sleep(0.1)

if hicon_indicator_off is None or hicon_indicator_on is None:
print("Error: Failed to load icons. Exiting TrayIconThread.")
return

tray_icon = SysTrayIcon(hwnd, hicon_indicator_off, "Internet Detector")

while not stop_event.is_set():
msg = win32gui.PeekMessage(hwnd, 0, 0, 0)
if msg and len(msg) == 6:
win32gui.TranslateMessage(msg)
win32gui.DispatchMessage(msg)
time.sleep(0.1)

def enable_transparency_effects():
try:
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
0, winreg.KEY_ALL_ACCESS)

winreg.SetValueEx(key, "EnableTransparency", 0, winreg.REG_DWORD, 1)
winreg.CloseKey(key)
except WindowsError as e:
print(f"Error accessing or modifying registry: {e}")

def get_wallpaper_path():
"""Attempts to retrieve the path to the current wallpaper image."""
# Try to get the path from the registry (for wallpapers set through Windows settings)
try:
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Control Panel\Desktop", 0, winreg.KEY_READ)
value, _ = winreg.QueryValueEx(key, "Wallpaper")
winreg.CloseKey(key)
if value:
return value
except WindowsError:
pass

# Check for cached wallpaper files (if the above fails)
cached_files_dir = os.path.join(os.getenv("APPDATA"), r"Microsoft\Windows\Themes\CachedFiles")
transcoded_wallpaper_path = os.path.join(os.getenv("APPDATA"), r"Microsoft\Windows\Themes\TranscodedWallpaper")

for file in os.listdir(cached_files_dir):
if file.endswith(('.jpg', '.jpeg', '.bmp', '.png')):
return os.path.join(cached_files_dir, file)

if os.path.exists(transcoded_wallpaper_path):
return transcoded_wallpaper_path

# If all else fails, return None
return None

def set_wallpaper(image_path):
"""Sets the desktop wallpaper to the image at the specified path."""
print(f"Setting wallpaper to: {image_path}")
result = ctypes.windll.user32.SystemParametersInfoW(
SPI_SETDESKWALLPAPER, 0, image_path, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE
)
if not result:
print("Error setting wallpaper. Make sure the image path is correct.")

def main_loop():
global stop_event, check_thread, tray_icon_thread, tray_icon
# Create and start the threads
tray_icon_thread = threading.Thread(target=tray_icon_loop)
check_thread = threading.Thread(target=check_internet_loop)

tray_icon_thread.start()
# Wait for the tray icon to finish initializing
while tray_icon is None:
time.sleep(0.1)

check_thread.start()

while not stop_event.is_set():
time.sleep(1)


if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
enable_transparency_effects()

# Create a hidden window to receive messages (required for system tray icons)
def wndProc(hwnd, msg, wparam, lparam):
if lparam == win32con.WM_LBUTTONDBLCLK:
print("Left button double clicked")
elif msg == win32con.WM_COMMAND:
if wparam == 1023: # Example menu item ID
print("Exit selected")
win32gui.DestroyWindow(hwnd)
return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)

wc = win32gui.WNDCLASS()
hinst = wc.hInstance = win32api.GetModuleHandle(None)
wc.lpszClassName = "Internet Detector"
wc.lpfnWndProc = wndProc
classAtom = win32gui.RegisterClass(wc)
hwnd = win32gui.CreateWindow(classAtom, "Internet Detector", 0, 0, 0, 0, 0, 0, 0, hinst, None)

print("Current wallpaper: {}".format(get_wallpaper_path()))

main_loop()

0 comments on commit c00bdd0

Please sign in to comment.