Desktop Notification Reminder

import json

import os

import time

import threading

from datetime import datetime, timedelta

from pathlib import Path


# Try importing notification libraries

try:

    from plyer import notification

    NOTIF_ENGINE = "plyer"

except ImportError:

    NOTIF_ENGINE = None


# Fallback for Windows

if NOTIF_ENGINE is None:

    try:

        from win10toast import ToastNotifier

        NOTIF_ENGINE = "win10toast"

        toaster = ToastNotifier()

    except ImportError:

        NOTIF_ENGINE = None


# ============================================================

# CONFIGURATION

# ============================================================


REMINDERS_FILE = "reminders.json"

APP_NAME       = "Python Reminder"

APP_ICON       = None   # Set path to a .ico file if desired e.g. "icon.ico"


# ============================================================

# LOAD & SAVE REMINDERS

# ============================================================


def load_reminders():

    if Path(REMINDERS_FILE).exists():

        try:

            with open(REMINDERS_FILE, "r") as f:

                return json.load(f)

        except:

            return []

    return []



def save_reminders(reminders):

    with open(REMINDERS_FILE, "w") as f:

        json.dump(reminders, f, indent=2)



# ============================================================

# SEND DESKTOP NOTIFICATION

# ============================================================


def send_notification(title, message, timeout=10):

    """

    Send a desktop pop-up notification.

    Tries plyer first, then win10toast, then terminal fallback.

    """

    print(f"\n  [REMINDER] {title}: {message}")


    if NOTIF_ENGINE == "plyer":

        try:

            notification.notify(

                title=title,

                message=message,

                app_name=APP_NAME,

                app_icon=APP_ICON,

                timeout=timeout

            )

            return True

        except Exception as e:

            print(f"  plyer error: {e}")


    elif NOTIF_ENGINE == "win10toast":

        try:

            toaster.show_toast(

                title,

                message,

                duration=timeout,

                threaded=True

            )

            return True

        except Exception as e:

            print(f"  win10toast error: {e}")


    # Terminal bell fallback

    print(f"\n  {'='*50}")

    print(f"  *** REMINDER ***")

    print(f"  Title  : {title}")

    print(f"  Message: {message}")

    print(f"  Time   : {datetime.now().strftime('%d-%m-%Y %H:%M:%S')}")

    print(f"  {'='*50}")

    print("\a")  # Terminal bell

    return True



# ============================================================

# REMINDER CLASS

# ============================================================


class ReminderManager:

    def __init__(self):

        self.reminders = load_reminders()

        self.running   = False

        self.fired     = set()  # IDs of already-fired one-time reminders


    # ----------------------------------------------------------

    # Add Reminder

    # ----------------------------------------------------------


    def add_reminder(self, title, message, remind_type,

                     remind_at=None, interval_mins=None,

                     repeat_times=1):

        reminder = {

            "id":           datetime.now().strftime("%Y%m%d%H%M%S%f"),

            "title":        title,

            "message":      message,

            "type":         remind_type,   # once / interval / daily / weekly

            "remind_at":    remind_at,     # "HH:MM" or "DD-MM-YYYY HH:MM"

            "interval_mins": interval_mins,

            "repeat_times": repeat_times,

            "fired_count":  0,

            "active":       True,

            "created_at":   datetime.now().strftime("%d-%m-%Y %H:%M:%S")

        }

        self.reminders.append(reminder)

        save_reminders(self.reminders)

        print(f"\n  Reminder saved: [{title}]")

        return reminder


    # ----------------------------------------------------------

    # List Reminders

    # ----------------------------------------------------------


    def list_reminders(self):

        active   = [r for r in self.reminders if r.get("active")]

        inactive = [r for r in self.reminders if not r.get("active")]


        print("\n" + "="*58)

        print(f"  REMINDERS  ({len(active)} active, {len(inactive)} done/disabled)")

        print("="*58)


        if not self.reminders:

            print("\n  No reminders set yet.")

            return


        if active:

            print("\n  ACTIVE:")

            for i, r in enumerate(active, 1):

                rtype = r.get("type", "")

                at    = r.get("remind_at", "")

                mins  = r.get("interval_mins")

                fired = r.get("fired_count", 0)

                repeat = r.get("repeat_times", 1)


                if rtype == "once":

                    schedule_str = f"Once at {at}"

                elif rtype == "interval":

                    schedule_str = f"Every {mins} min(s)"

                elif rtype == "daily":

                    schedule_str = f"Daily at {at}"

                elif rtype == "weekly":

                    schedule_str = f"Weekly on {at}"

                else:

                    schedule_str = at


                print(f"\n  [{i}] {r['title']}")

                print(f"       Message  : {r['message']}")

                print(f"       Schedule : {schedule_str}")

                print(f"       Fired    : {fired}/{repeat if repeat > 0 else 'unlimited'}")

                print(f"       Created  : {r['created_at']}")


        if inactive:

            print("\n  COMPLETED / DISABLED:")

            for r in inactive[-5:]:

                print(f"  - [{r['title']}] fired {r.get('fired_count',0)} time(s)")


        print("="*58)


    # ----------------------------------------------------------

    # Delete / Disable Reminder

    # ----------------------------------------------------------


    def delete_reminder(self, index):

        active = [r for r in self.reminders if r.get("active")]

        if index < 1 or index > len(active):

            print("  Invalid index.")

            return

        target = active[index - 1]

        target["active"] = False

        save_reminders(self.reminders)

        print(f"\n  Disabled: [{target['title']}]")


    def delete_all(self):

        confirm = input("\n  Delete ALL reminders? (yes/no): ").strip().lower()

        if confirm == "yes":

            self.reminders = []

            self.fired     = set()

            save_reminders(self.reminders)

            print("  All reminders cleared.")


    # ----------------------------------------------------------

    # Core Check Loop

    # ----------------------------------------------------------


    def _check_loop(self):

        print("  Reminder monitor running in background...")

        self.running = True


        # Track interval next-fire times

        interval_next = {}

        for r in self.reminders:

            if r.get("type") == "interval" and r.get("active"):

                interval_next[r["id"]] = datetime.now() + timedelta(

                    minutes=r.get("interval_mins", 1)

                )


        while self.running:

            now      = datetime.now()

            now_hhmm = now.strftime("%H:%M")

            now_str  = now.strftime("%d-%m-%Y %H:%M")


            for r in self.reminders:

                if not r.get("active"):

                    continue


                rid    = r["id"]

                rtype  = r.get("type")

                repeat = r.get("repeat_times", 1)

                fired  = r.get("fired_count", 0)


                should_fire = False


                # --- One-time ---

                if rtype == "once":

                    if rid not in self.fired:

                        try:

                            fire_dt = datetime.strptime(r["remind_at"], "%d-%m-%Y %H:%M")

                            if now >= fire_dt:

                                should_fire = True

                                self.fired.add(rid)

                        except:

                            pass


                # --- Interval ---

                elif rtype == "interval":

                    next_fire = interval_next.get(rid, now)

                    if now >= next_fire:

                        should_fire = True

                        interval_next[rid] = now + timedelta(

                            minutes=r.get("interval_mins", 1)

                        )


                # --- Daily ---

                elif rtype == "daily":

                    fire_key = f"{rid}_{now.strftime('%Y%m%d')}"

                    if now_hhmm == r.get("remind_at") and fire_key not in self.fired:

                        should_fire = True

                        self.fired.add(fire_key)


                # --- Weekly ---

                elif rtype == "weekly":

                    # remind_at format: "Monday 09:00"

                    try:

                        day_name, hhmm = r["remind_at"].split(" ", 1)

                        if now.strftime("%A") == day_name and now_hhmm == hhmm:

                            fire_key = f"{rid}_{now.strftime('%Y%W')}"

                            if fire_key not in self.fired:

                                should_fire = True

                                self.fired.add(fire_key)

                    except:

                        pass


                # Fire it!

                if should_fire:

                    send_notification(r["title"], r["message"])

                    r["fired_count"] = fired + 1


                    # Deactivate if repeat limit reached

                    if repeat > 0 and r["fired_count"] >= repeat:

                        if rtype in ["once", "interval"]:

                            r["active"] = False


                    save_reminders(self.reminders)


            time.sleep(15)  # Check every 15 seconds


    def start(self):

        t = threading.Thread(target=self._check_loop, daemon=True)

        t.start()


    def stop(self):

        self.running = False



