Vous êtes ici

open source

Synchroniser une timeline Twitter sur un compte Mastodon

Face aux limites imposées aux clients non officiels du réseau social Twitter - pourtant généralement meilleurs que l'officiel - notamment la limite des 200 statut par requête et des 15 requêtes toutes les 15 minutes maximum ou encore la non conservation de la position dans la timeline, j'ai eu l'idée de synchroniser le contenu de la timeline sur celui d'un compte privé Mastodon par l'intermédiaire d'un script, puisqu'en général instances et clients Mastodon son nettement moins limités. Il suffirait ensuite d'appeler de façon régulière ce script sans interaction humaine (car on ne peut pas passer sa vie à récupérer des tweets !).

Voici donc un début de résultat.

Pour l'exploiter à votre tour, il vous faudra au minimum PHP et son extension CURL. Sans oublier de faire le nécessaire pour obtenir vos jetons d'accès développeurs sur votre instance Mastodon et Twitter...

Son utilisation dans crontab sera ensuite on ne peut plus simple :

*/1 * * * * sudo -u www-data php /chemin/twitter_to_mastodon_timeline.php

Le script PHP

<?php // simple-php-twitter-to-mastodon-timeline.php

chdir('/script/path');

    function buildBaseString($baseURI, $method, $params) {
        $r = array();
        ksort($params);
        foreach($params as $key=>$value){
            $r[] = "$key=" . rawurlencode($value);
        }
        return $method."&" . rawurlencode($baseURI) . '&' . rawurlencode(implode('&', $r));
    }

    function buildAuthorizationHeader($oauth) {
        $r = 'Authorization: OAuth ';
        $values = array();
        foreach($oauth as $key=>$value)
            $values[] = "$key=\"" . rawurlencode($value) . "\"";
        $r .= implode(', ', $values);
        return $r;
    }


    $url = 'https://api.twitter.com/1.1/statuses/home_timeline.json'; // or home_timeline.json

    // Custom Twitter data

    $oauth_access_token = " ";
    $oauth_access_token_secret = " ";
    $consumer_key = " ";
    $consumer_secret = " ";

    // Custom Mastodon data

    $mastodon_token = ' ';
    $mastodon_instance_url = 'https://instance.url'; // example : https://framapiaf.org'

    $curl_opt_url = $mastodon_instance_url . "/api/v1/statuses";

    $oauth = array( 'oauth_consumer_key' => $consumer_key,
                    'oauth_nonce' => time(),
                    'oauth_signature_method' => 'HMAC-SHA1',
                    'oauth_token' => $oauth_access_token,
                    'oauth_timestamp' => time(),
                    'oauth_version' => '1.0');

    $base_info = buildBaseString($url, 'GET', $oauth);
    $composite_key = rawurlencode($consumer_secret) . '&' . rawurlencode($oauth_access_token_secret);
    $oauth_signature = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true));
    $oauth['oauth_signature'] = $oauth_signature;

    $header = array(buildAuthorizationHeader($oauth), 'Expect:');
    $options = array( CURLOPT_HTTPHEADER => $header,
                      CURLOPT_HEADER => false,
                      CURLOPT_URL => $url,
                      CURLOPT_RETURNTRANSFER => true,
                      CURLOPT_SSL_VERIFYPEER => false);

    $feed = curl_init();
    curl_setopt_array($feed, $options);
    $json = curl_exec($feed);
    curl_close($feed);

    $twitter_data = json_decode($json);

    $lastid='';

    $lastidfile = 'lastid.txt';
    $current = file_get_contents($lastidfile);
    $content = "";
    $i = 0;

    foreach ($twitter_data as $twt) {
        if ($i == 0) {$lastid = $twt->id_str;}
        if ($twt->id_str == $current OR !isset($twt->id_str)) { break;  } else {

        $headers = [
        'Authorization: Bearer ' . $mastodon_token
        ];

        // You can change twitter API fields here :
        $toots[] = $twt->created_at . " -  " . $twt->user->name . "\r\r " . $twt->text . "\r\r https://twitter.com/" . $twt->user->screen_name . "/stat$

        $i++;
        }

}

if (!isset($toots)) { } else { 

$toots = array_reverse($toots);

foreach ($toots as $toot) {

$status_data = array(
  "status" => $toot,
  "language" => "fre",
  "visibility" => "private"
);

$ch_status = curl_init();
curl_setopt($ch_status, CURLOPT_URL, $curl_opt_url);
curl_setopt($ch_status, CURLOPT_POST, 1);
curl_setopt($ch_status, CURLOPT_POSTFIELDS, $status_data);
curl_setopt($ch_status, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch_status, CURLOPT_HTTPHEADER, $headers);

$output_status = json_decode(curl_exec($ch_status));

curl_close ($ch_status);

}

file_put_contents($lastidfile, $lastid);

}

