Un bot qui tweete 4992 Super-Héros à raison de un toutes les 3,14 heures peut-il sauver Internet ? 10


Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Depuis l’Antiquité, en moyenne, environ une seule personne s’est posée cette question, dont moi.

Je me propose donc de partager avec vous mon expérience sur le sujet en vous expliquant ce que j’ai mis en œuvre pour tenter d’obtenir une réponse.

Bonsjours, je voudrais tweeter en Python, s’il vous plaît.

Il existe plusieurs bibliothèques en Python qui papotent avec l’API de Twitter. Neuf sont actuellement référencées par Twitter.

Parce qu’il y avait écrit “Actively maintained”, j’ai tenté twython .

sudo pip install twython

Il se trouve qu’elle fait très bien ce que j’attendais d’elle, donc je ne suis pas allé chercher plus loin.

Par ailleurs, le code pour envoyer un tweet est flippant de simplicité :

from twython import Twython
 
APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"
 
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
 
texte = 'Oh mon Dieu ! Mais cette librairie crache des flammes ! #Twython'
 
twitter.update_status(status=texte)

Et paf :

 

Pour obtenir ses petites clefs-clefs, il est nécessaire de se créer un compte sur la plate-forme de Twitter réservée aux développeurs.

Je vous la fait courte :

> My applications dans le menu de votre icône en haut à droite > Create New Apps
> Remplir le formulaire > accepter les conditions d’utilisation après les avoir lues
> Permissions > Read and Write
> API Keys > Generate my acces token

La partie la plus compliquée pour moi fut de passer les permissions en “Read and Write“.

En effet, il faut associer un numéro de téléphone au compte twitter utilisé et la manipulation doit être faite à partir du téléphone en question.
Or, je n’ai pas de téléphone portable.

Heureusement, la femme de ma Vie a eu la bonne idée de faire l’Amour il y a 17 ans et sa fille, la fille de ma Vie, possède ce genre d’appareil.
Sauvé.

Si vous n’avez pas de fille de votre Vie, ni même de femme de votre Vie, faites en sorte d’avoir un téléphone portable.

(J’ai mis Amour et Vie en gras pour gagner des points de Karma. Je ne sais pas si ça marche, mais je me dis que ce serait tout de même bien malheureux que ça ait un effet négatif.)

Bonsjours, je voudrais ajouter une image au tweet que je tweete en Python, s’il vous plaît.

En partant du principe que vous avez une image.png à côté de votre script :

from twython import Twython
 
APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"
 
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
 
texte = 'Mais quelle folie ! #Twython tweete aussi des images ! '
image = open("image.png", 'rb')
 
twitter.update_status_with_media(status=texte, media=image)

Et hop :

 

J’ai du mal à imaginer qu’on puisse faire plus simple.

Maintenant, j’aimerais tweeter toutes les 3,14 heures. C’est possible ?

Pour tweeter toutes les 3 heures, une petite crontab réglée de la sorte aurait fait l’affaire :

0 */3 * * * python chemin/vers/mon/script.py

Mais pour 3,14 heures, il faut faire dans le délicat.
Je fais donc patienter le script 11 304 secondes entre chaque envoi :

from time import sleep
 
while True:
 
	envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net()
	sleep(11304)

Tout à fait cochon.
Tout à fait parfait.

Et pour les 4992 Super-Héros.
Comment fait-on ?

Alors là, vous n’allez pas être déçus.

Afin de d’obtenir 4992 Super-Héros, je me suis dit qu’il me suffisait de trouver 24 têtes de Super-Héros, 13 torses de Super-Héros et 16 jambes de Super-Héros puis de réaliser toutes les combinaisons possibles de ces trois éléments.

24 * 13 * 16 = 4992

Hasard de folie, il se trouve qu’afin de récolter des fonds pour mener à bien leurs actions, La Quadrature Du Net propose une petite appli de personnalisation de Super-Héros pour celles et ceux qui font un don.

Et bien, aussi incroyable que cela puisse paraître, elle propose de choisir parmi 24 têtes de Super-Héros du Net, 13 torses de Super-Héros du Net, 16 jambes de Super-Héros du Net.

L’émotion est un peu retombée, mais, sur le moment, j’ai eu la chaire de poule face à une telle coïncidence, surtout quand on sait que La Quadrature défend les droits et les libertés des citoyens sur Internet depuis 2008 et que c’est pile-poil dans la thématique de la question qui fait l’objet de cet article.