# ============================================================

# QUICK ADD HELPERS

# ============================================================


def add_once(manager):

    print("\n  One-time Reminder")

    title   = input("  Title: ").strip()

    message = input("  Message: ").strip()

    at_str  = input("  When? (DD-MM-YYYY HH:MM): ").strip()

    try:

        datetime.strptime(at_str, "%d-%m-%Y %H:%M")

        manager.add_reminder(title, message, "once",

                             remind_at=at_str, repeat_times=1)

    except ValueError:

        print("  Invalid date/time format.")



def add_interval(manager):

    print("\n  Interval Reminder")

    title   = input("  Title: ").strip()

    message = input("  Message: ").strip()

    try:

        mins    = int(input("  Repeat every how many minutes? ").strip())

        repeats = input("  How many times? (0 = unlimited): ").strip()

        repeats = int(repeats) if repeats.isdigit() else 0

        manager.add_reminder(title, message, "interval",

                             interval_mins=mins, repeat_times=repeats)

    except ValueError:

        print("  Invalid number.")



def add_daily(manager):

    print("\n  Daily Reminder")

    title   = input("  Title: ").strip()

    message = input("  Message: ").strip()

    at_str  = input("  Daily at (HH:MM): ").strip()

    manager.add_reminder(title, message, "daily",

                         remind_at=at_str, repeat_times=0)



def add_weekly(manager):

    print("\n  Weekly Reminder")

    title   = input("  Title: ").strip()

    message = input("  Message: ").strip()

    days = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]

    print("  Days:", ", ".join(days))

    day  = input("  Day: ").strip().capitalize()

    if day not in days:

        print("  Invalid day.")

        return

    time_str = input("  Time (HH:MM): ").strip()

    at_str   = f"{day} {time_str}"

    manager.add_reminder(title, message, "weekly",

                         remind_at=at_str, repeat_times=0)



def add_quick(manager):

    print("\n  Quick Reminder (in X minutes from now)")

    title   = input("  Title: ").strip()

    message = input("  Message: ").strip()

    try:

        mins    = int(input("  Remind in how many minutes? ").strip())

        fire_at = (datetime.now() + timedelta(minutes=mins)).strftime("%d-%m-%Y %H:%M")

        manager.add_reminder(title, message, "once",

                             remind_at=fire_at, repeat_times=1)

        print(f"  Will remind at: {fire_at}")

    except ValueError:

        print("  Invalid number.")



# ============================================================

# MAIN MENU

# ============================================================


def print_menu(manager):

    active_count = sum(1 for r in manager.reminders if r.get("active"))

    running_str  = "Running" if manager.running else "Stopped"

    print("\n" + "-"*48)

    print(f"  DESKTOP REMINDER  [{running_str}] [{active_count} active]")

    print("-"*48)

    print("  1. Add one-time reminder (specific date & time)")

    print("  2. Add quick reminder   (in X minutes)")

    print("  3. Add interval reminder (every N minutes)")

    print("  4. Add daily reminder   (every day at HH:MM)")

    print("  5. Add weekly reminder  (day + time)")

    print("  6. View all reminders")

    print("  7. Disable a reminder")

    print("  8. Clear all reminders")

    print("  9. Start monitor (background)")

    print("  0. Exit")

    print("-"*48)



