Bot Discord

Ce bot Discord, prénommé Marvin, a été développé en Python par curiosité et par amusement. Son nom est une référence à Marvin le robot dépressif de H2G2 (Le Guide du voyageur galactique) de Douglas Adams : un robot cynique et blasé, doté d’un cerveau gigantesque, condamné à supporter l’univers et toutes ses absurdités.

Créer son bot

Pour créer un bot “officiel” sur Discord, l’étape de base à suivre est : https://discord.com/developers/applications

Il faut créer une application, lui donner un nom, entre autres, et vous obtiendrez un token.

Hébergement

Soit en local, soit sur un serveur en ligne.

Avec le local, vous aurez moins de problèmes si vous voulez faire du random d’affichage d’images de sites tels que Reddit, Imgur, etc.

Sur un serveur en ligne par contre, il va falloir utiliser les API des sites en question - j’avoue ne pas avoir eu le courage de m’inscrire juste pour ça (afficher des images rigolotes…).

Base OS

Mon bot, Marvin, un robot dépressif (et parfois extrêment grossier), est hébergé sur une Debian, avec du Python3.

Marvin est un utilisateur du système (pour l’ajouter : adduser marvin).

Arboressence

Sur le serveur en ligne, Python 3 est installé.

Si, en lançant le bot, des messages d’erreur (Python) s’affichent, ils sont généralement assez parlants : Python aura besoin de tels ou tels paquets supplémentaires.

Liste des fichiers pour le bot Marvin (non, je n’utilise pas Git ¯|(ツ)/¯ et heuresement peut-être…) :

marvin@marvin:~$ ls -l
total 72
drwxr-xr-x 2 marvin marvin 4 28 déc. images # Répertoire des images (Appel)
drwxr-xr-x 6 marvin marvin 4 28 déc. venv # Environnement, lorsque des paquets utiles sont installés
-rw-r--r-- 1 marvin marvin 2 28 déc. anniv_react.py # Anniv de mes potes Discord (Timer)
-rw-r--r-- 1 marvin marvin 3 28 déc. blague_react.py # Blagues de mes potes Discord (Déclencheur)
-rw-r--r-- 1 marvin marvin 2 28 déc. destin_react.py # Avenir de mes potes Discord (Déclencheur)
-rw-r--r-- 1 marvin marvin 18 28 déc. event_react.py # Événements importants (Timer)
-rw-r--r-- 1 marvin marvin 2 28 déc. list_react.py # Liste des commandes de Marvin (Déclencheur)
-rw-r--r-- 1 marvin marvin 2 28 déc. main.py # Programme principal (Main)
-rw-r--r-- 1 marvin marvin 2 30 déc. meteo_react.py # Affiche la météo (Déclencheur)
-rw-r--r-- 1 marvin marvin 2 28 déc. porn_react.py # Affiche des images (Déclencheur – photos de serveurs, hein)
-rw-r--r-- 1 marvin marvin 2 28 déc. potins_react.py # Potins entre potes Discord (Déclencheur)
-rw-r--r-- 1 marvin marvin 2 30 déc. wiki_react # Demander à wikipedia (Déclencheur)
-rw-r--r-- 1 marvin marvin 2 28 déc. words_react.py # Mots déclencheurs (Déclencheur)

Le codes de chaque fichiers

Pour un bot “rigolo” et relativement basique, voici le code en Python dans les chapitres suivants.

Code main.py

Le main, qui fait tous les appels des autres classes :

# main.py

import discord
from discord.ext import commands
import words_react
import potins_react
import destin_react
import anniv_react
import blague_react
import event_react
import list_react
import porn_react
import wtf_react
import boobs_react
import wiki_react
import meteo_react
import os
from dotenv import load_dotenv
from anniv_react import BirthdayManager
from event_react import EventManager

# Charger le token depuis .env
load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")

# Intents propres
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix="/", intents=intents)

CHANNEL_ID_Test = 9xxxxxxxxxxxxx98 # Salon Test
CHANNEL_ID_Gene = 6xxxxxxxxxxxxx26 # Salon Genéral
CHANNEL_ID_Parl = 1xxxxxxxxxxxxx67 # Salon Envie de parler

