Le truc hype du moment sur Internet est sans nul doute le réseau social twitter-like Mastodon, et je n’ai pas échappé au phénomène. Cet article va vous montrer de manière très simple (et donc très limitée) et très moche (private joke) comment récupérer et formater des pouets (les tweets sous Mastodon, ou toots en anglais) grâce à Powershell…
DISCLAIMER : ça va être moche, puisque nous allons afficher le résultat sous forme de texte dans la console Powershell. Je vous laisse le soin d’améliorer l’esthétique. Ça va aussi être très simple, car nous allons nous contenter de lire les pouets d’un utilisateur sur une instance donnée et c’est tout. Aucune autre fonctionnalité, même pas la possibilité de euh poueter, parce que c’est évidemment plus compliqué que ça en a l’air… mais j’y reviendrai peut-être dans un autre article.
Les ingrédients
Ce qu’il nous faut pour commencer :
- Un nom d’instance Mastodon : et oui, ce n’est pas comme twitter, il existe déjà des dizaines d’instances Mastodon, je n’en citerai que quelques une : mastodon.social, mastodon.xyz, mamot.fr…
- Un nom d’utilisateur Mastodon sur cette instance. Techniquement, vous n’avez pas besoin d’avoir vous-même un compte Mastodon (quelle que soit l’instance), mais pour lire des pouets et bien… ça va être nécessaire de connaître l’utilisateur dont vous voulez lire la prose !
- Powershell comme toujours dans mes articles qui traitent de Powershell…
La théorie
D’abord, je ne me suis pas foulé. Je n’ai pas lu la doc complète de Mastodon. Pour faire des choses poussées, il offre une API REST complète, qui permet de faire tout ce qu’il est possible de faire sur Mastodon. Et moi je ne veux que lire bêtement des pouets sans trop me casser le derrière. Heureusement, chaque instance mastodon met à disposition un flux Atom (ou RSS) pour chaque utilisateur… Vous me voyez venir ? Oui, nous allons simplement récupérer ce flux Atom, qui n’est qu’un fichier XML, le parser un minimum et afficher ce qui nous intéresse. Libre à vous d’améliorer le truc ensuite, je ne fais ici que vous donner une méthode simple pour lire des pouets.
L’URL de ce flux est de la forme : https://instance/users/nomutilisateur.atom. Évidemment, il va falloir remplacer dans cette URL instance par le vrai nom d’instance (ex: mastodon.social) et nomutilisateur par euh… ben le nom de l’utilisateur qui vous intéresse ! Par exemple, prenons l’utilisateur bidon bob qui se trouve sur l’instance bidon mastodon.local. L’URL bidon de son flux Atom sera https://mastodon.local/users/bob.atom – inutile de cliquer sur le lien, c’est bidon je vous dis. Une petite précision : ça m’étonnerait fortement que tous les pouets (publics) de l’utilisateur se retrouvent dans le flux Atom, ce qui serait une grosse erreur de conception. Il y a fort à parier que seules quelques dizaines de pouets apparaissent, évidemment les tout derniers.
Si vous ouvrez cette URL (enfin une qui existe) dans votre navigateur, alléluia, il se passe quelque chose ! Sauf que votre navigateur traite le flux Atom et le met en forme, et qu’avec Powershell, le but du jeu et d’avoir le XML brut de fonderie… Les plus avertis d’entre vous auront remarqué que l’URL indique le protocole https, ce qui implique un échange de certificats entre le serveur et votre machine pour pouvoir discuter. En soi, ce n’est pas un problème mais il y a une subtilité qu’il a fallu que je creuse un peu avant de trouver la solution : comme tout bonne application web moderne, Mastodon utilise TLS 1.2, or pas de bol, la cmdlet Invoke-RestMethod que je compte utiliser envoie le header TLS 1.0 (qui est obsolète) par défaut, et aucun moyen de forcer l’utilisation de TLS 1.2 (ni même 1.1)… Aïe, ça commence mal. Alors dans ce cas, utiliser http à la place d’https ? Ben non, parce que les données que nous voulons (le flux Atom) ne sont disponible que via https, ce qui est un minimum de nos jours… alors comment faire ?
La pratique
Il existe une solution toute simple pour préciser le protocole de sécurité à utiliser AVANT d’appeler Invoke-RestMethod :
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS12
Premier problème réglé, on peut donc récupérer notre flux Atom sans craindre la terrible erreur « Invoke-restmethod : La connexion sous-jacente a été fermée : Une erreur inattendue s’est produite lors de l’envoi. » (elle est terrible parce qu’elle ne veut pas dire grand chose, comme souvent avec les erreurs chez Microsoft…).
$toots=Invoke-restmethod -URI "https://mastodon.local/users/bob.atom" # pensez à mettre une URL valide, hein.
Si tout va bien, notre objet $toots va contenir donc le flux Atom. C’est chouette, mais si vous affichez son contenu, vous aurez droit à tout un tas de bazar pas génial. Personnellement, je veux la date et heure de publication du pouet, et son contenu. Et pas tout le tralala qui va avec.
Un rapide coup d’œil à l’objet $toots m’indique qu’il dispose de deux propriétés (il en a d’autres) qui m’intéressent fortement : published (avec l’horodatage de publication, cool) et content (le contenu). Cool ! Comme il ne contient pas qu’un seul pouet, il faudra faire une boucle pour lire chacun d’entre eux. Sauf que… les pouets contiennent des tags HTML et l’horodatage est au format ISO8601, qui est très bien en soi mais déjà qu’on m’a dit que le résultat de mon script était moche… (private joke). Et en plus des tags, tous les caractères spéciaux (comme les accents) sont encodés avec le nom d’entité HTML, par exemple é pour un é minuscule… Faudra se débarrasser de ça aussi.
Donc il va falloir :
- se débarrasser des entités HTML
- se débarrasser des tags HTML pas très compatibles avec la console de Powershell, et qui rendent les pouets difficilement lisibles
- mettre la date dans un autre format que l’ISO8601, du genre JJ/MM/AAAA HH:MM:SS utilisé dans notre contrée
Pour les tags, une bonne vieille expression régulière des familles va nous débarrasser de tout ce qui peut être entre < et >. C’est pas la façon la plus propre de procéder, mais au moins c’est rapide et pas cher (et ouais, je sais, c’est moche, mais purée, cessez avec vos considérations bassement esthétiques ! Place au fonctionnel et à la vitesse d’exécution ! (private joke))
Pour les entités, inutile de réinventer la roue quand une solution existe. Il suffit de faire appel à l’assembly system.web (et donc la charger) présente par défaut sur tous les Windows qui se respectent un minimum :
Add-Type -AssemblyName system.web
Pas d’inquiétude, je vous dis ensuite comment on s’en sert.
Et pour la date, il se trouve que Powershell sait parfaitement gérer l’ISO8601. Reste juste à lui demander gentiment de mettre l’horodatage au format que nous voulons, et c’est tout.
On met tout ça dans une boucle, on affiche, et hop, le tour est joué, même s’il est moche (private joke).
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS12 $toots=Invoke-restmethod -URI "https://mastodon.local/users/bob.atom" Add-Type -AssemblyName system.web foreach($toot in $toots) { $pub=[DateTime]::Parse($toot.published) $pub=Get-Date -f "dd/MM/yyyy HH:mm:ss" $pub $content=$toot.content.InnerText -replace '<[^>]+>','' $content=[System.Web.HttpUtility]::HtmlDecode($content) write-host "$pub -> $content `r`n" }
Voilà le script dans son intégralité. Avouez que ce n’est pas bien gros. Cependant, il nécessite quelques explications complémentaires. Les variables $pub et $content correspondent respectivement à l’horodatage de publication et au contenu du pouet.
Comme je l’ai dit, il faut formater la date, de l’ISO8601 vers notre format personnalisé. Pour cela, on la parse d’abord, puis on la convertit au format désiré :
$pub=[DateTime]::Parse($toot.published) $pub=Get-Date -f "dd/MM/yyyy HH:mm:ss" $pub
On est obligé de le faire en deux temps sinon Powershell n’est pas content. Mais ce n’est pas grave.
Pour $content, on se débarrasse d’abord de tout ce qui est tag HTML :
$content=$toot.content.InnerText -replace '<[^>]+>',''
Puis on appelle HtmlDecode (fourni par l’assembly system.web) pour se débarrasser des entités HTML et les transformer en caractères « lisibles » :
$content=[System.Web.HttpUtility]::HtmlDecode($content)
Ensuite on affiche le tout, d’une manière moche (private joke), mais ça, c’est pas mon problème, hein @cathyhope2 !
Sur ce, portez-vous bien et tchüss !