def main():

    print("\n" + "="*55)

    print("     DESKTOP NOTIFICATION REMINDER")

    print("="*55)


    # Show notification engine status

    if NOTIF_ENGINE == "plyer":

        print("\n  Notification engine : plyer (desktop pop-ups)")

    elif NOTIF_ENGINE == "win10toast":

        print("\n  Notification engine : win10toast (Windows toast)")

    else:

        print("\n  No notification library found.")

        print("  Install one:  pip install plyer")

        print("                pip install win10toast  (Windows)")

        print("  Falling back to terminal alerts.\n")


    manager = ReminderManager()


    if manager.reminders:

        active = sum(1 for r in manager.reminders if r.get("active"))

        print(f"\n  Loaded {len(manager.reminders)} reminder(s), {active} active.")


    # Auto-start monitor if there are active reminders

    if any(r.get("active") for r in manager.reminders):

        manager.start()

        print("  Monitor auto-started for existing reminders.")


    while True:

        print_menu(manager)

        choice = input("  > ").strip()


        if choice == "1":

            add_once(manager)


        elif choice == "2":

            add_quick(manager)


        elif choice == "3":

            add_interval(manager)


        elif choice == "4":

            add_daily(manager)


        elif choice == "5":

            add_weekly(manager)


        elif choice == "6":

            manager.list_reminders()


        elif choice == "7":

            manager.list_reminders()

            try:

                idx = int(input("\n  Enter reminder number to disable: ").strip())

                manager.delete_reminder(idx)

            except ValueError:

                print("  Invalid input.")


        elif choice == "8":

            manager.delete_all()


        elif choice == "9":

            if manager.running:

                print("\n  Monitor is already running.")

            else:

                manager.start()

                print("\n  Monitor started! Checks every 15 seconds.")

                print("  Keep this window open to receive reminders.")


        elif choice == "0":

            manager.stop()

            print("\n  Goodbye! Reminders will not fire after exit.\n")

            break


        else:

            print("  Invalid choice.")



# ============================================================

# RUN

# ============================================================


if __name__ == "__main__":

    main()

Simple Chatbot with Pattern Matching

import re

import random

import json

from datetime import datetime

from pathlib import Path

 

# ============================================================

# KNOWLEDGE BASE — Patterns & Responses

# ============================================================

 

PATTERNS = [

 

    # ---------- Greetings ----------

    {

        "tag": "greeting",

        "patterns": [

            r"\b(hi|hello|hey|howdy|hiya|greetings|good morning|good afternoon|good evening)\b"

        ],

        "responses": [

            "Hey there! How can I help you today?",

            "Hello! Great to see you. What's on your mind?",

            "Hi! I'm ready to chat. What can I do for you?",

            "Howdy! Ask me anything!"

        ]

    },

 

    # ---------- How are you ----------

    {

        "tag": "how_are_you",

        "patterns": [

            r"\b(how are you|how are you doing|how do you do|how's it going|are you ok)\b"

        ],

        "responses": [

            "I'm doing great, thanks for asking! How about you?",

            "Running at full capacity! What can I help with?",

            "All systems go! What's up?",

            "Fantastic! Ready to chat. How are YOU doing?"

        ]

    },

 

    # ---------- Name ----------

    {

        "tag": "name",

        "patterns": [

            r"\b(what is your name|what's your name|who are you|tell me your name)\b"

        ],

        "responses": [

            "I'm PyBot - your Python-powered chatbot!",

            "My name is PyBot! Nice to meet you.",

            "Call me PyBot. I'm a rule-based chatbot built in Python."

        ]

    },

 

    # ---------- Creator ----------

    {

        "tag": "creator",

        "patterns": [

            r"\b(who made you|who created you|who built you|who programmed you|who is your creator)\b"

        ],

        "responses": [

            "I was built using Python with regex pattern matching!",

            "A Python developer created me using re, random, and json.",

            "I'm powered by Python! Check out pythonforengineers.in for the code."

        ]

    },

 

    # ---------- Time ----------

    {

        "tag": "time",

        "patterns": [

            r"\b(what time is it|current time|tell me the time|what's the time|what is the time)\b"

        ],

        "responses": ["__TIME__"]

    },

 

    # ---------- Date ----------

    {

        "tag": "date",

        "patterns": [

            r"\b(what is today's date|what's today's date|what day is it|today's date|current date)\b"

        ],

        "responses": ["__DATE__"]

    },

 

    # ---------- Weather ----------

    {

        "tag": "weather",

        "patterns": [

            r"\b(weather|temperature|forecast|is it hot|is it cold|is it raining|is it sunny)\b"

        ],

        "responses": [

            "I can't check live weather, but try weather.com or Google Weather!",

            "I don't have internet access, but a quick Google search will give you the forecast!",

        ]

    },

 

    # ---------- Math ----------

    {

        "tag": "math",

        "patterns": [

            r"(\d+)\s*[\+\-\*\/]\s*(\d+)"

        ],

        "responses": ["__MATH__"]

    },

 

    # ---------- Jokes ----------

    {

        "tag": "joke",

        "patterns": [

            r"\b(tell me a joke|joke|make me laugh|say something funny|funny)\b"

        ],

        "responses": [

            "Why do Python programmers prefer dark mode? Because light attracts bugs!",

            "Why was the developer broke? Because he used up all his cache!",

            "What do you call a bear with no teeth? A gummy bear!",

            "Why did the programmer quit? Because he didn't get arrays (a raise)!",

            "Debugging: Being the detective in a crime movie where you're also the murderer."

        ]

    },

 

    # ---------- Python ----------

    {

        "tag": "python",

        "patterns": [

            r"\b(python|programming|code|coding|developer|script)\b"

        ],

        "responses": [

            "Python is one of the most popular languages! Great for automation, AI, web dev and more.",

            "Python is beginner-friendly yet powerful. What would you like to build?",

            "Love Python! Check out pythonforengineers.in for cool Python programs."

        ]

    },

 

    # ---------- Help ----------

    {

        "tag": "help",

        "patterns": [

            r"\b(help|what can you do|your features|capabilities|what do you know)\b"

        ],

        "responses": [

            "I can:\n  * Chat with you\n  * Tell jokes\n  * Answer basic questions\n  * Do simple math\n  * Tell time and date\n  * Remember your name\n\nJust type naturally!"

        ]

    },

 

    # ---------- Name capture ----------

    {

        "tag": "user_name",

        "patterns": [

            r"\bmy name is (\w+)\b",

            r"\bi am (\w+)\b",

            r"\bcall me (\w+)\b"

        ],

        "responses": ["__REMEMBER_NAME__"]

    },

 

    # ---------- Goodbye ----------

    {

        "tag": "goodbye",

        "patterns": [

            r"\b(bye|goodbye|see you|see ya|take care|quit|exit|later)\b"

        ],

        "responses": [

            "Goodbye! Have a great day!",

            "See you later! It was nice chatting!",

            "Take care! Come back anytime.",

            "Bye! Keep coding!"

        ]

    },

 

    # ---------- Thanks ----------

    {

        "tag": "thanks",

        "patterns": [

            r"\b(thank you|thanks|thank u|thx|ty|appreciated)\b"

        ],

        "responses": [

            "You're welcome!",

            "Happy to help!",

            "Anytime! That's what I'm here for.",

            "No problem at all!"

        ]

    },

 

    # ---------- Age ----------

    {

        "tag": "age",

        "patterns": [

            r"\b(how old are you|what's your age|what is your age|when were you born)\b"

        ],

        "responses": [

            "I was born the moment someone ran this Python script! So pretty young.",

            "Age is just a number, but I'm freshly compiled!"

        ]

    },

 

    # ---------- Favorites ----------

    {

        "tag": "favorites",

        "patterns": [

            r"\b(favorite language|favorite food|favorite color|what do you like)\b"

        ],

        "responses": [

            "My favorite language is obviously Python!",

            "I like Python, clean code, and well-commented scripts! What about you?",

        ]

    },

 

    # ---------- Meaning of life ----------

    {

        "tag": "meaning",

        "patterns": [

            r"\b(meaning of life|purpose of life|why are we here|42)\b"

        ],

        "responses": [

            "42. Obviously.",

            "The meaning of life is to write clean, well-documented Python code. At least, that's my belief.",

        ]

    },

 

]

 