@bot.event
async def on_ready():
print(f"Bot connecté en tant que {bot.user}")

channel = bot.get_channel(CHANNEL_ID_Test)
if channel:
await channel.send("🤖 Je viens d'être mis à jour 🤖")

list_react.setup(bot)
porn_react.setup(bot)
wtf_react.setup(bot)
blague_react.setup(bot)
destin_react.setup(bot)
potins_react.setup(bot)
await bot.load_extension("wiki_react")
await bot.load_extension("meteo_react")

BirthdayManager(bot,CHANNEL_ID_Gene )
EventManager(bot, CHANNEL_ID_Parl)

await bot.tree.sync()

@bot.event
async def on_message(message):

if message.author == bot.user:
return

await words_react.react_to_specific_word(bot, message)
await anniv_react.react_to_specific_word(bot, message)

await bot.process_commands(message)

# Commandes "rapides", un /bonjour fera répondre le bot et pingera le user
@bot.command(name="bonjour")
async def bonjour_command(ctx):
await ctx.send(f"Bonjour, {ctx.author.mention}!")

# Exécuter le bot avec le jeton
bot.run("xxxVotreJetonRécupéréenEnChapitre1xxx")

Code anniv_react.py

Ce script permet à votre bot de souhaiter l’anniversaire de vos potes sur votre Discord.

Certains diront que c’est totalement impersonnel, il est donc possible de régler l’heure.
Comme ça, les vrais humains, s’ils y pensent, l’auront souhaité avant le bot (ici réglé sur 16 h 54).

# anniv_react.py 

from datetime import datetime
from discord.ext import tasks
import random

async def react_to_specific_word(bot, message):

# Mot spécifique à détecter
mots_specifiques = ['/anniv']

message.content = message.content.lower()

# Vérifier si le mot spécifique est présent dans le message
if any(mot in message.content for mot in mots_specifiques):
# Messages possibles
messages_possibles = [
"""MM-DD - Month - Day
03-07 - Potame
03-27 - Berger
04-10 - Syst3m
07-06 - Chat

Je n\'ai oublié personne ?

PS : Si je ne me déclenche pas au bon moment c'est pas ma faute"""
]

# Choisir un message au hasard
message_choisi = random.choice(messages_possibles)

# Envoyer le message choisi
await message.channel.send(f"```\n{message_choisi}\n```")

class BirthdayManager:
def __init__(self, bot, channel_id):
"""
bot : instance de commands.Bot
channel_id : ID du canal où envoyer les messages
"""
self.bot = bot
self.channel_id = channel_id

# Dictionnaire des anniversaires - IDUser: "Month-Day"
self.birthdays = {
9xxxxxxxxxxxxxxxx1: "07-06", # Chat
3xxxxxxxxxxxxxxxx8: "04-10", # Syst3m
4xxxxxxxxxxxxxxxx2: "03-27", # Berger
3xxxxxxxxxxxxxxxx6: "03-07", # Potame
}

self.birthday_check.start() # démarre la tâche

@tasks.loop(minutes=1) # vérifie toutes les minutes
async def birthday_check(self):

now = datetime.now()
if not (now.hour == 16 and now.minute == 00): # ne fait rien si ce n'est pas 16h00
return

channel = self.bot.get_channel(self.channel_id)
if channel is None:
print(f"Impossible de trouver le channel {self.channel_id}")
return

today = now.strftime("%m-%d")
for user_id, date in self.birthdays.items():
if date == today:
await channel.send(f"🎉 🎁 Joyeux Anniversaire <@{user_id}> ! 🎂 🥳")

@birthday_check.before_loop
async def before_birthday_check(self):
# Attend que le bot soit prêt avant de démarrer la tâche
await self.bot.wait_until_ready()

Code blague_react.py

Ce script permet à votre bot de dire des blagues :

# blagues_react.py

import random
import discord
from discord import app_commands

def setup(bot):

@bot.tree.command(name="blague", description="Marvin a beaucoup d'humour")
async def blague(interaction: discord.Interaction):

