Category: Coding
About “vibe coding” #LLM #AI #coding
Un compteur pour déterminer quand Trump va être dégagé (ou pas)
Documentation du Script N8N
Description
Ce script JavaScript est conçu pour N8N afin de calculer et de publier quotidiennement des décomptes avant des événements politiques majeurs aux États-Unis, à savoir :
- Les Midterms (élections de mi-mandat).
- L'élection présidentielle.
- Le jour de l’investiture.
Il génère également une barre de progression graphique pour chaque événement sous forme de texte.
Fonctionnalités
1. Définition des dates clés
Le script définit les dates des événements cibles :
- Midterms : 3 novembre 2026.
- Élection présidentielle : 7 novembre 2028.
- Jour de l’investiture : 20 janvier 2029.
2. Calcul des jours restants
Le script calcule :
- Le nombre total de jours entre aujourd’hui et chaque événement.
- Le pourcentage de progression en fonction des jours écoulés.
3. Génération de barres de progression
Une fonction génère une barre de progression graphique, composée de blocs pleins (█
) et de blocs vides (▒
), représentant visuellement l’avancée jusqu’à l’événement.
4. Génération et publication du message
Le script produit un message comprenant :
- Le décompte des jours restants pour chaque événement.
- Les barres de progression associées.
Exemple de Résultat
Voici un exemple du message généré :
There are 646 days until the Midterms, 1381 days until the next Presidential Election, and 1490 days until the next Inauguration Day.
Midterms Progress: █▒▒▒▒▒▒▒▒▒ 9%
Presidential Election Progress: █▒▒▒▒▒▒▒▒▒ 4%
Until Inauguration: ▒▒▒▒▒▒▒▒▒▒ 0%
Code
// JavaScript code for N8N to calculate and post daily countdowns with a graphical loading bar
// Define the reference start date (Trump's 2025 inauguration)
const startOfMandate = new Date('2025-01-20T00:00:00Z');
// Define the target dates
const midtermsDate = new Date('2026-11-03T00:00:00Z'); // Next USA Midterms
const presidentialElectionDate = new Date('2028-11-07T00:00:00Z'); // Next Presidential Election
const inaugurationDate = new Date('2029-01-20T00:00:00Z'); // Next Inauguration Day
// Get the current date
const currentDate = new Date();
// Function to calculate correct progress
function calculateProgress(targetDate) {
const totalDays = Math.ceil((targetDate - startOfMandate) / (1000 * 60 * 60 * 24));
const elapsedDays = Math.ceil((currentDate - startOfMandate) / (1000 * 60 * 60 * 24));
return Math.min(100, Math.max(0, Math.floor((elapsedDays / totalDays) * 100)));
}
// Calculate days remaining
const daysUntilMidterms = Math.ceil((midtermsDate - currentDate) / (1000 * 60 * 60 * 24));
const daysUntilPresidentialElection = Math.ceil((presidentialElectionDate - currentDate) / (1000 * 60 * 60 * 24));
const daysUntilInauguration = Math.ceil((inaugurationDate - currentDate) / (1000 * 60 * 60 * 24));
// Calculate accurate progress percentages
const midtermsProgress = calculateProgress(midtermsDate);
const presidentialProgress = calculateProgress(presidentialElectionDate);
const inaugurationProgress = calculateProgress(inaugurationDate);
// Create a graphical loading bar function
function createLoadingBar(percentage) {
const totalBars = 10; // Length of the loading bar
const filledBars = Math.floor((percentage / 100) * totalBars);
const emptyBars = totalBars - filledBars;
return `${'█'.repeat(filledBars)}${'▒'.repeat(emptyBars)} ${percentage}%`;
}
// Generate the loading bars
const midtermsLoadingBar = createLoadingBar(midtermsProgress);
const presidentialLoadingBar = createLoadingBar(presidentialProgress);
const inaugurationLoadingBar = createLoadingBar(inaugurationProgress);
// Generate the message
const message = `There are ${daysUntilMidterms} days until the Midterms, ${daysUntilPresidentialElection} days until the next Presidential Election, and ${daysUntilInauguration} days until the next Inauguration Day.\n\n` +
`Midterms Progress: \n${midtermsLoadingBar}\n\n` +
`Presidential Election Progress: \n${presidentialLoadingBar}\n\n` +
`Inauguration Progress: \n${inaugurationLoadingBar}`;
// Output the message
return [{
json: {
message,
},
}];
Où il publie
Le script retourne le message sous forme d’un objet JSON, prêt à être utilisé dans un flux N8N pour une publication quotidienne via un nœud horaire configuré.
Configuration Recommandée
- Fuseau horaire du serveur : CET (heure allemande).
- Heure de publication : 14h00 CET, correspondant à 8h00 ET (heure de la côte Est des États-Unis).
Utilisation
-
Intégrez le script dans un nœud de fonction JavaScript dans N8N.
-
Ajoutez un nœud horaire configuré pour exécuter le flux quotidiennement.3. Reliez le nœud de fonction à un nœud de sortie ou à un service tiers pour publier le message (par exemple Bluesky.).
Résultat
Bluesky Won’t Save Us Like radiation, social media is invisible scientific effluence that leaves us both more knowledgeable and more ignorant of the causes of our own afflictions than ever
#bluesky #moderation #mastodon
Ça fait plusieurs semaines qu’au boulot je nage entre des Keycloak, des Tyk, des frontend #AngularJS et mine de rien ça avance, on va pouvoir décommissioner tout un bazar de serveurs qui a plusieurs années, hors support, non maintenu, bref une cata ambulante 🖖🏽
interesting for local development anchor.dev/blog/intr… #lclhost
Happy to be using many of these tools, both at home and work :)
Yay Github Multi Account Support 😎
geeks gonna geek
Mon extension Chrome/Vivaldi/Edge pour poster sur Bluesky marche en mode basique !
Mon extension Chrome/Vivaldi/Edge pour poster sur Bluesky depuis n’importe quel tab sur lequel on est dans le navigateur fonctionne, en mode décoffrage brut : le titre et le lien sont postés, mais pas de carte intégrée et le lien n’est pas clickable, mais pour 2 ou 3h de dev ça me va, next corriger les bugs!!
c’est aussi la première fois de ma vie que je crée une extension pour chrome et c’est une chouette learning curve !
ce qui marche
- login
- click to share
- text posts
ce qui ne marche pas
- URL avec carte intégrée
- se déconnecter via l’extension
JSON to RSS feed with N8N and optional HTML manipulation
I’m so thrilled to finally having done it right !
This workflow that you can copy and use on N8N does a few things
- HTTP request a JSON feed to get last published items at the source (belgian AFSCA)
- Using regex to pick image file from the description field
- Clean up of the Description field from any html tags
- Serve a new RSS feed (Afsca does not have one)
- Use the RSS feed to Publish anywhere
Testing your feed
- Disable the workflow
- Disable the Feed node (left webhook)
- Enable Manual Execution
- Run the Workflow to inspect it and pin data if you need to test it
To enable the servicing of the RSS feed
- Disable the Manual Execution node
- Enable the Feed node (left webhook)
- Enable the workflow
- Visit your production RSS URL provided by the Feed node
Use view-source:https://URL of your RSS feed to easily inspect the content of your rss feed. You can use Feedbro to test your RSS feed locally

Copy into N8N
- Select the entire content of this block code below and paste it inside N8N
- you will get the node and everything like in the image above.
- the Function node showcased below does not need copying, it’s already included in the workflow.
{
"meta": {
"instanceId": "58fd5c3ff393ef21f618201de491e9a03a72661d4848a2ad337fc80dc260a4d9"
},
"nodes": [
{
"parameters": {},
"id": "c7da8a26-baa3-47e5-a3c5-9f886d67dce4",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
460,
760
],
"disabled": true
},
{
"parameters": {
"fieldToSplitOut": "items",
"options": {}
},
"id": "042e6e0d-11ff-46e9-b49e-f440f44d6fa2",
"name": "Split out lists",
"type": "n8n-nodes-base.itemLists",
"position": [
1120,
600
],
"typeVersion": 1
},
{
"parameters": {
"path": "afsca.rss",
"responseMode": "responseNode",
"options": {}
},
"id": "bb82d17d-e134-40be-8f85-898600577ff5",
"name": "Feed",
"type": "n8n-nodes-base.webhook",
"position": [
460,
460
],
"webhookId": "63f12265-8387-4bb7-bef0-d7eb93e49e11",
"typeVersion": 1
},
{
"parameters": {
"respondWith": "text",
"responseBody": "={{ $json[\"data\"] }}",
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/rss+xml"
}
]
}
}
},
"id": "e6857661-3b79-4c8b-a73d-c67ef4aebd9d",
"name": "Serve feed",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
2040,
600
],
"typeVersion": 1
},
{
"parameters": {
"url": "https://www.inoreader.com/stream/user/1005072895/tag/Afsca/view/json",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "accept",
"value": "application/json"
}
]
},
"options": {}
},
"id": "4361bdc9-2795-4ac5-99eb-efc4db72a64c",
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
820,
600
]
},
{
"parameters": {
"functionCode": "const escapeHTML = str => {\n if (!str) return \"\";\n return str.replace(/[&<>'\"]/g, \n tag => ({\n '&': '&',\n '<': '<',\n '>': '>',\n \"'\": ''',\n '\"': '"'\n }[tag])\n );\n};\n\nconst unescapeHTML = str => {\n if (!str) return \"\";\n return str.replace(/(<|>|"|'|&)/g, \n tag => ({\n '<': '<',\n '>': '>',\n '"': '\"',\n ''': \"'\",\n '&': '&'\n }[tag])\n );\n};\n\nlet feedItems = [];\nfor (item of items) {\n feedItems.push(`<item>\n <title><![CDATA[${unescapeHTML(item.json.Title)}]]></title>\n <guid isPermaLink=\"false\">${item.json.guid}</guid>\n <media:content url=\"${item.json.Image}\" type=\"image/jpeg\" />\n <link>${item.json.Link}</link>\n <pubDate>${DateTime.fromISO(item.json.Date).toRFC2822()}</pubDate>\n <description><![CDATA[${unescapeHTML(item.json.Description || \"\")}]]></description>\n </item>`);\n}\n\nconst feedTitle = \"RappelConso\"; // Set this to your desired feed title\nconst feedDescription = \"Rappel Conso monitoring.\"; // Set this to your desired feed description\nconst feedLink = \"https://rmendes.net\"; // Set this to your main RSS page or main website URL\n\nreturn [{\n data: `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:media=\"http://search.yahoo.com/mrss/\" version=\"2.0\">\n <channel>\n <title><![CDATA[${feedTitle}]]></title>\n <link>${feedLink}</link>\n <description><![CDATA[${feedDescription}]]></description>\n <pubDate>${DateTime.fromISO(item.json.Date).toRFC2822()}</pubDate>\n ${feedItems.join('\\n')}\n </channel>\n</rss>`\n}];\n\n"
},
"id": "986ea46f-4206-431e-92a9-199072492648",
"name": "Define feed items1",
"type": "n8n-nodes-base.function",
"position": [
1840,
600
],
"typeVersion": 1
},
{
"parameters": {
"values": {
"string": [
{
"name": "ImageURL",
"value": "={{$json[\"content_html\"].match(/src=\"([^\"]+)\"/) ? $json[\"content_html\"].match(/src=\"([^\"]+)\"/)[1] : \"\"}}\n"
},
{
"name": "CleanDesc",
"value": "={{$json[\"content_html\"].replace(/<\\/?[^>]+(>|$)/g, \" \").trim().replace(/\\s\\s+/g, ' ').substring(0,200)}}\n"
}
]
},
"options": {}
},
"id": "b1f7e9b0-3270-4a44-bfdf-5fa7519e9d6b",
"name": "Fetch IMG + CleanDesc",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
1360,
600
]
},
{
"parameters": {
"keepOnlySet": true,
"values": {
"string": [
{
"name": "Title",
"value": "={{ $json.title }}"
},
{
"name": "Link",
"value": "={{ $json.url }}"
},
{
"name": "Date",
"value": "={{ $json.date_published }}"
},
{
"name": "guid",
"value": "={{ $json.id }}"
},
{
"name": "Description",
"value": "={{ $json.CleanDesc }}"
},
{
"name": "Image",
"value": "={{ $json.ImageURL }}"
}
]
},
"options": {}
},
"id": "5cdbd79f-6f5b-42ee-8f3d-b8d1de320949",
"name": "Set Everything",
"type": "n8n-nodes-base.set",
"typeVersion": 2,
"position": [
1620,
600
]
}
],
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Split out lists": {
"main": [
[
{
"node": "Fetch IMG + CleanDesc",
"type": "main",
"index": 0
}
]
]
},
"Feed": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Split out lists",
"type": "main",
"index": 0
}
]
]
},
"Define feed items1": {
"main": [
[
{
"node": "Serve feed",
"type": "main",
"index": 0
}
]
]
},
"Fetch IMG + CleanDesc": {
"main": [
[
{
"node": "Set Everything",
"type": "main",
"index": 0
}
]
]
},
"Set Everything": {
"main": [
[
{
"node": "Define feed items1",
"type": "main",
"index": 0
}
]
]
}
}
}
Javascript function node used in this workflow
- I have escapeHTML and unEscapeHTML for each use case, this is completely optional and pretty much there for me to have both use cases at hand when I work with feeds.
|
|
Bluesky, comment utiliser un newsbot pour générer des Customs Feeds thématiques ?
L’idée avec ce tuto, c’est de démontrer une mise en place d’utilisation des customs feeds sur Bluesky basé sur un News Bot qui va générer le contenu, ce qui va nous permettre de segmenter l’activité du newsbot en différents customs feeds auxquels les utilisateurs vont pouvoir s’abonner.
- D’abord, j’ai été récupérer les Flux RSS de chez LeMonde.fr
- Ensuite, j’ai mis en place un agrégateur de flux afin de rassembler les différents flux d’info par dossier (actu, culture, planète etc..)
- Ensuite, j’ai mis en place le newsbot en tant que tel en suivant mon tuto

12 flux de veilles sur un même compte bluesky
Il y a donc 12 processus de veille RSS qui tournent, un par catégorie maitresse sur le site LeMonde
Comment sont construits les custom feeds ?
J’avais besoin d’une ancre stable sur lequel me baser pour chaque catégorie et je ne pouvais pas prévoir tous les mots utilisés dans un titre ou la description pour segmenter les articles en différent flux thématique, du coup, en éditant mon fichier config.json, je peux segmenter avant même la publication, et ce, de manière stable :
config.json pour Actu
{
"string": "Actu: $title",
"publishEmbed": true,
"languages": ["fr"],
"truncate": true,
"runInterval": 60,
"dateField": ""
}
config.json pour Culture
{
"string": "Culture: $title",
"publishEmbed": true,
"languages": ["fr"],
"truncate": true,
"runInterval": 60,
"dateField": ""
}
Etc.. etc..ce qui me permet de prendre le dossier/catégorie culture, actu, sports, france etc… et d’avoir une segmentation simple sans devoir passer par des tas de requête regex qui ne donneraient pas une segmentation aussi simple et efficace.
Résultat de la veille sur Bluesky
Skyfeed custom feed builder
Vue de la mise en place d’un custom feed avec skyfeed.app
Listes des custom feeds sur @lfm.bsky.social
Résultats :
le problème des doublons (résolu)
Pour régler les problèmes de doublons, c’est-à-dire, un article qui apparaît dans 2 ou 3 flux, j’ai déjà viré les flux à la Une, vu qu’ils reprennent le contenu des catégories, ensuite grâce à Inoreader, je vire les doublons d’une même catégorie en faisant un tri sur les articles qui ont le même titre, mais qui sont publiées à plusieurs endroits et enfin, on utilise le flux de sortir du dossier (culture, actu, sports etc..) comme input d’entrée du bot qui veille à l’arrivée de nouvelles publications et qui s’en charge de les publier.
Comment déployer un news bot RSS sur Bluesky?
pré-requis
- un compte bluesky dédié
- un “app password”
- un flux RSS source de contenu à publier
- bsky.rss
- la commande git installée
- Docker et Docker-Compose installé

Pour l’exercice, on va déployer un newsbot https://disclose.ngo/ C’est un média d’investigation.
Setup
Pas besoin d’un gros serveurs, il suffit d’avoir un ordinateur, évidemment si vous le débranché, le bot s’éteint aussi, donc, c’est plus pratique de mettre ça en ligne, sur un petit vps, même un serveur à 5€ par mois fera tout à fait l’affaire.
Et sinon, votre machine, que ce soit sur Windows, Linux ou macOS, tout est bon !
un compte bluesky dédié
- Résultat de ce tuto visible sur ce bot : (sans les images parce que le site à la source ne support pas bien la technologie OpenGraph)
- disclosengo.bsky.social
un “app password”
- gardez-le au chaud pour plus tard
un flux RSS source du contenu à publier
le site Disclose.ngo est un site sur mesure, mais il propose 2 flux RSS
- Flux en Anglais : https://disclose.ngo/feed?lang=en
- Flux en Français : https://disclose.ngo/feed
Pour trouver le flux RSS d’un site source, l’extension Feedbro est très utile on pourrait faire tout un article sur cette question, mais en général, il y a toujours moyen de récupérer le flux RSS d’un site, même s’il est bien caché !
bsky.rss
bsky.rss est un petit bijou de code qu’on peut trouver ici :
la commande git installée
- hors du champ de ce tuto, mais voici l’essentiel
Docker et Docker-Compose installé
- Installer Docker et Docker-compose
mise en place
Je suis sur Linux Ubuntu, donc l’approche ici part de ce principe :
step 1
- Créer un dossier /bsky-bot/
- Cloner le dépôt à l’intérieur de ce dossier
git clone github.com/milanmdev/bsky.rss disclosengo
step 2
- rentrer dans le dossier via votre terminal
cd disclosengo
structure
fichier et dossier important
- docker-compose.example.yml
- data/config.example.json
- Dockerfile
ls disclosengo
- Vous devriez voir un dossier data, le fichier compose et le fichier data/config.json
- préparer vos fichiers de config
cp docker-compose.example.yml docker-compose.yml
cp data/config.example.json data/config.json
Mon compose file
Voici à quoi doit ressembler votre fichier docker-compose.yml une fois configuré
version: "3"
services:
bsky-rss:
restart: always # le conteneur se lance au démarrage
image: bsky-queue:latest
container_name: bsky-rss-disclosengo # le nom du container une fois qu'il tourne
mem_limit: "256m" #mémoire limitée à
mem_reservation: "128m" #mémoire réservée
environment:
- APP_PASSWORD=bqif-
- INSTANCE_URL=https://bsky.social
- FETCH_URL=https://disclose.ngo/feed?lang=en
- IDENTIFIER=disclosengo.bsky.social
volumes:
- ./data:/build/data # le fameux dossier data dans le dépôt ? il sera utilisé comme volume d'écriture
Passons sur la branche de test pour bénéficier du système de file d’attente
git fetch --all
Fetching origin
- par défaut, on est sur la branche main
disclosengo# git branch -a
* main
remotes/origin/HEAD -> origin/main
remotes/origin/main
remotes/origin/queue # on veut celle ci !!
- on bouge sur la branche “queue”
disclosengo# git checkout queue
Branch 'queue' set up to track remote branch 'queue' from 'origin'.
Switched to a new branch 'queue'
disclosengo#
- on confirme qu’on est bien sur la bonne branche
git branch
main
* queue
- Par défaut le fichier docker-compose.yml va construire la dernière version stable de bsky.rss, mais nous on veut aller sur la version de dev, du coup on va la construire :
- on va devoir changer une ligne dans notre fichier docker-compose.yml
ceci
image: ghcr.io/milanmdev/bsky.rss
devient :
image: bsky-queue:latest
on sauve le fichier et on fait :
docker build -t bsky-queue .
très important le point, il indique qu’on va utiliser le Dockerfile dans le dossier où l’on se trouve, ou se trouve le docker-compose.yml et le Dockerfile
Sending build context to Docker daemon 406kB
Step 1/8 : FROM node:lts
---> d9ad63743e72
Step 2/8 : LABEL org.opencontainers.image.description "A configurable RSS poster for Bluesky"
---> Using cache
---> 9899d560e894
Step 3/8 : LABEL org.opencontainers.image.source "https://github.com/milanmdev/bsky.rss"
---> Using cache
---> 3b9d7809df6e
Step 4/8 : WORKDIR /build
---> Using cache
---> d112e60329f1
Step 5/8 : COPY package.json yarn.lock ./
---> Using cache
---> 4d72a941e806
Step 6/8 : RUN yarn install --frozen-lockfile
---> Using cache
---> 8ce9a7982023
Step 7/8 : COPY . .
---> aa24c9bb0b84
Step 8/8 : CMD yarn start
---> Running in 8bcfa27fee9b
Removing intermediate container 8bcfa27fee9b
---> 4a3c1736110d
Successfully built 4a3c1736110d
Successfully tagged bsky-queue:latest
à ce stade vous avez la dernière image avec la version du code en phase de test, ce qui va nous permettre d’utiliser le système de file d’attente.
Ma config data/config.sjon
- le lien va être intégré en carte, avec image intégrée grâce à PublishEmbed
- en fonction du flux RSS, la description peut être utilisée pour générer un post plus long que juste le titre
- configurer la langue en fonction du contenu du flux RSS
- truncate c’est pour raccourcir le texte si la description est utilisée
- le runInterval c’est toutes les minutes, il va checker s’il y a du contenu à publier
- le datefield ne vous préoccupez pas avec ça pour le moment
{
"string": "$title",
"publishEmbed": true,
"languages": ["en"],
"truncate": true,
"runInterval": 60,
"dateField": ""
}
Options
Quand le “publishEmbed” est en “True”, il n’est pas nécéssaire d’ajouter la variable $link dans le champ string, qui correspond à ce qui va être posté, en effet la génération de la carte Embed, va se baser sur les meta OpenGraph du lien, l’image, la description et le titre qui y sont associés.
{
"string": "$title - $link $description", # en général le titre suffit
"publishEmbed": true, # false déconseillé de mettre en false sauf si pas de lien
"languages": ["en"], # fr, de, es, etc...
"truncate": true, #false
"runInterval": 60, # 120 absent de la branch main
"dateField": "" # par défaut, il cherche pubDate sauf si le flux RSS n'est pas standard
}
step 3
- à ce stade, vous êtes prêt pour lancer la bête !
Lancement
docker-compose up
D’abord le bot va remplir la file d’attente avec les posts dispo dans le flux RSS
:/home/bsky-bot/disclosengo# docker-compose up
[+] Running 1/1
⠿ Container bsky-rss-disclosengo Created 0.2s
Attaching to bsky-rss-disclosengo
bsky-rss-disclosengo | yarn run v1.22.19
bsky-rss-disclosengo | $ tsx ./app/index.ts
bsky-rss-disclosengo | [Mon, 14 Aug 2023 11:29:24 GMT] - [bsky.rss APP] Started RSS reader. Fetching from https://www.inoreader.com/stream/user/1005343511/tag/Disclosengo every 5 minutes.
[bsky.rss QUEUE] Starting queue handler. Running every 60 seconds
[bsky.rss QUEUE] Queuing item (Revealed: Perenco’s damaging oil spills in Gabon)
[bsky.rss QUEUE] Queuing item (Lützerath: French banks finance the extension of one of Europe’s largest coal mines )
À ce stade, le bot se déploie et vous devriez voir un retour dans votre terminal, si tout va bien, il va checker que le flux RSS et publier ce qu’il trouve à publier, ensuite le bot écrit dans un fichier txt (data/last.txt) la dernière fois qu’il a checker le flux RSS et va utiliser cette date pour comparer s’il y a du nouveau dans le flux RSS et ce toutes les 5 minutes.
[bsky.rss POST] Posting new item (Revealed: Perenco’s damaging oil spills in Gabon)
[bsky.rss POST] Posting new item (Lützerath: French banks finance the extension of one of Europe’s largest coal mines )
[bsky.rss QUEUE] Finished running queue. Next run in 60 seconds
[bsky.rss QUEUE] Running queue with 20 items
[bsky.rss QUEUE] Finished running queue. Next run in 60 seconds
[bsky.rss QUEUE] Running queue with 0 items
Vous pouvez interrompre le bot avec CTRL+C, changer la config, changer le flux RSS, bref fignoler les détails et quand vous êtes content du résultat, vous lancez le bot avec
docker-compose up -d
Cela va lancer le bot comme un processus de tâche de fond.
Repasser sur la branche main, stable du code :
ceci dans le fichier docker-compose.yml
image: bsky-queue:latest
redevient :
image: ghcr.io/milanmdev/bsky.rss
on sauve le fichier et on fait :
docker-compose pull
on vérifie bien que le fichier config.json est bien adapté à la version du code
{
"string": "$title", #vérifier la zone qui va construire le text du post
"publishEmbed": true, #intégration des liens/images
"languages": ["en"], #la,gues
"truncate": true, # couper la description si trop longue
"runInterval": 60, # si pas dans la branche queue
"dateField": "" # champ date spécifique pour flux RSS non-standard
}
Bien faire attention que la dernière ligne de configuration du fichier config.json, ne doit pas comporter de virgule, mais toutes celles qui la précèdent bien !!
On lance la sauce :
docker-compose up -d
vérifier l’état du bot sur laydocker ou docker logs -f nom-de-votre-container
lazydocker
et voilà, vous êtes en train de faire tourner la version stable du bot !
Outils
- J’utilise Lazydocker pour explorer les conteneurs qui tournent, le log, voir si tout va bien
- docker-ctop est aussi pas mal pour explorer vite fait les bots qui tournent
- Il y a moyen de “mixer” plusieurs flux RSS ensemble et ainsi avoir un bot multisources, mais ça, c’est pour un autre tuto !
Interesting tool for devs
Explore the #ActivityPub protocol interactively
ActivityPub.Academy is a learning resource for ActivityPub. The protocol is brought to life by showing Activities sent between different instances in real time!
Neat Comprehensive #Python Cheatsheet
Python & ActivityPub, initial research
A bunch of Python related ressources and ActivityPub
Implementations
These are examples of ActivityPub server implementations written in Python:
- ActivityPub Example: An example of an ActivityPub server implementation written in Python.
- dsblank/activitypub: Prototyping a Python ActivityPub distributed server in Tornado.
- pylodon: Flask-based ActivityPub server implementation. Source: GitHub - BasixKOR/awesome-activitypub
Libraries
These are libraries that can help you with implementing ActivityPub:
- activity: ActivityPub library for Golang.
- activitypub-php: A library to turn any PHP project into a full ActivityPub implementation. Source: GitHub - BasixKOR/awesome-activitypub
Other Resources
Here are some Python libraries and resources that might be useful:
- atoot: A library providing an easy way to create Mastodon API applications. Source: delightful-activitypub-development - Codeberg.org
- Federation: Library to abstract social web federation protocols like ActivityPub, Diaspora, and Matrix. Source: delightful-activitypub-development - Codeberg.org
- fuwuqi: A server for ActivityPub enthusiasts. Source: delightful-activitypub-development - Codeberg.org
- Little Boxes: A tiny ActivityPub framework that is both database and server agnostic. Source: delightful-activitypub-development - Codeberg.org
- Mastodon.py: A Python wrapper for the Mastodon API. Source: delightful-activitypub-development - Codeberg.org
- pynodeinfo: NodeInfo library implemented with poetry. Source: delightful-activitypub-development - Codeberg.org
- Python ActivityPub: A general ActivityPub library. Source: delightful-activitypub-development - Codeberg.org
Remember to check each resource to see if it’s suitable for your project’s specific needs and constraints.