📘 Handleiding: Dead Man’s Switch
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 gehuurde 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:
nano ~/deadmanswitch/.env
Plak de volgende inhoud en vul je eigen gegevens in:
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
TELEGRAM_CHAT_ID=123456789
EMAIL_USER=jouweigenemail@gmail.com
EMAIL_PASS=xxxxxxxxxxxxxxxx
MY_EMAIL=jouweigenemail@gmail.com # mail waarschuwing naar jezelf
EMAIL_NAME=Dead Man's Switch # weergavenaam, optioneel
Opslaan en afsluiten:
CTRL+O
→ Enter (opslaan)CTRL+X
→ afsluiten
🔹 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
Nu maak je de berichten die automatisch verstuurd moeten worden.
Deze bestanden plaats je in de map messages
. Je kunt er meerdere maken, bijvoorbeeld bericht1.txt
, bericht2.txt
, enz.
ℹ️ Let op: de bestandsnaam mag je zelf kiezen (bijv. bericht1.txt
, geheim.txt
, test123.txt
), zolang deze maar eindigt op .txt
. Het script leest automatisch alle .txt
bestanden in de map messages
.
1. Maak de map aan (als die nog niet bestaat):
mkdir -p ~/deadmanswitch/messages
2. Maak een nieuw berichtbestand aan met nano, bijvoorbeeld:
nano ~/deadmanswitch/messages/bericht1.txt
3. Zet de inhoud in dit bestand in dit formaat:
ontvanger1@example.com, ontvanger2@example.com
Onderwerp: Mijn laatste bericht
Dit is de tekst van mijn bericht.
Je mag meerdere regels gebruiken.
- Eerste regel = ontvangers (meerdere adressen scheiden met komma’s)
- Tweede regel = onderwerp (begin met
Onderwerp:
ofSubject:
) - Rest = het bericht zelf, zoveel regels als je wilt
4. Opslaan en afsluiten:
CTRL+O
→ Enter (opslaan)CTRL+X
→ afsluiten
5. Herhaal dit voor extra berichten (bericht2.txt
, geheim.txt
, enz.).
👉 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 ~/deadmanswitch
.
Helemaal onderaan deze handleiding vind je de volledige Python-code. Kopieer die code en plak hem in het bestand:
nano ~/deadmanswitch/deadmanswitch.py
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 ~/deadmanswitch
sudo chmod 600 ~/deadmanswitch/.env
Vervang USERNAME
door de naam van jouw Linux-account. Voer deze commando’s uit terwijl je bent ingelogd als de juiste gebruiker (USERNAME).
Activeer de virtuele omgeving en start het script:
cd ~/deadmanswitch
source venv/bin/activate
python3 deadmansswitch.py
📲 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=on-failure
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.
CTRL+O
→ Enter (opslaan)CTRL+X
→ 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
Om deadmanswitch.log
leeg te maken zonder het bestand zelf te verwijderen:
> ~/deadmanswitch/deadmanswitch.log
🔹 Stap 11. Finale e-mails
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.
🐍 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 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")
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")
EMAIL_NAME = os.getenv("EMAIL_NAME")
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:
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
msg["From"] = f"{EMAIL_NAME} <{EMAIL_USER}>" if EMAIL_NAME else 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():
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
recipients = [r.strip() for r in lines[0].split(",") if r.strip()]
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"
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])
async def send_final_notice(total_recipients):
try:
await bot.send_message(
chat_id=TELEGRAM_CHAT_ID,
text=f"📩 Alle finale mails zijn verzonden ({total_recipients} ontvangers).\n"
"Het Dead Man’s Switch script stopt nu definitief."
)
except Exception as e:
logging.error(f"Fout bij laatste Telegram-bericht: {e}")
def send_final_mail():
global final_sent
if os.path.exists(FINAL_MAIL_FLAG):
logging.info("Final flag bestaat al; stoppen zonder opnieuw te verzenden.")
os._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"Totaal {total_recipients} ontvangers gemaild. Flag aangemaakt en script sluit af.")
# 👉 Synchroon slotbericht versturen vóór afsluiten
asyncio.run(send_final_notice(total_recipients))
os._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
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.")
if elapsed_time >= warning_interval and not warning_sent:
send_warning_mail()
warning_sent = True
if elapsed_time >= final_interval and not final_sent:
send_final_mail()
threading.Thread(target=timer_checker, daemon=True).start()
def start_event_loop():
asyncio.set_event_loop(loop)
loop.run_forever()
# ----------------------------- Main -----------------------------
if os.path.exists(FINAL_MAIL_FLAG):
logging.info("Final flag bestaat al bij start → script stopt direct.")
os._exit(0)
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()