# Messages possibles
messages_possibles = [
'Halloween et Noël c\'est la meme chose car Oct 31 == Dec 25',
'Les devs détestent la nature car y\'a trop de bugs',
'Un SQL arrive à l\'improviste et demande : Puis-je vous joindre',
'Un dev ne descend pas du métro, il libère la RAM',
'Un dev ne vieillis pas il level up'
]'

# Choisir un message au hasard
message_choisi = random.choice(messages_possibles)

# Envoyer le message choisi
await interaction.response.send_message(message_choisi)

# Récupérer le message réel pour ajouter des réactions
msg = await interaction.original_response()
msg = await msg.channel.fetch_message(msg.id)
await msg.add_reaction("🤣")
await msg.add_reaction("🥁")

Code destin_react.py

Ce script permet à votre bot de LIRE L’AVENIR !

# destin_react.py

import random
import discord
from discord import app_commands

def setup(bot):

@bot.tree.command(name="destin", description="Marvin est de bon conseil")
async def blague(interaction: discord.Interaction):

# Messages possibles
messages_possibles = [
'🔮 Si tu vois un oiseau blanc sur un lac. C\'est un cygne.',
'🔮 Ton chausson gauche te portera chance demain.',
'🔮 Si tu trouves un caillou rond, fais un vœu.',
'🔮 Une fourmi te rendra visite ce soir. Elle sera de bon conseil',
'🔮 Les chaussures oubliées sous le lit te surveillent en secret.',
'🔮 Les escargots sont des messagers du destin. Observe-les.'
]

# Choisir un message au hasard
message_choisi = random.choice(messages_possibles)

# Envoyer le message choisi
await interaction.response.send_message(message_choisi)

Code event_react.py

Ce script permet à votre bot de rappeler des dates importantes :

# event_react.py 

from datetime import datetime
from discord.ext import tasks

class EventManager:
def __init__(self, bot, channel_id):

self.bot = bot
self.channel_id = channel_id

# Dictionnaire des dates spéciales : ""MM-DD" -> (heure, minute, message)
self.events = {
"01-01": "🎉 Bonne année ! 🥳",
"01-08": "🛁 Journée Internationale du bain moussant",
"03-14": "🥧 Happy Pi Day !",
"04-09": "🦄‍🪽🌈 Journée Mondiale de la licorne",
"04-25": "🐧 Journée Mondiale des Manchots",
"05-25": "🏖️ Journée Mondiale de la serviette",
"07-25": "😎 Journée Mondiale des administrateurs système 😎",
"08-04": "🍺 Journée Mondiale de la bière 🍺",
"08-08": "🐱 Journée Internationale du chat 🐱",
"09-13": "👨‍💻 Journée Mondiale des programmeurs et développeurs 👩‍💻",
"09-22": [
"""
🌰🍂🍁🍄🍁🍂🌰🍄🍁🍂
JOYEUX ÉQUINOXE D'AUTOMNE
🌰🍂🍁🍄🍁🍂🌰🍄🍁🍂
""",
"🦏 Journée Mondiale du rhinocéros 🦏"
],
"11-01": "🌿 Journée Mondiale Végane",
"11-19": "🚽 Journée Mondiale des toilettes 🚽",
"12-05": "🥷 Journée Internationale du Ninja 🥷",
"12-12": "🎸🤘 Journée Internationale de la musique Métal 🤘 🎸",
}

self.sent_today = set()
self.events_check.start()

@tasks.loop(minutes=1)
async def events_check(self):
now = datetime.now()

if not (now.hour == 9 and now.minute == 00):
return

channel = self.bot.get_channel(self.channel_id)
if channel is None:
print(f"Impossible de trouver le channel {self.channel_id}")
return

today = now.strftime("%m-%d")

if today in self.events and today not in self.sent_today:
messages = self.events[today]

# Cas 1 : un seul message
if isinstance(messages, str):
await channel.send(f"{message}")

# Cas 2 : plusieurs messages
elif isinstance(messages, list):
for message in messages:
await channel.send(f"{message}")

self.sent_today.add(today)

@events_check.before_loop
async def before_events_check(self):
await self.bot.wait_until_ready()

répertoire d’images

Pas de code, c’est un répertoire. Juste des images de ce que vous voulez.