?>

Revue de web automatisée sous Linux avec Semantic Scuttle, PHP, CutyCapt, GraphicsMagick ...

Les prérequis techniques

Pour ma part j'utilise de longue date Semantic Scuttle, qui me facilite grandement l'enregistrement des URL, notamment grâce à une extension pour Firefox ou le bookmarklet dédié. À défaut vous pourrez facilement adapter ce code avec une source de données en markdown... ou autre.

Certes, cela représente beaucoup de dépendances, mais on les trouve facilement sous GNU/Linux et ces composants restent gratuits.

Le script PHP


<?php // /chemin/script/RevueWeb.php $uneDate = date('Y-m-d'); $maDate = new DateTime($uneDate); $dateFormatee = date_format($maDate, 'Ymd'); $dateLisible = date_format($maDate, 'd/m/Y'); $imgDir = "/chemin/script/"; require_once 'src/Feed.php'; // (RSS to PHP) // Flux RSS des pages enregistrées dans Scuttle dédiés à votre revue de web : $rss = Feed::loadRss('https://instance.semantic.scuttle/rss.php/frenchhope/tag_de_la_revue_de_web?sort=date_desc&count=2048&privateKey=ma.cle.privee'); $versionMarkdown = "## Revue de web du $dateLisible" . PHP_EOL . PHP_EOL; // Titre // contenu foreach ($rss->item as $item) { $versionMarkdown .= "[" . $item->title . "](" . $item->link . ")" . PHP_EOL . PHP_EOL; // on formate le titre et les hyperliens // on réalise des captures d'écran au format JPEG, c'est plus joli et ça ne tiens pas trop de place. // pour cela on crée un serveur d'affichage virtuel (serveur X) avec xvfb-run parce qu'en ligne de commande sur un serveur il n'y en a pas ! // Voir la documentation de CutyCapt pour les options. $hash = hash('sha256',$item->link); $imgFile = $imgDir . "webScreenShot_" . $hash . ".jpg"; exec('xvfb-run --server-args="-screen 0, 1900x1080x24" cutycapt --min-width=1900 --min-height=1080 --smooth --url="' . $item->link . '" --out="' . $imgFile . '"'); // Recadrage avec GraphicsMagick exec('gm convert ' . $imgFile . ' -crop 1900x1080+0+0 ' . $imgFile); // Redimentionnement avec GraphicsMagick exec('gm mogrify -resize 950x540 -quality 65 ' . $imgFile); $versionMarkdown .= "![]($imgFile)" . PHP_EOL . PHP_EOL; $versionMarkdown .= "---" . PHP_EOL . PHP_EOL; } // Tout dans un fichier avec la date du jour file_put_contents("$dateFormatee.md", $versionMarkdown, FILE_APPEND | LOCK_EX); ?>

Affichage de la revue dans une page web

Pour ma part je copie ce fichier Markdown dans mon dossier de données Nextcloud car avec l'application Pico CMS je peux l'afficher directement. Dans ce cas il faut mettre à jour la base Nextcloud :