Me voilà donc parti pour trouver un moyen de récupérer tous ces morceaux de Super-Héros du Net et de reconstituer les 4992 Super-Héros entiers possibles.

Pour retrouver l’adresse de chaque morceau, un simple coup d’œil au code-source de la page a suffi.

Ensuite, en terme de n’importe quoi je vous propose un morceau de choix.

Car pour faire ma petite popote, j’ai utilisé Processing.

_ Processing ?!?!
_ Oui. Processing.
_ Mais… ça utilise une syntaxe Java, ça. Non ?
_ Oui. Mais pour Sam&Max j’ai utilisé le mode Python qui vient tout juste de sortir.
_ Utiliser un “framework Java” en Python. Excuse-moi, mais c’est un peu n’importe quoi.
_ Tout à fait.

Pour celles et ceux qui ne le connaissent pas, Processing est un logiciel/langage conçu pour que les artistes, les graphistes, les designers et les grosses bubuses comme moi puissent facilement et rapidement produire du code créatif (image, son, vidéo).

Je ne vais pas vous faire un cours. Il y a un FLOSS Manual en français pour ça.

Utilisant Processing depuis plusieurs années, je vous avoue que le mode Python m’a clairement donné l’impression de faire du skate avec des rollers, mais comme outils pédagogique, ça doit être pas mal.
Et puis ça marche !

Voici le code que j’ai pondu pour l’occasion :

# La fonction setup() est propre à Processing.
# Elle s’exécute en premier et une seule fois.
 
def setup():
 
    # On crée une image transparente de 1 pixel de côté qui sera utilisée
    # pour représenter l’absence de choix de tête, de torse et de jambes
    img_vide = createImage(1, 1, ARGB)
    img_vide.pixels[0] = color(0, 0)
 
    # On crée des listes qui contiendront les images des têtes, torses et jambes
    # et on place l'image vide crée juste avant comme premier élément.
    global liste_torses, liste_tetes, liste_jambes
    liste_tetes = [img_vide]
    liste_torses = [img_vide]
    liste_jambes = [img_vide]
 
    # On lance le téléchargement de toutes les images nécessaires.
    telechargement_images()
 
    # On fabrique les 4992 Super-Héros avec les images téléchargées.
    usine_super_heros()
 
def telechargement_images():
 
    # Sachant que chaque image est atteignable par une URL du type
    # http://soutien.laquadrature.net/images/bonus/head_01.png
 
    # On définit l'URL du dossier où sont situées les images.
    url = "http://soutien.laquadrature.net/images/bonus/"
 
    # On télécharge l'image du Super-Héros tout nu qui est placé au fond
    # de chaque image et qui sert de base à tous les autres.
    global base
    base = loadImage(url + "base.png")
 
    # On télécharge les 24 têtes, les 13 torses et les 16 jambes.
    choix_image(url, "head_", liste_tetes, 24)
    choix_image(url, "torso_", liste_torses, 13)
    choix_image(url, "legs_", liste_jambes, 16)
 
def choix_image(url, nom, liste, nb_image):
 
    # Pour chaque nombre entre 1 et nb_image (0 correspondant à img_vide),
    for i in range(1, nb_image):
 
        # on définit un numéro toujours composé de 2 chiffres. Ex: 05 pour 5,
        numero = str(i).zfill(2)
        # on récupère l'image en recomposant l'URL,
        image_recuperee = loadImage(url + nom + numero + ".png")
        # et on ajoute cette image dans la liste correspondante.
        liste.append(image_recuperee)
 
