📘 Handleiding: Dead Man’s Switch met Python, Gmail & Telegram
Met deze handleiding maak je je eigen Dead Man’s Switch: een systeem dat automatisch e-mails verstuurt als je een bepaalde tijd niet meer reageert. Je hebt hiervoor wel een beetje Linux-kennis nodig. Het script gebruikt Gmail SMTP en een Telegram-bot om alles te regelen.
🔹 Hoe werkt het?
- Het script draait op een Linux-server (ik gebruik zelf een Contabo VPS met Linux).
- Via een Telegram-bot bevestig je regelmatig dat je nog actief bent.
- Doe je dit niet → eerst krijg je een waarschuwing in Telegram, daarna per e-mail.
- Blijf je inactief → dan worden je vooraf ingestelde berichten automatisch verstuurd naar de ontvangers die jij hebt ingesteld.
🔹 Waarom handig?
Met een Dead Man’s Switch kun je ervoor zorgen dat belangrijke informatie of persoonlijke boodschappen alsnog terechtkomen bij de juiste mensen, ook als jij zelf niet meer in staat bent om dit te doen. Denk bijvoorbeeld aan:
- Persoonlijke berichten voor familie of vrienden
- Toegangsinformatie voor digitale accounts
- Belangrijke instructies die niet verloren mogen gaan