sudo -u www-data php /var/www/nextcloud/occ files:scan [nom d'utilisateur]"

Mais vous pourriez tout aussi bien créer un script en PHP pour afficher le Markdown dans une page web avec parsedown par exemple.

L'alternative à base de source de fichier Markdown plutôt que Semantic Scuttle et RSS

remplacer :

$rss = Feed::loadRss('https://instance.semantic.scuttle/rss.php/frenchhope/tag_de_la_revue_de_web?sort=date_desc&count=2048&privateKey=ma.cle.privee');

par :

$contenu = file_get_contents('/chemin/du/fichier.md');
$regex = "/\[([^\[\]]*)\](.*)/";
preg_match_all("`$regex`", $contenu, $matches);  

et :

foreach ($rss->item as $item) {
$versionMarkdown .= "[" . $item->title . "](" . $item->link . ")" . PHP_EOL . PHP_EOL; 
$hash = hash('sha256',$item->link);
$imgFile = $imgDir . "webScreenShot_" . $hash . ".jpg";
exec('xvfb-run --server-args="-screen 0, 1900x1080x24" cutycapt --min-width=1900 --min-height=1080 --smooth --url="' . $item->link . '" --out="' . $imgFile . '"');

par :

foreach ($matches as $item) {
$versionMarkdown .= "[" . $item[0] . "](" . $item[1] . ")" . PHP_EOL . PHP_EOL; 
$hash = hash('sha256',$item[1]);
$imgFile = $imgDir . "webScreenShot_" . $hash . ".jpg";
exec('xvfb-run --server-args="-screen 0, 1900x1080x24" cutycapt --min-width=1900 --min-height=1080 --smooth --url="' . $item[1] . '" --out="' . $imgFile . '"');

Utilisation

  • En ligne de commande
sudo -u www-data php /chemin/script/RevueWeb.php
  • Ou depuis l'URL de la page "RevueWeb.php" si vous la publiez avec un serveur web

http://mon.serveur.web/RevueWeb.php

  • Puis téléchargement d'une revue :

http://mon.serveur.web/20200127.md

Automatisation avec cron

Tous les jours à 20h00 par exemple :

00 20 * * *    sudo -u www-data php /chemin/script/RevueWeb.php

Installer un certificat électronique sur un serveur web derrière un reverse proxy avec ubuntu, apache et certbot

Si vous avez plusieurs serveurs serveurs web sur un votre réseau local et que vous souhaitez que plusieurs d'entre eux soient accessibles depuis internet par le même port (au hasard 80 ou 443 par exemple), il est fort probable que vous fassiez appel à un reverse-proxy, qui est un moyen somme toute assez commode pour y parvenir.

De même si vous souhaitez chiffrer le contenu publié en SSL/TLS, certbot est devenu ces dernières années un incontournable, de par sa simplicité et sa gratuité.

Faire que les deux fonctionnent nécessite bien sûr une configuration particulaire, voici donc comment procéder dans le cas ou vous avez créé un sous-domaine (A record) chez votre fournisseur de noms de domaine :

  • le nom du sous domaine sera : sous.domaine.com
  • l'adresse ip du serveur sur le réseau local : 192.168.1.123
  • l'adresse ip du serveur web dans la DMZ : 192.168.1.210
  • le port de l'application sur le serveur web local : 9000
  • la racine des fichiers du serveur web local : /racine/des/fichiers/de/mon/serveurweb (généralement sous ubuntu ou debian : /var/www ou /var/www/html etc.)

Du côté de la machine qui va servir de proxy inverse (reverse proxy) accessible par internet (en DMZ) :

Créer un nouveau fichier de configuration pour apache :

touch /etc/apache2/sites-available/mon-reverse-proxy1.conf
nano /etc/apache2/sites-available/mon-reverse-proxy1.conf

Et y ajouter :

# Si ce n'est déjà fait (peu probable par défaut...)
# NameVirtualHost *:80
# Listen 80
# NameVirtualHost *:443
# Listen 443
# Surtout :

# Nécessaire pour la validation du certificat électronique
<VirtualHost *:80>
ServerName nom.de.sous.domaine
ServerAdmin mon.adresse@e.mail
    <LocationMatch "/*">
    order allow,deny
    allow from all
    Satisfy any
    ProxyPass http://192.168.1.123
    ProxyPassReverse http://192.168.1.123
    </LocationMatch>
</virtualHost>

<VirtualHost *:443>
ServerName nom.de.sous.domaine
ServerAdmin mon.adresse@e.mail
ProxyRequests Off
ProxyPreserveHost On
SSLEngine on
SSLProxyEngine On
SSLProxyCheckPeerExpire On
SSLProxyVerifyDepth     10
SSLCertificateFile      /etc/letsencrypt/live/videos.espitallier.net/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/videos.espitallier.net/privkey.pem
    <LocationMatch "/*">
    order allow,deny
    allow from all
    Satisfy any
    ProxyPass http://192.168.1.123:9000
    ProxyPassReverse http://192.168.1.123:9000
    </LocationMatch>
</virtualHost>

Enregistrer le fichier puis :

sudo a2ensite mon-reverse-proxy1
sudo service apache2 restart

Du côté du serveur web de l'application (192.168.1.123) pour créer le certificat :

# Si certbot n'est pas encore installé :
# sudo apt install certbot # dans le cas de debian ou ubuntu par exemple
# ensuite
sudo certbot certonly

Répondre aux questions :

  • 2: Place files in webroot directory (webroot)
  • Please enter in your domain name(s) (comma and/or space separated) (Enter 'c' to cancel): sous.domaine.com
  • Input the webroot for sous.domaine.com: (Enter 'c' to cancel): /racine/des/fichiers/de/mon/serveurweb

Automatiser le renouvellement du certificat

Création du script

sudo nano /usr/local/sbin/certbot-renew.sh

Auquel on ajoute

sudo certbot -q renew
# Par clé SSH et à supposer que le dossier 192.168.1.210:/etc/letsencrypt/live/sous.domaine.com/ a été créé
sudo scp /etc/letsencrypt/live/sous.domaine.com/fullchain.pem sudouser@192.168.1.210:/etc/letsencrypt/live/sous.domaine.com/
sudo scp /etc/letsencrypt/live/sous.domaine.com/privkey.pem sudouser@192.168.1.210:/etc/letsencrypt/live/sous.domaine.com/

On le rend exécutable

chmod +x /usr/local/sbin/certbot-renew.sh

Autoriser la copie par SSH sans mot de passe

ssh-keygen -t rsa
cat .ssh/id_rsa.pub | ssh root@192.168.1.100 'cat >> .ssh/authorized_keys'

Planification du script

sudo crontab -e
# Une fois par mois
00   5   *   1   *           /usr/local/sbin/certbot-renew.sh

Extensions recommandées pour Mozilla Firefox (été 2019)

Mes extensions recommandées pour Mozilla Firefox

Version été 2019

Productivité

  • Copy URL To Clipboard Copier le nom de la page et/ou l'URL au format souhaité dans le presse-papier
  • Add to (Semantic)Scuttle ajouter des pages à un marque-pages collaboratif (nécessite une instance de Scuttle)
  • Wallabagger Sauvegarder et classer des pages web pour les lire ultérieurement (nécessite une instance de Wallabag)
  • Diaspora - Optimizer ajouter des fonctionnalités au réseau social Diaspora
  • Imgur-Uploader mettre en ligne des images sur imgur pour les partager par URL sur des réseaux sociaux ou forums
  • Switch Container Passer l'onglet d'un conteneur à un autre
  • Temporary Containers Ouvrir automatiquement le site web dans un conteneur temporaire (utile contre certains paywalls qui limitent le nombre de lectures)
  • Grammalecte Correcteur grammatical

Organisation

  • AutoPinTab épingler automatiquement les onglets lorsque vous affichez certains sites web
  • Snooze tabs Réouvir un onglet à une date/heure ultérieure planifiable

Cybersécurité et vie privée

  • ClearURLs supprime les variables de pistage dans toutes les URLs
  • Disconnect Supprime le pistage dans les pages web (en complément à Privacy Badger)
  • Facebook Container Isole les onglets du navigateur ouvert sur Facebook des autres sites visités sur les autres onglets du navigateur (l'équivalent existe aussi pour Google et bien d'autres)
  • Firefox Multi-Account Containers
  • HTTPS Everywhere Force le passage à la version chiffrée (SSL/TLS, le S de HTTPS) de bout en bout d'un site web si le site la propose.
  • I don't care about cookies Refuser systématiquement l'installation des cookies lorsqu'un site web conforme au RGPD vous le demande
  • Invidition Remplacer l'utilisation de Youtube par Indivious, une interface alternative à Youtube qui vous évite d'être pisté en plus de proposer d'autres fonctionnalités (comme le téléchargement des vidéos, etc.)
  • Open Page in Private Window Ouvrir l'onglet courant dans une fenêtre de navigation privée par le menu contextuel
  • Privacy Badger Bloque les pisteurs d'une page web (en complément à Disconnect)
  • uBlock Origin Bloquer les publicités
  • Chameleon Se faire passer pour un autre internaute, éviter le pistage
  • TrackMeNot Protège du profilage à l'aide de requêtes aléatoires sur des moteurs de recherche (technique d'Offuscation )

Dépannage

  • User-Agent Switcher and Manager Faire croire au site web visité que l'on utilise un autre navigateur (afin parfois de débloquer certaines fonctionnalités)

Libertés numériques

  • Wayback Machine Sauvegarder la page sur Internet Archive (archive.org)
  • Unpaywall Trouver une version en open access d'une publication scientifique.

Gérer le presse-papier comme un expert avec CopyQ

Connaissez-vous ce redoutable outil multiplateforme libre et gratuit pour utilisateurs intensifs qu'est CopyQ ? Si ce n'est pas le cas, je vous recommande de lire cette présentation et de vous renseigner sur son site officiel voir de lire sa documentation en anglais.

Avec la multiplicité des réseaux sociaux et sites web en tout genre qui nous obligent souvent à crossposter pour informer un maximum de personnes, sans toujours disposer des applications qui permettent de le faire automatiquement ou que celles-ci soient compatibles avec l'ensemble des plateformes, mais aussi à cause des messages plus ou moins identiques que l'on doit mettre en ligne régulièrement, les copier-coller se font fréquents même pour des personnes qui ne développent pas.

Ainsi avec CopyQ outre la praticité d'avoir un historique de ce que l'on place dans le presse-papier, de pouvoir y revenir à loisir et donc de s'en servir de prise de notes (images, texte, html, etc.) en classifiant ces données par onglets, étiquettes ou encore en les annotant (le logiciel intègre un moteur de recherche : essentiel lorsque le volume de l'historique devient trop important), on peut aussi modifier le contenu de la base de données ainsi produite à loisir (contenu et mise en forme).

Double cliquer pour coller dans une zone de saisie (formulaire web, traitement de texte, etc.) devient alors un plaisir. De même que la fusion / concaténation des éléments avec la touche entrée (un indispensable de plus lorsque l'on cite plusieurs passages d'un article).

Mais en revanche attention à la limite actuelle des 10.000 entrées par onglet de l'historique dont Ditto, utilitaire du même type (pour Microsoft Windows uniquement) ne souffre pas.

Je vous recommande de l'utiliser en complément d'extensions pour Firefox comme Copy URL to clipboard ou Clippings (réclame toutefois trop de droits à mon goût)

La cerise sur le gâteau vient du fait que le logiciel est hautement personnalisable et surtout automatisable. Là ou l'utilisateur de base fera généralement appel à l'interface graphique (accessible par une icône ou un raccourci clavier, on peut d'ailleurs associer à quasiment chaque fonction du logiciel un raccourci clavier), l'administrateur système devrait logiquement lui l'exploiter en ligne de commande (fort pratique dans un script externe) quand enfin l'expert du logiciel se créera des commandes automatisées pouvant être appelées par un menu, un raccourci clavier ou immédiatement dès qu'un contenu est placé dans le presse papier.

Afin d'illustrer cette dernière possibilité, je vous recommande de lire cette page pleine d'exemples ainsi que de prendre connaissance des commandes que j'ai moi-même modestement crééés ; elles restent très simples et sont commentées en français :

Lorsque je copie dans le presse-papier une URL de Youtube, me proposer de copier dans le presse-papier la commande pour télécharger la vidéo avec Youtube-DL

Plus besoin donc de saisir manuellement l'instruction dans un terminal, il suffira de la coller. Optionnellement on pourra créer la commande équivalente pour you-get par la sélection d'un élément différent dans une liste déroulante

Options de la commande

Automatiquement avec l'expression régulière : ^https?://youtube.com

Script de la commande

copyq:
var text = str(clipboard())
if (text) {
var command = dialog('.defaultChoice', 'Youtube-dl', 'Select', ['You-get', 'Youtube-dl'])

switch (command) {
  case 'Youtube-dl':
    text = "youtube-dl " + text 
    break;
  case 'You-get':
    text = 'you-get " + text 
    break;   
  default:

}
   copy(text)
}

Convertir des mots-clés / étiquettes séparés par des virgules en hashtags

Si comme moi vous jonglez entre des applications dont le contenu peut être enrichi de mots clés mais que leur format diffère (sur le web le hashtag règne mais ce n'est pas partout le cas) une telle "macro" peut vous faire gagner beaucoup de temps. Exemple type : Vous ajoutez dans SemanticScuttle un marque-page, dont les mots clés sont séparés par des virgules et vous effectuez la conversion en hashtags par un raccourci clavier global au système

Options de la commande

Raccourci global : META (touche Windows) + ALT + R

Mais vous pouvez le définir selon vos préférences...

Script de la commande

copyq:
function copy2() {
  try {
    var x = config('copy_clipboard')
    config('copy_clipboard', false)
    try {
      copy.apply(this, arguments)
    } finally {
      config('copy_clipboard', x)
    }
    } catch(e) {
      copy.apply(this, arguments)
  }
}

copy2()
var text = str(clipboard())
if (text) {
  var txt = \", \"
  var repl = \",\"    
  text.replace(txt, repl)
  var mots = text.split(',');
  var phrase = ''
  for(var i=0; i < mots.length; i++)
  {
  mots[i] = \"#\" + mots[i].trim()
  phrase = phrase + mots[i] + \" \"    
}
copy2(phrase)
// optionnellement pour coller directement dans une zone de saisie : 
// paste()
}
Subscribe to RSS - open source