3615 Twitch

un bot dans mon minitel ?

undefined

3615 GOUZ

Sylvain Gougouzian

Consultant Dev Senior

zenika

Clermont-Ferrand

https://gouz.dev

Historique du Minitel

MINITEL

Médium Interactif par Numérisation d’Information TELéphonique

A quoi ça servait ????

Déploiement

55 foyers à Saint-Malô en Juillet 1980

4000 en Ille-et-Vilaine en 1981

4 juin 1982 : lancement officiel

Février 1984

3615

Technique

🐓🇫🇷

Norme Antiope du service de télécommunications Vidéotex

Transpac

transpac

Videotex

transpac

Teletexte

teletexte

Caractéristiques techniques

undefined

Mode semi-graphique

undefined

undefined

undefined

Nouvelle résolution

undefined

Les motifs

code mosaïque

Bernard MARTI — Travail personnel

Extrait d'un cours donné à l'Université de Paris Dauphine sur l'histoire du Vidéotex : code mosaïque

Structure d'un caractère

undefined

0b1110111 ou 0x77

undefined

La luminance ou luma (Y')

undefined

Partie du signal qui transporte l'information de luminosité de chaque élément de l'écran

Et sur le minitel ?

Y’ = 0,299 x R + 0,587 x G + 0,114 x B

Blanc pur

Y’ = 0,299 x 255 + 0,587 x 255 + 0,114 x 255

Y' = 76,245 + 149,685 + 29,07

Y' = 255

Bleu pur

Y’ = 0,299 x 0 + 0,587 x 0 + 0,114 x 255

Y' = 0 + 0 + 29,07

Y' = 29,07

Les couleurs disponibles

undefined
CouleurLuma
Noir0
Bleu29.07
Rouge76.245
Magenta105.315
Vert149.685
Cyan178.755
Jaune225.93
Blanc255

Nouvelle table de conversion

CouleurLuma
Noir0 - 31
Bleu32 - 63
Rouge64 - 95
Magenta96 - 127
Vert128 - 159
Cyan160 - 191
Jaune192 - 223
Blanc224 - 255

Résumé

1 motif de 2x3

2 couleurs parmi 8 :

Conversion d'image

rillettes

Merci @aurelievache

Redimensionnement 80x72

rillettes

Traitement par bloc de 2x3

parts

Résultat

Résultat

Luma

const luma = (rgb) =>
  Math.round(
    (  parseInt(rgb[0]) * 299 
     + parseInt(rgb[1]) * 587 
     + parseInt(rgb[2]) * 114
    ) / 1000
  );

Texte ou Surlignage

const state = (x, y, colors, pixels) => {
  const color = luma(pixels[x + y * 80]);
  const minLuma = Math.abs(colors[0] - color);
  const maxLuma = Math.abs(colors[1] - color);
  return minLuma > maxLuma ? 0 : 1;
};

Motif

const motif = (x, y, colors, pixels) => {
  const xPix = x * 2;
  const yPix = y * 3;
  let car = 0;
  car += Math.pow(2, 0) * state(xPix + 0, yPix + 0, colors, pixels);
  car += Math.pow(2, 1) * state(xPix + 1, yPix + 0, colors, pixels);
  car += Math.pow(2, 2) * state(xPix + 0, yPix + 1, colors, pixels);
  car += Math.pow(2, 3) * state(xPix + 1, yPix + 1, colors, pixels);
  car += Math.pow(2, 4) * state(xPix + 0, yPix + 2, colors, pixels);
  car += Math.pow(2, 5) * 1;
  car += Math.pow(2, 6) * state(xPix + 1, yPix + 2, colors, pixels);
  return car;
};

Structure de la trame pour l'envoi

Structure de la trame pour un motif

Electronique

DIN

din

Fonctionnement

Serial Port

RX, TX, GND

Carte électronique

NodeMCU ESP8266

Branchement

Bibliothèques

https://github.com/eserandour/Minitel1B_Soft

🙏 Éric Sérandour

ESPAsyncWebSrv et ESPAsyncTCP

Arduino - Headers

#include "Minitel1B_Soft.h"
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebSrv.h>
Minitel minitel(3, 1); // RX, TX
const char *ssid = "3615Twitch";
const char *password = "gouz.dev";
AsyncWebServer server(80);

Arduino - Setup 1

void setup() 
{
  minitel.changeSpeed(4800);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    minitel.newScreen();
    minitel.println("Connecting to WiFi...");
    delay(1000);
  }
  minitel.newScreen();
  minitel.println(WiFi.localIP().toString());

Arduino - Setup 2

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { 
    request->send(200, "text/html", "<!doctype html><html lang=en>...</html>"); 
  });
  server.on("/new", HTTP_GET, [](AsyncWebServerRequest *request) {
    minitel.newScreen();
    request->send(200); 
  });
  server.on("/end", HTTP_GET, [](AsyncWebServerRequest *request) {
    minitel.noCursor();
    request->send(200); 
  });
  server.on("/post", HTTP_POST, action);
  DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", 
                                       "*");
  server.begin();
  minitel.println("Ready!");
}

Arduino - loop & action

void loop() {}
void action(AsyncWebServerRequest *request)
{
  AsyncWebParameter* p = request->getParam(0);
  char *code = strtok( (char*) p->value().c_str(), ",");
  while (code != NULL) {
    minitel.writeByte(atoi(code));
    code = strtok(NULL, ",");
  }
  request->send(200);
}

Twitch

🤖

Comment fonctionne un bot ?

Interactions avec le tchat

TMI.js

import tmi from "tmi.js";
const client = new tmi.Client({
  options: { debug: false, messagesLogLevel: "info" },
  connection: {
    reconnect: true,
    secure: true,
  },
  channels: ["gouz$_$_"],
});
client.connect().catch(console.error);
client.on("message", (channel, tags, message, self) => {
  if (self) return;
  treatMessage({ message, tags });
});

Traitement d'un message

const treatMessage = ({ message, tags }) => {
  const parts = message.toString().split(" ");
  const cmd = parts.shift();
  if (cmd.toLowerCase() = "!3615") {
    const prompt = parts.join(" ");
    if (
          (tags.mod || tags.badges.broadcaster) 
          && prompt.startsWith("http")
    ) {
      // is moderator, then this viewer can send image directly
      loadImage(prompt).then(treatImage);
    } else {
      // ...
    }
  }
};

StableDiffusion

axios
  .post(`${process.env.STABLE_DIFFUSION_URL}/sdapi/v1/txt2img`, {
    width: 512,
    height: 512,
    steps: 10,
    prompt: `${prompt}, black and white`,
    negative_prompt: "nsfw",
    cfg_scale: 7,
  })
  .then(async (response) => {
    if (response.data.images.length) {
      const img = new Image();
      img.src = `data:image/png;base64, ${response.data.images[0]}`;
      treatImage(img);
    }
  });

Conclusion

Survival

03 58 43 51 50 : IUT Auxerre

MiniMit : https://github.com/multiplie-fr/minimit

Crédit

SliDesk: https://github.com/slidesk/slidesk

Fonts: Minitel & Handlee

NodeMCU photo: Vowstar

Backgrounds : Unsplash, ...

Remerciements

📖 François Tonic & le magazine Programmez! (n°258)

🐼 Thierry Chantier & 📶 Kongduino

💾 Eric Sérandour

🐈‍⬛ Aurélie Vache

🎓 Bernard Marti

🫶

des questions ?

undefined

C'est à vous

C'est à vous ! http://192.168.0.101:1337/public