def usine_super_heros():
 
    # Sachant que toutes images récupérées ont la même taille (214x383)
    # et qu'elles sont des .png transparents dont le contenu trouve sa place
    # quand les images sont parfaitement superposées .
 
    # On définit une variable qui va compter les 4992 Super-Héros.
    compteur = 0
 
    # Pour toutes les images de têtes,
    for tete in liste_tetes:
 
        # pour toutes les images de torses,
        for torse in liste_torses:
 
            # et pour toutes les images de jambes,
            for jambe in liste_jambes:
 
                # on crée un élément graphique de 214x383,
                super_hero = createGraphics(214, 383)
                # on démarre le dessin de l'élément graphique,
                super_hero.beginDraw()
                # on place un fond blanc,
                super_hero.background(255)
                # on place le Super-Héros tout nu aux coordonnées 0, 0
                super_hero.image(base, 0, 0)
                # on place l'image des jambes en 0, 0
                super_hero.image(jambe, 0, 0)
                # on place l'image du torse en 0, 0
                super_hero.image(torse, 0, 0)
                # on place l'image de la tête en 0, 0
                super_hero.image(tete, 0, 0)
                # on clos le dessin de l'élément graphique,
                super_hero.endDraw()
                # on sauvegarde notre Super-Héros du Net dans le dossier data,
                super_hero.save("data/SuperHeros-{0}.png".format(compteur))
                # et on incrémente le compteur de 1 avant de passer
                # au Super-Héros suivant
                compteur += 1

Et hop !

Et sinon, c’est encore loin Grand Schtroumpf ?

On arrive au bout.

Pour ne pas tweeter les Super-Héros du Net dans l’ordre et pour ne pas non plus poster deux fois le même en cas de croutage du serveur, je me suis créé un petit ordre.txt comme cela :

from random import shuffle
 
tous_les_numeros = range(4992)
shuffle(tous_les_numeros)
 
with open ("ordre.txt", 'w') as base:
 
    for numero in tous_les_numeros:
 
        base.write("{0},".format(numero))

Et dont le contenu est juste :

1798,915,1402,1032,507,112,1098,600,323,787,1435,200,774,33,1419,106,1836... etc

Enfin, voici le script final, qui tweete donc toutes les 3,14 heures un des 4992 Super-Héros du Net.

# -*- coding: utf8 -*-
 
# Ce script permet d'envoyer un tweet avec une image toutes les 3,14 heures
# Il récupère dans une liste le numéro de l'image a envoyer,
# et enregistre le nombre de tweets déjà postés
# pour pouvoir changer d'image à la prochaine exécution
 
import os
from twython import Twython
from time import sleep
 
# Clef d'authentification à obtenir sur https://dev.twitter.com
APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"
 
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
 
ordre = []
combien_deja_poste = 0
 
# Récupère le répertoire courant
chemin_dossier = os.path.dirname(os.path.realpath(__file__))+"/"
 
def envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net():
 
    # Récupère l'ordre des images à poster
    with open (chemin_dossier+"ordre.txt", 'r') as contenu:
 
            ordre = contenu.readline().rstrip('\n').split(",")
 
    # Récupère le nombre d'images déjà postées
    with open (chemin_dossier+"deja_poste.txt", 'r') as contenu:
 
        combien_deja_poste = int(contenu.readline().rstrip('\n\r'))
 
    # Récupère le numéro de l'image
    numero = int(ordre[combien_deja_poste])
 
    # Construit le chemin vers cette image
    # Sachant que le dossier AllStar contient les 4992 Super-Héros du Net
    chemin_image = chemin_dossier+"AllStar/SuperHeros-{0}.png".format(numero)
 
    # Charge l'image
    image = open(chemin_image, 'rb')
 
    # Forme le texte du tweet
    texte = ("Voici le Super-Héros du Net n°{0}\n"
             "Si ce n'est pas déjà fait, aidez-le, lui et ses 4992 amis\n"
             "soutien.laquadrature.net").format(numero+1)
 
    # Envoie le tweet composé d'un texte et d'une image
    twitter.update_status_with_media(status=texte, media=image)
 
    # Met à jour le nombre d'images déjà postées
    with open (chemin_dossier+"deja_poste.txt", 'w') as contenu:
 
        contenu.write(str(combien_deja_poste + 1))
 
while True:
 
    envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net()
 
    # Patiente pendant les 3,14 heures, soit  les 11 304 secondes
    sleep(11304)
 
    # Le script plantera dans 2 ans quand les 4992 Super-Héros du Net
    # aurons tous été postés, et c'est très bien ainsi...

Et voilà, il ne reste plus qu’à choper 10 Millions de followers sur ce compte et à espérer que 3,14% d’entre eux filent 10€ à LQDN pour que l’on puisse augmenter les chances de répondre positivement à la question posée dans le titre.
Une simple formalité…

Je précise que La Quadrature n’a absolument rien à voir avec ce compte Twitter, c’était juste l’occasion pour moi de pondre un article de plus ici tout en soutenant une cause qui me tient hackeur (humour).

À la prochaine…