🔹 Stap 1. Server voorbereiden
Log in op je Linux server (bijv. via een gehuurde Contabo VPS met Ubuntu/Debian):
ssh user@jouwserverip
⚠️ Deze handleiding gaat ervan uit dat je een eigen Linux-gebruiker hebt aangemaakt en niet direct als root werkt. Root inloggen via SSH is onveilig en wordt daarom afgeraden.
Werk je systeem bij:
sudo apt update && sudo apt upgrade -y
Installeer Python en pip:
sudo apt install -y python3 python3-pip python3-venv git
🔹 Stap 2. Telegram Bot aanmaken
- Open Telegram en zoek naar @BotFather.
- Typ
/newbot
en volg de instructies.- Kies een naam → bv.
MijnDeadmanBot
- Kies een gebruikersnaam die eindigt op
bot
→ bv.mijndeadman_bot
- Kies een naam → bv.
- Je krijgt een API Token, bijvoorbeeld:
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
Chat ID achterhalen
- Stuur een bericht naar je bot (bijv.
Hallo
). - Ga naar je browser en open:
https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/getUpdates
- In de JSON-output zie je
"chat":{"id":123456789}
→ dat is je CHAT ID.
🔹 Stap 3. Gmail App Password aanmaken
- Ga naar https://myaccount.google.com/
- Zet 2-stapsverificatie aan (Beveiliging → Inloggen bij Google).
- Klik op App-wachtwoorden.
- Kies “Mail” + “Ander apparaat” → geef bv. de naam
DeadmanSwitch
. - Je krijgt een 16-cijferig wachtwoord.
👉 Dit gebruik je als EMAIL_PASS.
🔹 Stap 4. Scriptmap maken
Maak een map aan en ga erin:
mkdir ~/deadmanswitch
cd ~/deadmanswitch
Plaats hier:
deadmanswitch.py
(zie onderaan deze handleiding).env
bestand- map
messages/
🔹 Stap 5. .env
bestand
Maak een .env
bestand met je gegevens:
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
TELEGRAM_CHAT_ID=123456789
EMAIL_USER=jouweigenemail@gmail.com
EMAIL_PASS=xxxxxxxxxxxxxxxx
MY_EMAIL=jouweigenemail@gmail.com
EMAIL_NAME=Dead Man's Switch # <- weergavenaam, optioneel
🔹 Stap 6. Python omgeving + pakketten
Maak een virtuele omgeving en installeer de benodigde pakketten:
python3 -m venv venv
source venv/bin/activate
pip install python-telegram-bot python-dotenv
🔹 Stap 7. Berichten aanmaken
Maak een map:
mkdir messages
📂 Structuur van een berichtbestand
- Eerste regel = ontvangers (meerdere mogelijk, gescheiden door komma’s).
- Tweede regel = onderwerp van de mail (
Onderwerp:
ofSubject:
). - Derde regel en verder = het bericht (meerdere regels toegestaan).
Voorbeeld 1 – één ontvanger
messages/bericht1.txt
mijnadres@gmail.com
Onderwerp: TEST – Dead Man’s Switch
Hallo,
Dit is een testbericht.
Voorbeeld 2 – meerdere ontvangers
messages/bericht2.txt
vriend1@example.com, vriend2@example.com, vriend3@example.com
Onderwerp: Belangrijk bericht
Hallo allemaal,
Dit bericht gaat naar meerdere ontvangers tegelijk.
Voorbeeld 3 – meerdere bestanden
messages/
├─ bericht1.txt
├─ bericht2.txt
├─ bericht3.txt
👉 Elk bestand wordt als apart bericht verstuurd bij de finale trigger.
⚠️ Belangrijk: eerst testen
💡 Test altijd eerst met je eigen e-mailadres.
- Maak een bestand in
messages/
met alleen jouw adres. - Controleer of je het bericht ontvangt.
- Pas daarna vul je de echte ontvangers in.
🔹 Stap 8. Script starten
Script aanmaken
Maak het bestand deadmanswitch.py
in de map /home/USERNAME/deadmanswitch
.
Helemaal onderaan deze handleiding vind je de volledige Python-code. Kopieer die code en plak hem in het bestand:
nano /home/USERNAME/deadmanswitch/deadmanswitch.py
Vervang USERNAME
door de naam van jouw Linux-account.
Plak de code, sla op met CTRL+O, ENTER, en sluit af met CTRL+X.
Bestandsrechten instellen
Zorg dat de map en bestanden eigendom zijn van jouw Linux-gebruiker (de gebruiker waarmee je het script draait). Gebruik hiervoor:
sudo chown -R USERNAME:USERNAME /home/USERNAME/deadmanswitch
sudo chmod 600 /home/USERNAME/deadmanswitch/.env
Vervang USERNAME
door de naam van jouw Linux-account.
Activeer de virtuele omgeving en start het script:
cd /home/USERNAME/deadmanswitch
source venv/bin/activate
python3 deadmansswitch.py
Vervang USERNAME
door de naam van jouw Linux-account.
📲 Telegram commando’s
Met de Telegram-bot beheer je de Dead Man’s Switch. Tijden kun je instellen in minuten (m), uren (h) of dagen (d). Zonder eenheid geldt dagen.
Standaardwaarden:
- Check (Telegram): 7d
- Warning (Mail): 14d
- Final (Mail): 21d
Commando’s:
/status
→ toont volgende controle/reset
→ reset timer/setcheck <tijd>
→ Telegram-waarschuwing (bv.30m
,2h
,1d
)/setwarning <tijd>
→ mail-waarschuwing (bv.1h
,3d
)/setfinal <tijd>
→ finale mail (bv.2h
,7d
)/help
→ toont overzicht van alle commando’s
🔹 Stap 9. Automatisch starten na reboot
Maak een systemd service:
sudo nano /etc/systemd/system/deadmanswitch.service
Inhoud:
[Unit]
Description=Dead Man's Switch
After=network.target
[Service]
User=USERNAME
WorkingDirectory=/home/USERNAME/deadmanswitch
ExecStart=/home/USERNAME/deadmanswitch/venv/bin/python3 /home/USERNAME/deadmanswitch/deadmanswitch.py
Restart=always
StandardOutput=append:/home/USERNAME/deadmanswitch/deadmanswitch.log
StandardError=append:/home/USERNAME/deadmanswitch/deadmanswitch.log
[Install]
WantedBy=multi-user.target
➡️ Vervang USERNAME
door je servergebruikersnaam.
Opslaan en afsluiten.
Service activeren:
sudo systemctl daemon-reload
sudo systemctl enable deadmanswitch
sudo systemctl start deadmanswitch
Status bekijken:
sudo systemctl status deadmanswitch
🔹 Stap 10. Logs bekijken
Het script logt naar:
deadmanswitch.log
Bekijk live:
tail -f ~/deadmanswitch/deadmanswitch.log
🐍 Laatste versie deadmanswitch.py
import os
import time
import smtplib
import logging
import telegram
from dotenv import load_dotenv
from email.mime.text import MIMEText
from telegram import Update
from telegram.ext import Application, CommandHandler, CallbackContext
import threading
import sys
import asyncio
import glob
# ----------------------------- Logging -----------------------------
logging.basicConfig(
filename="deadmanswitch.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)
logging.getLogger("telegram").setLevel(logging.WARNING)
# ----------------------------- Config ------------------------------
load_dotenv()
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID") # enkel deze chat mag commando's
EMAIL_HOST = "smtp.gmail.com"
EMAIL_PORT = 587
EMAIL_USER = os.getenv("EMAIL_USER")
EMAIL_PASS = os.getenv("EMAIL_PASS")
MY_EMAIL = os.getenv("MY_EMAIL") # waarschuwing naar jezelf
EMAIL_NAME = os.getenv("EMAIL_NAME") # optioneel: weergavenaam voor From
FINAL_MAIL_FLAG = "final_mail_sent.txt"
MESSAGES_DIR = "messages"
bot = telegram.Bot(token=TELEGRAM_BOT_TOKEN)
last_response_time = time.time()
# Standaard intervals
check_interval = 7 * 86400 # 7 dagen
warning_interval = 14 * 86400 # 14 dagen
final_interval = 21 * 86400 # 21 dagen
lock = threading.Lock()
# Flags om dubbele meldingen te voorkomen
check_sent = False
warning_sent = False
final_sent = False
# Asyncio event loop voor Telegram-sends vanuit background thread
loop = asyncio.new_event_loop()
logging.info("Dead Man’s Switch gestart.")
# ----------------------------- Helpers -----------------------------
def is_authorized(update: Update) -> bool:
"""Alleen commando's van dit chat-id accepteren."""
return str(update.effective_chat.id) == TELEGRAM_CHAT_ID
def send_telegram_message(message: str):
async def async_send_message():
try:
await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message)
except Exception as e:
logging.error(f"Fout bij verzenden Telegram bericht: {e}")
asyncio.run_coroutine_threadsafe(async_send_message(), loop)
def send_email(subject: str, body: str, recipients: list[str]):
msg = MIMEText(body, "plain")
msg["Subject"] = subject
# From: met weergavenaam als EMAIL_NAME is gezet, anders alleen adres
if EMAIL_NAME:
msg["From"] = f"{EMAIL_NAME} <{EMAIL_USER}>"
else:
msg["From"] = EMAIL_USER
msg["To"] = ", ".join(recipients)
try:
with smtplib.SMTP(EMAIL_HOST, EMAIL_PORT) as server:
server.starttls()
server.login(EMAIL_USER, EMAIL_PASS)
server.sendmail(EMAIL_USER, recipients, msg.as_string())
logging.info(f"E-mail verzonden: '{subject}' naar {', '.join(recipients)}")
except Exception as e:
logging.error(f"Fout bij verzenden e-mail: {e}")
def load_messages_from_files():
"""Lees alle .txt berichten uit de map messages/ en parseer ontvangers/onderwerp/tekst."""
messages = []
try:
for file_path in glob.glob(f"{MESSAGES_DIR}/*.txt"):
with open(file_path, "r", encoding="utf-8") as f:
lines = f.read().splitlines()
if len(lines) < 3:
logging.error(f"Berichtbestand onvoldoende regels: {file_path}")
continue
# 1e regel: ontvangers (komma-gescheiden)
recipients = [r.strip() for r in lines[0].split(",") if r.strip()]
# 2e regel: onderwerp
subject_line = lines[1].strip()
if subject_line.lower().startswith("onderwerp:"):
subject = subject_line[len("onderwerp:"):].strip()
elif subject_line.lower().startswith("subject:"):
subject = subject_line[len("subject:"):].strip()
else:
subject = "Een laatste brief"
# Rest: berichttekst (meerdere regels toegestaan)
message = "\n".join(lines[2:]).strip()
if recipients and message:
messages.append((recipients, subject, message))
else:
logging.error(f"Lege ontvangers of bericht in: {file_path}")
return messages
except Exception as e:
logging.error(f"Fout bij inlezen berichten: {e}")
return []
def send_warning_mail():
body = (
"⚠️ Dead Man’s Switch actief!\n\n"
"Er is al een tijd geen activiteit gedetecteerd.\n\n"
"➡️ Gebruik het commando /reset in Telegram om de timer opnieuw te starten.\n"
"Als je dit niet doet, worden na het ingestelde interval je berichten automatisch verstuurd."
)
send_email("⚠️ Waarschuwing: Dead Man’s Switch actief", body, [MY_EMAIL])
logging.info(f"Waarschuwingsmail verzonden naar {MY_EMAIL}.")
def send_final_mail():
"""Verstuur alle berichten; maak daarna het flag-bestand aan en stop."""
global final_sent
if os.path.exists(FINAL_MAIL_FLAG):
logging.info("Final flag bestaat al; stoppen zonder opnieuw te verzenden.")
sys.exit(0)
messages = load_messages_from_files()
if not messages:
logging.error("Geen berichten gevonden in 'messages/'.")
return
total_recipients = 0
for recipients, subject, message in messages:
send_email(subject, message, recipients)
total_recipients += len(recipients)
open(FINAL_MAIL_FLAG, "w").write("sent")
final_sent = True
logging.info(f"Finale mails verzonden naar totaal {total_recipients} ontvangers. Flag aangemaakt en script sluit af.")
sys.exit(0)
# ----------------------------- Telegram Commands -----------------------------
async def set_interval(update: Update, context: CallbackContext, interval_type: str):
if not is_authorized(update):
await update.message.reply_text("🚫 Je bent niet geautoriseerd om dit commando te gebruiken.")
return
global check_interval, warning_interval, final_interval
try:
input_value = context.args[0]
if input_value.endswith("d"):
new_interval = float(input_value[:-1]) * 86400
elif input_value.endswith("h"):
new_interval = float(input_value[:-1]) * 3600
elif input_value.endswith("m"):
new_interval = float(input_value[:-1]) * 60
else:
new_interval = float(input_value) * 86400
with lock:
if interval_type == "check":
check_interval = new_interval
elif interval_type == "warning":
warning_interval = new_interval
elif interval_type == "final":
final_interval = new_interval
logging.info(f"/set{interval_type} ingesteld op {input_value}.")
await update.message.reply_text(f"{interval_type.capitalize()} interval ingesteld op {input_value}.")
except Exception:
await update.message.reply_text(f"Gebruik: /set{interval_type} <tijd> (bijv. '1d', '2h', '30m')")
async def reset_timer(update: Update, context: CallbackContext):
if not is_authorized(update):
await update.message.reply_text("🚫 Je bent niet geautoriseerd om dit commando te gebruiken.")
return
global last_response_time, check_sent, warning_sent, final_sent
with lock:
last_response_time = time.time()
check_sent = False
warning_sent = False
final_sent = False
logging.info("/reset ontvangen → timer gereset.")
await update.message.reply_text("✅ Timer gereset.")
async def show_status(update: Update, context: CallbackContext):
if not is_authorized(update):
await update.message.reply_text("🚫 Je bent niet geautoriseerd om dit commando te gebruiken.")
return
with lock:
next_check = time.strftime('%d-%m-%Y %H:%M:%S', time.localtime(last_response_time + check_interval))
logging.info("/status opgevraagd.")
await update.message.reply_text(f"Volgende controle: {next_check}")
async def show_help(update: Update, context: CallbackContext):
if not is_authorized(update):
await update.message.reply_text("🚫 Je bent niet geautoriseerd om dit commando te gebruiken.")
return
help_text = """
📌 Commando's:
/status - Volgende controle
/setcheck <tijd> - Check-interval
/setwarning <tijd> - Waarschuwingstijd
/setfinal <tijd> - Finale e-mail tijd
/reset - Reset timer
/help - Toon deze lijst
"""
await update.message.reply_text(help_text)
# ----------------------------- Background Task -----------------------------
def start_background_task():
def timer_checker():
global check_sent, warning_sent, final_sent
while True:
time.sleep(60)
with lock:
elapsed_time = time.time() - last_response_time
# 1) Telegram check
if elapsed_time >= check_interval and not check_sent:
send_telegram_message(
"⚠️ Dead Man’s Switch actief!\n"
"Er is al een tijd geen activiteit gedetecteerd.\n\n"
"➡️ Gebruik het commando /reset in Telegram om de timer opnieuw te starten.\n"
"Als je dit niet doet, worden na het ingestelde interval je berichten automatisch verstuurd."
)
check_sent = True
logging.info("Check-bericht via Telegram verstuurd.")
# 2) Waarschuwing per mail
if elapsed_time >= warning_interval and not warning_sent:
send_warning_mail()
warning_sent = True
# (send_warning_mail logt ook)
# 3) Finale mails
if elapsed_time >= final_interval and not final_sent:
send_final_mail()
# (send_final_mail logt & sluit af)
threading.Thread(target=timer_checker, daemon=True).start()
def start_event_loop():
asyncio.set_event_loop(loop)
loop.run_forever()
# ----------------------------- Main -----------------------------
threading.Thread(target=start_event_loop, daemon=True).start()
start_background_task()
application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
application.add_handler(CommandHandler("status", show_status))
application.add_handler(CommandHandler("setcheck", lambda u, c: set_interval(u, c, "check")))
application.add_handler(CommandHandler("setwarning", lambda u, c: set_interval(u, c, "warning")))
application.add_handler(CommandHandler("setfinal", lambda u, c: set_interval(u, c, "final")))
application.add_handler(CommandHandler("reset", reset_timer))
application.add_handler(CommandHandler("help", show_help))
application.run_polling()
Wanneer de finale e-mails worden verstuurd, maakt het script automatisch een flagbestand aan in de scriptmap:
final_mail_sent.txt
- Dit bestand voorkomt dat er meerdere keren dezelfde finale mails worden gestuurd.
- Zodra het script ziet dat dit bestand bestaat, stopt het zichzelf.
👉 Belangrijk:
Als je het script opnieuw wilt gebruiken of opnieuw wilt testen, moet je dit bestand handmatig verwijderen:
rm ~/deadmanswitch/final_mail_sent.txt
Daarna kun je het script weer starten en werkt het opnieuw vanaf nul.