LibreOffice: Inhalts- und Stichwortverzeichnis automatisch aktualisieren

Sunday, November 4, 2018

Die Verzeichnisse wie Inhalts- oder Stichwortverzeichnis in einem LibreOffice Writer Dokument, können beim Speichern oder Exportieren automatisch aktualisiert werden. Dazu verknüpft man folgendes Makro an die Ereignisse Dokument speichern und Dokumentkopie speichern oder exportieren.

Den Dialog dazu findet man unter Extras/Makros/Bearbeiten und für das Zuweisen unter Extras/Makros/Verwalten/LibreOffice Basic/Zuweisen.

loMakroDocumentIndexesUpdate1.png

loMakroDocumentIndexesUpdate2.png

Das Makro

REM  *****  BASIC  *****

Sub Main

End Sub	

REM Verzeichnisse aktualisieren
REM Siehe auch: https://www.oooforum.de/viewtopic.php?f=18&t=70890
REM             https://forum.openoffice.org/en/forum/viewtopic.php?f=20&t=2557
Sub DocumentUpdateIndexes
	oIndexes = ThisComponent.getDocumentIndexes()
	for i = 0 to oIndexes.getCount () - 1
		oIndexes (i).update
	next i
End Sub

Sub DokumentSpeichern
	DocumentUpdateIndexes
End Sub

Sub DokumentkopieSpeichernOderExportieren
	DocumentUpdateIndexes
End Sub

PhantomJS: Alle Hyperlinks einer Website auflisten, mit Unterstützung für JavaScript und Frames

Friday, June 1, 2018

Das folgende PhantomJS-Skript listlinks.js, listet alle Hyperlinks einer Website auf, wobei JavaScript und Frames unterstützt werden.

Damit ist es z.B. möglich…

  • sich über Gratisaktionen von Steam Informieren lassen (Free Weekend/Free For a Limited Time)
  • Werbebanner von Google Ads/Ad Scence automatisiert anklicken

Hinweis: Mittlerweile auch als Projekt auf Github zu finden: https://github.com/1nn3/listlinks

#!/usr/bin/phantomjs
// listlinks.js
// Lists all hyperlinks of an URL, supports JavaScript and Frames
// See also: http://phantomjs.org/api/
"use strict";

// Um Endlosrekursion in der Frametiefe zu erkennen und abzufangen
// FIXME: Make it a constant
// const FRAME_RECURSION_MAX_DEPTH = 10;
// SyntaxError: Unexpected token 'const'
// See: https://github.com/ariya/phantomjs/issues/14521
var FRAME_RECURSION_MAX_DEPTH = 10;

function getAttributesFromAllElementsByTagName(tagName, attributeList) {
	var elements = window.document.getElementsByTagName(tagName);

	var values = [];
	for (var i = 0; i < elements.length; i++) {
		values.push({});

		for (var j = 0; j < attributeList.length; j++) {
			values[i][attributeList[j]] = elements[i].getAttribute(attributeList[j]) || "";
		}

		values[i]["textContent"] = elements[i].textContent || "";
	}
	return values;
}

function listLinks () {
	var a = page.evaluate(getAttributesFromAllElementsByTagName, "a", ["href"]);

	for (var i = 0; i < a.length; i++) {
		// FIXME: Use the class URL to get an absolut URL
		// See: https://github.com/ariya/phantomjs/issues/14349
		var href = new URL(a[i]["href"], page.url).href || a[i]["href"];
		var textContent = a[i]["textContent"].replace(/\s+/g, " ");
		system.stdout.writeLine(href + "\t" + textContent);
	}
}

function getFrames (parrents) {
	var a = page.evaluate(getAttributesFromAllElementsByTagName, "iframe", []);

	var frames = [];
	for (var i = 0; i < a.length; i++) {
		frames.push(parrents.concat([i])); // frame position
	}
	return frames;
}

function walkThroughFrames (frames) {
	for (var i = 0; i < frames.length; i++) {
		system.stderr.writeLine("I: Switching to frame: " + frames[i].toString() + " frameName=" + page.frameName + " frameURL=" + page.frameUrl);

		if (frames[i].length > FRAME_RECURSION_MAX_DEPTH) {
			system.stderr.writeLine("E: FRAME_RECURSION_MAX_DEPTH reached!");
			continue;
		}

		page.switchToMainFrame();
		for (var j = 0; j < frames[i].length; j++) {
			page.switchToFrame(frames[i][j]);
		}

		listLinks();
		walkThroughFrames(getFrames(frames[i])); // subframes
	}
}