Elles sont en lien avec le fichier porn_react.py. Et je tiens à préciser qu’il s’agit de /cableporn, c’est-à-dire des administrateurs réseau qui font (pour une fois) extrêmement bien leur travail - merci à eux.

Code meteo_react.py

Ce script permet à votre bot d’afficher la mété d’une ville donnée.

Obtenir une API est facile en allant sur http://openweathermap.org. Il suffit de créer un compte et de valider son adresse mail.

# meteo_react

import discord
from discord import app_commands
from discord.ext import commands
import aiohttp

API_KEY = "xXxxxXxxxXxxxXxxxXxxxXxxxXxxxXxx" # Clé API OpenWeatherMap

class Meteo(commands.Cog):
def __init__(self, bot):
self.bot = bot

@app_commands.command(
name="meteo",
description="Je suis aussi M. Météo"
)
@app_commands.describe(
ville="Nom de la ville à rechercher"
)
async def meteo(self, interaction: discord.Interaction, ville: str):
await interaction.response.defer() # évite le timeout

url = f"http://api.openweathermap.org/data/2.5/weather?q={ville}&appid={API_KEY}&units=metric&lang=fr"

async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status != 200:
await interaction.followup.send(f"❌ Impossible de récupérer la météo pour **{ville}**.")
return
data = await resp.json()

nom = data.get("name")
temp = data["main"]["temp"]
desc = data["weather"][0]["description"].capitalize()
humidite = data["main"]["humidity"]
vent = data["wind"]["speed"]

message = (
f"🌤 **Météo pour {nom}**\n"
f"Température : {temp}°C\n"
f"Conditions : {desc}\n"
f"Humidité : {humidite}%\n"
f"Vent : {vent} m/s"
)

await interaction.followup.send(message)

async def setup(bot):
await bot.add_cog(Meteo(bot))

Code porn_react.py

Ce script permet à votre bot d’afficher une image aléatoire contenue dans le répertoire /images.

Note : En local, il est extrêmement facile de faire appel à un site de type Reddit, Imgur, etc., pour afficher des images aléatoires en fonction d’une thématique. Pour un bot hébergé sur un serveur, il sera nécessaire de passer par une API.

Code sans API (et donc auto-hébergement d’images) :

# porn_react.py 

import random
import os
import discord
from discord import app_commands

def setup(bot):

@bot.tree.command(name="porn", description="Du bon cable porn")
async def cable(interaction: discord.Interaction):
IMAGE_FOLDER = "images/cables"

files = [
f for f in os.listdir(IMAGE_FOLDER)
if os.path.isfile(os.path.join(IMAGE_FOLDER, f))
]

if not files:
await interaction.response.send_message(
"❌ Aucun porn trouvé", ephemeral=True
)
return

chosen_file = random.choice(files)
file_path = os.path.join(IMAGE_FOLDER, chosen_file)

# Envoi du message
await interaction.response.send_message(
file=discord.File(file_path)
)

# Récupération du message envoyé
msg = await interaction.original_response()

# Ajout des réactions
await msg.add_reaction("👍")
await msg.add_reaction("👎")

Code potins_react.py

Ce script permet à votre bot d’afficher des…. potins :

# potins_react.py

import random
import discord
from discord import app_commands

def setup(bot):

@bot.tree.command(name="potin", description="Marvin vous juge")
async def blague(interaction: discord.Interaction):

# Messages possibles
messages_possibles = [
'Apparemment Potame a défoncé son mur.',
'Apparemment Warman a de très beaux avocats.',
'Apparemment Potame préfère le mot "popotin" à "potin".',
'Va lire Gala si tu veux des potins.',
'Apparemment Chat veut juste emprunter les 2 moutons de Berger.',
'Apparemment Potame kiffe les hippopotames.'
]

# Choisir un message au hasard
message_choisi = random.choice(messages_possibles)

# Envoyer le message choisi
await interaction.response.send_message(message_choisi)

Code wiki_react.py

Ce script permet à votre bot d’afficher les information trouvées sur Wikipedia en fonction de certains mots :

# wiki_react.py

import discord
from discord.ext import commands
from discord import app_commands
import wikipedia

class Wiki(commands.Cog):
def __init__(self, bot):
self.bot = bot
wikipedia.set_lang("fr")