Information complémentaire :
Le Monde en date du 12 août – Près d’un compte Twitter sur 10 est alimenté automatiquement

10 thoughts on “Un bot qui tweete 4992 Super-Héros à raison de un toutes les 3,14 heures peut-il sauver Internet ?

  • foxmask

    Elle est pas belle la vie de la fille de la femme de ta vie de ton pere de ta mere de ta vie ? :D

    A l’auteur du billet : twython supporte quelle(s) version(s) de python ?
    J’ai essaye python-twitter qui ne supporte pas la 3 à cause d’une lib tièrce (oauth2) qui ne supporte pas python 3 itself.

    edit : j’ai décidement pas le zieu zen fesse des trous c’est comme le porc salut “Actively maintained and featuring support for Python 2.6+ and Python 3″
    je vais give it a try alors !

  • Zanguu

    petite coquille numérique
    “Enfin, voici le script final, qui tweete donc toutes les 3,14 heures un des 1881 Super-Héros du Net.”
    devrait plutot être :
    “Enfin, voici le script final, qui tweete donc toutes les 3,14 heures un des 4992 Super-Héros du Net.”
    Il me semble.

  • 01ivier Post author

    @ Zanguu : fixed. Merci.

    En fait, entre la rédaction de l’article, cet été, et la publication, la Quadrature a ajouté des éléments supplémentaires et fait passé le nombre du Super-Héros de 1881 à 4992… il en reste d’ailleurs encore les traces dans les captures d’écrans… que j’ai, bien entendu, volontairement laissées pour les archéologues, dans 3000 ans… :-p

  • Ho

    Pendant un moment j’ai cru que Sam s’était marié et avait eu un gosse depuis le début et qu’il venait de le révéler comme ça au débotté… ho putain.

    Les chatons, quand vous changerez de thème, pensez à indiquer de façon visible les posts d’invités (ou de commensaux, comme dirait Eolas). Bisous.

  • Stéphane

    Je ne pense pas que l’envoi se fasse toutes les 3,14 heures.
    Avec la crontab, script.py est exécuté toutes les 3 heures. Le script attend le 0,14 heure manquant.

    On lance un première fois le script et la crontab est en place
    À la seconde exécution, il y a bien 3,14 heures d’attente.
    Mais à partir de la troisième, on attend (3 heures + décalage de 0,14). Ce qui est le même décalage qu’à l’éxecution précédente, donc il n’y a que 3 heures entre les 2 twits.

    Une solution serait plutôt d’utiliser `at` pour exécuter la commande 3,14 heures plus tard à la fin du script.

  • 01ivier Post author

    @Stéphane : C’est peut-être ambiguë dans l’article, mais, pour les raisons que tu évoques, je n’utilise justement pas de crontab. C’est le “sleep()” qui s’occupe, tout seul de faire patienter le script pendant 3,14 heures, soit 11304 secondes…
    Ce qui, j’en conviens, n’est pas du tout élégant… mais fonctionne… :-p

  • foxmask

    Test concluant notamment pour le processus de oauth. Plus qu’à jouer sur les recherches et création de tweet ;)

  • Darqbit

    C’est génial ! Il faudrait inclure ce script par défaut pour tout les profils twitter :D

  • Seb

    Sympa l’article !

    2 remarques :
    Le code suivant n’est pas très Pythoneux… (une variable “compteur” déjà, ça sent pas bon :p)
    # On définit une variable qui va compter les 4992 Super-Héros.
    compteur = 0
    # Pour toutes les images de têtes,
    for tete in liste_tetes:
    # pour toutes les images de torses,
    for torse in liste_torses:
    # et pour toutes les images de jambes,
    for jambe in liste_jambes:

    Peut-être pythonnement écrit :
    from itertools import product
    for i, tete, jambe torse in enumerate(product(liste_tete,liste_jambe,liste_torse)) :

    et hop, 2 niveaux d’indentation en moins

    Et (oue, je suis pénible) pourquoi ne pas utiliser PIL au lieu d’utiliser tout l’attirail Processing (car si je comprends bien, le bouzin tourne en java non ?) pour coller deux images ?

  • Sam

    Les articles invités n’ont pas pour but de montrer le meilleur code Python possible, mais de faire partager l’univers d’un des auteurs. Néanmoins, cette remarque étant amenée poliement et respectueusement, elle est la bienvenue.

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.