var system = require('system');

if (system.args.length != 2) {
	system.stderr.writeLine("Usage: " + system.args[0] + " <url>");
	phantom.exit(1);
}

var page = require("webpage").create();
page.settings.javascriptEnabled = true;
page.settings.resourceTimeout = 10*1000; // milliseconds
page.settings.userAgent = "";

page.open(system.args[1], function (status) {
	if (status !== "success") {
		system.stderr.writeLine("E: Fail to load URL: " + system.args[1]);
		phantom.exit(1);
	}

	listLinks();
	walkThroughFrames(getFrames([]));
	phantom.exit(0);
});

Download: fp-content/attachs/listlinks.js

Zur Installation laden das Skript nach /usr/local/bin/ herunter und machen es ausführbar. Dies ist für die Beispiele unten notwendig!

# wget -O /usr/local/bin/listlinks https://0010100.net/blog/fp-content/attachs/listlinks.js
# chmod +x /usr/local/bin/listlinks

Benutzt wird es dann so:

$ listlinks <URL>

Sich über Gratisaktionen von Steam Informieren lassen (Free Weekend/Free For a Limited Time)

Mit dem Shell-Skript free-games-on-steam.sh, kann man sich über Gratisaktionen von Steam informieren lassen (Free Weekend/Free For a Limited Time). Dazu erstellt man einen Cronjob: @hourly free-games-on-steam 2>/dev/null

So bekommt man alle neuen Gratisaktionen via Mail mitgeteilt.

#!/bin/sh

set -e

seen="${XDG_CACHE_HOME:-$HOME/.cache}/free-games-on-steam.seen"
listlinks="$(which listlinks)"
url="https://store.steampowered.com/news/?headlines=1"

mkdir -p "$(dirname "$seen")"
touch "$seen"

xvfb-run --auto-servernum $listlinks "$url" | cut -f 2 | grep -i -w "free" | while read REPLY; do
	if grep -q --line-regexp "$REPLY" "$seen"; then
		continue
	fi

	echo "$REPLY" | tee --append "$seen"
done

cut-cache "$seen"

Download: fp-content/attachs/free-games-on-steam.sh

Werbebanner von Google Ads/Ad Scence automatisiert anklicken

Mit dem Shell-Skript gadclick.sh, können Werbebanner von Google Ads/Ad Scence automatisiert anklicken werden. Das funktioniert allerdings nicht immer/mit allen Websites (Ich tippe mal auf ein Timing-Problem o.ä.).

#!/bin/sh

set -e

temp="$(/bin/mktemp --tmpdir=/dev/shm/)"
trap "rm -f -- '$temp'" 0 1 2 3 15 # Lösche Datei beim Beenden, Abbruch ...

sites="${XDG_CONFIG_HOME:-$HOME/.config}/gadclick.sites"

log="${XDG_CACHE_HOME:-$HOME/.cache}/gadclick.log"
mkdir -p "$(dirname "$log")"
touch "$log"

shuf "$sites" | while read REPLY; do

	xvfb-run --auto-servernum listlinks "$REPLY" | cut -f 1 >"$temp" || continue

	cat <<! | grep -E -f - "$temp" | shuf -n 1 | wget -i - -qO /dev/null || continue
^https?://adclick\.g\.doubleclick\.net/
^https?://googleads\.g\.doubleclick\.net/
^https?://www\.googleadservices\.com/
!

	date +"%D %T	$REPLY" >>"$log"

done

cut-cache "$log"

Download: fp-content/attachs/gadclick.sh