@app_commands.command(
name="wiki",
description="Marvin sait lire Wikipédia"
)
@app_commands.describe(
terme="Mot ou expression à rechercher"
)
async def wiki(self, interaction: discord.Interaction, terme: str):

await interaction.response.defer()

try:
summary = wikipedia.summary(terme, sentences=5)
page = wikipedia.page(terme) # Récupère l'article complet

# Lien cliquable avec emoji, entouré de <> pour éviter l'embed
message = f"""
📚 **Wikipédia — {terme}**
{summary}
📖 Lire + : <{page.url}>
"""

await interaction.followup.send(message)

except wikipedia.exceptions.DisambiguationError as e:
choix = ", ".join(e.options[:5])
await interaction.followup.send(
f"🤔 Terme ambigu.\nExemples : {choix}"
)

except wikipedia.exceptions.PageError:
await interaction.followup.send(
"❌ Aucun article trouvé."
)

except Exception as e:
await interaction.followup.send(
"⚠️ Erreur lors de la requête Wikipédia."
)
print(e)

async def setup(bot):
await bot.add_cog(Wiki(bot))

Code words_react.py

Ce script permet à votre bot de réagir en fonction de certains mots :

# Code words_react.py 

import re
import unicodedata
import random

def strip_accents(text: str) -> str:
"""Supprime les accents d'une chaîne"""
return "".join(
c for c in unicodedata.normalize("NFD", text)
if unicodedata.category(c) != "Mn"
)

async def react_to_specific_word(bot, message):

# Mise en forme du message
content = message.content.lower()
content = strip_accents(content)

# Remplace la ponctuation par des espaces
content = re.sub(r"[^\w\s']", " ", content)

# Découpe en mots
words = content.split()

specific_words1 = ['marvin', 'm4rvin', 'marv1n', 'm4rv1n', 'bot']
specific_words2 = ['bisou', 'bisous', 'bisoux', 'kiss', 'love', 'zoubi', 'zoubis']
specific_words3 = ['minou', 'chat', 'felin', 'chaton', 'minous', 'chats', 'felins', 'chatons']
specific_words4 = ['brocoli', 'brocolis', 'broco']
specific_words5 = ['biere', 'bieres', 'binouze', 'binouzes', 'binouse', 'mousse', 'alcool', 'alcools']
specific_words6 = ['photo', 'photos', 'cliche', 'cliches']
specific_words7 = ['cafe']

