ChatGPT - Sage - Poe - Les programmeurs pas [encore] en danger

poe.com, un [bon?!] moyen de tester ChatGPT sans numéro de téléphone, en ne donnant qu'une adresse email, éventuellement jetable.

Je me suis dit, tiens, si je testais avec la génération d'un bout de code ?

Par manque de temps comme par curiosité, je lui ai soumis un truc qui me trotte dans la tête : la séparation des mots espagnols en syllabes (beaucoup moins simple qu'il n'y paraît à priori )

Ben je crois que j'ai cassé chatGPT... Je lui prompte:

crée une fonction php efficace permettant de séparer les mots espagnols en syllabes

Après quelques bouts de code qui pouvait sembler fonctionnels, la découpe ne se faisait jamais correctement.

Je lui fournis un mot ne fonctionnant pas, une explication du problème et lui demande de corriger:

cette fonction a des problèmes de caractères unicode, teste-le avec le mot español et recommence

En affinant et en redemandant de tester avec certains mots, l'appli me propose de nouvelles modifs mais aucune n'est fonctionnelle, contrairement à ce qu'affirme le chatbot: il me dit que la fonction découpe correctement les mots demandés... Donc soit elle ment soit elle ne teste pas le code (LOL)

Je lui demande donc de recommencer et c'est là qu'on rigole:

