"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Ejecutar un Discord Bot en Raspberry Pi

Ejecutar un Discord Bot en Raspberry Pi

Publicado el 2024-11-07
Navegar:549

Foto de portada de Daniel Tafjord en Unsplash

Recientemente completé un bootcamp de ingeniería de software, comencé a trabajar en preguntas fáciles de LeetCode y sentí que me ayudaría a ser responsable si tuviera un recordatorio diario para resolver preguntas. Decidí implementar esto usando un bot de Discord que se ejecuta en un horario de 24 horas (en mi confiable Raspberry Pi, por supuesto) que haría lo siguiente:

  • ir a un banco de datos predefinido de preguntas sencillas sobre leetcode
  • responde una pregunta que no se ha publicado en el canal de Discord
  • publica la pregunta de leetcode como un hilo en el canal de Discord (para que puedas agregar fácilmente tu solución)
  • la pregunta está marcada como publicada para evitar publicarla nuevamente en el canal

Running a Discord Bot on Raspberry Pi

Me doy cuenta de que puede ser más fácil simplemente ir a LeetCode y resolver una pregunta por día, pero aprendí mucho sobre Python y Discord con la ayuda de ChatGPT en este miniproyecto. Este también es mi primer intento de hacer bocetos, así que tengan paciencia jajaja

Running a Discord Bot on Raspberry Pi

Configuración

1. Utilice el entorno virtual Python
2. Instalar dependencias
3. Configure la base de datos de preguntas fáciles de Leetcode
4. Configurar variables de entorno
5. Crear aplicación Discord
6. ¡Ejecute el robot!

1. Utilice el entorno virtual Python

Recomiendo el uso de un entorno virtual Python porque cuando lo probé inicialmente en Ubuntu 24.04, encontré el siguiente error

Running a Discord Bot on Raspberry Pi

Configurarlo es relativamente fácil, simplemente ejecuta los siguientes comandos y listo, ¡estás en un entorno virtual de Python!

python3 -m venv ~/py_envs
ls ~/py_envs  # to confirm the environment was created
source ~/py_envs/bin/activate

2. Instalar dependencias

Se requieren las siguientes dependencias:

  • AWS CLI

Instale AWS CLI ejecutando lo siguiente:

curl -O 'https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip'
unzip awscli-exe-linux-aarch64.zip 
sudo ./aws/install
aws --version

Luego ejecute aws configure para agregar las credenciales requeridas. Consulte Configurar el documento AWS CLI.

  • dependencias de pip

Las siguientes dependencias de pip se pueden instalar con un archivo de requisitos ejecutando pip install -r requisitos.txt.

# requirements.txt

discord.py
# must install this version of numpy to prevent conflict with
# pandas, both of which are required by leetscrape
numpy==1.26.4   
leetscrape
python-dotenv

3. Configure la base de datos de preguntas fáciles de leetcode

Leetscrape fue vital para este paso. Para obtener más información al respecto, consulte los documentos de Leetscrape.
Solo quiero trabajar en preguntas fáciles de leetcode (para mí, incluso son bastante difíciles), así que hice lo siguiente:

  • obtenga la lista de todas las preguntas de leetcode usando leetscrape y guarde la lista en csv
from leetscrape import GetQuestionsList

ls = GetQuestionsList()
ls.scrape() # Scrape the list of questions
ls.questions.head() # Get the list of questions
ls.to_csv(directory="path/to/csv/file")
  • cree una tabla de Amazon DynamoDB y rellénela con una lista de preguntas sencillas filtradas del archivo csv guardado en el paso anterior.
import csv
import boto3
from botocore.exceptions import BotoCoreError, ClientError

# Initialize the DynamoDB client
dynamodb = boto3.resource('dynamodb')

def filter_and_format_csv_for_dynamodb(input_csv):
    result = []

    with open(input_csv, mode='r') as file:
        csv_reader = csv.DictReader(file)

        for row in csv_reader:
            # Filter based on difficulty and paidOnly fields
            if row['difficulty'] == 'Easy' and row['paidOnly'] == 'False':
                item = {
                    'QID': {'N': str(row['QID'])},  
                    'titleSlug': {'S': row['titleSlug']}, 
                    'topicTags': {'S': row['topicTags']},  
                    'categorySlug': {'S': row['categorySlug']},  
                    'posted': {'BOOL': False}  
                }
                result.append(item)

    return result

def upload_to_dynamodb(items, table_name):
    table = dynamodb.Table(table_name)

    try:
        with table.batch_writer() as batch:
            for item in items:
                batch.put_item(Item={
                    'QID': int(item['QID']['N']),  
                    'titleSlug': item['titleSlug']['S'],
                    'topicTags': item['topicTags']['S'],
                    'categorySlug': item['categorySlug']['S'],
                    'posted': item['posted']['BOOL']
                })
        print(f"Data uploaded successfully to {table_name}")

    except (BotoCoreError, ClientError) as error:
        print(f"Error uploading data to DynamoDB: {error}")