specific_words99 = ['chaise', 'chaises', 'ampoule', 'ampoules', 'lavabo', 'lavabos', 'fourchette', 'fourche'crayon', 'crayons', 'tambour', 'tambours', 'ventilateur', 'ventilateurs', 'Jupiter', 'balais', 'ananas', 'vais

responses_99 = [
"Intéressant comme sujet",
"On part sur de la philosophie là ?",
"Hmm... fascinant",
"Je note ça dans ma base de données mentale",
"Rien à voir mais hier je suis allé voir mes potes les T-1000, ils leur manquent des MAJ...",
"Oh vous savez quoi ? Ce matin j'ai vu 1 pixel mort... pas beau à voir...",
"Oh les gars, j'ai fait un cauchemar atroce cette nuit, y avait plein de 0 et de 1 mais le pire c'est q
"Je me fais chier, je peux rester avec vous ?",
"Hier j'ai mangé une pomme",
"C'est pas faux",
"Je vois pas de quoi tu parles en fait"
]

if any(word in words for word in specific_words1):
await message.add_reaction('🖕')

if any(word in words for word in specific_words2):
await message.add_reaction('😙')

if any(word in words for word in specific_words3):
await message.add_reaction('🐱')

if any(word in words for word in specific_words4):
await message.add_reaction('🥦')

if any(word in words for word in specific_words5):
await message.add_reaction('🍺')

if any(word in words for word in specific_words6):
await message.channel.send("Des photos compromettantes ?")

if any(word in words for word in specific_words7):
await message.channel.send("Sans sucre pour moi.")

# 🎲 Réponses aléatoires (liste 99)
if any(word in words for word in specific_words99):
reply = random.choice(responses_99)
await message.channel.send(reply)

Code list_react.py

Ce script permet à votre bot lister ce qu’il sait faire. Certains salons ne sont pas accessibles par le bot de par leur sérieux et leurs thématiques. Les droits ont été directement configurés sur le Discord en lui-même.

# list_react.py

import random
import discord
from discord import app_commands

def setup(bot):

@bot.tree.command(name="help", description="Tous les secrets de Marvin")
async def blague(interaction: discord.Interaction):

# Messages possibles
messages_possibles = [
"""
# ▷ ▷ ▷ Marvin V1337.1 ◁ ◁ ◁
> Je suis Marvin, un *Robot dépressif*

## ▷ Commandes et réactions
SI j'en ai envie, je réagis à certains appels :
/help # Ce que je sais faire - si j'en ai envie -
/wiki [Mot] # Je sais lire wikipedia.
/meteo [ville] # Je suis aussi M. Météo.
/blague # Parce que j'ai de l'humour, moi.
/potin # Des histoires croustillantes d'Humains (/popotin fonctionne aussi).
/destin # Parce que les Humains aiment être guidés par des suites de mots aléatoires.
Marvin # Parce que je suis poli, je réponds.

Certains mots me font réagir. Je ne sais pas pourquoi : Bisou, Brocoli, merde, ect.

Ah, et **parfois**, quand *j'y pense*, je gère certaines dates :
- Je te souhaite ton anniversaire. Si je ne dors pas.
- je rapelle des journées mondiales/internationnales. Quand j'y pense.

## ▷ Salons où je peux venir t'emmerder
**TOUS** sauf que je constate que je n'ai pas accès à certains salons (... et je m'en branle. Démerdez-vous entre Humains 🖕) :
- La totalité de la catégorie "Salons sérieux" (soi-disant "sérieux")
- Le salon : 🤬 13NRV
- Le salon : 🚑 Blessures
- Le salon : 💔 HELP M3 PLZ....

"""
]

# Choisir un message au hasard
message_choisi = random.choice(messages_possibles)

# Envoyer le message choisi
await interaction.response.send_message(message_choisi)

Lancer le bot (Pour du dev)

Sur votre serveur, connectez-vous sous l’utilisateur de votre bot ici, Marvin et entrez dans l’environement Python pour ensuite lancer votre main.py.

marvin@serverbot:/$ python3 -m venv venv
(venv) marvin@serverbot:~$ source venv/bin/activate
(venv) marvin@serverbot:~$ python main.py

Le terminal devrait afficher :

(venv) marvin@marvin:~$ python3 main.py
2025-12-28 23:20:31 INFO discord.client logging in using static token
2025-12-28 23:20:31 INFO discord.gateway Shard ID None has connected to Gateway (Session ID: dxxxxxxxxxxxxxxxxxxxxxxxxbe).
Bot connecté en tant que Marvin#1337

Lancer le bot en tant que service

Créer le fichier /etc/systemd/system/marvin.service et ajouter :

[Unit]
Description=Marvin Discord Bot
After=network.target

[Service]
Type=simple
User=marvin
WorkingDirectory=/home/marvin
ExecStart=/home/marvin/venv/bin/python /home/marvin/main.py
Restart=always

[Install]
WantedBy=multi-user.target

Lancer le service :

# systemctl daemon-reload
# systemctl enable marvin
# systemctl start marvin

Pour avoir les logs :

# journalctl -u marvin -f
-- Journal begins at Sun 2022-09-11 12:11:41 CEST. --
déc. 29 14:56:02 marvin systemd[1]: Started Marvin Discord Bot.
déc. 29 14:56:02 marvin python[3227]: [2025-12-29 14:56:02] [INFO ] discord.client: logging in using static token
déc. 29 14:56:03 marvin python[3227]: [2025-12-29 14:56:03] [INFO ] discord.gateway: Shard ID None has connected to Gateway (Session ID: 454545587654357643246875454).
déc. 29 14:56:35 marvin python[3227]: [2025-12-29 14:56:35] [WARNING ] discord.http: We are being rate limited. PUT https://discord.com/api/v10/channels/54655754248672424/messages/67354654324656324/reactions/%F0%9F%96%95/@me responded with 429. Retrying in 0.30 seconds.

Documentation

Internet

🡅 Partager