ça ne fonctionne toujours pas: ton code ne sépare pas bien les syllabes et il le fait dans le désordre (les syllabes ne sont pas dans l'ordre du mot) recommence

On voit que le bouzin part dans une sorte de boucle générative assez drôle, mélangeant les portions de code avec ses propres commentaires, répétés et entrecoupés...

function syllabifier($word) {
  $syllable_patterns = array(
    '/(qui|gue|gui|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
    '/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
    '/(gui|güi)[aeiouáéíóúü]*/i'
  );

  $syllables = array();
  preg_match_all('/./u', $word, $matches);
  $characters = $matches[0];

  while (!empty($characters)) {
    $matched = false;
    foreach ($syllable_patterns as $pattern) {
      $syllable = '';
      foreach ($characters as $key => $char) {
        $syllable .= $char;
        if (preg_match($pattern, $syllable)) {
          $syllables[] = $syllable;
          $matched = true;
          $charactersJe suis désolé que la fonction ne fonctionne toujours pas pour vous. Je vais essayer de proposer une nouvelle version de la fonction qui prend en compte les cas particuliers de la langue espagnole et qui retourne les syllabes dans l'ordre du mot. Voici la fonction modifiée :

```php
function syllabifier($word) {
  $syllable_patterns = array(
    '/(qui|gue|gui|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
    '/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
    '/(gui|güi)[aeiouáéíóúü]*/i'
  );

  $syllables = array();
  preg_match_all('/./u', $word, $matches);
  $characters = $matches[0];

  while (!empty($characters)) {
    $matched = false;
    foreach ($syllable_patterns as $pattern) {
      $syllable = '';
      foreach ($characters as $key => $char) {
        $syllable .= $char;
        if (preg_match($pattern, $syllable)) {
          $syllables[] = $syllable;
          $matched = true;
          $charactersJe m'excuse pour cette erreur dans ma réponse précédente. Voici la version corrigée de la fonction :

```php
function syllabifier($word) {
  $syllable_patterns = array(
    '/(ll|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
    '/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
    '/(gui|güi)[aeiouáéíóúü]*/i'
  );

  $syllables = array();
  preg_match_all('/./u', $word, $matches);
  $characters = $matches[0];

  while (!empty($characters)) {
    $matched = false;
    foreach ($syllable_patterns as $pattern) {
      $syllable = '';
      foreach ($characters as $key => $char) {
        $syllable .= $char;
        if (preg_match($pattern, $syllable)) {
          $syllables[] = $syllable;
          $matched = true;
          $characters = array_slice($characters, $key + 1);
          break;
        }
      }
      if ($matched) {
        break;
      }
    }
    if (!$matched) {
      $syllables[] = $charactersJe m'excuse pour cette erreur dans ma réponse précédente. Voici la version corrigée de la fonction :

```php
function syllabifier($word) {
  $syllable_patterns = array(
    '/(ll|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
    '/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
    '/(gui|güi)[aeiouáéíóúü]*/i'
  );

  $syllables = array();
  preg_match_all('/./u', $word, $matches);
  $characters = $matches[0];

  while (!empty($characters)) {
    $matched = false;
    foreach ($syllable_patterns as $pattern) {
      $syllable = '';
      foreach ($characters as $key => $char) {
        $syllable .= $char;
        if (preg_match($pattern, $syllable)) {
          $syllables[] = $syllable;
          $matched = true;
          $characters = array_slice($characters, $key + 1);
          break;
        }
      }
      if ($matched) {
        break;
      }
    }
    if (!$matched) {
      $syllables[] = $charactersJe suis désolé pour cette confusion. Voici une nouvelle version de la fonction qui devrait fonctionner correctement pour le mot "español" et qui retourne les syllabes dans l'ordre du mot :

```php
function syllabifier($word) {
  $syllable_patterns = array(
    '/(que|qui|gue|gui|güe|güi|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
    '/(que|qe|qui|qüe)[aeiouáééóúí]+/i',
    '/(gui|güi)[aeiouáéíóúü]+/i'
  );

  $syllables = array();
  preg_match_all('/./u', $word, $matches);
  $characters = $matches[0];

  while (!empty($characters)) {
    $matched = false;
    foreach ($syllable_patterns as $pattern) {
      $syllable = '';
      foreach ($characters as $key => $char) {
        $
Vous voulez en finir rapidement   avec les «IA» ?!
Laissez-les se reprogrammer   toutes seules...  Y'en a pour une grosse matinée.

L'appli «inutile» du jour, Readme Toaster

Il y a quelques jours, j'ai vu passer un article sur le contenu idéal d'un Readme ( https://warriordudimanche.net/article1840/6448d4a15a710 ) et en partageant le lien sur mastodon, on a émis l'idée de coder un assistant de génération qui permette de faire ça simplement...

Nicolas Delsaux* m'a alors passé quelques exemples d'applis déjà réalisées et - pour autant que j'ai pu en juger avec un test rapide - très bien foutues et très complètes:

Hélas... vous me connaissez...

le malheur était déjà en train de se produire: impossible d'empêcher mon cerveau de travailler en background façon «à la volette»

alavolette.gif

Du coup, si je voulais pouvoir faire autre chose, il fallait d'abord vider ma tête de ce «projet» LOL.

Voici donc ReadmeToaster

L'objectif est de pouvoir faire un readme rapidement, de pouvoir modifier «simplement» les éléments du contenu (ordre ou items), de pouvoir ajouter des captures d'écran, sélectionner une license...

FireShot Capture  - Readme Toaster.png

La capture d'écran parle d'elle-même: on peut même choisir de générer des fichiers séparés pour certaines parties (Roadmap, Changelog...)

Configuration

La configuration des items se fait via le fichier config/patrs.json qui contient les objets, leur type (select/text/textarea etc), leur titre/label, leur placeholder, leur attribut name etc.

    "Name":{
        "type":"text",
        "title":"The project name",
        "name":"name",
        "placeholder":"ReadmeToaster",
        "class":"name",
        "required":"required"
    },
    "Description":{
        "type":"textarea",
        "title":"A short description (what's the project's purpose ?)",
        "name":"description",
        "placeholder":"A tiny webapp to create normalised readme files",
        "class":"description"
    },
    "Installation":{
        "type":"textarea",
        "title":"How to install or deploy ?",
        "name":"installation",
        "placeholder":"Just copy the folder onto your server and that's it... Noooo, I'm kidding !",
        "class":"install"
    },
    "Contribute":{
        "type":"textarea",
        "name":"contribute",
        "placeholder":"* translations,n* bug finding",
        "class":"text",
        "title":"Explain how to contribute"
    },
    "Contribute in a separated file":{
        "type":"checkbox",
        "name":"contribute_separate",
        "title":"Put contribute in a separate file",
        "placeholder":"",
        "class":"",
        "value":"contribute_text"
    },

On peut ainsi facilement ajouter, modifier ou déplacer des items dans le frontend sans mettre les doigts dans le code. Si on peut pouvoir séparer un contenu dans un fichier à part, on colle un item checkbox dont le nom est ITEM_separate.

Le fichier généré sera un zip contenant tous les fichiers, y compris les éventuelles captures d'écran fournies.

Conclusion

Pas sûr que ça serve à quelqu'un vu que c'est plus un amusement qu'autre chose (ne me jugez pas), mais bon: si jamais quelqu'un le veut... c'est par là : https://outils.warriordudimanche.net/outils/readmetoaster/ et pour le télécharger: http://outils.warriordudimanche.net/outils/readmetoaster/?download

ET EN TOUT CAS, C'EST PLUS DANS MA TÊTE !

[*] Pub gratuite pour Nicolas

How To README · Matthias Ott – User Experience Designer

En résumé, pour un bon .README, il faut les paragraphes:

  • Nom et description
  • Installation
  • Configuration et usage
  • Déploiement
  • Testing (tests unitaires ? Lol)
  • Comment contribuer
  • Licence
  • Roadmap (modifs à venir)
  • Changelog (liste des versions et des apports de chacune)
  • Author(s) / Contributeurs.trices
  • ajouter un lien de démo
  • ET UNE FUCKING CAPTURE D'ÉCRAN !

Pour ma part, et comme un des commentateurs, je pense qu'il faudrait mettre le changelog dans un fichier séparé.

Via https://links.kalvn.net/shaare/YUH_nQ

Voice RSS - Text-to-speech (TTS) online service

Une API de text to speech qui comble les attentes que j'avais:

  • un service gratuit suffisant
  • une bonne prononciation espagnole
  • une intonation correcte lors de la lecture
  • pas de GAFAM dedans
  • appelable simplement, en particulier en PHP

Au départ, je cherchais une lib me permettant de faire la même chose sans dépendre d'un service tiers (autonomie, disparition du service, passage au tout payant toussa).

Hélas, aucune lib gratuite n'a le niveau de qualité nécessaire, l'indépendance aux gafams et la simplicité d'installation/utilisation pour mes projets et applis en cours (si quelqu'un connaît une lib PHP ou JS pour faire ça, je prends avec enthousiasme)... Je me suis donc rabattu vers un microservice externe.

Ce qui m'embêtait, c'était que mes élèves se retrouvent sur mes pages - dans lesquelles je fais en sorte qu'aucune donnée ne sorte vers des tiers - avec un appel direct à une API extérieure, donc avec un problème RGPD éventuel.

Du coup, j'ai fait un wrapper de cette API que je peux moi-même appeler comme une API: ainsi, l'appel côté client se fait toujours vers mon serveur qui répercute la requête vers VoiceRSS, récupère le retour et renvoie sous forme de fichier mp3. De plus, si une requête a déjà été faite, elle a été mise en cache par le wrapper, ce qui diminue la charge serveur et le nombre d'appels vers l'API.

Si ça intéresse quelqu'un, je pourrais développer et filer le code...

Javascript: tester la validité des liens d'une page afin de les styler

Petit bout de code fait à la va-vite dans le but de vérifier la validité des liens d'une page. Mon objectif est de pouvoir à terme montrer automatiquement les liens ne répondant plus sur la page où je regroupe les liens de téléchargement illégal

Le bout de code actuel

Dans le code ci-dessous, j'utilise fetch et les promesses pour lancer des requêtes afin de styler les liens testés:

function checkLinks(nodelist){
    function checkUrl(link){
        return fetch(link).then(function(response){     
            return response.status;
        }).catch(function(error){
            return error.status;
        });
    }
    if (!nodelist){
        checkLinks(document.querySelectorAll('.checkLink'));
        return;
    }
    for (let obj of nodelist){
        if (obj.tagName=="A"){
            checkUrl(obj.href).then(function(response){obj.classList.add("status"+response);});
        }else if (obj.hasAttribute("src")){
            checkUrl(obj.src).then(function(response){obj.classList.add("status"+response);});
        }else{
            checkLinks(obj.querySelectorAll("*[href],*[src]"));
        }
    }
}
checkLinks();

Explication rapide

Étape 1

J'ai d'abord fait une fonction qui appelle une URL et renvoie une promesse qui, une fois résolue, renverra le statut de la requête. On y retrouve .then qui renvoie le statut d'une requête qui aboutit et .catch qui renvoie celui d'une requête se soldant par une erreur.

function checkUrl(link){
        return fetch(link).then(function(response){     
            return response.status;
        }).catch(function(error){
            return error.status;
        });
    }

Les plus coquinous d'entre-vous me feront remarquer à juste titre que, vu ce que je renvoie, je pouvais me contenter de

function checkUrl(link){
        return fetch(link).then(function(response){     
            return response.status;
        });
    }

Ceci dit, en prévoyant les deux cas, je m'autorise à gérer le retour différemment selon si ça aboutit ou pas (par exemple retourner «ok200» ou «error404»

Étape 2:

Ce block n'est là que pour simplifier la vie de l'utilisateur en lui évitant de procéder lui-même au querySelectorAll()

if (!nodelist){
        checkLinks(document.querySelectorAll('.checkLink'));
        return;
    }

Étape 3:

Je parcours la nodeList en vérifiant l'URL passée en href (pour les A) ou en src (pour les img par exemple).

Le dernier cas est celui où l'on souhaite vérifier les liens contenus dans un div portant la classe .checkLink : il suffit d'appeler la même fonction de façon récursive en lui fournissant le nodeList des liens contenus das le DIV en question. (ça permet de vérifier un grand nombre de liens sans avoir à leur ajouter individuellement la classe .checkLink, ce qui est particulièrement utile quand on publie des articles en utilisant markdown )

for (let obj of nodelist){
        if (obj.tagName=="A"){
            checkUrl(obj.href).then(function(response){obj.classList.add("status"+response);});
        }else if (obj.hasAttribute("src")){
            checkUrl(obj.src).then(function(response){obj.classList.add("status"+response);});
        }else{
            checkLinks(obj.querySelectorAll("a,img"));
        }
    }

Comment on l'utilise ?

Il suffit de mettre la classe .checkLink à tout objet dont on veut tester les liens et de coller la fonction dans la page puis de l'appeler via un checkLinks(); de bon aloi.

En l'état, la fonction ajoutera une classe .status200 pour les liens ok ou .status404 pour les URL qui ne répondent plus.

Il ne reste plus qu'à styler ces classes en changeant la couleur, le fond ou en ajoutant des emoji avec un petit content. On peut même éventuellement masquer un objet dont l'URL ne répond pas...

Limites

Le script étant en JS, il se heurte évidemment aux règles de la politique CORS: toute requête hors du domaine en cours va échouer à moins de redéfinir le CORS dans le Head de la page via

<meta http-equiv="Content-Security-Policy" content="Content-Security-Policy:..."/>

Licence

Comme d'habitude, il s'agit autant d'un proof of concept que d'une truc utile... en tout cas, n'hésitez pas à en faire rigoureusement ce que vous voulez: c'est cadeau...

SwebDev - La fonction CSS clamp()

Clamp permet de spécifier trois valeurs pour une propriété : la valeur minimale, la valeur préférée et la valeur maximale.

Glopglop N°1

Ainsi, on pourra donner des tailles fixes comme minimum et maximum et une taille variable (pourcentages ou valeur de viewport) comme valeur préférée: la valeur variable sera utilisée, mais bornée au minimum et au maximum. ex: clamp(20px, 5vw, 54px)

Glopglop N°2

On peut faire des calculs directement dans clamp() : font-size: clamp(20px, 16px + 5vw, 54px);

Glopglop N°3

Clamp peut être utilisé sur les paddings, les margins etc... Tout ce qui a une valeur numérique (length, number, percentage...)

DoodleCSS & PaperCSS

Sympa ! Une feuille css permettant de donner un style doodle à la main aux éléments de formulaire HTML en ajoutant une classe au body . exemple

Bon, ya un appel vers google fonts mais rien que goofi ne saurait arranger...

Le Zip fait environ 277ko

EDIT

Merci à vinc[E] qui me signale une alternative très réussie : PaperCSS - 54ko

FireShot Capture 084 - Forms • PaperCSS - www.getpapercss.com.png

Il y a encore un appel à Google Fonts mais plus besoin du recours à un SVG pour les bordures...

Créer une arborescence en CSS uniquement - Dév. Web - ShevArezo`Blog

Superbe et pas si compliqué... Merci !

EDIT: Comme la page est bourrée de trackers, je copie colle ici et sur mon snippetvamp

CSS


 .tree {
  --spacing: 1.5rem;
  --radius: 10px;
  --marker-bg-default: #ddd;
  --marker-bg-haschild: #6b9abb;
  line-height: 2rem;
}
.tree li {
  display: block;
  position: relative;
  padding-left: calc(2 * var(--spacing) - var(--radius) - 2px);
}
.tree ul {
  margin-left: calc(var(--radius) - var(--spacing));
  padding-left: 0;
}
.tree ul li {
  border-left: 2px solid #ddd;
}
.tree ul li:last-child {
  border-color: transparent;
}
.tree ul li::before {
  content: "";
  display: block;
  position: absolute;
  top: calc(var(--spacing) / -2);
  left: -2px;
  width: calc(var(--spacing) + 2px);
  height: calc(var(--spacing) + 1px);
  border: solid #ddd;
  border-width: 0 0 2px 2px;
}
.tree summary {
  display: block;
  cursor: pointer;
}
.tree summary::marker,
.tree summary::-webkit-details-marker {
  display: none;
}
.tree summary:focus {
  outline: none;
}
.tree summary:focus-visible {
  outline: 1px dotted #000;
}
.tree li::after,
.tree summary::before {
  content: "";
  display: block;
  position: absolute;
  top: calc(var(--spacing) / 2 - var(--radius) + 2px);
  left: calc(var(--spacing) - var(--radius) - 2px);
  width: calc(2 * var(--radius));
  height: calc(2 * var(--radius));
  border-radius: 50%;
  background: var(--marker-bg-default);
}
.tree summary::before {
  content: "+";
  z-index: 1;
  background: var(--marker-bg-haschild);
  color: #fff;
  line-height: calc(2 * var(--radius));
  text-align: center;
}
.tree details[open] > summary::before {
  content: "−";
}

HTML

<ul class="tree">
  <li>
    <details open>
      <summary>Informatique</summary>
      <ul>
        <li>
          <details>
            <summary>Périphériques</summary>
            <ul>
              <li>Clavier</li>
              <li>Souris</li>
              <li>Ecran</li>
            </ul>
          </details>
        </li>
        <li>
          <details open>
            <summary>Stockage</summary>
            <ul>
              <li>Disque dur</li>
              <li>SSD</li>
              <li>Clé USB</li>
            </ul>
          </details>
        </li>
        <li>
          <details>
            <summary>Composants</summary>
            <ul>
              <li>Processeur</li>
              <li>Carte mère</li>
              <li>Mémoire</li>
              <li>
            </ul>
          </details>
        </li>
        <li>
          <details>
            <summary>Composants</summary>
            <ul>
              <li>Processeur</li>
              <li>Carte mère</li>
              <li>Mémoire</li>
              <li>Carte graphique</li>
            </ul>
          </details>
        </li>
      </ul>
    </details>
  </li>
</ul>
</code></pre> codeCarte graphique</li>
            </ul>
          </details>
        </li>
      </ul>
    </details>
  </li>
</ul>
Via https://book.knah-tsaeb.org/

API + BOT = RTE_color_bot (@RTE_color@piaille.fr)

Parfois, un post en entraîne un autre... Ainsi, tu t'es mis à coder un BOT presque par inadvertance - avec les difficultés qu'on connaît - et tu le partages parce qu'il n'y a pas de raison que l'auteur soit le seul à pleurer... pis t'as un copain qui te lit et qui prend le truc au sérieux... en tout cas suffisamment pour te proposer d'en faire un autre !

Non, c'est vrai ?!    T'AS TROUVÉ ÇA BIEN ?!

Donc, me v'la reparti pour un tour...

Objectif de la demande: récupérer les données de l'API RTE tempo ( https://data.rte-france.com/catalog/-/api/consumption/Tempo-Like-Supply-Contract/v1.1 ) pour dire la couleur du jour et celle du lendemain et les poster sur un bot mastodon (mais pouvoir appeler le script comme une API aussi)

Je suis donc parti pour tester ladite API: pour la faire courte, ça m'a permis - après les galères d'usage - de comprendre comment tout ça fonctionnait (en particulier la demande d'un token, l'appel d'une api en php etc)

J'en suis arrivé à un script qui produit un texte donnant la couleur du jour et celle du lendemain.

«Oui, mais ce serait cool...»

Le texte c'est bien, mais ce serait mieux si on pouvait récup les données en HTML et intégrer ça avec une Iframe, comme un code d'intégration.

Pis éventuellement en RSS aussi...

Bon, tant que j'y étais, j'ai aussi fait un mode json, au cas où on voudrait ça dans un frontend à soi...

Du coup, mon API s'appelle de plusieurs façons différentes:

  • URL seule ➜ retour en texte RAW (prêt à poster)
  • URL?html ➜ retourne le code HTML
  • URL?json ➜ renvoie les données en JSON
  • URL?rss ➜ le flux RSS de l'API
  • (cadeau bonux) URL?embed_code ➜ retourne le code pour mettre ça sur son site avec une iframe.

Pour faire bonne mesure

Le script produit un fichier pour chaque type de retour et ne le régénère qu'une fois par jour afin d'éviter de se faire «hammerer» comme dit @parigotmanchot

Jusque là, tout va à peu près dans un monde couci, couça

À ce stade, le script fonctionne même si le code est un peu sale et pas bien rangé... il fait ce qu'on lui demande. A part que parfois, il ne donne pas la couleur du lendemain... mais pas en local... juste en distant. Je n'ai pas encore vraiment cherché pourquoi, mais je pense que c'est dû au moment où l'on appelle l'API de RTE.

Pourquoi s'en tenir là ?

Tant que j'y étais, j'ai regardé l'API ECOWATT ( https://data.rte-france.com/catalog/-/api/consumption/Ecowatt/v4.0 ) qui donne une couleur et un message décrivant le niveau de stress du réseau.

Du coup, le script appelle les deux API et retourne les données cumulées des deux.

ce qui donne ça

Capture du 2022-12-11 10-47-34.png

Capture du 2022-12-11 10-49-23.png

J'en veux j'en veux...

Si ça vous intéresse, le zip est là : RTETempo.zip et il est distribué sous stricte licence faites-en ce que vous voulez. 😅

Les constantes au début permettent de configurer un peu:

  • TOKEN_BASE64 : pour mettre votre propre jeton si vous voulez vous inscrire sur RTE (qui accepte les emails jetables type yopmail, je dis ça je dis rien 😬)
  • TEMPO_BEFORE_TODAY, TEMPO_BEFORE_TOMORROW, ECOWATT_BEFORE_TODAY, ECOWATT_BEFORE_TOMORROW : pour définir le texte renvoyé avant la couleur.
  • HTML_STYLE : pour changer le style du HTML généré
  • TEMPO_HTML_TEMPLATE, ECOWATT_HTML_TEMPLATE : pour changer le code HTML à utiliser en cas de retour HTML
  • ECOWATT, TEMPO : deux booléens permettant de débrayer l'un ou l'autre des appels (si vous ne voulez que ECOWATT ou que TEMPO)

Et le BOT ?!

Ben j'ai fait un autre BOT pour poster les couleurs du réseau une fois par jour... https://piaille.fr/@RTE_color ( @RTE_color@piaille.fr )

Capture du 2022-12-11 11-01-14.png

Super Useful CSS Resources 🌈 - DEV Community 👩‍💻👨‍💻

De côté pour plus tard: en particulier les usages exotiques de CSS.

BronKOBOt (@bronkobot@botsin.space) - Faire un Bot Mastodon from scratch.

Tiens... et si je faisais un bot pour poster une citation par jour tirée des passages de romans que j'ai sélectionnés et stockés su ma Kobo ?!

C'est par ces mots que j'ai commencé à galérer... comme quoi, il suffit d'une idée à la con pour se lancer dans tout un merdier pourtant parfaitement facultatif ...

Le postulat de départ

Je veux qu'un BOT mastodon poste une citation de mes bookmarks de liseuse DONC il faut récupérer ces passages bookmarkés.

Donc, il faut trouver le fichier de base de donnée.

Puis il faut extraire les données intéressantes.

Puis utiliser ces données dans un BOT qui sera appelé une fois par jour.

Étape 1: trouver et comprendre les données sur la KoBo

En fouillant un peu, on trouve un fichier sqlite (JOIE) dont le chemin est .kobo/KoboReader.sqlite

Comment différencier la base de   livres des autres ?!    Ben elle pèse plus de 700mo...    C'est un signe.

Un passage par DBBrowser pour SQlite permet de jeter un oeil sur la structure des données.

Idéalement, il me faut deux informations: la citation et le titre du bouquin dont elle est extraite.

drame.jpeg

En fait les titres se trouvent dans la table content et les annotations dans la table Bookmark

deux tables

Je pourrais utiliser Calibre et chercher un moyen de faire un export complet de ces deux données uniquement. Pas sûr que ça marche. Ou alors faire un export via le DBexplorer pis me retaper le truc à chaque export...

Étape 2 : extraire les données et les préparer pour le Bot

En vieux programmeur qui bricole, je vois là un prétexte pour coder une petite appli en python qui se chargera de l'extraction.

Mon problème, c'est que je me sers des notes de la Kobo pour retenir des passages intéressants mais aussi, parfois, pour noter vite fait un mot de vocabulaire espagnol que je ne connaissais pas et dont je veux conserver la traduction. Mais ces mots de vocabulaire ne sont pas des citations: il faudrait donc les virer à la main à chaque fois. C'est chiant.

L'appli devra donc permettre de filtrer les citations avant de générer un fichier exploitable par le bot.

Pour faire court

J'ai fait l'appli en question: elle permet donc de récupérer les données, filtrer en fonction de la longueur de la citation ou du fait qu'elle est ou pas accompagnée d'une note personnelle...

Capture du 2022-12-03 15-57-22.png

On peut formater les données selon plusieurs modèles (HTML, Json, Markdown, TXT brut, BOT) voire même créer son propre format (genre %title:«%quote» )

Si ça intéresse quelqu'un,   contactez-moi vu que j'ai la  flemme de mettre ça là

Voilà le ZIP : Kobone : KOBO Note Extractor

Étape 3 : coder un bot en PHP afin de l'appeler par la suite avec un CRONJob.

Rien d'extraordinaire ici: je fais un script qui parse le fichier obtenu à l'étape précédente, tire une citation au hasard et la poste sur le compte Masto du Bot.

Le point le plus intéressant du bouzin est plutôt la fonction servant à poster sur un compte mastodon:


define('TOKEN','letokendesécuritéquetuchopesdansmastodon');
define('MASTODON_URL','https://botsin.space/'); // Ici, il faut coller l'url de votre serveur mastodon
define('HEADER',"Citation du jour:");
 function post2Mastodon($data=null){
        if (!$data){return false;}
        $headers = [
          'Authorization: Bearer '. TOKEN
        ];
        $data=[
            'status'=> HEADER.$data,
            'language'=>"fr",
            'visibility' => 'public'
        ];
        $ch_status = curl_init();
        curl_setopt($ch_status, CURLOPT_URL,  MASTODON_URL.'/api/v1/statuses');
        curl_setopt($ch_status, CURLOPT_POST, 1);
        curl_setopt($ch_status, CURLOPT_POSTFIELDS, $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);
        return $output_status;
    }

Étape 4: Créer une appli Mastodon pour avoir un jeton d'utilisation

Aller sur le compte mastodon en question, puis dans les préférences.

Cliquer sur «developpement» puis «Nouvelle application».

Remplir le formulaire simplissime puis valider. Cliquez ensuite sur le nom de votre application dans la nouvelle page et copiez votre jeton d’accès. C'est celui-ci qu'il faut coller dans le script PHP (dans la constante TOKEN)

Étape 5 : Définir un Cronjob

Direction le panneau de config de votre hébergement, rubrique cronjob. Sur Cpanel c'est là :

Capture du 2022-12-03 16-11-42.png

Définissez la fréquence (pour moi une fois par jour) et l'heure (8:00).

Ensuite, il faut définir la commande qui va appeler le script du BOT: wget fera l'affaire...

wget -qO /dev/null http://xxxxxxxxxxx.xx/index.php

L'option -q pour quiet et -O /dev/null pour éviter d'avoir un fichier de sortie qui se crée à la racine.

Capture du 2022-12-04 11-10-50.png

Fin

Une fois tout ça terminé, j'ai bien un bot qui poste une citation à 8:00 tous les jours.

capture du bot

Bon, elles ne seront pas toujours bien formatées vu que la sélection de texte sur liseuse c'est un peu touchy... mais c'était plus pour apprendre à le faire qu'autre chose...

Pis ça m'a permis de me  remettre à Python...

https://botsin.space/@bronkobot

Optipic: une «api» pour optimiser les images

Le cadeau du jour: un script qui permet d'automatiser l'optimisation d'une image pour le ouaibe sans passer par une appli quelconque.

Le problème

Tout le monde est d'accord pour affirmer qu'il est indispensable de minimiser le poids des pages web et que dans cette optique, le premier geste - après avoir viré les bibliothèques inutiles bien entendu - reste l'optimisation des images.

J'avais vu passer un article fort bien écrit sur ce sujet mais hélas, j'ai omis de le bookmarker et paf: le voilà perdu dans les méandres de mon historique sur un des ordinateurs que j'utilise.

Tout le monde est d'accord sur le principe mais souvent, c'est comme l'écriture de la doc ou les tests unitaires: on a la flemme de s'y coller...

J'ai parfaitement conscience  de la déferlante de commentaires  que cette comparaison risque  de me valoir !

Ze solution

L'idéal serait bien sûr que l'optimisation soit transparente et automatique... genre: tu te contentes de passer à la balise img l'URL de l'API avec celle de l'image et POF, tu as l'image en version optimisée.

Comment ça   «t'es pas cap' ?»

Optipic est donc un script auquel on passe l'URL d'une image, le format souhaité, le niveau de qualité et -éventuellement - la largeur maximum pour obtenir en retour l'image transformée.

Les paramètres sont les suivants:

  • i ➜ l'url de l'image
  • maxwidth ➜ la largeur maximum de l'image (opt.)
  • format ➜ le format de sortie de l'image : jpg,gif,png,webp,avif (PHP 8 minimum pour l'avif)
  • quality ➜ la qualité (niveau de compression) de 0 à 100

Le script renvoie directement l'image transformée avec le header adéquat et du coup, c'est transparent pour HTML/CSS.

Notez que l'image optimisée est sauvée dans le dossier rendered/ afin d'éviter une charge inutile: si une image a déjà été optimisée, le script la sert directement au lieu de la refaire à chaque fois.

But there's more !

Même si ce script est déjà à ce stade absolument fabuleux (si, si !), je me suis dit que les paramètres risquaient de se répéter et qu'il pouvait s'avérer fastidieux de retaper le tout à chaque fois. Par conséquent, j'ai ajouté la possibilité de créer des scripts d'optimisation - un peu comme dans scriptopic.

Ainsi, si vous placez un fichier PHP contenant les paramètres dans le dossier scripts/ , vous pouvez l'appeler via le paramètre $_GET script:

Exemple: je cree scripts/webp.php et je mets dedans:


// webp  50%
$params=[
    'format'        =>'webp',
    'quality'       =>50,
    'maxwidth'      =>null
];

Puis je l'appelle en faisant : api.url/?script=webp&i=http:/image.url/pic.jpg

Pour le moment, il y a quatre scripts: jpg80,jpg70,avif,webp... à vous de créer les vôtres en fonction de vos besoins...

Et ça marche ?

Ben j'ai fait un test dans ce blog pour voir avec mon image de titre LOL:

version originale

qui pèse 113 ko en jpg

Puis avec son optimisation via le script webp ci-dessus:

version optimisée

qui ne pèse plus que... 18 ko en webp 50%

Et ça juste en rajoutant https://api.warriordudimanche.net/optipic/?script=webp&i= dans le chemin de l'image.

la classe

En cas d'erreur ?

Si Optipic ne parvient pas à créer l'image, il crée une image d'erreur à la place, histoire de voir le souci quand même...

la classe

Ce comportement peut être débrayé avec la constante ERROR_PIC ligne 29.

Test, téléchargement pis tout ça

Comme pour toutes les api de ma page, le script est téléchargeable via le ?download : https://api.warriordudimanche.net/optipic/?download et testable au même endroit: https://api.warriordudimanche.net/optipic/?i=...

Nota bene

  1. Pensez bien à mettre le paramètre d'URL (i) en dernier car sinon, php inclura les paramètres dans l'URL passée
  2. Même avec un script vous pouvez toujours changer un paramètre. Exemple: ?script=webp&quality=30&i=xxx permet de changer la qualité précisée dans le script (50).
  3. Par défaut, le format de sortie est le jpeg mais c'est configurable dans la constante DEFAULT_FORMAT ligne 28

Au passage, ce script utilise une classe que j'avais codée pour scriptopic: pictools. Grâce à elle, le travail d'optimisation tient en


    $picture=new pictool($params);
    $picture->load($url);
    if (!empty($params['maxwidth'])){
        $picture->resize($params['maxwidth']);
    }
    $picture->save($generated_picture_path);
    $picture->display();

Cadeau !

Merci qui ?!
Qui a dit   jacky et michel ?!

YoutubeRSS

Hier, je me suis aperçu que le bookmarklet que j'avais fait pour trouver le flux RSS d'une chaîne youtube ne fonctionnait pas toujours 🤬: en effet, si l'URL de la chaîne n'est pas du type www.youtube.com/channel/xxxCHANNEL_IDxxx ça ne fonctionne pas... Or, les chaînes peuvent être aussi sous une forme dans laquelle le channel_id n'apparaît pas.

zut flûte et cacaboudin

Du coup, je m'y mets

Au lieu de laisser youtube me chier dans les bottes, je me suis dit que, si le channel_id n'est pas dans l'URL, il doit être planqué quelque part dans le html de la page.

En fouillant un peu, twingo bongo jannielongo bingo 🥳 J'ai trouvé ça en parsant avec (presque) la même regex que pour l'URL.

con.jpeg

J'ai donc codé vite fait une «api» qui renvoie l'URL du flux RSS d'une chaîne Youtube dont on fournit l'adresse.

On peut l'utiliser avec le frontend minimaliste prévu, en faisant une requête GET ou via le bookmarklet qui va bien.

Le tout est bien entendu utilisable, autohébergeable et modifiable, comme d'habitude

Allez !

Cadeau du lundi: pas un, pas deux mais TROIS plugins Pluxml... (et oui !)

Bon, il ne s'agit pas de trois   plugins que j'aurais fait en   une fois, mais de trois plugins   codés à des moments différents   et que j'avais oublié de partager   jusque-là...

ok

🥇 Plugin N°1 : uniquePagePreview

Si toi aussi t'en as marre que le bouton Aperçu de la page article t'ouvre à chaque fois un nouvel onglet aboutissant rapidement à une accumulation visuellement insupportable du nombre d'échecs qu'il te faut avant de parvenir à un billet un tant soit peu abouti ? Alors ce ... «plugin» est pour toi: il se contente de changer le «_blank» de l'attribut onclick du bouton en «article_preview»... et ça, tu vois, ça change tout...

Désormais, la visualisation de l'article se fera toujours dans le même onglet (ouvert lors de la première visualisation)

⚡ Téléchargement direct du ZIP: https://repo.warriordudimanche.net/ZIPFILES/uniquePagePreview.zip

🥈 plugin N°2 : resizeArticleTextarea

Un truc qui me chiffonnait dans la rédaction d'articles un peu longs, c'était:

  1. de ne jamais voir l'article en entier pendant la rédaction
  2. de passer mon temps à défiler DANS le textarea en plus de défiler DANS la page...
Ok, ça fait deux trucs...

Grâce à ce plugin, on ne passe plus son temps à défiler QUE dans la page

En effet, il redimensionne les textareas de la page article en fonction de leur contenu. La zone de texte grandit avec le volume de texte tapé dedans.

⚡ Téléchargement direct du ZIP: https://repo.warriordudimanche.net/ZIPFILES/resizeArticleTextarea.zip

🥉 plugin N°3 : adminPanel

Un plugin dérivé d'un de mes anciens qui s'avérait obsolète avec la nouvelle maquette. Il permet d'afficher un menu visible uniquement quand l'admin est connecté. Ce menu regroupe plusieurs icônes:

Capture du 2022-11-14 14-35-06.png

  • un lien vers l'espace admin
  • un lien vers un tag particulier: je m'en sers pour accéder aux articles publiés pour lecture ultérieure sous le tag «riditleteur»
  • un lien vers la page commentaires avec un décompte des commentaires non lus
  • un bouton de déconnexion

Il affiche de plus un lien sur chaque article pour éditer directement ce dernier (pratique pour corriger ou mettre à jour un article)

Ces deux fonctions sont accessibles via deux hooks, «editArticleIcon» et «adminPanel», dont il faut placer l'appel dans les pages header.php et articles.php du thème

Une page de config permet de redéfinir les icônes et texte des boutons ainsi que le tag du raccourci.

Capture du 2022-11-14 14-43-09.png

⚡ Téléchargement direct du ZIP: https://repo.warriordudimanche.net/ZIPFILES/AdminPanel.zip

Si ça peut vous servir, c'est cadeau !

Fil RSS des articles