[h2]cut-cache[(h2]

#!/bin/sh
# Cache kürzen
# cut-cache <path/to/cachefile> [keep [threshold]]

set -e

cachefile="$1"
keep="${2:-100}"
threshold="${3:-500}"

if test "$( cat "$cachefile" | wc -l )" -gt "$threshold"; then
	temp=$(mktemp)
	tail -n "$keep" "$cachefile" >"$temp"
	mv "$temp" "$cache"
fi

Download: fp-content/attachs/cut-cache.sh

Wie die anderen Skript auch, kann cut-cache.sh nach /usr/local/bin/ installiert und ausführbar gemacht werden:

$ wget -O /usr/local/bin/listlinks https://0010100.net/blog/fp-content/attachs/listlinks.js
$ wget -O /usr/local/bin/free-games-on-steam https://0010100.net/blog/fp-content/attachs/free-games-on-steam.sh
$ wget -O /usr/local/bin/gadclick https://0010100.net/blog/fp-content/attachs/gadclick.sh
$ wget -O /usr/local/bin/cut-cache https://0010100.net/blog/fp-content/attachs/cut-cache.sh
$ chmod +x /usr/local/bin/{listlinks,free-games-on-steam,gadclick,cut-cache}

Anhang: Weitere Lösungen mit Selenium

Die Skripts nutzen z.Z. Chromium, aber Firefox sollte auch funktionieren (entsprechenden Code aktivieren).

PhantomJS funktioniert z.Z. nicht (könnte aber einzurichten sein):

Message: Error - Unable to load Atom ‘find_elements’ from file ‘:/ghostdriver/./third_party/webdriver-atoms/find_elements.js’

Perl: Zufälligen Schlüssel/Key eines Hash

Friday, August 4, 2017

Auf einen zufälligen Schlüssel eines Hash, kann man mit der Funktion keys zugreifen. Die Funktion keys, gibt alle Schlüssel eines Hash, als Liste zurück, von der dann ein Wert zufällig abgefragt werden kann:

#!/usr/bin/perl

my %alphabet = (
    A => 1,
    B => 2,
    C => 3,
    D => 4,
    E => 5,
    F => 6,
    G => 7,
    H => 8,
    I => 9,
    J => 10,
    K => 11,
    L => 12,
    M => 13,
    N => 14,
    O => 15,
    P => 16,
    Q => 17,
    R => 18,
    S => 19,
    T => 20,
    U => 21,
    V => 22,
    W => 23,
    X => 24,
    Y => 25,
    Z => 26,
);

my @keys = keys %alphabet;

# zufälliger Key

my $buchstabe = $keys[ rand @keys ];
my $position  = $alphabet{$buchstabe};

printf "Der Buchstabe %s, ist der %i. im Alphabet.\n", $buchstabe, $position;

Es ist auch möglich, direkt auf einen zufälligen Wert eines Hash zuzugreifen. Das funktioniert, analog mit der Funktion values:

my @values = values %alphabet;
print $values[ rand @values ] . "\n";

PS: In den Kommentaren, hat Christian eine etwas schnellere Möglichkeit aufgezeigt:

my @list = %alphabet;
my $random_key = $list[(1|rand@list) -1];
my $random_val = $list[1|rand@list];

Das bitweise ODER mit 1 sorgt dafür, dass das letzte Bit des Ergebnisses von rand auf 1 gesetzt wird. Dadurch entsteht immer eine ungerade Zahl (1|0 => 1, 1|1 => 1, 1|2 => 3, 1|3 => 3, …). Es wird also nur auf ungerade Indizes des Arrays zugegriffen, wo in diesem Fall die Values abgelegt sind. Da dabei immer eine Zahl größer 0 generiert wird, kann mit einem einfachen -1 immer auf die geraden Indizes mit den Keys zugegriffen werden.

FlatPress: Statistik über Klickzahlen erzeugen

Thursday, June 29, 2017

Die FlatPress Blog Engine, bietet leider keinerlei Statistik über Klickzahlen usw.
Deswegen habe ich mir eine Skriptlösung geschrieben, die mir jetzt zeigt, wie viele Klicks ein Beitrag erzeugt (Über die Laufzeit der Logs).

Das Skript

Das Skript liegt auf meinem Webspace (bei Uberspace.de) als ~/bin/clicks.sh vor, die Logs unter ~/log/ und die FlatPress-Installation unter ~/html/blog/.

#!/bin/bash
set -e
cd

date

awk '
/ "(HEAD|GET) \/blog\/\?x=entry:entry[0-9-]+ .+" 200 / {
	entrys[$7]++
	counter++
}
/ "(HEAD|GET) \/blog\/fp-content\/attachs\/.+ .+" 200 / {
        attachs[$7]++
	counter++
}
END {
        for(i in entrys) {
                file="html/blog/fp-content/content/*/*/" substr(i, 16) ".txt"

                subject="N/A"
                "cut -d \\| -f 4 " file " 2>/dev/null" | getline subject

                printf "%5d %5.3g%% %s %s\n",
                        entrys[i],
                        100 / counter * entrys[i],
                        i,
                        subject
        }

        for(i in attachs) {
                printf "%5d %5.3g%% %s\n",
                        attachs[i],
                        100 / counter * attachs[i],
                        i
        }

        printf "%5d insgesamt\n", counter
}
' <(cat logs/access_log; zcat logs/access_log.*.gz) | sort -n

Als Beispielausgabe, hier mal die Top Ten der häufigsten Klicks aktuell (absolute Klicks und prozentual):

$ clicks.sh | tail -n 11
   31 0.455% /blog/?x=entry:entry170609-233049 Payday 2 kostenlos bei Steam
   46 0.675% /blog/?x=entry:entry160205-145935 Anonymous; Netzwerkverkehr eines Benutzers über das Tor-Netzwerk routen, zur Anonymisierung von Verbindungsdaten
   52 0.763% /blog/?x=entry:entry170511-162559 LOVOO bekommt eine "Kuppelfunktion"; Ich habe den Prototypen getestet
   53 0.777% /blog/?x=entry:entry150905-111426 Automatischer Login: nodm (an automatic display manager)
   65 0.953% /blog/?x=entry:entry160508-093015 CyanogenMod auf dem Samsung Galaxy S4 mini GT-I9195 (serranoltexx) installieren
  177   2.6% /blog/?x=entry:entry170629-155523 FlatPress: Klick-Statistik erzeugen
  777  11.4% /blog/?x=entry:entry170422-211028 Debian 9 (Stretch): Oracle VM VirtualBox installieren
 1249  18.3% /blog/?x=entry:entry170626-172443 Smartphones mit Unterstützung von LineageOS und TWRP
 1494  21.9% /blog/?x=entry:entry170608-122844 Perl: Dateien von I2P Eepsites und/oder Tor hidden services downloaden
 2342  34.4% /blog/?x=entry:entry170526-135407 openresolv
 6817 insgesamt

Das Skript kann man bestimmt noch verbessern und schöner schreiben.
Gerade die regulären Ausdrücke, welche die Klicks aus dem Log filtern, matchen vielleicht nicht hundertprozentig, was die Statistik dann verfälscht.
Hier mal Ausnahmen, die das Beispiel oben nicht abdeckt (So gefunden im Log):

  • /blog/?x=entry%3Aentry170626-172443 (URL Decode/Encode)
  • //blog/?x=entry:entry150805-091827

Ein weiterer fehleranfälliger Punkt ist, den Subject anhand des URL auszulesen. Das betrifft vor allem gelöschte Beiträge, die aber noch im Index der Suchmaschinen vorhanden sind.

PS: Eine tagesaktuelle Statistik über den Blog, gibt es jetzt unter https://0010100.net/tmp/clicks.txt.

Perl: Dateien von I2P Eepsites und/oder Tor hidden services downloaden

Thursday, June 8, 2017

Ich programmiere einen Feedreader.

Dieser hat nun auch das Proxy Attribut des Perl Moduls LWP::UserAgent implementiert, um z.B. Feeds von I2P Eepsites und/oder Tor hidden services abonnieren zu können.

Wie man mittels der Perl Module URI::Fetch und LWP::UserAgent, eine solche URL downloadet, will ich hier kurz skizzieren:

#!/usr/bin/env perl

use URI;
use URI::Fetch;

use LWP::UserAgent;
use LWP::Protocol::socks;

my $uri = URI->new("http://example.i2p/");

my $ua = LWP::UserAgent->new();
$ua->proxy( "http", "http://localhost:4444" );

my $response = URI::Fetch->fetch( $uri, UserAgent => $ua )
  or die URI::Fetch->errstr;

print $response->content;

Eepsite: Die (Pseudo-)Top-Level-Domain .i2p

Es wird mittels $ua->proxy( "http", "http://localhost:4444" ); der zu verwendende HTTP-Proxy gesetzt. Analog funktioniert das so auch mit anderen Protokollen, wie z.B. HTTPS, FTP usw.

Siehe auch: http://search.cpan.org/perldoc/LWP::UserAgent.

Tor hidden services: Die (Pseudo-)Top-Level-Domain .onion

Für das SOCKS-Protokoll, muss zusätzlich das Modul LWP::Protocol::socks eingebunden werden.

Um bspw. eine Datei von einem Tor hidden service downzuloaden, muss $ua->proxy( "http", "socks://localhost:9050" ); gesetzt werden.

Siehe auch: http://search.cpan.org/perldoc/LWP::Protocol::socks.