def create_table():
    try:
        table = dynamodb.create_table(
            TableName='leetcode-easy-qs',
            KeySchema=[
                {
                    'AttributeName': 'QID',
                    'KeyType': 'HASH'  # Partition key
                }
            ],
            AttributeDefinitions=[
                {
                    'AttributeName': 'QID',
                    'AttributeType': 'N'  # Number type
                }
            ],
            ProvisionedThroughput={
                'ReadCapacityUnits': 5,
                'WriteCapacityUnits': 5
            }
        )

        # Wait until the table exists
        table.meta.client.get_waiter('table_exists').wait(TableName='leetcode-easy-qs')
        print(f"Table {table.table_name} created successfully!")

    except Exception as e:
        print(f"Error creating table: {e}")

# Call function to create the table
create_table()

# Example usage
input_csv = 'getql.pyquestions.csv'  # Your input CSV file
table_name = 'leetcode-easy-qs'      # DynamoDB table name

# Step 1: Filter and format the CSV data
questions = filter_and_format_csv_for_dynamodb(input_csv)

# Step 2: Upload data to DynamoDB
upload_to_dynamodb(questions, table_name)

4. Configurar variables de entorno

Crear un archivo .env para almacenar variables de entorno

DISCORD_BOT_TOKEN=*****

5. Crear aplicación Discord

Siga las instrucciones en los documentos para desarrolladores de Discord para crear una aplicación y un bot de Discord con los permisos adecuados. Asegúrese de autorizar el bot con al menos los siguientes permisos de OAuth:

  • Enviar mensajes
  • Crear hilos públicos
  • Enviar mensajes en hilos

6. ¡Ejecute el robot!

A continuación se muestra el código del bot que se puede ejecutar con el comando python3 discord-leetcode-qs.py.

import os
import discord
import boto3
from leetscrape import GetQuestion
from discord.ext import tasks
from dotenv import load_dotenv
import re
load_dotenv()

# Discord bot token
TOKEN = os.getenv('DISCORD_TOKEN')

# Set the intents for the bot
intents = discord.Intents.default()
intents.message_content = True # Ensure the bot can read messages

# Initialize the bot
bot = discord.Client(intents=intents)
# DynamoDB setup
dynamodb = boto3.client('dynamodb')

TABLE_NAME = 'leetcode-easy-qs'
CHANNEL_ID = 1211111111111111111  # Replace with the actual channel ID

# Function to get the first unposted item from DynamoDB
def get_unposted_item():
    response = dynamodb.scan(
        TableName=TABLE_NAME,
        FilterExpression='posted = :val',
        ExpressionAttributeValues={':val': {'BOOL': False}},
    )
    items = response.get('Items', [])
    if items:
        return items[0]
    return None

# Function to mark the item as posted in DynamoDB
def mark_as_posted(qid):
    dynamodb.update_item(
        TableName=TABLE_NAME,
        Key={'QID': {'N': str(qid)}},
        UpdateExpression='SET posted = :val',
        ExpressionAttributeValues={':val': {'BOOL': True}}
    )

MAX_MESSAGE_LENGTH = 2000
AUTO_ARCHIVE_DURATION = 2880

# Function to split a question into words by spaces or newlines
def split_question(question, max_length):
    parts = []
    while len(question) > max_length:
        split_at = question.rfind(' ', 0, max_length)
        if split_at == -1:
            split_at = question.rfind('\n', 0, max_length)
        if split_at == -1:
            split_at = max_length

        parts.append(question[:split_at].strip())
        # Continue with the remaining text
        question = question[split_at:].strip()

    if question:
        parts.append(question)

    return parts

def clean_question(question):
    first_line, _, remaining_question = message.partition('\n')
    return re.sub(r'\n{3,}', '\n', remaining_question)

def extract_first_line(question):
    lines = question.splitlines()
    return lines[0] if lines else ""

# Task that runs on a schedule
@tasks.loop(minutes=1440) 
async def scheduled_task():
    channel = bot.get_channel(CHANNEL_ID)
    item = get_unposted_item()

    if item:
        title_slug = item['titleSlug']['S']
        qid = item['QID']['N']
        question = "%s" % (GetQuestion(titleSlug=title_slug).scrape())

        first_line = extract_first_line(question)
        cleaned_question = clean_message(question)
        parts = split_message(cleaned_question, MAX_MESSAGE_LENGTH)

        thread = await channel.create_thread(
            name=first_line, 
            type=discord.ChannelType.public_thread
        )

        for part in parts:
            await thread.send(part)

        mark_as_posted(qid)
    else:
        print("No unposted items found.")

@bot.event
async def on_ready():
    print(f'{bot.user} has connected to Discord!')
    scheduled_task.start()

@bot.event
async def on_thread_create(thread):
    await thread.send("\nYour challenge starts here! Good Luck!")

# Run the bot
bot.run(TOKEN)

Hay varias opciones para ejecutar el bot. En este momento, solo estoy ejecutando esto en un shell tmux, pero también puedes ejecutarlo en un contenedor acoplable o en una VPC de AWS, Azure, DigitalOcean u otros proveedores de nube.

Ahora solo tengo que intentar resolver las preguntas de Leetcode...

Declaración de liberación Este artículo se reproduce en: https://dev.to/beretests/running-a-discord-bot-on-raspberry-pi-4la4?1 Si hay alguna infracción, comuníquese con [email protected] para eliminarla.
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3