Mailman Spammer mit Python blocken – Teil 2

Ausgehend von meinem ersten Artikel zu diesem Thema habe ich jetzt noch eine Erweiterung des Skripts vorgenommen. Als Spammer erkannte E-Mail-Adressen werden jetzt auch automatisch geblockt.

Dazu suche ich alle „Dauerhaft von der Liste verbannen“ Checkboxen — ihre Namen beginnen alle mit „ban-“ — und klicke sie.

from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options
from selenium.common.exceptions import NoSuchElementException
 
opts = Options()
browser = Firefox(executable_path=r"C:\geckodriver-v0.27.0\geckodriver.exe",
                  options=opts)
browser.implicitly_wait(3)
 
browser.get('<url>')
search_form = browser.find_element_by_name('adminpw')
search_form.send_keys('<password>')
search_form.submit()
 
try:
    field = browser.find_element_by_name('discardalldefersp')
    field.click()
    browser.implicitly_wait(3)
    submit = browser.find_element_by_name('submit')
    submit.click()
except NoSuchElementException:
    print('No new messages to be discarded')
 
browser.implicitly_wait(3)
 
fields = browser.find_elements_by_xpath("//input[@value='3']")
emails = browser.find_elements_by_xpath('//td[contains(text(),"@")]')
banfields = browser.find_elements_by_xpath('//input[contains(@name,"ban-")]')
 
if len(fields) == 0:
    print('No new requests to be discarded, closing browser')
    browser.close()
else:
    if len(fields) == len(emails) and len(fields) == len(banfields) :
        zipped_list = list(zip(emails, fields, banfields))
 
        for i in zipped_list:
            email, field, banfield = i
            if not email.text.endswith(')'):
                field.click()
                banfield.click()

ESP32 unter Windows programmieren

Ein ESP32 ist ein sehr preiswerter Microcontroller, der aber einiges an Leistung „unter der Haube“ hat:

Wie kann man ihn unter Windows programmieren? Ich mache es wie folgt:

  1. Arduino IDE von https://www.arduino.cc/ installieren
  2. In den Voreinstellungen unter „Zusätzliche Boardverwalter-URLs“ die folgende URL hinzufügen: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  3. Boardverwalter-URL

  4. Unter Boardverwalter nach „ESP“ suchen und das Paket installieren
  5. ESP

  6. Eigenen Code oder Demo-Code schreiben/kompilieren und auf den ESP hochladen. Bei mir hat es mit den folgenden Einstellungen geklappt, zusätzlich musste ich aber beim Upload (nach dem Drücken des Upload-Knopfs) einmal den Boot-Button auf dem ESP drücken.
  7. Upload-Settings

Siemens Nixdorf VFD Displays ansteuern mit Python

Vor einigen Monaten habe ich mir günstig auf Ebay ein Siemens Nixdorf BA 63 USB Display gekauft, das VFD (vacuum fluorescent display) Technologie für die Anzeige nutzt und 2×20 Zeichen bietet.

Als passendste Python-Bibliothek nutze ich https://github.com/stephanemouton/VFD-WCN, die Anleitung zur Einrichtung wird im github sehr gut beschrieben.

Hier der Quellcode, der die Ausgabe im Bild erzeugt:

from vfdpos import *
factory=WincorNixdorfDisplayFactory()
VFDs = factory.get_vfd_pos()
MyVFD = VFDs[0]

MyVFD.clearscreen()

MyVFD.poscur(1, 1)
MyVFD.write_msg("Hallo")
MyVFD.write_msg("Welt")
MyVFD.write_msg("1234567abcd\r")
MyVFD.poscur(2, 1)
MyVFD.write_msg("ABCDEFüäößi")

MyVFD.close()

CTAN-Pakete per REST-API hochladen

Vermutlich ist es nicht so bekannt, dass man LaTeX-Pakete auch per REST-API auf CTAN hochladen kann. Das ist insbesondere dann praktisch, wenn man öfter Pakete aktualisieren muss, wie ich es beispielsweise mit der DTK Bibliografie mehrmals im Jahr mache.

Manfred Lotz vom CTAN-Team hat dazu ein Python-Skript geschrieben (https://gitlab.com/Lotz/pkgcheck/blob/master/ctan_upload.py), das diese API befüttert.

Ich habe sein Skript noch ein wenig angepasst (ich erstelle auch die ZIP-Datei damit und kopiere die richtigen Dateien an ihren Platz), mein Skript findet ihr im Github unter https://github.com/dante-ev/dtk-bibliography/blob/master/pack_and_upload_to_ctan.py. Für den Upload selbst benötigt man noch eine TOML-Datei, in der die Upload-Informationen als Key-Value-Paare stehen. Ein Beispiel für eine solche TOML-Datei findet ihr bei Manfred unter https://gitlab.com/Lotz/pkgcheck/-/blob/master/pkgcheck.toml.

Fortnite teilweise blockieren – Erweiterung um Status

This entry is part 2 of 2 in the series Pi-Hole

Im ersten Artikel zu diesem Thema hatte ich schon beschrieben, wie man mit Pi-Hole DNS Lookups so unterbinden kann, dass man Fortnite oder andere Zeitfresser temporär blockieren kann. In diesem Teil erweitern wir das Skript um eine Statusabfrage. Dazu fügen wir einfach eine Route hinzu, die die URL mit dem Parameter /status abfragt. Dann wird der Inhalt der epicstatus.txt abgefragt, die von den Routen /on und /off mit dem Zeitstempel versehen wurde.

import os
from datetime import datetime
from flask import Flask
 
app = Flask(__name__)
 
@app.route('/')
def index():
    return('<h1>Use /off and /on to enable/disable blocking, /status to get epic.log</h1>')
 
@app.route('/<param>')
def setter(param):
    if param=='status':
        with open('/home/pi/epicstatus.txt') as f:
            content = f.read()
            return '<h2>' + content + '</h2>'
    if param=='off':
        os.system("/usr/local/bin/pihole regex '.*\.epicgames.com' > /home/pi/epic.log")
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        with open('/home/pi/epicstatus.txt','w') as ausgabe:
            ausgabe.write(now + ' off')
        return '<h1>Turning off Fortnite</h1>'
    elif param=='on':
        os.system("/usr/local/bin/pihole regex -d '.*\.epicgames.com' > /home/pi/epic.log")
        now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        with open('/home/pi/epicstatus.txt','w') as ausgabe:
            ausgabe.write(now+ ' on')
        return '<h1>Turning on Fortnite</h1>'

Liste meiner genutzten VSC-Plugins

Neben UltraEdit, Notepad++, Emacs, etc. nutze ich auch Visual Studio Code. Hier die Liste der wichtigsten von mir genutzten Plugins:

  • German Language Pack for Visual Studio Code für deutsche Menüs
  • Render Line Endings, um zwischen LF und CRLF umzuschalten
  • Python für die Python-Unterstützung
  • LaTeX Workshop
  • Graphviz (dot) language support for Visual Studio Code für gv
  • Graphviz Preview für ebendas
  • Encryptor for VS Code, Text mit AES 256 zu verschlüsseln
  • Bracket Pair Colorizer um zueinandergehörige Klammern zu markieren

Dateien kopieren, zippen und löschen mit Python

Hier ein kurzes Beispiel, wie man mit Python-Modulen Dateien kopieren, zippen und löschen kann.

import zipfile
from shutil import copyfile
from os import unlink
 
# copy file
copyfile('dtk-authoryear.bbx'  , './dtk-bibliography/dtk-authoryear.bbx')
copyfile('dtk-authoryear.dbx'  , './dtk-bibliography/dtk-authoryear.dbx')
copyfile('dtk-bibliography.pdf', './dtk-bibliography/dtk-bibliography.pdf')
copyfile('dtk-bibliography.tex', './dtk-bibliography/dtk-bibliography.tex')
 
# create the zip file
with zipfile.ZipFile('dtk-bibliography.zip', 'w', zipfile.ZIP_DEFLATED) as z:
    z.write('./dtk-bibliography/README.md')
    z.write('./dtk-bibliography/dtk-authoryear.bbx')
    z.write('./dtk-bibliography/dtk-authoryear.dbx')
    z.write('./dtk-bibliography/dtk-bibliography.pdf')
    z.write('./dtk-bibliography/dtk-bibliography.tex')
 
# delete copied files
unlink('./dtk-bibliography/dtk-authoryear.bbx')
unlink('./dtk-bibliography/dtk-authoryear.dbx')
unlink('./dtk-bibliography/dtk-bibliography.pdf')
unlink('./dtk-bibliography/dtk-bibliography.tex')

Mailman Spammer mit Python blocken

Für Dante e.V. betreue ich einige E-Mail-Listen auf mailman-Basis, die seit einigen Tagen von Spammern geflutet werden. Jeden Tag sind dutzende bis hunderte Aufnahme-Requests in der Liste, die ich manuell wegwerfen müsste. Nachdem ich dies einmal händisch getan hatte, musste eine automatische Lösung gefunden werden.

Die Lösung bestand darin, einen Treiber für Firefox („geckodriver“) zu installieren, der das Fernsteuern des Browsers erlaubt. Dann kann mittels selenium Modul die Steuerung aus Python heraus erfolgen. Unten der wesentliche Quellcode als Basis für eigene Arbeiten, den Teil zum Erkennen von legitimen Anfragen habe ich weggelassen.

Nachtrag vom 25.10.2020: Siehe auch die Erweiterung unter https://www.uweziegenhagen.de/?p=4420

# -*- coding: utf-8 -*-
"""
https://www.edureka.co/community/47679/is-it-possible-to-run-headless-browser-using-python-selenium
"""
 
from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options
 
opts = Options()
#opts.set_headless() # Ich will sehen, wie selenium arbeitet
#assert opts.headless  # Operating in headless mode
browser = Firefox(executable_path=r"C:\Users\Uwe\Downloads\geckodriver-v0.27.0-win64\geckodriver.exe", options=opts)
browser.implicitly_wait(3)
 
# einloggen
browser.get('<url des mailman admin panels')
search_form = browser.find_element_by_name('<passwortfeld_ID>')
search_form.send_keys('<adminpasswort>')
search_form.submit()
 
#wegwerfen Button pro Zeile
fields = browser.find_elements_by_xpath("//input[@value='3']")
#email Adresse des Spammers
emails = browser.find_elements_by_xpath('//td[contains(text(),"@")]')
 
if len(fields) == len(emails):
    zipped_list = list(zip(emails, fields))
 
    for i in zipped_list:
        email, field = i
        field.click()

Linux-Software installieren mit Ansible

Ich habe deutlich mehr als nur ein oder zwei Linux-Rechner, die ich alle gern auf dem aktuellen Software-Stand halte. Da manuelle apt Kommandos auf Dauer lästig werden, habe ich mir jetzt Ansible-Skripte geschrieben, die sehr bald nach dem Aufsetzen eines Linux-Rechners die restliche Arbeit übernehmen. „Sehr bald“ heißt in dem Zusammenhang, nach der grundlegenden Installation und dem Installieren von Ansible selbst mittels apt install ansible. Ansible ist zwar eigentlich dafür gemacht, dutzende, hunderte oder gar tausende Server zu administrieren, aber für die Arbeiten am localhost bietet es auch Vorteile.

Ich nutze dabei zwei Skripte, sogenannte „Playbooks“, eines für die Kombination aus apt update/upgrade und ein Skript für die grundlegende Paketinstallation.

Hier das erste (Quelle: https://www.cyberciti.biz/faq/ansible-apt-update-all-packages-on-ubuntu-debian-linux/):


- hosts: all
become: true
become_user: root
tasks:
- name: Update apt repo and cache on all Debian/Ubuntu boxes
apt: update_cache=yes force_apt_get=yes cache_valid_time=3600

- name: Upgrade all packages on servers
apt: upgrade=dist force_apt_get=yes

Und hier das zweite Playbook für die Installation der grundlegenden Software (lieber aus dem github ziehen, WordPress verändert den Code):


- hosts: all
become: true
become_user: root
tasks:
- name: Install git
apt:
name: git
state: present
update_cache: yes

- name: Install gparted
apt:
name: gparted
state: present
update_cache: yes

- name: Install subversion
apt:
name: subversion
state: present
update_cache: yes

- name: Install emacs
apt:
name: emacs
state: present
update_cache: yes

- name: Install geany
apt:
name: geany
state: present
update_cache: yes

- name: Install vlc
apt:
name: vlc
state: present
update_cache: yes

- name: Install ssh
apt:
name: ssh
state: present
update_cache: yes

- name: Install VSC via snap
snap:
name: code
classic: yes

Ausgeführt werden die Skripte dann mittels


ansible-playbook --connection=local --inventory 127.0.0.1, playbookname.yml

Ansible kann noch unendlich viel mehr, dazu vielleicht in weiteren Beiträgen.

Nachtrag 04.10.2020: Die YAML-Skripte liegen jetzt im Github unter https://github.com/UweZiegenhagen/ansible.

Fehlerhafte SQLite Updates abfangen

Bei einem kleinen Projekt, an dem ich arbeite, fiel mir auf, dass fehlerhafte — d.h. syntaktisch richtige, aber inhaltlich falsche — SQLite Updates im Nirvana verschwinden. Danke Google und Stackexchange war die Lösung wie folgt: fehlerhafte SQL-Updates erzeugen keine Exception, hier muss man nach dem Update den rowcount prüfen, um die Anzahl der aktualisierten Zeilen zu checken. Das folgende Programm illustriert das ganze:

import sqlite3
 
conn = sqlite3.connect(':memory:')
conn.execute('create table if not exists mytable(id INTEGER PRIMARY KEY \
             AUTOINCREMENT, status TEXT, prio TEXT)')
 
today = '2020-08-06'
cursor = conn.cursor()
task = "Foobar"
 
cursor.execute('insert into mytable(status, prio) values (?, ?)',
               [task, today])
conn.commit()
 
my_id = 1 # For my_id = 1 it works, for any other number rowcount is 0
prio = 'A'
cursor = conn.cursor()
cursor.execute('UPDATE mytable SET prio = ? WHERE id = ?', [prio, my_id])
conn.commit()
print(cursor.rowcount)
 
try:
    cursor = conn.cursor()
    cursor.execute('select * from mytable')
    results = cursor.fetchall()
    for entry in results:
        print(entry)
except Exception as err:
    print('Listing failed: %s\nError: %s' % str(err))
 
finally:
    cursor.close()