# ============================================================

# SPECIAL RESPONSE HANDLERS

# ============================================================

 

def handle_special(response, user_input, memory):

    if response == "__TIME__":

        now = datetime.now().strftime("%I:%M %p")

        return f"The current time is {now}"

 

    if response == "__DATE__":

        today = datetime.now().strftime("%A, %d %B %Y")

        return f"Today is {today}"

 

    if response == "__MATH__":

        try:

            expr = re.search(r"(\d+\s*[\+\-\*\/]\s*\d+)", user_input)

            if expr:

                result = eval(expr.group(0))

                return f"The answer is: {result}"

        except:

            return "Hmm, I couldn't calculate that. Try something like: 5 + 3"

 

    if response == "__REMEMBER_NAME__":

        name_match = re.search(

            r"\b(?:my name is|i am|call me)\s+(\w+)\b",

            user_input, re.IGNORECASE

        )

        if name_match:

            name = name_match.group(1).capitalize()

            memory["user_name"] = name

            return f"Nice to meet you, {name}! I'll remember that."

        return "Nice name! What's it again?"

 

    return response

 

 

# ============================================================

# MATCH USER INPUT TO PATTERN

# ============================================================

 

def get_response(user_input, memory):

    text = user_input.lower().strip()

 

    for rule in PATTERNS:

        for pattern in rule["patterns"]:

            if re.search(pattern, text, re.IGNORECASE):

                response = random.choice(rule["responses"])

                response = handle_special(response, text, memory)

 

                # Occasionally personalize with name if known

                if "user_name" in memory and random.random() < 0.3:

                    response = f"{memory['user_name']}, " + response[0].lower() + response[1:]

 

                return response

 

    # Fallback

    fallbacks = [

        "Hmm, I'm not sure I understand. Could you rephrase that?",

        "Interesting! Tell me more.",

        "I don't have an answer for that yet, but I'm learning!",

        "That's a good one! I'm still a work in progress.",

        "Could you ask that differently? I want to help!",

        "I'm not sure about that. Try asking me something else!"

    ]

    return random.choice(fallbacks)

 

 

# ============================================================

# SAVE CHAT HISTORY

# ============================================================

 

def save_chat_history(history, session_start):

    filename = f"chat_history_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"

    with open(filename, "w", encoding="utf-8") as f:

        json.dump({

            "session": session_start,

            "messages": history

        }, f, indent=2, ensure_ascii=False)

    print(f"\n  Chat saved to {filename}")

 

 

# ============================================================

# MAIN CHAT LOOP

# ============================================================

 

def main():

    print("\n" + "="*55)

    print("     PYBOT - Pattern Matching Chatbot")

    print("="*55)

    print("\n  Type 'help'    - see what I can do")

    print("  Type 'history' - save chat log to file")

    print("  Type 'bye'     - exit\n")

    print("-"*55)

 

    memory        = {}

    history       = []

    session_start = datetime.now().strftime("%d-%m-%Y %H:%M:%S")

 

    while True:

        try:

            user_input = input("\n  You: ").strip()

        except (EOFError, KeyboardInterrupt):

            print("\n\n  Interrupted. Goodbye!")

            break

 

        if not user_input:

            continue

 

        # Save history command

        if user_input.lower() == "history":

            save_chat_history(history, session_start)

            continue

 

        # Get and print response

        response = get_response(user_input, memory)

        print(f"\n  PyBot: {response}")

 

        # Log conversation

        history.append({

            "user": user_input,

            "bot":  response,

            "time": datetime.now().strftime("%H:%M:%S")

        })

 

        # Exit trigger

        if any(re.search(p, user_input.lower())

               for p in [r"\b(bye|goodbye|quit|exit)\b"]):

            break

 

    print("\n" + "="*55)

    print("  Thanks for chatting! Session ended.")

    print("="*55 + "\n")

 

 

# ============================================================

# RUN

# ============================================================

 

if __name__ == "__main__":

    main()

 

Website Uptime Monitor

import requests

import json

import os

import time

import threading

import smtplib

from datetime import datetime

from pathlib import Path

from email.mime.text import MIMEText

from email.mime.multipart import MIMEMultipart

from collections import defaultdict

 

# ============================================================

# CONFIGURATION

# ============================================================

 

SITES_FILE    = "monitored_sites.json"

LOG_FILE      = "uptime_log.json"

CONFIG_FILE   = "uptime_config.json"

CHECK_TIMEOUT = 10      # seconds before a request is considered failed

