Heim  >  Artikel  >  Backend-Entwicklung  >  Ausführen eines Discord-Bots auf Raspberry Pi

Ausführen eines Discord-Bots auf Raspberry Pi

Susan Sarandon
Susan SarandonOriginal
2024-10-01 12:11:02595Durchsuche

Titelbild von Daniel Tafjord auf Unsplash

Ich habe kürzlich ein Software-Engineering-Bootcamp abgeschlossen, mit der Arbeit an einfachen LeetCode-Fragen begonnen und hatte das Gefühl, dass es mir helfen würde, Verantwortung zu übernehmen, wenn ich täglich daran erinnert würde, Fragen zu lösen. Ich beschloss, dies mithilfe eines Discord-Bots zu implementieren, der im 24-Stunden-Rhythmus läuft (natürlich auf meinem treuen Raspberry Pi), der Folgendes tun würde:

  • Gehen Sie zu einer vordefinierten Datenbank mit einfachen Leetcode-Fragen
  • Ergreifen Sie eine Frage, die noch nicht im Discord-Kanal gepostet wurde
  • Posten Sie die Leetcode-Frage als Thread im Discord-Kanal (damit Sie Ihre Lösung einfach hinzufügen können)
  • Frage wird als gepostet markiert, um zu vermeiden, dass sie erneut im Kanal gepostet wird

Running a Discord Bot on Raspberry Pi

Mir ist klar, dass es vielleicht einfacher ist, einfach zu LeetCode zu gehen und täglich eine Frage zu lösen, aber ich habe bei diesem Miniprojekt mit Hilfe von ChatGPT viel über Python und Discord lernen können. Dies ist auch mein erster Versuch, Sketchnoting zu erstellen, also haben Sie bitte Geduld, lol

Running a Discord Bot on Raspberry Pi

Aufstellen

1. Verwenden Sie eine virtuelle Python-Umgebung
2. Abhängigkeiten installieren
3. Richten Sie die Leetcode-Datenbank für einfache Fragen ein
4. Umgebungsvariablen einrichten
5. Discord-App erstellen
6. Führen Sie den Bot aus!

1. Verwenden Sie eine virtuelle Python-Umgebung

Ich empfehle die Verwendung einer virtuellen Python-Umgebung, da ich beim ersten Test unter Ubuntu 24.04 auf den folgenden Fehler gestoßen bin

Running a Discord Bot on Raspberry Pi

Das Einrichten ist relativ einfach, führen Sie einfach die folgenden Befehle aus und voilà, Sie befinden sich in einer virtuellen Python-Umgebung!

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

2. Abhängigkeiten installieren

Die folgenden Abhängigkeiten sind erforderlich:

  • AWS CLI

Installieren Sie AWS CLI, indem Sie Folgendes ausführen:

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

Führen Sie dann aws configure aus, um die erforderlichen Anmeldeinformationen hinzuzufügen. Siehe Konfigurieren des AWS CLI-Dokuments.

  • Pip-Abhängigkeiten

Die folgenden Pip-Abhängigkeiten können mit einer Anforderungsdatei installiert werden, indem pip install -r require.txt ausgeführt wird.

# 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. Richten Sie die Leetcode-Datenbank für einfache Fragen ein

Leetscrape war für diesen Schritt von entscheidender Bedeutung. Weitere Informationen dazu finden Sie in den Leetscrape-Dokumenten.
Ich möchte nur an einfachen Leetcode-Fragen arbeiten (für mich sind sie sogar ziemlich schwierig), also habe ich Folgendes getan:

  • Erhalten Sie die Liste aller Fragen von Leetcode mit Leetscrape und speichern Sie die Liste im CSV-Format
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")
  • Erstellen Sie eine Amazon DynamoDB-Tabelle und füllen Sie sie mit einer Liste einfacher Fragen, die aus der im vorherigen Schritt gespeicherten CSV-Datei gefiltert wurden.
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. Umgebungsvariablen einrichten

Erstellen Sie eine .env-Datei zum Speichern von Umgebungsvariablen

DISCORD_BOT_TOKEN=*****

5. Erstellen Sie eine Discord-App

Befolgen Sie die Anweisungen in den Discord-Entwicklerdokumenten, um eine Discord-App und einen Discord-Bot mit entsprechenden Berechtigungen zu erstellen. Stellen Sie sicher, dass Sie den Bot mit mindestens den folgenden OAuth-Berechtigungen autorisieren:

  • Nachrichten senden
  • Öffentliche Threads erstellen
  • Nachrichten in Threads senden

6. Führen Sie den Bot aus!

Unten finden Sie den Code für den Bot, der mit dem Befehl python3 discord-leetcode-qs.py ausgeführt werden kann.

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)

Es gibt mehrere Möglichkeiten, den Bot auszuführen. Im Moment führe ich das nur in einer tmux-Shell aus, aber Sie könnten es auch in einem Docker-Container oder auf einer VPC von AWS, Azure, DigitalOcean oder anderen Cloud-Anbietern ausführen.

Jetzt muss ich nur noch versuchen, die Leetcode-Fragen zu lösen...

Das obige ist der detaillierte Inhalt vonAusführen eines Discord-Bots auf Raspberry Pi. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn