[script] Les news dans le terminal (résolu)

Autres projets et contributions
Avatar de l’utilisateur
papajoke
Elfe
Messages : 773
Inscription : sam. 30 août 2014, 19:54

Re: [script] Les news dans le terminal (résolu)

Message par papajoke »

nouvelle version du script python
  • ordre inverse
  • passage de parametres optionnels: nombres de jours et -n pour même ordre que le fil
  • constantes couleurs au format rvb et lien vers doc pour customisation plus facile
  • python est objet donc pourquoi pas faire de l'objet (... même si aucunement nécessaire :wink: )

Code : Tout sélectionner

import os
import sys
import urllib.request
import html
from xml.etree import ElementTree as ET
import textwrap
import datetime

max_jours = 30  # limiter l'historique à ... on surcharge en parametre de notre script

#https://tforgione.fr/posts/ansi-escape-codes/
c_title = '\x1B[1m\x1b[38;2;80;130;255m' # format: 38;2;R;V;B
c_date  = '\x1b[38;2;45;80;200m'
c_txt   = '\x1B[3m\x1B[2m'
c_none  = '\x1b[0m'

class DataNew:

    def __init__(self, title: str, pubdate: str, text: str):
        self.title = title
        self.text = html.unescape(text)
        self.pubdate = datetime.datetime.strptime(pubdate, '%a, %d %b %Y %H:%M:%S %z').date()

    def __str__(self):
        try:
            cols, _ = os.get_terminal_size(0)
        except OSError: # si utilise : "script.py &""
            cols = 90
        return \
            f"{c_title}{self.title:<86}{c_none}\t" \
            f"{c_date}{self.pubdate.strftime('%d/%m/%Y')} ({self.days} jours){c_none}"  \
            f"\n{c_txt}{textwrap.fill(self.text, cols-4, initial_indent='  ', subsequent_indent='  ')}{c_none}\n"

    @property
    def days(self):
        delta = datetime.datetime.today().date() - self.pubdate
        return delta.days

for arg in sys.argv[1:]:
    try:
        max_jours = int(arg)
    except ValueError:
        pass

try:
    with urllib.request.urlopen('https://archlinux.fr/feed') as f_url:
        datas = f_url.read()
except urllib.error.HTTPError:
    exit(1)

root = ET.fromstring(datas)
news = []
for t in root.findall("channel/item"):
    new = DataNew(
        title=t.find('title').text,
        pubdate=t.find('pubDate').text,
        text=t.find('description').text
    )
    if new.days > max_jours:
        break
    news.append(new)

if "-n" not in sys.argv[1:]:
    news.sort(key=lambda x: x.pubdate)    # par défaut trier par date desc
for new in news:
    print(new)
if len(news) > 1:
    print(f"Voir détails: \x1B[4m{c_title}https://archlinux.fr/feed{c_none}")
Arch stable - Kde 5 / zsh - btrfs/mbr - Intel Core i3 - 6Go RAM - GeForce 405 video-nouveau
avi3000
Chu Ko Nu
Messages : 404
Inscription : dim. 19 juin 2011, 18:53
Localisation : dans le neuf trois

Re: [script] Les news dans le terminal (résolu)

Message par avi3000 »

@laurent85 le retour du sed tueur de chatons. Approche divertissante. Pour les linuxiens bien barbus !!
@papajoke des choses intéressantes, d'autres moins.

la gestion des couleurs étendues:
J'ai repris. Sur ma archlinux, comme sur ma devuan, urxvt ne gère pas le mode RVB, uniquement le mode palette.
xterm supporte les 2 modes; st supporte uniquement le mode RVB.
Je crois qu'en TTY on ne peut pas gérer plus de 32768 couleurs. 127;127;255 fonctionne. 128;128;255 ne fonctionne pas.
Les attributs de style ne fonctionnent pas non plus.

Limitation sur le nombre de jours:
J'avoue ne pas trop comprendre. Sur le nombre de news, d'accord.

largeur d'affichage:
Tu utilises la largeur du terminal ce qui n'est pas une bonne idée. Merci pour mes cervicales, c'est pire que Rolland-Garros.
Il me semble préférable de laisser l'utilisateur choisir ce qu'il souhaite, avec un défaut à 100 caractères ce qui me semble être bien.
De même l'affichage de la date à une position fixe en fin de ligne génère des sauts de lignes aléatoires et inutiles.

Gestion de la personnalisation par paramètres :
C'est une source de complication inutile. L'utilisateur se sert pratiquement toujours de la même.
La personnalisation à la mode suckless aura toujours ma préférence, dans un fichier séparé ou en début de script.

POO:
Pour afficher 3 news !! Je préfère ne rien dire.

"Moi, je me soucie avant tout de l'utilisateur du script":
Soyons sérieux. Les utilisateurs éventuels de nos scripts, ne sont pas vraiment des utilisateurs lambdas.
Faut du courage, pour venir jusqu'ici. 4 pages de contributions, de digressions, d'engueulades.
Vu les royalties que je perçois, l'opinion de l'utilisateur !! Pareil, vaux mieux que je me taise ...

Ma version du jour compatible ksh et zsh par modification du shebang ou lancement via l'interpréteur adéquat, avec:
le traitement du fichier 'feed' avec ou sans sauts de ligne;
le remplacement des 'html entities' figurant dans ce fichier.
l'affichage des nouvelles en ordre chronologique croissant ou décroissant
la gestion du nombre de nouvelles affichées
la gestion de la largeur d'affichage pour la description.
la gestion des couleurs étendues et des attributs de style.
S'il manque quelque chose, n'hésitez pas, demandez. Je vous enverrai la facture.

Code : Tout sélectionner

#!/bin/bash

IFS=$'\n'	#pas toucher, en-dessous on peut
LA_PAGE=${@:-https://archlinux.fr/feed}
WRAPMIN=80
WRAPMAX=100
INDENT='   '
ORDER=-1 		#-1 inverse de la page html, 1 chronolique décroissant
NB_NEWS=10		#C'est 10 max dans la page depuis des années

#https://tforgione.fr/posts/ansi-escape-codes/
RED="\033[38;2;255;10;10m" 		#RED="\033[38;5;197m" pour rxvt qui ne supporte pas le mode RVB
BLEU="\033[38;2;150;150;255m"		#BLEU="\033[38;5;33m" sur ma devuan
ITAL="\033[3m"
DEFO="\033[0m"

function textwrap {
	local l=${#1} i=0 j=$WRAPMAX
	while (( j < l )); do
		while (( j-- > i )); do
			[ "${1:$j:1}" = ' ' ] && break
		done
		(( j - i < WRAPMIN )) && j=$(( i + WRAPMAX ))
		printf "\n$INDENT${1:$i:$j - $i + 1}"
		i=$(( j + 1 ))
		j=$(( j + WRAPMAX ))
	done
	(( l > 0 )) && printf "\n$INDENT${1:$i}$DEFO\n"
}

function print_news {
	local i lb=-1 ub=${#tit[*]} first
	[ -n "${titre[0]}" ] && first=0 || first=1 			# premier indice=1 zsh, ...

	(( ORDER < 0 )) && { 
		(( ub > NB_NEWS )) && lb=$(( NB_NEWS + first )) || lb=$(( ub + first ))
	} || {
		(( ub > NB_NEWS )) && ub=$(( NB_NEWS - 2 + first )) || ub=$(( ub - 2 + first ))
	}
	for (( i = lb; (ORDER < 0 && i > 0) || (ORDER > 0 && i <= ub); i += ORDER )); do
		printf "${tit[((i + ORDER))]}$ITAL"
		textwrap "${dsc[((i + ORDER))]}"
	done
}

function parse_z {
	z=${z//&#8216;/\'}
	z=${z//&#8217;/\'}
	z=${z//’/\'}			# pour les TTY
	z=${z//&#8211;/-}
	z=${z//&#8230;/...}
	z=${z//&gt;/>}
}

echo -e "$RED\t\t>> News Archlinux.fr <<\n$DEFO"
while read -d '<' z; do
	case ${z:0:1} in
	't')
		parse_z
		[ -z "$on_one" ] || titre=${z:6};;
	'p')
		[ "${z:0:8}" = 'pubDate>' ] && tit+=("$BLEU${z:13:11}$DEFO  $titre");;
	'd')
		[ "${z:0:12}" = 'description>' ] && {
			read -d '<' z
			z=${z#*CDATA\[}
			z=${z%]]>*}
			parse_z
			[ -z "$on_one" ] || dsc+=("$z\n")
			on_one='no'
		};;
	esac
done < <(curl -s $LA_PAGE)

print_news
printf "voir détails: $RED$LA_PAGE$DEFO\n"

# vim: ts=4 sw=4 :
"Essaye de coder simplement ce qu'il a fait en bash / perl":
pour la version bash c'est fait.
Avatar de l’utilisateur
benjarobin
Maître du Kyudo
Messages : 17187
Inscription : sam. 30 mai 2009, 15:48
Localisation : Lyon

Re: [script] Les news dans le terminal (résolu)

Message par benjarobin »

@avi3000 C'est vraiment dommage, cela aurait pu donner des choses très constructives, mais je ne pense pas que tu te rendes compte du ton de ton dernier message. Ta critique n'est absolument pas justifié, tu cherches depuis le début à tout démonter, alors que le script de papajoke est juste très joli et fonctionnel. Ok, tu as une dent envers python, mais clairement ici personne va dans ton sens, donc s'il te plaît arrête !!!!!
Vraiment c'est dans l’intérêt de tout le monde sinon cela va déraper (en tout cas je risque...)
Zsh | KDE | PC fixe : core i7, carte nvidia
Titre d'un sujet : [Thème] Sujet (état) / Règles du forum
Avatar de l’utilisateur
papajoke
Elfe
Messages : 773
Inscription : sam. 30 août 2014, 19:54

Re: [script] Les news dans le terminal (résolu)

Message par papajoke »

il est presque normal de détester un language, c'est le lot de pratiquement tous les codeurs, c'est juste dommage de polluer un sujet là dessus. Il serait plus amusant d'ouvrir un sujet

Aller, il n'existe pas que python, une petite digression avec go (500Mo le compilateur installé donc pas pour tous) - avec go pas d'objet ce qui peut ravir certains...
la copie fonctionnelle du script python, ici le seul problème était wrap() avec indent qui n'existe pas en natif

Code : Tout sélectionner

package main

// version 23/11/2019

import (
	"encoding/xml"
	"fmt"
	"html"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	url_news  = "https://archlinux.fr/feed"
	MAX_JOURS = 30 // affiche que historique de x jours

	// couleurs doc https://tforgione.fr/posts/ansi-escape-codes/
	C_TITLE = "\x1B[1m\x1B[34m"
	C_DATE  = "\x1b[38;2;45;80;200m"
	C_TXT   = "\x1B[3m\x1B[2m"
	C_NONE  = "\x1b[0m"
	C_GRAY  = "\033[38;5;243m"
)

var ttycolums int = 100

type Item struct {
	XMLName     xml.Name `xml:"item"`
	Description string   `xml:"description"`
	Link        string   `xml:"link"`
	PubDate     string   `xml:"pubDate"`
	Title       string   `xml:"title"`
	jours       int
}

/*
 * représentation textuelle de la news
 */
func (p Item) String() string {
	t := html.UnescapeString(p.Description)
	t = p.word_wrap(t, ttycolums, "  ")
	d, _ := time.Parse("Mon, 02 Jan 2006 15:04:05 -0700", p.PubDate)
	dstr := d.Format("02 Jan 2006")

	return fmt.Sprintf(
		"%s%s%s \t %s(%s  %s)%s\n%s%s%s\n%s%s%s\n",
		C_TITLE, p.Title, C_NONE,
		C_DATE, dstr, p.Since(), C_NONE,
		C_TXT, t, C_NONE,
		C_GRAY, p.Link, C_NONE,
	)
}

/*
 * représentation de l'age de la news
 */
func (p *Item) Since() string {
	if p.jours < 1 {
		return "Aujourd'hui"
	}
	plus := "s"
	if p.jours == 1 {
		plus = ""
	}
	return fmt.Sprintf("%d jour%s", p.jours, plus)
}

/*
 * calcul de l'age de la news
 * sauvegarde pour ne pas le recalculer
 */
func (p *Item) Days() int {
	if p.jours < 1 {
		t, err := time.Parse("Mon, 02 Jan 2006 15:04:05 -0700", p.PubDate)
		if err != nil {
			return 0
		}
		current_time := time.Now()
		p.jours = int(current_time.Sub(t).Hours() / 24)
	}
	return p.jours
}

/*
 * affichage d'un paragraphe de texte avec un retrait à gauche
 * supp les baliles html
 */
func (p Item) word_wrap(s string, limit int, indent string) string {
	re := regexp.MustCompile(`<[^>]+>`)
	s = string(re.ReplaceAll([]byte(s), []byte{}))
	if len(s) > 1224 {
		s = s[0:1224] + " ..."
	}
	if strings.TrimSpace(s) == "" || len(s) < limit {
		return indent + s
	}
	strSlice := strings.Fields(s)
	result := indent
	lline := len(indent)

	for len(strSlice) >= 1 {
		nextlong := len(strSlice[0])
		if lline+nextlong <= limit {
			result = result + strSlice[0] + " "
			lline = lline + nextlong + 1
		} else {
			result = result + "\r\n" + indent + strSlice[0] + " "
			lline = len(indent) + nextlong + 1
		}
		strSlice = strSlice[1:]
	}
	return result
}

type Rss struct {
	Items []Item `xml:"channel>item"`
}

type Parametres struct {
	Reserve    bool
	Historique int
	Url        string
}

/*
 *  lecture des parametres passés au script
 */
func (p *Parametres) Parse() Parametres {
	p.Historique = p.getParamI(p.Historique)
	for _, arg := range os.Args[1:] {
		if strings.HasPrefix(arg, "http") {
			p.Url = arg
		}
	}
	if p.paramIsPresent("-n") {
		p.Reserve = false
	}
	if p.paramIsPresent("-h") {
		fmt.Println(os.Args[0], "[nombre de jours] [http url] [-n] [-h]", "\n\t-n: affichage croissant")
		fmt.Println("\nActuellement:",
			"\n\tSens décroissant:\t", p.Reserve,
			"\n\tNombre de jours:\t", p.Historique,
			"\n\tUrl:\t\t\t", p.Url,
		)
		os.Exit(0)
	}
	return *p
}

func (p Parametres) getParamI(def int) (ret int) {
	for _, arg := range os.Args[1:] {
		if x, err := strconv.ParseInt(arg, 10, 32); err == nil {
			return int(x)
		}
	}
	return def
}

func (p Parametres) paramIsPresent(attr string) bool {
	for _, arg := range os.Args[1:] {
		if arg == attr {
			return true
		}
	}
	return false
}

/*
 * Execution du programme
 */

func init() {
	// calcul de la largeur du terminal
	var h, w int
	cmd := exec.Command("stty", "size")
	cmd.Stdin = os.Stdin
	out, err := cmd.Output()
	if err == nil {
		fmt.Sscan(string(out), &h, &w)
		ttycolums = w - 2
	}
}

func main() {
	// tests des parametres du script
	params := Parametres{true, MAX_JOURS, url_news}
	//fmt.Println(params)
	params.Parse()
	//fmt.Println(params)

	// téléchargement du fil
	resp, err := http.Get(params.Url)
	if err != nil {
		fmt.Printf("Erreur HTTP: %v\n", err)
		return
	}
	defer resp.Body.Close()

	// convertion du xml
	fil := Rss{}
	decoder := xml.NewDecoder(resp.Body)
	err = decoder.Decode(&fil)
	if err != nil {
		fmt.Printf("Erreur Decode xml: %v\n", err)
		return
	}

	// affichage
	for _, item := range fil.Items {
		if item.Days() <= params.Historique {
			if params.Reserve {
				defer fmt.Println(item)
			} else {
				fmt.Println(item)
			}
		}
	}
}
Dernière modification par papajoke le sam. 23 nov. 2019, 19:00, modifié 2 fois.
Arch stable - Kde 5 / zsh - btrfs/mbr - Intel Core i3 - 6Go RAM - GeForce 405 video-nouveau
Avatar de l’utilisateur
waitnsea
Maître du Kyudo
Messages : 2114
Inscription : jeu. 15 mars 2012, 05:08

Re: [script] Les news dans le terminal (résolu)

Message par waitnsea »

Code : Tout sélectionner

$ time 'go run news.go'
	'go run news.go'  0,00s user 0,00s system 73% cpu 0,001 total
Ça dépote, et c'est joli...
...on n'est pas payé mais on rigole...
Avatar de l’utilisateur
benjarobin
Maître du Kyudo
Messages : 17187
Inscription : sam. 30 mai 2009, 15:48
Localisation : Lyon

Re: [script] Les news dans le terminal (résolu)

Message par benjarobin »

@waitnsea Il ne faut pas mettre entre guillemet la commande, elle n'est pas exécutée ici, ajoute juste avant la commande : time.
Jamais tu iras aussi vite. De toute façon comme déjà expliqué le temps d’exécution est négligeable par rapport au temps de téléchargement qui se chiffre à 400ms chez moi.
Sinon avec go il est préférable de le compiler au préalable pour avoir quelque chose de "rapide".
Zsh | KDE | PC fixe : core i7, carte nvidia
Titre d'un sujet : [Thème] Sujet (état) / Règles du forum
Avatar de l’utilisateur
waitnsea
Maître du Kyudo
Messages : 2114
Inscription : jeu. 15 mars 2012, 05:08

Re: [script] Les news dans le terminal (résolu)

Message par waitnsea »

benjarobin a écrit : ven. 22 nov. 2019, 20:01 @waitnsea Il ne faut pas mettre entre guillemet la commande, elle n'est pas exécutée ici, ajoute juste avant la commande : time.
Jamais tu iras aussi vite. De toute façon comme déjà expliqué le temps d’exécution est négligeable par rapport au temps de téléchargement qui se chiffre à 400ms chez moi.
Sinon avec go il est préférable de le compiler au préalable pour avoir quelque chose de "rapide".
Oui, vrai, une fois compilé :

Code : Tout sélectionner

time news                                                                              
Clarification concernant l’activité récente sur la liste arch-announce   (Sat, 26 Oct 2019 07:19:00 +0000 27 jours)
  Aujourd’hui[NdT: 25 octobre 2019], un mail envoyé à la liste de diffusion arch-announce a pu contourner les vérifications de liste 
 […] 
 news  0,08s user 0,01s system 15% cpu 0,574 total
Avatar de l’utilisateur
papajoke
Elfe
Messages : 773
Inscription : sam. 30 août 2014, 19:54

Re: [script] Les news dans le terminal (résolu)

Message par papajoke »

Pour compiler en Go, ajouter l'option -ldflags "-s -w" va te faire gagner quelques Mo (Mais il en reste encore quelques :mrgreen: ) - éventuellement avec upx on peut encore descendre (de 40%) mais le gros bébé reste
Et mesurer la vitesse ici, c'est comme donner une médaille pour avoir fini second une course à pied de 3 mètres au JO
Dernière modification par papajoke le sam. 23 nov. 2019, 01:29, modifié 2 fois.
Arch stable - Kde 5 / zsh - btrfs/mbr - Intel Core i3 - 6Go RAM - GeForce 405 video-nouveau
Avatar de l’utilisateur
waitnsea
Maître du Kyudo
Messages : 2114
Inscription : jeu. 15 mars 2012, 05:08

Re: [script] Les news dans le terminal (résolu)

Message par waitnsea »

papajoke a écrit : ven. 22 nov. 2019, 20:38 une course à pied de 3 mètres
Ça c'est encore dans mes moyens... Le marathon de Nice que je viens de voir passer (sous la pluie !), non ...
L'avantage de ton programme c'est qu'il suffit de modifier la page dans url_news = pour passer au News inter, parfois en avance d'une journée, moins facile avec les autres langages
Avatar de l’utilisateur
papajoke
Elfe
Messages : 773
Inscription : sam. 30 août 2014, 19:54

Re: [script] Les news dans le terminal (résolu)

Message par papajoke »

en fait ce programme à une option en plus du python : si on passe un http***** en paramètre alors il va charger cette url

PS: ne passe pas avec tous les fils :oops:

mais passe avec les news inter : (il y a de quoi faire)

Code : Tout sélectionner

./arch-gnews https://www.archlinux.org/feeds/news/
passe avec maj de repos

Code : Tout sélectionner

./arch-gnews  "https://www.archlinux.org/feeds/packages/x86_64/" 
Arch stable - Kde 5 / zsh - btrfs/mbr - Intel Core i3 - 6Go RAM - GeForce 405 video-nouveau
Répondre