DEFAULT_INTERVAL = 60   # seconds between checks

 

# ============================================================

# LOAD & SAVE HELPERS

# ============================================================

 

def load_json(filepath, default):

    if Path(filepath).exists():

        try:

            with open(filepath, "r") as f:

                return json.load(f)

        except:

            return default

    return default

 

 

def save_json(filepath, data):

    with open(filepath, "w") as f:

        json.dump(data, f, indent=2)

 

 

# ============================================================

# CHECK A SINGLE WEBSITE

# ============================================================

 

def check_site(url):

    """

    Ping a URL and return a result dict:

    status, response_time_ms, status_code, error

    """

    start = time.time()

    try:

        response = requests.get(url, timeout=CHECK_TIMEOUT,

                                allow_redirects=True,

                                headers={"User-Agent": "UptimeMonitor/1.0"})

        elapsed   = round((time.time() - start) * 1000, 1)

        is_up     = response.status_code < 400

 

        return {

            "url":             url,

            "status":          "UP" if is_up else "DOWN",

            "status_code":     response.status_code,

            "response_time_ms": elapsed,

            "error":           None,

            "checked_at":      datetime.now().strftime("%d-%m-%Y %H:%M:%S")

        }

 

    except requests.exceptions.ConnectionError:

        return {

            "url":             url,

            "status":          "DOWN",

            "status_code":     None,

            "response_time_ms": None,

            "error":           "Connection refused",

            "checked_at":      datetime.now().strftime("%d-%m-%Y %H:%M:%S")

        }

    except requests.exceptions.Timeout:

        return {

            "url":             url,

            "status":          "DOWN",

            "status_code":     None,

            "response_time_ms": None,

            "error":           f"Timeout after {CHECK_TIMEOUT}s",

            "checked_at":      datetime.now().strftime("%d-%m-%Y %H:%M:%S")

        }

    except Exception as e:

        return {

            "url":             url,

            "status":          "DOWN",

            "status_code":     None,

            "response_time_ms": None,

            "error":           str(e),

            "checked_at":      datetime.now().strftime("%d-%m-%Y %H:%M:%S")

        }

 

 

# ============================================================

# SEND EMAIL ALERT

# ============================================================

 

def send_alert(config, subject, body):

    try:

        msg = MIMEMultipart()

        msg["From"]    = config["email"]

        msg["To"]      = config["alert_email"]

        msg["Subject"] = subject

        msg.attach(MIMEText(body, "plain"))

 

        server = smtplib.SMTP(config["smtp_host"], config["smtp_port"])

        server.starttls()

        server.login(config["email"], config["password"])

        server.sendmail(config["email"], config["alert_email"], msg.as_string())

        server.quit()

        print(f"\n   Alert sent to {config['alert_email']}")

    except Exception as e:

        print(f"\n  ⚠  Email alert failed: {e}")

 

 

# ============================================================

# UPTIME MONITOR CLASS

# ============================================================

 

