zwitschiloesch.rb, oder: Wie man mit Ruby den eigenen Twitter-Account leert!

Ein Smartphone mit durchgestrichenem Twitter-Logo, daneben ein Kampfmesser. Twitter ist ein gefährliches Netz-Pflaster geworden. Irgendein schnoddriger Tweet von 2012 kann Dich einholen und Dir in der dort mittlerweile 24/7/365 tobenden Polit- und Ideologie-Schlacht um die Ohren gehauen werden. Davon abgesehen schadet es eh nicht, mal die Netz-Spuren von Jahren ein wenig zu verwischen…

Es ist im Hause Twitter nicht vorgesehen, dass man die Inhalte des eigenen Twitter-Account »einfach so« auf einmal löschen kann. Mit ein paar Zeilen Ruby und dem Twitter-Gem kann man sich aber selbst etwas bauen, was den eigenen Twitter-Account gründlich leert…

(Photo by Jeremy Zero on Unsplash, thanx!)

Twitter: »Du sollst nicht löschen!«

Wie gesagt, das Löschen von vielen (oder allen) Tweets des eigenen Accounts ist bei Twitter nicht vorgesehen. Dort erwartet man, dass man alle Tweets einzeln aufruft, auf »Löschen« klickt und das bestätigt. Das machen sie natürlich deshalb so umständlich wie möglich, weil der große digitale Müllberg der im Laufe der Jahre angefallenen Millionen und Abermillionen von Tweets ihr »Kapital« ist.

Es gibt verschiedene externe Dienste, die einem beim einmaligen oder zeitgesteuerten Löschen der eigenen Tweets unterstützen, aber denen muss man natürlich einen recht umfangreichen Zugriff auf den eigenen Account gewähren. Was immer ungut ist, denn solche Dienste können in die Hand von Leuten fallen, deren Ansinnen Schabernack ist

zwitschiloesch.rb

Also bauen wir lieber selbst etwas. Eine Warnung vorweg:

Dieser Code macht ernst! Wenn man sich das Ruby-Skript anlegt und laufen lässt, dann löscht es unwiderruflich die Tweets des eigenen Twitter-Accounts! Es gibt keine Nachfragen ob man »wirklich will« – es wird gelöscht und die Tweets sind weg!

Vorarbeiten

Bei eigenen Twitter-API-Anwendungen hat man nur eine begrenzte Anzahl an API-Requests pro Zeitraum zur Verfügung, was das Löschen von vielen Tweets (bei meinem seit 2007 existierenden Account über 60.000) zu einer langwierigen Angelegenheit machen würde. Denn um die ID der Tweets zu bekommen, müssten wir diese erst einmal alle einlesen. Nach etwa 3000 Requests wäre dann aber Schluss und man müsste erst einmal 15 Minuten Pause einlegen, um die nächsten 3000 einzulesen.

1. Twitter-Archiv besorgen

Damit das nicht nötig ist, holen wir uns die IDs aller im Account existierenden Tweets auf einem anderen Weg. Man kann sich in Twitter ein Archiv aller Account-Daten erstellen lassen. Nach diversen Abfragen von Passwort, Zugangs-Code etc. bekommt man dann irgendwann einen Downloadlink für das eigene Twitterarchiv. Packt man dieses aus, findet man im Verzeichnis »data« eine Datei namens tweet.js. Die brauchen wir!

2. Twitter-Gem besorgen

Die Kärrnerarbeit der Kommunikatiom mit Twitter übernimmt das Twitter-Gem. Dieses lässt sich unter Ruby 2.4 bis 2.7 mit gem install twitter installieren und ist dann verfügbar.

3. Twitter-App anlegen und Zugang konfigurieren

Vor die Nutzung des Twitter-Gems haben die Twitter-Götter das Erstellen eines Haufen von IDs und Token gesetzt, Details dazu findet man in der Doku des Twitter-Gems. Zum Löschen von Tweets braucht man »Single-user Authentication«. Das ist leider alles etwas kompliziert und aufwändig und die Beschreibung dieser Dinge wäre ein deutlich längerer Artikel als zwitschiloesch selbst. Wie man das angeht, kann man z.B. sich bspw. in envatos Tutorial an einem Beispiel in PHP ansehen. Am Ende des OAuth-Geweses sollte man Consumer-Key und -Secret für seine neue Twitter-API-App sowie ein Access-Token und ein Access-Token-Secret für den Zugriff dieser App auf den Account besitzen. Nun kann man endlich mit dem Löschen beginnen!

Wir löschen unsere Tweets!

Das gesamte Skript ist in einem Github-Gist verfügbar. Um es zu nutzen, müssen zunächst oben im Config-Block einige Einstellungen vorgenommen werden:

  # Config
  require "twitter"
  config = {
    consumer_key:    "Der Twitter-Consumer-Key",
    consumer_secret: "Das Twitter-Consumer-Secret",
    access_token: "Das Access-Token",
    access_token_secret: "Das Access-Token-Secret"
  }
  twitta = Twitter::REST::Client.new(config)

  twarchiv = "/pfad/zur/datei/im/twitter-archiv/namens/tweet.js"
  stopwords = ["nothing","old-school.dev","borussia"]
  rcount = 0