class UptimeMonitor:

    def __init__(self):

        self.sites        = load_json(SITES_FILE, [])

        self.log          = load_json(LOG_FILE, {})

        self.config       = load_json(CONFIG_FILE, {})

        self.running      = False

        self.last_status  = {}   # url -> "UP" or "DOWN"

        self.downtime_start = {} # url -> datetime when it went down

 

    # ----------------------------------------------------------

    # Site Management

    # ----------------------------------------------------------

 

    def add_site(self, url, name=None, interval=DEFAULT_INTERVAL):

        # Normalize URL

        if not url.startswith("http"):

            url = "https://" + url

 

        # Check duplicates

        if any(s["url"] == url for s in self.sites):

            print(f"\n  ⚠  Already monitoring: {url}")

            return

 

        site = {

            "url":      url,

            "name":     name or url,

            "interval": interval,

            "added_at": datetime.now().strftime("%d-%m-%Y %H:%M:%S")

        }

        self.sites.append(site)

        save_json(SITES_FILE, self.sites)

        print(f"\n   Added: {site['name']} ({url})")

 

    def remove_site(self, index):

        if 0 <= index < len(self.sites):

            removed = self.sites.pop(index)

            save_json(SITES_FILE, self.sites)

            print(f"\n  🗑  Removed: {removed['name']}")

        else:

            print("\n   Invalid index.")

 

    def list_sites(self):

        if not self.sites:

            print("\n  📭 No sites being monitored.")

            return

        print("\n" + "="*60)

        print(f"  {'#':<4} {'NAME':<20} {'URL':<30} {'INTERVAL'}")

        print("  " + "-"*56)

        for i, s in enumerate(self.sites, 1):

            status_icon = "🟢" if self.last_status.get(s["url"]) == "UP" else \

                          "🔴" if self.last_status.get(s["url"]) == "DOWN" else "⚪"

            print(f"  {status_icon} {i:<3} {s['name']:<20} {s['url']:<30} every {s['interval']}s")

        print("="*60)

 

    # ----------------------------------------------------------

    # Manual Check (all sites at once)

    # ----------------------------------------------------------

 

    def check_all_now(self):

        if not self.sites:

            print("\n  📭 No sites to check.")

            return

 

        print(f"\n   Checking {len(self.sites)} site(s)...\n")

        print(f"  {'STATUS':<8} {'SITE':<25} {'CODE':<8} {'RESPONSE':<14} {'TIME'}")

        print("  " + "-"*65)

 

        for site in self.sites:

            result = check_site(site["url"])

            self._record_result(site["url"], result)

 

            icon    = " UP  " if result["status"] == "UP" else " DOWN"

            code    = str(result["status_code"]) if result["status_code"] else "—"

            rt      = f"{result['response_time_ms']}ms" if result["response_time_ms"] else "—"

            error   = f"  ⚠ {result['error']}" if result["error"] else ""

            print(f"  {icon}  {site['name']:<25} {code:<8} {rt:<14} {result['checked_at']}{error}")

 

    # ----------------------------------------------------------

    # Continuous Monitor Loop (runs in thread)

    # ----------------------------------------------------------

 

    def _monitor_loop(self):

        print("   Monitor running in background...")

        self.running = True

        counters = {s["url"]: 0 for s in self.sites}

 

        while self.running:

            for site in self.sites:

                url      = site["url"]

                interval = site.get("interval", DEFAULT_INTERVAL)

                counters[url] = counters.get(url, 0) + 1

 

                if counters[url] >= interval:

                    counters[url] = 0

                    result = check_site(url)

                    prev   = self.last_status.get(url)

 

                    self._record_result(url, result)

 

                    # Status changed → alert

                    if prev and prev != result["status"]:

                        if result["status"] == "DOWN":

                            self.downtime_start[url] = datetime.now()

                            msg = (f" DOWN: {site['name']}\n"

                                   f"URL: {url}\n"

                                   f"Error: {result['error']}\n"

                                   f"Time: {result['checked_at']}")

                            print(f"\n   ALERT: {site['name']} is DOWN!")

                            print("  > ", end="", flush=True)

                            if self.config:

                                send_alert(self.config,

                                           f" DOWN: {site['name']}",

                                           msg)

                        else:

                            downtime = ""

                            if url in self.downtime_start:

                                delta = datetime.now() - self.downtime_start[url]

                                mins  = int(delta.total_seconds() // 60)

                                secs  = int(delta.total_seconds() % 60)

                                downtime = f" (was down for {mins}m {secs}s)"

                                del self.downtime_start[url]

                            print(f"\n   RECOVERED: {site['name']}{downtime}")

                            print("  > ", end="", flush=True)

                            if self.config:

                                send_alert(self.config,

                                           f" Recovered: {site['name']}",

                                           f"{site['name']} is back UP{downtime}\nURL: {url}\nTime: {result['checked_at']}")

 

                    self.last_status[url] = result["status"]

 

            time.sleep(1)

 

    def start_monitoring(self):

        if not self.sites:

            print("\n   No sites added yet.")

            return

        t = threading.Thread(target=self._monitor_loop, daemon=True)

        t.start()

 

    def stop_monitoring(self):

        self.running = False

 

    # ----------------------------------------------------------

    # Record Result to Log

    # ----------------------------------------------------------

 

    def _record_result(self, url, result):

        if url not in self.log:

            self.log[url] = []

        self.log[url].append(result)

        # Keep last 500 entries per site

        if len(self.log[url]) > 500:

            self.log[url] = self.log[url][-500:]

        save_json(LOG_FILE, self.log)

 

    # ----------------------------------------------------------

    # Uptime Stats Report

    # ----------------------------------------------------------

 

    def show_stats(self):

        if not self.log:

            print("\n  📭 No data logged yet. Run a check first.")

            return

 

        print("\n" + "="*60)

        print("   UPTIME STATISTICS")

        print("="*60)

 

        for site in self.sites:

            url     = site["url"]

            records = self.log.get(url, [])

            if not records:

                continue

 

            total    = len(records)

            up_count = sum(1 for r in records if r["status"] == "UP")

            uptime   = (up_count / total) * 100

 

            # Avg response time

            times = [r["response_time_ms"] for r in records if r["response_time_ms"]]

            avg_rt = round(sum(times) / len(times), 1) if times else None

 

            # Last checked

            last = records[-1]

 

            status_icon = "🟢" if last["status"] == "UP" else "🔴"

 

            print(f"\n  {status_icon} {site['name']}")

            print(f"     URL        : {url}")

            print(f"     Uptime     : {uptime:.2f}%  ({up_count}/{total} checks passed)")

            print(f"     Avg Response: {avg_rt}ms" if avg_rt else "     Avg Response: —")

            print(f"     Last Check : {last['checked_at']}  [{last['status']}]")

            if last.get("error"):

                print(f"     Last Error : {last['error']}")

 

        print("="*60)

 

    # ----------------------------------------------------------

    # Setup Email Alerts

    # ----------------------------------------------------------

 

    def setup_alerts(self):

        print("\n" + "="*55)

        print("   EMAIL ALERT SETUP")

        print("="*55)

        print("\n  Providers: Gmail (smtp.gmail.com:587) | Outlook (smtp.office365.com:587)")

 

        smtp_host   = input("\n  SMTP Host: ").strip()

        smtp_port   = int(input("  SMTP Port: ").strip() or 587)

        email       = input("  Your email: ").strip()

        password    = input("  App password: ").strip()

        alert_email = input("  Send alerts to (email): ").strip()

 

        self.config = {

            "smtp_host":   smtp_host,

            "smtp_port":   smtp_port,

            "email":       email,

            "password":    password,

            "alert_email": alert_email

        }

        save_json(CONFIG_FILE, self.config)

        print("\n  ✅ Alert config saved.")

 

 

# ============================================================

# MAIN MENU

# ============================================================

 

def print_menu(monitor):

    status = " Running" if monitor.running else "⚫ Stopped"

    print("\n" + "-"*45)

    print(f"   WEBSITE UPTIME MONITOR  [{status}]")

    print("-"*45)

    print("  1. Add website to monitor")

    print("  2. Remove website")

    print("  3. List monitored sites")

    print("  4. Check all sites NOW")

    print("  5. Start continuous monitor (background)")

    print("  6. View uptime statistics")

    print("  7. Setup email alerts")

    print("  0. Exit")

    print("-"*45)

 

 

def main():

    print("\n" + "="*55)

    print("      WEBSITE UPTIME MONITOR")

    print("="*55)

    print(f"\n  Check timeout : {CHECK_TIMEOUT}s")

    print(f"  Default interval: every {DEFAULT_INTERVAL}s")

    print(f"  Log file      : {LOG_FILE}")

 

    monitor = UptimeMonitor()

 

    if monitor.sites:

        print(f"\n   Loaded {len(monitor.sites)} monitored site(s).")

    if monitor.config:

        print(f"   Alerts configured for: {monitor.config.get('alert_email','')}")

 

    while True:

        print_menu(monitor)

        choice = input("  > ").strip()

 

        if choice == "1":

            url      = input("\n  Website URL (e.g. google.com): ").strip()

            name     = input("  Display name (or Enter to use URL): ").strip() or None

            interval = input(f"  Check interval in seconds (default {DEFAULT_INTERVAL}): ").strip()

            interval = int(interval) if interval.isdigit() else DEFAULT_INTERVAL

            monitor.add_site(url, name, interval)

 

        elif choice == "2":

            monitor.list_sites()

            if monitor.sites:

                try:

                    idx = int(input("\n  Enter site number to remove: ").strip()) - 1

                    monitor.remove_site(idx)

                except ValueError:

                    print("   Invalid input.")

 

        elif choice == "3":

            monitor.list_sites()

 

        elif choice == "4":

            monitor.check_all_now()

 

        elif choice == "5":

            if monitor.running:

                print("\n   Monitor is already running.")

            else:

                monitor.start_monitoring()

                print(f"\n   Continuous monitor started!")

                print(f"   Alerts will print here. Keep this window open.")

 

        elif choice == "6":

            monitor.show_stats()

 

        elif choice == "7":

            monitor.setup_alerts()

 

        elif choice == "0":

            monitor.stop_monitoring()

            print("\n   Goodbye!\n")

            break

 

        else:

            print("   Invalid choice.")

 

 

# ============================================================

# RUN

# ============================================================

 

if __name__ == "__main__":

    main()

Screen Time Tracker

import psutil

import json

import time

import os

import threading

from datetime import datetime, date

from pathlib import Path

from collections import defaultdict

 

# ============================================================

# CONFIGURATION

# ============================================================

 

DATA_FILE      = "screen_time_data.json"

POLL_INTERVAL  = 3       # seconds between process checks

DAILY_LIMIT    = {}      # optional per-app daily limits in minutes

 

# ============================================================

# LOAD & SAVE DATA

# ============================================================

 

def load_data():

    if Path(DATA_FILE).exists():

        try:

            with open(DATA_FILE, "r") as f:

                return json.load(f)

        except:

            return {}

    return {}

 

 

def save_data(data):

    with open(DATA_FILE, "w") as f:

        json.dump(data, f, indent=2)

 

 

# ============================================================

# GET ACTIVE PROCESSES

# ============================================================

 

def get_running_apps():

    """Return a set of currently running process names (lowercased)."""

    apps = set()

    for proc in psutil.process_iter(["name", "status"]):

        try:

            if proc.info["status"] == psutil.STATUS_RUNNING:

                name = proc.info["name"].lower().replace(".exe", "")

                apps.add(name)

        except (psutil.NoSuchProcess, psutil.AccessDenied):

            pass

    return apps

 

 

def get_all_processes():

    """Return all running process names (including sleeping/idle)."""

    apps = set()

    for proc in psutil.process_iter(["name"]):

        try:

            name = proc.info["name"].lower().replace(".exe", "")

            apps.add(name)

        except (psutil.NoSuchProcess, psutil.AccessDenied):

            pass

    return apps

 

 

# ============================================================

# SCREEN TIME TRACKER CLASS

# ============================================================

 

class ScreenTimeTracker:

    def __init__(self):

        self.data          = load_data()

        self.tracking      = {}       # app -> start_time (if currently active)

        self.watch_list    = set()    # apps to specifically watch

        self.running       = False

        self.today         = str(date.today())

        self.limits        = {}       # app -> minutes limit

        self.alerted       = set()    # apps already alerted today

 

        # Ensure today's entry exists

        if self.today not in self.data:

            self.data[self.today] = {}

 

    # ----------------------------------------------------------

    # Add / Remove from Watch List

    # ----------------------------------------------------------

 

    def add_to_watchlist(self, app_name):

        name = app_name.lower().replace(".exe", "")

        self.watch_list.add(name)

        print(f"\n   Added to watchlist: {name}")

 

    def remove_from_watchlist(self, app_name):

        name = app_name.lower().replace(".exe", "")

        self.watch_list.discard(name)

        print(f"\n   Removed from watchlist: {name}")

 

    def set_limit(self, app_name, minutes):

        name = app_name.lower().replace(".exe", "")

        self.limits[name] = minutes

        print(f"\n   Limit set: {name} → {minutes} min/day")

 

    # ----------------------------------------------------------

    # Core Tracking Loop

    # ----------------------------------------------------------

 

    def track(self):

        print("\n   Tracker running in background...")

        self.running = True

 

        while self.running:

            now        = datetime.now()

            today_str  = str(date.today())

 

            # New day reset

            if today_str != self.today:

                self.today   = today_str

                self.alerted = set()

                if self.today not in self.data:

                    self.data[self.today] = {}

 

            # Get active apps

            if self.watch_list:

                active_now = get_all_processes() & self.watch_list

            else:

                active_now = get_running_apps()

 

            # Apps that just started

            for app in active_now:

                if app not in self.tracking:

                    self.tracking[app] = now

 

            # Apps that just stopped

            stopped = set(self.tracking.keys()) - active_now

            for app in stopped:

                elapsed = (now - self.tracking[app]).total_seconds()

                self._add_time(app, elapsed)

                del self.tracking[app]

 

            # Check limits

            for app in active_now:

                total_mins = self._get_today_minutes(app)

                if app in self.limits and total_mins >= self.limits[app]:

                    if app not in self.alerted:

                        print(f"\n  ⚠  LIMIT REACHED: [{app}] — {total_mins:.1f} min used (limit: {self.limits[app]} min)")

                        print("  > ", end="", flush=True)

                        self.alerted.add(app)

 

            save_data(self.data)

            time.sleep(POLL_INTERVAL)

 

        # Final flush — save any still-running apps

        now = datetime.now()

        for app, start in self.tracking.items():

            elapsed = (now - start).total_seconds()

            self._add_time(app, elapsed)

        save_data(self.data)

 

    def stop(self):

        self.running = False

 

    # ----------------------------------------------------------

    # Time Helpers

    # ----------------------------------------------------------

 

    def _add_time(self, app, seconds):

        today = self.today

        if today not in self.data:

            self.data[today] = {}

        self.data[today][app] = self.data[today].get(app, 0) + seconds

 

    def _get_today_minutes(self, app):

        return self.data.get(self.today, {}).get(app, 0) / 60

 

    # ----------------------------------------------------------

    # Reports

    # ----------------------------------------------------------

 

    def report_today(self):

        today_data = self.data.get(self.today, {})

 

        # Add currently running session time (not yet saved)

        now = datetime.now()

        live = {}

        for app, start in self.tracking.items():

            live[app] = (now - start).total_seconds()

 

        merged = dict(today_data)

        for app, secs in live.items():

            merged[app] = merged.get(app, 0) + secs

 

        if not merged:

            print("\n   No screen time data for today yet.")

            return

 

        sorted_apps = sorted(merged.items(), key=lambda x: x[1], reverse=True)

 

        print("\n" + "="*55)

        print(f"   SCREEN TIME REPORT — {self.today}")

        print("="*55)

        print(f"\n  {'APP':<25} {'TIME':>12}  {'LIMIT':>10}")

        print("  " + "-"*50)

 

        total_secs = 0

        for app, secs in sorted_apps:

            h, m, s   = _hms(secs)

            time_str  = f"{h}h {m:02d}m {s:02d}s" if h else f"{m}m {s:02d}s"

            limit_str = f"{self.limits[app]} min" if app in self.limits else "—"

            bar       = _bar(secs, max(v for _, v in sorted_apps))

            print(f"  {app:<25} {time_str:>12}  {limit_str:>10}  {bar}")

            total_secs += secs

 

        print("  " + "-"*50)

        h, m, s = _hms(total_secs)

        print(f"  {'TOTAL':<25} {h}h {m:02d}m {s:02d}s")

        print("="*55)

 

    def report_week(self):

        print("\n" + "="*55)

        print("   WEEKLY SCREEN TIME SUMMARY")

        print("="*55)

 

        app_totals = defaultdict(float)

        day_totals = {}

 

        for day_str, apps in sorted(self.data.items())[-7:]:

            day_total = sum(apps.values())

            day_totals[day_str] = day_total

            for app, secs in apps.items():

                app_totals[app] += secs

 

        print("\n  Daily totals:")

        for day, secs in day_totals.items():

            h, m, _ = _hms(secs)

            bar      = _bar(secs, max(day_totals.values()) if day_totals else 1)

            print(f"  {day}   {h}h {m:02d}m  {bar}")

 

        print("\n  Top apps this week:")

        top = sorted(app_totals.items(), key=lambda x: x[1], reverse=True)[:10]

        for app, secs in top:

            h, m, _ = _hms(secs)

            print(f"  • {app:<25} {h}h {m:02d}m")

        print("="*55)

 

    def list_watchlist(self):

        print("\n  👁  Current watchlist:")

        if self.watch_list:

            for app in sorted(self.watch_list):

                limit = f"  (limit: {self.limits[app]} min)" if app in self.limits else ""

                print(f"  • {app}{limit}")

        else:

            print("  (empty — tracking ALL running processes)")

 

    def show_running_now(self):

        apps = get_all_processes()

        print(f"\n  🖥  Currently running processes ({len(apps)}):")

        for i, app in enumerate(sorted(apps), 1):

            active = "" if app in self.tracking else "  "

            print(f"  {active} {app}")

            if i % 30 == 0:

                cont = input("  ... show more? (y/n): ").strip().lower()

                if cont != "y":

                    break

 

 

# ============================================================

# HELPERS

# ============================================================

 

def _hms(seconds):

    seconds = int(seconds)

    h = seconds // 3600

    m = (seconds % 3600) // 60

    s = seconds % 60

    return h, m, s

 

 

def _bar(value, max_value, width=10):

    if max_value == 0:

        return ""

    filled = int((value / max_value) * width)

    return "█" * filled + "░" * (width - filled)

 

 

# ============================================================

# MAIN MENU

# ============================================================

 

def print_menu():

    print("\n" + "-"*45)

    print("       SCREEN TIME TRACKER")

    print("-"*45)

    print("  1. Start tracking (background)")

    print("  2. Today's report")

    print("  3. Weekly summary")

    print("  4. Manage watchlist")

    print("  5. Set app time limit")

    print("  6. Show all running processes")

    print("  0. Stop & Exit")

    print("-"*45)

 

 

def manage_watchlist(tracker):

    print("\n  Watchlist options:")

    print("  1. Add app to watchlist")

    print("  2. Remove app from watchlist")

    print("  3. View current watchlist")

    sub = input("  Choice: ").strip()

    if sub == "1":

        app = input("  App name (e.g. chrome, code, vlc): ").strip()

        tracker.add_to_watchlist(app)

    elif sub == "2":

        app = input("  App name to remove: ").strip()

        tracker.remove_from_watchlist(app)

    elif sub == "3":

        tracker.list_watchlist()

 

 

def main():

    print("\n" + "="*55)

    print("       SCREEN TIME TRACKER")

    print("="*55)

    print(f"\n  Data file    : {DATA_FILE}")

    print(f"  Poll interval: every {POLL_INTERVAL} seconds")

    print("\n   Tip: Add specific apps to watchlist for focused tracking.")

    print("         Or track ALL processes without a watchlist.")

 

    tracker = ScreenTimeTracker()

    tracker_thread = None

 

    while True:

        print_menu()

        choice = input("  > ").strip()

 

        if choice == "1":

            if tracker_thread and tracker_thread.is_alive():

                print("\n   Tracker is already running.")

            else:

                tracker_thread = threading.Thread(target=tracker.track, daemon=True)

                tracker_thread.start()

 

        elif choice == "2":

            tracker.report_today()

 

        elif choice == "3":

            tracker.report_week()

 

        elif choice == "4":

            manage_watchlist(tracker)

 

        elif choice == "5":

            app  = input("\n  App name: ").strip()

            mins = int(input("  Daily limit (minutes): ").strip())

            tracker.set_limit(app, mins)

 

        elif choice == "6":

            tracker.show_running_now()

 

        elif choice == "0":

            print("\n   Stopping tracker...")

            tracker.stop()

            if tracker_thread:

                tracker_thread.join(timeout=5)

            print("   Data saved.")

            print("   Goodbye!\n")

            break

 

        else:

            print("   Invalid choice.")

 

 

# ============================================================

# RUN

# ============================================================

 

if __name__ == "__main__":

    main()