In config müssen die Zugangsdaten zu Twitter (s.o.) gespeichert werden, außerdem braucht die Variable twarchiv den Pfad zu der tweet.js im Verzeichnis data des downgeloadeteten Twitterarchivs (ebenfalls s.o.).

Eine Besonderheit sind die stopwords. Enthält ein Tweet einen String aus diesem Array, so wird dieser nicht gelöscht. Das kann auch eine URL sein, das Skript löst dazu die t.co-Links von Twitter auf. Das ist praktisch, wenn man z.B. das ganze Gelaber löschen, aber Tweets mit Links zum eigenen Blog (oder über Borussia) erhalten möchte. Ausnahme sind »Replies«, das Skript geht davon aus, dass Antworten auf anderer Leuts Tweets immer weg sollen. Wenn man das nicht möchte, muss man das im Code ändern.

Damit ist alles bereit, die Verarbeitung selbst ist ganz einfach. Lediglich die Ermittlung der wahren URL hinter dem t.co-Link ist etwas umständlich, Twitter verwendet da für so etwas einfaches wie einen Tweet unnötig komplizierte Datenstrukturen. Aber auch das bekommen wir hin:

  # Verarbeitung
  JSON.parse(File.read(twarchiv).gsub("window.YTD.tweet.part0 = ","")).each do |tw|
    # Wenn wir Tweets mit Links auf eine bestimmte URL aufheben wollen,
    # muessen wir die richtige URL auslesen, der Tweettext enthält 
    # nur den gekuerzten Link auf t.co.
    tw_url = ''
    tw["tweet"].each do |el|
      if el[0]=="entities"
        el[1].each do |ent|
          if ent[0]=="urls"
            if ent[1].size > 0
              tw_url = ent[1][0]["expanded_url"]
            end
          end
        end
      end
    end
    full_text = "#{tw["tweet"]["full_text"].to_s} #{tw_url}".downcase

    # Ermitteln ob der Tweet eine Reply ist, Replies werden immer geloescht!
    is_reply = false
    if tw["tweet"]["in_reply_to_status_id_str"]
      is_reply = true
    end

    # Entscheiden ob man den Tweet loeschen moechte
    delete_it = false
    if is_reply
      # Replies immer loeschen!
      delete_it = true
    else
      # Schauen ob eines der stopwords enthalten ist
      unless stopwords.any? {|s| full_text.include?(s.downcase)}
        delete_it = true
      end
    end

    # Loeschen
    if delete_it
      begin
        puts "#{rcount}: zwitschiloesch #{tw["tweet"]["id_str"]}"
        twitta.destroy_tweet tw["tweet"]["id_str"].to_s if kill
      rescue Twitter::Error::NotFound => e
        puts "  => ERR not found"
      rescue Twitter::Error::Forbidden => e
        puts "  => ERR forbidden: #{e}"
      end
    else
      puts "#{rcount}: keep           #{tw["tweet"]["id_str"]}"
    end
    rcount += 1
  end

»So geht das!«

Damit ist alles klar, wir können uns an die Arbeit machen. Das Skript gibt es in einem Github-Gist, nach der Änderung der Config kann man es laufen lassen. Noch einmal eine Warnung: Das Skript fragt nicht mehr nach, es tut was es machen soll: Löschen! Und nichts bringt einmal gelöschte Tweets wieder zurück. Wat fott es es fott!

Allerdings nicht so ganz, führt man es einfach mit ruby zwitschiloesch.rb aus, so gibt es zunächst einmal nur aus, was es machen würde. Möchte man tatsächlich zur Tat schreiten, so muss man noch den Parameter kill angeben:

  ruby zwitschiloesch.rb kill

Dann gibt es kein zurück mehr, wie der Protagonist Billy Pilgrim in Kurt Vonneguts »Schlachthof 5« stets sagt, wenn jemand getötet wird: »So geht das!«

Übrigens scheint es für das Löschen via Twitter-API kein Limit zu geben, das Entfernen von über 60.000 Tweets aus meiner tweet.js lief ohne Probleme durch.

Mehr Spaß mit dem Twitter-Gem

Da man nun die ganze Mühsal der Konfiguration hinter sich hat, kann man das Skript auch als Basis für jegliche Aktionen mit dem Twitter-Gem nutzen, denn das kann eine ganze Menge. Z.B. könnte man ein Skript bauen, das regelmäßig per Cron den eigenen Account scannt und neu dazugekommene Tweets nach einem definierten Zeitraum löscht. Das wird das Thema eines weiteren Artikels sein!

ruby twitter

Autor: